Add several lines of variable text in fixed positions to a ggplot facet - r

I am tryig to add several lines of text to this facet. Sorry about the mess of code
From the object means1 I want to add the values of the variables "pCensCom" "pCensEx" and "pCensReg" for each facet, as described in the following figure
This is the object 'censTot1' used to build the chart
censo censTot tipoAni censAn año pCensAn
1: 2010-01-01 42 Hembra adulta 27 2010 64.285714
2: 2010-01-01 42 Joven 4 2010 9.523810
3: 2010-01-01 42 Macho adulto 1 2010 2.380952
4: 2010-01-01 42 Ternero 10 2010 23.809524
5: 2010-01-02 42 Hembra adulta 27 2010 64.285714
---
7300: 2014-12-30 57 Ternero 16 2014 28.070175
7301: 2014-12-31 57 Hembra adulta 32 2014 56.140351
7302: 2014-12-31 57 Joven 7 2014 12.280702
7303: 2014-12-31 57 Macho adulto 2 2014 3.508772
7304: 2014-12-31 57 Ternero 16 2014 28.070175
The following describes the code used to design the figure
# Plot color background
# %%%%%%%%%%%%%%%%%%%%%%
bg0<-data.table()
for(i in 1:5){
bg<-data.table(xstart=c(as.Date(paste0(años[i],"-01-01"), format="%Y-%m-%d"),as.Date(paste0(años[i],"-03-21"), format="%Y-%m-%d"), as.Date(paste0(años[i],"-06-21"),format = "%Y-%m-%d"),as.Date(paste0(años[i],"-09-21"),format = "%Y-%m-%d"),
as.Date(paste0(años[i],"-12-21"),format = "%Y-%m-%d")),xend=c(as.Date(paste0(años[i],"-03-21"), format="%Y-%m-%d"),
as.Date(paste0(años[i],"-06-21"),format = "%Y-%m-%d"), as.Date(paste0(años[i],"-09-21"),format = "%Y-%m-%d"),
as.Date(paste0(años[i],"-12-21"),format = "%Y-%m-%d"),as.Date(paste0(años[i],"-12-31"),format = "%Y-%m-%d")),
Estacion=c("Invierno","Primavera","Verano","Otoño","Invierno"))
l=list(bg0,bg); bg0<-rbindlist(l, fill=TRUE)
}
bg0<-bg0[,Estacion:=factor(ordered(Estacion,levels=c("Invierno","Primavera", "Verano", "Otoño")))]
cbPalette<-c("#FF3300","#006633","#FFFF00","#0000FF")
plotbg<-ggplot()+ geom_rect(data = bg0, aes(xmin = xstart, xmax = xend, ymin = 0, ymax = Inf, fill = Estacion), alpha = 0.10)+ scale_fill_manual(values=cbPalette)+ guides(fill=FALSE)+theme_bw()
means1<-data.table(tipoAni=c("Hembra adulta","Joven","Macho adulto","Ternero"),pCensCom=c(62.3,17.8,0.9,19.37),pCensEx=c(61.4,16.1,1.9,20.6),pCensReg=c(63.0,17.9,1.6,24.7))
# Plot
# %%%%
plotbg + geom_line(data=censTot1,aes(x=censo,y=pCensAn))+ facet_grid(tipoAni ~ .)+ xlab("Censos diarios") + ylab("Animales (%)") +theme_bw()+ theme(strip.text.x = element_text(size=8),strip.text.y = element_text(size=10, face="bold"),strip.background = element_rect(colour="red", fill="#CCCCFF"))
Please I need help, I tried several times using the functions annotation_custom, grobTree and textGrob and I have not been able to achieve

