如何优化百万级用户爱好数据中共享≥2个爱好的用户对查询
百万级TSV用户爱好数据:找共享≥2个爱好的用户对优化方案
问题背景
我有一个近百万行的TSV文件,每行对应唯一用户及其爱好ID列表,示例如下:
userID hobbyIDs uu0000001 ['hh0079187', 'hh0133268', 'hh0140845', 'hh0163721', 'hh9121232'] uu0000002 ['hh0011390', 'hh0494421'] uu0000003 ['hh0494421', 'hh0011390', 'hh0912384', 'hh0163721'] ...
需求是找出共享至少2个爱好的用户对,示例结果:
userPairIDs pairHobbyIDs (uu0000002,uu0000003) ['hh0011390', 'hh0494421'] ...
现有方案的问题
当前用Python双重循环实现速度极慢(时间复杂度O(n²),百万级数据完全不可行),尝试Pandas时又遇到内存溢出问题,现有代码如下:
csv.field_size_limit(1000000000) fp = '../data/user_hobbies.tsv' user_hobby= defaultdict(set) with open(fp, newline='', encoding='utf-8') as file: reader = csv.reader(file, delimiter='\t') for row in reader: user_id= row[0] hobbies = eval(row[1]) # Convert string representation of list to actual list user_hobby[user_id].update(hobbies) # Find pairs of actors who share at least 2 hobbies pairs = [] for user1 in user_hobby: for user2 in user_hobby: if user1 < user2: # prevent duplicates shared_hobbies = user_hobby[user1].intersection(user_hobby[user2]) if len(shared_hobbies) >= 2: pairs.append((user1, user2, shared_hobbies))
优化方案
1. 核心思路:倒排索引替代双重循环
放弃用户间的两两比对,转而构建「爱好ID → 拥有该爱好的用户列表」的倒排索引,通过统计用户对在爱好列表中的共现次数来判断是否符合条件——共现次数≥2即代表共享至少2个爱好,大幅降低时间复杂度。
2. 分步实现代码
步骤1:构建倒排索引+替换不安全的eval
eval存在安全风险且效率低,改用字符串切片+分割解析爱好列表:
import csv from collections import defaultdict, Counter csv.field_size_limit(1000000000) fp = '../data/user_hobbies.tsv' # 倒排索引:爱好ID -> 对应用户列表 hobby_to_users = defaultdict(list) # 保留用户到爱好的映射,后续用于获取共享爱好详情 user_to_hobbies = {} with open(fp, newline='', encoding='utf-8') as file: reader = csv.reader(file, delimiter='\t') next(reader) # 跳过表头 for row in reader: user_id = row[0] # 解析爱好字符串:去掉[]、分割元素、清理引号 hobby_str = row[1].strip('[]') hobbies = [h.strip("'") for h in hobby_str.split(', ')] if hobby_str else [] user_to_hobbies[user_id] = set(hobbies) for hobby in hobbies: hobby_to_users[hobby].append(user_id)
步骤2:统计用户对的共现次数
遍历每个爱好对应的用户列表,生成所有合法用户对(避免重复),用Counter统计共现次数:
pair_counter = Counter() for hobby, users in hobby_to_users.items(): user_count = len(users) # 生成当前爱好下的所有用户对(确保user1 < user2,避免重复统计) for i in range(user_count): for j in range(i + 1, user_count): u1, u2 = users[i], users[j] if u1 > u2: u1, u2 = u2, u1 pair_counter[(u1, u2)] += 1
步骤3:筛选结果并获取共享爱好
过滤出共现次数≥2的用户对,通过用户-爱好映射取交集得到共享爱好:
result = [] for (user1, user2), share_count in pair_counter.items(): if share_count >= 2: shared_hobbies = list(user_to_hobbies[user1] & user_to_hobbies[user2]) result.append({ 'userPairIDs': f"({user1}, {user2})", 'pairHobbyIDs': shared_hobbies })
步骤4:输出结果到TSV
with open('../data/user_pairs.tsv', 'w', newline='', encoding='utf-8') as outfile: writer = csv.writer(outfile, delimiter='\t') writer.writerow(['userPairIDs', 'pairHobbyIDs']) for item in result: hobby_str = str(item['pairHobbyIDs']) writer.writerow([item['userPairIDs'], hobby_str])
3. 进阶优化方向
- 内存压缩:将字符串格式的用户ID、爱好ID映射为整数ID,减少内存占用;若数据量仍过大,可分批次处理爱好列表。
- 并行加速:用
multiprocessing.Pool对爱好列表的遍历做并行处理,利用多核CPU提升速度。 - 数据库辅助:若数据量超大规模,可将用户-爱好的一对一记录导入SQLite/PostgreSQL,用自连接查询筛选符合条件的用户对。
内容的提问来源于stack exchange,提问作者Alfyn
相关产品推荐
相关产品推荐

