How to add edges in an igraph object from a dictionary - python

I'm using a dictionary to represent a set of edges. I'm using the keys of the dictionary to represent edges and the values to represent the weights. The dictionary currently looks like this:
{(0, 1): 2, (1, 2): 6, (0, 2): 3}
I try this:
edges, weights = [], []
for edge, weight in dict_edges.items():
edges += [edge]
weights.append(weight)
g.add_edges(edges)
g.es["weight"] = weights
But, I don't if there is a faster way or more cleaner to do this.
Anyone have any suggestions how to improve my new?

What you do is perfectly fine; maybe the for loop could be replaced with a zip call. If you are using Python 2.x::
from itertools import izip
edges, weights = izip(*dict_edges.iteritems())
g = Graph(edges, edge_attrs={"weight": weights})
If you are using Python 3.x::
edges, weights = zip(*dict_edges.items())
g = Graph(edges, edge_attrs={"weight": weights})

Related

Is there a way to simplify an undirected graph with OSMNX?

In my research, I use OpenStreetMap data for traffic-related simulations. Part of the data preparation involves using the osmnx library to get a simplified graph of the road network.
Currently, we do not want to consider one ways. In other words, every road should be represented as a single edge, regardless of whether or not it's a one-way or two-way street. This essentially means that I am looking to have an undirected graph rather than a directed graph.
The main problem is that osmnx's simplify graph only works with directed graphs.
If I call osmnx's simplify_graph function using a MultiDiGraph, I end up with something like this. In this example, the contiguous edges are not being merged because the part in purple is one-way whereas the pink and light blue parts are two-way streets. Relevant OpenStreetMap way IDs are 46678071, 110711994 and 237298378. However, this is not what I am looking for; I would like these three edges to be merged, regardless of the fact that one of them is one-way.
ox.settings.log_console = True
G = ox.graph_from_xml("osm_network_agglomeration_montreal.xml",
simplify=False,
retain_all=True,
bidirectional=False)
# Only retain graph that is inside a certain zone
G = ox.truncate.truncate_graph_polygon(G, boundary_polygon)
# Filter edges based on highway type
allowed_highway_types = ["primary",
"secondary",
"tertiary"]
edges_subset = []
for u, v, data in G.edges(data=True):
if data['highway'] in allowed_highway_types:
edges_subset.append((u, v, 0))
G_subset = G.edge_subgraph(edges_subset)
#G_subset = ox.get_undirected(G_subset) # Can't do this as simplify_graph only works with directed graphs.
# Simplify the graph: get rid of interstitial nodes
G_truncated = ox.simplify_graph(G_subset, strict=True, remove_rings=False)
# Convert to an undirected graph. We don't want parallel edges unless their geometries differ.
G_truncated = ox.get_undirected(G_truncated)
gdf_nodes, gdf_links = ox.graph_to_gdfs(G_truncated)
# Get rid of the variables we won't need anymore
#del G, edges_subset, G_subset
So, my question is: is there a way to simplify an undirected graph? I am fine with modifying OSMNX's code and submitting a pull request if that's what's required here.
Thanks!
One possibility is to convert the graph to undirected and then back to directed, so that all edges become two-way:
from networkx import Graph, DiGraph, path_graph
G = path_graph(4, create_using=DiGraph)
print(G.is_directed())
# True
print(G.edges)
# [(0, 1), (1, 2), (2, 3)]
G = DiGraph(Graph(G))
print(G.is_directed())
# True
print(G.edges)
# [(0, 1), (1, 0), (1, 2), (2, 1), (2, 3), (3, 2)]

NetworkX isomorphic connected components

