How would you replicate this graphic in R - r

I'm wondering how to do this graph in R?
I tought about using geom_bar from the ggplot package. However I don't know how to plot the changing thickness and the colors that reflect the growth for the years at the same time.
I'd appreciate any ideas.
Thank you.

Here are some steps to start. Nothing special, just some rectangles. There is a lot of information going on in this chart, most of it is in the text along the sides, usually meaning that the chart isnt very effective.
The only useful info the chart really shows is the yearly change in colors. You can do something like that below where I colored based on the rectangle height.
But +1 for finding another way to visualize your data.
set.seed(1)
nr <- 4
nc <- 50
mm <- matrix(sort(runif(nr * nc)) * 10, nr, nc)
nn <- matrix(sort(runif(nr * nc), decreasing = TRUE) * 10, nr, nc)
mm <- do.call('rbind', l <- list(mm, nn))[order(sequence(sapply(l, nrow))), ]
yy <- 50
mm <- rbind(mm, yy - colSums(mm))
nr <- nrow(mm)
plot(0:nc, type = 'n', ylim = c(0, yy), bty = 'n', axes = FALSE, ann = FALSE)
rect(s <- sequence(nc), 0, s + .95, mm[1, ], border = NA,
col = as.numeric(cut(mm[1, ], breaks = seq(0, 15, 3))))
axis(1, s + .5, labels = s + 2000, lwd = 0, lwd.ticks = 1)
axis(1, s, labels = NA, lwd = 0, lwd.ticks = .5, tcl = -.2)
# axis(2, las = 1, lwd = 0)
mtext('Share of private jobs', side = 2, at = par('usr')[3], adj = 0)
arrows(.5, 0, .5, yy, lwd = 2, xpd = NA, length = .1)
text(par('usr')[1:2] + c(.5, -.5), yy, labels = range(s) + 2000,
xpd = NA, pos = 3, adj = 0)
yy <- matrix(0, 1, nc)
for (ii in 2:nr) {
yy <- colSums(rbind(yy, mm[ii - 1, ]))
rect(s, yy + 1, s + .95, yy + mm[ii, ], border = NA,
col = as.numeric(cut(mm[ii, ], breaks = seq(0, 15, 3))))
}
(I hope no one submits this to junk charts :{ )
Also here is another incredibly simple way. Should have done this first, ie, create sample data, pad the rows, and let barplot take care of the rest. A lot less control over this approach, though.
mm <- matrix(sort(runif(10)), 2) * 10
nn <- matrix(.5, 2, ncol(mm))
mm <- do.call('rbind', l <- list(mm, nn))[order(sequence(sapply(l, nrow))), ]
yy <- 22
mm <- rbind(mm, yy - colSums(mm))
barplot(mm, col = 1:0, border = NA, axes = FALSE)

Related

How could I conduct meta-analysis on percentage outcomes using R?

My example data is as follows:
df <- data.frame(study = c("Hodaie","Kerrigan","Lee","Andrade","Lim"), SR = c(0.5460, 0.2270, 0.7540, 0.6420, 0.5000), SE = c(12.30, 15.70, 12.80, 13.80, 9.00), Patients = c(5, 5, 3, 6, 4))
I want to conduct the meta-analysis with SR(single group percentage), SE (standard error that I can compute based on sample size and percentage), and patients(sample size for each study), and I hope I could get the following forest plot (I found this example in an article, and they also have one group percentage data, but I can't find which R statement or argument they used):
Could anyone tell me which R statement or argument that I could use to conduct the meta-analysis and generate the forest plot above? Thank you!
I am sure there are plenty of ways to do this using packages but it can be accomplished in base R (and there are likely more elegant solutions using base R). The way I do it is to first build a blank plot much larger than the needed graphing portion, then overlay the relevant elements on it. I find one has more control over it this way. A basic example that could get you started is below. If you are new to R (based on your name NewRUser), I suggest running it line-by-line to see how it all works. Again, this is only one way and there are likely better approaches. Good luck!
Sample Data
#### Sample Data (modified from OP)
df <- data.frame(Study = c("Hodaie","Kerrigan","Lee","Andrade","Lim"),
SR = c(0.5460, 0.2270, 0.7540, 0.6420, 0.5000),
SE = c(12.30, 15.70, 12.80, 13.80, 9.00),
Patients = c(5, 5, 3, 6, 4),
ci_lo = c(30, -8.0, 50, 37, 32),
ci_hi = c(78, 53, 100, 91, 67))
### Set up plotting elements
n.studies <- nrow(df)
yy <- n.studies:1
seqx <- seq(-100, 100, 50)
## blank plot much larger than needed
plot(range(-550, 200), range(0, n.studies), type = 'n', axes = F, xlab = '', ylab = '') #blank plot, much bigger than plotting portion needed
# Set up axes
axis(side = 1, at = seqx, labels = seqx, cex.axis = 1, mgp = c(2, 1.5, 1)) # add axis and label (bottom)
mtext(side = 1, at = 0, 'Seizure Reduction', line = 2.5, cex = 0.85, padj = 1)
axis(side = 3, at = seqx, labels = seqx, cex.axis = 1, mgp = c(2, 1.5, 1)) # add axis and label (top)
mtext(side = 3, at = 0, 'Seizure Reduction', line = 2.5, cex = 0.85, padj = -1)
## add lines and dots
segments(df[, "ci_lo"], yy, df[,"ci_hi"], yy) # add lines
points(df[,"SR"]*100, yy, pch = 19) # add points
segments(x0 = 0, y0 = max(yy), y1 = 0, lty = 3, lwd = 0.75) #vertical line # 0
### Add text information
par(xpd = TRUE)
text(x = -550, y = yy, df[,"Study"], pos = 4)
text(x = -450, y = yy, df[,"SR"]*100, pos = 4)
text(x = -350, y = yy, df[,"SE"], pos = 4)
text(x = -250, y = yy, df[,"Patients"], pos = 4)
text(x = 150, y = yy, paste0(df[,"ci_lo"], "-", df[,"ci_hi"]), pos = 4)
text(x = c(seq(-550, -250, 100), 150), y = max(yy)+0.75,
c(colnames(df)[1:4], "CI"), pos = 4, font = 2)
# Add legend
legend(x = 50, y = 0.5, c("Point estimate", "95% Confidence interval"),
pch = c(19, NA), lty = c(NA, 19), bty = "n", cex = 0.65)

How to move y-axis labels away from R plot using lapply in R

I have the following code (Thanks to an answer from #Rawr in this question):
labes1 <- c("P(LNG)","","Volume(LNG)","","P(oil)","","Can.GDP","","US GDP","")
titles <- c("Levels","","","","","Log Difference","","","","")
par(mfrow = c(5, 2), mar = c(0.3, 6, 0, 2), oma = c(5, 0, 3, 2))
lapply(1:10, function(ii) {
x <- plotdata1[, ii, drop = FALSE]
plot(x, xlab = "Quarter", ylab = labes1[ii], axes = FALSE)
axis(2, las = 1)
box()
if (ii %in% 9:10) {
axis(1)
title(xlab = 'Quarter', xpd = NA)
}
if (ii %in% 1:2)
title(main = c('Levels', 'Log Difference')[ii], xpd = NA, line = 1)
})
This produces the following plot:
The obvious issue is the overlaying of the y-axis labels with the y-axis values. I have tried playing around with the mar() and oma() but these just change the margins around, I was hoping this would move things out of the way. How can I move the y-axis labels as separate from the plot? I will also be moving the margins a bit so that the white space between the two columns of plots will be closer together.
You can define the ylab separately, like what you're doing for the xlab, and set the line parameter to define its distance from the plot (as stated in this post).
I got a running example from combining your code and #rawr's from your previous question.
set.seed(1)
z <- ts(matrix(rt(200 * 10, df = 3), 200, 10), start = c(1961, 1), frequency = 12)
z <- z * 1e5 # to make "wide" y-axis labels
## vectors of x, y, and main labels
xl <- sprintf('x label %s', 1:10)
yl <- sprintf('y label %s', 1:10)
ml <- sprintf('main label %s', 1:10)
labes1 <- c("P(LNG)","","Volume(LNG)","","P(oil)","","Can.GDP","","US GDP","")
titles <- c("Levels","","","","","Log Difference","","","","")
par(mfrow = c(5, 2), mar = c(0.3, 6, 0, 2), oma = c(5, 0, 3, 2))
lapply(1:10, function(ii) {
x <- z[, ii, drop = FALSE]
plot(x, xlab = "Quarter", ylab = "", axes = FALSE) # set ylab to ""
axis(2, las = 1)
title(ylab = labes1[ii], line = 4) # set the line at an appropriate distance
box()
if (ii %in% 9:10) {
axis(1)
title(xlab = 'Quarter', xpd = NA)
}
if (ii %in% 1:2)
title(main = c('Levels', 'Log Difference')[ii], xpd = NA, line = 1)
})
The code above outputs the following graph for line = 4 :
and this plot for line = 3 :

R plot3d color gardient legend

I am having a 3D plot in which the points are colored acording to some extra vector. My problem is to add a color gradient legend. This is my code:
x = matrix(NA,100,6)
#x value
x[,1] = runif(100, 0, 10)
#y value
x[,2] = runif(100, 0, 10)
#z value
x[,3] = x[,1]+x[,2]
#additional value
x[,4] = runif(100, 0, 1)
#find out in which interval each additional value is
intervals = seq(0,1,1/10)
x[,5] = findInterval(x[,4], intervals)
colours = topo.colors(length(intervals))
x[,6] = colours[x[,5]]
library(rgl)
plot3d(as.numeric(x[,1]),as.numeric(x.stab.in[,2]), as.numeric(x[,3]),
type="p", col=x[,6], size=2, xlab = "x(t)", ylab = "y(t)",
zlab = "z(t)")
decorate3d(xlab = "x", ylab = "y", zlab = "z")
legend3d("topright", legend = intervals, pch = 16, col = colours, cex=1, inset=c(0.02))
grid3d(c("x", "y+", "z"),col = "gray")
The plot looks like this
but I want the legend in a gradient form. That means I don't want separate points for each color but one box in which the colors fade into each other.
Here is a possible solution if you are okay with using scatterplot3d package instead of rgl. It is basically same but non-interactive. Here is your code modified to produce your expected result.
x = matrix(NA,100,6)
#x value
x[,1] = runif(100, 0, 10)
#y value
x[,2] = runif(100, 0, 10)
#z value
x[,3] = x[,1]+x[,2]
#additional value
x[,4] = runif(100, 0, 1)
#find out in which interval each additional value is
intervals = seq(0,1,1/10)
x[,5] = findInterval(x[,4], intervals)
#produce gradient of colors
#you can define different colors (two or more)
gradient <- colorRampPalette(colors = c("yellow", "green", "blue"))
colours <- gradient(length(intervals))
x[,6] = colours[x[,5]]
library(scatterplot3d)
png('3d.png', width = 600, height = 400)
layout(matrix(1:2, ncol=2), width = c(3, 1), height = c(1, 1))
scatterplot3d(as.numeric(x[,1]),as.numeric(x[,2]), as.numeric(x[,3]), type = 'p',
cex.symbols = 1.25, color=x[,6], pch = 16, xlab = "x(t)", ylab = "y(t)", zlab = "z(t)")
plot(x = rep(1, 100), y = seq_along(x[,6]),
pch = 15, cex = 2.5,
col = gradient(length(x[,6])),
ann = F, axes = F, xlim = c(1, 2))
axis(side = 2, at = seq(1, nrow(x), length.out = 11),
labels = 1:11,
line = 0.15)
dev.off()
This will plot the following graph
Here is another solution if you want to plot a gradient on an interactive 3d plot, such as if you needed to animate the plot into a movie.
require(car)
require(rgl)
require(RColorBrewer)
require(mgcv)
require(magick) #Only for creating the animation of the plot as a gif
#Creating mock dataset
Example_Data <- data.frame(Axis1 = rnorm(100),
Axis2 = rnorm(100),
Axis3 = rnorm(100))
Example_Data$Value <- Example_Data$Axis1+Example_Data$Axis2
#Defining function that takes a vector of numeric values and converts them to
#a spectrum of rgb colors to help color my scatter3d plot
get_colors <- function(values){
v <- (values - min(values))/diff(range(values))
x <- colorRamp(rev(brewer.pal(11, "Spectral")))(v)
rgb(x[,1], x[,2], x[,3], maxColorValue = 255)
}
#Writing function that takes a vector of numeric values and a title and creates
#a gradient legend based on those values and the title and suitable for addition
#to a scatter3d plot via a call to bgplot3d()
#Note, I didn't have time to make this automatically adjust text position/size for different size
#plot windows, so values may need to be adjusted manually depending on the size of the plot window.
gradient_legend_3d <- function(values, title){
min_val <- min(values)
max_val <- max(values)
x <- colorRamp(brewer.pal(11, "Spectral"))((0:20)/20)
colors <- rgb(x[,1], x[,2], x[,3], maxColorValue = 255)
legend_image <- as.raster(matrix(colors, ncol=1))
plot(c(0,1),c(0,1),type = 'n', axes = F,xlab = '', ylab = '', main = '') #Generates a blank plot
text(x=0.92, y = seq(0.5, 1,l=5), labels = signif(seq(min_val, max_val,l=5), 2), cex = 1.5) #Creates the numeric labels on the scale
text(x = 0.85, y = 1, labels = title, adj = 1, srt = 90, cex = 1.5) #Determines where the title is placed
rasterImage(legend_image, 0.87, 0.5, 0.9,1) #Values can be modified here to alter where and how wide/tall the gradient is drawn in the plotting area
}
#Creating scatter3d plot
scatter3d(x = Example_Data$Axis1, y = Example_Data$Axis2, z = Example_Data$Axis3, xlab = "Axis1", ylab = "Axis2", zlab = "Axis3", surface = F, grid = F, ellipsoid = F, fogtype = "none", point.col = get_colors(Example_Data$Value))
#Changing size of plotting window and orientation to optimize for addition of static legend
#This may not work on another machine, so the window may need to be adjusted manually
par3d(windowRect = c(0,23,1536,824))
par3d(userMatrix = matrix(c(-0.98181450, -0.02413967, 0.18830180, 0, -0.03652956, 0.99736959, -0.06260729, 0, -0.18629514, -0.06834736, -0.98011345, 0, 0, 0, 0, 1), nrow = 4, ncol = 4, byrow = T))
#Adding legend
bgplot3d(gradient_legend_3d(Example_Data$Value, "Point Value"))
#Animating plot and saving as gif
movie3d(spin3d(axis = c(0,1,0), rpm = 5), duration = 12, dir = getwd(), fps = 5, convert = FALSE, clean = FALSE)
frames <- NULL
for(j in 0:60){
if(j == 1){
frames <- image_read(sprintf("%s%03d.png", "movie", j))
} else {
frames <- c(frames, image_read(sprintf("%s%03d.png", "movie", j)))
}
}
animation <- image_animate(frames, fps = 10, optimize = TRUE)
image_write(animation, path = "Example.gif")
for(j in 0:60){
unlink(sprintf("%s%03d.png", "movie", j))
}
See link to view 3d plot generated by this code:
gif of 3d plot with gradient color scale

Specifying axes in base plot for multiple plots

I'm trying to make a plot with specific axes, while keeping the aspect ratio as 1.
the problem is there is parts of the plot that i don't need and want to remove.
I can manage it using margin:
## Creating Data
x <- seq(1, 100, length.out = 100)
y <- seq(1, 400, length.out = 100)
## Playing with margins
par(fin = c(3.75, 5.3) , mar = c(2, 9, 1, 3) + 0.1 )
## Making
plot(y ~ x ,asp = 1)
abline(v = -10)
abline(v = 120)
But if i want to plot multiple plots i dont know how to remove it
## Using mfrow
par(mfrow = c(3,2))
for (i in 1:6) {
plot(y ~ x ,asp = 1,xlim = c(0,100), ylim = c(0,400))
abline(v = -10)
abline(v = 120)
}
How can i do it for the multiple plots?
This might be closer, using the layout method (see this question and layout R documentation):
x <- seq(1, 100, length.out = 100)
y <- seq(1, 400, length.out = 100)
plot.new()
par(mai = c(0.6,0.5,0.3,0.3))
layout(matrix(c(1,2,3,4,5,6), nrow = 2, ncol = 3, byrow = TRUE))
for (i in 1:6) {
plot(y ~ x ,asp = 1, ylim = c(0,400))
abline(v = -10)
abline(v = 120)
}
The par(mai=c(b,l,t,r)) option changes the size of the whitespace surrounding the subplots.

Line Graph Overlaying bar graph in base r

I have written the following code below. I would like to overlay a bar graph with a line graph. The code I have does it all but with just one problem. I would like the points on the line graph to be in the center of the bar graph, i.e. they should shift to the left a little bit. where Im I missing it? If this can be done in ggplot as well I would be happy too. but even base r would do
par(mai = c ( 1 , 2, 1, 1), omi = c(0, 0, 0, 0))
yy <- c(31,31,31,50,50,61,69,75,80,88,94,101,108,115,121,124,125,125,125,126,127)
name1 <- c ("15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35")
xx <- barplot(yy, ylab = "", names.arg = name1, ylim = c(0, 140),col="steelblue")
text(xx, yy + 3, labels = as.character(yy),srt=45)
mtext(2,text="",line=2)
par(new = T)
xx2 <- c(15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35)
yy2 <- c(379,474,579,725,922,1181,1473,1846,2316,2962,3688,4786,6069,7605,9504,10680,11074,11074,11074,11483,11484)
plot(xx2, yy2, xlim = c(14, 36), ylim = c(0, 14000),type ="n" , axes = F, xlab ="", ylab ="",col="blue",main="")
lines(xx2, yy2, lwd = 2,col="red",lty=1)
points(xx2, yy2, pch = 18, cex = 1,col="red")
text(xx2, yy2 + 4 , labels = as.character(yy2),srt=90)
par(new = T)
par(mai = c ( 1 , 1, 1, 1))
axis(2)
mtext(2,text="",line=2.5)
mtext("",side=1,col="black",line=2)
grid()
It can be quote tricky to get things to line up if you use barplot and a standard plot(). I recommend only calling plot once. In order to do this, you will need to rescale your yy2 values to the same scale as yy. Here's how you might do that
par(mai = c ( 1 , 2, 1, 1), omi = c(0, 0, 0, 0))
yy <- c(31,31,31,50,50,61,69,75,80,88,94,101,108,115,121,124,125,125,125,126,127)
name1 <- c ("15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35")
#draw bar plot
xx <- barplot(yy, ylab = "", names.arg = name1, ylim = c(0, 140),col="steelblue")
text(xx, yy + 3, labels = as.character(yy),srt=45)
mtext(2,text="",line=2)
xx2 <- xx #c(15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35)
yy2 <- c(379,474,579,725,922,1181,1473,1846,2316,2962,3688,4786,6069,7605,9504,10680,11074,11074,11074,11483,11484)
#transform data
yy2tx <- yy2/14000 * max(pretty(yy))
#draw line data
lines(xx2, yy2tx, lwd = 2,col="red",lty=1)
points(xx2, yy2tx, pch = 18, cex = 1,col="red")
text(xx2, yy2tx, labels = as.character(yy2),srt=90)
#draw axis for transformed data
par(mai = c ( 1 , 1, 1, 1))
axis(2, at=pretty(c(0,14000))/14000*max(pretty(yy)), labels=pretty(c(0,14000)))
grid()
This produces the following plot
The problem is that you have different x scale due to the different margins of the two plots.
Unless you want to find xx2 by hand... another solution to consider is to use a right y axis instead.
yy <- c(31,31,31,50,50,61,69,75,80,88,94,101,108,115,121,124,125,125,125,126,127)
name1 <- c ("15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35")
xx <- barplot(yy, ylab = "", names.arg = name1, ylim = c(0, 140),col="steelblue")
text(xx, yy + 3, labels = as.character(yy),srt=45)
mtext(2,text="",line=2)
par(new = T)
yy2 <- c(379,474,579,725,922,1181,1473,1846,2316,2962,3688,4786,6069,7605,9504,10680,11074,11074,11074,11483,11484)
plot(xx+0.5, yy2, "l", lwd = 2,col="red",lty=1,
axes=F, ylim=c(0, 14000), xlim=c(min(xx), max(xx)+1))
points(xx+0.5, yy2, pch = 18, cex = 1,col="red")
axis(4)
text(xx+0.5, yy2 + 4 , labels = as.character(yy2),srt=90)

Resources