Search code examples
rggplot2r-plotlyr-highcharter

R interactive stacked area chart using long data.frame or ggplotly


I am recreating the interactive plot from the plotly example code below (found here) but I'm wondering if a long data.frame format can be used to avoid adding an individual add_trace function for each variable in the legend. Similar to ggplot2 aesthetic layers.

Any interactive plotting solution would work (highcharter, plotly, etc.).

I also created an interactive stacked area chart from ggplotly below, however the interactive functionality is not the same. Specifically, when layers are toggled on/off at the legend, they do not scale themselves so they are flat along the x-axis. They appear as is. For example, if colB is isolated it floats in the middle of the plot.

The plotly example above does reset the layer and the user can visually inspect profiles of individual layers with a flat x-axis reference.

Thanks for any help.

library(plotly)

data <- t(USPersonalExpenditure)
data <- data.frame("year"=rownames(data), data)

p <- plot_ly(data, x = ~year, y = ~Food.and.Tobacco, name = 'Food and Tobacco', type = 'scatter', mode = 'none', stackgroup = 'one', fillcolor = '#F5FF8D') %>%
  add_trace(y = ~Household.Operation, name = 'Household Operation', fillcolor = '#50CB86') %>%
  add_trace(y = ~Medical.and.Health, name = 'Medical and Health', fillcolor = '#4C74C9') %>%
  add_trace(y = ~Personal.Care, name = 'Personal Care', fillcolor = '#700961') %>%
  add_trace(y = ~Private.Education, name = 'Private Education', fillcolor = '#312F44') %>%
  layout(title = 'United States Personal Expenditures by Categories',
         xaxis = list(title = "",
                      showgrid = FALSE),
         yaxis = list(title = "Expenditures (in billions of dollars)",
                      showgrid = FALSE))

p

#

library(data.table)
library(magrittr)
library(ggplot2)
library(plotly)
library(lubridate)

dt <- data.table(colA = seq(from = ymd_hms("2020-01-01 00:00:00"),
                            to = ymd_hms("2020-01-01 00:00:00") + days(99),
                            by = "1 day"),
                 colB = runif(100,0,100),
                 colC = runif(100,0,100),
                 colD = runif(100,0,100)) %>% 
  melt(id.vars = "colA")

ggplot <- ggplot(data = dt) +
  geom_area(aes(x = colA,
                y = value,
                fill = variable),
            stat = "identity",
            position = "stack",
            alpha = 0.5) +
  theme(legend.title = element_blank())
ggplot

ggplotly(ggplot)

Solution

  • The answer given by @s_t definitely works. But just for completeness I will add another way to accomplish this which may be cleaner.

    You could also create a long data frame and use the split argument in plot_ly

    From the plot_ly documentation:

    split: (Discrete) values used to create multiple traces (one trace per value).

    This will probably do under the hood the same as @s_t option. But it is a little cleaner.

    # create data frame in long format
    data.long <- data %>% tidyr::pivot_longer(-year, names_to = "type", values_to = "value")
    
    # create plot_ly using split argument to separate traces according to type
    p <- plot_ly(data.long, x = ~year, y = ~value, type = 'scatter', 
                 mode = 'none', stackgroup = 'one', split = ~type)
    
    p
    

    enter image description here