suppressPackageStartupMessages(library(animint2))
suppressPackageStartupMessages(library(data.table))
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(plyr))
suppressPackageStartupMessages(library(maps))
Animint 2
We follow a simplified version of the tutorial given here: https://rcdata.nau.edu/genomic-ml/animint2-manual/Ch08-WorldBank-facets.html
Loading useful libraries
EXAMPLE 1: World Bank data
Data loading and formatting
data(WorldBank)
$Region <- sub(" (all income levels)", "", WorldBank$region, fixed=TRUE)
WorldBank
# Remove NAs
<- data.table(WorldBank)[!(is.na(life.expectancy) | is.na(fertility.rate))]
df
# Display only the region south asia for ease of vizualization
<- df[df$region == "South Asia"]
df <- unique(df[, .(year)]) years
The helper functions FACETS etc will be used to generate adequate facet grids.
<- function(df, top, side){
add_facets data.frame(df,
top=factor(top, c("Fertility rate", "Years")),
side=factor(side, c("Years", "Life expectancy")))
}<- function(df) add_facets(df, "Years", "Life expectancy")
facet_right <- function(df) add_facets(df, "Fertility rate", "Life expectancy") facet_left
First animint plot: show life expectancy for each country over time
<- ggplot()+
life_expect_plot geom_line(aes(year, life.expectancy,
group = country, colour = country),
clickSelects = "country", data = facet_right(df),
size = 4, alpha = 3/5)
life_expect_plot
With animint, the plot is shown in the “Viewer” panel and one can select which countries should be left in the plot.
animint(life_expect_plot)
Adding a bar to highlights a year
The important parameter here is ‘clickSelects’.
# facet_right(years) contains the unique years of the df and 2 columns for facet used later in the tutorial
<- life_expect_plot +
plot_with_bar geom_tallrect(aes(xmin = year-1/2, xmax = year+1/2),
clickSelects="year", data = facet_right(years),
alpha=1/2, show.legend = T)
animint(plot_with_bar)
Modify the vertical cursor text display to show the average life expectancy
By default, the text displayed at the current state of the bar is the current value of the bar. Use the ‘toolkit’ argument in aes() to change what is displayed.
<- df %>% group_by(year) %>% dplyr::summarise(mean_life_expectancy = mean(life.expectancy))
years_df
<- life_expect_plot +
plot_with_bar geom_tallrect(aes(xmin=year-1/2, xmax=year+1/2,
tooltip = paste0("Mean life expectancy: ",
round(mean_life_expectancy, 1))),
clickSelects="year",
data = facet_right(years_df), alpha=1/2)
animint(plot_with_bar)
Add points to get exact value of life expectancy for each country
Like the bar above, we use the ‘tooltip’ argument
<- plot_with_bar +
full_plot geom_point(aes(year, life.expectancy, color = country,
tooltip = paste0(country, " - Life expectancy :",
round(life.expectancy, 1))),
showSelected = "country",
clickSelects = "country",
size = 2,
data = facet_right(df))
animint(full_plot)
Let us add a second facet
In this part, this is where the functions facet_right and facet_left are useful. These functions creates columns to order the axis. Like ggplot, the variable used for facet must be a factor.
# First add the facet grid
<- full_plot + theme_bw() +
plot_right theme(panel.margin=grid::unit(0, "lines")) +
facet_grid(side ~ top, scales="free") + xlab("") + ylab("") +
theme_animint(width=600)
When adding the second plot, we use the ‘showSelected’ argument, the variable must correspond to the one used in the first plot with the argument ‘clickSelected’
# Add the 2nd plot
<- plot_right +
plot_both geom_point(aes(fertility.rate, life.expectancy,
colour=country, size=population, key=country),
clickSelects="country",
showSelected = "year",
data = facet_left(df)) +
scale_size_animint(pixel.range=c(2, 20), breaks=10^(9:5))
animint(plot_both)
EXAMPLE 2: US tornadoes visualization
Example taken from https://suhaani-agarwal.github.io/tornado-visualization/ ## Loading and formatting data
data(UStornadoes)
<- UStornadoes[order(UStornadoes$year),] # sort the df by year so that they appear in the right order
UStornadoes $region <- tolower(state.name[match(UStornadoes$state, state.abb)]) UStornadoes
Generating a US map
We first generate an animint map of the US on which each state can be selected
<- map_data("state") # Animint function to create a map, "state" is for US states
USpolygons
= ggplot() +
map theme_animint(width=750, height=500) +
geom_polygon(aes(x=long, y=lat, group=group, tooltip = region),
clickSelects="region",
data=USpolygons,
fill="#000000",
colour="white",
size=0.5,
alpha=1)
animint(map)
Adding the tornadoes segments
Let us add to the maps segments that give the tornadoes trajectory (segment from start point to end point). We also use the parameter ‘showSelected’ so the the map displays the tornado only for a specific year.
<- "#55B1F7"
seg.color <- map +
tornadoes_map geom_segment(aes(x=startLong, y=startLat,
xend=endLong, yend=endLat),
colour=seg.color, size = 1.2,
showSelected="year",
data=UStornadoes)
animint(tornadoes_map)
Adding the tornadoes points
Superposing tornadoes end point to tornadoes segments.
<- "#9999F9"
pt.color <- tornadoes_map +
tornadoes_map geom_point(aes(endLong, endLat),
colour=pt.color,
showSelected="year",
data=UStornadoes,
size=1)
animint(tornadoes_map)
Creating histograms with the number of tornadoes per (state, year)
<- ddply(UStornadoes, .(region, year), summarize, count=length(region)) UStornadoCounts
The tornadoes histogram per (state, year). We use the parameter ‘showSelected’ = region so that it can be linked to the map later. Similarly for the argument ‘clickSelects’ = year, it corresponds to ‘showSelected’ in the map.
<- ggplot() +
tornadoes_hist xlab("year") +
ylab("Number of tornadoes") +
geom_bar(aes(year, count),
clickSelects="year",
showSelected="region",
data=UStornadoCounts,
stat="identity",
color = "black",
fill = "#22212100",
alpha = 1,
position="identity")
animint(tornadoes_hist)
Adding value on top of the bar when it is clicked.
<- tornadoes_hist +
tornadoes_hist geom_text(aes(year, count + 5, label=count),
showSelected=c("region", "year"),
data=UStornadoCounts, size=20)
animint(tornadoes_hist)
Combining the map and histogram
animint(tornadoes_map, tornadoes_hist)
EXAMPLE 3 : TEMPERATURE DATA
library(animint2)
library(terra)
terra 1.8.70
Attaching package: 'terra'
The following object is masked from 'package:data.table':
shift
library(dplyr)
library(tidyr)
Attaching package: 'tidyr'
The following object is masked from 'package:terra':
extract
Get the data to plot
First, let us get the data and usefull functions
=rast("data/EOBS_FR31.nc")
france_temperature
library(lubridate)
Attaching package: 'lubridate'
The following objects are masked from 'package:terra':
intersect, union
The following objects are masked from 'package:data.table':
hour, isoweek, mday, minute, month, quarter, second, wday, week,
yday, year
The following objects are masked from 'package:base':
date, intersect, setdiff, union
= seq.Date(ymd("1985-01-01"),ymd("2015-12-31"),by='day')
dates = dates[
dates -which((month(dates)==2 & day(dates)==29))]
= france_temperature[[1:length(dates)]]
france_temperature
= dates[(month(dates)%in%6:8) & ( year(dates) %in% c(2003,2015,1990,1999)) ]
datessummers =subset(france_temperature, month(time(france_temperature)) %in% c(6,7,8))
summers =subset(summers, year(time(summers)) %in% c(2003,2015,1990,1999))
summers
names(summers)=datessummers
## function to get quantile map ----
=function(raster,li_q){
get_qmap=length(li_q)
n= c()
s for(i in 1:n){
=li_q[i]
q<-c(s,app(raster, fun=function(i){quantile(i,q,na.rm=T)}))
s
}=rast(s)
snames(s) = li_q
return(s)
}## get quantile maps from observation 1985-2015-------
= (get_qmap(summers,c(0.9,0.95,0.98,0.99)))
qmaps plot(qmaps)
The different maps are quantiles of temperature for each pixels.
## function to get percentage of area over quantiles ----
=function(raster,qmaps){
get_area_over_qmaps#returns a time dataframe of % of area over the maps in qmaps
# n+1 columns with n the number of layers in qmaps, named after quantiles.
= global((not.na(raster)),sum)
ngrid
=data.frame(t=time(raster))
depsummer for(i in 1:nlyr(qmaps)){
= qmaps[[i]]
qmap = raster>qmap
summersq +1]= global(summersq,sum,na.rm=T)/ngrid
depsummer[,i
}names(depsummer)=c('t',names(qmaps))
return(depsummer)
}
=get_area_over_qmaps(summers,qmaps)
data=data
depsummer=gather(depsummer,quantile,percentage,-t)
longdep$year =year(longdep$t)
longdep$day_of_year=yday(longdep$t)
longdephead(depsummer)
t 0.9 0.95 0.98 0.99
1 1990-06-01 0 0 0 0
2 1990-06-02 0 0 0 0
3 1990-06-03 0 0 0 0
4 1990-06-04 0 0 0 0
5 1990-06-05 0 0 0 0
6 1990-06-06 0 0 0 0
head(longdep)
t quantile percentage year day_of_year
1 1990-06-01 0.9 0 1990 152
2 1990-06-02 0.9 0 1990 153
3 1990-06-03 0.9 0 1990 154
4 1990-06-04 0.9 0 1990 155
5 1990-06-05 0.9 0 1990 156
6 1990-06-06 0.9 0 1990 157
longdep contains for each date t, each quantile, each year and day_of_year the percentage of location over their quantile.
Objective number 1 : getting percentage = f(year) with color=quantile , selecting only chosen year and highlighting only chosen quantile.
library(animint2)
<- animint2::ggplot() +
gobs ::geom_line(
animint2::aes(x=day_of_year, y= percentage,group = interaction(quantile,year),color=quantile), data = longdep
animint2+
) ::labs(title = "some summers - proportion of FR map exceeding given quantile - observations")
animint2
gobs
This is not very pretty ! There is too much info at once. We want to only plot a given year, selected using showSelected=c( “year”). And also, why not choosing the quantiles to plot ?
<- animint2::ggplot()+
plot_QER
::geom_line(animint2::aes(
animint2group=quantile,color=quantile),
day_of_year,percentage,clickSelects=c("year","quantile"), #use selection to choose the year.
showSelected=c( "year"), #only show the selected year
data=(longdep), size=4, alpha=3/5)+
theme_bw()+
xlab("Day of year")+
ylab("Percentage")
animint(plot_QER)
We can add a rectangle, higlighting a selected day (will be useful later)
<- animint2::ggplot()+
plot_QER
# highlight the chosen year again
geom_tallrect(animint2::aes(
xmin=day_of_year-1/2, xmax=day_of_year+1/2),
clickSelects="day_of_year",
data=(longdep), alpha=1/2)+
::geom_line(animint2::aes(
animint2group=quantile,color=quantile),
day_of_year,percentage,clickSelects=c("year","quantile"), #use selection to choose the year.
showSelected=c( "year"), #only show the selected year
data=(longdep), size=4, alpha=3/5)+
theme_bw()+
xlab("Day of year")+
ylab("Percentage")
animint(plot_QER)
Objective 2 : plot the temperature for chosen day t of a given year
# --- palette and limits ---
<- c("black","#67001f",'#d73027','#f46d43','#fdae61','#fee090',
pal '#ffffbf','#e0f3f8','#abd9e9','#74add1','#4575b4','#313695')
<- c(0,36)
mm # be careful, this is heavy data
<- terra::aggregate(summers, fact = 1, fun = mean) summers_small
Warning: [aggregate] all values in argument 'fact' are 1, nothing to do
# --- 1. Raster data prepared for ggplot/animint2 ---
# Extract one value per cell per date -> convert to data.frame
# summers is a SpatRaster with layers named as dates
<- as.data.frame(summers_small, xy = TRUE, na.rm = FALSE)
map_df <- tidyr::pivot_longer(map_df, -c(x,y),
map_df names_to = "t",
values_to = "temperature")
$t <- as.Date(map_df$t)
map_df$year <- lubridate::year(map_df$t)
map_df$day_of_year <- lubridate::yday(map_df$t)
map_df
# --- 2. Map plot (left) ---
<- animint2::ggplot() +
map_plot ::geom_tile(animint2::aes(x, y, fill = temperature),
animint2data = map_df,
showSelected = c("year","day_of_year"),colour=NA) +
::ggtitle("Date t (selected)")+
animint2::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2::labs(fill = "temperature")+theme_bw()
animint2animint(map_plot)
Objective 3 : plot together the map of temperature at a day, highlighting that day on my first plot
Can take a while, because the dataframe for plotting is large. We have used shared variables names, so selecting from (year,day_of_year) has the same meaning in both graphs.
::animint(plot_QER,map_plot, first = list(year = c(2003), day_of_year = c(200))) animint2
Objective 4 : Show maps at different time steps : t-1, t and t+1, to show temporal evolution
# Map t-1
<- animint2::ggplot() +
map_t_minus1 ::geom_tile(
animint2::aes(x, y, fill = temperature),
animint2data = map_df %>% mutate(day_of_year = day_of_year + 1),
showSelected = c("year", "day_of_year"),
alpha = 0.4,
colour = NA
+
) ggtitle("t-1") +
::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2theme_bw()
# Map t
<- animint2::ggplot() +
map_t ::geom_tile(
animint2::aes(x, y, fill = temperature),
animint2data = map_df,
showSelected = c("year", "day_of_year"),
alpha = 1,
colour = NA
+
) ggtitle("t") +
::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2theme_bw()
# Map t+1
<- animint2::ggplot() +
map_t_plus1 ::geom_tile(
animint2::aes(x, y, fill = temperature),
animint2data = map_df %>% mutate(day_of_year = day_of_year - 1),
showSelected = c("year", "day_of_year"),
alpha = 0.4,
colour = NA
+
) ggtitle("t+1") +
::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2theme_bw()
animint(map_t_minus1,map_t,map_t_plus1)
Final objective
Put everything togeteher I wanted a specific layout but nothing worked. It does not give an error, but does not work anyway…
::animint(map_t_minus1,map_t,map_t_plus1,plot_QER, first = list(year = c(2003), day_of_year = c(200)),arrange = list(
animint2top_row = c("map_t_minus1", "map_t", "map_t_plus1"), # top row
bottom_row = "plot_QER" # bottom row
))
Final Final Objective : add facets to make it pretty
Many thanks to Blanche who did not let me leave the room with an ugly plot.
Helper functions add_facets add a column to a dataframe, allowing us to make a facet name. We choose to only put a top name for the date.
<- function(df, top){
add_facets data.frame(df,
top=factor(top, c("t-1", "t", "t+1")))
}<- function(df) add_facets(df, "t-1")
facet_left <- function(df) add_facets(df, "t")
facet_middle <- function(df) add_facets(df, "t+1") facet_right
# Map t-1
<- animint2::ggplot() +
map_t ::geom_tile(
animint2::aes(x, y, fill = temperature),
animint2data = facet_left(map_df %>% mutate(day_of_year = day_of_year + 1)),
showSelected = c("year", "day_of_year"),
alpha = 0.4,
colour = NA
+
) ::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2theme_bw() +
::geom_tile(
animint2::aes(x, y, fill = temperature),
animint2data = facet_middle(map_df),
showSelected = c("year", "day_of_year"),
alpha = 1,
colour = NA
+
) ::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2theme_bw() +
::geom_tile(
animint2::aes(x, y, fill = temperature),
animint2data = facet_right(map_df %>% mutate(day_of_year = day_of_year - 1)),
showSelected = c("year", "day_of_year"),
alpha = 0.4,
colour = NA
+
) ::scale_fill_gradientn(colors = rev(pal),
animint2na.value = "transparent",
limits = mm) +
::coord_equal() +
animint2theme_bw()+
facet_wrap(~top)
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
map_t
animint(map_t,plot_QER, first = list(year = c(2003), day_of_year = c(200)))
SAVING YOUR WORK
Option 1: in rmd and qmd
Output directory auto-generated, be careful use only one animint per named code chunk.
Option 2: html
animint2dir(animint(plot_both),
out.dir = "myplot",
open.browser = FALSE)
The plot can be viewed with index.html. If the web page is blank, configure your browser to allow execution of local JavaScript code, as explained on the FAQ.