-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy path03-attribute-operations.Rmd
781 lines (615 loc) · 50.2 KB
/
03-attribute-operations.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
# Opérations sur les tables attributaires {#attr}
## Prérequis {-}
- Ce chapitre nécessite l'installation et le chargement des paquets suivant :
```{r 03-attribute-operations-1, message=FALSE}
library(sf) # paquet pour les données vectorielles présenté dans le Chapitre 2
library(terra) # paquet pour les données raster présenté dans le Chapitre 2
library(dplyr) # paquet du tidyverse pour la manipulation de tableaux de données
```
- Il s'appuie également sur **spData**, qui charge des jeux de données utilisés dans les exemples :
```{r 03-attribute-operations-2, results='hide'}
library(spData) # paquet de données spatiales présenté dans le Chapitre 2
```
- Assurez-vous également d'avoir installé le paquet **tidyr**, ou le **tidyverse** dont il fait partie, si vous souhaitez exécuter des opérations restructurant des données pour la section \@ref(vec-attr-creation).
## Introduction
Les données attributaires sont des informations non spatiales associées à des données géographiques (géométrie).
Un arrêt de bus en est un exemple simple : sa position est généralement représentée par des coordonnées de latitude et de longitude (données géométriques), en plus de son nom.
L'arrêt [Elephant & Castle / New Kent Road](https://www.openstreetmap.org/relation/6610626) à Londres, par exemple, a pour coordonnées -0,098 degrés de longitude et 51,495 degrés de latitude, ce qui peut être représenté par `POINT (-0,098 51,495)` dans la représentation `sfc` décrite au chapitre \@ref(spatial-class).
Les attributs tels que le nom *attribut*\index{attribut} de élément POINT (pour utiliser la terminologie de *Simple Features*) sont le sujet de ce chapitre.
```{r, eval=FALSE, echo=FALSE}
# Objectif: trouver un arrêt de bus dans le centre de Londres
library(osmdata)
london_coords = c(-0.1, 51.5)
london_bb = c(-0.11, 51.49, -0.09, 51.51)
bb = tmaptools::bb(london_bb)
osm_data = opq(bbox = london_bb) %>%
add_osm_feature(key = "highway", value = "bus_stop") %>%
osmdata_sf()
osm_data_points = osm_data$osm_points
osm_data_points[4, ]
point_vector = round(sf::st_coordinates(osm_data_points[4, ]), 3)
point_df = data.frame(name = "London bus stop", point_vector)
point_sf = sf::st_as_sf(point_df, coords = c("X", "Y"))
```
Un autre exemple est la valeur d'altitude (attribut) pour un pixel spécifique dans les données raster.
Contrairement au modèle de données vectorielles, le modèle de données raster stocke indirectement les coordonnées de la cellule de grille, ce qui signifie que la distinction entre attribut et information spatiale est moins claire.
Pour illustrer ce point, pensez à un pixel dans la 3^e^ ligne et la 4^e^ colonne d'une matrice raster.
Son emplacement spatial est défini par son indice dans la matrice : déplacez-vous depuis l'origine de quatre cellules dans la direction x (généralement vers l'est et la droite sur les cartes) et de trois cellules dans la direction y (généralement vers le sud et le bas).
La *résolution* de la trame définit la distance pour chaque étape x et y qui est spécifiée dans l'*en-tête* du fichier.
L'en-tête est un composant essentiel des ensembles de données raster qui spécifie comment les pixels se rapportent aux coordonnées géographiques (voir également le chapitre \@ref(spatial-operations)).
Vous apprendrez ainsi à manipuler des objets géographiques en fonction d'attributs, tels que le nom des arrêts de bus, dans un jeux de données vectorielles et l'altitude des pixels dans un jeux de données raster.
Pour les données vectorielles, cela implique des techniques telles que le sous-ensemble et l'agrégation (voir les sections \@ref(vector-attribute-subsetting) et \@ref(vector-attribute-aggregation)).
Les sections \@ref(vector-attribute-joining) et \@ref(vec-attr-creation) montrent respectivement comment joindre des données à des objets d'entités simples (*simple features*) à l'aide d'un ID (identifiant) partagé et comment créer de nouvelles variables.
Chacune de ces opérations a un équivalent spatial :
L'opérateur `[` de la version de base de R, par exemple, fonctionne pour faire des sous-ensembles d'objets basé sur leur attribut que ces objets soient spatiaux ou non ; vous pouvez également joindre les attributs de deux jeux de données géographiques à l'aide de jointures spatiales.
C'est une bonne nouvelle : les compétences développées dans ce chapitre sont transférables.
Le chapitre \@ref(spatial-operations) étend les méthodes présentées ici au monde spatial.
Après une exploration en profondeur dans les différents types d'opérations sur les attributs *vectoriels*, les opérations sur les données attributaires *raster* sont abordées dans la section \@ref(manipulating-raster-objects). Elle montrera comment créer des couches raster contenant des attributs continus et catégoriels et comment extraire les valeurs des cellules d'une ou de plusieurs couches (sous-ensemble raster).
La section \@ref(summarizing-raster-objects) fournit une vue d'ensemble des calculs matricielles "globaux" qui peuvent être utilisées pour résumer des jeux de données raster entiers.
## Manipulations des attributs de données vectorielles.
Les jeux de données géographiques vectorielles sont bien supportés dans R grâce à la classe `sf`, qui étend la classe `data.frame` de R.
Comme les tableaux de données, les objets `sf` ont une colonne par variable attributaire (comme le 'nom') et une ligne par observation ou *entité* (par exemple, par station de bus).
Les objets `sf` diffèrent des cadres de données de base parce qu'ils ont une colonne `geometry` de la classe `sfc` qui peut contenir une gamme d'entités géographiques (points simples et 'multi', lignes et polygones) par ligne.
Ceci a été décrit dans le chapitre \@ref(spatial-class), qui a démontré comment les *méthodes génériques* telles que `plot()` et `summary()` fonctionnent avec les objets `sf`.
**sf** fournit également des méthodes génériques permettant aux objets `sf` de se comporter comme des tableaux de données ordinaires, comme le montre l'impression des méthodes de la classe :
```{r 03-attribute-operations-3, eval=FALSE}
methods(class = "sf") # liste des 12 premières méthodes possibles avec la class sf
```
```{r 03-attribute-operations-4}
#> [1] aggregate cbind coerce
#> [4] initialize merge plot
#> [7] print rbind [
#> [10] [[<- $<- show
```
```{r 03-attribute-operations-5, eval=FALSE, echo=FALSE}
# Une autre manière de lister les méthodes associées à la classe sf:
attributes(methods(class = "sf"))$info |>
dplyr::filter(!visible)
```
Beaucoup d'entre elles (`aggregate()`, `cbind()`, `merge()`, `rbind()` et `[`) servent à manipuler des tableaux de données.
`rbind()`, par exemple, lie deux tableaux de données, l'un "au-dessus" de l'autre.
`$<-` crée de nouvelles colonnes.
Une caractéristique essentielle des objets `sf` est qu'ils stockent des données spatiales et non spatiales de la même manière, comme des colonnes dans un `data.frame`.
```{block2 03-attribute-operations-6, type = 'rmdnote'}
La colonne géométrique des objets `sf` est typiquement appelée `geometry` ou `geom` mais n´importe quel nom peut être utilisé.
La commande suivante, par exemple, crée une colonne géométrique nommée g :
`st_sf(data.frame(n = world$name_long), g = world$geom)`
Cela permet aux géométries importées des bases de données spatiales d´avoir une variété de noms tels que `wkb_geometry` et `the_geom`.
```
Les objets `sf` peuvent également étendre les classes `tidyverse` pour les tableaux de données, `tibble` et `tbl`.
\index{tidyverse (package)}.
Ainsi, **sf** permet d'utiliser toute la puissance des capacités d'analyse de données de R sur les données géographiques, que vous utilisiez les fonctions de base de R ou du tidyverse pour l'analyse des données.
\index{tibble}
Les objets **sf** peuvent également être utilisés avec le package de traitement de données à hautes performances **data.table** bien que, comme documenté dans cette note [`Rdatatable/data.table#2273`](https://github.com/Rdatatable/ data.table/issues/2273), ce n'est pas entièrement [compatible](https://github.com/Rdatatable/data.table/issues/5352) avec les objets `sf`.
Avant d'utiliser ces capacités, il est utile de rappeler comment découvrir les propriétés de base des objets de données vectorielles.
Commençons par utiliser les fonctions de base de R pour découvrir l'ensemble de données `world` du paquet **spData**.
```{r 03-attribute-operations-7}
class(world) # c'est un objet sf et un (tidy) data.frame
dim(world) # c'est un objet de deux dimensions avec 177 lignes et 11 colonnes
```
`world` contient dix colonnes non-géographiques (et une colonne de liste de géométrie) avec presque 200 lignes représentant les pays du monde.
La fonction `st_drop_geometry()` ne conserve que les données attributaires d'un objet `sf`, c'est-à-dire qu'elle supprime sa géométrie.
```{r 03-attribute-operations-8}
world_df = st_drop_geometry(world)
class(world_df)
ncol(world_df)
```
Il peut être utile de supprimer la colonne de géométrie avant de travailler avec des données attributaires ; les processus de manipulation des données peuvent s'exécuter plus rapidement lorsqu'ils ne travaillent que sur les attributs et les colonnes de géométrie ne sont pas toujours nécessaires.
Dans la plupart des cas, cependant, il est judicieux de conserver la colonne géométrique, ce qui explique pourquoi cette colonne est "collante" (elle reste après la plupart des opérations sur les attributs, sauf si elle est spécifiquement abandonnée).
Les opérations de données non spatiales sur les objets `sf` ne modifient la géométrie d'un objet que lorsque cela est approprié (par exemple, en supprimant les frontières entre les polygones adjacents après l'agrégation).
Devenir compétent dans la manipulation des données d'attributs géographiques signifie devenir compétent dans la manipulation des tableaux de données.
Pour de nombreuses applications, le paquet du tidyverse\index{tidyverse (package)} **dplyr** offre une approche efficace pour travailler avec des tableaux de données.
La compatibilité avec le tidyverse est un avantage de **sf** par rapport à son prédécesseur **sp**, mais il y a quelques pièges à éviter (voir la vignette supplémentaire `tidyverse-pitfalls` à [geocompr.github.io](https://geocompr.github.io/geocompkg/articles/tidyverse-pitfalls.html) pour plus de détails).
### Sélection de sous-ensemble dans des attributs de données vectorielles
Les méthodes de sélection de sous-ensembles de base de R incluent l'opérateur `[` et la fonction `subset()`.
Les principales fonctions de sélection de sous-ensembles **dplyr** sont `filter()` et `slice()` pour la sélection des lignes, et `select()` pour la sélection des colonnes.
Ces deux approches préservent les composantes spatiales des données attributaires dans les objets `sf`, tandis que l'utilisation de l'opérateur `$` ou de la fonction **dplyr** `pull()` retournera une seule colonne d'attribut sous forme de vecteur et perdra les données attributaires, comme nous le verrons plus loin.
\index{attribut!subsetting}
Cette section se concentre sur la sélection de sous-ensembles de tableaux de données `sf` ; pour plus de détails sur les cas de vecteurs et de tableaux de données non géographiques, nous vous recommandons de lire respectivement la section [2.7](https://cran.r-project.org/doc/manuals/r-release/R-intro.html#Index-vectors) de An Introduction to R [@rcoreteam_introduction_2021] et le chapitre [4](https://adv-r.hadley.nz/subsetting.html) de Advanced R Programming [@wickham_advanced_2019].
L'opérateur `[` peut sélectionner à la fois les lignes et les colonnes.
Il est possible de spécifier les éléments à conserver en indiquant leur rang entre crochets, directement après le nom de l'objet de type tableau de données qui les contient.
La commande `object[i, j]` signifie 'retourner les lignes représentées par `i` et les colonnes représentées par `j`, où `i` et `j` contiennent typiquement des entiers ou des `TRUE` et `FALSE` (les index peuvent aussi être des chaînes de caractères, indiquant les noms de lignes ou de colonnes).
Par exemple, `objet[5, 1:3]` signifie 'retourner des données contenant la cinquième ligne et les colonnes 1 à 3 : le résultat devrait être un tableau de données avec seulement une ligne et trois colonnes, et une quatrième colonne de géométrie si c'est un objet `sf`.
Laisser `i` ou `j` vide retourne toutes les lignes ou colonnes, donc `world[1:5, ]` retourne les cinq premières lignes et les 11 colonnes.
Les exemples ci-dessous illustrent les sélections avec cette syntaxe de base de R.
Devinez le nombre de lignes et de colonnes dans les tableaux de données `sf` retournés par chaque commande et vérifiez les résultats sur votre propre ordinateur (cf. la fin du chapitre pour d'autres exercices) :
```{r 03-attribute-operations-9, eval=FALSE}
world[1:6, ] # sélection de lignes par position
world[, 1:3] # sélection de colonnes par position
world[1:6, 1:3] # sélection de lignes et colonnes par position
world[, c("name_long", "pop")] # sélection de colonnes par leurs noms
world[, c(T, T, F, F, F, F, F, T, T, F, F)] # sélection en utilisant un vecteur logique
world[, 888] # un index référençant une colonne non-existante
```
```{r, eval=FALSE, echo=FALSE}
# ceux-ci retournent une erreur
world[c(1, 5), c(T, T)]
world[c(1, 5), c(T, T, F, F, F, F, F, T, T, F, F, F)]
```
Une démonstration de l'intérêt de l'utilisation de vecteurs `logiques` pour la sélection est démontrée dans le morceau de code ci-dessous.
Il crée un nouvel objet, `small_countries`, contenant les nations dont la surface est inférieure à 10,000 km^2^ :
```{r 03-attribute-operations-10}
i_small = world$area_km2 < 10000
summary(i_small) # on confirme le vecteur logique
small_countries = world[i_small, ]
```
L'objet intermédiaire `i_small` (variable indicatrice des petits pays) est un vecteur logique qui peut être utilisé pour sélectionner les sept plus petits pays du `monde` en fonction de leur superficie.
Une commande plus concise, qui omet l'objet intermédiaire, génère le même résultat :
```{r 03-attribute-operations-11}
small_countries = world[world$area_km2 < 10000, ]
```
La fonction de base de R `subset()` fournit un autre moyen d'obtenir le même résultat :
```{r 03-attribute-operations-12, eval=FALSE}
small_countries = subset(world, area_km2 < 10000)
```
Les fonctions de base de R sont matures, stables et largement utilisées, ce qui en fait un choix solide, en particulier dans les contextes où la reproductibilité et la fiabilité sont essentielles.
Les fonctions **dplyr** permettent des flux de travail "ordonnés" que certaines personnes (dont les auteurs de ce livre) trouvent intuitifs et productifs pour l'analyse interactive des données, en particulier lorsqu'elles sont associées à des éditeurs de code tels que RStudio qui permettent l'[auto-complétion](https://support.rstudio.com/hc/en-us/articles/205273297-Code-Completion-in-the-RStudio-IDE) des noms de colonnes.
Les fonctions clés pour les sélection dans les tableaux de données (y compris les tableaux de données `sf`) avec les fonctions **dplyr** sont démontrées ci-dessous.
<!-- The sentence below seems to be untrue based on the benchmark below. -->
<!-- `dplyr` is also faster than base R for some operations, due to its C++\index{C++} backend. -->
<!-- Something on dbplyr? I've never seen anyone use it regularly for spatial data 'in the wild' so leaving out the bit on integration with dbs for now (RL 2021-10) -->
<!-- The main **dplyr** subsetting functions are `select()`, `slice()`, `filter()` and `pull()`. -->
```{r, echo=FALSE, eval=FALSE}
# Aim: benchmark base vs dplyr subsetting
# Could move elsewhere?
i = sample(nrow(world), size = 10)
benchmark_subset = bench::mark(
world[i, ],
world |> slice(i)
)
benchmark_subset[c("expression", "itr/sec", "mem_alloc")]
# # October 2021 on laptop with CRAN version of dplyr:
# # A tibble: 2 × 3
# expression `itr/sec` mem_alloc
# <bch:expr> <dbl> <bch:byt>
# 1 world[i, ] 1744. 5.55KB
# 2 world %>% slice(i) 671. 4.45KB
```
`select()` sélectionne les colonnes par nom ou par leur position.
Par exemple, vous pouvez sélectionner seulement deux colonnes, `name_long` et `pop`, avec la commande suivante :
```{r 03-attribute-operations-14}
world1 = dplyr::select(world, name_long, pop)
names(world1)
```
Note : comme avec la commande équivalente de base de R (`world[, c("name_long", "pop")]`), la colonne collante `geom` reste.
`select()` permet également de sélectionner une plage de colonnes à l'aide de l'opérateur `:` :
```{r 03-attribute-operations-15}
# toutes les colonnes entre name_long et pop incluses
world2 = dplyr::select(world, name_long:pop)
```
Vous pouvez supprimer des colonnes spécifiques avec l'opérateur `-` :
```{r 03-attribute-operations-16}
# toutes les colonnes sauf subregion et area_km2
world3 = dplyr::select(world, -subregion, -area_km2)
```
Sélectionner et renommer les colonnes en même temps avec la syntaxe `nouveau_nom = ancien_nom` :
```{r 03-attribute-operations-17}
world4 = dplyr::select(world, name_long, population = pop)
```
Il est intéressant de noter que la commande ci-dessus est plus concise que l'équivalent de base R, qui nécessite deux lignes de code :
```{r 03-attribute-operations-18, eval=FALSE}
world5 = world[, c("name_long", "pop")] # sélectionne les colonnes par nom
names(world5)[names(world5) == "pop"] = "population" # renommer les colonnes
```
`select()` fonctionne également avec des "fonctions d'aide" pour des opérations de sélections plus avancées, notamment `contains()`, `starts_with()` et `num_range()` (voir la page d'aide de `?select` pour plus de détails).
La plupart des verbes de **dplyr** retournent un tableau de données, mais vous pouvez extraire une seule colonne comme un vecteur avec `pull()`.
<!-- Note: I have commented out the statement below because it is not true for `sf` objects, it's a bit confusing that the behaviour differs between data frames and `sf` objects. -->
<!-- The subsetting operator in base R (see `?[`), by contrast, tries to return objects in the lowest possible dimension. -->
<!-- This means selecting a single column returns a vector in base R as demonstrated in code chunk below which returns a numeric vector representing the population of countries in the `world`: -->
Vous pouvez obtenir le même résultat via la syntaxe de base de R avec les opérateurs de sélection de listes `$` et `[[`, les trois commandes suivantes retournent le même tableau numérique :
```{r 03-attribute-operations-21, eval = FALSE}
pull(world, pop)
world$pop
world[["pop"]]
```
<!-- Commenting out the following because it's confusing and covered better in other places (RL, 2021-10) -->
<!-- To turn off this behavior, set the `drop` argument to `FALSE`, -->
```{r 03-attribute-operations-19, eval=FALSE, echo=FALSE}
# création d'un jeux de données test
d = data.frame(pop = 1:10, area = 1:10)
# produit un tableau de données en sélectionnant une seule colonne
d[, "pop", drop = FALSE] # similaire à d["pop"]
select(d, pop)
# renvoie un vecteur en sélectionnant une seule colonne
d[, "pop"]
pull(d, pop)
```
```{r 03-attribute-operations-20, echo=FALSE, eval=FALSE}
x1 = d[, "pop", drop = FALSE] # equivalent to d["pop"]
x2 = d["pop"]
identical(x1, x2)
```
`slice()` est l'équivalent pour les lignes de `select()`.
Le morceau de code suivant, par exemple, sélectionne les lignes 1 à 6 :
```{r 03-attribute-operations-22, eval=FALSE}
slice(world, 1:6)
```
`filter()` est l'équivalent pour**dplyr** de la fonction `subset()` de R de base.
Elle ne conserve que les lignes correspondant à des critères donnés, par exemple, uniquement les pays dont la superficie est inférieure à un certain seuil, ou dont l'espérance de vie moyenne est élevée, comme le montrent les exemples suivants :
```{r 03-attribute-operations-23, eval=FALSE}
world7 = filter(world ,area_km2 < 10000) # les pays avec une petite surface
world7 = filter(world, lifeExp > 82) # ceux avec une grande espérance de vie
```
L'ensemble des opérateurs de comparaison peut être utilisé dans la fonction `filter()`, comme illustré dans le tableau \@ref(tab:operators) :
```{r operators0, echo=FALSE}
if (knitr::is_html_output()){
operators = c("`==`", "`!=`", "`>`, `<`", "`>=`, `<=`", "`&`, <code>|</code>, `!`")
} else {
operators = c("==", "!=", ">, <", ">=, <=", "&, |, !")
}
```
```{r operators, echo=FALSE}
operators_exp = c("Egal à", "Non égal à", "Supérieur/inférieur à",
"Supérieur/inférieur ou égal",
"Opérateurs logiques : Et, Ou, Non")
knitr::kable(tibble(Symbole = operators, Nom = operators_exp),
caption = paste("Opérateurs de comparaison renvoyant des booléens",
"(TRUE/FALSE)."),
caption.short = "Opérateurs de comparaison renvoyant des booléens.",
booktabs = TRUE)
```
### Enchainement de commandes avec des *pipes*
La clé des flux de travail utilisant les fonctions **dplyr** est l'opérateur ['pipe'](http://r4ds.had.co.nz/pipes.html) `%>%` (ou depuis R `4.1.0` le *pipe* natif `|>`), qui tire son nom du *pipe* Unix `|` [@grolemund_r_2016].
Les *pipes* permettent un code expressif : la sortie d'une fonction précédente devient le premier argument de la fonction suivante, permettant l'*enchaînement*.
Ceci est illustré ci-dessous, dans lequel seuls les pays d'Asie sont filtrés de l'ensemble de données `world`, l'objet est ensuite sélectionné par les colonnes (`name_long` et `continent`) et les cinq premières lignes (résultat non montré).
```{r 03-attribute-operations-24}
world7 = world |>
filter(continent == "Asia") |>
dplyr::select(name_long, continent) |>
slice(1:5)
```
Les lignes de code ci-dessus montrent comment l'opérateur *pipe* permet d'écrire des commandes dans un ordre précis :
les commandes ci-dessus sont écrites de haut en bas (ligne par ligne) et de gauche à droite.
L'alternative à `%>%` est un appel de fonction imbriqué, ce qui est plus difficile à lire :
```{r 03-attribute-operations-25}
world8 = slice(
dplyr::select(
filter(world, continent == "Asia"),
name_long, continent),
1:5)
```
Une autre alternative consiste à diviser les opérations en plusieurs lignes autonomes, ce qui est recommandé lors du développement de nouveaux paquets R, une approche qui a l'avantage de sauvegarder les résultats intermédiaires avec des noms distincts qui peuvent être inspectés ultérieurement à des fins de débogage (en contrepartie cette approche présente l'inconvénient d'être verbeuse et d'encombrer l'environnement global lors d'une analyse interactive) :
```{r 03-attribute-operations-25-2}
world9_filtered = filter(world, continent == "Asia")
world9_selected = dplyr::select(world9_filtered, continent)
world9 = slice(world9_selected, 1:5)
```
Chaque approche présente des avantages et des inconvénients, dont l'importance dépend de votre style de programmation et de vos applications.
Pour l'analyse interactive des données, fait l'objet de ce chapitre, nous trouvons que les opérations enchainées par des *pipe* sont rapides et intuitives, surtout lorsqu'elles sont combinées avec les raccourcis [RStudio](https://support.rstudio.com/hc/en-us/articles/200711853-Keyboard-Shortcuts-in-the-RStudio-IDE)/[VSCode](https://github.com/REditorSupport/vscode-R/wiki/Keyboard-shortcuts) pour créer des *pipe* et utiliser [l'auto-complétion](https://support.rstudio.com/hc/en-us/articles/205273297-Code-Completion-in-the-RStudio-IDE) des noms de variables.
### Agrégation sur la base des attributs de données vectorielles
\index{attribute!aggregation}
\index{aggregation}
L'agrégation consiste à résumer les données à l'aide d'une ou plusieurs "variables de regroupement", généralement issues des colonnes du tableau de données à agréger (l'agrégation géographique est traitée au chapitre suivant).
Un exemple d'agrégation d'attributs est le calcul du nombre d'habitants par continent à partir des données par pays (une ligne par pays).
Le jeu de données `world` contient les ingrédients nécessaires : les colonnes `pop` et `continent`, respectivement la population et la variable de regroupement.
Le but est de trouver la `somme()` des populations des pays pour chaque continent, ce qui permet d'obtenir un tableau de données plus petit (l'agrégation est une forme de réduction des données et peut être une première étape utile lorsqu'on travaille avec de grands jeux de données).
Ceci peut être fait avec la fonction de base de R `aggregate()` comme suit :
```{r 03-attribute-operations-26}
world_agg1 = aggregate(pop ~ continent, FUN = sum, data = world,
na.rm = TRUE)
class(world_agg1)
```
Le résultat est un tableau de données non spatialisées comportant six lignes, une par continent, et deux colonnes indiquant le nom et la population de chaque continent (voir le tableau \@ref(tab:continents) avec les résultats pour les 3 continents les plus peuplés).
`aggregate()` est une [fonction générique](https://adv-r.hadley.nz/s3.html#s3-methods) ce qui signifie qu'elle se comporte différemment en fonction de ses entrées.
**sf** fournit la méthode `aggregate.sf()` qui est activée automatiquement lorsque `x` est un objet `sf` et qu'un argument `by` est fourni :
```{r 03-attribute-operations-27}
world_agg2 = aggregate(world["pop"], list(world$continent), FUN = sum,
na.rm = TRUE)
class(world_agg2)
nrow(world_agg2)
```
L'objet `world_agg2` résultant est un objet spatial contenant 8 entités représentant les continents du monde (et les océans).
`group_by() |> summarize()` est l'équivalent **dplyr** d' `aggregate()`: le nom de variable indiqué dans la fonction `group_by()` spécifie la variable de regroupement et les informations sur ce qui doit être résumé sont passées à la fonction `summarize()`, comme indiqué ci-dessous :
```{r 03-attribute-operations-28}
world_agg3 = world |>
group_by(continent) |>
summarize(pop = sum(pop, na.rm = TRUE))
```
Cette approche peut sembler plus complexe mais elle présente des avantages : flexibilité, lisibilité et contrôle sur les nouveaux noms de colonnes.
Cette flexibilité est illustrée dans la commande ci-dessous, qui calcule non seulement la population mais aussi la superficie et le nombre de pays de chaque continent :
```{r 03-attribute-operations-29}
world_agg4 = world |>
group_by(continent) |>
summarize(pop = sum(pop, na.rm = TRUE), `area (sqkm)` = sum(area_km2), n = n())
```
Dans le morceau de code précédent, `pop`, `area (sqkm)` et `n` sont des noms de colonnes du résultat, et `sum()` et `n()` sont les fonctions d'agrégation.
Ces fonctions d'agrégation renvoient des objets `sf` avec des lignes représentant les continents et des géométries contenant les multiples polygones représentant chaque masse terrestre et les îles associées (cela fonctionne grâce à l'opération géométrique 'union', comme expliqué dans la section \@ref(geometry-unions)).
Combinons ce que nous avons appris jusqu'à présent sur les fonctions **dplyr**, en enchaînant plusieurs commandes pour résumer les données attributaires des pays du monde entier par continent.
La commande suivante calcule la densité de population (avec `mutate()`), classe les continents selon le nombre de pays qu'ils contiennent (avec `dplyr::arrange()`), et ne conserve que les 3 continents les plus peuplés (avec `dplyr::slice_max()`), dont le résultat est présenté dans le Tableau \@ref(tab:continents)) :
```{r 03-attribute-operations-30}
world_agg5 = world |>
st_drop_geometry() |> # enlève la colonne géométrie pour un gain de temps
dplyr::select(pop, continent, area_km2) %>% # sélectionne les colonnes d’intérêt
group_by(continent) |> # regroupe par continents et synthétise:
summarize(Pop = sum(pop, na.rm = TRUE), Superficie = sum(area_km2), N = n()) |>
mutate(Densité = round(Pop / Superficie)) |> # calcule la densité de population
slice_max(Pop, n = 3) |> # ne garde que les 3 plus peuplés
arrange(desc(N)) # trie par ordre du nombre de pays
```
```{r continents, echo=FALSE}
options(scipen = 999)
knitr::kable(
world_agg5,
caption = "Les 3 continents les plus peuplés classés par densité de population ordonnés par nombre de pays.",
caption.short = "Les 3 continents les plus peuplés.",
booktabs = TRUE
)
```
```{block2 03-attribute-operations-31, type='rmdnote'}
Plus de détails sont fournis dans les pages d´aide (qui sont accessibles via `?summarize` et `vignette(package = "dplyr")` et le chapitre 5 de [R for Data Science](http://r4ds.had.co.nz/transform.html#grouped-summaries-with-summarize).
```
### Jointures attributaires de données vectorielles
La combinaison de données provenant de différentes sources est une tâche courante dans la préparation des données.
Les jointures le font en combinant des tables basées sur une variable "clé" partagée.
**dplyr** possède plusieurs fonctions de jointure, dont `left_join()` et `inner_join()` --- voir `vignette("two-table")` pour une liste complète.
Ces noms de fonctions suivent les conventions utilisées dans le langage des base de données [SQL](http://r4ds.had.co.nz/relational-data.html) [@grolemund_r_2016, Chapitre 13] ; leur utilisation pour joindre des ensembles de données non spatiales à des objets `sf` est l'objet de cette section.
Les fonctions de jointure **dplyr** fonctionnent de la même manière sur les tableau de données et les objets `sf`, la seule différence importante étant la colonne de liste `geometry`.
Le résultat des jointures de données peut être un objet `sf` ou `data.frame`.
Le type le plus courant de jointure attributaures sur des données spatiales prend un objet `sf` comme premier argument et lui ajoute des colonnes à partir d'un `data.frame` spécifié comme second argument.
\index{join}
\index{attribute!join}
Pour découvrir les jointures, nous allons combiner les données sur la production de café avec l'ensemble de données `world`.
Les données sur le café sont dans un tableau de données appelé `coffee_data` du paquet **spData** (voir `?coffee_data` pour plus de détails).
Il comporte 3 colonnes :
`name_long` nomme les principales nations productrices de café et `coffee_production_2016` et `coffee_production_2017` contiennent les valeurs estimées de la production de café en unités de sacs de 60 kg pour chaque année.
Une "jointure gauche" (*left join*), qui préserve le premier ensemble de données, fusionne `world` avec `coffee_data` :
```{r 03-attribute-operations-32, warning=FALSE}
world_coffee = left_join(world, coffee_data)
class(world_coffee)
```
Comme les données d'entrée partagent une "variable clé" (`name_long`), la jointure fonctionne sans utiliser l'argument `by` (voir `?left_join` pour plus de détails).
Le résultat est un objet `sf` identique à l'objet original `world` mais avec deux nouvelles variables (indexées comme les colonne 11 et 12) sur la production de café.
Cet objet peut être représenté sous forme de carte, comme l'illustre la figure \@ref(fig:coffeemap), générée avec la fonction `plot()` ci-dessous :
```{r coffeemap, fig.cap="Production mondiale de café (milliers de sacs de 60 kg) par pays, 2017. Source : Organisation internationale du café.", fig.scap="Production mondiale de café par pays."}
names(world_coffee)
plot(world_coffee["coffee_production_2017"])
```
Pour que la jointure fonctionne, une "variable clé" doit être fournie dans les deux ensembles de données.
Par défaut, **dplyr** utilise toutes les variables dont le nom correspond.
Dans ce cas, les deux objets `world_coffee` et `world` contenaient une variable appelée `name_long`, expliquant le message `Joining, by = "name_long"`.
Dans la majorité des cas où les noms des variables ne sont pas les mêmes, vous avez deux options :
1. Renommez la variable clé dans l'un des objets pour qu'ils correspondent.
2. Utiliser l'argument `by` pour spécifier les variables de jonction.
Cette dernière approche est présentée ci-dessous sur une version renommée de `coffee_data` :
```{r 03-attribute-operations-33, warning=FALSE}
coffee_renamed = rename(coffee_data, nm = name_long)
world_coffee2 = left_join(world, coffee_renamed, by = c(name_long = "nm"))
```
```{r 03-attribute-operations-34, eval=FALSE, echo=FALSE}
identical(world_coffee, world_coffee2)
nrow(world)
nrow(world_coffee)
```
Remarquez que la dénomination initiale est conservée, ce qui signifie que `world_coffee` et le nouvel objet `world_coffee2` sont identiques.
Une autre caractéristique de l'objet final est qu'il a le même nombre de lignes que le jeu de données initial.
Bien qu'il n'y ait que 47 lignes de données dans `coffee_data`, les 177 enregistrements sont conservés tels quels dans `world_coffee` et `world_coffee2`:
les lignes du jeu de données initial pour lesquelles aucune correspondance n'est trouvée contiennent alors des valeurs `NA` pour les nouvelles variables relatives à la production de café.
Mais alors, comment procéder pour conserver uniquement les pays dont l'identifiant est présent dans les deux tables ?
Dans ce cas, il faut recourir à une jointure interne, `ìnner join`:
```{r 03-attribute-operations-35, warning=FALSE}
world_coffee_inner = inner_join(world, coffee_data)
nrow(world_coffee_inner)
```
Notez que le résultat de `inner_join()` ne comporte que 45 lignes contre 47 dans `coffee_data`.
Qu'est-il arrivé aux lignes restantes ?
Nous pouvons identifier les lignes qui ne correspondent pas en utilisant la fonction `setdiff()` comme suit :
```{r 03-attribute-operations-36}
setdiff(coffee_data$name_long, world$name_long)
```
Le résultat montre que "Others" représente une ligne non présente dans la base de données "monde" et que le nom de la "Democratic Republic of the Congo" représente l'autre ligne :
il a été abrégé, ce qui a fait que la jointure l'a manqué.
La commande suivante utilise une fonction de correspondance de chaîne (regex) du paquet **stringr** pour confirmer ce que devrait être `Congo, Dem. Rep. of` :
```{r 03-attribute-operations-37}
(drc = stringr::str_subset(world$name_long, "Dem*.+Congo"))
```
```{r, echo=FALSE, eval=FALSE}
world$name_long[grepl(pattern = "Dem*.+Congo", world$name_long)] # base R
```
```{r 03-attribute-operations-38, eval=FALSE, echo=FALSE}
# aim: test names in coffee_data and world objects
str_subset(coffee_data$name_long, "Ivo|Congo,")
.Last.value %in% str_subset(world$name_long, "Ivo|Dem*.+Congo")
```
Pour résoudre ce problème, nous allons créer une nouvelle version de `coffee_data` et mettre à jour le nom.
En utilisant le tableau de données mis à jour, `inner_join()` renvoie un résultat avec les 46 pays producteurs de café :
```{r 03-attribute-operations-39, warning=FALSE}
coffee_data$name_long[grepl("Congo,", coffee_data$name_long)] = drc
world_coffee_match = inner_join(world, coffee_data)
nrow(world_coffee_match)
```
Il est également possible d'effectuer une jointure dans l'autre sens : en partant d'un ensemble de données non spatiales et en ajoutant des variables provenant d'un objet spatial en entités simples (`simples features`).
Ci-dessous, on commence avec l'objet `coffee_data` et on ajoute les variables du jeux de données `world`.
Contrairement aux jointures précédentes, le résultat n'est *pas* un autre objet *simple features* , mais un tableau de données sous la forme d'un tibble **tidyverse** :
Le résultat d'une jointure tend à correspondre à son premier argument :
```{r 03-attribute-operations-40, warning=FALSE}
coffee_world = left_join(coffee_data, world)
class(coffee_world)
```
```{block2 03-attribute-operations-41, type='rmdnote'}
Dans la plupart des cas, la colonne géométrique n´est utile que dans un objet `sf`.
Elle ne peut être utilisée pour créer des cartes et des opérations spatiales que si R "sait" qu´il s´agit d´un objet spatial, défini par un paquet spatial tel que **sf**.
Heureusement, les tableaux de données non spatiaux avec une colonne de liste de géométrie (comme `coffee_world`) peuvent être convertis en un objet `sf` comme suit : `st_as_sf(coffee_world)`.
```
Cette section couvre la majorité des cas d'utilisation de la jointure.
Pour plus d'informations, nous recommandons la lecture du chapitre [Données relationnelles](https://r4ds.had.co.nz/relational-data.html?q=join#relational-data) dans @grolemund_r_2016, la [vignette join](https://geocompr.github.io/geocompkg/articles/join.html) dans le paquet **geocompkg** qui accompagne ce livre, et la [documentation](https://asardaes.github.io/table.express/articles/joins.html) décrivant les jointures avec **data.table** et d'autres paquets.
Les jointures spatiales sont traitées dans le chapitre suivant (Section \@ref(spatial-joining)).
### Création d'attributs et suppression d'informations spatiales {#vec-attr-creation}
Souvent, nous souhaitons créer une nouvelle colonne à partir de colonnes déjà existantes.
Par exemple, nous voulons calculer la densité de population pour chaque pays.
Pour cela, nous devons diviser une colonne de population, ici `pop`, par une colonne de surface, ici `area_km2` avec une unité de surface en kilomètres carrés.
En utilisant les fonctions de base de R, nous pouvons écrire :
```{r 03-attribute-operations-42}
world_new = world # afin de ne pas écraser nos données
world_new$pop_dens = world_new$pop / world_new$area_km2
```
Alternativement, nous pouvons utiliser l'une des fonctions **dplyr** - `mutate()` ou `transmute()`.
`mutate()` ajoute de nouvelles colonnes à l'avant-dernière position dans l'objet `sf` (la dernière est réservée à la géométrie) :
```{r 03-attribute-operations-43, eval=FALSE}
world |>
mutate(pop_dens = pop / area_km2)
```
La différence entre `mutate()` et `transmute()` est que ce dernier supprime toutes les autres colonnes existantes (à l'exception de la colonne de géométrie collante) :
```{r 03-attribute-operations-44, eval=FALSE}
world |>
transmute(pop_dens = pop / area_km2)
```
`unite()` du paquet **tidyr** (qui fournit de nombreuses fonctions utiles pour remodeler les ensembles de données, notamment `pivot_longer()`) colle ensemble des colonnes existantes.
Par exemple, ici nous voulons combiner les colonnes `continent` et `region_un` dans une nouvelle colonne nommée `con_reg`.
De plus, nous pouvons définir un séparateur (ici : deux points `:`) qui définit comment les valeurs des colonnes d'entrée doivent être jointes, et si les colonnes originales doivent être supprimées (ici : `TRUE`) :
```{r 03-attribute-operations-45, eval=FALSE}
world_unite = world |>
tidyr::unite("con_reg", continent:region_un, sep = ":", remove = TRUE)
```
L'objet `sf` résultant a une nouvelle colonne appelée `con_reg` représentant le continent et la région de chaque pays, par exemple `South America:Americas` pour l'Argentine et les autres pays d'Amérique du Sud.
La fonction `separate()` de **tidyr** fait l'inverse de `unite()` : elle divise une colonne en plusieurs en utilisant soit une expression régulière, soit la position des caractères.
```{r 03-attribute-operations-46, eval=FALSE}
world_separate = world_unite |>
tidyr::separate(con_reg, c("continent", "region_un"), sep = ":")
```
```{r 03-attribute-operations-47, echo=FALSE, eval=FALSE}
identical(world, world_separate)
```
La fonction **dplyr** `rename()` et la fonction de base de R `setNames()` sont utiles pour renommer des colonnes.
La première remplace un ancien nom par un nouveau.
Ici, par exemple, elle renomme la longue colonne `name_long` en un simple `name` :
```{r 03-attribute-operations-48, eval=FALSE}
world |>
rename(name = name_long)
```
`setNames()` change tous les noms de colonnes en une fois, et nécessite un vecteur de caractères avec un nom correspondant pour chaque colonne.
Ceci est illustré ci-dessous et produit le même objet `world` mais avec des noms très courts :
```{r 03-attribute-operations-49, eval=FALSE, echo=FALSE}
abbreviate(names(world), minlength = 1) |> dput()
```
```{r 03-attribute-operations-50, eval=FALSE}
new_names = c("i", "n", "c", "r", "s", "t", "a", "p", "l", "gP", "geom")
world_new_names = world |>
setNames(new_names)
```
Il est important de noter que les opérations sur les données attributaires préservent la géométrie des entités simples.
Parfois, par exemple pour rendre un processus plus rapide, il peut être utile de supprimer la géométrie.
Il faut utiliser pour cela `st_drop_geometry()`, **et non** le faire manuellement avec des commandes telles que `select(world, -geom)`, comme montré ci-dessous . ^[
`st_geometry(world_st) = NULL` fonctionne également pour supprimer la géométrie de `world`, mais écrase l'objet original.
]
```{r 03-attribute-operations-51}
world_data = world |> st_drop_geometry()
class(world_data)
```
## Manipuler des objets raster
<!--jn-->
Contrairement au modèle de données vectorielles sous-tendu par les entités simples (qui représente les points, les lignes et les polygones comme des entités discrètes dans l'espace), les données matricielles représentent des surfaces continues.
Cette section présente le fonctionnement des objets raster en les créant *de bout en bout*, en s'appuyant sur la section \@ref(an-introduction-to-terra).
En raison de leur structure unique, les sélections et les autres opérations sur les jeux de données raster fonctionnent d'une manière différente, comme le montre la section \@ref(raster-subsetting).
\index{manipulation!raster}
Le code suivant recrée le jeu de données matricielles utilisé dans la section \@ref(raster-classes), dont le résultat est illustré dans la figure \@ref(fig:cont-raster).
Cela montre comment la fonction `rast()` fonctionne pour créer un exemple de données matricielles nommé `elev` (représentant les altitudes).
```{r 03-attribute-operations-52, message=FALSE, eval = FALSE}
elev = rast(nrows = 6, ncols = 6, resolution = 0.5,
xmin = -1.5, xmax = 1.5, ymin = -1.5, ymax = 1.5,
vals = 1:36)
```
Le résultat est un objet raster avec 6 lignes et 6 colonnes (spécifiées par les arguments `nrow` et `ncol`), et une étendue spatiale comprise dans les directions x et y (`xmin`, `xmax`, `ymin`, `ymax`).
L'argument `vals` définit les valeurs que chaque cellule contient : des données numériques allant de 1 à 36 dans ce cas.
Les objets raster peuvent également contenir des valeurs catégorielles de la classe des variables `logiques` ou `factor` de R.
Le code suivant crée les ensembles de données matricielles illustrés dans la figure \@ref(fig:cont-raster) :
```{r 03-attribute-operations-53, eval = FALSE}
grain_order = c("clay", "silt", "sand")
grain_char = sample(grain_order, 36, replace = TRUE)
grain_fact = factor(grain_char, levels = grain_order)
grain = rast(nrows = 6, ncols = 6, resolution = 0.5,
xmin = -1.5, xmax = 1.5, ymin = -1.5, ymax = 1.5,
vals = grain_fact)
```
```{r 03-attribute-operations-54, include = FALSE}
elev = rast(system.file("raster/elev.tif", package = "spData"))
grain = rast(system.file("raster/grain.tif", package = "spData"))
```
L'objet raster stocke la table de correspondance ou "Raster Attribute Table" (RAT) correspondante sous la forme d'une liste de tableaux de données, qui peuvent être visualisés avec `cats(grain)` (cf. `?cats()` pour plus d'informations).
Chaque élément de cette liste est une couche du raster.
Il est également possible d'utiliser la fonction `levels()` pour récupérer et ajouter de nouveaux niveaux de facteurs ou remplacer des niveaux existants
```{r 03-attribute-operations-56}
levels(grain) = data.frame(value = c(0, 1, 2), wetness = c("wet", "moist", "dry"))
levels(grain)
```
```{r cont-raster, echo = FALSE, message = FALSE, fig.asp=0.5, fig.cap = "Jeux de données raster avec des valeurs numériques (à gauche) et catégorielles (à droite).", fig.scap="Données raster avec des valeurs numériques et catégorielles.", warning=FALSE}
# knitr::include_graphics("https://user-images.githubusercontent.com/1825120/146617366-7308535b-30f6-4c87-83f7-21702c7d993b.png")
source("https://github.com/Robinlovelace/geocompr/raw/main/code/03-cont-raster-plot.R", print.eval = TRUE)
```
```{block2 coltab, type='rmdnote'}
Les objets raster catégoriels peuvent également stocker des informations sur les couleurs associées à chaque valeur à l´aide d´une table de couleurs.
La table de couleur est un tableau de données avec trois (rouge, vert, bleu) ou quatre (alpha) colonnes, où chaque ligne se rapporte à une valeur.
Les tables de couleurs dans **terra** peuvent être visualisées ou définies avec la fonction `coltab()` (voir `?coltab`).
Il est important de noter que la sauvegarde d´un objet raster avec une table de couleurs dans un fichier (par exemple, GeoTIFF) sauvegardera également les informations de couleurs
```
### Sélection sur des raster
La sélection de données raster est réalisée à l'aide de l'opérateur de base de R `[`, qui accepte une large gamme d'entrées :
\index{raster!subsetting}
- indexation ligne-colonne ;
- ID des cellules ;
- coordonnées (voir la section \@ref(spatial-raster-subsetting)) ;
- autre objet spatial (voir la section \@ref(spatial-raster-subsetting)).
Nous ne présentons ici que les deux premières options, car elles peuvent être considérées comme des opérations non spatiales.
Si nous avons besoin d'un objet spatial pour en sélectionner un autre ou si le résultat est un objet spatial, nous en parlerons comme une sélection spatiale.
Par conséquent, les deux dernières options seront présentées dans le chapitre suivant (voir la section \@ref(spatial-raster-subsetting)).
Les deux premières options de sous-ensembles sont présentées dans les commandes ci-dessous ---
toutes deux renvoient la valeur du pixel supérieur gauche dans l'objet raster `elev` (résultats non montrés) :
```{r 03-attribute-operations-58, eval = FALSE}
# ligne 1, colonne 1
elev[1, 1]
# pixel ID 1
elev[1]
```
Les sélections d'objets raster à couches multiples renverront la ou les valeurs des cellules pour chaque couche.
Par exemple, `two_layers = c(grain, elev); two_layers[1]` renvoie un tableau de données avec une ligne et deux colonnes --- une pour chaque couche.
Pour extraire toutes les valeurs ou des lignes complètes, vous pouvez également utiliser `values()`.
Les valeurs des cellules peuvent être modifiées en écrasant les valeurs existantes en conjonction avec une opération de sélection.
L'exemple de code suivant, par exemple, définit la cellule supérieure gauche de `elev` à 0 (résultats non montrés) :
```{r 03-attribute-operations-60, results='hide'}
elev[1, 1] = 0
elev[]
```
Laisser les crochets vides est une version raccourcie de `values()` pour récupérer toutes les valeurs d'un raster.
Plusieurs cellules peuvent également être modifiées de cette manière :
```{r 03-attribute-operations-61}
elev[1, c(1, 2)] = 0
```
Le remplacement des valeurs des rasters multicouches peut se faire avec une matrice comportant autant de colonnes que de couches et de lignes que de cellules remplaçables (résultats non montrés) :
```{r 03-attribute-operations-61b, eval=FALSE}
two_layers = c(grain, elev)
two_layers[1] = cbind(c(1), c(4))
two_layers[]
```
### Résumer les objets raster
**terra** contient des fonctions permettant d'extraire des statistiques descriptives\index{statistics} pour des rasters entiers.
L'impression d'un objet raster sur la console en tapant son nom renvoie les valeurs minimales et maximales d'un objet raster.
`summary()` fournit des statistiques descriptives courantes\index{statistiques} -- minimum, maximum, quartiles et nombre de `NA`s pour les matrices continues et un nombre de cellules de chaque classe pour les matrices catégorielles.
D'autres opérations de synthèse telles que l'écart-type (voir ci-dessous) ou des statistiques de synthèse personnalisées peuvent être calculées avec `global()`.
\index{raster!summarizing}
```{r 03-attribute-operations-62, eval = FALSE}
global(elev, sd)
```
```{block2 03-attribute-operations-63, type='rmdnote'}
Si vous fournissez aux fonctions `summary()` et `global()` un objet raster multi-couches, elles résumeront chaque couche séparément, comme on peut l´illustrer en exécutant : `summary(c(elev, grain))`.
```
De plus, la fonction `freq()` permet d'obtenir le tableau de fréquence des valeurs catégorielles.
Les statistiques des données raster peuvent être visualisées de différentes manières.
Des fonctions spécifiques telles que `boxplot()`, `density()`, `hist()` et `pairs()` fonctionnent également avec les objets raster, comme le montre l'histogramme créé avec la commande ci-dessous (non montré) :
```{r 03-attribute-operations-64, eval=FALSE}
hist(elev)
```
Si la fonction de visualisation souhaitée ne fonctionne pas avec les objets raster, on peut extraire les données à représenter à l'aide de `values()` (section \@ref(raster-subsetting)).
\index{valeurs!raster}
Les statistiques descriptives sur des raster font partie des opérations raster dites globales.
Ces opérations, ainsi que d'autres opérations de traitement raster typiques, font partie du traitement algébrique sur raster, qui est abordé dans le chapitre suivant (section \@ref(map-algebra)).
```{block 03-attribute-operations-65, type='rmdnote'}
Certains noms de fonctions sont réutilisés entre les paquets et rentrent en conflit(par exemple, une fonction avec le nom `extract()` existe à la fois dans les paquets **terra** et **tidyr**).
En plus de ne pas charger les paquets en se référant verbalement aux fonctions (par exemple, `tidyr::extract()`), une autre façon d'éviter les conflits de noms de fonctions est de décharger le paquet en question avec `detach()`.
La commande suivante, par exemple, décharge le paquet **terra** (ceci peut également être fait dans l'onglet *package* qui se trouve par défaut dans le panneau inférieur droit de RStudio) : `detach("package:terra", unload = TRUE, force = TRUE)`.
L'argument `force` permet de s'assurer que le paquet sera détaché même si d'autres paquets en dépendent.
Cependant, cela peut conduire à une utilisation restreinte des paquets dépendant du paquet détaché, et n'est donc pas recommandé.
```
## Exercices
```{r, echo=FALSE, results='asis'}
res = knitr::knit_child('_03-ex.Rmd', quiet = TRUE, options = list(include = FALSE, eval = FALSE))
cat(res, sep = '\n')
```