Grafica interattiva {package vari} e grafica animata {gganimate}

Author

Domenico Vistocco

Prepariamo l’ambiente di lavoro

Le funzioni usate in questo file di esempio sono contenute in alcune librerie generiche:

  • gapminder (tabella dati usata per la costruzione dei grafici di esempio)

  • dplyr (manipolazione della tabella dati)

  • ggplot2 (costruzione di rappresentazione grafiche statiche)

ed in alcune librerie specifiche, ed in particolare:

  • ggiraph : aggiunta di interazioni sui grafici costruiti con ggplot2

  • plotly : aggiunta di interazioni sfruttando la libreria plotly, libreria JavaScript. Anche in questo caso è possibile sfruttare un’interfaccia, ggplotly, che permette di aggiungere interazioni su un grafico statico costruito con ggplot2

  • Highcharter e dygraphs: interfacce a due librerie JavaScript che permettono di ottenere grafici interattivi sfruttando una sintassi ad hoc

  • gganimate : che introduce una grammar for data animation, molto simile a quella introdotta dal package ggplot2

Entrambi i package dplyr e ggplot2 sono caricati automaticamente quando si carica tidyverse. Gli altri package sono caricati nelle sezioni successive del documento:

library(tidyverse)  # ai fini della data manipulation e per ggplot2
library(gapminder)  # per utilizzare la tabella data gapminder

Le tabelle di esempio

Per la maggior parte degli esempi nel seguito di questo documento utilizzerò la tabella gapminder, resa famosa da questo Ted Talk che ha aperto la strada per l’uso degli effetti di animazione nella grafica statistica:

The best stats you’ve ever seen (Hans Rosling)

Procedo a creare un oggetto sul workspace a partire dalla tabella gapminder, rinominando due colonne ed eliminando un apostrofo dal nome del paese: la presenza dell’apostrofo generebbe infatti un errore successivamente quando utilizzo il nome del paese per inserire le etichette sui grafici interattivi:

gap <- gapminder |>
  rename(life_exp = lifeExp, gdp_per_cap = gdpPercap) |>
  mutate(country = recode(country,
                          `Cote d'Ivoire` = "Cote Ivoire"))

Una funzione che può essere utile per selezionare / cancellare alcune righe da una tabella dati è la funzione slice {dplyr}. Ecco alcuni esempi di utilizzo:

# posso selezionare una riga specificando una data posizione (indice di riga)
gap |> slice(10)
# A tibble: 1 × 6
  country     continent  year life_exp      pop gdp_per_cap
  <fct>       <fct>     <int>    <dbl>    <int>       <dbl>
1 Afghanistan Asia       1997     41.8 22227415        635.
# o escludere una riga inserendo il segno - prima dell'indice di riga
gap |> slice(-10)
# A tibble: 1,703 × 6
   country     continent  year life_exp      pop gdp_per_cap
   <fct>       <fct>     <int>    <dbl>    <int>       <dbl>
 1 Afghanistan Asia       1952     28.8  8425333        779.
 2 Afghanistan Asia       1957     30.3  9240934        821.
 3 Afghanistan Asia       1962     32.0 10267083        853.
 4 Afghanistan Asia       1967     34.0 11537966        836.
 5 Afghanistan Asia       1972     36.1 13079460        740.
 6 Afghanistan Asia       1977     38.4 14880372        786.
 7 Afghanistan Asia       1982     39.9 12881816        978.
 8 Afghanistan Asia       1987     40.8 13867957        852.
 9 Afghanistan Asia       1992     41.7 16317921        649.
10 Afghanistan Asia       2002     42.1 25268405        727.
# ℹ 1,693 more rows
# posso anche selezionare un insieme di righe usando l'operatore :
gap |> slice(10:15)
# A tibble: 6 × 6
  country     continent  year life_exp      pop gdp_per_cap
  <fct>       <fct>     <int>    <dbl>    <int>       <dbl>
