Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider.

Paket tidyr ist Teil eines der beliebtesten Bibliotheken in der Programmiersprache R - tidyverse.
Die Hauptfunktion des Pakets besteht darin, Daten in eine saubere Form zu bringen.

Auf Habr gibt es bereits Veröffentlichung einige Informationen zu diesem Paket, aber sie stammen aus dem Jahr 2015. Ich möchte jedoch über die aktuellsten Änderungen berichten, die vor einigen Tagen von seinem Autor Hadley Wickham bekannt gegeben wurden.

Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider.

SJK: Werden die Funktionen gather() und spread() als veraltet gelten?

Hadley Wickham: In gewissem Maße. Wir werden die Verwendung dieser Funktionen nicht mehr empfehlen und keine Fehler mehr beheben, aber sie werden weiterhin in der aktuellen Form im Paket vorhanden sein.

Inhalt

Wenn Sie sich für Datenanalyse interessieren, könnten Ihnen meine Telegram und youtube Kanäle. Ein großer Teil des Inhalts ist der Sprache R gewidmet.

Das Konzept der TidyData

Ziel tidyr — helfen Ihnen, Daten in die sogenannte saubere Form zu bringen. Saubere Daten sind Daten, bei denen:

  • Jede Variable in einer Spalte ist.
  • Jede Beobachtung eine Zeile ist.
  • Jeder Wert eine Zelle ist.

Mit Daten, die in tidying up formatiert sind, lässt sich bei der Analyse erheblich einfacher und bequemer arbeiten.

Die Hauptfunktionen des Pakets tidyr

tidyr enthält eine Reihe von Funktionen, die für die Transformation von Tabellen bestimmt sind:

  • fill() — füllt fehlende Werte in einer Spalte mit den vorherigen Werten auf;
  • separate() — teilt ein Feld in mehrere Felder mithilfe eines Trennzeichens;
  • unite() — führt mehrere Felder zu einem zusammen, das Gegenteil der Funktion separate();
  • pivot_longer() — eine Funktion, die Daten vom breiten Format in das lange Format umwandelt;
  • pivot_wider() — eine Funktion, die Daten vom langen Format in das breite Format umwandelt. Eine umgekehrte Operation dessen, was die Funktion pivot_longer().
  • gather()veraltet — eine Funktion, die Daten vom breiten Format in das lange Format umwandelt;
  • spread()veraltet — eine Funktion, die Daten vom langen Format in das breite Format umwandelt. Eine umgekehrte Operation dessen, was die Funktion gather().

Ein neues Konzept zur Transformation von Daten aus breitem Format in langes und umgekehrt

Früher wurden für diese Art von Transformation Funktionen verwendet gather() und spread(). Im Laufe der Jahre ist es offensichtlich geworden, dass die Namen dieser Funktionen und ihrer Argumente für die meisten Benutzer, einschließlich des Autors des Pakets, nicht sehr offensichtlich waren und Schwierigkeiten beim Finden und Verstehen der Funktionalität verursacht haben, die das DataFrame von breitem in langes Format und umgekehrt verwandelt.

In diesem Zusammenhang wurden in tidyr zwei neue, wichtige Funktionen hinzugefügt, die für die Transformation von DataFrames gedacht sind.

Neue Funktionen pivot_longer() und pivot_wider() Diese wurden inspiriert von einigen Funktionen aus dem Paket cdata, das von John Mount und Nina Zumel erstellt wurde.

Installation der aktuellsten Version tidyr 0.8.3.9000

Für die Installation der neuen, aktuellsten Version des Pakets tidyr 0.8.3.9000, in der die neuen Funktionen verfügbar sind, verwenden Sie den folgenden Code.

devtools::install_github("tidyverse/tidyr")

Zum Zeitpunkt der Erstellung dieses Artikels sind diese Funktionen nur in der Entwicklerversion des Pakets auf GitHub verfügbar.

Wechsel zu neuen Funktionen

Tatsächlich ist es nicht schwer, ältere Skripte auf die neuen Funktionen umzustellen; um ein besseres Verständnis zu erlangen, werde ich ein Beispiel aus der Dokumentation der alten Funktionen nehmen und zeigen, wie dieselben Operationen mit Hilfe der neuen pivot_*() Funktionen durchgeführt werden.

