如何使用Python有效地基于事务数据创建用户图?

时间:2014-04-10 05:40:38

标签: python pandas bigdata graph-theory networkx

我尝试使用networkx包在Python中创建用户图。我的原始数据是个人支付交易,其中支付数据包括用户,支付工具,IP地址等。我的节点是用户,如果任何两个用户共享IP地址,我就会创建边缘。

从该交易数据中,我创建了一个独特的[用户,IP]对的Pandas数据帧。要创建边缘,我需要找到两个用户共享IP的[user_a,user_b]对。我们称之为DataFrame' df'用列'用户'和' ip'。

我一直遇到内存问题,并尝试了之前概述的一些不同的解决方案。作为参考,原始交易清单约为500,000,包括~130,000个用户,~30,000个IP,以及可能约30,000,000个链接。

  1. 将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())
    

    这很好用,但很快给了我一个内存错误, 加入桌子本身的速度非常快。

  2. 创建一个矩阵,其中用户是行,IP是列, 如果该用户在IP和0上进行交易,则矩阵元素为1 除此以外。然后X.dot(X.transpose())是一个方阵 elements(i,j)表示用户i和用户共享的IP数量 学家

    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行×200,000列给出了一个 记忆错误。

  3. 蛮力。逐个迭代用户。对于每一个 用户,找到不同的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个链接。

  4. 任何建议都将不胜感激。我是不是已经走得太远了?大数据"像上面这样的传统方法不会削减它?我并不认为有50万笔交易符合“大数据”和“大数据”的标准。但我想存储130,000 x 30,000矩阵或创建一个包含30,000,000个元素的列表是非常大的?

1 个答案:

答案 0 :(得分:0)

我认为你的问题是矩阵表示不会削减它:

请注意记忆,你做的效率非常低。例如,您创建一个需要在RAM中分配大量零的矩阵。在RAM中没有任何对象不存在而不是零浮点数会更高效。你“滥用”线性代数数学来解决你的问题,这会让你使用大量的RAM。 (元素的数量在你的矩阵中是130k * 30k =一个gazilion,但你“只”拥有你实际关心的30米链接)

我真的感觉到你,因为大熊猫是我学到的第一个图书馆,我试图用熊猫解决几乎所有问题。我注意到随着时间的推移,矩阵方法对于很多问题并不是最佳的。

在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的信息。

重要的一课是:

如果矩阵中的大多数单元表示相同类型的信息,如果遇到内存问题,请不要使用矩阵方法

我见过很多大熊猫解决方案,可以通过简单使用pythons内置类型来完成这些问题,例如 dict set frozenset < / em>和计数器。特别是从MATLAB和R或Excel这样的统计工具箱中访问Python的人非常喜欢它(他们肯定喜欢他们的表)。我建议一个人试图不把熊猫作为他个人建造的图书馆,然后他首先来到这里......