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

Pandas多列set_index未按预期分组,如何实现指定显示效果?

问题与解决方案

问题背景

给定如下Pandas DataFrame:

import pandas as pd
df = pd.DataFrame([ 
    ['abc bank', 'Delhi', 'person a'], 
    ['abc bank', 'Delhi', 'person b'], 
    ['abc bank', 'Bombay', 'person c'], 
    ['abc bank', 'Bombay', 'person d'], 
    ['abc bank', 'Surat', 'person c'], 
    ['abc bank', 'Surat', 'person d'], 
    ['cde bank', 'Delhi', 'person z'], 
    ['cde bank', 'Delhi', 'person y'], 
    ['cde bank', 'Bombay', 'person x']  
], 
columns=['corporation', 'city', 'managers']) 

执行df = df.set_index(['corporation', 'city'])后,默认显示会重复输出索引列的重复值,而我们需要:

  1. 控制台显示时合并重复的索引项(视觉跨行)
  2. 最终生成带有真实rowspan属性的HTML表格(用于网页展示)

解决方案

1. 控制台实现合并索引显示

Pandas内置了多级索引合并显示的选项,只需开启即可:

# 开启多级索引合并显示
pd.set_option('display.multi_sparse', True)
# 打印DataFrame即可看到预期的合并效果
print(df)

开启后,重复的索引值会被留白代替,实现和示例一致的控制台输出效果。

2. 生成带rowspan的HTML表格

Pandas默认的to_html()方法不会自动添加跨行属性,需要手动处理,以下两种方法可选:

方法一:利用Pandas Style自定义渲染

通过Style对象计算跨行数并应用CSS样式:

def generate_merged_html(df):
    # 重置索引,将索引转为列以便处理
    temp_df = df.reset_index()
    
    # 计算每个corporation和city对应的跨行数
    temp_df['corp_rowspan'] = temp_df.groupby('corporation')['corporation'].transform('count')
    temp_df['city_rowspan'] = temp_df.groupby(['corporation', 'city'])['city'].transform('count')
    
    # 定义样式应用函数
    def apply_row_style(row):
        styles = []
        # 处理corporation列:仅组内第一行显示并设置rowspan
        if row.name == temp_df[temp_df['corporation'] == row['corporation']].index[0]:
            styles.append(f'rowspan: {row["corp_rowspan"]};')
        else:
            styles.append('display: none;')
        
        # 处理city列:仅组内第一行显示并设置rowspan
        if row.name == temp_df[(temp_df['corporation'] == row['corporation']) & (temp_df['city'] == row['city'])].index[0]:
            styles.append(f'rowspan: {row["city_rowspan"]};')
        else:
            styles.append('display: none;')
        
        # 第三列(managers)无特殊样式
        return styles + ['']
    
    # 应用样式并生成HTML,隐藏辅助列
    html_output = temp_df.style.apply(apply_row_style, axis=1)\
                               .hide(['corp_rowspan', 'city_rowspan'])\
                               .to_html()
    return html_output

# 使用示例
html_table = generate_merged_html(df)
# 保存到文件
with open('merged_bank_table.html', 'w', encoding='utf-8') as f:
    f.write(html_table)

方法二:手动构建HTML标签

如果需要更精细的控制(比如自定义表格样式),可以手动遍历分组构建HTML:

def build_merged_html_table(df):
    # 初始化HTML结构
    html = '''
    <table border="1" cellpadding="4" cellspacing="0">
        <thead>
            <tr>
                <th>corporation</th>
                <th>city</th>
                <th>managers</th>
            </tr>
        </thead>
        <tbody>
    '''
    
    # 按顶级索引(corporation)分组
    for corp_name, corp_group in df.groupby(level='corporation'):
        total_corp_rows = len(corp_group)
        first_city_in_corp = True
        
        # 按二级索引(city)分组
        for city_name, city_group in corp_group.groupby(level='city'):
            total_city_rows = len(city_group)
            
            # 处理第一行:添加corporation的rowspan(仅每组第一个city行显示)
            if first_city_in_corp:
                html += f'<tr><td rowspan="{total_corp_rows}">{corp_name}</td>'
                first_city_in_corp = False
            else:
                html += '<tr>'
            
            # 添加city的rowspan
            html += f'<td rowspan="{total_city_rows}">{city_name}</td>'
            
            # 遍历添加managers行
            for idx, row in enumerate(city_group.itertuples()):
                if idx != 0:
                    html += '<tr>'
                html += f'<td>{row.managers}</td></tr>'
    
    # 闭合HTML标签
    html += '</tbody></table>'
    return html

# 使用示例
html_table = build_merged_html_table(df)

两种方法生成的HTML表格在浏览器中都会显示为跨行合并的效果,满足需求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 07:15:56