如何使用 Python 基于交易数据高效地创建用户图?
Posted
技术标签:
【中文标题】如何使用 Python 基于交易数据高效地创建用户图?【英文标题】:How can I efficiently create a user graph based on transaction data using Python? 【发布时间】:2014-04-10 05:40:38 【问题描述】:我正在尝试使用 networkx 包在 Python 中创建用户图。我的原始数据是个人支付交易,其中支付数据包括用户、支付工具、IP 地址等。我的节点是用户,如果任何两个用户共享一个 IP 地址,我正在创建边缘。
根据该交易数据,我创建了一个包含唯一 [用户、IP] 对的 Pandas 数据框。要创建边,我需要找到两个用户共享 IP 的 [user_a, user_b] 对。让我们将此 DataFrame 称为“df”,其中包含“user”和“ip”列。
我一直遇到内存问题,并尝试了之前列出的几种不同的解决方案。作为参考,原始交易列表约为 500,000,包括约 130,000 个用户、约 30,000 个 IP 和可能的约 30,000,000 个链接。
将 df 加入自身,对对进行排序并删除重复项(这样 [X, Y] 和 [Y, X] 不会都显示为唯一对)。
df_pairs = df.join(df, how='inner', lsuffix='l', rsuffix='r')
df_sorted_pairs = [np.sort([df_pairs['userl'][i], df_pairs['userr'][i]]) for i in range(len(df_pairs))]
edges = np.asarray(pd.DataFrame(df_sorted_pairs).drop_duplicates())
这很好用,但很快就会给我一个内存错误, 因为将表连接到自身增长得非常快。
创建一个矩阵,其中用户是行,IP 是列, 如果该用户在 IP 上进行交易,则矩阵元素为 1,而矩阵元素为 0 除此以外。那么 X.dot(X.transpose()) 是一个方阵,其 元素 (i,j) 表示用户 i 和用户共享了多少 IP j.
user_list = df['user'].unique()
ip_list = df['ip'].unique()
df_x = pd.DataFrame(index=user_list, columns=ip_list)
df_x.fillna(0, inplace=True)
for row in range(len(df)):
df_x[df['ip'][row]][df['user'][row]] = 1
df_links = df_x.dot(df_x.transpose())
这非常有效,除非 len(ip_list) > 5000。只是创建 比如说,500,000 行 x 200,000 列的空数据框给出了 内存错误。
蛮力。逐个遍历用户。对于每个 用户,找到不同的 IP。对于每个 IP,找到不同的用户。 因此,这些结果用户与 当前迭代。将该 [User1, User2] 列表添加到 链接。
user_list = df['user'].unique()
ip_list = df['ip'].unique()
links=[]
for user in user_list:
related_ip_list = df[df['user'] == user]['ip'].unique()
for ip in related_ip_list:
related_user_list = df[df['ip'] == ip]['user'].unique()
for related_user in related_user_list:
if related_user != user:
links.append([user, related_user])
这可行,但速度极慢。跑了3个小时,终于给了 我是内存错误。因为链接一直在保存,我 可以检查它有多大 - 大约 23,000,000 个链接。
任何建议将不胜感激。我是否只是在“大数据”方面走得太远了,像上面这样的传统方法不会削减它?我不认为将 500,000 笔交易视为“大数据”,但我想存储 130,000 x 30,000 矩阵或创建包含 30,000,000 个元素的列表非常大?
【问题讨论】:
【参考方案1】:我认为你的问题是矩阵表示不会削减它:
请注意,在记忆方面,您做的事情效率非常低。例如,您创建了一个矩阵,其中包含许多需要在 RAM 中分配的零。对于不存在的连接而不是零浮点数,在 RAM 中没有任何对象会更有效。您“滥用”线性代数数学来解决您的问题,这使您使用大量 RAM。 (你的矩阵中的元素数量是 130k*30k = a gazilion,但你“只有”有 30m 的链接是你真正关心的)
我真的很同情你,因为 pandas 是我学习的第一个库,我试图解决几乎所有关于 pandas 的问题。随着时间的推移,我注意到矩阵方法对于很多问题并不是最优的。
在 numpy 的某个地方有一个“备用矩阵”,但我们不要去那里。
让我建议另一种方法:
使用简单的默认字典:
from collections import defaultdict
# a dict that makes an empty set if you add a key that doesnt exist
shared_ips = defaultdict(set)
# for each ip, you generate a set of users
for k, row in unique_user_ip_pairs.iterrows():
shared_ips[row['ip']].add(row['user'])
#filter the the dict for ips that have more than 1 user
shared_ips = k, v for k, v in shared_ips.items() if len(v) > 1
我不确定这是否能 100% 解决您的用户案例,但请注意效率:
这最多会从您的初始唯一用户 IP 对对象复制 RAM 使用情况。 但是你会得到哪些用户共享了哪些 ip 的信息。
最大的教训是:
如果矩阵中的大多数单元格表示相同类型的信息,如果遇到内存问题,请不要使用矩阵方法
我已经看到了很多 pandas 解决方案,这些问题可以通过简单使用 python 内置类型(如 dict、set、frozenset 和 计数器。尤其是从 MATLAB 和 R 或 Excel 等统计工具箱开始使用 Python 的人非常容易使用它(他们肯定喜欢它们的表格)。我建议不要让 pandas 成为他首先求助的个人内置库...
【讨论】:
谢谢。基于 500K txns、130K 用户、30K IP,生成该字典需要 20 秒。我仍然遇到内存问题来实际创建图形,使用`for ip in shared_ips:related_msisdn_list = shared_ips[ip] related_msisdn_edges = [(x, y) for x in related_msisdn_list for y in related_msisdn_list] G.add_edges_from(related_msisdn_edges) ` ,但如果 networkx 在保存 40,000,000 个节点时遇到问题,但它们存储数据时,我可能无法解决这个问题 没问题。但是,你应该接受我的回答是正确的:)以上是关于如何使用 Python 基于交易数据高效地创建用户图?的主要内容,如果未能解决你的问题,请参考以下文章