Package R Tidyr et ses nouvelles fonctions pivot_longer et pivot_wider

Forfait ranger inclus au cœur de l'une des bibliothèques les plus populaires du langage R - Tidyverse.
L'objectif principal du package est de présenter les données sous une forme précise.

Déjà disponible sur Habré publication dédié à ce package, mais il date de 2015. Et je veux vous parler des changements les plus récents, annoncés il y a quelques jours par son auteur, Hedley Wickham.

Package R Tidyr et ses nouvelles fonctions pivot_longer et pivot_wider

SJK: Est-ce que collect() et spread() seront obsolètes ?

Hadley Wickham: Dans une certaine mesure. Nous ne recommanderons plus l’utilisation de ces fonctions et n’y corrigerons plus les bugs, mais elles continueront d’être présentes dans le package dans leur état actuel.

Teneur

Si vous êtes intéressé par l'analyse des données, vous pourriez être intéressé par mon télégramme и Youtube canaux. La plupart du contenu est dédié au langage R.

Concept TidyData

Objectif ranger - vous aider à mettre les données sous une forme dite soignée. Les données soignées sont des données où :

  • Chaque variable est dans une colonne.
  • Chaque observation est une chaîne.
  • Chaque valeur est une cellule.

Il est beaucoup plus facile et pratique de travailler avec des données présentées de manière ordonnée lors de l'analyse.

Principales fonctions incluses dans le package Tidyr

Tidyr contient un ensemble de fonctions conçues pour transformer des tables :

  • fill() — remplir les valeurs manquantes dans une colonne avec les valeurs précédentes ;
  • separate() — divise un champ en plusieurs à l'aide d'un séparateur ;
  • unite() — effectue l'opération de combinaison de plusieurs champs en un seul, l'action inverse de la fonction separate();
  • pivot_longer() — une fonction qui convertit les données du format large au format long ;
  • pivot_wider() - une fonction qui convertit les données du format long au format large. L'opération inverse de celle effectuée par la fonction pivot_longer().
  • gather()obsolète — une fonction qui convertit les données du format large au format long ;
  • spread()obsolète - une fonction qui convertit les données du format long au format large. L'opération inverse de celle effectuée par la fonction gather().

Nouveau concept pour convertir des données du format large au format long et vice versa

Auparavant, les fonctions étaient utilisées pour ce type de transformation gather() и spread(). Au fil des années d'existence de ces fonctions, il est devenu évident que pour la plupart des utilisateurs, y compris l'auteur du package, les noms de ces fonctions et leurs arguments n'étaient pas tout à fait évidents, ce qui rendait difficile de les trouver et de comprendre laquelle de ces fonctions convertit un cadre de date du format large au format long, et vice versa.

À cet égard, dans ranger Deux nouvelles fonctions importantes ont été ajoutées, conçues pour transformer les cadres de date.

De nouvelles fonctionnalités pivot_longer() и pivot_wider() ont été inspirés par certaines des fonctionnalités du package données c, créé par John Mount et Nina Zumel.

Installation de la version la plus récente de Tidyr 0.8.3.9000

Pour installer la nouvelle version la plus récente du package ranger 0.8.3.9000, là où de nouvelles fonctionnalités sont disponibles, utilisez le code suivant.

devtools::install_github("tidyverse/tidyr")

Au moment de la rédaction, ces fonctions ne sont disponibles que dans la version dev du package sur GitHub.

Transition vers de nouvelles fonctionnalités

En fait, il n'est pas difficile de transférer d'anciens scripts pour travailler avec de nouvelles fonctions ; pour une meilleure compréhension, je prendrai un exemple de la documentation des anciennes fonctions et montrerai comment les mêmes opérations sont effectuées en utilisant les nouvelles. pivot_*() fonctions.

Convertissez le format large en format long.

Exemple de code de la documentation de la fonction de collecte

# example
library(dplyr)
stocks <- data.frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4)
)

# old
stocks_gather <- stocks %>% gather(key   = stock, 
                                   value = price, 
                                   -time)

# new
stocks_long   <- stocks %>% pivot_longer(cols      = -time, 
                                       names_to  = "stock", 
                                       values_to = "price")

Conversion du format long en format large.

Exemple de code de la documentation de la fonction spread

# old
stocks_spread <- stocks_gather %>% spread(key = stock, 
                                          value = price) 

# new 
stock_wide    <- stocks_long %>% pivot_wider(names_from  = "stock",
                                            values_from = "price")

Parce que dans les exemples ci-dessus de travail avec pivot_longer() и pivot_wider(), dans le tableau d'origine les stocks aucune colonne répertoriée dans les arguments noms_à и valeurs_à leurs noms doivent être entre guillemets.

