How to
draw a graph image?
A simple way to do it is to use prebuilt libraries using
algorithms like the Kamada-Kawai and Fructerman-Reingold layout algorithms
Sample implementation:
From igraph import *
g = Graph()
vertex_labels=[‘A’, ‘B’, ‘C’, ‘D’, ‘E’]
attributes={}
attributes[“label”] = vertex_labels
g.add_vertices(5, **attributes).add_edges([(0, 1),
(1, 2),
(2, 3),
(3, 4),
(4, 0),
(2, 4),
(0, 3),
(4, 1)]
layout = g.layout(“kamada_kawai”)
plot(g, layout=layout)
If we
control spacing ourselves to spread the edges with little or no crossing lines,
we could anneal the costs of overlapping.
# The
following program lays out a graph with little or no crossing lines.
from
PIL import Image, ImageDraw
import
math
import
random
vertex
= ['A','B','C','D','E']
links=[('A',
'B'),
('B', 'C'),
('C',
'D'),
('D',
'E'),
('E',
'A'),
('C',
'E'),
('A',
'D'),
('E',
'B')]
domain=[(10,370)]*(len(vertex)*2)
def
randomoptimize(domain,costf):
best=999999999
bestr=None
for
i in range(1000):
#
Create a random solution
r=[random.randint(domain[i][0],domain[i][1])
for i in range(len(domain))]
#
Get the cost
cost=costf(r)
#
Compare it to the best one so far
if
cost<best:
best=cost
bestr=r
return
r
def
annealingoptimize(domain,costf,T=10000.0,cool=0.95,step=1):
#
Initialize the values randomly
vec=[float(random.randint(domain[i][0],domain[i][1]))
for
i in range(len(domain))]
while
T>0.1:
#
Choose one of the indices
i=random.randint(0,len(domain)-1)
#
Choose a direction to change it
dir=random.randint(-step,step)
#
Create a new list with one of the values changed
vecb=vec[:]
vecb[i]+=dir
if
vecb[i]<domain[i][0]: vecb[i]=domain[i][0]
elif
vecb[i]>domain[i][1]: vecb[i]=domain[i][1]
#
Calculate the current cost and the new cost
ea=costf(vec)
eb=costf(vecb)
p=pow(math.e,(-eb-ea)/T)
#
Is it better, or does it make the probability
#
cutoff?
if
(eb<ea or random.random( )<p):
vec=vecb
#
Decrease the temperature
T=T*cool
return
vec
def
crosscount(v):
#
Convert the number list into a dictionary of person:(x,y)
loc=dict([(vertex[i],(v[i*2],v[i*2+1]))
for i in range(0,len(vertex))])
total=0
#
Loop through every pair of links
for
i in range(len(links)):
for
j in range(i+1,len(links)):
#
Get the locations
(x1,y1),(x2,y2)=loc[links[i][0]],loc[links[i][1]]
(x3,y3),(x4,y4)=loc[links[j][0]],loc[links[j][1]]
den=(y4-y3)*(x2-x1)-(x4-x3)*(y2-y1)
#
den==0 if the lines are parallel
if
den==0: continue
#
Otherwise ua and ub are the fraction of the
#
line where they cross
ua=((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/den
ub=((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/den
#
If the fraction is between 0 and 1 for both lines
#
then they cross each other
if
ua>0 and ua<1 and ub>0 and ub<1:
total+=1
for
i in range(len(vertex)):
for
j in range(i+1,len(vertex)):
#
Get the locations of the two nodes
(x1,y1),(x2,y2)=loc[vertex[i]],loc[vertex[j]]
#
Find the distance between them
dist=math.sqrt(math.pow(x1-x2,2)+math.pow(y1-y2,2))
#
Penalize any nodes closer than 50 pixels
if
dist<50:
total+=(1.0-(dist/50.0))
return
total
def
drawnetwork(loc):
#create
the image
img
= Image.new('RGB', (400,400),(255,255,255))
draw=ImageDraw.Draw(img)
#create
the position dict
pos=dict([(vertex[i],(loc[i*2],loc[i*2+1]))
for i in range(0, len(vertex))])
#Draw
Links
for
(a,b) in links:
draw.line((pos[a],pos[b]),
fill=(255,0,0))
#Draw
vertex
for
(n,p) in pos.items():
draw.text(p,n,(0,0,0))
img.save('graph.jpg',
'JPEG')
img.show()
sol=randomoptimize(domain,crosscount)
crosscount(sol)
sol=annealingoptimize(domain,crosscount,step=50,cool=0.99)
crosscount(sol)
drawnetwork(sol)
No comments:
Post a Comment