R igraph, how to plot vertices with mix of shapes and raster? - r

I'm trying to plot a graph with R and igraph, using a mix of shapes and raster images for the vertices. I've modified the igraph example below to reproduce my problem. Can someone see what is wrong? You'll need a png file to test the script.
library(png)
library(igraph)
img.1 <- readPNG(system.file("img", "Rlogo.png", package="png"))
shapes <- setdiff(shapes(), "")
g <- make_ring(length(shapes))
V(g)$shape <- shapes
#change the rectangle variants to raster
V(g)$shape[grepl("rect",V(g)$shape)] <- "raster"
#give every vertex the same image, regardless of shape
V(g)$raster <- replicate(vcount(g), img.1, simplify=FALSE)
plot(g,
vertex.size=15, vertex.size2=15,
vertex.pie=lapply(shapes, function(x) if (x=="pie") 2:6 else 0),
vertex.pie.color=list(heat.colors(5)))

This seems to be one way, but it needs a bit of manual tweaking to fit the rasters.
library(png)
library(igraph)
# Your code
img.1 <- readPNG(system.file("img", "Rlogo.png", package="png"))
shapes <- setdiff(shapes(), "")
g <- make_ring(length(shapes))
V(g)$shape <- shapes
# Change some shapes to user defined
V(g)$shape[grepl("rect",V(g)$shape)] <- "myimg"
# Using idea from http://igraph.org/r/doc/shapes.html
# define function for image
# manually tweaked the x any y to increase size of image
myimg <- function(coords, v=NULL, params) {
vertex.size <- 1/200 * params("vertex", "size")
if (length(vertex.size) != 1 && !is.null(v)) {
vertex.size <- vertex.size[v]
}
rasterImage(img.1,
coords[,1]-vertex.size, coords[,2]-vertex.size,
coords[,1]+vertex.size, coords[,2]+vertex.size)
}
# add shape
add_shape("myimg", plot=myimg)
# plot
plot(g, vertex.size=seq(5, 5*length(shapes), 5), vertex.size2=seq(5, 5*length(shapes), 5)
vertex.pie=lapply(shapes, function(x) if (x=="pie") 2:6 else 0),
vertex.pie.color=list(heat.colors(5)))
To give
I dare say there is a more igraph approach to this

Related

R plot coreness layout

I've tried to draw a spiral plot for my data.
Here is my igraph network:
https://bulut.omu.edu.tr/index.php/s/WoSFKhMV7Rxtgem
So far, I've created this script which creates a circle plot. My problem is that the color of node should change according k-core. It means that outside of circle should be lighter and the center should be darker!:
# Load Library
library(igraph)
library(RColorBrewer)
# Classic palette for red, with 5 colors
coul01 = brewer.pal(5, "RdPu")
# I can add more tones to this palette :
coul01 = colorRampPalette(coul01)(60)
# Load igraph object
g <- readRDS("~g.rds")
# Plot coreness
CorenessLayout <- function(g) {
coreness <- V(g)$kCore;#graph.coreness(g);
xy <- array(NA, dim=c(length(coreness), 2));
shells <- sort(unique(coreness));
for(shell in shells) {
v <- 1 - ((shell-1) / max(shells));
nodes_in_shell <- sum(coreness==shell);
angles <- seq(0,360,(360/nodes_in_shell));
angles <- angles[-length(angles)]; # remove last element
xy[coreness==shell, 1] <- sin(angles) * v;
xy[coreness==shell, 2] <- cos(angles) * v;
}
return(xy);
}
# create layout
ll <- CorenessLayout(g);
# plot
plot(g, layout=ll, vertex.size=1, vertex.color=coul01,
edge.width=.001,edge.arrow.size=.001,vertex.label=NA,
vertex.frame.color=coul01)
Thanks in advance for any suggestion.
PS: If you couldn't see the end result, please use this link
https://bulut.omu.edu.tr/index.php/s/sQpxqIOH04x9wtW

Coordinates in igraph in R

