Moving image on click in R with gganimate - r

I just started using the awesome gganimate package, and I managed to create a pretty cool animated gif.
However, I would like the image to move from one state to another on click, not automatically. Is there a way to do that with the gganimate package or with another package?
Thank you,
Sarah

I am 2 short of reputations for commenting hence answering directly, otherwise would have asked for data & code, nevertheless I'll try to answer based on understanding from the linked gif.
Since the main aim of gganimate is to create animations which are simply stitched frames from ggplot output, so to my knowledge there is no option to have a click functionality directly.
This would require a package which has capability for interactive graphics, hence plotly is best option. Not trying to demean gganimate (I am myself a big fan of it).
library(plotly)
library(ggplot2)
df <- data.frame(Dim1 = runif(100,-200,200), Dim2 = rpois(100,5),
Timepoint = rep(c("PRE","4WP"),50), pop = runif(100,1,10))
gg <- ggplot(df, aes(Dim1, Dim2)) +
geom_point(aes(size = pop, frame = Timepoint))
ggplotly(gg) %>%
animation_opts(
frame = 1000,
transition = 1000,
easing = "linear",
redraw = TRUE,
mode = "immediate"
)
you can try more easing options from plotly/animation_attributes.js.
This will directly create a interactive plot:
Another option would be to use same animations from gganimate by first saving each frame generated by it in file system using: (as I dont know how you are creating your animation I will create a basic one)
p <- gg + transition_states(Timepoint, transition_length = 100, state_length = 5)
animate(p, renderer = file_renderer(prefix = "gganim_plot", overwrite = TRUE))
which can then be fed into saveHTML command of animation R package.

Related

Animating Histograms with plotly

I'm trying to create an animated demonstration of the Law of Large Numbers, where I want to show the histogram converging to the density as the sample size increase.
I can do this with R shiny, putting a slider on the sample size, but when I try to set up a plotly animation using the sample size as the frame, I get an error deep in the bowels of ggploty. Here is the sample code:
library(tidyverse)
library(plotly)
XXX <- rnorm(200)
plotdat <- bind_rows(lapply(25:200, function(i) data.frame(x=XXX[1:i],f=i)))
hplot <- ggplot(plotdat,aes(x,frame=f)) + geom_histogram(binwidth=.25)
ggplotly(hplot)
The last line returns the error. Error in -data$group : invalid argument to unary operator.
I'm not sure where it is suppose to be getting data$group (this value has been magically set for me in other invocations of ggplotly).
Skipping the initial ggplot and going straight to plotly, does this work for you?
plotdat %>%
plot_ly(x=~x,
type = 'histogram',
frame = ~f) %>%
layout(yaxis = list(range = c(0,50)))
Or, using your original syntax, we can add a position specification that seems to prevent the bug. This version looks better, with standard ggplot formatting and tweened animation.
hplot <- ggplot(plotdat, aes(x, frame = f)) +
geom_histogram(binwidth=.25, position = "identity")
ggplotly(hplot) %>%
animation_opts(frame = 100) # minimum ms per frame to control speed
(I don't know why this fixes it, but when I googled your error I saw a plotly issue on github that was solved by specifying the position, and it seems to fix the error here too. https://github.com/plotly/plotly.R/issues/1544)

R open plotly in standalone window

I would like to display a plotly plot object in a standalone window that behaves similarly to the window that pops up using the base R plot() function.
Using a basic example from the plotly website:
library(ggplot2)
library(plotly)
d <- diamonds[sample(nrow(diamonds), 1000), ]
p <- ggplot(data = d, aes(x = carat, y = price)) +
geom_point(aes(text = paste("Clarity:", clarity))) +
geom_smooth(aes(colour = cut, fill = cut)) + facet_wrap(~ cut)
p2 <- ggplotly(p)
The p2 object is an htmlwidget object and I get some control over its display using the sizingPolicy element as described here. However, I can't find anything that allows me to set the viewer/browser to something other than my current browser (as a new tab) or within RStudio.
Ideally, I'd like to avoid applications outside of R packages to launch a separate window from within R. However, I would also be happy with figuring out how to granularly control browser output to display p2 as a new window in kiosk or app mode (see the answers to this question for some examples of kiosk/app mode).
Edit: Although I mentioned RStudio when discussing some of the options that I was able to find, I am talking about using R from a simple console. That said, granular display options should hopefully be independent of the user interface.
I have a working solution, but I'll be happy to change the accepted answer if someone has anything better.
I defined a print function that can be used to launch a custom browser command for an htmlwidget object. In this case, I used chromium-browser -app=..., but the overall approach should be general.
print_app <- function(widget) {
# Generate random file name
temp <- paste(tempfile('plotly'), 'html', sep = '.')
# Save. Note, leaving selfcontained=TRUE created files that froze my browser
htmlwidgets::saveWidget(widget, temp, selfcontained = FALSE)
# Launch with desired application
system(sprintf("chromium-browser -app=file://%s", temp))
# Return file name if it's needed for any other purpose
temp
}
Combining with the previous example:
library(ggplot2)
library(plotly)
d <- diamonds[sample(nrow(diamonds), 1000), ]
p <- ggplot(data = d, aes(x = carat, y = price)) +
geom_point(aes(text = paste("Clarity:", clarity))) +
geom_smooth(aes(colour = cut, fill = cut)) + facet_wrap(~ cut)
p2 <- ggplotly(p)
print_app(p2)
It seems like htmlwidgets normally uses the html_print function from htmltools, which in turn selects the browser to use via getOption("viewer", utils::browseURL), which bakes in a lot of the browser selection options -- making it challenging to change.
The idea for saving the html file locally came from this plotly issue: saving plotly plots locally?.
If you are using MacOS, change this line in #ssokolen's answer
# Launch with desired application
system(sprintf("chromium-browser -app=file://%s", temp))
to
system(sprintf("open -a 'google chrome' /%s", temp))
Works in zsh in MacOs Catalina with the Intellij R plugin.

external links within a plotly plot?

I'm wondering if there is a way to embed external links within a plotly plot (specifically I'm using the plotly package in R, but I don't have to). I want to be able to click on points in a scatter plot and have that take me to an external page with more detailed information about that point. Below is a fake example to demonstrate the idea.
Also, if this is not possible in plotly, but someone knows another library that could do this, I would love to know about that too! Thanks in advance.
THE EXAMPLE:
Imagine we're plotting the distances of each cruise taken by ships owned by two different cruise lines. The code below makes a plot which shows all the voyages and, when you hover over a point, it shows you the name of the ship that took that voyage. What I want is to be able to click on a point and have it take me to some web page with, for example, a detailed log of that particular voyage. (This is all a fake example, these logs don't actually exist.) Another sidenote: I'm using ggplotly because I'm comfortable with ggplot2 but if there's a way to do this in plot_ly I'm happy to use that.
library(ggplot2)
library(plotly)
shipNames <- c("Princess", "Voyager", "Paul Simon", "Dangelo", "Virginia", "Meade", "Capt America", "Naw Dawg")
set.seed(1)
cruise <- data.frame(company = sample(c("Carnival", "Norweigen"), 20, replace = T),
shipName = sample(shipNames, 20, replace=T),
date = sample(seq(from=as.Date('2016-01-01'), to=as.Date('2016-12-31'), by=1), 20),
distance = sample(seq(from=300, to=500, by=1), 20)
)
g <- ggplot(cruise, aes(x=date, y=distance, colour=company, name = shipName)) + geom_point()
# you have to put the geom_line first or it fucks it up
g <- ggplot(cruise, aes(x=date, y=distance)) + geom_line(aes(colour=company)) + geom_point(aes(colour=company, name = shipName))
ggplotly(g, tooltip = c("x", "name"))

