优化Reddit子社区共同用户数计算:多CSV重复读取提速方案
优化Reddit社区共同用户统计效率的方案
核心优化思路
因为要处理数十亿次两两对比,预处理+内存缓存是提升速度的关键,你的排序思路完全可行,以下是具体落地步骤:
1. 预处理阶段:一次性完成所有清洗与结构化
- 批量处理所有49000个sub的CSV:
- 移除机器人账号(从removal_users.csv中过滤)
- 筛选karma>30的用户
- 将每个sub的有效用户列表按用户名排序后保存为二进制格式(比如
pickle或feather),或者直接缓存到内存中的有序列表 - 不要按karma排序,按用户名排序才是对求交集有帮助的排序方式——有序列表可以用双指针法比集合交集更快,且内存占用更可控
2. 内存缓存策略
既然内存无限制,直接把所有预处理后的用户列表(有序)加载到内存字典中:sub_name -> sorted_user_list,这样每次对比无需重复读CSV,直接从内存取数据,彻底消除IO瓶颈。
3. 高效求交集的两种方案
方案A:双指针法(基于有序列表)
排序后的用户列表可用双指针遍历,时间复杂度O(n+m),比集合交集的哈希开销更低,适合大规模数据:
def count_common_sorted(list1, list2): count = 0 i = j = 0 len1, len2 = len(list1), len(list2) while i < len1 and j < len2: if list1[i] == list2[j]: count += 1 i += 1 j += 1 elif list1[i] < list2[j]: i += 1 else: j += 1 return count
方案B:预存为哈希集合(内存充足时)
如果内存完全无压力,直接把每个sub的用户列表转为frozenset(不可变,节省内存),交集计算用len(set1 & set2),代码更简洁,哈希查找的O(1)特性也能保证速度。
4. 原代码的即时优化点
- 原代码中同时用
dt.fread和pd.read_csv读取同一个文件,完全冗余,删掉其中一个(推荐用datatable.fread,读取速度远快于pandas) drops = remove_list['C0'].to_list()[0]存在逻辑错误,应该取整个列的列表:drops = remove_list['C0'].to_list()- 预处理时一次性完成过滤,避免每次对比都重复执行过滤逻辑
预处理代码示例
import pandas as pd import pickle from pathlib import Path # 加载机器人列表 remove_list = pd.read_csv('D:/Spring 2023/red/c7/removal_users.csv', header=None) drops = remove_list[0].tolist() # 预处理所有sub文件 sub_data = {} sub_dir = Path('D:/Spring 2023/red/sub_csvs') # 替换为你的sub CSV目录 for csv_path in sub_dir.glob('*.csv'): # 读取文件(用datatable更快,这里用pandas示例) sub_df = pd.read_csv(csv_path, header=None, names=['user', 'score', 'comment_count']) # 过滤机器人和低karma用户 filtered = sub_df[~sub_df['user'].isin(drops) & (sub_df['score'] > 30)] # 按用户名排序 sorted_users = filtered['user'].sort_values().tolist() # 获取sub名称 sub_name = csv_path.stem.split('.')[0] # 存入内存字典,同时可选保存为pickle备用 sub_data[sub_name] = sorted_users with open(f'./preprocessed/{sub_name}.pkl', 'wb') as f: pickle.dump(sorted_users, f) # 批量对比示例 def batch_compare(sub_data): sub_names = list(sub_data.keys()) results = {} for i in range(len(sub_names)): for j in range(i+1, len(sub_names)): sub1 = sub_names[i] sub2 = sub_names[j] common_count = count_common_sorted(sub_data[sub1], sub_data[sub2]) results[(sub1, sub2)] = common_count return results
关键结论
- 优先做预处理+内存缓存,这是消除重复IO和过滤操作的核心,能带来数量级的速度提升
- 用户名排序后用双指针法求交集,是大规模数据下比集合交集更高效的选择
- 不要按karma排序,对求交集无帮助,按用户名排序才是正确方向
内容的提问来源于stack exchange,提问作者user27606
相关产品推荐
相关产品推荐

