ProHoster > Blog > Uprava > R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider
R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider
Paket tidyr vključeno v jedro ene najbolj priljubljenih knjižnic v jeziku R - tidyverse.
Glavni namen paketa je spraviti podatke v natančno obliko.
Že na voljo na Habréju objave posvečen temu paketu, vendar sega v leto 2015. In želim vam povedati o najnovejših spremembah, ki jih je pred nekaj dnevi napovedal njen avtor Hedley Wickham.
S.J.K.: Bosta gather() in spread() opuščena?
Hadley Wickham: Do določene mere. Ne bomo več priporočali uporabe teh funkcij in odpravljali napak v njih, vendar bodo še naprej prisotne v paketu v trenutnem stanju.
Vsebina
Če vas zanima analiza podatkov, vas bo morda zanimal moj telegram и youtube kanalov. Večina vsebine je posvečena jeziku R.
Cilj tidyr — pomaga pripeljati podatke v tako imenovano čisto obliko. Čisti podatki so podatki, kjer:
Vsaka spremenljivka je v stolpcu.
Vsako opazovanje je niz.
Vsaka vrednost je celica.
Pri analizi je veliko lažje in bolj priročno delati s podatki, ki so predstavljeni v urejenih podatkih.
Glavne funkcije, vključene v paket tidyr
tidyr vsebuje nabor funkcij za preoblikovanje tabel:
fill() — polnjenje manjkajočih vrednosti v stolpcu s prejšnjimi vrednostmi;
separate() — razdeli eno polje na več z uporabo ločila;
unite() — izvede operacijo združevanja več polj v eno, inverzno delovanje funkcije separate();
pivot_longer() — funkcija, ki pretvori podatke iz širokega formata v dolgi format;
pivot_wider() - funkcija, ki pretvori podatke iz dolgega formata v široki format. Delovanje, obratno od tistega, ki ga izvaja funkcija pivot_longer().
gather()zastarel — funkcija, ki pretvori podatke iz širokega formata v dolgi format;
spread()zastarel - funkcija, ki pretvori podatke iz dolgega formata v široki format. Delovanje, obratno od tistega, ki ga izvaja funkcija gather().
Nov koncept za pretvorbo podatkov iz širokega v dolgi format in obratno
Prej so se za tovrstno transformacijo uporabljale funkcije gather() и spread(). Z leti obstoja teh funkcij je postalo očitno, da za večino uporabnikov, vključno z avtorjem paketa, imena teh funkcij in njihovi argumenti niso bili povsem očitni, kar je povzročalo težave pri iskanju in razumevanju, katera od teh funkcij pretvori datumski okvir iz širokega v dolgi format in obratno.
V zvezi s tem v tidyr Dodani sta bili dve novi pomembni funkciji, ki sta zasnovani za preoblikovanje datumskih okvirjev.
Nove funkcije pivot_longer() и pivot_wider() navdihnile nekatere funkcije v paketu cdata, ki sta ga ustvarila John Mount in Nina Zumel.
Namestitev najnovejše različice tidyr 0.8.3.9000
Za namestitev nove, najnovejše različice paketa tidyr0.8.3.9000, kjer so na voljo nove funkcije, uporabite naslednjo kodo.
devtools::install_github("tidyverse/tidyr")
V času pisanja so te funkcije na voljo samo v različici paketa za razvijalce na GitHubu.
Prehod na nove funkcije
Pravzaprav pretvorba starih skriptov za delo z novimi funkcijami ni težka, za boljše razumevanje bom vzel primer iz dokumentacije starih funkcij in pokazal, kako se iste operacije izvajajo z uporabo novih pivot_*() funkcije.
Pretvori širok format v dolg format.
Primer kode iz dokumentacije funkcije zbiranja
# 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")
Pretvorba dolgega formata v široki format.
Primer kode iz dokumentacije funkcije širjenja
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Ker v zgornjih primerih dela z pivot_longer() и pivot_wider(), v originalni tabeli Zaloge v argumentih ni navedenih stolpcev names_to и values_to njihova imena morajo biti v narekovajih.
Tabela, ki vam bo pomagala najlažje ugotoviti, kako preklopiti na delo z novim konceptom tidyr.
Opomba avtorja
Vse spodnje besedilo je prilagodljivo, rekel bi celo brezplačen prevod vinjete z uradne spletne strani knjižnice tidyverse.
Preprost primer pretvorbe podatkov iz širokega v dolgi format
pivot_longer () — podaljša nize podatkov z zmanjšanjem števila stolpcev in povečanjem števila vrstic.
Za zagon primerov, predstavljenih v članku, morate najprej povezati potrebne pakete:
library(tidyr)
library(dplyr)
library(readr)
Recimo, da imamo tabelo z rezultati ankete, ki je (med drugim) ljudi spraševala o njihovi veri in letnem dohodku:
Ta tabela vsebuje podatke o veri anketirancev v vrsticah, ravni dohodka pa so razpršene po imenih stolpcev. Število anketirancev iz vsake kategorije je shranjeno v vrednostih celic na presečišču vere in ravni dohodka. Če želite tabelo prenesti v urejeno, pravilno obliko, je dovolj, da uporabite pivot_longer():
Prvi argument cols, opisuje, katere stolpce je treba združiti. V tem primeru so vsi stolpci razen čas.
Prepir names_to poda ime spremenljivke, ki bo ustvarjena iz imen stolpcev, ki smo jih združili.
values_to podaja ime spremenljivke, ki bo ustvarjena iz podatkov, shranjenih v vrednostih celic združenih stolpcev.
Tehnični podatki
To je nova funkcionalnost paketa tidyr, ki prej ni bil na voljo pri delu s podedovanimi funkcijami.
Specifikacija je podatkovni okvir, katerega vsaka vrstica ustreza enemu stolpcu v novem izhodnem datumskem okviru, in dva posebna stolpca, ki se začneta z:
. Ime vsebuje izvirno ime stolpca.
.vrednost vsebuje ime stolpca, ki bo vseboval vrednosti celic.
Preostali stolpci specifikacije odražajo, kako bo novi stolpec prikazal ime stisnjenih stolpcev iz . Ime.
Specifikacija opisuje metapodatke, shranjene v imenu stolpca, z eno vrstico za vsak stolpec in enim stolpcem za vsako spremenljivko, skupaj z imenom stolpca, ta definicija se morda trenutno zdi zmedena, a po ogledu nekaj primerov bo postala zelo jasneje.
Bistvo specifikacije je, da lahko pridobite, spremenite in definirate nove metapodatke za podatkovni okvir, ki se pretvarja.
Če želite delati s specifikacijami pri pretvorbi tabele iz širokega formata v dolgi format, uporabite funkcijo pivot_longer_spec().
Ta funkcija deluje tako, da vzame poljuben datumski okvir in ustvari njegove metapodatke na zgoraj opisan način.
Za primer vzemimo nabor podatkov who, ki je priložen paketu tidyr. Ta niz podatkov vsebuje informacije, ki jih je zagotovila mednarodna zdravstvena organizacija o incidenci tuberkuloze.
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
Zgradimo njegovo specifikacijo.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
polja država, iso2, iso3 so že spremenljivke. Naša naloga je, da obrnemo stolpce z novo_sp_m014 o newrel_f65.
Imena teh stolpcev hranijo naslednje informacije:
Predpona new_ označuje, da stolpec vsebuje podatke o novih primerih tuberkuloze, trenutni datumski okvir vsebuje samo podatke o novih boleznih, zato ta predpona v trenutnem kontekstu nima nobenega pomena.
sp/rel/sp/ep opisuje metodo za diagnosticiranje bolezni.
Končno, da uporabimo specifikacijo, ki smo jo ustvarili, na prvotni datumski okvir kdo moramo uporabiti argument spec v funkciji 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
Vse, kar smo pravkar naredili, lahko shematično prikažemo na naslednji način:
Specifikacija z uporabo več vrednosti (.value)
V zgornjem primeru stolpec specifikacij .vrednost vseboval samo eno vrednost, v večini primerov je tako.
Toda občasno lahko pride do situacije, ko morate zbrati podatke iz stolpcev z različnimi vrstami podatkov v vrednosti. Uporaba podedovane funkcije spread() to bi bilo precej težko narediti.
Spodnji primer je vzet iz vinjete na paket podatki.tabela.
Ustvarjeni datumski okvir vsebuje podatke o otrocih ene družine v vsaki vrstici. Družine imajo lahko enega ali dva otroka. Za vsakega otroka so navedeni podatki o datumu rojstva in spolu, podatki za vsakega otroka pa so v ločenih stolpcih, naša naloga je, da te podatke spravimo v pravilno obliko za analizo.
Upoštevajte, da imamo dve spremenljivki s podatki o vsakem otroku: njihov spol in datum rojstva (stolpci s predpono Krst vsebujejo datum rojstva, stolpce s predpono spol vsebujejo spol otroka). Pričakovani rezultat je, da se prikažejo v ločenih stolpcih. To lahko naredimo tako, da ustvarimo specifikacijo, v kateri je stolpec .value bo imel dva različna pomena.
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
Torej, poglejmo korak za korakom dejanja, ki jih izvaja zgornja koda.
pivot_longer_spec(-family) — ustvarite specifikacijo, ki stisne vse obstoječe stolpce razen stolpca družine.
separate(col = name, into = c(".value", "child")) - razdelite stolpec . Ime, ki vsebuje imena izvornih polj, z uporabo podčrtaja in vnosom nastalih vrednosti v stolpce .vrednost и otrok.
mutate(child = parse_number(child)) — transformirajte vrednosti polja otrok iz besedilnega v številski podatkovni tip.
Zdaj lahko dobljeno specifikacijo uporabimo na izvirnem podatkovnem okvirju in tabelo pripeljemo v želeno obliko.
Uporabljamo argumente na.rm = TRUE, ker trenutna oblika podatkov zahteva ustvarjanje dodatnih vrstic za neobstoječa opazovanja. Ker družina 2 ima samo enega otroka, na.rm = TRUE zagotavlja, da bo družina 2 imela eno vrstico v izhodu.
Pretvorba datumskih okvirjev iz dolgega v široki format
pivot_wider() - je inverzna transformacija in obratno poveča število stolpcev datumskega okvira z zmanjšanjem števila vrstic.
Tovrstna transformacija se izredno redko uporablja za pripravo podatkov v natančno obliko, vendar je ta tehnika lahko uporabna za ustvarjanje vrtilnih tabel, ki se uporabljajo v predstavitvah, ali za integracijo z nekaterimi drugimi orodji.
Pravzaprav funkcije pivot_longer() и pivot_wider() so simetrični in ustvarjajo dejanja, inverzna drug drugemu, tj. df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) bo vrnil originalni df.
Najenostavnejši primer pretvorbe tabele v širok format
Za prikaz delovanja funkcije pivot_wider() uporabili bomo nabor podatkov fish_encounters, ki hrani podatke o tem, kako različne postaje beležijo gibanje rib vzdolž reke.
#> # 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
V večini primerov bo ta tabela bolj informativna in enostavnejša za uporabo, če podate podatke za vsako postajo v ločenem stolpcu.
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>
Ta niz podatkov beleži samo informacije, ko postaja zazna ribe, tj. če kakšne ribe katera postaja ni zabeležila, tega podatka ne bo v tabeli. To pomeni, da bo izhod zapolnjen z NA.
Vendar v tem primeru vemo, da odsotnost zapisa pomeni, da riba ni bila videna, zato lahko uporabimo argument vrednosti_polnila v funkciji pivot_wider() in izpolnite te manjkajoče vrednosti z ničlami:
Ustvarjanje imena stolpca iz več izvornih spremenljivk
Predstavljajte si, da imamo tabelo, ki vsebuje kombinacijo izdelka, države in leta. Če želite ustvariti preskusni datumski okvir, lahko zaženete naslednjo kodo:
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
Naša naloga je razširiti podatkovni okvir tako, da en stolpec vsebuje podatke za vsako kombinacijo izdelka in države. Če želite to narediti, samo vnesite argument imena_iz vektor, ki vsebuje imena polj, ki jih želite združiti.
Za funkcijo lahko uporabite tudi specifikacije pivot_wider(). Ko pa se predloži pivot_wider() specifikacija naredi nasprotno pretvorbo pivot_longer(): Stolpci, navedeni v . Ime, z uporabo vrednosti iz .vrednost in druge stolpce.
Za ta nabor podatkov lahko ustvarite specifikacijo po meri, če želite, da ima vsaka možna kombinacija držav in izdelkov svoj stolpec, ne samo tisti, ki so prisotni v podatkih:
#> # 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
Več naprednih primerov dela z novim konceptom tidyr
Čiščenje podatkov z uporabo nabora podatkov o dohodku in najemnini popisa v ZDA kot primer.
Nabor podatkov us_rent_income vsebuje informacije o medianem dohodku in najemnini za vsako državo v ZDA za leto 2017 (nabor podatkov je na voljo v paketu tidycensus).
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
V obliki, v kateri so podatki shranjeni v naboru podatkov us_rent_income delo z njimi je izjemno neprijetno, zato bi radi ustvarili nabor podatkov s stolpci: najem, rent_moe, kako, dohodek_moe. Obstaja veliko načinov za ustvarjanje te specifikacije, vendar je glavna točka, da moramo ustvariti vsako kombinacijo vrednosti spremenljivk in ocena/moein nato ustvarite ime stolpca.
Včasih je za prenos nabora podatkov v želeno obliko potrebno več korakov.
Nabor podatkov svetovna_banka_pop vsebuje podatke Svetovne banke o številu prebivalcev vsake države med letoma 2000 in 2018.
Naš cilj je ustvariti čist nabor podatkov z vsako spremenljivko v svojem stolpcu. Ni jasno, kateri koraki so potrebni, vendar bomo začeli z najbolj očitno težavo: leto je razporejeno v več stolpcih.
Če želite to popraviti, morate uporabiti funkcijo pivot_longer().
Naslednji korak je ogled spremenljivke indikatorja. 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
Kjer je SP.POP.GROW rast prebivalstva, SP.POP.TOTL skupno prebivalstvo in SP.URB. * isto, vendar samo za urbana območja. Razdelimo te vrednosti na dve spremenljivki: površina - površina (celotna ali urbana) in spremenljivka, ki vsebuje dejanske podatke (prebivalstvo ali rast):
Tabela tega seznama je precej težavna, ker ni spremenljivke, ki bi identificirala, kateri podatki pripadajo kateremu stiku. To lahko popravimo tako, da opazimo, da se podatki za vsak nov stik začnejo z "ime", tako da lahko ustvarimo edinstven identifikator in ga povečamo za eno vsakič, ko stolpec polja vsebuje vrednost "ime":
#> # 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
Zdaj, ko imamo edinstven ID za vsak stik, lahko spremenimo polje in vrednost v stolpce:
#> # 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>
Zaključek
Moje osebno mnenje je, da je nov koncept tidyr resnično bolj intuitiven in bistveno boljši v funkcionalnosti od podedovanih funkcij spread() и gather(). Upam, da vam je ta članek pomagal pri soočanju pivot_longer() и pivot_wider().