How to have x-axis span move with gganimate animation? - r

Using R, I am trying to make a line graph which is revealed left to right based on x-axis using gganimate. I have managed to do this but what I also wanted to do was make it so that the scale_x_continuous(limits = c(i-5,i+5)), i.e. there is a window around the point that is being revealed and the window will move along while the next point is being revealed.
I have tried many ways to get this including implementing some sort of loop in scale_x_continuous with and without aes(). Nothing seems to work. I am quite new with ggplot2 and especially with gganimate but I couldn't find any help online. I have a feeling the answer is probably quite simple and I just missed it.
Sort of like this but with gganimate:
The following is some reproducible code to show you roughly what I've done so far.
library(ggplot2)
library(gganimate)
library(gifski)
library(png)
Step <- c(1:50,1:50)
Name <- c(rep("A",50), rep("B",50))
Value <- c(runif(50,0,10), runif(50,10,20))
Final <- data.frame(Step, Name, Value)
a <- ggplot(Final, aes(x = Step, y = Value, group = Name, color = factor(Name))) +
geom_line(size=1) +
geom_point(size = 2) +
transition_reveal(Step) +
coord_cartesian(clip = 'off') +
theme_minimal() +
theme(plot.margin = margin(5.5, 40, 5.5, 5.5)) +
theme(legend.position = "none")
options(gganimate.dev_args = list(width = 7, height = 6, units = 'in', res=100))
animate(a, nframes = 100)

Don't use a transition, use a view. E.g.:
ggplot(Final, aes(x = Step, y = Value, color = factor(Name))) +
geom_line(size = 1) +
geom_point() +
view_zoom_manual(
0, 1, pause_first = FALSE, ease = 'linear', wrap = FALSE,
xmin = 1:40, xmax = 11:50, ymin = min(Final$Value), ymax = max(Final$Value)
) +
scale_x_continuous(breaks = seq(0, 50, 2))

Related

How to use your own image for geom_point in gganimate?

I am trying to use my own image for geom_point, something I can just read in. I am aware geom_point allows you to choose many shapes (well over 300) by simply writing shape = 243 but I want my own image such as a logo.
When I have not specified color = factor(Name) then it works as expected. When I do specify the colour of the line then the image becomes a solid single colour. I want this line to be coloured so is there any way around this? Thanks!
library(gganimate)
library(gifski)
library(png)
library(ggimage)
Step <- 1:50
Name <- rep("A",50)
Image <- rep(c("https://jeroenooms.github.io/images/frink.png"),50)
Value <- runif(50,0,10)
Final <- data.frame(Step, Name, Value, Image)
a <- ggplot(Final, aes(x = Step, y = Value, group = Name, color = factor(Name))) +
geom_line(size=1) +
geom_image(aes(image=Image)) +
transition_reveal(Step) +
coord_cartesian(clip = 'off') +
theme_minimal() +
theme(plot.margin = margin(5.5, 40, 5.5, 5.5)) +
theme(legend.position = "none")
options(gganimate.dev_args = list(width = 7, height = 6, units = 'in', res=100))
animate(a, nframes = 100)
Is this what your are looking for ?
I Just changed the color = factor(Name) position to geom_line statement.
If you use color = factor(Name) with ggplot in first row, it will affect to whole plot. So you should take care when using this statement.
a <- ggplot(Final, aes(x = Step, y = Value, group = Name)) +
geom_line(size=1, aes(color = factor(Name))) +
geom_image(aes(image=Image)) +
transition_reveal(Step) +
coord_cartesian(clip = 'off') +
theme_minimal() +
theme(plot.margin = margin(5.5, 40, 5.5, 5.5)) +
theme(legend.position = "none")
For convenience, i captured the picture .

Issue with shadow_mark() in gganimate

I've created a gif using gganimate that shows NBA players regular season points per game and compares it to their points per game in the playoffs. I have everything working except i have a graphical issue with shadow_mark(). The gif starts at the playoffs, transitions to the regular season mark, and then transitions back to playoffs mark.
I want shadow_mark() to keep the playoffs mark on the graph at all times with 50% opacity like i have it right now. I don't want the regular season mark to stay on the graph, but I don't know how to get rid of it. I've tried various combinations of past = TRUE and future = FALSE etc in shadow_mark() but it doesn't seem to have solved it. I've also tried exclude_layer = 1 but then that deletes both of the shadows instead of just the 1.
Here is my gif as of right now. Below is the code used to create it.
j <- ggplot(nba2, aes(x = PPG, y = Player)) +
geom_point(shape = 21, stroke = 1, aes(fill = Tm, size = 2)) +
theme(legend.title = element_blank(), legend.position = 'none') +
xlab("Points Per Game") +
labs(caption = 'Data via basketball-reference.com')
plot(j)
anim <- j +
transition_states(Playoff_or_reg,
transition_length = 2,
state_length = 2,
wrap = TRUE) +
shadow_mark(past = TRUE, future = FALSE, alpha = 0.5) +
ggtitle("{closest_state}")
anim
Any help on how to fix this issue would be appreciated!
What you wanna do is create a new variable that equals each player's PPG in the regular season. That variable is going to be your static, transparent point. The original variable is the one that is going to transition.
Here's what the code for that viz might look like:
df %>%
ggplot(aes(x = Player, y = PPG, color = Tm, fill = Tm)) +
geom_point(size = 4.5, shape = 21, alpha = 1, stroke = 1) +
geom_point(size = 4.5, shape = 21, alpha = .2, stroke = 1, aes(fill = Tm, color = Tm, x = Player, y = newPPG)) +
coord_flip() +
theme(legend.position = 'none') +
transition_states(
Playoff_or_reg,
transition_length = 1,
state_length = 2)
Hope that helps

