ProHoster > BLOG > administrare > Pachetul R tidyr și noile sale funcții pivot_longer și pivot_wider
Pachetul R tidyr și noile sale funcții pivot_longer și pivot_wider
pachet tidyr inclus în nucleul uneia dintre cele mai populare biblioteci în limbajul R - tidyverse.
Scopul principal al pachetului este de a aduce datele într-o formă exactă.
Deja disponibil pe Habré publicare dedicat acestui pachet, dar datează din 2015. Și vreau să vă povestesc despre cele mai actuale modificări, care au fost anunțate în urmă cu câteva zile de autorul său, Hedley Wickham.
SJK: Gather() și spread() vor fi depreciate?
Hadley Wickham: Într-o oarecare măsură. Nu vom mai recomanda utilizarea acestor funcții și nu vom mai remedia erorile din ele, dar acestea vor continua să fie prezente în pachet în starea lor actuală.
Conținut
Dacă ești interesat de analiza datelor, s-ar putea să fii interesat de analiza mea telegramă и youtube canale. Majoritatea conținutului este dedicat limbajului R.
Scop tidyr — vă ajută să aduceți datele într-o așa-numită formă ordonată. Datele clare sunt date în care:
Fiecare variabilă este într-o coloană.
Fiecare observație este un șir.
Fiecare valoare este o celulă.
Este mult mai ușor și mai convenabil să lucrezi cu date care sunt prezentate în date ordonate atunci când se efectuează analize.
Funcții principale incluse în pachetul tidyr
tidyr conține un set de funcții concepute pentru a transforma tabele:
fill() — completarea valorilor lipsă într-o coloană cu valorile anterioare;
separate() — împarte un câmp în mai multe folosind un separator;
unite() — realizează operația de combinare a mai multor câmpuri într-unul singur, acțiunea inversă a funcției separate();
pivot_longer() — o funcție care convertește datele din format larg în format lung;
pivot_wider() - o funcție care convertește datele din format lung în format larg. Operația inversă a celei efectuate de funcție pivot_longer().
gather()învechit — o funcție care convertește datele din format larg în format lung;
spread()învechit - o funcție care convertește datele din format lung în format larg. Operația inversă a celei efectuate de funcție gather().
Nou concept pentru conversia datelor din format larg în format lung și invers
Anterior, funcțiile erau folosite pentru acest tip de transformare gather() и spread(). De-a lungul anilor de existență a acestor funcții, a devenit evident că pentru majoritatea utilizatorilor, inclusiv pentru autorul pachetului, numele acestor funcții și argumentele lor nu erau chiar evidente și au cauzat dificultăți în găsirea lor și înțelegerea care dintre aceste funcții face o conversie. un cadru de dată de la format lat la format lung și invers.
În acest sens, în tidyr Au fost adăugate două funcții noi, importante, care sunt concepute pentru a transforma cadrele de date.
Caracteristici noi pivot_longer() и pivot_wider() au fost inspirate de unele dintre caracteristicile din pachet cdata, creat de John Mount și Nina Zumel.
Instalarea celei mai recente versiuni de tidyr 0.8.3.9000
Pentru a instala noua, cea mai actuală versiune a pachetului tidyr0.8.3.9000, unde sunt disponibile funcții noi, utilizați următorul cod.
devtools::install_github("tidyverse/tidyr")
La momentul scrierii, aceste funcții sunt disponibile numai în versiunea de dezvoltare a pachetului de pe GitHub.
Trecerea la funcții noi
De fapt, nu este dificil să transferi scripturi vechi pentru a lucra cu funcții noi; pentru o mai bună înțelegere, voi lua un exemplu din documentația funcțiilor vechi și voi arăta cum sunt efectuate aceleași operațiuni folosind altele noi. pivot_*() funcții.
Convertiți formatul larg în format lung.
Exemplu de cod din documentația funcției 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")
Conversia formatului lung în format larg.
Exemplu de cod din documentația funcției răspândite
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Deoarece în exemplele de mai sus de lucru cu pivot_longer() и pivot_wider(), în tabelul original stocurilor nu există coloane enumerate în argumente nume_pentru и values_to numele lor trebuie să fie între ghilimele.
Un tabel care vă va ajuta să vă dați seama cel mai ușor cum să treceți la lucrul cu un nou concept tidyr.
Notă de la autor
Tot textul de mai jos este adaptativ, aș spune chiar și traducere gratuită viniete de pe site-ul oficial al bibliotecii tidyverse.
Un exemplu simplu de conversie a datelor din format larg în format lung
pivot_longer () — face seturile de date mai lungi prin reducerea numărului de coloane și creșterea numărului de rânduri.
Pentru a rula exemplele prezentate în articol, mai întâi trebuie să conectați pachetele necesare:
library(tidyr)
library(dplyr)
library(readr)
Să presupunem că avem un tabel cu rezultatele unui sondaj care (printre altele) a întrebat oamenii despre religia lor și venitul anual:
Acest tabel conține datele despre religia respondenților pe rânduri, iar nivelurile de venit sunt împrăștiate pe numele coloanelor. Numărul de respondenți din fiecare categorie este stocat în valorile celulei de la intersecția dintre religie și nivelul veniturilor. Pentru a aduce masa într-un format îngrijit, corect, este suficient să utilizați pivot_longer():
Primul argument col, descrie ce coloane trebuie îmbinate. În acest caz, toate coloanele, cu excepția timp.
Argument nume_pentru dă numele variabilei care va fi creată din numele coloanelor pe care le-am concatenat.
values_to dă numele unei variabile care va fi creată din datele stocate în valorile celulelor coloanelor îmbinate.
Specificații
Aceasta este o nouă funcționalitate a pachetului tidyr, care anterior nu era disponibil când lucrați cu funcții vechi.
O specificație este un cadru de date, fiecare rând al căruia corespunde unei coloane din noul cadru de date de ieșire și două coloane speciale care încep cu:
. Nume conține numele coloanei inițiale.
.valoare conține numele coloanei care va conține valorile celulei.
Coloanele rămase ale specificației reflectă modul în care noua coloană va afișa numele coloanelor comprimate din . Nume.
Specificația descrie metadatele stocate într-un nume de coloană, cu câte un rând pentru fiecare coloană și o coloană pentru fiecare variabilă, combinate cu numele coloanei, această definiție poate părea confuză în acest moment, dar după ce se uită la câteva exemple va deveni mult mai clar.
Scopul specificației este că puteți prelua, modifica și defini metadate noi pentru cadrul de date care este convertit.
Pentru a lucra cu specificațiile atunci când convertiți un tabel dintr-un format larg într-un format lung, utilizați funcția pivot_longer_spec().
Cum funcționează această funcție este că ia orice cadru de dată și își generează metadatele în modul descris mai sus.
Ca exemplu, să luăm setul de date who care este furnizat împreună cu pachetul tidyr. Acest set de date conține informații furnizate de organizația internațională de sănătate cu privire la incidența tuberculozei.
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
Să-i construim specificația.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
domenii ţară, iso2, iso3 sunt deja variabile. Sarcina noastră este să răsturnăm coloanele cu new_sp_m014 pe newrel_f65.
Numele acestor coloane stochează următoarele informații:
prefix new_ indică faptul că coloana conține date despre cazuri noi de tuberculoză, data actuală conține informații doar despre boli noi, astfel încât acest prefix în contextul actual nu are nicio semnificație.
sp/rel/sp/ep descrie o metodă de diagnosticare a unei boli.
m/f genul pacientului.
014/1524/2535/3544/4554/65 intervalul de vârstă al pacientului.
Putem împărți aceste coloane folosind funcția extract()folosind expresia regulată.
În cele din urmă, pentru a aplica specificația pe care am creat-o la cadrul de dată original care trebuie să folosim un argument spec. în funcțiune 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
Tot ceea ce tocmai am făcut poate fi descris schematic după cum urmează:
Specificație folosind mai multe valori (.value)
În exemplul de mai sus, coloana de specificații .valoare conținea o singură valoare, în majoritatea cazurilor acesta este cazul.
Dar ocazional poate apărea o situație când trebuie să colectați date din coloane cu diferite tipuri de date în valori. Utilizarea unei funcții moștenite spread() asta ar fi destul de greu de realizat.
Exemplul de mai jos este preluat din viniete la pachet tabel de date.
Cadrul de date creat conține date despre copiii unei familii din fiecare rând. Familiile pot avea unul sau doi copii. Pentru fiecare copil, datele sunt furnizate cu privire la data nașterii și sexul, iar datele pentru fiecare copil sunt în coloane separate; sarcina noastră este să aducem aceste date în formatul corect pentru analiză.
Vă rugăm să rețineți că avem două variabile cu informații despre fiecare copil: sexul și data nașterii (coloane cu prefixul DOP conțin data nașterii, coloane cu prefix sex conţin sexul copilului). Rezultatul așteptat este că acestea ar trebui să apară în coloane separate. Putem face acest lucru prin generarea unei specificații în care coloana .value va avea două sensuri diferite.
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
Deci, să aruncăm o privire pas cu pas asupra acțiunilor efectuate de codul de mai sus.
pivot_longer_spec(-family) — creați o specificație care comprimă toate coloanele existente, cu excepția coloanei familie.
separate(col = name, into = c(".value", "child")) - împărțiți coloana . Nume, care conține numele câmpurilor sursă, folosind caracterul de subliniere și introducând valorile rezultate în coloane .valoare и copil.
mutate(child = parse_number(child)) — transforma valorile câmpului copil de la tipul de date text la tipul numeric.
Acum putem aplica specificația rezultată cadrului de date original și aducem tabelul în forma dorită.
Folosim argumente na.rm = TRUE, deoarece forma curentă a datelor forțează crearea de rânduri suplimentare pentru observații inexistente. Deoarece familia 2 are un singur copil, na.rm = TRUE garantează că familia 2 va avea un rând în ieșire.
Conversia cadrelor de date din format lung în format larg
pivot_wider() - este transformarea inversă, iar invers crește numărul de coloane ale cadrului de dată prin reducerea numărului de rânduri.
Acest tip de transformare este extrem de rar folosit pentru a aduce datele într-o formă precisă, cu toate acestea, această tehnică poate fi utilă pentru crearea tabelelor pivot utilizate în prezentări sau pentru integrarea cu alte instrumente.
De fapt, funcțiile pivot_longer() и pivot_wider() sunt simetrice și produc acțiuni inverse unele față de altele, adică: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) va returna df-ul original.
Cel mai simplu exemplu de conversie a unui tabel într-un format larg
Pentru a demonstra cum funcționează funcția pivot_wider() vom folosi setul de date întâlniri_pești, care stochează informații despre modul în care diferite stații înregistrează mișcarea peștilor de-a lungul râului.
#> # 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
În cele mai multe cazuri, acest tabel va fi mai informativ și mai ușor de utilizat dacă prezentați informații pentru fiecare stație într-o coloană separată.
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>
Acest set de date înregistrează doar informații atunci când peștii au fost detectați de stație, de exemplu. dacă vreun pește nu a fost înregistrat de o stație, atunci aceste date nu vor fi în tabel. Aceasta înseamnă că rezultatul va fi umplut cu NA.
Totuși, în acest caz știm că absența unei înregistrări înseamnă că peștele nu a fost văzut, așa că putem folosi argumentul values_fill în funcțiune pivot_wider() și completați aceste valori lipsă cu zerouri:
Generarea unui nume de coloană din mai multe variabile sursă
Imaginați-vă că avem un tabel care conține o combinație de produs, țară și an. Pentru a genera un cadru de dată de testare, puteți rula următorul cod:
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
Sarcina noastră este să extindem cadrul de date astfel încât o coloană să conțină date pentru fiecare combinație de produs și țară. Pentru a face acest lucru, trebuie doar să introduceți argumentul nume_de la un vector care conține numele câmpurilor de îmbinat.
De asemenea, puteți aplica specificații unei funcții pivot_wider(). Dar atunci când este supus pivot_wider() specificația face conversia opusă pivot_longer(): Coloanele specificate în . Nume, folosind valori de la .valoare și alte coloane.
Pentru acest set de date, puteți genera o specificație personalizată dacă doriți ca fiecare combinație posibilă de țară și produs să aibă propria sa coloană, nu doar cele prezente în date:
#> # 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
Câteva exemple avansate de lucru cu noul concept Tidyr
Curățarea datelor folosind ca exemplu setul de date privind veniturile și chiriile recensământului din SUA.
Set de date us_rent_income conține informații despre venitul mediu și chiria pentru fiecare stat din SUA pentru 2017 (setul de date disponibil în pachet recensământ ordonat).
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
În forma în care datele sunt stocate în setul de date us_rent_income lucrul cu ele este extrem de incomod, așa că am dori să creăm un set de date cu coloane: închiriază unul , rent_moe, cum, venit_moe. Există multe modalități de a crea această specificație, dar principalul aspect este că trebuie să generăm fiecare combinație de valori variabile și estimare/moeși apoi generați numele coloanei.
Uneori, aducerea unui set de date în forma dorită necesită mai mulți pași.
Setul de date world_bank_pop conține date ale Băncii Mondiale privind populația fiecărei țări între 2000 și 2018.
Scopul nostru este să creăm un set de date ordonat cu fiecare variabilă în propria sa coloană. Nu este clar exact ce pași sunt necesari, dar vom începe cu cea mai evidentă problemă: anul este repartizat pe mai multe coloane.
Pentru a remedia acest lucru, trebuie să utilizați funcția pivot_longer().
Următorul pas este să privim variabila indicator. 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
Unde SP.POP.GROW este creșterea populației, SP.POP.TOTL este populația totală și SP.URB. * acelasi lucru, dar numai pentru zonele urbane. Să împărțim aceste valori în două variabile: zonă - suprafață (totală sau urbană) și o variabilă care conține date reale (populație sau creștere):
Tabelarea acestei liste este destul de dificilă deoarece nu există nicio variabilă care să identifice datele care aparțin cărei persoane de contact. Putem remedia acest lucru observând că datele pentru fiecare contact nou încep cu „nume”, astfel încât să putem crea un identificator unic și să-l incrementam cu unul de fiecare dată când coloana câmpului conține valoarea „nume”:
#> # 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
Acum că avem un ID unic pentru fiecare contact, putem transforma câmpul și valoarea în coloane:
#> # 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>
Concluzie
Părerea mea personală este că noul concept tidyr cu adevărat mai intuitiv și semnificativ superior ca funcționalitate față de funcțiile vechi spread() и gather(). Sper că acest articol te-a ajutat să faci față pivot_longer() и pivot_wider().