diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index d206adcb..c57123f4 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -2071,7 +2071,6 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series[bool]: ... @overload def __ror__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... - def __rsub__(self, other: num | _ListLike | Series[S1]) -> Series: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] def __rxor__( # pyright: ignore[reportOverlappingOverload] @@ -2080,6 +2079,47 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def __rxor__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... @overload + def __sub__( + self, other: Timestamp | datetime | 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] + @overload + def __sub__( + self: Series[int], other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX] + ) -> Series[_T_COMPLEX]: ... + @overload + def __sub__(self: Series[int], other: np_ndarray_anyint) -> Series[int]: ... + @overload + def __sub__(self: Series[int], other: np_ndarray_float) -> Series[float]: ... + @overload + def __sub__(self: Series[int], other: np_ndarray_complex) -> Series[complex]: ... + @overload + def __sub__( + self: Series[float], + other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float | Series[int], + ) -> Series[float]: ... + @overload + def __sub__( + self: Series[float], + other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX], + ) -> Series[_T_COMPLEX]: ... + @overload + def __sub__(self: Series[float], other: np_ndarray_complex) -> Series[complex]: ... + @overload + def __sub__( + self: Series[complex], + other: ( + Sequence[_T_COMPLEX] + | Series[_T_COMPLEX] + | np_ndarray_anyint + | np_ndarray_float + | np_ndarray_complex + ), + ) -> Series[complex]: ... + @overload def __sub__( self: Series[Timestamp], other: Timedelta | TimedeltaSeries | TimedeltaIndex | np.timedelta64, @@ -2090,14 +2130,217 @@ class Series(IndexOpsMixin[S1], NDFrame): other: Timedelta | TimedeltaSeries | TimedeltaIndex | np.timedelta64, ) -> TimedeltaSeries: ... @overload - def __sub__( - self, other: Timestamp | datetime | TimestampSeries - ) -> TimedeltaSeries: ... + def __sub__(self, other: S1 | Series[S1]) -> Self: ... + @overload + def sub( + self: Series[Never], + other: Scalar | _ListLike | Series, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series: ... + @overload + def sub( + self: Series[int], + other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[_T_COMPLEX]: ... + @overload + def sub( + self: Series[int], + other: np_ndarray_anyint, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[int]: ... + @overload + def sub( + self: Series[int], + other: np_ndarray_float, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[float]: ... + @overload + def sub( + self: Series[int], + other: np_ndarray_complex, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[complex]: ... + @overload + def sub( + self: Series[float], + other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float | Series[int], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[float]: ... + @overload + def sub( + self: Series[float], + other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[_T_COMPLEX]: ... + @overload + def sub( + self: Series[float], + other: np_ndarray_complex, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[complex]: ... + @overload + def sub( + self: Series[complex], + other: ( + Sequence[_T_COMPLEX] + | np_ndarray_anyint + | np_ndarray_float + | np_ndarray_complex + | Series[_T_COMPLEX] + ), + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[complex]: ... + @overload + def sub( + self, + other: S1 | Series[S1], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Self: ... + @overload + def __rsub__(self: Series[Never], other: Scalar | _ListLike) -> Series: ... + @overload + def __rsub__( + self: Series[int], other: _T_COMPLEX | Sequence[_T_COMPLEX] + ) -> Series[_T_COMPLEX]: ... @overload - def __sub__(self, other: num | _ListLike | Series) -> Series: ... + def __rsub__(self: Series[int], other: np_ndarray_anyint) -> Series[int]: ... + @overload + def __rsub__(self: Series[int], other: np_ndarray_float) -> Series[float]: ... + @overload + def __rsub__(self: Series[int], other: np_ndarray_complex) -> Series[complex]: ... + @overload + def __rsub__( + self: Series[float], + other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float, + ) -> Series[float]: ... + @overload + def __rsub__( + self: Series[float], other: _T_COMPLEX | Sequence[_T_COMPLEX] + ) -> Series[_T_COMPLEX]: ... + @overload + def __rsub__(self: Series[float], other: np_ndarray_complex) -> Series[complex]: ... + @overload + def __rsub__( + self: Series[complex], + other: ( + np_ndarray_anyint + | np_ndarray_float + | np_ndarray_complex + | Sequence[_T_COMPLEX] + ), + ) -> Series[complex]: ... + @overload + def __rsub__(self, other: S1) -> Self: ... + @overload + def rsub( + self: Series[Never], + other: Scalar | _ListLike | Series, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series: ... + @overload + def rsub( + self: Series[int], + other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[_T_COMPLEX]: ... + @overload + def rsub( + self: Series[int], + other: np_ndarray_anyint, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[int]: ... + @overload + def rsub( + self: Series[int], + other: np_ndarray_float, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[float]: ... + @overload + def rsub( + self: Series[int], + other: np_ndarray_complex, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[complex]: ... + @overload + def rsub( + self: Series[float], + other: int | Sequence[int] | np_ndarray_anyint | np_ndarray_float | Series[int], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[float]: ... + @overload + def rsub( + self: Series[float], + other: _T_COMPLEX | Sequence[_T_COMPLEX] | Series[_T_COMPLEX], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[_T_COMPLEX]: ... + @overload + def rsub( + self: Series[float], + other: np_ndarray_complex, + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[complex]: ... + @overload + def rsub( + self: Series[complex], + other: ( + Sequence[_T_COMPLEX] + | np_ndarray_anyint + | np_ndarray_float + | np_ndarray_complex + | Series[_T_COMPLEX] + ), + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Series[complex]: ... + @overload + def rsub( + self, + other: S1 | Series[S1], + level: Level | None = None, + fill_value: float | None = None, + axis: int = 0, + ) -> Self: ... @overload def __truediv__( - self: Series[Never], other: Scalar | _ListLike | Series + self: Series[Never], other: complex | _ListLike | Series ) -> Series: ... @overload def __truediv__(self, other: Series[Never]) -> Series: ... @@ -2145,7 +2388,7 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def truediv( self: Series[Never], - other: Scalar | _ListLike | Series, + other: complex | _ListLike | Series, level: Level | None = None, fill_value: float | None = None, axis: AxisIndex = 0, @@ -2231,7 +2474,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series: ... div = truediv @overload - def __rtruediv__(self: Series[Never], other: Scalar | _ListLike) -> Series: ... + def __rtruediv__(self: Series[Never], other: complex | _ListLike) -> Series: ... @overload def __rtruediv__( self: Series[int], @@ -2274,7 +2517,7 @@ class Series(IndexOpsMixin[S1], NDFrame): @overload def rtruediv( self: Series[Never], - other: Scalar | _ListLike | Series, + other: complex | _ListLike | Series, level: Level | None = None, fill_value: float | None = None, axis: AxisIndex = 0, @@ -2675,13 +2918,6 @@ class Series(IndexOpsMixin[S1], NDFrame): fill_value: float | None = None, axis: AxisIndex = ..., ) -> Series[S1]: ... - def rsub( - self, - other: Series[S1] | Scalar, - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex = ..., - ) -> Series[S1]: ... def sem( self, axis: AxisIndex | None = 0, @@ -2705,20 +2941,6 @@ class Series(IndexOpsMixin[S1], NDFrame): numeric_only: _bool = False, **kwargs: Any, ) -> float: ... - def sub( - self, - other: num | _ListLike | Series[S1], - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex | None = 0, - ) -> Series[S1]: ... - def subtract( - self, - other: num | _ListLike | Series[S1], - level: Level | None = ..., - fill_value: float | None = None, - axis: AxisIndex | None = 0, - ) -> Series[S1]: ... @overload def sum( self: Series[Never], diff --git a/tests/series/arithmetic/complex/test_sub.py b/tests/series/arithmetic/complex/test_sub.py new file mode 100644 index 00000000..ebe20da3 --- /dev/null +++ b/tests/series/arithmetic/complex/test_sub.py @@ -0,0 +1,129 @@ +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +left = pd.Series([1j, 2j, 3j]) # left operand + + +def test_sub_py_scalar() -> None: + """Test pd.Series[complex] - Python native scalars""" + i, f, c = 1, 1.0, 1j + + check(assert_type(left - i, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - f, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(f - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(f), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check( + assert_type(left.rsub(i), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(f), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_py_sequence() -> None: + """Test pd.Series[complex] - Python native sequence""" + i, f, c = [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + + check(assert_type(left - i, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - f, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(f - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(f), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check( + assert_type(left.rsub(i), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(f), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_numpy_array() -> None: + """Test pd.Series[complex] - numpy array""" + i = np.array([2, 3, 5], np.int64) + 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 - i, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - f, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rsub__` cannot override. At runtime, they return + # `Series`s with the correct element type. + check(assert_type(i - left, "npt.NDArray[np.int64]"), pd.Series, np.complexfloating) + check( + assert_type(f - left, "npt.NDArray[np.float64]"), pd.Series, np.complexfloating + ) + check( + assert_type(c - left, "npt.NDArray[np.complex128]"), + pd.Series, + np.complexfloating, + ) + + check(assert_type(left.sub(i), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(f), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check( + assert_type(left.rsub(i), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(f), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_pd_series() -> None: + """Test pd.Series[complex] - pandas series""" + i = pd.Series([2, 3, 5]) + f = pd.Series([1.0, 2.0, 3.0]) + c = pd.Series([1.1j, 2.2j, 4.1j]) + + check(assert_type(left - i, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - f, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(f - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(f), "pd.Series[complex]"), pd.Series, np.complexfloating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check( + assert_type(left.rsub(i), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(f), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) diff --git a/tests/series/arithmetic/float/test_sub.py b/tests/series/arithmetic/float/test_sub.py new file mode 100644 index 00000000..da2d572e --- /dev/null +++ b/tests/series/arithmetic/float/test_sub.py @@ -0,0 +1,111 @@ +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +left = pd.Series([1.0, 2.0, 3.0]) # left operand + + +def test_sub_py_scalar() -> None: + """Test pd.Series[float] - Python native scalars""" + i, f, c = 1, 1.0, 1j + + check(assert_type(left - i, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(f - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_py_sequence() -> None: + """Test pd.Series[float] - Python native sequence""" + i, f, c = [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + + check(assert_type(left - i, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(f - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_numpy_array() -> None: + """Test pd.Series[float] - numpy array""" + i = np.array([2, 3, 5], np.int64) + 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 - i, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rsub__` cannot override. At runtime, they return + # `Series`s with the correct element type. + check(assert_type(i - left, "npt.NDArray[np.int64]"), pd.Series, np.floating) + check(assert_type(f - left, "npt.NDArray[np.float64]"), pd.Series, np.floating) + check( + assert_type(c - left, "npt.NDArray[np.complex128]"), + pd.Series, + np.complexfloating, + ) + + check(assert_type(left.sub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_pd_series() -> None: + """Test pd.Series[float] - pandas series""" + i = pd.Series([2, 3, 5]) + f = pd.Series([1.0, 2.0, 3.0]) + c = pd.Series([1.1j, 2.2j, 4.1j]) + + check(assert_type(left - i, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(f - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) diff --git a/tests/series/arithmetic/int/test_sub.py b/tests/series/arithmetic/int/test_sub.py new file mode 100644 index 00000000..5451663d --- /dev/null +++ b/tests/series/arithmetic/int/test_sub.py @@ -0,0 +1,111 @@ +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +left = pd.Series([1, 2, 3]) # left operand + + +def test_sub_py_scalar() -> None: + """Test pd.Series[int] - Python native scalars""" + i, f, c = 1, 1.0, 1j + + check(assert_type(left - i, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(f - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_py_sequence() -> None: + """Test pd.Series[int] - Python native sequence""" + i, f, c = [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + + check(assert_type(left - i, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(f - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_numpy_array() -> None: + """Test pd.Series[int] - numpy array""" + i = np.array([2, 3, 5], np.int64) + 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 - i, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + # `numpy` typing gives the corresponding `ndarray`s in the static type + # checking, where our `__rsub__` cannot override. At runtime, they return + # `Series`s with the correct element type. + check(assert_type(i - left, "npt.NDArray[np.int64]"), pd.Series, np.integer) + check(assert_type(f - left, "npt.NDArray[np.float64]"), pd.Series, np.floating) + check( + assert_type(c - left, "npt.NDArray[np.complex128]"), + pd.Series, + np.complexfloating, + ) + + check(assert_type(left.sub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) + + +def test_sub_pd_series() -> None: + """Test pd.Series[int] - pandas series""" + i = pd.Series([2, 3, 5]) + f = pd.Series([1.0, 2.0, 3.0]) + c = pd.Series([1.1j, 2.2j, 4.1j]) + + check(assert_type(left - i, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left - f, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left - c, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(i - left, "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(f - left, "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(c - left, "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.sub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.sub(f), "pd.Series[float]"), pd.Series, np.floating) + check(assert_type(left.sub(c), "pd.Series[complex]"), pd.Series, np.complexfloating) + + check(assert_type(left.rsub(i), "pd.Series[int]"), pd.Series, np.integer) + check(assert_type(left.rsub(f), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(left.rsub(c), "pd.Series[complex]"), pd.Series, np.complexfloating + ) diff --git a/tests/series/arithmetic/test_sub.py b/tests/series/arithmetic/test_sub.py new file mode 100644 index 00000000..20e7420d --- /dev/null +++ b/tests/series/arithmetic/test_sub.py @@ -0,0 +1,106 @@ +import numpy as np +from numpy import typing as npt # noqa: F401 +import pandas as pd +from typing_extensions import assert_type + +from tests import check + +left = pd.DataFrame({"a": [1, 2, 3]})["a"] # left operand + + +def test_sub_py_scalar() -> None: + """Test pd.Series[Any] - Python native scalars""" + i, f, c = 1, 1.0, 1j + + 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(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(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(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) + + +def test_sub_py_sequence() -> None: + """Test pd.Series[Any] - Python native sequence""" + i, f, c = [2, 3, 5], [1.0, 2.0, 3.0], [1j, 1j, 4j] + + 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(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(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(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) + + +def test_sub_numpy_array() -> None: + """Test pd.Series[Any] - numpy array""" + i = np.array([2, 3, 5], np.int64) + 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 - i, pd.Series), pd.Series) + check(assert_type(left - f, pd.Series), pd.Series) + check(assert_type(left - 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(i - left, "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] + ) + check( + assert_type(c - left, "npt.NDArray[np.complex128]"), pd.Series # type: ignore[assert-type] + ) + + 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(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) + + +def test_sub_pd_series() -> None: + """Test pd.Series[Any] - pandas series""" + i = pd.Series([2, 3, 5]) + f = pd.Series([1.0, 2.0, 3.0]) + c = pd.Series([1.1j, 2.2j, 4.1j]) + + 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(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(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(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) diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 4d1486dc..d9723588 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -818,8 +818,7 @@ def test_types_element_wise_arithmetic() -> None: check(assert_type(s + s2, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s.add(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) - # TODO this one below should type pd.Series[int] - check(assert_type(s - s2, pd.Series), pd.Series, np.integer) + check(assert_type(s - s2, "pd.Series[int]"), pd.Series, np.integer) check(assert_type(s.sub(s2, fill_value=0), "pd.Series[int]"), pd.Series, np.integer) # TODO these two below should type pd.Series[int] @@ -1643,7 +1642,7 @@ def test_series_mul() -> None: sm = s * 4 check(assert_type(sm, pd.Series), pd.Series) ss = s - 4 - check(assert_type(ss, pd.Series), pd.Series) + check(assert_type(ss, "pd.Series[int]"), pd.Series, np.integer) sm2 = s * s check(assert_type(sm2, pd.Series), pd.Series) sp = s + 4