Transformation von breitem Format in langes.

Beispielcode aus der Dokumentation der Funktion gather

# 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")

Transformation von langem Format in breites.

Beispielcode aus der Dokumentation der Funktion spread

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

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

Da in den obigen Beispielen mit pivot_longer() und pivot_wider(), in der Ausgangstabelle stocks keine Spalten aufgeführt sind, die in den Argumenten names_to und values_to angegeben werden, müssen ihre Namen in Anführungszeichen angegeben werden.

Eine Tabelle, mit der Sie am einfachsten verstehen können, wie Sie zur neuen Konzeptarbeit übergehen tidyr.

Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider.

Hinweis des Autors

Der nachfolgende Text ist eine adaptive, ich würde sogar sagen freie Übersetzung der Vignetten von der offiziellen Webseite der tidyverse-Bibliothek.

Ein einfaches Beispiel zur Umwandlung von Daten aus breitem Format in langes

pivot_longer () macht Datensätze länger, verringert die Anzahl der Spalten und erhöht die Anzahl der Zeilen.

Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider.

Um die in diesem Artikel dargestellten Beispiele auszuführen, müssen zunächst die benötigten Pakete geladen werden:

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

Angenommen, wir haben eine Tabelle mit den Ergebnissen einer Umfrage, in der (unter anderem) die Menschen nach ihrer Religion und ihrem Jahreseinkommen gefragt wurden:

#> # 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>

Diese Tabelle enthält Daten über die Religion der Befragten in den Zeilen, während das Einkommensniveau in den Spaltennamen verteilt ist. Die Anzahl der Befragten aus jeder Kategorie wird in den Zellen gespeichert, die das Kreuzungspunkt von Religion und Einkommensniveau darstellen. Um die Tabelle in ein ordentliches, korrektes Format zu bringen, genügt es, pivot_longer():

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

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")
#> # Ein Tibble: 180 x 3
#>    religion income             count
#>                      
#>  1 Agnostiker   2 Agnostiker $10-20k               34
#>  3 Agnostiker $20-30k               60
#>  4 Agnostiker $30-40k               81
#>  5 Agnostiker $40-50k               76
#>  6 Agnostiker $50-75k              137
#>  7 Agnostiker $75-100k             122
#>  8 Agnostiker $100-150k            109
#>  9 Agnostiker >150k                 84
#> 10 Agnostiker Weiß nicht/hat abgelehnt    96
#> # … mit 170 weiteren Zeilen

Funktionsargumente pivot_longer()

  • Das erste Argument cols, beschreibt, welche Spalten aggregiert werden sollen. In diesem Fall alle Spalten außer time.
  • Argument names_to gibt den Namen der Variablen an, die aus den Spaltennamen, die wir aggregiert haben, erstellt wird.
  • values_to gibt den Namen der Variablen an, die aus den Werten, die in den aggregierten Spalten gespeichert sind, erstellt wird.

Spezifikationen

Das ist eine neue Funktionalität des Pakets tidyr, die zuvor beim Arbeiten mit veralteten Funktionen nicht verfügbar war.

Die Spezifikation ist ein Datenrahmen, wobei jede Zeile einer Spalte im neuen Ausgabedatenrahmen entspricht, und zwei speziellen Spalten, die mit beginnen.:

  • .name enthält den ursprünglichen Spaltennamen.
  • .value enthält den Namen der Spalte, in die die Werte der Zellen eingehen.

Die anderen Spalten der Spezifikation spiegeln wider, wie im neuen Feld die Namen der aggregierten Spalten aus .name.

Die Spezifikation beschreibt die Metadaten, die im Spaltennamen gespeichert sind, mit einer Zeile für jede Spalte und einer Spalte für jede Variable, die mit dem Spaltennamen aggregiert wird. Das mag jetzt kompliziert erscheinen, aber nach dem Durchsehen mehrerer Beispiele wird alles viel klarer.

Der Sinn der Spezifikation besteht darin, dass Sie Metadaten zum transformierten Datenrahmen extrahieren, ändern und hinzufügen können.

