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
Prev Previous commit
Next Next commit
fix: comments
  • Loading branch information
cmp0xff committed Aug 19, 2025
commit 61de1a13d80f9ae9af7fc0d5cb530ab8cc76a96f
2 changes: 2 additions & 0 deletions pandas-stubs/_typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,8 @@ np_ndarray_float: TypeAlias = npt.NDArray[np.floating]
np_ndarray_complex: TypeAlias = npt.NDArray[np.complexfloating]
np_ndarray_bool: TypeAlias = npt.NDArray[np.bool_]
np_ndarray_str: TypeAlias = npt.NDArray[np.str_]
np_ndarray_dt: TypeAlias = npt.NDArray[np.datetime64]
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)
Expand Down
47 changes: 37 additions & 10 deletions pandas-stubs/core/series.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ from pandas._typing import (
np_ndarray_anyint,
np_ndarray_bool,
np_ndarray_complex,
np_ndarray_dt,
np_ndarray_float,
npt,
num,
Expand Down Expand Up @@ -2071,6 +2072,11 @@ class Series(IndexOpsMixin[S1], NDFrame):
@overload
def __rxor__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ...
@overload
def __sub__(
self: Series[Never],
other: datetime | np.datetime64 | np_ndarray_dt | TimestampSeries,
) -> TimedeltaSeries: ...
@overload
def __sub__(self: Series[Never], other: complex | _ListLike | Series) -> Series: ...
@overload
def __sub__(self, other: Series[Never]) -> Series: ... # type: ignore[overload-overlap]
Expand Down Expand Up @@ -2148,6 +2154,14 @@ class Series(IndexOpsMixin[S1], NDFrame):
other: timedelta | np.timedelta64 | TimedeltaSeries | TimedeltaIndex,
) -> TimedeltaSeries: ...
@overload
def sub(
self: Series[Never],
other: datetime | np.datetime64 | np_ndarray_dt | TimestampSeries,
level: Level | None = None,
fill_value: float | None = None,
axis: int = 0,
) -> TimedeltaSeries: ...
@overload
def sub(
self: Series[Never],
other: complex | _ListLike | Series,
Expand Down Expand Up @@ -2236,34 +2250,39 @@ class Series(IndexOpsMixin[S1], NDFrame):
) -> Series[complex]: ...
@overload
def sub(
self: Series[Timestamp],
other: timedelta | np.timedelta64 | TimedeltaSeries | TimedeltaIndex,
self: Series[_T_COMPLEX],
other: (
Just[complex]
| Sequence[Just[complex]]
| np_ndarray_complex
| Series[complex]
),
level: Level | None = None,
fill_value: float | None = None,
axis: int = 0,
) -> Series[complex]: ...
@overload
def sub(
self: Series[Timedelta],
self: Series[Timestamp],
other: timedelta | np.timedelta64 | TimedeltaSeries | TimedeltaIndex,
level: Level | None = None,
fill_value: float | None = None,
axis: int = 0,
) -> Series[complex]: ...
@overload
def sub(
self: Series[_T_COMPLEX],
other: (
Just[complex]
| Sequence[Just[complex]]
| np_ndarray_complex
| Series[complex]
),
self: Series[Timedelta],
other: timedelta | np.timedelta64 | TimedeltaSeries | TimedeltaIndex,
level: Level | None = None,
fill_value: float | None = None,
axis: int = 0,
) -> Series[complex]: ...
@overload
def __rsub__( # type: ignore[misc]
self: Series[Never],
other: datetime | np.datetime64 | np_ndarray_dt | TimestampSeries,
) -> TimedeltaSeries: ...
@overload
def __rsub__(
self: Series[Never], other: complex | _ListLike | Series
) -> Series: ...
Expand Down Expand Up @@ -2333,6 +2352,14 @@ class Series(IndexOpsMixin[S1], NDFrame):
),
) -> Series[complex]: ...
@overload
def rsub(
self: Series[Never],
other: datetime | np.datetime64 | np_ndarray_dt | TimestampSeries,
level: Level | None = None,
fill_value: float | None = None,
axis: int = 0,
) -> TimedeltaSeries: ...
@overload
def rsub(
self: Series[Never],
other: complex | _ListLike | Series,
Expand Down
204 changes: 135 additions & 69 deletions tests/series/arithmetic/test_sub.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
from typing import NoReturn
from datetime import (
datetime,
timedelta,
)
from typing import (
TYPE_CHECKING,
NoReturn,
)

import numpy as np
from numpy import typing as npt # noqa: F401
Expand All @@ -7,57 +14,60 @@

from tests import check

left = pd.DataFrame({"a": [1, 2, 3]})["a"] # left operand
if TYPE_CHECKING:
from pandas.core.series import TimedeltaSeries # noqa: F401

left_i = pd.DataFrame({"a": [1, 2, 3]})["a"] # left operand


def test_sub_py_scalar() -> None:
"""Test pd.Series[Any] - Python native scalars"""
b, i, f, c = True, 1, 1.0, 1j

check(assert_type(left - b, pd.Series), pd.Series)
check(assert_type(left - i, pd.Series), pd.Series)
check(assert_type(left - f, pd.Series), pd.Series)
check(assert_type(left - c, pd.Series), pd.Series)
check(assert_type(left_i - b, pd.Series), pd.Series)
check(assert_type(left_i - i, pd.Series), pd.Series)
check(assert_type(left_i - f, pd.Series), pd.Series)
check(assert_type(left_i - c, pd.Series), pd.Series)

check(assert_type(b - left, pd.Series), pd.Series)
check(assert_type(i - left, pd.Series), pd.Series)
check(assert_type(f - left, pd.Series), pd.Series)
check(assert_type(c - left, pd.Series), pd.Series)
check(assert_type(b - left_i, pd.Series), pd.Series)
check(assert_type(i - left_i, pd.Series), pd.Series)
check(assert_type(f - left_i, pd.Series), pd.Series)
check(assert_type(c - left_i, pd.Series), pd.Series)

check(assert_type(left.sub(b), pd.Series), pd.Series)
check(assert_type(left.sub(i), pd.Series), pd.Series)
check(assert_type(left.sub(f), pd.Series), pd.Series)
check(assert_type(left.sub(c), pd.Series), pd.Series)
check(assert_type(left_i.sub(b), pd.Series), pd.Series)
check(assert_type(left_i.sub(i), pd.Series), pd.Series)
check(assert_type(left_i.sub(f), pd.Series), pd.Series)
check(assert_type(left_i.sub(c), pd.Series), pd.Series)

check(assert_type(left.rsub(b), pd.Series), pd.Series)
check(assert_type(left.rsub(i), pd.Series), pd.Series)
check(assert_type(left.rsub(f), pd.Series), pd.Series)
check(assert_type(left.rsub(c), pd.Series), pd.Series)
check(assert_type(left_i.rsub(b), pd.Series), pd.Series)
check(assert_type(left_i.rsub(i), pd.Series), pd.Series)
check(assert_type(left_i.rsub(f), pd.Series), pd.Series)
check(assert_type(left_i.rsub(c), pd.Series), pd.Series)


def test_sub_py_sequence() -> None:
"""Test pd.Series[Any] - Python native sequence"""
b, i, f, c = [True, False, True], [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j]

check(assert_type(left - b, pd.Series), pd.Series)
check(assert_type(left - i, pd.Series), pd.Series)
check(assert_type(left - f, pd.Series), pd.Series)
check(assert_type(left - c, pd.Series), pd.Series)
check(assert_type(left_i - b, pd.Series), pd.Series)
check(assert_type(left_i - i, pd.Series), pd.Series)
check(assert_type(left_i - f, pd.Series), pd.Series)
check(assert_type(left_i - c, pd.Series), pd.Series)

check(assert_type(b - left, pd.Series), pd.Series)
check(assert_type(i - left, pd.Series), pd.Series)
check(assert_type(f - left, pd.Series), pd.Series)
check(assert_type(c - left, pd.Series), pd.Series)
check(assert_type(b - left_i, pd.Series), pd.Series)
check(assert_type(i - left_i, pd.Series), pd.Series)
check(assert_type(f - left_i, pd.Series), pd.Series)
check(assert_type(c - left_i, pd.Series), pd.Series)

check(assert_type(left.sub(b), pd.Series), pd.Series)
check(assert_type(left.sub(i), pd.Series), pd.Series)
check(assert_type(left.sub(f), pd.Series), pd.Series)
check(assert_type(left.sub(c), pd.Series), pd.Series)
check(assert_type(left_i.sub(b), pd.Series), pd.Series)
check(assert_type(left_i.sub(i), pd.Series), pd.Series)
check(assert_type(left_i.sub(f), pd.Series), pd.Series)
check(assert_type(left_i.sub(c), pd.Series), pd.Series)

check(assert_type(left.rsub(b), pd.Series), pd.Series)
check(assert_type(left.rsub(i), pd.Series), pd.Series)
check(assert_type(left.rsub(f), pd.Series), pd.Series)
check(assert_type(left.rsub(c), pd.Series), pd.Series)
check(assert_type(left_i.rsub(b), pd.Series), pd.Series)
check(assert_type(left_i.rsub(i), pd.Series), pd.Series)
check(assert_type(left_i.rsub(f), pd.Series), pd.Series)
check(assert_type(left_i.rsub(c), pd.Series), pd.Series)


def test_sub_numpy_array() -> None:
Expand All @@ -67,35 +77,35 @@ def test_sub_numpy_array() -> None:
f = np.array([1.0, 2.0, 3.0], np.float64)
c = np.array([1.1j, 2.2j, 4.1j], np.complex128)

check(assert_type(left - b, pd.Series), pd.Series)
check(assert_type(left - i, pd.Series), pd.Series)
check(assert_type(left - f, pd.Series), pd.Series)
check(assert_type(left - c, pd.Series), pd.Series)
check(assert_type(left_i - b, pd.Series), pd.Series)
check(assert_type(left_i - i, pd.Series), pd.Series)
check(assert_type(left_i - f, pd.Series), pd.Series)
check(assert_type(left_i - c, pd.Series), pd.Series)

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rsub__` cannot override. At runtime, they return
# `Series`s.
# `mypy` thinks the return types are `Any`, which is a bug.
check(assert_type(b - left, NoReturn), pd.Series) # type: ignore[assert-type]
check(assert_type(b - left_i, NoReturn), pd.Series) # type: ignore[assert-type]
check(
assert_type(i - left, "npt.NDArray[np.int64]"), pd.Series # type: ignore[assert-type]
assert_type(i - left_i, "npt.NDArray[np.int64]"), pd.Series # type: ignore[assert-type]
)
check(
assert_type(f - left, "npt.NDArray[np.float64]"), pd.Series # type: ignore[assert-type]
assert_type(f - left_i, "npt.NDArray[np.float64]"), pd.Series # type: ignore[assert-type]
)
check(
assert_type(c - left, "npt.NDArray[np.complex128]"), pd.Series # type: ignore[assert-type]
assert_type(c - left_i, "npt.NDArray[np.complex128]"), pd.Series # type: ignore[assert-type]
)

check(assert_type(left.sub(b), pd.Series), pd.Series)
check(assert_type(left.sub(i), pd.Series), pd.Series)
check(assert_type(left.sub(f), pd.Series), pd.Series)
check(assert_type(left.sub(c), pd.Series), pd.Series)
check(assert_type(left_i.sub(b), pd.Series), pd.Series)
check(assert_type(left_i.sub(i), pd.Series), pd.Series)
check(assert_type(left_i.sub(f), pd.Series), pd.Series)
check(assert_type(left_i.sub(c), pd.Series), pd.Series)

check(assert_type(left.rsub(b), pd.Series), pd.Series)
check(assert_type(left.rsub(i), pd.Series), pd.Series)
check(assert_type(left.rsub(f), pd.Series), pd.Series)
check(assert_type(left.rsub(c), pd.Series), pd.Series)
check(assert_type(left_i.rsub(b), pd.Series), pd.Series)
check(assert_type(left_i.rsub(i), pd.Series), pd.Series)
check(assert_type(left_i.rsub(f), pd.Series), pd.Series)
check(assert_type(left_i.rsub(c), pd.Series), pd.Series)


def test_sub_pd_series() -> None:
Expand All @@ -105,22 +115,78 @@ def test_sub_pd_series() -> None:
f = pd.Series([1.0, 2.0, 3.0])
c = pd.Series([1.1j, 2.2j, 4.1j])

check(assert_type(left - b, pd.Series), pd.Series)
check(assert_type(left - i, pd.Series), pd.Series)
check(assert_type(left - f, pd.Series), pd.Series)
check(assert_type(left - c, pd.Series), pd.Series)

check(assert_type(b - left, pd.Series), pd.Series)
check(assert_type(i - left, pd.Series), pd.Series)
check(assert_type(f - left, pd.Series), pd.Series)
check(assert_type(c - left, pd.Series), pd.Series)

check(assert_type(left.sub(b), pd.Series), pd.Series)
check(assert_type(left.sub(i), pd.Series), pd.Series)
check(assert_type(left.sub(f), pd.Series), pd.Series)
check(assert_type(left.sub(c), pd.Series), pd.Series)

check(assert_type(left.rsub(b), pd.Series), pd.Series)
check(assert_type(left.rsub(i), pd.Series), pd.Series)
check(assert_type(left.rsub(f), pd.Series), pd.Series)
check(assert_type(left.rsub(c), pd.Series), pd.Series)
check(assert_type(left_i - b, pd.Series), pd.Series)
check(assert_type(left_i - i, pd.Series), pd.Series)
check(assert_type(left_i - f, pd.Series), pd.Series)
check(assert_type(left_i - c, pd.Series), pd.Series)

check(assert_type(b - left_i, pd.Series), pd.Series)
check(assert_type(i - left_i, pd.Series), pd.Series)
check(assert_type(f - left_i, pd.Series), pd.Series)
check(assert_type(c - left_i, pd.Series), pd.Series)

check(assert_type(left_i.sub(b), pd.Series), pd.Series)
check(assert_type(left_i.sub(i), pd.Series), pd.Series)
check(assert_type(left_i.sub(f), pd.Series), pd.Series)
check(assert_type(left_i.sub(c), pd.Series), pd.Series)

check(assert_type(left_i.rsub(b), pd.Series), pd.Series)
check(assert_type(left_i.rsub(i), pd.Series), pd.Series)
check(assert_type(left_i.rsub(f), pd.Series), pd.Series)
check(assert_type(left_i.rsub(c), pd.Series), pd.Series)


anchor = datetime(2025, 8, 18)
left_ts = pd.DataFrame({"a": [anchor + timedelta(hours=h + 1) for h in range(3)]})["a"]


def test_sub_py_datetime() -> None:
"""Test pd.Series[Any] - Python native datetime(s)"""
s = anchor

check(assert_type(left_ts - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)

check(assert_type(s - left_ts, "TimedeltaSeries"), pd.Series, pd.Timedelta)

check(assert_type(left_ts.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)

check(assert_type(left_ts.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)


def test_sub_numpy_datetime() -> None:
"""Test pd.Series[Any] - numpy datetime(s)"""
s = np.datetime64(anchor)
a = np.array([s + np.timedelta64(m, "m") for m in range(3)])

# `numpy` typing gives the corresponding `ndarray`s in the static type
# checking, where our `__rsub__` cannot override. At runtime, they return
# `Series`s.
check(assert_type(left_ts - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(left_ts - a, "TimedeltaSeries"), pd.Series, pd.Timedelta) # type: ignore[assert-type]

check(assert_type(s - left_ts, "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(a - left_ts, "npt.NDArray[np.datetime64]"), pd.Series, pd.Timedelta) # type: ignore[assert-type]

check(assert_type(left_ts.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(left_ts.sub(a), "TimedeltaSeries"), pd.Series, pd.Timedelta) # type: ignore[assert-type]

check(assert_type(left_ts.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(left_ts.rsub(a), "TimedeltaSeries"), pd.Series, pd.Timedelta) # type: ignore[assert-type]


def test_sub_pd_datetime() -> None:
"""Test pd.Series[Any] - Pandas datetime(s)"""
s = pd.Timestamp(anchor)
a = pd.Series([s + pd.Timedelta(minutes=m) for m in range(3)])

check(assert_type(left_ts - s, "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(left_ts - a, "TimedeltaSeries"), pd.Series, pd.Timedelta)

check(assert_type(s - left_ts, "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(a - left_ts, "TimedeltaSeries"), pd.Series, pd.Timedelta)

check(assert_type(left_ts.sub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(left_ts.sub(a), "TimedeltaSeries"), pd.Series, pd.Timedelta)

check(assert_type(left_ts.rsub(s), "TimedeltaSeries"), pd.Series, pd.Timedelta)
check(assert_type(left_ts.rsub(a), "TimedeltaSeries"), pd.Series, pd.Timedelta)
Loading