按公司分组计算首尾非NaN值相对变化的高效实现方法咨询
问题:按公司分组计算数据首尾非NaN值的相对变化
我希望针对以下数据集,按Company字段分组计算每组第一个非NaN的Data值与最后一个非NaN的Data值之间的相对变化,计算公式为:
(last non-na value)/(first non-na value)-1
原始数据集
| Year | Company | Data |
|---|---|---|
| 2019 | X | 341976.00 |
| 2020 | X | 1.000 |
| 2021 | X | 282872.00 |
| 2019 | Y | NaN |
| 2020 | Y | NaN |
| 2021 | Y | NaN |
| 2019 | Z | 4394.00 |
| 2020 | Z | 173.70 |
| 2021 | Z | 518478.00 |
期望输出
仅在每组最后一行显示计算值,其余行保留NaN:
| Year | Company | Data | Data_Change |
|---|---|---|---|
| 2019 | X | 341976.00 | NaN |
| 2020 | X | 1.000 | NaN |
| 2021 | X | 282872.00 | -0.17 |
| 2019 | Y | NaN | NaN |
| 2020 | Y | NaN | NaN |
| 2021 | Y | NaN | NaN |
| 2019 | Z | 4394.00 | NaN |
| 2020 | Z | 173.70 | NaN |
| 2021 | Z | 518478.00 | 11.700 |
我尝试结合groupby与first_valid_index方法但未找到可行方案,请问实现上述需求的最高效方法是什么?
高效实现方法
用Pandas的groupby结合transform批量处理分组计算,配合条件赋值控制结果显示位置,是效率最高的方案之一,无需循环遍历每组。
完整代码
import pandas as pd import numpy as np # 构造原始数据集 data = { 'Year': [2019, 2020, 2021, 2019, 2020, 2021, 2019, 2020, 2021], 'Company': ['X', 'X', 'X', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z'], 'Data': [341976.00, 1.000, 282872.00, np.nan, np.nan, np.nan, 4394.00, 173.70, 518478.00] } df = pd.DataFrame(data) # 1. 计算每组第一个、最后一个非NaN的Data值,广播到全组 df['first_val'] = df.groupby('Company')['Data'].transform( lambda x: x.dropna().iloc[0] if not x.dropna().empty else np.nan ) df['last_val'] = df.groupby('Company')['Data'].transform( lambda x: x.dropna().iloc[-1] if not x.dropna().empty else np.nan ) # 2. 计算相对变化,处理全NaN或除零的异常情况 df['Data_Change'] = np.where( (df['first_val'].notna()) & (df['first_val'] != 0), (df['last_val'] / df['first_val']) - 1, np.nan ) # 3. 仅保留每组最后一行的计算值,其余行设为NaN is_last_row = df.groupby('Company').cumcount(ascending=False) == 0 df['Data_Change'] = np.where(is_last_row, df['Data_Change'].round(3), np.nan) # 清理临时列,得到最终结果 df = df.drop(['first_val', 'last_val'], axis=1) print(df)
关键逻辑说明
transform方法:将分组计算的首尾非NaN值广播到原DataFrame的每一行,避免手动循环分组,效率远高于逐组处理cumcount(ascending=False):生成每组的倒序索引,等于0时即为该组最后一行,精准定位结果显示位置np.where条件赋值:仅在每组最后一行保留计算结果,其余行设为NaN,同时处理全NaN组(如Company Y)和除零的边界情况,防止报错round(3):按照期望输出保留三位小数,可根据需求调整精度
内容的提问来源于stack exchange,提问作者peho15ae
相关产品推荐
相关产品推荐

