Crtanje grafova u pythonu

In [1]:
import platform
In [2]:
platform.platform()
Out[2]:
'Linux-4.14.74-1-MANJARO-x86_64-with-arch-Manjaro-Linux'
In [3]:
platform.python_version()
Out[3]:
'3.7.0'
In [4]:
import networkx as nx
In [5]:
nx.__version__
Out[5]:
'2.2'
In [6]:
from pylab import *
In [7]:
%matplotlib inline
In [8]:
import DSTG
In [9]:
from IPython.core.display import Image

Neke opcije kod crtanja grafova

In [10]:
papus=nx.pappus_graph()
In [11]:
DSTG.ispis(papus.edges(),80)
[(0, 1), (0, 17), (0, 5), (1, 2), (1, 8), (2, 3), (2, 13), (3, 4), (3, 10), (4, 
5), (4, 15), (5, 6), (6, 7), (6, 11), (7, 8), (7, 14), (8, 9), (9, 10), (9, 16),
 (10, 11), (11, 12), (12, 13), (12, 17), (13, 14), (14, 15), (15, 16), (16, 17)]
In [12]:
figure(figsize=(8,8))
nx.draw(papus,with_labels=True)

node_shape može poprimiti neku od vrijednosti iz skupa {s, o, ^, >, v, <, d, p, h, 8}
Bridovima možemo pridružiti boje iz već neke predefinirane matplotlib palete boja koju navedemo u varijabli edge_cmap, a u edge_color varijabli navedemo numeričke vrijednosti za svaki brid koje će biti pretvorene u odgovarajuću boju.

In [13]:
nx.draw_circular(papus,node_color="yellow",edgecolors='black', node_shape='p',node_size=800,
                 edge_cmap=cm.Dark2, edge_color=range(papus.number_of_edges()),with_labels=True)

Na isti način možemo pridružiti različite boje vrhovima, samo u tom slučaju koristimo varijable cmap i node_color.

In [14]:
nx.draw_circular(papus,node_color=range(papus.number_of_nodes()),node_shape='p',node_size=800,
                 cmap=cm.summer,with_labels=True,edgecolors='black')

Postojeće palete boja u matplolibu

In [15]:
print(colormaps())
['Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', 'BuGn', 'BuGn_r', 'BuPu', 'BuPu_r', 'CMRmap', 'CMRmap_r', 'Dark2', 'Dark2_r', 'GnBu', 'GnBu_r', 'Greens', 'Greens_r', 'Greys', 'Greys_r', 'OrRd', 'OrRd_r', 'Oranges', 'Oranges_r', 'PRGn', 'PRGn_r', 'Paired', 'Paired_r', 'Pastel1', 'Pastel1_r', 'Pastel2', 'Pastel2_r', 'PiYG', 'PiYG_r', 'PuBu', 'PuBuGn', 'PuBuGn_r', 'PuBu_r', 'PuOr', 'PuOr_r', 'PuRd', 'PuRd_r', 'Purples', 'Purples_r', 'RdBu', 'RdBu_r', 'RdGy', 'RdGy_r', 'RdPu', 'RdPu_r', 'RdYlBu', 'RdYlBu_r', 'RdYlGn', 'RdYlGn_r', 'Reds', 'Reds_r', 'Set1', 'Set1_r', 'Set2', 'Set2_r', 'Set3', 'Set3_r', 'Spectral', 'Spectral_r', 'Wistia', 'Wistia_r', 'YlGn', 'YlGnBu', 'YlGnBu_r', 'YlGn_r', 'YlOrBr', 'YlOrBr_r', 'YlOrRd', 'YlOrRd_r', 'afmhot', 'afmhot_r', 'autumn', 'autumn_r', 'binary', 'binary_r', 'bone', 'bone_r', 'brg', 'brg_r', 'bwr', 'bwr_r', 'cividis', 'cividis_r', 'cool', 'cool_r', 'coolwarm', 'coolwarm_r', 'copper', 'copper_r', 'cubehelix', 'cubehelix_r', 'flag', 'flag_r', 'gist_earth', 'gist_earth_r', 'gist_gray', 'gist_gray_r', 'gist_heat', 'gist_heat_r', 'gist_ncar', 'gist_ncar_r', 'gist_rainbow', 'gist_rainbow_r', 'gist_stern', 'gist_stern_r', 'gist_yarg', 'gist_yarg_r', 'gnuplot', 'gnuplot2', 'gnuplot2_r', 'gnuplot_r', 'gray', 'gray_r', 'hot', 'hot_r', 'hsv', 'hsv_r', 'inferno', 'inferno_r', 'jet', 'jet_r', 'magma', 'magma_r', 'nipy_spectral', 'nipy_spectral_r', 'ocean', 'ocean_r', 'pink', 'pink_r', 'plasma', 'plasma_r', 'prism', 'prism_r', 'rainbow', 'rainbow_r', 'seismic', 'seismic_r', 'spring', 'spring_r', 'summer', 'summer_r', 'tab10', 'tab10_r', 'tab20', 'tab20_r', 'tab20b', 'tab20b_r', 'tab20c', 'tab20c_r', 'terrain', 'terrain_r', 'viridis', 'viridis_r', 'winter', 'winter_r']

