Having a same y-axes height for subplots in R - r

I am trying to three subplots that differ in maximum y-axes to have same height. It seems that by default, R only writes y-axis ticks at even numbers but I would like to have the y-axes of the subplots to have the same height (and hence same margin between the plots and the title) even when the maximum y-axes are different.
Here are my codes:
df <- data.frame(mean.1 <- c(0.8, 0.7), sd.1 <- c(0.07, 0.1),
mean.2 <- c(14, 11), sd.2 <- c(5.2, 8.1),
mean.3 <- c(3.5, 5.5), sd.3 <- c(1.4, 0.3)
)
# Global setting
par(mfcol = c(1, 3),
mar = c(4, 4, 3, 2), tcl = -0.5, mgp = c(3, 1, 0),
oma = c(2, 2, 2, 2), las = 1
)
# Subplot 1
subplot.1 <- barplot(mean.1,
names.arg = c('A', 'B'),
main = 'Subplot 1',
ylab = 'Mean for subplot 1',
col = c('blue', 'red'),
border = NA,
ylim = c(0, (max(mean.1) + max(sd.1))*1.2)
)
# Error bars
arrows(subplot.1, mean.1 - sd.1, subplot.1, mean.1 + sd.1,
col = c('blue', 'red'),
length = 0.05, angle = 90,
code = 2
)
# Subplot 2
mean.2 <- c(14, 11)
sd.2 <- c(5.2, 8.1)
subplot.2 <- barplot(mean.2,
names.arg = c('A', 'B'),
main = 'Subplot 2',
ylab = 'Mean for subplot 2',
col = c('blue', 'red'),
border = NA,
ylim = c(0, (max(mean.2) + max(sd.2))*1.2)
)
# Error bars
arrows(subplot.2, mean.2 - sd.2, subplot.2, mean.2 + sd.2,
col = c('blue', 'red'),
length = 0.05, angle = 90,
code = 2
)
# Subplot 3
mean.3 <- c(3.5, 5.5)
sd.3 <- c(1.4, 0.3)
subplot.3 <- barplot(mean.3,
names.arg = c('A', 'B'),
main = 'Subplot 3',
ylab = 'Mean for subplot 3',
col = c('blue', 'red'),
border = NA,
ylim = c(0, (max(mean.3) + max(sd.3))*1.2)
)
# Error bars
arrows(subplot.3, mean.3 - sd.3, subplot.3, mean.3 + sd.3,
col = c('blue', 'red'),
length = 0.05, angle = 90,
code = 2
)
Here is what I currently get.

You can try ggplot. First I'm using dplyr and tidyr to transform the data according the required ggplot format. Then plotting the data using facet_wrap() with scales = "free_y" to get different y-axis scales.
library(tidyverse)
# The data
df = data.frame(mean.1 = c(0.8, 0.7), sd.1 = c(0.07, 0.1),
mean.2 = c(14, 11), sd.2 = c(5.2, 8.1),
mean.3 = c(3.5, 5.5), sd.3 = c(1.4, 0.3))
# Pipeline
library(tidyverse)
df %>% select(-starts_with("sd")) %>%
bind_cols(group=c("A","B")) %>%
gather(key, value, -group) %>%
bind_cols(sd=c(sd.1,sd.2,sd.3)) %>%
mutate(key=rep(paste("Subplot", 1:3), each = 2)) %>%
ggplot(aes(x=group, y=value, fill=group)) +
geom_bar(stat="identity") +
geom_errorbar(aes(ymin=value-sd, ymax=value+sd, col=group), width=0.1) +
theme_bw() + theme(legend.position="none") +
facet_wrap(~key, scales = "free_y")
Using base R I have no straightforward solution. I recommend to play around using ylim=c() and the axis() function in case of to small y-axis like follows:
par(mfrow=c(1, 3))
barplot(df$mean.1, ylim=c(0, round(max(df$mean.1 + df$sd.1))))
barplot(df$mean.2, ylim=c(0, round(max(df$mean.2 + df$sd.2))), axes=F)
axis(2, at=c(0, seq(1, 20, 2)))
barplot(df$mean.3, ylim=c(0, round(max(df$mean.3 + df$sd.3))))

Related

Creating a simple Graph in R