1 Afghanistan Asia       1997     41.8 22227415        635.
2 Afghanistan Asia       2002     42.1 25268405        727.
3 Afghanistan Asia       2007     43.8 31889923        975.
4 Albania     Europe     1952     55.2  1282697       1601.
5 Albania     Europe     1957     59.3  1476505       1942.
6 Albania     Europe     1962     64.8  1728137       2313.

Un esempio sicuramente più utile per l’utilizzo delle funzioni della famiglia slice è quello che permette di selezionare un gruppo di dati in base ad una caratteristica:

# slice_head permette di specificare le prime n osservazioni all'interno di
# ciascun gruppo
# NOTA: è possibile anche specificare una data proporzione di osservazioni
#       all'interno di ciascun gruppo sfruttando l'argomento prop invece
#       dell'argomento n
#
# raggruppo le osservazioni per paese e seleziono le prime due osservazioni
# all'interno di ciascun gruppo
gap_first_2 <- gap |> group_by(country) |> slice_head(n = 2)
gap_first_2
# A tibble: 284 × 6
# Groups:   country [142]
   country     continent  year life_exp      pop gdp_per_cap
   <fct>       <fct>     <int>    <dbl>    <int>       <dbl>
 1 Afghanistan Asia       1952     28.8  8425333        779.
 2 Afghanistan Asia       1957     30.3  9240934        821.
 3 Albania     Europe     1952     55.2  1282697       1601.
 4 Albania     Europe     1957     59.3  1476505       1942.
 5 Algeria     Africa     1952     43.1  9279525       2449.
 6 Algeria     Africa     1957     45.7 10270856       3014.
 7 Angola      Africa     1952     30.0  4232095       3521.
 8 Angola      Africa     1957     32.0  4561361       3828.
 9 Argentina   Americas   1952     62.5 17876956       5911.
10 Argentina   Americas   1957     64.4 19610538       6857.
# ℹ 274 more rows
# da notare che la tabella in output mantiene l'attributo di raggruppamento
class(gap_first_2)
[1] "grouped_df" "tbl_df"     "tbl"        "data.frame"
# poiché la presenza dell'attributo di raggruppamento potrebbe essere fonte
# di possibili errori o comportamenti non attesi è consigliabile eliminare
# l'attributo di raggruppamento sfruttando la funzione ungroup
gap_first_2 <- gap |> group_by(country) |> slice_head(n = 2) |>
  ungroup()
class(gap_first_2)
[1] "tbl_df"     "tbl"        "data.frame"
# è possibile sfruttare l'argomento by per indicare l'attributo di raggruppamento
# da utilizzare per la funzione di slice: in questo modo la tabella di output
# non mantiene l'attributo di raggruppamento
gap |> slice_head(n = 2, by = country)
# A tibble: 284 × 6
   country     continent  year life_exp      pop gdp_per_cap
   <fct>       <fct>     <int>    <dbl>    <int>       <dbl>
 1 Afghanistan Asia       1952     28.8  8425333        779.
 2 Afghanistan Asia       1957     30.3  9240934        821.
 3 Albania     Europe     1952     55.2  1282697       1601.
 4 Albania     Europe     1957     59.3  1476505       1942.
 5 Algeria     Africa     1952     43.1  9279525       2449.
 6 Algeria     Africa     1957     45.7 10270856       3014.
 7 Angola      Africa     1952     30.0  4232095       3521.
 8 Angola      Africa     1957     32.0  4561361       3828.
 9 Argentina   Americas   1952     62.5 17876956       5911.
