3

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 enter image description here

Type 2: Stacked board/alight barplot by mode enter image description here

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)

enter image description here

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.
- 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. It could be solved

Type 2:
- Cannot make grouped and stacked barplot.

Common issues
- Ribbon is not stepwise shape It could be solved
- Ribbon is not stacked (and position="stack" returns an error) It could be solved
- Ribbon make bars less-visible (perhaps could be adjusted by alpha but it is much better if anteroposterior relation can be set) It could be solved

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)

Example of geom_area enter image description here

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: enter image description here

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:

Generate a Filled geom_step

## 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)

enter image description here

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.

enter image description here

    ## 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()
HSJ
  • 687
  • 6
  • 16
  • 1
    if you call geom_ribbon before geom_bar the ribbon will be at the back – Richard Telford Jul 05 '18 at 07:05
  • Thank you. Yes, I also realized that during trial. – HSJ Jul 05 '18 at 07:10
  • 1
    You can't stack and group at the same time. Reversing the order in `interaction` will change the order. If all else fails, set the levels of the factor manually to the correct order. – Axeman Jul 05 '18 at 07:43
  • Yes, I also remembered that technique and just updated the post which shows reordered bars by changing its levels. I will give up one remaining issue of *stack and group* at this moment. Thank you. – HSJ Jul 05 '18 at 07:45

0 Answers0