Još neke opcije

In [16]:
figure(figsize=(6,6),facecolor='w')
G=nx.house_graph()
pozicije={0:(0,0),1:(1,0),2:(0,1),3:(1,1),4:(0.5,2.0)}
axis('off')
margins(x=0.1,y=0.2)
nx.draw_networkx_nodes(G,pos=pozicije,node_size=2000,nodelist=[4])
nx.draw_networkx_nodes(G,pos=pozicije,node_size=3000,nodelist=[0,1,2,3],node_color='b')
nx.draw_networkx_edges(G,pos=pozicije,alpha=0.5,width=6,edgelist=[(0,2),(0,1)])
nx.draw_networkx_edges(G,pos=pozicije,width=4,style='dashed',edgelist=[(2,4),(3,4)],edge_color='r')
nx.draw_networkx_edges(G,pos=pozicije,width=3,style='dotted',edgelist=[(2,3)],edge_color='k')
nx.draw_networkx_edges(G,pos=pozicije,width=2,style='dashdot',edgelist=[(1,3)],edge_color='k')
nx.draw_networkx_edge_labels(G,pos=pozicije,edge_labels={(0,1):'prvi',(2,3):'drugi'},label_pos=0.7,font_size=16,font_color='r')
nx.draw_networkx_labels(G,pos=pozicije,font_size=16,font_color='w',font_weight='bold');
In [17]:
nx.draw(G,pos=pozicije,node_size=[500,600,900,1200,1600],node_color=['r','r','#F0E68C','b','b'],width=6,with_labels=True)

Automatsko računanje pozicija vrhova za neke standardne rasporede vrhova

In [18]:
nx.layout.circular_layout(G)
Out[18]:
{0: array([1.00000000e+00, 2.38418583e-08]),
 1: array([0.30901696, 0.95105658]),
 2: array([-0.80901709,  0.58778522]),
 3: array([-0.80901698, -0.58778535]),
 4: array([ 0.30901711, -0.95105647])}
In [19]:
nx.circular_layout(G)
Out[19]:
{0: array([1.00000000e+00, 2.38418583e-08]),
 1: array([0.30901696, 0.95105658]),
 2: array([-0.80901709,  0.58778522]),
 3: array([-0.80901698, -0.58778535]),
 4: array([ 0.30901711, -0.95105647])}
In [20]:
nx.shell_layout(G)
Out[20]:
{0: array([1.00000000e+00, 2.38418583e-08]),
 1: array([0.30901697, 0.95105654]),
 2: array([-0.80901706,  0.58778518]),
 3: array([-0.80901694, -0.58778536]),
 4: array([ 0.30901712, -0.95105648])}
In [21]:
nx.layout.shell_layout(G)
Out[21]:
{0: array([1.00000000e+00, 2.38418583e-08]),
 1: array([0.30901697, 0.95105654]),
 2: array([-0.80901706,  0.58778518]),
 3: array([-0.80901694, -0.58778536]),
 4: array([ 0.30901712, -0.95105648])}
