R-pakke tidyr og dens nye funktioner pivot_longer og pivot_wider
pakke ryddelige inkluderet i kernen af et af de mest populære biblioteker i R-sproget - ryddelige.
Hovedformålet med pakken er at bringe dataene i en nøjagtig form.
Allerede tilgængelig på Habré publikation dedikeret til denne pakke, men den går tilbage til 2015. Og jeg vil gerne fortælle dig om de mest aktuelle ændringer, som blev annonceret for et par dage siden af dens forfatter, Hedley Wickham.
SJK: Vil gather() og spread() blive forældet?
Hadley Wickham: Til en vis grad. Vi vil ikke længere anbefale brugen af disse funktioner og rette fejl i dem, men de vil fortsat være til stede i pakken i deres nuværende tilstand.
Indhold
Hvis du er interesseret i dataanalyse, er du måske interesseret i min telegram и youtube kanaler. Det meste af indholdet er dedikeret til R-sproget.
mål ryddelige — hjælpe dig med at bringe dataene til en såkaldt pæn form. Pæne data er data, hvor:
Hver variabel er i en kolonne.
Hver observation er en streng.
Hver værdi er en celle.
Det er meget nemmere og mere bekvemt at arbejde med data, der præsenteres i ryddelige data, når man udfører analyser.
Hovedfunktioner inkluderet i tidyr-pakken
tidyr indeholder et sæt funktioner designet til at transformere tabeller:
fill() — udfyldning af manglende værdier i en kolonne med tidligere værdier;
separate() — opdeler et felt i flere ved hjælp af en separator;
unite() — udfører operationen med at kombinere flere felter til ét, den omvendte handling af funktionen separate();
pivot_longer() — en funktion, der konverterer data fra bredformat til langt format;
pivot_wider() - en funktion, der konverterer data fra langt format til bredformat. Den omvendte operation af den, der udføres af funktionen pivot_longer().
gather()forældet — en funktion, der konverterer data fra bredformat til langt format;
spread()forældet - en funktion, der konverterer data fra langt format til bredformat. Den omvendte operation af den, der udføres af funktionen gather().
Nyt koncept til konvertering af data fra bredt til langt format og omvendt
Tidligere blev funktioner brugt til denne form for transformation gather() и spread(). I løbet af årene, hvor disse funktioner eksisterede, blev det tydeligt, at for de fleste brugere, inklusive forfatteren af pakken, var navnene på disse funktioner og deres argumenter ikke helt indlysende, og det forårsagede vanskeligheder med at finde dem og forstå, hvilke af disse funktioner der konverterer en datoramme fra bredt til langt format og omvendt.
I denne forbindelse, i ryddelige To nye, vigtige funktioner er blevet tilføjet, der er designet til at transformere datorammer.
Nye funktioner pivot_longer() и pivot_wider() var inspireret af nogle af funktionerne i pakken cdata, skabt af John Mount og Nina Zumel.
Installerer den nyeste version af tidyr 0.8.3.9000
For at installere den nye, mest aktuelle version af pakken ryddelige0.8.3.9000, hvor nye funktioner er tilgængelige, skal du bruge følgende kode.
devtools::install_github("tidyverse/tidyr")
I skrivende stund er disse funktioner kun tilgængelige i dev-versionen af pakken på GitHub.
Overgang til nye funktioner
Faktisk er det ikke svært at overføre gamle scripts til at arbejde med nye funktioner; for bedre forståelse vil jeg tage et eksempel fra dokumentationen af gamle funktioner og vise, hvordan de samme operationer udføres med nye. pivot_*() funktioner.
Konverter bredformat til langt format.
Eksempelkode fra samlefunktionsdokumentationen
# 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 af langt format til bredformat.
Eksempelkode fra spredningsfunktionsdokumentation
# 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 ovenstående eksempler på at arbejde med pivot_longer() и pivot_wider(), i den originale tabel lagre ingen kolonner angivet i argumenter navne_til и værdier_til deres navne skal stå i anførselstegn.
En tabel, der hjælper dig nemmest med at finde ud af, hvordan du skifter til at arbejde med et nyt koncept ryddelige.
Notat fra forfatteren
Al teksten nedenfor er adaptiv, jeg vil endda sige fri oversættelse vignetter fra det officielle tidyverse biblioteks websted.
Et simpelt eksempel på konvertering af data fra bredt til langt format
pivot_longer () — gør datasæt længere ved at reducere antallet af kolonner og øge antallet af rækker.
For at køre de eksempler, der præsenteres i artiklen, skal du først forbinde de nødvendige pakker:
library(tidyr)
library(dplyr)
library(readr)
Lad os sige, at vi har en tabel med resultaterne af en undersøgelse, der (blandt andet) spurgte folk om deres religion og årlige indkomst:
Denne tabel indeholder respondenternes religionsdata i rækker, og indkomstniveauer er spredt på tværs af kolonnenavne. Antallet af respondenter fra hver kategori er gemt i celleværdierne i skæringspunktet mellem religion og indkomstniveau. For at bringe bordet i et pænt, korrekt format, er det nok at bruge pivot_longer():
Første argument cols, beskriver hvilke kolonner der skal flettes. I dette tilfælde er alle kolonner undtagen tid.
argument navne_til giver navnet på den variabel, der vil blive oprettet ud fra navnene på de kolonner, vi sammenkædede.
værdier_til giver navnet på en variabel, der vil blive oprettet ud fra de data, der er gemt i værdierne af cellerne i de flettede kolonner.
specifikation
Dette er en ny funktionalitet i pakken ryddelige, som tidligere ikke var tilgængelig, når man arbejdede med ældre funktioner.
En specifikation er en dataramme, hvor hver række svarer til én kolonne i den nye outputdatoramme og to specielle kolonner, der begynder med:
. Navn indeholder det oprindelige kolonnenavn.
.værdi indeholder navnet på den kolonne, der skal indeholde celleværdierne.
De resterende kolonner i specifikationen afspejler, hvordan den nye kolonne vil vise navnet på de komprimerede kolonner fra . Navn.
Specifikationen beskriver de metadata, der er gemt i et kolonnenavn, med en række for hver kolonne og en kolonne for hver variabel, kombineret med kolonnenavnet, denne definition kan virke forvirrende i øjeblikket, men efter at have kigget på et par eksempler vil det blive meget klarere.
Pointen med specifikationen er, at du kan hente, ændre og definere nye metadata for den dataramme, der konverteres.
For at arbejde med specifikationer, når du konverterer en tabel fra et bredt format til et langt format, skal du bruge funktionen pivot_longer_spec().
Hvordan denne funktion fungerer er, at den tager en hvilken som helst datoramme og genererer dens metadata på den måde, der er beskrevet ovenfor.
Som et eksempel, lad os tage who-datasættet, der leveres med pakken ryddelige. Dette datasæt indeholder oplysninger leveret af den internationale sundhedsorganisation om forekomsten af 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
Lad os bygge dens specifikation.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
felter land, isoxnumx, isoxnumx er allerede variable. Vores opgave er at vende kolonnerne med new_sp_m014 på newrel_f65.
Navnene på disse kolonner gemmer følgende oplysninger:
præfiks new_ angiver, at kolonnen indeholder data om nye tilfælde af tuberkulose, den aktuelle datoramme indeholder kun oplysninger om nye sygdomme, så dette præfiks i den aktuelle sammenhæng har ingen betydning.
sp/rel/sp/ep beskriver en metode til at diagnosticere en sygdom.
Til sidst, for at anvende specifikationen, vi oprettede til den originale datoramme der vi skal bruge et argument spec i funktion 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, hvad vi lige har lavet, kan skematisk afbildes som følger:
Specifikation ved hjælp af flere værdier (.value)
I eksemplet ovenfor, specifikationskolonnen .værdi indeholdt kun én værdi, i de fleste tilfælde er dette tilfældet.
Men af og til kan der opstå en situation, hvor du skal indsamle data fra kolonner med forskellige datatyper i værdier. Brug af en ældre funktion spread() dette ville være ret svært at gøre.
Eksemplet nedenfor er taget fra vignetter til pakken data.tabel.
Den oprettede datoramme indeholder data om børn i én familie i hver række. Familier kan have et eller to børn. For hvert barn oplyses data om fødselsdato og køn, og dataene for hvert barn er i separate kolonner; vores opgave er at bringe disse data til det korrekte format til analyse.
Bemærk venligst, at vi har to variabler med oplysninger om hvert barn: deres køn og fødselsdato (kolonner med præfikset dåb indeholder fødselsdato, kolonner med præfiks køn indeholde barnets køn). Det forventede resultat er, at de skal vises i separate kolonner. Det kan vi gøre ved at generere en specifikation, hvori kolonnen .value vil have to forskellige 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å lad os tage et trin-for-trin-kig på de handlinger, der udføres af ovenstående kode.
pivot_longer_spec(-family) — opret en specifikation, der komprimerer alle eksisterende kolonner undtagen familiekolonnen.
separate(col = name, into = c(".value", "child")) - opdele kolonnen . Navn, som indeholder navnene på kildefelterne, ved at bruge understregningen og indtaste de resulterende værdier i kolonnerne .værdi и barn.
mutate(child = parse_number(child)) — transformere feltværdierne barn fra tekst til numerisk datatype.
Nu kan vi anvende den resulterende specifikation på den originale dataramme og bringe tabellen til den ønskede form.
Vi bruger argumenter na.rm = TRUE, fordi den nuværende form for dataene tvinger oprettelsen af ekstra rækker til ikke-eksisterende observationer. Fordi familie 2 har kun ét barn, na.rm = TRUE garanterer, at familie 2 vil have én række i outputtet.
Konvertering af datorammer fra langt til bredt format
pivot_wider() - er den omvendte transformation, og vice versa øger antallet af kolonner i datorammen ved at reducere antallet af rækker.
Denne form for transformation bruges ekstremt sjældent til at bringe data i en nøjagtig form, men denne teknik kan være nyttig til at skabe pivottabeller, der bruges i præsentationer, eller til integration med nogle andre værktøjer.
Faktisk funktionerne pivot_longer() и pivot_wider() er symmetriske og frembringer handlinger i forhold til hinanden, 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 eksempel på at konvertere en tabel til et bredt format
For at demonstrere, hvordan funktionen fungerer pivot_wider() vi vil bruge datasættet fiske_møder, som gemmer information om, hvordan forskellige stationer registrerer bevægelsen af fisk langs floden.
#> # 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 tilfælde vil denne tabel være mere informativ og lettere at bruge, hvis du præsenterer oplysninger for hver station i en separat 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 datasæt registrerer kun information, når der er opdaget fisk af stationen, dvs. hvis nogen fisk ikke blev registreret af en station, vil disse data ikke være i tabellen. Det betyder, at outputtet vil være fyldt med NA.
Men i dette tilfælde ved vi, at fraværet af en registrering betyder, at fisken ikke blev set, så vi kan bruge argumentet værdier_fyld i funktion pivot_wider() og udfyld disse manglende værdier med nuller:
Generering af et kolonnenavn fra flere kildevariabler
Forestil dig, at vi har en tabel, der indeholder en kombination af produkt, land og år. For at generere en testdatoramme kan du kø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
Vores opgave er at udvide datarammen, så en kolonne indeholder data for hver kombination af produkt og land. For at gøre dette skal du bare sende argumentet navne_fra en vektor, der indeholder navnene på de felter, der skal flettes.
Du kan også anvende specifikationer til en funktion pivot_wider(). Men når indsendt til pivot_wider() specifikationen udfører den modsatte konvertering pivot_longer(): De kolonner, der er angivet i . Navn, ved hjælp af værdier fra .værdi og andre kolonner.
For dette datasæt kan du generere en brugerdefineret specifikation, hvis du ønsker, at alle mulige lande og produktkombinationer skal have sin egen kolonne, ikke kun dem, der findes 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 avancerede eksempler på at arbejde med det nye tidyr koncept
Oprydning af data ved hjælp af US Census Income and Rent-datasættet som eksempel.
Datasæt us_leje_indkomst indeholder medianindkomst og lejeoplysninger for hver stat i USA for 2017 (datasæt tilgængeligt 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 form, som dataene er gemt i datasættet us_leje_indkomst at arbejde med dem er ekstremt ubelejligt, så vi vil gerne oprette et datasæt med kolonner: leje, leje_moe, Kom, indkomst_moe. Der er mange måder at oprette denne specifikation på, men hovedpointen er, at vi skal generere hver kombination af variable værdier og skøn/moeog generer derefter kolonnenavnet.
Nogle gange kræver det flere trin at bringe et datasæt i den ønskede form.
Datasæt verdensbankpop indeholder Verdensbankens data om befolkningen i hvert land mellem 2000 og 2018.
Vores mål er at skabe et pænt datasæt med hver variabel i sin egen kolonne. Det er uklart, præcis hvilke trin der er nødvendige, men vi starter med det mest åbenlyse problem: året er spredt over flere kolonner.
For at løse dette skal du bruge funktionen pivot_longer().
Det næste trin er at se på indikatorvariablen. 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
Hvor SP.POP.GROW er befolkningstilvækst, er SP.POP.TOTL den samlede befolkning og SP.URB. * det samme, men kun for byområder. Lad os opdele disse værdier i to variable: areal - areal (totalt eller bymæssigt) og en variabel, der indeholder faktiske data (befolkning eller vækst):
Det er ret vanskeligt at opstille denne liste, fordi der ikke er nogen variabel, der identificerer, hvilke data der hører til hvilken kontaktperson. Vi kan rette dette ved at bemærke, at dataene for hver ny kontakt starter med "navn", så vi kan oprette en unik identifikator og øge den med én, hver gang feltkolonnen indeholder værdien "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
Nu hvor vi har et unikt ID for hver kontakt, kan vi omdanne feltet og værdien 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>
Konklusion
Min personlige mening er, at det nye koncept ryddelige virkelig mere intuitiv og væsentligt overlegen i funktionalitet i forhold til ældre funktioner spread() и gather(). Jeg håber, at denne artikel hjalp dig med at håndtere pivot_longer() и pivot_wider().