css - Pandas Styler, It should be simple to set a class for a row (<tr>) based on some cell logic - Stack Overflow

admin2025-04-21  1

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">&nbsp;</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">&nbsp;</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.

Share Improve this question edited Jan 29 at 19:21 John Tweed asked Jan 22 at 20:24 John TweedJohn Tweed 31 silver badge3 bronze badges 8
  • 4 can you provide a minimal reproducible example? – iBeMeltin Commented Jan 22 at 20:55
  • A row class/format can be done with a df.style.apply(lambda row: some-row-func, axis=1). – Quang Hoang Commented Jan 22 at 21:23
  • pandas.pydata.org/docs/reference/api/… – rehaqds Commented Jan 22 at 22:46
  • @rehaqds, The apply_index only applies a css function to the 'th' columns of the rows (or columns) not to the 'tr' row elements. – John Tweed Commented Jan 28 at 13:40
  • @Quang Hoang, I don't think that this achieves a row styling, rather a stlye applied across all columns, (but not the index). It's a very over-blown way of achieving something that can be accomplished with a simple row 'tr' style. I'll try and update my post to illustrate what I mean. – John Tweed Commented Jan 28 at 14:57
 |  Show 3 more comments

1 Answer 1

Reset to default 0

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" >&nbsp;</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" >&nbsp;</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.

转载请注明原文地址:http://anycun.com/QandA/1745229837a90518.html