Skip to content

Commit 9a8fcee

Browse files
committed
Don't add rowspan/colspan if it's 1.
Save a few bytes per row/col and it's already done in the other code path.
1 parent 9b5d848 commit 9a8fcee

File tree

3 files changed

+43
-51
lines changed

3 files changed

+43
-51
lines changed

doc/source/whatsnew/v0.20.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ Other enhancements
154154
- ``pandas.tools.hashing`` has gained a ``hash_tuples`` routine, and ``hash_pandas_object`` has gained the ability to hash a ``MultiIndex`` (:issue:`15224`)
155155
- ``Series/DataFrame.squeeze()`` have gained the ``axis`` parameter. (:issue:`15339`)
156156
- ``DataFrame.to_excel()`` has a new ``freeze_panes`` parameter to turn on Freeze Panes when exporting to Excel (:issue:`15160`)
157+
- HTML table output skips ``colspan`` or ``rowspan`` attribute if equal to 1. (:issue:`15403`)
157158

158159
.. _ISO 8601 duration: https://en.wikipedia.org/wiki/ISO_8601#Durations
159160

pandas/formats/style.py

+30-25
Original file line numberDiff line numberDiff line change
@@ -251,21 +251,23 @@ def format_attr(pair):
251251
"class": " ".join(cs),
252252
"is_visible": True})
253253

254-
for c in range(len(clabels[0])):
254+
for c, value in enumerate(clabels[r]):
255255
cs = [COL_HEADING_CLASS, "level%s" % r, "col%s" % c]
256256
cs.extend(cell_context.get(
257257
"col_headings", {}).get(r, {}).get(c, []))
258-
value = clabels[r][c]
259-
row_es.append({"type": "th",
260-
"value": value,
261-
"display_value": value,
262-
"class": " ".join(cs),
263-
"is_visible": _is_visible(c, r, col_lengths),
264-
"attributes": [
265-
format_attr({"key": "colspan",
266-
"value": col_lengths.get(
267-
(r, c), 1)})
268-
]})
258+
es = {
259+
"type": "th",
260+
"value": value,
261+
"display_value": value,
262+
"class": " ".join(cs),
263+
"is_visible": _is_visible(c, r, col_lengths),
264+
}
265+
colspan = col_lengths.get((r, c), 0)
266+
if colspan > 1:
267+
es["attributes"] = [
268+
format_attr({"key": "colspan", "value": colspan})
269+
]
270+
row_es.append(es)
269271
head.append(row_es)
270272

271273
if self.data.index.names and not all(x is None
@@ -289,19 +291,22 @@ def format_attr(pair):
289291

290292
body = []
291293
for r, idx in enumerate(self.data.index):
292-
# cs.extend(
293-
# cell_context.get("row_headings", {}).get(r, {}).get(c, []))
294-
row_es = [{"type": "th",
295-
"is_visible": _is_visible(r, c, idx_lengths),
296-
"attributes": [
297-
format_attr({"key": "rowspan",
298-
"value": idx_lengths.get((c, r), 1)})
299-
],
300-
"value": rlabels[r][c],
301-
"class": " ".join([ROW_HEADING_CLASS, "level%s" % c,
302-
"row%s" % r]),
303-
"display_value": rlabels[r][c]}
304-
for c in range(len(rlabels[r]))]
294+
row_es = []
295+
for c, value in enumerate(rlabels[r]):
296+
es = {
297+
"type": "th",
298+
"is_visible": _is_visible(r, c, idx_lengths),
299+
"value": value,
300+
"display_value": value,
301+
"class": " ".join([ROW_HEADING_CLASS, "level%s" % c,
302+
"row%s" % r]),
303+
}
304+
rowspan = idx_lengths.get((c, r), 0)
305+
if rowspan > 1:
306+
es["attributes"] = [
307+
format_attr({"key": "rowspan", "value": rowspan})
308+
]
309+
row_es.append(es)
305310

306311
for c, col in enumerate(self.data.columns):
307312
cs = [DATA_CLASS, "row%s" % r, "col%s" % c]

pandas/tests/formats/test_style.py

+12-26
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,18 @@ def test_empty_index_name_doesnt_display(self):
141141
'type': 'th',
142142
'value': 'A',
143143
'is_visible': True,
144-
'attributes': ["colspan=1"],
145144
},
146145
{'class': 'col_heading level0 col1',
147146
'display_value': 'B',
148147
'type': 'th',
149148
'value': 'B',
150149
'is_visible': True,
151-
'attributes': ["colspan=1"],
152150
},
153151
{'class': 'col_heading level0 col2',
154152
'display_value': 'C',
155153
'type': 'th',
156154
'value': 'C',
157155
'is_visible': True,
158-
'attributes': ["colspan=1"],
159156
}]]
160157