Un tableau qui vous aidera à comprendre plus facilement comment passer au travail avec un nouveau concept ranger.

Package R Tidyr et ses nouvelles fonctions pivot_longer et pivot_wider

Remarque de l'auteur

Tout le texte ci-dessous est adaptatif, je dirais même traduction gratuite vignettes sur le site officiel de la bibliothèque Tidyverse.

Un exemple simple de conversion de données du format large au format long

pivot_longer () — allonge les ensembles de données en réduisant le nombre de colonnes et en augmentant le nombre de lignes.

Package R Tidyr et ses nouvelles fonctions pivot_longer et pivot_wider

Pour exécuter les exemples présentés dans l'article, vous devez d'abord connecter les packages nécessaires :

library(tidyr)
library(dplyr)
library(readr)

Disons que nous avons un tableau avec les résultats d'une enquête qui (entre autres choses) interrogeait les gens sur leur religion et leur revenu annuel :

#> # A tibble: 18 x 11
#>    religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k`
#>    <chr>      <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
#>  1 Agnostic      27        34        60        81        76       137
#>  2 Atheist       12        27        37        52        35        70
#>  3 Buddhist      27        21        30        34        33        58
#>  4 Catholic     418       617       732       670       638      1116
#>  5 Don’t k…      15        14        15        11        10        35
#>  6 Evangel…     575       869      1064       982       881      1486
#>  7 Hindu          1         9         7         9        11        34
#>  8 Histori…     228       244       236       238       197       223
#>  9 Jehovah…      20        27        24        24        21        30
#> 10 Jewish        19        19        25        25        30        95
#> # … with 8 more rows, and 4 more variables: `$75-100k` <dbl>,
#> #   `$100-150k` <dbl>, `>150k` <dbl>, `Don't know/refused` <dbl>

Ce tableau contient les données sur la religion des répondants en lignes et les niveaux de revenus sont répartis entre les noms de colonnes. Le nombre de répondants de chaque catégorie est stocké dans les valeurs des cellules à l'intersection de la religion et du niveau de revenu. Pour remettre le tableau dans un format soigné et correct, il suffit d'utiliser pivot_longer():

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")
#> # A tibble: 180 x 3
#>    religion income             count
#>    <chr>    <chr>              <dbl>
#>  1 Agnostic <$10k                 27
#>  2 Agnostic $10-20k               34
#>  3 Agnostic $20-30k               60
#>  4 Agnostic $30-40k               81
#>  5 Agnostic $40-50k               76
#>  6 Agnostic $50-75k              137
#>  7 Agnostic $75-100k             122
#>  8 Agnostic $100-150k            109
#>  9 Agnostic >150k                 84
#> 10 Agnostic Don't know/refused    96
#> # … with 170 more rows

Arguments de la fonction pivot_longer()

  • Premier argument cols, décrit les colonnes qui doivent être fusionnées. Dans ce cas, toutes les colonnes sauf fiable.
  • Argument noms_à donne le nom de la variable qui sera créée à partir des noms des colonnes que nous avons concaténées.
  • valeurs_à donne le nom d'une variable qui sera créée à partir des données stockées dans les valeurs des cellules des colonnes fusionnées.

spécification

Il s'agit d'une nouvelle fonctionnalité du package ranger, qui n'était auparavant pas disponible lorsque vous utilisiez des fonctions héritées.

Une spécification est un bloc de données dont chaque ligne correspond à une colonne dans le nouveau bloc de dates de sortie et à deux colonnes spéciales qui commencent par :

  • . Nom contient le nom de la colonne d'origine.
  • .valeur contient le nom de la colonne qui contiendra les valeurs des cellules.

Les colonnes restantes de la spécification reflètent la façon dont la nouvelle colonne affichera le nom des colonnes compressées de . Nom.

La spécification décrit les métadonnées stockées dans un nom de colonne, avec une ligne pour chaque colonne et une colonne pour chaque variable combinée avec le nom de colonne, ce qui peut sembler déroutant pour le moment, mais après avoir examiné quelques exemples, cela deviendra beaucoup plus clair.

Le but de la spécification est que vous pouvez récupérer, modifier et définir de nouvelles métadonnées pour la trame de données en cours de conversion.

Pour travailler avec des spécifications lors de la conversion d'un tableau d'un format large en un format long, utilisez la fonction pivot_longer_spec().

Le fonctionnement de cette fonction est qu'elle prend n'importe quelle période et génère ses métadonnées de la manière décrite ci-dessus.

