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 notna
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
@@ -472,15 +478,15 @@ def _unstack_frame(obj, level, fill_value=None):
472
478
from pandas .core .internals import BlockManager , make_block
473
479
474
480
if obj ._is_mixed_type :
475
- unstacker = _Unstacker (np .empty (obj . shape , dtype = bool ), # dummy
481
+ unstacker = _Unstacker (np .empty (( 0 , 0 ) ), # dummy
476
482
obj .index , level = level ,
477
483
value_columns = obj .columns )
478
484
new_columns = unstacker .get_new_columns ()
479
485
new_index = unstacker .get_new_index ()
480
486
new_axes = [new_columns , new_index ]
481
487
482
488
new_blocks = []
483
- mask_blocks = []
489
+ mask_blocks = np . zeros_like ( new_columns , dtype = bool )
484
490
for blk in obj ._data .blocks :
485
491
blk_items = obj ._data .items [blk .mgr_locs .indexer ]
486
492
bunstacker = _Unstacker (blk .values .T , obj .index , level = level ,
@@ -490,15 +496,25 @@ def _unstack_frame(obj, level, fill_value=None):
490
496
new_placement = new_columns .get_indexer (new_items )
491
497
new_values , mask = bunstacker .get_new_values ()
492
498
493
- mblk = make_block (mask .T , placement = new_placement )
494
- mask_blocks .append (mblk )
499
+ mask_blocks [new_placement ] = mask .any (0 )
495
500
496
- newb = make_block (new_values .T , placement = new_placement )
497
- new_blocks .append (newb )
501
+ # BlockManager can't handle SparseBlocks with multiple items,
502
+ # so lets make one block for each item
503
+ if is_sparse (blk .values ):
504
+ new_placement = [[i ] for i in new_placement ]
505
+ new_values = new_values .T
506
+ else :
507
+ new_placement = [new_placement ]
508
+ new_values = [new_values .T ]
509
+
510
+ for cols , placement in zip (new_values , new_placement ):
511
+ newb = blk .make_block_same_class (cols , placement = placement )
512
+ new_blocks .append (newb )
498
513
499
- result = DataFrame (BlockManager (new_blocks , new_axes ))
500
- mask_frame = DataFrame (BlockManager (mask_blocks , new_axes ))
501
- return result .loc [:, mask_frame .sum (0 ) > 0 ]
514
+ klass = type (obj )
515
+ assert klass in (SparseDataFrame , DataFrame ), klass
516
+ result = klass (BlockManager (new_blocks , new_axes ))
517
+ return result .loc [:, mask_blocks ]
502
518
else :
503
519
unstacker = _Unstacker (obj .values , obj .index , level = level ,
504
520
value_columns = obj .columns ,
@@ -559,7 +575,9 @@ def factorize(index):
559
575
mask = notna (new_values )
560
576
new_values = new_values [mask ]
561
577
new_index = new_index [mask ]
562
- return Series (new_values , index = new_index )
578
+
579
+ klass = type (frame )._constructor_sliced
580
+ return klass (new_values , index = new_index )
563
581
564
582
565
583
def stack_multiple (frame , level , dropna = True ):
0 commit comments