Skip to content

Commit 98f51f5

Browse files
jseaboldjreback
authored andcommitted
ENH: Allow longtable in to_latex. Closes #6616.
DOC: Make note about booktabs package. DOC: Add longtable to release notes.
1 parent 42ef6fd commit 98f51f5

File tree

4 files changed

+79
-14
lines changed

4 files changed

+79
-14
lines changed

doc/source/v0.14.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ There are no deprecations of prior behavior in 0.14.0
286286
Enhancements
287287
~~~~~~~~~~~~
288288

289+
- ``DataFrame.to_latex`` now takes a longtable keyword, which if True will return a table in a longtable environment. (:issue:`6617`)
289290
- pd.read_clipboard will, if 'sep' is unspecified, try to detect data copied from a spreadsheet
290291
and parse accordingly. (:issue:`6223`)
291292
- ``plot(legend='reverse')`` will now reverse the order of legend labels for

pandas/core/format.py

+27-9
Original file line numberDiff line numberDiff line change
@@ -423,10 +423,12 @@ def _join_multiline(self, *strcols):
423423
st = ed
424424
return '\n\n'.join(str_lst)
425425

426-
def to_latex(self, force_unicode=None, column_format=None):
426+
def to_latex(self, force_unicode=None, column_format=None,
427+
longtable=False):
427428
"""
428-
Render a DataFrame to a LaTeX tabular environment output.
429+
Render a DataFrame to a LaTeX tabular/longtable environment output.
429430
"""
431+
#TODO: column_format is not settable in df.to_latex
430432
def get_col_type(dtype):
431433
if issubclass(dtype.type, np.number):
432434
return 'r'
@@ -460,14 +462,27 @@ def get_col_type(dtype):
460462
raise AssertionError('column_format must be str or unicode, not %s'
461463
% type(column_format))
462464

463-
def write(buf, frame, column_format, strcols):
464-
buf.write('\\begin{tabular}{%s}\n' % column_format)
465-
buf.write('\\toprule\n')
465+
def write(buf, frame, column_format, strcols, longtable=False):
466+
if not longtable:
467+
buf.write('\\begin{tabular}{%s}\n' % column_format)
468+
buf.write('\\toprule\n')
469+
else:
470+
buf.write('\\begin{longtable}{%s}\n' % column_format)
471+
buf.write('\\toprule\n')
466472