À titre d'exemple, prenons l'ensemble de données who fourni avec le package. ranger. Cet ensemble de données contient des informations fournies par l'organisation internationale de la santé sur l'incidence de la tuberculose.

who
#> # A tibble: 7,240 x 60
#>    country iso2  iso3   year new_sp_m014 new_sp_m1524 new_sp_m2534
#>    <chr>   <chr> <chr> <int>       <int>        <int>        <int>
#>  1 Afghan… AF    AFG    1980          NA           NA           NA
#>  2 Afghan… AF    AFG    1981          NA           NA           NA
#>  3 Afghan… AF    AFG    1982          NA           NA           NA
#>  4 Afghan… AF    AFG    1983          NA           NA           NA
#>  5 Afghan… AF    AFG    1984          NA           NA           NA
#>  6 Afghan… AF    AFG    1985          NA           NA           NA
#>  7 Afghan… AF    AFG    1986          NA           NA           NA
#>  8 Afghan… AF    AFG    1987          NA           NA           NA
#>  9 Afghan… AF    AFG    1988          NA           NA           NA
#> 10 Afghan… AF    AFG    1989          NA           NA           NA
#> # … with 7,230 more rows, and 53 more variables

Construisons sa spécification.

spec <- who %>%
  pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")

#> # A tibble: 56 x 3
#>    .name        .value name        
#>    <chr>        <chr>  <chr>       
#>  1 new_sp_m014  count  new_sp_m014 
#>  2 new_sp_m1524 count  new_sp_m1524
#>  3 new_sp_m2534 count  new_sp_m2534
#>  4 new_sp_m3544 count  new_sp_m3544
#>  5 new_sp_m4554 count  new_sp_m4554
#>  6 new_sp_m5564 count  new_sp_m5564
#>  7 new_sp_m65   count  new_sp_m65  
#>  8 new_sp_f014  count  new_sp_f014 
#>  9 new_sp_f1524 count  new_sp_f1524
#> 10 new_sp_f2534 count  new_sp_f2534
#> # … with 46 more rows

champs Pays, iso2, iso3 sont déjà des variables. Notre tâche est d'inverser les colonnes avec nouveau_sp_m014 sur newrel_f65.

Les noms de ces colonnes stockent les informations suivantes :

  • Préfixe new_ indique que la colonne contient des données sur les nouveaux cas de tuberculose, la période actuelle contient des informations uniquement sur les nouvelles maladies, donc ce préfixe dans le contexte actuel n'a aucune signification.
  • sp/rel/sp/ep décrit une méthode pour diagnostiquer une maladie.
  • m/f le sexe du patient.
  • 014/1524/2535/3544/4554/65 tranche d’âge des patients.

Nous pouvons diviser ces colonnes en utilisant la fonction extract()en utilisant une expression régulière.

spec <- spec %>%
        extract(name, c("diagnosis", "gender", "age"), "new_?(.*)_(.)(.*)")

#> # A tibble: 56 x 5
#>    .name        .value diagnosis gender age  
#>    <chr>        <chr>  <chr>     <chr>  <chr>
#>  1 new_sp_m014  count  sp        m      014  
#>  2 new_sp_m1524 count  sp        m      1524 
#>  3 new_sp_m2534 count  sp        m      2534 
#>  4 new_sp_m3544 count  sp        m      3544 
#>  5 new_sp_m4554 count  sp        m      4554 
#>  6 new_sp_m5564 count  sp        m      5564 
#>  7 new_sp_m65   count  sp        m      65   
#>  8 new_sp_f014  count  sp        f      014  
#>  9 new_sp_f1524 count  sp        f      1524 
#> 10 new_sp_f2534 count  sp        f      2534 
#> # … with 46 more rows

Veuillez noter la colonne . Nom devrait rester inchangé puisqu'il s'agit de notre index dans les noms de colonnes de l'ensemble de données d'origine.

Sexe et âge (colonnes le sexe и âge) ont des valeurs fixes et connues, il est donc recommandé de convertir ces colonnes en facteurs :

spec <-  spec %>%
            mutate(
              gender = factor(gender, levels = c("f", "m")),
              age = factor(age, levels = unique(age), ordered = TRUE)
            ) 

Enfin, afin d'appliquer la spécification que nous avons créée au cadre de date d'origine pour qui nous devons utiliser un argument spec en fonction pivot_longer().

who %>% pivot_longer(spec = spec)

