统计指定时间范围内活动出现次数:多索引DataFrame函数开发需求
我来帮你实现这个函数,结合Pandas的特性,我们可以分步骤完成需求,同时考虑不同数据规模下的效率问题给出两种方案:
实现思路
核心需求是:针对每个活动a的实例,统计同一用户(name)在该实例时间的±nhr小时范围内,出现的其他活动(排除当前a实例本身)的次数。
基础实现方案(适合中小数据集)
这个方案逻辑直观,用apply遍历每个目标活动实例,针对性筛选统计:
import pandas as pd from datetime import timedelta def find_close_activities(df, a, nhr): # 把多索引结构转为普通列,方便后续操作 df_flat = df.reset_index() # 筛选出所有活动为a的记录,作为我们的统计基准 target_records = df_flat[df_flat['activity'] == a].copy() # 定义辅助函数:计算单条记录对应的周边活动次数 def count_nearby(row): # 先锁定同一用户的所有记录 same_user = df_flat[df_flat['name'] == row['name']] # 计算时间范围:当前时间 ± nhr小时 time_start = row['time'] - timedelta(hours=nhr) time_end = row['time'] + timedelta(hours=nhr) # 筛选符合时间范围且不是当前活动a的记录(排除自身) valid_activities = same_user[(same_user['time'] >= time_start) & (same_user['time'] <= time_end) & (same_user['activity'] != a)] return len(valid_activities) # 对每个目标记录计算次数 target_records['close_activities_count'] = target_records.apply(count_nearby, axis=1) # 恢复原有的多索引格式并返回 return target_records.set_index(['name', 'time']).drop(columns=['activity'])
测试示例
用你提供的示例数据调用:
# 构造示例DataFrame data = [ ('Bill', '2013-10-09 05:27:00', 'run'), ('Bill', '2013-10-09 07:23:00', 'play'), ('Bill', '2013-10-09 07:25:00', 'hw'), ('Bill', '2013-10-09 08:25:00', 'swim'), ('Rick', '2014-11-07 06:27:00', 'eat'), ('Rick', '2014-11-07 07:25:00', 'swim'), ('Rick', '2014-11-07 08:25:00', 'hw'), ('Rick', '2014-11-07 10:30:00', 'play') ] df = pd.DataFrame(data, columns=['name', 'time', 'activity']) df['time'] = pd.to_datetime(df['time']) df = df.set_index(['name', 'time']) # 调用函数,统计swim实例2小时内的其他活动次数 result = find_close_activities(df, 'swim', 2) print(result)
输出结果:
close_activities_count name time Bill 2013-10-09 08:25:00 2 Rick 2014-11-07 07:25:00 2
高效优化方案(适合大数据集)
如果你的数据量很大,apply的遍历效率会很低,我们可以用merge_asof实现高效的时间范围连接,避免逐行遍历:
import pandas as pd from datetime import timedelta def find_close_activities(df, a, nhr): df_flat = df.reset_index() target_records = df_flat[df_flat['activity'] == a].copy() # 按name和time排序,merge_asof要求输入已排序 df_sorted = df_flat.sort_values(['name', 'time']) target_sorted = target_records.sort_values(['name', 'time']) # 为每个目标记录计算时间上下限 target_sorted['time_low'] = target_sorted['time'] - timedelta(hours=nhr) target_sorted['time_high'] = target_sorted['time'] + timedelta(hours=nhr) # 用merge_asof做时间范围连接,匹配同用户、时间在范围内的记录 merged = pd.merge_asof( target_sorted, df_sorted, on='time', by='name', left_bound='time_low', right_bound='time_high', direction='nearest' ) # 排除自身记录(当匹配到的活动是a且时间完全相同时) merged = merged[(merged['activity_y'] != a) | (merged['time_x'] != merged['time_y'])] # 按原目标记录分组统计数量 count_result = merged.groupby(['name', 'time_x']).size().reset_index(name='close_activities_count') # 恢复多索引格式 return count_result.set_index(['name', 'time_x']).rename_axis(index={'time_x': 'time'})
这个方案利用Pandas的向量化操作,效率比apply高一个数量级,适合处理十万级以上的数据集。
内容的提问来源于stack exchange,提问作者LogCapy
相关产品推荐
相关产品推荐

