10
10
from pandas .core .dtypes .common import (
11
11
_ensure_platform_int ,
12
12
is_list_like , is_bool_dtype ,
13
- needs_i8_conversion )
13
+ needs_i8_conversion , is_sparse )
14
14
from pandas .core .dtypes .cast import maybe_promote
15
15
from pandas .core .dtypes .missing import notnull
16
16
import pandas .core .dtypes .concat as _concat
@@ -75,10 +75,15 @@ def __init__(self, values, index, level=-1, value_columns=None,
75
75
fill_value = None ):
76
76
77
77
self .is_categorical = None
78
+ self .is_sparse = is_sparse (values )
78
79
if values .ndim == 1 :
79
80
if isinstance (values , Categorical ):
80
81
self .is_categorical = values
81
82
values = np .array (values )
83
+ elif self .is_sparse :
84
+ # XXX: Makes SparseArray *dense*, but it's supposedly
85
+ # a single column at a time, so it's "doable"
86
+ values = values .values
82
87
values = values [:, np .newaxis ]
83
88
self .values = values
84
89
self .value_columns = value_columns
@@ -177,7 +182,8 @@ def get_result(self):
177
182
ordered = ordered )
178
183
for i in range (values .shape [- 1 ])]
179
184
180
- return DataFrame (values , index = index , columns = columns )
185
+ klass = SparseDataFrame if self .is_sparse else DataFrame
186
+ return klass (values , index = index , columns = columns )
181
187
182
188
def get_new_values (self ):
183
189
values = self .values
@@ -463,15 +469,15 @@ def _unstack_frame(obj, level, fill_value=None):
463
469
from pandas .core .internals import BlockManager , make_block
464
470
465
471
if obj ._is_mixed_type :
466
- unstacker = _Unstacker (np .empty (obj . shape , dtype = bool ), # dummy
472
+ unstacker = _Unstacker (np .empty (( 0 , 0 ) ), # dummy
467
473
obj .index , level = level ,
468
474
value_columns = obj .columns )
469
475
new_columns = unstacker .get_new_columns ()
470
476
new_index = unstacker .get_new_index ()
471
477
new_axes = [new_columns , new_index ]
472
478
473
479
new_blocks = []
474
- mask_blocks = []
480
+ mask_blocks = np . zeros_like ( new_columns , dtype = bool )
475
481
for blk in obj ._data .blocks :
476
482
blk_items = obj ._data .items [blk .mgr_locs .indexer ]
477
483
bunstacker = _Unstacker (blk .values .T , obj .index , level = level ,
@@ -481,15 +487,25 @@ def _unstack_frame(obj, level, fill_value=None):
481
487
new_placement = new_columns .get_indexer (new_items )
482
488
new_values , mask = bunstacker .get_new_values ()
483
489
484
- mblk = make_block (mask .T , placement = new_placement )
485
- mask_blocks .append (mblk )
490
+ mask_blocks [new_placement ] = mask .any (0 )
486
491
487
- newb = make_block (new_values .T , placement = new_placement )
488
- new_blocks .append (newb )
492
+ # BlockManager can't handle SparseBlocks with multiple items,
493
+ # so lets make one block for each item
494
+ if is_sparse (blk .values ):
495
+ new_placement = [[i ] for i in new_placement ]
496
+ new_values = new_values .T
497
+ else :
498
+ new_placement = [new_placement ]
499
+ new_values = [new_values .T ]
500
+
501
+ for cols , placement in zip (new_values , new_placement ):
502
+ newb = blk .make_block_same_class (cols , placement = placement )
503
+ new_blocks .append (newb )
489
504
490
- result = DataFrame (BlockManager (new_blocks , new_axes ))
491
- mask_frame = DataFrame (BlockManager (mask_blocks , new_axes ))
492
- return result .loc [:, mask_frame .sum (0 ) > 0 ]
505
+ klass = type (obj )
506
+ assert klass in (SparseDataFrame , DataFrame ), klass
507
+ result = klass (BlockManager (new_blocks , new_axes ))
508
+ return result .loc [:, mask_blocks ]
493
509
else :
494
510
unstacker = _Unstacker (obj .values , obj .index , level = level ,
495
511
value_columns = obj .columns ,
@@ -550,7 +566,9 @@ def factorize(index):
550
566
mask = notnull (new_values )
551
567
new_values = new_values [mask ]
552
568
new_index = new_index [mask ]
553
- return Series (new_values , index = new_index )
569
+
570
+ klass = SparseSeries if isinstance (frame , SparseDataFrame ) else Series
571
+ return klass (new_values , index = new_index )
554
572
555
573
556
574
def stack_multiple (frame , level , dropna = True ):
0 commit comments