Um mit Spezifikationen beim Umwandeln der Tabelle vom breiten Format in das lange Format zu arbeiten, wird die Funktion pivot_longer_spec().

Die Funktionsweise dieser Funktion besteht darin, dass sie jeden Datenrahmen nimmt und dessen Metadaten auf die oben beschriebene Weise formt.

Als Beispiel nehmen wir den Datensatz who, der zusammen mit dem Paket bereitgestellt wird. tidyrDieser Datensatz enthält Informationen, die von der Weltgesundheitsorganisation über die Tuberkulose-Inzidenz bereitgestellt werden.

who
#> # Ein Tibble: 7.240 x 60
#>    land iso2  iso3   jahr neue_sp_m014 neue_sp_m1524 neue_sp_m2534
#>                                
#>  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
#> # … mit 7.230 weiteren Zeilen und 53 weiteren Variablen

Lass uns die Spezifikation erstellen.

spec %
  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

Felder land, iso2, iso3 sind bereits Variablen. Unsere Aufgabe ist es, die Spalten zu transponieren mit new_sp_m014 nach newrel_f65.

In den Bezeichnern dieser Spalten befindet sich folgende Information:

  • Präfix new_ zeigt an, dass die Spalte Daten zu neuen Tuberkulose-Fällen enthält; der aktuelle Datenrahmen enthält nur Informationen zu neuen Erkrankungen, deshalb hat dieses Präfix im aktuellen Kontext keine bedeutende Last.
  • sp/rel/sp/ep beschreibt die Diagnosemethode der Erkrankung.
  • m/f Geschlecht des Patienten.
  • 014/1524/2535/3544/4554/65 Altersgruppe des Patienten.

Wir können diese Spalten mit der Funktion extract(), mithilfe eines regulären Ausdrucks, aufteilen.

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

Bitte beachten, dass die Spalte .name unverändert bleiben sollte, da dies unser Index in den Namen der Spalten des ursprünglichen Datensatzes ist.

Geschlecht und Alter (Spalten gender und age) haben feste und bekannte Werte, daher wird empfohlen, diese Spalten in Faktoren umzuwandeln:

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

Schließlich, um die von uns erstellte Spezifikation auf den ursprünglichen Datenrahmen anzuwenden, who müssen wir das Argument verwenden spec in der Funktion 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

Alles, was wir gerade getan haben, kann schematisch wie folgt dargestellt werden:

Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider.

Spezifikation unter Verwendung mehrerer Werte (.value)

Im obigen Beispiel ist die Spezifikationsspalte .value enthielt nur einen Wert, was in den meisten Fällen so ist.

Aber gelegentlich kann eine Situation auftreten, in der Sie Daten aus Spalten mit unterschiedlichen Datentypen in den Werten sammeln müssen. Mit der veralteten Funktion spread() wäre es ziemlich schwierig zu verwirklichen.

Das folgende Beispiel stammt aus Vignetten dem Paket data.table.

Lassen Sie uns einen Trainingsdatenrahmen erstellen.

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 % 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

Der erstellte Datenrahmen enthält in jeder Zeile Daten zu den Kindern einer Familie. In Familien kann es ein oder zwei Kinder geben. Für jedes Kind werden Geburtsdatum und Geschlecht bereitgestellt, wobei die Daten für jedes Kind in separaten Spalten angeordnet sind, unsere Aufgabe ist es, diese Daten in das richtige Analyseformat zu bringen.

Beachten Sie, dass wir zwei Variablen mit Informationen über jedes Kind haben: sein Geschlecht und das Geburtsdatum (Spalten mit dem Präfix dop enthalten das Geburtsdatum, die Spalten mit dem Präfix gender enthalten das Geschlecht des Kindes). Im erwarteten Ergebnis sollten sie in separaten Spalten angezeigt werden. Wir können dies tun, indem wir eine Spezifikation generieren, in der die Spalte .value zwei verschiedene Werte annehmen wird.

spec %
  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

