Multiple plots in one figure in R - r

I have three plots and I want to show them in a figure like below
link
I made a few attempts but I was not successful. My codes are given below:
dat <- read.table(text="
dates PS.230 PS.286 PS.389
3.01.2018 20.75103 16.69312 -6.503637
15.01.2018 15.00284 16.03211 16.1058
8.02.2018 11.0789 7.438522 -2.970704
20.02.2018 15.10865 12.8969 3.935687
4.03.2018 24.74799 19.25148 9.186779
28.03.2018 -1.299456 7.028817 -8.126284
9.04.2018 4.778902 8.309322 -3.450085
21.04.2018 7.131915 9.484932 -4.326919
", header=T, stringsAsFactors=FALSE)
dat$dates <- as.Date(dat$dates, "%d.%m.%Y")
library(ggplot2)
library(tidyverse)
a <- ggplot(dat, aes(x=dates, y=PS.230)) +
geom_point() +
geom_line() +
geom_smooth(se = FALSE, method = lm, size = 0.15, color = "#da0018") + #cizgi eklemek icin
scale_x_date(date_breaks = "1 months",date_labels = "%Y-%m",
limits = as.Date.character(c("01/12/2017","31/12/2018"),
format = "%d/%m/%Y")) +
ylim(-20,40) +
ylab("[mm/year]") +
xlab("") +
theme_linedraw() #theme_light
a + theme(
axis.text.x = element_text(angle = 45, hjust = 1),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank()
)
b <- ggplot(dat, aes(x=dates, y=PS.286)) +
geom_point() +
geom_line() +
geom_smooth(se = FALSE, method = lm, size = 0.15, color = "#da0018") + #cizgi eklemek icin
scale_x_date(date_breaks = "1 months",date_labels = "%Y-%m",
limits = as.Date.character(c("01/12/2017","31/12/2018"),
format = "%d/%m/%Y")) +
ylim(-20,40) +
ylab("[mm/year]") +
xlab("") +
theme_linedraw() #theme_light
b + theme(
axis.text.x = element_text(angle = 45, hjust = 1),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank()
)
c <- ggplot(dat, aes(x=dates, y=PS.389)) +
geom_point() +
geom_line() +
geom_smooth(se = FALSE, method = lm, size = 0.15, color = "#da0018") + #cizgi eklemek icin
scale_x_date(date_breaks = "1 months",date_labels = "%Y-%m",
limits = as.Date.character(c("01/12/2017","31/12/2018"),
format = "%d/%m/%Y")) +
ylim(-20,40) +
ylab("[mm/year]") +
xlab("") +
theme_linedraw() #theme_light
c + theme(
axis.text.x = element_text(angle = 45, hjust = 1),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank()
)
in the link I provided, much better graphics were drawn with fewer lines. my codes seem a little more complicated and frankly i couldn't get out. a, b and c plots in one image and only one date axes. How can I modify the codes to achieve sample result? Thank you.

Thank you for posting your data. As mentioned, the first step is to arrange your dataset so that it is in Tidy Data format. The information in dat$PS.230, dat$PS.286 and dat$PS.389 should be better represented in two columns:
First Column: name of data type - We'll call this column dat$value_type and it will have values that indicate if dat$results comes from PS.230, PS.286, or PS.389.
Second Column: value of data - We'll call this column dat$result and it just shows the value. This will be the y= aesthetic for all plots.
Pre-Processing: Gather into TidyData format
Use the gather() function to gather all columns in to a key ("value_type") and a "value" ("result"). We'll gather all columns except for "dates", so we just note to exclude that column via -dates:
dat <- dat %>% gather(key='value_type', value='result', -dates)
Plot
For the plot, you apply x and y aesthetics to "date" and "result". You can use "value_type" to differentiate based on color and create your legend for points and lines. You also use "value_type" as the column for creating the facets (the three separate plots) via use of facet_grid() function. Note that value_type ~ . arranges by "value_type" vertically, whereas . ~ value_type would arrange horizontally:
ggplot(dat, aes(x=dates, y=result)) +
geom_line(aes(color=value_type)) +
geom_point(aes(color=value_type)) +
scale_x_date(date_breaks = '1 months', date_labels = '%Y-%m') +
facet_grid(value_type ~ .) +
theme_bw()

Related

How to use scale_x_date properly

I am a new user in R and I hope you can help me.
setwd("C:/Users/USER/Desktop/Jorge")
agua <- read_excel("agua.xlsx")
pbi <- read_excel("PBIagro.xlsx")
str(agua);
names(agua)[2] <- "Variación";
agua[,1] <- as.Date(agua$Trimestre)
lagpbi <- lag(pbi$PBIAgropecuario, k=1)
pbi[,3]<- lagpbi; pbi <- pbi[-c(1),];
names(pbi)[3] <- "PBIlag"
growth <- ((pbi$PBIAgropecuario-pbi$PBIlag)/pbi$PBIlag)*100
Anual_growth <- data.frame(growth); Anual_growth[,2] <- pbi$Año; names(Anual_growth)[2] <- "Año"
# Plot
Agro <- ggplot(Anual_growth, aes(x=Año, y=growth)) +
geom_line(color="steelblue") +
geom_point() +
geom_text(aes(label = round(Anual_growth$growth, 1)),
vjust = "inward", hjust = "inward", size=2.5, show.legend = FALSE) +
xlab("") +
theme_ipsum() +
theme(axis.text.x=element_text(angle=60, hjust=1)) +
ylim(-9.9,13.4) +
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
axis.line.x = element_blank(), plot.margin = unit(c(1,1,0.5,1),"cm"),
axis.line.y = element_blank(), axis.text.x=element_text(face = "bold", size=8,
angle=1,hjust=0.95,vjust=0.2),
axis.text.y = element_blank(), axis.title.y=element_blank())+
scale_x_continuous("Año", labels = as.character(Anual_growth$Año), breaks = Anual_growth$Año)
print(Agro)
The problem is that it shows all the years, but I only want pair years (in X-axis) or years with step equal to 2.
I hope you can really help me.
Thank you.
Notice that the X-axis variable is a numeric string.
You can add something like
scale_x_date(date_breaks = "2 years", date_labels = "%Y") to your ggplot.
This is how it looks with my data, since you haven't posted yours. I am plotting a type date on x axis.
1.
ggplot(mydata) +
aes(x = date, y = number, color = somevar) +
geom_line()
ggplot(mydata) +
aes(x = date, y = number, color = somevar) +
geom_line() +
scale_x_date(date_breaks = "1 year", date_labels = "%Y")
3.
ggplot(mydata) +
aes(x = date, y = number, color = somevar) +
geom_line() +
scale_x_date(date_breaks = "2 years", date_labels = "%Y")
If you want pair years and because your x-axis variable is numeric, you can specify in scale_x_continous that breaks argument should take only even numbers.
Here how you can do it using this small example:
year = 1998:2020
value = rnorm(23,mean = 3)
df = data.frame(year,value)
library(ggplot2)
ggplot(df, aes(x = year, y = value))+
geom_point()+
geom_line()+
scale_x_continuous(breaks = year[year %%2 ==0])
Reciprocally, if you want odd years, you just have to specify scale_x_continuous(breaks = year[year %%2 != 0])
So, in your code, you should write:
scale_x_continuous(breaks = Anual_growth$Año[Anual_growth$Año %%2 ==0])
Does it answer your question ?

