rkraft
rkraft

Reputation: 545

R Plotly -Stacked Barchart with total amount of bars text

Problem: I have a stacked barchart. Above each of the stacked bars I want to have a total number (i.e. the sum of the two bars - here: total). I tried to take usage of add_text but it did not work out as expected. Any suggestions what to change?

Remark: I do not want to cast the data.table object. The target frame on which the visualizations shall be made is new_frame.

Many thanks

enter image description here

library(data.table)
library(plotly)

Animals <- c("giraffes", "orangutans", "monkeys")
SF_Zoo <- c(20, 14, 23)
LA_Zoo <- c(12, 18, 29)
data <- data.table(Animals, SF_Zoo, LA_Zoo)

new_frame <- data.table::melt(data, id.vars='Animals')

new_frame[, total := sum(value), by = Animals]

  plot_ly(new_frame, x = ~Animals, 
          y = ~value, 
          type = 'bar', 
          name = ~variable,
          hoverinfo = "text",
          text = ~paste(variable, value, total)) %>%
## Not working
    add_text(x = ~Animals, y = ~value,
             text = ~total, textposition = "top center") %>%
  layout(yaxis = list(title = 'Count'), barmode = 'stack')


Upvotes: 1

Views: 2416

Answers (1)

Ray
Ray

Reputation: 2268

Plotly is a powerful package and I feel with you ... some of the logic is not always self-evident. In particular when adding hovertexts, text elements, and/or annotations. You have to be careful about what are your variables and what is the textual element you want to add/control. I hope the following allows you to step through the in-and-outs.

(I) Hovertext With your data.table you have a data.frame that you provide to a trace (think broadly of a geom in ggplot). In this case a "bar" trace. I changed a bit the code to make this clearer by pulling the dataframe out and only use an empty plot_ly() call and adding deliberately a "bar"-trace. This is strictly not necessary. However it allows you to understand what you are working on.

new_frame %>%
plot_ly() %>%
  add_bars(x = ~Animals, 
           y = ~value, 
           type = 'bar', 
           name = ~variable,
           hovertemplate = "my value: %{y}"
           ) %>%
## Not working
#    add_text(x = ~Animals, y = ~value,
#             text = "gotcha", textposition = "top center") %>%
  layout(yaxis = list(title = 'Count'), barmode = 'stack')

With this code you have altered the presentation of the "hover" tooltip. The name parameter controls the "title" of the tooltip. For example, hovering over the plot shows the formatted hover, i.e. "my value: " and the name of the stacked bar "SF_Zoo". In the template definition you refer to the y-variable and not your dataframe (column) variable (name).

enter image description here

(II) Adding a "text" Again think geom_text with adding a "text" trace. Note that trace is not fully correct here. You then map the position of the text to the x and y parameter of add_text() call. This will display a text fragment at each mapped position (x,y). In the example, I provide a text vector of the same length than (x,y) combinations. If you provide a single string, this will be replicated. Here the text trace inherits from your new_frame. Note you could provide a new object here similar to what is defined in the next step.

(III) adding annotations With add_annotations() you can add text elements and pointers. Again, you can provide a new object. As we only need the 3 totals for the group of animals, I reduce the data for this step to what is needed. With this "data format" I can now supply the variable total to the text parameter. I set showarrow = TRUE to provide an example on how to augment the text add on with an arrow.

new_frame %>%
plot_ly() %>%
  add_bars(x = ~Animals, 
           y = ~value, 
           type = 'bar', 
           name = ~variable,
           hovertemplate = "my value: %{y}"
           ) %>%
## Now let's add a text trace (think ggplot layer)
  add_text( x = ~Animals
           ,y = ~value,
           ,text = c("one","two","three","four","five","six")    # this overwrites your values with a string
           ,textposition = "center"
           ) %>%
## add annotation
  add_annotations(data = new_frame %>% select(Animals, total) %>% unique()
                  ,x = ~Animals
                  ,y = ~total
                  ,text = ~total
        ## remove arrow and offset
                  ,showarrow = TRUE
        ## with showarrow = TRUE you can control the arrow          
        #          , ax = 20
        #          , ay = 40
                  ) %>%
## control the layout of the chart
  layout(yaxis = list(title = 'Count'), barmode = 'stack')

enter image description here

Feel free to explore further options.

In summary, in plotly you provide a vector of values to x, y, or text. Strictly speaking these do not need to be organised as a dataframe. The dataframe allows you to make use of the ~notation for variables. There are 2 ways to add text. Make sure to understand how you use the position mapping (x, y) in combination with the text. For purists add_annotation() is more in line with a ggplot mind to have arbitrary text. But by defining the data object you supply to add_text() you can achieve a similar result. add_annotations() give you some additional styling elements with various arrow-designs.

Upvotes: 1

Related Questions