In [22]:
nx.layout.spring_layout(G)
Out[22]:
{0: array([-0.5232587 ,  0.64460879]),
 1: array([0.06862117, 0.77136187]),
 2: array([ 0.30935333, -0.10152558]),
 3: array([-0.19880101, -0.31444508]),
 4: array([ 0.34408521, -1.        ])}
In [23]:
nx.spring_layout(G)
Out[23]:
{0: array([0.62081607, 0.62364475]),
 1: array([ 0.84444327, -0.24663331]),
 2: array([-0.33587609,  0.34183208]),
 3: array([-0.12938325, -0.46163951]),
 4: array([-1.      , -0.257204])}

Grafovi s puno vrhova i bridova

In [24]:
geom=nx.random_geometric_graph(200,0.14)
In [25]:
geom.number_of_nodes(),geom.number_of_edges()
Out[25]:
(200, 1070)
In [26]:
nx.draw(geom,node_size=20)
In [27]:
gr=nx.Graph() 
bridovi=[(randint(k+1),k+2) for k in range(1000)] 
gr.add_edges_from(bridovi) 
nx.draw(gr,node_size=10)
In [28]:
gr.number_of_nodes(),gr.number_of_edges()
Out[28]:
(1002, 1000)
In [29]:
nx.draw_circular(gr,node_size=10,node_color="yellow",edgecolors='black')

Radijalni raspored vrhova

In [30]:
gr2=nx.balanced_tree(5,3) 
pos2=nx.nx_agraph.graphviz_layout(gr2,prog='twopi',args='')
figure(figsize=(7,7))
nx.draw(gr2,pos2,node_size=20,node_color="yellow",edgecolors='black')
In [31]:
gr3=nx.balanced_tree(3,4) 
pos3=nx.nx_agraph.graphviz_layout(gr3,prog='twopi',args='')
figure(figsize=(5,5))
nx.draw(gr3,pos3,node_size=20,node_color="yellow",edgecolors='black') 

GraphViz

In [32]:
graf1=nx.MultiGraph({0:[1,2,3],1:[1,1,1],2:[2,4,4],3:[4,4,4]})

Ne crtaju se petlje i višestruki bridovi

In [33]:
figure(figsize=(5,3))
nx.draw(graf1,with_labels=True)

Pretvorimo u GraphViz format

In [34]:
graf1_viz=nx.nx_agraph.to_agraph(graf1)

Zapis grafa u GraphViz formatu

In [35]:
print(graf1_viz.string())
graph "" {
	0 -- 1 [key=0];
0 -- 2 [key=0];
0 -- 3 [key=0];
1 -- 1 [key=0];
1 -- 1 [key=1];
1 -- 1 [key=2];
2 -- 2 [key=0];
2 -- 4 [key=0];
2 -- 4 [key=1];
3 -- 4 [key=0];
3 -- 4 [key=1];
3 -- 4 [key=2];
}

Sada je sve nacrtano kako treba

In [36]:
graf1_viz.draw('graf1.png',prog='dot')
In [37]:
Image(filename='graf1.png')
Out[37]:

Još neke opcije za razmještaj vrhova u GraphVizu

In [38]:
graf1_viz.draw('graf2.png',prog='circo')
Image(filename='graf2.png')
Out[38]:
In [39]:
graf1_viz.draw('graf3.png',prog='neato')
Image(filename='graf3.png')
Out[39]:
In [40]:
graf1_viz.draw('graf4.png',prog='fdp')
Image(filename='graf4.png')
Out[40]:
In [41]:
graf1_viz.graph_attr['splines']='true'
graf1_viz.draw('graf5.png',prog='twopi')
Image(filename='graf5.png')
Out[41]:

možemo direktno konstruirati graf u PyGraphViz-u umjesto da ga stvaramo u NetworkX-u pa onda pretvaramo u PyGraphViz format

