how to draw multigraph in networkx using matplotlib or graphviz - python

when I pass multigraph numpy adjacency matrix to networkx (using from_numpy_matrix function)
and then try to draw the graph using matplotlib, it ignores the multiple edges.
how can I make it draw multiple edges as well ?

Graphviz does a good job drawing parallel edges. You can use that with NetworkX by writing a dot file and then processing with Graphviz (e.g. neato layout below). You'll need pydot or pygraphviz in addition to NetworkX
In [1]: import networkx as nx
In [2]: G=nx.MultiGraph()
In [3]: G.add_edge(1,2)
In [4]: G.add_edge(1,2)
In [5]: nx.write_dot(G,'multi.dot')
In [6]: !neato -T png multi.dot > multi.png
On NetworkX 1.11 and newer, nx.write_dot doesn't work as per issue on networkx github. The workaround is to call write_dot using
from networkx.drawing.nx_pydot import write_dot
or
from networkx.drawing.nx_agraph import write_dot

You can use matplotlib directly using the node positions you calculate.
G=nx.MultiGraph ([(1,2),(1,2),(1,2),(3,1),(3,2)])
pos = nx.random_layout(G)
nx.draw_networkx_nodes(G, pos, node_color = 'r', node_size = 100, alpha = 1)
ax = plt.gca()
for e in G.edges:
ax.annotate("",
xy=pos[e[0]], xycoords='data',
xytext=pos[e[1]], textcoords='data',
arrowprops=dict(arrowstyle="->", color="0.5",
shrinkA=5, shrinkB=5,
patchA=None, patchB=None,
connectionstyle="arc3,rad=rrr".replace('rrr',str(0.3*e[2])
),
),
)
plt.axis('off')
plt.show()

You can use pyvis package.
I just copy-paste this code from my actual project in Jupyter notebook.
from pyvis import network as pvnet
def plot_g_pyviz(G, name='out.html', height='300px', width='500px'):
g = G.copy() # some attributes added to nodes
net = pvnet.Network(notebook=True, directed=True, height=height, width=width)
opts = '''
var options = {
"physics": {
"forceAtlas2Based": {
"gravitationalConstant": -100,
"centralGravity": 0.11,
"springLength": 100,
"springConstant": 0.09,
"avoidOverlap": 1
},
"minVelocity": 0.75,
"solver": "forceAtlas2Based",
"timestep": 0.22
}
}
'''
net.set_options(opts)
# uncomment this to play with layout
# net.show_buttons(filter_=['physics'])
net.from_nx(g)
return net.show(name)
G = nx.MultiDiGraph()
[G.add_node(n) for n in range(5)]
G.add_edge(0, 1, label=1)
G.add_edge(0, 1, label=11)
G.add_edge(0, 2, label=2)
G.add_edge(0, 3, label=3)
G.add_edge(3, 4, label=34)
plot_g_pyviz(G)
result

Refer to atomh33ls's answer
import numpy as np
import pandas as pd
import networkx as nx
import matplotlib.pyplot as plt
import random as rd
column_from = 'from_here'
column_to = 'to_there'
column_attribute = 'edges_count'
# test data
pdf = pd.DataFrame([
['a', 'b', 3],
['b', 'a', 1],
['a', 'c', 1],
['b', 'c', 1],
['a', 'd', 1],
['e', 'b', 2],
['c', 'f', 1],
['f', 'g', 1]],
columns=[column_from, column_to, column_attribute])
with pd.option_context('display.max_rows', None, 'display.max_columns', None): # more options can be specified also
print(pdf)
def get_adjacency_matrix(pdf):
id_set = set(pdf[column_from].drop_duplicates().values.tolist() +
pdf[column_to].drop_duplicates().values.tolist())
id_dict_kv = {k : v for k, v in enumerate(id_set)}
id_dict_vk = {v : k for k, v in id_dict_kv.items()}
count = len(id_set)
adjacency_matrix = np.zeros([count, count], dtype='int32')
for row in pdf.itertuples():
index_from = id_dict_vk[getattr(row, column_from)]
index_to = id_dict_vk[getattr(row, column_to)]
adjacency_matrix[index_from, index_to] += getattr(row, column_attribute)
label_mapping = id_dict_kv
return adjacency_matrix, label_mapping
def pdf_to_MDG(pdf):
adjacency_matrix, label_mapping = get_adjacency_matrix(pdf)
G = nx.from_numpy_matrix(adjacency_matrix, parallel_edges=True, create_using=nx.MultiDiGraph())
G = nx.relabel_nodes(G, label_mapping)
return G
MDG = pdf_to_MDG(pdf)
edges_data = MDG.edges.data(column_weight)
print(edges_data)
#—————————————————————————————just see the below: draw MultiDiGraph—————————————————————————————————
pos = nx.spring_layout(MDG, seed = 1)
nx.draw(MDG, pos, with_labels=True, edge_color = (1,1,1))
for e in MDG.edges:
plt.gca().annotate("",
xy=pos[e[1]],
xycoords='data',
xytext=pos[e[0]],
textcoords='data',
arrowprops=dict(arrowstyle="->", color="0",
shrinkA=15, shrinkB=15,
patchA=None, patchB=None,
connectionstyle="arc3,rad=rrr".replace('rrr',str(rd.random()*0.5+0.1)))
)
plt.axis('off')
plt.show()
output:
from_here to_there edges_count
0 a b 3
1 b a 1
2 a c 1
3 b c 1
4 a d 1
5 e b 2
6 c f 1
7 f g 1
[('c', 'f', 1), ('e', 'b', 1), ('e', 'b', 1), ('b', 'c', 1), ('b', 'a', 1), ('f', 'g', 1), ('a', 'c', 1), ('a', 'd', 1), ('a', 'b', 1), ('a', 'b', 1), ('a', 'b', 1)]
the output img

