【问题标题】:K-th order neighbors in graph - Python networkx图中的 K 阶邻居 - Python networkx
【发布时间】:2013-08-23 02:45:58
【问题描述】:

我有一个有向图,我想在其中有效地找到一个节点的所有 K 阶邻居的列表。 K 阶邻居被定义为可以从问题中的节点到达的所有节点确切K hops。

我查看了networkx,唯一相关的功能是neighbors。但是,这只会返回顺序 1 的邻居。对于更高阶,我们需要迭代来确定完整的集合。我相信应该有一种更有效的方式来访问networkx 中的 K 阶邻居。

是否有一个函数可以有效地返回第 K 阶邻居,而无需增量构建集合?

编辑:如果 Python 中存在其他可能在这里有用的图形库,请务必提及。

【问题讨论】:

    标签: python networkx adjacency-list


    【解决方案1】:

    您可以使用: nx.single_source_shortest_path_length(G, node, cutoff=K)

    G 是您的图形对象。

    【讨论】:

    • 如果它在整个图上运行 Dijkstra 算法,这将是一种浪费。这是它的作用吗?
    【解决方案2】:

    对于 NetworkX,最好的方法可能是在每个 k 处构建一组邻居。您没有发布您的代码,但似乎您可能已经这样做了:

    import networkx as nx
    
    def knbrs(G, start, k):
        nbrs = set([start])
        for l in range(k):
            nbrs = set((nbr for n in nbrs for nbr in G[n]))
        return nbrs
    
    if __name__ == '__main__':
        G = nx.gnp_random_graph(50,0.1,directed=True)
        print(knbrs(G, 0, 3))
    

    【讨论】:

    • 确实如此。我也在做同样的事情。事实上,为了加快速度,我正在为i 创建和存储第 i 个邻居,范围从 1 一直到 K。我希望我能找到一条捷径。好像一个都没有感谢您的回复。
    • 你在哪里使用 l 在你的循环中?
    【解决方案3】:

    您使用修改后的 BFS 算法解决了您的问题。当您将节点存储在队列中时,也要存储它的级别(与根的距离)。当您完成处理节点(所有邻居访问 - 节点标记为黑色)时,您可以将其添加到其级别的节点列表中。这是基于this simple implementation的示例:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    from collections import defaultdict 
    from collections import deque
    
    kth_step = defaultdict(list)
    
    class BFS:
        def __init__(self, node,edges, source):
            self.node = node
            self.edges = edges
            self.source = source
            self.color=['W' for i in range(0,node)] # W for White
            self.graph =color=[[False for i in range(0,node)] for j in range(0,node)]
            self.queue = deque()
    
            # Start BFS algorithm
            self.construct_graph()
            self.bfs_traversal()
    
        def construct_graph(self):
            for u,v in self.edges:
                self.graph[u][v], self.graph[v][u] = True, True
    
        def bfs_traversal(self):
            self.queue.append((self.source, 1))
            self.color[self.source] = 'B' # B for Black
            kth_step[0].append(self.source)
    
            while len(self.queue):
                u, level =  self.queue.popleft()
                if level > 5: # limit searching there
                    return
                for v in range(0, self.node):
                    if self.graph[u][v] == True and self.color[v]=='W':
                        self.color[v]='B'
                        kth_step[level].append(v)
                        self.queue.append((v, level+1))
    
    '''
    0 -- 1---7
    |    |
    |    |
    2----3---5---6
    |
    |
    4
    
    '''
    
    
    node = 8 # 8 nodes from 0 to 7
    edges =[(0,1),(1,7),(0,2),(1,3),(2,3),(3,5),(5,6),(2,4)] # bi-directional edge
    source = 0 # set fist node (0) as source
    
    bfs = BFS(node, edges, source)
    
    
    for key, value in kth_step.items():
        print key, value
    

    输出:

    $ python test.py
    0 [0]
    1 [1, 2]
    2 [3, 7, 4]
    3 [5]
    4 [6]
    

    我不知道networkx,我也没有发现可以在 Graph Tool 中使用算法。我相信这样的问题还不够普遍,无法拥有自己的功能。此外,我认为为图形实例中的任何节点存储第 k 个邻居的列表会过于复杂、效率低下和冗余,因此这样的函数可能无论如何都必须遍历节点。

    【讨论】:

    • 感谢您的回答,尽管这不是我想要的。我已经有一个版本,它通过增加 1 阶 nbrs 的集合,然后添加它们的 nbrs,依此类推,直到我达到 K 阶邻居。但这超出了 networkx 提供的范围。
    【解决方案4】:

    我有一个类似的问题,除了我有一个有向图,我需要维护边缘属性字典。如果您需要,此相互递归解决方案保留边缘属性字典

    def neighbors_n(G, root, n):
        E = nx.DiGraph()
    
        def n_tree(tree, n_remain):
            neighbors_dict = G[tree]
    
            for neighbor, relations in neighbors_dict.iteritems():
              E.add_edge(tree, neighbor, rel=relations['rel'])
    
            #you can use this map if you want to retain functional purity
            #map(lambda neigh_rel: E.add_edge(tree, neigh_rel[0], rel=neigh_rel[1]['rel']), neighbors_dict.iteritems() )
    
            neighbors = list(neighbors_dict.iterkeys())
            n_forest(neighbors, n_remain= (n_remain - 1))
    
        def n_forest(forest, n_remain):
            if n_remain <= 0:
                return
            else:
                map(lambda tree: n_tree(tree, n_remain=n_remain), forest)
    
        n_forest( [root] , n)
    
        return E
    

    【讨论】:

      【解决方案5】:

      如前所述,以下解决方案为您提供所有次要邻居(邻居的邻居)并列出所有邻居一次(该解决方案基于 BFS):

      {n: path for n, path in nx.single_source_shortest_path(G, 'a', cutoff=2).items() if len(path)==3}
      

      另一个稍微快一点的解决方案(6.68 µs ± 191 ns 对比 13.3 µs ± 32.1 ns,使用timeit 测量)包括在无向图中,邻居的邻居可以再次成为源:

      def k_neighbors(G, source, cutoff):
          neighbors = {}
          neighbors[0] = {source}
          for k in range(1, cutoff+1):
              neighbors[k] = set()
              for node in level[k-1]:
                  neighbors[k].update(set(G.neighbors(node)))
          return neighbors
      
      k_neighbors(B, 'a', 2) #dict keyed with level until `cutoff`, in this case 2
      

      两种解决方案都将源本身作为 0 阶邻居。

      所以这取决于你喜欢哪一个。

      【讨论】:

        猜你喜欢
        • 2016-06-16
        • 2019-06-05
        • 2018-12-27
        • 2017-07-19
        • 1970-01-01
        • 2018-04-20
        • 2013-08-11
        • 1970-01-01
        • 2016-09-10
        相关资源
        最近更新 更多