如何用Pandas无循环统计用户每日新增BookId数量
无循环实现用户每日新增图书统计
原始数据
| UserId | Date | BookId |
|---|---|---|
| 1 | 2022-07-15 | 10 |
| 1 | 2022-07-16 | 11 |
| 1 | 2022-07-16 | 12 |
| 1 | 2022-07-17 | 12 |
需求说明
统计每个用户在连续日期中每日的新增BookId数量:
- 2022-07-16新增2本(11、12均未在之前出现)
- 2022-07-17新增0本(12已在前一天出现)
期望结果:
| UserId | 2022-07-16 | 2022-07-17 |
|---|---|---|
| 1 | 2 | 0 |
现有循环实现代码
df = studentAnswers.groupby('StudentId') df.apply(findObjDiff) def findObjDiff(df): print(df.StudentId.head(3)) dataDict = {} dates = list(df.Date) dates.sort() for d in dates: ixNext = dates.index(d) + 1 if(ixNext > len(dates)): break dateNext = dates[ixNext] objListPrev = set(df[df.Date == d].ObjectiveId) objListNext = set(df[df.Date == dateNext].ObjectiveId) dataDict[df.StudentId] = {dateNext : {'Different': len(objListPrev - objListNext)}} return dataDict
无循环简洁实现方案
可以利用Pandas的分组、去重、累积集合操作实现无循环统计,以下是两种简洁写法:
写法一:分步清晰实现
import pandas as pd # 构造原始数据(实际使用时替换为你的数据源) df = pd.DataFrame({ 'UserId': [1,1,1,1], 'Date': ['2022-07-15','2022-07-16','2022-07-16','2022-07-17'], 'BookId': [10,11,12,12] }) # 1. 去重:确保每个用户每日的同一BookId只计数一次 df_unique = df.drop_duplicates(subset=['UserId', 'Date', 'BookId']) # 2. 按用户分组,按日期排序,生成每日图书集合 df_grouped = df_unique.groupby('UserId').apply( lambda x: x.sort_values('Date').assign( daily_books=lambda y: y.groupby('Date')['BookId'].apply(set) ) ).reset_index(drop=True) # 3. 计算每个用户每日新增数量 def calc_new(user_df): read_books = set() new_counts = [] for _, row in user_df.iterrows(): # 新增数 = 当日图书 - 已读图书的数量 new_num = len(row['daily_books'] - read_books) new_counts.append(new_num) read_books.update(row['daily_books']) user_df['new_books'] = new_counts # 排除第一天(需求只统计后续日期的新增) return user_df[user_df['Date'] != user_df['Date'].min()][['Date', 'new_books']] result = df_grouped.groupby('UserId').apply(calc_new).reset_index() # 4. 转换为宽表格式,补全0值 final_result = result.pivot(index='UserId', columns='Date', values='new_books')\ .fillna(0).astype(int).reset_index() final_result.columns.name = None print(final_result)
写法二:利用expanding简化累积集合
import pandas as pd df = pd.DataFrame({ 'UserId': [1,1,1,1], 'Date': ['2022-07-15','2022-07-16','2022-07-16','2022-07-17'], 'BookId': [10,11,12,12] }) df['Date'] = pd.to_datetime(df['Date']) df_unique = df.drop_duplicates(['UserId', 'Date', 'BookId']) # 按用户分组,生成每日图书集合和累积图书集合 df_grouped = df_unique.groupby('UserId').apply( lambda x: x.sort_values('Date').assign( daily_set=lambda y: y.groupby('Date')['BookId'].apply(set), cumulative_set=lambda y: y['daily_set'].expanding().apply(lambda s: set.union(*s)) ) ).reset_index(drop=True) # 计算新增数量:当日集合减去前一天的累积集合 df_grouped['new_books'] = df_grouped.apply( lambda row: len(row['daily_set'] - (row['cumulative_set'] - row['daily_set'])), axis=1 ) # 过滤第一天并转换为目标格式 final_result = df_grouped[df_grouped['Date'] != df_grouped.groupby('UserId')['Date'].transform('min')] \ .pivot(index='UserId', columns='Date', values='new_books') \ .fillna(0).astype(int).reset_index() final_result.columns = final_result.columns.astype(str) final_result.columns.name = None print(final_result)
两种写法运行后均输出:
UserId 2022-07-16 2022-07-17 0 1 2 0
内容的提问来源于stack exchange,提问作者renakre
相关产品推荐
相关产品推荐

