|
5 | 5 | """
|
6 | 6 | import datetime
|
7 | 7 | import operator
|
8 |
| -from typing import Any, Callable, Tuple, Union |
| 8 | +from typing import Tuple |
9 | 9 |
|
10 | 10 | import numpy as np
|
11 | 11 |
|
12 | 12 | from pandas._libs import Timedelta, Timestamp, lib
|
13 |
| -from pandas.errors import NullFrequencyError |
14 | 13 | from pandas.util._decorators import Appender
|
15 | 14 |
|
16 |
| -from pandas.core.dtypes.common import ( |
17 |
| - is_datetime64_dtype, |
18 |
| - is_extension_array_dtype, |
19 |
| - is_integer_dtype, |
20 |
| - is_list_like, |
21 |
| - is_object_dtype, |
22 |
| - is_scalar, |
23 |
| - is_timedelta64_dtype, |
24 |
| -) |
25 |
| -from pandas.core.dtypes.generic import ( |
26 |
| - ABCDataFrame, |
27 |
| - ABCExtensionArray, |
28 |
| - ABCIndexClass, |
29 |
| - ABCSeries, |
30 |
| -) |
| 15 | +from pandas.core.dtypes.common import is_list_like, is_timedelta64_dtype |
| 16 | +from pandas.core.dtypes.generic import ABCDataFrame, ABCIndexClass, ABCSeries |
31 | 17 | from pandas.core.dtypes.missing import isna
|
32 | 18 |
|
33 |
| -from pandas._typing import ArrayLike |
34 |
| -from pandas.core.construction import array, extract_array |
| 19 | +from pandas.core.construction import extract_array |
35 | 20 | from pandas.core.ops.array_ops import (
|
36 | 21 | arithmetic_op,
|
37 | 22 | comparison_op,
|
38 | 23 | define_na_arithmetic_op,
|
39 | 24 | logical_op,
|
40 | 25 | )
|
41 | 26 | from pandas.core.ops.array_ops import comp_method_OBJECT_ARRAY # noqa:F401
|
| 27 | +from pandas.core.ops.dispatch import maybe_dispatch_ufunc_to_dunder_op # noqa:F401 |
| 28 | +from pandas.core.ops.dispatch import should_series_dispatch |
42 | 29 | from pandas.core.ops.docstrings import (
|
43 | 30 | _arith_doc_FRAME,
|
44 | 31 | _flex_comp_doc_FRAME,
|
@@ -358,71 +345,6 @@ def fill_binop(left, right, fill_value):
|
358 | 345 | # Dispatch logic
|
359 | 346 |
|
360 | 347 |
|
361 |
| -def should_extension_dispatch(left: ABCSeries, right: Any) -> bool: |
362 |
| - """ |
363 |
| - Identify cases where Series operation should use dispatch_to_extension_op. |
364 |
| -
|
365 |
| - Parameters |
366 |
| - ---------- |
367 |
| - left : Series |
368 |
| - right : object |
369 |
| -
|
370 |
| - Returns |
371 |
| - ------- |
372 |
| - bool |
373 |
| - """ |
374 |
| - if ( |
375 |
| - is_extension_array_dtype(left.dtype) |
376 |
| - or is_datetime64_dtype(left.dtype) |
377 |
| - or is_timedelta64_dtype(left.dtype) |
378 |
| - ): |
379 |
| - return True |
380 |
| - |
381 |
| - if not is_scalar(right) and is_extension_array_dtype(right): |
382 |
| - # GH#22378 disallow scalar to exclude e.g. "category", "Int64" |
383 |
| - return True |
384 |
| - |
385 |
| - return False |
386 |
| - |
387 |
| - |
388 |
| -def should_series_dispatch(left, right, op): |
389 |
| - """ |
390 |
| - Identify cases where a DataFrame operation should dispatch to its |
391 |
| - Series counterpart. |
392 |
| -
|
393 |
| - Parameters |
394 |
| - ---------- |
395 |
| - left : DataFrame |
396 |
| - right : DataFrame |
397 |
| - op : binary operator |
398 |
| -
|
399 |
| - Returns |
400 |
| - ------- |
401 |
| - override : bool |
402 |
| - """ |
403 |
| - if left._is_mixed_type or right._is_mixed_type: |
404 |
| - return True |
405 |
| - |
406 |
| - if not len(left.columns) or not len(right.columns): |
407 |
| - # ensure obj.dtypes[0] exists for each obj |
408 |
| - return False |
409 |
| - |
410 |
| - ldtype = left.dtypes.iloc[0] |
411 |
| - rdtype = right.dtypes.iloc[0] |
412 |
| - |
413 |
| - if (is_timedelta64_dtype(ldtype) and is_integer_dtype(rdtype)) or ( |
414 |
| - is_timedelta64_dtype(rdtype) and is_integer_dtype(ldtype) |
415 |
| - ): |
416 |
| - # numpy integer dtypes as timedelta64 dtypes in this scenario |
417 |
| - return True |
418 |
| - |
419 |
| - if is_datetime64_dtype(ldtype) and is_object_dtype(rdtype): |
420 |
| - # in particular case where right is an array of DateOffsets |
421 |
| - return True |
422 |
| - |
423 |
| - return False |
424 |
| - |
425 |
| - |
426 | 348 | def dispatch_to_series(left, right, func, str_rep=None, axis=None):
|
427 | 349 | """
|
428 | 350 | Evaluate the frame operation func(left, right) by evaluating
|
@@ -489,58 +411,6 @@ def column_op(a, b):
|
489 | 411 | return new_data
|
490 | 412 |
|
491 | 413 |
|
492 |
| -def dispatch_to_extension_op( |
493 |
| - op, |
494 |
| - left: Union[ABCExtensionArray, np.ndarray], |
495 |
| - right: Any, |
496 |
| - keep_null_freq: bool = False, |
497 |
| -): |
498 |
| - """ |
499 |
| - Assume that left or right is a Series backed by an ExtensionArray, |
500 |
| - apply the operator defined by op. |
501 |
| -
|
502 |
| - Parameters |
503 |
| - ---------- |
504 |
| - op : binary operator |
505 |
| - left : ExtensionArray or np.ndarray |
506 |
| - right : object |
507 |
| - keep_null_freq : bool, default False |
508 |
| - Whether to re-raise a NullFrequencyError unchanged, as opposed to |
509 |
| - catching and raising TypeError. |
510 |
| -
|
511 |
| - Returns |
512 |
| - ------- |
513 |
| - ExtensionArray or np.ndarray |
514 |
| - 2-tuple of these if op is divmod or rdivmod |
515 |
| - """ |
516 |
| - # NB: left and right should already be unboxed, so neither should be |
517 |
| - # a Series or Index. |
518 |
| - |
519 |
| - if left.dtype.kind in "mM" and isinstance(left, np.ndarray): |
520 |
| - # We need to cast datetime64 and timedelta64 ndarrays to |
521 |
| - # DatetimeArray/TimedeltaArray. But we avoid wrapping others in |
522 |
| - # PandasArray as that behaves poorly with e.g. IntegerArray. |
523 |
| - left = array(left) |
524 |
| - |
525 |
| - # The op calls will raise TypeError if the op is not defined |
526 |
| - # on the ExtensionArray |
527 |
| - |
528 |
| - try: |
529 |
| - res_values = op(left, right) |
530 |
| - except NullFrequencyError: |
531 |
| - # DatetimeIndex and TimedeltaIndex with freq == None raise ValueError |
532 |
| - # on add/sub of integers (or int-like). We re-raise as a TypeError. |
533 |
| - if keep_null_freq: |
534 |
| - # TODO: remove keep_null_freq after Timestamp+int deprecation |
535 |
| - # GH#22535 is enforced |
536 |
| - raise |
537 |
| - raise TypeError( |
538 |
| - "incompatible type for a datetime/timedelta " |
539 |
| - "operation [{name}]".format(name=op.__name__) |
540 |
| - ) |
541 |
| - return res_values |
542 |
| - |
543 |
| - |
544 | 414 | # -----------------------------------------------------------------------------
|
545 | 415 | # Series
|
546 | 416 |
|
@@ -906,92 +776,3 @@ def f(self, other):
|
906 | 776 | f.__name__ = op_name
|
907 | 777 |
|
908 | 778 | return f
|
909 |
| - |
910 |
| - |
911 |
| -# ----------------------------------------------------------------------------- |
912 |
| -# Sparse |
913 |
| - |
914 |
| - |
915 |
| -def maybe_dispatch_ufunc_to_dunder_op( |
916 |
| - self: ArrayLike, ufunc: Callable, method: str, *inputs: ArrayLike, **kwargs: Any |
917 |
| -): |
918 |
| - """ |
919 |
| - Dispatch a ufunc to the equivalent dunder method. |
920 |
| -
|
921 |
| - Parameters |
922 |
| - ---------- |
923 |
| - self : ArrayLike |
924 |
| - The array whose dunder method we dispatch to |
925 |
| - ufunc : Callable |
926 |
| - A NumPy ufunc |
927 |
| - method : {'reduce', 'accumulate', 'reduceat', 'outer', 'at', '__call__'} |
928 |
| - inputs : ArrayLike |
929 |
| - The input arrays. |
930 |
| - kwargs : Any |
931 |
| - The additional keyword arguments, e.g. ``out``. |
932 |
| -
|
933 |
| - Returns |
934 |
| - ------- |
935 |
| - result : Any |
936 |
| - The result of applying the ufunc |
937 |
| - """ |
938 |
| - # special has the ufuncs we dispatch to the dunder op on |
939 |
| - special = { |
940 |
| - "add", |
941 |
| - "sub", |
942 |
| - "mul", |
943 |
| - "pow", |
944 |
| - "mod", |
945 |
| - "floordiv", |
946 |
| - "truediv", |
947 |
| - "divmod", |
948 |
| - "eq", |
949 |
| - "ne", |
950 |
| - "lt", |
951 |
| - "gt", |
952 |
| - "le", |
953 |
| - "ge", |
954 |
| - "remainder", |
955 |
| - "matmul", |
956 |
| - } |
957 |
| - aliases = { |
958 |
| - "subtract": "sub", |
959 |
| - "multiply": "mul", |
960 |
| - "floor_divide": "floordiv", |
961 |
| - "true_divide": "truediv", |
962 |
| - "power": "pow", |
963 |
| - "remainder": "mod", |
964 |
| - "divide": "div", |
965 |
| - "equal": "eq", |
966 |
| - "not_equal": "ne", |
967 |
| - "less": "lt", |
968 |
| - "less_equal": "le", |
969 |
| - "greater": "gt", |
970 |
| - "greater_equal": "ge", |
971 |
| - } |
972 |
| - |
973 |
| - # For op(., Array) -> Array.__r{op}__ |
974 |
| - flipped = { |
975 |
| - "lt": "__gt__", |
976 |
| - "le": "__ge__", |
977 |
| - "gt": "__lt__", |
978 |
| - "ge": "__le__", |
979 |
| - "eq": "__eq__", |
980 |
| - "ne": "__ne__", |
981 |
| - } |
982 |
| - |
983 |
| - op_name = ufunc.__name__ |
984 |
| - op_name = aliases.get(op_name, op_name) |
985 |
| - |
986 |
| - def not_implemented(*args, **kwargs): |
987 |
| - return NotImplemented |
988 |
| - |
989 |
| - if method == "__call__" and op_name in special and kwargs.get("out") is None: |
990 |
| - if isinstance(inputs[0], type(self)): |
991 |
| - name = "__{}__".format(op_name) |
992 |
| - return getattr(self, name, not_implemented)(inputs[1]) |
993 |
| - else: |
994 |
| - name = flipped.get(op_name, "__r{}__".format(op_name)) |
995 |
| - return getattr(self, name, not_implemented)(inputs[0]) |
996 |
| - else: |
997 |
| - return NotImplemented |
0 commit comments