Skip to content

Commit

Permalink
ENH: Don't add rowspan/colspan if it's 1.
Browse files Browse the repository at this point in the history
Just a small thing I noticed in a [footnote
here](https://danluu.com/web-bloat/#appendix-irony). Probably can't do
much about the extra classes, but rowspan/colspan seem like easy fixes
to save a few bytes per row/col and it's already done in the other
code path.

Author: Elliott Sales de Andrade <quantum.analyst@gmail.com>

Closes #15403 from QuLogic/no-extra-span and squashes the following commits:

9a8fcee [Elliott Sales de Andrade] Don't add rowspan/colspan if it's 1.
  • Loading branch information
QuLogic authored and jreback committed Feb 17, 2017
1 parent 763f42f commit f65a641
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 51 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.20.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ Other enhancements
- ``pandas.tools.hashing`` has gained a ``hash_tuples`` routine, and ``hash_pandas_object`` has gained the ability to hash a ``MultiIndex`` (:issue:`15224`)
- ``Series/DataFrame.squeeze()`` have gained the ``axis`` parameter. (:issue:`15339`)
- ``DataFrame.to_excel()`` has a new ``freeze_panes`` parameter to turn on Freeze Panes when exporting to Excel (:issue:`15160`)
- HTML table output skips ``colspan`` or ``rowspan`` attribute if equal to 1. (:issue:`15403`)

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

Expand Down
55 changes: 30 additions & 25 deletions pandas/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,21 +251,23 @@ def format_attr(pair):
"class": " ".join(cs),
"is_visible": True})

for c in range(len(clabels[0])):
for c, value in enumerate(clabels[r]):
cs = [COL_HEADING_CLASS, "level%s" % r, "col%s" % c]
cs.extend(cell_context.get(
"col_headings", {}).get(r, {}).get(c, []))
value = clabels[r][c]
row_es.append({"type": "th",
"value": value,
"display_value": value,
"class": " ".join(cs),
"is_visible": _is_visible(c, r, col_lengths),
"attributes": [
format_attr({"key": "colspan",
"value": col_lengths.get(
(r, c), 1)})
]})
es = {
"type": "th",
"value": value,
"display_value": value,
"class": " ".join(cs),
"is_visible": _is_visible(c, r, col_lengths),
}
colspan = col_lengths.get((r, c), 0)
if colspan > 1:
es["attributes"] = [
format_attr({"key": "colspan", "value": colspan})
]
row_es.append(es)
head.append(row_es)

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

body = []
for r, idx in enumerate(self.data.index):
# cs.extend(
# cell_context.get("row_headings", {}).get(r, {}).get(c, []))
row_es = [{"type": "th",
"is_visible": _is_visible(r, c, idx_lengths),
"attributes": [
format_attr({"key": "rowspan",
"value": idx_lengths.get((c, r), 1)})
],
"value": rlabels[r][c],
"class": " ".join([ROW_HEADING_CLASS, "level%s" % c,
"row%s" % r]),
"display_value": rlabels[r][c]}
for c in range(len(rlabels[r]))]
row_es = []
for c, value in enumerate(rlabels[r]):
es = {
"type": "th",
"is_visible": _is_visible(r, c, idx_lengths),
"value": value,
"display_value": value,
"class": " ".join([ROW_HEADING_CLASS, "level%s" % c,
"row%s" % r]),
}
rowspan = idx_lengths.get((c, r), 0)
if rowspan > 1:
es["attributes"] = [
format_attr({"key": "rowspan", "value": rowspan})
]
row_es.append(es)

for c, col in enumerate(self.data.columns):
cs = [DATA_CLASS, "row%s" % r, "col%s" % c]
Expand Down
38 changes: 12 additions & 26 deletions pandas/tests/formats/test_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,21 +141,18 @@ def test_empty_index_name_doesnt_display(self):
'type': 'th',
'value': 'A',
'is_visible': True,
'attributes': ["colspan=1"],
},
{'class': 'col_heading level0 col1',
'display_value': 'B',
'type': 'th',
'value': 'B',
'is_visible': True,
'attributes': ["colspan=1"],
},
{'class': 'col_heading level0 col2',
'display_value': 'C',
'type': 'th',
'value': 'C',
'is_visible': True,
'attributes': ["colspan=1"],
}]]

