You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化百万级用户爱好数据中共享≥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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:25:25