|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import operator |
3 | 4 | import warnings |
4 | | -from typing import TYPE_CHECKING, Any, Literal, cast |
| 5 | +from typing import TYPE_CHECKING, Any, Callable, Literal, cast |
5 | 6 |
|
6 | 7 | import numpy as np |
7 | 8 |
|
@@ -304,6 +305,11 @@ def _scatter_in_place(self, indices: Self, values: Self) -> None: |
304 | 305 | self.native.iloc[indices.native] = values_native |
305 | 306 |
|
306 | 307 | def cast(self, dtype: IntoDType) -> Self: |
| 308 | + if self.dtype == dtype and self.native.dtype != "object": |
| 309 | + # Avoid dealing with pandas' type-system if we can. Note that it's only |
| 310 | + # safe to do this if we're not starting with object dtype, see tests/expr_and_series/cast_test.py::test_cast_object_pandas |
| 311 | + # for an example of why. |
| 312 | + return self._with_native(self.native, preserve_broadcast=True) |
307 | 313 | pd_dtype = narwhals_to_native_dtype( |
308 | 314 | dtype, |
309 | 315 | dtype_backend=get_dtype_backend(self.native.dtype, self._implementation), |
@@ -387,103 +393,87 @@ def first(self) -> PythonLiteral: |
387 | 393 | def last(self) -> PythonLiteral: |
388 | 394 | return self.native.iloc[-1] if len(self.native) else None |
389 | 395 |
|
| 396 | + def _with_binary(self, op: Callable[..., PandasLikeSeries], other: Any) -> Self: |
| 397 | + ser, other_native = align_and_extract_native(self, other) |
| 398 | + preserve_broadcast = self._broadcast and getattr(other, "_broadcast", True) |
| 399 | + return self._with_native( |
| 400 | + op(ser, other_native), preserve_broadcast=preserve_broadcast |
| 401 | + ).alias(self.name) |
| 402 | + |
| 403 | + def _with_binary_right(self, op: Callable[..., PandasLikeSeries], other: Any) -> Self: |
| 404 | + return self._with_binary(lambda x, y: op(y, x), other).alias(self.name) |
| 405 | + |
390 | 406 | def __eq__(self, other: object) -> Self: # type: ignore[override] |
391 | | - ser, other = align_and_extract_native(self, other) |
392 | | - return self._with_native(ser == other).alias(self.name) |
| 407 | + return self._with_binary(operator.eq, other) |
393 | 408 |
|
394 | 409 | def __ne__(self, other: object) -> Self: # type: ignore[override] |
395 | | - ser, other = align_and_extract_native(self, other) |
396 | | - return self._with_native(ser != other).alias(self.name) |
| 410 | + return self._with_binary(operator.ne, other) |
397 | 411 |
|
398 | 412 | def __ge__(self, other: Any) -> Self: |
399 | | - ser, other = align_and_extract_native(self, other) |
400 | | - return self._with_native(ser >= other).alias(self.name) |
| 413 | + return self._with_binary(operator.ge, other) |
401 | 414 |
|
402 | 415 | def __gt__(self, other: Any) -> Self: |
403 | | - ser, other = align_and_extract_native(self, other) |
404 | | - return self._with_native(ser > other).alias(self.name) |
| 416 | + return self._with_binary(operator.gt, other) |
405 | 417 |
|
406 | 418 | def __le__(self, other: Any) -> Self: |
407 | | - ser, other = align_and_extract_native(self, other) |
408 | | - return self._with_native(ser <= other).alias(self.name) |
| 419 | + return self._with_binary(operator.le, other) |
409 | 420 |
|
410 | 421 | def __lt__(self, other: Any) -> Self: |
411 | | - ser, other = align_and_extract_native(self, other) |
412 | | - return self._with_native(ser < other).alias(self.name) |
| 422 | + return self._with_binary(operator.lt, other) |
413 | 423 |
|
414 | 424 | def __and__(self, other: Any) -> Self: |
415 | | - ser, other = align_and_extract_native(self, other) |
416 | | - return self._with_native(ser & other).alias(self.name) |
| 425 | + return self._with_binary(operator.and_, other) |
417 | 426 |
|
418 | 427 | def __rand__(self, other: Any) -> Self: |
419 | | - ser, other = align_and_extract_native(self, other) |
420 | | - ser = cast("pd.Series[Any]", ser) |
421 | | - return self._with_native(ser.__and__(other)).alias(self.name) |
| 428 | + return self._with_binary_right(operator.and_, other) |
422 | 429 |
|
423 | 430 | def __or__(self, other: Any) -> Self: |
424 | | - ser, other = align_and_extract_native(self, other) |
425 | | - return self._with_native(ser | other).alias(self.name) |
| 431 | + return self._with_binary(operator.or_, other) |
426 | 432 |
|
427 | 433 | def __ror__(self, other: Any) -> Self: |
428 | | - ser, other = align_and_extract_native(self, other) |
429 | | - ser = cast("pd.Series[Any]", ser) |
430 | | - return self._with_native(ser.__or__(other)).alias(self.name) |
| 434 | + return self._with_binary_right(operator.or_, other) |
431 | 435 |
|
432 | 436 | def __add__(self, other: Any) -> Self: |
433 | | - ser, other = align_and_extract_native(self, other) |
434 | | - return self._with_native(ser + other).alias(self.name) |
| 437 | + return self._with_binary(operator.add, other) |
435 | 438 |
|
436 | 439 | def __radd__(self, other: Any) -> Self: |
437 | | - _, other_native = align_and_extract_native(self, other) |
438 | | - return self._with_native(self.native.__radd__(other_native)).alias(self.name) |
| 440 | + return self._with_binary_right(operator.add, other) |
439 | 441 |
|
440 | 442 | def __sub__(self, other: Any) -> Self: |
441 | | - ser, other = align_and_extract_native(self, other) |
442 | | - return self._with_native(ser - other).alias(self.name) |
| 443 | + return self._with_binary(operator.sub, other) |
443 | 444 |
|
444 | 445 | def __rsub__(self, other: Any) -> Self: |
445 | | - _, other_native = align_and_extract_native(self, other) |
446 | | - return self._with_native(self.native.__rsub__(other_native)).alias(self.name) |
| 446 | + return self._with_binary_right(operator.sub, other) |
447 | 447 |
|
448 | 448 | def __mul__(self, other: Any) -> Self: |
449 | | - ser, other = align_and_extract_native(self, other) |
450 | | - return self._with_native(ser * other).alias(self.name) |
| 449 | + return self._with_binary(operator.mul, other) |
451 | 450 |
|
452 | 451 | def __rmul__(self, other: Any) -> Self: |
453 | | - _, other_native = align_and_extract_native(self, other) |
454 | | - return self._with_native(self.native.__rmul__(other_native)).alias(self.name) |
| 452 | + return self._with_binary_right(operator.mul, other) |
455 | 453 |
|
456 | 454 | def __truediv__(self, other: Any) -> Self: |
457 | | - ser, other = align_and_extract_native(self, other) |
458 | | - return self._with_native(ser / other).alias(self.name) |
| 455 | + return self._with_binary(operator.truediv, other) |
459 | 456 |
|
460 | 457 | def __rtruediv__(self, other: Any) -> Self: |
461 | | - _, other_native = align_and_extract_native(self, other) |
462 | | - return self._with_native(self.native.__rtruediv__(other_native)).alias(self.name) |
| 458 | + return self._with_binary_right(operator.truediv, other) |
463 | 459 |
|
464 | 460 | def __floordiv__(self, other: Any) -> Self: |
465 | | - ser, other = align_and_extract_native(self, other) |
466 | | - return self._with_native(ser // other).alias(self.name) |
| 461 | + return self._with_binary(operator.floordiv, other) |
467 | 462 |
|
468 | 463 | def __rfloordiv__(self, other: Any) -> Self: |
469 | | - _, other_native = align_and_extract_native(self, other) |
470 | | - return self._with_native(self.native.__rfloordiv__(other_native)).alias(self.name) |
| 464 | + return self._with_binary_right(operator.floordiv, other) |
471 | 465 |
|
472 | 466 | def __pow__(self, other: Any) -> Self: |
473 | | - ser, other = align_and_extract_native(self, other) |
474 | | - return self._with_native(ser**other).alias(self.name) |
| 467 | + return self._with_binary(operator.pow, other) |
475 | 468 |
|
476 | 469 | def __rpow__(self, other: Any) -> Self: |
477 | | - _, other_native = align_and_extract_native(self, other) |
478 | | - return self._with_native(self.native.__rpow__(other_native)).alias(self.name) |
| 470 | + return self._with_binary_right(operator.pow, other) |
479 | 471 |
|
480 | 472 | def __mod__(self, other: Any) -> Self: |
481 | | - ser, other = align_and_extract_native(self, other) |
482 | | - return self._with_native(ser % other).alias(self.name) |
| 473 | + return self._with_binary(operator.mod, other) |
483 | 474 |
|
484 | 475 | def __rmod__(self, other: Any) -> Self: |
485 | | - _, other_native = align_and_extract_native(self, other) |
486 | | - return self._with_native(self.native.__rmod__(other_native)).alias(self.name) |
| 476 | + return self._with_binary_right(operator.mod, other) |
487 | 477 |
|
488 | 478 | # Unary |
489 | 479 |
|
|
0 commit comments