Skip to content

Commit d1a600f

Browse files
committed
BUG: Don't print stray newline with MultiIndex
Closes gh-6618.
1 parent 58199c5 commit d1a600f

File tree

4 files changed

+40
-24
lines changed

4 files changed

+40
-24
lines changed

doc/source/whatsnew/v0.19.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1355,6 +1355,7 @@ Bug Fixes
13551355
- Bug in using NumPy ufunc with ``PeriodIndex`` to add or subtract integer raise ``IncompatibleFrequency``. Note that using standard operator like ``+`` or ``-`` is recommended, because standard operators use more efficient path (:issue:`13980`)
13561356
- Bug in operations on ``NaT`` returning ``float`` instead of ``datetime64[ns]`` (:issue:`12941`)
13571357
- Bug in ``Series`` flexible arithmetic methods (like ``.add()``) raises ``ValueError`` when ``axis=None`` (:issue:`13894`)
1358+
- Bug in ``DataFrame.to_csv()`` with ``MultiIndex`` columns in which a stray empty line was added (:issue:`6618`)
13581359

13591360

13601361
- Bug in ``Index`` raises ``KeyError`` displaying incorrect column when column is not in the df and columns contains duplicate values (:issue:`13822`)

pandas/formats/format.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -1524,9 +1524,9 @@ def _save_header(self):
15241524

15251525
if not has_mi_columns:
15261526
encoded_labels += list(write_cols)
1527-
1528-
# write out the mi
1529-
if has_mi_columns:
1527+
writer.writerow(encoded_labels)
1528+
else:
1529+
# write out the mi
15301530
columns = obj.columns
15311531

15321532
# write out the names for each level, then ALL of the values for
@@ -1547,12 +1547,12 @@ def _save_header(self):
15471547

15481548
writer.writerow(col_line)
15491549

1550-
# add blanks for the columns, so that we
1551-
# have consistent seps
1552-
encoded_labels.extend([''] * len(columns))
1553-
1554-
# write out the index label line
1555-
writer.writerow(encoded_labels)
1550+
# Write out the index line if it's not empty.
1551+
# Otherwise, we will print out an extraneous
1552+
# blank line between the mi and the data rows.
1553+
if encoded_labels and set(encoded_labels) != set(['']):
1554+
encoded_labels.extend([''] * len(columns))
1555+
writer.writerow(encoded_labels)
15561556

15571557
def _save(self):
15581558

pandas/tests/formats/test_format.py

+27
Original file line numberDiff line numberDiff line change
@@ -3327,6 +3327,33 @@ def test_to_csv_date_format(self):
33273327
self.assertEqual(df_sec_grouped.mean().to_csv(date_format='%Y-%m-%d'),
33283328
expected_ymd_sec)
33293329

3330+
def test_to_csv_multi_index(self):
3331+
# see gh-6618
3332+
df = DataFrame([1], columns=pd.MultiIndex.from_arrays([[1],[2]]))
3333+
3334+
exp = ",1\n,2\n0,1\n"
3335+
self.assertEqual(df.to_csv(), exp)
3336+
3337+
exp = "1\n2\n1\n"
3338+
self.assertEqual(df.to_csv(index=False), exp)
3339+
3340+
df = DataFrame([1], columns=pd.MultiIndex.from_arrays([[1],[2]]),
3341+
index=pd.MultiIndex.from_arrays([[1],[2]]))
3342+
3343+
exp = ",,1\n,,2\n1,2,1\n"
3344+
self.assertEqual(df.to_csv(), exp)
3345+
3346+
exp = "1\n2\n1\n"
3347+
self.assertEqual(df.to_csv(index=False), exp)
3348+
3349+
df = DataFrame([1], columns=pd.MultiIndex.from_arrays([['foo'],['bar']]))
3350+
3351+
exp = ",foo\n,bar\n0,1\n"
3352+
self.assertEqual(df.to_csv(), exp)
3353+
3354+
exp = "foo\nbar\n1\n"
3355+
self.assertEqual(df.to_csv(index=False), exp)
3356+
33303357
def test_period(self):
33313358
# GH 12615
33323359
df = pd.DataFrame({'A': pd.period_range('2013-01',

pandas/tests/frame/test_to_csv.py

+3-15
Original file line numberDiff line numberDiff line change
@@ -587,21 +587,9 @@ def _make_frame(names=None):
587587
df = _make_frame(True)
588588
df.to_csv(path, tupleize_cols=False)
589589

590-
# catch invalid headers
591-
with assertRaisesRegexp(CParserError,
592-
'Passed header=\[0,1,2\] are too many '
593-
'rows for this multi_index of columns'):
594-
read_csv(path, tupleize_cols=False,
595-
header=lrange(3), index_col=0)
596-
597-
with assertRaisesRegexp(CParserError,
598-
'Passed header=\[0,1,2,3,4,5,6\], len of '
599-
'7, but only 6 lines in file'):
600-
read_csv(path, tupleize_cols=False,
601-
header=lrange(7), index_col=0)
602-
603-
for i in [4, 5, 6]:
604-
with tm.assertRaises(CParserError):
590+
for i in [5, 6, 7]:
591+
msg = 'len of {i}, but only 5 lines in file'.format(i=i)
592+
with assertRaisesRegexp(CParserError, msg):
605593
read_csv(path, tupleize_cols=False,
606594
header=lrange(i), index_col=0)
607595

0 commit comments

Comments
 (0)