Skip to content

Commit 06b6ca1

Browse files
authored
Merge branch 'main' into free-threaded-wheels
2 parents 2397f4c + 8f737b3 commit 06b6ca1

File tree

17 files changed

+135
-80
lines changed

17 files changed

+135
-80
lines changed

doc/source/user_guide/groupby.rst

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,8 +668,9 @@ column, which produces an aggregated result with a hierarchical column index:
668668
grouped[["C", "D"]].agg(["sum", "mean", "std"])
669669
670670
671-
The resulting aggregations are named after the functions themselves. If you
672-
need to rename, then you can add in a chained operation for a ``Series`` like this:
671+
The resulting aggregations are named after the functions themselves.
672+
673+
For a ``Series``, if you need to rename, you can add in a chained operation like this:
673674

674675
.. ipython:: python
675676
@@ -679,8 +680,19 @@ need to rename, then you can add in a chained operation for a ``Series`` like th
679680
.rename(columns={"sum": "foo", "mean": "bar", "std": "baz"})
680681
)
681682
683+
Or, you can simply pass a list of tuples each with the name of the new column and the aggregate function:
684+
685+
.. ipython:: python
686+
687+
(
688+
grouped["C"]
689+
.agg([("foo", "sum"), ("bar", "mean"), ("baz", "std")])
690+
)
691+
682692
For a grouped ``DataFrame``, you can rename in a similar manner:
683693

694+
By chaining ``rename`` operation,
695+
684696
.. ipython:: python
685697
686698
(
@@ -689,6 +701,16 @@ For a grouped ``DataFrame``, you can rename in a similar manner:
689701
)
690702
)
691703
704+
Or, passing a list of tuples,
705+
706+
.. ipython:: python
707+
708+
(
709+
grouped[["C", "D"]].agg(
710+
[("foo", "sum"), ("bar", "mean"), ("baz", "std")]
711+
)
712+
)
713+
692714
.. note::
693715

694716
In general, the output column names should be unique, but pandas will allow

doc/source/user_guide/text.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ and replacing any remaining whitespaces with underscores:
204204

205205
.. warning::
206206

207-
The type of the Series is inferred and the allowed types (i.e. strings).
207+
The type of the Series is inferred and is one among the allowed types (i.e. strings).
208208

209209
Generally speaking, the ``.str`` accessor is intended to work only on strings. With very few
210210
exceptions, other uses are not supported, and may be disabled at a later point.

doc/source/whatsnew/v3.0.0.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ Datetimelike
507507
- Bug in :meth:`Dataframe.agg` with df with missing values resulting in IndexError (:issue:`58810`)
508508
- Bug in :meth:`DatetimeIndex.is_year_start` and :meth:`DatetimeIndex.is_quarter_start` does not raise on Custom business days frequencies bigger then "1C" (:issue:`58664`)
509509
- Bug in :meth:`DatetimeIndex.is_year_start` and :meth:`DatetimeIndex.is_quarter_start` returning ``False`` on double-digit frequencies (:issue:`58523`)
510-
- Bug in :meth:`DatetimeIndex.union` when ``unit`` was non-nanosecond (:issue:`59036`)
510+
- Bug in :meth:`DatetimeIndex.union` and :meth:`DatetimeIndex.intersection` when ``unit`` was non-nanosecond (:issue:`59036`)
511511
- Bug in :meth:`Series.dt.microsecond` producing incorrect results for pyarrow backed :class:`Series`. (:issue:`59154`)
512512
- Bug in :meth:`to_datetime` not respecting dayfirst if an uncommon date string was passed. (:issue:`58859`)
513513
- Bug in setting scalar values with mismatched resolution into arrays with non-nanosecond ``datetime64``, ``timedelta64`` or :class:`DatetimeTZDtype` incorrectly truncating those scalars (:issue:`56410`)

pandas/_libs/tslibs/dtypes.pyx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ cdef dict c_PERIOD_AND_OFFSET_DEPR_FREQSTR = {
359359
"b": "B",
360360
"c": "C",
361361
"MIN": "min",
362+
"US": "us",
363+
"NS": "ns",
362364
}
363365

364366
cdef str INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"

pandas/_libs/tslibs/offsets.pyx

Lines changed: 40 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4697,13 +4697,9 @@ _lite_rule_alias = {
46974697
"BYS": "BYS-JAN", # BYearBegin(month=1),
46984698

46994699
"Min": "min",
4700-
"min": "min",
4701-
"ms": "ms",
4702-
"us": "us",
4703-
"ns": "ns",
47044700
}
47054701