how can I create this in R?:
I can't seem to get the data.frame right in order to display the variables correctly.
It can be created step by step with "base R" functions. It can be done with data frames, but simple vectors will also do here.
create data sets for A and B
plot both with steps type="s"
optionally: suppress default axes and make your own, with labels from 0.95 to 1.00 only
add a legend
optionally: add the two hand-drawn red points
x <- seq(0.94, 1.0, 0.01)
A <- seq(0, 1, length.out = length(x))
x2 <- c(0.94, 1, 1.01)
B <- c(0, 1, 1)
plot(x, A, type = "s", xlim = c(0.94, 1.01), ylab = "F(x)", axes = FALSE)
lines(x2, B, type = "s", col = "blue")
axis(1, at=pretty(c(0.95, 1)))
axis(2)
box()
legend("topleft", lty=1,
legend = c("Lotterie A", "Lotterie B"),
col = c("black", "blue"))
points(c(1, 1), c(0, 1), col = "red")
Something like this?
df1 <- data.frame(x = rep(seq(0.95:1, by = 0.01), 2), cat = "A")
df1 <- df1[order(df1$x), ]
y <- c(rep(seq(0, 1, by = 1/6), 2))
df1$y <- sort(y)[2:13]
df2 <- data.frame(x = c(0, 1, 1, 1.5), y = c(0, 0, 1, 1), cat = "B")
df <- rbind(df1, df2)
library(ggplot2)
ggplot(df, aes(x, y, colour = cat)) +
geom_path() +
coord_cartesian(xlim = c(0.95, 1)) +
scale_y_continuous(breaks = seq(0, 1, by = 0.2))

Display more than three variables using different aesthetics in a ggplot2 bar chart

Can the following chart be generated using ggplot2:
There are two variables mapped onto the axises, one variable (Region) mapped onto the colour (using grouped bars) and one variable (Product) mapped onto some other aethetics (alpha, pattern, line style)
How would that be possible? An example using R is welcome.
Update
In my original question I did not think about facets. Of course with facets you are able to display four variables. The question should be reformulated as Display more than four variables using different aesthetics in a ggplot2 bar chart ...
Here is an approach by abusing facets to serve as an x-axis so you can both stack and "dodge" the data. You can look into the ggpattern package, but I'm not fluent in its use.
library(ggplot2)
df <- expand.grid(
region = c("North", "East", "South", "West"),
product = c("Red wine", 'White wine'),
year = 2013:2015
)
set.seed(42)
df$value <- runif(nrow(df))
ggplot(df, aes(region, value)) +
geom_col(aes(alpha = product, fill = region), width = 1) +
# Expand x axis to control the width of 'dodging'
scale_x_discrete(expand = c(0.5, 0), breaks = NULL, name = NULL) +
scale_alpha_manual(values = c(0.6, 1)) +
facet_grid(~ year, switch = "x") +
# 0 spacing gives impression it is a single panel
theme(panel.spacing.x = unit(0, "pt"))
Created on 2021-09-24 by the reprex package (v2.0.1)
EDIT: An alternative without using facets, but with use of a helper function to position everything on the x-axis:
helper <- function(center, offset, width = 0.6) {
if (!is.numeric(center)) {
center <- match(center, sort(unique(center)))
}
offset <- match(offset, sort(unique(offset)))
offset <- scales::rescale(offset, to = c(-0.5, 0.5) * width)
center + offset
}
ggplot(df, aes(helper(year, region), value)) +
geom_col(aes(alpha = product, fill = region), width = 0.15) +
scale_alpha_manual(values = c(0.6, 1)) +
scale_x_continuous(breaks = scales::breaks_width(1)) +
theme(panel.spacing.x = unit(0, "pt"))
Here's a base version that gets most of the way there
set.seed(1)
d <- replicate(15, rpois(2, 10))
s <- replace(rep(0.1, 15), 1:2 * 5 + 1, 1)
op <- par(mar = c(5, 4, 2, 7), las = 1)
bp <- barplot(colSums(d), space = s, col = 2:6)
barplot(d, space = s, add = TRUE, density = c(0, 10), col = 'black', border = 'black')
abline(h = 0)
axis(1L, bp[1:3 * 5 - 2], 13:15 + 2000, lwd = 0)
title(xlab = 'Year', cex.lab = 1.5)
l <- list(
list(
title = 'Region', fill = 2:6,
legend = c('North', 'South', 'East', 'West', 'Center')
),
list(
title = 'Product', density = c(20, 0),
legend = c('Red wine', 'White wine')
)
)
lg <- legend('topright', legend = '', bty = 'n', inset = c(-0.025, 0))
for (ii in seq_along(l)) {
lg <- do.call('legend', c(
list(x = lg$rect$left, y = lg$rect$top - lg$rect$h,
xpd = NA, bty = 'n', title.adj = 0), l[[ii]]
))
}
par(op)

How to plot a density function of two variables?

