Adding annotation to R plots with layout - r

I am plotting multiple panels of variable size in R, and using layout to accomplish this. I, however, would like to add arrows and text between the plots but seem to, in my best attempts using arrow and text functions, have the annotations being cut-off at the end of the axis I'm plotting on.
MWE:
circle <- function(){
theta <- seq(from = 0, to = 2*pi - pi/1000, by = 2*pi/1000)
x <- cos(theta)
y <- sin(theta)
circ <- list(x, y)
}
shape_plot <- function(xs, ys, typ){
plot(xs, ys, type=typ, axes=FALSE, xlab='', ylab='', asp=1, lwd=4)
}
sample1 <- function(data, n){
pts <- sort(floor(runif(n, min=1, max=1000)))
xs <- data[[1]]
x <- xs[pts] + .1*runif(n, min=-1, max=1)
x <- c(x, x[1])
ys <- data[[2]]
y <- ys[pts] + .1*runif(n, min=-1, max=1)
y <- c(y, y[1])
samp <- list(x, y)
}
layout(matrix(c(1,2, 1,3, 1,4), 3, 2, byrow = TRUE))
circ <- circle()
shape_plot(circ[[1]], circ[[2]], 'l')
label <- rep('circle', 3)
for (i in 1:3){
samp <- sample1(circ, 50)
shape_plot(samp[[1]], samp[[2]], 'p')
}
This returns the following figure:
What I would like is something like this (but less ugly, and not created in GIMP):
Thanks!

In response to a comment by #rawr, I came up with the following modifications which enabled me to accomplish this goal:
...
layout(matrix(c(1,2, 1,3, 1,4), 3, 2, byrow = TRUE))
par(xpd=NA)
...
...
arrows(x0=1.5, x1=2.5, y0=1, y1=2, length=0.1, lwd = lweight)
arrows(x0=1.5, x1=2.5, y0=0, y1=0, length=0.1, lwd = lweight)
arrows(x0=1.5, x1=2.5, y0=-1, y1=-2, length=0.1, lwd = lweight)
text(x=1.8, y=1.8, expression('s'[1]^'a'), cex=2)
text(x=1.8, y=0.3, expression('s'[2]^'a'), cex=2)
text(x=1.8, y=-1.8, expression('s'[3]^'a'), cex=2)
...
Result:

Using layout seemed to cause problems.
Below is a modified version of your MWE that I think does what you are looking for.
circle <- function(){
theta <- seq(from = 0, to = 2*pi - pi/1000, by = 2*pi/1000)
x <- cos(theta)
y <- sin(theta)
circ <- data.frame(x, y)
}
shape_plot <- function(xs, ys, typ){
plot(xs, ys, type=typ, axes=FALSE, xlab='', ylab='', asp=1, lwd=4)
}
sample1 <- function(data, n){
pts <- sort(floor(runif(n, min=1, max=1000)))
xs <- data[[1]]
x <- xs[pts] + .1*runif(n, min=-1, max=1)
x <- c(x, x[1])
ys <- data[[2]]
y <- ys[pts] + .1*runif(n, min=-1, max=1)
y <- c(y, y[1])
samp <- data.frame(x, y)
}
circ <- circle()
shape_plot(circ[[1]], circ[[2]], 'l')
label <- rep('circle', 3)
samp <- list()
nudge <- cbind(5,c(3,0,-3))
for (i in 1:3){
s <- sample1(circ, 50)
samp[[i]] <- t(apply(s, 1, function(x) x + nudge[i,]))
}
samp <- Reduce(rbind, samp)
tot <- rbind(circ, samp)
shape_plot(tot[[1]], tot[[2]], typ = 'n')
points(circ, type = 'l')
points(samp, type = 'p')
arrows(1.2,1,3.7,2.5)
arrows(1.2,0,3.7,0)
arrows(1.2,-1,3.7,-2.5)
text(2.5,2, label = "arrow 1", srt = atan(1/1.5)*180/pi)
text(2.5,0.25, label = "arrow 2")
text(2.5,-2, label = "arrow 3", srt = atan(-1/1.5)*180/pi)

Related

Is there a way to add a line to a specific subplot in R