In [42]:
import pygraphviz as pyviz
In [43]:
graf1_pyviz=pyviz.AGraph({0:[1,2,3],1:[1,1,1],2:[2,4,4],3:[4,4,4]},strict=False)
In [44]:
graf1_pyviz.draw('graf_pyviz.png',prog='dot')
Image(filename='graf_pyviz.png')
Out[44]:

možemo sami konstruirati direktno GraphViz graf dodajući još mnoge druge opcije (naravno, treba proučiti dot jezik; na webu postoje helpovi o tome)

sadržaj datoteke s2.dot

In [45]:
f=open('s2.dot','r')
s2=f.read()
f.close()
In [46]:
print(s2)
graph G {
size="5,4!";
node [width=0.1, height=0.1, shape=circle, fontsize=10, fontcolor=red, fillcolor=yellow, style=filled, margin=0];
0 [label="root"];
0 -- 1;
0 -- 2;
0 -- 3;
1 -- 1;
1 -- 1;
1 -- 1;
2 -- 2;
2 -- 4;
2 -- 4;
3 -- 4;
3 -- 4;
3 -- 4;
}

Kreiranje grafa učitavanjem podataka iz datoteke s2.dot

In [47]:
graf2=pyviz.AGraph('s2.dot')
In [48]:
graf2.draw('graf6.png',prog='dot')
Image(filename='graf6.png')
Out[48]:
In [49]:
graf2.draw('graf7.png',prog='fdp')
Image(filename='graf7.png')
Out[49]:

možemo i lokalno urediti svaki pojedini vrh ukoliko želimo da izgleda drukčije od ostalih vrhova

In [50]:
f=open('s3.dot','r')
s3=f.read()
f.close()
In [51]:
print(s3)
graph H {
size="5,4!";
node [width=0.3, height=0.3, fontsize=10, fontcolor=red, fillcolor=yellow, style=filled, margin=0];
0 [label="root"];
1 [label="1",shape=hexagon, margin=0.2, fontsize=18];
2 [label="2", fillcolor=mintcream, fontcolor=purple];
3 [label="3",shape=house];
4 [label="4", shape=doublecircle];

"0" -- "1";
"0" -- "2";
"0" -- "3";
"1" -- "1";
"1" -- "1";
"1" -- "1";
"2" -- "2";
"2" -- "4";
"2" -- "4";
"3" -- "4";
"3" -- "4";
"3" -- "4";
}

In [52]:
graf3=pyviz.AGraph('s3.dot')
In [53]:
graf3.draw('graf8.png',prog='dot')
Image(filename='graf8.png')
Out[53]:
In [54]:
graf3.draw('graf9.png',prog='circo')
Image(filename='graf9.png')
Out[54]:

neke opcije za prikaz bridova

In [55]:
f=open('s4.dot','r')
s4=f.read()
f.close()
In [56]:
print(s4)
graph {
size="4,5!";
node [width=0.3, height=0.3, fontsize=10, fontcolor=red, fillcolor=yellow, style=filled, margin=0];
edge [color=blue, style=bold, tailport=s, headport=n];
"0" [label="root"];
"1" [label="1", shape=hexagon, margin=0.2, fontsize=18];
"2" [label="2", fillcolor=mintcream, fontcolor=purple];
"3" [label="3", shape=house];
"4" [label="4", shape=doublecircle];

"0" -- "1";
"0" -- "2";
"0" -- "3";
"1" -- "1";
"1" -- "1";
"1" -- "1";
"2" -- "2";
"2" -- "4";
"2" -- "4";
"3" -- "4";
"3" -- "4";
"3" -- "4";
} 

In [57]:
graf4=pyviz.AGraph('s4.dot')
In [58]:
graf4.draw('graf10.png',prog='dot')
Image(filename='graf10.png')
Out[58]:
In [59]:
f=open('s5.dot','r')
s5=f.read()
f.close()
In [60]:
print(s5)
graph {
size="4,5!";
node [width=0.3, height=0.3, fontsize=10, fontcolor=red, fillcolor=yellow, style=filled, margin=0];
edge [color=blue, fontsize=10];
"0" [label="root"];
"1" [label="1", shape=hexagon, margin=0.2, fontsize=18];
"2" [label="2", fillcolor=mintcream, fontcolor=purple];
"3" [label="3", shape=house];
"4" [label="4", shape=doublecircle];

"0" -- "1" [taillabel="tri", labelangle=10, labelfontcolor=red, labeldistance=4];
"0" -- "2" [label="dva", decorate=true];
"0" -- "3";
"1" -- "1";
"1" -- "1" [style=dotted];
"1" -- "1";
"2" -- "2";
"2" -- "4";
"2" -- "4";
"3" -- "4";
"3" -- "4";
"3" -- "4";
}

