Skip to content

Commit f1ec745

Browse files
bswegerTomAugspurger
authored andcommitted
ENH: Add to_latex() method to Series (#16180) (#16465)
* ENH: Add to_latex() method to Series (#16180) This changeset adds _repr_latex_ to the Series class and moves the to_latex() method from the DataFrame class to the NDFrame class. * Add Series to_latex test * Move _repr_latex_ to NDFrame Streamline things a bit by moving _repr_latex_ methods out of the Series and DataFrame classes * DOC: Added versionchanged (cherry picked from commit 6a6227d)
1 parent 7c6c19f commit f1ec745

File tree

6 files changed

+142
-101
lines changed

6 files changed

+142
-101
lines changed

doc/source/api.rst

+1
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,7 @@ Serialization / IO / Conversion
724724
Series.to_dense
725725
Series.to_string
726726
Series.to_clipboard
727+
Series.to_latex
727728

728729
Sparse
729730
~~~~~~

doc/source/whatsnew/v0.20.2.txt

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Enhancements
2020
~~~~~~~~~~~~
2121

2222
- Unblocked access to additional compression types supported in pytables: 'blosc:blosclz, 'blosc:lz4', 'blosc:lz4hc', 'blosc:snappy', 'blosc:zlib', 'blosc:zstd' (:issue:`14478`)
23+
- ``Series`` provides a ``to_latex`` method (:issue:`16180`)
2324

2425
.. _whatsnew_0202.performance:
2526

pandas/core/frame.py

-98
Original file line numberDiff line numberDiff line change
@@ -620,16 +620,6 @@ def _repr_html_(self):
620620
else:
621621
return None
622622

623-
def _repr_latex_(self):
624-
"""
625-
Returns a LaTeX representation for a particular Dataframe.
626-
Mainly for use with nbconvert (jupyter notebook conversion to pdf).
627-
"""
628-
if get_option('display.latex.repr'):
629-
return self.to_latex()
630-
else:
631-
return None
632-
633623
@property
634624
def style(self):
635625
"""
@@ -1596,94 +1586,6 @@ def to_html(self, buf=None, columns=None, col_space=None, header=True,
15961586
if buf is None:
15971587
return formatter.buf.getvalue()
15981588

1599-
@Substitution(header='Write out column names. If a list of string is given, \
1600-
it is assumed to be aliases for the column names.')
1601-
@Appender(fmt.common_docstring + fmt.return_docstring, indents=1)
1602-
def to_latex(self, buf=None, columns=None, col_space=None, header=True,
1603-
index=True, na_rep='NaN', formatters=None, float_format=None,
1604-
sparsify=None, index_names=True, bold_rows=True,
1605-
column_format=None, longtable=None, escape=None,
1606-
encoding=None, decimal='.', multicolumn=None,
1607-
multicolumn_format=None, multirow=None):
1608-
r"""
1609-
Render a DataFrame to a tabular environment table. You can splice
1610-
this into a LaTeX document. Requires \usepackage{booktabs}.
1611-
1612-
`to_latex`-specific options:
1613-
1614-
bold_rows : boolean, default True
1615-
Make the row labels bold in the output
1616-
column_format : str, default None
1617-
The columns format as specified in `LaTeX table format
1618-
<https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3
1619-
columns
1620-
longtable : boolean, default will be read from the pandas config module
1621-
Default: False.
1622-
Use a longtable environment instead of tabular. Requires adding
1623-
a \usepackage{longtable} to your LaTeX preamble.
1624-
escape : boolean, default will be read from the pandas config module
1625-
Default: True.
1626-
When set to False prevents from escaping latex special
1627-
characters in column names.
1628-
encoding : str, default None
1629-
A string representing the encoding to use in the output file,
1630-
defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
1631-
decimal : string, default '.'
1632-
Character recognized as decimal separator, e.g. ',' in Europe.
1633-
1634-
.. versionadded:: 0.18.0
1635-
1636-
multicolumn : boolean, default True
1637-
Use \multicolumn to enhance MultiIndex columns.
1638-
The default will be read from the config module.
1639-
1640-
.. versionadded:: 0.20.0
1641-
1642-
multicolumn_format : str, default 'l'
1643-
The alignment for multicolumns, similar to `column_format`
1644-
The default will be read from the config module.
1645-
1646-
.. versionadded:: 0.20.0
1647-
1648-
multirow : boolean, default False
1649-
Use \multirow to enhance MultiIndex rows.
1650-
Requires adding a \usepackage{multirow} to your LaTeX preamble.
1651-
Will print centered labels (instead of top-aligned)
1652-
across the contained rows, separating groups via clines.
1653-
The default will be read from the pandas config module.
1654-
1655-
.. versionadded:: 0.20.0
1656-
1657-
"""
1658-
# Get defaults from the pandas config
1659-
if longtable is None:
1660-
longtable = get_option("display.latex.longtable")
1661-
if escape is None:
1662-
escape = get_option("display.latex.escape")
1663-
if multicolumn is None:
1664-
multicolumn = get_option("display.latex.multicolumn")
1665-
if multicolumn_format is None:
1666-
multicolumn_format = get_option("display.latex.multicolumn_format")
1667-
if multirow is None:
1668-
multirow = get_option("display.latex.multirow")
1669-
1670-
formatter = fmt.DataFrameFormatter(self, buf=buf, columns=columns,
1671-
col_space=col_space, na_rep=na_rep,
1672-
header=header, index=index,
1673-
formatters=formatters,
1674-
float_format=float_format,
1675-
bold_rows=bold_rows,
1676-
sparsify=sparsify,
1677-
index_names=index_names,
1678-
escape=escape, decimal=decimal)
1679-
formatter.to_latex(column_format=column_format, longtable=longtable,
1680-
encoding=encoding, multicolumn=multicolumn,
1681-
multicolumn_format=multicolumn_format,
1682-
multirow=multirow)
1683-
1684-
if buf is None:
1685-
return formatter.buf.getvalue()
1686-
16871589
def info(self, verbose=None, buf=None, max_cols=None, memory_usage=None,
16881590
null_counts=None):
16891591
"""

pandas/core/generic.py

+105-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import pandas.core.common as com
4747
import pandas.core.missing as missing
4848
from pandas.io.formats.printing import pprint_thing
49-
from pandas.io.formats.format import format_percentiles
49+
from pandas.io.formats.format import format_percentiles, DataFrameFormatter
5050
from pandas.tseries.frequencies import to_offset
5151
from pandas import compat
5252
from pandas.compat.numpy import function as nv
@@ -1051,6 +1051,16 @@ def __setstate__(self, state):
10511051
# ----------------------------------------------------------------------
10521052
# IO
10531053

1054+
def _repr_latex_(self):
1055+
"""
1056+
Returns a LaTeX representation for a particular object.
1057+
Mainly for use with nbconvert (jupyter notebook conversion to pdf).
1058+
"""
1059+
if config.get_option('display.latex.repr'):
1060+
return self.to_latex()
1061+
else:
1062+
return None
1063+
10541064
# ----------------------------------------------------------------------
10551065
# I/O Methods
10561066

@@ -1489,6 +1499,100 @@ def to_xarray(self):
14891499
coords=coords,
14901500
)
14911501

1502+
_shared_docs['to_latex'] = """
1503+
Render an object to a tabular environment table. You can splice
1504+
this into a LaTeX document. Requires \\usepackage{booktabs}.
1505+
1506+
.. versionchanged:: 0.20.2
1507+
Added to Series
1508+
1509+
`to_latex`-specific options:
1510+
1511+
bold_rows : boolean, default True
1512+
Make the row labels bold in the output
1513+
column_format : str, default None
1514+
The columns format as specified in `LaTeX table format
1515+
<https://en.wikibooks.org/wiki/LaTeX/Tables>`__ e.g 'rcl' for 3
1516+
columns
1517+
longtable : boolean, default will be read from the pandas config module
1518+
Default: False.
1519+
Use a longtable environment instead of tabular. Requires adding
1520+
a \\usepackage{longtable} to your LaTeX preamble.
1521+
escape : boolean, default will be read from the pandas config module
1522+
Default: True.
1523+
When set to False prevents from escaping latex special
1524+
characters in column names.
1525+
encoding : str, default None
1526+
A string representing the encoding to use in the output file,
1527+
defaults to 'ascii' on Python 2 and 'utf-8' on Python 3.
1528+
decimal : string, default '.'
1529+
Character recognized as decimal separator, e.g. ',' in Europe.
1530+
1531+
.. versionadded:: 0.18.0
1532+
1533+
multicolumn : boolean, default True
1534+
Use \multicolumn to enhance MultiIndex columns.
1535+
The default will be read from the config module.
1536+
1537+
.. versionadded:: 0.20.0
1538+
1539+
multicolumn_format : str, default 'l'
1540+
The alignment for multicolumns, similar to `column_format`
1541+
The default will be read from the config module.
1542+
1543+
.. versionadded:: 0.20.0
1544+
1545+
multirow : boolean, default False
1546+
Use \multirow to enhance MultiIndex rows.
1547+
Requires adding a \\usepackage{multirow} to your LaTeX preamble.
1548+
Will print centered labels (instead of top-aligned)
1549+
across the contained rows, separating groups via clines.
1550+
The default will be read from the pandas config module.
1551+
1552+
.. versionadded:: 0.20.0
1553+
"""
1554+
1555+
@Substitution(header='Write out column names. If a list of string is given, \
1556+
it is assumed to be aliases for the column names.')
1557+
@Appender(_shared_docs['to_latex'] % _shared_doc_kwargs)
1558+
def to_latex(self, buf=None, columns=None, col_space=None, header=True,
1559+
index=True, na_rep='NaN', formatters=None, float_format=None,
1560+
sparsify=None, index_names=True, bold_rows=True,
1561+
column_format=None, longtable=None, escape=None,
1562+
encoding=None, decimal='.', multicolumn=None,
1563+
multicolumn_format=None, multirow=None):
1564+
# Get defaults from the pandas config
1565+
if self.ndim == 1:
1566+
self = self.to_frame()
1567+
if longtable is None:
1568+
longtable = config.get_option("display.latex.longtable")
1569+
if escape is None:
1570+
escape = config.get_option("display.latex.escape")
1571+
if multicolumn is None:
1572+
multicolumn = config.get_option("display.latex.multicolumn")
1573+
if multicolumn_format is None:
1574+
multicolumn_format = config.get_option(
1575+
"display.latex.multicolumn_format")
1576+
if multirow is None:
1577+
multirow = config.get_option("display.latex.multirow")
1578+
1579+
formatter = DataFrameFormatter(self, buf=buf, columns=columns,
1580+
col_space=col_space, na_rep=na_rep,
1581+
header=header, index=index,
1582+
formatters=formatters,
1583+
float_format=float_format,
1584+
bold_rows=bold_rows,
1585+
sparsify=sparsify,
1586+
index_names=index_names,
1587+
escape=escape, decimal=decimal)
1588+
formatter.to_latex(column_format=column_format, longtable=longtable,
1589+
encoding=encoding, multicolumn=multicolumn,
1590+
multicolumn_format=multicolumn_format,
1591+
multirow=multirow)
1592+
1593+
if buf is None:
1594+
return formatter.buf.getvalue()
1595+
14921596
# ----------------------------------------------------------------------
14931597
# Fancy Indexing
14941598

pandas/tests/io/formats/test_to_latex.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import pytest
44

55
import pandas as pd
6-
from pandas import DataFrame, compat
6+
from pandas import DataFrame, compat, Series
77
from pandas.util import testing as tm
88
from pandas.compat import u
99
import codecs
@@ -491,3 +491,18 @@ def test_to_latex_decimal(self, frame):
491491
"""
492492

493493
assert withindex_result == withindex_expected
494+
495+
def test_to_latex_series(self):
496+
s = Series(['a', 'b', 'c'])
497+
withindex_result = s.to_latex()
498+
withindex_expected = r"""\begin{tabular}{ll}
499+
\toprule
500+
{} & 0 \\
501+
\midrule
502+
0 & a \\
503+
1 & b \\
504+
2 & c \\
505+
\bottomrule
506+
\end{tabular}
507+
"""
508+
assert withindex_result == withindex_expected

pandas/tests/series/test_repr.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import numpy as np
99
import pandas as pd
1010

11-
from pandas import (Index, Series, DataFrame, date_range)
11+
from pandas import (Index, Series, DataFrame, date_range, option_context)
1212
from pandas.core.index import MultiIndex
1313

1414
from pandas.compat import lrange, range, u
@@ -180,3 +180,21 @@ def test_timeseries_repr_object_dtype(self):
180180

181181
ts2 = ts.iloc[np.random.randint(0, len(ts) - 1, 400)]
182182
repr(ts2).splitlines()[-1]
183+
184+
def test_latex_repr(self):
185+
result = r"""\begin{tabular}{ll}
186+
\toprule
187+
{} & 0 \\
188+
\midrule
189+
0 & $\alpha$ \\
190+
1 & b \\
191+
2 & c \\
192+
\bottomrule
193+
\end{tabular}
194+
"""
195+
with option_context('display.latex.escape', False,
196+
'display.latex.repr', True):
197+
s = Series([r'$\alpha$', 'b', 'c'])
198+
assert result == s._repr_latex_()
199+
200+
assert s._repr_latex_() is None

0 commit comments

Comments
 (0)