@@ -158,28 +158,14 @@ def __init__(self, values, placement, ndim: int):
158
158
Parameters
159
159
----------
160
160
values : np.ndarray or ExtensionArray
161
+ We assume maybe_coerce_values has already been called.
161
162
placement : BlockPlacement (or castable)
162
163
ndim : int
163
164
1 for SingleBlockManager/Series, 2 for BlockManager/DataFrame
164
165
"""
165
166
self .ndim = ndim
166
167
self .mgr_locs = placement
167
- self .values = self ._maybe_coerce_values (values )
168
-
169
- @classmethod
170
- def _maybe_coerce_values (cls , values ):
171
- """
172
- Ensure we have correctly-typed values.
173
-
174
- Parameters
175
- ----------
176
- values : np.ndarray or ExtensionArray
177
-
178
- Returns
179
- -------
180
- np.ndarray or ExtensionArray
181
- """
182
- return values
168
+ self .values = values
183
169
184
170
@property
185
171
def _holder (self ):
@@ -278,13 +264,17 @@ def make_block(self, values, placement=None) -> Block:
278
264
if self .is_extension :
279
265
values = ensure_block_shape (values , ndim = self .ndim )
280
266
267
+ # TODO: perf by not going through new_block
268
+ # We assume maybe_coerce_values has already been called
281
269
return new_block (values , placement = placement , ndim = self .ndim )
282
270
283
271
@final
284
272
def make_block_same_class (self , values , placement = None ) -> Block :
285
273
""" Wrap given values in a block of same type as self. """
286
274
if placement is None :
287
275
placement = self .mgr_locs
276
+ # TODO: perf by not going through new_block
277
+ # We assume maybe_coerce_values has already been called
288
278
return type (self )(values , placement = placement , ndim = self .ndim )
289
279
290
280
@final
@@ -416,6 +406,7 @@ def _split_op_result(self, result) -> List[Block]:
416
406
return nbs
417
407
418
408
if not isinstance (result , Block ):
409
+ result = maybe_coerce_values (result )
419
410
result = self .make_block (result )
420
411
421
412
return [result ]
@@ -619,6 +610,7 @@ def astype(self, dtype, copy: bool = False, errors: str = "raise"):
619
610
620
611
new_values = astype_array_safe (values , dtype , copy = copy , errors = errors )
621
612
613
+ new_values = maybe_coerce_values (new_values )
622
614
newb = self .make_block (new_values )
623
615
if newb .shape != self .shape :
624
616
raise TypeError (
@@ -677,6 +669,7 @@ def to_native_types(self, na_rep="nan", quoting=None, **kwargs):
677
669
values = np .array (values , dtype = "object" )
678
670
679
671
values [mask ] = na_rep
672
+ values = values .astype (object , copy = False )
680
673
return self .make_block (values )
681
674
682
675
# block actions #
@@ -1501,24 +1494,6 @@ def putmask(self, mask, new) -> List[Block]:
1501
1494
new_values [mask ] = new
1502
1495
return [self .make_block (values = new_values )]
1503
1496
1504
- @classmethod
1505
- def _maybe_coerce_values (cls , values ):
1506
- """
1507
- Unbox to an extension array.
1508
-
1509
- This will unbox an ExtensionArray stored in an Index or Series.
1510
- ExtensionArrays pass through. No dtype coercion is done.
1511
-
1512
- Parameters
1513
- ----------
1514
- values : np.ndarray or ExtensionArray
1515
-
1516
- Returns
1517
- -------
1518
- ExtensionArray
1519
- """
1520
- return extract_array (values )
1521
-
1522
1497
@property
1523
1498
def _holder (self ):
1524
1499
# For extension blocks, the holder is values-dependent.
@@ -1847,6 +1822,7 @@ def to_native_types(
1847
1822
values = np .array (values , dtype = "object" )
1848
1823
1849
1824
values [mask ] = na_rep
1825
+ values = values .astype (object , copy = False )
1850
1826
return self .make_block (values )
1851
1827
1852
1828
from pandas .io .formats .format import FloatArrayFormatter
@@ -1860,6 +1836,7 @@ def to_native_types(
1860
1836
fixed_width = False ,
1861
1837
)
1862
1838
res = formatter .get_result_as_array ()
1839
+ res = res .astype (object , copy = False )
1863
1840
return self .make_block (res )
1864
1841
1865
1842
@@ -1913,6 +1890,7 @@ def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]:
1913
1890
1914
1891
# TODO(EA2D): reshape not needed with 2D EAs
1915
1892
res_values = res_values .reshape (self .values .shape )
1893
+ res_values = maybe_coerce_values (res_values )
1916
1894
nb = self .make_block_same_class (res_values )
1917
1895
return [nb ]
1918
1896
@@ -1940,12 +1918,14 @@ def diff(self, n: int, axis: int = 0) -> List[Block]:
1940
1918
values = self .array_values ().reshape (self .shape )
1941
1919
1942
1920
new_values = values - values .shift (n , axis = axis )
1921
+ new_values = maybe_coerce_values (new_values )
1943
1922
return [self .make_block (new_values )]
1944
1923
1945
1924
def shift (self , periods : int , axis : int = 0 , fill_value : Any = None ) -> List [Block ]:
1946
1925
# TODO(EA2D) this is unnecessary if these blocks are backed by 2D EAs
1947
1926
values = self .array_values ().reshape (self .shape )
1948
1927
new_values = values .shift (periods , fill_value = fill_value , axis = axis )
1928
+ new_values = maybe_coerce_values (new_values )
1949
1929
return [self .make_block_same_class (new_values )]
1950
1930
1951
1931
def fillna (
@@ -1961,6 +1941,7 @@ def fillna(
1961
1941
values = self .array_values ()
1962
1942
values = values if inplace else values .copy ()
1963
1943
new_values = values .fillna (value = value , limit = limit )
1944
+ new_values = maybe_coerce_values (new_values )
1964
1945
return [self .make_block_same_class (values = new_values )]
1965
1946
1966
1947
@@ -1970,30 +1951,6 @@ class DatetimeLikeBlockMixin(NDArrayBackedExtensionBlock):
1970
1951
is_numeric = False
1971
1952
_can_hold_na = True
1972
1953
1973
- @classmethod
1974
- def _maybe_coerce_values (cls , values ):
1975
- """
1976
- Input validation for values passed to __init__. Ensure that
1977
- we have nanosecond datetime64/timedelta64, coercing if necessary.
1978
-
1979
- Parameters
1980
- ----------
1981
- values : np.ndarray or ExtensionArray
1982
- Must be convertible to datetime64/timedelta64
1983
-
1984
- Returns
1985
- -------
1986
- values : ndarray[datetime64ns/timedelta64ns]
1987
- """
1988
- values = extract_array (values , extract_numpy = True )
1989
- if isinstance (values , np .ndarray ):
1990
- values = sanitize_to_nanoseconds (values )
1991
- elif isinstance (values .dtype , np .dtype ):
1992
- # i.e. not datetime64tz
1993
- values = values ._data
1994
-
1995
- return values
1996
-
1997
1954
def array_values (self ):
1998
1955
return ensure_wrapped_if_datetimelike (self .values )
1999
1956
@@ -2010,6 +1967,7 @@ def to_native_types(self, na_rep="NaT", **kwargs):
2010
1967
arr = self .array_values ()
2011
1968
2012
1969
result = arr ._format_native_types (na_rep = na_rep , ** kwargs )
1970
+ result = result .astype (object , copy = False )
2013
1971
return self .make_block (result )
2014
1972
2015
1973
@@ -2067,12 +2025,6 @@ class ObjectBlock(Block):
2067
2025
is_object = True
2068
2026
_can_hold_na = True
2069
2027
2070
- @classmethod
2071
- def _maybe_coerce_values (cls , values ):
2072
- if issubclass (values .dtype .type , str ):
2073
- values = np .array (values , dtype = object )
2074
- return values
2075
-
2076
2028
@property
2077
2029
def is_bool (self ):
2078
2030
"""
@@ -2198,6 +2150,38 @@ def replace(
2198
2150
# Constructor Helpers
2199
2151
2200
2152
2153
+ def maybe_coerce_values (values ) -> ArrayLike :
2154
+ """
2155
+ Input validation for values passed to __init__. Ensure that
2156
+ any datetime64/timedelta64 dtypes are in nanoseconds. Ensure
2157
+ that we do not have string dtypes.
2158
+
2159
+ Parameters
2160
+ ----------
2161
+ values : np.ndarray or ExtensionArray
2162
+
2163
+ Returns
2164
+ -------
2165
+ values : np.ndarray or ExtensionArray
2166
+ """
2167
+
2168
+ # Note: the only test that needs extract_array here is one where we
2169
+ # pass PandasDtype to Series.astype, then need to extract PandasArray here.
2170
+ values = extract_array (values , extract_numpy = True )
2171
+
2172
+ if isinstance (values , np .ndarray ):
2173
+ values = sanitize_to_nanoseconds (values )
2174
+
2175
+ if issubclass (values .dtype .type , str ):
2176
+ values = np .array (values , dtype = object )
2177
+
2178
+ elif isinstance (values .dtype , np .dtype ):
2179
+ # i.e. not datetime64tz, extract DTA/TDA -> ndarray
2180
+ values = values ._data
2181
+
2182
+ return values
2183
+
2184
+
2201
2185
def get_block_type (values , dtype : Optional [Dtype ] = None ):
2202
2186
"""
2203
2187
Find the appropriate Block subclass to use for the given values and dtype.
@@ -2256,6 +2240,7 @@ def new_block(values, placement, *, ndim: int, klass=None) -> Block:
2256
2240
if klass is None :
2257
2241
klass = get_block_type (values , values .dtype )
2258
2242
2243
+ values = maybe_coerce_values (values )
2259
2244
return klass (values , ndim = ndim , placement = placement )
2260
2245
2261
2246
0 commit comments