Create Condition for Connectors of Datapoints R - r

This is my Initial Data
library(igraph)
From <- c(1,2,3,4,5,6,7,8)
To <- c("NULL",1,2,3,2,"NULL",6,7)
Value<-c(4,2,2,7,1,2,7,2)
Data <- data.frame(From,To,Value)
Network <- graph.data.frame(Data[,c("From","To")],directed=TRUE)
Network<- Network - "NULL"
plot(Network)
I want to create a condition which adds a characterization of the Datapoints.
So far i was capable of creating, the first and 2nd Degree of Condition, but i'm not capable of finding an Solution on how to create the 3rd condition to finalize the whole Construct.
I was capable of creating the Condition for the Root and the Starter, my Problem is how could i construct the condition for the connectors of the Starters. In this case the Connectors should be number 2 and 3. And Finally who didn't got characterized should be called Follower.
Root: Writes to no NULL
Starter: Value>X
Connector: Connects the starters(There will always be only one way of connecting them)
Follower= Rest
Some code here
cond<-Data$To=="NULL"
ToP<-ifelse(cond,"Root","Follower")
cond<-Data$Value>3
ToP<- ifelse(cond,"Starter",ToP)
NewData<-cbind(Data,ToP)
View(NewData)
My FinalData should kinda look like this:
From <- c(1,2,3,4,5,6,7,8)
To <- c("NULL",1,2,3,2,"NULL",6,7)
Value<-c(4,2,2,7,1,2,7,2)
ToP<-c(Starter,Connector,Connector,Starter,Follower,Root,Starter,Follower)
Data <- data.frame(From,To,Value, ToP)

It's still a bit messier than I would like, bit this appears to assing the correct lables to the vertices
V(Network)$ToP <- ifelse(Data$To=="NULL","Root","Follower")
V(Network)$ToP[Data$Value>3] <- "Starter"
vs <- V(Network)[ToP=="Starter"]
sp <- shortest.paths(Network, vs,vs)
cx <- which(is.finite(sp) & lower.tri(sp), arr.ind=T)
for(i in nrow(cx)) {
pp <- get.shortest.paths(Network, c(vs)[cx[i,1]], c(vs)[cx[i,2]])
fidx <- tail(head(pp$vpath[[1]], -1), -1)
if(length(fidx)>0) {
V(Network)[fidx]$ToP<-"Connector"
}
}
#verify with plot
V(Network)$color <- as.numeric(factor(V(Network)$ToP))+1
plot(Network)
legend(.5, -.5, levels(factor(V(Network)$ToP)), col=2:5, pch=20)

Related

How to use the convex_hull() function in the loop?

In my previous question I have used the convex_hull() function. We have a graph, where all nodes have zero degree and (x,y) coordinats. We need to create graph as sequence of convex hulls. We can stop the loop if only just one node has zero degree.
My attempt is:
library(igraph)
######################################################################
set.seed(5)
n=15
g <- graph.empty(n)
xy <- cbind(runif(n), runif(n))
in_points <- V(g)[degree(g)==0]
repeat {
cp <- convex_hull(xy[in_points, ])$resverts+1
g <- as.undirected(add_edges(g, c(t(embed(cp, 2)), cp[1], cp[length(cp)])))
in_points <- V(g)[degree(g)==0]
if (length(in_points)=1) {break}
}
plot(g, vertex.size=10, layout=xy)
One can see in the repeate loop the node 3 used twice.
Expected result is:
Question. How are correctly use output of convex_hull() function for sequential adding edges in the loop?
You can iteratively use chull and add_edges. You just need to make sure that input to add_edges is of correct format and track which nodes are not already part of outer convex hull.
xy <- cbind(seq_len(n), xy)
while(nrow(xy) > 1){
current_hull <- chull(xy[,2], xy[,3])
current_hull <- c(current_hull, current_hull[[1]])
g <- add_edges(g, as.vector(t(embed(xy[,1][current_hull], 2)[,2:1])))
xy <- xy[-current_hull,,drop = FALSE]
}

raster calculation with condition of each cell by layers in R

