I have a directed tree graph with a single root vertice, where the directions seem arbitrary.
I want all edges to be directed from the single root, towards the ends of the respective branches.
My first primitive attempt was to swap the source and target vertices like below (but as assumed, it would not work.
temp = g.es[e_idx].source
g.es[e_idx].source = g.es[e_idx].target
g.es[e_idx].target = temp
Is there a function or a set of functions that allow to swap the direction of a specific edge available?
Or a way to manipulate the source / target attributes of edges, without needing to change any vertice attributes?
Please let me know if I need to specify anything further.
Your help would be greatly appreciated.
Here is a possible solution that preserves all graph attributes:
We are going to reorder vertices so that those closer to the root come first
Then we use to_directed with the "acyclic" mode, which directs edges from lower to higher index vertices
Finally we restore the original vertex order
import igraph as ig
from igraph import Graph
# Set seed for reproducibility
import random
random.seed(123)
# Create an undirected tree. If your tree is not undirected,
# convert it to undirected first.
g = Graph.Tree_Game(10)
# Our chosen root:
root = 3
ig.plot(g, vertex_label=range(g.vcount()), layout = g.layout_reingold_tilford(root=root), bbox=(300,300))
# Distances from the root, will be used for ordering:
dist=g.shortest_paths(source=root)[0]
# This function computes the permutation that would
# sort 'elems'. It also serves as a way to invert
# permutations.
def ordering(elems):
return sorted(range(len(elems)), key=elems.__getitem__)
# Compute orderings based on the distance from the root:
perm = ordering(dist)
invperm = ordering(perm)
# Reorder, direct, restore order:
dg = g.permute_vertices(invperm)
dg.to_directed('acyclic')
dg = dg.permute_vertices(perm)
# Plot again.
# Now the root does not need to be given,
# as it is auto-detected from the directions.
ig.plot(dg, vertex_label=range(g.vcount()), layout='reingold_tilford', bbox=(300,300))
Related
I am going to draw a network with python3 and networkxmoduel.
First of all, I am sorry I could not write any example code because I did not receive any raw data.
The network consists of 3 groups of nodes and a below attached is what I imagined.
It is a hand drawn.
I would like to refer any layout or tips to draw this kind of above.
I know Multipartite Layouthttps://networkx.org/documentation/stable/auto_examples/drawing/plot_multipartite_graph.html#multipartite-layout, however, I am not sure it is suitable for me.
Thanks.
The multi-partite layout is going to put your nodes in rows/columns based on the partitions you specify, but it seems like what you want is to align your nodes so that the provided groups/partitions are clustered together and separated from the other groups/clusters. You can do this by making a position dictionary that you can pass to the networkx drawing functions. The example function below takes your graph, the name of the node attribute in your Graph object that specifies which group/partition each node belongs to (partition_attr), an optional list of partition names specifying the order you want to display your groups/components left-to-right (partition_order) and the minimum space between nodes in different partitions (epsilon).
#%% Function to make position dicts by partition
def make_node_positions(graph,partition_attr,partition_order=None,epsilon=.5):
if not partition_order:
# get a list of all the partition names if not specified
partition_order = list(set(dict(graph.nodes(data=partition_attr)).values()))
# make position dict for each partition
orig_partition_pos_dicts = {partition:nx.spring_layout(graph.subgraph([node for node,part in graph.nodes(data=partition_attr)
if part == partition]))
for partition in partition_order}
# update the x coordinate in the position dicts so partitions
# don't overlap and are in the specified order left-to-right
final_pos_dict = orig_partition_pos_dicts[partition_order[0]]
for i,partition in enumerate(partition_order[1:]):
# get the largest x coordinate from the previous partition's nodes
max_previous = max([x for x,y in final_pos_dict.values()])
# get smallest x coordinate from this partition's nodes
current_min = min([x for x,y in orig_partition_pos_dicts[partition].values()])
# update the x coordinates for this partition to be at least epsilon units
# to the right of the right-most node in the previous partition
final_pos_dict.update({node:(pos[0]+max_previous+abs(current_min)+epsilon,pos[1])
for node,pos in orig_partition_pos_dicts[partition].items()})
return(final_pos_dict)
Now I've made a graph similar to your drawing and applied the function below
#%% Set up toy graph
import networkx as nx
# make the initial graphs
k5 = nx.complete_graph(5)
triangle=nx.from_edgelist([(5,6),(6,7),(5,7)])
single_node = nx.Graph()
single_node.add_node(8)
# edges to connect the components
extra_edges = [(3,5),(2,6),(5,8),(6,8),(7,8)]
# combine graphs and specify the original graphs
orig_graphs = {'k5':{'graph':k5,'color':'blue'},
'triangle':{'graph':triangle,'color':'green'},
'single_node':{'graph':single_node,'color':'red'}}
g = nx.Graph()
for g_name,g_val_dict in orig_graphs.items():
# add the nodes from that graph and specify the partition and node colors
g.add_nodes_from(g_val_dict['graph'].nodes,partition=g_name,color=g_val_dict['color'])
if len(g_val_dict['graph'].edges) > 0:
# if the graph has edges then add the edges
g.add_edges_from(g_val_dict['graph'].edges,partition=g_name,color=g_val_dict['color'])
# add the extra edges to combine the graphs
g.add_edges_from(extra_edges,color='black')
#%% Draw graph #####
my_pos = make_node_positions(g,partition_attr='partition',partition_order=['k5','triangle','single_node'])
nx.draw_networkx_nodes(g,my_pos,node_color=[c for n,c in g.nodes(data='color')])
nx.draw_networkx_labels(g,my_pos)
nx.draw_networkx_edges(g,my_pos,edge_color=[c for u,v,c in g.edges(data='color')])
I have a problem involving graph theory. To solve it, I would like to create a weighted graph using networkx. At the moment, I have a dictionnary where each key is a node, and each value is the associated weight (between 10 and 200 000 or so).
weights = {node: weight}
I believe I do not need to normalize the weights with networks.
At the moment, I create a non-weighted graph by adding the edges:
def create_graph(data):
edges = create_edges(data)
# Create the graph
G = nx.Graph()
# Add edges
G.add_edges_from(edges)
return G
From what I read, I can add a weight to the edge. However, I would prefer the weight to be applied to a specific node instead of an edge. How can I do that?
Idea: I create the graph by adding the nodes weighted, and then I add the edges between the nodes.
def create_graph(data, weights):
nodes = create_nodes(data)
edges = create_edges(data) # list of tuples
# Create the graph
G = nx.Graph()
# Add edges
for node in nodes:
G.add_node(node, weight=weights[node])
# Add edges
G.add_edges_from(edges)
return G
Is this approach correct?
Next step is to find the path between 2 nodes with the smallest weight. I found this function: networkx.algorithms.shortest_paths.generic.shortest_path which I think is doing the right thing. However, it uses weights on the edge instead of weights on the nodes. Could someone explain me what this function does, what the difference between wieghts on the nodes and weights on the edges is for networkx, and how I could achieve what I am looking for? Thanks :)
This generally looks right.
You might use bidirectional_dijkstra. It can be significantly faster if you know the source and target nodes of your path (see my comments at the bottom).
To handle the edge vs node weight issue, there are two options. First note that you are after the sum of the nodes along the path. If I give each edge a weight w(u,v) = w(u) + w(v) then the sum of weights along this is w(source) + w(target) + 2 sum(w(v)) where the nodes v are all nodes found along the way. Whatever has the minimum weight with these edge weights will have the minimum weight with the node weights.
So you could go and assign each edge the weight to be the sum of the two nodes.
for edge in G.edges():
G.edges[edge]['weight'] = G.nodes[edge[0]]['weight'] + G.nodes[edge[1]]['weight']
But an alternative is to note that the weight input into bidirectional_dijkstra can be a function that takes the edge as input. Define your own function to give the sum of the two node weights:
def f(edge):
u,v = edge
return G.nodes[u]['weight'] + G.nodes[v]['weight']
and then in your call do bidirectional_dijkstra(G, source, target, weight=f)
So the choices I'm suggesting are to either assign each edge a weight equal to the sum of the node weights or define a function that will give those weights just for the edges the algorithm encounters. Efficiency-wise I expect it will take more time to figure out which is better than it takes to code either algorithm. The only performance issue is that assigning all the weights will use more memory. Assuming memory isn't an issue, use whichever one you think is easiest to implement and maintain.
Some comments on bidirectional dijkstra: Imagine you have two points in space a distance R apart and you want to find the shortest distance between them. The dijkstra algorithm (which is the default of shortest_path) will explore every point within distance D of the source point. Basically it's like expanding a balloon centered at the first point until it reaches the other. This has a volume (4/3) pi R^3. With bidirectional_dijkstra we inflate balloons centered at each until they touch. They will each have radius R/2. So the volume is (4/3)pi (R/2)^3 + (4/3) pi (R/2)^3, which is a quarter the volume of the original balloon, so the algorithm has explored a quarter of the space. Since networks can have very high effective dimension, the savings is often much bigger.
I have a network, and how to generate a random network but ensure each node retains the same degre of the original network using networkx? My first thought is to get the adjacency matrix, and perform a random in each row of the matrix, but this way is somwhat complex, e.g. need to avoid self-conneted (which is not seen in the original network) and re-label the nodes. Thanks!
I believe what you're looking for is expected_degree_graph. It generates a random graph based on a sequence of expected degrees, where each degree in the list corresponds to a node. It also even includes an option to disallow self-loops!
You can get a list of degrees using networkx.degree. Here's an example of how you would use them together in networkx 2.0+ (degree is slightly different in 1.0):
import networkx as nx
from networkx.generators.degree_seq import expected_degree_graph
N,P = 3, 0.5
G = nx.generators.random_graphs.gnp_random_graph(N, P)
G2 = expected_degree_graph([deg for (_, deg) in G.degree()], selfloops=False)
Note that you're not guaranteed to have the exact degrees for each node using expected_degree_graph; as the name implies, it's probabilistic given the expected value for each of the degrees. If you want something a little more concrete you can use configuration_model, however it does not protect against parallel edges or self-loops, so you'd need to prune those out and replace the edges yourself.
I searched but found there are many examples about how to create a graph with edges weight, but none of them shows how to create a graph with vertices weight. I start to wonder if it is possible.
If a vertices-weighted graph can be created with igraph, then is it possible to calculate the weighted independence or other weighted numbers with igraph?
As far as I know, there are no functions in igraph that accept arguments for weighted vertices. However, the SANTA package that is a part of the Bioconductor suite for R does have routines for weighted vertices, if you are willing to move to R for this. (Seems like maybe you can run bioconductor in python.)
Another hacky option is the use (when possible) unweighted routines from igraph and then back in the weights. E.g. something like this for weighted maximal independent sets:
def maxset(graph,weight):
ms = g.maximal_independent_vertex_sets()
w = []
t = []
for i in range(0, 150):
m = weights.loc[weights['ids'].isin(ms[i]),"weights"]
w.append(m)
s = sum(w[i])
t.append(s)
return(ms[t.index(max(t))])
maxset(g,weights)
(Where weights is a two column data frame with column 1 = vertex ids and column 2 = weights). This gets the maximal independent set taking vertex weights into consideration.
You want to use vs class to define vertices and their attributes in igraph.
As example for setting weight on vertices, taken from documentation:
http://igraph.org/python/doc/igraph.VertexSeq-class.html
g=Graph.Full(3) # generate a full graph as example
for idx, v in enumerate(g.vs):
v["weight"] = idx*(idx+1) # set the 'weight' of vertex to integer, in function of a progressive index
>>> g.vs["weight"]
[0, 2, 6]
Note that a sequence of vertices are called through g.vs, here g the instance of your Graph object.
I suggested you this page, I found it practical to look for iGraph methods here:
http://igraph.org/python/doc/identifier-index.html
I am trying to create a connected graph where each node has some attributes that determine what other nodes it is connected to. The network is a circular space to make it easy to establish links (there are a 1000 nodes).
The way this network works is that a node has both neighbors (the ones to its immediate left/right - i.e. node 3 has neighbors 1 and 2) and also k long distance links. The way a node picks long distance links is that it just randomly picks nodes from the clockwise direction (i.e. node 25 might have 200 as its long distance link instead of 15).
Here is a sample image of what it might looks like: http://i.imgur.com/PkYk5bz.png
Given is a symphony network but my implementation is a simplification of that.
I partially implemented this in java(via a linked list holding an arraylist) but am lost on how to do this in NetworkX. I am especially confused on how to add these specific node attributes that say that a node will find k long links but after k will not accept any more links. Is there a specific built in graph in networkx that is suited towards this model or is any graph acceptable as long as I have the correct node attributes?
It's a simplification of a more complicated network where no node leaves and no edge dissapears.
Any help or a link to an example would be appreciated on this.
This approximates to your need:
import networkx as nx
import matplotlib.pyplot as plt
import random
N = 20 # number of nodes
K = 3 # number of "long" edges
G = nx.cycle_graph(N)
for node in G.nodes():
while len(G.neighbors(node)) < K+2:
# Add K neighbors to each node
# (each node already has two neighbors from the cycle)
valid_target_found = False
while not valid_target_found:
# CAUTION
# This loop will not terminate
# if K is too high relative to N
target = random.randint(0,N-1)
# pick a random node
if (not target in G.neighbors(node)
and len(G.neighbors(target)) < K+2):
# Accept the target if (a) it is not already
# connected to source and (b) target itself
# has less than K long edges
valid_target_found = True
G.add_edge(node, target)
nx.draw_circular(G)
plt.show()
It creates the graph below. There are improvements to be made, for example, a more efficient selection of the target nodes for the long edges, but this gets you started, I hope.
In NetworkX, if there's any logic about connecting your node everything should be left to you.
Nevertheless, if you want to iterate on nodes in Python (not tested):
for (nodeId, data) in yourGraph.nodes(data=True):
// some logic here over data
// to connect your node
yourGraph.add_edge(nodeId, otherNodeId)
Side note: if you want to stay in Java you can also consider using Jung and Gephi.