In [61]:
graf5=pyviz.AGraph('s5.dot')
In [62]:
graf5.draw('graf11.png',prog='dot')
Image(filename='graf11.png')
Out[62]:

Digraf

In [63]:
digraf1=nx.MultiDiGraph({0:[1,2,3],1:[1,1,1],2:[2,4,4],3:[4,4,4]})

NetworkX ne daje na slici petlje, niti višestruke lukove

In [64]:
nx.draw(digraf1,with_labels=True)

Pretvorimo u GraphViz format

In [65]:
d1=nx.nx_agraph.to_agraph(digraf1)
In [66]:
print(d1.string())
digraph "" {
	0 -> 1 [key=0];
0 -> 2 [key=0];
0 -> 3 [key=0];
1 -> 1 [key=0];
1 -> 1 [key=1];
1 -> 1 [key=2];
2 -> 2 [key=0];
2 -> 4 [key=0];
2 -> 4 [key=1];
3 -> 4 [key=0];
3 -> 4 [key=1];
3 -> 4 [key=2];
}

GraphViz nacrta lijepu sliku na kojoj je sve jasno vidljivo

In [67]:
d1.draw('digraf1.png',prog='dot')
Image(filename='digraf1.png')
Out[67]:

možemo odmah direktno u PyGraphViz-u kreirati graf

In [68]:
digraf_pyviz=pyviz.AGraph({0:[1,2,3],1:[1,1,1],2:[2,4,4],3:[4,4,4]},strict=False,directed=True)
In [69]:
digraf_pyviz.draw('digraf_pyviz.png',prog='dot')
Image(filename='digraf_pyviz.png')
Out[69]:

Možemo sami direktno u GraphVizu kreirati digraf s raznim dodatnim opcijama

In [70]:
f=open('s6.dot','r')
s6=f.read()
f.close()
In [71]:
print(s6)
digraph {
size="6,4!";
edge [color=blue, fontsize=10, arrowhead=vee];

0 -> 1;
0 -> 2;
0 -> 3 [dir=both, arrowhead=odiamond, arrowtail=dot, color=red];
1 -> 1;
1 -> 1;
1 -> 1;
2 -> 2;
2 -> 4 [headlabel="2->4", labelangle=20, labeldistance=4];
2 -> 4;
3 -> 4;
3 -> 4;
3 -> 4;
}

In [72]:
d2=pyviz.AGraph('s6.dot')
In [73]:
d2.draw('digraf2.png',prog='dot')
Image(filename='digraf2.png')
Out[73]:
In [74]:
f=open('s7.dot','r')
s7=f.read()
f.close()
In [75]:
print(s7)
digraph {
size="6,5!"; 
node [style=filled, fillcolor=yellow]; 
edge [color=blue, fontsize=10, arrowhead=vee];

0 -> 1 [weight=2];
0 -> 2;
0 -> 3 [dir=both, arrowhead=odiamond, arrowtail=dot,color=red];
1 -> 1;
1 -> 1;
1 -> 1;
2 -> 2;
2 -> 4 [headlabel="2->4", labelangle=35, labeldistance=5];
2 -> 4;
3 -> 4;
3 -> 4;
3 -> 4;
}

In [76]:
d3=pyviz.AGraph('s7.dot')
In [77]:
d3.draw('digraf3.png',prog='circo')
Image(filename='digraf3.png')
Out[77]:

Petersenov graf

