简体   繁体   中英

Networkx: Randomly Traverse directed graph

I have a directed graph with weighted edges. Each node is connected to every other node, and the weights represent the likelihood of moving from Node X to Node Y (sum of weights for each node out = 1 - this is a stochastic matrix).

I need to create a function that randomly traverses the graph and goes in and out of each node only once, returning to the starting point

I don't want to return the most likely output, just the first random walk through the tree that hits each node only once and returns the path it took, and the likelihood of each jump it took.

Here's a simple implementation I'm looking for:

import pandas as pd
import numpy as np
from numpy.random import choice
import networkx as nx

testData = [('A','B',.5),('A','C',.4),('A','D',.1),('B','A',.5),('B','C',.3),('B','D',.2),('C','A',.3),('C','B',.1), 
            ('C','D',.6),('D','A',.35),('D','B',.15),('D','C',.5)]

G = nx.DiGraph()
G.add_weighted_edges_from(testData)

#traverse g from randomly selected starting node to every other node randomly and back to random start node
def randomWalk(g):
    start_node = choice(G.nodes())

    #dfs implementation available?

    return pathTaken

print (randomWalk(G))
>>> [('C','A',.3),('A':'D',.1),('D':'B',.15),('B':'C',.3)]

I can't find a way to incorporate the random walk component to any of the traversal algorithms available.

Any thoughts on available implementations I could use? I'd prefer to not write a custom DFS if I can avoid it...

Thanks!

I don't know if you will consider this as an available implementation, but this :

random.sample(G.nodes(), len(G.nodes()))

will return a random and valid path for your problem as the graph is strongly connected.

Then you need to process the result to have a list of tuples :

def random_walk(G):
    path = random.sample(G.nodes(), len(G.nodes()))
    return [(path[i-1], node, G[path[i-1]][node]['weight']) for i, node in enumerate(path)]

In your example :

print(random_walk(G))
>>> [('B', 'C', 0.3), ('C', 'D', 0.6), ('D', 'A', 0.35), ('A', 'B', 0.5)]

What I ended up doing was deleting nodes as I used them, then just assigning the last node to the first node. At each step, I'd re-weight to get 100% likelihoods. I'm sure this isn't super efficient, but my graph is small, so it's ok. I then merged on the likelihood of each item happening at the end.

matchedPath = []

currNode = choice(G.nodes())
firstNode = currNode

while G.number_of_nodes() >1:
    connectNodes = [x[1] for x in G.out_edges(currNode,data = True)]
    connectWeights = [x[2]['weight'] for x in G.out_edges(currNode,data = True)]
    remainingWeights = [z/sum(connectWeights) for z in connectWeights]

    nextNode = choice(connectNodes, 1, p=remainingWeights)[0]
    matchedPath.append((currNode,nextNode))

    G.remove_node(currNode)
    currNode = nextNode
matchedPath.append((currNode,firstNode))    

matched_df = pd.DataFrame(matchedPath,columns = ['from','to'])
matched_df = pd.merge(matched_df,rawData,how = 'outer',left_on =['from'],right_on = ['Name']).drop(['Name'],axis = 1)
matched_df = pd.merge(matched_df,link_df,how = 'left',left_on = ['from','to'],right_on = ['person_x','person_y']).drop(['person_x','person_y'],axis = 1)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM