6
accumulate_by <- function(dat, var) {
  var <- lazyeval::f_eval(var, dat)
  lvls <- plotly:::getLevels(var)
  dats <- lapply(seq_along(lvls), function(x) {
    cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
  })
  dplyr::bind_rows(dats)
}

d <- txhousing %>%
  filter(year > 2005, city %in% c("Abilene", "Bay Area")) %>%
  accumulate_by(~date)

If you implement a cumulative animation in the manner of the function above, the number of rows will increase too much.

I use a thousand frames and a row of ten thousand. Because of the large number of data, the work in progress has been disturbed.

https://plot.ly/r/cumulative-animations/

Is there any way to create a cumulative animation other than the example? Help me!

Ungurrer
  • 111
  • 5

2 Answers2

2

I'm currently facing the same issue. The approach described here is not applicable for a few thousand rows of data.

I don't have a fully working solution, but my idea was to adapt the x-axis range based on the slider value instead of re-using the data for each frame (see example plot p_range_slider). This unfortunately doesn't provide us with the "Play"-button.

I thought it might be possible to use animation_slider() in a similar way but the steps argument passed to animation_slider() is not evaluated (see example plot p_animation_slider). The steps remain tied to the animation frames (as stated in ?animation_slider).

Update: this behaviour is intended by design see the sources:

  # don't let the user override steps
  slider$steps <- steps

Also building a subplot of both sharing the x-axis wasn't successful.

library(plotly)

DF <- data.frame(
  n = 1:50,
  x = seq(0, 12, length = 50),
  y = runif(n = 50, min = 0, max = 10)
)

steps <- list()

for (i in seq_len(nrow(DF))) {
  steps[[i]] <- list(
    args = list("xaxis", list(range = c(0, i))),
    label = i,
    method = "relayout",
    value = i
  )
}

# Custom range slider -----------------------------------------------------

p_range_slider <- plot_ly(
  DF,
  x = ~ x,
  y = ~ y,
  type = "scatter",
  mode = "markers"
) %>% layout(title = "Custom range slider",
       xaxis = list(range = steps[[1]]$args[[2]]$range),
       sliders = list(
         list(
           active = 0, 
           currentvalue = list(prefix = "X-max: "), 
           pad = list(t = 20), 
           steps = steps)))

p_range_slider


# Animation slider --------------------------------------------------------

p_animation_slider <- plot_ly(
  DF,
  x = ~ x,
  y = ~ y,
  type = "scatter",
  mode = "markers",
  frame = ~ n
) %>% layout(title = "Animation slider") %>% animation_slider(
  active = 6,
  currentvalue = list(prefix = "X-max: "),
  pad = list(t = 20),
  steps = steps # custom steps are ignored
)

p_animation_slider

# subplot(p_range_slider, p_animation_slider, nrows = 2, margin = 0.05, shareX = TRUE)

It seems for this approach to work animation_slider() would need to allow it's steps argument to perform custom actions (untied from the defined frames). Any other Ideas to approach this are highly appreciated.

Maybe it is possible to reproduce this approach for the python api using a filter (avoids axis-rescaling) in R? - Filter in R

Here is an example on how to use filter transform and a custom range slider in R, however, still no animation (without precomputing each frame):

DF <- data.frame(x = rep(1:10, 2), y = runif(20), group = rep(LETTERS[1:2], each = 10))

steps <- list()

for (i in seq_along(unique(DF$x))) {
  steps[[i]] <- list(
    args = list('transforms[0].value', i),
    label = i,
    method = "restyle",
    value = i
  )
}

p_filter_slider <- plot_ly(
  DF,
  x = ~ x,
  y = ~ y,
  color = ~ group,
  type = "scatter",
  mode = "lines+markers",
  transforms = list(
    list(
      type = 'filter',
      target = 'x',
      operation = '<=',
      value = ~ x
    )
  )
) %>% layout(title = "Custom filter slider",
             xaxis = list(range = c(0.5, length(unique(DF$x))+0.5)),
             sliders = list(
               list(
                 active = 0, 
                 currentvalue = list(prefix = "X-max: "), 
                 pad = list(t = 20), 
                 steps = steps))
             )

p_filter_slider

Result


Additional Infos:

animation_slider documentation

JS slider attributes

Related GitHub issue

RStudio Community question

Here is the same question on the plotly forum.

ismirsehregal
  • 30,045
  • 5
  • 31
  • 78
0

Had a similar problem. We had thousands of data points per minute, so as a first step I just made another column called "time_chunk" which just rounded the time to the minute.

Then I accumulated by the time_chunks instead of the very precise minute data (with decimal places). That gave me a more reasonable number of chunks.

The animation is not completely smooth but our data is approximately 20-40 minutes long, so it's reasonable and processes quickly.

So, theoretically:

fig <- fig %>%
mutate(time_chunk = floor(time_min))

fig <- accumulate_by(fig, ~ time_chunk)
ismirsehregal
  • 30,045
  • 5
  • 31
  • 78