Lassen Sie uns also die Schritte durchgehen, die der obige Code ausführt.

  • pivot_longer_spec(-family) — wir erstellen eine Spezifikation, die alle vorhandenen Spalten außer der Spalte family komprimiert.
  • separate(col = name, into = c(".value", "child")) — wir teilen die Spalte .name, die die Namen der ursprünglichen Felder enthält, durch Unterstriche und tragen die erhaltenen Werte in die Spalten .value und child.
  • mutate(child = parse_number(child)) — wir wandeln die Werte des Feldes child von текстового в числовой тип данных um.

Jetzt können wir die erhaltene Spezifikation auf den ursprünglichen Datenrahmen anwenden und die Tabelle in die gewünschte Form bringen.

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

Wir verwenden das Argument na.rm = TRUE, weil die aktuelle Form der Daten dazu zwingt, zusätzliche Zeilen für nicht vorhandene Beobachtungen zu erstellen. Da Familie 2 nur ein Kind hat, na.rm = TRUE garantiert, dass Familie 2 eine Zeile in den Ausgaben haben wird.

Umwandlung von Data Frames vom langen Format ins breite

pivot_wider() ist eine Umwandlung, die umgekehrt ist, und wiederum die Anzahl der Spalten im Dataframe erhöht, indem die Anzahl der Zeilen verringert wird.

Das R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider.

Eine solche Umwandlung wird äußerst selten verwendet, um Daten in eine ordentliche Form zu bringen; dennoch kann dieses Verfahren nützlich sein, um Pivot-Tabellen für Präsentationen zu erstellen oder für die Integration mit anderen Tools.

Tatsächlich sind die Funktionen pivot_longer() und pivot_wider() symmetrisch und führen gegensätzliche Aktionen aus, d.h.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) und df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) wird das ursprüngliche df zurückgeben.

Ein einfaches Beispiel zur Umwandlung einer Tabelle in breites Format

Zur Demonstration der Funktionsweise pivot_wider() werden wir den Datensatz fish_encounters, der Informationen darüber enthält, wie verschiedene Stationen die Bewegungen von Fischen im Fluss dokumentieren.

#> # 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

In den meisten Fällen wird diese Tabelle informativer und benutzerfreundlicher sein, wenn die Informationen für jede Station in einer separaten Spalte dargestellt werden.

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

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)
#> # Ein tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>        &;  &; &;   &; &; &; &; &; &; &;
#>  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
#> # … mit 9 weiteren Zeilen und 1 weiterer Variable: MAW

Dieser Datensatz zeichnet Informationen nur dann auf, wenn der Fisch von einer Station erkannt wurde, d.h. wenn ein Fisch von einer Station nicht erfasst wurde, dann werden diese Daten in der Tabelle nicht vorhanden sein. Das bedeutet, dass die Ausgabe mit NA gefüllt wird.

In diesem Fall wissen wir jedoch, dass das Fehlen eines Eintrags bedeutet, dass der Fisch nicht gesehen wurde; daher können wir das Argument values_fill in der Funktion pivot_wider() verwenden und diese fehlenden Werte mit nullen auffüllen:

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>

Generierung des Spaltennamens aus mehreren Ausgangsvariablen

Stellen Sie sich vor, wir haben eine Tabelle, die Kombinationen aus Produkt, Land und Jahr enthält. Um ein Test-Datenframe zu generieren, kann der folgende Code ausgeführt werden:

df %
  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

Unsere Aufgabe ist es, das Datenframe so zu erweitern, dass eine Spalte Daten für jede Kombination aus Produkt und Land enthält. Dafür genügt es, das Argument names_from mit einem Vektor, der die Namen der zu kombinierenden Felder enthält, zu übergeben.

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

Sie können auch Spezifikationen für die Funktion pivot_wider(). Aber bei der Übergabe zu pivot_wider() führt die Spezifikation eine umgekehrte Transformation aus, pivot_longer(): es werden Spalten erstellt, die in .nameangegeben sind, unter Verwendung der Werte aus .value und anderen Spalten.

Für diesen Datensatz können Sie eine benutzerdefinierte Spezifikation generieren, wenn Sie möchten, dass jede mögliche Kombination aus Land und Produkt eine eigene Spalte hat und nicht nur die, die in den Daten vorhanden sind:

spec % 
  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

Einige fortgeschrittene Beispiele für die Arbeit mit dem neuen Konzept von tidyr

