ProHoster > Блог > Administrado > R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider
R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider
Pako tidyr inkluzivita en la kerno de unu el la plej popularaj bibliotekoj en la R-lingvo - tidyverse.
La ĉefa celo de la pakaĵo estas alporti la datumojn en precizan formon.
Jam disponebla ĉe Habré publikigo dediĉita al ĉi tiu pako, sed ĝi devenas de 2015. Kaj mi volas rakonti al vi pri la plej aktualaj ŝanĝoj, kiuj estis anoncitaj antaŭ kelkaj tagoj de ĝia aŭtoro, Hedley Wickham.
SJK: Ĉu gather() kaj spread() estos malrekomenditaj?
Hadley Wickham: Iagrade. Ni ne plu rekomendos la uzon de ĉi tiuj funkcioj kaj riparos cimojn en ili, sed ili daŭre ĉeestos en la pakaĵo en sia nuna stato.
Enhavo
Se vi interesiĝas pri datuma analizo, vi eble interesiĝos pri mia telegramo и youtube kanaloj. Plejparto de la enhavo estas dediĉita al la R-lingvo.
Golo tidyr — helpi vin alporti la datumojn al tiel nomata neta formo. Neta datumo estas datumoj kie:
Ĉiu variablo estas en kolumno.
Ĉiu observo estas ŝnuro.
Ĉiu valoro estas ĉelo.
Estas multe pli facile kaj pli oportune labori kun datumoj, kiuj estas prezentitaj en ordaj datumoj, kiam oni faras analizon.
Ĉefaj funkcioj inkluzivitaj en la tidyr-pakaĵo
tidyr enhavas aron de funkcioj dizajnitaj por transformi tabelojn:
fill() — plenigi mankantajn valorojn en kolumno kun antaŭaj valoroj;
separate() — dividas unu kampon en plurajn uzante apartigilon;
unite() — faras la operacion kunigi plurajn kampojn en unu, la inversan agon de la funkcio separate();
pivot_longer() — funkcio, kiu konvertas datumojn de larĝa formato al longa formato;
pivot_wider() - funkcio, kiu konvertas datumojn de longa formato al larĝa formato. La inversa operacio de tiu farita de la funkcio pivot_longer().
gather()malmoderna — funkcio, kiu konvertas datumojn de larĝa formato al longa formato;
spread()malmoderna - funkcio, kiu konvertas datumojn de longa formato al larĝa formato. La inversa operacio de tiu farita de la funkcio gather().
Nova koncepto por konverti datumojn de larĝa al longa formato kaj inverse
Antaŭe, funkcioj estis uzitaj por ĉi tiu speco de transformo gather() и spread(). Dum la jaroj de ekzisto de ĉi tiuj funkcioj, iĝis evidente, ke por la plej multaj uzantoj, inkluzive de la aŭtoro de la pakaĵo, la nomoj de ĉi tiuj funkcioj kaj iliaj argumentoj ne estis tute evidentaj, kaj kaŭzis malfacilaĵojn trovi ilin kaj kompreni, kiun el tiuj funkcioj konvertas. datkadro de larĝa ĝis longa formato, kaj inverse.
Ĉi-rilate, en tidyr Du novaj, gravaj funkcioj estis aldonitaj, kiuj estas dezajnitaj por transformi datkadrojn.
Novaj funkcioj pivot_longer() и pivot_wider() estis inspiritaj de kelkaj el la funkcioj en la pakaĵo cdata, kreita fare de John Mount kaj Nina Zumel.
Instalante la plej aktualan version de tidyr 0.8.3.9000
Por instali la novan, plej aktualan version de la pakaĵo tidyr0.8.3.9000, kie novaj funkcioj estas disponeblaj, uzu la sekvan kodon.
devtools::install_github("tidyverse/tidyr")
Dum la skribado, ĉi tiuj funkcioj estas disponeblaj nur en la dev-versio de la pakaĵo en GitHub.
Transiro al novaj funkcioj
Fakte, ne estas malfacile translokigi malnovajn skriptojn por labori kun novaj funkcioj; por pli bona kompreno, mi prenos ekzemplon el la dokumentado de malnovaj funkcioj kaj montros kiel la samaj operacioj estas faritaj uzante novajn. pivot_*() funkcioj.
Konverti larĝan formaton al longa formato.
Ekzempla kodo el la kolekta funkciodokumentado
# 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")
Konverti longan formaton al larĝa formato.
Ekzempla kodo de disvastiga funkciodokumentado
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Ĉar en la supraj ekzemploj pri laboro kun pivot_longer() и pivot_wider(), en la origina tabelo akcioj neniuj kolumnoj listigitaj en argumentoj nomoj_al и valoroj_al iliaj nomoj devas esti inter citiloj.
Tabelo, kiu helpos vin plej facile eltrovi kiel ŝanĝi al laboro kun nova koncepto tidyr.
Noto de la aŭtoro
La tuta suba teksto estas adapta, mi eĉ dirus liberan tradukon vinjetoj de la oficiala retejo de la biblioteko de tidyverse.
Simpla ekzemplo de konvertado de datumoj de larĝa al longa formato
pivot_longer () — plilongigas datumojn reduktante la nombron da kolumnoj kaj pliigante la nombron da vicoj.
Por ruli la ekzemplojn prezentitajn en la artikolo, vi unue devas konekti la necesajn pakaĵojn:
library(tidyr)
library(dplyr)
library(readr)
Ni diru, ke ni havas tabelon kun la rezultoj de enketo, kiu (interalie) demandis homojn pri ilia religio kaj jara enspezo:
Ĉi tiu tabelo enhavas religiajn datumojn de respondantoj en vicoj, kaj enspezniveloj estas disigitaj tra kolonnomoj. La nombro da respondantoj de ĉiu kategorio estas konservita en la ĉelvaloroj ĉe la intersekco de religio kaj enspeznivelo. Por alporti la tablon en bonordan, ĝustan formaton, sufiĉas uzi pivot_longer():
Unua argumento kolumoj, priskribas kiuj kolumnoj devas esti kunfanditaj. En ĉi tiu kazo, ĉiuj kolumnoj krom tempo.
argumenton nomoj_al donas la nomon de la variablo kiu estos kreita el la nomoj de la kolumnoj, kiujn ni kunligis.
valoroj_al donas la nomon de variablo, kiu estos kreita el la datumoj stokitaj en la valoroj de la ĉeloj de la kunfanditaj kolumnoj.
Specifoj (redakti)
Ĉi tio estas nova funkcio de la pako tidyr, kiu antaŭe estis neatingebla kiam oni laboris kun heredaj funkcioj.
Specifo estas datumkadro, ĉiu vico de kiu egalrilatas al unu kolumno en la nova produktaĵdatkadro, kaj du specialaj kolumnoj kiuj komenciĝas per:
.nomo enhavas la originan kolonnomon.
.valoro enhavas la nomon de la kolumno, kiu enhavos la ĉelvalorojn.
La ceteraj kolumnoj de la specifo reflektas kiel la nova kolumno montros la nomon de la kunpremitaj kolumnoj de .nomo.
La specifo priskribas la metadatenojn konservitajn en kolumnonomo, kun unu vico por ĉiu kolumno kaj unu kolumno por ĉiu variablo, kombinita kun la kolumnonomo, ĉi tiu difino povas ŝajni konfuza nuntempe, sed post rigardado de kelkaj ekzemploj ĝi fariĝos multe. pli klara.
La punkto de la specifo estas, ke vi povas preni, modifi kaj difini novajn metadatenojn por la konverta datuma kadro.
Por labori kun specifoj konvertante tabelon de larĝa formato al longa formato, uzu la funkcion pivot_longer_spec().
Kiel ĉi tiu funkcio funkcias estas ke ĝi prenas ajnan datkadron kaj generas siajn metadatenojn en la maniero priskribita supre.
Kiel ekzemplo, ni prenu la who-datumaron, kiu estas provizita kun la pakaĵo tidyr. Ĉi tiu datumaro enhavas informojn provizitajn de la internacia sanorganizo pri la incidenco de tuberkulozo.
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
Ni konstruu ĝian specifon.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
kampoj lando, isoxnumx, isoxnumx estas jam variabloj. Nia tasko estas renversi la kolumnojn kun nova_sp_m014 sur newrel_f65.
La nomoj de ĉi tiuj kolumnoj konservas la sekvajn informojn:
Prefikso new_ indikas, ke la kolumno enhavas datumojn pri novaj kazoj de tuberkulozo, la nuna datkadro enhavas informojn nur pri novaj malsanoj, do ĉi tiu prefikso en la nuna kunteksto ne havas ajnan signifon.
sp/rel/sp/ep priskribas metodon por diagnozi malsanon.
m/f sekso de paciento.
014/1524/2535/3544/4554/65 pacienca aĝo gamo.
Ni povas dividi ĉi tiujn kolumnojn uzante la funkcion extract()uzante regulan esprimon.
Fine, por apliki la specifon, kiun ni kreis al la origina datkadro kiu ni bezonas uzi argumenton spec en funkcio 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
Ĉio, kion ni ĵus faris, povas esti skeme prezentita jene:
Specifo uzante multoblajn valorojn (.value)
En la supra ekzemplo, la specifkolumno .valoro enhavis nur unu valoron, en la plej multaj kazoj tio estas la kazo.
Sed foje povas aperi situacio kiam vi bezonas kolekti datumojn de kolumnoj kun malsamaj datumtipoj en valoroj. Uzante heredan funkcion spread() ĉi tio estus sufiĉe malfacile fari.
La malsupra ekzemplo estas prenita de vinjetoj al la pako datumo.tablo.
La kreita datkadro enhavas datumojn pri infanoj de unu familio en ĉiu vico. Familioj povas havi unu aŭ du infanojn. Por ĉiu infano, datumoj estas provizitaj pri la dato de naskiĝo kaj sekso, kaj la datumoj por ĉiu infano estas en apartaj kolumnoj; nia tasko estas alporti ĉi tiujn datumojn al la ĝusta formato por analizo.
Bonvolu noti, ke ni havas du variablojn kun informoj pri ĉiu infano: ilia sekso kaj naskiĝdato (kolumnoj kun la prefikso dop enhavas naskiĝdaton, kolumnojn kun prefikso sekso enhavas la sekson de la infano). La atendata rezulto estas, ke ili aperu en apartaj kolumnoj. Ni povas fari ĉi tion generante specifon en kiu la kolumno .value havos du malsamajn signifojn.
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
Do, ni rigardu paŝon post paŝo al la agoj faritaj de la supra kodo.
pivot_longer_spec(-family) — kreu specifon, kiu kunpremas ĉiujn ekzistantajn kolumnojn krom la familia kolumno.
separate(col = name, into = c(".value", "child")) - dividi la kolonon .nomo, kiu enhavas la nomojn de la fontaj kampoj, uzante la substrekon kaj enigante la rezultajn valorojn en la kolumnojn .valoro и infano.
mutate(child = parse_number(child)) — transformi la kampajn valorojn infano de teksto ĝis nombra datumtipo.
Nun ni povas apliki la rezultan specifon al la origina datuma kadro kaj alporti la tabelon al la dezirata formo.
Ni uzas argumenton na.rm = TRUE, ĉar la nuna formo de la datenoj devigas la kreadon de ekstraj vicoj por neekzistantaj observaĵoj. Ĉar familio 2 havas nur unu infanon, na.rm = TRUE garantias ke familio 2 havos unu vicon en la eligo.
Konvertado de dataj kadroj de longa al larĝa formato
pivot_wider() - estas la inversa transformo, kaj inverse pliigas la nombron da kolumnoj de la datokadro reduktante la nombron da vicoj.
Ĉi tiu speco de transformo estas ekstreme malofte uzata por alporti datumojn en precizan formon, tamen ĉi tiu tekniko povas esti utila por krei pivotajn tabelojn uzatajn en prezentoj aŭ por integriĝo kun iuj aliaj iloj.
Fakte la funkcioj pivot_longer() и pivot_wider() estas simetriaj, kaj produktas agojn inversajn unu al la alia, t.e.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) redonos la originalan df.
La plej simpla ekzemplo de konverti tabelo al larĝa formato
Por montri kiel funkcias la funkcio pivot_wider() ni uzos la datumaron fish_encounters, kiu stokas informojn pri kiel malsamaj stacioj registras la movadon de fiŝoj laŭ la rivero.
#> # 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
Plejofte, ĉi tiu tabelo estos pli informa kaj pli facile uzebla se vi prezentas informojn por ĉiu stacio en aparta kolumno.
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>
Ĉi tiu datumaro nur registras informojn kiam fiŝoj estis detektitaj de la stacio, t.e. se iu fiŝo ne estis registrita de iu stacio, tiam ĉi tiuj datumoj ne estos en la tabelo. Ĉi tio signifas, ke la eligo estos plenigita per NA.
Tamen, en ĉi tiu kazo ni scias ke la foresto de registro signifas ke la fiŝo ne estis vidita, do ni povas uzi la argumenton valoroj_plenigi en funkcio pivot_wider() kaj plenigu ĉi tiujn mankantajn valorojn per nuloj:
Generante kolumnan nomon de multoblaj fontvariabloj
Imagu, ke ni havas tabelon enhavantan kombinaĵon de produkto, lando kaj jaro. Por generi testan datkadron, vi povas ruli la sekvan kodon:
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
Nia tasko estas vastigi la datumkadron tiel ke unu kolumno enhavas datumojn por ĉiu kombinaĵo de produkto kaj lando. Por fari tion, simple enigu la argumenton nomoj_de vektoro enhavanta la nomojn de la kampoj kunfandataj.
Vi ankaŭ povas apliki specifojn al funkcio pivot_wider(). Sed kiam submetita al pivot_wider() la specifo faras la kontraŭan konvertiĝon pivot_longer(): La kolumnoj specifitaj en .nomo, uzante valorojn de .valoro kaj aliaj kolumnoj.
Por ĉi tiu datumaro, vi povas generi kutiman specifon, se vi volas, ke ĉiu ebla lando kaj produkta kombinaĵo havu sian propran kolumnon, ne nur tiujn ĉeestantajn en la datumoj:
#> # 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
Pluraj altnivelaj ekzemploj de laborado kun la nova tidyr koncepto
Purigado de datumoj uzante la usonan Censan Enspezon kaj Rento-datumojn kiel ekzemplon.
Datenaro us_rent_enspezo enhavas mezajn enspezojn kaj luo-informojn por ĉiu ŝtato en Usono por 2017 (datenoj disponeblaj en pakaĵo 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
En la formo en kiu la datumoj estas konservitaj en la datumaro us_rent_enspezo labori kun ili estas ege maloportuna, do ni ŝatus krei datuman aron kun kolumnoj: renton, lui_moe, venu, income_moe. Estas multaj manieroj krei ĉi tiun specifon, sed la ĉefa punkto estas, ke ni devas generi ĉiun kombinaĵon de variaj valoroj kaj takso/moekaj poste generi la kolumnan nomon.
Foje alporti datuman aron en la deziratan formon postulas plurajn paŝojn.
Datenaro monda_banko_pop enhavas datumojn de la Monda Banko pri la loĝantaro de ĉiu lando inter 2000 kaj 2018.
Nia celo estas krei bonordan datuman aron kun ĉiu variablo en sia propra kolumno. Estas neklare precize kiaj paŝoj necesas, sed ni komencos per la plej evidenta problemo: la jaro estas disvastigita tra pluraj kolumnoj.
Por ripari tion vi devas uzi la funkcion pivot_longer().
La sekva paŝo estas rigardi la indikilon variablo. 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
Kie SP.POP.GROW estas loĝantarkresko, SP.POP.TOTL estas totala populacio, kaj SP.URB. * la sama afero, sed nur por urbaj areoj. Ni dividu ĉi tiujn valorojn en du variablojn: areo - areo (totala aŭ urba) kaj variablo enhavanta realajn datumojn (loĝantaro aŭ kresko):
Tabeligi ĉi tiun liston estas sufiĉe malfacila ĉar ne ekzistas variablo, kiu identigas, kiuj datumoj apartenas al kiu kontakto. Ni povas ripari ĉi tion rimarkante, ke la datumoj de ĉiu nova kontakto komenciĝas per "nomo", do ni povas krei unikan identigilon kaj pligrandigi ĝin je unu ĉiufoje kiam la kampa kolumno enhavas la valoron "nomo":
#> # 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
Nun kiam ni havas unikan identigilon por ĉiu kontakto, ni povas turni la kampon kaj valoron en kolumnojn:
#> # 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>
konkludo
Mia persona opinio estas ke la nova koncepto tidyr vere pli intuicia, kaj signife supera en funkcieco al heredaj funkcioj spread() и gather(). Mi esperas, ke ĉi tiu artikolo helpis vin trakti pivot_longer() и pivot_wider().