Skip to content

Commit 9524383

Browse files
authored
REF: implement dtypes.cast.can_hold_element (#39094)
1 parent c4f15e4 commit 9524383

File tree

2 files changed

+47
-40
lines changed

2 files changed

+47
-40
lines changed

pandas/core/dtypes/cast.py

+43
Original file line numberDiff line numberDiff line change
@@ -1880,3 +1880,46 @@ def validate_numeric_casting(dtype: np.dtype, value: Scalar) -> None:
18801880
raise ValueError(
18811881
f"Cannot assign {type(value).__name__} to float/integer series"
18821882
)
1883+
1884+
1885+
def can_hold_element(dtype: np.dtype, element: Any) -> bool:
1886+
"""
1887+
Can we do an inplace setitem with this element in an array with this dtype?
1888+
1889+
Parameters
1890+
----------
1891+
dtype : np.dtype
1892+
element : Any
1893+
1894+
Returns
1895+
-------
1896+
bool
1897+
"""
1898+
tipo = maybe_infer_dtype_type(element)
1899+
1900+
if dtype.kind in ["i", "u"]:
1901+
if tipo is not None:
1902+
return tipo.kind in ["i", "u"] and dtype.itemsize >= tipo.itemsize
1903+
1904+
# We have not inferred an integer from the dtype
1905+
# check if we have a builtin int or a float equal to an int
1906+
return is_integer(element) or (is_float(element) and element.is_integer())
1907+
1908+
elif dtype.kind == "f":
1909+
if tipo is not None:
1910+
return tipo.kind in ["f", "i", "u"]
1911+
return lib.is_integer(element) or lib.is_float(element)
1912+
1913+
elif dtype.kind == "c":
1914+
if tipo is not None:
1915+
return tipo.kind in ["c", "f", "i", "u"]
1916+
return (
1917+
lib.is_integer(element) or lib.is_complex(element) or lib.is_float(element)
1918+
)
1919+
1920+
elif dtype.kind == "b":
1921+
if tipo is not None:
1922+
return tipo.kind == "b"
1923+
return lib.is_bool(element)
1924+
1925+
raise NotImplementedError(dtype)

pandas/core/internals/blocks.py

+4-40
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from pandas.core.dtypes.cast import (
2222
astype_dt64_to_dt64tz,
2323
astype_nansafe,
24+
can_hold_element,
2425
convert_scalar_for_putitemlike,
2526
find_common_type,
2627
infer_dtype_from,
@@ -40,7 +41,6 @@
4041
is_datetime64tz_dtype,
4142
is_dtype_equal,
4243
is_extension_array_dtype,
43-
is_float,
4444
is_integer,
4545
is_list_like,
4646
is_object_dtype,
@@ -1906,24 +1906,14 @@ class NumericBlock(Block):
19061906
is_numeric = True
19071907
_can_hold_na = True
19081908

1909+
def _can_hold_element(self, element: Any) -> bool:
1910+
return can_hold_element(self.dtype, element)
1911+
19091912

19101913
class FloatBlock(NumericBlock):
19111914
__slots__ = ()
19121915
is_float = True
19131916

1914-
def _can_hold_element(self, element: Any) -> bool:
1915-
tipo = maybe_infer_dtype_type(element)
1916-
if tipo is not None:
1917-
return issubclass(tipo.type, (np.floating, np.integer)) and not issubclass(
1918-
tipo.type, np.timedelta64
1919-
)
1920-
return isinstance(
1921-
element, (float, int, np.floating, np.int_)
1922-
) and not isinstance(
1923-
element,
1924-
(bool, np.bool_, np.timedelta64),
1925-
)
1926-
19271917
def to_native_types(
19281918
self, na_rep="", float_format=None, decimal=".", quoting=None, **kwargs
19291919
):
@@ -1962,32 +1952,12 @@ class ComplexBlock(NumericBlock):
19621952
__slots__ = ()
19631953
is_complex = True
19641954

1965-
def _can_hold_element(self, element: Any) -> bool:
1966-
tipo = maybe_infer_dtype_type(element)
1967-
if tipo is not None:
1968-
return tipo.kind in ["c", "f", "i", "u"]
1969-
return (
1970-
lib.is_integer(element) or lib.is_complex(element) or lib.is_float(element)
1971-
)
1972-
19731955

19741956
class IntBlock(NumericBlock):
19751957
__slots__ = ()
19761958
is_integer = True
19771959
_can_hold_na = False
19781960

1979-
def _can_hold_element(self, element: Any) -> bool:
1980-
tipo = maybe_infer_dtype_type(element)
1981-
if tipo is not None:
1982-
return (
1983-
issubclass(tipo.type, np.integer)
1984-
and not issubclass(tipo.type, np.timedelta64)
1985-
and self.dtype.itemsize >= tipo.itemsize
1986-
)
1987-
# We have not inferred an integer from the dtype
1988-
# check if we have a builtin int or a float equal to an int
1989-
return is_integer(element) or (is_float(element) and element.is_integer())
1990-
19911961

19921962
class DatetimeLikeBlockMixin(Block):
19931963
"""Mixin class for DatetimeBlock, DatetimeTZBlock, and TimedeltaBlock."""
@@ -2284,12 +2254,6 @@ class BoolBlock(NumericBlock):
22842254
is_bool = True
22852255
_can_hold_na = False
22862256

2287-
def _can_hold_element(self, element: Any) -> bool:
2288-
tipo = maybe_infer_dtype_type(element)
2289-
if tipo is not None:
2290-
return issubclass(tipo.type, np.bool_)
2291-
return isinstance(element, (bool, np.bool_))
2292-
22932257

22942258
class ObjectBlock(Block):
22952259
__slots__ = ()

0 commit comments

Comments
 (0)