Daten in eine saubere Form bringen am Beispiel eines Datensatzes zur Einkommens- und Mietstatistik in den USA

Datensatz us_rent_income enthält Informationen über das durchschnittliche Einkommen und die Mietkosten für jeden Bundesstaat in den USA im Jahr 2017 (der Datensatz ist im Package tidycensus).

us_rent_income
#> # A tibble: 104 x 5
#>    GEOID NAME       variable estimate   moe
#>                   
#>  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
#> # … mit 94 weiteren Zeilen

In der Form, in der die Daten im Datensatz gespeichert sind, ist es äußerst unpraktisch, mit ihnen zu arbeiten, daher möchten wir einen Datensatz mit den Spalten: us_rent_income rent rent_moe, come, income_moe, . Es gibt viele Möglichkeiten, diese Spezifikation zu erstellen, aber das Wichtigste ist, dass wir jede Kombination aus Variablenwerten undestimate/moe , und dann den Namen der Spalte generieren müssen.spec % expand(variable, .value = c("estimate", "moe")) %>% mutate( .name = paste0(variable, ifelse(.value == "moe", "_moe", "")) )

  Die Bereitstellung dieser Spezifikation

#> # 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

gibt uns das Ergebnis, das wir suchen: pivot_wider() us_rent_income %>% pivot_wider(spec = spec)

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

Weltbank

Manchmal erfordert die Umformung eines Datensatzes mehrere Schritte.
Datensatz world_bank_pop enthält Daten der Weltbank zur Bevölkerung jedes Landes im Zeitraum von 2000 bis 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>

Unser Ziel ist es, einen ordentlichen Datensatz zu erstellen, bei dem jede Variable in einer eigenen Spalte steht. Es ist noch unklar, welche Schritte genau erforderlich sind, aber wir beginnen mit dem offensichtlichsten Problem: Das Jahr ist über mehrere Spalten verteilt.

Um dies zu beheben, muss die Funktion verwendet werden pivot_longer().

pop2 % 
  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

Der nächste Schritt besteht darin, die Variable indicator zu betrachten.
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

Wo SP.POP.GROW das Bevölkerungswachstum, SP.POP.TOTL die Gesamtbevölkerung und SP.URB.* das Gleiche, aber nur für städtische Gebiete ist. Lassen Sie uns diese Werte in zwei Variablen aufteilen: area – Gebiet (total oder urban) und eine Variable, die die tatsächlichen Daten enthält (population oder growth):

pop3 % 
  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

Jetzt müssen wir nur noch die Variable variable in zwei Spalten aufteilen:

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

Kontaktliste

Als letztes Beispiel, stellen Sie sich vor, Sie haben eine Liste von Kontakten, die Sie von einer Website kopiert und eingefügt haben:

contacts <- tribble(
  ~field, ~value,
  "name", "Jiena McLellan",
  "company", "Toyota", 
  "name", "John Smith", 
  "company", "google", 
  "email", "john@google.com",
  "name", "Huxley Ratcliffe"
)

Es ist ziemlich schwierig, diese Liste in ein tabellarisches Format zu bringen, da es keine Variable gibt, die identifiziert, welche Daten zu welchem Kontakt gehören. Wir können dies beheben, indem wir feststellen, dass die Daten für jeden neuen Kontakt mit dem Namen ("name") beginnen, daher können wir eine eindeutige ID erstellen und diese jedes Mal um eins erhöhen, wenn im Feld field der Wert "name" erscheint:

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   john@google.com          2
#> 6 name    Huxley Ratcliffe         3

Jetzt, wo wir eine eindeutige ID für jeden Kontakt haben, können wir das Feld und den Wert in Spalten drehen:

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  john@google.com
#> 3         3 Huxley Ratcliffe <NA>    <NA>

Fazit

Persönlich denke ich, dass das neue Konzept tidyr wirklich intuitiver ist und die veralteten Funktionen in der Funktionalität deutlich übertrifft. spread() und gather()Ich hoffe, dieser Artikel hat Ihnen geholfen, sich mit pivot_longer() und pivot_wider().

Quelle: habr.com

60GB SSD 8Gb DDR4