kporkka
kporkka

Reputation: 71

Bar chart overlay in Plotly R

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

Upvotes: 6

Views: 3448

Answers (1)

Maximilian Peters
Maximilian Peters

Reputation: 31659

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

Upvotes: 6

Related Questions