Ggplot does not show plots in sourced function

I've been trying to draw two plots using R's ggplot library in RStudio. Problem is, when I draw two within one function, only the last one displays (in RStudio's "plots" view) and the first one disappears. Even worse, when I run ggsave() after each plot - which saves them to a file - neither of them appear (but the files save as expected). However, I want to view what I've saved in the plots as I was able to before.
Is there a way I can both display what I'll be plotting in RStudio's plots view and also save them? Moreover, when the plots are not being saved, why does the display problem happen when there's more than one plot? (i.e. why does it show the last one but not the ones before?)
The code with the plotting parts are below. I've removed some parts because they seem unnecessary (but can add them if they are indeed relevant).
HHIplot = ggplot(pergame)
# some ggplot geoms and misc. here
ggsave(paste("HHI Index of all games,",year,"Finals.png"),
path = plotpath, width = 6, height = 4)
HHIAvePlot = ggplot(AveHHI, aes(x = AveHHI$n_brokers))
# some ggplot geoms and misc. here
ggsave(paste("Average HHI Index of all games,",year,"Finals.png"),
path = plotpath, width = 6, height = 4)
I've already taken a look here and here but neither have helped. Adding a print(HHIplot) or print(HHIAvePlot) after the ggsave() lines has not displayed the plot.
Many thanks in advance.
Update 1: The solution suggested below didn't work, although it works for the answer's sample code. I passed the ggplot objects to .Globalenv and print() gives me an empty gray box on the plot area (which I imagine is an empty ggplot object with no layers). I think the issue might lie in some of the layers or manipulators I have used, so I've brought the full code for one ggplot object below. Any thoughts? (Note: I've tried putting the assign() line in all possible locations in relation to ggsave() and ggplot().)
HHIplot = ggplot(pergame)
HHIplot +
geom_point(aes(x = pergame$n_brokers, y = pergame$HHI)) +
scale_y_continuous(limits = c(0,10000)) +
scale_x_discrete(breaks = gameSizes) +
labs(title = paste("HHI Index of all games,",year,"Finals"),
x = "Game Size", y = "Herfindahl-Hirschman Index") +
theme(text = element_text(size=15),axis.text.x = element_text(angle = 0, hjust = 1))
assign("HHIplot",HHIplot, envir = .GlobalEnv)
ggsave(paste("HHI Index of all games,",year,"Finals.png"),
path = plotpath, width = 6, height = 4)
I'll preface this by saying that the following is bad practice. It's considered bad practice to break a programming language's scoping rules for something as trivial as this, but here's how it's done anyway.
So within the body of your function you'll create both plots and put them into variables. Then you'll use ggsave() to write them out. Finally, you'll use assign() to push the variables to the global scope.
library(ggplot2)
myFun <- function() {
#some sample data that you should be passing into the function via arguments
df <- data.frame(x=1:10, y1=1:10, y2=10:1)
p1 <- ggplot(df, aes(x=x, y=y1))+geom_point()
p2 <- ggplot(df, aes(x=x, y=y2))+geom_point()
ggsave('p1.jpg', p1)
ggsave('p2.jpg', p2)
assign('p1', p1, envir=.GlobalEnv)
assign('p2', p2, envir=.GlobalEnv)
return()
}
Now, when you run myFun() it will write out your two plots to .jpg files, and also drop the plots into your global environment so that you can just run p1 or p2 on the console and they'll appear in RStudio's Plot pane.
ONCE AGAIN, THIS IS BAD PRACTICE
Good practice would be to not worry about the fact that they're not popping up in RStudio. They wrote out to files, and you know they did, so go look at them there.

