|
41 | 41 | is_null_datelike_scalar) |
42 | 42 | import pandas.types.concat as _concat |
43 | 43 |
|
44 | | -from pandas.types.generic import ABCSeries |
| 44 | +from pandas.types.generic import ABCSeries, ABCDatetimeIndex |
45 | 45 | from pandas.core.common import is_null_slice |
46 | 46 | import pandas.core.algorithms as algos |
47 | 47 |
|
@@ -379,7 +379,8 @@ def fillna(self, value, limit=None, inplace=False, downcast=None, |
379 | 379 |
|
380 | 380 | # fillna, but if we cannot coerce, then try again as an ObjectBlock |
381 | 381 | try: |
382 | | - values, _, value, _ = self._try_coerce_args(self.values, value) |
| 382 | + values, _, _, _ = self._try_coerce_args(self.values, value) |
| 383 | + # value may be converted to internal, thus drop |
383 | 384 | blocks = self.putmask(mask, value, inplace=inplace) |
384 | 385 | blocks = [b.make_block(values=self._try_coerce_result(b.values)) |
385 | 386 | for b in blocks] |
@@ -673,8 +674,43 @@ def setitem(self, indexer, value, mgr=None): |
673 | 674 | if self.is_numeric: |
674 | 675 | value = np.nan |
675 | 676 |
|
676 | | - # coerce args |
677 | | - values, _, value, _ = self._try_coerce_args(self.values, value) |
| 677 | + # coerce if block dtype can store value |
| 678 | + values = self.values |
| 679 | + try: |
| 680 | + values, _, value, _ = self._try_coerce_args(values, value) |
| 681 | + # can keep its own dtype |
| 682 | + if hasattr(value, 'dtype') and is_dtype_equal(values.dtype, |
| 683 | + value.dtype): |
| 684 | + dtype = self.dtype |
| 685 | + else: |
| 686 | + dtype = 'infer' |
| 687 | + |
| 688 | + except (TypeError, ValueError): |
| 689 | + # current dtype cannot store value, coerce to common dtype |
| 690 | + find_dtype = False |
| 691 | + |
| 692 | + if hasattr(value, 'dtype'): |
| 693 | + dtype = value.dtype |
| 694 | + find_dtype = True |
| 695 | + |
| 696 | + elif is_scalar(value): |
| 697 | + if isnull(value): |
| 698 | + # NaN promotion is handled in latter path |
| 699 | + dtype = False |
| 700 | + else: |
| 701 | + dtype, _ = _infer_dtype_from_scalar(value, |
| 702 | + pandas_dtype=True) |
| 703 | + find_dtype = True |
| 704 | + else: |
| 705 | + dtype = 'infer' |
| 706 | + |
| 707 | + if find_dtype: |
| 708 | + dtype = _find_common_type([values.dtype, dtype]) |
| 709 | + if not is_dtype_equal(self.dtype, dtype): |
| 710 | + b = self.astype(dtype) |
| 711 | + return b.setitem(indexer, value, mgr=mgr) |
| 712 | + |
| 713 | + # value must be storeable at this moment |
678 | 714 | arr_value = np.array(value) |
679 | 715 |
|
680 | 716 | # cast the values to a type that can hold nan (if necessary) |
@@ -704,87 +740,52 @@ def setitem(self, indexer, value, mgr=None): |
704 | 740 | raise ValueError("cannot set using a slice indexer with a " |
705 | 741 | "different length than the value") |
706 | 742 |
|
707 | | - try: |
708 | | - |
709 | | - def _is_scalar_indexer(indexer): |
710 | | - # return True if we are all scalar indexers |
711 | | - |
712 | | - if arr_value.ndim == 1: |
713 | | - if not isinstance(indexer, tuple): |
714 | | - indexer = tuple([indexer]) |
715 | | - return all([is_scalar(idx) for idx in indexer]) |
716 | | - return False |
717 | | - |
718 | | - def _is_empty_indexer(indexer): |
719 | | - # return a boolean if we have an empty indexer |
| 743 | + def _is_scalar_indexer(indexer): |
| 744 | + # return True if we are all scalar indexers |
720 | 745 |
|
721 | | - if arr_value.ndim == 1: |
722 | | - if not isinstance(indexer, tuple): |
723 | | - indexer = tuple([indexer]) |
724 | | - return any(isinstance(idx, np.ndarray) and len(idx) == 0 |
725 | | - for idx in indexer) |
726 | | - return False |
727 | | - |
728 | | - # empty indexers |
729 | | - # 8669 (empty) |
730 | | - if _is_empty_indexer(indexer): |
731 | | - pass |
732 | | - |
733 | | - # setting a single element for each dim and with a rhs that could |
734 | | - # be say a list |
735 | | - # GH 6043 |
736 | | - elif _is_scalar_indexer(indexer): |
737 | | - values[indexer] = value |
738 | | - |
739 | | - # if we are an exact match (ex-broadcasting), |
740 | | - # then use the resultant dtype |
741 | | - elif (len(arr_value.shape) and |
742 | | - arr_value.shape[0] == values.shape[0] and |
743 | | - np.prod(arr_value.shape) == np.prod(values.shape)): |
744 | | - values[indexer] = value |
745 | | - values = values.astype(arr_value.dtype) |
746 | | - |
747 | | - # set |
748 | | - else: |
749 | | - values[indexer] = value |
750 | | - |
751 | | - # coerce and try to infer the dtypes of the result |
752 | | - if hasattr(value, 'dtype') and is_dtype_equal(values.dtype, |
753 | | - value.dtype): |
754 | | - dtype = value.dtype |
755 | | - elif is_scalar(value): |
756 | | - dtype, _ = _infer_dtype_from_scalar(value) |
757 | | - else: |
758 | | - dtype = 'infer' |
759 | | - values = self._try_coerce_and_cast_result(values, dtype) |
760 | | - block = self.make_block(transf(values), fastpath=True) |
761 | | - |
762 | | - # may have to soft convert_objects here |
763 | | - if block.is_object and not self.is_object: |
764 | | - block = block.convert(numeric=False) |
765 | | - |
766 | | - return block |
767 | | - except ValueError: |
768 | | - raise |
769 | | - except TypeError: |
| 746 | + if arr_value.ndim == 1: |
| 747 | + if not isinstance(indexer, tuple): |
| 748 | + indexer = tuple([indexer]) |
| 749 | + return all([is_scalar(idx) for idx in indexer]) |
| 750 | + return False |
770 | 751 |
|
771 | | - # cast to the passed dtype if possible |
772 | | - # otherwise raise the original error |
773 | | - try: |
774 | | - # e.g. we are uint32 and our value is uint64 |
775 | | - # this is for compat with older numpies |
776 | | - block = self.make_block(transf(values.astype(value.dtype))) |
777 | | - return block.setitem(indexer=indexer, value=value, mgr=mgr) |
| 752 | + def _is_empty_indexer(indexer): |
| 753 | + # return a boolean if we have an empty indexer |
778 | 754 |
|
779 | | - except: |
780 | | - pass |
781 | | - |
782 | | - raise |
| 755 | + if arr_value.ndim == 1: |
| 756 | + if not isinstance(indexer, tuple): |
| 757 | + indexer = tuple([indexer]) |
| 758 | + return any(isinstance(idx, np.ndarray) and len(idx) == 0 |
| 759 | + for idx in indexer) |
| 760 | + return False |
783 | 761 |
|
784 | | - except Exception: |
| 762 | + # empty indexers |
| 763 | + # 8669 (empty) |
| 764 | + if _is_empty_indexer(indexer): |
785 | 765 | pass |
786 | 766 |
|
787 | | - return [self] |
| 767 | + # setting a single element for each dim and with a rhs that could |
| 768 | + # be say a list |
| 769 | + # GH 6043 |
| 770 | + elif _is_scalar_indexer(indexer): |
| 771 | + values[indexer] = value |
| 772 | + |
| 773 | + # if we are an exact match (ex-broadcasting), |
| 774 | + # then use the resultant dtype |
| 775 | + elif (len(arr_value.shape) and |
| 776 | + arr_value.shape[0] == values.shape[0] and |
| 777 | + np.prod(arr_value.shape) == np.prod(values.shape)): |
| 778 | + values[indexer] = value |
| 779 | + values = values.astype(arr_value.dtype) |
| 780 | + |
| 781 | + # set |
| 782 | + else: |
| 783 | + values[indexer] = value |
| 784 | + |
| 785 | + # coerce and try to infer the dtypes of the result |
| 786 | + values = self._try_coerce_and_cast_result(values, dtype) |
| 787 | + block = self.make_block(transf(values), fastpath=True) |
| 788 | + return block |
788 | 789 |
|
789 | 790 | def putmask(self, mask, new, align=True, inplace=False, axis=0, |
790 | 791 | transpose=False, mgr=None): |
@@ -1255,6 +1256,7 @@ def func(cond, values, other): |
1255 | 1256 |
|
1256 | 1257 | values, values_mask, other, other_mask = self._try_coerce_args( |
1257 | 1258 | values, other) |
| 1259 | + |
1258 | 1260 | try: |
1259 | 1261 | return self._try_coerce_result(expressions.where( |
1260 | 1262 | cond, values, other, raise_on_error=True)) |
@@ -1534,6 +1536,7 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, |
1534 | 1536 | new = new[mask] |
1535 | 1537 |
|
1536 | 1538 | mask = _safe_reshape(mask, new_values.shape) |
| 1539 | + |
1537 | 1540 | new_values[mask] = new |
1538 | 1541 | new_values = self._try_coerce_result(new_values) |
1539 | 1542 | return [self.make_block(values=new_values)] |
@@ -1703,7 +1706,7 @@ def fillna(self, value, **kwargs): |
1703 | 1706 |
|
1704 | 1707 | # allow filling with integers to be |
1705 | 1708 | # interpreted as seconds |
1706 | | - if not isinstance(value, np.timedelta64) and is_integer(value): |
| 1709 | + if not isinstance(value, np.timedelta64): |
1707 | 1710 | value = Timedelta(value, unit='s') |
1708 | 1711 | return super(TimeDeltaBlock, self).fillna(value, **kwargs) |
1709 | 1712 |
|
@@ -1937,6 +1940,15 @@ def _maybe_downcast(self, blocks, downcast=None): |
1937 | 1940 | def _can_hold_element(self, element): |
1938 | 1941 | return True |
1939 | 1942 |
|
| 1943 | + def _try_coerce_args(self, values, other): |
| 1944 | + """ provide coercion to our input arguments """ |
| 1945 | + |
| 1946 | + if isinstance(other, ABCDatetimeIndex): |
| 1947 | + # to store DatetimeTZBlock as object |
| 1948 | + other = other.asobject.values |
| 1949 | + |
| 1950 | + return values, False, other, False |
| 1951 | + |
1940 | 1952 | def _try_cast(self, element): |
1941 | 1953 | return element |
1942 | 1954 |
|
@@ -2276,8 +2288,6 @@ def _try_coerce_args(self, values, other): |
2276 | 2288 | "naive Block") |
2277 | 2289 | other_mask = isnull(other) |
2278 | 2290 | other = other.asm8.view('i8') |
2279 | | - elif hasattr(other, 'dtype') and is_integer_dtype(other): |
2280 | | - other = other.view('i8') |
2281 | 2291 | else: |
2282 | 2292 | try: |
2283 | 2293 | other = np.asarray(other) |
@@ -2453,6 +2463,8 @@ def _try_coerce_args(self, values, other): |
2453 | 2463 | raise ValueError("incompatible or non tz-aware value") |
2454 | 2464 | other_mask = isnull(other) |
2455 | 2465 | other = other.value |
| 2466 | + else: |
| 2467 | + raise TypeError |
2456 | 2468 |
|
2457 | 2469 | return values, values_mask, other, other_mask |
2458 | 2470 |
|
|
0 commit comments