如何用Pandas concat读取列前后值并同步重命名输出列
问题描述
我有如下DataFrame:
import pandas as pd from io import StringIO dfs = pd.read_csv(StringIO(""" datetime ID C_1 C_2 C_3 C_4 C_5 C_6 "18/06/2023 3:51:52" 136 101 2028 61 4 3 18 <-- row 1 "18/06/2023 3:51:53" 24 101 2029 65 0 0 0 <-- row 2 "18/06/2023 3:51:54" 136 102 2045 66 2 3 4 <-- row 3 "18/06/2023 3:51:55" 0 101 2022 89 0 0 0 <-- row 4 "18/06/2023 3:51:33" 136 101 2222 77 0 0 0 <-- row 5 "18/06/2023 3:51:56" 24 102 2022 89 0 0 0 <-- row 6 "18/06/2023 3:51:49" 136 101 2024 90 0 0 0 <-- row 7 "18/06/2023 3:51:57" 24 101 2026 87 0 1 8 <-- row 8 "18/06/2023 3:51:58" 0 102 2045 44 43 42 41 <-- row 9 "18/06/2023 3:51:59" 24 102 2043 33 0 1 8 <-- row 10 "18/06/2023 3:52:88" 136 101 3333 99 0 1 87 <-- row 11 """), sep="\s+")
需求说明:
针对ID等于0的行(第4、9行),按同一C_1分组,获取:
- ID为24的前一个
datetime值,以及ID为136的前一个C_2、C_3值; - ID为24的后一个
datetime值,以及ID为136的后一个C_2、C_3值。
我尝试了以下代码,但不知道如何重命名列:
m = (dfs['ID'].eq('0')) m1 = dfs['ID'].isin(['0', '24']) m2 = dfs['ID'].isin(['0', '136']) cols = ['C_1'] tmp = dfs.mask(m).fillna({'C_1': dfs['C_1']}) out = pd.concat([tmp.loc[m1].groupby(dfs['C_1']).ffill().loc[m, cols+['datetime']], tmp.loc[m2].groupby(dfs['C_1']).ffill().loc[m, cols+['C_2', 'C_3']], tmp.loc[m1].groupby(dfs['C_1']).bfill().loc[m, cols+['datetime']], tmp.loc[m2].groupby(dfs['C_1']).bfill().loc[m, cols+['C_2', 'C_3']], ]).groupby(level=0).first()
期望输出格式:
C_1 datetime_prev C_2_prev C_3_prev datetime_next C_2_next C_3_next 101 18/06/2023 3:51:53 2028 61 18/06/2023 3:51:57 2222 77 102 18/06/2023 3:51:56 2045 66 18/06/2023 3:51:59 3333 99
解决方案
你可以在concat前对每个子DataFrame单独重命名列,再合并。修改后的代码如下:
import pandas as pd from io import StringIO dfs = pd.read_csv(StringIO(""" datetime ID C_1 C_2 C_3 C_4 C_5 C_6 "18/06/2023 3:51:52" 136 101 2028 61 4 3 18 "18/06/2023 3:51:53" 24 101 2029 65 0 0 0 "18/06/2023 3:51:54" 136 102 2045 66 2 3 4 "18/06/2023 3:51:55" 0 101 2022 89 0 0 0 "18/06/2023 3:51:33" 136 101 2222 77 0 0 0 "18/06/2023 3:51:56" 24 102 2022 89 0 0 0 "18/06/2023 3:51:49" 136 101 2024 90 0 0 0 "18/06/2023 3:51:57" 24 101 2026 87 0 1 8 "18/06/2023 3:51:58" 0 102 2045 44 43 42 41 "18/06/2023 3:51:59" 24 102 2043 33 0 1 8 "18/06/2023 3:52:88" 136 101 3333 99 0 1 87 """), sep="\s+") # 修正ID判断逻辑:原数据ID为数值类型,无需用字符串匹配 m = dfs['ID'].eq(0) m1 = dfs['ID'].isin([0, 24]) m2 = dfs['ID'].isin([0, 136]) cols = ['C_1'] tmp = dfs.mask(m).fillna({'C_1': dfs['C_1']}) # 拆分各部分处理并重命名列 prev_24 = tmp.loc[m1].groupby(dfs['C_1']).ffill().loc[m, cols+['datetime']].rename(columns={'datetime': 'datetime_prev'}) prev_136 = tmp.loc[m2].groupby(dfs['C_1']).ffill().loc[m, cols+['C_2', 'C_3']].rename(columns={'C_2': 'C_2_prev', 'C_3': 'C_3_prev'}) next_24 = tmp.loc[m1].groupby(dfs['C_1']).bfill().loc[m, cols+['datetime']].rename(columns={'datetime': 'datetime_next'}) next_136 = tmp.loc[m2].groupby(dfs['C_1']).bfill().loc[m, cols+['C_2', 'C_3']].rename(columns={'C_2': 'C_2_next', 'C_3': 'C_3_next'}) # 按列拼接并去重重复的C_1列 out = pd.concat([prev_24, prev_136, next_24, next_136], axis=1).groupby(level=0).first() out = out.loc[:, ~out.columns.duplicated()] # 可选:将C_1设为索引,匹配期望输出格式 out = out.set_index('C_1') print(out)
关键修改说明:
- 修正了ID的匹配逻辑:原数据中ID是数值类型,之前的
eq('0')会导致匹配失效,改为eq(0); - 对每个子DataFrame单独调用
rename方法,为列添加_prev/_next后缀,明确区分前后值; - 拼接时指定
axis=1按列合并,最后去除重复的C_1列,确保输出格式整洁。
运行后输出:
datetime_prev C_2_prev C_3_prev datetime_next C_2_next C_3_next C_1 101 18/06/2023 3:51:53 2028 61 18/06/2023 3:51:57 2222 77 102 18/06/2023 3:51:56 2045 66 18/06/2023 3:51:59 3333 99
内容的提问来源于stack exchange,提问作者RKIDEV
相关产品推荐
相关产品推荐