I'm new to R, trying to understand how plotting works.
I'm trying to plot a graph consisting of three subgraphs. I use the par(mfrow=c(1, 3)) to deal with that. But inside a loop, I want to add various lines to the three graphs. How do I choose which of the three subplots I apply the lines command to?
As an example, see the code below. All the lines commands get applied to the third subgraph, but this is of course not desired. See also the commands # apply lines to first figure! but how? and # apply lines to second figure! but how?
set.seed(1)
n <- 100
x <- seq(0, 4, length.out = n)
no_datasets <- 50
par(mfrow=c(1, 3))
for (i in 1:no_datasets) {
x <- seq(0, 4, length.out = n) # x <- seq(-pi, pi, length.out = n)
y <- sin(x)
errs <- rnorm(n, mean = 0, sd = 0.25) # rnorm(n) generates random numbers whose distribution is normal
t <- y + errs
df <- data.frame(x, y, t, errs)
model1 <- lm(t ~ poly(x, 1), data = df)
model5 <- lm(t ~ poly(x, 5), data = df)
model25 <- lm(t ~ poly(x, 25), data = df)
if (i == 1) {
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
}
t_hat <- predict(model1, df)
# apply lines to first figure! but how?
lines(df$x, t_hat, col="blue")
t_hat <- predict(model5, df)
# apply lines to second figure! but how?
lines(df$x, t_hat, col="blue")
t_hat <- predict(model25, df)
# apply lines to third figure!
lines(df$x, t_hat, col="blue")
}
Since the third plot is last, then it is just adding all the lines to the last plot. But if you nest everything in an if statement for each plot, then you will get the lines on each respective plot.
set.seed(1)
n <- 100
x <- seq(0, 4, length.out = n)
no_datasets <- 50
par(mfrow=c(1, 3))
for (i in 1:no_datasets) {
x <- seq(0, 4, length.out = n) # x <- seq(-pi, pi, length.out = n)
y <- sin(x)
errs <- rnorm(n, mean = 0, sd = 0.25) # rnorm(n) generates random numbers whose distribution is normal
t <- y + errs
df <- data.frame(x, y, t, errs)
model1 <- lm(t ~ poly(x, 1), data = df)
model5 <- lm(t ~ poly(x, 5), data = df)
model25 <- lm(t ~ poly(x, 25), data = df)
if (i == 1) {
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
t_hat <- predict(model1, df)
lines(df$x, t_hat, col="blue")
}
if (i == 2) {
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
t_hat <- predict(model5, df)
lines(df$x, t_hat, col="blue")
}
if (i == 3) {
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
t_hat <- predict(model25, df)
lines(df$x, t_hat, col="blue")
}
}
Ok, if I understand AndrewGB, it is not possible. So then the answer should be:
set.seed(1)
n <- 100
no_datasets <- 50
par(mfrow=c(1, 3))
polynomials <- c(1, 5, 25)
x <- seq(0, 4, length.out = n) # x <- seq(-pi, pi, length.out = n)
y <- sin(x)
for (i in 1:length(polynomials)) {
degree <- polynomials[i]
for (j in 1:no_datasets) {
errs <- rnorm(n, mean = 0, sd = 0.25) # rnorm(n) generates random numbers whose distribution is normal
t <- y + errs
df <- data.frame(x, y, t, errs)
model <- lm(t ~ poly(x, degree), data = df)
if (j == 1) {
plot(df$x, df$y, xlab = "x", ylab="", col="black", type="l")
}
t_hat <- predict(model, df)
lines(df$x, t_hat, col="blue")
}
}

How can I draw a 3D plot for this function?

Can anybody help me how to plot an image or 3D plot for this function that the output is in array class?
mu <- function(x,y,t,par){
return(par[1]+ par[2]*(x-t)+par[3]*x*t)
}
x <- seq(0,1,0.1)
y <- seq(0,1,0.1)
t <- seq(0,1,0.1)
mu.values <- array(NA, dim=c(length(x),length(y),length(t)))
for(i in 1:length(x)){
for(j in 1:length(y)){
for(k in 1:length(t)){
mu.values[i,j,k] <- mu(x[i], y[j], t[k], par=c(0.25,0.25,0))
}
}
}
Try this, now including slices:
library(reshape2)
library(plot3D)
m = melt(mu.values)
par(mfrow = c(1,2))
scatter3D(x = m$Var1, y = m$Var2, z = m$Var3, colvar = m$value, pch = 16, cex = 0.1)
# contour slices
x <- y <- z <- 1:11
slicecont3D (x, y, z, ys = 1:11, colvar = mu.values,
theta = 60, border = "gray")
Created on 2020-07-08 by the reprex package (v0.3.0)

Increasing vertical space between segments in base R