I am solving a specific problem, I would appreciate any suggestions you had for this problem, its trivial but I am not sure if this is the best way to solve this problem.
The Problem: Given a graph G, lets say you get a set of connected component subgraphs {C_1, C_2, ....., C_N}. Now partition the aforementioned set into K subsets such that every element in the subset are isomorphic with each-other. You can have atmost N subsets.
The Solution:
Run a find connected sugraphs. This will return all the connected components as graphs.
Partition this connected components subgraphs based on number of nodes (Which should be fine in our case). Optional
For the subgraphs. Run the following:
Initialize a dictionary with the following. A random node as the key and a singleton set containing the graph itself as its value and add remaining nodes to a deque.
For the remaining nodes:
pop the element in the front of the deque. If there's an isomorphism match add that graph to the dictionary of subgraph, else push the element to its back.
Once there's absolute certainity that we have reached a cycle. I.e. we are scanning the same element which we pushed to the back of the queue. This node's cycle is done. Pop the front of the queue and add the <graph:set([graph])> entry to the dictionary mentioned before. Repeat until there are elements left in the queue.
Can this be further otpimized?
import networkx as nx
nx_graph = nx.Graph()
edges = [
(1, 2),
(2, 3),
(3, 4),
(5, 6),
(6, 7),
(7, 8),
(9, 10),
(10, 11),
(10, 12)
]
for edge in edges:
nx_graph.add_edge(edge[0], edge[1])
nx.draw(nx_graph, pos=nx.spring_layout(nx_graph), node_color='#1ab2c3', with_labels=True)
Graph:
from collections import deque
def paritition_isomorphic_subgraphs(graph):
subgraphs_gen = (graph.subgraph(c) for c in nx.connected_components(graph))
subgraphs_list = list(subgraphs_gen)
graph_queue = deque(subgraphs_list)
graph_of_interest = graph_queue.popleft()
isomprohic_elements = {
graph_of_interest: set([graph_of_interest])
}
last_element_popped = None
count = 0
first_mismatch = None
while graph_queue:
if graph_queue[0] == first_mismatch:
count = 0
graph_of_interest = graph_queue.popleft()
isomprohic_elements[graph_of_interest] = set([graph_of_interest])
if graph_queue:
graph = graph_queue.popleft()
if nx.is_isomorphic(graph_of_interest, graph):
isomprohic_elements[graph_of_interest].add(graph)
else:
if count == 0:
first_mismatch = graph
graph_queue.append(graph)
count += 1
return list(isomprohic_elements.values())

Adding specific weights to edges through dictionary

I have two dictionaries of nodes with edges like below:
all_nodes_edges = {20101: (20102, 20201), 20102: (20101, 20103), 20103: (20102, 20104)}
nodes_with_weights = {20101: 20201, 20119: 20219, 20201: (20301, 20101)}
I've created the graph and default weights for all the edges in the graph with the below code:
g = nx.Graph(all_nodes_edges)
nx.set_edge_attributes(g, 1, 'weight')
I'm trying to use the nodes_with_weights dict to create weights of 2 on specific edges. How do I achieve this?. Do I have to loop through the dictionary or can I just use a specific nx function?
Sorry kinda new to graphs.
I finally figured it out!
for u,v,a in g.edges(data=True):
if u and v in nodes_with_weights:
a = 2

Networkx: How to remove single direction edges?

Suppose I have edges between [(1,2),(2,1),(1,3)], how can I remove (1,3) since it is not bidrectional like the edge between 1 and 2?
I'm assuming this is a DiGraph. In that case, first find the edges you want to remove. Then remove them.
to_remove = [(v,u) for v,u in G.edges() if not G.has_edge(u,v)]
G.remove_edges_from(to_remove)
The list to_remove has all those edges in G for which G does not have the opposite edge (it is a list comprehension).
This one is a little more lengthy, but don't modify the edges directly
import networkx as nx
# create the graph
G = nx.DiGraph()
G.add_edges_from([(1,2),(2,1),(1,3),(4,1),(1,5),(1,6),(5,1)])
H = nx.difference(G.to_undirected().to_directed(), G) # get the uni-directional edges
G = nx.difference(G, H.to_undirected()) # get the difference graph
G.edges()
# [(1, 2), (1, 5), (2, 1), (5, 1)]

Is there a way to run pagerank algorithm on NetworkX's MultiGraph?

I'm working on a graph with multiple edges between the same nodes (edges are having different values). In order to model this graph I need to use MultiGraph instead of normal Graph. Unfortunately, it's not possible to run PageRank algo on it.
Any workarounds known ?
NetworkXNotImplemented: not implemented for multigraph type
You could create make a graph without parallel edges and then run pagerank.
Here is an example of summing edge weights of parallel edges to make a simple graph:
import networkx as nx
G = nx.MultiGraph()
G.add_edge(1,2,weight=7)
G.add_edge(1,2,weight=10)
G.add_edge(2,3,weight=9)
# make new graph with sum of weights on each edge
H = nx.Graph()
for u,v,d in G.edges(data=True):
w = d['weight']
if H.has_edge(u,v):
H[u][v]['weight'] += w
else:
H.add_edge(u,v,weight=w)
print H.edges(data=True)
#[(1, 2, {'weight': 17}), (2, 3, {'weight': 9})]
print nx.pagerank(H)
#{1: 0.32037465332634, 2: 0.4864858243244209, 3: 0.1931395223492388}
You can still compose a Digraph by combining the edges
while adding their weights.
# combining edges using defaultdict
# input-- combined list of all edges
# ouput-- list of edges with summed weights for duplicate edges
from collections import defaultdict
def combine_edges(combined_edge_list):
ddict = defaultdict(list)
for edge in combined_edge_list:
n1,n2,w = edge
ddict[(n1,n2)].append(w)
for k in ddict.keys():
ddict[k] = sum(ddict[k])
edges = list(zip( ddict.keys(), ddict.values() ) )
return [(n1,n2,w) for (n1,n2),w in edges]

Categories