【问题标题】:Python: how to find triplets of items visited by triplets of usersPython:如何找到三组用户访问的项目的三组
【发布时间】:2017-03-08 08:47:57
【问题描述】:

我有一个 CSV 文件,其中包含用户访问过的项目列表,例如:

user_id item_id
370 293
471 380
280 100
280 118
219 118
...

列表很长 - 30M 行。

我需要找到三个用户访问过的项目的三元组(即所有三个用户都访问了所有三个项目)。这样的三胞胎很少见。我试图找到的示例结果:

user_id item_id
1  15
1  26
1  31
77 15
77 26
77 31
45 15
45 26
45 31

有什么好的方法可以做到这一点?我可以使用 Pandas 或任何其他库。

【问题讨论】:

    标签: python csv pandas


    【解决方案1】:

    您可以将transformsize 一起使用,然后按boolean indexing 过滤:

    print (df)
        user_id  item_id
    0         1       15
    1         1       26
    2         1       31
    3        77       15
    4        77       26
    5        77       31
    6        45       15
    7        45       26
    8        45       31
    9       370      293
    10      471      380
    11      280      100
    12      280      118
    13      219      118
    
    print (df.groupby('user_id')['item_id'].transform('size'))
    0     3
    1     3
    2     3
    3     3
    4     3
    5     3
    6     3
    7     3
    8     3
    9     1
    10    1
    11    2
    12    2
    13    1
    Name: item_id, dtype: int64
    
    print (df[df.groupby('user_id')['item_id'].transform('size') == 3])
       user_id  item_id
    0        1       15
    1        1       26
    2        1       31
    3       77       15
    4       77       26
    5       77       31
    6       45       15
    7       45       26
    8       45       31
    

    filtration 的解决方案更慢:

    df = df.groupby('user_id').filter(lambda x: len(x.item_id) == 3)
    print (df)
       user_id  item_id
    0        1       15
    1        1       26
    2        1       31
    3       77       15
    4       77       26
    5       77       31
    6       45       15
    7       45       26
    8       45       31
    

    【讨论】:

    • 该解决方案在这种情况下有效,但在一般情况下不起作用。例如:df = pd.DataFrame( { 'user_id': [1, 2, 1, 77, 70, 77, 45, 45, 45, 370, 471, 280, 280, 219], 'item_id': [15, 26, 31, 15, 26, 31, 15, 26, 31, 293, 380, 100, 118, 118] } ) print(df[df.groupby('user_id')['item_id'].transform('size') == 3]) 应该什么都不返回,但它返回 user_id=45 和项目 15、26 和 31。但我需要三个用户和三个项目。一个用户拥有三个项目是不够的。
    • 我明白了。对不起,给我时间
    • 好的,现在它理解得更好了。那么如果有 2 个三胞胎呢?可以加样吗?
    • 我也认为第一步是transform 解决方案。实际数据中有 3 项的所有值的输出大小是多少?
    【解决方案2】:

    您可以在graph theory 的帮助下解决您的问题。在您的情况下,您正在寻找类型为 K3,3complete bipartite graphs(下图中的中间那个)。

    你可以使用这个answermaximal cliques的帮助下找到所有最大完全二分图,然后过滤掉K3,3图。

    示例:

    我使用了networkx 包和jupyter notebook

    import networkx as nx
    from networkx.algorithms import bipartite
    from itertools import combinations, chain
    

    让我们创建一个随机二分图(如您的 CSV 数据)。

    B = bipartite.random_graph(n=8, m=8, p=0.5, seed=3)
    print(B.edges)
    

    打印:

    [(0, 8), (0, 10), (0, 11), (0, 13), (0, 15), (1, 8), (1, 9), (1, 12), (1, 13), (1, 14), (2, 14), (2, 15), (3, 10), (3, 11), (3, 13), (3, 14), (4, 8), (4, 11), (4, 13), (4, 15), (5, 9), (5, 10), (5, 13), (5, 15), (6, 8), (6, 9), (6, 12), (6, 13), (6, 15), (7, 11), (7, 13)]
    

    我用这个answer 创建了一个函数来绘制漂亮的二分图。

    def draw_bipart(graph, set_X, set_Y):
        pos = dict()
        pos.update( (n, (1, i)) for i, n in enumerate(set_X) ) # put nodes from X at x=1
        pos.update( (n, (2, i)) for i, n in enumerate(set_Y) ) # put nodes from Y at x=2
        nx.draw(graph, pos=pos, with_labels=True)
    
    X, Y = bipartite.sets(B) #two sets of data (in your case user_id and item_id)
    
    %matplotlib inline
    draw_bipart(B, X, Y) 
    

    让我们创建图 B 的副本并连接每个集合(X 和 Y)中的所有顶点,以便我们可以搜索派系。

    connect_B = B.copy()
    edges_X = combinations(X, 2)
    edges_Y = combinations(Y, 2)
    connect_B.add_edges_from(chain(edges_X, edges_Y))
    draw_bipart(connect_B, X, Y)
    

    现在集合 X (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) 中的每一对顶点都由一条边连接(由于重叠,并非全部可见)。 Y 集也是如此。

    现在让我们搜索最大派系:

    cliques = list(nx.find_cliques(connect_B))
    print(cliques)
    

    打印所有最大团:

    [[2, 0, 4, 5, 6, 1, 3, 7], [2, 0, 4, 5, 6, 15], [2, 14, 1, 3], [2, 14, 15], [13, 0, 10, 11, 8, 15], [13, 0, 10, 11, 3], [13, 0, 10, 5, 3], [13, 0, 10, 5, 15], [13, 0, 4, 11, 8, 15], [13, 0, 4, 11, 3, 7], [13, 0, 4, 6, 1, 8], [13, 0, 4, 6, 1, 3, 5, 7], [13, 0, 4, 6, 15, 8], [13, 0, 4, 6, 15, 5], [13, 9, 8, 12, 6, 1], [13, 9, 8, 12, 6, 15], [13, 9, 8, 12, 14, 1], [13, 9, 8, 12, 14, 10, 11, 15], [13, 9, 5, 10, 15], [13, 9, 5, 6, 1], [13, 9, 5, 6, 15], [13, 14, 3, 1], [13, 14, 3, 10, 11]]
    

    现在我们必须过滤 K3,3 图。我在这里使用两个条件:K3,3 图应该有 6 个顶点,其中 3 个应该属于一个集合。

    cliques_K33 = [c for c in cliques if len(c) == 6 and len(X.intersection(c)) == 3]
    print(cliques_K33)
    

    打印:

    [[13, 0, 4, 6, 15, 8]]
    

    最后我们画出由找到的K3,3团的顶点诱导的图B的子图:

    draw_bipart(B.subgraph(cliques_K33[0]), X, Y)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-24
      • 1970-01-01
      • 2018-10-04
      • 2013-12-29
      相关资源
      最近更新 更多