在Pandas中高效计算分组堆叠数据框中的变量比率

在Pandas中高效计算分组堆叠数据框中的变量比率

本教程详细介绍了如何在pandas数据框中,针对特定分组(如g1和g2)内的变量(如tpe列中的’ts’与’td’)计算比率,并将结果作为新行添加回原数据框。文章通过一个实际案例,展示了如何利用`set_index`、`unstack`和向量化操作,以高效且优雅的方式处理数据重塑、比率计算以及缺失值(nan)的填充,避免了传统`groupby().apply()`方法可能存在的性能瓶颈和复杂性。

数据框中分组比率计算的挑战与解决方案

在数据分析中,我们经常需要根据数据框中的某些列进行分组,然后计算组内特定变量之间的比率。例如,在一个包含交易类型(TPE)和数量(QC)的数据框中,我们可能需要计算每组(G1, G2)中’ts’类型数量与’td’类型数量的比率(ts/td)。此外,还需要将计算出的比率作为新行添加到原始数据框中,并且要妥善处理那些不包含完整’ts’和’td’值的组。

初始数据结构

假设我们有以下Pandas DataFrame df_in:

import pandas as pdimport numpy as npdata = {    'G1': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'D'],    'G2': ['S1', 'S1', 'S2', 'S2', 'S1', 'S1', 'S2', 'S2', 'S1', 'S2'],    'TPE': ['td', 'ts', 'td', 'ts', 'td', 'ts', 'td', 'ts', 'td', 'ts'],    'QC': [2, 4, 6, 3, 20, 40, 60, 30, 90, 7]}df_in = pd.DataFrame(data)# 模拟原始数据中可能存在的缺失类型df_in.loc[8, 'TPE'] = 'td' # C S1 只有 tddf_in.loc[9, 'TPE'] = 'ts' # D S2 只有 tsdf_in = df_in.drop(index=[8,9]).append(pd.DataFrame([['C', 'S1', 'td', 90], ['D', 'S2', 'ts', 7]], columns=df_in.columns), ignore_index=True)print("原始数据框 df_in:")print(df_in)

输出 df_in 如下:

  G1  G2 TPE  QC0  A  S1  td   21  A  S1  ts   42  A  S2  td   63  A  S2  ts   34  B  S1  td  205  B  S1  ts  406  B  S2  td  607  B  S2  ts  308  C  S1  td  909  D  S2  ts   7

目标输出

我们的目标是生成一个包含原始数据和计算出的比率的新数据框 df_out。对于每个 (G1, G2) 组,如果同时存在 ‘td’ 和 ‘ts’ 类型的 QC 值,则计算 ts_QC / td_QC 作为比率,并将新行的 TPE 列标记为 ‘ratio’。如果缺少 ‘td’ 或 ‘ts’,则比率应为空(NaN)。

期望的 df_out 结构如下:

   G1  G2    TPE   QC0   A  S1     td  2.01   A  S1     ts  4.02   A  S2     td  6.03   A  S2     ts  3.04   B  S1     td 20.05   B  S1     ts 40.06   B  S2     td 60.07   B  S2     ts 30.08   C  S1     td 90.09   D  S2     ts  7.010  A  S1  ratio  2.011  A  S2  ratio  0.512  B  S1  ratio  2.013  B  S2  ratio  0.514  C  S1  ratio  NaN15  D  S2  ratio  NaN

传统 groupby().apply() 方法的局限性

一种常见的思路是使用 groupby().apply() 方法,为每个组编写一个自定义函数来计算比率。

def calculate_ratio_apply(group):    td_row = group[group['TPE'] == 'td']    ts_row = group[group['TPE'] == 'ts']    if not td_row.empty and not ts_row.empty:        ratio = ts_row['QC'].values[0] / td_row['QC'].values[0]        return pd.DataFrame({'G1': [group['G1'].iloc[0]],                              'G2': [group['G2'].iloc[0]],                              'TPE': ['ratio'],                              'QC': [ratio]})    # 如果缺少td或ts,返回一个空的DataFrame,这会导致这些组的比率行被省略    return pd.DataFrame() # 这种方法会忽略没有完整td和ts值的组# grouped = df_in.groupby(['G1', 'G2']).apply(calculate_ratio_apply).reset_index(drop=True)# df_out_apply = pd.concat([df_in, grouped], ignore_index=True)# print("n使用 apply 方法(可能遗漏空比率):")# print(df_out_apply)

上述 apply 方法虽然能计算比率,但如果某个组没有同时包含 ‘td’ 和 ‘ts’ 值,它会返回一个空的DataFrame,导致这些组的比率行被完全省略,而不是填充 NaN。对于大型数据集,apply 方法也可能因为循环迭代而导致性能问题。

算家云 算家云

高效、便捷的人工智能算力服务平台

算家云 37 查看详情 算家云

优化方案:利用 unstack 和向量化操作

更高效和优雅的解决方案是利用 Pandas 的数据重塑功能,将 ‘TPE’ 列中的 ‘td’ 和 ‘ts’ 值转化为独立的列,然后进行向量化计算。

