It should be simple, but so far google has not been a friend, it's wasted a lot of time with non-answers, that answer the wrong question.
So, I know that we have df.style.set_td_classes(classes)
that allows me to set a class on each 'td' based on a dataframe of class names. However it does nothing for the index values. I want the whole row to get a class. eg. <tr class=myclass>
, that would make it so simple, I could create a series with class names where I wanted, exactly the same way as for set_td_classes. But there is no function available.
What can I do to get pandas to set a class name onto a tr element ?
In response to a few comments. Here is a small bit of python code to show the issue...
def highlight_max(x, color):
if len(x.shape) < 2:
if any(abs(x)>1.0):
return pd.Series(f'color:{color};', x.index)
else:
return pd.Series(None, x.index)
else:
return np.where(x.to_numpy()>1.0, f"color: {color};", None)
df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
# 1
df.style.apply(highlight_max, color='red').to_html(Path.home()/'teststyle.htm')
# 2
df.style.apply(highlight_max, color='blue', axis=1).to_html(Path.home()/'teststyle.htm')
# 3
df.style.apply(highlight_max, color='green', axis=None).to_html(Path.home()/'teststyle.htm')
If style.apply was to achieve the result I'm looking for then #2 should have given me my html (@Quang Hoang), but it doesn't. It does highlight across all the columns in the row but not the index. Also the html is quite ugly, with a separate class defined for each cell! E.g.
<html>
<head>
<style type="text/css">
#T_16f7a_row2_col0, #T_16f7a_row2_col1, #T_16f7a_row4_col0, #T_16f7a_row4_col1 {
color: blue;
}
</style>
</head><body><table id="T_16f7a">
<thead>
<tr>
<th class="blank level0"> </th>
<th id="T_16f7a_level0_col0" class="col_heading level0 col0">A</th>
<th id="T_16f7a_level0_col1" class="col_heading level0 col1">B</th>
</tr>
</thead>
<tbody>
<tr>
<th id="T_16f7a_level0_row0" class="row_heading level0 row0">0</th>
<td id="T_16f7a_row0_col0" class="data row0 col0">0.570318</td>
<td id="T_16f7a_row0_col1" class="data row0 col1">-0.791125</td>
</tr>
<tr>
<th id="T_16f7a_level0_row1" class="row_heading level0 row1">1</th>
<td id="T_16f7a_row1_col0" class="data row1 col0">0.734733</td>
<td id="T_16f7a_row1_col1" class="data row1 col1">0.344844</td>
</tr>
<tr>
<th id="T_16f7a_level0_row2" class="row_heading level0 row2">2</th>
<td id="T_16f7a_row2_col0" class="data row2 col0">1.703771</td>
<td id="T_16f7a_row2_col1" class="data row2 col1">0.693211</td>
</tr>
<tr>
<th id="T_16f7a_level0_row3" class="row_heading level0 row3">3</th>
<td id="T_16f7a_row3_col0" class="data row3 col0">0.740752</td>
<td id="T_16f7a_row3_col1" class="data row3 col1">-0.588767</td>
</tr>
<tr>
<th id="T_16f7a_level0_row4" class="row_heading level0 row4">4</th>
<td id="T_16f7a_row4_col0" class="data row4 col0">-1.817743</td>
<td id="T_16f7a_row4_col1" class="data row4 col1">0.602709</td>
</tr>
</tbody>
</table>
</body></html>
There is a way to force your own table id (almost, it always gets the 'T_' prefix), so you could then have predictable class names for an external css file. However it's not going to be too useful, since to change a set of rows, you are going to have to call a unique class for each row's class identifier (in each cell). There is still no class exported into the 'tr' cell which is what I'm looking for.
Update: I've found way that least allows a single class to control multiple rows (at present not including the index column). But it's not very intuitive.
hls=df.loc[abs(df['B']) > 1.0].index
classes = pd.DataFrame().reindex_like(df).astype(object)
classes.loc[hls]='shade'
classes.fillna('', inplace=True)
df.style.set_uuid('table').set_td_classes(classes).to_html(Path.home()/'teststyle.htm')
So this adds a class 'shade' on every 'td' cell that meets the given condition. However, I do need to add my own extra css code into the html to control the 'shade' class. But at least, to change color or some other style, I only change 1 class property.
It should be simple, but so far google has not been a friend, it's wasted a lot of time with non-answers, that answer the wrong question.
So, I know that we have df.style.set_td_classes(classes)
that allows me to set a class on each 'td' based on a dataframe of class names. However it does nothing for the index values. I want the whole row to get a class. eg. <tr class=myclass>
, that would make it so simple, I could create a series with class names where I wanted, exactly the same way as for set_td_classes. But there is no function available.
What can I do to get pandas to set a class name onto a tr element ?
In response to a few comments. Here is a small bit of python code to show the issue...
def highlight_max(x, color):
if len(x.shape) < 2:
if any(abs(x)>1.0):
return pd.Series(f'color:{color};', x.index)
else:
return pd.Series(None, x.index)
else:
return np.where(x.to_numpy()>1.0, f"color: {color};", None)
df = pd.DataFrame(np.random.randn(5, 2), columns=["A", "B"])
# 1
df.style.apply(highlight_max, color='red').to_html(Path.home()/'teststyle.htm')
# 2
df.style.apply(highlight_max, color='blue', axis=1).to_html(Path.home()/'teststyle.htm')
# 3
df.style.apply(highlight_max, color='green', axis=None).to_html(Path.home()/'teststyle.htm')
If style.apply was to achieve the result I'm looking for then #2 should have given me my html (@Quang Hoang), but it doesn't. It does highlight across all the columns in the row but not the index. Also the html is quite ugly, with a separate class defined for each cell! E.g.
<html>
<head>
<style type="text/css">
#T_16f7a_row2_col0, #T_16f7a_row2_col1, #T_16f7a_row4_col0, #T_16f7a_row4_col1 {
color: blue;
}
</style>
</head><body><table id="T_16f7a">
<thead>
<tr>
<th class="blank level0"> </th>
<th id="T_16f7a_level0_col0" class="col_heading level0 col0">A</th>
<th id="T_16f7a_level0_col1" class="col_heading level0 col1">B</th>
</tr>
</thead>
<tbody>
<tr>
<th id="T_16f7a_level0_row0" class="row_heading level0 row0">0</th>
<td id="T_16f7a_row0_col0" class="data row0 col0">0.570318</td>
<td id="T_16f7a_row0_col1" class="data row0 col1">-0.791125</td>
</tr>
<tr>
<th id="T_16f7a_level0_row1" class="row_heading level0 row1">1</th>
<td id="T_16f7a_row1_col0" class="data row1 col0">0.734733</td>
<td id="T_16f7a_row1_col1" class="data row1 col1">0.344844</td>
</tr>
<tr>
<th id="T_16f7a_level0_row2" class="row_heading level0 row2">2</th>
<td id="T_16f7a_row2_col0" class="data row2 col0">1.703771</td>
<td id="T_16f7a_row2_col1" class="data row2 col1">0.693211</td>
</tr>
<tr>
<th id="T_16f7a_level0_row3" class="row_heading level0 row3">3</th>
<td id="T_16f7a_row3_col0" class="data row3 col0">0.740752</td>
<td id="T_16f7a_row3_col1" class="data row3 col1">-0.588767</td>
</tr>
<tr>
<th id="T_16f7a_level0_row4" class="row_heading level0 row4">4</th>
<td id="T_16f7a_row4_col0" class="data row4 col0">-1.817743</td>
<td id="T_16f7a_row4_col1" class="data row4 col1">0.602709</td>
</tr>
</tbody>
</table>
</body></html>
There is a way to force your own table id (almost, it always gets the 'T_' prefix), so you could then have predictable class names for an external css file. However it's not going to be too useful, since to change a set of rows, you are going to have to call a unique class for each row's class identifier (in each cell). There is still no class exported into the 'tr' cell which is what I'm looking for.
Update: I've found way that least allows a single class to control multiple rows (at present not including the index column). But it's not very intuitive.
hls=df.loc[abs(df['B']) > 1.0].index
classes = pd.DataFrame().reindex_like(df).astype(object)
classes.loc[hls]='shade'
classes.fillna('', inplace=True)
df.style.set_uuid('table').set_td_classes(classes).to_html(Path.home()/'teststyle.htm')
So this adds a class 'shade' on every 'td' cell that meets the given condition. However, I do need to add my own extra css code into the html to control the 'shade' class. But at least, to change color or some other style, I only change 1 class property.
Actually you don't need to add the CSS to your external HTML file, you can use set_table_styles
to add the class CSS directly.
from pandas.io.formats.style import Styler
s = Styler(df, cell_ids=False)
hls=df.loc[abs(df['B']) > 0.5].index
classes = pd.DataFrame().reindex_like(df).astype(object)
classes.loc[hls]='shade'
classes.fillna('', inplace=True)
html = s.set_uuid('table').set_td_classes(classes).set_table_styles([
{"selector": ".shade", "props": "font-weight:bold; color: green;"}
], overwrite=False).to_html()
HTML:
<style type="text/css">
#T_table .shade {
font-weight: bold;
color: green;
}
</style>
<table id="T_table">
<thead>
<tr>
<th class="blank level0" > </th>
<th class="col_heading level0 col0" >A</th>
<th class="col_heading level0 col1" >B</th>
</tr>
</thead>
<tbody>
<tr>
<th class="row_heading level0 row0" >0</th>
<td class="data row0 col0" >-0.584093</td>
<td class="data row0 col1" >0.309629</td>
</tr>
<tr>
<th class="row_heading level0 row1" >1</th>
<td class="data row1 col0 shade" >0.997064</td>
<td class="data row1 col1 shade" >-0.779591</td>
</tr>
...
Alternatively you can use set_table_styles
for each row.
from pandas.io.formats.style import Styler
s = Styler(df, cell_ids=False)
hls=df.loc[abs(df['B']) > 1.0].index
s.set_table_styles({
row: [{"selector": "td", "props": "color: red; font-weight: bold;"}]
for row in hls
}, axis=1)
This will give:
<style type="text/css">
#T_35571 td.row2 {
color: red;
font-weight: bold;
}
#T_35571 td.row3 {
color: red;
font-weight: bold;
}
#T_35571 td.row4 {
color: red;
font-weight: bold;
}
</style>
<table id="T_35571">
<thead>
<tr>
<th class="blank level0" > </th>
<th class="col_heading level0 col0" >A</th>
<th class="col_heading level0 col1" >B</th>
</tr>
</thead>
<tbody>
<tr>
<th class="row_heading level0 row0" >0</th>
<td class="data row0 col0" >-0.584093</td>
<td class="data row0 col1" >0.309629</td>
</tr>
<tr>
<th class="row_heading level0 row1" >1</th>
<td class="data row1 col0" >0.997064</td>
<td class="data row1 col1" >-0.779591</td>
</tr>
...
You are correct in saying that there is no set_tr_classes
. This would require an enhancement to the API.
df.style.apply(lambda row: some-row-func, axis=1)
. – Quang Hoang Commented Jan 22 at 21:23