Skip to content

Commit d00b12c

Browse files
committed
refactor: Move is_close to CompliantColumn
Now that #2962 has merged, this part of the plan is possible (#2962 (comment))
1 parent 0719af4 commit d00b12c

File tree

3 files changed

+60
-90
lines changed

3 files changed

+60
-90
lines changed

narwhals/_compliant/column.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,66 @@ def is_between(
103103
return (self > lower_bound) & (self < upper_bound)
104104
return (self >= lower_bound) & (self <= upper_bound)
105105

106+
def is_close(
107+
self,
108+
other: Self | NumericLiteral,
109+
*,
110+
abs_tol: float,
111+
rel_tol: float,
112+
nans_equal: bool,
113+
) -> Self:
114+
from decimal import Decimal
115+
116+
other_abs: Self | NumericLiteral
117+
other_is_nan: Self | bool
118+
other_is_inf: Self | bool
119+
other_is_not_inf: Self | bool
120+
121+
if isinstance(other, (float, int, Decimal)):
122+
from math import isinf, isnan
123+
124+
# NOTE: See https://discuss.python.org/t/inferred-type-of-function-that-calls-dunder-abs-abs/101447
125+
other_abs = other.__abs__()
126+
other_is_nan = isnan(other)
127+
other_is_inf = isinf(other)
128+
129+
# Define the other_is_not_inf variable to prevent triggering the following warning:
130+
# > DeprecationWarning: Bitwise inversion '~' on bool is deprecated and will be
131+
# > removed in Python 3.16.
132+
other_is_not_inf = not other_is_inf
133+
134+
else:
135+
other_abs, other_is_nan = other.abs(), other.is_nan()
136+
other_is_not_inf = other.is_finite() | other_is_nan
137+
other_is_inf = ~other_is_not_inf
138+
139+
rel_threshold = self.abs().clip(lower_bound=other_abs, upper_bound=None) * rel_tol
140+
tolerance = rel_threshold.clip(lower_bound=abs_tol, upper_bound=None)
141+
142+
self_is_nan = self.is_nan()
143+
self_is_not_inf = self.is_finite() | self_is_nan
144+
145+
# Values are close if abs_diff <= tolerance, and both finite
146+
is_close = (
147+
((self - other).abs() <= tolerance) & self_is_not_inf & other_is_not_inf
148+
)
149+
150+
# Handle infinity cases: infinities are close/equal if they have the same sign
151+
self_sign, other_sign = self > 0, other > 0
152+
is_same_inf = (~self_is_not_inf) & other_is_inf & (self_sign == other_sign)
153+
154+
# Handle nan cases:
155+
# * If any value is NaN, then False (via `& ~either_nan`)
156+
# * However, if `nans_equals = True` and if _both_ values are NaN, then True
157+
either_nan = self_is_nan | other_is_nan
158+
result = (is_close | is_same_inf) & ~either_nan
159+
160+
if nans_equal:
161+
both_nan = self_is_nan & other_is_nan
162+
result = result | both_nan
163+
164+
return result
165+
106166
def is_duplicated(self) -> Self:
107167
return ~self.is_unique()
108168

narwhals/_compliant/series.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
NativeSeriesT,
1818
NativeSeriesT_co,
1919
)
20-
from narwhals._compliant.utils import IsClose
2120
from narwhals._translate import FromIterable, FromNative, NumpyConvertible, ToNarwhals
2221
from narwhals._typing_compat import TypeVar, assert_never
2322
from narwhals._utils import (
@@ -74,7 +73,6 @@ class HistData(TypedDict, Generic[NativeSeriesT, "_CountsT_co"]):
7473

7574

7675
class CompliantSeries(
77-
IsClose,
7876
NumpyConvertible["_1DArray", "Into1DArray"],
7977
FromIterable,
8078
FromNative[NativeSeriesT],

narwhals/_compliant/utils.py

Lines changed: 0 additions & 88 deletions
This file was deleted.

0 commit comments

Comments
 (0)