I wrote a code to get boxplots for visualisation in R. The code is running but I am not getting any boxplots. Can you please help identify where I went wrong.
create_boxplots <- function(x, y){
ggplot(data = forest_fires) +
aes_string(x = x, y = y) +
geom_boxplot() +
theme(panel.background = element_rect(fill = "white"))
}
x_var_month <- names(forest_fires)[3]
y_var <- names(forest_fires)[5:12]
month_box <- map2(x_var_month, y_var, create_boxplots)
The code is ok, just that when you call ggplot inside a function, you return the object and it is stored in a list. You need to print it. For example:
library(ggplot2)
library(purrr)
library(gridExtra)
create_boxplots <- function(x, y){
ggplot(data = forest_fires) +
aes_string(x = x, y = y) +
geom_boxplot() +
theme(panel.background = element_rect(fill = "white"))
}
forest_fires = data.frame(matrix(runif(1300),ncol=13))
forest_fires[,3] = factor(sample(1:12,nrow(forest_fires),replace=TRUE))
x_var_month <- names(forest_fires)[3]
y_var <- names(forest_fires)[5:12]
month_box <- map2(x_var_month, y_var, create_boxplots)
This shows you the plot for y_var[1]
month_box[[1]]
#or print(month_box[[1]])
To get everything in 1 plot do:
grid.arrange(grobs=month_box)
Related
currently I am trying to making a plot list with ggplot from a list of data frames (94 time series). Then I want to export the plots to a PDF. This, so far, was successful using following code:
plot.list = lapply(HR_clean, function(x) {
y = length(x)
z = data.frame("HR" = x, "Time" = rep(1:y, 1))
ggplot(z, aes(x = Time, y = HR)) +
theme_bw() +
geom_line(linetype = "solid") +
ggtitle("Plot Title")
})
ggsave(
filename = "plots2.pdf",
plot = marrangeGrob(plot.list, nrow=1, ncol=1),
width = 15, height = 9
)
However, I also want that the main title of each plot is equal name of the corresponding list object. Perhaps anyone knows a smart solution for this problem.
Best,
Johnson
I couldn't test this (since you are not providing example data) but this should work using purrr's imap():
plot.list <- purrr::imap(HR_clean, function(x, name) {
y <- length(x)
z <- data.frame("HR" = x, "Time" = rep(1:y, 1))
ggplot(z, aes(x = Time, y = HR)) +
theme_bw() +
geom_line(linetype = "solid") +
ggtitle(name)
})
I am trying to create some ggplots automaticly. Here is my working code example for adding stat_functions:
require(ggplot2)
p1 <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x)) + theme_minimal()+
stat_function(fun= function(x){1*x},lwd=1.25, colour = "navyblue") +
stat_function(fun= function(x){2*x},lwd=1.25, colour = "navyblue") +
stat_function(fun= function(x){3*-x},lwd=1.25, colour = "red")
p1
As you can see the stat_functions all use (nearly) the same function just with a different parameter.
Here is what i have tried to write:
f <- function(plot,list){
for (i in 1:length(list)){
plot <- plot + stat_function(fun= function(x){x*list[i]})
}
return(plot)
}
p1 <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x)) + theme_minimal()
p2 <- f(p1,c(1,2,3))
p2
This however doesnt return 3 lines, but only one. Why?
Your question is a bit confusing, because the first plot actually contains some other variable bits, but in your function you have a single stat_summary call for only one variable element.
Anyways. Keep the ggplot main object separate and create a list of additional objects, very easy for example with lapply. Add this list to your main plot as usual.
Check also https://ggplot2-book.org/programming.html
library(ggplot2)
p <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x)) + theme_minimal()
ls_sumfun <- lapply(1:3, function(y){
stat_function(fun= function(x){y*x}, lwd=1.25, colour = "navyblue")
}
)
p + ls_sumfun
Created on 2021-04-26 by the reprex package (v2.0.0)
In R, you can pass functions as arguments. You can also return functions from functions. This might make your code simpler and cleaner.
Here's an example:
p1 <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x))
add_stat_fun <- function (ggp, f) {
ggp + stat_function(fun = f)
}
make_multiply_fun <- function (multiplier) {
force(multiplier) # not sure if this is required...
f <- function (x) {multiplier * x}
return(f)
}
my_funs <- lapply(1:3, make_multiply_fun)
# my_funs is now a list of functions
add_stat_fun(p1, my_funs[[1]])
add_stat_fun(p1, my_funs[[2]])
add_stat_fun(p1, my_funs[[3]])
(x-posted to community.rstudio.com)
I'm wondering if it's possible to change the axis text in ggplot2 programatically or if there is some native way to do this in ggplot2. In this reprex, the idea is that I want to bold the axis text of a variable y that has an absolute value of x over 1.5. I can add it in manually via theme(), and that works fine:
library(ggplot2)
library(dplyr)
library(forcats)
set.seed(2939)
df <- data.frame(x = rnorm(15), y = paste0("y", 1:15), group = rep(1:3, 5))
df <- mutate(df, big_number = abs(x) > 1.5, face = ifelse(big_number, "bold",
"plain"))
p <- ggplot(df, aes(x = x, y = fct_inorder(y), col = big_number)) + geom_point() +
theme(axis.text.y = element_text(face = df$face))
p
Plot 1 with no facets
But if I facet it by group, y gets reordered and ggplot2 has no idea how face is connected to df and thus y, so it just bolds in the same order as the first plot.
p + facet_grid(group ~ .)
Plot 2 with facets
And it's worse if I use a different scale for each.
p + facet_grid(group ~ ., scales = "free")
Plot 3 with facets and different scales
What do you think? Is there a general way to handle this that would work consistently here?
Idea: Don't change theme, change y-axis labels. Create a call for every y with if/else condition and parse it with parse.
Not the most elegant solution (using for loop), but works (need loop as bquote doesn't work with ifelse). I always get confused when trying to work with multiple expressions (more on that here).
Code:
# Create data
library(tidyverse)
set.seed(2939)
df <- data.frame(x = rnorm(15), y = paste0("y", 1:15), group = rep(1:3, 5)) %>%
mutate(yF = fct_inorder(y),
big_number = abs(x) > 1.5)
# Expressions for y-axis
# ifelse doesn't work
# ifelse(df$big_number, bquote(bold(1)), bquote(plain(2)))
yExp <- c() # Ignore terrible way of concatenating
for(i in 1:nrow(df)) {
if (df$big_number[i]) {
yExp <- c(yExp, bquote(bold(.(as.character(df$yF[i])))))
} else {
yExp <- c(yExp, bquote(plain(.(as.character(df$yF[i])))))
}
}
# Plot with facets
ggplot(df, aes(x, yF, col = big_number)) +
geom_point() +
scale_y_discrete(breaks = levels(df$yF),
labels = parse(text = yExp)) +
facet_grid(group ~ ., scales = "free")
Result:
Inspired by #PoGibas, I also used a function in scale_y_discrete(), which works, too.
bold_labels <- function(breaks) {
big_nums <- filter(df, y %in% breaks) %>%
pull(big_number)
labels <- purrr::map2(
breaks, big_nums,
~ if (.y) bquote(bold(.(.x))) else bquote(plain(.(.x)))
)
parse(text = labels)
}
ggplot(df, aes(x, fct_inorder(y), col = big_number)) +
geom_point() +
scale_y_discrete(labels = bold_labels) +
facet_grid(group ~ ., scales = "free")
I'm using ggplot2 within a function and trying to create average lines for the y-axis. I'm running into trouble seemingly because the variable that defines the y-axis is one of the function args, and I can't call it directly within ggplot.
library(ggplot2)
west <- data.frame(
spend = sample(50:100,50,replace=T),
trials = sample(100:200,50,replace=T),
courts = sample(25:50,50,replace=T),
country = sample(c("usa","canada","uk"),50,replace = T)
)
Here's a basic version of the function I'm working with:
ggfun <- function(data, xvar, yvar) {
newplot <- ggplot(data=west, aes_string(x=xvar, y=yvar)) +
geom_point(shape=21, fill="blue") +
newplot
}
and calling that as below works:
ggfun(west, "spend", "trials")
But when I try to add in geom_hline, I get an error:
ggfun <- function(data, xvar, yvar) {
newplot <- ggplot(data=west, aes_string(x=xvar, y=yvar)) +
geom_point(shape=21, fill="blue") +
geom_hline(yintercept=mean(yvar))
newplot
}
ggfun(west, "spend", "trials")
Warning messages:
1: In mean.default(data$yvar) :
argument is not numeric or logical: returning NA
2: Removed 1 rows containing missing values (geom_hline).
Is it not possible to call data this way within a function using ggplot?
aes_string substitutes the whole string, not just the var. You can create the right string using paste:
library(ggplot2)
west <- data.frame(
spend = sample(50:100,50,replace=T),
trials = sample(100:200,50,replace=T),
courts = sample(25:50,50,replace=T),
country = sample(c("usa","canada","uk"),50,replace = T)
)
ggfun <- function(data, xvar, yvar) {
newplot <- ggplot(data=data, aes_string(x=xvar, y=yvar)) +
geom_point(shape=21, fill="blue") +
geom_hline(aes_string(yintercept = paste0('mean(', yvar, ')')))
newplot
}
ggfun(west, "spend", "trials")
yvar is a string, it works exactly as if you were doing this, not in a function:
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_hline(yintercept = mean("mpg"))
Warning messages:
1: In mean.default("mpg") :
argument is not numeric or logical: returning NA
2: Removed 1 rows containing missing values (geom_hline).
I'd recommend pre-computing the mean, that way you can pass a value to the yintercept:
ggfun <- function(data, xvar, yvar) {
mean_yvar = mean(data[[yvar]])
newplot <- ggplot(data = west, aes_string(x = xvar, y = yvar)) +
geom_point(shape=21, fill="blue") +
geom_hline(yintercept = mean_yvar)
newplot
}
ggfun(west, "spend", "trials")
# works fine
I am trying to write a function in ggplot2 and obtain this error message:
Error in layout_base(data, vars, drop = drop) :
At least one layer must contain all variables used for facetting
Here is my code:
growth.plot<-function(data,x,y,fac){
gp <- ggplot(data = data,aes(x = x, y = y))
gp <- gp + geom_point() + facet_wrap(~ fac)
return(gp)
}
growth.plot(data=mydata, x=x.var, y=y.var,fac= fac.var)
If I try without the function, the plot appears perfectly
gp1 <- ggplot(data = mydata,aes(x = x.var), y = y.var))
gp1+ geom_point()+ facet_wrap(~ fac.var) # this works
Here is reproducible solution where your x, y, and fac arguments must be passed as character:
library(ggplot2)
make_plot = function(data, x, y, fac) {
p = ggplot(data, aes_string(x=x, y=y, colour=fac)) +
geom_point(size=3) +
facet_wrap(as.formula(paste("~", fac)))
return(p)
}
p = make_plot(iris, x="Sepal.Length", y="Petal.Length", fac="Species")
ggsave("iris_plot.png", plot=p, height=4, width=8, dpi=120)
Thanks to commenters #Roland and #aosmith for pointing the way to this solution.