R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider
pakke ryddig inkludert i kjernen av et av de mest populære bibliotekene i R-språket - ryddig.
Hovedformålet med pakken er å bringe dataene inn i en nøyaktig form.
Allerede tilgjengelig på Habré utgivelse dedikert til denne pakken, men den dateres tilbake til 2015. Og jeg vil fortelle deg om de mest aktuelle endringene, som ble annonsert for noen dager siden av forfatteren, Hedley Wickham.
SJK: Vil gather() og spread() bli avskrevet?
Hadley Wickham: Til en viss grad. Vi vil ikke lenger anbefale bruk av disse funksjonene og fikse feil i dem, men de vil fortsette å være tilstede i pakken i den nåværende tilstanden.
Innhold
Hvis du er interessert i dataanalyse, kan du være interessert i min telegram и youtube kanaler. Det meste av innholdet er dedikert til R-språket.
target ryddig — hjelpe deg med å bringe dataene til en såkalt ryddig form. Ryddige data er data der:
Hver variabel er i en kolonne.
Hver observasjon er en streng.
Hver verdi er en celle.
Det er mye enklere og mer praktisk å jobbe med data som presenteres i ryddige data når man utfører analyse.
Hovedfunksjoner inkludert i tidyr-pakken
tidyr inneholder et sett med funksjoner designet for å transformere tabeller:
fill() — fylle manglende verdier i en kolonne med tidligere verdier;
separate() — deler ett felt i flere ved hjelp av en skilletegn;
unite() — utfører operasjonen med å kombinere flere felt til ett, den inverse handlingen til funksjonen separate();
pivot_longer() — en funksjon som konverterer data fra bredformat til langt format;
pivot_wider() - en funksjon som konverterer data fra langt format til bredt format. Omvendt operasjon av den som utføres av funksjonen pivot_longer().
gather()Utdatert — en funksjon som konverterer data fra bredformat til langt format;
spread()Utdatert - en funksjon som konverterer data fra langt format til bredt format. Omvendt operasjon av den som utføres av funksjonen gather().
Nytt konsept for konvertering av data fra bredt til langt format og omvendt
Tidligere ble funksjoner brukt for denne typen transformasjon gather() и spread(). I løpet av årene med eksistensen av disse funksjonene ble det åpenbart at for de fleste brukere, inkludert forfatteren av pakken, var navnene på disse funksjonene og deres argumenter ikke helt åpenbare, og forårsaket vanskeligheter med å finne dem og forstå hvilke av disse funksjonene som konverterer en datoramme fra bredt til langt format, og omvendt.
I denne forbindelse, i ryddig To nye, viktige funksjoner er lagt til som er designet for å transformere datorammer.
Nye funksjoner pivot_longer() и pivot_wider() ble inspirert av noen av funksjonene i pakken cdata, laget av John Mount og Nina Zumel.
Installerer den nyeste versjonen av tidyr 0.8.3.9000
For å installere den nye, nyeste versjonen av pakken ryddig0.8.3.9000, der nye funksjoner er tilgjengelige, bruk følgende kode.
devtools::install_github("tidyverse/tidyr")
I skrivende stund er disse funksjonene kun tilgjengelige i dev-versjonen av pakken på GitHub.
Overgang til nye funksjoner
Faktisk er det ikke vanskelig å overføre gamle skript for å jobbe med nye funksjoner; for bedre forståelse vil jeg ta et eksempel fra dokumentasjonen av gamle funksjoner og vise hvordan de samme operasjonene utføres med nye. pivot_*() funksjoner.
Konverter bredformat til langt format.
Eksempelkode fra dokumentasjonen for samlefunksjonen
# 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")
Konvertering av langt format til bredformat.
Eksempelkode fra spredningsfunksjonsdokumentasjon
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Fordi i eksemplene ovenfor på arbeid med pivot_longer() и pivot_wider(), i den opprinnelige tabellen bestandene ingen kolonner oppført i argumenter navn_til и verdier_til deres navn må stå i anførselstegn.
En tabell som hjelper deg lettest å finne ut hvordan du bytter til å jobbe med et nytt konsept ryddig.
Merknad fra forfatteren
All teksten nedenfor er adaptiv, jeg vil til og med si fri oversettelse vignetter fra det offisielle nettstedet til tidyverse-biblioteket.
Et enkelt eksempel på konvertering av data fra bredt til langt format
pivot_longer () — gjør datasett lengre ved å redusere antall kolonner og øke antall rader.
For å kjøre eksemplene presentert i artikkelen, må du først koble til de nødvendige pakkene:
library(tidyr)
library(dplyr)
library(readr)
La oss si at vi har en tabell med resultatene av en undersøkelse som (blant annet) spurte folk om deres religion og årsinntekt:
Denne tabellen inneholder respondentenes religionsdata i rader, og inntektsnivåene er spredt over kolonnenavn. Antall respondenter fra hver kategori er lagret i celleverdiene i skjæringspunktet mellom religion og inntektsnivå. For å bringe bordet i et pent, riktig format, er det nok å bruke pivot_longer():
Første argument krage, beskriver hvilke kolonner som må slås sammen. I dette tilfellet er alle kolonner unntatt tid.
argument navn_til gir navnet på variabelen som vil bli opprettet fra navnene på kolonnene vi sammenkoblet.
verdier_til gir navnet på en variabel som vil bli opprettet fra dataene som er lagret i verdiene til cellene i de sammenslåtte kolonnene.
Spesifikasjoner
Dette er en ny funksjonalitet i pakken ryddig, som tidligere ikke var tilgjengelig når du arbeidet med eldre funksjoner.
En spesifikasjon er en dataramme, der hver rad tilsvarer én kolonne i den nye utdatarammen, og to spesielle kolonner som begynner med:
. Navn inneholder det opprinnelige kolonnenavnet.
.verdi inneholder navnet på kolonnen som skal inneholde celleverdiene.
De resterende kolonnene i spesifikasjonen gjenspeiler hvordan den nye kolonnen vil vise navnet på de komprimerte kolonnene fra . Navn.
Spesifikasjonen beskriver metadata som er lagret i et kolonnenavn, med en rad for hver kolonne og en kolonne for hver variabel, kombinert med kolonnenavnet, denne definisjonen kan virke forvirrende for øyeblikket, men etter å ha sett på noen få eksempler vil det bli mye klarere.
Poenget med spesifikasjonen er at du kan hente, modifisere og definere nye metadata for datarammen som konverteres.
For å jobbe med spesifikasjoner når du konverterer en tabell fra et bredt format til et langt format, bruk funksjonen pivot_longer_spec().
Hvordan denne funksjonen fungerer er at den tar en hvilken som helst datoramme og genererer metadataene på den måten som er beskrevet ovenfor.
Som et eksempel, la oss ta who-datasettet som følger med pakken ryddig. Dette datasettet inneholder informasjon gitt av den internasjonale helseorganisasjonen om forekomsten av tuberkulose.
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
La oss bygge spesifikasjonen.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
felt land, iso2, iso3 er allerede variabler. Vår oppgave er å vende kolonnene med new_sp_m014 på newrel_f65.
Navnene på disse kolonnene lagrer følgende informasjon:
prefiks new_ indikerer at kolonnen inneholder data om nye tilfeller av tuberkulose, gjeldende datoramme inneholder kun informasjon om nye sykdommer, så dette prefikset i den aktuelle konteksten har ingen betydning.
sp/rel/sp/ep beskriver en metode for å diagnostisere en sykdom.
Til slutt, for å bruke spesifikasjonen vi opprettet på den originale daterammen som vi må bruke et argument spec i funksjon 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
Alt vi nettopp gjorde kan skjematisk avbildes som følger:
Spesifikasjon som bruker flere verdier (.value)
I eksemplet ovenfor, spesifikasjonskolonnen .verdi inneholdt bare én verdi, i de fleste tilfeller er dette tilfellet.
Men av og til kan det oppstå en situasjon når du trenger å samle inn data fra kolonner med ulike datatyper i verdier. Bruker en eldre funksjon spread() dette ville være ganske vanskelig å gjøre.
Eksemplet nedenfor er hentet fra vignetter til pakken data bord.
Den opprettede datorammen inneholder data om barn i én familie i hver rad. Familier kan ha ett eller to barn. For hvert barn er det gitt data om fødselsdato og kjønn, og dataene for hvert barn er i separate kolonner; vår oppgave er å bringe disse dataene til riktig format for analyse.
Vær oppmerksom på at vi har to variabler med informasjon om hvert barn: deres kjønn og fødselsdato (kolonner med prefikset dåp inneholder fødselsdato, kolonner med prefiks kjønn inneholde barnets kjønn). Det forventede resultatet er at de skal vises i separate kolonner. Vi kan gjøre dette ved å generere en spesifikasjon der kolonnen .value vil ha to forskjellige betydninger.
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
Så la oss ta en trinnvis titt på handlingene utført av koden ovenfor.
pivot_longer_spec(-family) — lag en spesifikasjon som komprimerer alle eksisterende kolonner bortsett fra familiekolonnen.
separate(col = name, into = c(".value", "child")) - del kolonnen . Navn, som inneholder navnene på kildefeltene, ved å bruke understreken og skrive inn de resulterende verdiene i kolonnene .verdi и barn.
mutate(child = parse_number(child)) — transformere feltverdiene barn fra tekst til numerisk datatype.
Nå kan vi bruke den resulterende spesifikasjonen på den originale datarammen og bringe tabellen til ønsket form.
Vi bruker argument na.rm = TRUE, fordi den nåværende formen for dataene tvinger opprettelsen av ekstra rader for ikke-eksisterende observasjoner. Fordi familie 2 har bare ett barn, na.rm = TRUE garanterer at familie 2 vil ha én rad i utgangen.
Konvertering av datorammer fra langt til bredt format
pivot_wider() - er den inverse transformasjonen, og vice versa øker antall kolonner i datorammen ved å redusere antall rader.
Denne typen transformasjon brukes ekstremt sjelden for å bringe data til en nøyaktig form, men denne teknikken kan være nyttig for å lage pivottabeller som brukes i presentasjoner, eller for integrasjon med noen andre verktøy.
Egentlig funksjonene pivot_longer() и pivot_wider() er symmetriske, og produserer handlinger som er omvendt til hverandre, dvs.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) vil returnere den originale df.
Det enkleste eksemplet på å konvertere en tabell til et bredt format
For å demonstrere hvordan funksjonen fungerer pivot_wider() vi vil bruke datasettet fiske_møter, som lagrer informasjon om hvordan ulike stasjoner registrerer bevegelsen av fisk langs elva.
#> # 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
I de fleste tilfeller vil denne tabellen være mer informativ og enklere å bruke hvis du presenterer informasjon for hver stasjon i en egen kolonne.
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>
Dette datasettet registrerer kun informasjon når det er oppdaget fisk av stasjonen, dvs. hvis noen fisk ikke ble registrert av en stasjon, vil ikke disse dataene være i tabellen. Dette betyr at utgangen vil bli fylt med NA.
Men i dette tilfellet vet vi at fraværet av en registrering betyr at fisken ikke ble sett, så vi kan bruke argumentet verdier_fyll i funksjon pivot_wider() og fyll disse manglende verdiene med nuller:
Generering av et kolonnenavn fra flere kildevariabler
Tenk deg at vi har en tabell som inneholder en kombinasjon av produkt, land og år. For å generere en testdatoramme kan du kjøre følgende kode:
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
Vår oppgave er å utvide datarammen slik at én kolonne inneholder data for hver kombinasjon av produkt og land. For å gjøre dette, bare gi argumentet navn_fra en vektor som inneholder navnene på feltene som skal slås sammen.
Du kan også bruke spesifikasjoner for en funksjon pivot_wider(). Men når det sendes til pivot_wider() spesifikasjonen gjør den motsatte konverteringen pivot_longer(): Kolonnene spesifisert i . Navn, ved å bruke verdier fra .verdi og andre kolonner.
For dette datasettet kan du generere en egendefinert spesifikasjon hvis du vil at alle mulige land og produktkombinasjoner skal ha sin egen kolonne, ikke bare de som finnes i dataene:
#> # 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
Flere avanserte eksempler på arbeid med det nye tidyr-konseptet
Rydd opp i data ved å bruke US Census Income and Rent datasettet som eksempel.
Datasett us_leieinntekt inneholder medianinntekt og leieinformasjon for hver stat i USA for 2017 (datasett tilgjengelig i pakken 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
I den formen dataene er lagret i datasettet us_leieinntekt å jobbe med dem er ekstremt upraktisk, så vi ønsker å lage et datasett med kolonner: leie, leie_moe, Kom, inntekt_moe. Det er mange måter å lage denne spesifikasjonen på, men hovedpoenget er at vi må generere hver kombinasjon av variabelverdier og anslag/moeog generer deretter kolonnenavnet.
Noen ganger krever det flere trinn å bringe et datasett til ønsket form.
Datasett verdensbankpop inneholder data fra Verdensbanken om befolkningen i hvert land mellom 2000 og 2018.
Målet vårt er å lage et ryddig datasett med hver variabel i sin egen kolonne. Det er uklart nøyaktig hvilke trinn som trengs, men vi starter med det mest åpenbare problemet: året er spredt over flere kolonner.
For å fikse dette må du bruke funksjonen pivot_longer().
Neste trinn er å se på indikatorvariabelen. 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
Der SP.POP.GROW er befolkningsvekst, SP.POP.TOTL er total befolkning, og SP.URB. * det samme, men bare for urbane områder. La oss dele disse verdiene inn i to variabler: område - område (totalt eller urbant) og en variabel som inneholder faktiske data (befolkning eller vekst):
Tabellering av denne listen er ganske vanskelig fordi det ikke er noen variabel som identifiserer hvilke data som tilhører hvilken kontakt. Vi kan fikse dette ved å merke oss at hver ny kontakts data starter med "navn", så vi kan lage en unik identifikator og øke den med én hver gang feltkolonnen inneholder verdien "navn":
#> # 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
Nå som vi har en unik ID for hver kontakt, kan vi gjøre om feltet og verdien til kolonner:
#> # 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>
Konklusjon
Min personlige mening er at det nye konseptet ryddig virkelig mer intuitiv, og betydelig overlegen i funksjonalitet i forhold til eldre funksjoner spread() и gather(). Jeg håper denne artikkelen hjalp deg med å håndtere pivot_longer() и pivot_wider().