467473
nlevels = frame.index.nlevels
468474
for i, row in enumerate(zip(*strcols)):
469475
if i == nlevels:
470476
buf.write('\\midrule\n') # End of header
477+
if longtable:
478+
buf.write('\\endhead\n')
479+
buf.write('\\midrule\n')
480+
buf.write('\\multicolumn{3}{r}{{Continued on next '
481+
'page}} \\\\\n')
482+
buf.write('\midrule\n')
483+
buf.write('\endfoot\n\n')
484+
buf.write('\\bottomrule\n')
485+
buf.write('\\endlastfoot\n')
471486
crow = [(x.replace('\\', '\\textbackslash') # escape backslashes first
472487
.replace('_', '\\_')
473488
.replace('%', '\\%')
@@ -481,14 +496,17 @@ def write(buf, frame, column_format, strcols):
481496
buf.write(' & '.join(crow))
482497
buf.write(' \\\\\n')
483498

484-
buf.write('\\bottomrule\n')
485-
buf.write('\\end{tabular}\n')
499+
if not longtable:
500+
buf.write('\\bottomrule\n')
501+
buf.write('\\end{tabular}\n')
502+
else:
503+
buf.write('\\end{longtable}\n')
486504

487505
if hasattr(self.buf, 'write'):
488-
write(self.buf, frame, column_format, strcols)
506+
write(self.buf, frame, column_format, strcols, longtable)
489507
elif isinstance(self.buf, compat.string_types):
490508
with open(self.buf, 'w') as f:
491-
write(f, frame, column_format, strcols)
509+
write(f, frame, column_format, strcols, longtable)
492510
else:
493511
raise TypeError('buf is not a file name and it has no write '
494512
'method')

pandas/core/frame.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -1380,15 +1380,18 @@ def to_html(self, buf=None, columns=None, col_space=None, colSpace=None,
13801380
def to_latex(self, buf=None, columns=None, col_space=None, colSpace=None,
13811381
header=True, index=True, na_rep='NaN', formatters=None,
13821382
float_format=None, sparsify=None, index_names=True,
1383-
bold_rows=True, force_unicode=None):
1383+
bold_rows=True, force_unicode=None, longtable=False):
13841384
"""
1385-
Render a DataFrame to a tabular environment table.
1386-
You can splice this into a LaTeX document.
1385+
Render a DataFrame to a tabular environment table. You can splice
1386+
this into a LaTeX document. Requires \\usepackage(booktabs}.
13871387
13881388
`to_latex`-specific options:
13891389
13901390
bold_rows : boolean, default True
13911391
Make the row labels bold in the output
1392+
longtable : boolean, default False
1393+
Use a longtable environment instead of tabular. Requires adding
1394+
a \\usepackage{longtable} to your LaTeX preamble.
13921395
13931396
"""
13941397

@@ -1409,7 +1412,7 @@ def to_latex(self, buf=None, columns=None, col_space=None, colSpace=None,
14091412
bold_rows=bold_rows,
14101413
sparsify=sparsify,
14111414
index_names=index_names)
1412-
formatter.to_latex()
1415+
formatter.to_latex(longtable=longtable)
14131416

14141417
if buf is None:
14151418
return formatter.buf.getvalue()

pandas/tests/test_format.py

+44-1
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,49 @@ def test_to_latex(self):
16671667
2 & b2 \\
16681668
\bottomrule
16691669
\end{tabular}
1670+
"""
1671+
self.assertEqual(withoutindex_result, withoutindex_expected)
1672+
1673+
def test_to_latex_longtable(self):
1674+
self.frame.to_latex(longtable=True)
1675+
1676+
df = DataFrame({'a': [1, 2],
1677+
'b': ['b1', 'b2']})
1678+
withindex_result = df.to_latex(longtable=True)
1679+
withindex_expected = r"""\begin{longtable}{lrl}
1680+
\toprule
1681+
{} & a & b \\
1682+
\midrule
1683+
\endhead
1684+
\midrule
1685+
\multicolumn{3}{r}{{Continued on next page}} \\
1686+
\midrule
1687+
\endfoot
1688+
1689+
\bottomrule
1690+
\endlastfoot
1691+
0 & 1 & b1 \\
1692+
1 & 2 & b2 \\
1693+
\end{longtable}
1694+
"""
1695+
self.assertEqual(withindex_result, withindex_expected)
1696+
1697+
withoutindex_result = df.to_latex(index=False, longtable=True)
1698+
withoutindex_expected = r"""\begin{longtable}{rl}
1699+
\toprule
1700+
a & b \\
1701+
\midrule
1702+
\endhead
1703+
\midrule
1704+
\multicolumn{3}{r}{{Continued on next page}} \\
1705+
\midrule
1706+
\endfoot
1707+
1708+
\bottomrule
1709+
\endlastfoot
1710+
1 & b1 \\
1711+
2 & b2 \\
1712+
\end{longtable}
16701713
"""
16711714
self.assertEqual(withoutindex_result, withoutindex_expected)
16721715

@@ -1791,7 +1834,7 @@ def test_csv_to_string(self):
17911834
df = DataFrame({'col' : [1,2]})
17921835
expected = ',col\n0,1\n1,2\n'
17931836
self.assertEqual(df.to_csv(), expected)
1794-
1837+
17951838

17961839
class TestSeriesFormatting(tm.TestCase):
17971840
_multiprocess_can_split_ = True

0 commit comments

Comments
 (0)