4706-
_dont_uppercase = {"h", "bh", "cbh", "MS", "ms", "s"}
4702+
_dont_uppercase = {"min", "h", "bh", "cbh", "s", "ms", "us", "ns"}
47074703

47084704

47094705
INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"
@@ -4713,6 +4709,37 @@ INVALID_FREQ_ERR_MSG = "Invalid frequency: {0}"
47134709
_offset_map = {}
47144710

47154711

4712+
def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str:
4713+
if name in _lite_rule_alias:
4714+
return name
4715+
if name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR:
4716+
warnings.warn(
4717+
f"\'{name}\' is deprecated and will be removed "
4718+
f"in a future version, please use "
4719+
f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' "
4720+
f" instead.",
4721+
FutureWarning,
4722+
stacklevel=find_stack_level(),
4723+
)
4724+
return c_PERIOD_AND_OFFSET_DEPR_FREQSTR[name]
4725+
4726+
for _name in (name.lower(), name.upper()):
4727+
if name == _name:
4728+
continue
4729+
if _name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR.values():
4730+
warnings.warn(
4731+
f"\'{name}\' is deprecated and will be removed "
4732+
f"in a future version, please use "
4733+
f"\'{_name}\' "
4734+
f" instead.",
4735+
FutureWarning,
4736+
stacklevel=find_stack_level(),
4737+
)
4738+
return _name
4739+
4740+
return name
4741+
4742+
47164743
def _validate_to_offset_alias(alias: str, is_period: bool) -> None:
47174744
if not is_period:
47184745
if alias.upper() in c_OFFSET_RENAMED_FREQSTR:
@@ -4750,35 +4777,6 @@ def _get_offset(name: str) -> BaseOffset:
47504777
--------
47514778
_get_offset('EOM') --> BMonthEnd(1)
47524779
"""
4753-
if (
4754-
name not in _lite_rule_alias
4755-
and (name.upper() in _lite_rule_alias)
4756-
and name != "ms"
4757-
):
4758-
warnings.warn(
4759-
f"\'{name}\' is deprecated and will be removed "
4760-
f"in a future version, please use \'{name.upper()}\' instead.",
4761-
FutureWarning,
4762-
stacklevel=find_stack_level(),
4763-
)
4764-
elif (
4765-
name not in _lite_rule_alias
4766-
and (name.lower() in _lite_rule_alias)
4767-
and name != "MS"
4768-
):
4769-
warnings.warn(
4770-
f"\'{name}\' is deprecated and will be removed "
4771-
f"in a future version, please use \'{name.lower()}\' instead.",
4772-
FutureWarning,
4773-
stacklevel=find_stack_level(),
4774-
)
4775-
if name not in _dont_uppercase:
4776-
name = name.upper()
4777-
name = _lite_rule_alias.get(name, name)
4778-
name = _lite_rule_alias.get(name.lower(), name)
4779-
else:
4780-
name = _lite_rule_alias.get(name, name)
4781-
47824780
if name not in _offset_map:
47834781
try:
47844782
split = name.split("-")
@@ -4880,6 +4878,7 @@ cpdef to_offset(freq, bint is_period=False):
48804878

48814879
tups = zip(split[0::4], split[1::4], split[2::4])
48824880
for n, (sep, stride, name) in enumerate(tups):
4881+
name = _warn_about_deprecated_aliases(name, is_period)
48834882
_validate_to_offset_alias(name, is_period)
48844883
if is_period:
48854884
if name.upper() in c_PERIOD_TO_OFFSET_FREQSTR:
@@ -4888,31 +4887,21 @@ cpdef to_offset(freq, bint is_period=False):
48884887
f"\'{name}\' is no longer supported, "
48894888
f"please use \'{name.upper()}\' instead.",
48904889
)
4891-
name = c_PERIOD_TO_OFFSET_FREQSTR.get(name.upper())
4892-
4893-
if name in c_PERIOD_AND_OFFSET_DEPR_FREQSTR:
4894-
warnings.warn(
4895-
f"\'{name}\' is deprecated and will be removed "
4896-
f"in a future version, please use "
4897-
f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' "
4898-
f"instead.",
4899-
FutureWarning,
4900-
stacklevel=find_stack_level(),
4901-
)
4902-
name = c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)
4890+
name = c_PERIOD_TO_OFFSET_FREQSTR[name.upper()]
4891+
name = _lite_rule_alias.get(name, name)
4892+
49034893
if sep != "" and not sep.isspace():
49044894
raise ValueError("separator must be spaces")
4905-
prefix = _lite_rule_alias.get(name) or name
49064895
if stride_sign is None:
49074896
stride_sign = -1 if stride.startswith("-") else 1
49084897
if not stride:
49094898
stride = 1
49104899

4911-
if prefix in {"D", "h", "min", "s", "ms", "us", "ns"}:
4900+
if name in {"D", "h", "min", "s", "ms", "us", "ns"}:
49124901
# For these prefixes, we have something like "3h" or
49134902
# "2.5min", so we can construct a Timedelta with the
49144903
# matching unit and get our offset from delta_to_tick
4915-
td = Timedelta(1, unit=prefix)
4904+
td = Timedelta(1, unit=name)
49164905
off = delta_to_tick(td)
49174906
offset = off * float(stride)
49184907
if n != 0:
@@ -4921,7 +4910,7 @@ cpdef to_offset(freq, bint is_period=False):
49214910
offset *= stride_sign
49224911
else:
49234912
stride = int(stride)
4924-
offset = _get_offset(prefix)
4913+
offset = _get_offset(name)
49254914
offset = offset * int(np.fabs(stride) * stride_sign)
49264915

49274916
if result is None:
@@ -4931,7 +4920,7 @@ cpdef to_offset(freq, bint is_period=False):
49314920
except (ValueError, TypeError) as err:
49324921
raise ValueError(INVALID_FREQ_ERR_MSG.format(
49334922
f"{freq}, failed to parse with error message: {repr(err)}")
4934-
)
4923+
) from err
49354924
else:
49364925
result = None
49374926

pandas/_typing.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,3 +526,5 @@ def closed(self) -> bool:
526526

527527
# maintaine the sub-type of any hashable sequence
528528
SequenceT = TypeVar("SequenceT", bound=Sequence[Hashable])
529+
530+
SliceType = Optional[Hashable]

pandas/core/generic.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,14 +1491,12 @@ def __invert__(self) -> Self:
14911491
return res.__finalize__(self, method="__invert__")
14921492

14931493
@final
1494-
def __nonzero__(self) -> NoReturn:
1494+
def __bool__(self) -> NoReturn:
14951495
raise ValueError(
14961496
f"The truth value of a {type(self).__name__} is ambiguous. "
14971497
"Use a.empty, a.bool(), a.item(), a.any() or a.all()."
14981498
)
14991499

1500-
__bool__ = __nonzero__
1501-
15021500
@final
15031501
def abs(self) -> Self:
15041502
"""

