Nevedha Ayyanar
Nevedha Ayyanar

Reputation: 865

R plotly show only labels in the stacked bar chart where percentage value for the stack is above 5

I'm having a dataframe

df <- data.frame("QuarterYear" = c("2019 Q1","2019 Q1","2019 Q2","2019 Q2","2019 Q3","2019 Q3","2019 Q3"), "Size" = c("Medium","Small","Large","Medium","Large","Medium","Small"),
                 "percentage" = c(98,2,29,71,13,74,13))

I need to plot a stacked bar chart with labels for each stack

 plot_ly(df, x = df$QuarterYear,
        y = df$percentage,
        type = 'bar',
        name = df$Size,
        text = paste(df$percentage,"%"),
        textposition = 'top',
        hoverinfo = 'text',
        hovertext = paste('Size: ', df$Size,
                          '<br> % of Total count: ', paste(df$percentage,"%")),
        color = df$Size) %>%
  layout(yaxis = list(title = "% of Count", zeroline = FALSE, 
                      showline = FALSE, ticksuffix = "%"), barmode = 'stack',hoverlabel = list(bgcolor= 'white')) %>%
  layout(legend = list(orientation = "h",
                       xanchor = "center",
                       x = 0.5,
                       y = -0.13))%>%
  add_annotations(text = paste0(df$percentage, "%"),
                  x = df$QuarterYear,
                  y = unlist(tapply(df$percentage, df$QuarterYear, FUN=cumsum))-(df$percentage/2),
                  showarrow = FALSE)

The output obtained is

enter image description here

But 2% is not visible since the size of the stack is smaller to accomodate the value. Is there any possibilities to show the labels only when the value is greater than 5?

Thanks in advance!!

Upvotes: 2

Views: 1856

Answers (2)

bri85
bri85

Reputation: 170

I found this post while trying to put value labels on a stacked bar chart. Some of my values I'm plotting are positive while others are negative, so Mohanasundaram's more-elegant solution didn't work well for me. I'm new to R, but maybe others in a similar situation would find this example useful.

library(plotly)
library(tidyverse)
set.seed(123)
animals<-c('cats','dogs')
qty_decimals<-2
crit<-0.5

# create some random data 
df_raw <- data.frame(
  my_xval  =          sample(animals, size=100, replace=TRUE),
  my_yval  =          sample(-10:10 , size=100, replace=TRUE), # can be >0 or <0 
  my_ycolor=as.factor(sample(1:5    , size=100, replace=TRUE))  ) %>% 
  group_by(my_xval, my_ycolor) %>% 
  summarize(my_yval=mean(my_yval), .groups='drop_last') 


create_yvallab_pos <-function(df) {
  # this function creates a column called -lab_position- that identifies where 
  # the annotations should go (vertically)
  df<-df %>%
    mutate(dm_positive = (my_yval>0)) %>% 
    arrange(my_xval, dm_positive, my_ycolor ) %>%
    group_by(my_xval, dm_positive ) %>%
    mutate(cumul = cumsum(my_yval),
           mylag = lag(cumul), 
           mylag = replace(mylag,is.na(mylag),0),
           lab_position = (cumul+mylag)/2) %>%
    ungroup()%>%
    select(!c('cumul','mylag','dm_positive')) %>%
    arrange(my_xval, my_ycolor ) 

  print(df)
  return(df)
}


df<-create_yvallab_pos(df=df_raw)

my_plot<- plotly::plot_ly(data=df, x = ~my_xval, 
                    y = ~my_yval, name = ~my_ycolor, color = ~my_ycolor, 
                    legendgroup = ~my_ycolor, type='bar',showlegend=TRUE)  %>%
  plotly::layout(barmode='relative')


# v1: this is Mohanasundaram's solution, which works fine when the y-values are either all positive or all negative 
my_plot1 <- my_plot %>% 
  add_annotations(text = ifelse(abs(df$my_yval)>=crit, sprintf(df$my_yval, fmt = paste0('%#.',qty_decimals,'f') ), ''), showarrow=FALSE, 
                  x = ~my_xval, y = unlist(tapply(df$my_yval, df$my_xval, FUN=cumsum))-(df$my_yval/2) ) %>%
  plotly::layout(  annotations = list(text = 'V1', xref = "paper", yref = "paper", xanchor = "center", yanchor = "bottom", x = 0.5, y = 0.95, showarrow = FALSE)  )

# v2: this solution might be better if you have some positive and some negative numbers
my_plot2 <- my_plot %>% 
  add_annotations(text = ifelse(abs(df$my_yval)>=crit, sprintf(df$my_yval, fmt = paste0('%#.',qty_decimals,'f') ), ''), showarrow=FALSE, 
                  x = ~my_xval, y = ~lab_position)  %>%
  plotly::layout(  annotations = list(text = 'V2', xref = "paper", yref = "paper", xanchor = "center", yanchor = "bottom", x = 0.5, y = 0.95, showarrow = FALSE)  )

subplot(my_plot1,my_plot2 )

enter image description here

Upvotes: 0

Mohanasundaram
Mohanasundaram

Reputation: 2949

Use ifelse() function for the annotation text.

plot_ly(df, x = df$QuarterYear,
        y = df$percentage,
        type = 'bar',
        name = df$Size,
        text = paste(df$percentage,"%"),
        textposition = 'top',
        hoverinfo = 'text',
        hovertext = paste('Size: ', df$Size,
                          '<br> % of Total count: ', paste(df$percentage,"%")),
        color = df$Size) %>%
  layout(yaxis = list(title = "% of Count", zeroline = FALSE, 
                      showline = FALSE, ticksuffix = "%"), barmode = 'stack',hoverlabel = list(bgcolor= 'white')) %>%
  layout(legend = list(orientation = "h",
                       xanchor = "center",
                       x = 0.5,
                       y = -0.13))%>%
  add_annotations(text = ifelse(df$percentage > 2, paste0(df$percentage, "%"), ""),
                  x = df$QuarterYear,
                  y = unlist(tapply(df$percentage, df$QuarterYear, FUN=cumsum))-(df$percentage/2),
                  showarrow = FALSE)

enter image description here

Upvotes: 1

Related Questions