Plot data using loop in R - r

I want to make a plot of the Daily Streamflow in each Station and save it in png format. I want a separate png for each station, something like the image below:
I have a list with the data frame for each station, as shown in the figure below:
I am trying using the following code, but it is not working because R aborted, I am not sure if it is because of the quantity of data:
for (i in 1:length(listDF2))
{
df1 <- as.data.frame(listDF2[[i]])
df1[is.na(df1)] <- 0
temp_plot <- ggplot(df1, aes(x = day, y = DailyMeanStreamflow, colour=Station)) +
geom_line(size = 1) +
geom_point(size=1.5, shape=21, fill="white") +
facet_wrap(~ month, ncol = 3) +
labs(title = "Daily Mean Streamflow",
subtitle = "Data plotted by month",
y = "Daily Mean Streamflow [m3/s]", x="Days") +
scale_y_continuous (breaks=seq(0,max(df1$DailyMeanStreamflow, na.rm=TRUE),by=1500)) +
scale_x_continuous (breaks=seq(1,max(df1$day),by=1)) + theme(axis.text.x = element_text(size=9))
print(temp_plot)
name4<- paste("DailyStreamflow_byMonth","_", siteNumber[i], ".png", sep="")
ggsave(temp_plot,filename = name4,width=22,height=11,units="in",dpi=500)
#while (!is.null(dev.list()))
dev.off()
}
I have also a "big" data frame with the data for each station one after the other. This data frame is useful when I want to apply functions like data_frame %>% group_by(station) %>% summarise(...)
Any idea in how to make the plots for each station? Is it better to use the list or the "big" data frame for this purpose?

I am not sure where the problem in your workflow occures. It is quite hard to help you, as we have not minimal working example. Also I am not sure if you just want to produce your plots in a loop or if you (also) want to put them together in one visualization?
Anyways ... I tried to give you a starting point ... maybe this will help?
"%>%" <- magrittr::"%>%"
df_list <- list(
A=dplyr::tibble(ID=1:10,
x=rnorm(10),
y=rnorm(10)),
B=dplyr::tibble(ID=1:10,
x=rnorm(10),
y=rnorm(10)),
C=dplyr::tibble(ID=1:10,
x=rnorm(10),
y=rnorm(10)),
D=dplyr::tibble(ID=1:10,
x=rnorm(10),
y=rnorm(10)))
# Lapply approach
lapply(df_list, function(dat){
p <- dat %>%
ggplot2::ggplot(ggplot2::aes(x=x,y=y)) +
ggplot2::geom_point()
print(p)
})
# Loop approach
for (i in 1:length(df_list)){
p <- df_list[[i]] %>%
ggplot2::ggplot(ggplot2::aes(x=x,y=y)) +
ggplot2::geom_point()
print(p)
fname <- paste("test","_", i, ".png", sep="")
ggsave(p,
filename=fname,
width=22,
height=11,
units="in",
dpi=500)
}

Related

problem plotting time series graph in r with date

