class: center, middle, inverse, title-slide .title[ # Connaître et manipuler les vecteurs ] .subtitle[ ## .doana-title[
] ] .author[ ### DoAna - Statistiques Réunion ] .date[ ### 2024 ] --- layout: true .my-footer[![](img/logoDoAna.png) .footer-title[Connaître et manipuler les vecteurs]] --- name: plan ## Sommaire *** .pull-left[ 1) Introduction - Les vecteurs - Propriétés communes des vecteurs 2) Les vecteurs atomiques - Création avec `c()` - Vecteurs numériques - Vecteurs textes - Vecteurs logiques 3) Utiliser les vecteurs - Les convertions de type - Tester le type - Le recyclage ou convertion de longueur - Nommer les vecteurs ] .pull-right[ 4) Les listes - Création avec `list()` - Dessiner des listes - Indexer 5) Les vecteurs augmentés - Des attributs en plus - Les facteurs - Les dates - Les jeux de données BONUS - Répéter un vecteur avec `rep()` - Indexer les vecteurs atomiques ] --- class: inverse, center, middle # Introduction ## Il est temps de rentrer dans les entrailles de R ! ??? On peut faire beaucoup de choses avec les `tibble`s, mais il y a une limite... Source (plan, exemples) : https://r4ds.had.co.nz/vectors.html --- ## Les vecteurs *** .pull-left[ ### Vecteurs "simples" - **vecteurs atomiques** : contenu homogène 6 types qui sont `logical`, `integer`, `double`, `character`, `complex`, et `raw` - **listes** ou vecteurs récursifs : contenu hétérogène - `NULL` : absence de vecteur. Se comporte comme un vecteur de longueur `0`. ### Vecteurs "augmentés" `factor`, `Date`, `data.frame` et `tibble` -> on détaille plus tard ] -- .pull-right[ ![](img/10-vecteurs_overview.png) ] ??? **Simples** : - On n'étudie pas `complex`, et `raw` car on s'en sert très peu - Les listes peuvent se contenir elles-mêmes (d'où le terme récursif) - Alors que `NA` est absence d'un élément dans un vecteur ??? **Augmentés** : on y rajoute un sens/comportement supplémentaire - variables qualitatives - variables temporelles - jeux de données **Déroulé** : vecteurs atomiques puis listes puis augmentés --- ## Les vecteurs *** <br> <br> <br> <br> > AVOIR TOUJOURS EN TETE LA NATURE DE L'OBJET QUE L'ON MANIPULE ! --- ## Propriétés communes des vecteurs *** .colonnes3[ ### Le type ``` r typeof(letters) ## [1] "character" typeof(pi) ## [1] "double" typeof(1:10 > 4) ## [1] "logical" typeof(factor(letters)) ## [1] "integer" typeof(data.frame("a", "b", 1:10)) ## [1] "list" ``` ### La classe ``` r class(letters) ## [1] "character" class(pi) ## [1] "numeric" class(1:10 > 4) ## [1] "logical" class(factor(letters)) ## [1] "factor" class(data.frame("a", "b", 1:10)) ## [1] "data.frame" ``` ### La longueur ``` r length(letters) ## [1] 26 length(pi) ## [1] 1 length(1:10 > 4) ## [1] 10 length(factor(letters)) ## [1] 26 length(data.frame("a", "b", 1:10)) ## [1] 3 ``` ] ??? Attention à ne pas alourdir de trop d'information Regarder l'environnement Exercice : nature de la sortie de ces fonctions ? --- template: plan --- class: inverse, center, middle # Les vecteurs atomiques --- ## Création avec `c()` *** Vecteur atomique = LEGO ``` r __ __ __ __ __ __ | || || || || || | |__||__||__||__||__||__| ``` -- `c()` comme Concaténer ``` r 2 # création d'1 vecteur de longueur 1 ## [1] 2 c(1, 4, 23, 8, 1, 6, 32, 101) # concaténation de 8 vecteurs de longueur 1 ## [1] 1 4 23 8 1 6 32 101 c(1, 5, 3) + c(2, 8, 1) # Rappel : comment appelle-t-on cela ? ## [1] 3 13 4 ``` --- ## Vecteurs numériques *** ### Deux types distincts .pull-left[ **Réels** : `double`, par défaut ``` r typeof(1) ## [1] "double" ``` ] .pull-right[ **Entiers** : `integer`, ajouter un `L` ``` r typeof(1L) ## [1] "integer" ``` ] -- **Remarque** : Les réels sont toujours des approximations ``` r x <- sqrt(2) ^ 2 x - 2 ## [1] 4.440892e-16 x == 2 ## [1] FALSE dplyr::near(x, 2) ## [1] TRUE ``` ??? integer : globalement ne pas s'en soucier, uniquement gestion du stockage dans l'ordinateur **exercice** : regarder le code source de `near` --- ## Vecteurs numériques *** ### Valeurs spéciales .pull-left[ - `NA` commune aux entiers et réels - `NaN`, `Inf` et `-Inf` en plus chez les réels ] .pull-right[ ``` r c(-1, 0, 1) / 0 ## [1] -Inf NaN Inf ``` ] <br> Test | `0` |`Inf`|`NA`|`NaN` ---------------|-----|-----|----|----- `is.finite()` | x | | | `is.infinite()`| | x | | `is.na()` | | | x | x `is.nan()` | | | | x ??? **exercice** : différence entre `is.finite(x)` et `!is.infinite(x)` ? --- ## Vecteurs textes *** - chaque élément : une chaîne de caractères (*string*) ``` r "zing" ## [1] "zing" c("je", "suis" , "content") ## [1] "je" "suis" "content" paste("je", "suis" , "content") # pour coller des chaînes de caractères ensemble ## [1] "je suis content" paste("je suis" , "content", sep = " super ") ## [1] "je suis super content" nchar("je suis content") # nombre de caractères dans la chaîne ## [1] 15 ``` -- <br> > Pour manipuler des strings -> package **stringr** ??? type le plus complexe aller voir la *cheat sheet* de **stringr** **exercice** : - longueur des vecteurs donnés en exemple - quelles fonctions de **readr** permettent de transformer un `character` en `logical`, `integer`, et `double` ? (transition pour la suite) --- ## Vecteurs logiques *** - 3 valeurs possibles : `TRUE`, `FALSE` et `NA` -- - Servent à **poser des questions** grâce aux comparaisons logiques -- ``` r c(TRUE, TRUE, FALSE, NA) # création à la main ## [1] TRUE TRUE FALSE NA 7 > 10 ## [1] FALSE 1:10 %% 3 == 0 # calcul le reste de la division par 3 et demande si c'est nul ## [1] FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE is.na(NA) # des fonctions renvoient une sortie logique ## [1] TRUE ``` ??? comparaisons logiques : voir tutoriel filtrer-donnees --- template: plan --- class: inverse, center, middle # Utiliser les vecteurs ??? 1. contraintes : convertir un type en un autre et conversion automatique 1. tester le type de vecteur 1. le recyclage : travail sur différentes longueurs 1. nommer 1. indexer : extraire des éléments d'un vecteur --- name: conversion ## Les conversions de type (*coercion*) *** - conversion explicite : `as.logical()`, `as.numeric()`, `as.character()`, `as.data.frame()`, `as_tibble()`, ... Rare, chercher à mettre le bon type **le plus tôt possible** - conversion implicite : un vecteur ne peut avoir qu'un seul type ! {{content}} --- template: conversion ### Exercice Quelle couleur de Lego gagne quand on assemble différentes couleurs ? --- template: conversion ### Concaténer des vecteurs de différents types ``` r typeof(c(TRUE, 1L)) ## [1] "integer" typeof(c(1L, 1.5)) ## [1] "double" typeof(c(1.5, "a")) ## [1] "character" ``` -- > logical -> integer -> double -> character ??? - vecteur atomique : homogène ! - essayer de rendre le code clair et ne pas trop compter là-dessus - le type le plus complexe gagne - pour mettre plusieurs types dans le même vecteur -> listes ! --- template: conversion ### Utilité détournée ``` r x <- sample(20, 100, replace = TRUE) y <- x > 10 sum(y) # combien sont plus grands que 10 ? ## [1] 40 mean(y) # quelle proportion est supérieure à 10 ? ## [1] 0.4 ``` ??? - le bon type à l'importation avec `col_types` - conversion automatique (ou pas) - Exercice : que donne `mean(is.na(x))` ? et `sum(!is.finite(x))` ? --- ## Tester le type *** Test | lgl | int | dbl | chr | list --------------|-----|-----|-----|-----|------ is_logical() | x | | | | is_integer() | | x | | | is_double() | | | x | | is_numeric() | | x | x | | is_character()| | | | x | is_atomic() | x | x | x | x | is_list() | | | | | x is_vector() | x | x | x | x | x -- <Br> Version scalaire : `is_scalar_atomic()`, vérifie aussi que le vecteur est de longueur 1 ??? - alternative plus spécifique à `typeof()` - on évite les fonction de R base : `is.vector()`, `is.atomic()`, ... - scalaire : exemple, tester la validité de l'argument d'une fonction - les véritables scalaires n'existent pas dans R --- ## Le recyclage ou conversion de longueur *** > Le vecteur le plus court est répété autant de fois que nécessaire pour atteindre la longueur du plus long. -- ### Exercice Additionner des vecteurs de longueurs différentes. Deviner quel sera le résultat. --- ## Le recyclage ou conversion de longueur *** > Le vecteur le plus court est répété autant de fois que nécessaire pour atteindre la longueur du plus long. ``` r sample(10) + 100 ## [1] 104 103 101 108 110 102 106 107 105 109 runif(10) > 0.5 ## [1] TRUE TRUE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE # 2 longueurs différentes 1:10 + 1:2 ## [1] 2 4 4 6 6 8 8 10 10 12 1:10 + 1:3 ## Warning in 1:10 + 1:3: la taille d'un objet plus long n'est pas multiple de la ## taille d'un objet plus court ## [1] 2 4 6 5 7 9 8 10 12 11 ``` ??? - Fonctionne sur des fonctions **vectorisées** - code concis et intelligent - mais facile de créer des problèmes involontaires avec cette règle ! --- ## Le recyclage ou conversion de longueur *** > Le vecteur le plus court est répété autant de fois que nécessaire pour atteindre la longueur du plus long. Attention quand on recherche un ensemble d'éléments dans un vecteur ! ``` r LETTERS == c("R", "S", "T", "U", "D", "I", "O") ## Warning in LETTERS == c("R", "S", "T", "U", "D", "I", "O"): la taille d'un ## objet plus long n'est pas multiple de la taille d'un objet plus court ## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE ## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE ## [25] FALSE FALSE LETTERS %in% c("R", "S", "T", "U", "D", "I", "O") ## [1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE ## [13] FALSE FALSE TRUE FALSE FALSE TRUE TRUE TRUE TRUE FALSE FALSE FALSE ## [25] FALSE FALSE ``` --- ## Nommer les vecteurs *** - pendant leur création : ``` r c(x = 1, y = 2, z = 4) ## x y z ## 1 2 4 ``` -- - après coup : ``` r set_names(1:3, c("a", "b", "c")) # ou setNames() ou names() ## a b c ## 1 2 3 ``` Les étiquettes sont considérées comme du texte. ??? - Mettre des étiquettes sur les LEGOs - fonctionnera avec les listes et les vecteurs augmentés - regarder l'aide de `set_names()` - existe aussi la fonction `names()` - regarder dans la console et environnement un vecteur avec des noms - ici : exercices sur les noms ? --- template: plan --- class: inverse, center, middle # Les listes ## ou vecteurs récursifs --- ## Création avec `list()` *** .pull-left[ - Une liste toute simple : ``` r x <- list(1, 2, 3) x ## [[1]] ## [1] 1 ## ## [[2]] ## [1] 2 ## ## [[3]] ## [1] 3 str(x) ## List of 3 ## $ : num 1 ## $ : num 2 ## $ : num 3 ``` ] -- .pull-right[ - Et avec des noms : ``` r x_named <- list(a = 1, b = 2, c = 3) x_named ## $a ## [1] 1 ## ## $b ## [1] 2 ## ## $c ## [1] 3 str(x_named) ## List of 3 ## $ a: num 1 ## $ b: num 2 ## $ c: num 3 ``` ] ??? - regarder dans l'environnement - comprendre les histoires de crochets ! --- ## Création avec `list()` *** .pull-left[ - Eléments hétérogènes : ``` r y <- list("a", 1L, 1.5, TRUE) str(y) ## List of 4 ## $ : chr "a" ## $ : int 1 ## $ : num 1.5 ## $ : logi TRUE ``` ] -- .pull-right[ - Une liste peut contenir une liste : un cran de complexité en plus par rapport aux vecteurs atomiques ``` r z <- list(list(1, 2), list(3, 4)) str(z) ## List of 2 ## $ :List of 2 ## ..$ : num 1 ## ..$ : num 2 ## $ :List of 2 ## ..$ : num 3 ## ..$ : num 4 ``` ] --- ## Dessiner des listes *** .left-column[ <br> <br> ``` r _____________ | _________ | | | * 1| | | |_________| | | _________ | | | * nom| | | |_________| | | _________ | |_| |_| ``` ] -- .right-column[ **Exercice** : Ecrivez ces listes en R ![](img/10-vecteurs_index-liste.png) **Règles du jeu** : 1. coins arrondis = listes, coins carrés = vecteurs atomiques 1. les enfants dans les parents avec un fond plus foncé 1. orientation (ligne ou colonne) sans importance ] ??? C'est bien de pouvoir se faire une représentation dans sa tête Réponses : - `list(-1)` - `-1` --- ## Dessiner des listes *** **Exercice** : plus compliqué... .tidy[![](img/10-vecteurs_listes.png)] ] ??? Réponses : ``` r x1 <- list(c(1, 2), c(3, 4)) x2 <- list(list(1, 2), list(3, 4)) x3 <- list(1, list(2, list(3))) ``` --- ## Indexer les listes *** ``` r a <- list(a = 1:3, b = "R c'est trop bien", c = pi, d = list(-1, -5)) ``` .pull-left[ - `[ ]` extrait une sous-liste. Le résultat est une liste. ``` r a[1:2] a[4] ``` On peut utiliser vecteurs entier, logiques ou texte pour cela. ] .pull-right[ - `[[ ]]` extrait d'**un seul** élément d'une liste. On perd un niveau de hiérarchie. ``` r a[[1]] a[[4]] ``` - `$` est un raccourci pour `[[" "]]`. Permet de récupérer les éléments nommés. ``` r a$a a[["a"]] ``` ] **Exercice** : Dessinez les 7 listes de cette diapo ! ??? - sortie peut-être n'importe quoi. - 2 bras : on ouvre le tiroir et on prend ce qu'il y a dedans - attention `a` : liste ET nom du tiroir Réfléchir à l'indexation d'un tibble ou data.frame : - le faire en vrai avec des [ ] et [[ ]] - quelles différences ? --- ## Indexer les listes *** <br> **Exercice** : Aller mettre les mains dans la sortie complexe de la fonction `lm()` (pour *linear model*) ! --- ## Indexer les vecteurs atomiques *** ### Extraire de l'information d'un vecteur - c'est une logique "base R", j'utilise rarement, mais ça peut être très utile -- - fonction générale pour tous les vecteurs : `[ ]` Utilisation sous la forme `x[a]` avec `a` qui peut prendre différents types -- ### Exercice En fonction de ce qui est mis dans `[]` (texte, entier ou logique) qu’est-ce qu’on pourrait obtenir ? -- - texte : indexer par les **noms** (étiquettes) - entier : indexer par la **position** - logique : poser une **question** et récupérer ce qui est vrai ??? - `select()` et `filter()` - variante : `[[ ]]` qui extrait toujours un seul élément et enlève les noms plus souvent utilisé pour les `list`s --- template: plan --- class: inverse, center, middle # Les vecteurs augmentés --- ## Des attributs en plus *** - attributs d'un objet : métadonnées qui modifient le comportement d'un vecteur noms : `names()`, dimensions : `dim()`, **classe** : `class()`... - Exemple : `print()` et `summary()` ne feront pas la même chose avec un facteur ou un vecteur texte. -- <br> **Exercice** : Deviner le type de ces 3 classes de vecteurs augmentés : - `factor` - `Date` - `data.frame` ??? - montrer le comportement de summary - version rapide on entre dans object-oriented programming - un facteur ne se comportera pas exactement comme un entier suivant la fonction dans laquelle il est utilisé, etc. --- ## Les facteurs *** - Représentent les variables qualitatives avec un nombre fixé de modalités (= valeurs observables) ``` r __ __ __ __ __ __ | 1|| 2|| 1|| 1|| 3|| 1| |__||__||__||__||__||__| # vecteur d'observation __ __ __ | 1|| 2|| 3| |__||__||__| # vecteur de modalités ``` --- ## Les facteurs *** - Attribut supplémentaire : `levels()` .pull-left[ ``` r x <- factor(c("ab", "cd", "ab"), levels = c("ab", "cd", "ef")) x ## [1] ab cd ab ## Levels: ab cd ef typeof(x) ## [1] "integer" ``` ] .pull-right[ ``` r attributes(x) ## $levels ## [1] "ab" "cd" "ef" ## ## $class ## [1] "factor" ``` ] <br> > Pour manipuler des facteurs -> package **forcats** ??? - Dans un facteur, on ne peut observer que ce qui est observable - ATTENTION par exemple indexer avec un facteur ne donne pas ce que l'on croit `unclass(x)` - cf. **forcats** *cheat sheet* --- ## Les dates *** **Date** : nombre de jours depuis le 1er janvier 1970. ``` r x <- as.Date("1971-01-01") x ## [1] "1971-01-01" unclass(x) ## [1] 365 typeof(x) ## [1] "double" attributes(x) ## $class ## [1] "Date" ``` -- <br> > Pour manipuler des dates -> package **lubridate** --- ## Les dates *** **Date-time** : nombre de secondes depuis le 1er janvier 1970. ``` r x <- lubridate::ymd_hm("1970-01-01 01:00") x ## [1] "1970-01-01 01:00:00 UTC" unclass(x) ## [1] 3600 ## attr(,"tzone") ## [1] "UTC" typeof(x) ## [1] "double" attributes(x) ## $class ## [1] "POSIXct" "POSIXt" ## ## $tzone ## [1] "UTC" ``` ??? POSIXct : "Portable Operating System Interface", calendar time - cf. **lubridate** *cheat sheet* - **Exercice** : Que renvoie `hms::hms(3600)` ? comment s'imprime-t-il dans la console ? type primitif sur lequel il est construit ? attributs ? --- ## Les jeux de données *** .pull-left[ ``` r # variables # $x $y ________________ | ___ ___ _| | | x| | y| | | | | | | | | |* | |* | | | | | | | | | |___| |___| |_ |________________| ``` > Vraie différence avec une liste : les éléments d'un jeu de données doivent être des vecteurs de la même longueur. ] --- ## Les jeux de données *** .pull-left[ ``` r # variables # $x $y ________________ | ___ ___ _| | | x| | y| | | | | | | | | |* | |* | | *| | | | | | | |___| |___| |_ |________________| ``` > Vraie différence avec une liste : les éléments d'un jeu de données doivent être des vecteurs de la même longueur. ] -- .pull-right[ ``` r tb <- tibble::tibble(x = 1:5, y = 5:1) typeof(tb) ## [1] "list" attributes(tb) ## $class ## [1] "tbl_df" "tbl" "data.frame" ## ## $row.names ## [1] 1 2 3 4 5 ## ## $names ## [1] "x" "y" ``` ] --- ## Les jeux de données *** .pull-left[ ``` r # variables # $x $y ________________ | ___ ___ _| | | x| | y| | | | | | | | | |* | |* | | *| | | | | | | |___| |___| |_ |________________| ``` > Vraie différence avec une liste : les éléments d'un jeu de données doivent être des vecteurs de la même longueur. ] .pull-right[ ``` r df <- data.frame(x = 1:5, y = 5:1) typeof(df) ## [1] "list" attributes(df) ## $names ## [1] "x" "y" ## ## $class ## [1] "data.frame" ## ## $row.names ## [1] 1 2 3 4 5 ``` ] ??? `tibble` hérite de toutes les fonctionnalités de `data.frame` **Exercice** : - Essayer de faire un jeu de données avec des colonnes de longueurs différentes - Peut-on mettre une liste dans une colonne d'un jeu de données ? --- class: inverse, bottom, center ### Et voilà pour votre premier tour dans les entrailles de R... -- -- -- ### ☺ -- -- -- [Accueil](/) --- class: inverse, center, middle # BONUS ## rep() et l'indexation des vecteurs --- ## Répéter un vecteur avec `rep()` *** ``` r tibble(x = 1:4, y = 1:2) ## Error in `tibble()`: ## ! Tibble columns must have compatible sizes. ## • Size 4: Existing data. ## • Size 2: Column `y`. ## ℹ Only values of size one are recycled. ``` .pull-left[ ``` r tibble(x = 1:4, y = rep(1:2, 2)) ``` | x| y| |--:|--:| | 1| 1| | 2| 2| | 3| 1| | 4| 2| ] .pull-right[ ``` r tibble(x = 1:4, y = rep(1:2, each = 2)) ``` | x| y| |--:|--:| | 1| 1| | 2| 1| | 3| 2| | 4| 2| ] --- ## Indexer... par des entiers (indices) *** ``` r x <- c("un", "deux", "trois", "quatre", "cinq") x[c(3, 2, 5)] # garde l'ordre choisi ## [1] "trois" "deux" "cinq" x[c(1, 1, 5, 5, 5, 2)] # la longueur correspond au nombre d'indices ## [1] "un" "un" "cinq" "cinq" "cinq" "deux" x[c(-1, -3, -5)] # enlève les éléments indiqués ## [1] "deux" "quatre" x[c(1, -1)] # on ne mélange pas ! ## Error in x[c(1, -1)]: les indices négatifs ne peuvent être mélangés qu'à des 0 x[0] # rien ## character(0) ``` ??? -on utilise très peu DANGER extraire un élément plusieurs fois de suite... - Exercice : si on prend un indice plus grand que la longeur du vecteur ? --- ## Indexer... par du logique (questions) *** ``` r x <- c(10, 3, NA, 5, 8, 1, NA) # Les valeurs non manquantes de x x[!is.na(x)] ## [1] 10 3 5 8 1 # Les valeurs paires de x (et les NA !) x[x %% 2 == 0] ## [1] 10 NA 8 NA ``` ### Exercice Comment faire pour prendre les valeurs paires sans les NA ? ??? - pochoir avec des trous pour les `TRUE` - correspond à ce que l'on faisait avec `filter()` --- ## Indexer... par du texte (noms) *** ``` r x <- c(abc = 1, def = 2, xyz = 5) x ## abc def xyz ## 1 2 5 x[c("xyz", "def")] ## xyz def ## 5 2 ``` ### Exercice Si on prend un nom qui n'existe pas ? ??? - correspond à ce que l'on ferait avec `select()` --- ## Indexer... par rien du tout *** ``` r x <- c(abc = 1, def = 2, xyz = 5) x[] ## abc def xyz ## 1 2 5 ``` Permet de récupérer tout le monde. Utilité pour les vecteurs à plus de 1 dimension (`matrix`, `data.frame`, `array`,...) .pull-left[ ``` r x <- matrix(1:10, nrow = 2) x ## [,1] [,2] [,3] [,4] [,5] ## [1,] 1 3 5 7 9 ## [2,] 2 4 6 8 10 ``` ] .pull-right[ ``` r x[, 3:4] # [ligne, colonne] ## [,1] [,2] ## [1,] 5 7 ## [2,] 6 8 ``` ] ??? on ira pas plus loin sur les matrices, mais bien de savoir les reconnaître