【问题标题】:Get node list from random walk in networkX从networkX中的随机游走中获取节点列表
【发布时间】:2016-09-15 15:37:08
【问题描述】:

我是 networkX 的新手。我创建了一个图表如下:

G = nx.read_edgelist(filename,
                     nodetype=int,
                     delimiter=',',
                     data=(('weight', float),))

边是正数,但总和不等于一。

是否有内置方法可以从某个节点随机游走k 步并返回节点列表?如果没有,最简单的方法是什么(节点可以重复)?

伪代码:

node = random
res = [node]
for i in range(0, k)
    read edge weights from this node
    an edge from this node has probability weight / sum_weights
    node = pick an edge from this node 
    res.append(node)

【问题讨论】:

    标签: python graph machine-learning statistics networkx


    【解决方案1】:

    您可以使用邻接矩阵。然后你可以对其进行归一化,使行的总和等于1,每一行是节点跳转到另一个节点的概率分布。 如果walker跳转到随机节点,也可以有跳转概率。

    M = nx.adjacency_matrix(g) #obtain the adj. matrix for the graph
    #normalise the adjacency matrix
    for i in range(M.shape[1]):
        if (np.sum(M[i]) > 0):
        M[i] = M[i]/np.sum(M[i])
    p = generate a random number between 0 and 1
    if p < restart_prob:
        #do restart
    else:
        #choose next node
    

    然后你可以随机选择一个节点,然后以概率1-restart_prob选择下一个或者以概率restart_prob重启walker。

    为了更好地理解算法,您可以查看 PageRank 的工作原理。

    【讨论】:

      【解决方案2】:

      最简单的方法是使用转移矩阵T,然后使用普通的马尔可夫随机游走(简而言之,该图可以被视为有限状态马尔可夫链)。

      AD分别是图G的邻接矩阵和度矩阵。转移矩阵 T 定义为 T = D^(-1) A
      p^(0) 为状态向量(简而言之,第 i 个分量表示在节点 i 处的概率)行走开始,第一步(行走)可以计算为p^(1) = T p^(0)。
      迭代地,第 k 个随机游走步骤可以评估为 p^(k) = T p^ (k-1)。

      简单的 Networkx 术语...

      import networkx
      import numpy
      # let's generate a graph G
      G = networkx.gnp_random_graph(5, 0.5)
      # let networkx return the adjacency matrix A
      A = networkx.adj_matrix(G)
      A = A.todense()
      A = numpy.array(A, dtype = numpy.float64)
      # let's evaluate the degree matrix D
      D = numpy.diag(numpy.sum(A, axis=0))
      # ...and the transition matrix T
      T = numpy.dot(numpy.linalg.inv(D),A)
      # let's define the random walk length, say 10
      walkLength = 10
      # define the starting node, say the 0-th
      p = numpy.array([1, 0, 0, 0, 0]).reshape(-1,1)
      visited = list()
      for k in range(walkLength):
          # evaluate the next state vector
          p = numpy.dot(T,p)
          # choose the node with higher probability as the visited node
          visited.append(numpy.argmax(p))
      

      【讨论】:

      • 您可能不想反转该对角矩阵或进行完整的矩阵矩阵乘法。考虑像 dinv = 1./numpy.sum(A, axis=0) 这样的东西。然后遍历行并应用 dinv 的每个元素。
      • 很漂亮,尽管在我看来,从邻接矩阵 $A$ 中获取转换矩阵 $T$ 只是将每一行除以其总和的问题。这实质上是说它同样有可能获取图的每条边。事实上,我们可以引入任何加权方案,然后除以这种邻接矩阵中的边权重之和。
      • @lericson 这确实是真的。您的第一个观察结果成立,因为我的示例中的邻接矩阵是二进制的(即,如果节点 ij)。然而,众所周知,邻接矩阵可以在位置 i,j 中对连接节点 ij 的边的权重进行评分。因此,度矩阵 D 将不包含“连接到给定节点的节点数”,而是包含给定节点的边的权重之和。因此,您的结论是:在构建 T 时,一个除以权重的总和。
      【解决方案3】:

      扩展@AlessioX的答案

      令 A 为邻接矩阵,即如果顶点 i 和 j 之间有边,则 A_ij 为 1.0,如果没有边,则为 0.0。 (但请注意,以下内容甚至适用于加权邻接矩阵。)

      >>> A
      array([[1, 0, 1],
             [1, 1, 1],
             [0, 0, 1]])
      

      然后我们可以通过将每一行除以其总和来找到转移矩阵T,简单地说:

      >>> A / A.sum(axis=1, keepdims=True)
      array([[0.5       , 0.        , 0.5       ],
             [0.33333333, 0.33333333, 0.33333333],
             [0.        , 0.        , 1.        ]])
      

      【讨论】:

        【解决方案4】:

        免责声明:我是以下软件包的作者。

        我很惊讶没有库能有效地做到这一点,所以我为此构建并开源了一个 python 包。它是用 C++ 编写的,并使用并行化来获得最大速度。它可以在几秒钟内生成数百万次随机游走。

        import walker
        
        walks = walker.random_walks(G, n_walks=15, walk_len=10)
        

        这将为图表 G 中的每个节点创建 15 个步行,长度为 10。

        如果您只想从单个节点开始创建一个随机游走:

        node = 42
        walks = walker.random_walks(G, n_walks=1, walk_len=10, start_node=[node])
        

        您还可以通过指定 pq 参数来创建偏向 node2vec 的随机游走。

        安装需要 pybind11 才能允许 C++ 绑定:

        pip install pybind11
        pip install graph-walker
        

        【讨论】:

          【解决方案5】:

          最有效的方法是使用 CSR 稀疏格式的图的转换矩阵,当然,有一个很棒的包:csrgraph (pip install csrgraph)。以下是你可以做到的:

          import csrgraph as cg
          import numpy as np
          
          G = cg.csrgraph(G, threads=12) 
          node_names = G.names
          walks = G.random_walks(walklen=10, # length of the walks
                          epochs=100, # how many times to start a walk from each node
                          start_nodes=None, # the starting node. It is either a list (e.g., [2,3]) or None. If None it does it on all nodes and returns epochs*G.number_of_nodes() walks
                          return_weight=1.,
                          neighbor_weight=1.)
          

          结果是一个大小为 (epochs*number_of_nodes, walklen) 的数组。有关该函数及其参数的更多信息可以找到here

          在 2,130 个节点和 36,560 条边的图上,我用上面的 sn-p 生成 213,000 条长度为 20 的路径需要 0.5 秒:

          >>> array([[   0,    4, 1678, ...,   48,  728,   30],
                 [   1,   57,  102, ...,  947,  456,  240],
                 [   2,  156,  177, ...,  175, 1363,  539],
                 ...,
                 [2127, 1655, 1656, ..., 1655, 1656, 2127],
                 [2128,    4, 1432, ...,  111,   32,  162],
                 [2129,    4,  521, ..., 1280,  180,  608]], dtype=uint32)
          
          walks.shape
          >>> (213000, 20)
          

          可以使用下面的 sn-p 或其他类似方法将节点名称映射回其原始格式:

          walks = np.vectorize(lambda x: node_names[x])(walks) # map to original node names
          

          注意:这个包不仅仅是随机游走,您可能需要查看他们的 GitHub 存储库here

          【讨论】:

            猜你喜欢
            • 2015-01-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-07-29
            • 1970-01-01
            • 2021-05-05
            • 1970-01-01
            • 2014-03-26
            相关资源
            最近更新 更多