Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ ci:
autofix_prs: false
repos:
- repo: https://github.com/python/black
rev: 25.1.0
rev: 25.9.0
hooks:
- id: black
- repo: https://github.com/PyCQA/isort
rev: 6.0.1
hooks:
- id: isort
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.10
rev: v0.13.1
hooks:
- id: ruff-check
args: [
Expand Down
2 changes: 1 addition & 1 deletion pandas-stubs/core/frame.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack):
@overload
def to_xml(
self,
path_or_buffer: Literal[None] = ...,
path_or_buffer: None = ...,
index: bool = ...,
root_name: str | None = ...,
row_name: str | None = ...,
Expand Down
2 changes: 1 addition & 1 deletion pandas-stubs/core/groupby/indexing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ class GroupByNthSelector(Generic[_GroupByT]):
def __call__(
self,
n: PositionalIndexer | tuple,
dropna: Literal["any", "all", None] = ...,
dropna: Literal["any", "all"] | None = ...,
) -> DataFrame | Series: ...
def __getitem__(self, n: PositionalIndexer | tuple) -> DataFrame | Series: ...
25 changes: 19 additions & 6 deletions pandas-stubs/core/series.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,14 @@ class _SupportsMul(Protocol[_T_co]):
def __mul__(self, value: Self, /) -> _T_co: ...

@type_check_only
class _SupportsAddRMul(Protocol[_T_co]):
class _SupportsAdd_MulFloat(Protocol[_T_co]):
def __add__(self, value: Self, /) -> _T_co: ...
def __mul__(self, value: float, /) -> _T_co: ...

@type_check_only
class SupportsTruedivInt(Protocol[_T_co]):
def __truediv__(self, value: int, /) -> _T_co: ...

class _iLocIndexerSeries(_iLocIndexer, Generic[S1]):
# get item
@overload
Expand Down Expand Up @@ -4474,7 +4478,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
) -> Timestamp: ...
@overload
def mean(
self: SupportsGetItem[Scalar, _SupportsAddRMul[S1]],
self: SupportsGetItem[Scalar, _SupportsAdd_MulFloat[S1]],
axis: AxisIndex | None = 0,
skipna: _bool = True,
level: None = ...,
Expand All @@ -4489,25 +4493,34 @@ class Series(IndexOpsMixin[S1], NDFrame):
level: None = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> Any: ...
@overload
def median(
self: Series[complex],
axis: AxisIndex | None = 0,
skipna: _bool = True,
level: None = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> float: ...
@overload
def median(
self: Series[Timestamp],
self: SupportsGetItem[Scalar, SupportsTruedivInt[S2]],
axis: AxisIndex | None = 0,
skipna: _bool = True,
level: None = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> Timestamp: ...
) -> S2: ...
@overload
def median(
self,
self: Series[Timestamp],
axis: AxisIndex | None = 0,
skipna: _bool = True,
level: None = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> S1: ...
) -> Timestamp: ...
def min(
self,
axis: AxisIndex | None = 0,
Expand Down
2 changes: 1 addition & 1 deletion tests/series/arithmetic/float/test_floordiv.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_floordiv_numpy_array() -> None:
assert_type(left // c, Never)

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rsub__` cannot override. At runtime, they lead to
# checking, where our `__rfloordiv__` cannot override. At runtime, they lead to
# errors or pd.Series.
assert_type(b // left, "npt.NDArray[np.int8]")
check(i // left, pd.Series, np.floating)
Expand Down
2 changes: 1 addition & 1 deletion tests/series/arithmetic/int/test_floordiv.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_floordiv_numpy_array() -> None:
assert_type(left // c, Never)

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rsub__` cannot override. At runtime, they lead to
# checking, where our `__rfloordiv__` cannot override. At runtime, they lead to
# errors or pd.Series.
assert_type(b // left, "npt.NDArray[np.int8]")
check(i // left, pd.Series, np.integer)
Expand Down
2 changes: 1 addition & 1 deletion tests/series/arithmetic/timedelta/test_floordiv.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def test_floordiv_numpy_array() -> None:
check(assert_type(left // d, "pd.Series[int]"), pd.Series, np.integer)

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rsub__` cannot override. At runtime, they lead to
# checking, where our `__rfloordiv__` cannot override. At runtime, they lead to
# errors or pd.Series.
if TYPE_CHECKING_INVALID_USAGE:
assert_type(b // left, "npt.NDArray[np.int8]")
Expand Down
2 changes: 1 addition & 1 deletion tests/series/arithmetic/timedelta/test_truediv.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def test_truediv_numpy_array() -> None:
check(assert_type(left / d, "pd.Series[float]"), pd.Series, np.floating)

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rsub__` cannot override. At runtime, they lead to
# checking, where our `__rtruediv__` cannot override. At runtime, they lead to
# errors or pd.Series.
if TYPE_CHECKING_INVALID_USAGE:
assert_type(b / left, "npt.NDArray[np.float64]")
Expand Down
60 changes: 60 additions & 0 deletions tests/series/test_agg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from typing import Any

import numpy as np
import pandas as pd
from typing_extensions import assert_type

from tests import (
TYPE_CHECKING_INVALID_USAGE,
check,
pytest_warns_bounded,
)


def test_agg_any_float() -> None:
series = pd.DataFrame({"A": [1.0, float("nan"), 2.0]})["A"]
check(assert_type(series.median(), Any), np.float64)


def test_agg_bool() -> None:
series = pd.Series([True, False, True])
check(assert_type(series.median(), float), np.float64)


def test_agg_int() -> None:
series = pd.Series([3, 1, 2])
check(assert_type(series.median(), float), np.float64)


def test_agg_float() -> None:
series = pd.Series([3.0, float("nan"), 2.0])
check(assert_type(series.median(), float), np.float64)


def test_agg_complex() -> None:
series = pd.Series([3j, 3 + 4j, 2j])
with pytest_warns_bounded(
np.exceptions.ComplexWarning,
r"Casting complex values to real discards the imaginary part",
):
check(assert_type(series.median(), float), np.float64)


def test_agg_str() -> None:
series = pd.Series(["1", "a", "🐼"])
if TYPE_CHECKING_INVALID_USAGE:
series.median() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue]


def test_agg_ts() -> None:
series = pd.Series(pd.to_datetime(["2025-09-18", "2025-09-18", "2025-09-18"]))
check(assert_type(series, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)

check(assert_type(series.median(), pd.Timestamp), pd.Timestamp)


def test_agg_td() -> None:
series = pd.Series(pd.to_timedelta(["1 days", "2 days", "3 days"]))
check(assert_type(series, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta)

check(assert_type(series.median(), pd.Timedelta), pd.Timedelta)
8 changes: 8 additions & 0 deletions tests/series/test_cumul.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ def test_cumul_ts() -> None:

if TYPE_CHECKING_INVALID_USAGE:
series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue]


def test_cumul_td() -> None:
series = pd.Series(pd.to_timedelta(["1 days", "2 days", "3 days"]))
check(assert_type(series, "pd.Series[pd.Timedelta]"), pd.Series, pd.Timedelta)

if TYPE_CHECKING_INVALID_USAGE:
series.cumprod() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue]
1 change: 0 additions & 1 deletion tests/series/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,6 @@ def test_types_mean() -> None:

def test_types_median() -> None:
s = pd.Series([1, 2, 3, np.nan])
check(assert_type(s.median(), float), float)
check(
assert_type(s.groupby(level=0).median(), "pd.Series[float]"),
pd.Series,
Expand Down
10 changes: 7 additions & 3 deletions tests/test_timefuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,12 @@ def test_series_types_to_numpy() -> None:
np_1darray,
dtype=np.int64,
)
check(
# mypy gives error: Expression is of type "ndarray[tuple[int], dtype[timedelta64[Any]]]", not "ndarray[tuple[int], dtype[timedelta64[timedelta | int | None]]]" [assert-type]
assert_type(td_s.to_numpy(dtype=np.timedelta64), np_1darray[np.timedelta64]), # type: ignore[assert-type]
np_1darray,
dtype=np.timedelta64,
)
check(
assert_type(ts_s.to_numpy(dtype=np.int64), np_1darray[np.int64]),
np_1darray,
Expand Down Expand Up @@ -1718,10 +1724,9 @@ def test_timedeltaseries_add_timestampseries() -> None:
check(assert_type(plus, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)


def test_mean_median_std() -> None:
def test_mean_std() -> None:
s = pd.Series([pd.Timedelta("1 ns"), pd.Timedelta("2 ns"), pd.Timedelta("3 ns")])
check(assert_type(s.mean(), pd.Timedelta), pd.Timedelta)
check(assert_type(s.median(), pd.Timedelta), pd.Timedelta)
check(assert_type(s.std(), pd.Timedelta), pd.Timedelta)

s2 = pd.Series(
Expand All @@ -1732,7 +1737,6 @@ def test_mean_median_std() -> None:
]
)
check(assert_type(s2.mean(), pd.Timestamp), pd.Timestamp)
check(assert_type(s2.median(), pd.Timestamp), pd.Timestamp)
check(assert_type(s2.std(), pd.Timedelta), pd.Timedelta)


Expand Down
11 changes: 11 additions & 0 deletions ttest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from typing import reveal_type

import pandas as pd

s1 = pd.Series(pd.to_datetime(["2022-05-01", "2022-06-01"]))
reveal_type(s1)
s2 = pd.Series(pd.to_datetime(["2022-05-15", "2022-06-15"]))
reveal_type(s2)
td = s1 - s2
reveal_type(td)
ssum = s1 + s2
Loading