Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 8 additions & 0 deletions pandas-stubs/_typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,14 @@ np_ndarray_td: TypeAlias = npt.NDArray[np.timedelta64]

# Define shape and generic type variables with defaults similar to numpy
GenericT = TypeVar("GenericT", bound=np.generic, default=Any)
TD64ItemT = TypeVar(
"TD64ItemT",
bound=datetime.timedelta | int | None,
default=datetime.timedelta | int | None,
)
DT64ItemT = TypeVar(
"DT64ItemT", bound=datetime.date | int | None, default=datetime.date | int | None
)
GenericT_co = TypeVar("GenericT_co", bound=np.generic, default=Any, covariant=True)
ShapeT = TypeVar("ShapeT", bound=tuple[int, ...], default=tuple[Any, ...])
# Numpy ndarray with more ergonomic typevar
Expand Down
36 changes: 25 additions & 11 deletions pandas-stubs/core/series.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,6 @@ class _SupportsAdd(Protocol[_T_co]):
class _SupportsMul(Protocol[_T_co]):
def __mul__(self, value: Self, /) -> _T_co: ...

@type_check_only
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: ...
Expand Down Expand Up @@ -4466,7 +4461,7 @@ class Series(IndexOpsMixin[S1], NDFrame):
level: None = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> float: ...
) -> Any: ...
@overload
def mean(
self: Series[Timestamp],
Expand All @@ -4478,13 +4473,13 @@ class Series(IndexOpsMixin[S1], NDFrame):
) -> Timestamp: ...
@overload
def mean(
self: SupportsGetItem[Scalar, _SupportsAdd_MulFloat[S1]],
self: SupportsGetItem[Scalar, SupportsTruedivInt[S2]],
axis: AxisIndex | None = 0,
skipna: _bool = True,
level: None = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> S1: ...
) -> S2: ...
@overload
def median(
self: Series[Never],
Expand Down Expand Up @@ -4632,7 +4627,26 @@ class Series(IndexOpsMixin[S1], NDFrame):
) -> Scalar: ...
@overload
def std(
self: Series[Timestamp] | Series[Timedelta],
self: Series[Never],
axis: AxisIndex | None = 0,
skipna: _bool | None = True,
ddof: int = 1,
numeric_only: _bool = False,
**kwargs: Any,
) -> Any: ...
@overload
def std(
self: Series[complex],
axis: AxisIndex | None = 0,
skipna: _bool | None = True,
level: None = ...,
ddof: int = ...,
numeric_only: _bool = False,
**kwargs: Any,
) -> np.float64: ...
@overload
def std(
self: Series[Timestamp],
axis: AxisIndex | None = 0,
skipna: _bool | None = True,
level: None = ...,
Expand All @@ -4642,13 +4656,13 @@ class Series(IndexOpsMixin[S1], NDFrame):
) -> Timedelta: ...
@overload
def std(
self,
self: SupportsGetItem[Scalar, SupportsTruedivInt[S2]],
axis: AxisIndex | None = 0,
skipna: _bool | None = True,
ddof: int = 1,
numeric_only: _bool = False,
**kwargs: Any,
) -> float: ...
) -> S2: ...
def sum(
self: SupportsGetItem[Scalar, _SupportsAdd[_T]],
axis: AxisIndex | None = 0,
Expand Down
25 changes: 25 additions & 0 deletions tests/series/test_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,73 @@

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


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


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


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


def test_agg_complex() -> None:
series = pd.Series([3j, 3 + 4j, 2j])
check(assert_type(series, "pd.Series[complex]"), pd.Series, np.complex128)

check(assert_type(series.mean(), complex), np.complex128)
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)
with (
pytest_warns_bounded(
np.exceptions.ComplexWarning,
r"Casting complex values to real discards the imaginary part",
),
pytest_warns_bounded(RuntimeWarning, r"invalid value encountered in sqrt"),
):
check(assert_type(series.std(), np.float64), np.float64)


def test_agg_str() -> None:
series = pd.Series(["1", "a", "🐼"])
if TYPE_CHECKING_INVALID_USAGE:
series.mean() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue]
series.median() # type: ignore[misc] # pyright: ignore[reportAttributeAccessIssue]
series.std() # 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.mean(), pd.Timestamp), pd.Timestamp)
check(assert_type(series.median(), pd.Timestamp), pd.Timestamp)
check(assert_type(series.std(), pd.Timedelta), pd.Timedelta)


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.mean(), pd.Timedelta), pd.Timedelta)
check(assert_type(series.median(), pd.Timedelta), pd.Timedelta)
check(assert_type(series.std(), pd.Timedelta), pd.Timedelta)
2 changes: 0 additions & 2 deletions tests/series/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,6 @@ def test_types_rank() -> None:

def test_types_mean() -> None:
s = pd.Series([1, 2, 3, np.nan])
check(assert_type(s.mean(), float), float)
check(
assert_type(s.groupby(level=0).mean(), "pd.Series[float]"),
pd.Series,
Expand Down Expand Up @@ -740,7 +739,6 @@ def test_types_var() -> None:

def test_types_std() -> None:
s = pd.Series([-10, 2, 3, 10])
s.std()
s.std(axis=0, ddof=1)
s.std(skipna=True, numeric_only=False)

Expand Down
16 changes: 0 additions & 16 deletions tests/test_timefuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1724,22 +1724,6 @@ def test_timedeltaseries_add_timestampseries() -> None:
check(assert_type(plus, "pd.Series[pd.Timestamp]"), pd.Series, pd.Timestamp)


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.std(), pd.Timedelta), pd.Timedelta)

s2 = pd.Series(
[
pd.Timestamp("2021-01-01"),
pd.Timestamp("2021-01-02"),
pd.Timestamp("2021-01-03"),
]
)
check(assert_type(s2.mean(), pd.Timestamp), pd.Timestamp)
check(assert_type(s2.std(), pd.Timedelta), pd.Timedelta)


def test_timestamp_strptime_fails():
if TYPE_CHECKING_INVALID_USAGE:
assert_never(
Expand Down
11 changes: 0 additions & 11 deletions ttest.py

This file was deleted.

Loading