Search code examples
rchartssliderr-plotly

Plotly R : how to add dynamic annotation which belongs to frame


I'm struggeling on a simple task. I have a database with 3 columns :

  • Year (numeric)
  • Age (numeric)
  • Pop (numeric)
  • Part60 : The % of individuals with age >= 60 (string like '% of poeple over 60 : 12%'). This value is the same for each rows of a year. Dataset looks like :

enter image description here

I built a plotly bargraph with a frame based on the year. So I have a slider which allow me to show for each age the number of individuals and this is animated year by year.

I would like to add an anotation which shows the value of Part60 for the year of the frame... I know that it's possible with a ggplot sent to ggplotly function, however I want to do it from scratch with a plot_ly function as parameters are (for me) easier to control and follow the logic of my code.

enter image description here

This is my code :

gH <- plot_ly(data = dataH,
              name = 'Hommes',
              marker = list(color = ispfPalette[4]),
              x = ~Pop,
              y = ~Age,
              frame = ~Annee)
gH <- gH %>% layout(yaxis = list(categoryorder="array",
                                 categoryarray=dataH$Age))
gH <- gH %>% layout(yaxis = list(title = '',
                                 zeroline = TRUE,
                                 showline = TRUE,
                                 showticklabels = TRUE,
                                 showgrid = FALSE), 
                    xaxis = list(title = '',
                                 zeroline = TRUE,
                                 showline = TRUE,
                                 autorange = "reversed"),
                    shapes = hline(60)) 
gH <- gH %>% add_annotations(
  x = 3000,
  y = 62,
  text = 'Part des 60 ans et + : 12 %',
  showarrow = F,
  color = ispfPalette[8]

Where text = 'Part des 60 ans et + : 12 %' should be replaced by something which allow me to get the value which belongs to the year of the slider.

Is someone may help me to do it ?

Thanks in advance for your great help.


Solution

  • Since I don't have your data, it's pretty difficult to give you the best answer. Although, here is a method in which you can add text that changes throughout the animation.

    library(plotly)
    library(tidyverse)
    
    data(gapminder, package = "gapminder")
    str(gapminder)
    
    funModeling::df_status(gapminder)
    # continent, lifeExp, year
    
    gap <- gapminder %>% group_by(year, continent) %>%
      summarise(Expectancy = mean(lifeExp))
    
    # plot 
    p1 <- plot_ly(gap, x = ~Expectancy, y = ~continent,
                  frame = ~year, type = 'bar', 
                  showlegend = F,
                  hovertemplate = paste0("Continent: %{y}<br>",
                                         "<extra></extra>"),
                  texttemplate = "Life Expectancy: %{x:.2f}") %>%
      layout(yaxis=list(title=""), 
             xaxis=list(title="Average Life Expectancy per Continent By Year"),
             title=list(text=paste("Fancy Title")),
             margin = list(t = 100))
    p1
    

    enter image description here

    If you had text you wanted to animate that is not connected to each marker (bar, point, line), then you could do it this way.

    # Something to add in the annotation text
    gap2 <- gap %>% filter(continent == "Asia") %>% 
      droplevels() %>% 
      arrange(year)
    
    # build to see frames
    p2 <- plotly_build(p1)
    
    # modify frames; need an annotation for each frame
    # make sure the data is in order by year (order by frame)
    lapply(1:nrow(gap2), # for each frame
           function(i){
             annotation = list(
               data = gap2,
               type = "text",
               x = 77,
               y = .5,
               yref = "paper",
               showarrow = F,
               text = paste0("Asian Life Expectancy<br>", 
                             sprintf("%.2f", gap2[i, ]$Expectancy)), 
               font = list(color = "#b21e29", size = 16))
             p2$x$frames[[i]]$layout <<- list(annotations = list(annotation)) # change plot
           })
    p2
    

    enter image description here

    If anything is unclear, let me know.