10 Argentina   Americas   1957     64.4 19610538       6857.
# ℹ 274 more rows
# la funzione slice_min permette di selezionare le unità che presenta i valori
# più bassi di una variabile
# ad esempio seleziono i record dei paesi che presentano i valori della variabile
# gdp_per_cap più bassi
gap |> slice_min(gdp_per_cap, by = country)
# A tibble: 142 × 6
   country     continent  year life_exp      pop gdp_per_cap
   <fct>       <fct>     <int>    <dbl>    <int>       <dbl>
 1 Afghanistan Asia       1997     41.8 22227415        635.
 2 Albania     Europe     1952     55.2  1282697       1601.
 3 Algeria     Africa     1952     43.1  9279525       2449.
 4 Angola      Africa     1997     41.0  9875024       2277.
 5 Argentina   Americas   1952     62.5 17876956       5911.
 6 Australia   Oceania    1952     69.1  8691212      10040.
 7 Austria     Europe     1952     66.8  6927772       6137.
 8 Bahrain     Asia       1952     50.9   120447       9867.
 9 Bangladesh  Asia       1972     45.3 70759295        630.
10 Belgium     Europe     1952     68    8730405       8343.
# ℹ 132 more rows
# la funzione slice_max lavora allo stesso modo ma restituendo i record che
# presentano i valori più alti
gap |> slice_max(gdp_per_cap, by = country)
# A tibble: 142 × 6
   country     continent  year life_exp       pop gdp_per_cap
   <fct>       <fct>     <int>    <dbl>     <int>       <dbl>
 1 Afghanistan Asia       1982     39.9  12881816        978.
 2 Albania     Europe     2007     76.4   3600523       5937.
 3 Algeria     Africa     2007     72.3  33333216       6223.
 4 Angola      Africa     1967     36.0   5247469       5523.
 5 Argentina   Americas   2007     75.3  40301927      12779.
 6 Australia   Oceania    2007     81.2  20434176      34435.
 7 Austria     Europe     2007     79.8   8199783      36126.
 8 Bahrain     Asia       2007     75.6    708573      29796.
 9 Bangladesh  Asia       2007     64.1 150448339       1391.
10 Belgium     Europe     2007     79.4  10392226      33693.
# ℹ 132 more rows

Creo un oggetto con i dati relativi all’ultimo anno che utilizzo per alcuni grafici nel seguito:

# posso selezionare tutti i record dell'ultimo anno disponibile (2007)
# sfruttando la funzione filter
gap |> filter(year == 2007)
# A tibble: 142 × 6
   country     continent  year life_exp       pop gdp_per_cap
   <fct>       <fct>     <int>    <dbl>     <int>       <dbl>
 1 Afghanistan Asia       2007     43.8  31889923        975.
 2 Albania     Europe     2007     76.4   3600523       5937.
 3 Algeria     Africa     2007     72.3  33333216       6223.
 4 Angola      Africa     2007     42.7  12420476       4797.
 5 Argentina   Americas   2007     75.3  40301927      12779.
 6 Australia   Oceania    2007     81.2  20434176      34435.
 7 Austria     Europe     2007     79.8   8199783      36126.
 8 Bahrain     Asia       2007     75.6    708573      29796.
 9 Bangladesh  Asia       2007     64.1 150448339       1391.
10 Belgium     Europe     2007     79.4  10392226      33693.
# ℹ 132 more rows
# utilizzando la funzione slice_tail seleziono l'ultimo anno disponibile
# per ciascun paese: questo approccio è consigliabile quando non sono
# sicuro che l'ultimo anno disponibile non sia lo stesso per tutti i paesi
gap_last <- gap |> slice_tail(n = 1, by = country)

Elementi interattivi sfruttando il package ggiraph

Il package ggiraph permette di aggiungere elementi di interazione sfruttando la stessa sintassi di ggplot2 attraverso una serie di geom_*_interactive. La pagina ufficiale del package è disponibile su questo link, da cui si può accedere ad un manuale esteso, ggiraph-book.

library(ggiraph)

Conviene partire costruire un grafico statico sfruttando ggplot2: a titolo esemplificativo consideriamo uno scatter plot del gdp_per_cap vs la life_exp sui dati del 2007:

ggplot(gap_last) +
  aes(x = gdp_per_cap, y = life_exp) +
  geom_point()