I'm trying to increase the space between each segment for each successive interval to avoid overplotting. Not sure how to approach this so that it will increment vertically in the loop.
Here is my code and some reproducible data:
set.seed(200)
x <- rnorm(100, 10)
truemean <- mean(x)
mat <- replicate(100, t.test(sample(x, rep = T))$conf.int)
mat <- t(mat)
myfunc <- function(mat, truemean) {
plot(x = c(min(mat[ , 1]), max(mat[ , 2])),
y = c(1, 100),
type = "n",
xlab = "0:100",
ylab = "0:100")
abline(v = truemean)
for (i in 1:nrow(mat)) {
if (mat[i, 1] <= truemean & mat[i, 2] >= truemean) {
segments(x0 = mat[i, 1], y0 = i,
x1 = mat[i, 2], y1 = i,
col = "blue",
lwd = 2)
} else {
segments(x0 = mat[i, 1], y0 = i,
x1 = mat[i, 2], y1 = i,
col = "red",
lwd = 2)
}
}
}
myfunc(mat, truemean)
You can certainly add anything you want in the segment call, but I'm not sure what you are asking. First we can greatly simplify your code:
set.seed(200)
x <- rnorm(100, 10)
truemean <- mean(x)
mat <- replicate(100, t.test(sample(x, rep = T))$conf.int)
mat <- t(mat)
yval <- seq(dim(mat)[1])
clr <- ifelse(mat[, 1] <= truemean & mat[, 2] >= truemean, "blue", "red")
plot(NA, xlim=c(min(mat[ , 1]), max(mat[ , 2])), ylim=c(0, length(yval)), type="n",
xlab="Conf Int", ylab="Trials")
abline(v=truemean)
segments(mat[, 1], yval, mat[, 2], yval, col=clr)
This produces the following plot:
You could replace yval with yval+.1 in the segments function to shift everything up. If there are so many lines that they overlap you can increase the height of the plot to make more room.

R plotly mesh3 coordinate lines through origin

I try to plot cube and ball around it in 3D using plotly in R, type = "mesh3D". I was successful to get the plots right, however, can't get the coordinate lines to go through the origin, point (0,0,0). Any suggestions on how to do that? Code example:
library(misc3d)
library(plotly)
grid <- 100
f.1 <- function(x, y, z){
(x^2+y^2+z^2)
}
f.2 <- function(x, y, z){
abs(x)+abs(y)+abs(z)
}
x <- seq(-1, 1, length=grid)
y <- seq(-1, 1, length=grid)
z <- seq(-1, 1, length=grid)
g <- expand.grid(x=x, y=y, z=z)
voxel.1 <- array(with(g, f.1(x,y,z)), c(grid,grid,grid))
voxel.2 <- array(with(g, f.2(x,y,z)), c(grid,grid,grid))
cont.1 <- computeContour3d(voxel.1, level=1, x=x, y=y, z=z)
idx.1 <- matrix(0:(nrow(cont.1)-1), ncol=3, byrow=TRUE)
cont.2 <- computeContour3d(voxel.2, level=1, x=x, y=y, z=z)
idx.2 <- matrix(0:(nrow(cont.2)-1), ncol=3, byrow=TRUE)
axx <- list(gridcolor="rgb(255,255,255)",zerolinecolor="rgb(0,0,0)")
axy <- list(gridcolor="rgb(255,255,255)",zerolinecolor="rgb(0,0,0)")
axz <- list(gridcolor="rgb(255,255,255)",zerolinecolor="rgb(0,0,0)")
plot.1 <- plot_ly(x = cont.1[,1], y = cont.1[,2], z = cont.1[,3],
i = idx.1[,1], j = idx.1[,2], k = idx.1[,3],
type = "mesh3d",opacity = 0.1,intensity=seq(0,1,length=4),showscale = FALSE,colors="red") %>%
add_trace(x = cont.2[,1], y = cont.2[,2], z = cont.2[,3],
i = idx.2[,1], j = idx.2[,2], k = idx.2[,3],color="blue",
type="mesh3d",opacity=0.7,intensity=seq(0,1,length=4),
showscale=FALSE,inherit=FALSE) %>%
layout(scene = list(xaxis=axx,yaxis=axy,zaxis=axz))
plot.1
It plots zerolines through (1,0,0), (0,1,0) and (0,0,1), but cant get it to plot through (0,0,0).

xyplot time series with positive values in green, negative in red, in R

