You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Pandas Styler基于列值为不同行组设置差异化样式

DataFrame分组样式定制方案

需求说明

现有如下DataFrame df:

import pandas as pd

data = {'person': {0: 'a',
  1: 'a',
  2: 'a',
  3: 'a',
  4: 'a',
  5: 'a',
  6: 'b',
  7: 'b',
  8: 'b',
  9: 'b',
  10: 'b',
  11: 'b',
  12: 'c',
  13: 'c',
  14: 'c',
  15: 'c',
  16: 'c',
  17: 'c'},
 'x': {0: 1,
  1: 1,
  2: 1,
  3: 1,
  4: 1,
  5: 1,
  6: 1,
  7: 1,
  8: 1,
  9: 1,
  10: 1,
  11: 1,
  12: 1,
  13: 1,
  14: 1,
  15: 1,
  16: 1,
  17: 1},
 'y': {0: 2,
  1: 2,
  2: 2,
  3: 2,
  4: 2,
  5: 2,
  6: 2,
  7: 2,
  8: 2,
  9: 2,
  10: 2,
  11: 2,
  12: 2,
  13: 2,
  14: 2,
  15: 2,
  16: 2,
  17: 2},
 'z': {0: 'foo',
  1: 'foo',
  2: 'foo',
  3: 'bar',
  4: 'bar',
  5: 'bar',
  6: 'foo',
  7: 'foo',
  8: 'foo',
  9: 'bar',
  10: 'bar',
  11: 'bar',
  12: 'foo',
  13: 'foo',
  14: 'foo',
  15: 'bar',
  16: 'bar',
  17: 'bar'}}

df = pd.DataFrame.from_dict(data, orient='columns')

需要实现两个样式效果:

  1. 每个person分组内,不同z值对应的行组使用交替背景色
  2. 每个person的最后一行添加底部边框

期望效果

每个person分组内,z为foo的行组和z为bar的行组呈现不同的背景色(如浅灰和白色交替),且每个person的最后一行有明显的底部分隔边框。

已尝试方案及问题

  • 嵌套循环拆分分组后拼接Styler对象:报错TypeError: cannot concatenate object of type '<class 'pandas.io.formats.style.Styler'>'; only Series and DataFrame objs are valid,因为Styler对象不支持拼接。
  • 使用嵌套np.where的lambda函数:报错AttributeError: 'Styler' object has no attribute 'style',因为循环后变量变为Styler对象,无法再调用style属性。

解决方案

1. 实现分组内z值行组的交替背景色

通过生成分组内的样式标记,结合Styler.apply批量设置背景色:

def set_alternate_colors(df):
    # 为每个person内的z组生成唯一标记
    df['color_group'] = df.groupby(['person', 'z']).ngroup()
    # 同一person内,z组的标记取模2得到交替标识
    df['color_flag'] = df.groupby('person')['color_group'].transform(lambda x: x % 2)
    # 根据标识生成样式矩阵
    styles = pd.DataFrame('', index=df.index, columns=df.columns)
    styles.loc[df['color_flag'] == 1, :] = 'background-color: #f0f0f0'
    return styles

# 应用背景色样式
styled_df = df.style.apply(set_alternate_colors, axis=None)

2. 为每个person最后一行添加底部边框

通过groupby定位最后一行索引,生成边框样式矩阵:

def set_bottom_border(df):
    # 获取每个person分组的最后一行索引
    last_rows = df.groupby('person').tail(1).index
    styles = pd.DataFrame('', index=df.index, columns=df.columns)
    styles.loc[last_rows, :] = 'border-bottom: 2px solid black'
    return styles

# 叠加边框样式
styled_df = styled_df.apply(set_bottom_border, axis=None)

# 显示最终样式效果
styled_df

完整代码

import pandas as pd

data = {'person': {0: 'a',
  1: 'a',
  2: 'a',
  3: 'a',
  4: 'a',
  5: 'a',
  6: 'b',
  7: 'b',
  8: 'b',
  9: 'b',
  10: 'b',
  11: 'b',
  12: 'c',
  13: 'c',
  14: 'c',
  15: 'c',
  16: 'c',
  17: 'c'},
 'x': {0: 1,
  1: 1,
  2: 1,
  3: 1,
  4: 1,
  5: 1,
  6: 1,
  7: 1,
  8: 1,
  9: 1,
  10: 1,
  11: 1,
  12: 1,
  13: 1,
  14: 1,
  15: 1,
  16: 1,
  17: 1},
 'y': {0: 2,
  1: 2,
  2: 2,
  3: 2,
  4: 2,
  5: 2,
  6: 2,
  7: 2,
  8: 2,
  9: 2,
  10: 2,
  11: 2,
  12: 2,
  13: 2,
  14: 2,
  15: 2,
  16: 2,
  17: 2},
 'z': {0: 'foo',
  1: 'foo',
  2: 'foo',
  3: 'bar',
  4: 'bar',
  5: 'bar',
  6: 'foo',
  7: 'foo',
  8: 'foo',
  9: 'bar',
  10: 'bar',
  11: 'bar',
  12: 'foo',
  13: 'foo',
  14: 'foo',
  15: 'bar',
  16: 'bar',
  17: 'bar'}}

df = pd.DataFrame.from_dict(data, orient='columns')

def set_alternate_colors(df):
    df['color_group'] = df.groupby(['person', 'z']).ngroup()
    df['color_flag'] = df.groupby('person')['color_group'].transform(lambda x: x % 2)
    styles = pd.DataFrame('', index=df.index, columns=df.columns)
    styles.loc[df['color_flag'] == 1, :] = 'background-color: #f0f0f0'
    return styles

def set_bottom_border(df):
    last_rows = df.groupby('person').tail(1).index
    styles = pd.DataFrame('', index=df.index, columns=df.columns)
    styles.loc[last_rows, :] = 'border-bottom: 2px solid black'
    return styles

styled_df = df.style.apply(set_alternate_colors, axis=None).apply(set_bottom_border, axis=None)
styled_df

内容的提问来源于stack exchange,提问作者bismo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 06:54:54