Skip to content

Commit e082eb2

Browse files
yui-knkjreback
authored andcommitted
BUG: pivot_table always returns a DataFrame
Before this commit, if * `values` is not list like * `columns` is `None` * `aggfunc` is not instance of `list` `pivot_table` returns a `Series`. This commit adds checking for `columns.nlevels` is greater than 1 to prevent from casting `table` to a `Series`. This will fix #4386. DOC: add docs for #13554
1 parent 2522efa commit e082eb2

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

doc/source/whatsnew/v0.20.0.txt

+32
Original file line numberDiff line numberDiff line change
@@ -1287,6 +1287,38 @@ joins, :meth:`DataFrame.join` and :func:`merge`, and the ``.align`` methods.
12871287

12881288
left.join(right, how='inner')
12891289

1290+
.. _whatsnew_0200.api_breaking.pivot_table:
1291+
1292+
Pivot Table always returns a DataFrame
1293+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1294+
1295+
The documentation for :meth:`pivot_table` states that a ``DataFrame`` is *always* returned. Here a bug
1296+
is fixed that allowed this to return a ``Series`` under a narrow circumstance. (:issue:`4386`)
1297+
1298+
.. ipython:: python
1299+
1300+
df = DataFrame({'col1': [3, 4, 5],
1301+
'col2': ['C', 'D', 'E'],
1302+
'col3': [1, 3, 9]})
1303+
df
1304+
1305+
Previous Behavior:
1306+
1307+
.. code-block:: ipython
1308+
1309+
In [2]: df.pivot_table('col1', index=['col3', 'col2'], aggfunc=np.sum)
1310+
Out[2]:
1311+
col3 col2
1312+
1 C 3
1313+
3 D 4
1314+
9 E 5
1315+
Name: col1, dtype: int64
1316+
1317+
New Behavior:
1318+
1319+
.. ipython:: python
1320+
1321+
df.pivot_table('col1', index=['col3', 'col2'], aggfunc=np.sum)
12901322

12911323
.. _whatsnew_0200.api:
12921324

pandas/core/reshape/pivot.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ def pivot_table(data, values=None, index=None, columns=None, aggfunc='mean',
170170
margins_name=margins_name)
171171

172172
# discard the top level
173-
if values_passed and not values_multi and not table.empty:
173+
if values_passed and not values_multi and not table.empty and \
174+
(table.columns.nlevels > 1):
174175
table = table[values[0]]
175176

176177
if len(index) == 0 and len(columns) > 0:

pandas/tests/reshape/test_pivot.py

+38
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,44 @@ def test_categorical_pivot_index_ordering(self):
940940
columns=expected_columns)
941941
tm.assert_frame_equal(result, expected)
942942

943+
def test_pivot_table_not_series(self):
944+
# GH 4386
945+
# pivot_table always returns a DataFrame
946+
# when values is not list like and columns is None
947+
# and aggfunc is not instance of list
948+
df = DataFrame({'col1': [3, 4, 5],
949+
'col2': ['C', 'D', 'E'],
950+
'col3': [1, 3, 9]})
951+
952+
result = df.pivot_table('col1', index=['col3', 'col2'], aggfunc=np.sum)
953+
m = MultiIndex.from_arrays([[1, 3, 9],
954+
['C', 'D', 'E']],
955+
names=['col3', 'col2'])
956+
expected = DataFrame([3, 4, 5],
957+
index=m, columns=['col1'])
958+
959+
tm.assert_frame_equal(result, expected)
960+
961+
result = df.pivot_table(
962+
'col1', index='col3', columns='col2', aggfunc=np.sum
963+
)
964+
expected = DataFrame([[3, np.NaN, np.NaN],
965+
[np.NaN, 4, np.NaN],
966+
[np.NaN, np.NaN, 5]],
967+
index=Index([1, 3, 9], name='col3'),
968+
columns=Index(['C', 'D', 'E'], name='col2'))
969+
970+
tm.assert_frame_equal(result, expected)
971+
972+
result = df.pivot_table('col1', index='col3', aggfunc=[np.sum])
973+
m = MultiIndex.from_arrays([['sum'],
974+
['col1']])
975+
expected = DataFrame([3, 4, 5],
976+
index=Index([1, 3, 9], name='col3'),
977+
columns=m)
978+
979+
tm.assert_frame_equal(result, expected)
980+
943981

944982
class TestCrosstab(tm.TestCase):
945983

0 commit comments

Comments
 (0)