I have stack raster dataset with several layers, however, I want to calculate the sum of each cell with for different layer selection, and finally generate a new layer, anyone has some good suggestion by using calc or overlay or some other raster calculation in R?
I can do by loops and make the calculation, but it will consume many times when I have many layers, and also use many of the storage, my script as follows,
## library(raster)
make_calc <- function(rr, start, end) {
rr <- as.array(rr)
start <- as.array(start)
end <- as.array(end)
dms <- dim(raster)
tmp <- array(NA, dim = dms[1:2])
for (i in 1:dms[1]) {
for (j in 1:dms[2]) {
tmp[i,j] <- sum(raster[i,j,start[i,j,1]:end[i,j,1]], na.rm = TRUE)
}
}
return(tmp)
}
rr <- raster(res = 10)
rr[] <- 1
rr <- stack(rr, rr, rr, rr)
start <- raster(res = 10)
start[] <- sample(1:2, ncell(start), replace = TRUE)
end <- raster(res = 10)
end[] <- sample(3:4, ncell(end), replace = TRUE)
result <- make_calc(rr, start, end)
Why are you coercing into arrays? You can easily collapse a raster into a vector but, that does not even seem necessary here. In the future, please try to be more clear on what your expected outcome is.
Based on your code, I really don't know what you are getting at. I am going to take a few guesses on summing specified rasters in the stack, drawing a random sample, across rasters to be summed and finally, drawing a random sample of cells to be summed.
For a sum on specified rasters in a stack, you can just index what you are after in the stack using a double bracket. In this case, rasters 1 and 3 in the stack would be the only ones summed.
library(raster)
rr <- raster(res = 10)
rr[] <- 1
rr <- stack(rr, rr, rr, rr)
( sum_1_3 <- calc(rr[[c(1,3)]], sum) )
If you are wanting a random sample of the values across rasters, for every cell, you could write a function that is passed to calc. Here is an example that grabs a random sample of n size, across the raster layers values and sums them.
rs.sum <- function(x, n=2) {sum( x[sample(1:length(x),n)], na.rm=TRUE)}
rs.sum.raster <- calc(rr, rs.sum)
If you are wanting to apply a function to a limited random selection of cells, you could create a random sample of the raster that would be used as an index. Here we create a random sample of cells, create an empty raster and pipe the sum of rasters 1 and 2 (in the stack) based on the random sample cell index. A raster in the stack is indexed using the double bracket and the raster values are indexed using a single bracket so, for raster 1 in the stack with limiting to the values in the random sample you would use: rr[[1]][rs]
rs <- sample(1:ncell(rr[[1]]), 300)
r.sum <- rr[[1]]
r.sum[] <- NA
r.sum[rs] <- rr[[1]][rs] + rr[[2]][rs]
plot(r.sum)

Writing a graph and keeping the vertex names

I'm trying to create a graph and then write it with the function write.graph (package igraph). Hence, I create the distance matrix
require(vegan)
data(dune)
dis <- vegdist(dune)
and then I define explicitly the rownames:
x <- c("dune1")
for (i in 1: 20){
n <- paste("dune", i, sep="")
x <- append(x, n)
}
rownames(dune) <- x
With the following procedure I create an undirected graph through the minimum spanning tree algorithm.
gg <- graph.adjacency(as.matrix(dis), weighted=TRUE)
gg_mst <- as.undirected(mst(gg))
At this point I want to represent it such to open it with pajek. In order to do that I use write.graph:
write.graph(gg_mst, "graph.net", format="pajek")
obtaining the following graph:
The names are lost!
Nevertheless, if i use the same function using a different format:
write.graph(gg_mst, "graph.txt", format="ncol")
I obtain a file keeping the rownames:
dune1 dune3 0.448275862068966
dune2 dune3 0.341463414634146
dune2 dune10 0.294117647058824
dune3 dune4 0.270588235294118
... ... ...
Is it a bug related to the use of write.graph with the format "pajek"?
You need to assign id attributes of the vertices in order to be able to have the vertices' names shown in a pajek viewer such as this one http://vlado.fmf.uni-lj.si/pub%20/networks/pajek/default.htm or gephi. Need to modify a few lines of your code like the following:
dis <- vegdist(dune)
x <- c()
for (i in 1: 20){
n <- paste("dune", i, sep="")
x <- append(x, n)
}
gg <- graph.adjacency(as.matrix(dis), weighted=TRUE)
gg_mst <- as.undirected(mst(gg))
V(gg_mst)$id <- x # assign the ids
write.graph(gg_mst, "graph.net", format="pajek")
Opening with pajek shows the vertex ids correctly.

Solving Chinese Postman algorithm with eulerization

