ProHoster > Bloc > Administració > El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider
El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider
Paquet ordenar inclòs al nucli d'una de les biblioteques més populars en llenguatge R: endreçat.
L'objectiu principal del paquet és portar les dades en una forma precisa.
Ja disponible a Habré publicació dedicat a aquest paquet, però es remunta al 2015. I us vull parlar dels canvis més actuals, que va anunciar fa uns dies la seva autora, Hedley Wickham.
SJK: Gather() i spread() quedaran obsolets?
Hadley Wickham: Fins a cert punt. Ja no recomanarem l'ús d'aquestes funcions i solucionarem errors, però continuaran presents al paquet en el seu estat actual.
Contingut
Si esteu interessats en l'anàlisi de dades, potser us interessa el meu telegram и youtube canals. La major part del contingut està dedicat al llenguatge R.
Objectiu ordenar — ajudar-vos a portar les dades a una forma anomenada ordenada. Les dades netes són dades on:
Cada variable es troba en una columna.
Cada observació és una cadena.
Cada valor és una cel·la.
És molt més fàcil i còmode treballar amb dades que es presenten en dades ordenades quan es realitza l'anàlisi.
Funcions principals incloses al paquet tidyr
tidyr conté un conjunt de funcions dissenyades per transformar taules:
fill() — omplir els valors que falten en una columna amb valors anteriors;
separate() — divideix un camp en diversos mitjançant un separador;
unite() — realitza l'operació de combinar diversos camps en un, l'acció inversa de la funció separate();
pivot_longer() — una funció que converteix dades de format ample a format llarg;
pivot_wider() - una funció que converteix dades de format llarg a format ample. Operació inversa a la realitzada per la funció pivot_longer().
gather()obsolet — una funció que converteix dades de format ample a format llarg;
spread()obsolet - una funció que converteix dades de format llarg a format ample. Operació inversa a la realitzada per la funció gather().
Nou concepte per convertir dades de format ample a format llarg i viceversa
Anteriorment, s'utilitzaven funcions per a aquest tipus de transformació gather() и spread(). Al llarg dels anys d'existència d'aquestes funcions, es va fer evident que per a la majoria d'usuaris, inclòs l'autor del paquet, els noms d'aquestes funcions i els seus arguments no eren del tot evidents, i van causar dificultats per trobar-les i entendre quina d'aquestes funcions convertia. un marc de data de format ample a format llarg, i viceversa.
En aquest sentit, en ordenar S'han afegit dues funcions noves i importants que estan dissenyades per transformar els marcs de data.
Noves característiques pivot_longer() и pivot_wider() es van inspirar en algunes de les característiques del paquet cdata, creat per John Mount i Nina Zumel.
Instal·lant la versió més actual de tidyr 0.8.3.9000
Per instal·lar la versió nova i més actual del paquet ordenar0.8.3.9000, quan hi hagi noves funcions disponibles, utilitzeu el codi següent.
devtools::install_github("tidyverse/tidyr")
En el moment d'escriure, aquestes funcions només estan disponibles a la versió de desenvolupament del paquet a GitHub.
Transició a noves funcions
De fet, convertir scripts antics per treballar amb noves funcions no és difícil, per a una major comprensió, agafaré un exemple de la documentació de les funcions antigues i mostraré com es realitzen les mateixes operacions utilitzant les noves. pivot_*() funcions.
Converteix format ample a format llarg.
Exemple de codi de la documentació de la funció de recopilació
# 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")
Conversió de format llarg a format ample.
Exemple de codi de la documentació de la funció de difusió
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Perquè en els exemples anteriors de treball amb pivot_longer() и pivot_wider(), a la taula original estocs no hi ha columnes enumerades als arguments noms_a и valors_a els seus noms han d'anar entre cometes.
Una taula que us ajudarà a esbrinar més fàcilment com canviar a treballar amb un concepte nou ordenar.
Nota de l'autor
Tot el text següent és adaptatiu, fins i tot diria traducció gratuïta vinyetes del lloc web oficial de la biblioteca tidyverse.
Un exemple senzill de conversió de dades de format ample a format llarg
pivot_longer () — fa que els conjunts de dades siguin més llargs reduint el nombre de columnes i augmentant el nombre de files.
Per executar els exemples presentats a l'article, primer heu de connectar els paquets necessaris:
library(tidyr)
library(dplyr)
library(readr)
Suposem que tenim una taula amb els resultats d'una enquesta que (entre altres coses) preguntava a la gent sobre la seva religió i els ingressos anuals:
Aquesta taula conté les dades religioses dels enquestats en files i els nivells d'ingressos es distribueixen entre els noms de les columnes. El nombre d'enquestats de cada categoria s'emmagatzema als valors de la cel·la a la intersecció de la religió i el nivell d'ingressos. Per portar la taula en un format net i correcte, n'hi ha prou amb utilitzar pivot_longer():
Primer argument collars, descriu quines columnes s'han de combinar. En aquest cas, totes les columnes excepte temps.
Argument noms_a dóna el nom de la variable que es crearà a partir dels noms de les columnes que hem concatenat.
valors_a dóna el nom d'una variable que es crearà a partir de les dades emmagatzemades en els valors de les cel·les de les columnes combinades.
Спецификации
Aquesta és una nova funcionalitat del paquet ordenar, que abans no estava disponible quan es treballava amb funcions heretades.
Una especificació és un marc de dades, cada fila del qual correspon a una columna del nou marc de data de sortida i dues columnes especials que comencen per:
. Nom conté el nom de la columna original.
.valor conté el nom de la columna que contindrà els valors de la cel·la.
Les columnes restants de l'especificació reflecteixen com la nova columna mostrarà el nom de les columnes comprimides . Nom.
L'especificació descriu les metadades emmagatzemades en un nom de columna, amb una fila per a cada columna i una columna per a cada variable, combinades amb el nom de la columna, aquesta definició pot semblar confusa de moment, però després de mirar alguns exemples es convertirà en molt. més clar.
El punt de l'especificació és que podeu recuperar, modificar i definir metadades noves per al marc de dades que s'està convertint.
Per treballar amb especificacions en convertir una taula d'un format ampli a un format llarg, utilitzeu la funció pivot_longer_spec().
El funcionament d'aquesta funció és que pren qualsevol marc de data i genera les seves metadades de la manera descrita anteriorment.
Com a exemple, prenem el conjunt de dades who que es proporciona amb el paquet ordenar. Aquest conjunt de dades conté informació proporcionada per l'organització internacional de la salut sobre la incidència de la tuberculosi.
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
Construïm la seva especificació.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
camps país, isoxnumx, isoxnumx ja són variables. La nostra tasca és capgirar les columnes amb nou_sp_m014 en newrel_f65.
Els noms d'aquestes columnes emmagatzemen la informació següent:
Prefix new_ indica que la columna conté dades sobre nous casos de tuberculosi, el marc de data actual només conté informació sobre noves malalties, de manera que aquest prefix en el context actual no té cap significat.
sp/rel/sp/ep descriu un mètode per diagnosticar una malaltia.
m/f gènere del pacient.
014/1524/2535/3544/4554/65 interval d'edat del pacient.
Podem dividir aquestes columnes mitjançant la funció extract()utilitzant expressió regular.
#> # A tibble: 56 x 5
#> .name .value diagnosis gender age
#> <chr> <chr> <chr> <chr> <chr>
#> 1 new_sp_m014 count sp m 014
#> 2 new_sp_m1524 count sp m 1524
#> 3 new_sp_m2534 count sp m 2534
#> 4 new_sp_m3544 count sp m 3544
#> 5 new_sp_m4554 count sp m 4554
#> 6 new_sp_m5564 count sp m 5564
#> 7 new_sp_m65 count sp m 65
#> 8 new_sp_f014 count sp f 014
#> 9 new_sp_f1524 count sp f 1524
#> 10 new_sp_f2534 count sp f 2534
#> # … with 46 more rows
Si us plau, tingueu en compte la columna . Nom hauria de romandre sense canvis, ja que aquest és el nostre índex als noms de columnes del conjunt de dades original.
Gènere i edat (columnes gènere и edat) tenen valors fixos i coneguts, per la qual cosa es recomana convertir aquestes columnes en factors:
Finalment, per aplicar l'especificació que hem creat al marc de data original que hem de fer servir un argument spec en funció 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
Tot el que acabem de fer es pot representar esquemàticament de la següent manera:
Especificació utilitzant diversos valors (.value)
A l'exemple anterior, la columna d'especificacions .valor contenia només un valor, en la majoria dels casos aquest és el cas.
Però de vegades pot sorgir una situació quan necessiteu recopilar dades de columnes amb diferents tipus de dades en valors. Ús d'una funció heretada spread() això seria bastant difícil de fer.
L'exemple següent està extret de vinyetes al paquet dades.taula.
El marc de data creat conté dades sobre els fills d'una família a cada fila. Les famílies poden tenir un o dos fills. Per a cada nen, es proporcionen dades sobre la data de naixement i el gènere, i les dades de cada nen es troben en columnes separades; la nostra tasca és portar aquestes dades al format correcte per analitzar-les.
Tingueu en compte que tenim dues variables amb informació sobre cada nen: el seu gènere i la data de naixement (columnes amb el prefix DOP conté data de naixement, columnes amb prefix gènere continguin el sexe del nen). El resultat esperat és que apareguin en columnes separades. Podem fer-ho generant una especificació en què la columna .value tindrà dos significats diferents.
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
Per tant, fem una ullada pas a pas a les accions realitzades pel codi anterior.
pivot_longer_spec(-family) — Creeu una especificació que comprimiu totes les columnes existents excepte la columna de la família.
separate(col = name, into = c(".value", "child")) - dividir la columna . Nom, que conté els noms dels camps d'origen, utilitzant el guió baix i introduint els valors resultants a les columnes .valor и nen.
mutate(child = parse_number(child)) — transformar els valors del camp nen del text al tipus de dades numèriques.
Ara podem aplicar l'especificació resultant al marc de dades original i portar la taula a la forma desitjada.
Utilitzem arguments na.rm = TRUE, perquè la forma actual de les dades obliga a crear files addicionals per a observacions inexistents. Perquè la família 2 només té un fill, na.rm = TRUE garanteix que la família 2 tindrà una fila a la sortida.
Conversió de marcs de data de format llarg a format ample
pivot_wider() - és la transformació inversa, i viceversa augmenta el nombre de columnes del marc de data reduint el nombre de files.
Aquest tipus de transformació s'utilitza molt poques vegades per portar les dades en una forma precisa, però aquesta tècnica pot ser útil per crear taules dinàmiques utilitzades en presentacions o per a la integració amb altres eines.
De fet, les funcions pivot_longer() и pivot_wider() són simètrics i produeixen accions inverses entre si, és a dir: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) retornarà el df original.
L'exemple més senzill de convertir una taula a un format ampli
Per demostrar com funciona la funció pivot_wider() utilitzarem el conjunt de dades trobades_peixos, que emmagatzema informació sobre com les diferents estacions registren el moviment dels peixos al llarg del riu.
#> # 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
En la majoria dels casos, aquesta taula serà més informativa i més fàcil d'utilitzar si presenteu la informació de cada estació en una columna independent.
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>
Aquest conjunt de dades només registra informació quan l'estació ha detectat peixos, és a dir. si alguna estació no va registrar cap peix, aquestes dades no apareixeran a la taula. Això significa que la sortida s'omplirà amb NA.
No obstant això, en aquest cas sabem que l'absència d'un registre significa que el peix no es va veure, per tant podem utilitzar l'argument omplir_valors en funció pivot_wider() i ompliu aquests valors que falten amb zeros:
Generació d'un nom de columna a partir de diverses variables d'origen
Imagineu que tenim una taula que conté una combinació de producte, país i any. Per generar un marc de data de prova, podeu executar el codi següent:
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
La nostra tasca és ampliar el marc de dades de manera que una columna contingui dades per a cada combinació de producte i país. Per fer-ho, només cal passar l'argument noms_de un vector que conté els noms dels camps a combinar.
També podeu aplicar especificacions a una funció pivot_wider(). Però quan es sotmet a pivot_wider() l'especificació fa la conversió oposada pivot_longer(): les columnes especificades a . Nom, utilitzant valors de .valor i altres columnes.
Per a aquest conjunt de dades, podeu generar una especificació personalitzada si voleu que cada possible combinació de país i producte tingui la seva pròpia columna, no només les que hi ha a les dades:
#> # 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
Diversos exemples avançats de treball amb el nou concepte tidyr
Neteja de dades utilitzant el conjunt de dades d'ingressos i lloguers del cens dels EUA com a exemple.
Conjunt de dades us_rent_ingressos conté informació sobre els ingressos i lloguers mitjans per a cada estat dels EUA per al 2017 (conjunt de dades disponible al paquet cens ordenat).
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 forma en què s'emmagatzemen les dades al conjunt de dades us_rent_ingressos treballar amb ells és extremadament inconvenient, així que ens agradaria crear un conjunt de dades amb columnes: llogar, rent_moe, Venir, income_moe. Hi ha moltes maneres de crear aquesta especificació, però el punt principal és que hem de generar totes les combinacions de valors variables i estimació/moei després genereu el nom de la columna.
De vegades, portar un conjunt de dades a la forma desitjada requereix diversos passos.
Conjunt de dades world_bank_pop conté dades del Banc Mundial sobre la població de cada país entre el 2000 i el 2018.
El nostre objectiu és crear un conjunt de dades net amb cada variable a la seva pròpia columna. No està clar quins passos calen exactament, però començarem pel problema més evident: l'any es distribueix en diverses columnes.
Per solucionar-ho, heu d'utilitzar la funció pivot_longer().
El següent pas és mirar la variable indicadora. 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
On SP.POP.GROW és el creixement de la població, SP.POP.TOTL és la població total i SP.URB. * el mateix, però només per a zones urbanes. Dividim aquests valors en dues variables: àrea - àrea (total o urbana) i una variable que conté dades reals (població o creixement):
La tabulació d'aquesta llista és bastant difícil perquè no hi ha cap variable que identifiqui quines dades pertanyen a quin contacte. Podem solucionar-ho observant que les dades de cada contacte nou comencen amb "nom", de manera que podem crear un identificador únic i augmentar-lo en un cada vegada que la columna del camp contingui el valor "nom":
#> # 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
Ara que tenim un identificador únic per a cada contacte, podem convertir el camp i el valor en columnes:
#> # 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>
Conclusió
La meva opinió personal és que el nou concepte ordenar realment més intuïtiu i significativament superior en funcionalitat a les funcions heretades spread() и gather(). Espero que aquest article t'hagi ajudat a tractar-ho pivot_longer() и pivot_wider().