How to include "think-cell"-like percentage changes in a waterfall-chart generated in ggplot2

I try to establish R as data visualisation tool in my company. A typical graph type used in my department are waterfall charts (https://en.wikipedia.org/wiki/Waterfall_chart).
In R, there are some packages and hints for ggplot to generate a waterfall chart (https://learnr.wordpress.com/2010/05/10/ggplot2-waterfall-charts/), which I used already.
Unfortunately, a common feature for the used waterfall charts are annotations with arrows to indicate the percentage changes within the steps.
See an example below:
Or here in this video (https://www.youtube.com/watch?v=WMHf7uFR6Rk)
The software used to produce such kind of plots is think cell (https://www.think-cell.com/), which is an add-on to Excel and Powerpoint.
The problem I have is that I don't know how to start to tackle the topic. My first thoughts are going in this direction:
Use geom_segment for generating the arrows and boxes
Use ggplot's annotate funktion to place the text at the arrows or in the boxes
Calculate the positions automatically based on the data provided to the waterfall chart.
May I ask you, if you have additional thoughts/ideas to implement such graphs in ggplot?
Best Regards Markus
Here's an example of the approach I would take.
Step 1. Pick which elements should be added, and add them one at a time.
Let's say we're starting with this simple chart:
df <- data.frame(x = c(2007, 2008, 2009),
y = c(100, 120, 140))
ggplot(df, aes(x, y, label = y)) +
geom_col() +
geom_text(vjust = -0.5)
First of all, we need some extra vertical space:
ggplot(df, aes(x, y, label = y)) +
geom_col() +
geom_text(vjust = -0.5) +
scale_y_continuous(expand = expand_scale(add = c(10, 50))) # Add 50 y padding
Now, I incrementally add layers until it looks like I want:
# Semi-manual proof of concept
ggplot(df, aes(x, y, label = y)) +
geom_col() +
geom_text(vjust = -0.5) +
scale_y_continuous(expand = expand_scale(add = c(10, 50))) + # Add 50 y padding
# Line with arrow
geom_segment(aes(x = df$x[3], y = df$y[3] + 50,
xend = df$x[3], yend = df$y[3] + 50),
arrow = arrow(length = unit(0.02, "npc"), type = "closed")) +
# Background box
geom_tile(aes(x = mean(c(df$x[3], df$x[3])),
y = mean(c(df$y[3], df$y[3])) + 50, width = 1, height = 40),
fill = "white", color = "black", size = 0.5) +
# Text
geom_text(aes(x = mean(c(df$x[3], df$x[3])),
y = mean(c(df$y[3], df$y[3])) + 50,
label = paste0("CAGR\n",
df$x[3], "-", df$x[3], "\n",
scales::percent((df$y[3] / df$y[3]) ^ (1/(df$x[3]-df$x[3])) - 1))))
Step 2. Make it into a function
Now I move the CAGR-related layers into a function, replacing most of the constants with function parameters.
add_CAGR <- function(df, first_val_pos, second_val_pos,
y_offset, box_width = 1, box_height) {
list(
# Line with arrow
geom_segment(aes(x = df$x[first_val_pos],
xend = df$x[second_val_pos],
y = df$y[first_val_pos] + y_offset,
yend = df$y[second_val_pos] + y_offset),
arrow = arrow(length = unit(0.02, "npc"), type = "closed")),
# Background box
geom_tile(aes(x = mean(c(df$x[first_val_pos], df$x[second_val_pos])),
y = mean(c(df$y[first_val_pos], df$y[second_val_pos])) + y_offset,
width = box_width, height = box_height),
fill = "white", color = "black", size = 0.5),
# Text
geom_text(aes(x = mean(c(df$x[first_val_pos], df$x[second_val_pos])),
y = mean(c(df$y[first_val_pos], df$y[second_val_pos])) + y_offset,
label = paste0("CAGR\n",
df$x[first_val_pos], "-", df$x[second_val_pos], "\n",
scales::percent((df$y[second_val_pos] / df$y[1]) ^
(1/(df$x[second_val_pos]-df$x[first_val_pos])) - 1))),
lineheight = 0.8)
)
}
Step 3: Use in plot
ggplot(df, aes(x, y, label = y)) +
geom_col() +
geom_text(vjust = -0.5) +
scale_y_continuous(expand = expand_scale(add = c(0, 50))) + # Add 50 y padding
add_CAGR(df, first_val_pos = 1, second_val_pos = 3,
y_offset = 50,
box_width = 0.7, box_height = 40)
Or the same thing just between the first two bars:
ggplot(df, aes(x, y, label = y)) +
geom_col() +
geom_text(vjust = -0.5) +
scale_y_continuous(expand = expand_scale(add = c(0, 50))) + # Add 50 y padding
add_CAGR(df, first_val_pos = 1, second_val_pos = 2,
y_offset = 50,
box_width = 0.7, box_height = 40)

Determine the width of a ggplot2 plot area

I'm trying to use gridExtra to combine two ggplot2 bar plots (with coordinates flipped so that the labels take up horizontal space). The problem is that some of the labels are short and some are long. I want the width of the left and right columns to be the same, irrespective of the width of the labels. Here's an example:
library(ggplot2)
library(gridExtra)
datashort <- data.frame(ecks = c("Short1", "Short2", "Short3"), why = c(30, 20, 40))
datalong <- data.frame(ecks = c(paste0("Really, Really, Really, Really Long", c(1:3))),
why = c(20, 30, 40))
plotshort <- ggplot(data = datashort, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") +
scale_y_continuous(breaks = c(10, 20, 30, 40, 50), limits = c(0, 50)) +
coord_flip()
plotlong <- ggplot(data = datalong, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") +
scale_y_continuous(breaks = c(10, 20, 30, 40, 50), limits = c(0, 50)) +
coord_flip()
grid.arrange(plotshort, plotlong, ncol = 2)
If you do that, you get a really wide left chart and a really squished right chart. I know that you can add widths to the grid arrange, but I'd like to know exactly what they should be. Here it looks like widths=c(.4, .6) works pretty well, but it's not exact. Also, you have to use trial and error to get there, which isn't ideal. Is there any way to figure out what the actual plot area is so that you can calculate the right widths?
One very simple solution would be to use cowplot:
cowplot::plot_grid(plotshort, plotlong, ncol = 2,align = "v")
The argument align allows you to set the plot areas equal.
Or another option would be to add some breaks to the titles:
library(stringr)
plotlong <- datalong %>%
mutate(ecks = str_replace_all(ecks, "[[:punct:]]\\s|\\s", "\\\n")) %>%
ggplot(aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") +
scale_y_continuous(breaks = c(10, 20, 30, 40, 50), limits = c(0, 50)) +
coord_flip()
cowplot::plot_grid(plotshort, plotlong, ncol = 2,align = "v")
If you add the breaks, then you could use grid.arrange
grid.arrange(plotshort, plotlong, ncol = 2)
You can wrap the labels using stringr and modify your ggplot2 script to define your labels in st_wrap and plot as scale_x_discrete:
library(stringr)
plotshort <- ggplot(data = datashort, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") + coord_flip() +
scale_x_discrete(labels = str_wrap(c("Short1", "Short2", "Short3"), width = 10))
plotlong <- ggplot(data = datalong, aes(x = ecks, y = why, width = .5)) +
geom_bar(stat = "identity") + coord_flip() +
scale_x_discrete(labels = str_wrap(c("Really, Really, Really, Really Long1", "Really, Really, Really, Really Long2", "Really, Really, Really, Really Long3"), width = 10))
grid.arrange(plotshort, plotlong, ncol = 2)

R: gradient fill for geom_rect in ggplot2

I want to create in R a graphic similar to the one below to show where a certain person or company ranks relative to its peers. The score will always be between 1 and 100.
Although I am amenable to any ggplot solution it seemed to me that the best way would be to use geom_rect and then to adapt and add the arrowhead described in baptiste's answer to this question. However, I came unstuck on something even simpler - getting the geom_rect to fill properly with a gradient like that shown in the guide to the right of the plot below. This should be easy. What am I doing wrong?
library(ggplot2)
library(scales)
mydf <- data.frame(id = rep(1, 100), sales = 1:100)
ggplot(mydf) +
geom_rect(aes(xmin = 1, xmax = 1.5, ymin = 0, ymax = 100, fill = sales)) +
scale_x_discrete(breaks = 0:2, labels = 0:2) +
scale_fill_gradient2(low = 'blue', mid = 'white', high = 'red', midpoint = 50) +
theme_minimal()
I think that geom_tile() will be better - use sales for y and fill. With geom_tile() you will get separate tile for each sales value and will be able to see the gradient.
ggplot(mydf) +
geom_tile(aes(x = 1, y=sales, fill = sales)) +
scale_x_continuous(limits=c(0,2),breaks=1)+
scale_fill_gradient2(low = 'blue', mid = 'white', high = 'red', midpoint = 50) +
theme_minimal()

Resources