Here is a simplified answer. First I simulate some data dat, then a second data.table backgr that has the information for the background, and lastly textdt, which holds the information about the text elements.
The code looks like this:
library(data.table)
library(ggplot2)
library(scales)
dat <- data.table(x = rep(1:100, 2),
group = rep(LETTERS[1:2], each = 100),
val = rnorm(200))
dat[, price := 100 + cumsum(val), by = group]
# plot empty
ggplot(dat, aes(x = x, y = price)) +
geom_line() +
facet_grid(group~.)
# plot with added polygons
# for the background colors
backgr <- data.table(minval = c(10, 40, 60, 90),
maxval = c(20, 60, 80, 100),
backgroup = LETTERS[1:4])
# for the text elements
textdt <- data.table(xval = c(10, 50, 70),
yval = c(105, 100, 95),
textlabel = c("foo", "bar", "lorum"),
group = c("A", "A", "B"))
# plot
ggplot() +
geom_rect(data = backgr, aes(xmin = minval, xmax = maxval, ymin = -Inf,
ymax = Inf, fill = backgroup)) +
geom_line(data = dat, aes(x = x, y = price)) +
geom_text(data = textdt, aes(x = xval, y = yval, label = textlabel,
group = group)) +
facet_grid(group~.) +
scale_fill_manual(values = alpha(c("red", "green", "blue", "yellow"), 0.5))
Which results in a plot like this, which you can adjust to fit your data:

Related

label mean lines in ggplot that are mapped in a group

I have density plots for each shift and year. The means are plotted by grouping in a df called mu. I also add vertical reference lines which I can label without issue but I cannot seem to get the labels on the grouped vertical lines. You will see my latest attempt which throws an error "Aesthetics must be either length 1 or the same as the data (134): x"
My code
library(ggplot2)
library(dplyr)
df <- read.csv("f4_bna_no_cup.csv")
head(df)
ï..n yr s ys x
1 1 2021 1 2021-1 116.83
2 2 2021 1 2021-1 114.83
3 3 2021 1 2021-1 115.50
4 4 2021 1 2021-1 115.42
5 5 2021 1 2021-1 115.58
6 6 2021 1 2021-1 115.58
#summarize means by ys (year-shift)
mu <- df %>%
group_by(ys,s) %>%
summarise(grp.mean = mean(x))
mu
ys s grp.mean
<chr> <int> <dbl>
1 2021-1 1 116.
2 2021-2 2 117.
3 2022-1 1 114.
4 2022-2 2 115.
llab<-mu
shift <- c("Shift 1", "Shift 2")
#density charts on df
ggplot(data=df, aes(x=x,group =ys, fill = yr, color = yr)) +
geom_density(alpha = 0.4) +
scale_x_continuous(limits=c(112,120))+
geom_vline(aes(xintercept = grp.mean), data = mu, linetype = "dashed", size = 0.5) +
geom_text(aes(x=llab$grp.mean, y=.6), label = llab$ys) + #this throws the error
geom_vline(aes(xintercept=114.8), linetype="dashed", size=0.5, color = 'green3') +
geom_text(aes(x=114.8, y=.6), label = "Target", angle = 90, color="black",size=3) +
geom_vline(aes(xintercept=114.1), linetype="solid", size=0.5, color = 'limegreen') +
geom_text(aes(x=114.1, y=.55), label = "Potential", angle = 90, color="black",size=3 ) +
geom_vline(aes(xintercept=113.4), linetype="solid", size=0.5, color = 'firebrick3') +
geom_text(aes(x=113.4, y=.62), label = "Label wt", angle = 90,
color="black",size=3, family = "Times New Roman", vjust=0) +
facet_grid(
.~s,
labeller = labeller(
s = c(`1` = "Shift 1", `2` = "Shift 2")
))+
theme_light()+
theme(legend.position = "none")
Output so far...I'm so close.
Persistence pays off. I figured it out and thought I would share it in case someone else has a similar problem:
All code remains the same as in my question except a slight change to grouping for the mu df, AND replace the line that I noted as throwing the error as follows:
#small change to group_by, retaining yr
mu <- df %>%
group_by(yr,s,ys) %>%
summarise(grp.mean = mean(x))
Replace: geom_text(aes(x=llab$grp.mean, y=.6), label = llab$ys), with
geom_text(data = mu, aes(label = yr), x = mu$grp.mean, y = .60, color = "black", angle = 90, vjust = 0)

ggplot, facet, piechart, missing values