Is there a neat way to color negative values in red and others in green for a (simplified) time series plot below, using lattice::xyplot?
set.seed(0)
xyplot(zoo(cumsum(rnorm(100))), grid=T)
Lattice is based on grid so you can use grid's clipping functionality
library(lattice)
library(grid)
set.seed(0)
x <- zoo(cumsum(rnorm(100)))
xyplot(x, grid=TRUE, panel = function(x, y, ...){
panel.xyplot(x, y, col="red", ...)
grid.clip(y=unit(0,"native"),just=c("bottom"))
panel.xyplot(x, y, col="green", ...) })
When using type="l" you only have one "line" and it's all one color, so you might instead choose to color points:
set.seed(0); require(zoo); require(lattice)
vals <- zoo(cumsum(rnorm(100)))
png()
xyplot(vals, type=c("l","p"), col=c("red", "green")[1+( vals>0)], grid=T)
dev.off()
I found a solution by, Sundar Dorai-Rag, a fellow now at Google, to a similar request (to color the enclosed areas above and below 0, for which his approach to getting the crossing values for the X's was to invert the results of approx ) as seen here: http://r.789695.n4.nabble.com/shading-under-the-lines-in-a-lattice-xyplot-td793875.html. Instead of coloring the enclosed areas, I gave the borders of the polygons the desired colors and left the interior "transparent":
lpolygon <- function (x, y = NULL, border = NULL, col = NULL, ...) {
require(grid, TRUE)
xy <- xy.coords(x, y)
x <- xy$x
y <- xy$y
gp <- list(...)
if (!is.null(border)) gp$col <- border
if (!is.null(col)) gp$fill <- col
gp <- do.call("gpar", gp)
grid.polygon(x, y, gp = gp, default.units = "native")
}
find.zero <- function(x, y) {
n <- length(y)
yy <- c(0, y)
wy <- which(yy[-1] * yy[-n - 1] < 0)
if(!length(wy)) return(NULL)
xout <- sapply(wy, function(i) {
n <- length(x)
ii <- c(i - 1, i)
approx(y[ii], x[ii], 0)$y
})
xout
}
trellis.par.set(theme = col.whitebg())
png();
xyplot(vals, panel = function(x,y, ...) {
x.zero <- find.zero(x, y)
y.zero <- y > 0
yy <- c(y[y.zero], rep(0, length(x.zero)))
xx <- c(x[y.zero], x.zero)
ord <- order(xx)
xx <- xx[ord]
xx <- c(xx[1], xx, xx[length(xx)])
yy <- c(0, yy[ord], 0)
lpolygon(xx, yy, col="transparent", border = "green")
yy <- c(y[!y.zero], rep(0, length(x.zero)))
xx <- c(x[!y.zero], x.zero)
ord <- order(xx)
xx <- xx[ord]
xx <- c(xx[1], xx, xx[length(xx)])
yy <- c(0, yy[ord], 0)
lpolygon(xx, yy, col = "transparent", border = "red")
panel.abline(h = 0) ;panel.grid(v=-1, h=-1 )
}); dev.off()
I tried writing a custom panel function for this that will break a line on a given value
panel.breakline <- function(x,y,breakat=0,col.line,upper.col="red",lower.col="green",...){
f <- approxfun(x,y)
ff <- function(x) f(x)-breakat
psign <- sign(y-breakat)
breaks <- which(diff(psign) != 0)
interp <- sapply(breaks, function(i) uniroot(ff,c(x[i], x[i+1]))$root)
starts <- c(1,breaks+1)
ends <- c(breaks, length(x))
Map(function(start,end,left,right) {
x <- x[start:end]
y <- y[start:end]
col <- ifelse(y[1]>breakat,upper.col,lower.col)
panel.xyplot(c(left, x, right) ,c(breakat,y,breakat), col.line=col,...)
}, starts, ends, c(NA,interp), c(interp,NA))
}
You can run with
library(zoo)
library(lattice)
set.seed(0)
zz<-zoo(cumsum(rnorm(100)))
xyplot(zz, grid=T, panel.groups=panel.breakline)
And you can change the break point or the colors as well
xyplot(zz, grid=T, panel.groups=panel.breakline,
breakat=2, upper.col="blue", lower.col="orange")
If one was to do it without points, then I'd stick to plot (instead of lattice) and use clip , like in one of the answers here :
Plot a line chart with conditional colors depending on values
dat<- zoo(cumsum(rnorm(100)))
plot(dat, col="red")
clip(0,length(dat),0,max(dat) )
lines(dat, col="green")

Resources