#> # A tibble: 405,440 x 8
#>    country     iso2  iso3   year diagnosis gender age   count
#>    <chr>       <chr> <chr> <int> <chr>     <fct>  <ord> <int>
#>  1 Afghanistan AF    AFG    1980 sp        m      014      NA
#>  2 Afghanistan AF    AFG    1980 sp        m      1524     NA
#>  3 Afghanistan AF    AFG    1980 sp        m      2534     NA
#>  4 Afghanistan AF    AFG    1980 sp        m      3544     NA
#>  5 Afghanistan AF    AFG    1980 sp        m      4554     NA
#>  6 Afghanistan AF    AFG    1980 sp        m      5564     NA
#>  7 Afghanistan AF    AFG    1980 sp        m      65       NA
#>  8 Afghanistan AF    AFG    1980 sp        f      014      NA
#>  9 Afghanistan AF    AFG    1980 sp        f      1524     NA
#> 10 Afghanistan AF    AFG    1980 sp        f      2534     NA
#> # … with 405,430 more rows

Tout ce que nous venons de faire peut être schématiquement représenté comme suit :

Package R Tidyr et ses nouvelles fonctions pivot_longer et pivot_wider

Spécification utilisant plusieurs valeurs (.value)

Dans l'exemple ci-dessus, la colonne de spécification .valeur ne contenait qu’une seule valeur, c’est le cas dans la plupart des cas.

Mais parfois, une situation peut survenir lorsque vous devez collecter des données à partir de colonnes avec différents types de données dans les valeurs. Utiliser une fonction héritée spread() ce serait assez difficile à faire.

L'exemple ci-dessous est tiré de vignettes au colis données.table.

Créons un dataframe de formation.

family <- tibble::tribble(
  ~family,  ~dob_child1,  ~dob_child2, ~gender_child1, ~gender_child2,
       1L, "1998-11-26", "2000-01-29",             1L,             2L,
       2L, "1996-06-22",           NA,             2L,             NA,
       3L, "2002-07-11", "2004-04-05",             2L,             2L,
       4L, "2004-10-10", "2009-08-27",             1L,             1L,
       5L, "2000-12-05", "2005-02-28",             2L,             1L,
)
family <- family %>% mutate_at(vars(starts_with("dob")), parse_date)

#> # A tibble: 5 x 5
#>   family dob_child1 dob_child2 gender_child1 gender_child2
#>    <int> <date>     <date>             <int>         <int>
#> 1      1 1998-11-26 2000-01-29             1             2
#> 2      2 1996-06-22 NA                     2            NA
#> 3      3 2002-07-11 2004-04-05             2             2
#> 4      4 2004-10-10 2009-08-27             1             1
#> 5      5 2000-12-05 2005-02-28             2             1

Le cadre de date créé contient des données sur les enfants d'une famille dans chaque ligne. Les familles peuvent avoir un ou deux enfants. Pour chaque enfant, des données sont fournies sur la date de naissance et le sexe, et les données pour chaque enfant sont dans des colonnes séparées ; notre tâche est d'amener ces données au format correct pour l'analyse.