161158
self.assertEqual(result['head'], expected)
@@ -168,11 +165,9 @@ def test_index_name(self):
168165
expected = [[{'class': 'blank level0', 'type': 'th', 'value': '',
169166
'display_value': '', 'is_visible': True},
170167
{'class': 'col_heading level0 col0', 'type': 'th',
171-
'value': 'B', 'display_value': 'B',
172-
'is_visible': True, 'attributes': ['colspan=1']},
168+
'value': 'B', 'display_value': 'B', 'is_visible': True},
173169
{'class': 'col_heading level0 col1', 'type': 'th',
174-
'value': 'C', 'display_value': 'C',
175-
'is_visible': True, 'attributes': ['colspan=1']}],
170+
'value': 'C', 'display_value': 'C', 'is_visible': True}],
176171
[{'class': 'index_name level0', 'type': 'th',
177172
'value': 'A'},
178173
{'class': 'blank', 'type': 'th', 'value': ''},
@@ -191,9 +186,7 @@ def test_multiindex_name(self):
191186
{'class': 'blank level0', 'type': 'th', 'value': '',
192187
'display_value': '', 'is_visible': True},
193188
{'class': 'col_heading level0 col0', 'type': 'th',
194-
'value': 'C', 'display_value': 'C',
195-
'is_visible': True, 'attributes': ['colspan=1'],
196-
}],
189+
'value': 'C', 'display_value': 'C', 'is_visible': True}],
197190
[{'class': 'index_name level0', 'type': 'th',
198191
'value': 'A'},
199192
{'class': 'index_name level1', 'type': 'th',
@@ -618,16 +611,14 @@ def test_mi_sparse(self):
618611
body_1 = result['body'][0][1]
619612
expected_1 = {
620613
"value": 0, "display_value": 0, "is_visible": True,
621-
"type": "th", "attributes": ["rowspan=1"],
622-
"class": "row_heading level1 row0",
614+
"type": "th", "class": "row_heading level1 row0",
623615
}
624616
tm.assert_dict_equal(body_1, expected_1)
625617

626618
body_10 = result['body'][1][0]
627619
expected_10 = {
628620
"value": 'a', "display_value": 'a', "is_visible": False,
629-
"type": "th", "attributes": ["rowspan=1"],
630-
"class": "row_heading level0 row1",
621+
"type": "th", "class": "row_heading level0 row1",
631622
}
632623
tm.assert_dict_equal(body_10, expected_10)
633624

@@ -637,9 +628,8 @@ def test_mi_sparse(self):
637628
'is_visible': True, "display_value": ''},
638629
{'type': 'th', 'class': 'blank level0', 'value': '',
639630
'is_visible': True, 'display_value': ''},
640-
{'attributes': ['colspan=1'], 'class': 'col_heading level0 col0',
641-
'is_visible': True, 'type': 'th', 'value': 'A',
642-
'display_value': 'A'}]
631+
{'type': 'th', 'class': 'col_heading level0 col0', 'value': 'A',
632+
'is_visible': True, 'display_value': 'A'}]
643633
self.assertEqual(head, expected)
644634

645635
def test_mi_sparse_disabled(self):
@@ -650,7 +640,7 @@ def test_mi_sparse_disabled(self):
650640
result = df.style._translate()
651641
body = result['body']
652642
for row in body:
653-
self.assertEqual(row[0]['attributes'], ['rowspan=1'])
643+
assert 'attributes' not in row[0]
654644

655645
def test_mi_sparse_index_names(self):
656646
df = pd.DataFrame({'A': [1, 2]}, index=pd.MultiIndex.from_arrays(
@@ -686,28 +676,24 @@ def test_mi_sparse_column_names(self):
686676
'type': 'th', 'is_visible': True},
687677
{'class': 'index_name level1', 'value': 'col_1',
688678
'display_value': 'col_1', 'is_visible': True, 'type': 'th'},
689-
{'attributes': ['colspan=1'],
690-
'class': 'col_heading level1 col0',
679+
{'class': 'col_heading level1 col0',
691680
'display_value': 1,
692681
'is_visible': True,
693682
'type': 'th',
694683
'value': 1},
695-
{'attributes': ['colspan=1'],
696-
'class': 'col_heading level1 col1',
684+
{'class': 'col_heading level1 col1',
697685
'display_value': 0,
698686
'is_visible': True,
699687
'type': 'th',
700688
'value': 0},
701689

702-
{'attributes': ['colspan=1'],
703-
'class': 'col_heading level1 col2',
690+
{'class': 'col_heading level1 col2',
704691
'display_value': 1,
705692
'is_visible': True,
706693
'type': 'th',
707694
'value': 1},
708695

709-
{'attributes': ['colspan=1'],
710-
'class': 'col_heading level1 col3',
696+
{'class': 'col_heading level1 col3',
711697
'display_value': 0,
712698
'is_visible': True,
713699
'type': 'th',

0 commit comments

Comments
 (0)