incomprehensible behavior of the for loop [duplicate] - r

This question already has answers here:
Storing ggplot objects in a list from within loop in R
(4 answers)
Closed 5 years ago.
I have really strange problem... I'm trying to create plots in a loop and put them to the list and after that drew them all on one page. when I do it:
for (i in 1:ncol(df)){
plot[[i]] <- ggplot(df, aes(x = df[,i], y=(..count..)/sum(..count..))) +
geom_bar(fill="#003366") +
labs(title = dfcolnames[i], x = "Variable Values", y = "Frequency")
}
return(multiplot(plotlist=plot, cols = 2))
I get 3 this same plots, but when I do it without for loop:
plot[[1]] <- ggplot(df, aes(x = df[,1], y=(..count..)/sum(..count..))) + geom_bar(fill="#003366") + labs(title = dfcolnames[1], x = "Variable Values", y = "Frequency")
plot[[2]] <- ggplot(df, aes(x = df[,2], y=(..count..)/sum(..count..))) + geom_bar(fill="#003366") + labs(title = dfcolnames[2], x = "Variable Values", y = "Frequency")
plot[[3]] <- ggplot(df, aes(x = df[,3], y=(..count..)/sum(..count..))) + geom_bar(fill="#003366") + labs(title = dfcolnames[3], x = "Variable Values", y = "Frequency")
return(multiplot(plotlist=plot, cols = 2))
I get correct plots (three different). I don't understand what is going on.... Any ideas?

The problem is that aes() performs lazy evaluation. The values you pass to aes() are only resolved when you actually draw the plot. You really should only be passing symbol names to aes(). This is because after the loop ends, i will be fixed at it's last value from the loop and you'll just get the same plot three times.
You could instead use aes_string and loop over the column names
plot <- lapply(names(df), function(colname) {
ggplot(df, aes_string(x = colname, y="(..count..)/sum(..count..)")) +
geom_bar(fill="#003366") +
labs(title = colname, x = "Variable Values", y = "Frequency")
})
Tested with
set.seed(10)
df <- data.frame(a=rpois(100, 5), b=rpois(100, 7), c=rpois(100, 3))

Related

R: ggplot - Different Title for each object in a plot list

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)
})

Handle ggplot2 axis text face programmatically

