insert image into ggplot + gganimate when x axis are dates - r

I want to animate some data using gganimate. Taking an example from their github page I changed it a bit to reflect my case. X-axis are dates and I want a logo in the same position for all frames.
Reproducible code:
library(magick)
library(gapminder)
library(ggplot2)
library(rsvg)
library(gganimate)
tiger <- image_read_svg('http://jeroen.github.io/images/tiger.svg', width = 400)
(p <- ggplot(gapminder, aes(year, lifeExp, size = pop, colour = country)) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
scale_x_log10() +
annotation_raster(tiger, ymin = 75, ymax = 100, xmin = 1965, xmax = 2005) )
# here the animate part (not needed just for ilustrative purposes)
p + labs(title = 'Year: {frame_time}', x = 'Year', y = 'life expectancy') +
transition_time(year) +
ease_aes('linear')
The issue is I can plot the logo in any chart when x-axis are not dates.
I suspect this issue is related to date type but no success so far.

Your issue appears to relate to the log scale you are calling for your x-axis. It's taking your year vector — which is not a date, rather a 4-digit integer — and applying a log transformation to it... which as #camille has pointed out means that when you are calling animation_raster the coordinates (xmin / xmax) are off the plot grid.
Here's a solution that incorporates dates by changing the year in your data frame to a date format. It also layers the image behind the geoms and renders in its original scale ie. 1x1.
library(magick)
library(gapminder)
library(ggplot2)
library(rsvg)
library(gganimate)
library(lubridate)
tiger <- image_read_svg('http://jeroen.github.io/images/tiger.svg', width =
400)
xmin <- ymd("1965/01/01")
xmax <- ymd("2005/01/01")
ymin <- 30
height <- round(as.numeric(xmax-xmin)/356, 0)
ymax <- ymin + height
gapminder %>%
mutate(year = ymd(year, truncated = 2L)) %>%
ggplot() +
aes(year, lifeExp, size = pop, colour = country) +
annotation_raster(tiger, ymin = ymin, ymax = ymax, xmin = xmin, xmax =
xmax) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
labs(title = 'Year: {frame_time}', x = 'Year', y = 'life expectancy') +
transition_time(year) +
ease_aes('linear') +
anim_save("animated_tiger_timeseries.gif")
Which produces this...
Is this what you are looking for?

Related

How to smooth out a time-series geom_area with fill in ggplot?

I have the following graph and code:
Graph
ggplot(long2, aes(x = DATA, y = value, fill = variable)) + geom_area(position="fill", alpha=0.75) +
scale_y_continuous(labels = scales::comma,n.breaks = 5,breaks = waiver()) +
scale_fill_viridis_d() +
scale_x_date(date_labels = "%b/%Y",date_breaks = "6 months") +
ggtitle("Proporcions de les visites, només 9T i 9C") +
xlab("Data") + ylab("% visites") +
theme_minimal() + theme(legend.position="bottom") + guides(fill=guide_legend(title=NULL)) +
annotate("rect", fill = "white", alpha = 0.3,
xmin = as.Date.character("2020-03-16"), xmax = as.Date.character("2020-06-22"),
ymin = 0, ymax = 1)
But it has some sawtooth, how am I supposed to smooth it out?
I believe your situation is roughly analogous to the following, wherein we have missing x-positions for one group, but not the other at the same position. This causes spikes if you set position = "fill".
library(ggplot2)
x <- seq_len(100)
df <- data.frame(
x = c(x[-c(25, 75)], x[-50]),
y = c(cos(x[-c(25, 75)]), sin(x[-50])) + 5,
group = rep(c("A", "B"), c(98, 99))
)
ggplot(df, aes(x, y, fill = group)) +
geom_area(position = "fill")
To smooth out these spikes, it has been suggested to linearly interpolate the data at the missing positions.
# Find all used x-positions
ux <- unique(df$x)
# Split data by group, interpolate data groupwise
df <- lapply(split(df, df$group), function(xy) {
approxed <- approx(xy$x, xy$y, xout = ux)
data.frame(x = ux, y = approxed$y, group = xy$group[1])
})
# Recombine data
df <- do.call(rbind, df)
# Now without spikes :)
ggplot(df, aes(x, y, fill = group)) +
geom_area(position = "fill")
Created on 2022-06-17 by the reprex package (v2.0.1)
P.S. I would also have expected a red spike at x=50, but for some reason this didn't happen.

Custom interaction Plotly in ggplot - R

