Pakiet R tidyr i jego nowe funkcje obrotowe_longer i obrotowe_wider

Pakiet porządek zawarte w rdzeniu jednej z najpopularniejszych bibliotek w języku R - schludny świat.
Głównym celem pakietu jest doprowadzenie danych do dokładnej postaci.

Już dostępny na Habré opublikowanie poświęcone temu pakietowi, ale jego początki sięgają 2015 r. A ja chcę Wam opowiedzieć o najbardziej aktualnych zmianach, które kilka dni temu ogłosił jej autor, Hedley Wickham.

Pakiet R tidyr i jego nowe funkcje obrotowe_longer i obrotowe_wider

SJK: Czy metody zbierania() i spread() staną się przestarzałe?

Hadleya Wickhama: Do pewnego stopnia. Nie będziemy już zalecać korzystania z tych funkcji i naprawiać w nich błędów, ale nadal będą one obecne w pakiecie w ich obecnym stanie.

Zawartość

Jeśli interesuje Cię analiza danych, być może zainteresuje Cię mój telegram и youtube kanały. Większość treści jest poświęcona językowi R.

Koncepcja TidyData

cel porządek — pomogą Ci doprowadzić dane do tzw. zgrabnej formy. Czyste dane to dane, w których:

  • Każda zmienna znajduje się w kolumnie.
  • Każda obserwacja jest ciągiem znaków.
  • Każda wartość jest komórką.

O wiele łatwiej i wygodniej jest pracować z danymi, które są prezentowane w uporządkowanej formie podczas przeprowadzania analizy.

Główne funkcje zawarte w pakiecie tidyr

tidyr zawiera zestaw funkcji przeznaczonych do transformacji tabel:

  • fill() — uzupełnienie brakujących wartości w kolumnie poprzednimi wartościami;
  • separate() — dzieli jedno pole na kilka za pomocą separatora;
  • unite() — wykonuje operację łączenia kilku pól w jedno, działanie odwrotne funkcji separate();
  • pivot_longer() — funkcja konwertująca dane z formatu szerokiego na format długi;
  • pivot_wider() - funkcja konwertująca dane z formatu długiego na format szeroki. Operacja odwrotna do tej wykonywanej przez funkcję pivot_longer().
  • gather()przestarzały — funkcja konwertująca dane z formatu szerokiego na format długi;
  • spread()przestarzały - funkcja konwertująca dane z formatu długiego na format szeroki. Operacja odwrotna do tej wykonywanej przez funkcję gather().

Nowa koncepcja konwersji danych z formatu szerokiego na długi i odwrotnie

Wcześniej do tego rodzaju transformacji używano funkcji gather() и spread(). Z biegiem lat istnienia tych funkcji stało się oczywiste, że dla większości użytkowników, w tym także dla autora pakietu, nazwy tych funkcji i ich argumenty nie były do ​​końca oczywiste, co powodowało trudności w ich odnalezieniu i zrozumieniu, która z tych funkcji konwertuje ramkę daty z formatu szerokiego na długi i odwrotnie.

W związku z tym w porządek Dodano dwie nowe, ważne funkcje, które mają za zadanie przekształcać ramki dat.

Nowe funkcje pivot_longer() и pivot_wider() zostały zainspirowane niektórymi funkcjami pakietu cdane, stworzony przez Johna Mounta i Ninę Zumel.

Instalowanie najnowszej wersji tidyr 0.8.3.9000

Aby zainstalować nową, najbardziej aktualną wersję pakietu porządek 0.8.3.9000, jeśli dostępne są nowe funkcje, użyj poniższego kodu.

devtools::install_github("tidyverse/tidyr")

W chwili pisania tego tekstu funkcje te są dostępne tylko w wersji deweloperskiej pakietu na GitHub.

Przejście na nowe funkcje

Tak naprawdę przeniesienie starych skryptów do pracy z nowymi funkcjami nie jest trudne; dla lepszego zrozumienia wezmę przykład z dokumentacji starych funkcji i pokażę, jak te same operacje są wykonywane przy użyciu nowych pivot_*() Funkcje.

Konwertuj szeroki format na długi format.

Przykładowy kod z dokumentacji funkcji zbierania

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

Konwersja formatu długiego na format szeroki.

Przykładowy kod z dokumentacji funkcji rozprzestrzeniania

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

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

Ponieważ w powyższych przykładach pracy z pivot_longer() и pivot_wider(), w oryginalnej tabeli Zapasy brak kolumn wymienionych w argumentach nazwy_do и wartości_do ich nazwy muszą być ujęte w cudzysłów.