(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")

How to mix a character and a numerical variable in axis label of ggplot2 [duplicate]

This question already has an answer here:
How to use simultaneously superscript and variable in a axis label with ggplot2
(1 answer)
Closed 5 years ago.
Hi I have written a simple function to make a plot of a-th polynomial of x in ggplot2. Following is my code:
PlotPower <- function(x,a){
y <- x^a
dat <- data.frame(x,y)
f <- function(x) x^a
ggplot(dat,aes(x,y)) + geom_point() + stat_function(fun = f, colour = "red") +
scale_x_continuous(breaks = pretty(dat$x, n = 10)) +
scale_y_continuous(breaks = pretty(dat$y, n = 10)) +
labs(x = "x", y = expression(x^a), title = "Polynomial plot of x")
}
Now say I am calling PlotPower(1:10,3), the plot looks like
But I want y axis label to automatically change into x^3. I have not been able to do that. I have tried to write
labs(y = "x"^a)
but I am getting error message: Error in "x"^a : non-numeric argument to binary operator, which is expected as I am mixing a character variable with a numerical variable.
Please help me writing the ggplot2 y axis label properly inside my PlotPower function so that with whatever "a" I call the PlotPower function, I get y axis label as x^a, where x is the character x but a is the value of argument a.
Replace the expression with bquote. The variable inside .(a) is the one that gets substituted. For more variations -like the ones mentioned in the comments- look at this answer.
PlotPower <- function(x,a){
y <- x^a
dat <- data.frame(x,y)
ylab = paste("x", a, sep='^')
f <- function(x) x^a
ggplot(dat,aes(x,y)) + geom_point() + stat_function(fun = f, colour = "red") +
scale_x_continuous(breaks = pretty(dat$x, n = 10)) +
scale_y_continuous(breaks = pretty(dat$y, n = 10)) +
labs(x = "x", y = bquote(x^.(a)), title = "Polynomial plot of x")
}

how to add superscript into facet labels of facet_wrap? [duplicate]

This question already has answers here:
Changing facet label to math formula in ggplot2
(5 answers)
Closed 9 years ago.
I have a dataset from which I would like to plot small multiples, specifically in a 2-by-2 array, like this:
mydf <- data.frame(letter = factor(rep(c("A", "B", "C", "D"), each = 20)), x = rnorm(80), y = rnorm(80))
ggplot(mydf, aes(x = x, y = y)) + geom_smooth(method = "lm") + geom_point() + facet_wrap(~ letter, ncol = 2)
However, I want each facet label to include an expression, such as
expression(paste("A or ", alpha))
I can make this happen using facet_grid() via
f_names <- list('A' = expression(paste("A or ", alpha)), 'B' = expression(paste("B or ", beta)), 'C' = expression(paste("C or ", gamma)), 'D' = expression(paste("D or ", delta)))
f_labeller <- function(variable, value){return(f_names[value])}
ggplot(mydf, aes(x = x, y = y)) + geom_smooth(method = "lm") + geom_point() + facet_grid(~ letter, labeller = f_labeller)
But then I lose the 2-by-2 array. How can I rename the facet_wrap() facet labels with an expression? Or, how can I solve this by recreating the 2-by-2 array using facet_grid(), but only faceting by a single variable?
(This question builds off of the parenthetical note in #baptiste's answer to this previous question.)
Thanks!
In order to do what I asked, first load this labeller function from #Roland first appearing here:
facet_wrap_labeller <- function(gg.plot,labels=NULL) {
#works with R 3.0.1 and ggplot2 0.9.3.1
require(gridExtra)
g <- ggplotGrob(gg.plot)
gg <- g$grobs
strips <- grep("strip_t", names(gg))
for(ii in seq_along(labels)) {
modgrob <- getGrob(gg[[strips[ii]]], "strip.text",
grep=TRUE, global=TRUE)
gg[[strips[ii]]]$children[[modgrob$name]] <- editGrob(modgrob,label=labels[ii])
}
g$grobs <- gg
class(g) = c("arrange", "ggplot",class(g))
g
}
Then save the original ggplot() object:
myplot <- ggplot(mydf, aes(x = x, y = y)) + geom_smooth(method = "lm") + geom_point() + facet_wrap(~ letter, ncol = 2)
Then call facet_wrap_labeller() and feed the expression labels as an argument:
facet_wrap_labeller(myplot, labels = c(expression(paste("A or ", alpha)), expression(beta), expression(gamma), expression(delta)))
The expressions should now appear as the facet_wrap() labels.

How to add expressions to labels in facet_wrap? [duplicate]

This question already has answers here:
Changing facet label to math formula in ggplot2
(5 answers)
Closed 9 years ago.
I have a dataset from which I would like to plot small multiples, specifically in a 2-by-2 array, like this:
mydf <- data.frame(letter = factor(rep(c("A", "B", "C", "D"), each = 20)), x = rnorm(80), y = rnorm(80))
ggplot(mydf, aes(x = x, y = y)) + geom_smooth(method = "lm") + geom_point() + facet_wrap(~ letter, ncol = 2)
However, I want each facet label to include an expression, such as
expression(paste("A or ", alpha))
I can make this happen using facet_grid() via
f_names <- list('A' = expression(paste("A or ", alpha)), 'B' = expression(paste("B or ", beta)), 'C' = expression(paste("C or ", gamma)), 'D' = expression(paste("D or ", delta)))
f_labeller <- function(variable, value){return(f_names[value])}
ggplot(mydf, aes(x = x, y = y)) + geom_smooth(method = "lm") + geom_point() + facet_grid(~ letter, labeller = f_labeller)
But then I lose the 2-by-2 array. How can I rename the facet_wrap() facet labels with an expression? Or, how can I solve this by recreating the 2-by-2 array using facet_grid(), but only faceting by a single variable?
(This question builds off of the parenthetical note in #baptiste's answer to this previous question.)
Thanks!
In order to do what I asked, first load this labeller function from #Roland first appearing here:
facet_wrap_labeller <- function(gg.plot,labels=NULL) {
#works with R 3.0.1 and ggplot2 0.9.3.1
require(gridExtra)
g <- ggplotGrob(gg.plot)
gg <- g$grobs
strips <- grep("strip_t", names(gg))
for(ii in seq_along(labels)) {
modgrob <- getGrob(gg[[strips[ii]]], "strip.text",
grep=TRUE, global=TRUE)
gg[[strips[ii]]]$children[[modgrob$name]] <- editGrob(modgrob,label=labels[ii])
}
g$grobs <- gg
class(g) = c("arrange", "ggplot",class(g))
g
}
Then save the original ggplot() object:
myplot <- ggplot(mydf, aes(x = x, y = y)) + geom_smooth(method = "lm") + geom_point() + facet_wrap(~ letter, ncol = 2)
Then call facet_wrap_labeller() and feed the expression labels as an argument:
facet_wrap_labeller(myplot, labels = c(expression(paste("A or ", alpha)), expression(beta), expression(gamma), expression(delta)))
The expressions should now appear as the facet_wrap() labels.

Resources