Skip to content

Commit 02126dd

Browse files
committed
BUG: Fix implicit conversion to float64 with isin()
1 parent ea9c247 commit 02126dd

File tree

8 files changed

+90
-4
lines changed

8 files changed

+90
-4
lines changed

doc/source/reference/arrays.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ Data type introspection
667667
api.types.is_dtype_equal
668668
api.types.is_extension_array_dtype
669669
api.types.is_float_dtype
670+
api.types.is_implicit_conversion_to_float64
670671
api.types.is_int64_dtype
671672
api.types.is_integer_dtype
672673
api.types.is_interval_dtype

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ Other API changes
404404
- Index set operations (like union or intersection) will now ignore the dtype of
405405
an empty ``RangeIndex`` or empty ``Index`` with object dtype when determining
406406
the dtype of the resulting Index (:issue:`60797`)
407+
- Added :func:`pandas.api.types.is_implicit_conversion_to_float64` to check if there is a silent conversion to float64 between two dtypes(:issue:`61676`)
407408

408409
.. ---------------------------------------------------------------------------
409410
.. _whatsnew_300.deprecations:

pandas/core/algorithms.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,14 @@
4747
is_bool_dtype,
4848
is_complex_dtype,
4949
is_dict_like,
50-
is_dtype_equal,
5150
is_extension_array_dtype,
5251
is_float,
5352
is_float_dtype,
53+
is_implicit_conversion_to_float64,
5454
is_integer,
5555
is_integer_dtype,
5656
is_list_like,
5757
is_object_dtype,
58-
is_signed_integer_dtype,
5958
needs_i8_conversion,
6059
)
6160
from pandas.core.dtypes.concat import concat_compat
@@ -511,8 +510,7 @@ def isin(comps: ListLike, values: ListLike) -> npt.NDArray[np.bool_]:
511510
if (
512511
len(values) > 0
513512
and values.dtype.kind in "iufcb"
514-
and not is_signed_integer_dtype(comps)
515-
and not is_dtype_equal(values, comps)
513+
and is_implicit_conversion_to_float64(values, comps)
516514
):
517515
# GH#46485 Use object to avoid upcast to float64 later
518516
# TODO: Share with _find_common_type_compat

pandas/core/dtypes/api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
is_float,
1818
is_float_dtype,
1919
is_hashable,
20+
is_implicit_conversion_to_float64,
2021
is_int64_dtype,
2122
is_integer,
2223
is_integer_dtype,
@@ -59,6 +60,7 @@
5960
"is_float",
6061
"is_float_dtype",
6162
"is_hashable",
63+
"is_implicit_conversion_to_float64",
6264
"is_int64_dtype",
6365
"is_integer",
6466
"is_integer_dtype",

pandas/core/dtypes/common.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,80 @@ def is_dtype_equal(source, target) -> bool:
713713
return False
714714

715715

716+
def is_implicit_conversion_to_float64(source, target) -> bool:
717+
"""
718+
Check if there is an implicit conversion to float64 with both dtypes.
719+
720+
Parameters
721+
----------
722+
source : type or str
723+
The first dtype to compare.
724+
target : type or str
725+
The second dtype to compare.
726+
727+
Returns
728+
-------
729+
boolean
730+
Whether or not there is an implicit conversion to float64.
731+
732+
See AlsoMore actions
733+
--------
734+
api.types.is_categorical_dtype : Check whether the provided array or dtype
735+
is of the Categorical dtype.
736+
api.types.is_string_dtype : Check whether the provided array or dtype
737+
is of the string dtype.
738+
api.types.is_object_dtype : Check whether an array-like or dtype is of the
739+
object dtype.
740+
741+
Examples
742+
--------
743+
>>> from pandas.api.types import is_implicit_conversion_to_float64
744+
>>> is_implicit_conversion_to_float64(int, float)
745+
False
746+
>>> is_implicit_conversion_to_float64("int", int)
747+
False
748+
>>> import numpy as np
749+
>>> is_implicit_conversion_to_float64(int, np.int64)
750+
False
751+
>>> is_implicit_conversion_to_float64(np.uint64, np.int64)
752+
True
753+
>>> is_implicit_conversion_to_float64(np.uint64, np.float64)
754+
False
755+
>>> is_implicit_conversion_to_float64(np.uint64, np.uint64)
756+
False
757+
>>> is_implicit_conversion_to_float64(np.uint32, np.uint32)
758+
False
759+
>>> is_implicit_conversion_to_float64(np.uint32, np.int32)
760+
False
761+
>>> is_implicit_conversion_to_float64(np.int32, np.int32)
762+
False
763+
>>> is_implicit_conversion_to_float64(object, "category")
764+
False
765+
import pandas as pd
766+
>>> is_implicit_conversion_to_float64(np.int64, pd.UInt64Dtype())
767+
True
768+
>>> from pandas.core.dtypes.dtypes import CategoricalDtype
769+
>>> is_implicit_conversion_to_float64(CategoricalDtype(), "category")
770+
False
771+
"""
772+
try:
773+
src = _get_dtype(source)
774+
tar = _get_dtype(target)
775+
# check only valid dtypes related to implicit conversion to float64
776+
# other data types derived from 64-bit integers such as U/Int64Dtype
777+
# should also work
778+
if (
779+
src.kind in "iu" and src.itemsize == 8
780+
and tar.kind in "iu" and tar.itemsize == 8
781+
):
782+
return src != tar
783+
else:
784+
return False
785+
except (TypeError, AttributeError, ImportError):
786+
# invalid comparison
787+
return False
788+
789+
716790
def is_integer_dtype(arr_or_dtype) -> bool:
717791
"""
718792
Check whether the provided array or dtype is of an integer dtype.
@@ -1934,6 +2008,7 @@ def is_all_strings(value: ArrayLike) -> bool:
19342008
"is_extension_array_dtype",
19352009
"is_file_like",
19362010
"is_float_dtype",
2011+
"is_implicit_conversion_to_float64",
19372012
"is_int64_dtype",
19382013
"is_integer_dtype",
19392014
"is_interval_dtype",

pandas/tests/api/test_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ class TestApi(Base):
295295
"is_float",
296296
"is_float_dtype",
297297
"is_hashable",
298+
"is_implicit_conversion_to_float64",
298299
"is_int64_dtype",
299300
"is_integer",
300301
"is_integer_dtype",

pandas/tests/api/test_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class TestTypes(Base):
2020
"is_dtype_equal",
2121
"is_float",
2222
"is_float_dtype",
23+
"is_implicit_conversion_to_float64",
2324
"is_int64_dtype",
2425
"is_integer",
2526
"is_integer_dtype",

pandas/tests/test_algos.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,13 @@ def test_isin_unsigned_dtype(self):
11971197
expected = Series(False)
11981198
tm.assert_series_equal(result, expected)
11991199

1200+
def test_isin_unsigned_dtype_other_side(self):
1201+
# GH#46485
1202+
ser = Series([1378774140726870442], dtype=np.int64)
1203+
result = ser.isin([np.uint64(1378774140726870528)])
1204+
expected = Series(False)
1205+
tm.assert_series_equal(result, expected)
1206+
12001207

12011208
class TestValueCounts:
12021209
def test_value_counts(self):

0 commit comments

Comments
 (0)