I need to plot a time series graph but the data that I'm using is proving to be quite challenging.
Ideally, I'd like a graph that looks something like this:
But mine looks like this:
I have tried a series of different things but none of them have worked.
The dataset can be found here and I'll attach a picture of what the dataset itself looks like:
some code I have tried includes
ggplot( aes(x=date, y=northEast)) +
geom_area(fill="#69b3a2", alpha=0.5) +
geom_line(color="#69b3a2") +
ylab("test") +
theme_ipsum()
ggplot(covidData2) +
geom_line(
mapping = aes(x = weekBeginning, y=northEast, group=northEast)
)
Any help would be greatly appreciated!
You need to tidy your data up before plotting it. If you look at your data frame, all of the "numeric" columns have been interpreted as character vectors because the column names are nested and therefore appear in the first couple of rows. You need to consolidate these and convert them to column names. Then, you need to convert the numeric columns to numbers. Finally, you need to parse the dates, as ggplot will simply read the periods as character vectors:
library(readxl)
library(lubridate)
library(ggplot2)
library(hrbrthemes)
wb <- read_xlsx(path.expand("~/covid.xlsx"), sheet = "Table 9")
df <- as.data.frame(wb)
df[1, 1] <- ""
for(i in 2:length(df)) {
if(is.na(df[1, i])) df[1, i] <- df[1, i - 1]
}
nms <- trimws(paste(df[1,], df[2,]))
df <- df[-c(1:2),]
names(df) <- nms
df <- df[sapply(df, function(x) !all(is.na(x)))]
df[-1] <- lapply(df[-1], as.numeric)
df <- head(df, -3)
df$Period <- dmy(substr(df$Period, 1, 10))
Now we can plot:
ggplot(df, aes(x = Period, y = `North East Rate`)) +
geom_area(fill = "#69b3a2", alpha=0.5) +
geom_line(color = "#69b3a2") +
ylab("Rate per 100,000") +
xlab("") +
theme_ipsum()
Created on 2022-03-08 by the reprex package (v2.0.1)

how to get geom_point and legend onto line plot in R?

This is my R-script, I've been trying to include a legend onto the line plot but it isn't working? Any guidance? I also can't seem to get the geom_point() working either (I've taken the code for it out below).
library(ggsignif)
library(readxl)
library(svglite)
library(tidyverse)
library(ggplot2)
library(tidyr)
library(dplyr)
url <-'https://static-content.springer.com/esm/art%3A10.1038%2Fs41586-020-2850-3/MediaObjects/41586_2020_2850_MOESM10_ESM.xlsx'
temp <-tempfile()
download.file(url, temp, mode='wb')
myData <- read_excel(path=temp, sheet = "ExFig.5f")
names(myData) <- NULL
view(myData)
Time_post_inj <- (myData[1])
Time_post_inj <- Time_post_inj[-c(1),]
dose_450_ug <- (myData[2])
dose_450_ug <- dose_450_ug[-c(1),]
dose_150_ug <- (myData[4])
dose_150_ug <- dose_150_ug[-c(1),]
dose_100_ug <- (myData[6])
dose_100_ug <- dose_100_ug[-c(1),]
dose_50_ug <- (myData[8])
dose_50_ug <- dose_50_ug[-c(1),]
colnames(Time_post_inj) <-c("Time_Post_Injection")
colnames(dose_450_ug) <-c("dose_450_µg")
colnames(dose_150_ug) <-c("dose_150_µg")
colnames(dose_100_ug) <-c("dose_100_µg")
colnames(dose_50_ug) <-c("dose_50_µg")
Newdata <-data.frame(Time_post_inj, dose_450_ug, dose_150_ug, dose_100_ug, dose_50_ug)
Newdata$Time_Post_Injection <-as.numeric(Newdata$Time_Post_Injection)
Newdata$dose_450_µg <-as.numeric(Newdata$dose_450_µg)
Newdata$dose_150_µg <-as.numeric(Newdata$dose_150_µg)
Newdata$dose_100_µg <-as.numeric(Newdata$dose_100_µg)
Newdata$dose_50_µg <-as.numeric(Newdata$dose_50_µg)
str(Newdata)
ggplot(data=Newdata, aes(x=Time_Post_Injection, y=hCD4_occupancy, group = 1)) + geom_line(aes(y=dose_450_µg)) + geom_line(aes(y=dose_150_µg)) + geom_line(aes(y=dose_100_µg)) + geom_line(aes(y=dose_50_µg))
Newdata
tidyr::pivot_longer(Time_Post_Injection, names_to = "DOSE", values_to = "VALUE") %>%
ggplot2::ggplot(aes(Time_Post_Injection, VALUE, group = DOSE, color = DOSE)) + ggplot2::geom_line()
The following is a full reprex, meaning that if you copy and paste, it will reproduce the plot exactly as below. You can see I have simplified your parsing considerably too; this starts with the url and produces the plot with a lot less data wrangling:
library(ggplot2) # Only load packages you really need
# This format is a handy way of keeping a long string on a single page
url <- paste0("https://static-content.springer.com/esm/art%3A10.",
"1038%2Fs41586-020-2850-3/MediaObjects/41586_2020",
"_2850_MOESM10_ESM.xlsx")
temp <- tempfile()
download.file(url, temp, mode = 'wb')
# Instead of loading an entire library to use one function, we can
# access read_excel by doing readxl::read_excel
myData <- readxl::read_excel(temp, sheet = "ExFig.5f")
# This single line subsets the data frame to chop out the first row
# and the empty columns. It also converts all columns to numeric
NewData <- as.data.frame(lapply(myData[-1, -c(3, 5, 7)], as.numeric))
names(NewData) <-c("Time_Post_Injection", "dose_450_ug",
"dose_150_ug", "dose_100_ug", "dose_50_ug")
# This switches your data to long format, which helps ggplot to work
# We put all the values in one column and have the dosages as labels
# in another column instead of having multiple columns. This allows us
# to map Color to the dosages.
NewData <- cbind(NewData[1], stack(NewData[-1]))
# Now we just tell ggplot to map colours to ind
ggplot(NewData, aes(x = Time_Post_Injection, y = values, color = ind)) +
geom_line() +
geom_point() +
scale_color_discrete(name = "Dose") +
labs(x = "Time Pist Injection") +
theme_bw()
Created on 2020-11-11 by the reprex package (v0.3.0)
Hi the main problem is that you did not get your data into a easy to handle format
library(dplyr)
library(tidyr)
library(ggplot2)
Newdata %>%
# get data in easy to handle format
tidyr::pivot_longer(-Time_Post_Injection, names_to = "DOSE", values_to = "VALUE") %>%
# plot and use the new DOSE column as group and color so you do not need one geom per line! (you can change geom_line() to geom_point also())
ggplot2::ggplot(aes(Time_Post_Injection, VALUE, group = DOSE, color = DOSE)) +
ggplot2::geom_line()

