1
1
import itertools
2
2
import re
3
- from datetime import datetime
3
+ from datetime import datetime , timedelta
4
4
import copy
5
5
from collections import defaultdict
6
6
@@ -41,6 +41,7 @@ class Block(PandasObject):
41
41
is_integer = False
42
42
is_complex = False
43
43
is_datetime = False
44
+ is_timedelta = False
44
45
is_bool = False
45
46
is_object = False
46
47
is_sparse = False
@@ -326,6 +327,8 @@ def _maybe_downcast(self, blocks, downcast=None):
326
327
# unless indicated
327
328
if downcast is None and self .is_float :
328
329
return blocks
330
+ elif downcast is None and (self .is_timedelta or self .is_datetime ):
331
+ return blocks
329
332
330
333
result_blocks = []
331
334
for b in blocks :
@@ -485,6 +488,10 @@ def _try_cast_result(self, result, dtype=None):
485
488
# may need to change the dtype here
486
489
return _possibly_downcast_to_dtype (result , dtype )
487
490
491
+ def _try_operate (self , values ):
492
+ """ return a version to operate on as the input """
493
+ return values
494
+
488
495
def _try_coerce_args (self , values , other ):
489
496
""" provide coercion to our input arguments """
490
497
return values , other
@@ -703,8 +710,11 @@ def interpolate(self, method='pad', axis=0, inplace=False,
703
710
else :
704
711
return [self .copy ()]
705
712
713
+ fill_value = self ._try_fill (fill_value )
706
714
values = self .values if inplace else self .values .copy ()
715
+ values = self ._try_operate (values )
707
716
values = com .interpolate_2d (values , method , axis , limit , fill_value )
717
+ values = self ._try_coerce_result (values )
708
718
709
719
blocks = [ make_block (values , self .items , self .ref_items , ndim = self .ndim , klass = self .__class__ , fastpath = True ) ]
710
720
return self ._maybe_downcast (blocks , downcast )
@@ -1008,6 +1018,55 @@ def _try_cast(self, element):
1008
1018
def should_store (self , value ):
1009
1019
return com .is_integer_dtype (value ) and value .dtype == self .dtype
1010
1020
1021
+ class TimeDeltaBlock (IntBlock ):
1022
+ is_timedelta = True
1023
+ _can_hold_na = True
1024
+
1025
+ def _try_fill (self , value ):
1026
+ """ if we are a NaT, return the actual fill value """
1027
+ if isinstance (value , type (tslib .NaT )) or isnull (value ):
1028
+ value = tslib .iNaT
1029
+ elif isinstance (value , np .timedelta64 ):
1030
+ pass
1031
+ elif com .is_integer (value ):
1032
+ # coerce to seconds of timedelta
1033
+ value = np .timedelta64 (int (value * 1e9 ))
1034
+ elif isinstance (value , timedelta ):
1035
+ value = np .timedelta64 (value )
1036
+
1037
+ return value
1038
+
1039
+ def _try_operate (self , values ):
1040
+ """ return a version to operate on """
1041
+ return values .view ('i8' )
1042
+
1043
+ def _try_coerce_result (self , result ):
1044
+ """ reverse of try_coerce_args / try_operate """
1045
+ if isinstance (result , np .ndarray ):
1046
+ result = result .astype ('m8[ns]' )
1047
+ elif isinstance (result , np .integer ):
1048
+ result = np .timedelta64 (result )
1049
+ return result
1050
+
1051
+ def should_store (self , value ):
1052
+ return issubclass (value .dtype .type , np .timedelta64 )
1053
+
1054
+ def to_native_types (self , slicer = None , na_rep = None , ** kwargs ):
1055
+ """ convert to our native types format, slicing if desired """
1056
+
1057
+ values = self .values
1058
+ if slicer is not None :
1059
+ values = values [:, slicer ]
1060
+ mask = isnull (values )
1061
+
1062
+ rvalues = np .empty (values .shape , dtype = object )
1063
+ if na_rep is None :
1064
+ na_rep = 'NaT'
1065
+ rvalues [mask ] = na_rep
1066
+ imask = (- mask ).ravel ()
1067
+ rvalues .flat [imask ] = np .array ([lib .repr_timedelta64 (val )
1068
+ for val in values .ravel ()[imask ]], dtype = object )
1069
+ return rvalues .tolist ()
1011
1070
1012
1071
class BoolBlock (NumericBlock ):
1013
1072
is_bool = True
@@ -1216,6 +1275,10 @@ def _try_cast(self, element):
1216
1275
except :
1217
1276
return element
1218
1277
1278
+ def _try_operate (self , values ):
1279
+ """ return a version to operate on """
1280
+ return values .view ('i8' )
1281
+
1219
1282
def _try_coerce_args (self , values , other ):
1220
1283
""" provide coercion to our input arguments
1221
1284
we are going to compare vs i8, so coerce to integer
@@ -1242,11 +1305,12 @@ def _try_coerce_result(self, result):
1242
1305
1243
1306
def _try_fill (self , value ):
1244
1307
""" if we are a NaT, return the actual fill value """
1245
- if isinstance (value , type (tslib .NaT )):
1308
+ if isinstance (value , type (tslib .NaT )) or isnull ( value ) :
1246
1309
value = tslib .iNaT
1247
1310
return value
1248
1311
1249
1312
def fillna (self , value , inplace = False , downcast = None ):
1313
+ # straight putmask here
1250
1314
values = self .values if inplace else self .values .copy ()
1251
1315
mask = com .isnull (self .values )
1252
1316
value = self ._try_fill (value )
@@ -1267,12 +1331,9 @@ def to_native_types(self, slicer=None, na_rep=None, **kwargs):
1267
1331
na_rep = 'NaT'
1268
1332
rvalues [mask ] = na_rep
1269
1333
imask = (- mask ).ravel ()
1270
- if self .dtype == 'datetime64[ns]' :
1271
- rvalues .flat [imask ] = np .array (
1272
- [Timestamp (val )._repr_base for val in values .ravel ()[imask ]], dtype = object )
1273
- elif self .dtype == 'timedelta64[ns]' :
1274
- rvalues .flat [imask ] = np .array ([lib .repr_timedelta64 (val )
1275
- for val in values .ravel ()[imask ]], dtype = object )
1334
+ rvalues .flat [imask ] = np .array (
1335
+ [Timestamp (val )._repr_base for val in values .ravel ()[imask ]], dtype = object )
1336
+
1276
1337
return rvalues .tolist ()
1277
1338
1278
1339
def should_store (self , value ):
@@ -1551,6 +1612,8 @@ def make_block(values, items, ref_items, klass=None, ndim=None, dtype=None, fast
1551
1612
klass = SparseBlock
1552
1613
elif issubclass (vtype , np .floating ):
1553
1614
klass = FloatBlock
1615
+ elif issubclass (vtype , np .integer ) and issubclass (vtype , np .timedelta64 ):
1616
+ klass = TimeDeltaBlock
1554
1617
elif issubclass (vtype , np .integer ) and not issubclass (vtype , np .datetime64 ):
1555
1618
klass = IntBlock
1556
1619
elif dtype == np .bool_ :
@@ -3404,12 +3467,13 @@ def _lcd_dtype(l):
3404
3467
have_float = len (counts [FloatBlock ]) > 0
3405
3468
have_complex = len (counts [ComplexBlock ]) > 0
3406
3469
have_dt64 = len (counts [DatetimeBlock ]) > 0
3470
+ have_td64 = len (counts [TimeDeltaBlock ]) > 0
3407
3471
have_sparse = len (counts [SparseBlock ]) > 0
3408
3472
have_numeric = have_float or have_complex or have_int
3409
3473
3410
3474
if (have_object or
3411
3475
(have_bool and have_numeric ) or
3412
- (have_numeric and have_dt64 )):
3476
+ (have_numeric and ( have_dt64 or have_td64 ) )):
3413
3477
return np .dtype (object )
3414
3478
elif have_bool :
3415
3479
return np .dtype (bool )
@@ -3432,6 +3496,8 @@ def _lcd_dtype(l):
3432
3496
3433
3497
elif have_dt64 and not have_float and not have_complex :
3434
3498
return np .dtype ('M8[ns]' )
3499
+ elif have_td64 and not have_float and not have_complex :
3500
+ return np .dtype ('m8[ns]' )
3435
3501
elif have_complex :
3436
3502
return np .dtype ('c16' )
3437
3503
else :
0 commit comments