pandas/core/indexes/base.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@
4545
ArrayLike,
4646
Axes,
4747
Axis,
48+
AxisInt,
4849
DropKeep,
50+
Dtype,
4951
DtypeObj,
5052
F,
5153
IgnoreRaise,
@@ -57,6 +59,7 @@
5759
ReindexMethod,
5860
Self,
5961
Shape,
62+
SliceType,
6063
npt,
6164
)
6265
from pandas.compat.numpy import function as nv
@@ -1087,7 +1090,7 @@ def view(self, cls=None):
10871090
result._id = self._id
10881091
return result
10891092

1090-
def astype(self, dtype, copy: bool = True):
1093+
def astype(self, dtype: Dtype, copy: bool = True):
10911094
"""
10921095
Create an Index with values cast to dtypes.
10931096
@@ -2907,14 +2910,12 @@ def __iadd__(self, other):
29072910
return self + other
29082911

29092912
@final
2910-
def __nonzero__(self) -> NoReturn:
2913+
def __bool__(self) -> NoReturn:
29112914
raise ValueError(
29122915
f"The truth value of a {type(self).__name__} is ambiguous. "
29132916
"Use a.empty, a.bool(), a.item(), a.any() or a.all()."
29142917
)
29152918

2916-
__bool__ = __nonzero__
2917-
29182919
# --------------------------------------------------------------------
29192920
# Set Operation Methods
29202921

@@ -2957,7 +2958,7 @@ def _dti_setop_align_tzs(self, other: Index, setop: str_t) -> tuple[Index, Index
29572958
return self, other
29582959

29592960
@final
2960-
def union(self, other, sort=None):
2961+
def union(self, other, sort: bool | None = None):
29612962
"""
29622963
Form the union of two Index objects.
29632964
@@ -3334,7 +3335,7 @@ def _intersection_via_get_indexer(
33343335
return result
33353336

33363337
@final
3337-
def difference(self, other, sort=None):
3338+
def difference(self, other, sort: bool | None = None):
33383339
"""
33393340
Return a new Index with elements of index not in `other`.
33403341
@@ -3420,7 +3421,12 @@ def _wrap_difference_result(self, other, result):
34203421
# We will override for MultiIndex to handle empty results
34213422
return self._wrap_setop_result(other, result)
34223423

3423-
def symmetric_difference(self, other, result_name=None, sort=None):
3424+
def symmetric_difference(
3425+
self,
3426+
other,
3427+
result_name: abc.Hashable | None = None,
3428+
sort: bool | None = None,
3429+
):
34243430
"""
34253431
Compute the symmetric difference of two Index objects.
34263432
@@ -6389,7 +6395,7 @@ def _transform_index(self, func, *, level=None) -> Index:
63896395
items = [func(x) for x in self]
63906396
return Index(items, name=self.name, tupleize_cols=False)
63916397

6392-
def isin(self, values, level=None) -> npt.NDArray[np.bool_]:
6398+
def isin(self, values, level: str_t | int | None = None) -> npt.NDArray[np.bool_]:
63936399
"""
63946400
Return a boolean array where the index values are in `values`.
63956401
@@ -6687,7 +6693,12 @@ def get_slice_bound(self, label, side: Literal["left", "right"]) -> int:
66876693
else:
66886694
return slc
66896695

6690-
def slice_locs(self, start=None, end=None, step=None) -> tuple[int, int]:
6696+
def slice_locs(
6697+
self,
6698+
start: SliceType = None,
6699+
end: SliceType = None,
6700+
step: int | None = None,
6701+
) -> tuple[int, int]:
66916702
"""
66926703
Compute slice locations for input labels.
66936704
@@ -6781,7 +6792,9 @@ def slice_locs(self, start=None, end=None, step=None) -> tuple[int, int]:
67816792

67826793
return start_slice, end_slice
67836794

6784-
def delete(self, loc) -> Self:
6795+
def delete(
6796+
self, loc: int | np.integer | list[int] | npt.NDArray[np.integer]
6797+
) -> Self:
67856798
"""
67866799
Make new Index with passed location(-s) deleted.
67876800
@@ -7227,7 +7240,9 @@ def _maybe_disable_logical_methods(self, opname: str_t) -> None:
72277240
raise TypeError(f"cannot perform {opname} with {type(self).__name__}")
72287241

72297242
@Appender(IndexOpsMixin.argmin.__doc__)
7230-
def argmin(self, axis=None, skipna: bool = True, *args, **kwargs) -> int:
7243+
def argmin(
7244+
self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs
7245+
) -> int:
72317246
nv.validate_argmin(args, kwargs)
72327247
nv.validate_minmax_axis(axis)
72337248

@@ -7240,7 +7255,9 @@ def argmin(self, axis=None, skipna: bool = True, *args, **kwargs) -> int:
72407255
return super().argmin(skipna=skipna)
72417256

72427257
@Appender(IndexOpsMixin.argmax.__doc__)
7243-
def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int:
7258+
def argmax(
7259+
self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs
7260+
) -> int:
72447261
nv.validate_argmax(args, kwargs)
72457262
nv.validate_minmax_axis(axis)
72467263

@@ -7251,7 +7268,7 @@ def argmax(self, axis=None, skipna: bool = True, *args, **kwargs) -> int:
72517268
raise ValueError("Encountered all NA values")
72527269
return super().argmax(skipna=skipna)
72537270

7254-
def min(self, axis=None, skipna: bool = True, *args, **kwargs):
7271+
def min(self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs):
72557272
"""
72567273
Return the minimum value of the Index.
72577274
@@ -7314,7 +7331,7 @@ def min(self, axis=None, skipna: bool = True, *args, **kwargs):
73147331

73157332
return nanops.nanmin(self._values, skipna=skipna)
73167333

7317-
def max(self, axis=None, skipna: bool = True, *args, **kwargs):
7334+
def max(self, axis: AxisInt | None = None, skipna: bool = True, *args, **kwargs):
73187335
"""
73197336
Return the maximum value of the Index.
73207337

0 commit comments

Comments
 (0)