Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

ENH: Allow use of longtable package in to_latex. #6617

Closed
wants to merge 3 commits into
from
Jump to file or symbol
Failed to load files and symbols.
+79 −14
Split
View
@@ -286,6 +286,7 @@ There are no deprecations of prior behavior in 0.14.0
Enhancements
~~~~~~~~~~~~
+- ``DataFrame.to_latex`` now takes a longtable keyword, which if True will return a table in a longtable environment. (:issue:`6617`)
- pd.read_clipboard will, if 'sep' is unspecified, try to detect data copied from a spreadsheet
and parse accordingly. (:issue:`6223`)
- ``plot(legend='reverse')`` will now reverse the order of legend labels for
View
@@ -423,10 +423,12 @@ def _join_multiline(self, *strcols):
st = ed
return '\n\n'.join(str_lst)
- def to_latex(self, force_unicode=None, column_format=None):
+ def to_latex(self, force_unicode=None, column_format=None,
+ longtable=False):
"""
- Render a DataFrame to a LaTeX tabular environment output.
+ Render a DataFrame to a LaTeX tabular/longtable environment output.
"""
+ #TODO: column_format is not settable in df.to_latex
def get_col_type(dtype):
if issubclass(dtype.type, np.number):
return 'r'
@@ -460,14 +462,27 @@ def get_col_type(dtype):
raise AssertionError('column_format must be str or unicode, not %s'
% type(column_format))
- def write(buf, frame, column_format, strcols):
- buf.write('\\begin{tabular}{%s}\n' % column_format)
- buf.write('\\toprule\n')
+ def write(buf, frame, column_format, strcols, longtable=False):
+ if not longtable:
+ buf.write('\\begin{tabular}{%s}\n' % column_format)
+ buf.write('\\toprule\n')
+ else:
+ buf.write('\\begin{longtable}{%s}\n' % column_format)
+ buf.write('\\toprule\n')
nlevels = frame.index.nlevels
for i, row in enumerate(zip(*strcols)):
if i == nlevels:
buf.write('\\midrule\n') # End of header
+ if longtable:
+ buf.write('\\endhead\n')
+ buf.write('\\midrule\n')
+ buf.write('\\multicolumn{3}{r}{{Continued on next '
+ 'page}} \\\\\n')
+ buf.write('\midrule\n')
+ buf.write('\endfoot\n\n')
+ buf.write('\\bottomrule\n')
+ buf.write('\\endlastfoot\n')
crow = [(x.replace('\\', '\\textbackslash') # escape backslashes first
.replace('_', '\\_')
.replace('%', '\\%')
@@ -481,14 +496,17 @@ def write(buf, frame, column_format, strcols):
buf.write(' & '.join(crow))
buf.write(' \\\\\n')
- buf.write('\\bottomrule\n')
- buf.write('\\end{tabular}\n')
+ if not longtable:
+ buf.write('\\bottomrule\n')
+ buf.write('\\end{tabular}\n')
+ else:
+ buf.write('\\end{longtable}\n')
if hasattr(self.buf, 'write'):
- write(self.buf, frame, column_format, strcols)
+ write(self.buf, frame, column_format, strcols, longtable)
elif isinstance(self.buf, compat.string_types):
with open(self.buf, 'w') as f:
- write(f, frame, column_format, strcols)
+ write(f, frame, column_format, strcols, longtable)
else:
raise TypeError('buf is not a file name and it has no write '
'method')
View
@@ -1380,15 +1380,18 @@ def to_html(self, buf=None, columns=None, col_space=None, colSpace=None,
def to_latex(self, buf=None, columns=None, col_space=None, colSpace=None,
header=True, index=True, na_rep='NaN', formatters=None,
float_format=None, sparsify=None, index_names=True,
- bold_rows=True, force_unicode=None):
+ bold_rows=True, force_unicode=None, longtable=False):
"""
- Render a DataFrame to a tabular environment table.
- You can splice this into a LaTeX document.
+ Render a DataFrame to a tabular environment table. You can splice
+ this into a LaTeX document. Requires \\usepackage(booktabs}.
`to_latex`-specific options:
bold_rows : boolean, default True
Make the row labels bold in the output
+ longtable : boolean, default False
+ Use a longtable environment instead of tabular. Requires adding
+ a \\usepackage{longtable} to your LaTeX preamble.
@jreback

jreback Mar 12, 2014

Contributor

@jorisvandenbossche I think this is being interpreted as unicode in the doc-string, maybe use a r as a raw string?

@jorisvandenbossche

jorisvandenbossche Mar 12, 2014

Owner

You can do one of both (result is the same I think), using r""" of the double backslash. Seems only a problem with python 3

@jseabold

jseabold Mar 12, 2014

Contributor

Yeah that's equivalent. I just wasn't sure if the Appender magic decorator was doing this somehow. I didn't realize I had missed the other one.

"""
@@ -1409,7 +1412,7 @@ def to_latex(self, buf=None, columns=None, col_space=None, colSpace=None,
bold_rows=bold_rows,
sparsify=sparsify,
index_names=index_names)
- formatter.to_latex()
+ formatter.to_latex(longtable=longtable)
if buf is None:
return formatter.buf.getvalue()
@@ -1670,6 +1670,49 @@ def test_to_latex(self):
"""
self.assertEqual(withoutindex_result, withoutindex_expected)
+ def test_to_latex_longtable(self):
+ self.frame.to_latex(longtable=True)
+
+ df = DataFrame({'a': [1, 2],
+ 'b': ['b1', 'b2']})
+ withindex_result = df.to_latex(longtable=True)
+ withindex_expected = r"""\begin{longtable}{lrl}
+\toprule
+{} & a & b \\
+\midrule
+\endhead
+\midrule
+\multicolumn{3}{r}{{Continued on next page}} \\
+\midrule
+\endfoot
+
+\bottomrule
+\endlastfoot
+0 & 1 & b1 \\
+1 & 2 & b2 \\
+\end{longtable}
+"""
+ self.assertEqual(withindex_result, withindex_expected)
+
+ withoutindex_result = df.to_latex(index=False, longtable=True)
+ withoutindex_expected = r"""\begin{longtable}{rl}
+\toprule
+ a & b \\
+\midrule
+\endhead
+\midrule
+\multicolumn{3}{r}{{Continued on next page}} \\
+\midrule
+\endfoot
+
+\bottomrule
+\endlastfoot
+ 1 & b1 \\
+ 2 & b2 \\
+\end{longtable}
+"""
+ self.assertEqual(withoutindex_result, withoutindex_expected)
+
def test_to_latex_escape_special_chars(self):
special_characters = ['&','%','$','#','_',
'{','}','~','^','\\']
@@ -1791,7 +1834,7 @@ def test_csv_to_string(self):
df = DataFrame({'col' : [1,2]})
expected = ',col\n0,1\n1,2\n'
self.assertEqual(df.to_csv(), expected)
-
+
class TestSeriesFormatting(tm.TestCase):
_multiprocess_can_split_ = True