Per aggiungere l’interazione è sufficiente sostituire geom_point_interactive a geom_point, specificando la colonna da usare come tooltip da abbinare all’azione mouse over e dare in pasto il grafico risultante alla funzione girafe, che funge da parser per trasformare il grafico statico in un grafico interattivo:

gg1 <- ggplot(gap_last) +
  aes(x = gdp_per_cap, y = life_exp) +
  geom_point_interactive(aes(tooltip = country,
                             data_id = country),
                         hover_nearest = TRUE,
                         size = 3)
girafe(ggobj = gg1)

Gli elementi di interazioni possono essere aggiunti su qualunque grafico ggplot2: ecco un esempio di un grafico statico in cui sfrutto il colore per differenziare i punti dello scatter plot in base al continente:

ggplot(gap_last) +
  aes(x = gdp_per_cap, y = life_exp, colour = continent) +
  geom_point() +
  theme(legend.position = "top")

Ed ecco il corrispondente grafico interattivo:

gg2 <- ggplot(gap_last) +
  aes(x = gdp_per_cap, y = life_exp, colour = continent) +
  geom_point_interactive(aes(tooltip = country,
                             data_id = country),
                         hover_nearest = TRUE,
                         size = 3) +
  theme(legend.position = "top")
girafe(ggobj = gg2)

L’interazione con i punti del grafico viene gestita automaticamente anche nel caso divisi in pannelli usando facet_wrap o facet_grid: passando con il mouse su un punto in un pannello, viene automaticamente evidenziato il punto con lo stesso data_id anche negli altri pannelli.

Per mostrare un esempio di questa funzionalità seleziono i dati degli ultimi tre anni e rappresento lo scatter plot usando un pannelo separato per ciascun anno:

last_three <- gap |>
  slice_tail(n = 3, by = country)

ggplot(last_three) +
  aes(x = gdp_per_cap, y = life_exp) +
  geom_point() +
  facet_grid(rows = vars(year))

Costruisco il corrispondente grafico ggiraph sfruttando geom_point_interactive: scorrendo con il mouse su uno dei pannelli si può notare come il punto dello stesso paese sia automaticamente evidenziato anche negli altri pannelli.

gg3 <- ggplot(last_three) +
  aes(x = gdp_per_cap, y = life_exp) +
  geom_point_interactive(aes(data_id = country,
                             tooltip = country)) +
  facet_grid(rows = vars(year))
girafe(ggobj = gg3)

Oltre alle azioni legate al movimento del mouse è possibile aggiungere anche effetti di interazione legati alle azioni di selezione sfruttando le numerose opzioni che offre il package ggiraph. Ecco un esempio che sfrutta la selezione di un singolo punto (anche in questo caso la selezione su uno dei pannelli produce automaticamente una selezione del punto relativo allo stesso paese anche negli altri pannelli):

girafe(ggobj = gg3,
       options = list(
         opts_selection(
           type = "single",
           only_shiny = FALSE))
)

Ed ecco un esempio in cui è possibile sfruttare la selezione di più punti in successione, ottenuta impostando a multiple il tipo di selezione:

girafe(ggobj = gg3,
       options = list(
         opts_selection(
           type = "multiple",
           only_shiny = FALSE))
)

Riporto a seguire un altro esempio di grafico costruito con ggiraph.

A tale scopo, a partire dalla tabella originaria, calcolo il valore medio del gpd_per_cap per i vari anni aggregando i dati per continente e rappresentando la tabella risultante usando un grafico a linee, dove ogni linea fa riferimento ai valori medi del gdp_per_cap per ciascuno dei cinque continenti:

tb_medie <- gap |>
  group_by(continent, year) |>
  summarise(gdp_per_cap = mean(gdp_per_cap))
`summarise()` has grouped output by 'continent'. You can override using the
`.groups` argument.
tb_medie
# A tibble: 60 × 3
# Groups:   continent [5]
   continent  year gdp_per_cap
   <fct>     <int>       <dbl>
 1 Africa     1952       1253.
 2 Africa     1957       1385.
 3 Africa     1962       1598.
 4 Africa     1967       2050.
 5 Africa     1972       2340.
 6 Africa     1977       2586.
 7 Africa     1982       2482.
 8 Africa     1987       2283.
 9 Africa     1992       2282.
