R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider

Das Paket tidyr ist Teil eines der beliebtesten Bibliotheken in der Programmiersprache R — tidyverse.
Die Hauptfunktion des Pakets besteht darin, Daten in eine aufgeräumte Form zu bringen.

Im Habr gibt es bereits Veröffentlichung die diesem Paket gewidmet ist, aber auf das Jahr 2015 datiert. Ich möchte über die aktuellsten Änderungen berichten, die vor einigen Tagen von seinem Autor Hadley Wickham angekündigt wurden.

R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider

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

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

Inhalt

Falls Sie sich für Datenanalyse interessieren, könnten meine telegram und youtube Kanäle gefallen. Der Großteil des Inhalts ist der Sprache R gewidmet.

Das Konzept von TidyData

Ziel tidyr — helfen Ihnen, Ihre Daten in das sogenannte ordentliche Format zu bringen. Ordentliche Daten sind Daten, bei denen:

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

Mit ordentlichen Daten lässt sich wesentlich einfacher und bequemer arbeiten, wenn es um Analysen geht.

Die Hauptfunktionen, die im Paket tidyr enthalten sind

tidyr enthält eine Reihe von Funktionen, die zur Transformation von Tabellen dienen:

  • fill() — füllt fehlende Werte in einer Spalte mit den vorherigen Werten auf;
  • separate() — teilt ein Feld in mehrere Teile durch einen Trennzeichen;
  • unite() — führt die Operation zusammen, mehrere Felder in eines, was das Gegenteil der Funktion ist separate();
  • pivot_longer() — Funktion, die Daten von einem breiten Format in ein langes transformiert;
  • pivot_wider() — Funktion, die Daten von einem langen Format in ein breites umwandelt. Die Rückoperation von dem, was die Funktion pivot_longer().
  • gather()obsolet — Funktion, die Daten von einem breiten Format in ein langes transformiert;
  • spread()obsolet — Funktion, die Daten von einem langen Format in ein breites umwandelt. Die Rückoperation von dem, was die Funktion gather().

Ein neues Konzept zur Umwandlung von Daten aus einem breiten Format in ein langes und umgekehrt

Früher wurden Funktionen für derartige Transformationen verwendet gather() und spread(). Im Laufe der Jahre ist klar geworden, dass die Namen dieser Funktionen und ihrer Argumente für die meisten Benutzer, einschließlich des Paketautors, nicht intuitiv waren und das Finden sowie das Verständnis, welche dieser Funktionen einen Data Frame von einem breiten in ein langes Format und umgekehrt bringt, erschwerten.

Daher wurden tidyr zwei neue, wichtige Funktionen hinzugefügt, die zur Transformation von Data Frames gedacht sind.

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

Installation der aktuellsten Version von tidyr 0.8.3.9000

Um die neue, aktuellste Version des Pakets tidyr 0.8.3.9000, in dem die neuen Funktionen verfügbar sind, zu installieren, verwenden Sie den folgenden Code.

devtools::install_github("tidyverse/tidyr")

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

Übergang zu neuen Funktionen

In der Tat ist es nicht schwierig, alte Skripte auf die neuen Funktionen umzustellen; um dies besser zu verstehen, werde ich ein Beispiel aus der Dokumentation der alten Funktionen verwenden und zeigen, wie dieselben Operationen mit den neuen pivot_*() Funktionen ausgefü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")

Umwandlung von Long-Format in Wide-Format.

Codebeispiel 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()keine der in den Argumenten stocks aufgeführten Spalten in der ursprünglichen Tabelle vorhanden sind, names_to und values_to müssen deren Namen in Anführungszeichen angegeben werden.

Eine Tabelle, die Ihnen am einfachsten helfen wird, die Umstellung auf das neue Konzept zu verstehen. tidyr.

R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider

Anmerkung des Autors

Der gesamte folgende Text ist adaptiv, ich würde sogar sagen, eine freiere Übersetzung Vignetten von der offiziellen Website der Bibliothek tidyverse.

Ein einfaches Beispiel zur Umwandlung von Daten aus einem breiten Format in ein langes

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

R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider

Um die in dem Artikel dargestellten Beispiele auszuführen, müssen zunächst die erforderlichen 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 zur Religion der Befragten in den Zeilen, während das Einkommen über die Spaltennamen verteilt ist. Die Anzahl der Befragten in jeder Kategorie wird in den Zellen angezeigt, wo Religion und Einkommensniveau sich kreuzen. 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/abgelehnt    96
#> # … mit 170 weiteren Zeilen

Funktionsargumente pivot_longer()

  • Erstes Argument cols, beschreibt, welche Spalten zusammengeführt werden müssen. In diesem Fall sind es alle Spalten außer time.
  • Das Argument names_to gibt den Namen der Variable an, die aus den zusammengeführten Spaltennamen erstellt wird.
  • values_to gibt den Namen der Variablen an, die aus den in den Werten der zusammengeführten Spalten gespeicherten Daten erstellt wird.

