We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
2 parents 5d953e3 + 7ca878e commit db884d9Copy full SHA for db884d9
doc/source/whatsnew/v0.17.1.txt
@@ -87,7 +87,7 @@ Bug Fixes
87
88
- Bug in list-like indexing with a mixed-integer Index (:issue:`11320`)
89
90
-
+- Bug in ``pivot_table`` with ``margins=True`` when indexes are of ``Categorical`` dtype (:issue:`10993`)
91
- Bug in ``DataFrame.plot`` cannot use hex strings colors (:issue:`10299`)
92
93
pandas/core/index.py
@@ -627,6 +627,10 @@ def astype(self, dtype):
627
return Index(self.values.astype(dtype), name=self.name,
628
dtype=dtype)
629
630
+ def _to_safe_for_reshape(self):
631
+ """ convert to object if we are a categorical """
632
+ return self
633
+
634
def to_datetime(self, dayfirst=False):
635
"""
636
For an Index containing strings or datetime.datetime objects, attempt
@@ -3190,6 +3194,10 @@ def duplicated(self, keep='first'):
3190
3194
from pandas.hashtable import duplicated_int64
3191
3195
return duplicated_int64(self.codes.astype('i8'), keep)
3192
3196
3197
3198
3199
+ return self.astype('object')
3200
3193
3201
def get_loc(self, key, method=None):
3202
3203
Get integer location for requested label
@@ -4529,6 +4537,10 @@ def format(self, space=2, sparsify=None, adjoin=True, names=False,
4529
4537
else:
4530
4538
return result_levels
4531
4539
4540
4541
4542
+ return self.set_levels([ i._to_safe_for_reshape() for i in self.levels ])
4543
4532
4544
def to_hierarchical(self, n_repeat, n_shuffle=1):
4533
4545
4534
4546
Return a MultiIndex reshaped to conform to the
pandas/core/internals.py
@@ -3427,6 +3427,9 @@ def insert(self, loc, item, value, allow_duplicates=False):
3427
if not isinstance(loc, int):
3428
raise TypeError("loc must be int")
3429
3430
+ # insert to the axis; this could possibly raise a TypeError
3431
+ new_axis = self.items.insert(loc, item)
3432
3433
block = make_block(values=value,
3434
ndim=self.ndim,
3435
placement=slice(loc, loc+1))
@@ -3449,8 +3452,7 @@ def insert(self, loc, item, value, allow_duplicates=False):
3449
3452
self._blklocs = np.insert(self._blklocs, loc, 0)
3450
3453
self._blknos = np.insert(self._blknos, loc, len(self.blocks))
3451
3454
- self.axes[0] = self.items.insert(loc, item)
3455
+ self.axes[0] = new_axis
3456
self.blocks += (block,)
3457
self._shape = None
3458
pandas/tools/pivot.py
@@ -189,7 +189,13 @@ def _add_margins(table, data, values, rows, cols, aggfunc):
189
margin_dummy = DataFrame(row_margin, columns=[key]).T
190
191
row_names = result.index.names
192
- result = result.append(margin_dummy)
+ try:
193
+ result = result.append(margin_dummy)
194
+ except TypeError:
195
196
+ # we cannot reshape, so coerce the axis
197
+ result.index = result.index._to_safe_for_reshape()
198
199
result.index.names = row_names
200
201
return result
@@ -218,6 +224,7 @@ def _compute_grand_margin(data, values, aggfunc):
218
224
219
225
220
226
def _generate_marginal_results(table, data, values, rows, cols, aggfunc, grand_margin):
227
221
228
if len(cols) > 0:
222
229
# need to "interleave" the margins
223
230
table_pieces = []
@@ -235,7 +242,13 @@ def _all_key(key):
235
242
236
243
# we are going to mutate this, so need to copy!
237
244
piece = piece.copy()
238
- piece[all_key] = margin[key]
245
246
+ piece[all_key] = margin[key]
247
248
249
250
+ piece.set_axis(cat_axis, piece._get_axis(cat_axis)._to_safe_for_reshape())
251
239
252
240
253
table_pieces.append(piece)
241
254
margin_keys.append(all_key)
pandas/tools/tests/test_pivot.py
@@ -719,6 +719,26 @@ def test_crosstab_dropna(self):
719
('two', 'dull'), ('two', 'shiny')])
720
assert_equal(res.columns.values, m.values)
721
722
+ def test_categorical_margins(self):
723
+ # GH 10989
724
+ df = pd.DataFrame({'x': np.arange(8),
725
+ 'y': np.arange(8) // 4,
726
+ 'z': np.arange(8) % 2})
727
728
+ expected = pd.DataFrame([[1.0, 2.0, 1.5],[5, 6, 5.5],[3, 4, 3.5]])
729
+ expected.index = Index([0,1,'All'],name='y')
730
+ expected.columns = Index([0,1,'All'],name='z')
731
732
+ data = df.copy()
733
+ table = data.pivot_table('x', 'y', 'z', margins=True)
734
+ tm.assert_frame_equal(table, expected)
735
736
737
+ data.y = data.y.astype('category')
738
+ data.z = data.z.astype('category')
739
740
741
742
if __name__ == '__main__':
743
import nose
744
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
0 commit comments