10 Africa     1997       2379.
# ℹ 50 more rows
ggplot(tb_medie) +
  aes(x = year, y = gdp_per_cap, colour = continent) +
  geom_line() +
  theme(legend.position = "bottom")

Anche in questo caso si può aggiungere l’interazione sostituendo geom_line_interactive a geom_line e sfruttando la funzione girafe:

gg4 <- ggplot(tb_medie) +
  aes(x = year, y = gdp_per_cap, colour = continent) +
  theme(legend.position = "bottom") +
  geom_line_interactive(aes(tooltip = continent))
girafe(ggobj = gg4)

Elementi interattivi sfruttando il package plotly (e la funzione ggplotly)

Una differente libreria che permette di aggiungere elementi interattivi su un grafico è la libreria plotly:

library(plotly)

In questo caso è possibile sfruttare la libreria usando una sintassi ad hoc, come illustrato sulla pagina web dedicata all’interfaccia tra R e la libreria grafica. E’ però possibile sfruttare anche in questo caso la sintassi tipica di ggplot2 sfruttando la funzione ggplotly come parser finale di un grafico statico ottenuto in ggplot2. Per maggiori dettagli si rimanda alla pagina ufficiale.

Si riporta di seguito a titolo esemplificativo il codice che permette di ottenere un boxplot sui dati gapminder:

gg5 <- ggplot(gap_last) +
  aes(x = continent, y = gdp_per_cap, fill = continent) +
  geom_boxplot(show.legend = FALSE)

ggplotly(gg5)

ed uno scatter plot:

gg6 <- ggplot(gap_last) +
  aes(x = gdp_per_cap, y = life_exp, label = country) +
  geom_point()
ggplotly(gg6)

Dai due esempi si nota immediatamente come in questo caso è sufficiente costruire il grafico in ggplot2 e passare in input alla funzione ggplotly {plotly} per aggiungere gli elementi interattivi, che variano da grafico a grafico.

Cenni al package highcharter

La libreria highcharter è invece un’interfaccia tra R e la libreria JavaScript Highcharts:

library(highcharter)

La sintassi di questa libreria è in questo caso differente e varia per i differenti tipi di grafico. Per dettagli si rimanda alla pagina ufficile dove sono riportati esempi per i differenti tipi di grafici.

A titolo esemplificativo riporto lo scatter plot mostrato già in precedenza con le altre librerie grafiche:

hchart(gap_last,
       "point",
       hcaes(x = gdp_per_cap, y = life_exp, group = continent))

L’esempio successivo mostra l’andamento del gdp_per_cap tra i vari continenti sfruttando dei boxplot paralleli:

data_to_plot <- data_to_boxplot(data = gap_last,
                       variable = gdp_per_cap,
                       group_var = continent,
                       name = "GDP per Cap")
highchart() |>
  hc_xAxis(type = "category") |>
  hc_add_series_list(data_to_plot)

e sovraimponendo allo stesso grafico aggiungendo dei punti con la tecnica del jittering:

highchart() |>
  hc_xAxis(type = "category") |>
  hc_add_series_list(data_to_plot) |>
  hc_add_series(
    data = gap_last,
    type = "scatter",
    hcaes(x = "continent", y = "gap_last$gdp_per_cap", group = "continent")
  ) |>
  hc_plotOptions(scatter = list(
    marker = list(
      radius = 3,
      symbol = "circle",
      lineWidth = 1
    )
  ))  |>
  hc_plotOptions(scatter = list(jitter = list(x = .1, y = 0)))

Cenni al package dygraphs

La libreria dygraphs è invece un’interfaccia tra R e la libreria JavaScript dygraphs:

library(dygraphs)

Si tratta di una libreria che permette di costruire dei grafici interattivi su dati di tipo temporali. Per dettagli anche in questo caso si rimanda alla pagina ufficiale.

