Skip to content

Commit f1b2bb1

Browse files
authored
ENH: Enable short_caption in to_latex (#35668)
1 parent 7eb1063 commit f1b2bb1

File tree

5 files changed

+266
-13
lines changed

5 files changed

+266
-13
lines changed

doc/source/whatsnew/v1.2.0.rst

+26
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,32 @@ For example:
9696
buffer = io.BytesIO()
9797
data.to_csv(buffer, mode="w+b", encoding="utf-8", compression="gzip")
9898
99+
Support for short caption and table position in ``to_latex``
100+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
101+
102+
:meth:`DataFrame.to_latex` now allows one to specify
103+
a floating table position (:issue:`35281`)
104+
and a short caption (:issue:`36267`).
105+
106+
New keyword ``position`` is implemented to set the position.
107+
108+
.. ipython:: python
109+
110+
data = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
111+
table = data.to_latex(position='ht')
112+
print(table)
113+
114+
Usage of keyword ``caption`` is extended.
115+
Besides taking a single string as an argument,
116+
one can optionally provide a tuple of ``(full_caption, short_caption)``
117+
to add a short caption macro.
118+
119+
.. ipython:: python
120+
121+
data = pd.DataFrame({'a': [1, 2], 'b': [3, 4]})
122+
table = data.to_latex(caption=('the full long caption', 'short caption'))
123+
print(table)
124+
99125
.. _whatsnew_120.read_csv_table_precision_default:
100126

101127
Change in default floating precision for ``read_csv`` and ``read_table``

pandas/core/generic.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -3013,6 +3013,9 @@ def to_latex(
30133013
.. versionchanged:: 1.0.0
30143014
Added caption and label arguments.
30153015
3016+
.. versionchanged:: 1.2.0
3017+
Added position argument, changed meaning of caption argument.
3018+
30163019
Parameters
30173020
----------
30183021
buf : str, Path or StringIO-like, optional, default None
@@ -3074,11 +3077,16 @@ def to_latex(
30743077
centered labels (instead of top-aligned) across the contained
30753078
rows, separating groups via clines. The default will be read
30763079
from the pandas config module.
3077-
caption : str, optional
3078-
The LaTeX caption to be placed inside ``\caption{{}}`` in the output.
3080+
caption : str or tuple, optional
3081+
Tuple (full_caption, short_caption),
3082+
which results in ``\caption[short_caption]{{full_caption}}``;
3083+
if a single string is passed, no short caption will be set.
30793084
30803085
.. versionadded:: 1.0.0
30813086
3087+
.. versionchanged:: 1.2.0
3088+
Optionally allow caption to be a tuple ``(full_caption, short_caption)``.
3089+
30823090
label : str, optional
30833091
The LaTeX label to be placed inside ``\label{{}}`` in the output.
30843092
This is used with ``\ref{{}}`` in the main ``.tex`` file.
@@ -3087,6 +3095,8 @@ def to_latex(
30873095
position : str, optional
30883096
The LaTeX positional argument for tables, to be placed after
30893097
``\begin{{}}`` in the output.
3098+
3099+
.. versionadded:: 1.2.0
30903100
{returns}
30913101
See Also
30923102
--------
@@ -3097,8 +3107,8 @@ def to_latex(
30973107
Examples
30983108
--------
30993109
>>> df = pd.DataFrame(dict(name=['Raphael', 'Donatello'],
3100-
... mask=['red', 'purple'],
3101-
... weapon=['sai', 'bo staff']))
3110+
... mask=['red', 'purple'],
3111+
... weapon=['sai', 'bo staff']))
31023112
>>> print(df.to_latex(index=False)) # doctest: +NORMALIZE_WHITESPACE
31033113
\begin{{tabular}}{{lll}}
31043114
\toprule

pandas/io/formats/format.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1021,7 +1021,7 @@ def to_latex(
10211021
multicolumn: bool = False,
10221022
multicolumn_format: Optional[str] = None,
10231023
multirow: bool = False,
1024-
caption: Optional[str] = None,
1024+
caption: Optional[Union[str, Tuple[str, str]]] = None,
10251025
label: Optional[str] = None,
10261026
position: Optional[str] = None,
10271027
) -> Optional[str]:

pandas/io/formats/latex.py

+78-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Module for formatting output data in Latex.
33
"""
44
from abc import ABC, abstractmethod
5-
from typing import IO, Iterator, List, Optional, Type
5+
from typing import IO, Iterator, List, Optional, Tuple, Type, Union
66

77
import numpy as np
88

@@ -11,6 +11,39 @@
1111
from pandas.io.formats.format import DataFrameFormatter, TableFormatter
1212

1313

14+
def _split_into_full_short_caption(
15+
caption: Optional[Union[str, Tuple[str, str]]]
16+
) -> Tuple[str, str]:
17+
"""Extract full and short captions from caption string/tuple.
18+
19+
Parameters
20+
----------
21+
caption : str or tuple, optional
22+
Either table caption string or tuple (full_caption, short_caption).
23+
If string is provided, then it is treated as table full caption,
24+
while short_caption is considered an empty string.
25+
26+
Returns
27+
-------
28+
full_caption, short_caption : tuple
29+
Tuple of full_caption, short_caption strings.
30+
"""
31+
if caption:
32+
if isinstance(caption, str):
33+
full_caption = caption
34+
short_caption = ""
35+
else:
36+
try:
37+
full_caption, short_caption = caption
38+
except ValueError as err:
39+
msg = "caption must be either a string or a tuple of two strings"
40+
raise ValueError(msg) from err
41+
else:
42+
full_caption = ""
43+
short_caption = ""
44+
return full_caption, short_caption
45+
46+
1447
class RowStringConverter(ABC):
1548
r"""Converter for dataframe rows into LaTeX strings.
1649
@@ -275,6 +308,8 @@ class TableBuilderAbstract(ABC):
275308
Use multirow to enhance MultiIndex rows.
276309
caption: str, optional
277310
Table caption.
311+
short_caption: str, optional
312+
Table short caption.
278313
label: str, optional
279314
LaTeX label.
280315
position: str, optional
@@ -289,6 +324,7 @@ def __init__(
289324
multicolumn_format: Optional[str] = None,
290325
multirow: bool = False,
291326
caption: Optional[str] = None,
327+
short_caption: Optional[str] = None,
292328
label: Optional[str] = None,
293329
position: Optional[str] = None,
294330
):
@@ -298,6 +334,7 @@ def __init__(
298334
self.multicolumn_format = multicolumn_format
299335
self.multirow = multirow
300336
self.caption = caption
337+
self.short_caption = short_caption
301338
self.label = label
302339
self.position = position
303340

@@ -384,8 +421,23 @@ def _position_macro(self) -> str:
384421

385422
@property
386423
def _caption_macro(self) -> str:
387-
r"""Caption macro, extracted from self.caption, like \caption{cap}."""
388-
return f"\\caption{{{self.caption}}}" if self.caption else ""
424+
r"""Caption macro, extracted from self.caption.
425+
426+
With short caption:
427+
\caption[short_caption]{caption_string}.
428+
429+
Without short caption:
430+
\caption{caption_string}.
431+
"""
432+
if self.caption:
433+
return "".join(
434+
[
435+
r"\caption",
436+
f"[{self.short_caption}]" if self.short_caption else "",
437+
f"{{{self.caption}}}",
438+
]
439+
)
440+
return ""
389441

390442
@property
391443
def _label_macro(self) -> str:
@@ -596,15 +648,32 @@ def env_end(self) -> str:
596648

597649

598650
class LatexFormatter(TableFormatter):
599-
"""
651+
r"""
600652
Used to render a DataFrame to a LaTeX tabular/longtable environment output.
601653
602654
Parameters
603655
----------
604656
formatter : `DataFrameFormatter`
657+
longtable : bool, default False
658+
Use longtable environment.
605659
column_format : str, default None
606660
The columns format as specified in `LaTeX table format
607661
<https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3 columns
662+
multicolumn : bool, default False
663+
Use \multicolumn to enhance MultiIndex columns.
664+
multicolumn_format : str, default 'l'
665+
The alignment for multicolumns, similar to `column_format`
666+
multirow : bool, default False
667+
Use \multirow to enhance MultiIndex rows.
668+
caption : str or tuple, optional
669+
Tuple (full_caption, short_caption),
670+
which results in \caption[short_caption]{full_caption};
671+
if a single string is passed, no short caption will be set.
672+
label : str, optional
673+
The LaTeX label to be placed inside ``\label{}`` in the output.
674+
position : str, optional
675+
The LaTeX positional argument for tables, to be placed after
676+
``\begin{}`` in the output.
608677
609678
See Also
610679
--------
@@ -619,18 +688,18 @@ def __init__(
619688
multicolumn: bool = False,
620689
multicolumn_format: Optional[str] = None,
621690
multirow: bool = False,
622-
caption: Optional[str] = None,
691+
caption: Optional[Union[str, Tuple[str, str]]] = None,
623692
label: Optional[str] = None,
624693
position: Optional[str] = None,
625694
):
626695
self.fmt = formatter
627696
self.frame = self.fmt.frame
628697
self.longtable = longtable
629-
self.column_format = column_format # type: ignore[assignment]
698+
self.column_format = column_format
630699
self.multicolumn = multicolumn
631700
self.multicolumn_format = multicolumn_format
632701
self.multirow = multirow
633-
self.caption = caption
702+
self.caption, self.short_caption = _split_into_full_short_caption(caption)
634703
self.label = label
635704
self.position = position
636705

@@ -658,6 +727,7 @@ def builder(self) -> TableBuilderAbstract:
658727
multicolumn_format=self.multicolumn_format,
659728
multirow=self.multirow,
660729
caption=self.caption,
730+
short_caption=self.short_caption,
661731
label=self.label,
662732
position=self.position,
663733
)
@@ -671,7 +741,7 @@ def _select_builder(self) -> Type[TableBuilderAbstract]:
671741
return TabularBuilder
672742

673743
@property
674-
def column_format(self) -> str:
744+
def column_format(self) -> Optional[str]:
675745
"""Column format."""
676746
return self._column_format
677747

0 commit comments

Comments
 (0)