R-pakket Tidr en zijn nieuwe functies pivot_longer en pivot_wider
Verpakking opgeruimder opgenomen in de kern van een van de meest populaire bibliotheken in de R-taal - opruimen.
Het belangrijkste doel van het pakket is om de gegevens in een nauwkeurige vorm te brengen.
Al beschikbaar op Habré publicatie gewijd aan dit pakket, maar dateert uit 2015. En ik wil je vertellen over de meest recente veranderingen, die een paar dagen geleden zijn aangekondigd door de auteur ervan, Hedley Wickham.
SJK: Worden collect() en spread() verouderd?
Hadley Wickham: Tot op zekere hoogte. We zullen het gebruik van deze functies niet langer aanbevelen en er bugs in oplossen, maar ze zullen in hun huidige staat nog steeds in het pakket aanwezig zijn.
Inhoud
Als u geïnteresseerd bent in data-analyse, bent u wellicht geïnteresseerd in mijn telegram и youtube kanalen. Het grootste deel van de inhoud is gewijd aan de R-taal.
doelwit opgeruimder — u helpen de gegevens in een zogenaamde nette vorm te brengen. Nette gegevens zijn gegevens waarbij:
Elke variabele staat in een kolom.
Elke waarneming is een string.
Elke waarde is een cel.
Het is veel eenvoudiger en handiger om bij het uitvoeren van analyses te werken met gegevens die in overzichtelijke gegevens worden gepresenteerd.
Belangrijkste functies inbegrepen in het Tidr-pakket
opgeruimdr bevat een reeks functies die zijn ontworpen om tabellen te transformeren:
fill() — ontbrekende waarden in een kolom vullen met eerdere waarden;
separate() — splitst een veld in meerdere met behulp van een scheidingsteken;
unite() — voert de bewerking uit waarbij verschillende velden in één worden gecombineerd, de omgekeerde actie van de functie separate();
pivot_longer() — een functie die gegevens converteert van breedformaat naar langformaat;
pivot_wider() - een functie die gegevens converteert van lang formaat naar breed formaat. De omgekeerde werking van degene die door de functie wordt uitgevoerd pivot_longer().
gather()verouderd — een functie die gegevens converteert van breedformaat naar langformaat;
spread()verouderd - een functie die gegevens converteert van lang formaat naar breed formaat. De omgekeerde werking van degene die door de functie wordt uitgevoerd gather().
Nieuw concept voor het converteren van gegevens van breed naar lang formaat en omgekeerd
Voorheen werden functies gebruikt voor dit soort transformaties gather() и spread(). Gedurende de jaren dat deze functies bestonden, werd het duidelijk dat voor de meeste gebruikers, inclusief de auteur van het pakket, de namen van deze functies en hun argumenten niet helemaal duidelijk waren, en problemen veroorzaakten bij het vinden ervan en bij het begrijpen welke van deze functies converteert een datumframe van breed naar lang formaat, en omgekeerd.
In dit verband is in opgeruimder Er zijn twee nieuwe, belangrijke functies toegevoegd die zijn ontworpen om datumframes te transformeren.
Nieuwe functies pivot_longer() и pivot_wider() zijn geïnspireerd door enkele functies in het pakket cdata, gemaakt door John Mount en Nina Zumel.
Installatie van de meest recente versie van Tidr 0.8.3.9000
Om de nieuwe, meest recente versie van het pakket te installeren opgeruimder0.8.3.9000, waar nieuwe functies beschikbaar zijn, gebruikt u de volgende code.
devtools::install_github("tidyverse/tidyr")
Op het moment van schrijven zijn deze functies alleen beschikbaar in de dev-versie van het pakket op GitHub.
Overgang naar nieuwe functies
In feite is het niet moeilijk om oude scripts over te zetten om met nieuwe functies te werken; voor een beter begrip zal ik een voorbeeld nemen uit de documentatie van oude functies en laten zien hoe dezelfde bewerkingen worden uitgevoerd met nieuwe functies pivot_*() functies.
Converteer breedformaat naar langformaat.
Voorbeeldcode uit de documentatie van de verzamelfunctie
# 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")
Lang formaat naar breed formaat converteren.
Voorbeeldcode uit de documentatie van de spreadfunctie
# 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 de bovenstaande voorbeelden van werken met pivot_longer() и pivot_wider(), in de oorspronkelijke tabel voorraden geen kolommen vermeld in argumenten namen_naar и waarden_to hun namen moeten tussen aanhalingstekens staan.
Een tabel waarmee u het gemakkelijkst kunt bepalen hoe u kunt overstappen op het werken met een nieuw concept opgeruimder.
Opmerking van de auteur
Alle onderstaande tekst is adaptief, ik zou zelfs zeggen: gratis vertaling vignetten van de officiële riddverse-bibliotheekwebsite.
Een eenvoudig voorbeeld van het converteren van gegevens van breed naar lang formaat
pivot_longer () — maakt datasets langer door het aantal kolommen te verminderen en het aantal rijen te vergroten.
Om de voorbeelden in het artikel uit te voeren, moet u eerst de benodigde pakketten verbinden:
library(tidyr)
library(dplyr)
library(readr)
Laten we zeggen dat we een tabel hebben met de resultaten van een onderzoek waarin mensen (onder andere) werden gevraagd naar hun religie en jaarinkomen:
Deze tabel bevat de religiegegevens van de respondenten in rijen, en de inkomensniveaus zijn verspreid over de kolomnamen. Het aantal respondenten uit elke categorie wordt opgeslagen in de celwaarden op het snijvlak van religie en inkomensniveau. Om de tafel in een netjes, correct formaat te brengen, is het voldoende om te gebruiken pivot_longer():
Eerste argument cols, beschrijft welke kolommen moeten worden samengevoegd. In dit geval zijn alle kolommen behalve niet de tijd of.
argument namen_naar geeft de naam van de variabele die wordt gemaakt op basis van de namen van de kolommen die we hebben samengevoegd.
waarden_to geeft de naam van een variabele die zal worden gemaakt op basis van de gegevens die zijn opgeslagen in de waarden van de cellen van de samengevoegde kolommen.
Specificaties
Dit is een nieuwe functionaliteit van het pakket opgeruimder, dat voorheen niet beschikbaar was bij het werken met oudere functies.
Een specificatie is een dataframe, waarvan elke rij overeenkomt met één kolom in het nieuwe uitvoerdatumframe, en twee speciale kolommen die beginnen met:
. Naam bevat de oorspronkelijke kolomnaam.
.waarde bevat de naam van de kolom die de celwaarden zal bevatten.
De overige kolommen van de specificatie geven weer hoe de nieuwe kolom de naam van de gecomprimeerde kolommen weergeeft . Naam.
De specificatie beschrijft de metagegevens die zijn opgeslagen in een kolomnaam, met één rij voor elke kolom en één kolom voor elke variabele, gecombineerd met de kolomnaam. Deze definitie lijkt op dit moment misschien verwarrend, maar na het bekijken van een paar voorbeelden zal het veel worden. duidelijker.
Het punt van de specificatie is dat u nieuwe metagegevens kunt ophalen, wijzigen en definiëren voor het dataframe dat wordt geconverteerd.
Gebruik de functie om met specificaties te werken bij het converteren van een tabel van een breed formaat naar een lang formaat pivot_longer_spec().
Hoe deze functie werkt, is dat het elk datumframe gebruikt en de metagegevens genereert op de hierboven beschreven manier.
Laten we als voorbeeld de who-gegevensset nemen die bij het pakket wordt geleverd opgeruimder. Deze dataset bevat informatie van de internationale gezondheidsorganisatie over de incidentie van tuberculose.
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
Laten we de specificatie ervan opstellen.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
velden Land, iso2, iso3 zijn al variabelen. Onze taak is om de kolommen om te draaien nieuwe_sp_m014 op newrel_f65.
De namen van deze kolommen bevatten de volgende informatie:
voorvoegsel new_ geeft aan dat de kolom gegevens bevat over nieuwe gevallen van tuberculose, het huidige datumframe bevat alleen informatie over nieuwe ziekten, dus dit voorvoegsel heeft in de huidige context geen enkele betekenis.
sp/rel/sp/ep beschrijft een methode voor het diagnosticeren van een ziekte.
m/f het geslacht van de patiënt.
014/1524/2535/3544/4554/65 leeftijdscategorie van de patiënt.
We kunnen deze kolommen splitsen met behulp van de functie extract()met behulp van reguliere expressie.
#> # 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
Let op de kolom . Naam zou ongewijzigd moeten blijven, aangezien dit onze index is van de kolomnamen van de oorspronkelijke gegevensset.
Geslacht en leeftijd (kolommen geslacht и leeftijd) hebben vaste en bekende waarden, dus het wordt aanbevolen om deze kolommen naar factoren te converteren:
Ten slotte, om de specificatie die we hebben gemaakt toe te passen op het oorspronkelijke datumframe die we moeten een argument gebruiken spec in functie 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 we zojuist hebben gedaan, kan als volgt schematisch worden weergegeven:
Specificatie met behulp van meerdere waarden (.value)
In het bovenstaande voorbeeld de specificatiekolom .waarde slechts één waarde bevatte, is dit in de meeste gevallen het geval.
Maar af en toe kan er een situatie ontstaan waarin u gegevens moet verzamelen uit kolommen met verschillende gegevenstypen in waarden. Een verouderde functie gebruiken spread() dit zou heel moeilijk zijn om te doen.
Het onderstaande voorbeeld is afkomstig uit vignetten naar het pakket data tafel.
Het gemaakte datumframe bevat gegevens over kinderen van één gezin op elke regel. Gezinnen kunnen één of twee kinderen hebben. Voor elk kind worden gegevens verstrekt over de geboortedatum en het geslacht, en de gegevens voor elk kind staan in aparte kolommen; het is onze taak om deze gegevens in het juiste formaat voor analyse te brengen.
Houd er rekening mee dat we twee variabelen hebben met informatie over elk kind: hun geslacht en geboortedatum (kolommen met het voorvoegsel Doop bevatten geboortedatum, kolommen met voorvoegsel geslacht het geslacht van het kind bevatten). Het verwachte resultaat is dat ze in afzonderlijke kolommen moeten verschijnen. Dit kunnen we doen door een specificatie te genereren waarin de column .value zal twee verschillende betekenissen hebben.
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
Laten we dus stap voor stap kijken naar de acties die door de bovenstaande code worden uitgevoerd.
pivot_longer_spec(-family) — maak een specificatie die alle bestaande kolommen comprimeert, behalve de familiekolom.
separate(col = name, into = c(".value", "child")) - splits de kolom . Naam, dat de namen van de bronvelden bevat, waarbij het onderstrepingsteken wordt gebruikt en de resulterende waarden in de kolommen worden ingevoerd .waarde и kind.
mutate(child = parse_number(child)) — transformeer de veldwaarden kind van tekst naar numeriek gegevenstype.
Nu kunnen we de resulterende specificatie toepassen op het originele dataframe en de tabel in de gewenste vorm brengen.
Wij gebruiken argumenten na.rm = TRUE, omdat de huidige vorm van de gegevens het creëren van extra rijen voor niet-bestaande waarnemingen afdwingt. Omdat gezin 2 heeft slechts één kind, na.rm = TRUE garandeert dat familie 2 één rij in de uitvoer heeft.
Datumframes converteren van lang naar breed formaat
pivot_wider() - is de omgekeerde transformatie, en omgekeerd vergroot het aantal kolommen van het datumframe door het aantal rijen te verkleinen.
Dit soort transformatie wordt uiterst zelden gebruikt om gegevens in een nauwkeurige vorm te brengen, maar deze techniek kan nuttig zijn voor het maken van draaitabellen die in presentaties worden gebruikt, of voor integratie met andere tools.
Eigenlijk de functies pivot_longer() и pivot_wider() zijn symmetrisch en veroorzaken acties die omgekeerd zijn aan elkaar, dat wil zeggen: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) zal de originele df retourneren.
Het eenvoudigste voorbeeld van het converteren van een tabel naar een breed formaat
Om te demonstreren hoe de functie werkt pivot_wider() we zullen de dataset gebruiken vis_ontmoetingen, waarin informatie wordt opgeslagen over hoe verschillende stations de beweging van vissen langs de rivier registreren.
#> # 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 de meeste gevallen zal deze tabel informatiever en gemakkelijker te gebruiken zijn als u de informatie voor elk station in een aparte kolom presenteert.
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>
Deze dataset registreert alleen informatie wanneer er vissen zijn gedetecteerd door het station, d.w.z. als een vis niet door een bepaald station is geregistreerd, staan deze gegevens niet in de tabel. Dit betekent dat de uitvoer wordt gevuld met NA.
In dit geval weten we echter dat het ontbreken van een record betekent dat de vis niet is gezien, dus we kunnen het argument gebruiken waarden_fill in functie pivot_wider() en vul deze ontbrekende waarden in met nullen:
Een kolomnaam genereren op basis van meerdere bronvariabelen
Stel je voor dat we een tabel hebben met daarin een combinatie van product, land en jaar. Om een testdatumframe te genereren, kunt u de volgende code uitvoeren:
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
Het is onze taak om het dataframe uit te breiden, zodat één kolom gegevens bevat voor elke combinatie van product en land. Om dit te doen, geeft u gewoon het argument door namen_van een vector die de namen bevat van de velden die moeten worden samengevoegd.
U kunt ook specificaties op een functie toepassen pivot_wider(). Maar wanneer ingediend pivot_wider() de specificatie doet de tegenovergestelde conversie pivot_longer(): De kolommen gespecificeerd in . Naam, met behulp van waarden uit .waarde en andere kolommen.
Voor deze dataset kunt u een aangepaste specificatie genereren als u wilt dat elke mogelijke land- en productcombinatie een eigen kolom heeft, en niet alleen de kolommen die in de gegevens aanwezig zijn:
#> # 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
Een aantal geavanceerde voorbeelden van het werken met het nieuwe closer-concept
Gegevens opschonen met de dataset US Census Income and Rent als voorbeeld.
gegevensset us_rent_income bevat mediaan inkomen en huurinformatie voor elke staat in de VS voor 2017 (dataset beschikbaar in pakket nette volkstelling).
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 de vorm waarin de gegevens in de dataset zijn opgeslagen us_rent_income ermee werken is buitengewoon lastig, dus we willen graag een dataset met kolommen maken: huren, huur_moe, hoe, inkomen_moe. Er zijn veel manieren om deze specificatie te maken, maar het belangrijkste punt is dat we elke combinatie van variabele waarden en waarden moeten genereren schatting/moeen genereer vervolgens de kolomnaam.
Soms zijn er meerdere stappen nodig om een dataset in de gewenste vorm te brengen.
Gegevensset wereld_bank_pop bevat gegevens van de Wereldbank over de bevolking van elk land tussen 2000 en 2018.
Ons doel is om een nette dataset te maken met elke variabele in een eigen kolom. Het is onduidelijk welke stappen precies nodig zijn, maar we beginnen met het meest voor de hand liggende probleem: het jaartal is verspreid over meerdere kolommen.
Om dit op te lossen, moet je de functie gebruiken pivot_longer().
De volgende stap is het kijken naar de indicatorvariabele. 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 de bevolkingsgroei is, is SP.POP.TOTL de totale bevolking, en SP.URB. * hetzelfde, maar alleen voor stedelijke gebieden. Laten we deze waarden in twee variabelen verdelen: oppervlakte - oppervlakte (totaal of stedelijk) en een variabele die feitelijke gegevens bevat (bevolking of groei):
Het in tabelvorm brengen van deze lijst is behoorlijk lastig omdat er geen variabele is die identificeert welke gegevens bij welk contact horen. We kunnen dit oplossen door op te merken dat de gegevens van elk nieuw contact beginnen met "naam", zodat we een unieke identificatie kunnen creëren en deze met één kunnen verhogen telkens wanneer de veldkolom de waarde "naam" bevat:
#> # 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 we voor elk contact een unieke ID hebben, kunnen we het veld en de waarde in kolommen omzetten:
#> # 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>
Conclusie
Mijn persoonlijke mening is dat het nieuwe concept opgeruimder echt intuïtiever en qua functionaliteit aanzienlijk superieur aan oudere functies spread() и gather(). Ik hoop dat dit artikel je heeft geholpen ermee om te gaan pivot_longer() и pivot_wider().