Last week we
visualized the temperature anomalies during 1880-2022 as deviations from
the benchmark period 1951-1990. We used interactive climate
spirals following Code Club (episode 223) by the Riffomonas
Project YouTube Channel and Pat Schloss (highly recommended!), who
in turn built from climatologist Ed Hawkins from the University of
Reading.
This week we will work on animating the previous visuals to create a dynamic chart showing the changes over time. A complete dive into animations will take more than one session. Therefore, in this session we will focus on animations with plotly and we will leave time for a discussion using animations with gganimate or even other packages in Phython recommended by Prof. Bill Mongan. If you’re interested in our previous IDSIRI meetings, here are the links: Week2, Week 3.
This time, we will work in groups, and each group station will project in their own monitor. To do this, you need to connect to the monitor using Solstice (Mersive). Here are the instructions. The direct link to donwload Mersive is here. /
Anyone can connect to the monitors, but you need to have Solstice (Mersive) installed. If you install it, you can connect and project your screen in your station. For this week (first one we are doing this), however, it is more likely that Ursinus professors that usually attend (e.g., Andrea Kauffman-Berry, Leslie New, Olga Nicoara, Bill Mongan, Hugo Montesinos, Eric Takyi, and Chris Tralie) already have this program downloaded. Therefore, they can be natural leaders of the group.
Install and load the the following R packages: tidyverse, plotly, readr, glue, and htmlwidgets.
As last week, data come from NASA: https://data.giss.nasa.gov/gistemp/. We want to read the
data from the section: Tables of Global and Hemispheric Monthly
Means and Zonal Annual Means, subsection: Combined
Land-Surface Air and Sea-Surface Water Temperature Anomalies (Land-Ocean
Temperature Index, L-OTI). Specific data: Global-mean
monthly, seasonal, and annual means, 1880-present, updated
through most recent month: TXT, CSV.
If you right-click on the CSV file, you can copy the link which is the
URL we will use. Then do the following:
#Get latest data from NASA
url <- "https://data.giss.nasa.gov/gistemp/tabledata_v4/GLB.Ts+dSST.csv"
data<-read.csv(url, skip=1, na="***")
head(data)
## Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## 1 1880 -0.20 -0.25 -0.09 -0.16 -0.09 -0.22 -0.19 -0.09 -0.15 -0.22 -0.21 -0.18
## 2 1881 -0.19 -0.15 0.03 0.04 0.07 -0.18 0.01 -0.04 -0.16 -0.22 -0.18 -0.07
## 3 1882 0.16 0.14 0.05 -0.15 -0.13 -0.22 -0.16 -0.07 -0.14 -0.23 -0.16 -0.36
## 4 1883 -0.29 -0.37 -0.12 -0.18 -0.18 -0.07 -0.07 -0.14 -0.22 -0.11 -0.24 -0.11
## 5 1884 -0.13 -0.08 -0.36 -0.40 -0.34 -0.35 -0.31 -0.28 -0.27 -0.25 -0.33 -0.31
## 6 1885 -0.58 -0.33 -0.26 -0.42 -0.45 -0.43 -0.33 -0.31 -0.29 -0.24 -0.24 -0.10
## J.D D.N DJF MAM JJA SON
## 1 -0.17 NA NA -0.11 -0.17 -0.19
## 2 -0.09 -0.10 -0.17 0.05 -0.07 -0.18
## 3 -0.11 -0.08 0.08 -0.08 -0.15 -0.18
## 4 -0.18 -0.20 -0.34 -0.16 -0.09 -0.19
## 5 -0.28 -0.27 -0.11 -0.37 -0.31 -0.28
## 6 -0.33 -0.35 -0.41 -0.38 -0.36 -0.25
data<-tibble(data)
The data are in wide format (each year is a column). We want to make
it into long format, where year is a single-column variable
containing all the years from 1880 to the present. Do the
following:
# Convert the data from wide to long (tidy).
data_wide<-data %>%
select(year = Year, all_of(month.abb))
data_long<- data_wide %>%
pivot_longer(-year,
names_to = "month",
values_to = "t_diff") %>%
drop_na() %>%
mutate(month = factor(month, levels=month.abb)) %>%
arrange(year, month)
data_long
## # A tibble: 1,712 × 3
## year month t_diff
## <int> <fct> <dbl>
## 1 1880 Jan -0.2
## 2 1880 Feb -0.25
## 3 1880 Mar -0.09
## 4 1880 Apr -0.16
## 5 1880 May -0.09
## 6 1880 Jun -0.22
## 7 1880 Jul -0.19
## 8 1880 Aug -0.09
## 9 1880 Sep -0.15
## 10 1880 Oct -0.22
## # … with 1,702 more rows
To visualize the data in a circular fashion (necessary for the
climate spiral) we need to use polar coordinates:
1. Convert the data to Polar Coordinates. 2. Inspect the data.
# Transform data to Polar Coordinates.
t_data <- data_long %>%
mutate(month_number = as.numeric(month),
year_cont = year + month_number/12,
Date = make_date(year = year, month = month),
radius = t_diff + 1.5, #We add an arbitrary constant to avoid negative radius
theta = 2*pi*(month_number - 1)/12,
x = radius * cos(theta),
y = radius * sin(theta),
z = year,
label = glue("{month} {year}\n {t_diff} \u00B0C"))
head(t_data)
## # A tibble: 6 × 12
## year month t_diff month_number year_cont Date radius theta x
## <int> <fct> <dbl> <dbl> <dbl> <date> <dbl> <dbl> <dbl>
## 1 1880 Jan -0.2 1 1880. 1880-01-01 1.3 0 1.3 e+ 0
## 2 1880 Feb -0.25 2 1880. 1880-02-01 1.25 0.524 1.08e+ 0
## 3 1880 Mar -0.09 3 1880. 1880-03-01 1.41 1.05 7.05e- 1
## 4 1880 Apr -0.16 4 1880. 1880-04-01 1.34 1.57 8.20e-17
## 5 1880 May -0.09 5 1880. 1880-05-01 1.41 2.09 -7.05e- 1
## 6 1880 Jun -0.22 6 1880. 1880-06-01 1.28 2.62 -1.11e+ 0
## # … with 3 more variables: y <dbl>, z <int>, label <glue>
Last week we did the transformation explicitly. The data came
naturally in polar coordinates (the month being the angle, and the
temperature deviation being the radius), so we needed to tell R the
corresponding Cartesian coordinates (x, y, z), manually. Alternatively,
we could have instructed R to do the graph in polar coordinates
automatically.
Before we animate the climate spiral from last week, let’s animate the simple line chart using plotly, to understand how the syntax works.
First, produce a simple (not animated) visual of the data using Plot_ly
# Simple visualization with animation
t_data %>% plot_ly(x = ~year_cont, y = ~t_diff,text = ~label,
hoverinfo=~'text',
type='scatter', mode='lines', name="ΔT (°C)") -> fig1
fig1 <- fig1 %>% add_lines(y = ~0.5, name = '+0.5 °C',
line=list(color='orange',width=1, dash='dot'))
fig1 <- fig1 %>% add_lines(y = ~1, name = '+1 °C',
line=list(color='red',width=1, dash='dot'))
fig1 <- fig1 %>% add_lines(y = ~-0.5, name = '-0.5 °C',
line=list(color='blue',width=1, dash='dot'))
fig1 <- fig1 %>% layout(xaxis = list(title = "Year"),
yaxis = list(title = "Temperature Deviation (°C)"),
title = "Global Temperature Deviations Relative to 1951-1980 \n")
fig1
Then, we can animate the previous plot_ly figure simply with add_markers(frame = ~year)
# Simple visualization with animation
fig1 %>% add_markers(frame = ~year)
There are a few options we can control with the animation, such as the speed and type of the transitions. Let’s explore a few of them together.
See example at: https://plotly.com/r/animations/ Documentation: https://rdrr.io/cran/plotly/man/animation.html
# Simple visualization with animation
fig1 %>% add_markers(frame = ~year_cont) %>% animation_opts(frame=100, transition=100, redraw = FALSE)
#The frame and transition argument options receive time in milliseconds. So, 1000 would be 1 second. The smaller we make them, the faster the animation should go.
Now, let’s animate the climate spiral interactively using plot_ly:
Let’s see what happens if we make year as a direct argument of the
function:
Let’s display this in one of the stations and discuss it.
# Interactive Climate Spiral (Version 1.0)
fig2<-plot_ly(t_data,
x = ~x, y = ~y, z = ~z, frame = ~year,
type = 'scatter3d',
mode = 'lines',
line = list(width = 4, color = ~t_diff,
cmid = 0,
colorscale = list(c(0,'blue'),
c(0.5,'white'),
c(1,'red'))))
fig2
#saveWidget(fig2, "climate_spiral_plotly1.html")
#fig2 %>% add_markers(frame = ~year, mode='lines+markers') %>% animation_opts(frame=100, transition=100, redraw = TRUE)
Here, let’s do the same as above but trying to make it cumulative frame by frame. This is inefficient done this way, but it’s good to practice.
accumulate_by <- function(dat, var) {
var <- lazyeval::f_eval(var, dat)
lvls <- plotly:::getLevels(var)
dats <- lapply(seq_along(lvls), function(x) {
cbind(dat[var %in% lvls[seq(1, x)], ], frame = lvls[[x]])
})
dplyr::bind_rows(dats)
}
fig3 <- t_data %>% accumulate_by(~year)
fig3<-fig3 %>% plot_ly(x = ~x, y = ~y, z = ~z, frame = ~frame,
type = 'scatter3d',
mode = 'lines',
line = list(simplify=F, width = 4, color = ~t_diff,
cmid = 0,
colorscale = list(c(0,'blue'),
c(0.5,'white'),
c(1,'red'))))
fig3
#saveWidget(fig2, "climate_spiral_plotly1.html")
#fig2 %>% add_markers(frame = ~year, mode='lines') %>% animation_opts(frame=1000, transition=1000, redraw = TRUE)
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
# Interactive Climate Spiral (Version 1.0)
fig2<-plot_ly(t_data,
x = ~x, y = ~y, z = ~z, #frame = ~year,
type = 'scatter3d',
mode = 'lines',
line = list(width = 4, color = ~t_diff,
cmid = 0,
colorscale = list(c(0,'blue'),
c(0.5,'white'),
c(1,'red'))))
#fig2
#saveWidget(fig2, "climate_spiral_plotly1.html")
fig2 %>% add_markers(frame = ~year, mode='lines+markers') %>% animation_opts(frame=100, transition=100, redraw = TRUE)
# Interactive Climate Spiral (Version 2.0)
axx <- list(title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE)
axy <- list(title = "", showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE)
axz <- list(title = "")
fig3<-plot_ly(t_data,
x = ~x, y = ~y, z = ~z, text=~label,
hoverinfo = "text",
type = 'scatter3d',
mode = 'lines',
line = list(width = 4, color = ~t_diff,
cmid = 0,
colorscale = list(c(0,'blue'),
c(0.5,'white'),
c(1,'red'))))
fig3<- fig3 %>% layout(scene = list(xaxis=axx,yaxis=axy,zaxis=axz))
fig3 %>% add_trace( frame = ~year, mode='lines+markers', name="Current Year")
saveWidget(fig3, "climate_spiral_plotly2.html")
# Non Interactive Data using Base R (Version 2.0)
t_data <- t_data %>% mutate(year2=year+month_number/12)
p2<-plot(t_data$t_diff~t_data$year2, type="l", col="blue",
xlab="Year", ylab="Temperature Deviation (°C)",
main = "Global Temperature Anomalies Relative to 1951-1980")
abline(h=-0.5, lty=2); abline(h=+0.5, lty=2); abline(h=+1, lty=2, col="red")
p2
## NULL
This figure was not made using plot_ly but rather ggplot. The option of using ggplotly to convert ggplot figures into plotly does not work well all the time, especially in rich figures like this one.
The code could look like this (Station 5?):
#recreate the figure from scracth using plot_ly
#Create figure:
fig5 <- plot_ly(t_data,
theta = ~120+360*((-month_number-1)/12), r = ~t_diff+1.5, text=~label,
#color=~t_diff,
hoverinfo = "text",
type = 'scatterpolar',
mode = 'lines+markers', opacity=0.9,
line=list( width=1),
#color=list(color=~t_diff, cmin="blue", cmid="white", cmax="red"),
marker=list(size=2))
# line = list(width = 4, color = ~t_diff,
# cmid = 0,
# colorscale = list(c(0,'blue'),
# c(0.5,'white'),
# c(1,'red'))))
fig5
fig5 %>% add_trace( frame = ~year, mode='lines+markers') %>% animation_opts(frame = 100, transition = 5, redraw = TRUE)
If anyone completes this figure nicely using plot_ly, please share it with me and everyone else here.
Again, the trend we see here is alarming, especially if we use these data alone to forecast future global temperatures (say in the next 100 years). Doing so, however, would be a mistake. Why? We need to understand the fundamental drivers and mechanisms of the global temperature rise and these data don’t tell us that. Is it man-made or is it not? And to what degree? In addition, it is convenient to see the temperature anomalies on a larger time frame (say, thousands of years). While the data are less precise the further back we go in time, there are some projects attempting to measure that accurately. For example, see this amazing 800 Thousand Years Graph from the European Project for Ice Coring in Antartica (EPICA): Read more about it at Wikipedia. Also, see this another wonderful graph showing Multiple Models spanning up to 500 Years. Read more about it in Wikipedia