Related

Unwanted edge displayed in the network which shouldn't exist in principle in hvplot.networkx

I'm using this code to make a network and have edges shown differently based on an edge attribute, in this case 'Polarity'. But the result I get has extra edge(s) which are not specified. Please have a look. I can't figure it out.
import networkx as nx
import hvplot.networkx as hvnx
G = nx.Graph()
G.add_edge('a', 'b', Polarity="positive")
G.add_edge('a', 'c', Polarity="positive")
G.add_edge('c', 'd', Polarity="positive")
G.add_edge('c', 'e', Polarity="negative")
G.add_edge('c', 'f', Polarity="negative")
G.add_edge('a', 'd', Polarity="negative")
elarge = [(u, v) for (u, v, attr) in G.edges(data=True) if attr['Polarity'] == "positive"]
esmall = [(u, v) for (u, v, attr) in G.edges(data=True) if attr['Polarity'] == "negative"]
pos = nx.spring_layout(G) # positions for all nodes
# nodes
nodes = hvnx.draw_networkx_nodes(G, pos, node_size=700)
# edges
edges1 = hvnx.draw_networkx_edges(
G, pos, edgelist=elarge, edge_width=6)
edges2 = hvnx.draw_networkx_edges(
G, pos, edgelist=esmall, edge_width=6, alpha=0.5, edge_color='blue', style='dashed')
labels = hvnx.draw_networkx_labels(G, pos, font_size=20, font_family='sans-serif')
edges2 * nodes * labels
Why do I get additional edge(s) 'c','d' in this case? Thanks.

Add a custom color map to legend using matplotlib in python

