|
1 | 1 | import inspect |
2 | 2 | import re |
3 | 3 | from typing import TYPE_CHECKING, Any, List, Optional, Type, Union, cast |
4 | | -import warnings |
5 | 4 |
|
6 | 5 | import numpy as np |
7 | 6 |
|
|
42 | 41 | is_dtype_equal, |
43 | 42 | is_extension_array_dtype, |
44 | 43 | is_float, |
45 | | - is_float_dtype, |
46 | 44 | is_integer, |
47 | | - is_integer_dtype, |
48 | 45 | is_list_like, |
49 | 46 | is_object_dtype, |
50 | 47 | is_re, |
|
54 | 51 | ) |
55 | 52 | from pandas.core.dtypes.dtypes import CategoricalDtype, ExtensionDtype |
56 | 53 | from pandas.core.dtypes.generic import ABCDataFrame, ABCIndex, ABCPandasArray, ABCSeries |
57 | | -from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna, isna_compat |
| 54 | +from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna |
58 | 55 |
|
59 | 56 | import pandas.core.algorithms as algos |
| 57 | +from pandas.core.array_algos.putmask import putmask_inplace, putmask_smart |
60 | 58 | from pandas.core.array_algos.replace import compare_or_regex_search, replace_regex |
61 | 59 | from pandas.core.array_algos.transforms import shift |
62 | 60 | from pandas.core.arrays import ( |
@@ -437,7 +435,7 @@ def fillna( |
437 | 435 |
|
438 | 436 | if self._can_hold_element(value): |
439 | 437 | nb = self if inplace else self.copy() |
440 | | - nb._putmask_simple(mask, value) |
| 438 | + putmask_inplace(nb.values, mask, value) |
441 | 439 | # TODO: should be nb._maybe_downcast? |
442 | 440 | return self._maybe_downcast([nb], downcast) |
443 | 441 |
|
@@ -762,7 +760,7 @@ def replace( |
762 | 760 | ) |
763 | 761 |
|
764 | 762 | blk = self if inplace else self.copy() |
765 | | - blk._putmask_simple(mask, value) |
| 763 | + putmask_inplace(blk.values, mask, value) |
766 | 764 | blocks = blk.convert(numeric=False, copy=not inplace) |
767 | 765 | return blocks |
768 | 766 |
|
@@ -991,35 +989,6 @@ def setitem(self, indexer, value): |
991 | 989 | block = self.make_block(values) |
992 | 990 | return block |
993 | 991 |
|
994 | | - def _putmask_simple(self, mask: np.ndarray, value: Any): |
995 | | - """ |
996 | | - Like putmask but |
997 | | -
|
998 | | - a) we do not cast on failure |
999 | | - b) we do not handle repeating or truncating like numpy. |
1000 | | -
|
1001 | | - Parameters |
1002 | | - ---------- |
1003 | | - mask : np.ndarray[bool] |
1004 | | - We assume _extract_bool_array has already been called. |
1005 | | - value : Any |
1006 | | - We assume self._can_hold_element(value) |
1007 | | - """ |
1008 | | - values = self.values |
1009 | | - |
1010 | | - if lib.is_scalar(value) and isinstance(values, np.ndarray): |
1011 | | - value = convert_scalar_for_putitemlike(value, values.dtype) |
1012 | | - |
1013 | | - if self.is_extension or (self.is_object and not lib.is_scalar(value)): |
1014 | | - # GH#19266 using np.putmask gives unexpected results with listlike value |
1015 | | - if is_list_like(value) and len(value) == len(values): |
1016 | | - values[mask] = value[mask] |
1017 | | - else: |
1018 | | - values[mask] = value |
1019 | | - else: |
1020 | | - # GH#37833 np.putmask is more performant than __setitem__ |
1021 | | - np.putmask(values, mask, value) |
1022 | | - |
1023 | 992 | def putmask(self, mask, new, axis: int = 0) -> List["Block"]: |
1024 | 993 | """ |
1025 | 994 | putmask the data to the block; it is possible that we may create a |
@@ -1121,7 +1090,7 @@ def f(mask, val, idx): |
1121 | 1090 | # we need to explicitly astype here to make a copy |
1122 | 1091 | n = n.astype(dtype) |
1123 | 1092 |
|
1124 | | - nv = _putmask_smart(val, mask, n) |
| 1093 | + nv = putmask_smart(val, mask, n) |
1125 | 1094 | return nv |
1126 | 1095 |
|
1127 | 1096 | new_blocks = self.split_and_operate(mask, f, True) |
@@ -1560,7 +1529,7 @@ def _replace_coerce( |
1560 | 1529 | nb = self.coerce_to_target_dtype(value) |
1561 | 1530 | if nb is self and not inplace: |
1562 | 1531 | nb = nb.copy() |
1563 | | - nb._putmask_simple(mask, value) |
| 1532 | + putmask_inplace(nb.values, mask, value) |
1564 | 1533 | return [nb] |
1565 | 1534 | else: |
1566 | 1535 | regex = _should_use_regex(regex, to_replace) |
@@ -2665,86 +2634,6 @@ def safe_reshape(arr, new_shape: Shape): |
2665 | 2634 | return arr |
2666 | 2635 |
|
2667 | 2636 |
|
2668 | | -def _putmask_smart(v: np.ndarray, mask: np.ndarray, n) -> np.ndarray: |
2669 | | - """ |
2670 | | - Return a new ndarray, try to preserve dtype if possible. |
2671 | | -
|
2672 | | - Parameters |
2673 | | - ---------- |
2674 | | - v : np.ndarray |
2675 | | - `values`, updated in-place. |
2676 | | - mask : np.ndarray[bool] |
2677 | | - Applies to both sides (array like). |
2678 | | - n : `new values` either scalar or an array like aligned with `values` |
2679 | | -
|
2680 | | - Returns |
2681 | | - ------- |
2682 | | - values : ndarray with updated values |
2683 | | - this *may* be a copy of the original |
2684 | | -
|
2685 | | - See Also |
2686 | | - -------- |
2687 | | - ndarray.putmask |
2688 | | - """ |
2689 | | - # we cannot use np.asarray() here as we cannot have conversions |
2690 | | - # that numpy does when numeric are mixed with strings |
2691 | | - |
2692 | | - # n should be the length of the mask or a scalar here |
2693 | | - if not is_list_like(n): |
2694 | | - n = np.repeat(n, len(mask)) |
2695 | | - |
2696 | | - # see if we are only masking values that if putted |
2697 | | - # will work in the current dtype |
2698 | | - try: |
2699 | | - nn = n[mask] |
2700 | | - except TypeError: |
2701 | | - # TypeError: only integer scalar arrays can be converted to a scalar index |
2702 | | - pass |
2703 | | - else: |
2704 | | - # make sure that we have a nullable type |
2705 | | - # if we have nulls |
2706 | | - if not isna_compat(v, nn[0]): |
2707 | | - pass |
2708 | | - elif not (is_float_dtype(nn.dtype) or is_integer_dtype(nn.dtype)): |
2709 | | - # only compare integers/floats |
2710 | | - pass |
2711 | | - elif not (is_float_dtype(v.dtype) or is_integer_dtype(v.dtype)): |
2712 | | - # only compare integers/floats |
2713 | | - pass |
2714 | | - else: |
2715 | | - |
2716 | | - # we ignore ComplexWarning here |
2717 | | - with warnings.catch_warnings(record=True): |
2718 | | - warnings.simplefilter("ignore", np.ComplexWarning) |
2719 | | - nn_at = nn.astype(v.dtype) |
2720 | | - |
2721 | | - comp = nn == nn_at |
2722 | | - if is_list_like(comp) and comp.all(): |
2723 | | - nv = v.copy() |
2724 | | - nv[mask] = nn_at |
2725 | | - return nv |
2726 | | - |
2727 | | - n = np.asarray(n) |
2728 | | - |
2729 | | - def _putmask_preserve(nv, n): |
2730 | | - try: |
2731 | | - nv[mask] = n[mask] |
2732 | | - except (IndexError, ValueError): |
2733 | | - nv[mask] = n |
2734 | | - return nv |
2735 | | - |
2736 | | - # preserves dtype if possible |
2737 | | - if v.dtype.kind == n.dtype.kind: |
2738 | | - return _putmask_preserve(v, n) |
2739 | | - |
2740 | | - # change the dtype if needed |
2741 | | - dtype, _ = maybe_promote(n.dtype) |
2742 | | - |
2743 | | - v = v.astype(dtype) |
2744 | | - |
2745 | | - return _putmask_preserve(v, n) |
2746 | | - |
2747 | | - |
2748 | 2637 | def _extract_bool_array(mask: ArrayLike) -> np.ndarray: |
2749 | 2638 | """ |
2750 | 2639 | If we have a SparseArray or BooleanArray, convert it to ndarray[bool]. |
|
0 commit comments