# 1. 重塑数据:将 'TPE' 列中的 'td' 和 'ts' 值转换为独立的列#    - set_index(['G1', 'G2', 'TPE']): 将这三列设为索引#    - unstack()['QC']: 将 TPE 索引层的数据(QC值)unstack(逆透视)成列#      结果是一个多级索引的 DataFrame,列为 TPE 的不同值(td, ts)tmp = df_in.set_index(['G1', 'G2', 'TPE']).unstack()['QC']print("n中间结果 tmp (重塑后的数据):")print(tmp)

中间结果 tmp 的结构如下,我们可以清晰地看到每个 (G1, G2) 组对应的 ‘td’ 和 ‘ts’ 值,以及缺失值(NaN):

TPE     td    tsG1 G2           A  S1    2.0   4.0   S2    6.0   3.0B  S1   20.0  40.0   S2   60.0  30.0C  S1   90.0   NaND  S2    NaN   7.0
# 2. 计算比率:直接对重塑后的列进行向量化除法#    - tmp['ts'].div(tmp['td']): 计算 'ts' 列与 'td' 列的比率#    - reset_index(name='QC'): 将多级索引重置为列,并将比率结果命名为 'QC'#    - assign(TPE='ratio'): 添加一个新列 'TPE',其值为 'ratio'ratio_df = tmp['ts'].div(tmp['td']).reset_index(name='QC').assign(TPE='ratio')print("n计算出的比率数据框 ratio_df:")print(ratio_df)

计算出的比率数据框 ratio_df 如下:

  G1  G2   QC    TPE0  A  S1  2.0  ratio1  A  S2  0.5  ratio2  B  S1  2.0  ratio3  B  S2  0.5  ratio4  C  S1  NaN  ratio5  D  S2  NaN  ratio
# 3. 合并数据:将原始数据框和计算出的比率数据框进行纵向合并df_out = pd.concat([df_in, ratio_df], ignore_index=True)print("n最终输出数据框 df_out:")print(df_out)

最终的 df_out 完美符合我们的要求:

   G1  G2    TPE   QC0   A  S1     td  2.01   A  S1     ts  4.02   A  S2     td  6.03   A  S2     ts  3.04   B  S1     td 20.05   B  S1     ts 40.06   B  S2     td 60.07   B  S2     ts 30.08   C  S1     td 90.09   D  S2     ts  7.010  A  S1  ratio  2.011  A  S2  ratio  0.512  B  S1  ratio  2.013  B  S2  ratio  0.514  C  S1  ratio  NaN15  D  S2  ratio  NaN

完整代码示例

import pandas as pdimport numpy as np# 原始数据框data = {    'G1': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'D'],    'G2': ['S1', 'S1', 'S2', 'S2', 'S1', 'S1', 'S2', 'S2', 'S1', 'S2'],    'TPE': ['td', 'ts', 'td', 'ts', 'td', 'ts', 'td', 'ts', 'td', 'ts'],    'QC': [2, 4, 6, 3, 20, 40, 60, 30, 90, 7]}df_in = pd.DataFrame(data)# 模拟原始数据中可能存在的缺失类型,确保 C S1 只有 td,D S2 只有 tsdf_in = df_in.drop(index=[8,9]).append(pd.DataFrame([['C', 'S1', 'td', 90], ['D', 'S2', 'ts', 7]], columns=df_in.columns), ignore_index=True)# 1. 重塑数据:将 'TPE' 列中的 'td' 和 'ts' 值转换为独立的列#    通过 set_index 和 unstack,将数据从长格式转换为宽格式,便于计算tmp = df_in.set_index(['G1', 'G2', 'TPE']).unstack()['QC']# 2. 计算比率并格式化结果#    - tmp['ts'].div(tmp['td']): 执行向量化除法,自动处理缺失值(NaN)#    - reset_index(name='QC'): 将多级索引重置为常规列,并将比率结果列命名为 'QC'#    - assign(TPE='ratio'): 添加一个新列 'TPE',其值为 'ratio'ratio_df = tmp['ts'].div(tmp['td']).reset_index(name='QC').assign(TPE='ratio')# 3. 合并数据:将原始数据框和计算出的比率数据框进行纵向合并df_out = pd.concat([df_in, ratio_df], ignore_index=True)print("最终输出数据框 df_out:")print(df_out)

注意事项与总结

效率提升: 相比于 groupby().apply(),使用 set_index().unstack() 结合向量化操作(如 .div())在处理大型数据集时通常更高效,因为它利用了 Pandas 底层的优化 C 语言实现。缺失值处理: unstack() 操作会自动将缺失的数据填充为 NaN。在进行除法运算时,任何包含 NaN 的运算结果仍为 NaN,这自然地满足了对“空比率”的需求。可读性与简洁性: 这种方法代码更简洁,逻辑更清晰,避免了自定义函数中复杂的条件判断。通用性: 这种数据重塑和向量化计算的模式适用于各种需要计算分组内变量之间比率或差值等场景。

通过本教程,您应该能够高效且优雅地在 Pandas 数据框中处理分组比率计算问题,即使面对复杂的数据结构和缺失值情况也能游刃有余。

以上就是在Pandas中高效计算分组堆叠数据框中的变量比率的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/590924.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月10日 15:52:56
下一篇 2025年11月10日 15:53:53

相关推荐

发表回复

登录后才能评论
关注微信