【问题标题】:Build adjacency matrix from a list of nodes by avoiding for loops通过避免 for 循环从节点列表构建邻接矩阵
【发布时间】:2021-02-08 01:23:00
【问题描述】:

我需要解决什么问题?

从索引列表构建二进制矩阵。 这是我如何进行的,但我想找到一种有效的方法来避免循环

输入:

list_indices =[
[0,3,4],
[2,1,0],
[3,5]
]

预期输出:

results=[
[0,1,1,1,1,0],
[1,0,1,0,0,0],
[0,0,0,0,0,0],
[1,0,0,0,1,1],
[1,0,0,1,0,0],
[0,0,1,0,0,0],
]

结果对应于从索引列表构造的二进制邻接(对称)矩阵。结果中的 1 对应于属于同一行 list_indices 的一对索引。

list_indices 中的对是:

row 1 : (0,3), (3,0), (0,4),(4,0), (3,4), (4,3)
row 2 : (0,1), (1,0), (2,0), (0,2),(1,2), (2,1)
row 3 : (3,5), (5,3)


number of column and number of rows in results = np.max(list_indices)+1=6 

我尝试了什么?

results=np.zeros((np.max(list_indices)+1,np.max(list_indices)+1))

for pair in itertools.combinations(list_indices, r=2) :
                      
         results[pair[0],pair[1]]=results[pair[1],pair[0]]=1.0

构建它的有效方法是什么? (避免循环)

itertools.combinations 返回一个对列表,然后用于填充矩阵结果。由于矩阵是对称的,itertools.combinations 提供了对应于上对角矩阵的对列表。对角线设置为零

【问题讨论】:

  • 是什么让你觉得这个for循环效率不高?无论做什么都必须遍历所有索引对。
  • 我正在寻找一种 pytonic/vectorized 的方式来编写它。我认为在要填充 200,000 对和 700,000 个元素的矩阵的情况下效率不高
  • 什么déjà-vu。这似乎是所有简单路径联合的问题(提示:networkx.all_simple_paths(G, i, j, cutoff=2) 的每一对 (i, j) 的顶点 networkx.Graph(G)。当我试图解决 Codesignal Graphs Arcade 中的问题 3 时出现了这个问题,所以我经历了研究了很久,终于找到了解决办法,很快就贴出来了。

标签: python list numpy itertools adjacency-matrix


【解决方案1】:

这个问题与我10天前discussed的调查密切相关,所以我将在这里发布最重要的发现摘要。

  • 将社区存储为不平衡长度的列表会强制使用效率不高的迭代或串联。相反,您可以使用单个数组并像这样计数:

        flow = [0,3,4,2,1,0,3,5]
        counts = [3,3,2]
    
  • 单个组合的更快方法是np.triu_indices 方法而不是itertools.combinations

    def combs(t):
         x, y = np.triu_indices(len(t), 1)
         return np.transpose([t[x], t[y]])
    

    在我的解决方案中指出,您正在寻找如何避免串联和列表理解:

    np.concatenate([combs(n) for n in list_indices])
    

    或者,或者 (from itertools import*):

    np.array(list(chain(*[combinations(n,2) for n in list_indices])))
    
  • 根据您的输入,我找到了几种矢量化方法:

    def repeat_blocks(a, sizes, repeats):
        #Thanks @Divakar: https://stackoverflow.com/a/51156138/3044825
        r1 = np.repeat(np.arange(len(sizes)), repeats)
        N = (sizes*repeats).sum() # or np.dot(sizes, repeats)
        id_ar = np.ones(N, dtype=int)
        id_ar[0] = 0
        insert_index = sizes[r1[:-1]].cumsum()
        insert_val = (1-sizes)[r1[:-1]]
        insert_val[r1[1:] != r1[:-1]] = 1
        id_ar[insert_index] = insert_val
        out = a[id_ar.cumsum()] 
        return out
    
    def concat_combs1(flow, counts):
        #way 1 based on repetition of blocks of consecutive items
        col1 = repeat_blocks(flow, counts, counts)
        col2 = np.repeat(flow, np.repeat(counts, counts))
        return np.transpose([col1, col2])[col1 < col2]
    
    def concat_combs2(targets, counts):
        #way 2 based on repetition of blocks dissociated from each other
        counts = list(map(len, targets))
        col1 = np.concatenate(np.repeat(targets, counts, axis=0))
        col2 = np.repeat(np.concatenate(targets), np.repeat(counts, counts))
        return np.transpose([col1, col2])[col1 < col2]
    

    测试:

    list_indices = [np.array([0,3,4]), np.array([2,1,0]), np.array([3,5])]
    flow = np.array([0,3,4,2,1,0,3,5])
    counts = np.array([3, 3, 2])
    # Usage:
    np.concatenate([combs(n) for n in list_indices])
    concat_combs1(flow, counts)
    concat_combs2(list_indices)
    

    输出:

    array([[0, 3],
           [0, 4],
           [3, 4],
           [1, 2],
           [0, 2],
           [0, 1],
           [3, 5]])
    

结论

perfploted igraph.Graph.Barabasi(n = x, m = 3) 有四种方法,包括 itertools.combinationsnp.triu_indices。该图的每个顶点平均有 3 个邻居。总之,connection of repeated consecutive blocks 效果最好。这次连接 numpy 数组比链接组合要慢,因为要连接大量的小列表。

最终解决方案

为了以最快的方式构建关联矩阵,您需要应用concat_combs1 方法的小变化:

flow = np.array([0,3,4,2,1,0,3,5])
counts = np.array([3,3,2])
results = np.zeros((np.max(flow)+1, np.max(flow)+1), dtype=int)
col1 = repeat_blocks(flow, counts, counts)
col2 = np.repeat(flow, np.repeat(counts, counts))
results[col1, col2] = 1
np.fill_diagonal(results, 0)

输出

[[0 1 1 1 1 0]
 [1 0 1 0 0 0]
 [1 1 0 0 0 0]
 [1 0 0 0 1 1]
 [1 0 0 1 0 0]
 [0 0 0 1 0 0]]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多