This is my final dataset. I originally obtained this table by calculating the values separately and doing rbind between females (F) and males (M) from an original bigger dataset.
I am trying to make a handsome piechart, with the percentage labels outside and I've encountered ALL problems possible for which I cannot find a solution.
Notice there is no value A for males in the dataframe.
Dataframe:
sex ms n_ms n msPerc value
1 F A 1 91 0.01098901 1.098901
2 F B 18 91 0.19780220 19.780220
3 F C 65 91 0.71428571 71.428571
4 F D 7 91 0.07692308 7.692308
5 M B 11 108 0.10185185 10.185185
6 M C 86 108 0.79629630 79.629630
7 M D 11 108 0.10185185 10.185185
library(ggplot2)
library(ggrepel)
library(tidyverse)
n<- c(91, 91 , 91, 91, 108, 108, 108 )
n_ms<-c(1,18,65,7,11,86,11)
sex<- c("F","F","F","F", "M"," M","M")
ms<- c("A","B","C","D","B","C","D")
df <- data.frame(sex, ms, n_ms, n)
df[is.na(df)]<- 0
df$msPerc <- df$n_ms /df$n
df$value <- 100*df$n_ms /df$n
df$n_ms<- as.integer(df$n_ms) # original big dataframe (doing for replication purposes)
df$n<- as.integer(df$n)
#creating position of labels
df2 <- df %>%
mutate(csum = rev(cumsum(rev(value))),
pos = value/2 + lead(csum, 1),
pos = if_else(is.na(pos), value/2, pos))
ms_pie<-ggplot(df, aes(x="", y=msPerc, group=sex, fill=ms)) +
geom_bar(width = 1, stat = "identity") +
coord_polar("y", start=0) +
facet_grid(.~ sex) +
theme_void()+
theme(legend.position="top",
legend.text = element_text(size = 9),
legend.title = element_text(size = 9,face = "bold"))+
scale_fill_manual(values=c("#d7191c", "#fdae61", "#abd9e9","#5e3c99"),
name="Moulting stage",
labels=c("A","B","C","D"))+
# geom_label(aes(label = percent(msPerc)),
# position = position_stack(vjust = 0.5),
# show.legend = FALSE)
# geom_text(aes(label = percent(msPerc)),size = 3,color = "black",
# position = position_stack(vjust = 0.5),
# show.legend = FALSE)
geom_label_repel(data = df2,
aes(y = pos, label = paste0(value, "%")),
size = 4.5, nudge_x = 1, show.legend = FALSE)
ms_pie
This is what happens...
What I would like is a piechart like this one from https://r-charts.com/part-whole/pie-chart-labels-outside-ggplot2/ but including facet_grid in variable "sex".
So far this is the closest I've got. Using geom_label, however my values overlap and I do not know how to separate them either... the joys of being a beginner in R.
I also tried the solutions provided in ggplot, facet, piechart: placing text in the middle of pie chart slices but coor_polar won't work with scales "free".
I would much appreciate the help.
Kind regards.
There are several problems with your code I've tried to clean (see comments preceeded by ####) - this should get you closer:
library(ggplot2)
library(ggrepel)
library(tidyverse)
library(scales) #### using scales for number formatting
n<- c(91, 91 , 91, 91, 108, 108, 108 )
n_ms<-c(1,18,65,7,11,86,11)
sex<- c("F","F","F","F", "M","M","M") #### changed " M" to "M" at last but one element
ms<- c("A","B","C","D","B","C","D")
df <- data.frame(sex, ms, n_ms, n)
df[is.na(df)]<- 0
df$msPerc <- df$n_ms /df$n
df$value <- 100*df$n_ms /df$n
df$n_ms<- as.integer(df$n_ms) # original big dataframe (doing for replication purposes)
df$n<- as.integer(df$n)
#creating position of labels
df2 <- df %>% group_by(sex) %>% #### you need to group your data by the facets you want to show
mutate(csum = cumsum(msPerc), #### adjusted example code to use "msPerc"
pos = msPerc/2 + lag(csum, 1),
pos = if_else(is.na(pos), msPerc/2, pos))
ms_pie<-ggplot(df, aes(x="", y=msPerc, group=sex, fill=ms)) +
geom_col(width = 1) +
coord_polar("y", start=0) +
facet_grid(~sex) +
theme_void()+
theme(legend.position="top",
legend.text = element_text(size = 9),
legend.title = element_text(size = 9,face = "bold"))+
scale_fill_manual(values=c("#d7191c", "#fdae61", "#abd9e9","#5e3c99"),
name="Moulting stage",
labels=c("A","B","C","D"))+
geom_label_repel(data = df2,
aes(y = pos, label = percent(msPerc, digits = 1)),
size = 4.5, nudge_x = 1, show.legend = FALSE)
ms_pie

