|
115 | 115 | ) |
116 | 116 |
|
117 | 117 | if TYPE_CHECKING: |
118 | | - from pandas import MultiIndex, RangeIndex, Series |
| 118 | + from pandas import IntervalIndex, MultiIndex, RangeIndex, Series |
119 | 119 | from pandas.core.indexes.datetimelike import DatetimeIndexOpsMixin |
120 | 120 |
|
121 | 121 |
|
@@ -4316,27 +4316,8 @@ def where(self, cond, other=None): |
4316 | 4316 | >>> idx.where(idx.isin(['car', 'train']), 'other') |
4317 | 4317 | Index(['car', 'other', 'train', 'other'], dtype='object') |
4318 | 4318 | """ |
4319 | | - if other is None: |
4320 | | - other = self._na_value |
4321 | | - |
4322 | | - values = self.values |
4323 | | - |
4324 | | - try: |
4325 | | - self._validate_fill_value(other) |
4326 | | - except (ValueError, TypeError): |
4327 | | - return self.astype(object).where(cond, other) |
4328 | | - |
4329 | | - if isinstance(other, np.timedelta64) and self.dtype == object: |
4330 | | - # https://github.com/numpy/numpy/issues/12550 |
4331 | | - # timedelta64 will incorrectly cast to int |
4332 | | - other = [other] * (~cond).sum() |
4333 | | - values = cast(np.ndarray, values).copy() |
4334 | | - # error: Unsupported target for indexed assignment ("ArrayLike") |
4335 | | - values[~cond] = other # type:ignore[index] |
4336 | | - else: |
4337 | | - values = np.where(cond, values, other) |
4338 | | - |
4339 | | - return Index(values, name=self.name) |
| 4319 | + cond = np.asarray(cond, dtype=bool) |
| 4320 | + return self.putmask(~cond, other) |
4340 | 4321 |
|
4341 | 4322 | # construction helpers |
4342 | 4323 | @final |
@@ -4551,17 +4532,32 @@ def putmask(self, mask, value): |
4551 | 4532 | numpy.ndarray.putmask : Changes elements of an array |
4552 | 4533 | based on conditional and input values. |
4553 | 4534 | """ |
4554 | | - values = self._values.copy() |
| 4535 | + mask = np.asarray(mask, dtype=bool) |
| 4536 | + if mask.shape != self.shape: |
| 4537 | + raise ValueError("putmask: mask and data must be the same size") |
| 4538 | + if not mask.any(): |
| 4539 | + return self.copy() |
| 4540 | + |
| 4541 | + if value is None: |
| 4542 | + value = self._na_value |
4555 | 4543 | try: |
4556 | 4544 | converted = self._validate_fill_value(value) |
4557 | 4545 | except (ValueError, TypeError) as err: |
4558 | 4546 | if is_object_dtype(self): |
4559 | 4547 | raise err |
4560 | 4548 |
|
4561 | | - # coerces to object |
4562 | | - return self.astype(object).putmask(mask, value) |
| 4549 | + dtype = self._find_common_type_compat(value) |
| 4550 | + return self.astype(dtype).putmask(mask, value) |
| 4551 | + |
| 4552 | + values = self._values.copy() |
| 4553 | + if isinstance(converted, np.timedelta64) and self.dtype == object: |
| 4554 | + # https://github.com/numpy/numpy/issues/12550 |
| 4555 | + # timedelta64 will incorrectly cast to int |
| 4556 | + converted = [converted] * mask.sum() |
| 4557 | + values[mask] = converted |
| 4558 | + else: |
| 4559 | + np.putmask(values, mask, converted) |
4563 | 4560 |
|
4564 | | - np.putmask(values, mask, converted) |
4565 | 4561 | return type(self)._simple_new(values, name=self.name) |
4566 | 4562 |
|
4567 | 4563 | def equals(self, other: Any) -> bool: |
@@ -5198,18 +5194,31 @@ def _maybe_promote(self, other: Index): |
5198 | 5194 |
|
5199 | 5195 | return self, other |
5200 | 5196 |
|
5201 | | - def _find_common_type_compat(self, target: Index) -> DtypeObj: |
| 5197 | + @final |
| 5198 | + def _find_common_type_compat(self, target) -> DtypeObj: |
5202 | 5199 | """ |
5203 | 5200 | Implementation of find_common_type that adjusts for Index-specific |
5204 | 5201 | special cases. |
5205 | 5202 | """ |
5206 | | - dtype = find_common_type([self.dtype, target.dtype]) |
| 5203 | + if is_interval_dtype(self.dtype) and is_valid_nat_for_dtype(target, self.dtype): |
| 5204 | + # e.g. setting NA value into IntervalArray[int64] |
| 5205 | + self = cast("IntervalIndex", self) |
| 5206 | + return IntervalDtype(np.float64, closed=self.closed) |
| 5207 | + |
| 5208 | + target_dtype, _ = infer_dtype_from(target, pandas_dtype=True) |
| 5209 | + dtype = find_common_type([self.dtype, target_dtype]) |
5207 | 5210 | if dtype.kind in ["i", "u"]: |
5208 | 5211 | # TODO: what about reversed with self being categorical? |
5209 | | - if is_categorical_dtype(target.dtype) and target.hasnans: |
| 5212 | + if ( |
| 5213 | + isinstance(target, Index) |
| 5214 | + and is_categorical_dtype(target.dtype) |
| 5215 | + and target.hasnans |
| 5216 | + ): |
5210 | 5217 | # FIXME: find_common_type incorrect with Categorical GH#38240 |
5211 | 5218 | # FIXME: some cases where float64 cast can be lossy? |
5212 | 5219 | dtype = np.dtype(np.float64) |
| 5220 | + if dtype.kind == "c": |
| 5221 | + dtype = np.dtype(object) |
5213 | 5222 | return dtype |
5214 | 5223 |
|
5215 | 5224 | @final |
|
0 commit comments