Spezifikationen

Dies ist eine neue Funktion des Pakets tidyr, der zuvor bei der Verwendung veralteter Funktionen nicht verfügbar war.

Die Spezifikation ist ein Datenrahmen, wobei jede Zeile einer Spalte im neuen Ausgangs-Datenrahmen entspricht und zwei spezielle Spalten aufweist, die mit . beginnen:

  • .name enthält den ursprünglichen Spaltennamen.
  • .value enthält den Namen der Spalte, in die die Zellwerte eingegeben werden.

Die übrigen Spalten der Spezifikation spiegeln wider, wie der Name der komprimierten Spalten im neuen Spaltenausdruck ausgegeben wird. .name.

Die Spezifikation beschreibt die Metadaten, die im Spaltennamen gespeichert werden, mit einer Zeile für jede Spalte und einer Spalte für jede Variable, die mit dem Spaltennamen verbunden ist. Vielleicht erscheint diese Definition jetzt verwirrend, aber nach der Betrachtung mehrerer Beispiele wird alles viel klarer.

Der Zweck der Spezifikation besteht darin, dass Sie Metadaten aus dem zu verwandelnden Datenrahmen extrahieren, ändern und festlegen können.

Für die Arbeit mit Spezifikationen beim Konvertieren von einer breiten zu einer langen Tabelle dient die Funktion pivot_longer_spec().

Wie diese Funktion funktioniert, sie nimmt einen beliebigen Data Frame und erstellt seine Metadaten auf die oben beschriebene Weise.

Zum Beispiel nehmen wir den Datensatz 'who', der zusammen mit dem Paket bereitgestellt wird. tidyrDieser Datensatz enthält Informationen, die von der internationalen Gesundheitsorganisation über die Tuberkuloserkrankungen bereitgestellt werden.

who
#> # A tibble: 7,240 x 60
#>    country iso2  iso3   year new_sp_m014 new_sp_m1524 new_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

Lassen Sie uns seine 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 country, iso2, iso3 sind bereits Variablen. Unsere Aufgabe ist es, die Spalten mit new_sp_m014 zum newrel_f65.

In den Spaltenüberschriften sind folgende Informationen gespeichert:

  • Präfix new_ deutet darauf hin, dass die Spalte Daten über neue Tuberkulosefälle enthält; der aktuelle Datensatz enthält Informationen nur zu neuen Erkrankungen, daher hat dieses Präfix in diesem Kontext keine semantische Bedeutung.
  • sp/rel/sp/ep beschreibt die Methode zur Diagnose der Krankheit.
  • m/f Geschlecht des Patienten.
  • 014/1524/2535/3544/4554/65 Altersspanne des Patienten.

Wir können diese Spalten mit der Funktion extract(), unter Verwendung 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 Sie, dass die Spalte .name unverändert bleiben muss, da dies unser Index in den Spaltennamen 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 müssen wir, um die von uns erstellte Spezifikation auf den ursprünglichen Datensatz anzuwenden, who 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:

R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider

Spezifikation mit mehreren Werten (.value)

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

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

Das folgende Beispiel ist entnommen 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 über die Kinder einer Familie. In den 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 zusammengestellt sind. Unsere Aufgabe besteht darin, diese Daten in das richtige Format für die Analyse zu bringen.

Bitte beachten Sie, dass wir zwei Variablen mit Informationen über jedes Kind haben: sein Geschlecht und sein 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 stehen. Wir können dies erreichen, indem wir eine Spezifikation generieren, in der die Spalte .value zwei unterschiedliche Werte haben 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 trennen die Spalte .name, die die Namen der ursprünglichen Felder enthält, nach Unterstrichen und tragen die erhaltenen Werte in die Spalten ein. .value und child.
  • mutate(child = parse_number(child)) — wir wandeln die Werte des Feldes child von Text in den numerischen Datentyp um.

Jetzt können wir die erhaltene Spezifikation auf den ursprünglichen Dataframe anwenden und die Tabelle in das gewünschte Format 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, da die aktuelle Datenstruktur unnötige Zeilen für nicht existierende Beobachtungen erfordert. Da Familie 2 nur ein Kind hat, na.rm = TRUE wird sichergestellt, dass Familie 2 eine Zeile in den Ausgabedaten hat.

Umwandlung von Data Frames vom langen Format in das breite Format

pivot_wider() — ist die Umkehrung der Transformation und erhöht umgekehrt die Anzahl der Spalten des Dataframes, indem die Anzahl der Zeilen verringert wird.

R-Paket tidyr und seine neuen Funktionen pivot_longer und pivot_wider

Eine solche Art der Transformation wird sehr selten verwendet, um Daten ordentlich darzustellen, dennoch kann diese Methode nützlich sein, um Pivot-Tabellen zu erstellen, die in Präsentationen verwendet werden, oder um die Integration mit anderen Werkzeugen zu erleichtern.