I want to create a custom color map (between two greens[0,150,0] to [0,250,0]) and then use it in the legend in the plot. This is an image plot using RGBA.
Code to create the color map is as follows:
from matplotlib.colors import ListedColormap
N = 256
vals = np.ones((N, 4))
vals[:, 0] = np.linspace(0/256, 0, N)
vals[:, 1] = np.linspace(250/256, 150/255, N)
vals[:, 2] = np.linspace(0/256, 0, N)
newcmp = ListedColormap(vals)
Code to create the legend is as follows:
cmap = {1: [155/255, 118/255, 83/255, 1],
2: [168/255, 210/255, 151/255, 1],
3: newcmp,
4: [175/255, 206/255, 208/255, 1],
5: [249/255, 231/255, 157/255, 1],
6: [209/255, 217/255, 208/255, 1],
7: [225/255, 166/255, 49/255, 1],
8: [128/255, 128/255, 0, 1],
9: [120/255, 89/255, 86/255, 1],
10: [60/255, 45/255, 21/255, 1],
11: [230/255, 167/255, 125/255, 1],
12: [170/255, 157/255, 132/255, 1],
13: [84/255, 96/255, 76/255, 1]
}
labels = {1: 'A',
2: 'B',
3: 'C',
4: 'D',
5: 'E',
6: 'F',
7: 'G',
8: 'H',
9: 'I',
10: 'J',
11: 'K',
12: 'L',
13: 'M'
}
patches = [mpatches.Patch(color=cmap[i], label=labels[i]) for i in cmap]
plt.legend(handles=patches, loc='center left', bbox_to_anchor=(1, 0.5))
But since the objects are of 2 different types (one is Patch and the other a ListedColormap.
How should i go about this to make it happen?
I understand your approach with Patch - that was my original approach too (I never got it to work). Instead, you can use "tuple legend handler" to create a colorbar in the legend. Following the answer in Display matplotlib legend element as 2D line of colormap, here is how you can go about it:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib.legend_handler import HandlerTuple
if __name__ == "__main__":
# Custom colormap
N = 256
vals = np.ones((N, 4))
vals[:, 0] = np.linspace(0/256, 0, N)
vals[:, 1] = np.linspace(250/256, 150/255, N)
vals[:, 2] = np.linspace(0/256, 0, N)
newcmp = ListedColormap(vals)
# Start figure
fig, ax = plt.subplots()
lines = [] # list of lines to be used for the legend
for i in range(5):
# Create 5 sine waves
t = 20
fs = 10 + i
samples = np.linspace(0, t, int(fs*t), endpoint=False)
wave = np.sin(samples)
# Plot - the i * 30 + 50 is to get different colors in the cmap that are far away from each other,
# otherwise all greens look the same
line, = ax.plot(wave, color=newcmp(i * 40 + 50))
lines.append(line)
ax.legend(handles=[tuple(lines)],
labels=['Radius'],
handlelength=5, handler_map={tuple: HandlerTuple(ndivide=None, pad=0)})
plt.show()
Cheers!

How to generate back to back chart in D3?

I am using Matplotlib for a back to back plot:
import numpy as np
import matplotlib.pyplot as plt
# create data
A = np.array([3,6,9,4,2,5])
B = np.array([2,8,1,9,7,3])
X = np.arange(6)
# plot the bars
plt.barh(X, A, align='center',
alpha=0.9, color = 'y')
plt.barh(X, -B, align='center',
alpha=0.6, color = 'c')
plt.yticks([0, 1, 2,3,4,5], ['A', 'B', 'C', 'D', 'E', 'F'])
plt.xticks([], [])
plt.show()
I am wondering how to generate d3 code using python?
Is Altair the right option?
Here is an example of generating a similar chart with Altair:
import pandas as pd
import altair as alt
df = pd.DataFrame({
"A": np.array([3,6,9,4,2,5]),
"B": np.array([2,8,1,9,7,3]),
"X": ['A', 'B', 'C', 'D', 'E', 'F'],
})
alt.Chart(df).transform_calculate(
A=-alt.datum.A
).transform_fold(
["A", "B"], as_=["key", "value"]
).mark_bar().encode(
x=alt.X("value:Q", axis=None),
y='X:N',
color="key:N"
).properties(
width=300,
height=200
)

Missing labels in matplotlib pie chart

I try to plot a pie chart using Python 3 Matplotlib v2.2.2-4build1 on Ubuntu 18.10. Everything seems to be ok except labels - they are missing. Tried to add it according to official documentation (https://matplotlib.org/api/_as_gen/matplotlib.pyplot.pie.html), tried to use an example from the web (https://pythonspot.com/matplotlib-pie-chart/) - same result, no labels.
Here is a simplified version of my code:
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
sum = sum(values)
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sum))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, labels=labels, textprops=dict(color="w"))
plt.show()
Here is what I see - no labels:
Tried to use a tuple instead of a list - same thing.
Could anybody help me?
You might want to make the color of your labels non-white on a white background :)
Also using sum as a variable name overwrites the function, so your're better off choosing something else.
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
sumT = sum(values)
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sumT))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, labels=labels, textprops=dict(color="k"))
plt.show()
Or if you want the labels to be inside:
import numpy as np
import matplotlib.pyplot as plt
import sys
def func(pct, allvals):
absolute = int(pct/100.*sum(allvals))
return "{:.1f}%)".format(pct)
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
sumT = sum(values)
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sumT))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, autopct=lambda pct: func(pct,
values), textprops=dict(color="w"))
plt.show()
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
colors=['yellow','blue','red','pink','green']
plt.pie(values,labels=headers,
colors=colors,autopct='%1.2f%%',
shadow=True,startangle=90)
plt.title('pie chart')
plt.show()
Adding the plt.legend() statement before the plt.show() will do the job.
import numpy as np
import matplotlib.pyplot as plt
import sys
headers = ['a', 'b', 'c', 'd', 'e']
values = [5, 4, 3, 2, 1]
labels = []
for v in values:
labels.append('{:.1f}%'.format(100 * v / sum))
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(values, labels=labels, textprops=dict(color="w"))
plt.legend()
plt.show()

NetworkX: Drawing Graph

I am trying to draw a graph using NetworkX in Python. I am using the following code:
G=nx.to_networkx_graph(adj)
pos=nx.spring_layout(G)
#G=nx.path_graph(8)
nx.draw(G,pos,labels)
plt.savefig("simple_path.png") # save as png
plt.show() # display
I get this output:
But I want to get the following output with Labels:
What can I do on the code? thank you.
So for the positioning, you've set pos based on spring_layout. pos gives the positions of your nodes. Check it out - once you've defined it, ask python to print pos for you and see what it's doing.
Here's an alternative code:
import networkx as nx
import pylab as py
blue_edges = [('B', 'C'), ('B', 'D'), ('B', 'E'), ('E', 'F')]
red_edges = [('A', 'B'), ('A', 'C'), ('C', 'E'), ('D', 'E'), ('D', 'F')]
G=nx.Graph() #define G
G.add_edges_from(blue_edges)
G.add_edges_from(red_edges)
pos = {'A':(0,0), 'B':(1,1), 'C':(1,-1), 'D':(2,1), 'E':(2,-1), 'F':(3,0)}
nx.draw_networkx(G, pos=pos, edgelist = [], node_color = 'k')
nx.draw_networkx(G, pos=pos, nodelist = [], edgelist = blue_edges, edge_color = 'b')
nx.draw_networkx(G, pos=pos, nodelist = [], edgelist = red_edges, edge_color = 'r')
and if you want it without the x and y axes showing, change the last bit to:
nx.draw(G, pos=pos, edgelist = [], node_color = 'k')
nx.draw(G, pos=pos, nodelist = [], edgelist = blue_edges, edge_color = 'b')
nx.draw(G, pos=pos, nodelist = [], edgelist = red_edges, edge_color = 'r')

Categories