I am trying to produce a bar chart combining ribbon/area chart which illustrates boarding/alighting passengers of BRT/MRT lines at each station as well as sectional passenger volume along a corridor by direction.
Initial data frame is shown below:
df <- data.frame(
mode=c("mrt","mrt","mrt","mrt","mrt","mrt","mrt","mrt","mrt","mrt",
"brt","brt","brt","brt","brt","brt","brt","brt","brt","brt"),
direction=c("Northbound","Northbound","Northbound","Northbound","Northbound",
"Southbound","Southbound","Southbound","Southbound","Southbound",
"Northbound","Northbound","Northbound","Northbound","Northbound",
"Southbound","Southbound","Southbound","Southbound","Southbound"),
station=c("a","b","c","d","e","e","d","c","b","a","a","b","c","d","e","e","d","c","b","a"),
on=c(40,30,20,10,0,40,30,20,10,0,20,15,10,5,0,20,15,10,5,0),
off=c(0,10,20,30,40,0,10,20,30,40,0,5,10,15,20,0,5,10,15,20),
volume=c(40,60,60,40,0,40,60,60,40,0,20,30,30,20,0,20,30,30,20,0))
Initial data frame is converted into long format for ggplot2
.
library(tidyverse)
df2 <- df %>% gather(key=key,value=trip,on,off) %>% mutate(
station=factor(station,levels=c("a","b","c","d","e"),labels=c("a","b","c","d","e")),
key=factor(key,levels=c("on","off"),labels=c("on","off")),
direction=factor(direction,levels=c("Northbound","Southbound"),
labels=c("Northbound","Southbound")),
mode=factor(mode,levels=c("mrt","brt"),labels=c("mrt","brt")))
Printed df2
look like this:
> df2
mode direction station volume key trip
1 mrt Northbound a 40 on 40
2 mrt Northbound b 60 on 30
3 mrt Northbound c 60 on 20
4 mrt Northbound d 40 on 10
5 mrt Northbound e 0 on 0
6 mrt Southbound e 40 on 40
7 mrt Southbound d 60 on 30
8 mrt Southbound c 60 on 20
9 mrt Southbound b 40 on 10
10 mrt Southbound a 0 on 0
11 brt Northbound a 20 on 20
12 brt Northbound b 30 on 15
13 brt Northbound c 30 on 10
14 brt Northbound d 20 on 5
15 brt Northbound e 0 on 0
16 brt Southbound e 20 on 20
17 brt Southbound d 30 on 15
18 brt Southbound c 30 on 10
19 brt Southbound b 20 on 5
20 brt Southbound a 0 on 0
21 mrt Northbound a 40 off 0
22 mrt Northbound b 60 off 10
23 mrt Northbound c 60 off 20
24 mrt Northbound d 40 off 30
25 mrt Northbound e 0 off 40
26 mrt Southbound e 40 off 0
27 mrt Southbound d 60 off 10
28 mrt Southbound c 60 off 20
29 mrt Southbound b 40 off 30
30 mrt Southbound a 0 off 40
31 brt Northbound a 20 off 0
32 brt Northbound b 30 off 5
33 brt Northbound c 30 off 10
34 brt Northbound d 20 off 15
35 brt Northbound e 0 off 20
36 brt Southbound e 20 off 0
37 brt Southbound d 30 off 5
38 brt Southbound c 30 off 10
39 brt Southbound b 20 off 15
40 brt Southbound a 0 off 20
There are two types of output that I am trying to reach:
Type 1: Separate board/alight barplot by mode
Type 2: Stacked board/alight barplot by mode
Currently I could make following figure by following scripts:
ggplot(df2,aes(x=station)) +
geom_bar(aes(y=trip,fill=interaction(mode,key)),stat="identity",
position=position_dodge(preserve="single"),width=0.8) +
geom_ribbon(aes(ymin=0,ymax=volume,group=interaction(mode,key),fill=mode),alpha=0.2) +
facet_wrap(~direction,nrow=2)
The issues are:
Type 1:
- Cannot make white space between mrt
and brt
in x-axis with label of mode
. I would make it to clarify the difference of each mode.
- It could be solved interaction
function in x-axis does not provide ideal order of mode
and key
. As mentioned in the first point, mrt-on
,mrt-off
,brt-on
,brt-off
is the best positioning of labels.
Type 2:
- Cannot make grouped and stacked barplot.
Common issues
- Ribbon is not stepwise shape It could be solved
- Ribbon is not stacked (and It could be solved position="stack"
returns an error)
- Ribbon make bars less-visible (perhaps could be adjusted by It could be solvedalpha
but it is much better if anteroposterior relation can be set)
I am not sure if such adjustment could be made in ggplot2
but I would have your suggestions to obtain ideal graph.
I also tested some other examples, such as geom_step
but did not work well. Another issue is that if I use geom_area
instead of geom_ribbon
, unnecessary value stored in volume
field related to key==off
is also plotted:
ggplot(df2,aes(x=station)) +
geom_bar(aes(y=trip,fill=interaction(mode,key)),stat="identity",
position=position_dodge(preserve="single"),width=0.8) +
geom_area(aes(y=volume,group=interaction(mode,key),fill=interaction(mode,key)),position="stack") +
facet_wrap(~direction,nrow=2)
I highly appreciate your suggestion to obtain ideal outcome that I am trying to produce for these few days.
== UPDATED ==
I could have slight update of the figure as shown below:
I used geom_stepribbon
, an extension of ggplot2:
https://cran.r-project.org/web/packages/RcmdrPlugin.KMggplot2/vignettes/geom-stepribbon.html
Also, it is necessary to adjust passenger volume for southbound using lag
as mentioned below:
## Prepare df for geom_stepribbon [it requires some more packages]
df3 <- df2 %>% group_by(direction) %>%
mutate_at(vars(volume),funs(ifelse(direction=="Southbound",lag(volume),.))) %>% data.frame()
## Plot
ggplot(df3,aes(x=station)) +
geom_stepribbon(aes(ymin=0,ymax=volume,group=mode,fill=mode),alpha=0.5) +
geom_bar(aes(y=trip,fill=interaction(mode,key)),stat="identity",
position=position_dodge(preserve="single"),width=0.8) +
facet_wrap(~direction,nrow=2)
However, I still have following issues:
Order of barsIt could be solved- Space between
mode
in type1 - Stacked and grouped bars in type2
Further suggestions are appreciated.
== UPDATED ver.2==
Remaining issue on order of bar is solved by manually changing levels of factor in the following script.
ggplot(df5,aes(x=station)) +
geom_stepribbon(aes(ymin=0,ymax=volume,group=mode,fill=mode),alpha=0.5) +
geom_bar(aes(y=trip,fill=factor(interaction(mode,key),
levels=c("mrt.on","mrt.off","brt.on","brt.off"),
labels=c("MRT-Board","MRT-Alight","BRT-Board","BRT-Alight"))),
stat="identity", position=position_dodge(preserve="single"),width=0.8) +
facet_wrap(~direction,nrow=2)
Perhaps space between bars of MRT
and BRT
is a bit complicated.
Suggestions for one remaining issue, stacked and grouped bars are still welcomed.
It is reported that stacking and grouping are not possible to be done at the same time by @Axeman
== FINAL UPDATE ==
As I could reach the one that I need to produce, I will share the scripts herewith:
However, I am still confusing how scale_fill_manual
works. Order of values
and breaks/labels
are not corresponded each other. I just manually adjusted it based on the result and highly appreciate if you could provide any suggestions to make the codes better.
## Set year
year <- 18
## df5 corresponds to df3 in the previous update
## Maximum passenger
max.mrt.n <- df5 %>% filter(mode=="mrt" & direction=="Northbound") %>%
summarize(max=max(volume)) %>% as.integer()
max.mrt.s <- df5 %>% filter(mode=="mrt" & direction=="Southbound") %>%
summarize(max=max(volume)) %>% as.integer()
max.brt.n <- df5 %>% filter(mode=="brt" & direction=="Northbound") %>%
summarize(max=max(volume)) %>% as.integer()
max.brt.s <- df5 %>% filter(mode=="brt" & direction=="Southbound") %>%
summarize(max=max(volume)) %>% as.integer()
max.mrt <- data.frame(direction=c("Northbound","Southbound"),label=c(max.mrt.n,max.mrt.s))
max.brt <- data.frame(direction=c("Northbound","Southbound"),label=c(max.brt.n,max.brt.s))
## Set print window
png("CorridorPax.png",height=720, width=960, res=144)
## Specify data frame and x-axis
g <- ggplot(df5,aes(x=station)) +
## Draw step ribbon chart
geom_stepribbon(aes(ymin=0,ymax=volume,
group=factor(mode,levels=c("mrt","brt"),labels=c("MRT Pax.","BRT Pax.")),
fill=factor(mode,levels=c("mrt","brt"),labels=c("MRT Pax.","BRT Pax."))),
alpha=0.4) +
## Draw bar chart
geom_bar(aes(y=trip,fill=factor(interaction(mode,key),
levels=c("mrt.on","mrt.off","brt.on","brt.off"),
labels=c("MRT-Board","MRT-Alight","BRT-Board","BRT-Alight"))),
stat="identity", position=position_dodge(preserve="single"),width=0.8) +
## Facet by direction
facet_wrap(~direction,nrow=2) +
## Appearance
## Title and xy-axis
ggtitle(paste0("Passenger volume of MRT and BRT along the corridor in 20",year)) +
xlab("Station") +
ylab("Number of Daily Passengers ('000)") +
## Theme
theme_bw() +
## Rotate label and adjust font size
theme(title=element_text(size=10),
axis.title=element_text(size=10),
axis.text.x=element_text(angle=90,hjust=1,vjust=0.25,size=10),
axis.text.y=element_text(size=8),
legend.text=element_text(size=8)
) +
## Limit and breaks of y-axis
scale_y_continuous(breaks=seq(0,60,by=5),limits=c(0,60)) +
## Legend
scale_fill_manual(name="Passenger",
values=c("cadetblue2","darkorange","gold",
"royalblue","firebrick","lawngreen"),
breaks=c("MRT Pax.","BRT Pax.","MRT-Board","MRT-Alight","BRT-Board","BRT-Alight"),
labels=c("MRT Pax.","BRT Pax.", "MRT-Board","MRT-Alight","BRT-Board","BRT-Alight")) +
## Maximum load
geom_text(mapping=aes(x=Inf,y=Inf,
label="Maximum sectional load (1,000 pax/day)",vjust=1.3,hjust=1.02),size=3) +
geom_text(data=max.mrt,mapping=aes(x=Inf,y=Inf,
label=paste0("MRT=",label)), vjust=2.6,hjust=2.4,size=3) +
geom_text(data=max.brt,mapping=aes(x=Inf,y=Inf,
label=paste0("BRT=",label)), vjust=2.6,hjust=1.2,size=3)
## Export png
print(g)
## Close printing window
dev.off()