分组统计空值:为何Pandas内置.isna().sum()方法无法生效?
问题:LeetCode「找出所有未进行交易的访客」分组统计报错原因分析
问题背景
我正在解决LeetCode的「找出所有未进行交易的访客」问题,使用的数据如下:
data = [[1, 23], [2, 9], [4, 30], [5, 54], [6, 96], [7, 54], [8, 54]] visits = pd.DataFrame(data, columns=['visit_id', 'customer_id']).astype({'visit_id':'Int64', 'customer_id':'Int64'}) data = [[2, 5, 310], [3, 5, 300], [9, 5, 200], [12, 1, 910], [13, 2, 970]] transactions = pd.DataFrame(data, columns=['transaction_id', 'visit_id', 'amount']).astype({'transaction_id':'Int64', 'visit_id':'Int64', 'amount':'Int64'})
数据展示:
Visits +----------+-------------+ | visit_id | customer_id | +----------+-------------+ | 1 | 23 | | 2 | 9 | | 4 | 30 | | 5 | 54 | | 6 | 96 | | 7 | 54 | | 8 | 54 | +----------+-------------+ Transactions +----------------+----------+--------+ | transaction_id | visit_id | amount | +----------------+----------+--------+ | 2 | 5 | 310 | | 3 | 5 | 300 | | 9 | 5 | 200 | | 12 | 1 | 910 | | 13 | 2 | 970 | +----------------+----------+--------+
问题要求输出每个customer_id对应的无交易访问次数(非零值),预期结果:
+-------------+----------------+ | customer_id | count_no_trans | +-------------+----------------+ | 54 | 2 | | 30 | 1 | | 96 | 1 | +-------------+----------------+
解题步骤
- 移除transactions中visit_id重复的行
- 将visits与处理后的transactions按visit_id左连接
- 按customer_id分组,统计每组中空值行数(无交易的访问次数)
步骤1和2的代码:
df = pd.merge(left=visits, right = transactions.drop_duplicates(subset = ['visit_id']), how = 'left', on = 'visit_id')
代码对比与报错
使用自定义聚合函数的写法可以正常运行:
df1 = (df[df['transaction_id'].isna()] .groupby('customer_id', as_index = False)['transaction_id'] .agg(lambda x: x.isna().sum())).rename(columns = {'transaction_id':'count_no_trans'})
但直接链式调用.isna().sum()会报错:
df2 = (df[df['transaction_id'].isna()] .groupby('customer_id', as_index = False)['transaction_id'] .isna().sum().rename(columns = {'transaction_id':'count_no_trans'}))
报错信息:
AttributeError: 'SeriesGroupBy' object has no attribute 'isna'
报错原因
核心问题在于**groupby之后得到的是SeriesGroupBy对象,不是普通的Series**:
- 普通Pandas Series有
.isna()方法,用来逐个元素检测是否为空; - 但
SeriesGroupBy是分组后的聚合容器,它只提供聚合类方法(比如.sum()、.mean()、.agg()),没有.isna()这种元素级的方法。
可行代码里的.agg(lambda x: x.isna().sum()),是把每个分组后的子Series传给lambda函数,此时x是普通Series,所以能调用.isna();而报错代码直接在SeriesGroupBy上调用.isna(),自然找不到这个属性。
另外补充个小优化:你已经提前筛选了df[df['transaction_id'].isna()],这部分数据里的transaction_id全是空值,统计次数直接用.size()更高效:
df1 = df[df['transaction_id'].isna()].groupby('customer_id', as_index=False).size().rename(columns={'size':'count_no_trans'})
内容的提问来源于stack exchange,提问作者Max Zhou
相关产品推荐
相关产品推荐

