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

Pandas:为Styler添加WoW/MoM列颜色样式及解决层叠问题

Pandas Styler实现WoW/MoM列正负值颜色样式层叠

问题背景

现有一段生成带样式DataFrame的Pandas代码,需为WoW和MoM列添加额外样式规则:正值显示为绿色,负值显示为红色,其余值保持默认样式。遇到以下问题:

  • 多个样式函数层叠时,后续应用的样式会覆盖之前的样式
  • 出现'Styler' object has no attribute 'map'错误

解决方案

核心说明

  • Pandas Styler的样式支持增量叠加,通过链式调用或保留Styler对象的连续赋值,配合subset参数指定作用范围,即可实现多样式共存
  • 不存在Styler.map方法,正确用于单元格级样式的方法是Styler.applymap,行/列级样式使用Styler.apply

修改后的完整代码

import pandas as pd

df = pd.DataFrame(index=pd.Index(['KPI ABC', 'KPI DEF', 'KPI GHI', 'KPI JKL']),
                  columns=pd.MultiIndex.from_product([
                      ['North America', 'Europe', 'Asia'],
                      ['Weekly', 'Monthly'],
                      ['WK-01', 'WK-02', 'WK-03', 'WK-04', 'WK-05', 'WoW', 'Jan', 'Feb', 'Mar', 'MoM']]))


df.at['KPI ABC', ('North America', 'Weekly', 'WoW')] = 1.44
df.at['KPI ABC', ('Europe', 'Monthly', 'MoM')] = -0.24
df.at['KPI ABC', ('Asia', 'Monthly', 'MoM')] = 0

mapper =  {('North America', 'Weekly', 'WoW'): '{0:.2f}%',
           ('Europe', 'Monthly', 'MoM'): '{0:.2f}%',
           ('Asia', 'Monthly', 'MoM'): '{0:.2f}%'}

s = df.style.format(mapper).hide([
    ('North America', 'Weekly', 'Jan'),
    ('North America', 'Weekly', 'Feb'),
    ('North America', 'Weekly', 'Mar'),
    ('North America', 'Weekly', 'MoM'),
    ('North America', 'Monthly', 'WK-01'),
    ('North America', 'Monthly', 'WK-02'),
    ('North America', 'Monthly', 'WK-03'),
    ('North America', 'Monthly', 'WK-04'),
    ('North America', 'Monthly', 'WK-05'),
    ('North America', 'Monthly', 'WoW'),

    ('Europe', 'Weekly', 'Jan'),
    ('Europe', 'Weekly', 'Feb'),
    ('Europe', 'Weekly', 'Mar'),
    ('Europe', 'Weekly', 'MoM'),
    ('Europe', 'Monthly', 'WK-01'),
    ('Europe', 'Monthly', 'WK-02'),
    ('Europe', 'Monthly', 'WK-03'),
    ('Europe', 'Monthly', 'WK-04'),
    ('Europe', 'Monthly', 'WK-05'),
    ('Europe', 'Monthly', 'WoW'),

    ('Asia', 'Weekly', 'Jan'),
    ('Asia', 'Weekly', 'Feb'),
    ('Asia', 'Weekly', 'Mar'),
    ('Asia', 'Weekly', 'MoM'),
    ('Asia', 'Monthly', 'WK-01'),
    ('Asia', 'Monthly', 'WK-02'),
    ('Asia', 'Monthly', 'WK-03'),
    ('Asia', 'Monthly', 'WK-04'),
    ('Asia', 'Monthly', 'WK-05'),
    ('Asia', 'Monthly', 'WoW')],
    axis="columns")

cell_hover = {'selector': 'td:hover', 'props': [('background-color', '#ffffb3')]}
headers = {'selector': 'th:not(.index_name)', 'props': 'background-color: #000066; color: white;'}
global_left_align = {'selector': 'th', 'props': 'text-align: left'}

s = s.set_table_styles([cell_hover, headers, global_left_align])

border_styles = {
    ('North America', 'Weekly', 'WoW'): {'selector': 'th, td', 'props': 'border-left: 1px solid #000066; border-right: 1px solid #000066'},
    ('North America', 'Monthly', 'MoM'): {'selector': 'th, td', 'props': 'border-left: 1px solid #000066; border-right: 1px solid #000066'},
    ('Europe', 'Weekly', 'WoW'): {'selector': 'th, td', 'props': 'border-left: 1px solid #000066; border-right: 1px solid #000066'},
    ('Europe', 'Monthly', 'MoM'): {'selector': 'th, td', 'props': 'border-left: 1px solid #000066; border-right: 1px solid #000066'},
    ('Asia', 'Weekly', 'WoW'): {'selector': 'th, td', 'props': 'border-left: 1px solid #000066; border-right: 1px solid #000066'},
    ('Asia', 'Monthly', 'MoM'): {'selector': 'th, td', 'props': 'border-left: 1px solid #000066; border-right: 1px solid #000066'}
}

for key, value in border_styles.items():
    s = s.set_table_styles({key: [value]}, overwrite=False, axis=0)

s = s.set_table_styles([{'selector': 'th.col_heading', 'props': 'text-align: center;'}], overwrite=False)

def larger_headers(val):
    if val in ['North America', 'Europe', 'Asia']:
        return 'font-size: 16pt'
    return ''

s = s.applymap_index(larger_headers, axis='columns', level=0)

# --- 新增:WoW/MoM列正负值颜色样式 ---
def color_pos_neg(val):
    if pd.notna(val):
        if val > 0:
            return 'color: green'
        elif val < 0:
            return 'color: red'
    return ''

# 筛选所有WoW和MoM列
wow_mom_cols = df.columns[df.columns.get_level_values(2).isin(['WoW', 'MoM'])]
# 应用颜色样式,subset指定作用列,增量叠加不覆盖原有样式
s = s.applymap(color_pos_neg, subset=wow_mom_cols)
# --- 新增结束 ---

tooltips = pd.DataFrame('', index=df.index, columns=df.columns)
tooltips.at['KPI JKL', ('Europe', 'Monthly', 'Feb')] = 'Tooltip for Europe, Monthly, Feb [KPI JKL]'
tooltips.at['KPI GHI', ('Asia', 'Weekly', 'WK-04')] = 'Tooltip for Asia, Weekly, WK-04 [KPI GHI]'
s = s.set_tooltips(tooltips)

html = s.to_html()

tooltips_index = {
    'KPI ABC': 'Tooltip for KPI ABC',
    'KPI DEF': 'Tooltip for KPI DEF',
    'KPI GHI': 'Tooltip for KPI GHI',
    'KPI JKL': 'Tooltip for KPI JKL'
}

for kpi, tooltip in tooltips_index.items():
    html = html.replace(f'>{kpi}<', f' title=\"{tooltip}\">{kpi}<')

with open('styled_table_with_index_tooltips.html', 'w') as f:
    f.write(html)

from IPython.core.display import display, HTML
display(HTML(html))

关键修改点解析

  1. 颜色样式函数定义:
    color_pos_neg函数判断单元格值的正负,返回对应的CSS颜色样式,空值或0返回默认样式。
  2. 精准作用于目标列:
    通过df.columns.get_level_values(2).isin(['WoW', 'MoM'])筛选MultiIndex中第三层为WoW/MoM的列,用subset参数传递给applymap,确保样式只作用于目标列。
  3. 样式叠加规则:
    所有set_table_styles调用都添加overwrite=False,新增的applymap操作直接叠加在原有Styler对象上,不会覆盖之前的边框、悬停等样式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 16:48:10