I am trying to
1) get the coordinates of a network
2) use them for other networks to have always the same position of nodes.
When I get the coordinates of the nodes and set the coordinates to the same network from which I got them, it changes. The x position remains the same and the y position becomes symmetric to the hypothetical y axes. Thus, when applied twice, the position is the one that I want.
The problem is probably in the tkplot.getcoords() function. Do you know if there is a trick to avoid applying it twice?
n <- 20
mat <- matrix(1:n^2, n,n)
g <- graph.adjacency(mat, mode="directed", weighted=TRUE, diag=FALSE)
V(g)$color <- "white"
id <- tkplot(g, edge.curved = 0.5)
coor <- tkplot.getcoords(id,norm=F)
coor
tkplot.setcoords(id, coor) # wrong position
coor <- tkplot.getcoords(id,norm=F)
coor
tkplot.setcoords(id, coor) # desired position
Do you know if there is a trick to avoid applying it twice?
It seems as if you had to flip the y coordinates; this works on my computer:
library(igraph)
set.seed(1);n <- 5
mat <- matrix(1:n^2, n,n)
g <- graph.adjacency(mat, mode="directed", weighted=TRUE, diag=FALSE)
V(g)$color <- "white"
id <- tkplot(g, 200, 200, edge.curved = 0.5)
coor <- tkplot.getcoords(id,norm=F)
canvas_height <- as.numeric(tcltk::tkcget(tk_canvas(id), "-height"))-20 # twenty by trial&error - prly the frame border top&bottom?
coor[,2] <- canvas_height-coor[,2]
# move some vertices and...
tkplot.setcoords(id, coor) # reset

Colors don't show using persp3d() function in R

I'm having some issues when using persp3d() function in R. Here is my code:
library(rgl)
vero=function(mu,sigma,datos)
{
n=length(datos)
media=mean(datos)
S2=sd(datos)^2
lvero=(-n/2)*log(2*pi*sigma)-(n/(2*sigma))*S2-(n/(2*sigma))*(media-mu)^2
return(exp(lvero))
}
nbebes=rnorm(20, mean=75, sd=2.5)
mu.seq <- seq(60,100,length=2000)
sigma.seq <- seq(1,20,length=2000)
f <- Vectorize(vero,vectorize.args=c("mu","sigma"))
z <- outer(mu.seq,sigma.seq,f,datos=nbebes)
zlim <- range(z[!is.na(z)])
palette <- rev(rainbow(20))
colors <- palette[19*(z-zlim[1])/diff(zlim) + 1]
persp3d(mu.seq,sigma.seq,z,col=colors)
And the output of the code is this graph:
What am I doing wrong? Sometimes I get another result which is a rainbow colored function like this one:
But as you can see it is not completely colored and I don't know what to change or rewrite to get the proper result.
I can't say eaxactly why this is an issue, but it seems to have to do with the limits of your z-axis. When I rescale z to z2 <- z / max(z) then it ranges between 0 and 1 and plots well. This might be an issue with rgl. Here's an example:
nbebes=rnorm(20, mean=75, sd=2.5)
mu.seq <- seq(60,100,length=500)
sigma.seq <- seq(1,20,length=500)
f <- Vectorize(vero,vectorize.args=c("mu","sigma"))
z <- outer(mu.seq,sigma.seq,f,datos=nbebes)
z2 <- z/max(z)
colors <- rev(rainbow(20))
breaks <- seq(zlim[1], zlim[2], length.out=(length(colors)+1))
CUT <- cut(z2, breaks=breaks, include.lowest = TRUE)
colorlevels <- colors[match(CUT, levels(CUT))] # assign colors to heights for each point
persp3d(mu.seq,sigma.seq,z2,color=colorlevels)

How does one turn contour lines into filled contours?

