Is there any other way to implement plotly R’s cumulative animation? - r

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!

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
Additional Infos:
animation_slider documentation
JS slider attributes
Related GitHub issue
RStudio Community question
Here is the same question on the plotly forum.

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)

Related

Line graph not displayed with plotly in R

My final aim is to create 2 time series line graphs on the same plot, with the one being static and the other being animated (the former refers to the actual data and the latter on my model's fitted values). I am trying to accomplish that with plotly, however I am completely new and have crossed difficulties.
In order to get familiar with plotly first before attempting the above I initially tried to create just one animated graph on a plot. However I cannot even make that ostensibly simple script work. When running the below no graph is displayed on my plot area, like there are no data. My script is created based on following link: https://plot.ly/r/cumulative-animations/
plot_ly(data
, x=~data$RequCreatedFull_Date
, y=~data$fitted_TotalRequ_Qnt_pm
, name="Fitted"
, type='scatter'
, mode = "lines"
, line = list(color = "rgb(255,128,0)")
, frame = ~data$RequCreatedFull_Date
, line = list(simplyfy = F)) %>%
layout(title="name"
, xaxis = list(range =
c(as.numeric(min(data$RequCreatedFull_Date))*1000
,as.numeric(max(data$RequCreatedFull_Date))*1000)
, type = "date"
, title = "Requisition Date"
, zeroline = F)
, yaxis = list(title="Total Requisition Qnts"
, range = c(1000,30000)
, zeroline = F)) %>%
animation_opts(frame = 100,
transition = 0,
redraw=FALSE) %>%
animation_button(x = 1, xanchor = "right", y = 0, yanchor = "bottom")
data is a 53 obs, 4 variables (dates, actuals, fits, index) data frame.
When 'Play' button for animation is clicked and while the animation's frames proceed, when hovering on the plot area the data points' tooltips are displayed for a moment, however no graph is displayed.
Thank you in advance for all your assistance, hope I provided you with sufficient info.
I mistakenly took part of the script the below link for the animated plotting (https://plot.ly/r/cumulative-animations/). The problem is that I did not modify the to-be-framed variable (variable to be used in frame parameter of plot_ly function) before using it.
Therefore, in order for the plot to work properly I should: 1. define accumulate_by function, 2. use it with the to-be-framed variable as input, 3. the output column produced from step 2 will be the value for the frame parameter of 'plot_ly' function.
Initial working data frame is data2, with columns RequCreatedFull-Date(as POSIXct), Requs_Qnt_pm(as num), Type(as Factor), date(as num) where
date=(year(RequCreatedFull_Date)+(month(RequCreatedFull_Date)-1)/12).
Please refer to working script below:
library(plotly)
library(dplyr)
library(lubridate)
#step 1: function definition
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)
}
#step 2: creation of to-be-used for framing variable
data2mod <- data2 %>%
accumulate_by(~date)
#graph creation
my_graph<-data2mod %>%
plot_ly(
x = ~date,
y = ~Requs_Qnt_pm,
split = ~Type,
frame = ~frame, #step 3, to be frame variable insertion
type = 'scatter',
mode = 'lines',
line = list(simplyfy = F)
) %>%
layout(
xaxis = list(
title = "x axis title",
zeroline = F
),
yaxis = list(
title = "y axis title",
zeroline = F
)
) %>%
animation_opts(
frame = 100,
transition = 0,
redraw = FALSE
) %>%
animation_slider(
hide = T
) %>%
animation_button(
x = 1, xanchor = "right", y = 0, yanchor = "bottom"
)
In xaxis and in yaxis showline=TRUE

Plot a static and an animated line graph in same plot with plotly in R

My aim is to create 2 line graphs in the same plot area, however 1 should be static (always displayed), while the other animated (it will be displayed as soon as user clicks on the 'Play' button, as plotly provides). The concept is that we have a line graph with the actual values always displayed, and the animated one will be the fitted observations, showing how well they fit the actual line.
What I have achieved until now is to create both lines in an animated manner, however as mentioned before, I want the observations with Type=="Actuals" to be static, and the others (Type=="Fitted") only animated . Please refer to my current script below.
Initial working data frame is data2 which holds the actual and fitted values unified; with columns RequCreatedFull-Date(as POSIXct), Requs_Qnt_pm(as num), Type(as Factor-indicates whether the record refers to 'actual' or 'fitted' observation), date(as num) where
date=(year(RequCreatedFull_Date)+(month(RequCreatedFull_Date)-1)/12).
library(plotly)
library(dplyr)
library(lubridate)
#function definition for to be framed variable manipulation
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)
}
#creation of to-be-used for framing variable
data2mod <- data2 %>%
accumulate_by(~date)
#graph creation
my_graph<-data2mod %>%
plot_ly(
x = ~date,
y = ~Requs_Qnt_pm,
split = ~Type,
frame = ~frame,
type = 'scatter',
mode = 'lines',
line = list(simplyfy = F)
) %>%
layout(
xaxis = list(
title = "x axis title",
zeroline = F
),
yaxis = list(
title = "y axis title",
zeroline = F
)
) %>%
animation_opts(
frame = 100,
transition = 0,
redraw = FALSE
) %>%
animation_slider(
hide = T
) %>%
animation_button(
x = 1, xanchor = "right", y = 0, yanchor = "bottom"
)
UPDATE:
data2 table
#preparing 2 data tables for the plot (got to be merged with new Type var indicating actuals and fit observations)
agg_acts_rags_rf_ama2<-data.frame(agg_acts_rags_rf_ama, Type="Actuals", date=year(agg_acts_rags_rf_ama$RequCreatedFull_Date)+(month(agg_acts_rags_rf_ama$RequCreatedFull_Date)-1)/12)
agg_fits_rags_rf_ama2<-data.frame(agg_fits_rags_rf_ama, Type="Fitted", date=year(agg_fits_rags_rf_ama$RequCreatedFull_Date)+(month(agg_fits_rags_rf_ama$RequCreatedFull_Date)-1)/12)
names(agg_acts_rags_rf_ama2)[2]<-"Requs_Qnt_pm"
names(agg_fits_rags_rf_ama2)[2]<-"Requs_Qnt_pm"
#unifying the tables
data2<-rbind(agg_acts_rags_rf_ama2, agg_fits_rags_rf_ama2)
agg_acts_rags_rf_ama and agg_fits_rags_rf_ama hold only 2 columns, the RequCreatedFull_Date and a quantity column

