I would like to animate a ggplot with gganimate using geom_area with different values for the years between 2000 and 2050. However, for some reason if I include view_zoom to keep the y-axis fixed and to zoom out along the x-axis for around the first 50 frames it zooms in between the values 1999.95 and 2000.05 and for the last 50 frames it shows the whole range of the x-axis (from year 2000 to 2050). How can I fix this, so that it gradually zooms out until it shows the whole range of the x-axis at the end?
library(gganimate)
library(tidyverse)
gif_data <-
tibble(year = as.numeric(2000:2050),
value = as.numeric(seq(0.5, 0.3, length.out = 51)))
gif <-
ggplot(gif_data,
aes(x = year,
y = value)) +
geom_area() +
transition_reveal(year) +
ggtitle('Frame {frame} of {nframes}') +
view_zoom(fixed_y = TRUE)
animate(gif,
fps = 10,
duration = 10,
height = 337.5,
width = 600,
units = "px",
res = 50,
renderer = gifski_renderer())
anim_save("~/Desktop/gif.gif",
animation = last_animation())
Use view_follow instead of view_zoom like this:
gif <-
ggplot(gif_data,
aes(x = year,
y = value)) +
geom_area() +
transition_reveal(year) +
ggtitle('Frame {frame} of {nframes}') +
view_follow(fixed_y = TRUE)
Related
I was wanting to make an animated ggplot of daily values for countries over the course of a year. I was able to do that thanks to some helpful questions/answers here.
While the code below successfully makes a gif, the changes/transitions when countries move up or down/overtake one another is very abrupt (i.e instantaneous). It is not smooth like the examples in the linked post.
I'm not entirely sure why this is happening and would really appreciate some help. Thanks in advance to anyone who is able to provide some insight. Code is below.
library(tidyverse)
library(ggplot2)
library(gganimate)
library(gifski)
library(png)
# Generate some fake data
n=365
df <- data.frame(country_name = rep(c("Country_1","Country_2","Country_3","Country_4","Country_5"), n, replace=FALSE),
date = rep(seq.Date(as.Date("2021-01-01"), as.Date("2021-12-31"), "day"),each=5), # each is number of countries
incidents=sample(1:100, size = 25, replace=TRUE
))
# Make cumulative number of events
df = df %>%
group_by(country_name) %>%
arrange(date) %>%
mutate(cumulative_incidents = cumsum(incidents)) %>%
ungroup()
# create integer rankings (I thought the *1 would make things smoother)
df = df %>%
group_by(date) %>%
mutate(rank = min_rank(-cumulative_incidents *1),
Value_rel = cumulative_incidents/cumulative_incidents[rank==1],
Value_lbl = paste0(" ",round(cumulative_incidents/1e9))) %>%
ungroup()
# make the static plot
my_plot = ggplot(df, aes(-rank,Value_rel, fill = country_name)) +
geom_col(width = 0.8, position="identity") +
coord_flip() +
geom_text(aes(-rank,y=0,label = country_name,hjust=0)) +
geom_text(aes(-rank,y=Value_rel,label = cumulative_incidents, hjust=0)) +
theme_minimal() +
theme(legend.position = "none",axis.title = element_blank()) +
# animate along Year
transition_states(date,4,1)
# animate the plot
animate(my_plot, 100, fps = 25, duration = 20, width = 800, height = 600)
The temporal resolution of your data is very high, you have 365 timepoints, but only 500 frames in your animation. These smooth switches therefore happen within 500 / 365 = ~1.4 frames and aren't visible.
Either make your animation much longer, reduce the time resolution of the data, or manually code in transitions. For example, here's what happens if we give data only once a month:
df2 <- filter(df, date %in% seq(min(date), max(date), 'week'))
my_plot <- ggplot(df2, aes(-rank,Value_rel, fill = country_name)) +
geom_col(width = 0.8, position="identity") +
coord_flip() +
geom_text(aes(-rank,y=0,label = country_name,hjust=0)) +
geom_text(aes(-rank,y=Value_rel,label = cumulative_incidents, hjust=0)) +
theme_minimal() +
theme(legend.position = "none",axis.title = element_blank()) +
# animate along Year
transition_states(date, transition_length = 10, state_length = 1)
animate(my_plot, fps = 25, duration = 20, width = 800, height = 600)
I'm new to gganimate and was having difficulty figuring out how to do this.
I'd like to show the spread in two different levels of a variable by animating colour transitions. I want to show this by having the narrow level transition through a smaller range of colours than the wider level in the same amount of time. Is this possible?
Here's the reproducible example I have up-to now.
library(ggplot2)
df <- data.frame(x = rep(c("narrow", "wide"), each = 1000),
y = c(sort(runif(n = 1000, min = 6, max = 7)),
sort(runif(n = 1000, min = 3, 10))),
animate.time = c(1:1000))
ggplot(data = df, aes(x = x, fill = y)) +
geom_bar()
Created on 2021-06-12 by the reprex package (v2.0.0)
As part of this, the color scale that each bar uses should be the same, but the narrow x should occupy a narrower range of the color range than the wide x.
I used geom_bar because I don't want the shape of the visualisation to change, however I'm not sure how to proceed. I tried adding animate.time so that gganimate would have something to step through, but then I don't see how I'm supposed to make it change colors based on y.
I'd appreciate any pointers.
There is an easier way to do this based on this.
library(colorspace)
library(gganimate)
library(ggplot2)
# Narrow Visualization
narrow<-data.frame(x = rep(10,10),
state_cont=rep(1:2, each = 5))
ggplot(data = narrow, aes(x = x, fill = state_cont)) +
geom_bar() +
colorspace::scale_fill_continuous_sequential(palette = "Reds 3", begin = 0.4, end = .6) +
transition_states(states = state_cont) + theme_void() +
theme(legend.position = "none")
# Wide Visualization
wide <- data.frame(x=rep(10,10),
state_cont=rep(1:2, each = 5))
ggplot(data = wide, aes(x = x, fill = state_cont)) +
geom_bar() +
colorspace::scale_fill_continuous_sequential(palette = "Reds 3", begin = 0, end = 1) +
transition_states(states = state_cont) + theme_void() + theme(legend.position = "none")
The explanation is that for this particular question, you don't want to set every single value that it should oscillate through, it'd be much easier to simply set the boundaries in the scale_fill_continuous_sequential() via the begin and end arguments. Then, gganimate will automatically cycle through based on state_cont
I want to create an animated barplot with the gganimate package. The barplot should contain 4 bars, but only three of the bars should be shown at the same time. When a bar drops out and a new bar comes in, the animation should be smooth (as it is when two bars switch position within the plot).
Consider the following example:
# Set seed
set.seed(642)
# Create example data
df <- data.frame(ordering = c(rep(1:3, 2), 3:1, rep(1:3, 2)),
year = factor(sort(rep(2001:2005, 3))),
value = round(runif(15, 0, 100)),
group = c(letters[sample(1:4, 3)],
letters[sample(1:4, 3)],
letters[sample(1:4, 3)],
letters[sample(1:4, 3)],
letters[sample(1:4, 3)]))
# Load packages
library("gganimate")
library("ggplot2")
# Create animated ggplot
ggp <- ggplot(df, aes(x = ordering, y = value)) +
geom_bar(stat = "identity", aes(fill = group)) +
transition_states(year, transition_length = 2, state_length = 0)
ggp
If a bar is exchanged, the color of the bar just changes without any smooth animation (i.e. the new bar should fly in from the side and the replaced bar should fly out).
Question: How could I smoothen the replacement of bars?
I'm getting a little glitch at 2003 (b and c seem to swap upon transition), but hopefully this helps you get closer. I think enter_drift and exit_drift are what you're looking for.
library("gganimate")
library("ggplot2")
ggp <- ggplot(df, aes(x = ordering, y = value, group = group)) +
geom_bar(stat = "identity", aes(fill = group)) +
transition_states(year, transition_length = 2, state_length = 0) +
ease_aes('quadratic-in-out') + # Optional, I used to see settled states clearer
enter_drift(x_mod = -1) + exit_drift(x_mod = 1) +
labs(title = "Year {closest_state}")
animate(ggp, width = 600, height = 300, fps = 20)
I'm attempting to display a grid figure of summarized weekly data of several variables. The two components of this graph that are most pertinent are a distributional summary graph (so box plot or violin plot) of the values that a certain variables took over a given week and a cumulative count graph of an integer variable accumulating over weeks (so a step plot). I would like to plot these two graphs in on an aligned x-axis using grid. I'll be using ggplot2 to make the individual graphs, because I've got a crush on Hadley Wickham (j/k, ggplot is just really, really nice).
The problem is that geom_boxplot only takes factors for x-axis and the geom_step only takes continuous data for the x-axis. These don't necessarily align even if you force similar x-limits with coord_cartesian or scale_x_....
I've cobbled together a hack using geom_rect that will work for this specific application, but that will be a pain to adapt if, for example, I have some other factor that results in multiple boxes for a single week.
The obligatory reproducible:
library(ggplot2)
library(grid)
var1 <- data.frame(val = rnorm(300),
week = c(rep(25, 100),
rep(26, 100),
rep(27, 100))
)
var2 <- data.frame(cumul = cumsum(c(0, rpois(2, 15))),
week = c(25, 26, 27)
)
g1 <- ggplot(var1, aes(x = factor(week), y = val)) +
geom_boxplot()
g2 <- ggplot(var2, aes(x = week, y = cumul)) +
geom_step() + scale_x_continuous(breaks = 25:27)
grid.newpage()
grid.draw(rbind(ggplotGrob(g1),
ggplotGrob(g2),
size = "last"))
And the kludge:
library(dplyr)
chiggity_check <- var1 %>%
group_by(week) %>%
summarise(week.avg = mean(val),
week.25 = quantile(val)[2],
week.75 = quantile(val)[4],
week.05 = quantile(val)[1],
week.95 = quantile(val)[5])
riggity_rect <- ggplot(chiggity_check) +
geom_rect(aes(xmin = week - 0.25, xmax = week + 0.25,
ymin = week.25,
ymax = week.75)) +
geom_segment(aes(x = week - 0.25, xend = week + 0.25,
y = week.avg, yend=week.avg),
color = "white") +
geom_segment(aes(x = week, xend = week ,
y = week.25, yend=week.05)) +
geom_segment(aes(x = week, xend = week ,
y = week.75, yend=week.95)) +
coord_cartesian(c(24.5,27.5)) +
scale_x_continuous(breaks = 25:27)
grid.newpage()
grid.draw(rbind(ggplotGrob(riggity_rect),
ggplotGrob(g2 + coord_cartesian(c(24.5,27.5))),
size = "last"))
So the question(s) is/are: is there a way to force geom_boxplot to a continuous axis or geom_step to a factor axis? Or is there some other implementation, perhaps stat_summary that will be a bit more flexible so that I can align axes and also potentially easily add in things like grouping color variables?
One approach is to plot the two charts on an x-axis set up with factor(week), but in the g2 plot (the step plot) do so in geom_blank() so that the scale is set up. Then in geom_step(), plot on a numeric scale: as.numeric(factor(week))
library(ggplot2)
library(grid)
# Your data
var1 <- data.frame(val = rnorm(300),
week = c(rep(25, 100),
rep(26, 100),
rep(27, 100))
)
var2 <- data.frame(cumul = cumsum(c(0, rpois(2, 15))),
week = c(25, 26, 27)
)
# Your g1
g1 <- ggplot(var1, aes(x = factor(week), y = val)) +
geom_boxplot()
# Modified g2
g2 <- ggplot(var2) + geom_blank(aes(x = factor(week), y = cumul)) +
geom_step(aes(x = as.numeric(as.factor(week)), y = cumul))
grid.newpage()
grid.draw(gridExtra::rbind.gtable(ggplotGrob(g1),
ggplotGrob(g2),
size = "last"))
I have a dataframe in R like this:
dat = data.frame(Sample = c(1,1,2,2,3), Start = c(100,300,150,200,160), Stop = c(180,320,190,220,170))
And I would like to plot it such that the x-axis is the position and the y-axis is the number of samples at that position, with each sample in a different colour. So in the above example you would have some positions with height 1, some with height 2 and one area with height 3. The aim being to find regions where there are a large number of samples and what samples are in that region.
i.e. something like:
&
---
********- -- **
where * = Sample 1, - = Sample 2 and & = Sample 3
My first try:
dat$Sample = factor(dat$Sample)
ggplot(aes(x = Start, y = Sample, xend = Stop, yend = Sample, color = Sample), data = dat) +
geom_segment(size = 2) +
geom_segment(aes(x = Start, y = 0, xend = Stop, yend = 0), size = 2, alpha = 0.2, color = "black")
I combine two segment geometries here. One draws the colored vertical bars. These show where Samples have been measured. The second geometry draws the grey bar below where the density of the samples is shown. Any comments to improve on this quick hack?
This hack may be what you're looking for, however I've greatly increased the size of the dataframe in order to take advantage of stacking by geom_histogram.
library(ggplot2)
dat = data.frame(Sample = c(1,1,2,2,3),
Start = c(100,300,150,200,160),
Stop = c(180,320,190,220,170))
# Reformat the data for plotting with geom_histogram.
dat2 = matrix(ncol=2, nrow=0, dimnames=list(NULL, c("Sample", "Position")))
for (i in seq(nrow(dat))) {
Position = seq(dat[i, "Start"], dat[i, "Stop"])
Sample = rep(dat[i, "Sample"], length(Position))
dat2 = rbind(dat2, cbind(Sample, Position))
}
dat2 = as.data.frame(dat2)
dat2$Sample = factor(dat2$Sample)
plot_1 = ggplot(dat2, aes(x=Position, fill=Sample)) +
theme_bw() +
opts(panel.grid.minor=theme_blank(), panel.grid.major=theme_blank()) +
geom_hline(yintercept=seq(0, 20), colour="grey80", size=0.15) +
geom_hline(yintercept=3, linetype=2) +
geom_histogram(binwidth=1) +
ylim(c(0, 20)) +
ylab("Count") +
opts(axis.title.x=theme_text(size=11, vjust=0.5)) +
opts(axis.title.y=theme_text(size=11, angle=90)) +
opts(title="Segment Plot")
png("plot_1.png", height=200, width=650)
print(plot_1)
dev.off()
Note that the way I've reformatted the dataframe is a bit ugly, and will not scale well (e.g. if you have millions of segments and/or large start and stop positions).