Skip to content

Commit 447ac8a

Browse files
jeschwarproost
authored andcommitted
ENH: added optional caption and label arguments to DataFrame.to_latex() (pandas-dev#25437)
* ENH: added optional caption and label support to DataFrame.to_latex() (pandas-dev#25436)
1 parent 242a5ce commit 447ac8a

File tree

5 files changed

+283
-19
lines changed

5 files changed

+283
-19
lines changed

doc/source/whatsnew/v1.0.0.rst

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ including other versions of pandas.
2020

2121
Enhancements
2222
~~~~~~~~~~~~
23-
24-
-
23+
- :meth:`DataFrame.to_latex` now accepts ``caption`` and ``label`` arguments (:issue:`25436`)
2524
-
2625

2726
.. _whatsnew_1000.enhancements.other:

pandas/core/generic.py

+24-5
Original file line numberDiff line numberDiff line change
@@ -2925,15 +2925,21 @@ def to_latex(
29252925
multicolumn=None,
29262926
multicolumn_format=None,
29272927
multirow=None,
2928+
caption=None,
2929+
label=None,
29282930
):
29292931
r"""
2930-
Render an object to a LaTeX tabular environment table.
2932+
Render object to a LaTeX tabular, longtable, or nested table/tabular.
29312933
2932-
Render an object to a tabular environment table. You can splice
2933-
this into a LaTeX document. Requires \usepackage{booktabs}.
2934+
Requires ``\usepackage{booktabs}``. The output can be copy/pasted
2935+
into a main LaTeX document or read from an external file
2936+
with ``\input{table.tex}``.
29342937
29352938
.. versionchanged:: 0.20.2
2936-
Added to Series
2939+
Added to Series.
2940+
2941+
.. versionchanged:: 1.0.0
2942+
Added caption and label arguments.
29372943
29382944
Parameters
29392945
----------
@@ -3002,6 +3008,17 @@ def to_latex(
30023008
from the pandas config module.
30033009
30043010
.. versionadded:: 0.20.0
3011+
3012+
caption : str, optional
3013+
The LaTeX caption to be placed inside ``\caption{}`` in the output.
3014+
3015+
.. versionadded:: 1.0.0
3016+
3017+
label : str, optional
3018+
The LaTeX label to be placed inside ``\label{}`` in the output.
3019+
This is used with ``\ref{}`` in the main ``.tex`` file.
3020+
3021+
.. versionadded:: 1.0.0
30053022
%(returns)s
30063023
See Also
30073024
--------
@@ -3014,7 +3031,7 @@ def to_latex(
30143031
>>> df = pd.DataFrame({'name': ['Raphael', 'Donatello'],
30153032
... 'mask': ['red', 'purple'],
30163033
... 'weapon': ['sai', 'bo staff']})
3017-
>>> print(df.to_latex(index=False)) # doctest: +NORMALIZE_WHITESPACE
3034+
>>> print(df.to_latex(index=False)) # doctest: +NORMALIZE_WHITESPACE
30183035
\begin{tabular}{lll}
30193036
\toprule
30203037
name & mask & weapon \\
@@ -3061,6 +3078,8 @@ def to_latex(
30613078
multicolumn=multicolumn,
30623079
multicolumn_format=multicolumn_format,
30633080
multirow=multirow,
3081+
caption=caption,
3082+
label=label,
30643083
)
30653084

30663085
def to_csv(

pandas/io/formats/format.py

+4
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,8 @@ def to_latex(
888888
multicolumn: bool = False,
889889
multicolumn_format: Optional[str] = None,
890890
multirow: bool = False,
891+
caption: Optional[str] = None,
892+
label: Optional[str] = None,
891893
) -> Optional[str]:
892894
"""
893895
Render a DataFrame to a LaTeX tabular/longtable environment output.
@@ -902,6 +904,8 @@ def to_latex(
902904
multicolumn=multicolumn,
903905
multicolumn_format=multicolumn_format,
904906
multirow=multirow,
907+
caption=caption,
908+
label=label,
905909
).get_result(buf=buf, encoding=encoding)
906910

907911
def _format_col(self, i: int) -> List[str]:

pandas/io/formats/latex.py

+118-10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def __init__(
3636
multicolumn: bool = False,
3737
multicolumn_format: Optional[str] = None,
3838
multirow: bool = False,
39+
caption: Optional[str] = None,
40+
label: Optional[str] = None,
3941
):
4042
self.fmt = formatter
4143
self.frame = self.fmt.frame
@@ -45,11 +47,14 @@ def __init__(
4547
self.multicolumn = multicolumn
4648
self.multicolumn_format = multicolumn_format
4749
self.multirow = multirow
50+
self.caption = caption
51+
self.label = label
4852
self.escape = self.fmt.escape
4953

5054
def write_result(self, buf: IO[str]) -> None:
5155
"""
52-
Render a DataFrame to a LaTeX tabular/longtable environment output.
56+
Render a DataFrame to a LaTeX tabular, longtable, or table/tabular
57+
environment output.
5358
"""
5459

5560
# string representation of the columns
@@ -114,12 +119,12 @@ def pad_empties(x):
114119
"not {typ}".format(typ=type(column_format))
115120
)
116121

117-
if not self.longtable:
118-
buf.write("\\begin{{tabular}}{{{fmt}}}\n".format(fmt=column_format))
119-
buf.write("\\toprule\n")
122+
if self.longtable:
123+
self._write_longtable_begin(buf, column_format)
120124
else:
121-
buf.write("\\begin{{longtable}}{{{fmt}}}\n".format(fmt=column_format))
122-
buf.write("\\toprule\n")
125+
self._write_tabular_begin(buf, column_format)
126+
127+
buf.write("\\toprule\n")
123128

124129
ilevels = self.frame.index.nlevels
125130
clevels = self.frame.columns.nlevels
@@ -183,11 +188,10 @@ def pad_empties(x):
183188
if self.multirow and i < len(strrows) - 1:
184189
self._print_cline(buf, i, len(strcols))
185190

186-
if not self.longtable:
187-
buf.write("\\bottomrule\n")
188-
buf.write("\\end{tabular}\n")
191+
if self.longtable:
192+
self._write_longtable_end(buf)
189193
else:
190-
buf.write("\\end{longtable}\n")
194+
self._write_tabular_end(buf)
191195

192196
def _format_multicolumn(self, row: List[str], ilevels: int) -> List[str]:
193197
r"""
@@ -268,3 +272,107 @@ def _print_cline(self, buf: IO[str], i: int, icol: int) -> None:
268272
buf.write("\\cline{{{cl:d}-{icol:d}}}\n".format(cl=cl[1], icol=icol))
269273
# remove entries that have been written to buffer
270274
self.clinebuf = [x for x in self.clinebuf if x[0] != i]
275+
276+
def _write_tabular_begin(self, buf, column_format):
277+
"""
278+
Write the beginning of a tabular environment or
279+
nested table/tabular environments including caption and label.
280+
281+
Parameters
282+
----------
283+
buf : string or file handle
284+
File path or object. If not specified, the result is returned as
285+
a string.
286+
column_format : str, default None
287+
The columns format as specified in `LaTeX table format
288+
<https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl'
289+
for 3 columns
290+
291+
"""
292+
if self.caption is not None or self.label is not None:
293+
# then write output in a nested table/tabular environment
294+
if self.caption is None:
295+
caption_ = ""
296+
else:
297+
caption_ = "\n\\caption{{{}}}".format(self.caption)
298+
299+
if self.label is None:
300+
label_ = ""
301+
else:
302+
label_ = "\n\\label{{{}}}".format(self.label)
303+
304+
buf.write("\\begin{{table}}\n\\centering{}{}\n".format(caption_, label_))
305+
else:
306+
# then write output only in a tabular environment
307+
pass
308+
309+
buf.write("\\begin{{tabular}}{{{fmt}}}\n".format(fmt=column_format))
310+
311+
def _write_tabular_end(self, buf):
312+
"""
313+
Write the end of a tabular environment or nested table/tabular
314+
environment.
315+
316+
Parameters
317+
----------
318+
buf : string or file handle
319+
File path or object. If not specified, the result is returned as
320+
a string.
321+
322+
"""
323+
buf.write("\\bottomrule\n")
324+
buf.write("\\end{tabular}\n")
325+
if self.caption is not None or self.label is not None:
326+
buf.write("\\end{table}\n")
327+
else:
328+
pass
329+
330+
def _write_longtable_begin(self, buf, column_format):
331+
"""
332+
Write the beginning of a longtable environment including caption and
333+
label if provided by user.
334+
335+
Parameters
336+
----------
337+
buf : string or file handle
338+
File path or object. If not specified, the result is returned as
339+
a string.
340+
column_format : str, default None
341+
The columns format as specified in `LaTeX table format
342+
<https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl'
343+
for 3 columns
344+
345+
"""
346+
buf.write("\\begin{{longtable}}{{{fmt}}}\n".format(fmt=column_format))
347+
348+
if self.caption is not None or self.label is not None:
349+
if self.caption is None:
350+
pass
351+
else:
352+
buf.write("\\caption{{{}}}".format(self.caption))
353+
354+
if self.label is None:
355+
pass
356+
else:
357+
buf.write("\\label{{{}}}".format(self.label))
358+
359+
# a double-backslash is required at the end of the line
360+
# as discussed here:
361+
# https://tex.stackexchange.com/questions/219138
362+
buf.write("\\\\\n")
363+
else:
364+
pass
365+
366+
@staticmethod
367+
def _write_longtable_end(buf):
368+
"""
369+
Write the end of a longtable environment.
370+
371+
Parameters
372+
----------
373+
buf : string or file handle
374+
File path or object. If not specified, the result is returned as
375+
a string.
376+
377+
"""
378+
buf.write("\\end{longtable}\n")

0 commit comments

Comments
 (0)