R-pakket tidyr en sy nuwe funksies pivot_longer en pivot_wider
pakket netjies is ingesluit in die kern van een van die gewildste biblioteke in die R-taal - netjies.
Die hoofdoel van die pakket is om die data na 'n netjiese vorm te bring.
Habré het reeds publikasie aan hierdie pakket opgedra, maar dit dateer terug na 2015. En ek wil jou vertel van die mees relevante veranderinge, wat 'n paar dae gelede deur die skrywer Hedley Wickham aangekondig is.
S.J.K.: Sal die versamel() en spread() funksies afgekeur word?
Hadley Wickham: Tot 'n mate. Ons sal nie meer die gebruik van hierdie funksies aanbeveel en foute daarin regstel nie, maar hulle sal steeds in hul huidige toestand in die pakket teenwoordig wees.
inhoud
As jy belangstel in data-analise, sal jy dalk belangstel in my telegram и YouTube kanale. Die meeste van die inhoud daarvan word aan die R-taal gewy.
Doel netjies - om jou te help om die data na die sogenaamde netjiese vorm te bring. Netjiese data is data waar:
Elke veranderlike is in 'n kolom.
Elke waarneming is 'n lyn.
Elke waarde is 'n sel.
Dit is baie makliker en geriefliker om met data te werk wat tydens ontleding na data gebring word.
Die hooffunksies ingesluit in die tidyr-pakket
tidyr bevat 'n stel funksies vir die transformasie van tabelle:
fill() - vul die ontbrekende waardes in die kolom in met die vorige waardes;
separate() - verdeel een veld in verskeie deur 'n skeier;
unite() - voer die operasie uit om verskeie velde in een te kombineer, die aksie is die teenoorgestelde van die funksie separate();
pivot_longer() - 'n funksie wat data van 'n wye formaat na 'n lang een omskakel;
pivot_wider() - 'n funksie wat data van lang formaat na wye formaat omskakel. Die bewerking is die omgekeerde van dit wat die funksie verrig. pivot_longer().
gather()verouderd - 'n funksie wat data van 'n wye formaat na 'n lang een omskakel;
spread()verouderd - 'n funksie wat data van lang formaat na wye formaat omskakel. Die bewerking is die omgekeerde van dit wat die funksie verrig. gather().
Nuwe konsep vir die omskakeling van data van wye na lang formaat en omgekeerd
Voorheen, vir hierdie soort transformasie, die funksies gather() и spread(). Oor die jare van die bestaan van hierdie funksies, het dit duidelik geword dat vir die meeste gebruikers, insluitend die outeur van die pakket, die name van hierdie funksies, en hul argumente, taamlik duister was, en probleme veroorsaak het om hulle te vind en te verstaan watter van hierdie funksies skakel 'n datumraam om van wye na lang formaat en omgekeerd.
In verband hiermee, netjies twee nuwe, belangrike funksies is bygevoeg wat ontwerp is om datarame te transformeer.
Nuwe kenmerke pivot_longer() и pivot_wider() is geïnspireer deur sommige van die kenmerke in die pakket cdatageskep deur John Mount en Nina Zumel.
Die installering van die nuutste weergawe van tidyr 0.8.3.9000
Om 'n nuwe, mees opgedateerde pakketweergawe te installeer netjies0.8.3.9000, waar nuwe kenmerke beskikbaar is, gebruik die volgende kode.
devtools::install_github("tidyverse/tidyr")
Ten tyde van die skryf hiervan is hierdie funksies slegs beskikbaar in die dev-weergawe van die pakket op GitHub.
Oorgang na nuwe funksies
Trouens, dit is nie moeilik om ou skrifte te vertaal om met nuwe funksies te werk nie, vir beter begrip, sal ek 'n voorbeeld uit die dokumentasie van ou funksies neem en wys hoe dieselfde bewerkings uitgevoer word deur nuwes te gebruik. pivot_*() funksies.
Skakel wye formaat na lang formaat.
Voorbeeldkode uit die dokumentasie van die versamelfunksie
# 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")
Skakel lang formaat om na wye formaat.
Voorbeeldkode uit die dokumentasie van die verspreidingsfunksie
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Omdat in die bogenoemde voorbeelde van werk met pivot_longer() и pivot_wider(), in die oorspronklike tabel aandele geen kolomme in argumente gelys nie name_aan и waardes_aan hulle name moet tussen aanhalingstekens ingesluit word.
'n Tabel met behulp waarvan dit vir jou die maklikste sal wees om uit te vind hoe om oor te skakel na werk met 'n nuwe konsep netjies.
Nota van die skrywer
Al die teks hieronder is aanpasbaar, ek sou selfs sê vrye vertaling vignette vanaf die amptelike webwerf van die tidyverse-biblioteek.
'n Eenvoudige voorbeeld van die omskakeling van data van wyd na lank
pivot_longer () - maak datastelle langer deur die aantal kolomme te verminder en die aantal rye te verhoog.
Om die voorbeelde wat in die artikel aangebied word uit te voer, moet u eers die nodige pakkette koppel:
library(tidyr)
library(dplyr)
library(readr)
Kom ons sê ons het 'n tabel met die resultate van 'n opname wat mense (onder andere) oor hul godsdiens en jaarlikse inkomste gevra het:
Hierdie tabel bevat die godsdiensdata van die respondente in rye, en die inkomstevlak is oor die kolomname versprei. Die aantal respondente van elke kategorie word in selwaardes gestoor by die kruising van godsdiens en inkomstevlak. Om die tabel in 'n netjiese, korrekte formaat te bring, is dit genoeg om te gebruik pivot_longer():
Eerste argument krae, beskryf watter kolomme saamgevoeg moet word. In hierdie geval, alle kolomme behalwe tyd.
argument name_aan gee die naam van die veranderlike wat geskep sal word uit die kolomname wat ons aaneengeskakel het.
waardes_aan gee die naam van die veranderlike wat geskep sal word uit die data wat in die selwaardes van die saamgevoegde kolomme gestoor is.
Spesifikasies
Dit is 'n nuwe pakketfunksie. netjies, wat voorheen nie beskikbaar was wanneer daar met verouderde kenmerke gewerk word nie.
'n Spesifikasie is 'n dataraam, waarvan elke ry ooreenstem met een kolom in die nuwe uitvoerdataraam, en twee spesiale kolomme wat begin met:
. Naam bevat die oorspronklike kolomnaam.
.waarde bevat die naam van die kolom wat die selwaardes sal bevat.
Die oorblywende kolomme van die spesifikasie weerspieël hoe die naam van die saamgeperste kolomme in die nuwe kolom vertoon sal word. . Naam.
Die spesifikasie beskryf die metadata wat in 'n kolomnaam gestoor word, met een ry vir elke kolom en een kolom vir elke veranderlike saamgevoeg met die kolomnaam, wat dalk op die oomblik verwarrend kan lyk, maar na 'n paar voorbeelde sal dit baie duideliker word.
Die punt van die spesifikasie is dat jy nuwe metadata vir die omgeskakelde dataraam kan ophaal, wysig en stel.
Om met spesifikasies te werk wanneer 'n tabel van wye formaat na lang formaat omgeskakel word, gebruik die funksie pivot_longer_spec().
Die manier waarop hierdie funksie werk, is dat dit enige dataraamwerk neem en sy metadata genereer op die wyse hierbo beskryf.
Kom ons neem byvoorbeeld die who-datastel wat saam met die pakket kom netjies. Hierdie datastel bevat inligting verskaf deur die Internasionale Gesondheidsorganisasie oor tuberkulosevoorkoms.
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
Kom ons bou sy spesifikasie.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
velde land, iso2, iso3 is reeds veranderlikes. Ons taak is om die kolomme om te draai nuwe_sp_m014 op newrel_f65.
Die volgende inligting word in die name van hierdie kolomme gestoor:
voorvoegsel new_ dui aan dat die kolom data bevat oor nuwe gevalle van tuberkulose, die huidige datumraam bevat slegs inligting oor nuwe siektes, so hierdie voorvoegsel in die huidige konteks dra geen semantiese las nie.
sp/rel/sp/ep beskryf 'n metode om 'n siekte te diagnoseer.
Ten slotte, om die spesifikasie wat ons geskep het op die oorspronklike dataraam toe te pas wat ons moet die argument gebruik spec in funksie 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 wat ons sopas gedoen het kan skematies soos volg uitgebeeld word:
Spesifikasie wat veelvuldige waardes (.value) gebruik
In die voorbeeld hierbo, die spesifikasiekolom .waarde slegs een waarde bevat het, in die meeste gevalle is dit die geval.
Maar soms kan 'n situasie ontstaan wanneer jy data van kolomme met verskillende datatipes in waardes moet insamel. Met 'n afgekeurde funksie spread() dit sal nogal moeilik wees om te doen.
Die voorbeeld hieronder is geneem uit vignette na die pakkie data.tabel.
Die geskepde datumraam in elke reël bevat data oor die kinders van een gesin. Gesinne kan een of twee kinders hê. Vir elke kind word data oor die geboortedatum en geslag verskaf, en die data vir elke kind gaan in aparte kolomme, ons taak is om hierdie data in die korrekte formaat te bring vir ontleding.
Let daarop dat ons twee veranderlikes het met inligting oor elke kind: hul geslag en geboortedatum (kolomme met voorvoegsel dop bevat geboortedatum, voorafgevoegde kolomme geslag die geslag van die kind bevat). In die verwagte resultaat moet hulle in aparte kolomme gaan. Ons kan dit doen deur 'n spesifikasie te genereer waar die kolom .value twee verskillende betekenisse sal hê.
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
Dus, kom ons kyk na die stappe van die aksies wat deur die bogenoemde kode uitgevoer word.
pivot_longer_spec(-family) - skep 'n spesifikasie wat alle beskikbare kolomme saamdruk, behalwe vir die familiekolom.
separate(col = name, into = c(".value", "child")) - verdeel die kolom . Naam, wat die name van die oorspronklike velde bevat, deur onderstreep en die resulterende waardes in kolomme te plaas .waarde и kind.
mutate(child = parse_number(child)) - transformeer die veldwaardes kind van teks tot numeriese datatipe.
Nou kan ons die gevolglike spesifikasie op die oorspronklike dataraam toepas, en die tabel na die verlangde vorm bring.
Ons gebruik die argument na.rm = TRUE, want die huidige vorm van die data dwing die skepping van ekstra rye vir nie-bestaande waarnemings af. Omdat gesin 2 het net een kind, na.rm = TRUE waarborg dat gesin 2 een ry in die uitvoer sal hê.
Omskakeling van datarame van lang formaat na wye formaat
pivot_wider() - is die omgekeerde transformasie, en omgekeerd verhoog die aantal kolomme van die datumraam deur die aantal rye te verminder.
Hierdie soort transformasie word uiters selde gebruik om data in 'n netjiese vorm te bring, maar hierdie tegniek kan nuttig wees vir die skep van spilpunttabelle wat in aanbiedings gebruik word, of vir integrasie met ander instrumente.
Eintlik die funksies pivot_longer() и pivot_wider() is simmetries en voer die teenoorgestelde aksies as mekaar uit, d.w.s.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) sal die oorspronklike df terugstuur.
Die eenvoudigste voorbeeld van die omskakeling van 'n tabel na 'n wye formaat
Om te demonstreer hoe die funksie werk pivot_wider() ons sal die datastel gebruik vis_ontmoetings, wat inligting stoor oor hoe verskeie stasies die beweging van visse langs die rivier aanteken.
#> # 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 die meeste gevalle sal hierdie tabel meer insiggewend en makliker wees om te gebruik as die inligting vir elke stasie in 'n aparte kolom aangebied word.
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>
Hierdie datastel teken inligting slegs aan wanneer visse deur die stasie opgespoor is, m.a.w. as enige vis nie deur een of ander stasie aangeteken is nie, sal hierdie data nie in die tabel wees nie. Dit beteken dat die uitset met NA gevul sal word.
In hierdie geval weet ons egter dat die afwesigheid van die inskrywing beteken dat die vis nie gesien is nie, dus kan ons die argument gebruik waardes_vul in funksie pivot_wider() en vul daardie ontbrekende waardes met nulle in:
Genereer 'n kolomnaam uit verskeie bronveranderlikes
Stel jou voor ons het 'n tabel wat 'n kombinasie van produk, land en jaar bevat. Om 'n toetsdatum raam te genereer, kan jy die volgende kode hardloop:
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
Ons taak is om die dataraam uit te brei sodat een kolom data vir elke kombinasie van produk en land bevat. Om dit te doen, is dit genoeg om die argument deur te gee name_van 'n vektor wat die name bevat van die velde wat saamgevoeg moet word.
Jy kan ook spesifikasies op 'n funksie toepas pivot_wider(). Maar wanneer voorgelê word aan pivot_wider() die spesifikasie voer die teenoorgestelde omskakeling uit pivot_longer(): skep die kolomme gespesifiseer in . Naam, met behulp van waardes van .waarde en ander kolomme.
Vir hierdie datastel kan jy 'n pasgemaakte spesifikasie genereer as jy wil hê dat elke moontlike kombinasie van land en produk sy eie kolom moet hê, nie net dié wat in die data teenwoordig is nie:
#> # 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
Enkele gevorderde voorbeelde van werk met die nuwe tidyr-konsep
Ruim die data op deur die Amerikaanse sensus van inkomste en huurdatastel te gebruik
Datastel ons_huurinkomste bevat inligting oor mediaan inkomste en huur vir elke staat in die VSA vir 2017 (datastel beskikbaar in pakket netjiese sensus).
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
In die vorm waarin die data in die datastel gestoor word ons_huurinkomste om met hulle te werk is uiters ongerieflik, daarom wil ons graag 'n datastel met kolomme skep: huur, huur_moe, hoe, inkomste_moe. Daar is baie maniere om hierdie spesifikasie te skep, maar die belangrikste ding is dat ons elke kombinasie van veranderlike waardes en skat/moeen genereer dan die kolomnaam.
Soms neem dit verskeie stappe om 'n datastel in die regte vorm te kry.
Datastel wêreld_bank_pop bevat Wêreldbankdata oor die bevolking van elke land tussen 2000 en 2018.
Ons doel is om 'n netjiese datastel te skep waar elke veranderlike in sy eie kolom is. Dit is nog nie duidelik presies watter stappe nodig is nie, maar ons begin met die mees ooglopende probleem: die jaar is oor verskeie kolomme versprei.
Om dit reg te stel, moet jy die funksie gebruik pivot_longer().
Die volgende stap is om die aanwyser veranderlike te oorweeg. 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
Waar SP.POP.GROW bevolkingsgroei is, is SP.POP.TOTL totale bevolking, en SP.URB. * dieselfde, maar slegs vir stedelike gebiede. Kom ons verdeel hierdie waardes in twee veranderlikes: oppervlakte - die oppervlakte (totaal of stedelik) en 'n veranderlike wat die werklike data (bevolking of groei) bevat:
Om hierdie lys te tabelle is moeilik omdat daar geen veranderlike is om te identifiseer watter data aan watter kontak behoort nie. Ons kan dit regmaak deur op te let dat die data vir elke nuwe kontak met 'n naam ("naam") begin, sodat ons 'n unieke ID kan skep, en dit met een verhoog elke keer as die waarde "naam" in die veldkolom teëgekom word:
#> # 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
Noudat ons 'n unieke ID vir elke kontak het, kan ons die veld en waarde in kolomme verander:
#> # 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>
Gevolgtrekking
My persoonlike mening is dat die nuwe konsep netjies is regtig meer intuïtief, en vaar aansienlik beter as verouderde funksies in terme van funksionaliteit spread() и gather(). Ek hoop hierdie artikel het jou gehelp om te verstaan pivot_longer() и pivot_wider().