How to create an animation of geospatial / temporal data

I have a set of data which contains around 150,000 observations of 800 subjects. Each observation has: subject ID, latitude, longitude, and the time that the subject was at those coordinates. The data covers a 24-hour period.
If I plot all the data at once I just get a blob. Is anyone able to give me some tips as to how I can animate this data so that I can observe the paths of the subjects as a function of time?
I've read the spacetime vignette but I'm not entirely sure it will do what I want. At this point I'm spending a whole lot of time googling but not really coming up with anything that meets my needs.
Any tips and pointers greatly appreciated!
Here my first use of animation package. It was easier than I anticipated and especially the saveHTML is really amazing. Here my scenario(even I think that my R-code will be clearer:)
I generate some data
I plot a basic plot for all persons as a background plot.
I reshape data to get to a wide format in a way I can plot an arrow between present and next position for each person.
I loop over hours , to generate many plots. I put the llop within the powerful saveHTML function.
You get a html file with a nice animation. I show here one intermediate plot.
Here my code:
library(animation)
library(ggplot2)
library(grid)
## creating some data of hours
N.hour <- 24
dat <- data.frame(person=rep(paste0('p',1:3),N.hour),
lat=sample(1:10,3*N.hour,rep=TRUE),
long=sample(1:10,3*N.hour,rep=TRUE),
time=rep(1:N.hour,each=3))
# the base plot with
base <- ggplot() +
geom_point(data=dat,aes(x=lat, y=long,colour = person),
size=5)+ theme(legend.position = "none")
## reshape data to lat and long formats
library(plyr)
dat.segs <- ddply(dat,.(person),function(x){
dd <- do.call(rbind,
lapply(seq(N.hour-1),
function(y)c(y,x[x$time %in% c(y,y+1),]$lat,
x[x$time %in% c(y,y+1),]$long)))
dd
})
colnames(dat.segs) <- c('person','path','x1','x2','y1','y2')
# a function to create the animation
oopt <- ani.options(interval = 0.5)
saveHTML({
print(base)
interval = ani.options("interval")
for(hour in seq(N.hour-1)){
# a segment for each time
tn <- geom_segment(aes(x= x1, y= y1, xend = x2,
yend = y2,colour = person),
arrow = arrow(), inherit.aes = FALSE,
data =subset(dat.segs,path==hour))
print(base <- base + tn)
ani.pause()
}
}, img.name = "plots", imgdir = "plots_dir",
htmlfile = "random.html", autobrowse = FALSE,
title = "Demo of animated lat/long for different persons",
outdir=getwd())
Your question is a bit vague, but I will share how I have done this kind of animation in the past.
Create a function that plots all the subject locations for one time slice:
plot_time = function(dataset, time_id) {
# make a plot with your favorite plotting package (e.g. `ggplot2`)
# Save it as a file on disk (e.g. using `ggsave`), under a regular name,
# frame001.png, frame002.png, see sprintf('frame%03d', time_index)
}
Call this function on each of your timeslices, e.g. using lapply:
lapply(start_time_id:stop_time_id, plot_time)
leading to a set of graphics files on the hard drive called frame001 to framexxx.
Use a tool to render those frames into a movie, e.g. using ffmpeg, see for example.
This is a general workflow, which has been already implemented in the animation package (thanks for reminding me #mdsummer). You can probably leverage that package to get your animation.

Resources