8000 REF: implement _wrap_reduction_result by jbrockmendel · Pull Request #37660 · pandas-dev/pandas · GitHub
[go: up one dir, main page]

Skip to content

REF: implement _wrap_reduction_result #37660

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

Merged
merged 18 commits into from
Nov 8, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
checkpoint tests passing
  • Loading branch information
jbrockmendel committed Oct 27, 2020
commit 434692ac4e51322f179d1d9398c835a71e23cc13
4 changes: 2 additions & 2 deletions pandas/compat/numpy/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,20 +387,20 @@ def validate_resampler_func(method: str, args, kwargs) -> None:
raise TypeError("too many arguments passed in")


def validate_minmax_axis(axis: Optional[int]) -> None:
def validate_minmax_axis(axis: Optional[int], ndim: int = 1) -> None:
"""
Ensure that the axis argument passed to min, max, argmin, or argmax is zero
or None, as otherwise it will be incorrectly ignored.

Parameters
----------
axis : int or None
ndim : int, default 1

Raises
------
ValueError
"""
ndim = 1 # hard-coded for Index
if axis is None:
return
if axis >= ndim or (axis < 0 and ndim + axis < 0):
Expand Down
50 changes: 28 additions & 22 deletions pandas/core/arrays/datetimelike.py
10000
Original file line number Diff line number Diff line change
Expand Up @@ -1264,13 +1264,21 @@ def min(self, axis=None, skipna=True, *args, **kwargs):
Series.min : Return the minimum value in a Series.
"""
nv.validate_min(args, kwargs)
nv.validate_minmax_axis(axis)
nv.validate_minmax_axis(axis, self.ndim)

result = nanops.nanmin(self.asi8, skipna=skipna, mask=self.isna())
if isna(result):
# Period._from_ordinal does not handle np.nan gracefully
return NaT
return self._box_func(result)
# View as M8[ns] to get correct NaT/masking semantics for PeriodDtype
result = nanops.nanmin(
self._ndarray.view("M8[ns]"), skipna=skipna, mask=self.isna()
)
if lib.is_scalar(result):
if isna(result):
# Period._from_ordinal does not handle NaT gracefully
return NaT
# nanops may unwantedly cast to Timestamp
result = getattr(result, "value", result)
return self._box_func(result)
result = result.astype("i8", copy=False)
return self._from_backing_data(result)

def max(self, axis=None, skipna=True, *args, **kwargs):
"""
Expand All @@ -1286,23 +1294,21 @@ def max(self, axis=None, skipna=True, *args, **kwargs):
# TODO: skipna is broken with max.
# See https://github.com/pandas-dev/pandas/issues/24265
nv.validate_max(args, kwargs)
nv.validate_minmax_axis(axis)
nv.validate_minmax_axis(axis, self.ndim)

mask = self.isna()
if skipna:
values = self[~mask].asi8
elif mask.any():
return NaT
else:
values = self.asi8

if not len(values):
# short-circuit for empty max / min
return NaT

result = nanops.nanmax(values, skipna=skipna)
# Don't have to worry about NA `result`, since no NA went in.
return self._box_func(result)
# View as M8[ns] to get correct NaT/masking semantics for PeriodDtype
result = nanops.nanmax(
self._ndarray.view("M8[ns]"), skipna=skipna, mask=self.isna()
)
if lib.is_scalar(result):
if isna(result):
# Period._from_ordinal does not handle NaT gracefully
return NaT
# nanops may unwantedly cast to Timestamp
result = getattr(result, "value", result)
return self._box_func(result)
result = result.astype("i8", copy=False)
return self._from_backing_data(result)

def mean(self, skipna=True):
"""
Expand Down
17 changes: 16 additions & 1 deletion pandas/core/nanops.py
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,15 @@ def reduction(
mask: Optional[np.ndarray] = None,
) -> Dtype:

orig_values = values
values, mask, dtype, dtype_max, fill_value = _get_values(
values, skipna, fill_value_typ=fill_value_typ, mask=mask
)

datetimelike = orig_values.dtype.kind in ["m", "M"]
if datetimelike and mask is None:
mask = isna(orig_values)

if (axis is not None and values.shape[axis] == 0) or values.size == 0:
try:
result = getattr(values, meth)(axis, dtype=dtype_max)
Expand All @@ -941,7 +946,17 @@ def reduction(
result = getattr(values, meth)(axis)

result = _wrap_results(result, dtype, fill_value)
return _maybe_null_out(result, axis, mask, values.shape)
result = _maybe_null_out(result, axis, mask, values.shape)

if datetimelike and not skipna:
if axis is None or values.ndim == 1:
if mask.any():
return orig_values.dtype.type("NaT")
else:
axis_mask = mask.any(axis=axis)
result[axis_mask] = orig_values.dtype.type("NaT")

return result

return reduction

Expand Down
25 changes: 25 additions & 0 deletions pandas/tests/frame/test_analytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,31 @@ def test_min_max_dt64_with_NaT(self):
exp = Series([pd.NaT], index=["foo"])
tm.assert_series_equal(res, exp)

def test_min_max_dt64_with_NaT_skipna_false(self, tz_naive_fixture):
# GH#36907
tz = tz_naive_fixture
df = pd.DataFrame(
{
"a": [
pd.Timestamp("2020-01-01 08:00:00", tz=tz),
pd.Timestamp("1920-02-01 09:00:00", tz=tz),
],
"b": [pd.Timestamp("2020-02-01 08:00:00", tz=tz), pd.NaT],
}
)

res = df.min(axis=1, skipna=False)
expected = pd.Series([df.loc[0, "a"], pd.NaT])
assert expected.dtype == df["a"].dtype

tm.assert_series_equal(res, expected)

res = df.max(axis=1, skipna=False)
expected = pd.Series([df.loc[0, "b"], pd.NaT])
assert expected.dtype == df["a"].dtype

tm.assert_series_equal(res, expected)

def test_min_max_dt64_api_consistency_with_NaT(self):
# Calling the following sum functions returned an error for dataframes but
# returned NaT for series. These tests check that the API is consistent in
Expand Down
0