Tabela, która pomoże Ci najłatwiej zorientować się, jak przejść do pracy z nową koncepcją porządek.

Pakiet R tidyr i jego nowe funkcje obrotowe_longer i obrotowe_wider

Notatka od autora

Cały poniższy tekst ma charakter adaptacyjny, powiedziałbym nawet, że jest darmowym tłumaczeniem winiety z oficjalnej strony biblioteki tidyverse.

Prosty przykład konwersji danych z formatu szerokiego na długi

pivot_longer () — wydłuża zbiory danych poprzez zmniejszenie liczby kolumn i zwiększenie liczby wierszy.

Pakiet R tidyr i jego nowe funkcje obrotowe_longer i obrotowe_wider

Aby uruchomić przykłady przedstawione w artykule, należy najpierw podłączyć niezbędne pakiety:

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

Załóżmy, że mamy tabelę z wynikami ankiety, w której (między innymi) pytano ludzi o ich religię i roczne dochody:

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

Ta tabela zawiera dane dotyczące religii respondentów w wierszach, a poziomy dochodów są rozproszone w nazwach kolumn. Liczba respondentów z każdej kategorii jest zapisywana w wartościach komórek na przecięciu religii i poziomu dochodów. Aby nadać tabeli schludny, prawidłowy format, wystarczy użyć 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

Argumenty funkcji pivot_longer()

  • Pierwszy argument cols, opisuje, które kolumny należy scalić. W tym przypadku wszystkie kolumny z wyjątkiem czas.
  • argument nazwy_do podaje nazwę zmiennej, która zostanie utworzona z nazw kolumn, które połączyliśmy.
  • wartości_do podaje nazwę zmiennej, która zostanie utworzona z danych zapisanych w wartościach komórek łączonych kolumn.

Specyfikacja

To nowa funkcjonalność pakietu porządek, co było wcześniej niedostępne podczas pracy ze starszymi funkcjami.

Specyfikacja to ramka danych, której każdy wiersz odpowiada jednej kolumnie w nowej wyjściowej ramce daty oraz dwóm specjalnym kolumnom rozpoczynającym się od:

  • . Nazwa zawiera oryginalną nazwę kolumny.
  • .wartość zawiera nazwę kolumny, która będzie zawierać wartości komórek.

Pozostałe kolumny specyfikacji odzwierciedlają sposób, w jaki nowa kolumna będzie wyświetlać nazwę skompresowanych kolumn . Nazwa.

Specyfikacja opisuje metadane zapisane w nazwie kolumny, z jednym wierszem na każdą kolumnę i jedną kolumną na każdą zmienną, w połączeniu z nazwą kolumny. Definicja ta może w tej chwili wydawać się myląca, ale po przejrzeniu kilku przykładów stanie się znacznie jaśniejszy.

Celem specyfikacji jest to, że możesz pobierać, modyfikować i definiować nowe metadane dla konwertowanej ramki danych.

Aby pracować ze specyfikacjami podczas konwertowania tabeli z formatu szerokiego na długi, użyj funkcji pivot_longer_spec().

Zasada działania tej funkcji polega na tym, że pobiera dowolną ramkę daty i generuje jej metadane w sposób opisany powyżej.

Jako przykład weźmy zestaw danych who dostarczony z pakietem porządek. Ten zbiór danych zawiera informacje dostarczone przez Międzynarodową Organizację Zdrowia na temat zachorowalności na gruźlicę.

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

Zbudujmy jego specyfikację.

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

Pola kraj, iso2, iso3 są już zmienne. Naszym zadaniem jest przewracanie kolumn za pomocą nowy_sp_m014 nadotycząca nowyrel_f65.

Nazwy tych kolumn przechowują następujące informacje:

  • Prefiks new_ wskazuje, że kolumna zawiera dane o nowych przypadkach gruźlicy, bieżąca ramka dat zawiera informacje tylko o nowych chorobach, więc ten przedrostek w bieżącym kontekście nie ma żadnego znaczenia.
  • sp/rel/sp/ep opisuje metodę diagnozowania choroby.
  • m/f płeć pacjenta.
  • 014/1524/2535/3544/4554/65 przedział wiekowy pacjenta.

Możemy podzielić te kolumny za pomocą funkcji extract()za pomocą wyrażeń regularnych.

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

Proszę zwrócić uwagę na kolumnę . Nazwa powinno pozostać niezmienione, ponieważ jest to nasz indeks nazw kolumn oryginalnego zbioru danych.