In [78]:
P=nx.petersen_graph()
In [79]:
figure(figsize=(6,6))
poz=nx.shell_layout(P,[[5,6,7,8,9],[0,1,2,3,4]])
nx.draw(P,pos=poz,with_labels=True)
In [80]:
Pviz=nx.nx_agraph.to_agraph(P)
In [81]:
print(Pviz.string())
strict graph "Petersen Graph" {
	graph [name="Petersen Graph"];
	0 -- 1;
	0 -- 4;
	0 -- 5;
	1 -- 2;
	1 -- 6;
	4 -- 9;
	5 -- 7;
	5 -- 8;
	2 -- 3;
	2 -- 7;
	6 -- 8;
	6 -- 9;
	3 -- 4;
	3 -- 8;
	7 -- 9;
}

In [82]:
Pviz.draw('petersen1.png',prog='dot')
Image(filename='petersen1.png')
Out[82]:
In [83]:
f=open('p2.dot','r')
p2=f.read()
f.close()
In [84]:
print(p2)
graph {
1 [shape=box,style=filled]; 

0 -- 1;
0 -- 4;
0 -- 5;
1 -- 2;
1 -- 6;
2 -- 3;
2 -- 7;
3 -- 4;
3 -- 8;
4 -- 9;
5 -- 7;
5 -- 8;
6 -- 8;
6 -- 9;
7 -- 9;
{rank=same; 0 1 2 3 4};
{rank=same; 5 6 7 8 9};
}

In [85]:
petersen2=pyviz.AGraph('p2.dot')
In [86]:
petersen2.draw('petersen2.png',prog='dot')
Image(filename='petersen2.png')
Out[86]:
In [87]:
petersen2.draw('petersen2dva.png',prog='circo')
Image(filename='petersen2dva.png')
Out[87]:
In [88]:
petersen2.draw('petersen2tri.png',prog='twopi')
Image(filename='petersen2tri.png')
Out[88]:
In [89]:
f=open('p3.dot','r')
p3=f.read()
f.close()
In [90]:
print(p3)
graph {
splines=true;
node [width=0.3, height=0.2, margin=0];
1 [shape=box, style=filled];

0 -- 1;
0 -- 4;
0 -- 5;
1 -- 2;
1 -- 6;
2 -- 3;
2 -- 7;
3 -- 4;
3 -- 8;
4 -- 9;
5 -- 7;
5 -- 8;
6 -- 8;
6 -- 9;
7 -- 9;
} 

In [91]:
petersen3=pyviz.AGraph('p3.dot')
In [92]:
petersen3.draw('petersen3.png',prog='twopi')
Image(filename='petersen3.png')
Out[92]:

Grafovi s puno vrhova i bridova u GraphViz-u

NetworkX nije namijenjen za crtanje grafova s puno vrhova i bridova

In [93]:
H=nx.gnm_random_graph(200,500)
In [94]:
figure(figsize=(7,7))
nx.draw(H,node_size=20)
In [95]:
nx.draw_circular(H,node_size=20)

GraphViz je specijaliziran za crtanje grafova pa se stoga dobro snalazi u crtanju grafova s puno vrhova i bridova. Jasno, ako želimo preglednu sliku, slika mora biti većih dimenzija.

In [96]:
H1=nx.nx_agraph.to_agraph(H)
In [97]:
H1.draw('veliki.png',prog='dot')
In [98]:
Image(filename='veliki.png')
Out[98]:

radijalni raspored vrhova ipak nije u ovom slučaju najbolje rješenje

In [99]:
H1.draw('veliki2.png',prog='twopi')
In [100]:
Image(filename='veliki2.png')
Out[100]:

Opcija 'fdp' također ne daju lijepu sliku

In [101]:
H1.draw('veliki3.png',prog='fdp')
In [102]:
Image(filename='veliki3.png')
Out[102]:

Uz dodatnu opciju splines=True, dobit ćemo lijepu sliku. Budite strpljivi jer u ovom slučaju crtanje slike može malo dulje potrajati.

In [103]:
H1.graph_attr['splines']='true'
H1.draw('veliki4.png',prog='fdp')
In [104]:
Image(filename='veliki4.png')
Out[104]:
In [ ]: