Dans ce TP nous allons pratiquer un peu de R, en explorant les données de la série Game of thrones avec les librairies dplyr et sf et nous les visualiserons à l’aide de la bibliothèque ggplot2. Les données tabulaires utilisées ont été collectées par Jeffrey Lancaster et sont issues de ce projet. Les données spatiales ont été créer par ESRI et sont disponnibles ici. Pour débuter vous pouvez cloner le repository https://github.com/comeetie/got afin de récupérer l’ensemble des données de ce tp.

Exercise 1 : importer les données et visualiser leurs structures

Lire des données

Importez les fichiers characters.csv, episodes.csv, scenes.csv et appearances.csv du répertoire data et les stocker dans des variables de mêmes nom.
Utilisez read_csv() de préférence à de la libraririe readr pour une meilleure reconnaissance automatique des types des variables.
library(readr)
characters = read_csv("data/characters.csv")
episodes = read_csv("data/episodes.csv")
scenes = read_csv("data/scenes.csv")
appearances = read_csv("data/appearances.csv")

Opérations élémentaires

Regardez la taille de la data.frame appearences, observez les variables communes aux tables scenes et appearences. Utilisez les fonctions str et summary sur les différentes tables pour bien comprendre leurs structures et leurs relations. Faites un schéma des relations entre les différentes tables.
Utiliser nrow, ncol ou dim pour connaitres les dimensions des data.frame. Utiliser la fonction names pour connaitres le noms des colones et l’opérateur %in% ou la fonction intersect par exemple pour trouver les variables communes.
dim(appearances)
[1] 12114     2
appearances_cols= names(appearances) 
appearances_cols[appearances_cols %in% names(scenes)]
[1] "sceneId"
summary(characters)
     name               sex               house             killedBy        
 Length:587         Length:587         Length:587         Length:587        
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
    image          
 Length:587        
 Class :character  
 Mode  :character  
str(characters)
spec_tbl_df [587 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ name    : chr [1:587] "Addam Marbrand" "Adrack Humble" "Aeron Greyjoy" "Aerys Targaryen" ...
 $ sex     : chr [1:587] "male" "male" "male" "male" ...
 $ house   : chr [1:587] NA NA "Greyjoy" NA ...
 $ killedBy: chr [1:587] NA NA NA NA ...
 $ image   : chr [1:587] NA NA "https://images-na.ssl-images-amazon.com/images/M/MV5BNzI5MDg0ZDAtN2Y2ZC00MzU1LTgyYjQtNTBjYjEzODczZDVhXkEyXkFqcG"| __truncated__ NA ...
 - attr(*, "spec")=
  .. cols(
  ..   name = col_character(),
  ..   sex = col_character(),
  ..   house = col_character(),
  ..   killedBy = col_character(),
  ..   image = col_character()
  .. )

Exercise 2 : commandes R de bases

Faire une somme

Servez vous de la table scenes pour calculer le nombre de personnages morts dans l’ensemble de la série.
Utilisez l’opérateur $ et la fonction sum.
sum(scenes$nbdeath)
[1] 376

Sélection conditionelle

Servez vous de la table scenes pour calculer le nombre de personnages morts lors de la première saison (sachant que celle-ci est constituée de 10 épisodes)
Utilisez l’opérateur $ et la fonction sum comme précédement mais selectionez les bonnes ligne à l’aide d’un vecteur booléen consrtuit à l’aide de la colonne épisode id.
sum(scenes$nbdeath[scenes$episodeId<=10])
[1] 32

Comptage et trie

Servez vous de la table characters pour trouver les 5 plus grands meurtriers de la série.
Utiliser les fonction table et sort.
sort(table(characters$killedBy),decreasing = TRUE)[1:5]

          Jon Snow Daenerys Targaryen         Arya Stark     Sandor Clegane 
                12                 11                 10                  9 
  Cersei Lannister 
                 7 

Sélection

Trouvez la durée de la scène la plus longue et l’id de l’episode.
Utilisez la fonction which.max .
scenes[which.max(scenes$duration),]
# A tibble: 1 x 9
  sceneStart sceneEnd location    subLocation     episodeId duration   nbc sceneId nbdeath
  <time>     <time>   <chr>       <chr>               <dbl>    <dbl> <dbl>   <dbl>   <dbl>
1 45'58"     56'59"   The Crownl… Outside King's…        73      661    18    3795       0

Exercise 3 : dplyr

Trie et filtrage

Trouvez la durée de la scène la plus longue et l’id de l’episode en utilisant dplyr cette fois.
Utilisez les fonctions arrange,desc et head.
library(dplyr)
scenes %>% arrange(desc(duration)) %>% head(1)
# A tibble: 1 x 9
  sceneStart sceneEnd location    subLocation     episodeId duration   nbc sceneId nbdeath
  <time>     <time>   <chr>       <chr>               <dbl>    <dbl> <dbl>   <dbl>   <dbl>
1 45'58"     56'59"   The Crownl… Outside King's…        73      661    18    3795       0

Jointure

Trouvez les personnages de la scène la plus longue.
Utilisez la fonction left_join pour faire une jointure avec la table appearences.
scenes %>% arrange(desc(duration)) %>% head(1) %>% left_join(appearances)
# A tibble: 18 x 10
   sceneStart sceneEnd location subLocation episodeId duration   nbc sceneId nbdeath name 
   <time>     <time>   <chr>    <chr>           <dbl>    <dbl> <dbl>   <dbl>   <dbl> <chr>
 1 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Tyri…
 2 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Grey…
 3 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Samw…
 4 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Edmu…
 5 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Arya…
 6 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Bran…
 7 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Sans…
 8 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Brie…
 9 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Davo…
10 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Gend…
11 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Yara…
12 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Robi…
13 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Yohn…
14 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Dorn…
15 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Lord…
16 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Lord…
17 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Lord…
18 45'58"     56'59"   The Cro… Outside Ki…        73      661    18    3795       0 Lord…

Jointure et aggrégation

Trouvez le lieu le plus visité en nombre de scènes.
Utilisez les fonctions group_by et summarize pour faire une aggrégation.
scenes %>% group_by(location) %>% summarise(nbsc = n()) %>% arrange(desc(nbsc))
# A tibble: 26 x 2
   location           nbsc
   <chr>             <int>
 1 The Crownlands     1252
 2 The North           888
 3 North of the Wall   342
 4 The Wall            309
 5 The Riverlands      301
 6 Meereen             168
 7 Braavos             103
 8 The Vale             54
 9 Dorne                49
10 The Reach            40
# … with 16 more rows

Filtrage

Trouvez le nombre de scènes se passant à King’s Landing.
Utilisez la fonction filter.
scenes %>% filter(subLocation=="King's Landing") %>% summarise(nbsc = n())
# A tibble: 1 x 1
   nbsc
  <int>
1  1001

Aggrégation

Trouvez le lieux précis ou le plus de personages meurent ?
Utiliser la fonction sum lors de l’aggrégation et la variable subLocation.
scenes %>% group_by(subLocation) %>% summarise(nbd=sum(nbdeath)) %>% arrange(desc(nbd))
# A tibble: 97 x 2
   subLocation          nbd
   <chr>              <dbl>
 1 King's Landing        74
 2 Winterfell            60
 3 <NA>                  51
 4 Castle Black          41
 5 The Twins             12
 6 The Haunted Forest     9
 7 The Wall               9
 8 Craster's Keep         8
 9 The Wolfswood          8
10 Dragonstone            6
# … with 87 more rows

Jointure, filtrage, aggrégation

Trouvez l’épisode ou Jon Snow est le plus longtemps visible.
Utilisez la fonction sum lors de l’aggrégation et faites des jointures pour pouvoir aggréger à l’échelle de l’épisode.
appearances %>%filter(name=="Jon Snow") %>% 
  left_join(scenes) %>% left_join(episodes) %>% 
  group_by(name,episodeId,episodeTitle) %>% 
  summarise(screenTime=sum(duration)) %>% 
  arrange(desc(screenTime)) %>% head(1)
# A tibble: 1 x 4
# Groups:   name, episodeId [1]
  name     episodeId episodeTitle    screenTime
  <chr>        <dbl> <chr>                <dbl>
1 Jon Snow        73 The Iron Throne       2526

Filtrage, numérique

Combien de personnages passent plus de 30 minutes à l’écran sur l’ensemble des saisons ?
appearances %>% left_join(scenes)  %>% 
  group_by(name) %>% 
  summarise(screenTime=sum(duration)) %>% 
  filter(screenTime>30*60) %>% 
  nrow()
[1] 98
# en version racourci avec count 
appearances %>% left_join(scenes)  %>% 
  count(name,wt=duration,name = "duration") %>% 
  filter(duration >30*60) %>% 
  nrow()
[1] 98

Jointure ?

Quels sont les deux personnages qui passent le plus de scènes ensembles ?
Faites une jointure de la table appearances avec elle même sur la colonne sceneId.
appearances %>% left_join(appearances,by=c("sceneId"="sceneId")) %>% 
  filter(name.x!=name.y) %>% 
  group_by(name.x,name.y) %>% 
  summarise(nbs=n()) %>% 
  arrange(desc(nbs))
# A tibble: 8,472 x 3
# Groups:   name.x [576]
   name.x             name.y               nbs
   <chr>              <chr>              <int>
 1 Daenerys Targaryen Drogon               172
 2 Drogon             Daenerys Targaryen   172
 3 Daenerys Targaryen Jorah Mormont        148
 4 Jorah Mormont      Daenerys Targaryen   148
 5 Jon Snow           Tormund Giantsbane   145
 6 Tormund Giantsbane Jon Snow             145
 7 Lord Varys         Tyrion Lannister     141
 8 Tyrion Lannister   Lord Varys           141
 9 Davos Seaworth     Jon Snow             136
10 Jon Snow           Davos Seaworth       136
# … with 8,462 more rows

Jointure ?

Quels sont les deux personnages qui passent le plus de temps ensembles ?
Faites une jointure de la table appearances avec elle même sur la colonne sceneId, puis une jointure avec la data.frame scenes.
appearances %>% left_join(appearances,by=c("sceneId"="sceneId")) %>% 
  filter(name.x!=name.y) %>% 
  left_join(scenes %>% select(sceneId,duration)) %>%
  group_by(name.x,name.y) %>% 
  summarise(commonTime=sum(duration)) %>% 
  arrange(desc(commonTime))
# A tibble: 8,472 x 3
# Groups:   name.x [576]
   name.x             name.y             commonTime
   <chr>              <chr>                   <dbl>
 1 Daenerys Targaryen Jorah Mormont           12923
 2 Jorah Mormont      Daenerys Targaryen      12923
 3 Lord Varys         Tyrion Lannister        10764
 4 Tyrion Lannister   Lord Varys              10764
 5 Davos Seaworth     Jon Snow                10380
 6 Jon Snow           Davos Seaworth          10380
 7 Daenerys Targaryen Missandei                9924
 8 Missandei          Daenerys Targaryen       9924
 9 Jon Snow           Tormund Giantsbane       9352
10 Tormund Giantsbane Jon Snow                 9352
# … with 8,462 more rows

Format large

Construisez une data.frame avec une ligne par personnages contenant une colone name et une colone pour chaque lieux avec les durées de présences de chaque personnages. Si un personnage n’est jamais allé dans un lieux la valeur est égale à 0.
Calculez la durée de présence par personnage et lieux avec un group_by et un summary puis servez vous de la fonction pivot_wider de la librairie tidyr pour transformer le résultat en format large et compléter les valeures manquantes avec des zéros.
library(tidyr)
duration_location_character = scenes %>% left_join(appearances) %>% 
  group_by(name,location) %>% 
  summarize(duration=sum(duration))

duration_large = duration_location_character %>% 
  pivot_wider(values_from = duration,names_from = location,values_fill = c("duration"=0))

Matrice

Construisez à partir de la data.frame précédente une matrice contenant uniquement les variables numériques. Filtrez la pour ne conservez que les lignes dont la somme est supérieur à 3600. Normalisez là pour que les sommes en lignes soient égales à 1. Donnez le nom de chaqu personnage conservé à la ligne correspondante dans la matrice avec la fonction rownames.
Uilisez les fonctions as.matrix et rowSums
X=as.matrix(duration_large[,-1])
Xs=X[rowSums(X)>60*60,]
Xns=Xs/rowSums(Xs)
rownames(Xns)=duration_large$name[rowSums(X)>60*60]

Clustering hierarchique

Utilisez la fonction dist pour calculer la distance de manhatan netre chaque ligne de la matrice précedente. Réalisez ensuite un clustering hierarchique sur cette matrice et tracer le dendogramme. Vous devriez obtenir une figure similaire a la suivante:

hc=hclust(dist(Xns,method="manhattan"))
plot(hc,main = "Clustering des personnages principaux suivant leur lieux de présences",sub ="@comeetie, 2020",xlab = "")

Exercise 4 : ggplot

Géométrie et graphique simple

Créer une table jstime contenant pour chaque épisode le temps de présence à l’écran de Jon Snow puis reproduire ce graphique :

Uilisez geom_line mais spécifiez qu’aucune aggrégation ne doit être faites avec l’option stat='identity'
library(ggplot2)
jstime = appearances %>% filter(name=="Jon Snow") %>% 
  left_join(scenes) %>% 
  group_by(episodeId) %>% 
  summarise(time=sum(duration))
ggplot(jstime) + 
  geom_line(aes(x=episodeId,y=time))+
  theme_bw()+
  xlab("épisode")+ylab("temps")+
  ggtitle("Temps de présence par épisode de John Snow")

Variations

Utilisez d’autres géométries : aire, bares. Comparez et commentez.
Uilisez geom_bar mais spécifiez qu’aucune aggrégation ne doit être faites avec l’option stat='identity' et geom_area.

Cumul

Calculez pour l’ensemble des scenes le nombre de mort cumulé et le temps passé depuis la première scène. Réalisez ensuite le graphique suivant:

Utilisez la fonction cumsum pour calculer le nombre de morts cumulé et le temps passé. Uilisez geom_line préparez un vecteur avec les temps de début de chaque saison et modifiez les axes avece les fonction scale_x_continuous et scale_y_continuous.
# nombre de morts cumulé et temps passé
deaths = scenes %>% select(nbdeath,duration,location,episodeId) %>% 
  mutate(t=cumsum(duration),tdeath=cumsum(nbdeath))

# instant de changement de saison
# ? lag
season_t = episodes %>% mutate(ld=lag(total_duration)) %>% 
  mutate(ld=if_else(is.na(ld),0,ld), td = cumsum(ld)) %>% 
  filter(episodeNum==1) %>% pull(td)

# geom_line + labels personalisés
ggplot(deaths) + geom_line(aes(x=t/3600,y=tdeath)) +
  scale_x_continuous("",expand = c(0,0),breaks = season_t/3600,
                     labels =   paste("Saison",1:8),)+
  scale_y_continuous("Nombre de morts cumulés", expand=c(0,0))+
  theme_bw()+
    theme(axis.text.x=element_text(angle=90))+
  ggtitle("Evolution du nombre de mort au cours du temps")

Scatter-plots

Construisez une data.frame contenant pour chaque épisode son titre, la saison, la durée de la scène la plus longue, le nombre de scènes et le nombre de morts. Réaliser ensuite un scater plot des variables nombres de scènes et durée de la scène la plus longue.
Uilisez geom_point et dplyr.
scenes_stats=scenes %>% left_join(episodes) %>% 
  group_by(episodeTitle,seasonNum) %>% 
  summarize(nb_scenes=n(),duration_max=max(duration),nbdeath=sum(nbdeath))

ggplot(scenes_stats,aes(x=nb_scenes,y=duration_max))+geom_point()

Scatter-plots

Enfin, utilisez la couleur et la taille des points pour encoder des informations sur les saisons et le nombre de morts et finalisez le graphique qui pourrait ressembler à cette version en paramétrant les échelles et en rajoutant quelques labels.
Uilisez geom_text et paramètrez les échelles et le thème.

labels = scenes_stats %>% filter(duration_max>400|nb_scenes>200)
ggplot(scenes_stats,aes(x=nb_scenes,y=duration_max,col=factor(seasonNum)))+
  geom_point(aes(size=nbdeath))+
  geom_text(data=labels,aes(label=episodeTitle),vjust=-0.6)+
  scale_x_continuous("Nombre de scène",limits = c(0,280))+
  scale_y_continuous("Durée de la scène la plus longue",limits = c(100,800))+
  scale_color_brewer("Saison",palette ="Spectral")+
  guides(colour = "legend", size = "legend")+
  theme_bw()

Box-plots

Réalisez une série de box-plots pour représenter les distributions des durées des scènes par épisodes.
ggplot(scenes %>% left_join(episodes))+geom_boxplot(aes(x=factor(episodeId),y=duration))

Box-plots

Finalisez la figures, vous pourrez vous inspirer du résultat suivant:

labels = scenes %>% filter(duration>400)
ggplot(scenes %>% left_join(episodes))+
  geom_boxplot(aes(x=factor(episodeId),y=duration,fill=factor(seasonNum)))+
  geom_text(data=labels ,aes(x=factor(episodeId),y=duration,label=subLocation),hjust = "right",vjust="top")+
  scale_x_discrete("N° épisode",as.character(seq(1,73, by=5)))+
  scale_fill_brewer(palette="Spectral",guide="none")+
  ylab("Durée des scènes (min)")+
  ggtitle("Répartition des durées des scènes par épisodes")+
  theme_bw()

Stacked bars

Construisez une table contenant pour chaque personnage et chaque saison le temps de présence à l’écran. Filtrez cette table pour ne conservez que les personnages qui appaissent plus d’une heure sur l’ensemble des saisons. Réordonner les niveaux du facteur name pour que les niveaux soient trier par ordre croissant de temps d’apparition.

Réalisez un bar-plot stacké de ces données identique a la figure suivante. Servez vous de color-brewer pour retrouver la palette utilisée.

screenTimePerSeasons = appearances %>% left_join(scenes) %>% 
  left_join(episodes) %>% 
  group_by(name,seasonNum) %>% 
  summarise(screenTime=sum(duration)) %>% 
  arrange(desc(screenTime)) 
screenTimeTotal = screenTimePerSeasons %>% 
  group_by(name) %>% 
  summarise(screenTimeTotal=sum(screenTime))
mainCharacters = screenTimeTotal %>% 
  filter(screenTimeTotal>60*60) %>% 
  arrange(screenTimeTotal) %>% 
  mutate(nameF=factor(name,levels = name))
data = screenTimePerSeasons %>% left_join(mainCharacters) %>% filter(!is.na(nameF))
ggplot(data)+
  geom_bar(aes(y=nameF,x=screenTime/60,fill=factor(seasonNum,level=8:1)),stat="identity")+
  scale_fill_brewer("Saison",palette = "Spectral")+theme_bw()+
  geom_text(data=mainCharacters,aes(y=nameF,x=screenTimeTotal/60+5,label=paste(round(screenTimeTotal/60),'min')),hjust = "left")+
  scale_x_continuous("Temps d'apparition (min)",breaks = seq(0,750,by=120),limits = c(0,780),expand = c(0,1))+
  ylab("")+ggtitle("Temps d'apparition cumulé par personnage et saison")

Exercise 5 : données spatiale et géo-traitement avec sf

Lecture de données géographiques

Lire les données géographiques decrivant l’univers de GoT stockés dans le repertoire data/GoTRelease sous forme de fichiers shapefile avec la libraries sf et chargez les dans des data-frames aux noms explicites, vous spécifierez lors de l’import que le système de coordonnées est (comparable avec) le système wgs-84 dont le code crs est 4326. Observez les différentes tables créées : dimensions, variables, type de géométrie, méta-données.
Utilisez la fonction st_read et l’agurment crs et les fonctions classiques des data.frames nrow, dim, summary,…
library(sf)
library(tidyr)
library(ggplot2)
locations=st_read("./data/GoTRelease/Locations.shp",crs=4326)
Reading layer `Locations' from data source `/home/comeetie/Projets/got/data/GoTRelease/Locations.shp' using driver `ESRI Shapefile'
Simple feature collection with 247 features and 5 fields
geometry type:  POINT
dimension:      XY
bbox:           xmin: 5.140883 ymin: -34.58818 xmax: 87.984 ymax: 37.21249
geographic CRS: WGS 84
lakes=st_read("./data/GoTRelease/Lakes.shp",crs=4326)
Reading layer `Lakes' from data source `/home/comeetie/Projets/got/data/GoTRelease/Lakes.shp' using driver `ESRI Shapefile'
Simple feature collection with 19 features and 3 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 8.288085 ymin: -16.38318 xmax: 85.85195 ymax: 45.10014
geographic CRS: WGS 84
conts=st_read("./data/GoTRelease/Continents.shp",crs=4326)
Reading layer `Continents' from data source `/home/comeetie/Projets/got/data/GoTRelease/Continents.shp' using driver `ESRI Shapefile'
Simple feature collection with 3 features and 2 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 0.9011003 ymin: -42.00216 xmax: 91.99359 ymax: 49.10222
geographic CRS: WGS 84
land=st_read("./data/GoTRelease/Land.shp",crs=4326)
Reading layer `Land' from data source `/home/comeetie/Projets/got/data/GoTRelease/Land.shp' using driver `ESRI Shapefile'
Simple feature collection with 2 features and 2 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 0.9011003 ymin: -42.00216 xmax: 91.99359 ymax: 49.10222
geographic CRS: WGS 84
wall=st_read("./data/GoTRelease/Wall.shp",crs=4326)
Reading layer `Wall' from data source `/home/comeetie/Projets/got/data/GoTRelease/Wall.shp' using driver `ESRI Shapefile'
Simple feature collection with 1 feature and 2 fields
geometry type:  LINESTRING
dimension:      XY
bbox:           xmin: 16.39357 ymin: 34.79226 xmax: 20.71819 ymax: 35.19238
geographic CRS: WGS 84
islands=st_read("./data/GoTRelease/Islands.shp",crs=4326)
Reading layer `Islands' from data source `/home/comeetie/Projets/got/data/GoTRelease/Islands.shp' using driver `ESRI Shapefile'
Simple feature collection with 86 features and 3 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 5.433233 ymin: -36.04357 xmax: 67.70794 ymax: 37.58262
geographic CRS: WGS 84
kingdoms=st_read("./data/GoTRelease/Political.shp",crs=4326)
Reading layer `Political' from data source `/home/comeetie/Projets/got/data/GoTRelease/Political.shp' using driver `ESRI Shapefile'
Simple feature collection with 12 features and 3 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 0.9011003 ymin: -11.40838 xmax: 26.29015 ymax: 49.10222
geographic CRS: WGS 84
landscapes=st_read("./data/GoTRelease/Landscape.shp",crs=4326)
Reading layer `Landscape' from data source `/home/comeetie/Projets/got/data/GoTRelease/Landscape.shp' using driver `ESRI Shapefile'
Simple feature collection with 37 features and 5 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 5.942558 ymin: -41.92678 xmax: 91.91442 ymax: 47.14478
geographic CRS: WGS 84
roads=st_read("./data/GoTRelease/Roads.shp",crs=4326)
Reading layer `Roads' from data source `/home/comeetie/Projets/got/data/GoTRelease/Roads.shp' using driver `ESRI Shapefile'
Simple feature collection with 21 features and 4 fields
geometry type:  LINESTRING
dimension:      XY
bbox:           xmin: 6.540938 ymin: -24.64828 xmax: 67.18459 ymax: 34.89038
geographic CRS: WGS 84
rivers=st_read("./data/GoTRelease/Rivers.shp",crs=4326)
Reading layer `Rivers' from data source `/home/comeetie/Projets/got/data/GoTRelease/Rivers.shp' using driver `ESRI Shapefile'
Simple feature collection with 74 features and 4 fields
geometry type:  MULTILINESTRING
dimension:      XY
bbox:           xmin: 6.054601 ymin: -36.23881 xmax: 78.73064 ymax: 42.82271
geographic CRS: WGS 84

Calcul de distances

Utiliser la fonction st_distance pour calculer les distances en m entre les localisations de taille supérieur à 5. Quelles villes sont les plus proches ? Les plus éloignées ?
N’oubliez pas de filtrer la tables des localisations pour ne conserver que les grosses villes. La fonction which peut être utilisée pour trouver les indices des valeurs maximale et minimale dans une matrice en la combinant avec les fonctions min/max. Enfin, les fonctions upper.tri et lower.tri permetent de ne conserver que la partie triangulaire inférieur (respectivement supérieur) d’une matrice, cela pourrait vous être utile pour trouver les deux villes les plus proches.
cities = locations %>% filter(size==5)
dists = st_distance(cities)
cities$name[which(dists==max(dists),arr.ind = TRUE)[1,]]
[1] "Qarth"        "White Harbor"
cities$name[which(dists==min(dists[upper.tri(dists)]) ,arr.ind = TRUE)[1,]]
[1] "Tolos"  "Elyria"

Jointures spatiales

Quelle famille possède le plus de chateau ? Utilisez la fonction la fonction st_join pour faire une jointure spatiale en la table location et la table kingdoms (political).
CastlesPerKingdoms= st_join(locations,kingdoms) %>% 
  filter(type=="Castle") %>% 
  st_drop_geometry() %>% 
  count(ClaimedBy)
CastlesPerKingdoms
       ClaimedBy  n
1          Arryn 12
2      Baratheon 14
3        Greyjoy  2
4      Lannister 13
5        Martell 17
6  Night's Watch  2
7          Stark 13
8      Targaryen  7
9          Tully 12
10        Tyrell 16
11     Wildlings  1
12          <NA>  5

Prédicat géométrique binaires

Quelle famille possède le plus de chateau ? Cette fois si utilisez la fonction la fonction st_covers .
La longueur des vecteur retournés pour chaque polygones est utile. Vous pouvez appliquer une fonction à chaque éléments d’une liste avec sapply et lapply
castles_cover = st_covers(kingdoms,locations %>% filter(type=="Castle"))
kingdoms$nbcastles = sapply(castles_cover,length)
kingdoms %>% arrange(desc(nbcastles))
Simple feature collection with 12 features and 4 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 0.9011003 ymin: -11.40838 xmax: 26.29015 ymax: 49.10222
geographic CRS: WGS 84
First 10 features:
   id             name     ClaimedBy nbcastles                       geometry
1   7            Dorne       Martell        17 MULTIPOLYGON (((10.07742 -9...
2  12        The Reach        Tyrell        16 MULTIPOLYGON (((7.605566 -2...
3   8       Stormlands     Baratheon        14 MULTIPOLYGON (((17.98872 -4...
4   5        The North         Stark        13 MULTIPOLYGON (((10.6842 31....
5  10  The Westerlands     Lannister        13 MULTIPOLYGON (((11.96501 11...
6   2       Riverlands         Tully        12 MULTIPOLYGON (((17.46128 4....
7   9         The Vale         Arryn        12 MULTIPOLYGON (((19.00333 18...
8  11       Crownsland     Targaryen         7 MULTIPOLYGON (((24.62625 7....
9   4      Bran's Gift Night's Watch         2 MULTIPOLYGON (((15.17952 34...
10  6 The Iron Islands       Greyjoy         2 MULTIPOLYGON (((8.194052 13...

Intersections, aggrégations

Quelle famille possède la plus grande superficie de forêts ?
La fonction st_intersection devrait vous faciliter la tâche mais n’oubliez pas non plus la fonction st_area pour calculer les aires ni les fonctions de base de dplyr.
st_intersection(kingdoms,landscapes) %>% mutate(area=st_area(geometry)) %>% count(ClaimedBy,wt=area) %>% st_drop_geometry() %>% arrange(desc(n))
       ClaimedBy                  n
1          Stark 1.304682e+12 [m^2]
2      Wildlings 6.000348e+11 [m^2]
3          Arryn 4.988246e+11 [m^2]
4        Martell 4.640324e+11 [m^2]
5      Lannister 3.983993e+11 [m^2]
6      Baratheon 3.724850e+11 [m^2]
7          Tully 1.388066e+11 [m^2]
8         Tyrell 7.842595e+10 [m^2]
9  Night's Watch 4.558375e+10 [m^2]
10     Targaryen 2.750644e+10 [m^2]

Le boss final

Quelle distance ont parcouru les principaux personnage de la série (“Jon Snow”, “Tyrion Lannister”,“Daenerys Targaryen”,“Sansa Stark”,“Cersei Lannister”,“Arya Stark”) ? La table spatiale data/GoTRelease/ScenesLocations.shp vous donnera les localisations des lieux référencés dans la table scène et vous utiliserz la fonction lag de dplyr.
Utilisez la fonction lag après un group_by par personnage pour construire une colone contenant la localisation précédente du personnage. Faire une double jointure avec la table scenes_locations et utilisez la fonction st_distance en utilisant l’option by_element pour calculer la distance parcouru entre deux scènes et enfin résumez le tout grâce à une somme.
main_char= c("Jon Snow", "Tyrion Lannister","Daenerys Targaryen","Sansa Stark","Cersei Lannister","Arya Stark")
scenes_locations=st_read("./data/GoTRelease/ScenesLocations.shp",crs=4326)
Reading layer `ScenesLocations' from data source `/home/comeetie/Projets/got/data/GoTRelease/ScenesLocations.shp' using driver `ESRI Shapefile'
Simple feature collection with 26 features and 2 fields
geometry type:  POINT
dimension:      XY
bbox:           xmin: 4.143957 ymin: -34.58818 xmax: 87.984 ymax: 38.61921
geographic CRS: WGS 84
distance_characters = scenes %>% left_join(appearances) %>% 
  filter(name %in% main_char) %>%
  group_by(name) %>% 
  mutate(previous_location=lag(location)) %>%
  filter(location!=previous_location) %>%
  left_join(scenes_locations)%>%
  left_join(scenes_locations,by=c("previous_location"="location")) %>% 
  mutate(dist=st_distance(geometry.x,geometry.y,by_element = TRUE)) %>% 
  summarise(total_dist=sum(as.numeric(dist),na.rm=TRUE)/1000)

distance_characters 
# A tibble: 6 x 2
  name               total_dist
  <chr>                   <dbl>
1 Arya Stark             12728.
2 Cersei Lannister        4709.
3 Daenerys Targaryen     61185.
4 Jon Snow               39059.
5 Sansa Stark            11569.
6 Tyrion Lannister       28375.

Une autre voie

Quelle distance ont parcouru les principaux personnage de la série (“Jon Snow”, “Tyrion Lannister”,“Daenerys Targaryen”,“Sansa Stark”,“Cersei Lannister”,“Arya Stark”) ? La table spatiale data/GoTRelease/ScenesLocations.shp vous donnera les localisations des lieux référencés dans la table scène. Vous n’utiliserez pas la fonction lag de dplyr.
Construisez une table spatiale contenant pour chauqe personnage ses scènes et leurs localisation sous frome de points. Groupez par personnage et utilisez la fonction summary pour groupez ces points dans muntipoint qui conservera l’ordre (l’option do_union devrait vous intéresser). Convertissez ces points en ligne à l’aide de la fonction st_cast et calculer la longueur de ces lignes.
distance_characters = scenes %>% left_join(appearances) %>% 
  filter(name %in% main_char) %>% 
  left_join(scenes_locations) %>% 
  st_as_sf() %>%
  group_by(name) %>% summarise(do_union=FALSE) %>% sf::st_cast("LINESTRING") %>% mutate(dist=as.numeric(st_length(geometry))/1000)

distance_characters 
Simple feature collection with 6 features and 2 fields
geometry type:  LINESTRING
dimension:      XY
bbox:           xmin: 4.143957 ymin: -34.58818 xmax: 87.984 ymax: 38.61921
geographic CRS: WGS 84
# A tibble: 6 x 3
  name                                                                     geometry   dist
* <chr>                                                            <LINESTRING [°]>  <dbl>
1 Arya Stark     (14.89991 25.02354, 14.89991 25.02354, 14.89991 25.02354, 14.8999… 12728.
2 Cersei Lannis… (21.22862 5.640294, 14.89991 25.02354, 14.89991 25.02354, 14.8999…  4709.
3 Daenerys Targ… (31.12798 4.632835, 31.12798 4.632835, 31.12798 4.632835, 31.1279… 61185.
4 Jon Snow       (14.89991 25.02354, 14.89991 25.02354, 14.89991 25.02354, 14.8999… 39059.
5 Sansa Stark    (14.89991 25.02354, 14.89991 25.02354, 14.89991 25.02354, 14.8999… 11569.
6 Tyrion Lannis… (14.89991 25.02354, 14.89991 25.02354, 14.89991 25.02354, 14.8999… 28375.

Exercise 6 : cartographie avec les packages sf et ggplot

Fond de carte

Réalisez un fond de carte de l’univers GoT avec les lacs, rivières et forêts ainsi que les noms des principales villes. Vous pourrez vous servir de geom_sf et de geom_sf_text et vous inspirer de cette carte:
Empilez les couches, regardez comment modifier un theme ggplot ainsi que les options du système de coordonées du graphique avec coord_sf.

colforest="#c0d7c2"
colriver="#7ec9dc"
colriver="#87cdde"
colland="ivory"
borderland = "ivory3"  
ggplot()+geom_sf(data=land,fill=colland,col=borderland,size=0.1)+
  geom_sf(data=islands,fill=colland,col="ivory3")+
  geom_sf(data=landscapes %>% filter(type=="forest"),fill=colforest,col=colforest)+
  geom_sf(data=rivers,col=colriver)+
  geom_sf(data=lakes,col=colriver,fill=colriver)+
  geom_sf(data=wall,col="black",size=1)+
  geom_sf_text(data= locations %>% filter(size>4,name!='Tolos'),aes(label=name),size=2.5,family="Palatino", fontface="italic")+
  theme_minimal()+coord_sf(expand = 0,ndiscr = 0)+
  theme(panel.background = element_rect(fill = colriver,color=NA)) +
  labs(title = "GoT",caption = "Etiennne Côme, 2020",x="",y="")

Symboles proportionels

Construisez une data.frame contenant pour chaque lieux le tempsd eprésence à l’écran de “Tyrion Lannister”. Chargez les données spatiales data//GoTRelease/ScenesLocations.shp et joindre celle-ci avec la table précéndente. Enfin réalisez une carte avec des symboles proportionels pour visualisez ces données.
FALSE Reading layer `ScenesLocations' from data source `/home/comeetie/Projets/got/data/GoTRelease/ScenesLocations.shp' using driver `ESRI Shapefile'
FALSE Simple feature collection with 26 features and 2 fields
FALSE geometry type:  POINT
FALSE dimension:      XY
FALSE bbox:           xmin: 4.143957 ymin: -34.58818 xmax: 87.984 ymax: 38.61921
FALSE geographic CRS: WGS 84

Symboles proportionels et facettes (1)

Préparation des fond de carte simplifiés : Créez une data.frame spatiale backgound en utilisant la fonction st_as_sf contenant une colonne name avec les noms des 6 personnages principaux de la série “Jon Snow”, “Tyrion Lannister”,“Daenerys Targaryen”,“Sansa Stark”,“Cersei Lannister”,“Arya Stark” et une colonne geometry identique pour tous les personnages contenant l’union de tout les polygones de terre et d’île.
Utilisez st_geometry pour extraire les géométrie uniquement et st_union plusieurs fois pour combiner les polygones en un unique multi-polygone. Enfin, utilisez st_as_sf pour convertir une data.frame avec une colone geometrie et une colonne name en data.frame spatiale.
main_char= c("Jon Snow", "Tyrion Lannister","Daenerys Targaryen","Sansa Stark","Cersei Lannister","Arya Stark")
landpol = st_union(st_geometry(land)) 
islandpol = st_union(st_geometry(islands))
backpol=st_union(landpol,islandpol)
background = st_as_sf(data.frame(name=main_char,geometry=rep(backpol,6)))

Symboles proportionels et facettes (1)

Créez une data.frame avec pour chaque personnage principal et chaque localisation le temps de présence à l’écran. Joindre cette table à la table des lieux géo-référencés.
loc_time=appearances %>% filter(name %in% main_char) %>% left_join(scenes) %>% group_by(location,name) %>% summarize(duration=sum(duration,na.rm=TRUE)) 
loc_time_mc = scenes_locations %>% left_join(loc_time)

Symboles proportionels et facettes (2)

Faire une série de carte (une par personnage) avec les temps de présence représentés en symboles proportionel. La figure finale pourrait ressembler à cela:
Utilisez les possibilités de facet_wrap avec les deux data.frame construites précédement.

ggplot()+geom_sf(data=background,color=borderland,fill=colland)+
  geom_sf(data=loc_time_mc%>% filter(!is.na(duration)),aes(size=duration/60,color=name))+
  geom_sf_text(data=loc_time_mc%>% filter(duration>60*60),aes(label=location),color="#000000",vjust="bottom",family="Palatino", fontface="italic")+
  coord_sf(expand = 0,ndiscr = 0)+
  scale_color_discrete(guide="none")+
  scale_size_area("Durée (min) :",max_size = 12,breaks=c(30,60,120,240))+
  facet_wrap(~name)+
  theme(panel.background = element_rect(fill = colriver,color=NA),
        text = element_text(family="Palatino",face = "bold",size = 14),
        legend.key = element_rect(fill="#ffffff"),
        ) +
  labs(title = "Temps de présence des personnage principaux",caption = "@comeetie, 2020",x="",y="")


reproducibility

sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.2 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.9.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.9.0

locale:
 [1] LC_CTYPE=fr_FR.UTF-8       LC_NUMERIC=C               LC_TIME=fr_FR.UTF-8       
 [4] LC_COLLATE=fr_FR.UTF-8     LC_MONETARY=fr_FR.UTF-8    LC_MESSAGES=fr_FR.UTF-8   
 [7] LC_PAPER=fr_FR.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=fr_FR.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] sf_0.9-6      ggplot2_3.3.3 tidyr_1.1.2   dplyr_1.0.5   readr_1.4.0   knitr_1.30   

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.6         RColorBrewer_1.1-2 pillar_1.5.1       compiler_4.0.3    
 [5] unilur_0.4.0.9000  class_7.3-18       tools_4.0.3        digest_0.6.27     
 [9] evaluate_0.14      lifecycle_1.0.0    tibble_3.1.0       gtable_0.3.0      
[13] pkgconfig_2.0.3    rlang_0.4.10       cli_2.3.1          DBI_1.1.0         
[17] rstudioapi_0.13    yaml_2.2.1         xfun_0.19          e1071_1.7-4       
[21] withr_2.4.1        stringr_1.4.0      generics_0.1.0     vctrs_0.3.6       
[25] hms_0.5.3          classInt_0.4-3     grid_4.0.3         tidyselect_1.1.0  
[29] glue_1.4.2         R6_2.5.0           fansi_0.4.2        rmarkdown_2.5     
[33] farver_2.1.0       purrr_0.3.4        magrittr_2.0.1     units_0.6-7       
[37] scales_1.1.1       ellipsis_0.3.1     htmltools_0.5.0    assertthat_0.2.1  
[41] colorspace_2.0-0   labeling_0.4.2     KernSmooth_2.23-18 utf8_1.2.1        
[45] stringi_1.5.3      munsell_0.5.0      lwgeom_0.2-5       crayon_1.4.1