annotation in highchart doesn't work as expected

I was using Highchart to plot some time series and wanted to add some annotation to the plot to highlight some key points. I knew putting the cursor on the graph can pop up the context, however, some automatic graph generation is needed and hence annotating is the best approach.
And I did that, with the last line in the code below. However, the effect is not what I expected. The text was located at the bottom left corner, not located at the right horizontal position yet the vertical position is right. The time series are created using xts library, which means the horizontal axis is simply the date data structure, nothing fancy. xValue is specified as the 900th element of all the time points which have a total length of 1018, so the 900th time point must be in the second half of the graph.
Anyone knows how I can put the annotation at the right location? Many thanks.
hc <- highchart(type = "stock") %>%
hc_title(text = "Some time series") %>%
hc_add_series(x, color='green', name="x", showInLegend = TRUE) %>%
hc_add_series(y, color='red', name="y", showInLegend = TRUE) %>%
hc_add_series(z, color='blue', name="z", showInLegend = TRUE) %>%
hc_navigator(enabled=FALSE) %>%
hc_scrollbar(enabled=FALSE) %>%
hc_legend(enabled=TRUE, layout="horizontal") %>%
hc_annotations(list(enabledButtons=FALSE, xValue = index(x)[900], yValue = -5, title =list(text = "Hello world! How can I make this work!")))
hc
The data can be roughly generated using the following script:
dt <- seq(as.Date("2014/1/30"), as.Date("2018/2/6"), "days")
dt <- dt[!weekdays(dt) %in% c("Saturday", "Sunday")]
n <- length(dt)
x <- xts(rnorm(n), order.by=dt)
y <- xts(rnorm(n), order.by=dt)
z <- xts(rnorm(n), order.by=dt)
Let's star with the #kamil-kulig example, this will be a little out of R world but I want to give some justification if you don't mind.
If we see annotations options is not a object but a list of object(s), so in highcharter is implemented the hc_add_annotation function.
Now, you are using a old version of highcharter. Highcharter devlopment version is using v6 of highchartsJS which made some changes: before the annotations.js was a pluging now is included as a module with some changes in the names of arguments.
Example I: Simple
The example by Kamil Kulig is replicated doing:
highchart(type = "stock") %>%
hc_add_annotation(
labelOptions = list(
backgroundColor = 'rgba(255,255,255,0.5)',
verticalAlign = 'top',
y = 15
),
labels = list(
list(
point = list(
xAxis = 0,
yAxis = 0,
x = datetime_to_timestamp(as.Date("2017/01/02")),
y = 1.5
),
text = "Some annotation"
)
)
) %>%
hc_xAxis(
minRange = 1
) %>%
hc_add_series(
pointStart = start,
pointInterval = day,
data = c(3, 4, 1)
)
Example II: With your data
Be careful in the way you add the x position. Highcharter include a datetime_to_timestamp function to convert a date into a epoch/timestap which is required for highcharts.
library(xts)
dt <- seq(as.Date("2014/1/30"), as.Date("2018/2/6"), "days")
dt <- dt[!weekdays(dt) %in% c("Saturday", "Sunday")]
n <- length(dt)
x <- xts(rnorm(n), order.by=dt)
y <- xts(rnorm(n), order.by=dt)
z <- xts(rnorm(n), order.by=dt)
highchart(type = "stock") %>%
hc_title(text = "Some time series") %>%
hc_add_series(x, color='green', name="x", showInLegend = TRUE) %>%
hc_add_series(y, color='red', name="y", showInLegend = TRUE) %>%
hc_add_series(z, color='blue', name="z", showInLegend = TRUE) %>%
hc_navigator(enabled=FALSE) %>%
hc_scrollbar(enabled=FALSE) %>%
hc_legend(enabled=TRUE, layout="horizontal") %>%
hc_add_annotation(
labels = list(
list(
point = list(
xAxis = 0,
yAxis = 0,
x = datetime_to_timestamp(as.Date(index(x)[900])),
y = 1
),
text = "Hello world! How can I make this work!"
)
)
)

