diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 9c2e5427fcd3f..e1fd0ddf99336 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -321,6 +321,7 @@ I/O - :meth:`to_csv` did not support zip compression for binary file object not having a filename (:issue:`35058`) - :meth:`to_csv` and :meth:`read_csv` did not honor `compression` and `encoding` for path-like objects that are internally converted to file-like objects (:issue:`35677`, :issue:`26124`, and :issue:`32392`) - :meth:`to_picke` and :meth:`read_pickle` did not support compression for file-objects (:issue:`26237`, :issue:`29054`, and :issue:`29570`) +- Bug in :func:`LongTableBuilder.middle_separator` was duplicating LaTeX longtable entires in the List of Tables of a LaTeX document (:issue:`34360`) - Bug in :meth:`read_csv` with `engine='python'` truncating data if multiple items present in first row and first element started with BOM (:issue:`36343`) Plotting diff --git a/pandas/io/formats/latex.py b/pandas/io/formats/latex.py index 8080d953da308..eb35fff3a4f8e 100644 --- a/pandas/io/formats/latex.py +++ b/pandas/io/formats/latex.py @@ -431,13 +431,18 @@ class LongTableBuilder(GenericTableBuilder): >>> from pandas.io.formats import format as fmt >>> df = DataFrame({"a": [1, 2], "b": ["b1", "b2"]}) >>> formatter = fmt.DataFrameFormatter(df) - >>> builder = LongTableBuilder(formatter, caption='caption', label='lab', - ... column_format='lrl') + >>> builder = LongTableBuilder(formatter, caption='a long table', + ... label='tab:long', column_format='lrl') >>> table = builder.get_result() >>> print(table) \\begin{longtable}{lrl} - \\caption{caption} - \\label{lab}\\\\ + \\caption{a long table} + \\label{tab:long}\\\\ + \\toprule + {} & a & b \\\\ + \\midrule + \\endfirsthead + \\caption[]{a long table} \\\\ \\toprule {} & a & b \\\\ \\midrule @@ -476,7 +481,16 @@ def _caption_and_label(self) -> str: @property def middle_separator(self) -> str: iterator = self._create_row_iterator(over="header") + + # the content between \endfirsthead and \endhead commands + # mitigates repeated List of Tables entries in the final LaTeX + # document when dealing with longtable environments; GH #34360 elements = [ + "\\midrule", + "\\endfirsthead", + f"\\caption[]{{{self.caption}}} \\\\" if self.caption else "", + self.top_separator, + self.header, "\\midrule", "\\endhead", "\\midrule", diff --git a/pandas/tests/io/formats/test_to_latex.py b/pandas/tests/io/formats/test_to_latex.py index a93ab6f9cc7aa..8df8796d236a5 100644 --- a/pandas/tests/io/formats/test_to_latex.py +++ b/pandas/tests/io/formats/test_to_latex.py @@ -411,6 +411,11 @@ def test_to_latex_longtable(self): 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 +\endfirsthead + \toprule {} & a & b \\ \midrule @@ -430,6 +435,11 @@ def test_to_latex_longtable(self): withoutindex_result = df.to_latex(index=False, longtable=True) withoutindex_expected = r"""\begin{longtable}{rl} +\toprule + a & b \\ +\midrule +\endfirsthead + \toprule a & b \\ \midrule @@ -525,6 +535,9 @@ def test_to_latex_longtable_caption_label(self): df = DataFrame({"a": [1, 2], "b": ["b1", "b2"]}) + # test when no caption and no label is provided + # is performed by test_to_latex_longtable() + # test when only the caption is provided result_c = df.to_latex(longtable=True, caption=the_caption) @@ -533,6 +546,11 @@ def test_to_latex_longtable_caption_label(self): \toprule {} & a & b \\ \midrule +\endfirsthead +\caption[]{a table in a \texttt{longtable} environment} \\ +\toprule +{} & a & b \\ +\midrule \endhead \midrule \multicolumn{3}{r}{{Continued on next page}} \\ @@ -552,6 +570,11 @@ def test_to_latex_longtable_caption_label(self): expected_l = r"""\begin{longtable}{lrl} \label{tab:longtable}\\ +\toprule +{} & a & b \\ +\midrule +\endfirsthead + \toprule {} & a & b \\ \midrule @@ -578,6 +601,11 @@ def test_to_latex_longtable_caption_label(self): \toprule {} & a & b \\ \midrule +\endfirsthead +\caption[]{a table in a \texttt{longtable} environment} \\ +\toprule +{} & a & b \\ +\midrule \endhead \midrule \multicolumn{3}{r}{{Continued on next page}} \\ @@ -623,6 +651,11 @@ def test_to_latex_longtable_position(self): result_p = df.to_latex(longtable=True, position=the_position) expected_p = r"""\begin{longtable}[t]{lrl} +\toprule +{} & a & b \\ +\midrule +\endfirsthead + \toprule {} & a & b \\ \midrule