Adjusting the secondary y axis using ggplot

I am trying to graph two different datasets, reconstructed temperatures (10-16) and charcoal data (0-140), with two different time series values, using ggplot. Is this possible?
I used this code (see below) but unfortunately it produced a plot (see below) that limits the variability of the temperature reconstruction. Is there a way to adjust the y axis so we can see more variability in the temperature record?
Thank you very much for your support.
R code
df <- data.frame(Charfiretempdata$AGETEMPS, Charfiretempdata$FIREAGE, Charfiretempdata$Comp2TEMPS,Charfiretempdata$Char.Acc.Rate..Char...cm.2.yr.1.)
ggplot(df) +
geom_col(mapping = aes(x = Charfiretempdata$FIREAGE,
y = Charfiretempdata$Char.Acc.Rate..Char...cm.2.yr.1. * 16/150), size = 2, color = "darkblue",
fill = "white") +
geom_line(mapping = aes(x = Charfiretempdata$AGETEMPS, y = Charfiretempdata$Comp2TEMPS)) +
geom_point(mapping = aes(x = Charfiretempdata$AGETEMPS, y = Charfiretempdata$Comp2TEMPS), size
= 3, shape = 21, fill = "white")+
scale_y_continuous(
name = expression("Temperature ("~degree~"C)"),
sec.axis = sec_axis(~ . * 150/16 , name = "Charcoal (mm)"))
R plot
I create a random sample data that would share similar characteristics to your data.
library(dplyr)
library(ggplot2)
set.seed(282930)
df <- tibble(x_axis = c(1400, 1500, 1600, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
2007, 2008, 2009, 2010, 2011, 2012, 2013, 2015, 2016, 2017),
y_axis_1 = runif(20, min = 10, max = 16),
y_axis_2 = runif(20, min = 0, max = 150))
Here is the df
> df
# A tibble: 20 x 3
x_axis y_axis_1 y_axis_2
<dbl> <dbl> <dbl>
1 1400 15.7 5.28
2 1500 11.8 141.
3 1600 14.5 149.
4 2000 11.6 121.
5 2001 15.6 37.3
6 2002 15.0 72.5
7 2003 10.7 130.
8 2004 15.4 84.7
9 2005 11.5 118.
10 2006 10.4 17.4
11 2007 11.3 124.
12 2008 13.6 22.6
13 2009 13.0 14.5
14 2010 15.9 142.
15 2011 12.3 103.
16 2012 10.3 131.
17 2013 12.6 93.6
18 2015 14.6 12.4
19 2016 11.4 27.9
20 2017 15.3 116.
Here is the ggplot similar to your but with the different Axis adjustment
ggplot(df,
# as they sharing same X-axis you can define share variable aes in the
# main call of ggplot
aes(x = x_axis)) +
geom_col(mapping =
# added 10 to 2nd axis value as will scale from 10 instead of 0
aes(y = (y_axis_2 * 10 / 150) + 10),
# the size here is size of the border - and due to the nature of
# your data, the col suppose to be very thin to match with that one
# tick on x-axis - so the inner fill is covered by dark blue border
size = 2, color = "darkblue",
# The fill is not really useful as you cannot see it.
fill = "white") +
geom_line(mapping = aes(y = y_axis_1)) +
geom_point(mapping = aes(y = y_axis_1), size
= 3, shape = 21, fill = "white") +
# Set the main Axis start at 10 instead of 0 so it would allow more zoom into it
coord_cartesian(ylim = c(10, 20), expand = c(0, 0)) +
scale_y_continuous(
name = expression("Temperature ("~degree~"C)"),
# The calculation of second axis lable is calculate base on 1st axis.
# and as the 1st axis start at 10, there fore the fomular need to minus 10
# before multiply back 15 - I keep 150 / 10 so it clear reverse of original
# transform of the 2nd axis value above.
sec.axis = sec_axis(~ (. - 10) * 150 / 10 , name = "Charcoal (mm)"))
Here is the sample output plot
And even with the adjsut y-axis we can hardly see the temperature at the end of the data because there are a lot more data points at the end. I think if you don't need all of data point at the end you may just take every 10 x as the data was on the range of 600 years so you don't need to graph so much details at the end. And if you need details just graph that time frame separately
Filter data at the end to only take every 10 year instead
ggplot(df %>% filter(x_axis <= 2000 | x_axis %% 10 == 0),
aes(x = x_axis)) +
# similar code to above but I use geom_bar instead
geom_bar(mapping =
aes(y = (y_axis_2 * 10 / 150) + 10),
stat = "identity", size = 2, color = "darkblue",
fill = "white") +
geom_line(mapping = aes(y = y_axis_1)) +
geom_point(mapping = aes(y = y_axis_1), size
= 3, shape = 21, fill = "white")+
scale_y_continuous(
name = expression("Temperature ("~degree~"C)"),
sec.axis = sec_axis(~ (. - 10) * 150/10 , name = "Charcoal (mm)")) +
coord_cartesian(ylim = c(10, 20), expand = c(0, 0))
(As you can see that with less data point, we started to see the fill as plot have more space)
Zoom in at the end of the data
ggplot(df %>% filter(x_axis >= 2000),
aes(x = x_axis)) +
# similar code to above but I use geom_bar instead
geom_bar(mapping =
aes(y = (y_axis_2 * 10 / 150) + 10),
stat = "identity", size = 2, color = "darkblue",
fill = "white") +
geom_line(mapping = aes(y = y_axis_1)) +
geom_point(mapping = aes(y = y_axis_1), size
= 3, shape = 21, fill = "white")+
scale_y_continuous(
name = expression("Temperature ("~degree~"C)"),
sec.axis = sec_axis(~ (. - 10) * 150/10 , name = "Charcoal (mm)")) +
coord_cartesian(ylim = c(10, 20), expand = c(0, 0))
(Now we can see both the darkblue border and the white fill inside)

