say I want to plot two layers in ggplot, one containing points and another one containing lines if a certain criteria is fulfilled.
The code without the criteria could look like this:
library("ggplot2")
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p +
geom_point()+
geom_line()
now the condition for plotting the points and not only the lines would be, that an object called tmp.data does not equal the expression "no value".
tmp.data<-c(1,2,3) # in this case the condition is fulfilled
# attempt to plot the two layers including the condition in the plotting function
p+
if(tmp.data[1]!="no value"){ geom_point()+}
geom_line()
fails....
Error: unexpected '}' in:
"p+
if(tmp.data[1]!="no value"){ geom_point()+}"
geom_line()
geom_line:
stat_identity:
position_identity: (width = NULL, height = NULL)
This was done using ggplot2 2.1.0. I think you can do exactly what the OP wished, just by switching the parenthesis so that they encompass the entire if statement.
Here is an example that add a horizontal line depending on if Swtich is T or F. First, where the condition is TRUE
library(ggplot2)
df<-data.frame(x=1:10,y=11:20)
Switch=T
ggplot(df,aes(x,y))+
{if(Switch)geom_hline(yintercept=15)}+
geom_point()
Now, the same thing but the condition is FALSE
df<-data.frame(x=1:10,y=11:20)
Switch=F
ggplot(df,aes(x,y))+
{if(Switch)geom_hline(yintercept=15)}+
geom_point()
What you are seeing is a syntax error. The most robust way I can think of is:
tmp.data<-c(1,2,3)
if(tmp.data[1]!="no value") {
p = p + geom_point()
}
p + geom_line()
So you compose the object p in a sequence, only adding geom_point() when the if statements yields TRUE.
Following the ggplot2 book, you can create a function which returns a list. Any NULL components will be ignored.
library(ggplot2)
library(ggplot2movies)
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
# create function to add points conditionally
# If the list contains any NULL elements, they’re ignored.
my_plot <- function(point = FALSE){
list(
geom_line(),
if (point)
geom_point()
)
}
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p + my_plot()
p + my_plot(point = TRUE)
Created on 2020-02-25 by the reprex package (v0.3.0)
library(ggplot2)
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
tmp.data<-c(1,2,3) # in this case the condition is fulfilled
p <- ggplot(mry, aes(x=year, y=number, group=rating))
# this won't "loop through" the data points but it's what you asked for
if (tmp.data[1]!="no value") {
p <- p + geom_point() + geom_line()
} else {
p <- p + geom_line()
}
p
but perhaps this is more like what you really want?
mry$rating <- factor(mry$rating)
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p <- p + geom_line()
p <- p + geom_point(data=mry[!(mry$rating %in% tmp.data),],
aes(x=year, y=number, group=rating, color=rating), size=2)
p <- p + scale_color_brewer()
p
An alternative example using iris dataset creating a plot with conditional components, some with more than one line, and with multiple switches.
library("ggplot2")
p <- ggplot(iris,
aes(x=Sepal.Length,
y=Sepal.Width,
colour=Species))
# set plot switches to turn on or off various components
include_points = TRUE
include_lines = FALSE
include_density = TRUE
include_facet = TRUE
p +
list(
if(include_points){geom_point()},
if(include_lines){geom_line()},
if(include_facet){
# multi-line conditional elements can be included as a list
list(
facet_grid(rows = "Species"),
labs(subtitle = "This plot has been faceted, and colour has been removed"),
aes(colour = NULL)
)
},
if(include_density){geom_density2d()}) +
# additional elements common to all possible conditional combinations
# can be added as well
theme_minimal() +
labs(title = "Plot with conditional elements")
With the conditions as follows:
include_points = TRUE
include_lines = FALSE
include_density = TRUE
include_facet = TRUE
The following plot is generated:
Faceted plot with density and points
Setting include_facet = FALSE the following plot is generated:
Coloured plot with density - no facets
Related
Problem
I am creating a boxplot of 14 water chemistry elements. Each element has a different dataframe. I have create a for loop to loop through each data frame and plot the appropriate graph. I want to add each of the plots to a list of plots that I have created outside of the for loop.
Working Code
# Libraries
library(dplyr)
library(ggplot2)
library(readr)
# read in all files
myFiles <- list.files(pattern= ".csv")
# create a list of all 14 data frames
dataList <- list()
for (var in myFiles) {
filepath <- file.path(paste(var))
dataList[[var]] <- read.csv(filepath, sep=",", header=T)
}
# Plot the data as a boxplot
for (data in dataList){
p <-
data %>%
ggplot(aes_string(x='Month', y=data[,5])) +
geom_boxplot() +
theme_classic() +
labs(y= colnames(data[5])) +
scale_x_discrete
print(p)
}
Attempts
Attempt 1:
# Plot the data and add to list
bplot_list <- list()
for (data in dataList){
plot_list[[data]] <-
data %>%
ggplot(aes_string(x='Month', y=data[,5])) +
geom_boxplot() +
theme_classic() +
labs(y= colnames(data[5])) +
scale_x_discrete
}
Attempt 2:
# Plot the data and add to list
bplot_list <- list()
for (data in dataList){
p <-
data %>%
ggplot(aes_string(x='Month', y=data[,5])) +
geom_boxplot() +
theme_classic() +
labs(y= colnames(data[5])) +
scale_x_discrete
bplot_list[[]] <- p
}
This is the solution I came up with. I created a name for the graph based off the column name and used this to add to the list.
# Plot the data and add to list
bplot_list <- list()
for (data in dataList){
chemElement <- colnames(data[5])
p <-
data %>%
ggplot(aes_string(x='Month', y=data[,5])) +
geom_boxplot() +
theme_classic() +
labs(y= chemElement) +
scale_x_discrete()
bplot_list[[chemElement]] <- p
}
I have a list of values and a list of ggplots. I would like to attach the values from the list on to the ggplots. Is there a good way to do that?
Here's what I have for the list of ggplots:
p.list <- lapply(sort(unique(ind_steps$AnimalID)), function(i){
ggplot(ind_steps[ind_steps$AnimalID == i,], aes(x = t2, y = NSD)) +
geom_line() + theme_bw() +
theme(axis.text.x = element_text(angle = 90)) +
scale_x_datetime(date_breaks = '10 days', date_labels = '%y%j') +
facet_grid( ~ AnimalID, scales = "free") +
scale_colour_manual(values=hcl(seq(15,365,length.out=4)[match(i, sort(unique(ind_steps$AnimalID)))], 100, 65))
})
Assuming I have another list the same length as this one, and each one has a single value in each list.
I want to pair the ggplots with the list of values, and have the values show up in each respective plot. My expected output would be to have each value from the list of values be on each respective plot within the list of plots.
Since you don't provide any example data here I put an example with the iris built-in dataset. You can add values to plots with geom_text or geom_label (if I well understood what you want). For example, here we add the R^2 values to all the plot in a list:
library(ggplot2)
data(iris)
rsq <- lapply(1:length(unique(iris$Species)), function(i) {
cor(iris[iris$Species == unique(iris$Species)[i], "Sepal.Length"], iris[iris$Species == unique(iris$Species)[i], "Petal.Length"])^2
})
p.list <- lapply(1:length(unique(iris$Species)), function(i) {
ggplot(iris[iris$Species == unique(iris$Species)[i], ], aes(x = Sepal.Length, y = Petal.Length)) +
geom_point() + theme_bw()+
geom_text(aes(x=min(Sepal.Length),y=max(Petal.Length),label=paste0("R= ",round(rsq[[i]],2))))
})
library(gridExtra)
grid.arrange(p.list[[1]],p.list[[2]],p.list[[3]],nrow=3)
which return :
I have barplots, but would like to run a Wilcox.test within each "grp1" comparing the bars to the control for that group, and then putting an asterix if it is significant.
I've seen "compare_means" to get the comparisons, but I'm trying to make it automated and not so manual. Would "geom_signif" or "stat_compare_means" do this? Can someone help with this? Thank you very much.
I need the comparison to be made using the full dataset, not just the means (which is only one value per bar). I added a line at the end of the code running one of the comparisons so you can see where I need the p-values from.
y <- c(runif(100,0,4.5),runif(100,3,6),runif(100,4,7))
grp1 <- sample(c("A","B","C","D"),size = 300, replace = TRUE)
grp2 <- rep(c("High","Med","Contrl"),each=100)
dataset <- data.frame(y,grp1,grp2)
means <- aggregate(y~grp1+grp2,data=dataset,mean)
sd <- aggregate(y~grp1+grp2,data=dataset,function(x){sd(x)})
means.all <- merge(sd,means,by=c("grp1","grp2"))
names(means.all)[3:4] <- c("sd","y.mean")
library(ggplot2)
p<- ggplot(means.all, aes(x=grp1, y=y.mean, fill=grp2))+
geom_bar(stat="identity", color="black",
position=position_dodge()) +
geom_errorbar(aes(ymin=y.mean-sd, ymax=y.mean+sd), width=.2,
position=position_dodge(.9))
p
compare_means(y~grp2,data = dataset[dataset$grp1=="A",],method="wilcox.test")
Maybe this is not the optimal way but you can create a list splitting the data and applying the stat_compare_means() function individually at each level of your data. After that you can arrange the plots in one using patchwork:
library(ggplot2)
library(ggpubr)
library(patchwork)
#Split data
List <- split(means.all,means.all$grp1)
#Function for plot
myfun <- function(x)
{
#Ref group
rg <- paste0(unique(x$grp1),'.','Contrl')
#Plot
G <- ggplot(x, aes(x=interaction(grp1,grp2), y=y.mean, fill=grp2))+
geom_bar(stat="identity", color="black",
position=position_dodge()) +
geom_errorbar(aes(ymin=y.mean-sd, ymax=y.mean+sd), width=.2,
position=position_dodge(.9))+
stat_compare_means(ref.group = rg,label = "p.signif",method = "wilcox.test",label.y = 7)+
theme(axis.text.x = element_blank())+
xlab(unique(x$grp1))
return(G)
}
#Apply
Lplot <- lapply(List, myfun)
#Wrap plots
wrap_plots(Lplot,nrow = 1)+plot_layout(guides = 'collect')
Output:
Consider this update that takes the values for asterisks stored in a new dataframe:
#Create p-vals dataset
List2 <- split(dataset,dataset$grp1)
#p-val function
mypval <- function(x)
{
y <- compare_means(y~grp2,data = x,method="wilcox.test")
y <- y[,c('group2', 'group1','p.signif')]
names(y)<-c('grp2','grp1','p.signif')
y <- y[y$grp2=='Contrl',]
y$grp2 <- y$grp1
y <- rbind(y,data.frame(grp2='Contrl',grp1='',p.signif=''))
y$grp1 <- unique(x$grp1)
y$y.mean=7
return(y)
}
#Apply
dfpvals <- lapply(List2, mypval)
df <- do.call(rbind,dfpvals)
#Plot
ggplot(means.all, aes(x=grp1, y=y.mean, fill=grp2,group=grp2))+
geom_bar(stat="identity", color="black",
position=position_dodge()) +
geom_errorbar(aes(ymin=y.mean-sd, ymax=y.mean+sd), width=.2,
position=position_dodge(.9))+
geom_text(data=df,aes(x=grp1, y=y.mean,group=grp2,label=p.signif),
position=position_dodge(0.9))
Output:
say I want to plot two layers in ggplot, one containing points and another one containing lines if a certain criteria is fulfilled.
The code without the criteria could look like this:
library("ggplot2")
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p +
geom_point()+
geom_line()
now the condition for plotting the points and not only the lines would be, that an object called tmp.data does not equal the expression "no value".
tmp.data<-c(1,2,3) # in this case the condition is fulfilled
# attempt to plot the two layers including the condition in the plotting function
p+
if(tmp.data[1]!="no value"){ geom_point()+}
geom_line()
fails....
Error: unexpected '}' in:
"p+
if(tmp.data[1]!="no value"){ geom_point()+}"
geom_line()
geom_line:
stat_identity:
position_identity: (width = NULL, height = NULL)
This was done using ggplot2 2.1.0. I think you can do exactly what the OP wished, just by switching the parenthesis so that they encompass the entire if statement.
Here is an example that add a horizontal line depending on if Swtich is T or F. First, where the condition is TRUE
library(ggplot2)
df<-data.frame(x=1:10,y=11:20)
Switch=T
ggplot(df,aes(x,y))+
{if(Switch)geom_hline(yintercept=15)}+
geom_point()
Now, the same thing but the condition is FALSE
df<-data.frame(x=1:10,y=11:20)
Switch=F
ggplot(df,aes(x,y))+
{if(Switch)geom_hline(yintercept=15)}+
geom_point()
What you are seeing is a syntax error. The most robust way I can think of is:
tmp.data<-c(1,2,3)
if(tmp.data[1]!="no value") {
p = p + geom_point()
}
p + geom_line()
So you compose the object p in a sequence, only adding geom_point() when the if statements yields TRUE.
Following the ggplot2 book, you can create a function which returns a list. Any NULL components will be ignored.
library(ggplot2)
library(ggplot2movies)
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
# create function to add points conditionally
# If the list contains any NULL elements, they’re ignored.
my_plot <- function(point = FALSE){
list(
geom_line(),
if (point)
geom_point()
)
}
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p + my_plot()
p + my_plot(point = TRUE)
Created on 2020-02-25 by the reprex package (v0.3.0)
library(ggplot2)
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
tmp.data<-c(1,2,3) # in this case the condition is fulfilled
p <- ggplot(mry, aes(x=year, y=number, group=rating))
# this won't "loop through" the data points but it's what you asked for
if (tmp.data[1]!="no value") {
p <- p + geom_point() + geom_line()
} else {
p <- p + geom_line()
}
p
but perhaps this is more like what you really want?
mry$rating <- factor(mry$rating)
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p <- p + geom_line()
p <- p + geom_point(data=mry[!(mry$rating %in% tmp.data),],
aes(x=year, y=number, group=rating, color=rating), size=2)
p <- p + scale_color_brewer()
p
An alternative example using iris dataset creating a plot with conditional components, some with more than one line, and with multiple switches.
library("ggplot2")
p <- ggplot(iris,
aes(x=Sepal.Length,
y=Sepal.Width,
colour=Species))
# set plot switches to turn on or off various components
include_points = TRUE
include_lines = FALSE
include_density = TRUE
include_facet = TRUE
p +
list(
if(include_points){geom_point()},
if(include_lines){geom_line()},
if(include_facet){
# multi-line conditional elements can be included as a list
list(
facet_grid(rows = "Species"),
labs(subtitle = "This plot has been faceted, and colour has been removed"),
aes(colour = NULL)
)
},
if(include_density){geom_density2d()}) +
# additional elements common to all possible conditional combinations
# can be added as well
theme_minimal() +
labs(title = "Plot with conditional elements")
With the conditions as follows:
include_points = TRUE
include_lines = FALSE
include_density = TRUE
include_facet = TRUE
The following plot is generated:
Faceted plot with density and points
Setting include_facet = FALSE the following plot is generated:
Coloured plot with density - no facets
say I want to plot two layers in ggplot, one containing points and another one containing lines if a certain criteria is fulfilled.
The code without the criteria could look like this:
library("ggplot2")
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p +
geom_point()+
geom_line()
now the condition for plotting the points and not only the lines would be, that an object called tmp.data does not equal the expression "no value".
tmp.data<-c(1,2,3) # in this case the condition is fulfilled
# attempt to plot the two layers including the condition in the plotting function
p+
if(tmp.data[1]!="no value"){ geom_point()+}
geom_line()
fails....
Error: unexpected '}' in:
"p+
if(tmp.data[1]!="no value"){ geom_point()+}"
geom_line()
geom_line:
stat_identity:
position_identity: (width = NULL, height = NULL)
This was done using ggplot2 2.1.0. I think you can do exactly what the OP wished, just by switching the parenthesis so that they encompass the entire if statement.
Here is an example that add a horizontal line depending on if Swtich is T or F. First, where the condition is TRUE
library(ggplot2)
df<-data.frame(x=1:10,y=11:20)
Switch=T
ggplot(df,aes(x,y))+
{if(Switch)geom_hline(yintercept=15)}+
geom_point()
Now, the same thing but the condition is FALSE
df<-data.frame(x=1:10,y=11:20)
Switch=F
ggplot(df,aes(x,y))+
{if(Switch)geom_hline(yintercept=15)}+
geom_point()
What you are seeing is a syntax error. The most robust way I can think of is:
tmp.data<-c(1,2,3)
if(tmp.data[1]!="no value") {
p = p + geom_point()
}
p + geom_line()
So you compose the object p in a sequence, only adding geom_point() when the if statements yields TRUE.
Following the ggplot2 book, you can create a function which returns a list. Any NULL components will be ignored.
library(ggplot2)
library(ggplot2movies)
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
# create function to add points conditionally
# If the list contains any NULL elements, they’re ignored.
my_plot <- function(point = FALSE){
list(
geom_line(),
if (point)
geom_point()
)
}
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p + my_plot()
p + my_plot(point = TRUE)
Created on 2020-02-25 by the reprex package (v0.3.0)
library(ggplot2)
# Summarise number of movie ratings by year of movie
mry <- do.call(rbind, by(movies, round(movies$rating), function(df) {
nums <- tapply(df$length, df$year, length)
data.frame(rating=round(df$rating[1]), year = as.numeric(names(nums)), number=as.vector(nums))
}))
tmp.data<-c(1,2,3) # in this case the condition is fulfilled
p <- ggplot(mry, aes(x=year, y=number, group=rating))
# this won't "loop through" the data points but it's what you asked for
if (tmp.data[1]!="no value") {
p <- p + geom_point() + geom_line()
} else {
p <- p + geom_line()
}
p
but perhaps this is more like what you really want?
mry$rating <- factor(mry$rating)
p <- ggplot(mry, aes(x=year, y=number, group=rating))
p <- p + geom_line()
p <- p + geom_point(data=mry[!(mry$rating %in% tmp.data),],
aes(x=year, y=number, group=rating, color=rating), size=2)
p <- p + scale_color_brewer()
p
An alternative example using iris dataset creating a plot with conditional components, some with more than one line, and with multiple switches.
library("ggplot2")
p <- ggplot(iris,
aes(x=Sepal.Length,
y=Sepal.Width,
colour=Species))
# set plot switches to turn on or off various components
include_points = TRUE
include_lines = FALSE
include_density = TRUE
include_facet = TRUE
p +
list(
if(include_points){geom_point()},
if(include_lines){geom_line()},
if(include_facet){
# multi-line conditional elements can be included as a list
list(
facet_grid(rows = "Species"),
labs(subtitle = "This plot has been faceted, and colour has been removed"),
aes(colour = NULL)
)
},
if(include_density){geom_density2d()}) +
# additional elements common to all possible conditional combinations
# can be added as well
theme_minimal() +
labs(title = "Plot with conditional elements")
With the conditions as follows:
include_points = TRUE
include_lines = FALSE
include_density = TRUE
include_facet = TRUE
The following plot is generated:
Faceted plot with density and points
Setting include_facet = FALSE the following plot is generated:
Coloured plot with density - no facets