如何为包含card_id与num_week的DataFrame添加多列,标记卡片ID在过去4周内的使用状态
解决卡片周使用历史标记问题
嘿,我来帮你搞定这个需求!你之前用循环没成功,大概率是因为没先处理重复记录,或者没高效地按卡片分组去查询历史周数。其实用Pandas的内置方法就能轻松实现,而且比循环高效很多,我给你两种可行的方案:
方案一:利用集合查询(直观易读)
这个方法先把每个卡片的所有使用周数存成集合,然后直接检查当前周的前n周是否在集合里,逻辑非常清晰:
import pandas as pd # 构造你的示例数据 data = { 'card_id': [123234, 124531, 345124, 451433, 512453, 123234, 124531, 235467, 145246, 134353, 512453, 123234], 'num_week': [1,1,1,1,2,2,2,2,3,3,3,3] } df = pd.DataFrame(data) # 第一步:先去重,得到每个卡片在哪些周有使用记录(避免重复计算) unique_usage = df.drop_duplicates(subset=['card_id', 'num_week']) # 第二步:为每个卡片建立使用周数的集合,方便快速查询 card_week_sets = unique_usage.groupby('card_id')['num_week'].apply(set) # 第三步:定义函数,判断当前卡片在n周前是否被使用 def has_used_prior(row, n): target_week = row['num_week'] - n # 如果目标周数小于1(比如第1周的前1周是0,不存在),直接返回0 if target_week < 1: return 0 # 检查目标周是否在该卡片的使用集合里 return 1 if target_week in card_week_sets.get(row['card_id'], set()) else 0 # 第四步:生成前1到4周的标记列 for n in range(1, 5): # 列名处理:第1周用used_week_prior,其余用used_n_weeks_prior col_name = 'used_week_prior' if n == 1 else f'used_{n}_weeks_prior' df[col_name] = df.apply(lambda x: has_used_prior(x, n), axis=1) # 查看结果 print(df.sort_values(by=['num_week', 'card_id']).reset_index(drop=True))
方案二:利用Merge连接(高效,适合大数据量)
如果你的数据集很大,apply可能会有点慢,这时候用Pandas的merge操作更高效,因为它是向量化的:
import pandas as pd # 同样先构造示例数据 data = { 'card_id': [123234, 124531, 345124, 451433, 512453, 123234, 124531, 235467, 145246, 134353, 512453, 123234], 'num_week': [1,1,1,1,2,2,2,2,3,3,3,3] } df = pd.DataFrame(data) # 第一步:去重得到唯一的卡片-周数组合 unique_usage = df.drop_duplicates(subset=['card_id', 'num_week']) # 第二步:循环生成前1到4周的标记列 for n in range(1, 5): col_name = 'used_week_prior' if n == 1 else f'used_{n}_weeks_prior' # 构造临时表:把每个卡片的使用周数加上n,得到"哪些卡片在X周的n周前有使用" temp = unique_usage.copy() temp['current_week'] = temp['num_week'] + n # 只保留需要的列,避免冗余 temp = temp[['card_id', 'current_week']] # 左连接到原始表,匹配卡片ID和当前周数 df = df.merge(temp, left_on=['card_id', 'num_week'], right_on=['card_id', 'current_week'], how='left') # 能匹配到说明n周前有使用,标记为1,否则0 df[col_name] = df['current_week'].notna().astype(int) # 删除临时列 df = df.drop(columns=['current_week']) # 查看结果 print(df.sort_values(by=['num_week', 'card_id']).reset_index(drop=True))
结果说明
两种方案都会生成你需要的4列,比如对于card_id=123234、num_week=3的记录:
used_week_prior是1(因为它在第2周有使用)used_2_weeks_prior是1(因为它在第1周有使用)used_3_weeks_prior和used_4_weeks_prior都是0(没有更早的记录)
这完全符合你给出的期望结果,而且自动处理了周数小于1的情况(比如第1周的前1周不存在,直接返回0)。
内容的提问来源于stack exchange,提问作者TomasC8
相关产品推荐
相关产品推荐

