4
4
from numpy import nan
5
5
import numpy as np
6
6
7
- from pandas .core .common import _possibly_downcast_to_dtype
7
+ from pandas .core .common import _possibly_downcast_to_dtype , isnull
8
8
from pandas .core .index import Index , _ensure_index , _handle_legacy_indexes
9
9
from pandas .core .indexing import _check_slice_bounds , _maybe_convert_indices
10
10
import pandas .core .common as com
@@ -260,32 +260,14 @@ def _try_cast_result(self, result):
260
260
return result
261
261
262
262
def replace (self , to_replace , value , inplace = False ):
263
- new_values = self .values if inplace else self .values .copy ()
264
- if self ._can_hold_element (value ):
265
- value = self ._try_cast (value )
266
-
267
- if not isinstance (to_replace , (list , np .ndarray )):
268
- if self ._can_hold_element (to_replace ):
269
- to_replace = self ._try_cast (to_replace )
270
- msk = com .mask_missing (new_values , to_replace )
271
- np .putmask (new_values , msk , value )
272
- else :
273
- try :
274
- to_replace = np .array (to_replace , dtype = self .dtype )
275
- msk = com .mask_missing (new_values , to_replace )
276
- np .putmask (new_values , msk , value )
277
- except Exception :
278
- to_replace = np .array (to_replace , dtype = object )
279
- for r in to_replace :
280
- if self ._can_hold_element (r ):
281
- r = self ._try_cast (r )
282
- msk = com .mask_missing (new_values , to_replace )
283
- np .putmask (new_values , msk , value )
284
-
285
- if inplace :
286
- return self
287
- else :
288
- return make_block (new_values , self .items , self .ref_items )
263
+ """ replace the to_replace value with value, possible to create new blocks here
264
+ this is just a call to putmask """
265
+ mask = com .mask_missing (self .values , to_replace )
266
+ if not mask .any ():
267
+ if inplace :
268
+ return [ self ]
269
+ return [ self .copy () ]
270
+ return self .putmask (mask , value , inplace = inplace )
289
271
290
272
def putmask (self , mask , new , inplace = False ):
291
273
""" putmask the data to the block; it is possible that we may create a new dtype of block
@@ -309,19 +291,34 @@ def putmask(self, mask, new, inplace=False):
309
291
310
292
# maybe upcast me
311
293
elif mask .any ():
312
- # type of the new block
313
- if ((isinstance (new , np .ndarray ) and issubclass (new .dtype , np .number )) or
314
- isinstance (new , float )):
315
- typ = np .float64
316
- else :
317
- typ = np .object_
318
294
319
- # we need to exiplicty astype here to make a copy
320
- new_values = new_values .astype (typ )
295
+ # need to go column by column
296
+ new_blocks = []
297
+ for i , item in enumerate (self .items ):
321
298
322
- # we create a new block type
323
- np .putmask (new_values , mask , new )
324
- return [ make_block (new_values , self .items , self .ref_items ) ]
299
+ m = mask [i ]
300
+
301
+ # need a new block
302
+ if m .any ():
303
+
304
+ n = new [i ] if isinstance (new , np .ndarray ) else new
305
+
306
+ # type of the new block
307
+ dtype , _ = com ._maybe_promote (np .array (n ).dtype )
308
+
309
+ # we need to exiplicty astype here to make a copy
310
+ nv = new_values [i ].astype (dtype )
311
+
312
+ # we create a new block type
313
+ np .putmask (nv , m , n )
314
+
315
+ else :
316
+ nv = new_values [i ] if inplace else new_values [i ].copy ()
317
+
318
+ nv = _block_shape (nv )
319
+ new_blocks .append (make_block (nv , [ item ], self .ref_items ))
320
+
321
+ return new_blocks
325
322
326
323
if inplace :
327
324
return [ self ]
@@ -350,7 +347,7 @@ def interpolate(self, method='pad', axis=0, inplace=False,
350
347
if missing is None :
351
348
mask = None
352
349
else : # todo create faster fill func without masking
353
- mask = _mask_missing (transf (values ), missing )
350
+ mask = com . mask_missing (transf (values ), missing )
354
351
355
352
if method == 'pad' :
356
353
com .pad_2d (transf (values ), limit = limit , mask = mask )
@@ -532,31 +529,14 @@ def create_block(result, items, transpose = True):
532
529
if len (result ) == 1 :
533
530
result = np .repeat (result ,self .shape [1 :])
534
531
535
- result = result . reshape ((( 1 ,) + self .shape [1 :]) )
532
+ result = _block_shape ( result , ndim = self . ndim , shape = self .shape [1 :])
536
533
result_blocks .append (create_block (result , item , transpose = False ))
537
534
538
535
return result_blocks
539
536
else :
540
537
result = func (cond ,values ,other )
541
538
return create_block (result , self .items )
542
539
543
- def _mask_missing (array , missing_values ):
544
- if not isinstance (missing_values , (list , np .ndarray )):
545
- missing_values = [missing_values ]
546
-
547
- mask = None
548
- missing_values = np .array (missing_values , dtype = object )
549
- if com .isnull (missing_values ).any ():
550
- mask = com .isnull (array )
551
- missing_values = missing_values [com .notnull (missing_values )]
552
-
553
- for v in missing_values :
554
- if mask is None :
555
- mask = array == missing_values
556
- else :
557
- mask |= array == missing_values
558
- return mask
559
-
560
540
class NumericBlock (Block ):
561
541
is_numeric = True
562
542
_can_hold_na = True
@@ -659,7 +639,7 @@ def convert(self, convert_dates = True, convert_numeric = True, copy = True):
659
639
values = self .get (c )
660
640
661
641
values = com ._possibly_convert_objects (values , convert_dates = convert_dates , convert_numeric = convert_numeric )
662
- values = values . reshape ((( 1 ,) + values . shape ) )
642
+ values = _block_shape ( values )
663
643
items = self .items .take ([i ])
664
644
newb = make_block (values , items , self .ref_items )
665
645
blocks .append (newb )
@@ -949,23 +929,37 @@ def replace(self, *args, **kwargs):
949
929
950
930
def replace_list (self , src_lst , dest_lst , inplace = False ):
951
931
""" do a list replace """
952
- if not inplace :
953
- self = self .copy ()
954
-
955
- sset = set (src_lst )
956
- if any ([k in sset for k in dest_lst ]):
957
- masks = {}
958
- for s in src_lst :
959
- masks [s ] = [b .values == s for b in self .blocks ]
960
-
961
- for s , d in zip (src_lst , dest_lst ):
962
- [b .putmask (masks [s ][i ], d , inplace = True ) for i , b in
963
- enumerate (self .blocks )]
964
- else :
965
- for s , d in zip (src_lst , dest_lst ):
966
- self .replace (s , d , inplace = True )
967
932
968
- return self
933
+ # figure out our mask a-priori to avoid repeated replacements
934
+ values = self .as_matrix ()
935
+ def comp (s ):
936
+ if isnull (s ):
937
+ return isnull (values )
938
+ return values == s
939
+ masks = [ comp (s ) for i , s in enumerate (src_lst ) ]
940
+
941
+ result_blocks = []
942
+ for blk in self .blocks :
943
+
944
+ # its possible to get multiple result blocks here
945
+ # replace ALWAYS will return a list
946
+ rb = [ blk if inplace else blk .copy () ]
947
+ for i , d in enumerate (dest_lst ):
948
+ new_rb = []
949
+ for b in rb :
950
+ # get our mask for this element, sized to this
951
+ # particular block
952
+ m = masks [i ][b .ref_locs ]
953
+ if m .any ():
954
+ new_rb .extend (b .putmask (m , d , inplace = True ))
955
+ else :
956
+ new_rb .append (b )
957
+ rb = new_rb
958
+ result_blocks .extend (rb )
959
+
960
+ bm = self .__class__ (result_blocks , self .axes )
961
+ bm ._consolidate_inplace ()
962
+ return bm
969
963
970
964
def is_consolidated (self ):
971
965
"""
@@ -1302,8 +1296,7 @@ def set(self, item, value):
1302
1296
Set new item in-place. Does not consolidate. Adds new Block if not
1303
1297
contained in the current set of items
1304
1298
"""
1305
- if value .ndim == self .ndim - 1 :
1306
- value = value .reshape ((1 ,) + value .shape )
1299
+ value = _block_shape (value ,self .ndim - 1 )
1307
1300
if value .shape [1 :] != self .shape [1 :]:
1308
1301
raise AssertionError ('Shape of new values must be compatible '
1309
1302
'with manager shape' )
@@ -1873,6 +1866,14 @@ def _merge_blocks(blocks, items):
1873
1866
return new_block .reindex_items_from (items )
1874
1867
1875
1868
1869
+ def _block_shape (values , ndim = 1 , shape = None ):
1870
+ """ guarantee the shape of the values to be at least 1 d """
1871
+ if values .ndim == ndim :
1872
+ if shape is None :
1873
+ shape = values .shape
1874
+ values = values .reshape (tuple ((1 ,) + shape ))
1875
+ return values
1876
+
1876
1877
def _vstack (to_stack ):
1877
1878
if all (x .dtype == _NS_DTYPE for x in to_stack ):
1878
1879
# work around NumPy 1.6 bug
0 commit comments