Płeć i wiek (kolumny płeć и wiek) mają stałe i znane wartości, dlatego zaleca się przeliczenie tych kolumn na współczynniki:

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

Na koniec, aby zastosować specyfikację, którą stworzyliśmy do oryginalnej ramki daty którzy testują i oceniają narzędzia, przedstawiając swoje potrzeby i wyzwania w kontekście stosowanych narzędzi musimy użyć argumentu specyfikacja w funkcji 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

Wszystko, co właśnie zrobiliśmy, można schematycznie przedstawić w następujący sposób:

Pakiet R tidyr i jego nowe funkcje obrotowe_longer i obrotowe_wider

Specyfikacja wykorzystująca wiele wartości (.value)

W powyższym przykładzie kolumna specyfikacji .wartość zawierał tylko jedną wartość, w większości przypadków tak jest.

Czasami jednak może zaistnieć sytuacja, gdy zajdzie potrzeba zebrania danych z kolumn o różnych typach danych w wartościach. Korzystanie ze starszej funkcji spread() byłoby to dość trudne.

Poniższy przykład został zaczerpnięty z winiety do paczki Tabela danych.

Stwórzmy uczącą ramkę danych.

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

Utworzona ramka daty zawiera w każdym wierszu dane o dzieciach jednej rodziny. Rodzina może mieć jedno lub dwójkę dzieci. Dla każdego dziecka podawana jest data urodzenia i płeć, a dane dla każdego dziecka znajdują się w osobnych kolumnach, naszym zadaniem jest doprowadzenie tych danych do odpowiedniego formatu do analizy.

Należy pamiętać, że mamy dwie zmienne z informacjami o każdym dziecku: jego płeć i data urodzenia (kolumny z przedrostkiem Chrzest zawierają datę urodzenia, kolumny z prefiksem płeć zawierać płeć dziecka). Oczekiwanym rezultatem jest to, że powinny one pojawić się w oddzielnych kolumnach. Możemy to zrobić generując specyfikację, w której kolumna .value będzie miało dwa różne znaczenia.

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

Przyjrzyjmy się zatem krok po kroku czynnościom wykonywanym przez powyższy kod.

  • pivot_longer_spec(-family) — utwórz specyfikację, która kompresuje wszystkie istniejące kolumny z wyjątkiem kolumny rodziny.
  • separate(col = name, into = c(".value", "child")) - podziel kolumnę . Nazwa, który zawiera nazwy pól źródłowych, stosując podkreślenie i wpisując powstałe wartości do kolumn .wartość и dziecko.
  • mutate(child = parse_number(child)) — przekształca wartości pól dziecko z tekstowego na numeryczny typ danych.

Teraz możemy zastosować otrzymaną specyfikację do oryginalnej ramki danych i doprowadzić tabelę do pożądanej formy.

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

Używamy argumentu na.rm = TRUE, ponieważ obecna forma danych wymusza utworzenie dodatkowych wierszy dla nieistniejących obserwacji. Ponieważ rodzina 2 ma tylko jedno dziecko, na.rm = TRUE gwarantuje, że rodzina 2 będzie miała jeden wiersz na wyjściu.

Konwersja ramek daty z formatu długiego na szeroki

pivot_wider() - jest transformacją odwrotną i odwrotnie zwiększa liczbę kolumn ramki daty poprzez zmniejszenie liczby wierszy.

Pakiet R tidyr i jego nowe funkcje obrotowe_longer i obrotowe_wider

Ten rodzaj transformacji jest niezwykle rzadko stosowany w celu doprowadzenia danych do dokładnej postaci, jednak technika ta może być przydatna do tworzenia tabel przestawnych używanych w prezentacjach lub do integracji z niektórymi innymi narzędziami.

A właściwie funkcje pivot_longer() и pivot_wider() są symetryczne i powodują działania odwrotne do siebie, tj .: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) zwróci oryginalny plik df.

Najprostszy przykład konwersji tabeli do szerokiego formatu

Aby zademonstrować działanie tej funkcji pivot_wider() skorzystamy ze zbioru danych spotkania_rybne, który przechowuje informacje o tym, jak różne stacje rejestrują ruch ryb wzdłuż rzeki.

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

W większości przypadków ta tabela będzie bardziej pouczająca i łatwiejsza w użyciu, jeśli przedstawisz informacje dla każdej stacji w osobnej kolumnie.

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>

Ten zestaw danych rejestruje informacje tylko wtedy, gdy stacja wykryje ryby, tj. jeżeli jakaś ryba nie została zarejestrowana przez jakąś stację, to danych tych nie będzie w tabeli. Oznacza to, że dane wyjściowe zostaną wypełnione NA.