I have two variables with the same length, v1 = actual alpha and v2 = stimulated alpha.
v1= (0.1, 0.6, 0.8, 0.11)
v2= (0.3, 0.1, 0.5, 0.7)
I want to show a density function where these two are compared, kind replicating this picture:
To make the plotting easier, I would create a data frame like this:
v1 <- c(0.1, 0.6, 0.8, 0.11)
v2 <- c(0.3, 0.1, 0.5, 0.7)
df <- data.frame(x = c(v1, v2), group = rep(c("Actual", "Simulated"), each = 4))
Now you can plot the densities easily using ggplot:
library(ggplot2)
ggplot(df) +
stat_density(aes(x, linetype = group), geom = "line", position = "identity") +
scale_linetype_manual(values = c(1, 2)) +
theme_bw() +
theme(legend.position = c(0.9, 0.85))
Of course, this doesn't look much like the density plot you provided - that's just because the data in v1 and v2 are too short to have a central tendency. Here's exactly the same plot with some toy data that better matches the data used in your plot:
set.seed(69)
v1 <- rnorm(100, -0.1, 0.12)
v2 <- rnorm(100, 0, 0.06)
df <- data.frame(x = c(v1, v2), group = rep(c("Actual", "Simulated"), each = 100))
ggplot(df) +
stat_density(aes(x, linetype = group), geom = "line", position = "identity") +
scale_linetype_manual(values = c(1, 2)) +
theme_bw() +
theme(legend.position = c(0.9, 0.85)) +
scale_x_continuous(limits = c(-.6, .4))
Created on 2020-05-21 by the reprex package (v0.3.0)
Here's a base R solution (based on #Allan's second dataframe):
hist(df$x[df$group=="Simulated"],
freq = F,
xlab = "Alpha in %",
border = "white",
main = "Density function for Actual and Simulated data", cex.main = 0.9,
xlim = range(df$x[df$group=="Actual"]))
lines(density(df$x[df$group=="Simulated"]), lty = 2)
lines(density(df$x[df$group=="Actual"]), lty = 1)
legend("topleft", legend = c("Actual", "Simulated"), bty = "n", lty = c(1,2))
grid()
Alternatively, with a bit more color:
hist(df$x[df$group=="Simulated"],
freq = F,
xlab = "Alpha in %",
border = "white",
main = "Density function for Actual and Simulated Alpha", cex.main = 0.9,
xlim = range(df$x[df$group=="Actual"]))
bg <- par("usr")
rect(bg[1], bg[3], bg[2], bg[4], col="grey50", border = NA, density = 70)
grid()
lines(density(df$x[df$group=="Simulated"]), lty = 2, col = "blue")
lines(density(df$x[df$group=="Actual"]), lty = 1, col = "blue")
legend("topleft", legend = c("Actual", "Simulated"), bty = "n", lty = c(1,2), col = "blue")

How to change y axis limits in decimal points in R?

I can plot the barplot in Excel with decimal points in y-axis limits as shown below.
But I would not be able to change the y-axis limits in R.
Here is my code in R.
par(mfrow=c(1, 1), mar=c(7, 4, 5, 6))
mydata <- data.frame(Algorithm1=c(95.85, 96.94), Algorithm2=c(96.04, 96.84), Algorithm3=c(95, 95.30))
barplot(as.matrix(mydata), main="Precision", ylim=range(0:100),
beside=T, col=c("red", "blue"), las=1, border = 0, cex.lab=1, cex.axis=1, font=1,col.axis="black", ylab = "Percentage",
legend.text = c("X1", "X2"),
args.legend = list(x ='topright', bty='n', inset=c(-0.20,0)))
Thanks in advance for your answer.
You can also use ggplot2 and scales.
library(dplyr)
library(ggplot2)
library(scales)
mydata <- data.frame(Algorithm = rep(c('Algorithm1','Algorithm2','Algorithm3'), each=2),
variable_x = rep(c('X1','X2'),3),
values=c(0.9585, 0.9694,0.9604, 0.9684, 0.95, 0.9530))
mydata %>%
ggplot(aes(x=Algorithm,y=values,fill=variable_x))+
geom_bar(stat='identity', position='dodge')+
scale_y_continuous(labels = scales::percent, limits = c(0.94,0.975), oob = rescale_none)+
scale_fill_manual(values= c(X1='red',X2='blue'))
Set the limit of y and xpd = FALSE.
FALSE : all plotting is clipped to the plot region
TRUE : all plotting is clipped to the figure region
NA : all plotting is clipped to the device region
library(RColorBrewer)
color <- brewer.pal(3, "Set1")[2:1]
plot.new()
plot.window(xlim = c(0, 10), ylim = c(94, 97.5), yaxs = "i")
abline(h = seq(94, 97.5, 0.5), col = 8)
barplot(as.matrix(mydata), beside = T, col = color,
border = NA, legend.text = c("X1", "X2"),
args.legend = list(x = 'topright', bty = "n"), xpd = F, add = T)
You could do:
tickPoints <- 20 * (0:5)
par(mfrow = c(1, 1), mar = c(7, 4, 5, 6))
mydata <- data.frame(
Algorithm1 = c(95.85, 96.94),
Algorithm2 = c(96.04, 96.84),
Algorithm3 = c(95, 95.30)
)
barplot(
as.matrix(mydata), main = "Precision", beside = T, col = c("red", "blue"),
las = 1, border = 0, cex.lab = 1, cex.axis = 1, font = 1, col.axis = "black",
ylab = "Percentage", legend.text = c("X1", "X2"),
args.legend = list(x = 'topright', bty = 'n',inset = c(-0.20, 0)),
axes = FALSE,
ylim = range(tickPoints)
)
axis(side = 2, at = tickPoints, labels = sprintf("%0.1f", tickPoints))
Note the axes = FALSE in the call to barplot