Tatsächlich sind die Funktionen pivot_longer() und pivot_wider() symmetrisch und führen gegenseitig entgegengesetzte Aktionen aus, d.h.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) und df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) stellt den ursprünglichen df wieder her.

Ein einfaches Beispiel zur Umwandlung einer Tabelle in ein breites Format

Um die Funktionsweise zu demonstrieren, pivot_wider() werden wir den Datensatz fish_encounters, verwenden, der Informationen darüber speichert, wie verschiedene Stationen die Bewegung von Fischen im Fluss erfassen.

#> # 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 zu jeder 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)
#> # A 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 weiteren Variable: MAW

Dieser Datensatz zeichnet Informationen nur in den Fällen auf, in denen ein Fisch von der Station erfasst wurde, d.h. wenn ein Fisch von einer Station nicht registriert wurde, sind diese Daten nicht in der Tabelle enthalten. Das bedeutet, dass die Ausgabewerte mit NA gefüllt werden.

In diesem Fall wissen wir jedoch, dass das Fehlen eines Eintrags bedeutet, dass der Fisch nicht beobachtet wurde, daher können wir das Argument values_fill in der Funktion pivot_wider() verwenden, um diese fehlenden Werte mit Nullen zu fü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 eines Spaltennamens aus mehreren Ausgangsvariablen

Stellen Sie sich vor, wir haben eine Tabelle, die eine Kombination aus Produkt, Land und Jahr enthält. Um einen Test-Datenrahmen zu generieren, können Sie den folgenden Code ausführen:

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 besteht darin, den Datenrahmen so zu erweitern, dass eine Spalte Daten für jede Kombination von Produkt und Land enthält. Dazu genügt es, den Argument names_from einen Vektor zu übergeben, der die Namen der zu kombinierenden Felder enthält.

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 auf die Funktion anwenden. pivot_wider(). Aber bei der Eingabe in pivot_wider() führt die Spezifikation eine umgekehrte Umwandlung durch, pivot_longer(): es werden die in .nameauf der Grundlage von Werten aus .value und anderen Spalten erstellt.

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 diejenigen, 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 ein ordentliches Format bringen am Beispiel des Datensatzes zur Einkommens- und Mietschätzung in den USA

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

us_rent_income
#> # Ein 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    Kalifornien income      29454   109
#> 10 06    Kalifornien rent         1358     3
#> # … mit 94 weiteren Zeilen

in der Form, in der die Daten im Datensatz gespeichert sind us_rent_income Die Zusammenarbeit mit ihnen ist äußerst umständlich, daher möchten wir ein Datenset mit den Spalten erstellen: miete, miete_moe, kommen, einkommen_moe. Es gibt viele Möglichkeiten, diese Spezifikation zu erstellen, aber das Wichtigste ist, dass wir jede Kombination von Variablenwerten generieren müssen und schätzung/moe, und dann den Spaltennamen generieren.

  spezi <- us_rent_income %>% 
    erweitern(variable, .value = c("schätzung", "moe")) %>% 
    mutieren(
      .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

Die Bereitstellung dieser Spezifikation pivot_wider() liefert uns das Ergebnis, das wir suchen:

us_rent_income %>% pivot_wider(spez = spezi)

#> # 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 Anpassung eines Datensatzes an das gewünschte Format mehrere Schritte.
Datensatz weltbank_bevölkerung 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 sauberen 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 <- world_bank_pop %>% 
  pivot_longer(`2000`:`2017`, names_to = "jahr")

#> # 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 — Bevölkerungswachstum, SP.POP.TOTL — Gesamtbevölkerung, und SP.URB. * ist das Gleiche, aber nur für städtische Gebiete. Lassen Sie uns diese Werte in zwei Variablen aufteilen: area — Gebiet (gesamt oder urban) und eine Variable, die die tatsächlichen Daten enthält (Bevölkerung oder Wachstum):

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 bleibt uns nur noch, die Variable variable in zwei Spalten aufzuteilen:

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

Kontakliste

Im letzten Beispiel stellen Sie sich vor, dass Sie eine Kontaktliste haben, 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"
)

Diese Liste in tabellarische Form zu bringen, ist ziemlich schwierig, 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 jedes neuen Kontakts mit dem Namen ("name") beginnen, sodass wir einen eindeutigen Identifikator erstellen können, der bei jedem Auftreten des Wertes "name" in der Spalte field um eins erhöht wird:

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 einzigartige Kennung für jeden Kontakt haben, können wir das Feld und den Wert in Spalten umwandeln:

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

Meiner Meinung nach ist das neue Konzept tidyr tatsächlich intuitiver und übertrifft die veralteten Funktionen deutlich in der Funktionalität. spread() und gather()Ich hoffe, dieser Artikel hat Ihnen geholfen, das Thema zu verstehen. pivot_longer() und pivot_wider().

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster