I would like to pass igraph a matrix of a series of edges for it to form between nodes. The edges are undirected. However, it is not adding the edges I would like for it to add
my graph looks like this
mygraph
IGRAPH U--- 30 11 --
+ attr: color (v/c), color (e/c)
+ edges:
[1] 3-- 4 3-- 9 4-- 5 4-- 6 6--10 12--14 15--20 16--21 25--27 25--30 26--29
I now want to add these undirected edges (edges in m go horizontally e.g. 12--13 is an edge, 9--13 is an edge, etc). If the edges are repeating they should be removed since it is undirected (meaning 23--20 is the same as 20--23).
m
value L1
[1,] 6 2
[2,] 4 5
[3,] 6 5
[4,] 2 6
[5,] 12 13
[6,] 9 13
[7,] 23 20
[8,] 20 23
when I do
add_edges(mygraph, m)
I get the following (note, the total number of edges is correct, but not the nodes that should have edges. For example, 12--13 does not exist and instead 12--9 is formed, which was not specified in m). It seems like add_edges is adding an edge vertically to make 12--9 instead of horizontally to make 12--13 from m
IGRAPH U--- 30 19 --
+ attr: color (v/c), color (e/c)
+ edges:
[1] 3-- 4 3-- 9 4-- 5 4-- 6 6--10 12--14 15--20 16--21 25--27 25--30 26--29 4-- 6 2-- 6 9--12 20--23 2-- 5 5-- 6 13--13
[19] 20--23
how can edges be added horizontally from a matrix to a graph using igraph?
You need to make a transpose of your edge matrix before adding them to your graph, the reason is that data in a matrix is stored by column, and it seems that igraph does not provide a proper interface for matrix, i.e, it doesn't interpret your matrix as by row edge matrix but just a vector and interpret each adjacent pair as a new edge:
Take a look at this simple example:
library(igraph)
mygraph <- graph(c(1,2,3,4,5,6))
mygraph
IGRAPH D--- 6 3 --
+ edges:
[1] 1->2 3->4 5->6
m <- matrix(c(6,2,4,5), byrow = TRUE, ncol = 2)
m
[,1] [,2]
[1,] 6 2
[2,] 4 5
If I add m directly to the graph object:
add_edges(mygraph, m)
IGRAPH D--- 6 5 --
+ edges:
[1] 1->2 3->4 5->6 6->4 2->5
I have 6 -> 4 and 2 -> 5 added as graph, which is because:
as.vector(m)
# [1] 6 4 2 5
So adjacent nodes are interpreted as edges. But if you transpose m before adding it as edges, it gives the correct result.
add_edges(mygraph, t(m))
IGRAPH D--- 6 5 --
+ edges:
[1] 1->2 3->4 5->6 6->2 4->5
Related
I'm trying to generate a Scale-Free graph, according to the Barabasi-Albert Model. I used the barabasi.game function, gerating 1000 vertices of the graph:
library('igraph')
g <- barabasi.game(1000)
I would like, now, to pick at random a vertex of g and, among its neighbors, to pick at random another vertex. How can I access the vertices of the graph?
Edit. I had problems with the solution kindly suggested by G5W. For this graph:
I obtained, from the first instruction
RV<-sample(V(g), 1)
the result RV=4, but from the second
RVn<-sample(neighbors(g, RV, mode="all"), 1)
I obtained RVn=1. As we can see from the pic this is a mistake; moreover, instruction
neighbors(g, i)
returns
+ 1/10 vertex, named, from 57207c1:
[1] 2
Why?
Thank you.
Modified
You can pick a random vertex and a random neighbor like this:
RV = sample(V(g), 1)
NRV = neighbors(g, RV, mode="all")
RVn = ifelse(length(NRV) == 1, NRV, sample(NRV, 1))
This should work when RV has one neighbor or more.
I would like to mention that most vertices have only one neighbor, so the random selection of a neighbor doesn't do much.
table(sapply(V(g), function(v) length(neighbors(g, v, mode="all"))))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 18 21 37 38 92
657 183 67 35 12 11 3 7 4 6 2 4 1 2 2 1 1 1 1
I'm working with some graph models in both Gephi, Python and R. Until by chance I decided to compare the results they gave me.
So I had the following problem. When calculating the betweenness centrality with Gephi and R (using igraph and statnet), the three give me different results (igraph and statnet, not very different). Since I am working a very large network, I decided to take a small network and perform the calculation by hand, the figure shown below (taked from: enter link description here )
enter image description here
Using the adjacency list:
source target
1 2
1 3
1 4
2 3
3 4
4 5
4 6
5 6
5 8
5 7
6 8
6 7
7 8
7 9
To then see what results I threw R and Gephi. I discovered that Gephi gives me the same results:
enter image description here
But R (both for igraph and statnet not).
> library('igraph')
> data <- read.csv(file.choose())
> set.seed(123456)
> graph_1<-graph.data.frame(data)
> summary(graph_1)
IGRAPH cfa51db DN-- 9 14 --
+ attr: name (v/c)
> graph_1
IGRAPH cfa51db DN-- 9 14 --
+ attr: name (v/c)
+ edges from cfa51db (vertex names):
[1] 1->2 1->3 1->4 2->3 3->4 4->5 4->6 5->6 5->8 5->7 6->8 6->7 7->8 7->9
> betweenness(graph_1)
1 2 3 4 5 6 7 8 9
0 0 6 15 6 6 6 0 0
> detach("package:igraph", unload=TRUE)
> library(statnet)
> library(intergraph)
> graph_2<-asNetwork(graph_1)
> betweenness(graph_2)
[1] 0 0 6 15 6 6 6 0 0
Am I doing something wrong by running my R code or is it using another algorithm to calculate the betweenness centrality?
Thank you :)
You are computing two different things.
First, to make your example reproducible, here is code that all of us can use to make your example.
library(igraph)
EL = matrix(c(1,2, 1,3, 1,4, 2,3, 3,4, 4,5, 4,6, 5,6, 5,8,
5,7, 6,8, 6,7, 7,8, 7,9), ncol=2, byrow=T)
graph_1 = graph_from_edgelist(EL)
Now, using your code, I get the same result.
betweenness(graph_1)
[1] 0 0 6 15 6 6 6 0 0
However,
betweenness(graph_1, directed=F)
[1] 3 0 3 15 6 6 7 0 0
Gives the same result as you got from Gephi.
The help page ?betweenness says:
directed
Logical, whether directed paths should be considered while
determining the shortest paths.
Clearly, Gephi has different defaults than R.
I am using the igraph package and I am uncertain whether it is a bug or not, but the $father output makes no sense sometimes. Specifically, when I rename the vertex attributes.
h<-make_tree(10)
#with normal vertex attributes
graph.bfs(h, root="1", neimode='out', order=TRUE, father=TRUE,unreachable=FALSE) #father output seems correct
plot(h,layout=layout_as_tree)
#with renamed vertex attributes
set.seed(1)
h<-set.vertex.attribute(h, "name", value=sample(1:10,10))
plot(h,layout=layout_as_tree)
graph.bfs(h, root="3", neimode='out', order=TRUE, father=TRUE,unreachable=FALSE) #father output seems wrong
I obtain the output as below
#with normal vertex attributes
$order
+ 10/10 vertices, from ff55a96:
[1] 1 2 3 4 5 6 7 8 9 10
$rank
NULL
$father
+ 10/10 vertices, from ff55a96:
[1] NA 1 1 2 2 3 3 4 4 5
#with renamed vertex attributes
$order
+ 10/10 vertices, named, from 170f7a0:
[1] 3 4 5 7 2 8 9 6 10 1
$rank
NULL
$father
+ 10/10 vertices, named, from 170f7a0:
[1] 3 4 5 7 2 8 9 6 10 1
I do not understand why the father for the renamed vertex attributes case is wrong. For example, the first element should be NA but its not.
Can someone explain what is happening? If so how do I fix this such that my father elements reflects something similar to the first case.
It's a bit strange, but for some reason, the bfs function has a straight assignment of the vertex names to the names of the father vector. See the 54-55 line of code in the source code:
if (father)
names(res$father) <- V(graph)$name
Clearly, this simply overwrites the names of res$father with the vector of names in the graph. Notice that this conditional statement requires the argument igraph_opt("add.vertex.names") to be true.
So we can avoid this behavior by setting the global option for adding vertex names to false.
> igraph_options()$add.vertex.names
[1] TRUE
> igraph_options(add.vertex.names=F)
> igraph_options()$add.vertex.names
[1] FALSE
Now it should work:
h<-make_tree(10)
set.seed(1)
h<-set_vertex_attr(h, "name", value=sample(1:10,10))
bfs(h, root=1, neimode='out', order=TRUE, rank=TRUE, father=TRUE,unreachable=FALSE)
Output:
$root
[1] 1
$neimode
[1] "out"
$order
+ 10/10 vertices, named:
[1] 3 4 5 7 2 8 9 6 10 1
$rank
[1] 1 2 3 4 5 6 7 8 9 10
$father
+ 10/10 vertices, named:
[1] <NA> 3 3 4 4 5 5 7 7 2
$pred
NULL
$succ
NULL
$dist
NULL
Might be worth raising this on the igraph github, since this seems (at least to me) like undesirable behavior.
I created a graph using a code
g <- graph.ring(10)
and then used betweenness function which outputted value 8 for every vertex and 12.5 for every edge. However, what I have understood this function does, according to me answer must be 7 for every vertex .
Can someone explain me how is it 8 and for edge 12.5?
Assume the following graph:
str(g)
# IGRAPH U--- 10 10 -- Ring graph
# + attr: name (g/c), mutual (g/l), circular (g/l)
# + edges:
# [1] 1-- 2 2-- 3 3-- 4 4-- 5 5-- 6 6-- 7 7-- 8 8-- 9 9--10 1--10
Let's take vertex 2, it appears on eight shortest paths:
1-2-3
1-2-3-4
1-2-3-4-5
1-2-3-4-5-6 (non-unique, 1-10-9-8-7-6)
3-2-1-10-9-8 (non-unique, 3-4-5-6-7-8)
3-2-1-10-9
3-2-1-10
4-3-2-1-10-9 (non-unique, 4-5-6-7-8-9)
4-3-2-1-10
5-4-3-2-1-10 (non-unique, 5-6-7-8-9-10)
So this is altogether 6 unique shortest paths, and four that each count as 1/2, as there is one alternative path without 2 for each. This is 6 plus 2, which is 8.
I have the following code to plot the minimum spanning tree of a graph
## g is an igraph graph
mst = minimum.spanning.tree(g)
E(g)$color <- "SkyBlue2"
## how to I make mst a different color
E(g)[E(mst)]$color = "red" ### <---- I WANT TO DO ESSENTIALLY THIS
plot(g, edge.label=E(g)$weight)
That is, for a simple graph, I find the mst. I want to change the mst to red and plot the mst as part of the main graph. To do this, I want to select the edges of g that are also in mst. How do I do this?
UPDATE:
More generally, I have a graph g0 which is the mst of g, which has n vertices. It was constructed as follows
## implementing the Dijkstra-Prim algorithm
v0 = sample(1:n, 1)
g0 = graph.empty(n=n, directed=FALSE)
weight.g0 = 0
while(length(setdiff(1:n, v0) > 0)) {
## chose the shortest edge in the cut set of g
## to find the cut, figure out the set of edges where vertex is
## in v0 and the other is not
cutset = E(g)[ v0 %->% setdiff(1:n, v0)]
## find the lightest weight edge
cutweights = E(g)$weight[cutset]
lightest_edge_idx = which(cutweights == min(cutweights))[1]
weight.g0 = weight.g0 + min(cutweights)
## get the vertices of the lightest weight edge, add to path
lightest_edge = cutset[as.numeric(cutset)[lightest_edge_idx]]
vertices = get.edges(g, as.numeric(lightest_edge))
g0 <- add.edges(g0, vertices, weight=min(cutweights))
## now that we have the vertices, add the one that is not in the
## graph already
for(vtx in vertices) {
if(!(vtx %in% v0)) {
v0 = c(vtx, v0)
}
}
}
I know I am probably not using a lot of useful features of igraph, but I do get g0 to be a mst at the end of this loop. Given this, I have
E(g0)
Edge sequence:
[1] 8 -- 1
[2] 2 -- 1
[3] 9 -- 8
[4] 9 -- 5
[5] 3 -- 2
[6] 4 -- 3
[7] 7 -- 3
[8] 11 -- 4
[9] 7 -- 6
[10] 11 -- 10
> E(g)
Edge sequence:
[1] 2 -- 1
[2] 5 -- 1
[3] 8 -- 1
[4] 3 -- 2
[5] 5 -- 2
[6] 6 -- 2
[7] 4 -- 3
[8] 6 -- 3
[9] 7 -- 3
[10] 7 -- 4
[11] 11 -- 4
[12] 6 -- 5
[13] 8 -- 5
[14] 9 -- 5
[15] 7 -- 6
[16] 9 -- 6
[17] 10 -- 6
[18] 10 -- 7
[19] 11 -- 7
[20] 9 -- 8
[21] 10 -- 9
[22] 11 -- 10
My question was, how do I assign an attribute to the edges in E(g) that are also in E(g0)?
This is actually quite easy because minimum.spanning.tree() keeps edge attributes. So you just need to assign an edge id attribute, and you'll see which edges to color red. It goes like this:
# Some test data, no edge weights, quite boring
g <- erdos.renyi.game(20,2/20)
g
# IGRAPH U--- 20 24 -- Erdos renyi (gnp) graph
# + attr: name (g/c), type (g/c), loops (g/l), p (g/n)
E(g)$id <- seq_len(ecount(g))
mst <- minimum.spanning.tree(g)
mst
# IGRAPH U--- 20 18 -- Erdos renyi (gnp) graph
# + attr: name (g/c), type (g/c), loops (g/l), p (g/n), id (e/n)
E(mst)$id
# [1] 1 2 3 6 7 8 9 10 11 12 13 16 18 19 20 22 23 24
E(g)$color <- "black"
E(g)$color[E(mst)$id] <- "red"
plot(g)