I have a massive dataset that makes graph plotting tedious and complex.
Assume this simplified dataset:
library(data.table)
library(plotly)
library(ggplot2)
library(dplyr)
df <- data.table(continent = c(rep("America",3), rep("Europe",4)),
state = c("USA", "Brazil", "Chile", "Italy", "Swiss", "Spain", "Greece"),
X = rnorm(7, 5, 1),
Y = rnorm(7, -13, 1),
)
df$X_sd = sd(df$X)
df$Y_sd = sd(df$Y)
Consider having > 30 levels for "state", which makes it very difficult to show them with different colours or shapes.
I have decided to use plotly to show this dataset.
Here what I have done:
p <- df %>%
ggplot(aes(x=X,
y=Y,
fill = continent,
color = continent)) +
geom_errorbarh(aes(xmin = X - X_sd,
xmax = X + X_sd),
size = 0.5,
alpha = 0.3) +
geom_errorbar(aes(ymin = Y - Y_sd,
ymax = Y + Y_sd),
size = 0.5,
alpha = 0.3) +
geom_point(shape=21,
color="black",
size=3) +
theme_bw()
ggplotly(p)
However, the interactive window does not show information regarding the country, which is what I want to achieve.
In fact, every time I go over a point, I would like to have a window that shows: Continent, Country, X and Y (and in case I will have more factors or columns, I would like to be to include them too).
I have tried to add shape = country within the aesthetics, but 1) there are not enough shapes, 2) it fights against my decision of having shape = 21 for geom_point(), and 3) it adds a huge legend which I don't want.
How can I personalize the interaction window of plotly without adding extra and not-needed aesthetics?
Furthermore, I have tried to remove the legend by using:
guides(fill="none", color="none")+
or by
%>% hide_legend()
but either way, do not work. How can I remove the legend?
What you can do is add label in your aes to add factors like state. You can do that multiple times. You can use the following code:
p <- df %>%
ggplot(aes(label = state,
x=X,
y=Y,
fill = continent)) +
geom_errorbarh(aes(xmin = X - X_sd,
xmax = X + X_sd),
size = 0.5,
alpha = 0.3) +
geom_errorbar(aes(ymin = Y - Y_sd,
ymax = Y + Y_sd),
size = 0.5,
alpha = 0.3) +
geom_point(shape=21,
color="black",
size=3) +
theme_bw() +
theme(legend.position = "none")
ggplotly(p)
Output:

geom_rect missing when converting ggplot2 to ggplotly

I'm trying to put together a ggplotly graph with three elements (geom_point, geom_line, and geom_rect) and it looks fine in ggplot2. However, when I convert to ggplotly, the geom_rect disappears. I'm thinking it's something with the inherit.aes function?
The code to build the test data is below.
library(ggplot2)
library(plotly)
dates_seq = seq.Date(as.Date("2019-03-13"), as.Date("2019-04-21"), by = "1 day")
df = data.frame(ds = dates_seq,
y = rnorm(length(dates_seq), mean = 50, sd = 5),
yhat = rnorm(length(dates_seq), mean = 50, sd = 5)
)
df$yhat_lower = df$yhat - 5
df$yhat_upper = df$yhat + 5
gg <- ggplot(df, aes(x = ds, y = y)) +
labs(x = 'Date', y = 'Sales') +
geom_ribbon(aes(ymin = yhat_lower, ymax = yhat_upper), fill = 'blue',
alpha = 0.2,
na.rm = TRUE)
start_date = as.Date("2019-04-19")
gg <- gg +
geom_point(na.rm=TRUE) +
geom_vline(xintercept = as.numeric(as.Date(start_date - lubridate::days(1))), linetype = 2, color = "black") +
geom_line(aes(y = yhat), color = 'blue',
na.rm = TRUE) +
theme_classic()
promo_df = data.frame(xmin = c("2019-03-15", "2019-04-01"), xmax = c("2019-03-18", "2019-04-08"),
ymin = -Inf, ymax = Inf, Promo = "Yes")
promo_df$id = 1:nrow(promo_df)
gg = gg +
geom_rect(data=promo_df, inherit.aes=FALSE,
aes(xmin=as.Date(xmin),
xmax=as.Date(xmax),
ymin=ymin,ymax=ymax,
group=id, fill = factor(Promo)), alpha=0.2) +
scale_fill_discrete(name = "On Promo?")
The ggplot image shows the desired output with the geom_rect.
gg
And now the ggplotly version:
ggplotly(gg)
Is there any way to get the ggplotly image to look like the basic ggplot2 chart?
Clara is right with respect to ggplotly's inability to support the ymin/max parameters. The best work around is to just manually set the parameters equal to the scale of your previous (main) layer. So in this case, it would be equal to 0/65.

Using geom_rect for time series shading in R

