如何在Pandas中按日期范围统计每月各标签用户数量?
实现月度标签用户统计方案
步骤概述
要实现用户标签在日期区间内的月度统计,核心是将每个用户的标签生效区间展开为对应的月份,再按月份和标签分组计数,最后整理成目标格式。
具体代码实现
首先导入依赖库并创建示例数据:
import pandas as pd # 构建示例DataFrame data = { 'user': [1, 2, 3, 1], 'label': ['x', 'x', 'y', 'b'], 'start_date': ['2018-01-01', '2019-05-10', '2019-04-01', '2018-10-01'], 'end_date': ['2018-10-01', '2020-01-01', '2022-04-20', '2020-05-08'] } df = pd.DataFrame(data)
1. 转换日期格式
先将日期列转为datetime类型,方便后续时间操作:
df['start_date'] = pd.to_datetime(df['start_date']) df['end_date'] = pd.to_datetime(df['end_date'])
2. 展开标签生效区间为月度记录
对每一行生成标签生效期内的所有月份,确保每个用户在区间内的每个月都有对应的记录:
def get_month_range(row): # 取生效起始月和结束月的第一天 start_month = row['start_date'].replace(day=1) end_month = row['end_date'].replace(day=1) # 生成月度序列并转为YYYY-MM格式 months = pd.date_range(start=start_month, end=end_month, freq='MS').strftime('%Y-%m') return pd.Series(months) # 展开数据,合并原表的user和label字段 expanded_data = df.apply(get_month_range, axis=1).stack()\ .reset_index(level=1, drop=True)\ .to_frame('date')\ .join(df[['user', 'label']])
3. 去重与分组计数
去重避免同一用户同一标签在同月重复计数,再按月份和标签统计用户数:
# 去重:同一用户-标签-月份仅保留一条记录 expanded_data = expanded_data.drop_duplicates(subset=['user', 'label', 'date']) # 分组统计每个月各标签的用户数 monthly_counts = expanded_data.groupby(['date', 'label'])['user'].nunique()\ .unstack(fill_value=0)
4. 调整列名与补全月份
将列名改为count_label_xxx格式,并补全所有涉及的月份(避免遗漏无数据的月份):
# 重命名列 monthly_counts.columns = [f'count_label_{col}' for col in monthly_counts.columns] # 生成完整的月度序列 min_month = df['start_date'].min().replace(day=1).strftime('%Y-%m') max_month = df['end_date'].max().replace(day=1).strftime('%Y-%m') all_months = pd.date_range(start=min_month, end=max_month, freq='MS').strftime('%Y-%m') # 补全缺失月份,缺失标签计数填0 monthly_counts = monthly_counts.reindex(all_months).fillna(0).astype(int)
5. 补充无标签用户统计(可选)
如果需要统计当月没有任何标签的用户数(对应示例中的count_label_列),可添加以下步骤:
total_users = df['user'].nunique() # 统计当月有至少一个标签的用户数 monthly_active = expanded_data.groupby('date')['user'].nunique() # 计算无标签用户数 monthly_counts['count_label_'] = total_users - monthly_active monthly_counts['count_label_'] = monthly_counts['count_label_'].fillna(total_users).astype(int)
6. 整理最终格式
将索引转为普通列,得到目标输出:
final_result = monthly_counts.reset_index().rename(columns={'index': 'date'}) print(final_result)
输出示例
运行上述代码后,最终结果结构如下(以示例数据为例):
| date | count_label_x | count_label_y | count_label_b | count_label_ |
|---|---|---|---|---|
| 2018-01 | 1 | 0 | 0 | 2 |
| 2018-02 | 1 | 0 | 0 | 2 |
| ... | ... | ... | ... | ... |
| 2022-04 | 0 | 1 | 0 | 2 |
内容的提问来源于stack exchange,提问作者shinjitos
相关产品推荐
相关产品推荐

