29
29
is_bool_dtype ,
30
30
is_object_dtype ,
31
31
is_datetimelike_v_numeric ,
32
+ is_complex_dtype ,
32
33
is_float_dtype , is_numeric_dtype ,
33
34
is_numeric_v_string_like , is_extension_type ,
34
35
is_list_like ,
@@ -454,8 +455,11 @@ def make_a_block(nv, ref_loc):
454
455
nv = _block_shape (nv , ndim = self .ndim )
455
456
except (AttributeError , NotImplementedError ):
456
457
pass
458
+
457
459
block = self .make_block (values = nv ,
458
- placement = ref_loc , fastpath = True )
460
+ placement = ref_loc ,
461
+ fastpath = True )
462
+
459
463
return block
460
464
461
465
# ndim == 1
@@ -1020,7 +1024,7 @@ def f(m, v, i):
1020
1024
1021
1025
return [self .make_block (new_values , fastpath = True )]
1022
1026
1023
- def coerce_to_target_dtype (self , other ):
1027
+ def coerce_to_target_dtype (self , other , copy = False ):
1024
1028
"""
1025
1029
coerce the current block to a dtype compat for other
1026
1030
we will return a block, possibly object, and not raise
@@ -1037,7 +1041,7 @@ def coerce_to_target_dtype(self, other):
1037
1041
1038
1042
if self .is_bool or is_object_dtype (dtype ) or is_bool_dtype (dtype ):
1039
1043
# we don't upcast to bool
1040
- return self .astype (object )
1044
+ return self .astype (object , copy = copy )
1041
1045
1042
1046
elif ((self .is_float or self .is_complex ) and
1043
1047
(is_integer_dtype (dtype ) or is_float_dtype (dtype ))):
@@ -1051,14 +1055,14 @@ def coerce_to_target_dtype(self, other):
1051
1055
# not a datetime
1052
1056
if not ((is_datetime64_dtype (dtype ) or
1053
1057
is_datetime64tz_dtype (dtype )) and self .is_datetime ):
1054
- return self .astype (object )
1058
+ return self .astype (object , copy = copy )
1055
1059
1056
1060
# don't upcast timezone with different timezone or no timezone
1057
1061
mytz = getattr (self .dtype , 'tz' , None )
1058
1062
othertz = getattr (dtype , 'tz' , None )
1059
1063
1060
1064
if str (mytz ) != str (othertz ):
1061
- return self .astype (object )
1065
+ return self .astype (object , copy = copy )
1062
1066
1063
1067
raise AssertionError ("possible recursion in "
1064
1068
"coerce_to_target_dtype: {} {}" .format (
@@ -1068,18 +1072,18 @@ def coerce_to_target_dtype(self, other):
1068
1072
1069
1073
# not a timedelta
1070
1074
if not (is_timedelta64_dtype (dtype ) and self .is_timedelta ):
1071
- return self .astype (object )
1075
+ return self .astype (object , copy = copy )
1072
1076
1073
1077
raise AssertionError ("possible recursion in "
1074
1078
"coerce_to_target_dtype: {} {}" .format (
1075
1079
self , other ))
1076
1080
1077
1081
try :
1078
- return self .astype (dtype )
1082
+ return self .astype (dtype , copy = copy )
1079
1083
except (ValueError , TypeError ):
1080
1084
pass
1081
1085
1082
- return self .astype (object )
1086
+ return self .astype (object , copy = copy )
1083
1087
1084
1088
def interpolate (self , method = 'pad' , axis = 0 , index = None , values = None ,
1085
1089
inplace = False , limit = None , limit_direction = 'forward' ,
@@ -1440,6 +1444,11 @@ def where(self, other, cond, align=True, errors='raise',
1440
1444
if hasattr (other , 'reindex_axis' ):
1441
1445
other = other .values
1442
1446
1447
+ if is_scalar (other ) or is_list_like (other ):
1448
+ fill_value = other
1449
+ else :
1450
+ fill_value = None
1451
+
1443
1452
if hasattr (cond , 'reindex_axis' ):
1444
1453
cond = cond .values
1445
1454
@@ -1452,6 +1461,9 @@ def where(self, other, cond, align=True, errors='raise',
1452
1461
if not hasattr (cond , 'shape' ):
1453
1462
raise ValueError ("where must have a condition that is ndarray "
1454
1463
"like" )
1464
+ else :
1465
+ if self .is_sparse :
1466
+ cond = cond .flatten ()
1455
1467
1456
1468
# our where function
1457
1469
def func (cond , values , other ):
@@ -1489,7 +1501,7 @@ def func(cond, values, other):
1489
1501
transpose = transpose )
1490
1502
return self ._maybe_downcast (blocks , 'infer' )
1491
1503
1492
- if self ._can_hold_na or self .ndim == 1 :
1504
+ if self ._can_hold_element ( fill_value ) or values .ndim == 1 :
1493
1505
1494
1506
if transpose :
1495
1507
result = result .T
@@ -1498,7 +1510,12 @@ def func(cond, values, other):
1498
1510
if try_cast :
1499
1511
result = self ._try_cast_result (result )
1500
1512
1501
- return self .make_block (result )
1513
+ if isinstance (result , np .ndarray ):
1514
+ ndim = result .ndim
1515
+ else :
1516
+ ndim = None
1517
+
1518
+ return self .make_block (result , ndim = ndim , fill_value = fill_value )
1502
1519
1503
1520
# might need to separate out blocks
1504
1521
axis = cond .ndim - 1
@@ -1512,7 +1529,8 @@ def func(cond, values, other):
1512
1529
r = self ._try_cast_result (result .take (m .nonzero ()[0 ],
1513
1530
axis = axis ))
1514
1531
result_blocks .append (
1515
- self .make_block (r .T , placement = self .mgr_locs [m ]))
1532
+ self .make_block_same_class (r .T ,
1533
+ placement = self .mgr_locs [m ]))
1516
1534
1517
1535
return result_blocks
1518
1536
@@ -1832,6 +1850,7 @@ class FloatBlock(FloatOrComplexBlock):
1832
1850
is_float = True
1833
1851
_downcast_dtype = 'int64'
1834
1852
1853
+ @classmethod
1835
1854
def _can_hold_element (self , element ):
1836
1855
tipo = maybe_infer_dtype_type (element )
1837
1856
if tipo is not None :
@@ -1881,6 +1900,7 @@ class ComplexBlock(FloatOrComplexBlock):
1881
1900
__slots__ = ()
1882
1901
is_complex = True
1883
1902
1903
+ @classmethod
1884
1904
def _can_hold_element (self , element ):
1885
1905
tipo = maybe_infer_dtype_type (element )
1886
1906
if tipo is not None :
@@ -2042,6 +2062,7 @@ class BoolBlock(NumericBlock):
2042
2062
is_bool = True
2043
2063
_can_hold_na = False
2044
2064
2065
+ @classmethod
2045
2066
def _can_hold_element (self , element ):
2046
2067
tipo = maybe_infer_dtype_type (element )
2047
2068
if tipo is not None :
@@ -2751,11 +2772,63 @@ class SparseBlock(NonConsolidatableMixIn, Block):
2751
2772
is_sparse = True
2752
2773
is_numeric = True
2753
2774
_box_to_block_values = False
2754
- _can_hold_na = True
2755
2775
_ftype = 'sparse'
2756
2776
_holder = SparseArray
2757
2777
_concatenator = staticmethod (_concat ._concat_sparse )
2758
2778
2779
+ def __init__ (self , values , placement , ndim = None , fastpath = False , ** kwargs ):
2780
+ super (SparseBlock , self ).__init__ (values , placement ,
2781
+ ndim , fastpath ,
2782
+ ** kwargs )
2783
+
2784
+ dtype = self .values .sp_values .dtype
2785
+
2786
+ if is_float_dtype (dtype ):
2787
+ self .is_float = True
2788
+ self ._can_hold_na = True
2789
+ elif is_complex_dtype (dtype ):
2790
+ self .is_complex = True
2791
+ self ._can_hold_na = True
2792
+ elif is_integer_dtype (dtype ):
2793
+ self .is_integer = True
2794
+ self ._can_hold_na = False
2795
+ elif is_bool_dtype (dtype ):
2796
+ self .is_bool = True
2797
+ self ._can_hold_na = False
2798
+ elif is_object_dtype (dtype ):
2799
+ self .is_object = True
2800
+ self ._can_hold_na = True
2801
+ else :
2802
+ self ._can_hold_na = False
2803
+
2804
+ def _can_hold_element (self , element ):
2805
+ """ require the same dtype as ourselves """
2806
+ dtype = self .values .sp_values .dtype
2807
+
2808
+ if is_bool_dtype (dtype ):
2809
+ return BoolBlock ._can_hold_element (element )
2810
+ elif is_integer_dtype (dtype ):
2811
+ if is_list_like (element ):
2812
+ element = np .array (element )
2813
+ tipo = element .dtype .type
2814
+ return (issubclass (tipo , np .integer ) and
2815
+ not issubclass (tipo ,
2816
+ (np .datetime64 ,
2817
+ np .timedelta64 )) and
2818
+ dtype .itemsize >= element .dtype .itemsize )
2819
+ return is_integer (element )
2820
+ elif is_float_dtype (dtype ):
2821
+ return FloatBlock ._can_hold_element (element )
2822
+ elif is_complex_dtype (dtype ):
2823
+ return ComplexBlock ._can_hold_element (element )
2824
+ elif is_object_dtype (dtype ):
2825
+ return True
2826
+ else :
2827
+ return False
2828
+
2829
+ def coerce_to_target_dtype (self , other , copy = True ):
2830
+ return super (SparseBlock , self ).coerce_to_target_dtype (other , copy )
2831
+
2759
2832
@property
2760
2833
def shape (self ):
2761
2834
return (len (self .mgr_locs ), self .sp_index .length )
@@ -2816,6 +2889,20 @@ def copy(self, deep=True, mgr=None):
2816
2889
kind = self .kind , copy = deep ,
2817
2890
placement = self .mgr_locs )
2818
2891
2892
+ def make_block (self , values , placement = None ,
2893
+ ndim = None , fill_value = None , ** kwargs ):
2894
+ """
2895
+ Create a new block, with type inference propagate any values that are
2896
+ not specified
2897
+ """
2898
+ if fill_value is not None and isinstance (values , SparseArray ):
2899
+ values = SparseArray (values .to_dense (), fill_value = fill_value ,
2900
+ kind = values .kind , dtype = values .dtype )
2901
+
2902
+ return super (SparseBlock , self ).make_block (values , placement = placement ,
2903
+ ndim = ndim , fill_value = None ,
2904
+ ** kwargs )
2905
+
2819
2906
def make_block_same_class (self , values , placement , sparse_index = None ,
2820
2907
kind = None , dtype = None , fill_value = None ,
2821
2908
copy = False , fastpath = True , ** kwargs ):
@@ -2912,9 +2999,15 @@ def sparse_reindex(self, new_index):
2912
2999
return self .make_block_same_class (values , sparse_index = new_index ,
2913
3000
placement = self .mgr_locs )
2914
3001
3002
+ def _try_coerce_result (self , result ):
3003
+ """ reverse of try_coerce_args """
3004
+ if isinstance (result , np .ndarray ):
3005
+ result = SparseArray (result .flatten (), kind = self .kind )
3006
+ return result
3007
+
2915
3008
2916
3009
def make_block (values , placement , klass = None , ndim = None , dtype = None ,
2917
- fastpath = False ):
3010
+ fastpath = False , ** kwargs ):
2918
3011
if klass is None :
2919
3012
dtype = dtype or values .dtype
2920
3013
vtype = dtype .type
0 commit comments