ggvis multiple lines with tooltips

I am trying to create an interactive Version of this plot:
So far I have the following code that creates an interactive plot but is not exactly what I am looking for:
#Create Data
library(ggvis)
set.seed(123)
tdat <- data.frame(group = rep(LETTERS[1:2], each = 50),
time = rep(seq(from = as.Date("2010-01-01"), length.out = 50, by = "day"), 2),
val = c(cumsum(rnorm(50)) + 100,
cumsum(rnorm(50)) + 100))
# ggvis Code
# Function for the tooltip
getData <- function(dat){
paste(paste("Time:", as.character(dat$time)),
paste("Group:", as.character(dat$group)),
paste("Value:", dat$val),
sep = "<br />")
}
# Visualisation
tdat %>% ggvis(~time, ~val, stroke = ~group) %>% layer_lines(strokeWidth := 1) %>%
layer_points(size = 1, fill = ~group) %>% add_tooltip(getData)
This results in the following plot:
There are however some issues:
1) I don't want to have points, just lines. Without the layer_points, there are no tooltips...
2) The variable time is a date but shows up as an integer. How can I fix the ugly number?
Thank you very much.
Edit
The field of the tooltip can be formated to date if it is coerced to a char before calling the ggvis function but it introduces other issues. For example, the x-axis does not shown properly.
I found a solution for both:
#Create Data
library(ggvis)
set.seed(123)
tdat <- data.frame(group = rep(LETTERS[1:2], each = 50),
time = rep(seq(from = as.Date("2010-01-01"), length.out = 50, by = "day"), 2),
val = c(cumsum(rnorm(50)) + 100,
cumsum(rnorm(50)) + 100))
For the getData function a bit of reverse engineering made me find the solution. Apparently if you divide the numeric date by 86400000 and add the origin of 1970-01-01 makes it work.
# ggvis Code
# Function for the tooltip
getData <- function(dat){
paste(paste("Time:", as.Date(dat$time/86400000, origin='1970-01-01') ),
paste("Group:", as.character(dat$group)),
paste("Value:", dat$val),
sep = "<br />")
}
As for the points, just setting the opacity to zero makes it work:
# Visualisation
tdat %>% ggvis(~time, ~val, stroke = ~group) %>% layer_lines(strokeWidth := 1) %>%
layer_points(size = 1, fill = ~group, opacity := 0) %>% add_tooltip(getData)
Ouput:
Sorry for the bad output but this was the best I could get via a print screen.

rCharts: Change the individual point colors of a time series plot (Highcharts)

I am trying to create a time-series plot using the plotting interface of rCharts to the Highcharts library.
I am trying to figure out how I can set the color of an individual point depending on its y-value. I found a way to have different colors for the line and the points, but only as a group, not for the data points individually.
Here's the test code:
library(rCharts)
library(rjson)
TransformDate <- function(x){
as.numeric(as.POSIXct(x, origin="1970-01-01")) * 1000
}
x <- TransformDate(c('2013-01-01 11:05:35', '2013-03-03 04:50:35', '2013-05-05 21:09:37', '2013-07-07 12:49:05'))
y <- c(1,56,123,1000)
w<-TransformDate(c('2013-01-10 11:05:35', '2013-03-13 04:50:35', '2013-05-15 21:09:37', '2013-07-17 12:49:05'))
z<-c(10, 100, 70, 500)
df1 <- data.frame(x = x,y = y)
df2 <- data.frame(x = w, y = z)
combo <- rCharts:::Highcharts$new()
combo$series(list(list(data = rCharts::toJSONArray2(df1, json = F, names = F), name = "Temp1", marker = list(fillColor = c('#999'), lineWidth=6, lineColor=c('#999'))),
list(data = rCharts::toJSONArray2(df2, json = F, names = F), name = "Temp2")))
combo$xAxis(type='datetime')
combo$chart(type = "scatter")
combo$chart(zoomType="x")
combo
I believe that this can be done in Polycharts but the reason why I am using highcharts is that it plots time-series data nicely and it has also cool zoom-in functionality.
Thanks in advance for your help & suggestions.
Jan
Here's one way to control color/size for lines/markers separately:
h <- rCharts:::Highcharts$new()
h$series(list(
list(data = rCharts::toJSONArray2(df1, json = FALSE, names = FALSE),
name = "Big Reds",
color = '#FF0000',
lineWidth = 4,
marker = list(
fillColor = '#FFA500',
radius = 10)
),
list(data = rCharts::toJSONArray2(df2, json = FALSE, names = FALSE),
name = "Small Blues",
color = '#0000FF',
lineWidth = 2,
marker = list(
fillColor = '#ADD8E6',
radius = 6)
)))
h$xAxis(type = 'datetime')
h$chart(type = "scatter")
h$chart(zoomType = "x")
h

Resources