How can I add a title to a list of plots based on values of the listed df's?

Here's that illustrates the obstacle I'm facing.
library(tidyverse)
co2_list <- CO2 %>%
group_split(Type)
reprex_fun <- function(x){
x %>%
ggplot(aes(conc, uptake)) +
geom_point() +
facet_wrap(~Plant, ncol = 2)
}
lapply(co2_list, reprex_fun)
Since the listed dataframes are based on the Type value,
How can I add the corresponding title with the type, to the plots I just made?
You can also try labs, similar to ggtitle:
#Data
data("CO2")
#Plot
co2_list <- CO2 %>%
group_split(Type)
#Function
reprex_fun <- function(x){
x %>%
ggplot(aes(conc, uptake)) +
geom_point() +
labs(title = unique(x$Type))+
facet_wrap(~Plant, ncol = 2)
}
#Plots
lapply(co2_list, reprex_fun)

Assigning plot to a variable in a loop

I am trying to create 2 line plots.
But I noticed that using a for loop will generate two plots with y=mev2 (instead of a plot based on y=mev1 and another one based on y=mev2).
The code below shows the observation here.
mev1 <- c(1,3,7)
mev2 <- c(9,8,2)
Period <- c(1960, 1970, 1980)
df <- data.frame(Period, mev1, mev2)
library(ggplot2)
# Method 1: Creating plot1 and plot2 without using "for" loop (hard-code)
plot1 <- ggplot(data = df, aes(x=Period, y=unlist(as.list(df[2])))) + geom_line()
plot2 <- ggplot(data = df, aes(x=Period, y=unlist(as.list(df[3])))) + geom_line()
# Method 2: Creating plot1 and plot2 using "for" loop
for (i in 1:2) {
y_var <- unlist(as.list(df[i+1]))
assign(paste("plot", i, sep = ""), ggplot(data = df, aes(x=Period, y=y_var)) + geom_line())
}
Seems like this is due to some ggplot()'s way of working that I am not aware of.
Question:
If I want to use Method 2, how should I modify the logic?
People said that using assign() is not an "R-style", so I wonder what's an alternate way to do this? Say, using list?
One possible answer with no tidyverse command added is :
library(ggplot2)
y_var <- colnames(df)
for (i in 1:2) {
assign(paste("plot", i, sep = ""),
ggplot(data = df, aes_string(x=y_var[1], y=y_var[1 + i])) +
geom_line())
}
plot1
plot2
You may use aes_string. I hope it helps.
EDIT 1
If you want to stock your plot in a list, you can use this :
Initialize your list :
n <- 2 # number of plots
list_plot <- vector(mode = "list", length = n)
names(list_plot) <- paste("plot", 1:n)
Fill it :
for (i in 1:2) {
list_plot[[i]] <- ggplot(data = df, aes_string(x=y_var[1], y=y_var[1 + i])) +
geom_line()
}
Display :
list_plot[[1]]
list_plot[[2]]
For lines in different "plots", you can simplify it with facet_wrap():
library(tidyverse)
df %>%
gather(variable, value, -c(Period)) %>% # wide to long format
ggplot(aes(Period, value)) + geom_line() + facet_wrap(vars(variable))
You can also put it in a loop if necessary and store the results in a list:
# empty list
listed <- list()
# fill the list with the plots
for (i in c(2:3)){
listed[[i-1]] <- df[,-i] %>%
gather(variable, value, -c(Period)) %>%
ggplot(aes(Period, value)) + geom_line()
}
# to get the plots
listed[[1]]
listed[[2]]
Why do you want 2 separate plots? ggplots way to do this would be to get data in long format and then plot.
library(tidyverse)
df %>%
pivot_longer(cols = -Period) %>%
ggplot() + aes(Period, value, color = name) + geom_line()
Here is an alternative approach using a function and lapply. I recognize that you asked how to solve this using a loop. Still, I think it might be useful to consider this approach.
library(ggplot2)
mev1 <- c(1,3,7)
mev2 <- c(9,8,2)
Period <- c(1960, 1970, 1980)
df <- data.frame(Period, mev1, mev2)
myplot <- function(yvar){
plot <- ggplot(df, aes(Period, !!sym(yvar))) + geom_line()
return(plot)
}
colnames <- c("mev1","mev2")
list <- lapply(colnames, myplot)
names(list) <- paste0("plot_", colnames)
# Alternativing naming: names(list) <- paste0("plot", 1:2)
Using this approach you can easily apply your plot function to whatever columns you like. You can specify the columns by name, which may be preferrabe to specifying by position. Plots are saved in a list, and they are named afterwards using the names attribute. In my example I named the plots plot_mev1 and plot_mev2. But you can easily adjust to some other naming. E.g. write names(list) <- paste0("plot", 1:2) to get plot1 and plot2.
Note that I used !!sym() in the ggplot call. This is essentally an alternative to aes_string which was used in the answer of Rémi Coulaud. In this way ggplot understands even in the context of a function or in the context of a loop that "mev1" is a column of your dataset and not just a text string