Veuillez noter que nous disposons de deux variables avec des informations sur chaque enfant : son sexe et sa date de naissance (colonnes avec le préfixe baptême contenir la date de naissance, les colonnes avec préfixe le sexe contenir le sexe de l'enfant). Le résultat attendu est qu’ils apparaissent dans des colonnes distinctes. Nous pouvons le faire en générant une spécification dans laquelle la colonne .value aura deux significations différentes.

spec <- family %>%
  pivot_longer_spec(-family) %>%
  separate(col = name, into = c(".value", "child"))%>%
  mutate(child = parse_number(child))

#> # A tibble: 4 x 3
#>   .name         .value child
#>   <chr>         <chr>  <dbl>
#> 1 dob_child1    dob        1
#> 2 dob_child2    dob        2
#> 3 gender_child1 gender     1
#> 4 gender_child2 gender     2

Examinons donc étape par étape les actions effectuées par le code ci-dessus.

  • pivot_longer_spec(-family) — créer une spécification qui compresse toutes les colonnes existantes à l'exception de la colonne famille.
  • separate(col = name, into = c(".value", "child")) - diviser la colonne . Nom, qui contient les noms des champs sources, en utilisant le trait de soulignement et en saisissant les valeurs résultantes dans les colonnes .valeur и enfant.
  • mutate(child = parse_number(child)) — transformer les valeurs du champ enfant du texte au type de données numérique.

Nous pouvons maintenant appliquer la spécification résultante à la trame de données d'origine et amener la table à la forme souhaitée.

family %>% 
    pivot_longer(spec = spec, na.rm = T)

#> # A tibble: 9 x 4
#>   family child dob        gender
#>    <int> <dbl> <date>      <int>
#> 1      1     1 1998-11-26      1
#> 2      1     2 2000-01-29      2
#> 3      2     1 1996-06-22      2
#> 4      3     1 2002-07-11      2
#> 5      3     2 2004-04-05      2
#> 6      4     1 2004-10-10      1
#> 7      4     2 2009-08-27      1
#> 8      5     1 2000-12-05      2
#> 9      5     2 2005-02-28      1

Nous utilisons l'argumentation na.rm = TRUE, car la forme actuelle des données force la création de lignes supplémentaires pour les observations inexistantes. Parce que la famille 2 n'a qu'un seul enfant, na.rm = TRUE garantit que la famille 2 aura une ligne dans la sortie.

Conversion des cadres de date du format long au format large

pivot_wider() - est la transformation inverse, et vice versa augmente le nombre de colonnes du cadre de date en réduisant le nombre de lignes.

Package R Tidyr et ses nouvelles fonctions pivot_longer et pivot_wider

Ce type de transformation est extrêmement rarement utilisé pour mettre des données sous une forme précise. Cependant, cette technique peut être utile pour créer des tableaux croisés dynamiques utilisés dans des présentations ou pour l'intégration avec d'autres outils.

En fait les fonctions pivot_longer() и pivot_wider() sont symétriques et produisent des actions inverses les unes des autres, c'est-à-dire : df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) renverra le df original.

L'exemple le plus simple de conversion d'un tableau en format large

Pour démontrer le fonctionnement de la fonction pivot_wider() nous utiliserons l'ensemble de données rencontres_poissons, qui stocke des informations sur la manière dont différentes stations enregistrent le mouvement des poissons le long de la rivière.

#> # A tibble: 114 x 3
#>    fish  station  seen
#>    <fct> <fct>   <int>
#>  1 4842  Release     1
#>  2 4842  I80_1       1
#>  3 4842  Lisbon      1
#>  4 4842  Rstr        1
#>  5 4842  Base_TD     1
#>  6 4842  BCE         1
#>  7 4842  BCW         1
#>  8 4842  BCE2        1
#>  9 4842  BCW2        1
#> 10 4842  MAE         1
#> # … with 104 more rows

Dans la plupart des cas, ce tableau sera plus informatif et plus facile à utiliser si vous présentez les informations pour chaque station dans une colonne distincte.

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)
#> # A tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int>
#>  1 4842        1     1      1     1       1     1     1     1     1     1
#>  2 4843        1     1      1     1       1     1     1     1     1     1
#>  3 4844        1     1      1     1       1     1     1     1     1     1
#>  4 4845        1     1      1     1       1    NA    NA    NA    NA    NA
#>  5 4847        1     1      1    NA      NA    NA    NA    NA    NA    NA
#>  6 4848        1     1      1     1      NA    NA    NA    NA    NA    NA
#>  7 4849        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#>  8 4850        1     1     NA     1       1     1     1    NA    NA    NA
#>  9 4851        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#> 10 4854        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#> # … with 9 more rows, and 1 more variable: MAW <int>

Cet ensemble de données n'enregistre les informations que lorsque des poissons ont été détectés par la station, c'est-à-dire si un poisson n'a pas été enregistré par une station, ces données ne figureront pas dans le tableau. Cela signifie que la sortie sera remplie de NA.

Cependant, dans ce cas, nous savons que l'absence d'enregistrement signifie que le poisson n'a pas été vu, nous pouvons donc utiliser l'argument valeurs_remplissage en fonction pivot_wider() et remplissez ces valeurs manquantes avec des zéros :

fish_encounters %>% pivot_wider(
  names_from = station, 
  values_from = seen,
  values_fill = list(seen = 0)
)

#> # A tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int>
#>  1 4842        1     1      1     1       1     1     1     1     1     1
#>  2 4843        1     1      1     1       1     1     1     1     1     1
#>  3 4844        1     1      1     1       1     1     1     1     1     1
#>  4 4845        1     1      1     1       1     0     0     0     0     0
#>  5 4847        1     1      1     0       0     0     0     0     0     0
#>  6 4848        1     1      1     1       0     0     0     0     0     0
#>  7 4849        1     1      0     0       0     0     0     0     0     0
#>  8 4850        1     1      0     1       1     1     1     0     0     0
#>  9 4851        1     1      0     0       0     0     0     0     0     0
#> 10 4854        1     1      0     0       0     0     0     0     0     0
#> # … with 9 more rows, and 1 more variable: MAW <int>

Générer un nom de colonne à partir de plusieurs variables sources