self.assertEqual(result['head'], expected)
Expand All @@ -168,11 +165,9 @@ def test_index_name(self):
expected = [[{'class': 'blank level0', 'type': 'th', 'value': '',
'display_value': '', 'is_visible': True},
{'class': 'col_heading level0 col0', 'type': 'th',
'value': 'B', 'display_value': 'B',
'is_visible': True, 'attributes': ['colspan=1']},
'value': 'B', 'display_value': 'B', 'is_visible': True},
{'class': 'col_heading level0 col1', 'type': 'th',
'value': 'C', 'display_value': 'C',
'is_visible': True, 'attributes': ['colspan=1']}],
'value': 'C', 'display_value': 'C', 'is_visible': True}],
[{'class': 'index_name level0', 'type': 'th',
'value': 'A'},
{'class': 'blank', 'type': 'th', 'value': ''},
Expand All @@ -191,9 +186,7 @@ def test_multiindex_name(self):
{'class': 'blank level0', 'type': 'th', 'value': '',
'display_value': '', 'is_visible': True},
{'class': 'col_heading level0 col0', 'type': 'th',
'value': 'C', 'display_value': 'C',
'is_visible': True, 'attributes': ['colspan=1'],
}],
'value': 'C', 'display_value': 'C', 'is_visible': True}],
[{'class': 'index_name level0', 'type': 'th',
'value': 'A'},
{'class': 'index_name level1', 'type': 'th',
Expand Down Expand Up @@ -618,16 +611,14 @@ def test_mi_sparse(self):
body_1 = result['body'][0][1]
expected_1 = {
"value": 0, "display_value": 0, "is_visible": True,
"type": "th", "attributes": ["rowspan=1"],
"class": "row_heading level1 row0",
"type": "th", "class": "row_heading level1 row0",
}
tm.assert_dict_equal(body_1, expected_1)

body_10 = result['body'][1][0]
expected_10 = {
"value": 'a', "display_value": 'a', "is_visible": False,
"type": "th", "attributes": ["rowspan=1"],
"class": "row_heading level0 row1",
"type": "th", "class": "row_heading level0 row1",
}
tm.assert_dict_equal(body_10, expected_10)

Expand All @@ -637,9 +628,8 @@ def test_mi_sparse(self):
'is_visible': True, "display_value": ''},
{'type': 'th', 'class': 'blank level0', 'value': '',
'is_visible': True, 'display_value': ''},
{'attributes': ['colspan=1'], 'class': 'col_heading level0 col0',
'is_visible': True, 'type': 'th', 'value': 'A',
'display_value': 'A'}]
{'type': 'th', 'class': 'col_heading level0 col0', 'value': 'A',
'is_visible': True, 'display_value': 'A'}]
self.assertEqual(head, expected)

def test_mi_sparse_disabled(self):
Expand All @@ -650,7 +640,7 @@ def test_mi_sparse_disabled(self):
result = df.style._translate()
body = result['body']
for row in body:
self.assertEqual(row[0]['attributes'], ['rowspan=1'])
assert 'attributes' not in row[0]

def test_mi_sparse_index_names(self):
df = pd.DataFrame({'A': [1, 2]}, index=pd.MultiIndex.from_arrays(
Expand Down Expand Up @@ -686,28 +676,24 @@ def test_mi_sparse_column_names(self):
'type': 'th', 'is_visible': True},
{'class': 'index_name level1', 'value': 'col_1',
'display_value': 'col_1', 'is_visible': True, 'type': 'th'},
{'attributes': ['colspan=1'],
'class': 'col_heading level1 col0',
{'class': 'col_heading level1 col0',
'display_value': 1,
'is_visible': True,
'type': 'th',
'value': 1},
{'attributes': ['colspan=1'],
'class': 'col_heading level1 col1',
{'class': 'col_heading level1 col1',
'display_value': 0,
'is_visible': True,
'type': 'th',
'value': 0},

{'attributes': ['colspan=1'],
'class': 'col_heading level1 col2',
{'class': 'col_heading level1 col2',
'display_value': 1,
'is_visible': True,
'type': 'th',
'value': 1},

{'attributes': ['colspan=1'],
'class': 'col_heading level1 col3',
{'class': 'col_heading level1 col3',
'display_value': 0,
'is_visible': True,
'type': 'th',
Expand Down

0 comments on commit f65a641

Please sign in to comment.