Grouped bar chart, ggplot2 in R, need to display one observation at a time

I have a dataset that looks like the following:
df <- data.frame(Name=rep(c('Sarah', 'Casey', 'Mary', 'Tom'), 3),
Scale=rep(c('Scale1', 'Scale2', 'Scale3'), 4),
Score=sample(1:7, 12, replace=T))
I am trying to create a barchat in ggplot2 that currently looks like this:
ggplot(df, aes(x=Name, y=Score, fill=Scale)) + geom_bar(stat='identity', position='dodge') +
coord_flip() +
scale_y_continuous(breaks=seq(0, 7, 1), limits = c(0, 7)) +
scale_x_discrete() +
scale_fill_manual(values=c('#253494', '#2c7fb8', '#000000')) +
theme(panel.background = element_blank(),
legend.position = 'right',
axis.line = element_line(),
axis.title = element_blank(),
axis.text = element_text(size=10))
However, I only want to show one observation (one Name) at a time. Is this possible to do without creating a ton of separate datasets, one for each person? I would like the end result to look like the example below, where I can just iterate through the names to produce a separate plot for each, or some similar process.
# Trying to avoid creating separate datasets, but for the sake of the example:
df2 <- data.frame(Name=rep(c('Sarah'), 3),
Scale=c('Scale1', 'Scale2', 'Scale3'),
Score=sample(1:7, 3, replace=T))
ggplot(df2, aes(x=Name, y=Score, fill=Scale)) + geom_bar(stat='identity', position='dodge') +
coord_flip() +
scale_y_continuous(breaks=seq(0, 7, 1), limits = c(0, 7)) +
scale_x_discrete() +
scale_fill_manual(values=c('#253494', '#2c7fb8', '#000000')) +
theme(panel.background = element_blank(),
legend.position = 'right',
axis.line = element_line(),
axis.title = element_blank(),
axis.text = element_text(size=10))
Since your data is already tidy ie. in long format, you can use facet_wrap as suggested and set the scales as "free" thus creating facets with your different Name groups.
df %>% ggplot(aes(y = Score, x = Name)) +
geom_bar(stat = "identity", aes(colour = Scale, fill = Scale),
position = "dodge") +
coord_flip() +
facet_wrap(~Name, scales = "free")
You can get rid of the facet labels or the axis labels depending which you prefer.
EDIT: in response to comment.
You can use the same data frame to create seperate plots by just piping a filter in at the start, hence,
df %>%
filter(Name == "Sarah") %>%
ggplot(aes(y = Score, x = Name)) +
geom_bar(stat = "identity", aes(colour = Scale, fill = Scale),
position = "dodge") +
coord_flip()
Since you are using Rmarkdown you could throw a for loop around that to plot all the names
for(i in c("Sarah", "Casey", "Mary", "Tom")){
df %>%
filter(Name == i) %>%
ggplot(aes(y = Score, x = Name)) +
geom_bar(stat = "identity", aes(colour = Scale, fill = Scale),
position = "dodge") +
coord_flip()
}
If you want to arrange all these into a group you can use ggpubr::ggarrange to place all the plots into the same object.
facet_grid(.~Name)
Maybe somehow implement this, it'll plot them all, but should do so in individual plots.

