I want to remove an edge attribute from a Graph object in python-igraph. The equivalent igraph R function is cleverly called delete_edge_attr. I haven't been able to find an equivalent function/method in Python... is there one?
If not, is there another way to do it? (I tried a simple g.es['edge_attr']= [] but this didn't work).
Example Code
g = ig.Graph.Tree(10, 2) #Generate random graph
g_betweenness = g.betweenness() #Calculate betweenness for graph
g['betweenness'] = betweenness #Assign betweenness as edge attribute
print(g.attributes())
g['betweenness'] = [] #Attempt to remove betweenness edge attribute (incorrect)
print(g.attributes())
Output
['betweenness']
['betweenness']
Desired output
['betweenness']
[]
You might not be able to set it to an empty array, but your intuition about directly altering the EdgeSequence was already pretty good. You can simply delete the argument with Python's internal del(), I've included a minimal example below:
import igraph
# minimal graph
G = igraph.Graph()
G.add_vertices(2)
G.add_edge(0,1)
# add your property
G.es['betweenness'] = [1]
# print edge attribute (note the difference to your example)
print(G.es.attribute_names())
# output: ['weight']
# delete argument
del(G.es['weight'])
# print again
print(G.es.attribute_names())
# output: []
I am writing a Python script to generate a network graph using graphviz. Some of my nodes represents injection into a network and I am wondering if it is possible to have a Pie-Chart inside some of the nodes.
Python code for a simple two node network is following:
import graphviz as gv
g1 = gv.Graph(format='svg')
g1.node('A')
g1.node('B')
g1.edge('A', 'B')
filename = g1.render(filename='img/g1')
I let the PyGraphViz implementation up to you. But to answer to the core of your question, since graphviz 2.30, you can use the wedged style for nodes, to achieve the desired result. Here is an example in plain dot:
digraph G {
{
node [shape=circle style=wedged fillcolor="red;0.3:green;0.6:orange"]
A
node [style=solid fillcolor="white" ]
B
C
}
B -> A
B -> C
}
The list of colors is expressed as a colon separated list. The value after the **semi-colon* in the weight of the given color. The sum of all weights must be equal to 1.0. See the colorList attribute
I am using the igraph-python's Graph.Read_Ncol function. Below is my code for reading the data.
def loadData(filename):
data = None
data = ig.Graph.Read_Ncol(filename, directed=False)
return data
I am using this dataset from SNAP group: https://snap.stanford.edu/data/ca-GrQc.html
As mentioned the dataset has 14496 edges and 5242 nodes.
However when I do data.summary() on my graph I get the following output.
>>> data.summary()
'IGRAPH UN-- 5242 28980 -- \n+ attr: name (v)'
Even when I am doing data.to_undirected() and trying data.summary() again I am getting the same result as above.
>>> data.to_undirected()
>>> data.summary()
'IGRAPH UN-- 5242 28980 -- \n+ attr: name (v)'
When I am loading the graph using the SNAP library in an undirected fashion then I am getting the correct output.
def loadData(filename):
data = None
data = snap.LoadEdgeList(snap.PUNGraph,filename,0,1)
return data
What am I doing wrong? Or is there an issue with the igraph API?
Most of the edges appear twice in your network, and igraphs adds them as multiple edges. Call simplify() on the graph to remove these multiple edges. http://igraph.org/python/doc/igraph.GraphBase-class.html#simplify
I need to make computations in a python program, and I would prefer to make some of them in R. Is it possible to embed R code in python ?
You should take a look at rpy (link to documentation here).
This allows you to do:
from rpy import *
And then you can use the object called r to do computations just like you would do in R.
Here is an example extracted from the doc:
>>> from rpy import *
>>>
>>> degrees = 4
>>> grid = r.seq(0, 10, length=100)
>>> values = [r.dchisq(x, degrees) for x in grid]
>>> r.par(ann=0)
>>> r.plot(grid, values, type=’lines’)
RPy is your friend for this type of thing.
The scipy, numpy and matplotlib packages all do simular things to R and are very complete, but if you want to mix the languages RPy is the way to go!
from rpy2.robjects import *
def main():
degrees = 4
grid = r.seq(0, 10, length=100)
values = [r.dchisq(x, degrees) for x in grid]
r.par(ann=0)
r.plot(grid, values, type='l')
if __name__ == '__main__':
main()
When I need to do R calculations, I usually write R scripts, and run them from Python using the subprocess module. The reason I chose to do this was because the version of R I had installed (2.16 I think) wasn't compatible with RPy at the time (which wanted 2.14).
So if you already have your R installation "just the way you want it", this may be a better option.
Using rpy2.objects. (Tried and ran some sample R programs)
from rpy2.robjects import r
print(r('''
# Create a vector.
apple <- c('red','green',"yellow")
print(apple)
# Get the class of the vector.
print(class(apple))
##########################
# Create the data for the chart.
v <- c(7,12,28,3,41)
# Give the chart file a name.
png(file = "line_chart.jpg")
# Plot the bar chart.
plot(v,type = "o")
# Save the file.
dev.off()
##########################
# Give the chart file a name.
png(file = "scatterplot_matrices.png")
# Plot the matrices between 4 variables giving 12 plots.
# One variable with 3 others and total 4 variables.
pairs(~wt+mpg+disp+cyl,data = mtcars,
main = "Scatterplot Matrix")
# Save the file.
dev.off()
install.packages("plotly") # Please select a CRAN mirror for use in this session
library(plotly) # to load "plotly"
'''))
I have been working with pygraph on some project. I completed this example, it works fine.
Now, the problem is the following: the graph is drawn in a picture format (gif). What I need is to get the actual coordinates for each node for the graph layout shown on the gif image. How do I do this? I've been trying and trying, but couldn't find solution to this problem. I thought the the problem's solution would be somehow with manipulating one of the two following lines:
gv.layout(gvv,'dot')
gv.render(gvv,'png','europe.png')
Thanks in advance!
You can add the layout information into the graph with :
gv.render(gvv)
and then find out the position of a node getting its attribute pos :
n_france = gv.findnode(gvv, "France")
pos = gv.getv(n_france, "pos")
Then depending on what you want to do, you might need to convert dot coordinates into png image coordinates. You can get useful information from here :
http://www.graphviz.org/faq/#FaqCoordTransformation
It explains in great details the computation from the graph-units to image pixels.
Hope that this is what you are looking for.
I just found a similar solution that works perfectly for my needs
pos = nx.drawing.nx_agraph.graphviz_layout(G, prog='dot', args='-Grankdir=LR')
cheers!
Using pydotplus you can load in and parse a dot/gv file and interrogate the data structure pydotplus produces, but this internal representation seems not to initially possess all the node attributes, like pos, unless they were already in the file.
But you can also call .write_dot() to produce a much more verbose dot file version. If you parse this then the resulting data structure seems to have pos of all the nodes (and even pos for the splines)
Note: maybe best to index the nodes by name not by index because any text with square brackets after it in the verbose file will be parsed as a node, so the node list may have spurious extra elements.
In the following (slightly edited) experiment at the Spyder prompt I have a terse dot file interior.gv (that does not have pos for nodes) which I .graph_from_dot_file(), then .write_dot(). Then .graph_from_dot_file() again on the verbose generated file, and so find the pos as required.
import pydotplus as pdp
interior = pdp.graphviz.graph_from_dot_file('interior.gv')
interior.write_dot('interior2.dot')
Out[210]: True
interior2 = pdp.graphviz.graph_from_dot_file('interior2.dot')
interior2.get_nodes()[3].get_pos()
Out[214]: '"213.74,130"'
Networkx can do this:
import networkx as nx
def setup_europe():
G = nx.Graph()
G.add_edge("Portugal", "Spain")
G.add_edge("Spain","France")
G.add_edge("France","Belgium")
G.add_edge("France","Germany")
G.add_edge("France","Italy")
G.add_edge("Belgium","Netherlands")
G.add_edge("Germany","Belgium")
G.add_edge("Germany","Netherlands")
G.add_edge("England","Wales")
G.add_edge("England","Scotland")
G.add_edge("Scotland","Wales")
G.add_edge("Switzerland","Austria")
G.add_edge("Switzerland","Germany")
G.add_edge("Switzerland","France")
G.add_edge("Switzerland","Italy")
G.add_edge("Austria","Germany")
G.add_edge("Austria","Italy")
G.add_edge("Austria","Czech Republic")
G.add_edge("Austria","Slovakia")
G.add_edge("Austria","Hungary")
G.add_edge("Denmark","Germany")
G.add_edge("Poland","Czech Republic")
G.add_edge("Poland","Slovakia")
G.add_edge("Poland","Germany")
G.add_edge("Czech Republic","Slovakia")
G.add_edge("Czech Republic","Germany")
G.add_edge("Slovakia","Hungary")
return G
G = setup_europe()
Write a dot file:
nx.write_dot(G, '/tmp/out.dot')
Compute the position of the nodes:
pos = nx.pygraphviz_layout(G, prog = 'dot')
print(pos)
# {'Netherlands': (713.86, 167.0), 'Italy': (473.86, 389.0), 'Czech Republic': (100.86, 241.0), 'Portugal': (879.86, 315.0), 'England': (1024.9, 241.0), 'Denmark': (568.86, 167.0), 'Poland': (100.86, 167.0), 'Scotland': (1024.9, 389.0), 'France': (571.86, 315.0), 'Belgium': (713.86, 19.0), 'Austria': (320.86, 167.0), 'Slovakia': (156.86, 315.0), 'Wales': (990.86, 315.0), 'Switzerland': (473.86, 241.0), 'Hungary': (294.86, 241.0), 'Germany': (465.86, 93.0), 'Spain': (879.86, 241.0)}
Render an png:
agraph = nx.to_agraph(G)
agraph.draw("/tmp/europe.png", format = 'png', prog = 'dot')
Using just pydot/dot you can do it by generating the SVG and then reading the position of the nodes from the SVG. It is a bit hacky, but works reliably enough
from xml.dom import minidom
import pydot
from io import BytesIO
def extract_node_positions(
in_dot: pydot.Dot
) -> Dict[str, Tuple[str, float, float]]:
"""
Extract the x,y positions from a pydot graph by rendering
Args:
in_dot: the graph to render
Returns:
a list of all the nodes
Examples:
>>> g = pydot.Dot()
>>> g.add_node(pydot.Node("A"))
>>> g.add_node(pydot.Node("B"))
>>> g.add_node(pydot.Node("C"))
>>> g.add_edge(pydot.Edge("A", "B"))
>>> g.add_edge(pydot.Edge("B", "C"))
>>> extract_node_positions(g)
{'A': ('A', 27.0, -157.8), 'B': ('B', 27.0, -85.8), 'C': ('C', 27.0, -13.8)}
"""
node_mapping = {f'node{i}': k.get_name()
for i, k in enumerate(in_dot.get_nodes(), 1)}
svg_io = BytesIO(in_dot.create_svg())
doc = minidom.parse(svg_io) # parseString also exists
node_dict = {node_mapping[p.getAttribute('id')]: (c_text.firstChild.data,
float(c_text.getAttribute('x')),
float(c_text.getAttribute('y')))
for p in doc.getElementsByTagName("g") if "node" == p.getAttribute('class').lower()
for c_text in p.getElementsByTagName('text')
}
doc.unlink()
return node_dict
To directly access the results from the Graphviz rendering command (e.g. dot) as binary data string from within Python instead of writing to a file, use the pipe()-method of your Graph or Digraph object:
h = Graph('hello', format='svg')
h.edge('Hello', 'World')
print(h.pipe().decode('utf-8'))
I struggled with this recently, and the answers here were some help but not quite there. The suggestions about networkx are on the right track. Networkx uses pygraphviz to interface with graphviz, and so you can instead use pygraphviz directly if you wish:
import pygraphviz as pgv
G = pgv.AGraph(strict=False,directed=True)
# add nodes and edges
G.add_edge(1,2)
G.add_edge(2,1)
# generate a layout--this creates `pos` attributes for both nodes and edges
G.layout(prog="dot")
#change something about the graph that would change the layout, e.g.,
edge = G.get_edge("1", "2")
edge.attr["label"] = "test label"
# create the graph using the layout already generated; note, do not provide `prog`
G.draw("test.pdf")
# compare it to a newly generated layout
G.draw("test2.pdf", prog="dot")
The important part is to not provide prog to the draw command if you want to use the node and edge positions already generated by the layout command. I see now this is stated in the pygraphviz docstring for draw. BTW, it is does the same as prog="neato" with the -n2 argument. Looking at the sources, pygraphviz calls graphviz to generate the layout.