Does anyone know of a way to turn the output of contourLines polygons in order to plot as filled contours, as with filled.contours. Is there an order to how the polygons must then be plotted in order to see all available levels? Here is an example snippet of code that doesn't work:
#typical plot
filled.contour(volcano, color.palette = terrain.colors)
#try
cont <- contourLines(volcano)
fun <- function(x) x$level
LEVS <- sort(unique(unlist(lapply(cont, fun))))
COLS <- terrain.colors(length(LEVS))
contour(volcano)
for(i in seq(cont)){
COLNUM <- match(cont[[i]]$level, LEVS)
polygon(cont[[i]], col=COLS[COLNUM], border="NA")
}
contour(volcano, add=TRUE)
A solution that uses the raster package (which calls rgeos and sp). The output is a SpatialPolygonsDataFrame that will cover every value in your grid:
library('raster')
rr <- raster(t(volcano))
rc <- cut(rr, breaks= 10)
pols <- rasterToPolygons(rc, dissolve=T)
spplot(pols)
Here's a discussion that will show you how to simplify ('prettify') the resulting polygons.
Thanks to some inspiration from this site, I worked up a function to convert contour lines to filled contours. It's set-up to process a raster object and return a SpatialPolygonsDataFrame.
raster2contourPolys <- function(r, levels = NULL) {
## set-up levels
levels <- sort(levels)
plevels <- c(min(values(r), na.rm=TRUE), levels, max(values(r), na.rm=TRUE)) # pad with raster range
llevels <- paste(plevels[-length(plevels)], plevels[-1], sep=" - ")
llevels[1] <- paste("<", min(levels))
llevels[length(llevels)] <- paste(">", max(levels))
## convert raster object to matrix so it can be fed into contourLines
xmin <- extent(r)#xmin
xmax <- extent(r)#xmax
ymin <- extent(r)#ymin
ymax <- extent(r)#ymax
rx <- seq(xmin, xmax, length.out=ncol(r))
ry <- seq(ymin, ymax, length.out=nrow(r))
rz <- t(as.matrix(r))
rz <- rz[,ncol(rz):1] # reshape
## get contour lines and convert to SpatialLinesDataFrame
cat("Converting to contour lines...\n")
cl <- contourLines(rx,ry,rz,levels=levels)
cl <- ContourLines2SLDF(cl)
## extract coordinates to generate overall boundary polygon
xy <- coordinates(r)[which(!is.na(values(r))),]
i <- chull(xy)
b <- xy[c(i,i[1]),]
b <- SpatialPolygons(list(Polygons(list(Polygon(b, hole = FALSE)), "1")))
## add buffer around lines and cut boundary polygon
cat("Converting contour lines to polygons...\n")
bcl <- gBuffer(cl, width = 0.0001) # add small buffer so it cuts bounding poly
cp <- gDifference(b, bcl)
## restructure and make polygon number the ID
polys <- list()
for(j in seq_along(cp#polygons[[1]]#Polygons)) {
polys[[j]] <- Polygons(list(cp#polygons[[1]]#Polygons[[j]]),j)
}
cp <- SpatialPolygons(polys)
cp <- SpatialPolygonsDataFrame(cp, data.frame(id=seq_along(cp)))
## cut the raster by levels
rc <- cut(r, breaks=plevels)
## loop through each polygon, create internal buffer, select points and define overlap with raster
cat("Adding attributes to polygons...\n")
l <- character(length(cp))
for(j in seq_along(cp)) {
p <- cp[cp$id==j,]
bp <- gBuffer(p, width = -max(res(r))) # use a negative buffer to obtain internal points
if(!is.null(bp)) {
xy <- SpatialPoints(coordinates(bp#polygons[[1]]#Polygons[[1]]))[1]
l[j] <- llevels[extract(rc,xy)]
}
else {
xy <- coordinates(gCentroid(p)) # buffer will not be calculated for smaller polygons, so grab centroid
l[j] <- llevels[extract(rc,xy)]
}
}
## assign level to each polygon
cp$level <- factor(l, levels=llevels)
cp$min <- plevels[-length(plevels)][cp$level]
cp$max <- plevels[-1][cp$level]
cp <- cp[!is.na(cp$level),] # discard small polygons that did not capture a raster point
df <- unique(cp#data[,c("level","min","max")]) # to be used after holes are defined
df <- df[order(df$min),]
row.names(df) <- df$level
llevels <- df$level
## define depressions in higher levels (ie holes)
cat("Defining holes...\n")
spolys <- list()
p <- cp[cp$level==llevels[1],] # add deepest layer
p <- gUnaryUnion(p)
spolys[[1]] <- Polygons(p#polygons[[1]]#Polygons, ID=llevels[1])
for(i in seq(length(llevels)-1)) {
p1 <- cp[cp$level==llevels[i+1],] # upper layer
p2 <- cp[cp$level==llevels[i],] # lower layer
x <- numeric(length(p2)) # grab one point from each of the deeper polygons
y <- numeric(length(p2))
id <- numeric(length(p2))
for(j in seq_along(p2)) {
xy <- coordinates(p2#polygons[[j]]#Polygons[[1]])[1,]
x[j] <- xy[1]; y[j] <- xy[2]
id[j] <- as.numeric(p2#polygons[[j]]#ID)
}
xy <- SpatialPointsDataFrame(cbind(x,y), data.frame(id=id))
holes <- over(xy, p1)$id
holes <- xy$id[which(!is.na(holes))]
if(length(holes)>0) {
p2 <- p2[p2$id %in% holes,] # keep the polygons over the shallower polygon
p1 <- gUnaryUnion(p1) # simplify each group of polygons
p2 <- gUnaryUnion(p2)
p <- gDifference(p1, p2) # cut holes in p1
} else { p <- gUnaryUnion(p1) }
spolys[[i+1]] <- Polygons(p#polygons[[1]]#Polygons, ID=llevels[i+1]) # add level
}
cp <- SpatialPolygons(spolys, pO=seq_along(llevels), proj4string=CRS(proj4string(r))) # compile into final object
cp <- SpatialPolygonsDataFrame(cp, df)
cat("Done!")
cp
}
It probably holds several inefficiencies, but it has worked well in the tests I've conducted using bathymetry data. Here's an example using the volcano data:
r <- raster(t(volcano))
l <- seq(100,200,by=10)
cp <- raster2contourPolys(r, levels=l)
cols <- terrain.colors(length(cp))
plot(cp, col=cols, border=cols, axes=TRUE, xaxs="i", yaxs="i")
contour(r, levels=l, add=TRUE)
box()
Building on the excellent work of Paul Regular, here is a version that should ensure exclusive polygons (i.e. no overlapping).
I've added a new argument fd for fairy dust to address an issue I discovered working with UTM-type coordinates. Basically as I understand the algorithm works by sampling lateral points from the contour lines to determine which side is inside the polygon. The distance of the sample point from the line can create problems if it ends up in e.g. behind another contour. So if your resulting polygons looks wrong try setting fd to values 10^±n until it looks very wrong or about right..
raster2contourPolys <- function(r, levels = NULL, fd = 1) {
## set-up levels
levels <- sort(levels)
plevels <- c(min(values(r)-1, na.rm=TRUE), levels, max(values(r)+1, na.rm=TRUE)) # pad with raster range
llevels <- paste(plevels[-length(plevels)], plevels[-1], sep=" - ")
llevels[1] <- paste("<", min(levels))
llevels[length(llevels)] <- paste(">", max(levels))
## convert raster object to matrix so it can be fed into contourLines
xmin <- extent(r)#xmin
xmax <- extent(r)#xmax
ymin <- extent(r)#ymin
ymax <- extent(r)#ymax
rx <- seq(xmin, xmax, length.out=ncol(r))
ry <- seq(ymin, ymax, length.out=nrow(r))
rz <- t(as.matrix(r))
rz <- rz[,ncol(rz):1] # reshape
## get contour lines and convert to SpatialLinesDataFrame
cat("Converting to contour lines...\n")
cl0 <- contourLines(rx, ry, rz, levels = levels)
cl <- ContourLines2SLDF(cl0)
## extract coordinates to generate overall boundary polygon
xy <- coordinates(r)[which(!is.na(values(r))),]
i <- chull(xy)
b <- xy[c(i,i[1]),]
b <- SpatialPolygons(list(Polygons(list(Polygon(b, hole = FALSE)), "1")))
## add buffer around lines and cut boundary polygon
cat("Converting contour lines to polygons...\n")
bcl <- gBuffer(cl, width = fd*diff(bbox(r)[1,])/3600000) # add small buffer so it cuts bounding poly
cp <- gDifference(b, bcl)
## restructure and make polygon number the ID
polys <- list()
for(j in seq_along(cp#polygons[[1]]#Polygons)) {
polys[[j]] <- Polygons(list(cp#polygons[[1]]#Polygons[[j]]),j)
}
cp <- SpatialPolygons(polys)
cp <- SpatialPolygonsDataFrame(cp, data.frame(id=seq_along(cp)))
# group by elev (replicate ids)
# ids = sapply(slot(cl, "lines"), slot, "ID")
# lens = sapply(1:length(cl), function(i) length(cl[i,]#lines[[1]]#Lines))
## cut the raster by levels
rc <- cut(r, breaks=plevels)
## loop through each polygon, create internal buffer, select points and define overlap with raster
cat("Adding attributes to polygons...\n")
l <- character(length(cp))
for(j in seq_along(cp)) {
p <- cp[cp$id==j,]
bp <- gBuffer(p, width = -max(res(r))) # use a negative buffer to obtain internal points
if(!is.null(bp)) {
xy <- SpatialPoints(coordinates(bp#polygons[[1]]#Polygons[[1]]))[1]
l[j] <- llevels[raster::extract(rc,xy)]
}
else {
xy <- coordinates(gCentroid(p)) # buffer will not be calculated for smaller polygons, so grab centroid
l[j] <- llevels[raster::extract(rc,xy)]
}
}
## assign level to each polygon
cp$level <- factor(l, levels=llevels)
cp$min <- plevels[-length(plevels)][cp$level]
cp$max <- plevels[-1][cp$level]
cp <- cp[!is.na(cp$level),] # discard small polygons that did not capture a raster point
df <- unique(cp#data[,c("level","min","max")]) # to be used after holes are defined
df <- df[order(df$min),]
row.names(df) <- df$level
llevels <- df$level
## define depressions in higher levels (ie holes)
cat("Defining holes...\n")
spolys <- list()
p <- cp[cp$level==llevels[1],] # add deepest layer
p <- gUnaryUnion(p)
spolys[[1]] <- Polygons(p#polygons[[1]]#Polygons, ID=llevels[1])
for(i in seq(length(llevels)-1)) {
p1 <- cp[cp$level==llevels[i+1],] # upper layer
p2 <- cp[cp$level==llevels[i],] # lower layer
x <- numeric(length(p2)) # grab one point from each of the deeper polygons
y <- numeric(length(p2))
id <- numeric(length(p2))
for(j in seq_along(p2)) {
xy <- coordinates(p2#polygons[[j]]#Polygons[[1]])[1,]
x[j] <- xy[1]; y[j] <- xy[2]
id[j] <- as.numeric(p2#polygons[[j]]#ID)
}
xy <- SpatialPointsDataFrame(cbind(x,y), data.frame(id=id))
holes <- over(xy, p1)$id
holes <- xy$id[which(!is.na(holes))]
if(length(holes)>0) {
p2 <- p2[p2$id %in% holes,] # keep the polygons over the shallower polygon
p1 <- gUnaryUnion(p1) # simplify each group of polygons
p2 <- gUnaryUnion(p2)
p <- gDifference(p1, p2) # cut holes in p1
} else { p <- gUnaryUnion(p1) }
spolys[[i+1]] <- Polygons(p#polygons[[1]]#Polygons, ID=llevels[i+1]) # add level
}
cp <- SpatialPolygons(spolys, pO=seq_along(llevels), proj4string=CRS(proj4string(r))) # compile into final object
## make polygons exclusive (i.e. no overlapping)
cpx = gDifference(cp[1,], cp[2,], id=cp[1,]#polygons[[1]]#ID)
for(i in 2:(length(cp)-1)) cpx = spRbind(cpx, gDifference(cp[i,], cp[i+1,], id=cp[i,]#polygons[[1]]#ID))
cp = spRbind(cpx, cp[length(cp),])
## it's a wrap
cp <- SpatialPolygonsDataFrame(cp, df)
cat("Done!")
cp
}

Highlight and color a specific node on a dendrogram plotted on a heatmap

I have a dendrogram plotted on a heatmap.
How can I select and color only one node and the corresponding leaves?
Thanks in advance!
Eleonora
Make use of dendrapply to traverse the nodes. There is a worked example in ?dendrapply that illustrates how to set the colour of nodes:
require(graphics)
## a smallish simple dendrogram
dhc <- as.dendrogram(hc <- hclust(dist(USArrests), "ave"))
(dhc21 <- dhc[[2]][[1]])
## too simple:
dendrapply(dhc21, function(n) utils::str(attributes(n)))
## toy example to set colored leaf labels :
local({
colLab <<- function(n) {
if(is.leaf(n)) {
a <- attributes(n)
i <<- i+1
attr(n, "nodePar") <-
c(a$nodePar, list(lab.col = mycols[i], lab.font= i%%3))
}
n
}
mycols <- grDevices::rainbow(attr(dhc21,"members"))
i <- 0
})
dL <- dendrapply(dhc21, colLab)
op <- par(mfrow=2:1)
plot(dhc21)
plot(dL) ## --> colored labels!
par(op)

Resources