Adding title and another line to line graph using ggplot2

I got help from another user on how to create these plots (thank you!):
test <- data.frame("Site_No" = c("01370", "01332", "01442"),"0.99" = c(12, 15, 18), "0.98" = c(14,
15, 18), "0.90" = c(7, 22, 30), ".80" = c(3,2,1), ".75" = c(1, 6, 8), ".70" = c(5,6,9), ".60" = c(15,6,19), ".50" = c(5,6,9), ".40" = c(9,16,20), ".30" = c(1, 15, 3), ".25" = c(5,16,19), ".20" = c(5,1,20), ".10" = c(11,12,13), ".05" = c(15,16,28), "0.02" = c(22,20,12), ".01" = c(3,26,29))
dt <- as.data.table(test)
melted <- data.table::melt(dt, measure = c("X0.99","X0.98","X0.90"))
for (i in unique(melted$Site_No)){
dev.new()
print(ggplot2::ggplot(data = melted[Site_No == i,], mapping = aes(x = variable, y = value, group
= Site_No)) +
ggplot2::geom_line())
}
I just have a few questions for some additions
1) I would like to add a title to each of these graphs with the Site_No. I tried adding title = Site_no to the code, but it didn't work.
2) I would like to add another line to this graph that has this data (a different color than the other line):
test2 <- data.frame("Site_No" = c("01370", "01332", "01442"),"0.99" = c(19, 36, 22), "0.98" = c(19,
10, 28), "0.90" = c(2, 6, 8))
I tried copying the same code to add the other line, but it didn't work.
3) I would like to have each of these 3 plots save to my local directory automatically. So I don't have to do it individually for each plot (I am running 100 plots in reality, not 3).
Thank you so much for your help :)
For your question 1), to add a title, you can use ggtitle in your function.
For the question 2), a possible solution is to bind together your both dataframe.
library(data.table)
melted2 <- melt(setDT(test2), measure = c("X0.99","X0.98","X0.90"))
library(dplyr)
DF <- left_join(melted, melted2, by = c("Site_No","variable"))
DF <- melt(setDT(DF), measure = c("value.x","value.y"), variable.name = "Test",value.name = "Value")
Site_No variable Test Value
1: 01370 X0.99 value.x 12
2: 01332 X0.99 value.x 15
3: 01442 X0.99 value.x 18
4: 01370 X0.98 value.x 14
5: 01332 X0.98 value.x 15
6: 01442 X0.98 value.x 18
7: 01370 X0.90 value.x 7
8: 01332 X0.90 value.x 22
9: 01442 X0.90 value.x 30
10: 01370 X0.99 value.y 19
11: 01332 X0.99 value.y 36
12: 01442 X0.99 value.y 22
13: 01370 X0.98 value.y 19
14: 01332 X0.98 value.y 10
15: 01442 X0.98 value.y 28
16: 01370 X0.90 value.y 2
17: 01332 X0.90 value.y 6
18: 01442 X0.90 value.y 8
Then, to add a second line to your graph, you can modify group in the aes and add the color argument.
So, your function should look like this:
for (i in unique(DF$Site_No)){
dev.new()
print(ggplot2::ggplot(data = DF[Site_No == i,], mapping = aes(x = variable, y = Value, group
= Test)) +
ggplot2::geom_line(aes(color = Test)) +
ggplot2::scale_color_discrete(labels = c("test1","test2"))+
ggplot2::ggtitle(paste("Title:", i)))
}
For your question 3), you can use ggsave to directly save the graph into your current directory.
library(ggplot2)
for (i in unique(DF$Site_No)){
graph <- ggplot(data = DF[Site_No == i,], mapping = aes(x = variable, y = Value, group
= Test)) +
geom_line(aes(color = Test)) +
scale_color_discrete(labels = c("test1","test2"))+
ggtitle(paste("Title:", i))
ggsave(filename = paste0("Site_",i,".png"), plot = graph, device = "png", width = 5, height = 5, units = "in")
}
here an example of the graph saved:
EDIT: With more x values: continuous vs discrete plot
You mentioned you have 18 x values representing some percentiles and you would like them to be nicely display on your graph (they are confounded right now).
One way is to keep those values discrete and simply reduce the size of the x axis text in theme.
Here, the preparation of the datatable based on your new example:
library(data.table)
melted <- melt(setDT(test), measure = list(grep("X",colnames(test))))
melted2 <- melt(setDT(test2), measure = list(grep("X",colnames(test2))))
DF <- left_join(melted, melted2, by = c("Site_No","variable"))
DF <- melt(setDT(DF), measure = c("value.x","value.y"), variable.name = "Test",value.name = "Value")
DF$variable <- gsub("X\\.","X0\\.",DF$variable)
For the plot, you can get:
for (i in unique(DF$Site_No)){
graph <- ggplot(data = DF[Site_No == i,], mapping = aes(x = variable, y = Value, group
= Test)) +
geom_line(aes(color = Test)) +
scale_color_discrete(labels = c("test1","test2"))+
ggtitle(paste("Title:", i))+
theme(axis.text.x = element_text(angle = 90, size = 10, vjust = 0.5))
ggsave(filename = paste0("Site_",i,".png"), plot = graph, device = "png", width = 5, height = 5, units = "in")
}
Which gives you the following graph:
An another possibilty is to represent your data on a continuous scale and arrange the labeling to show a little bit less of text:
DF2 <- DF %>% mutate(variable = as.numeric(gsub("X","",variable)))
setDT(DF2)
for (i in unique(DF2$Site_No)){
graph <- ggplot(data = DF2[Site_No == i,], mapping = aes(x = variable, y = Value, group
= Test)) +
geom_line(aes(color = Test)) +
scale_color_discrete(labels = c("test1","test2"))+
scale_x_continuous(breaks = seq(0,1,by = 0.1))+
ggtitle(paste("Title:", i))
ggsave(filename = paste0("Site_",i,"_conti_.png"), plot = graph, device = "png", width = 5, height = 5, units = "in")
}
Which gives this kind of graph:
Finally, a third possibility is to add a scale to ggsave:
for (i in unique(DF$Site_No)){
graph <- ggplot(data = DF[Site_No == i,], mapping = aes(x = variable, y = Value, group
= Test)) +
geom_line(aes(color = Test)) +
scale_color_discrete(labels = c("test1","test2"))+
ggtitle(paste("Title:", i))
ggsave(filename = paste0("Site_",i,".png"), plot = graph, device = "png", width = 5, height = 5, units = "in", scale = 2)
}
You can also mix those solutions together and get some continuous scale with rotating labeling fro example. It's up to you.
Does it answer your question ?

