@@ -548,6 +548,42 @@ def test_factorize_interval_non_nano(self, unit):
548548 codes3 , cats3 = idx3 .factorize ()
549549 assert cats3 .dtype == f"interval[datetime64[{ unit } , US/Pacific], right]"
550550
551+ def test_factorize_bool_int_distinguished (self ):
552+ # GH#62888 - factorize on object dtype should distinguish bool from int
553+ ser = Series ([0 , 1 , True , False ], dtype = object )
554+ codes , uniques = ser .factorize ()
555+
556+ expected_codes = np .array ([0 , 1 , 2 , 3 ], dtype = np .intp )
557+ expected_uniques = Index ([0 , 1 , True , False ], dtype = object )
558+ tm .assert_numpy_array_equal (codes , expected_codes )
559+ tm .assert_index_equal (uniques , expected_uniques , exact = True )
560+
561+ # Check that the actual types are preserved, not just values
562+ assert type (uniques [0 ]) is int
563+ assert type (uniques [1 ]) is int
564+ assert type (uniques [2 ]) is bool
565+ assert type (uniques [3 ]) is bool
566+
567+ def test_factorize_bool_int_distinguished_reverse (self ):
568+ # GH#62888 - order should not matter
569+ ser = Series ([True , False , 0 , 1 ], dtype = object )
570+ codes , uniques = ser .factorize ()
571+
572+ expected_codes = np .array ([0 , 1 , 2 , 3 ], dtype = np .intp )
573+ expected_uniques = Index ([True , False , 0 , 1 ], dtype = object )
574+ tm .assert_numpy_array_equal (codes , expected_codes )
575+ tm .assert_index_equal (uniques , expected_uniques , exact = True )
576+
577+ def test_factorize_bool_int_with_duplicates (self ):
578+ # GH#62888
579+ ser = Series ([0 , True , 0 , False , 1 , True ], dtype = object )
580+ codes , uniques = ser .factorize ()
581+
582+ expected_codes = np .array ([0 , 1 , 0 , 2 , 3 , 1 ], dtype = np .intp )
583+ expected_uniques = Index ([0 , True , False , 1 ], dtype = object )
584+ tm .assert_numpy_array_equal (codes , expected_codes )
585+ tm .assert_index_equal (uniques , expected_uniques , exact = True )
586+
551587
552588class TestUnique :
553589 def test_ints (self ):
@@ -582,6 +618,17 @@ def test_index_returned(self, index):
582618 expected = expected .normalize ()
583619 tm .assert_index_equal (result , expected , exact = True )
584620
621+ def test_unique_bool_int_distinguished (self ):
622+ # GH#18111, GH#62888
623+ arr = np .array ([0 , False , 1 , True ], dtype = object )
624+ result = algos .unique (arr )
625+ expected = np .array ([0 , False , 1 , True ], dtype = object )
626+ tm .assert_numpy_array_equal (result , expected )
627+ assert type (result [0 ]) is int
628+ assert type (result [1 ]) is bool
629+ assert type (result [2 ]) is int
630+ assert type (result [3 ]) is bool
631+
585632 def test_factorize_multiindex_empty (self ):
586633 # GH#57517
587634 mi = MultiIndex .from_product (
@@ -1207,6 +1254,22 @@ def test_isin_unsigned_dtype(self):
12071254 expected = Series (False )
12081255 tm .assert_series_equal (result , expected )
12091256
1257+ def test_isin_bool_int_distinguished (self ):
1258+ # GH#62888
1259+ ser = Series ([0 , 1 , False , True ], dtype = object )
1260+
1261+ result = algos .isin (ser , [0 ])
1262+ expected = np .array ([True , False , False , False ])
1263+ tm .assert_numpy_array_equal (result , expected )
1264+
1265+ result = algos .isin (ser , [False ])
1266+ expected = np .array ([False , False , True , False ])
1267+ tm .assert_numpy_array_equal (result , expected )
1268+
1269+ result = algos .isin (ser , [0 , False ])
1270+ expected = np .array ([True , False , True , False ])
1271+ tm .assert_numpy_array_equal (result , expected )
1272+
12101273
12111274class TestValueCounts :
12121275 def test_value_counts (self ):
@@ -1446,6 +1509,17 @@ def test_value_counts_series(self):
14461509 )
14471510 tm .assert_series_equal (result , expected )
14481511
1512+ def test_value_counts_bool_int_distinguished (self ):
1513+ # GH#62888
1514+ ser = Series ([0 , False , 0 , True , 1 , False ], dtype = object )
1515+ result = ser .value_counts ()
1516+ expected = Series (
1517+ [2 , 2 , 1 , 1 ],
1518+ index = Index ([0 , False , True , 1 ], dtype = object ),
1519+ name = "count" ,
1520+ )
1521+ tm .assert_series_equal (result , expected )
1522+
14491523 def test_value_counts_stability (self ):
14501524 # GH 63155
14511525 arr = np .random .default_rng (2 ).integers (0 , 32 , 64 )
@@ -1461,6 +1535,18 @@ def test_value_counts_stability(self):
14611535
14621536
14631537class TestDuplicated :
1538+ def test_duplicated_bool_int_distinguished (self ):
1539+ # GH#62888
1540+ keys = np .array ([0 , False , 1 , True , 0 , False ], dtype = object )
1541+
1542+ result = algos .duplicated (keys )
1543+ expected = np .array ([False , False , False , False , True , True ])
1544+ tm .assert_numpy_array_equal (result , expected )
1545+
1546+ result = algos .duplicated (keys , keep = "last" )
1547+ expected = np .array ([True , True , False , False , False , False ])
1548+ tm .assert_numpy_array_equal (result , expected )
1549+
14641550 def test_duplicated_with_nas (self ):
14651551 keys = np .array ([0 , 1 , np .nan , 0 , 2 , np .nan ], dtype = object )
14661552
0 commit comments