I am trying to shade a certain section of a time series plot (a bit like recession shading - similarly to the graph at the bottom of this article on recession shading in excel). I have put a little, possibly clumsy, sample together to illustrate.
I first create a time series, plot it with ggplot2 and then want to use geom_rect to provide the shading. But I must get something wrong in the arguments.
a<-rnorm(300)
a_ts<-ts(a, start=c(1910, 1), frequency=12)
a_time<-time(a_ts)
a_series<-ts.union(big=a_ts, month=a_time)
a_series_df<-as.data.frame(a_series)
ggplot(a_series)+
geom_line(mapping=aes_string(x="month", y="big"))+
geom_rect(
fill="red",alpha=0.5,
mapping=aes_string(x="month", y="big"),
xmin=as.numeric(as.Date(c("1924-01-01"))),
xmax=as.numeric(as.Date(c("1928-12-31"))),
ymin=0,
ymax=2
)
Note that I have also tried which also did not work.
geom_rect(
fill="red",alpha=0.5,
mapping=aes_string(x="month", y="big"),
aes(
xmin=as.numeric(as.Date(c("1924-01-01"))),
xmax=as.numeric(as.Date(c("1928-12-31"))),
ymin=0,
ymax=2)
)
Its a bit easier using annotate and also note that the bounds for the rectange can be specified as shown:
ggplot(a_series_df, aes(month, big)) +
geom_line() +
annotate("rect", fill = "red", alpha = 0.5,
xmin = 1924, xmax = 1928 + 11/12,
ymin = -Inf, ymax = Inf) +
xlab("time")
This would also work:
library(zoo)
z <- read.zoo(a_series_df, index = 2)
autoplot(z) +
annotate("rect", fill = "red", alpha = 0.5,
xmin = 1924, xmax = 1928 + 11/12,
ymin = -Inf, ymax = Inf) +
xlab("time") +
ylab("big")
Either one gives this:
Code works fine, conversion to decimal date is needed for xmin and xmax, see below, requires lubridate package.
library("lubridate")
library("ggplot2")
ggplot(a_series_df)+
geom_line(mapping = aes_string(x = "month", y = "big")) +
geom_rect(
fill = "red", alpha = 0.5,
mapping = aes_string(x = "month", y = "big"),
xmin = decimal_date(as.Date(c("1924-01-01"))),
xmax = decimal_date(as.Date(c("1928-12-31"))),
ymin = 0,
ymax = 2
)
Cleaner version, shading plotted first so the line colour doesn't change.
ggplot() +
geom_rect(data = data.frame(xmin = decimal_date(as.Date(c("1924-01-01"))),
xmax = decimal_date(as.Date(c("1928-12-31"))),
ymin = -Inf,
ymax = Inf),
aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),
fill = "grey", alpha = 0.5) +
geom_line(data = a_series_df,aes(month, big), colour = "blue") +
theme_classic()
To use geom_rect you need to define your rectangle coordinate through a data.frame:
shade = data.frame(x1=c(1918,1930), x2=c(1921,1932), y1=c(-3,-3), y2=c(4,4))
# x1 x2 y1 y2
#1 1918 1921 -3 4
#2 1930 1932 -3 4
Then you give ggplot your data and the shade data.frame:
ggplot() +
geom_line(aes(x=month, y=big), color='red',data=a_series_df)+
geom_rect(data=shade,
mapping=aes(xmin=x1, xmax=x2, ymin=y1, ymax=y2), color='grey', alpha=0.2)
library(xts)
library(zoo)
library(ggts)
Creating an xts object
data<-as.xts(x=runif(228,20,40),order.by = seq(as.Date("2000/01/01"), by = "month", length.out = 228))
Creating data frame of dates for which you want to crate shades
date<-data.frame(as.Date("2008-01-01"),as.Date("2009-01-01"))
Now create plot with shaded area
plot_data<-ggts(data)+geom_cycle(date)

Difference plot

I don't know the name of this type of plot (comments around this are welcomed). Essentially it is a barplot with glyphs that are filled to indicate a loss/gain. The glyph is arrow like encoding information about direction, magnitude, and allowing the bar geom under to be seen.
This looks interesting but can't think of how to do it in ggplot2 (grid frame work). How could we recreate this plot in ggplot2/grid framework (base solutions welcomed as well for completeness of question). Specifically the glyphs, not the text as this is pretty straight forward in ggplot2 already.
Here is some code to create data and traditional overlaid & coordinate flipped dodged bar plots and line graphs to show typical ways of visualizing this type of data.
set.seed(10)
x <- sample(30:60, 12)
y <- jitter(x, 60)
library(ggplot2)
dat <- data.frame(
year = rep(2012:2013, each=12),
month = rep(month.abb, 2),
profit = c(x, y)
)
ggplot() +
geom_bar(data=subset(dat, year==2012), aes(x=month, weight=profit)) +
geom_bar(data=subset(dat, year==2013), aes(x=month, weight=profit), width=.5, fill="red")
ggplot(dat, aes(x=month, fill=factor(year))) +
geom_bar(position="dodge", aes(weight=profit)) +
coord_flip
ggplot(dat, aes(x=month, y=profit, group = year, color=factor(year))) +
geom_line(size=1)
Here is an example, perhaps there are other ways though,
dat <- data.frame(
year = rep(2012:2013, each=12),
month = factor(rep(1:12, 2), labels=month.abb),
profit = c(x, y)
)
dat2 <- reshape2::dcast(dat, month~ year, value.var = "profit")
names(dat2)[2:3] <- paste0("Y", names(dat2)[2:3])
ggplot(dat2) +
geom_bar(aes(x=month, y = Y2012), stat = "identity", fill = "grey80", width = 0.6) +
geom_segment(aes(x=as.numeric(month)-0.4, xend = as.numeric(month)+0.4, y = Y2013, yend = Y2013)) +
geom_segment(aes(x = month, xend = month, y = Y2013, yend = Y2012, colour = Y2013 < Y2012),
arrow = arrow(60, type = "closed", length = unit(0.1, "inches")), size = 1.5) +
theme_bw()

Resources