Per mostrare qualche esempio è necessario fare una premessa sui dati di tipo temporali. In particolare sfruttando le funzioni seq e as.Date è possibile costruire una sequenza di date che va dal 1952 al 2007 con un passo di 5 anni:

seq(from = as.Date("1952-01-01"), by = "5 years", length.out = 12)
 [1] "1952-01-01" "1957-01-01" "1962-01-01" "1967-01-01" "1972-01-01"
 [6] "1977-01-01" "1982-01-01" "1987-01-01" "1992-01-01" "1997-01-01"
[11] "2002-01-01" "2007-01-01"

Per sfruttare le funzioni della libreria dygraphs è utile (non necessario) preparare adeguatamenti gli oggetti da rappresentare: se si utilizzano infatti oggetti di tipo serie storiche (ts o xts) la rappresentazione gestisce in automatico le etichette. Di seguito il codice, abbastanza articolato a dire il vero, necessario per costruire un oggetto da usare per la reppresentazione:

# la libreria xts permette di ottenere degli oggetti di tipo xts
# che sono tipicamente utilizzati per dati di serie storiche
library(xts)

# calcolo una tabella di sintesi con la speranza di vita media
# per i vari continenti e i vari anni
continent_life_exp <- gap |>
  group_by(continent, year) |>
  summarise(life_exp = mean(life_exp)) |>
  ungroup()
# stampo una porzione della tabella
continent_life_exp
# A tibble: 60 × 3
   continent  year life_exp
   <fct>     <int>    <dbl>
 1 Africa     1952     39.1
 2 Africa     1957     41.3
 3 Africa     1962     43.3
 4 Africa     1967     45.3
 5 Africa     1972     47.5
 6 Africa     1977     49.6
 7 Africa     1982     51.6
 8 Africa     1987     53.3
 9 Africa     1992     53.6
10 Africa     1997     53.6
# ℹ 50 more rows
# a partire da questa tabella di sintesi costruisco un oggetto
# di tipo xts (serie storica) per ciascun continente
# questo è l'esempio per i dati relativi all'Africa
continent_life_exp |>
  filter(continent == "Africa") |>
  pull(life_exp) |>
  xts(order.by = seq(from = as.Date("1952-01-01"),
                     by = "5 years",
                     length.out = 12))
               [,1]
1952-01-01 39.13550
1957-01-01 41.26635
1962-01-01 43.31944
1967-01-01 45.33454
1972-01-01 47.45094
1977-01-01 49.58042
1982-01-01 51.59287
1987-01-01 53.34479
1992-01-01 53.62958
1997-01-01 53.59827
2002-01-01 53.32523
2007-01-01 54.80604
# sfruttando la funzione cbind concanteno per colonna
# in un unico oggetto le serie storiche relative ai vari continenti
xts_obj <- cbind(
  Africa = {
    continent_life_exp |>
      filter(continent == "Africa") |>
      pull(life_exp) |>
      xts(order.by = seq(from = as.Date("1952-01-01"),
                         by = "5 years",
                         length.out = 12))
  },
  Americas = {
    continent_life_exp |>
      filter(continent == "Americas") |>
      pull(life_exp) |>
      xts(order.by = seq(from = as.Date("1952-01-01"),
                         by = "5 years",
                         length.out = 12))
  },
  Asia = {
    continent_life_exp |>
      filter(continent == "Asia") |>
      pull(life_exp) |>
      xts(order.by = seq(from = as.Date("1952-01-01"),
                         by = "5 years",
                         length.out = 12))
  },
  Europe = {
    continent_life_exp |>
      filter(continent == "Europe") |>
      pull(life_exp) |>
      xts(order.by = seq(from = as.Date("1952-01-01"),
                         by = "5 years",
                         length.out = 12))
  },
  Oceania = {
    continent_life_exp |>
      filter(continent == "Oceania") |>
      pull(life_exp) |>
      xts(order.by = seq(from = as.Date("1952-01-01"),
                         by = "5 years",
                         length.out = 12))
  }
  )