I'm would like to solve Chinese Postman problem in a graph where an eulerian cycle does not exist. So basically I'm looking for a path in a graph which visits every edge exactly once, and starts and ends at the same node. A graph will have an euler cycle if and only if every node has same number of edges entering into and going out of it. Obviously my graph doesn't .
I found out that Eulerization (making a graph Eulerian) could solve my question LINK. Can anyone suggest a script to add duplicate edges to a graph so that the resulting graph has no vertices of odd degree (and thus does have an Euler Circuit)?
Here is my example:
require(igraph)
require(graph)
require(eulerian)
require(GA)
g1 <- graph(c(1,2, 1,3, 2,4, 2,5, 1,5, 3,5, 4,7, 5,7, 5,8, 3,6, 6,8, 6,9, 9,11, 8,11, 8,10, 8,12, 7,10, 10,12, 11,12), directed = FALSE)
mat <- get.adjacency(g1)
mat <- as.matrix(mat)
rownames(mat) <- LETTERS[1:12]
colnames(mat) <- LETTERS[1:12]
g2 <- as(graphAM(adjMat=mat), "graphNEL")
hasEulerianCycle(g2)
Fun problem.
The graph you sugest in the code above, can be made to have duplicates that enable a eulerian cycle to be created. The function I provide below tries to add the minimum amount of duplicate edges, but also readily breaks the graph structure by adding new links if it has to.
You can run:
eulerian.g1 <- make.eulerian(g1)$graph
Check what the function did to your graph with:
make.eulerian(g1)$info
Bare in mind that:
This is not the only graph structure where duplicates added to the original g1 graph can form an eulerian cycle. Imagine for example my function looping the vertices of the graph backwards instead.
Your graph already has an uneven number of vertices with uneven degree, and all of the vertices that are, have neighbours with uneven degrees to pair them with. This function therefore works well four your particular example data.
The function could fail to produce a graph using only duplicates even in graphs where eulerian cycles are possible with correctly added duplicates. This is since it always goes for connecting a node with the first of its neighbours with uneven degree. If this is something that you'd absolutely like to get around, an MCMC-approach would be the way to go.
See also this excellent answer on probability calculation:
Here's my function in a full script that you can source out-of-the-box:
library(igraph)
# You asked about this graph
g1 <- graph(c(1,2, 1,3, 2,4, 2,5, 1,5, 3,5, 4,7, 5,7, 5,8, 3,6, 6,8, 6,9, 9,11, 8,11, 8,10, 8,12, 7,10, 10,12, 11,12), directed = FALSE)
# Make a CONNECTED random graph with at least n nodes
connected.erdos.renyi.game <- function(n,m){
graph <- erdos.renyi.game(n,m,"gnm",directed=FALSE)
graph <- delete_vertices(graph, (degree(graph) == 0))
}
# This is a random graph
g2 <- connected.erdos.renyi.game(n=12, m=16)
make.eulerian <- function(graph){
# Carl Hierholzer (1873) had explained how eulirian cycles exist for graphs that are
# 1) connected, and 2) contain only vertecies with even degrees. Based on this proof
# the posibility of an eulerian cycle existing in a graph can be tested by testing
# on these two conditions.
#
# This function assumes a connected graph.
# It adds edges to a graph to ensure that all nodes eventuall has an even numbered. It
# tries to maintain the structure of the graph by primarily adding duplicates of already
# existing edges, but can also add "structurally new" edges if the structure of the
# graph does not allow.
# save output
info <- c("broken" = FALSE, "Added" = 0, "Successfull" = TRUE)
# Is a number even
is.even <- function(x){ x %% 2 == 0 }
# Graphs with an even number of verticies with uneven degree will more easily converge
# as eulerian.
# Should we even out the number of unevenly degreed verticies?
search.for.even.neighbor <- !is.even(sum(!is.even(degree(graph))))
# Loop to add edges but never to change nodes that have been set to have even degree
for(i in V(graph)){
set.j <- NULL
#neighbors of i with uneven number of edges are good candidates for new edges
uneven.neighbors <- !is.even(degree(graph, neighbors(graph,i)))
if(!is.even(degree(graph,i))){
# This node needs a new connection. That edge e(i,j) needs an appropriate j:
if(sum(uneven.neighbors) == 0){
# There is no neighbor of i that has uneven degree. We will
# have to break the graph structure and connect nodes that
# were not connected before:
if(sum(!is.even(degree(graph))) > 0){
# Only break the structure if it's absolutely nessecary
# to force the graph into a structure where an euclidian
# cycle exists:
info["Broken"] <- TRUE
# Find candidates for j amongst any unevenly degreed nodes
uneven.candidates <- !is.even(degree(graph, V(graph)))
# Sugest a new edge between i and any node with uneven degree
if(sum(uneven.candidates) != 0){
set.j <- V(graph)[uneven.candidates][[1]]
}else{
# No candidate with uneven degree exists!
# If all edges except the last have even degrees, thith
# function will fail to make the graph eulerian:
info["Successfull"] <- FALSE
}
}
}else{
# A "structurally duplicated" edge may be formed between i one of
# the nodes of uneven degree that is already connected to it.
# Sugest a new edge between i and its first neighbor with uneven degree
set.j <- neighbors(graph, i)[uneven.neighbors][[1]]
}
}else if(search.for.even.neighbor == TRUE & is.null(set.j)){
# This only happens once (probably) in the beginning of the loop of
# treating graphs that have an uneven number of verticies with uneven
# degree. It creates a duplicate between a node and one of its evenly
# degreed neighbors (if possible)
info["Added"] <- info["Added"] + 1
set.j <- neighbors(graph, i)[ !uneven.neighbors ][[1]]
# Never do this again if a j is correctly set
if(!is.null(set.j)){search.for.even.neighbor <- FALSE}
}
# Add that a new edge to alter degrees in the desired direction
# OBS: as.numeric() since set.j might be NULL
if(!is.null(set.j)){
# i may not link to j
if(i != set.j){
graph <- add_edges(graph, edges=c(i, set.j))
info["Added"] <- info["Added"] + 1
}
}
}
# return the graph
(list("graph" = graph, "info" = info))
}
# Look at what we did
eulerian <- make.eulerian(g1)
eulerian$info
g <- eulerian$graph
par(mfrow=c(1,2))
plot(g1)
plot(g)

