From 66a5de3f06002522484caa5b35c663e678904796 Mon Sep 17 00:00:00 2001 From: Irv Lustig Date: Sun, 27 Feb 2022 15:08:13 -0500 Subject: [PATCH] Typeinterval part2 (#46098) --- pandas/_libs/interval.pyi | 174 ++++++++++++++++++++++++++++++++ pandas/core/indexes/interval.py | 10 +- pandas/io/formats/style.py | 8 +- 3 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 pandas/_libs/interval.pyi diff --git a/pandas/_libs/interval.pyi b/pandas/_libs/interval.pyi new file mode 100644 index 0000000000000..67cb604083c6b --- /dev/null +++ b/pandas/_libs/interval.pyi @@ -0,0 +1,174 @@ +from __future__ import annotations + +from typing import ( + Any, + Generic, + TypeVar, + Union, + overload, +) + +import numpy as np +import numpy.typing as npt + +from pandas._typing import ( + IntervalClosedType, + Timedelta, + Timestamp, +) + +VALID_CLOSED: frozenset[str] + +_OrderableScalarT = TypeVar("_OrderableScalarT", int, float) +_OrderableTimesT = TypeVar("_OrderableTimesT", Timestamp, Timedelta) +_OrderableT = TypeVar("_OrderableT", int, float, Timestamp, Timedelta) + +class _LengthDescriptor: + @overload + def __get__( + self, instance: Interval[_OrderableScalarT], owner: Any + ) -> _OrderableScalarT: ... + @overload + def __get__( + self, instance: Interval[_OrderableTimesT], owner: Any + ) -> Timedelta: ... + @overload + def __get__(self, instance: IntervalTree, owner: Any) -> np.ndarray: ... + +class _MidDescriptor: + @overload + def __get__(self, instance: Interval[_OrderableScalarT], owner: Any) -> float: ... + @overload + def __get__( + self, instance: Interval[_OrderableTimesT], owner: Any + ) -> _OrderableTimesT: ... + @overload + def __get__(self, instance: IntervalTree, owner: Any) -> np.ndarray: ... + +class IntervalMixin: + @property + def closed_left(self) -> bool: ... + @property + def closed_right(self) -> bool: ... + @property + def open_left(self) -> bool: ... + @property + def open_right(self) -> bool: ... + mid: _MidDescriptor + length: _LengthDescriptor + @property + def is_empty(self) -> bool: ... + def _check_closed_matches(self, other: IntervalMixin, name: str = ...) -> None: ... + +class Interval(IntervalMixin, Generic[_OrderableT]): + @property + def left(self: Interval[_OrderableT]) -> _OrderableT: ... + @property + def right(self: Interval[_OrderableT]) -> _OrderableT: ... + @property + def closed(self) -> IntervalClosedType: ... + def __init__( + self, + left: _OrderableT, + right: _OrderableT, + closed: IntervalClosedType = ..., + ): ... + def __hash__(self) -> int: ... + @overload + def __contains__(self: Interval[_OrderableTimesT], _OrderableTimesT) -> bool: ... + @overload + def __contains__( + self: Interval[_OrderableScalarT], key: Union[int, float] + ) -> bool: ... + def __repr__(self) -> str: ... + def __str__(self) -> str: ... + @overload + def __add__( + self: Interval[_OrderableTimesT], y: Timedelta + ) -> Interval[_OrderableTimesT]: ... + @overload + def __add__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __add__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __add__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __radd__( + self: Interval[_OrderableTimesT], y: Timedelta + ) -> Interval[_OrderableTimesT]: ... + @overload + def __radd__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __radd__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __radd__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __sub__( + self: Interval[_OrderableTimesT], y: Timedelta + ) -> Interval[_OrderableTimesT]: ... + @overload + def __sub__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __sub__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __sub__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __rsub__( + self: Interval[_OrderableTimesT], y: Timedelta + ) -> Interval[_OrderableTimesT]: ... + @overload + def __rsub__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __rsub__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __rsub__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __mul__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __mul__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __mul__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __rmul__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __rmul__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __rmul__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __truediv__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __truediv__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __truediv__(self: Interval[float], y: Union[int, float]) -> Interval[float]: ... + @overload + def __floordiv__(self: Interval[int], y: int) -> Interval[int]: ... + @overload + def __floordiv__(self: Interval[int], y: float) -> Interval[float]: ... + @overload + def __floordiv__( + self: Interval[float], y: Union[int, float] + ) -> Interval[float]: ... + def overlaps(self: Interval[_OrderableT], other: Interval[_OrderableT]) -> bool: ... + +def intervals_to_interval_bounds( + intervals: np.ndarray, validate_closed: bool = ... +) -> tuple[np.ndarray, np.ndarray, str]: ... + +class IntervalTree(IntervalMixin): + def __init__( + self, + left: np.ndarray, + right: np.ndarray, + closed: IntervalClosedType = ..., + leaf_size: int = ..., + ): ... + def get_indexer(self, target) -> npt.NDArray[np.intp]: ... + def get_indexer_non_unique( + self, target + ) -> tuple[npt.NDArray[np.intp], npt.NDArray[np.intp]]: ... + _na_count: int + @property + def is_overlapping(self) -> bool: ... + @property + def is_monotonic_increasing(self) -> bool: ... + def clear_mapping(self) -> None: ... diff --git a/pandas/core/indexes/interval.py b/pandas/core/indexes/interval.py index aea0326bed2fb..c1d7eb972e1f4 100644 --- a/pandas/core/indexes/interval.py +++ b/pandas/core/indexes/interval.py @@ -321,9 +321,10 @@ def from_tuples( return cls._simple_new(arr, name=name) # -------------------------------------------------------------------- - + # error: Return type "IntervalTree" of "_engine" incompatible with return type + # "Union[IndexEngine, ExtensionEngine]" in supertype "Index" @cache_readonly - def _engine(self) -> IntervalTree: + def _engine(self) -> IntervalTree: # type: ignore[override] left = self._maybe_convert_i8(self.left) right = self._maybe_convert_i8(self.right) return IntervalTree(left, right, closed=self.closed) @@ -515,7 +516,10 @@ def _maybe_convert_i8(self, key): left = self._maybe_convert_i8(key.left) right = self._maybe_convert_i8(key.right) constructor = Interval if scalar else IntervalIndex.from_arrays - return constructor(left, right, closed=self.closed) + # error: "object" not callable + return constructor( + left, right, closed=self.closed + ) # type: ignore[operator] if scalar: # Timestamp/Timedelta diff --git a/pandas/io/formats/style.py b/pandas/io/formats/style.py index 27f9801ea35e3..89e74cf19ee8e 100644 --- a/pandas/io/formats/style.py +++ b/pandas/io/formats/style.py @@ -3810,14 +3810,10 @@ def _highlight_between( Return an array of css props based on condition of data values within given range. """ if np.iterable(left) and not isinstance(left, str): - left = _validate_apply_axis_arg( - left, "left", None, data # type: ignore[arg-type] - ) + left = _validate_apply_axis_arg(left, "left", None, data) if np.iterable(right) and not isinstance(right, str): - right = _validate_apply_axis_arg( - right, "right", None, data # type: ignore[arg-type] - ) + right = _validate_apply_axis_arg(right, "right", None, data) # get ops with correct boundary attribution if inclusive == "both":