Skip to content

Commit 3d515dd

Browse files
committed
ENH: Raise error on trying to write excel file with a MultiIndexed DataFrame. closes #9794
1 parent cef3c85 commit 3d515dd

File tree

3 files changed

+54
-25
lines changed

3 files changed

+54
-25
lines changed

doc/source/whatsnew/v0.16.1.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Enhancements
5353

5454
- Allow timedelta string conversion when leading zero is missing from time definition, ie `0:00:00` vs `00:00:00`. (:issue:`9570`)
5555

56+
- Trying to write an excel file now raises NotImplementedError if the DataFrame has a MultiIndex instead of writing a broken Excel file. (:issue:`9794`)
57+
5658
.. _whatsnew_0161.api:
5759

5860
API changes
@@ -137,7 +139,6 @@ Bug Fixes
137139

138140

139141
- Bug in unequal comparisons between categorical data and a scalar, which was not in the categories (e.g. ``Series(Categorical(list("abc"), ordered=True)) > "d"``. This returned ``False`` for all elements, but now raises a ``TypeError``. Equality comparisons also now return ``False`` for ``==`` and ``True`` for ``!=``. (:issue:`9848`)
140-
141142
- Bug in DataFrame ``__setitem__`` when right hand side is a dictionary (:issue:`9874`)
142143
- Bug in ``where`` when dtype is ``datetime64/timedelta64``, but dtype of other is not (:issue:`9804`)
143144
- Bug in ``MultiIndex.sortlevel()`` results in unicode level name breaks (:issue:`9875`)

pandas/core/frame.py

+3
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,9 @@ def to_excel(self, excel_writer, sheet_name='Sheet1', na_rep='',
12441244
>>> writer.save()
12451245
"""
12461246
from pandas.io.excel import ExcelWriter
1247+
if self.columns.nlevels > 1:
1248+
raise NotImplementedError("Writing as Excel with a MultiIndex is "
1249+
"not yet implemented.")
12471250

12481251
need_save = False
12491252
if encoding == None:

pandas/io/tests/test_excel.py

+49-24
Original file line numberDiff line numberDiff line change
@@ -1133,30 +1133,32 @@ def roundtrip(df, header=True, parser_hdr=0):
11331133
nrows = 5
11341134
ncols = 3
11351135

1136-
for i in range(1, 4): # row multindex upto nlevel=3
1137-
for j in range(1, 4): # col ""
1138-
df = mkdf(nrows, ncols, r_idx_nlevels=i, c_idx_nlevels=j)
1139-
res = roundtrip(df)
1140-
# shape
1141-
self.assertEqual(res.shape, (nrows, ncols + i))
1142-
1143-
# no nans
1144-
for r in range(len(res.index)):
1145-
for c in range(len(res.columns)):
1146-
self.assertTrue(res.ix[r, c] is not np.nan)
1147-
1148-
for i in range(1, 4): # row multindex upto nlevel=3
1149-
for j in range(1, 4): # col ""
1150-
df = mkdf(nrows, ncols, r_idx_nlevels=i, c_idx_nlevels=j)
1151-
res = roundtrip(df, False)
1152-
# shape
1153-
self.assertEqual(res.shape, (
1154-
nrows - 1, ncols + i)) # first row taken as columns
1155-
1156-
# no nans
1157-
for r in range(len(res.index)):
1158-
for c in range(len(res.columns)):
1159-
self.assertTrue(res.ix[r, c] is not np.nan)
1136+
with tm.assertRaises(NotImplementedError):
1137+
for i in range(1, 4): # row multindex upto nlevel=3
1138+
for j in range(1, 4): # col ""
1139+
df = mkdf(nrows, ncols, r_idx_nlevels=i, c_idx_nlevels=j)
1140+
res = roundtrip(df)
1141+
1142+
self.assertEqual(res.shape, (nrows, ncols + i))
1143+
1144+
# no nans
1145+
for r in range(len(res.index)):
1146+
for c in range(len(res.columns)):
1147+
self.assertTrue(res.ix[r, c] is not np.nan)
1148+
1149+
with tm.assertRaises(NotImplementedError):
1150+
for i in range(1, 4): # row multindex upto nlevel=3
1151+
for j in range(1, 4): # col ""
1152+
df = mkdf(nrows, ncols, r_idx_nlevels=i, c_idx_nlevels=j)
1153+
res = roundtrip(df, False)
1154+
# shape
1155+
self.assertEqual(res.shape, (
1156+
nrows - 1, ncols + i)) # first row taken as columns
1157+
1158+
# no nans
1159+
for r in range(len(res.index)):
1160+
for c in range(len(res.columns)):
1161+
self.assertTrue(res.ix[r, c] is not np.nan)
11601162

11611163
res = roundtrip(DataFrame([0]))
11621164
self.assertEqual(res.shape, (1, 1))
@@ -1394,6 +1396,29 @@ class XlwtTests(ExcelWriterBase, tm.TestCase):
13941396
engine_name = 'xlwt'
13951397
check_skip = staticmethod(_skip_if_no_xlwt)
13961398

1399+
def test_excel_raise_not_implemented_error_on_multiindex_columns(self):
1400+
_skip_if_no_xlwt()
1401+
#MultiIndex as columns is not yet implemented 9794
1402+
cols = pd.MultiIndex.from_tuples([('site',''),
1403+
('2014','height'),
1404+
('2014','weight')])
1405+
df = pd.DataFrame(np.random.randn(10,3), columns=cols)
1406+
with tm.assertRaises(NotImplementedError):
1407+
with ensure_clean(self.ext) as path:
1408+
df.to_excel(path, index=False)
1409+
1410+
def test_excel_multiindex_index(self):
1411+
_skip_if_no_xlwt()
1412+
#MultiIndex as index works so assert no error #9794
1413+
cols = pd.MultiIndex.from_tuples([('site',''),
1414+
('2014','height'),
1415+
('2014','weight')])
1416+
df = pd.DataFrame(np.random.randn(3,10), index=cols)
1417+
with ensure_clean(self.ext) as path:
1418+
df.to_excel(path, index=False)
1419+
1420+
1421+
13971422
def test_to_excel_styleconverter(self):
13981423
_skip_if_no_xlwt()
13991424

0 commit comments

Comments
 (0)