Using igraph: community membership of components built by decompose.graph()

I would appreciate help with using decompose.graph, community detection functions from igraph and lapply.
I have an igraph object G with vertex attribute "label" and edge attribute "weight". I want to calculate community memberships using different functions from igraph, for simplicity let it be walktrap.community.
This graph is not connected, that is why I decided to decompose it
into connected components and run walktrap.community on each component, and afterwards add a community membership vertex attribute to the original graph G.
I am doing currently the following
comps <- decompose.graph(G,min.vertices=2)
communities <- lapply(comps,walktrap.community)
At this point I get stuck since I get the list object with the structure I cannot figure out. The documentation on decompose.graph tells only that it returns list object, and when I use lapply on the result I get completely confused. Moreover, the communities are numbered from 0 in each component, and I don't know how to supply weights parameter into walktrap.community function.
If it were not for the components, I would have done the following:
wt <- walktrap.community(G, modularity=TRUE, weights=E(G)$weight)
wmemb <- community.to.membership(G, wt$merges,steps=which.max(wt$modularity)-1)
V(G)$"walktrap" <- wmemb$membership
Could anyone please help me solve this issue? Or provide some
information/links which could help?
You could use a loop:
library(igraph)
set.seed(2)
G <- erdos.renyi.game(100, 1/50)
comps <- decompose.graph(G,min.vertices=2)
length(comps) # 2 components, in this example
for(i in seq_along(comps)) { # For each subgraph comps[[i]]
wt <- walktrap.community(comps[[i]], modularity=TRUE, weights=E(comps[[i]])$weight)
wmemb <- community.to.membership(comps[[i]], wt$merges,steps=which.max(wt$modularity)-1)
V(comps[[i]])$"walktrap" <- wmemb$membership
}
It is possible to do it with lapply and mapply, but it is less readable.
comps <- decompose.graph(G,min.vertices=2)
wt <- lapply( comps, function(u)
walktrap.community(u, modularity=TRUE, weights=E(u)$weight)
)
wmemb <- mapply(
function(u,v) community.to.membership(u, v$merges,steps=which.max(v$modularity)-1),
comps, wt,
SIMPLIFY=FALSE
)
comps <- mapply(
function(u,v) { V(u)$"walktrap" <- v$membership; u },
comps, wmemb,
SIMPLIFY=FALSE
)

Resources