how to loop a geographic mapping function over a list of dataframes (or a subsetted dataframe)

I have a dataframe consisting of species names, longitude and latitude coordinates. there are 115 different species with 25000 lat/long coordinates. I need to make individual maps that show observations for each specific species.
first, I created a function that would generate the kind of map that I want, called platmaps. when I call the function for my full dataset (platmaps(df1)), it creates a map displaying all lat long observations.
Then I constructed a for loop which was supposed to subset my df by species name, and insert that subsetted dataframe into my platmaps function. It runs for a couple of minutes and then nothing happens.
so I then I split the dataframe by species name, and created a list of dataframes(out1), and used lapply(out1, platmaps) but it only returned a list of the names of my dfs.
Then I tried a variation of an example that I saw here, but it also did not work.
function
platmaps<-function(df1){
wm <- wm <- borders("world", colour="gray50", fill="gray50")
ggplot()+
coord_fixed()+
wm +
geom_point(data =df1 , aes(x = decimalLongitude, y = decimalLatitude),
colour = "pink", size = 0.5)
subset
for(i in 1:nrow(PP)){
query<-paste(PP$species[i])
p<-subset(df1, df1$species== query))
platmaps(p)
}
list
for (i in 1:length(out1)){
pp<-out1[[i]]
platmaps(pp)
}
applied example
p =
wm <- wm <- borders("world", colour="gray50", fill="gray50")
ggplot()+
coord_fixed()+
wm +
geom_point(data =df1 , aes(x = decimalLongitude, y = decimalLatitude),
colour = "pink", size = 0.5)
plots = df1 %>%
group_by(species) %>%
do(plots = p %+% . + facet_wrap(~species))
the error for the applied example is:
Error: Cannot add ggproto objects together. Did you forget to add this
object to a ggplot object?
As I'm new to R (and coding), I assume I'm getting the syntax wrong, or am not applying my function correctly to/within either of my loops, or I fundamentally misunderstand the way looping works.
data frame sample
species decimalLongitude decimalLatitude
Platanthera lacera -71.90000 42.80000
Platanthera lacera -90.54861 40.12083
Platanthera lacera -71.00889 42.15500
Platanthera lacera -93.20833 45.20028
Platanthera lacera -72.45833 41.91666
Platanthera bifolia 5.19800 59.64310
Platanthera sparsiflora -117.67472 34.36278
fixed platmaps function
ggplot(data=df1 %>% filter(species == s))+
coord_fixed()+
borders("world", colour="gray50", fill="gray50")+
geom_point(aes(x = decimalLongitude, y = decimalLatitude),
colour = "pink", size = 0.5)+
labs(title=as.character(s))
Because you didn't provide a test data set, let me give you a general idea how to make multiple plots you can inspect later. The code below will plot a parameter for a number of countries and save plot pdfs to a given path. You can replace the code behind the pl variable in the loop with your function.
library(ggplot2)
library(dplyr)
df <- data.frame(country = c(rep('USA',20), rep('Canada',20), rep('Mexico',20)),
wave = c(1:20, 1:20, 1:20),
par = c(1:20 + 5*runif(20), 21:40 + 10*runif(20), 1:20 + 15*runif(20)))
countries <- unique(df$country)
plot_list <- list()
i <- 1
for (c in countries){
pl <- ggplot(data = df %>% filter(country == c)) +
geom_point(aes(wave, par), size = 3, color = 'red') +
labs(title = as.character(c), x = 'wave', y = 'value') +
theme_bw(base_size = 16)
plot_list[[i]] <- pl
i <- i + 1
}
pdf('path/to/pdf')
pdf.options(width = 9, height = 7)
for (i in 1:length(plot_list)){
print(plot_list[[i]])
}
dev.off()
After the plots are obtained (the plot_list variable), we turn on the pdf terminal and print them. In the end, we turn off the pdf terminal.
there is a neat way to apply any function to a list of items. I have outlined a way to do this with the data you added. I cannot get platmaps to work so I have just made a scatter plot.
The method is to split your data frame into individual subsets using split() and then apply the plotting function to the resulting list using lapply(). Since lapply() returns a list, this can be passed directly to a function such as ggpubr::ggarrange() for visualizing.
library(ggplot2)
plot_function <- function(x){
p <- ggplot(x, aes(x = decimalLongitude, y = decimalLatitude)) + geom_point()
p
}
plot_list <-
df %>%
split(.$species) %>% # Separate df into subset dfs based on species column
lapply(., plot_function) # map plot_function to list
# Display on a grid (many ways to do this - I just find this package simple)
ggpubr::ggarrange(plotlist = plot_list)

Resources