基于列值存在性聚合DataFrame的更优实现方案问询
问题描述
我有一个DataFrame,希望根据Result列中特定值的存在情况进行聚合:对于每个index1和index2分组,若分组内存在'A'或'As',则total_result列取值为'A';若不存在'A'但存在'B',则取值为'B',以此类推。目前已有可行代码,想询问是否有更简便的实现方式。
原始代码:
import pandas as pd import numpy as np df = pd.DataFrame({'index1':[1,2,2,2,3,3,4], 'index2':[1,2,2,2,3,3,4], 'Result':['A','','B','As','C','B','D']}) df_out = (df.groupby(['index1', 'index2']) .agg(A=('Result', lambda x: (x == 'A').any() | (x == 'As').any()), B=('Result', lambda x: (x == 'B').any()), C=('Result', lambda x: (x == 'C').any()), D=('Result', lambda x: (x == 'D').any())) .reset_index() .assign(tot_result = lambda x: np.where(x['A'], 'A', np.where(x['B'], 'B', np.where(x['C'], 'C', 'D')))) .drop(columns=['A', 'B', 'C', 'D']) )
给你几个更简洁的实现思路:
思路1:直接在分组apply里做优先级判断
把逻辑整合到一个lambda函数里,省去中间生成A/B/C/D列的步骤,可读性也更强:
import pandas as pd df = pd.DataFrame({'index1':[1,2,2,2,3,3,4], 'index2':[1,2,2,2,3,3,4], 'Result':['A','','B','As','C','B','D']}) df_out = df.groupby(['index1', 'index2'])['Result'].apply( lambda grp: 'A' if ('A' in grp or 'As' in grp) else 'B' if 'B' in grp else 'C' if 'C' in grp else 'D' ).reset_index(name='total_result')
思路2:利用分类类型的排序特性
先把As映射成A,再将结果列转为指定优先级的分类类型,分组取最大值即可(因为分类的顺序由我们定义,最大值就是优先级最高的):
import pandas as pd import numpy as np df = pd.DataFrame({'index1':[1,2,2,2,3,3,4], 'index2':[1,2,2,2,3,3,4], 'Result':['A','','B','As','C','B','D']}) # 先处理映射:As转A,空值置为NaN df['processed_result'] = df['Result'].replace({'As':'A', '':np.nan}) # 定义优先级分类,注意这里是逆序,因为max会取排序靠后的(优先级更高的) df['processed_result'] = pd.Categorical(df['processed_result'], categories=['D','C','B','A'], ordered=True) # 分组取max就是优先级最高的结果 df_out = df.groupby(['index1', 'index2'])['processed_result'].max().reset_index(name='total_result')
思路3:过滤空值后按优先级匹配
先过滤掉空字符串,把As转成A,然后分组后按优先级顺序检查哪个值存在,取第一个匹配的:
import pandas as pd import numpy as np df = pd.DataFrame({'index1':[1,2,2,2,3,3,4], 'index2':[1,2,2,2,3,3,4], 'Result':['A','','B','As','C','B','D']}) priority = ['A','B','C','D'] # 预处理:替换As为A,过滤空值 processed = df[df['Result'] != ''].replace({'Result':'As'}, 'A') def get_top(grp): present = set(grp['Result']) for p in priority: if p in present: return p return 'D' # 兜底 df_out = processed.groupby(['index1', 'index2']).apply(get_top).reset_index(name='total_result')
这几种方法都比原代码更简洁,避免了多层np.where嵌套和中间列的生成,逻辑也更直观。
内容的提问来源于stack exchange,提问作者corianne1234
相关产品推荐
相关产品推荐

