|
13 | 13 | ) |
14 | 14 | from narwhals._utils import _validate_rolling_arguments, ensure_type, flatten |
15 | 15 | from narwhals.dtypes import _validate_dtype |
16 | | -from narwhals.exceptions import InvalidOperationError |
| 16 | +from narwhals.exceptions import ComputeError, InvalidOperationError |
17 | 17 | from narwhals.expr_cat import ExprCatNamespace |
18 | 18 | from narwhals.expr_dt import ExprDateTimeNamespace |
19 | 19 | from narwhals.expr_list import ExprListNamespace |
@@ -2254,6 +2254,97 @@ def sqrt(self) -> Self: |
2254 | 2254 | """ |
2255 | 2255 | return self._with_elementwise(lambda plx: self._to_compliant_expr(plx).sqrt()) |
2256 | 2256 |
|
| 2257 | + def is_close( |
| 2258 | + self, |
| 2259 | + other: Self | NumericLiteral, |
| 2260 | + *, |
| 2261 | + abs_tol: float = 0.0, |
| 2262 | + rel_tol: float = 1e-09, |
| 2263 | + nans_equal: bool = False, |
| 2264 | + ) -> Self: |
| 2265 | + r"""Check if this expression is close, i.e. almost equal, to the other expression. |
| 2266 | +
|
| 2267 | + Two values `a` and `b` are considered close if the following condition holds: |
| 2268 | +
|
| 2269 | + $$ |
| 2270 | + |a-b| \le max \{ \text{rel\_tol} \cdot max \{ |a|, |b| \}, \text{abs\_tol} \} |
| 2271 | + $$ |
| 2272 | +
|
| 2273 | + Arguments: |
| 2274 | + other: Values to compare with. |
| 2275 | + abs_tol: Absolute tolerance. This is the maximum allowed absolute difference |
| 2276 | + between two values. Must be non-negative. |
| 2277 | + rel_tol: Relative tolerance. This is the maximum allowed difference between |
| 2278 | + two values, relative to the larger absolute value. Must be in the range |
| 2279 | + [0, 1). |
| 2280 | + nans_equal: Whether NaN values should be considered equal. |
| 2281 | +
|
| 2282 | + Returns: |
| 2283 | + Expression of Boolean data type. |
| 2284 | +
|
| 2285 | + Notes: |
| 2286 | + The implementation of this method is symmetric and mirrors the behavior of |
| 2287 | + `math.isclose`. Specifically note that this behavior is different to |
| 2288 | + `numpy.isclose`. |
| 2289 | +
|
| 2290 | + Examples: |
| 2291 | + >>> import duckdb |
| 2292 | + >>> import pyarrow as pa |
| 2293 | + >>> import narwhals as nw |
| 2294 | + >>> |
| 2295 | + >>> data = { |
| 2296 | + ... "x": [1.0, float("inf"), 1.41, None, float("nan")], |
| 2297 | + ... "y": [1.2, float("inf"), 1.40, None, float("nan")], |
| 2298 | + ... } |
| 2299 | + >>> _table = pa.table(data) |
| 2300 | + >>> df_native = duckdb.table("_table") |
| 2301 | + >>> df = nw.from_native(df_native) |
| 2302 | + >>> df.with_columns( |
| 2303 | + ... is_close=nw.col("x").is_close( |
| 2304 | + ... nw.col("y"), abs_tol=0.1, nans_equal=True |
| 2305 | + ... ) |
| 2306 | + ... ) |
| 2307 | + ┌──────────────────────────────┐ |
| 2308 | + | Narwhals LazyFrame | |
| 2309 | + |------------------------------| |
| 2310 | + |┌────────┬────────┬──────────┐| |
| 2311 | + |│ x │ y │ is_close │| |
| 2312 | + |│ double │ double │ boolean │| |
| 2313 | + |├────────┼────────┼──────────┤| |
| 2314 | + |│ 1.0 │ 1.2 │ false │| |
| 2315 | + |│ inf │ inf │ true │| |
| 2316 | + |│ 1.41 │ 1.4 │ true │| |
| 2317 | + |│ NULL │ NULL │ NULL │| |
| 2318 | + |│ nan │ nan │ true │| |
| 2319 | + |└────────┴────────┴──────────┘| |
| 2320 | + └──────────────────────────────┘ |
| 2321 | + """ |
| 2322 | + if abs_tol < 0: |
| 2323 | + msg = f"`abs_tol` must be non-negative but got {abs_tol}" |
| 2324 | + raise ComputeError(msg) |
| 2325 | + |
| 2326 | + if not (0 <= rel_tol < 1): |
| 2327 | + msg = f"`rel_tol` must be in the range [0, 1) but got {rel_tol}" |
| 2328 | + raise ComputeError(msg) |
| 2329 | + |
| 2330 | + kwargs = {"abs_tol": abs_tol, "rel_tol": rel_tol, "nans_equal": nans_equal} |
| 2331 | + return self.__class__( |
| 2332 | + lambda plx: apply_n_ary_operation( |
| 2333 | + plx, |
| 2334 | + lambda *exprs: exprs[0].is_close(exprs[1], **kwargs), |
| 2335 | + self, |
| 2336 | + other, |
| 2337 | + str_as_lit=False, |
| 2338 | + ), |
| 2339 | + combine_metadata( |
| 2340 | + self, |
| 2341 | + other, |
| 2342 | + str_as_lit=False, |
| 2343 | + allow_multi_output=False, |
| 2344 | + to_single_output=False, |
| 2345 | + ), |
| 2346 | + ) |
| 2347 | + |
2257 | 2348 | @property |
2258 | 2349 | def str(self) -> ExprStringNamespace[Self]: |
2259 | 2350 | return ExprStringNamespace(self) |
|
0 commit comments