My layout doesn't allow me to show xlab and ylab

It looks like something simple I am missing but have no idea how to deal with this.
So I used a layout() function and I managed to get the layout as I wanted as below picture. Iris data was used in my coding.
Problem is, it does not show me the x label and y label on the output when I use plot() functions after this. And xaxis and yaxis for plot() looks overlapping. I am not sure how to deal with this problem.
There was no problem for x and y labelling before introducing plot.new() and par() to set up the main name of my diagram. (i.e. before I use the code from plot.new() to title(), xlab and ylab were shown)
I used 6 different plots in my original code, including, the plot.new() for title(), but I omitted the rest of them for convenience
Here is my code below,
x <- iris$Sepal.Length
y <- iris$Species
x_min <- min(iris$Sepal.Length)
x_max <- max(iris$Sepal.Length)
y_min <- min(iris$Sepal.Width)
y_max <- max(iris$Sepal.Width)
layout(matrix(c(1,1,1,1,1,1,
2,2,3,3,4,4,
5,5,5,6,6,6), nc=6, byrow = TRUE), heights=c(lcm(1),1,1,1,1))
layout.show(6)
par("mar"=c(1,1,1,1,1,1))
plot.new()
plot.window(xlim=c(0,1), ylim=c(0,1))
text(x=0.5,y=0.5,"scatter and density plots for Sepal and Length and Sepal Width" ,font=2, cex=1.5)
plot(...)
You can use the xlab and ylab arguments in title. However, the way you have constructed the plot means that when you reset par at the end, these are drawn off the page due ti their position relative to your custom axis. If you simply leave par alone, you get:
den1 = density(CDE1$V1)
den2 = density(CDE1$V2)
col1 = hsv(h = 0.65, s = 0.6, v = 0.8, alpha = 0.5)
col2 = hsv(h = 0.85, s = 0.6, v = 0.8, alpha = 0.5)
plot.new()
plot.window(xlim = c(25,65), ylim = c(0, 0.14))
axis(side = 1, pos = 0, at = seq(from = 25, to = 65, by = 5), col = "gray20",
lwd.ticks = 0.25, cex.axis = 1, col.axis = "gray20", lwd = 1.5)
axis(side = 2, pos = 25, at = seq(from = 0, to = 0.14, by = 0.02),
col = "gray20", las = 2, lwd.ticks = 0.5, cex.axis = 1,
col.axis = "gray20", lwd = 1.5)
polygon(den1$x, den1$y, col = col1, border ="black",lwd = 2)
polygon(den2$x, den2$y, col = col2, border ="black",lwd = 2)
text(52, 0.10, labels ="CDET", col =col1, cex = 1.25,font=2)
text(35, 0.03, labels ="SDFT", col =col2, cex = 1.25,font=2)
title(main = "Gestational Day 100/283",
xlab = "Fibril Diameter (nm)",
ylab = "density")
Of course, you could get a similar plot with less code and much easier adjustments using ggplot:
library(ggplot2)
ggplot(tidyr::pivot_longer(CDE1, 1:2), aes(value, fill = name)) +
geom_density() +
scale_fill_manual(values = c(col1, col2), labels = c("CDET", "SDFT")) +
scale_x_continuous(breaks = seq(25, 65, 5), limits = c(25, 65)) +
scale_y_continuous(breaks = seq(0, 0.14, 0.02), limits = c(0, 0.14)) +
theme_classic(base_size = 16) +
labs(title = "Gestational Day 100/283", x = "Fibril Diameter (nm)",
fill = NULL) +
theme(plot.title = element_text(hjust = 0.5))
Data used
Obviously, we don't have your data, so I had to create a reproducible approximation:
set.seed(123)
CDE1 <- data.frame(V1 = rnorm(20, 47.5, 4), V2 = rnorm(20, 44, 5))

Resources