Manually change order of y axis items on complicated stacked bar chart in ggplot2

I've been stuck on an issue and can't find a solution. I've tried many suggestions on Stack Overflow and elsewhere about manually ordering a stacked bar chart, since that should be a pretty simple fix, but those suggestions don't work with the huge complicated mess of code I plucked from many places. My only issue is y-axis item ordering.
I'm making a series of stacked bar charts, and ggplot2 changes the ordering of the items on the y-axis depending on which dataframe I am trying to plot. I'm trying to make 39 of these plots and want them to all have the same ordering. I think ggplot2 only wants to plot them in ascending order of their numeric mean or something, but I'd like all of the bar charts to first display the group "Bird Advocates" and then "Cat Advocates." (This is also the order they appear in my data frame, but that ordering is lost at the coord_flip() point in plotting.)
I think that taking the data frame through so many changes is why I can't just add something simple at the end or use the reorder() function. Adding things into aes() also doesn't work, since the stacked bar chart I'm creating seems to depend on those items being exactly a certain way.
Here's one of my data frames where ggplot2 is ordering my y-axis items incorrectly, plotting "Cat Advocates" before "Bird Advocates":
Group,Strongly Opposed,Opposed,Slightly Opposed,Neutral,Slightly Support,Support,Strongly Support
Bird Advocates,0.005473026,0.010946052,0.012509773,0.058639562,0.071149335,0.31118061,0.530101642
Cat Advocates,0.04491726,0.07013396,0.03624901,0.23719464,0.09141056,0.23404255,0.28605201
And here's all the code that takes that and turns it into a plot:
library(ggplot2)
library(reshape2)
library(plotly)
#Importing data from a .csv file
data <- read.csv("data.csv", header=TRUE)
data$s.Strongly.Opposed <- 0-data$Strongly.Opposed-data$Opposed-data$Slightly.Opposed-.5*data$Neutral
data$s.Opposed <- 0-data$Opposed-data$Slightly.Opposed-.5*data$Neutral
data$s.Slightly.Opposed <- 0-data$Slightly.Opposed-.5*data$Neutral
data$s.Neutral <- 0-.5*data$Neutral
data$s.Slightly.Support <- 0+.5*data$Neutral
data$s.Support <- 0+data$Slightly.Support+.5*data$Neutral
data$s.Strongly.Support <- 0+data$Support+data$Slightly.Support+.5*data$Neutral
#to percents
data[,2:15]<-data[,2:15]*100
#melting
mdfr <- melt(data, id=c("Group"))
mdfr<-cbind(mdfr[1:14,],mdfr[15:28,3])
colnames(mdfr)<-c("Group","variable","value","start")
#remove dot in level names
mylevels<-c("Strongly Opposed","Opposed","Slightly Opposed","Neutral","Slightly Support","Support","Strongly Support")
mdfr$variable<-droplevels(mdfr$variable)
levels(mdfr$variable)<-mylevels
pal<-c("#bd7523", "#e9aa61", "#f6d1a7", "#999999", "#c8cbc0", "#65806d", "#334e3b")
ggplot(data=mdfr) +
geom_segment(aes(x = Group, y = start, xend = Group, yend = start+value, colour = variable,
text=paste("Group: ",Group,"<br>Percent: ",value,"%")), size = 5) +
geom_hline(yintercept = 0, color =c("#646464")) +
coord_flip() +
theme(legend.position="top") +
theme(legend.key.width=unit(0.5,"cm")) +
guides(col = guide_legend(ncol = 12)) + #has 7 real columns, using to adjust legend position
scale_color_manual("Response", labels = mylevels, values = pal, guide="legend") +
theme(legend.title = element_blank()) +
theme(axis.title.x = element_blank()) +
theme(axis.title.y = element_blank()) +
theme(axis.ticks = element_blank()) +
theme(axis.text.x = element_blank()) +
theme(legend.key = element_rect(fill = "white")) +
scale_y_continuous(breaks=seq(-100,100,100), limits=c(-100,100)) +
theme(panel.background = element_rect(fill = "#ffffff"),
panel.grid.major = element_line(colour = "#CBCBCB"))
The plot:
I think this works, you may need to play around with the axis limits/breaks:
library(dplyr)
mdfr <- mdfr %>%
mutate(group_n = as.integer(case_when(Group == "Bird Advocates" ~ 2,
Group == "Cat Advocates" ~ 1)))
ggplot(data=mdfr) +
geom_segment(aes(x = group_n, y = start, xend = group_n, yend = start + value, colour = variable,
text=paste("Group: ",Group,"<br>Percent: ",value,"%")), size = 5) +
scale_x_continuous(limits = c(0,3), breaks = c(1, 2), labels = c("Cat", "Bird")) +
geom_hline(yintercept = 0, color =c("#646464")) +
theme(legend.position="top") +
theme(legend.key.width=unit(0.5,"cm")) +
coord_flip() +
guides(col = guide_legend(ncol = 12)) + #has 7 real columns, using to adjust legend position
scale_color_manual("Response", labels = mylevels, values = pal, guide="legend") +
theme(legend.title = element_blank()) +
theme(axis.title.x = element_blank()) +
theme(axis.title.y = element_blank()) +
theme(axis.ticks = element_blank()) +
theme(axis.text.x = element_blank()) +
theme(legend.key = element_rect(fill = "white"))+
scale_y_continuous(breaks=seq(-100,100,100), limits=c(-100,100)) +
theme(panel.background = element_rect(fill = "#ffffff"),
panel.grid.major = element_line(colour = "#CBCBCB"))
produces this plot:
You want to factor the 'Group' variable in the order by which you want the bars to appear.
mdfr$Group <- factor(mdfr$Group, levels = c("Bird Advocates", "Cat Advocates")

Trim first and last labels in ggplot2

I've got a plot that is tabulating two types of data by day and I'm looking to just trim the first and last label from the plot. Here is a reproducible example of the data:
library(dplyr)
library(ggplot2)
library(scales)
dates <- paste0("2014-01-", 1:31)
dat <- data.frame("Date" = sample(dates, 4918, replace=T),
"Type" = sample(c('Type1', 'Type2'), 4918, replace=T, probs=c(.55, .45)))
p.data <- dat %>% group_by(Date, Type) %>% summarise(Freq = n())
p.data$Date <- as.Date(p.data$Date)
Here is the code for the plot:
p <- ggplot(data=p.data, aes(x=Date, y=Freq, fill=Type)) +
geom_bar(stat='identity', position='dodge') +
labs(x='Date', y='Count', title='Frequency of Data by Day') +
theme_bw() +
theme(axis.text.x = element_text(angle=90),
panel.grid.minor = element_blank(),
plot.title = element_text(vjust=1.4),
legend.position='bottom') +
scale_x_date(labels=date_format("%a %d"),
breaks=date_breaks("day"),
limits=c(as.Date('2014-01-01'), as.Date('2014-01-31'))) +
scale_y_continuous(limits=c(0, 150), breaks=seq(from=0, to=150, by=25)) +
scale_fill_manual(values=c('dark grey', 'light green'))
As you can see, there are two label points for the day prior to the beginning of the month and the day after the last day of the month. How do I trim those off? Can I just subset the labels and breaks call in scale_x_date()?
The expand argument in scale_x_date is one way to do it. It tries to be helpful by making some extra space around the edges, but in this case it adds more than a day, so the axis labels have those extra days.
p <- ggplot(data=p.data, aes(x=Date, y=Freq, fill=Type)) +
geom_bar(stat='identity', position='dodge') +
labs(x='Date', y='Count', title='Frequency of Data by Day') +
theme_bw() +
theme(axis.text.x = element_text(angle=90),
panel.grid.minor = element_blank(),
plot.title = element_text(vjust=1.4),
legend.position='bottom') +
scale_x_date(labels=date_format("%a %d"),
breaks=date_breaks("day"),
limits=c(as.Date('2014-01-01'), as.Date('2014-01-31')),
expand=c(0, .9)) +
scale_y_continuous(limits=c(0, 150), breaks=seq(from=0, to=150, by=25)) +
scale_fill_manual(values=c('dark grey', 'light green'))

Time intervals in ggplot

I tried to plot time intervals for my data. Basically I want to compare recording of 2 systems for different individuals. The code might be a bit inefficient.
require(reshape2)
require(ggplot2)
df.system1.person1<-data.frame(
time.start.person1=c("12:12:30","12:13:10","12:13:50"),
time.end.person1=c("12:12:35","12:13:20","12:13:55")
)
df.system2.person1=data.frame(
time.start.person1=c("12:12:30","12:13:50"),
time.end.person1=c("12:13:25","12:14:00")
)
df.system1.person2=data.frame(
time.start.person2=c("12:12:30","12:13:10","12:13:50"),
time.end.person2=c("12:12:35","12:13:20","12:13:55")
)
df.system2.person2=data.frame(
time.start.person2=c("12:12:30","12:13:50"),
time.end.person2=c("12:13:25","12:14:00")
)
ndf.system1.person1 <- melt(df.system1.person1, measure.vars = c("time.start.person1", "time.end.person1"))
ndf.system2.person1 <- melt(df.system2.person1, measure.vars = c("time.start.person1", "time.end.person1"))
ndf.system1.person2 <- melt(df.system1.person2, measure.vars = c("time.start.person2", "time.end.person2"))
ndf.system2.person2 <- melt(df.system2.person2, measure.vars = c("time.start.person2", "time.end.person2"))
ndf.system1.person1$value2<-as.POSIXct(strptime(ndf.system1.person1$value, "%H:%M:%S"))
ndf.system2.person1$value2<-as.POSIXct(strptime(ndf.system2.person1$value, "%H:%M:%S"))
ndf.system1.person2$value2<-as.POSIXct(strptime(ndf.system1.person2$value, "%H:%M:%S"))
ndf.system2.person2$value2<-as.POSIXct(strptime(ndf.system2.person2$value, "%H:%M:%S"))
data=rbind(ndf.system1.person1,ndf.system2.person1,ndf.system1.person2,ndf.system2.person2)
data$Arg[1:6]="System1"
data$Arg[7:10]="System2"
data$Arg[11:16]="System1"
data$Arg[17:20]="System2"
data$Ind[1:10]="Person 1"
data$Ind[11:20]="Person 2"
ggplot(data,aes(x=value2,y=Arg))+geom_line(size=1)+ facet_grid(.~Ind,scales="free_x",space="free_x")+xlab("") + ylab("") +theme_bw()+theme(legend.position="none",axis.text=element_text(size=14),panel.grid.major = element_blank(),axis.title.y = element_text(vjust=+1),panel.grid.minor = element_blank(), axis.title=element_text(size=14))
However, instead of time intervals I get one solid line. The time intervals should be start.time-end.time. Also, on the x axis I would like to get HH:MM format. Thank you.
The way you combine your data does not seem to be suitable for the plot you want to make. Below you find a different approach to combine the data that you originally created. Then you can use geom_linerange to make your plot.
df <- do.call(rbind, lapply(list(cbind(df.system1.person1, Arg = "System1", Ind = "Person 1"),
cbind(df.system2.person1, Arg = "System2", Ind = "Person 1"),
cbind(df.system1.person2, Arg = "System1", Ind = "Person 2"),
cbind(df.system2.person2, Arg = "System2", Ind = "Person 2")),
'colnames<-', c('start.time', 'end.time', 'Arg', 'Ind')))
#
ggplot(df, aes(x = Arg, ymin = start.time, ymax=end.time)) +
geom_linerange(size = 1) +
coord_flip() +
facet_grid(.~Ind,scales="free_y",space="free_y") +
xlab("") + ylab("") +
theme_bw() +
theme(axis.text=element_text(size=14),
axis.text.x = element_text(angle = 20, vjust=0.5),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())
EDIT: In order to change the time format, you need to first convert the times to POSIXct format and then use scale_y_datetime.
# change formats
df$start.time <- as.POSIXct(df$start.time, format='%H:%M:%S')
df$end.time <- as.POSIXct(df$end.time, format='%H:%M:%S')
# load additional package
require(scales)
# added scale_y_datetime.
# The example data you provide span less than 2 minutes,
# so I used the date format with seconds and 30 second breaks
# You need to change this to get 15 minute breaks and no seconds.
ggplot(df, aes(x = Arg, ymin = start.time, ymax=end.time)) +
geom_linerange(size = 1) +
scale_y_datetime(breaks = date_breaks("30 secs"), # change to "15 mins"
labels = date_format("%H:%M:%S")) + # change to "%H:%M"
coord_flip() +
facet_grid(.~Ind,scales="free_y",space="free_y") +
xlab("") + ylab("") +
theme_bw() +
theme(axis.text=element_text(size=14),
axis.text.x = element_text(angle = 20, vjust=0.5),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank())

Resources