Imaginez que nous ayons un tableau contenant une combinaison de produit, de pays et d'année. Pour générer une période de test, vous pouvez exécuter le code suivant :

df <- expand_grid(
  product = c("A", "B"), 
  country = c("AI", "EI"), 
  year = 2000:2014
) %>%
  filter((product == "A" & country == "AI") | product == "B") %>% 
  mutate(value = rnorm(nrow(.)))

#> # A tibble: 45 x 4
#>    product country  year    value
#>    <chr>   <chr>   <int>    <dbl>
#>  1 A       AI       2000 -2.05   
#>  2 A       AI       2001 -0.676  
#>  3 A       AI       2002  1.60   
#>  4 A       AI       2003 -0.353  
#>  5 A       AI       2004 -0.00530
#>  6 A       AI       2005  0.442  
#>  7 A       AI       2006 -0.610  
#>  8 A       AI       2007 -2.77   
#>  9 A       AI       2008  0.899  
#> 10 A       AI       2009 -0.106  
#> # … with 35 more rows

Notre tâche consiste à étendre le bloc de données afin qu'une colonne contienne des données pour chaque combinaison de produit et de pays. Pour ce faire, transmettez simplement l'argument noms_from un vecteur contenant les noms des champs à fusionner.

df %>% pivot_wider(names_from = c(product, country),
                 values_from = "value")

#> # A tibble: 15 x 4
#>     year     A_AI    B_AI    B_EI
#>    <int>    <dbl>   <dbl>   <dbl>
#>  1  2000 -2.05     0.607   1.20  
#>  2  2001 -0.676    1.65   -0.114 
#>  3  2002  1.60    -0.0245  0.501 
#>  4  2003 -0.353    1.30   -0.459 
#>  5  2004 -0.00530  0.921  -0.0589
#>  6  2005  0.442   -1.55    0.594 
#>  7  2006 -0.610    0.380  -1.28  
#>  8  2007 -2.77     0.830   0.637 
#>  9  2008  0.899    0.0175 -1.30  
#> 10  2009 -0.106   -0.195   1.03  
#> # … with 5 more rows

Vous pouvez également appliquer des spécifications à une fonction pivot_wider(). Mais lorsqu'il est soumis à pivot_wider() la spécification fait la conversion inverse pivot_longer(): Les colonnes spécifiées dans . Nom, en utilisant les valeurs de .valeur et d'autres colonnes.

Pour cet ensemble de données, vous pouvez générer une spécification personnalisée si vous souhaitez que chaque combinaison possible de pays et de produits ait sa propre colonne, et pas seulement celles présentes dans les données :

spec <- df %>% 
  expand(product, country, .value = "value") %>% 
  unite(".name", product, country, remove = FALSE)

#> # A tibble: 4 x 4
#>   .name product country .value
#>   <chr> <chr>   <chr>   <chr> 
#> 1 A_AI  A       AI      value 
#> 2 A_EI  A       EI      value 
#> 3 B_AI  B       AI      value 
#> 4 B_EI  B       EI      value

df %>% pivot_wider(spec = spec) %>% head()

#> # A tibble: 6 x 5
#>    year     A_AI  A_EI    B_AI    B_EI
#>   <int>    <dbl> <dbl>   <dbl>   <dbl>
#> 1  2000 -2.05       NA  0.607   1.20  
#> 2  2001 -0.676      NA  1.65   -0.114 
#> 3  2002  1.60       NA -0.0245  0.501 
#> 4  2003 -0.353      NA  1.30   -0.459 
#> 5  2004 -0.00530    NA  0.921  -0.0589
#> 6  2005  0.442      NA -1.55    0.594

Plusieurs exemples avancés de travail avec le nouveau concept Tidyr

Nettoyage des données en utilisant l'ensemble de données du recensement américain sur les revenus et les loyers comme exemple.

Ensemble de données us_rent_ Income contient des informations sur le revenu médian et le loyer pour chaque État des États-Unis pour 2017 (ensemble de données disponible dans le package recensement bien rangé).

us_rent_income
#> # A tibble: 104 x 5
#>    GEOID NAME       variable estimate   moe
#>    <chr> <chr>      <chr>       <dbl> <dbl>
#>  1 01    Alabama    income      24476   136
#>  2 01    Alabama    rent          747     3
#>  3 02    Alaska     income      32940   508
#>  4 02    Alaska     rent         1200    13
#>  5 04    Arizona    income      27517   148
#>  6 04    Arizona    rent          972     4
#>  7 05    Arkansas   income      23789   165
#>  8 05    Arkansas   rent          709     5
#>  9 06    California income      29454   109
#> 10 06    California rent         1358     3
#> # … with 94 more rows

