Skip to content

Commit 5bf7f9a

Browse files
toobazjreback
authored andcommitted
BUG: support "fill_value" for ".unstack()" called with list of levels (#17887)
closes #13971
1 parent 9092445 commit 5bf7f9a

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

doc/source/whatsnew/v0.21.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,7 @@ Reshaping
10101010
- Fixes regression when sorting by multiple columns on a ``datetime64`` dtype ``Series`` with ``NaT`` values (:issue:`16836`)
10111011
- Bug in :func:`pivot_table` where the result's columns did not preserve the categorical dtype of ``columns`` when ``dropna`` was ``False`` (:issue:`17842`)
10121012
- Bug in ``DataFrame.drop_duplicates`` where dropping with non-unique column names raised a ``ValueError`` (:issue:`17836`)
1013+
- Bug in :func:`unstack` which, when called on a list of levels, would discard the ``fillna`` argument (:issue:`13971`)
10131014

10141015
Numeric
10151016
^^^^^^^

pandas/core/reshape/reshape.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def get_new_index(self):
292292
names=self.new_index_names, verify_integrity=False)
293293

294294

295-
def _unstack_multiple(data, clocs):
295+
def _unstack_multiple(data, clocs, fill_value=None):
296296
if len(clocs) == 0:
297297
return data
298298

@@ -330,7 +330,7 @@ def _unstack_multiple(data, clocs):
330330
if isinstance(data, Series):
331331
dummy = data.copy()
332332
dummy.index = dummy_index
333-
unstacked = dummy.unstack('__placeholder__')
333+
unstacked = dummy.unstack('__placeholder__', fill_value=fill_value)
334334
new_levels = clevels
335335
new_names = cnames
336336
new_labels = recons_labels
@@ -347,7 +347,7 @@ def _unstack_multiple(data, clocs):
347347
dummy = data.copy()
348348
dummy.index = dummy_index
349349

350-
unstacked = dummy.unstack('__placeholder__')
350+
unstacked = dummy.unstack('__placeholder__', fill_value=fill_value)
351351
if isinstance(unstacked, Series):
352352
unstcols = unstacked.index
353353
else:
@@ -460,7 +460,7 @@ def unstack(obj, level, fill_value=None):
460460
if len(level) != 1:
461461
# _unstack_multiple only handles MultiIndexes,
462462
# and isn't needed for a single level
463-
return _unstack_multiple(obj, level)
463+
return _unstack_multiple(obj, level, fill_value=fill_value)
464464
else:
465465
level = level[0]
466466

pandas/tests/frame/test_reshape.py

+31-7
Original file line numberDiff line numberDiff line change
@@ -116,22 +116,22 @@ def test_pivot_index_none(self):
116116
tm.assert_frame_equal(result, expected)
117117

118118
def test_stack_unstack(self):
119-
f = self.frame.copy()
120-
f[:] = np.arange(np.prod(f.shape)).reshape(f.shape)
119+
df = self.frame.copy()
120+
df[:] = np.arange(np.prod(df.shape)).reshape(df.shape)
121121

122-
stacked = f.stack()
122+
stacked = df.stack()
123123
stacked_df = DataFrame({'foo': stacked, 'bar': stacked})
124124

125125
unstacked = stacked.unstack()
126126
unstacked_df = stacked_df.unstack()
127127

128-
assert_frame_equal(unstacked, f)
129-
assert_frame_equal(unstacked_df['bar'], f)
128+
assert_frame_equal(unstacked, df)
129+
assert_frame_equal(unstacked_df['bar'], df)
130130

131131
unstacked_cols = stacked.unstack(0)
132132
unstacked_cols_df = stacked_df.unstack(0)
133-
assert_frame_equal(unstacked_cols.T, f)
134-
assert_frame_equal(unstacked_cols_df['bar'].T, f)
133+
assert_frame_equal(unstacked_cols.T, df)
134+
assert_frame_equal(unstacked_cols_df['bar'].T, df)
135135

136136
def test_unstack_fill(self):
137137

@@ -154,6 +154,30 @@ def test_unstack_fill(self):
154154
index=['x', 'y', 'z'], dtype=np.float)
155155
assert_frame_equal(result, expected)
156156

157+
# GH #13971: fill_value when unstacking multiple levels:
158+
df = DataFrame({'x': ['a', 'a', 'b'],
159+
'y': ['j', 'k', 'j'],
160+
'z': [0, 1, 2],
161+
'w': [0, 1, 2]}).set_index(['x', 'y', 'z'])
162+
unstacked = df.unstack(['x', 'y'], fill_value=0)
163+
key = ('w', 'b', 'j')
164+
expected = unstacked[key]
165+
result = pd.Series([0, 0, 2], index=unstacked.index, name=key)
166+
assert_series_equal(result, expected)
167+
168+
stacked = unstacked.stack(['x', 'y'])
169+
stacked.index = stacked.index.reorder_levels(df.index.names)
170+
# Workaround for GH #17886 (unnecessarily casts to float):
171+
stacked = stacked.astype(np.int64)
172+
result = stacked.loc[df.index]
173+
assert_frame_equal(result, df)
174+
175+
# From a series
176+
s = df['w']
177+
result = s.unstack(['x', 'y'], fill_value=0)
178+
expected = unstacked['w']
179+
assert_frame_equal(result, expected)
180+
157181
def test_unstack_fill_frame(self):
158182

159183
# From a dataframe

0 commit comments

Comments
 (0)