Small ggplots on a ggmap - a purrr map version

Based on Small ggplot2 plots placed on coordinates on a ggmap
I would like to have the same solution, but with ggplot function outside the pipeline, applied with purrr::map().
The data for small bar subplots indicating 2 values, may contain
lon, lat, id, valueA, valueB,
After tidyr::gather operation it may look like:
Town, Potential_Sum, lon, lat, component , sales
Aaa, 9.00, 20.80, 54.25, A, 5.000
Aaa, 9.00, 20.80, 54.25, B, 4.000
Bbb, 5.00, 19.60, 50.50, A, 3.000
Bbb, 5.00, 19.60, 50.50, B, 2.000
Current working solution is to use do() to generate sublopts and then ggplotGrob to generate a column with objects "grobs" to be placed at lon,lat locations on a ggmap.
maxSales <- max(df$sales)
df.grobs <- df %>%
do(subplots = ggplot(., aes(1, sales, fill = component)) +
geom_col(position = "dodge", alpha = 0.50, colour = "white") +
coord_cartesian(ylim = c(0, maxSales)) +
scale_fill_manual(values = c("green", "red"))+
geom_text(aes(label=if_else(sales>0,round(sales), NULL)), vjust=0.35,hjust=1.1, colour="black",
position=position_dodge(.9), size=2.5, angle=90)+
theme_void()+ guides(fill = F)) %>%
mutate(subgrobs = list(annotation_custom(ggplotGrob(subplots),
x = lon-0.14, y = lat-0.20,
xmax = lon+0.14, ymax = lat+1.2)))
df.grobs %>%
{p + geom_label(aes(x = 15, y = 49.8, label = "A"), colour = c("black"),fill = "green", size=3)+
geom_label(aes(x = 15, y = 5.01, label = "B"), colour = c("black"),fill = "red", size=3)+
.$subgrobs +
geom_text(data=df, aes(label = Miasto), vjust = 3.5,nudge_x = 0.05, size=2.5) +
geom_col(data = df,
aes(0,0, fill = component),
colour = "white")}
p is a ggmap object, map of Poland, on which I would like to place small plots:
# p <-
# get_googlemap(
# "Poland",
# maptype = "roadmap",
# zoom = 6,
# color = "bw",
# crop = T,
# style = "feature:all|element:labels|visibility:off" # 'feature:administrative.country|element:labels|visibility:off'
# ) %>% # or 'feature:all|element:labels|visibility:off'
# ggmap() + coord_cartesian() +
# scale_x_continuous(limits = c(14, 24.3), expand = c(0, 0)) +
# scale_y_continuous(limits = c(48.8, 55.5), expand = c(0, 0))
#
How to translate this solution to the syntax nest - apply -unnest so that the ggplot part should be outside of the piped expression as a function.
In other words. How to replace do() with map(parameters, GGPlot_function) and then plot grobs on a ggmap .
What I did so far was I tried to write a ggplot function
#----barplots----
maxSales <- max(df$sales)
fn_ggplot <- function (df, x, component, maxX) {
x <- enquo(x)
component <-enquo(component)
maxX <-enquo(maxX)
p <- ggplot(df, aes(1, !!x, fill = !!component)) +
geom_col(position = "dodge", alpha = 0.50, colour = "white") +
coord_cartesian(ylim = c(0, !!maxX)) +
scale_fill_manual(values = c("green", "red"))+
geom_text(aes(label=if_else(x>0,round(!!x), NULL)), vjust=0.35,hjust=1.1, colour="black",
position=position_dodge(.9), size=2.5, angle=90)+
theme_void()+ guides(fill = F)
return(p)
}
And got totaly confused trying to apply it like this (I am a constant beginner unfortunately)... this is not working, showing
df.grobs <- df %>%
mutate(subplots = pmap(list(.,sales,component,Potential_Sum),fn_ggplot)) %>%
mutate(subgrobs = list(annotation_custom(ggplotGrob(subplots),
x = lon-0.14, y = lat-0.20,
xmax = lon+0.14, ymax = lat+1.2)))
I get errors indicating I do not know what I am doing, ie lengths of arguments are incorrect and something else is expected.
message: Element 2 of `.l` must have length 1 or 7, not 2
class: `purrr_error_bad_element_length`
backtrace:
1. dplyr::mutate(...)
12. purrr:::stop_bad_length(...)
13. dplyr::mutate(...)
Call `rlang::last_trace()` to see the full backtrace
> rlang::last_trace()
x
1. +-`%>%`(...)
2. | +-base::withVisible(eval(quote(`_fseq`(`_lhs`)), env, env))
3. | \-base::eval(quote(`_fseq`(`_lhs`)), env, env)
4. | \-base::eval(quote(`_fseq`(`_lhs`)), env, env)
5. | \-global::`_fseq`(`_lhs`)
6. | \-magrittr::freduce(value, `_function_list`)
7. | \-function_list[[i]](value)
8. | +-dplyr::mutate(...)
9. | \-dplyr:::mutate.tbl_df(...)
10. | \-dplyr:::mutate_impl(.data, dots, caller_env())
11. +-purrr::pmap(list(., sales, component, Potential_Sum), fn_ggplot)
12. \-purrr:::stop_bad_element_length(...)
13. \-purrr:::stop_bad_length(...)
data
First let's build some sample data close to yours but reproducible without the need for an api key.
As a starting point we have a plot of a country map stored in p, and some data in long form to build the charts stored in plot_data.
library(maps)
library(tidyverse)
p <- ggplot(map_data("france"), aes(long,lat,group=group)) +
geom_polygon(fill = "lightgrey") +
theme_void()
set.seed(1)
plot_data <- tibble(lon = c(0,2,5), lat = c(44,48,46)) %>%
group_by(lon, lat) %>%
do(tibble(component = LETTERS[1:3], value = runif(3,min=1,max=5))) %>%
mutate(total = sum(value)) %>%
ungroup()
plot_data
# # A tibble: 9 x 5
# lon lat component value total
# <dbl> <dbl> <chr> <dbl> <dbl>
# 1 0 44 A 2.06 7.84
# 2 0 44 B 2.49 7.84
# 3 0 44 C 3.29 7.84
# 4 2 48 A 4.63 11.0
# 5 2 48 B 1.81 11.0
# 6 2 48 C 4.59 11.0
# 7 5 46 A 4.78 11.9
# 8 5 46 B 3.64 11.9
# 9 5 46 C 3.52 11.9
define a plotting function
we isolate the plotting code in a separate function
my_plot_fun <- function(data){
ggplot(data, aes(1, value, fill = component)) +
geom_col(position = position_dodge(width = 1),
alpha = 0.75, colour = "white") +
geom_text(aes(label = round(value, 1), group = component),
position = position_dodge(width = 1),
size = 3) +
theme_void()+ guides(fill = F)
}
build a wrapper
This function takes a data set, some coordinates and the plotting function as parameters, to annotate at the right spot.
annotation_fun <- function(data, lat,lon, plot_fun) {
subplot = plot_fun(data)
sub_grob <- annotation_custom(ggplotGrob(subplot),
x = lon-0.5, y = lat-0.5,
xmax = lon+0.5, ymax = lat+0.5)
}
The final code
The the code becomes simple, using nest and pmap
subgrobs <- plot_data %>%
nest(-lon,-lat) %>%
pmap(annotation_fun,plot_fun = my_plot_fun)
p + subgrobs

Resources