Sous la forme sous laquelle les données sont stockées dans l'ensemble de données us_rent_ Income travailler avec eux est extrêmement gênant, nous aimerions donc créer un ensemble de données avec des colonnes : location, rent_moe, comment, revenu_moe. Il existe de nombreuses façons de créer cette spécification, mais le point principal est que nous devons générer chaque combinaison de valeurs de variables et estimation/moispuis générez le nom de la colonne.

  spec <- us_rent_income %>% 
    expand(variable, .value = c("estimate", "moe")) %>% 
    mutate(
      .name = paste0(variable, ifelse(.value == "moe", "_moe", ""))
    )

#> # A tibble: 4 x 3
#>   variable .value   .name     
#>   <chr>    <chr>    <chr>     
#> 1 income   estimate income    
#> 2 income   moe      income_moe
#> 3 rent     estimate rent      
#> 4 rent     moe      rent_moe

Fournir cette spécification pivot_wider() nous donne le résultat que nous recherchons :

us_rent_income %>% pivot_wider(spec = spec)

#> # A tibble: 52 x 6
#>    GEOID NAME                 income income_moe  rent rent_moe
#>    <chr> <chr>                 <dbl>      <dbl> <dbl>    <dbl>
#>  1 01    Alabama               24476        136   747        3
#>  2 02    Alaska                32940        508  1200       13
#>  3 04    Arizona               27517        148   972        4
#>  4 05    Arkansas              23789        165   709        5
#>  5 06    California            29454        109  1358        3
#>  6 08    Colorado              32401        109  1125        5
#>  7 09    Connecticut           35326        195  1123        5
#>  8 10    Delaware              31560        247  1076       10
#>  9 11    District of Columbia  43198        681  1424       17
#> 10 12    Florida               25952         70  1077        3
#> # … with 42 more rows

Banque mondiale

Parfois, amener un ensemble de données sous la forme souhaitée nécessite plusieurs étapes.
Base de données world_bank_pop contient des données de la Banque mondiale sur la population de chaque pays entre 2000 et 2018.

#> # A tibble: 1,056 x 20
#>    country indicator `2000` `2001` `2002` `2003`  `2004`  `2005`   `2006`
#>    <chr>   <chr>      <dbl>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
#>  1 ABW     SP.URB.T… 4.24e4 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4  4.49e+4
#>  2 ABW     SP.URB.G… 1.18e0 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2
#>  3 ABW     SP.POP.T… 9.09e4 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5  1.01e+5
#>  4 ABW     SP.POP.G… 2.06e0 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0  7.98e-1
#>  5 AFG     SP.URB.T… 4.44e6 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6  5.93e+6
#>  6 AFG     SP.URB.G… 3.91e0 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0  4.12e+0
#>  7 AFG     SP.POP.T… 2.01e7 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7  2.59e+7
#>  8 AFG     SP.POP.G… 3.49e0 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0  3.23e+0
#>  9 AGO     SP.URB.T… 8.23e6 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7  1.15e+7
#> 10 AGO     SP.URB.G… 5.44e0 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0  4.92e+0
#> # … with 1,046 more rows, and 11 more variables: `2007` <dbl>,
#> #   `2008` <dbl>, `2009` <dbl>, `2010` <dbl>, `2011` <dbl>, `2012` <dbl>,
#> #   `2013` <dbl>, `2014` <dbl>, `2015` <dbl>, `2016` <dbl>, `2017` <dbl>

Notre objectif est de créer un ensemble de données soigné avec chaque variable dans sa propre colonne. On ne sait pas exactement quelles étapes sont nécessaires, mais nous allons commencer par le problème le plus évident : l'année est répartie sur plusieurs colonnes.

Pour résoudre ce problème, vous devez utiliser la fonction pivot_longer().

pop2 <- world_bank_pop %>% 
  pivot_longer(`2000`:`2017`, names_to = "year")

#> # A tibble: 19,008 x 4
#>    country indicator   year  value
#>    <chr>   <chr>       <chr> <dbl>
#>  1 ABW     SP.URB.TOTL 2000  42444
#>  2 ABW     SP.URB.TOTL 2001  43048
#>  3 ABW     SP.URB.TOTL 2002  43670
#>  4 ABW     SP.URB.TOTL 2003  44246
#>  5 ABW     SP.URB.TOTL 2004  44669
#>  6 ABW     SP.URB.TOTL 2005  44889
#>  7 ABW     SP.URB.TOTL 2006  44881
#>  8 ABW     SP.URB.TOTL 2007  44686
#>  9 ABW     SP.URB.TOTL 2008  44375
#> 10 ABW     SP.URB.TOTL 2009  44052
#> # … with 18,998 more rows