Jednak w tym przypadku wiemy, że brak zapisu oznacza, że ​​ryby nie widziano, więc możemy posłużyć się argumentem wartości_wypełnienia w funkcji pivot_wider() i uzupełnij te brakujące wartości zerami:

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>

Generowanie nazwy kolumny z wielu zmiennych źródłowych

Wyobraź sobie, że mamy tabelę zawierającą kombinację produktu, kraju i roku. Aby wygenerować testową ramkę daty, możesz uruchomić następujący kod:

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

Naszym zadaniem jest rozwinięcie ramki danych tak, aby w jednej kolumnie znajdowały się dane dla każdej kombinacji produktu i kraju. Aby to zrobić, po prostu przekaż argument nazwy_od wektor zawierający nazwy pól, które mają zostać scalone.

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

Można także zastosować specyfikacje do funkcji pivot_wider(). Ale kiedy został złożony pivot_wider() specyfikacja dokonuje odwrotnej konwersji pivot_longer(): Kolumny określone w . Nazwa, używając wartości z .wartość i inne kolumny.

Dla tego zbioru danych możesz wygenerować niestandardową specyfikację, jeśli chcesz, aby każda możliwa kombinacja krajów i produktów miała własną kolumnę, a nie tylko te obecne w danych:

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

Kilka zaawansowanych przykładów pracy z nową koncepcją tidyr

Czyszczenie danych na przykładzie zbioru danych o dochodach i czynszach ze spisu powszechnego Stanów Zjednoczonych.

Zbiór danych us_rent_income zawiera informacje o średnich dochodach i czynszach dla każdego stanu w USA za rok 2017 (zestaw danych dostępny w pakiecie porządek).

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

W formie, w jakiej dane są przechowywane w zbiorze danych us_rent_income praca z nimi jest wyjątkowo niewygodna, dlatego chcielibyśmy stworzyć zbiór danych z kolumnami: wynajem, rent_moe, jak, dochód_moe. Istnieje wiele sposobów na utworzenie tej specyfikacji, ale najważniejsze jest to, że musimy wygenerować każdą kombinację wartości zmiennych i oszacowanie/miesa następnie wygeneruj nazwę kolumny.

  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

Podanie tej specyfikacji pivot_wider() daje nam wynik, którego szukamy:

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

Bank Światowy

Czasami doprowadzenie zbioru danych do pożądanej formy wymaga kilku kroków.
Zbiór danych świat_bank_pop zawiera dane Banku Światowego dotyczące populacji każdego kraju w latach 2000–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>

Naszym celem jest utworzenie schludnego zestawu danych z każdą zmienną w osobnej kolumnie. Nie jest jasne, jakie dokładnie kroki są potrzebne, ale zaczniemy od najbardziej oczywistego problemu: rok jest rozłożony na wiele kolumn.

Aby to naprawić, musisz użyć funkcji 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

Następnym krokiem jest przyjrzenie się zmiennej wskaźnikowej.
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

Gdzie SP.POP.GROW to wzrost populacji, SP.POP.TOTL to całkowita populacja, a SP.URB. * to samo, ale tylko dla obszarów miejskich. Podzielmy te wartości na dwie zmienne: obszar – obszar (ogółem lub miejski) oraz zmienną zawierającą rzeczywiste dane (populacja lub wzrost):

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

Teraz wystarczy podzielić zmienną na dwie kolumny:

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

Lista kontaktów

Ostatni przykład: wyobraź sobie, że masz listę kontaktów skopiowaną i wklejoną ze strony internetowej:

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

Zestawienie tej listy w formie tabelarycznej jest dość trudne, ponieważ nie ma zmiennej identyfikującej, które dane należą do jakiego kontaktu. Możemy to naprawić zauważając, że dane każdego nowego kontaktu zaczynają się od „nazwa”, dzięki czemu możemy utworzyć unikalny identyfikator i zwiększać go o jeden za każdym razem, gdy kolumna pola zawiera wartość „nazwa”:

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

Teraz, gdy mamy unikalny identyfikator każdego kontaktu, możemy zamienić pole i wartość w kolumny:

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>

wniosek

Moim osobistym zdaniem jest to nowa koncepcja porządek naprawdę bardziej intuicyjny i znacznie przewyższający funkcjonalność starszymi funkcjami spread() и gather(). Mam nadzieję, że ten artykuł pomógł ci sobie z tym poradzić pivot_longer() и pivot_wider().

Źródło: www.habr.com

Dodaj komentarz