1
- from datetime import date , datetime , timedelta
1
+ from datetime import datetime , timedelta
2
2
import functools
3
3
import inspect
4
4
import re
7
7
8
8
import numpy as np
9
9
10
- from pandas ._libs import NaT , Timestamp , lib , tslib , writers
10
+ from pandas ._libs import NaT , lib , tslib , writers
11
+ from pandas ._libs .index import convert_scalar
11
12
import pandas ._libs .internals as libinternals
12
13
from pandas ._libs .tslibs import Timedelta , conversion
13
14
from pandas ._libs .tslibs .timezones import tz_compare
54
55
from pandas .core .dtypes .dtypes import CategoricalDtype , ExtensionDtype
55
56
from pandas .core .dtypes .generic import (
56
57
ABCDataFrame ,
57
- ABCDatetimeIndex ,
58
58
ABCExtensionArray ,
59
59
ABCPandasArray ,
60
60
ABCSeries ,
64
64
array_equivalent ,
65
65
is_valid_nat_for_dtype ,
66
66
isna ,
67
- notna ,
68
67
)
69
68
70
69
import pandas .core .algorithms as algos
@@ -663,28 +662,6 @@ def _can_hold_element(self, element: Any) -> bool:
663
662
return issubclass (tipo .type , dtype )
664
663
return isinstance (element , dtype )
665
664
666
- def _try_coerce_args (self , other ):
667
- """ provide coercion to our input arguments """
668
-
669
- if np .any (notna (other )) and not self ._can_hold_element (other ):
670
- # coercion issues
671
- # let higher levels handle
672
- raise TypeError (
673
- "cannot convert {} to an {}" .format (
674
- type (other ).__name__ ,
675
- type (self ).__name__ .lower ().replace ("Block" , "" ),
676
- )
677
- )
678
- if np .any (isna (other )) and not self ._can_hold_na :
679
- raise TypeError (
680
- "cannot convert {} to an {}" .format (
681
- type (other ).__name__ ,
682
- type (self ).__name__ .lower ().replace ("Block" , "" ),
683
- )
684
- )
685
-
686
- return other
687
-
688
665
def to_native_types (self , slicer = None , na_rep = "nan" , quoting = None , ** kwargs ):
689
666
""" convert to our native types format, slicing if desired """
690
667
values = self .get_values ()
@@ -766,7 +743,11 @@ def replace(
766
743
)
767
744
768
745
values = self .values
769
- to_replace = self ._try_coerce_args (to_replace )
746
+ if lib .is_scalar (to_replace ) and isinstance (values , np .ndarray ):
747
+ # The only non-DatetimeLike class that also has a non-trivial
748
+ # try_coerce_args is ObjectBlock, but that overrides replace,
749
+ # so does not get here.
750
+ to_replace = convert_scalar (values , to_replace )
770
751
771
752
mask = missing .mask_missing (values , to_replace )
772
753
if filter is not None :
@@ -813,7 +794,8 @@ def _replace_single(self, *args, **kwargs):
813
794
return self if kwargs ["inplace" ] else self .copy ()
814
795
815
796
def setitem (self , indexer , value ):
816
- """Set the value inplace, returning a a maybe different typed block.
797
+ """
798
+ Set the value inplace, returning a a maybe different typed block.
817
799
818
800
Parameters
819
801
----------
@@ -841,7 +823,10 @@ def setitem(self, indexer, value):
841
823
# coerce if block dtype can store value
842
824
values = self .values
843
825
if self ._can_hold_element (value ):
844
- value = self ._try_coerce_args (value )
826
+ # We only get here for non-Extension Blocks, so _try_coerce_args
827
+ # is only relevant for DatetimeBlock and TimedeltaBlock
828
+ if lib .is_scalar (value ):
829
+ value = convert_scalar (values , value )
845
830
846
831
else :
847
832
# current dtype cannot store value, coerce to common dtype
@@ -862,7 +847,12 @@ def setitem(self, indexer, value):
862
847
return b .setitem (indexer , value )
863
848
864
849
# value must be storeable at this moment
865
- arr_value = np .array (value )
850
+ if is_extension_array_dtype (getattr (value , "dtype" , None )):
851
+ # We need to be careful not to allow through strings that
852
+ # can be parsed to EADtypes
853
+ arr_value = value
854
+ else :
855
+ arr_value = np .array (value )
866
856
867
857
# cast the values to a type that can hold nan (if necessary)
868
858
if not self ._can_hold_element (value ):
@@ -938,7 +928,10 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, transpose=False)
938
928
new = self .fill_value
939
929
940
930
if self ._can_hold_element (new ):
941
- new = self ._try_coerce_args (new )
931
+ # We only get here for non-Extension Blocks, so _try_coerce_args
932
+ # is only relevant for DatetimeBlock and TimedeltaBlock
933
+ if lib .is_scalar (new ):
934
+ new = convert_scalar (new_values , new )
942
935
943
936
if transpose :
944
937
new_values = new_values .T
@@ -1176,7 +1169,10 @@ def _interpolate_with_fill(
1176
1169
return [self .copy ()]
1177
1170
1178
1171
values = self .values if inplace else self .values .copy ()
1179
- fill_value = self ._try_coerce_args (fill_value )
1172
+
1173
+ # We only get here for non-ExtensionBlock
1174
+ fill_value = convert_scalar (self .values , fill_value )
1175
+
1180
1176
values = missing .interpolate_2d (
1181
1177
values ,
1182
1178
method = method ,
@@ -1375,7 +1371,10 @@ def func(cond, values, other):
1375
1371
and np .isnan (other )
1376
1372
):
1377
1373
# np.where will cast integer array to floats in this case
1378
- other = self ._try_coerce_args (other )
1374
+ if not self ._can_hold_element (other ):
1375
+ raise TypeError
1376
+ if lib .is_scalar (other ) and isinstance (values , np .ndarray ):
1377
+ other = convert_scalar (values , other )
1379
1378
1380
1379
fastres = expressions .where (cond , values , other )
1381
1380
return fastres
@@ -1641,7 +1640,6 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0, transpose=False)
1641
1640
# use block's copy logic.
1642
1641
# .values may be an Index which does shallow copy by default
1643
1642
new_values = self .values if inplace else self .copy ().values
1644
- new = self ._try_coerce_args (new )
1645
1643
1646
1644
if isinstance (new , np .ndarray ) and len (new ) == len (mask ):
1647
1645
new = new [mask ]
@@ -2194,38 +2192,6 @@ def _can_hold_element(self, element: Any) -> bool:
2194
2192
2195
2193
return is_valid_nat_for_dtype (element , self .dtype )
2196
2194
2197
- def _try_coerce_args (self , other ):
2198
- """
2199
- Coerce other to dtype 'i8'. NaN and NaT convert to
2200
- the smallest i8, and will correctly round-trip to NaT if converted
2201
- back in _try_coerce_result. values is always ndarray-like, other
2202
- may not be
2203
-
2204
- Parameters
2205
- ----------
2206
- other : ndarray-like or scalar
2207
-
2208
- Returns
2209
- -------
2210
- base-type other
2211
- """
2212
- if is_valid_nat_for_dtype (other , self .dtype ):
2213
- other = np .datetime64 ("NaT" , "ns" )
2214
- elif isinstance (other , (datetime , np .datetime64 , date )):
2215
- other = Timestamp (other )
2216
- if other .tz is not None :
2217
- raise TypeError ("cannot coerce a Timestamp with a tz on a naive Block" )
2218
- other = other .asm8
2219
- elif hasattr (other , "dtype" ) and is_datetime64_dtype (other ):
2220
- # TODO: can we get here with non-nano?
2221
- pass
2222
- else :
2223
- # coercion issues
2224
- # let higher levels handle
2225
- raise TypeError (other )
2226
-
2227
- return other
2228
-
2229
2195
def to_native_types (
2230
2196
self , slicer = None , na_rep = None , date_format = None , quoting = None , ** kwargs
2231
2197
):
@@ -2364,10 +2330,6 @@ def _slice(self, slicer):
2364
2330
return self .values [loc ]
2365
2331
return self .values [slicer ]
2366
2332
2367
- def _try_coerce_args (self , other ):
2368
- # DatetimeArray handles this for us
2369
- return other
2370
-
2371
2333
def diff (self , n : int , axis : int = 0 ) -> List ["Block" ]:
2372
2334
"""
2373
2335
1st discrete difference.
@@ -2505,34 +2467,6 @@ def fillna(self, value, **kwargs):
2505
2467
value = Timedelta (value , unit = "s" )
2506
2468
return super ().fillna (value , ** kwargs )
2507
2469
2508
- def _try_coerce_args (self , other ):
2509
- """
2510
- Coerce values and other to datetime64[ns], with null values
2511
- converted to datetime64("NaT", "ns").
2512
-
2513
- Parameters
2514
- ----------
2515
- other : ndarray-like or scalar
2516
-
2517
- Returns
2518
- -------
2519
- base-type other
2520
- """
2521
-
2522
- if is_valid_nat_for_dtype (other , self .dtype ):
2523
- other = np .timedelta64 ("NaT" , "ns" )
2524
- elif isinstance (other , (timedelta , np .timedelta64 )):
2525
- other = Timedelta (other ).to_timedelta64 ()
2526
- elif hasattr (other , "dtype" ) and is_timedelta64_dtype (other ):
2527
- # TODO: can we get here with non-nano dtype?
2528
- pass
2529
- else :
2530
- # coercion issues
2531
- # let higher levels handle
2532
- raise TypeError (other )
2533
-
2534
- return other
2535
-
2536
2470
def should_store (self , value ):
2537
2471
return issubclass (
2538
2472
value .dtype .type , np .timedelta64
@@ -2668,21 +2602,6 @@ def _maybe_downcast(self, blocks: List["Block"], downcast=None) -> List["Block"]
2668
2602
def _can_hold_element (self , element : Any ) -> bool :
2669
2603
return True
2670
2604
2671
- def _try_coerce_args (self , other ):
2672
- """ provide coercion to our input arguments """
2673
-
2674
- if isinstance (other , ABCDatetimeIndex ):
2675
- # May get a DatetimeIndex here. Unbox it.
2676
- other = other .array
2677
-
2678
- if isinstance (other , DatetimeArray ):
2679
- # hit in pandas/tests/indexing/test_coercion.py
2680
- # ::TestWhereCoercion::test_where_series_datetime64[datetime64tz]
2681
- # when falling back to ObjectBlock.where
2682
- other = other .astype (object )
2683
-
2684
- return other
2685
-
2686
2605
def should_store (self , value ):
2687
2606
return not (
2688
2607
issubclass (
0 commit comments