Pandas按两列分组,获取其他列非空唯一值计数及值
分组统计非空唯一值并批量更新分组内容
需求
按vendor和event两列分组,对指定列完成以下操作:
- 统计非空唯一值的数量
- 获取逗号分隔的非空唯一值
后续需实现:当某列分组后的唯一值仅为1个时,更新该分组下所有行的对应列内容。
示例数据
[ { "vendor": "ven1", "event": "event", "code": "sd", "gender": "M", "female": "0" }, { "vendor": "ven1", "event": "event", "code": "sd", "gender": "M", "female": "" }, { "vendor": "ven1", "event": "event", "code": "sd", "gender": "M", "female": "0" }, { "vendor": "ven1", "event": "event2", "code": "sd", "gender": "M", "female": "0" }, { "vendor": "ven1", "event": "event2", "code": "sd", "gender": "F", "female": "0" } ]
尝试过的代码
未得到预期输出的分组聚合
result = df.groupby(['vendor', 'event']).agg({ 'code': ['nunique', 'unique'], 'gender': ['nunique', 'unique'], 'cancel': ['nunique', 'unique'], 'female': ['nunique', 'unique'], 'male': ['nunique', 'unique'] }).reset_index()
成功获取唯一值计数的代码
result = df.groupby(['vendor', 'event']).agg({ 'code': lambda x: x.nunique(), 'gender': lambda x: x.nunique(), 'cancel': lambda x: x.nunique(), 'female': lambda x: x.nunique(), 'male': lambda x: x.nunique() }).reset_index()
解决方案
1. 同时获取非空唯一值计数和逗号分隔的唯一值
原代码的问题在于使用['nunique', 'unique']会生成多层列索引,且未过滤空字符串。以下代码生成扁平化列名,同时处理空值:
import pandas as pd import numpy as np # 替换空字符串为NaN,方便后续空值处理 df['female'] = df['female'].replace('', pd.NA) # 分组聚合,自定义列名区分计数和唯一值 agg_result = df.groupby(['vendor', 'event']).agg( code_count=('code', lambda x: x[x != ''].nunique()), code_unique=('code', lambda x: ','.join(sorted(x[x != ''].unique()))), gender_count=('gender', lambda x: x[x != ''].nunique()), gender_unique=('gender', lambda x: ','.join(sorted(x[x != ''].unique()))), female_count=('female', lambda x: x.nunique(dropna=True)), female_unique=('female', lambda x: ','.join(sorted(x.dropna().unique().astype(str)))) ).reset_index()
2. 批量更新分组内的列内容
当某列分组后唯一值仅1个时,将该分组所有行的对应列替换为这个唯一值,有两种实现方式:
方式一:使用transform逐列处理
cols_to_update = ['code', 'gender', 'female'] for col in cols_to_update: # 获取分组内的非空唯一值列表 group_unique = df.groupby(['vendor', 'event'])[col].transform( lambda x: x[x != ''].unique() if len(x[x != ''].unique()) > 0 else [] ) # 当唯一值数量为1时替换,否则保留原内容 df[col] = np.where( group_unique.apply(len) == 1, group_unique.str[0], df[col] )
方式二:构建映射表合并更新(更高效)
cols_to_update = ['code', 'gender', 'female'] # 构建分组到唯一值的映射,仅保留唯一值数量为1的情况 update_mapping = df.groupby(['vendor', 'event']).agg( **{col: lambda x: x[x != ''].unique()[0] if len(x[x != ''].unique()) == 1 else None for col in cols_to_update} ).reset_index() # 合并映射表到原数据,更新对应列 df = df.merge(update_mapping, on=['vendor', 'event'], suffixes=('', '_new')) for col in cols_to_update: df[col] = df[f'{col}_new'].fillna(df[col]) # 清理临时列 df.drop([f'{col}_new' for col in cols_to_update], axis=1, inplace=True)
内容的提问来源于stack exchange,提问作者Md Parvez Alam
相关产品推荐
相关产品推荐

