5
votes

I would like to overlay 2 bar charts in Plotly (R), but cannot figure it out. Can easily be done in ggplot2, but qqplotly renders it incorrectly so I would like to make the chart in plot_ly. Thanks for any suggestions.

data:

df = data.frame(
year = c(2014,2014,2014,2015,2015,2015,2016,2016,2016),
pet = c("dog","cat","bird","dog","cat","bird","dog","cat","bird"),
wt_before = c(56, 25, 26, 10, 19, 41, 16, 17, 13),
wt_after = c(49, 18, 19,  3, 12, 34,  9, 10,  6)
)

ggplot:

ggplot(df)+
geom_bar(aes(year,wt_before,fill=pet),stat="identity",position="dodge",width = 0.9,alpha=0.5)+
geom_bar(aes(year,wt_after,fill=pet),stat="identity",position="dodge",width = 0.9)+
xlab("Year") +
ylab("Weight")

ggplot

plotly attempt:

plot_ly(df,x= ~year) %>%
add_bars(y= ~wt_before, color = ~pet, alpha = 0.5) %>% 
add_bars(y= ~wt_after, color = ~pet, showlegend=FALSE) %>% 
layout(xaxis=list(title="Year"),
yaxis=list(title="Weight"))

plotly

1
You are actually plotting one bar chart over another in ggplot2, which is very misleading I find. I thought they were stacked, but no, they are actually overlaid. I don't think you can do that in plotly, but I don't actually think one should be able to do it.Mike Wise
This also might help, as there is a way to convert a ggplot to plotly: stackoverflow.com/a/45415888/4240654alchemy

1 Answers

5
votes

As Mike Wise pointed out, it's not a stacked plot but an overlayed bar chart which might lead to weird results (what if the pet gained weight? this kind of information would be lost in the graph). You could plot the weight before and after next to each other, that's more informative and covers all cases.

But let's assume we just want to have a stacked bar chart with multiple identical categorical x-values.

Each bar chart would need to be plotted with "virtual" x-values, i.e a position which is composed of the year (seq) and the animal (i):

xaxis_length <- length(unique(df$year))
animal_no <- length(unique(df$pet))

i <- 0
for (animal in unique(df$pet)) {
  x <- seq(0, 
           animal_no  * xaxis_length + xaxis_length, 
           by = xaxis_length + 1) + i
  i <- i + 1
}

Those x-values can be used to plot the bars, one bar for the base line, one for the difference (by subtracting the two dataframe columns).

for (animal in unique(df$pet)) {
  x <- seq(0, 
           animal_no  * xaxis_length + xaxis_length, 
           by = xaxis_length + 1) + i
  i <- i + 1
  p <- add_trace(p,
                 data=df[df$pet == animal, ], 
                 x = x, 
                 y = ~wt_after, 
                 type = 'bar'
                 )

  p <- add_trace(p, 
                 data=df[df$pet == animal, ], 
                 x = x,
                 y = df[df$pet == animal, ]$wt_before - df[df$pet == animal, ]$wt_after, 
                 type = 'bar'                 
  )
}

Only for the relevant x-axis ticks the values are shown.

layout(barmode = 'stack', 
       xaxis=list(ticktext = unique(df$year),
                  tickvals = seq(1, 
                                 xaxis_length * animal_no +  xaxis_length, 
                                 by = xaxis_length + 1)
                  ),
       bargap = 0)

The colors are created by using a list of colors and setting one to half transparent and other not transparent at all.

colors <- c('rgba(97,156,255,', 
            'rgba(0,186,56,', 
            'rgba(248,118,109,') 
marker=list(color = paste(colors[[animal]], 
                          ",0.5)", 
                          sep = "")

enter image description here

Complete code

library(plotly)
df = data.frame(
  year = c(2014, 2014, 2014, 2015, 2015, 2015, 2016, 2016, 2016, 2017, 2017, 2017),
  pet = c("dog", "cat", "bird", "dog", "cat", "bird", "dog", "cat", "bird", "dog", "cat", "bird"),
  wt_before = c(56, 25, 26, 10, 19, 41, 16, 17, 13, 20, 25, 30),
  wt_after = c(49, 18, 19,  3, 12, 34,  9, 10,  6, 15, 20, 22)
)

colors <- c('rgba(97,156,255,', 
            'rgba(0,186,56,', 
            'rgba(248,118,109,')

xaxis_length <- length(unique(df$year))
animal_no <- length(unique(df$pet))

names(colors) <- unique(df$pet)

p <- plot_ly() %>% layout(barmode = 'stack') %>% 
  layout(barmode = 'stack', 
         xaxis=list(ticktext = unique(df$year),
                    tickvals = seq(1, 
                                   xaxis_length * animal_no +  xaxis_length, 
                                   by = xaxis_length + 1)
                    ),
         bargap=0)

i <- 0
for (animal in unique(df$pet)) {

  x <- seq(0, 
           animal_no  * xaxis_length + xaxis_length, 
           by = xaxis_length + 1) + i
  i <- i + 1
  p <- add_trace(p,
               data=df[df$pet == animal, ], 
               x = x, 
               y = ~wt_after, 
               type = 'bar', 
               name = animal,
               marker = list(color = paste(colors[[animal]], 
                                           ",1)", 
                                           sep = "")
                           ),
               legendgroup = animal,
               text = ~wt_after,
               hoverinfo = 'text'
               )

  p <- add_trace(p, 
                 data=df[df$pet == animal, ], 
                 x = x,
                 y = df[df$pet == animal, ]$wt_before - df[df$pet == animal, ]$wt_after, 
                 type = 'bar', 
                 name = animal,
                 marker=list(color = paste(colors[[animal]], 
                                           ",0.5)", 
                                           sep = "")
                             ),
                 legendgroup = animal,
                 showlegend = FALSE,
                 text = ~wt_before,
                 hoverinfo = 'text'

  )
}
p