diff --git a/doc/source/whatsnew/v2.1.0.rst b/doc/source/whatsnew/v2.1.0.rst index 137f2e5c12211..24def10266ab7 100644 --- a/doc/source/whatsnew/v2.1.0.rst +++ b/doc/source/whatsnew/v2.1.0.rst @@ -523,6 +523,7 @@ I/O - Bug in :func:`read_html`, tail texts were removed together with elements containing ``display:none`` style (:issue:`51629`) - Bug in :func:`read_sql` when reading multiple timezone aware columns with the same column name (:issue:`44421`) - Bug in :func:`read_xml` stripping whitespace in string data (:issue:`53811`) +- Bug in :meth:`DataFrame.to_html` where ``colspace`` was incorrectly applied in case of multi index columns (:issue:`53885`) - Bug when writing and reading empty Stata dta files where dtype information was lost (:issue:`46240`) - Bug where ``bz2`` was treated as a hard requirement (:issue:`53857`) diff --git a/pandas/io/formats/html.py b/pandas/io/formats/html.py index 32a0cab1fbc41..151bde4e1c4c2 100644 --- a/pandas/io/formats/html.py +++ b/pandas/io/formats/html.py @@ -73,10 +73,16 @@ def __init__( self.table_id = table_id self.render_links = render_links - self.col_space = { - column: f"{value}px" if isinstance(value, int) else value - for column, value in self.fmt.col_space.items() - } + self.col_space = {} + is_multi_index = isinstance(self.columns, MultiIndex) + for column, value in self.fmt.col_space.items(): + col_space_value = f"{value}px" if isinstance(value, int) else value + self.col_space[column] = col_space_value + # GH 53885: Handling case where column is index + # Flatten the data in the multi index and add in the map + if is_multi_index and isinstance(column, tuple): + for column_index in column: + self.col_space[str(column_index)] = col_space_value def to_string(self) -> str: lines = self.render() diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 9c128756339ab..bea0eebef2cf6 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -896,3 +896,59 @@ def test_to_html_float_format_object_col(datapath): result = df.to_html(float_format=lambda x: f"{x:,.0f}") expected = expected_html(datapath, "gh40024_expected_output") assert result == expected + + +def test_to_html_multiindex_col_with_colspace(): + # GH#53885 + df = DataFrame([[1, 2]]) + df.columns = MultiIndex.from_tuples([(1, 1), (2, 1)]) + result = df.to_html(col_space=100) + expected = ( + '\n' + " \n" + " \n" + ' \n' + ' \n' + ' \n' + " \n" + " \n" + ' \n' + ' \n' + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
12
11
012
" + ) + assert result == expected + + +def test_to_html_tuple_col_with_colspace(): + # GH#53885 + df = DataFrame({("a", "b"): [1], "b": [2]}) + result = df.to_html(col_space=100) + expected = ( + '\n' + " \n" + ' \n' + ' \n' + ' \n' + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
(a, b)b
012
" + ) + assert result == expected