# stampo a video l'anteprima dell'oggetto risultante
xts_obj
             Africa Americas     Asia   Europe Oceania
1952-01-01 39.13550 53.27984 46.31439 64.40850 69.2550
1957-01-01 41.26635 55.96028 49.31854 66.70307 70.2950
1962-01-01 43.31944 58.39876 51.56322 68.53923 71.0850
1967-01-01 45.33454 60.41092 54.66364 69.73760 71.3100
1972-01-01 47.45094 62.39492 57.31927 70.77503 71.9100
1977-01-01 49.58042 64.39156 59.61056 71.93777 72.8550
1982-01-01 51.59287 66.22884 62.61794 72.80640 74.2900
1987-01-01 53.34479 68.09072 64.85118 73.64217 75.3200
1992-01-01 53.62958 69.56836 66.53721 74.44010 76.9450
1997-01-01 53.59827 71.15048 68.02052 75.50517 78.1900
2002-01-01 53.32523 72.42204 69.23388 76.70060 79.7400
2007-01-01 54.80604 73.60812 70.72848 77.64860 80.7195
# e la relativa classe
class(xts_obj)
[1] "xts" "zoo"

Una volta preparato l’oggetto è sufficiente passarlo in input alla funzione dygraph:

xts_obj |> dygraph()

Se si aggiunge anche la funzione dyRangeSelector viene aggiunto anche un selettore che permette di effettuare zoom sui dati, impostare finestre mobili e navigare sui dati:

xts_obj |> dygraph() |> dyRangeSelector()

Si segnala che nell’esempio riportato sopra, tenuto conto della scarsa numerosità, non si apprezza molto quest’ultima funzionalità, che è invece molto interessante per dati ad alta frequenza. Sulla documentazione ufficiale sono riportati diversi esempi per altri grafici tipicamenti utilizzati nel campo finanziario.

Animazioni sfruttando il package gganimate

La libreria gganimate è ispirata al TED-Talk Gapminder di cui sopra:

library(gganimate)

La libreria usa un approccio basato su ggplot2 per la costruzione del grafico di partenza, cui aggiunge delle animazioni. Per dettagli si rimanda alla pagina ufficiale del package.

Il codice seguente permette di riprodurre l’animazione che Hans Rosling ha presentato nel suo Ted-Talk (vedi video in testa alla pagina). Si parte costruendo un grafico rappresentando tutti i dati che poi verranno divisi per ottenere i vari frame del grafico animato:

ggplot(gap) +
  aes(x = gdp_per_cap, y = life_exp, colour = continent) +
  geom_point() +
  theme(legend.position = "top")

A questo punto è sufficiente sfruttare la funzione transition_states impostando la variabile year per separare i dati nei vari frame:

gg7 <- ggplot(gap) +
  aes(x = gdp_per_cap, y = life_exp, colour = continent) +
  geom_point() +
  theme(legend.position = "top") +
  transition_states(year)
gg7

Si può migliorare il grafico precedente usando lo strato labs(title = "Anno: {closest_state}") che permette di mostrare nel titolo del grafico l’anno cui i dati fanno riferimento:

gg7 <- ggplot(gap) +
  aes(x = gdp_per_cap, y = life_exp, colour = continent) +
  geom_point() +
  theme(legend.position = "top") +
  labs(title = "Anno: {closest_state}") +
  transition_states(year) +
  ease_aes('linear')
gg7

Di seguito un grafico animato che mostra i dati relativi ai vari paesi, separandoli questa volta in singoli pannelli per i vari continenti e usando la dimensione del punto per mostrare la popolazione dei singoli paesi:

ggplot(gap) +
  aes(gdp_per_cap, life_exp, 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() +
  facet_wrap(~continent) +
  # Here comes the gganimate specific bits
  labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'life expectancy') +
  transition_time(year) +
  ease_aes('linear')