@@ -1605,6 +1605,84 @@ def test_stack_sort_false(future_stack):
1605
1605
tm .assert_frame_equal (result , expected )
1606
1606
1607
1607
1608
+ def assert_na_safe_equal (left , right ):
1609
+ """Compare DataFrames ignoring NA type differences"""
1610
+ left = left .rename (columns = {pd .NA : np .nan }, level = 1 )
1611
+ right = right .rename (columns = {pd .NA : np .nan }, level = 1 )
1612
+ tm .assert_frame_equal (left , right , check_dtype = False )
1613
+
1614
+ def test_unstack_sort_false_na ():
1615
+ # GH 61221
1616
+ levels1 = ['b' ,'a' ]
1617
+ levels2 = pd .Index ([1 , 2 , 3 , pd .NA ], dtype = pd .Int64Dtype ())
1618
+ index = pd .MultiIndex .from_product ([levels1 , levels2 ], names = ['level1' , 'level2' ])
1619
+ df = pd .DataFrame (dict (value = range (len (index ))), index = index )
1620
+ result = df .unstack (level = 'level2' , sort = False )
1621
+ expected = pd .DataFrame (
1622
+ {
1623
+ ('value' , 1 ): [0 , 4 ],
1624
+ ('value' , 2 ): [1 , 5 ],
1625
+ ('value' , 3 ): [2 , 6 ],
1626
+ ('value' , pd .Int64Dtype ().na_value ): [3 , 7 ]
1627
+ },
1628
+ index = pd .Index (['b' , 'a' ], name = 'level1' ),
1629
+ columns = pd .MultiIndex .from_tuples ([
1630
+ ('value' , 1 ), ('value' , 2 ), ('value' , 3 ), ('value' , pd .Int64Dtype ().na_value )
1631
+ ], names = [None , 'level2' ])
1632
+ )
1633
+ assert_na_safe_equal (result , expected )
1634
+ levels2 = pd .Index ([pd .NA , 1 , 2 , 3 ], dtype = pd .Int64Dtype ())
1635
+ index = pd .MultiIndex .from_product ([levels1 , levels2 ], names = ['level1' , 'level2' ])
1636
+ df = pd .DataFrame (dict (value = range (len (index ))), index = index )
1637
+ result = df .unstack (level = 'level2' , sort = False )
1638
+ expected = pd .DataFrame (
1639
+ {
1640
+ ('value' , pd .Int64Dtype ().na_value ): [0 , 4 ],
1641
+ ('value' , 1 ): [1 , 5 ],
1642
+ ('value' , 2 ): [2 , 6 ],
1643
+ ('value' , 3 ): [3 , 7 ] # Use actual pd.NA object
1644
+ },
1645
+ index = pd .Index (['b' , 'a' ], name = 'level1' ),
1646
+ columns = pd .MultiIndex .from_tuples ([
1647
+ ('value' , pd .Int64Dtype ().na_value ), ('value' , 1 ), ('value' , 2 ), ('value' , 3 )
1648
+ ], names = [None , 'level2' ])
1649
+ )
1650
+ assert_na_safe_equal (result , expected )
1651
+ levels2 = pd .Index ([ 1 , pd .NA , 2 , 3 ], dtype = pd .Int64Dtype ())
1652
+ index = pd .MultiIndex .from_product ([levels1 , levels2 ], names = ['level1' , 'level2' ])
1653
+ df = pd .DataFrame (dict (value = range (len (index ))), index = index )
1654
+ result = df .unstack (level = 'level2' , sort = False )
1655
+ expected = pd .DataFrame (
1656
+ {
1657
+ ('value' , 1 ): [0 , 4 ],
1658
+ ('value' , pd .Int64Dtype ().na_value ): [1 , 5 ],
1659
+ ('value' , 2 ): [2 , 6 ],
1660
+ ('value' , 3 ): [3 , 7 ] # Use actual pd.NA object
1661
+ },
1662
+ index = pd .Index (['b' , 'a' ], name = 'level1' ),
1663
+ columns = pd .MultiIndex .from_tuples ([
1664
+ ('value' , 1 ), ('value' , pd .Int64Dtype ().na_value ), ('value' , 2 ), ('value' , 3 )
1665
+ ], names = [None , 'level2' ])
1666
+ )
1667
+ assert_na_safe_equal (result , expected )
1668
+ levels2 = pd .Index ([3 , pd .NA , 1 , 2 ], dtype = pd .Int64Dtype ())
1669
+ index = pd .MultiIndex .from_product ([levels1 , levels2 ], names = ['level1' , 'level2' ])
1670
+ df = pd .DataFrame (dict (value = range (len (index ))), index = index )
1671
+ result = df .unstack (level = 'level2' , sort = False )
1672
+ expected = pd .DataFrame (
1673
+ {
1674
+ ('value' , 3 ): [0 , 4 ],
1675
+ ('value' , pd .Int64Dtype ().na_value ): [1 , 5 ],
1676
+ ('value' , 1 ): [2 , 6 ],
1677
+ ('value' , 2 ): [3 , 7 ] # Use actual pd.NA object
1678
+ },
1679
+ index = pd .Index (['b' , 'a' ], name = 'level1' ),
1680
+ columns = pd .MultiIndex .from_tuples ([
1681
+ ('value' , 3 ), ('value' , pd .Int64Dtype ().na_value ), ('value' , 1 ), ('value' , 2 )
1682
+ ], names = [None , 'level2' ])
1683
+ )
1684
+ assert_na_safe_equal (result , expected )
1685
+
1608
1686
@pytest .mark .filterwarnings ("ignore:The previous implementation of stack is deprecated" )
1609
1687
def test_stack_sort_false_multi_level (future_stack ):
1610
1688
# GH 15105
0 commit comments