I am trying to implement the Kou's algorithm to identify Steiner Tree(s) in R using igraph.
The Kou's algorithm can be described like this:
Find the complete distance graph G' (G' has V' = S (steiner nodes) , and for each pair of nodes (u,v) in VxV there is an edge with weight equal to the weight of the min-cost path between these nodes p_(u,v) in G)
Find a minimum spanning tree T' in G'
Construct the subgraph Gs, of G by substituting every edge of T', which is an edge of G' with the corresponding shortest path of G (it there are several shortest paths, pick an arbitrary one).
Find the minimal spanning tree, Ts, of Gs (If there are several minimal spanning trees, pick an arbitrary one)
Construct a Steiner tree, Th, from Ts by deleting edges in Ts, if necessary, to that all the leaves in Th are Steiner nodes.
The first 2 steps are easy:
g <- erdos.renyi.game(100, 1/10) # graph
V(g)$name <- 1:100
# Some steiner nodes
steiner.points <- sample(1:100, 5)
# Complete distance graph G'
Gi <- graph.full(5)
V(Gi)$name <- steiner.points
# Find a minimum spanning tree T' in G'
mst <- minimum.spanning.tree(Gi)
However, I don't know how to replace the edges in T' for the shortest path in G. I know that with get.shortest.paths I can get the vpath from a pair of nodes, but how I replace and edge in T' with the shortest.path in G?
Many thanks in advance
If I'm understanding the algorithm as you've written it, I think this gets you through step 3, but please clarify if that's not the case:
library(igraph)
set.seed(2002)
g <- erdos.renyi.game(100, 1/10) # graph
V(g)$name <- as.character(1:100)
## Some steiner nodes:
steiner.points <- sample(1:100, 5)
## Complete distance graph G'
Gi <- graph.full(5)
V(Gi)$name <- steiner.points
## Find a minimum spanning tree T' in G'
mst <- minimum.spanning.tree(Gi)
## For each edge in mst, replace with shortest path:
edge_list <- get.edgelist(mst)
Gs <- mst
for (n in 1:nrow(edge_list)) {
i <- edge_list[n,2]
j <- edge_list[n,1]
## If the edge of T' mst is shared by Gi, then remove the edge from T'
## and replace with the shortest path between the nodes of g:
if (length(E(Gi)[which(V(mst)$name==i) %--% which(V(mst)$name==j)]) == 1) {
## If edge is present then remove existing edge from the
## minimum spanning tree:
Gs <- Gs - E(Gs)[which(V(mst)$name==i) %--% which(V(mst)$name==j)]
## Next extract the sub-graph from g corresponding to the
## shortest path and union it with the mst graph:
g_sub <- induced.subgraph(g, (get.shortest.paths(g, from=V(g)[i], to=V(g)[j])$vpath[[1]]))
Gs <- graph.union(Gs, g_sub, byname=T)
}
}
par(mfrow=c(1,2))
plot(mst)
plot(Gs)
Plot of minimum spanning tree on the left, replaced with shortest paths on right:
Related
I have an igraph network object constructed in R and generated weight information for each edge. I want to see the nodes of the most weighted edges (descending). What codes should I use to do that? Thank you!
# create an igraph project of user interaction network and check descriptives.
library(igraph)
#edge list
EL = read.csv("(file path omitted)user_interaction_structure.csv")
head(EL)
#node list: I do not have a node list
#construct an igraph oject
g <- graph_from_data_frame(EL, directed = TRUE, vertices = NULL)
#check the edge and node number of the network
gsize(g)
vcount(g)
#check nodes based on degree (descending)
deg <- igraph::degree(g)
dSorted <-sort.int(deg,decreasing=TRUE,index.return=FALSE)
dSorted
#check edges based on weight
E(g)
#the network will contain loop edges and multiple edges
#simplify multiple edges
g_simple <- graph.adjacency(get.adjacency(g),weighted=TRUE)
#check edge weight
E(g_simple)$weight
#igraph can generate a matrix
g_simple[]
Then I wanted to see who were interacting heavily with whom (the nodes of the edges with the largest weight),so I tried
e_top_weights <- order(order(E(g_simple))$weight, decreasing=TRUE)
but it did not work.
I think what you want is the igraph function strength(), which gives the sum of the weights of the edges incident to each node. Here's an example:
library(igraph)
# A small graph we can visualize
g <- make_ring(5)
# Assign each edge an increasing weight, to make things
# easy
edgeweights<- 1:ecount(g)
E(g)$weight <- edgeweights
# The strength() function sums the weights of edges incident
# to each node
strengths <- strength(g)
# We can collect the top two strengths by sorting the
# strengths vector, then asking for which elements of the
# strengths vector are equal to or greater than the second
# largest element.
toptwo <- which(strengths >= sort(strengths, decreasing = TRUE)[2])
## [1] 4 5
# Assign nodes a color blue that is more saturated when nodes
# have greater strength.
cr <- colorRamp(c(rgb(0,0,1,.1), rgb(0,0,1,1)), alpha = TRUE)
colors <- cr(strengths/max(strengths))
V(g)$color <- apply(colors, 1, function(row) rgb(row[1], row[2], row[3], row[4], maxColorValue = 255))
# Plot to confirm
plot(g, edge.width = edgeweights)
Edit
Here are two different ways to find the two nodes (the "from" node and the "to" node) which are the ends of the edge with the maximum weight:
## 1
edge_df <- as_data_frame(g, "edges")
edge_df[which(edge_df$weight == max(edge_df$weight)), c("from", "to")]
## 2
max_weight_edge <- E(g)[which(E(g)$weight == max(E(g)$weight))]
ends(g, es = max_weight_edge)
I have an unweighted graph and I want to get a subgraph that has just the nodes and edges that contain the shortest paths between n known nodes. In this case 3 nodes (11, 29, & 13 are the names).
Question
How can I get a subgraph of shortest path between n nodes in R?
MWE
library(ggraph)
library(igraph)
hs <- highschool[highschool$year == '1958',]
set.seed(11)
graph <- graph_from_data_frame(hs[sample.int(nrow(hs), 60),])
# plot using ggraph
ggraph(graph, layout = 'kk') +
geom_edge_fan() +
geom_node_text(aes(label = name))
Desired Output
The desired output would be the following green subgraph (Or close, I'm eyeballing the graph above and visually picking out what would be the subgraph) ignoring/removing the other nodes and edges.
You can't find the shortest path between n nodes. Since the shortest path is defined only between two nodes.
I think you want shortest path from 1 node to other n-1 node you can use
get_all_shortest_paths(v, to=None, mode=ALL) from igraph library.
v - the source for the calculated paths
to - a vertex selector describing the destination for the
calculated paths. This can be a single vertex ID, a list of vertex
IDs, a single vertex name, a list of vertex names. None means all the vertices.
mode - the directionality of the paths. IN means to calculate
incoming paths, OUT mean to calculate outgoing paths, ALL means to calculate both ones.
Returns: all of the shortest path from the given node to every other reachable node in the graph in a list.
get_all_shortest_paths
So, now you have to create a graph from a list of the shortest paths.
Initialize an empty graph then add all path to it from the list of
the path
adding path in graph
OR
make a graph for every shortest path found and take graphs union.
union igraph
You need a matrix of shortest paths to then create a sub-graph using a union of all edges belonging to those paths.
Let key vertices be those vertices between which your desired sub-graph appears. You say you have three such key vertices.
Consider that the shortest path between any i and j of them is unlist(shortest_paths(g, i, j, mode="all", weights=NULL)$vpath). You'd want to list all i-j combinations (1-2, 1-3, 2-3 in your case) of your key-verticies, and then list all vertices that appear on the paths between them. Sometime, surely, the same vertices appear on the shortest paths of more than one of your ij-pairs (See betweenness centrality). Your desired subgraph should include only these vertices, which you can give to induced_subgraph().
Then arises another interesting problem. Not all edges between your choosen vertices are part of your shortest paths. I'm not sure about what you desire in your sub-graph, but I assume that you only want vertices and edges that are part of shortest paths. The manual for induced_subgraph() says that eids can be provided to filter sub-graphs on edges too, but I didn't get that to work. Comments on that are welcome if anyone cracks it. To create a subgraph with only edges and vertices actually in your shortest path, some surplus edges must be deleted.
Below is an example where some key verticies are chosen at random, the surplus-edge problem of subgraphs is visualized, and a proper shortert-paths-only subgraph is generated:
library(igraph)
N <- 40 # Number of vertices in a random network
E <- 70 # Number of edges in a random network
K <- 5 # Number of KEY vertices between which we are to calculate the
# shortest paths and extract a sub-graph.
# Make a random network
g <- erdos.renyi.game(N, E, type="gnm", directed = FALSE, loops = FALSE)
V(g)$label <- NA
V(g)$color <- "white"
V(g)$size <- 8
E(g)$color <- "gray"
# Choose some random verteces and mark them as KEY vertices
key_vertices <- sample(1:N, 5)
g <- g %>% set_vertex_attr("color", index=key_vertices, value="red")
g <- g %>% set_vertex_attr("size", index=key_vertices, value=12)
# Find shortest paths between two vertices in vector x:
get_path <- function(x){
# Get atomic vector of two key verteces and return their shortest path as vector.
i <- x[1]; j <- x[2]
# Check distance to see if any verticy is outside component. No possible
# connection will return infinate distance:
if(distances(g,i,j) == Inf){
path <- c()
} else {
path <- unlist(shortest_paths(g, i, j, mode="all", weights=NULL)$vpath)
}
}
# List pairs of key vertices between which we need the shortest path
key_el <- expand.grid(key_vertices, key_vertices)
key_el <- key_el[key_el$Var1 != key_el$Var2,]
# Get all shortest paths between each pair of key_vertices:
paths <- apply(key_el, 1, get_path)
# These are the vertices BETWEEN key vertices - ON the shortest paths between them:
path_vertices <- setdiff(unique(unlist(paths)), key_vertices)
g <- g %>% set_vertex_attr("color", index=path_vertices, value="gray")
# Mark all edges of a shortest path
mark_edges <- function(path, edges=c()){
# Get a vector of id:s of connected vertices, find edge-id:s of all edges between them.
for(n in 1:(length(path)-1)){
i <- path[n]
j <- path[1+n]
edge <- get.edge.ids(g, c(i,j), directed = TRUE, error=FALSE, multi=FALSE)
edges <- c(edges, edge)
}
# Return all edges in this path
(edges)
}
# Find all edges that are part of the shortest paths between key vertices
key_edges <- lapply(paths, function(x) if(length(x) > 1){mark_edges(x)})
key_edges <- unique(unlist(key_edges))
g <- g %>% set_edge_attr("color", index=key_edges, value="green")
# This now shoes the full graph and the sub-graph which will be created
plot(g)
# Create sub-graph:
sg_vertices <- sort(union(key_vertices, path_vertices))
unclean_sg <- induced_subgraph(g, sg_vertices)
# Note that it is essential to provide both a verticy AND an edge-index for the
# subgraph since edges between included vertices do not have to be part of the
# calculated shortest path. I never used it before, but eids=key_edges given
# to induced_subgraph() should work (even though it didn't for me just now).
# See the problem here:
plot(unclean_sg)
# Kill edges of the sub-graph that were not part of shortest paths of the mother
# graph:
sg <- delete.edges(unclean_sg, which(E(unclean_sg)$color=="gray"))
# Plot a comparison:
l <-layout.auto(g)
layout(matrix(c(1,1,2,3), 2, 2, byrow = TRUE))
plot(g, layout=l)
plot(unclean_sg, layout=l[sg_vertices,]) # cut l to keep same layout in subgraph
plot(sg, layout=l[sg_vertices,]) # cut l to keep same layout in subgraph
I'm using the igraph package in R and I'm looking for a way to subgraph the max top 10 shortest path from a previous graph.
Anyone have some suggestions?
First create some graph:
set.seed(1)
require(igraph)
g <- erdos.renyi.game(100,.2)
Then extract all shortest paths and calculate their length:
plist <- do.call(c,
lapply(V(g), function(v) get.shortest.paths(g,v,V(g), output='epath')$epath))
Now figure out which paths are the top ten:
psize <- data.frame(i = 1:length(plist), plength = sapply(plist,length))
top10 <- head(psize[order(-psize$plength),],10)
Now figure out which edges this involves:
elist <- unlist(plist[top10$i])
And finally, get the subgraph which contains these vertices:
finalg <- subgraph.edges(g, elist)
The before and after plots:
I have a graph G(V,E) unweighted, undirected and connected graph with 12744 nodes and 166262 edges. I have a set of nodes (sub_set) that is a subset of V. I am interested in extracting the smallest connected subgraph where sub_set is a part of this new graph. I have managed to get a subgraph where my subset of nodes is included but I would like to know if there is a way to minimise the graph.
Here is my code (adapted from http://sidderb.wordpress.com/2013/07/16/irefr-ppi-data-access-from-r/)
library('igraph')
g <- erdos.renyi.game(10000, 0.003) #graph for illustrating my propose
sub_set <- sample(V(g), 80)
order <- 1
edges <- get.edges(g, 1:(ecount(g)))
neighbours.vid <- unique(unlist(neighborhood(g, order, which(V(g) %in% sub_set))))
rel.vid <- edges[intersect(which(edges[,1] %in% neighbours.vid), which(edges[,2] %in% neighbours.vid)),]
rel <- as.data.frame(cbind(V(g)[rel.vid[,1]], V(g)[rel.vid[,2]]), stringsAsFactors=FALSE)
names(rel) <- c("from", "to")
subgraph <- graph.data.frame(rel, directed=F)
subgraph <- simplify(subgraph)
I have read this post
minimum connected subgraph containing a given set of nodes, so I guess that my problem could be "The Steiner Tree problem", is there any way to try to find a suboptimal solution using igraph?
Not sure if that's what you meant but
subgraph<-minimum.spanning.tree(subgraph)
produces a graph with the minimum number of edges in which all nodes stay connected in one component.
Given a vector of fractions(on the domain [0:1]), I want to plot the vertex just partially filled.
I.e. if the fraction is 1/2 the corresponding vertex should be half-filled(half-sphere)
I.e. if the fraction is 1/4 the corresponding vertex is only in one quarter (quarter-sphere)
and so forth...
library('igraph')
N <- 10
g <- graph.full(N)
values <- runif(N,0,1) # vector of fractions
V(g)$shape <-'circle'
plot.igraph(g,...)
for example:
http://www.google.ch/imgres?q=three+quarter-filled+circle&um=1&hl=de&sa=N&biw=1024&bih=751&tbm=isch&tbnid=fCz7FZ6JG38DzM:&imgrefurl=http://www.clipartstation.com/clipart_indexer4/index/search%3Fkeywords%3DART&docid=UPphGFugM1pYGM&imgurl=http://www.clipartstation.com/clipart/resized/Math/Transformations/__100x100//three%252520quarters%252520blue%252520circle.gif&w=100&h=99&ei=ETsNUNDeHbCL4gTT5rjACg&zoom=1&iact=rc&dur=331&sig=101088608959992434501&page=1&tbnh=79&tbnw=80&start=0&ndsp=24&ved=1t:429,r:8,s:0,i:97&tx=49&ty=38
If you have the igraph package version 0.6, you should be able to use the pie vertex in plot(), in which case your code would be:
library(igraph)
N <- 10
g <- graph.full(N)
values <- runif(N,0,1) # vector of fractions
plot(g, vertex.shape="pie", vertex.pie=values, vertex.frame.color="white",
vertex.pie.color=list(heat.colors(5)))
If you don't have this working (which I didn't), you can find the code here, which you can run and then use pie as a vertex.