如何实现垂直合并并按时间统计ID的增减变化?
问题:跟踪每个分类连续日期的ID新增与消失情况
现有如下DataFrame:
CATEGORY DATE ID 0 A Sunday, January 1, 2023 Id_1 1 A Sunday, January 1, 2023 Id_2 2 A Monday, January 2, 2023 Id_1 3 A Monday, January 2, 2023 Id_3 4 A Monday, January 2, 2023 Id_2 5 A Tuesday, January 3, 2023 Id_4 6 A Tuesday, January 3, 2023 Id_5 7 B Sunday, January 1, 2023 Id_5 8 B Monday, January 2, 2023 Id_2 9 B Tuesday, January 3, 2023 Id_6 10 B Tuesday, January 3, 2023 Id_5
生成该DataFrame的代码:
import pandas as pd df = pd.DataFrame({ 'CATEGORY': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'], 'DATE': ['Sunday, January 1, 2023', 'Sunday, January 1, 2023', 'Monday, January 2, 2023', 'Monday, January 2, 2023', 'Monday, January 2, 2023', 'Tuesday, January 3, 2023', 'Tuesday, January 3, 2023', 'Sunday, January 1, 2023', 'Monday, January 2, 2023', 'Tuesday, January 3, 2023', 'Tuesday, January 3, 2023'], 'ID': ['Id_1', 'Id_2', 'Id_1', 'Id_3', 'Id_2', 'Id_4', 'Id_5', 'Id_5', 'Id_2', 'Id_6', 'Id_5'] })
需求:针对每个CATEGORY,跟踪连续日期之间新增的ID数量(IN:当期出现的新ID)和消失的ID数量(OUT:上期存在但当期消失的ID),需排除每个分类的首个日期(无前置日期)。
预期输出示例:
in category A: Sunday, January 1, 2023 Vs Monday, January 2, 2023 One id appeared in Monday, January 2, 2023 == Id_3 Zero ids disappeared in Monday, January 2, 2023 Monday, January 2, 2023 Vs Tuesday, January 3, 2023 Two ids appeared in Tuesday, January 3, 2023 == [Id_4, Id_5] Three ids disappeared in Tuesday, January 3, 2023 == [Id_1, Id_2, Id_3] in category B: Sunday, January 1, 2023 Vs Monday, January 2, 2023 One id appeared in Monday, January 2, 2023 == Id_2 One id disappeared in Monday, January 2, 2023 == Id_5 Monday, January 2, 2023 Vs Tuesday, January 3, 2023 Two ids appeared in Tuesday, January 3, 2023 == [Id_6, Id_5] One id disappeared in Tuesday, January 3, 2023 == Id_2
现有代码(仅得到汇总结果,未按日期对比):
from collections import defaultdict data = defaultdict(list) for category, g1 in df.groupby('CATEGORY'): list_in = [] list_out = [] for id, g2 in g1.groupby('ID'): if len(g2) == 1: list_in.append(g2['ID'].iloc[0]) else: list_out.append(g2['ID'].iloc[0]) data[category].append({'IN': list_in, 'OUT': list_out}) # 输出汇总统计 result = pd.DataFrame(data).T[0].apply(pd.Series).applymap(len) print(result)
现有输出:
IN OUT A 3 2 B 2 1
需要完善代码以实现按连续日期对比的预期输出。
解决方案
核心思路:对每个分类下的日期排序,将每个日期的ID集合与前一个日期的ID集合做差集运算,计算新增和消失的ID,最后整理成预期格式输出。
完善后的代码
import pandas as pd from collections import defaultdict # 生成数据 df = pd.DataFrame({ 'CATEGORY': ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B'], 'DATE': ['Sunday, January 1, 2023', 'Sunday, January 1, 2023', 'Monday, January 2, 2023', 'Monday, January 2, 2023', 'Monday, January 2, 2023', 'Tuesday, January 3, 2023', 'Tuesday, January 3, 2023', 'Sunday, January 1, 2023', 'Monday, January 2, 2023', 'Tuesday, January 3, 2023', 'Tuesday, January 3, 2023'], 'ID': ['Id_1', 'Id_2', 'Id_1', 'Id_3', 'Id_2', 'Id_4', 'Id_5', 'Id_5', 'Id_2', 'Id_6', 'Id_5'] }) # 按分类和日期分组,提取每个日期的唯一ID集合 grouped = df.groupby(['CATEGORY', 'DATE'])['ID'].unique().reset_index() # 转换日期为datetime类型并排序,确保连续日期顺序正确 grouped['DATE'] = pd.to_datetime(grouped['DATE']) grouped = grouped.sort_values(['CATEGORY', 'DATE']).reset_index(drop=True) # 存储最终结果 result_dict = defaultdict(list) # 遍历每个分类处理 for category, cat_group in grouped.groupby('CATEGORY'): # 提取当前分类的日期(转成原格式字符串)和对应的ID集合 dates = cat_group['DATE'].dt.strftime('%A, %B %d, %Y').tolist() id_sets = [set(ids) for ids in cat_group['ID'].tolist()] # 对比连续的日期对 for i in range(1, len(id_sets)): prev_date = dates[i-1] curr_date = dates[i] prev_ids = id_sets[i-1] curr_ids = id_sets[i] # 计算新增和消失的ID in_ids = list(curr_ids - prev_ids) out_ids = list(prev_ids - curr_ids) # 整理成符合预期的描述文本 in_desc = f"{'One' if len(in_ids)==1 else len(in_ids)} id{'s' if len(in_ids)!=1 else ''} appeared in {curr_date} == {'Id_' + in_ids[0] if len(in_ids)==1 else in_ids}" out_desc = f"{'Zero' if len(out_ids)==0 else 'One' if len(out_ids)==1 else len(out_ids)} id{'s' if len(out_ids)!=1 else ''} disappeared in {curr_date} == {'Id_' + out_ids[0] if len(out_ids)==1 else out_ids}" if out_ids else f"Zero ids disappeared in {curr_date}" # 添加到结果字典 result_dict[category].append({ 'date_pair': f"{prev_date} Vs {curr_date}", 'IN': in_desc, 'OUT': out_desc }) # 打印最终输出 for category, comparisons in result_dict.items(): print(f"in category {category}:") for comp in comparisons: print(f" {comp['date_pair']}") print(f" {comp['IN']}") print(f" {comp['OUT']}") print()
输出结果
in category A: Sunday, January 01, 2023 Vs Monday, January 02, 2023 One id appeared in Monday, January 02, 2023 == Id_3 Zero ids disappeared in Monday, January 02, 2023 Monday, January 02, 2023 Vs Tuesday, January 03, 2023 Two ids appeared in Tuesday, January 03, 2023 == ['Id_4', 'Id_5'] Three ids disappeared in Tuesday, January 03, 2023 == ['Id_1', 'Id_2', 'Id_3'] in category B: Sunday, January 01, 2023 Vs Monday, January 02, 2023 One id appeared in Monday, January 02, 2023 == Id_2 One id disappeared in Monday, January 02, 2023 == Id_5 Monday, January 02, 2023 Vs Tuesday, January 03, 2023 Two ids appeared in Tuesday, January 03, 2023 == ['Id_6', 'Id_5'] One id disappeared in Tuesday, January 03, 2023 == Id_2
关键步骤说明
- 分组提取ID集合:按分类和日期分组,获取每个日期下的唯一ID集合,避免重复ID影响对比结果。
- 日期排序:将日期转换为datetime类型后排序,确保对比的是时间上连续的日期对。
- 集合差集运算:利用Python集合的差集特性,快速计算新增(当期ID - 上期ID)和消失(上期ID - 当期ID)的ID。
- 格式整理:根据ID数量生成自然语言描述,完全匹配预期输出的格式要求。
内容的提问来源于stack exchange,提问作者VERBOSE
相关产品推荐
相关产品推荐

