-
-
Notifications
You must be signed in to change notification settings - Fork 18.7k
REF: back DatetimeBlock, TimedeltaBlock by DTA/TDA #40456
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
fae2a97
c5e3dd9
7a4d545
c776390
62b5806
c3ce503
3b0e819
868f436
14b4087
f076370
d7f2718
626c2a9
3fbc3a5
f148306
4b820d2
6f4e5e6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9517,6 +9517,9 @@ def func(values: np.ndarray): | |
|
||
def blk_func(values, axis=1): | ||
if isinstance(values, ExtensionArray): | ||
if values.ndim == 2: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't this just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure. next pass |
||
# i.e. DatetimeArray, TimedeltaArray | ||
return values._reduce(name, axis=1, skipna=skipna, **kwds) | ||
return values._reduce(name, skipna=skipna, **kwds) | ||
else: | ||
return op(values, axis=axis, skipna=skipna, **kwds) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,7 +26,6 @@ | |
writers, | ||
) | ||
from pandas._libs.internals import BlockPlacement | ||
from pandas._libs.tslibs import conversion | ||
from pandas._typing import ( | ||
ArrayLike, | ||
Dtype, | ||
|
@@ -45,7 +44,6 @@ | |
maybe_downcast_numeric, | ||
maybe_downcast_to_dtype, | ||
maybe_upcast, | ||
sanitize_to_nanoseconds, | ||
soft_convert_objects, | ||
) | ||
from pandas.core.dtypes.common import ( | ||
|
@@ -919,7 +917,11 @@ def setitem(self, indexer, value): | |
return self.coerce_to_target_dtype(value).setitem(indexer, value) | ||
|
||
if self.dtype.kind in ["m", "M"]: | ||
arr = self.array_values().T | ||
arr = self.values | ||
if self.ndim > 1: | ||
# Dont transpose with ndim=1 bc we would fail to invalidate | ||
# arr.freq | ||
arr = arr.T | ||
arr[indexer] = value | ||
return self | ||
|
||
|
@@ -1153,6 +1155,7 @@ def _interpolate_with_fill( | |
limit_area=limit_area, | ||
) | ||
|
||
values = maybe_coerce_values(values) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. prob can have maybe_coerce_values do downcasting (maybe) |
||
blocks = [self.make_block_same_class(values)] | ||
return self._maybe_downcast(blocks, downcast) | ||
|
||
|
@@ -1208,6 +1211,7 @@ def func(yvalues: np.ndarray) -> np.ndarray: | |
|
||
# interp each column independently | ||
interp_values = np.apply_along_axis(func, axis, data) | ||
interp_values = maybe_coerce_values(interp_values) | ||
|
||
blocks = [self.make_block_same_class(interp_values)] | ||
return self._maybe_downcast(blocks, downcast) | ||
|
@@ -1790,15 +1794,23 @@ class NDArrayBackedExtensionBlock(HybridMixin, Block): | |
Block backed by an NDArrayBackedExtensionArray | ||
""" | ||
|
||
values: NDArrayBackedExtensionArray | ||
|
||
@property | ||
def is_view(self) -> bool: | ||
""" return a boolean if I am possibly a view """ | ||
# check the ndarray values of the DatetimeIndex values | ||
return self.values._ndarray.base is not None | ||
|
||
def internal_values(self): | ||
# Override to return DatetimeArray and TimedeltaArray | ||
return self.array_values() | ||
return self.values | ||
|
||
def get_values(self, dtype: Optional[DtypeObj] = None) -> np.ndarray: | ||
""" | ||
return object dtype as boxed values, such as Timestamps/Timedelta | ||
""" | ||
values = self.array_values() | ||
values = self.values | ||
if is_object_dtype(dtype): | ||
# DTA/TDA constructor and astype can handle 2D | ||
values = values.astype(object) | ||
|
@@ -1808,7 +1820,7 @@ def get_values(self, dtype: Optional[DtypeObj] = None) -> np.ndarray: | |
def iget(self, key): | ||
# GH#31649 we need to wrap scalars in Timestamp/Timedelta | ||
# TODO(EA2D): this can be removed if we ever have 2D EA | ||
return self.array_values().reshape(self.shape)[key] | ||
return self.values.reshape(self.shape)[key] | ||
|
||
def putmask(self, mask, new) -> List[Block]: | ||
mask = extract_bool_array(mask) | ||
|
@@ -1817,14 +1829,13 @@ def putmask(self, mask, new) -> List[Block]: | |
return self.astype(object).putmask(mask, new) | ||
|
||
# TODO(EA2D): reshape unnecessary with 2D EAs | ||
arr = self.array_values().reshape(self.shape) | ||
arr = cast("NDArrayBackedExtensionArray", arr) | ||
arr = self.values.reshape(self.shape) | ||
arr.T.putmask(mask, new) | ||
return [self] | ||
|
||
def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]: | ||
# TODO(EA2D): reshape unnecessary with 2D EAs | ||
arr = self.array_values().reshape(self.shape) | ||
arr = self.values.reshape(self.shape) | ||
|
||
cond = extract_bool_array(cond) | ||
|
||
|
@@ -1835,7 +1846,6 @@ def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]: | |
|
||
# TODO(EA2D): reshape not needed with 2D EAs | ||
res_values = res_values.reshape(self.values.shape) | ||
res_values = maybe_coerce_values(res_values) | ||
nb = self.make_block_same_class(res_values) | ||
return [nb] | ||
|
||
|
@@ -1860,17 +1870,15 @@ def diff(self, n: int, axis: int = 0) -> List[Block]: | |
by apply. | ||
""" | ||
# TODO(EA2D): reshape not necessary with 2D EAs | ||
values = self.array_values().reshape(self.shape) | ||
values = self.values.reshape(self.shape) | ||
|
||
new_values = values - values.shift(n, axis=axis) | ||
new_values = maybe_coerce_values(new_values) | ||
return [self.make_block(new_values)] | ||
|
||
def shift(self, periods: int, axis: int = 0, fill_value: Any = None) -> List[Block]: | ||
# TODO(EA2D) this is unnecessary if these blocks are backed by 2D EAs | ||
values = self.array_values().reshape(self.shape) | ||
values = self.values.reshape(self.shape) | ||
new_values = values.shift(periods, fill_value=fill_value, axis=axis) | ||
new_values = maybe_coerce_values(new_values) | ||
return [self.make_block_same_class(new_values)] | ||
|
||
def fillna( | ||
|
@@ -1883,38 +1891,36 @@ def fillna( | |
# TODO: don't special-case td64 | ||
return self.astype(object).fillna(value, limit, inplace, downcast) | ||
|
||
values = self.array_values() | ||
values = self.values | ||
values = values if inplace else values.copy() | ||
new_values = values.fillna(value=value, limit=limit) | ||
new_values = maybe_coerce_values(new_values) | ||
return [self.make_block_same_class(values=new_values)] | ||
|
||
|
||
class DatetimeLikeBlockMixin(NDArrayBackedExtensionBlock): | ||
"""Mixin class for DatetimeBlock, DatetimeTZBlock, and TimedeltaBlock.""" | ||
|
||
values: Union[DatetimeArray, TimedeltaArray] | ||
|
||
is_numeric = False | ||
_can_hold_na = True | ||
|
||
def array_values(self): | ||
return ensure_wrapped_if_datetimelike(self.values) | ||
return self.values | ||
|
||
def external_values(self): | ||
# NB: for dt64tz this is different from np.asarray(self.values), | ||
# since that return an object-dtype ndarray of Timestamps. | ||
return self.values._ndarray | ||
|
||
@property | ||
def _holder(self): | ||
return type(self.array_values()) | ||
return type(self.values) | ||
|
||
|
||
class DatetimeBlock(DatetimeLikeBlockMixin): | ||
__slots__ = () | ||
|
||
def set_inplace(self, locs, values): | ||
""" | ||
See Block.set.__doc__ | ||
""" | ||
values = conversion.ensure_datetime64ns(values, copy=False) | ||
|
||
self.values[locs] = values | ||
|
||
|
||
class DatetimeTZBlock(ExtensionBlock, DatetimeBlock): | ||
""" implement a datetime64 block with a tz attribute """ | ||
|
@@ -1932,20 +1938,14 @@ class DatetimeTZBlock(ExtensionBlock, DatetimeBlock): | |
where = DatetimeBlock.where | ||
putmask = DatetimeLikeBlockMixin.putmask | ||
fillna = DatetimeLikeBlockMixin.fillna | ||
external_values = DatetimeLikeBlockMixin.external_values | ||
|
||
array_values = ExtensionBlock.array_values | ||
# error: Incompatible types in assignment (expression has type | ||
# "Callable[[NDArrayBackedExtensionBlock], bool]", base class "ExtensionBlock" | ||
# defined the type as "bool") [assignment] | ||
is_view = NDArrayBackedExtensionBlock.is_view # type: ignore[assignment] | ||
|
||
@property | ||
def is_view(self) -> bool: | ||
""" return a boolean if I am possibly a view """ | ||
# check the ndarray values of the DatetimeIndex values | ||
return self.values._data.base is not None | ||
|
||
def external_values(self): | ||
# NB: this is different from np.asarray(self.values), since that | ||
# return an object-dtype ndarray of Timestamps. | ||
# Avoid FutureWarning in .astype in casting from dt64tz to dt64 | ||
return self.values._data | ||
array_values = ExtensionBlock.array_values | ||
|
||
|
||
class TimeDeltaBlock(DatetimeLikeBlockMixin): | ||
|
@@ -2079,15 +2079,11 @@ def maybe_coerce_values(values) -> ArrayLike: | |
values = extract_array(values, extract_numpy=True) | ||
|
||
if isinstance(values, np.ndarray): | ||
values = sanitize_to_nanoseconds(values) | ||
values = ensure_wrapped_if_datetimelike(values) | ||
|
||
if issubclass(values.dtype.type, str): | ||
values = np.array(values, dtype=object) | ||
|
||
elif isinstance(values.dtype, np.dtype): | ||
# i.e. not datetime64tz, extract DTA/TDA -> ndarray | ||
values = values._data | ||
|
||
return values | ||
|
||
|
||
|
Uh oh!
There was an error while loading. Please reload this page.