L'étape suivante consiste à examiner la variable indicatrice.
pop2 %>% count(indicator)

#> # A tibble: 4 x 2
#>   indicator       n
#>   <chr>       <int>
#> 1 SP.POP.GROW  4752
#> 2 SP.POP.TOTL  4752
#> 3 SP.URB.GROW  4752
#> 4 SP.URB.TOTL  4752

Où SP.POP.GROW représente la croissance démographique, SP.POP.TOTL la population totale et SP.URB. * la même chose, mais uniquement pour les zones urbaines. Divisons ces valeurs en deux variables : superficie - superficie (totale ou urbaine) et une variable contenant des données réelles (population ou croissance) :

pop3 <- pop2 %>% 
  separate(indicator, c(NA, "area", "variable"))

#> # A tibble: 19,008 x 5
#>    country area  variable year  value
#>    <chr>   <chr> <chr>    <chr> <dbl>
#>  1 ABW     URB   TOTL     2000  42444
#>  2 ABW     URB   TOTL     2001  43048
#>  3 ABW     URB   TOTL     2002  43670
#>  4 ABW     URB   TOTL     2003  44246
#>  5 ABW     URB   TOTL     2004  44669
#>  6 ABW     URB   TOTL     2005  44889
#>  7 ABW     URB   TOTL     2006  44881
#>  8 ABW     URB   TOTL     2007  44686
#>  9 ABW     URB   TOTL     2008  44375
#> 10 ABW     URB   TOTL     2009  44052
#> # … with 18,998 more rows

Il ne nous reste plus qu'à diviser la variable en deux colonnes :

pop3 %>% 
  pivot_wider(names_from = variable, values_from = value)

#> # A tibble: 9,504 x 5
#>    country area  year   TOTL    GROW
#>    <chr>   <chr> <chr> <dbl>   <dbl>
#>  1 ABW     URB   2000  42444  1.18  
#>  2 ABW     URB   2001  43048  1.41  
#>  3 ABW     URB   2002  43670  1.43  
#>  4 ABW     URB   2003  44246  1.31  
#>  5 ABW     URB   2004  44669  0.951 
#>  6 ABW     URB   2005  44889  0.491 
#>  7 ABW     URB   2006  44881 -0.0178
#>  8 ABW     URB   2007  44686 -0.435 
#>  9 ABW     URB   2008  44375 -0.698 
#> 10 ABW     URB   2009  44052 -0.731 
#> # … with 9,494 more rows

Liste de contacts

Un dernier exemple, imaginez que vous ayez une liste de contacts que vous avez copiée et collée depuis un site Web :

contacts <- tribble(
  ~field, ~value,
  "name", "Jiena McLellan",
  "company", "Toyota", 
  "name", "John Smith", 
  "company", "google", 
  "email", "[email protected]",
  "name", "Huxley Ratcliffe"
)

La tabulation de cette liste est assez difficile car il n'existe aucune variable permettant d'identifier quelles données appartiennent à quel contact. Nous pouvons résoudre ce problème en notant que les données de chaque nouveau contact commencent par « nom », nous pouvons donc créer un identifiant unique et l'incrémenter de un à chaque fois que la colonne du champ contient la valeur « nom » :

contacts <- contacts %>% 
  mutate(
    person_id = cumsum(field == "name")
  )
contacts

#> # A tibble: 6 x 3
#>   field   value            person_id
#>   <chr>   <chr>                <int>
#> 1 name    Jiena McLellan           1
#> 2 company Toyota                   1
#> 3 name    John Smith               2
#> 4 company google                   2
#> 5 email   [email protected]          2
#> 6 name    Huxley Ratcliffe         3

Maintenant que nous avons un identifiant unique pour chaque contact, nous pouvons transformer le champ et la valeur en colonnes :

contacts %>% 
  pivot_wider(names_from = field, values_from = value)

#> # A tibble: 3 x 4
#>   person_id name             company email          
#>       <int> <chr>            <chr>   <chr>          
#> 1         1 Jiena McLellan   Toyota  <NA>           
#> 2         2 John Smith       google  [email protected]
#> 3         3 Huxley Ratcliffe <NA>    <NA>

Conclusion

Mon opinion personnelle est que le nouveau concept ranger vraiment plus intuitif et nettement supérieur en termes de fonctionnalités aux fonctions existantes spread() и gather(). J'espère que cet article vous a aidé à gérer pivot_longer() и pivot_wider().

Source: habr.com

Ajouter un commentaire