ProHoster > Blog > podávání > R balíček tidyr a jeho nové funkce pivot_longer a pivot_wider
R balíček tidyr a jeho nové funkce pivot_longer a pivot_wider
Balíček pořádek součástí jádra jedné z nejpopulárnějších knihoven v jazyce R - pořádek.
Hlavním účelem balíčku je uvést data do přesné podoby.
Již k dispozici na Habré vydání věnované tomuto balíčku, ale pochází z roku 2015. A já vám chci říct o nejaktuálnějších změnách, které před pár dny oznámil její autor Hedley Wickham.
SJK: Budou funkce collect() a spread() zastaralé?
Hadley Wickham: Do určité míry. Tyto funkce již nebudeme doporučovat a opravovat v nich chyby, ale v balíčku budou nadále přítomny v aktuálním stavu.
Obsah
Pokud vás zajímá analýza dat, mohla by vás zajímat moje telegram и Youtube kanály. Většina obsahu je věnována jazyku R.
terč pořádek — pomůže vám převést data do takzvané úhledné podoby. Čistá data jsou data, kde:
Každá proměnná je ve sloupci.
Každé pozorování je řetězec.
Každá hodnota je buňka.
Při provádění analýzy je mnohem jednodušší a pohodlnější pracovat s daty, která jsou prezentována v čistých datech.
Hlavní funkce obsažené v balíčku tidyr
tidyr obsahuje sadu funkcí určených k transformaci tabulek:
fill() — doplnění chybějících hodnot ve sloupci předchozími hodnotami;
separate() — rozdělí jedno pole na několik pomocí oddělovače;
unite() — provádí operaci sloučení několika polí do jednoho, inverzní činnost funkce separate();
pivot_longer() — funkce, která převádí data ze širokého formátu na dlouhý;
pivot_wider() - funkce, která převádí data z dlouhého formátu na široký. Opačná činnost funkce, kterou provádí funkce pivot_longer().
gather()zastaralý — funkce, která převádí data ze širokého formátu na dlouhý;
spread()zastaralý - funkce, která převádí data z dlouhého formátu na široký. Opačná činnost funkce, kterou provádí funkce gather().
Nový koncept pro převod dat ze širokého na dlouhý formát a naopak
Dříve se pro tento druh transformace používaly funkce gather() и spread(). V průběhu let existence těchto funkcí se ukázalo, že pro většinu uživatelů, včetně autora balíčku, nebyly názvy těchto funkcí a jejich argumenty zcela zřejmé, což způsobilo potíže s jejich nalezením a pochopením, která z těchto funkcí převádí datumový rámeček ze širokého na dlouhý formát a naopak.
V tomto ohledu v pořádek Byly přidány dvě nové důležité funkce, které jsou určeny k transformaci rámců data.
Nové funkce pivot_longer() и pivot_wider() byly inspirovány některými funkcemi v balíčku cdata, kterou vytvořili John Mount a Nina Zumel.
Instalace nejnovější verze tidyr 0.8.3.9000
Chcete-li nainstalovat novou, nejaktuálnější verzi balíčku pořádek0.8.3.9000, kde jsou k dispozici nové funkce, použijte následující kód.
devtools::install_github("tidyverse/tidyr")
V době psaní tohoto článku jsou tyto funkce dostupné pouze v dev verzi balíčku na GitHubu.
Přechod na nové funkce
Ve skutečnosti není těžké přenést staré skripty do práce s novými funkcemi, pro lepší pochopení vezmu příklad z dokumentace starých funkcí a ukážu, jak se stejné operace provádějí pomocí nových pivot_*() funkcí.
Převést široký formát na dlouhý formát.
Příklad kódu z dokumentace funkce shromažďování
# 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")
Převod dlouhého formátu na široký.
Ukázkový kód z dokumentace rozprostřených funkcí
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Protože ve výše uvedených příkladech práce s pivot_longer() и pivot_wider(), v původní tabulce zásoby v argumentech nejsou uvedeny žádné sloupce jmen_kom и hodnoty_k jejich jména musí být v uvozovkách.
Tabulka, která vám pomůže nejsnáze přijít na to, jak přejít na práci s novým konceptem pořádek.
Poznámka od autora
Veškerý text níže je adaptivní, řekl bych dokonce volný překlad viněty z oficiálních stránek knihovny Tidyverse.
Jednoduchý příklad převodu dat z širokého na dlouhý formát
pivot_longer () — prodlužuje datové sady snížením počtu sloupců a zvýšením počtu řádků.
Chcete-li spustit příklady uvedené v článku, musíte nejprve připojit potřebné balíčky:
library(tidyr)
library(dplyr)
library(readr)
Řekněme, že máme tabulku s výsledky průzkumu, který se (mimo jiné) ptal lidí na jejich náboženství a roční příjem:
Tato tabulka obsahuje údaje o náboženském vyznání respondentů v řádcích a úrovně příjmů jsou rozptýleny v názvech sloupců. Počet respondentů z každé kategorie je uložen v hodnotách buněk na křižovatce náboženství a úrovně příjmu. K uvedení tabulky do úhledného a správného formátu stačí použít pivot_longer():
První argument sloupců, popisuje, které sloupce je třeba sloučit. V tomto případě všechny sloupce kromě čas.
argument jmen_kom udává název proměnné, která bude vytvořena z názvů sloupců, které jsme zřetězili.
hodnoty_k udává název proměnné, která bude vytvořena z dat uložených v hodnotách buněk sloučených sloupců.
Specifikace
Toto je nová funkce balíčku pořádek, který byl dříve při práci se staršími funkcemi nedostupný.
Specifikace je datový rámec, jehož každý řádek odpovídá jednomu sloupci v novém datovém rámci výstupu a dvěma speciálním sloupcům, které začínají:
. Název obsahuje původní název sloupce.
.hodnota obsahuje název sloupce, který bude obsahovat hodnoty buněk.
Zbývající sloupce specifikace odrážejí, jak bude nový sloupec zobrazovat název komprimovaných sloupců . Název.
Specifikace popisuje metadata uložená v názvu sloupce, s jedním řádkem pro každý sloupec a jedním sloupcem pro každou proměnnou, v kombinaci s názvem sloupce se tato definice může v tuto chvíli zdát matoucí, ale po zhlédnutí několika příkladů bude mnohem jasnější.
Smyslem specifikace je, že můžete načíst, upravit a definovat nová metadata pro převáděný datový rámec.
Chcete-li pracovat se specifikacemi při převodu tabulky ze širokého formátu na dlouhý formát, použijte funkci pivot_longer_spec().
Tato funkce funguje tak, že přebírá libovolný datový rámec a generuje svá metadata výše popsaným způsobem.
Jako příklad si vezměme datovou sadu who, která je součástí balíčku pořádek. Tento soubor údajů obsahuje informace poskytnuté mezinárodní zdravotnickou organizací o výskytu tuberkulózy.
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
Pojďme sestavit jeho specifikaci.
spec <- who %>%
pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")
pole země, iso2, iso3 jsou již proměnné. Naším úkolem je obracet sloupce pomocí new_sp_m014 na newrel_f65.
Názvy těchto sloupců ukládají následující informace:
Předpona new_ označuje, že sloupec obsahuje údaje o nových případech tuberkulózy, aktuální datový rámec obsahuje informace pouze o nových onemocněních, takže tato předpona v aktuálním kontextu nemá žádný význam.
sp/rel/sp/ep popisuje způsob diagnostiky onemocnění.
m/f pohlaví pacienta.
014/1524/2535/3544/4554/65 věkové rozmezí pacientů.
Tyto sloupce můžeme rozdělit pomocí funkce extract()pomocí regulárního výrazu.
Nakonec, abychom mohli použít specifikaci, kterou jsme vytvořili, na původní datum který musíme použít argument spec ve funkci 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
Vše, co jsme právě udělali, lze schematicky znázornit takto:
Specifikace pomocí více hodnot (.value)
Ve výše uvedeném příkladu sloupec specifikace .hodnota obsahoval pouze jednu hodnotu, ve většině případů tomu tak je.
Občas ale může nastat situace, kdy potřebujete shromáždit data ze sloupců s různými datovými typy v hodnotách. Použití starší funkce spread() to by bylo docela těžké udělat.
Níže uvedený příklad je převzat z viněty do balíčku datová tabulka.
Vytvořený datový rámec obsahuje v každém řádku údaje o dětech jedné rodiny. Rodiny mohou mít jedno nebo dvě děti. U každého dítěte jsou uvedeny údaje o datu narození a pohlaví a údaje pro každé dítě jsou v samostatných sloupcích, naším úkolem je uvést tato data do správného formátu pro analýzu.
Upozorňujeme, že máme dvě proměnné s informacemi o každém dítěti: jeho pohlaví a datum narození (sloupce s předponou Křest obsahují datum narození, sloupce s předponou rod obsahovat pohlaví dítěte). Očekávaným výsledkem je, že by se měly objevit v samostatných sloupcích. Můžeme to udělat vygenerováním specifikace, ve které je sloupec .value bude mít dva různé významy.
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
Pojďme se tedy krok za krokem podívat na akce prováděné výše uvedeným kódem.
pivot_longer_spec(-family) — vytvořit specifikaci, která komprimuje všechny existující sloupce kromě sloupce rodiny.
separate(col = name, into = c(".value", "child")) - rozdělit sloupec . Název, který obsahuje názvy zdrojových polí, pomocí podtržítka a zadáním výsledných hodnot do sloupců .hodnota и dítě.
mutate(child = parse_number(child)) — transformovat hodnoty pole dítě z textového na číselný datový typ.
Nyní můžeme výslednou specifikaci aplikovat na původní dataframe a převést tabulku do požadované podoby.
Používáme argument na.rm = TRUE, protože současná podoba dat si vynucuje vytváření dalších řádků pro neexistující pozorování. Protože rodina 2 má pouze jedno dítě, na.rm = TRUE zaručuje, že rodina 2 bude mít na výstupu jeden řádek.
Převod datových snímků z dlouhého na široký formát
pivot_wider() - je inverzní transformace a naopak zvyšuje počet sloupců rámce data snížením počtu řádků.
Tento druh transformace se k převedení dat do přesné podoby používá velmi zřídka, nicméně tato technika může být užitečná pro vytváření kontingenčních tabulek používaných v prezentacích nebo pro integraci s některými dalšími nástroji.
Vlastně funkce pivot_longer() и pivot_wider() jsou symetrické a vytvářejí vzájemně inverzní akce, tj. df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) vrátí původní df.
Nejjednodušší příklad převodu tabulky do širokého formátu
Ukázat, jak funkce funguje pivot_wider() použijeme datovou sadu rybí_setkání, která uchovává informace o tom, jak různé stanice zaznamenávají pohyb ryb po řece.
#> # 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
Ve většině případů bude tato tabulka informativnější a snáze použitelná, pokud uvedete informace pro každou stanici v samostatném sloupci.
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>
Tato datová sada zaznamenává informace pouze tehdy, když byly ryby detekovány stanicí, tzn. pokud některá stanice nezaznamenala nějakou rybu, tak tento údaj v tabulce nebude. To znamená, že výstup bude naplněn NA.
V tomto případě však víme, že absence záznamu znamená, že ryba nebyla viděna, takže můžeme použít argument value_fill ve funkci pivot_wider() a doplňte tyto chybějící hodnoty nulami:
Generování názvu sloupce z více zdrojových proměnných
Představte si, že máme tabulku obsahující kombinaci produktu, země a roku. Chcete-li vygenerovat testovací datový rámec, můžete spustit následující kód:
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
Naším úkolem je rozšířit datový rámec tak, aby jeden sloupec obsahoval data pro každou kombinaci produktu a země. Chcete-li to provést, stačí předat argument jména_od vektor obsahující názvy polí, která mají být sloučena.
Na funkci můžete také použít specifikace pivot_wider(). Ale když se podrobí pivot_wider() specifikace provádí opačnou konverzi pivot_longer(): Sloupce uvedené v . Názevpomocí hodnot z .hodnota a další sloupce.
Pro tuto datovou sadu můžete vygenerovat vlastní specifikaci, pokud chcete, aby každá možná kombinace zemí a produktů měla svůj vlastní sloupec, nejen ty, které jsou v datech:
#> # 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
Několik pokročilých příkladů práce s novým konceptem tidyr
Vyčištění dat pomocí datové sady US Census Income and Rent jako příkladu.
Soubor dat příjem_z_nájmu obsahuje informace o středních příjmech a nájemném pro každý stát v USA za rok 2017 (soubor dat je k dispozici v balíčku pořádek).
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
Ve formě, ve které jsou data uložena v datové sadě příjem_z_nájmu práce s nimi je extrémně nepohodlná, proto bychom chtěli vytvořit datovou sadu se sloupci: pronajmout, rent_moe, Přijít, příjem_moe. Existuje mnoho způsobů, jak vytvořit tuto specifikaci, ale hlavním bodem je, že musíme vygenerovat každou kombinaci hodnot proměnných a odhad/moea poté vygenerujte název sloupce.
Převedení datové sady do požadované podoby někdy vyžaduje několik kroků.
Dataset world_bank_pop obsahuje údaje Světové banky o počtu obyvatel jednotlivých zemí v letech 2000 až 2018.
Naším cílem je vytvořit úhlednou sadu dat s každou proměnnou ve vlastním sloupci. Není přesně jasné, jaké kroky jsou vyžadovány, ale začneme nejzjevnějším problémem: rok je rozložen do několika sloupců.
Chcete-li to opravit, musíte použít funkci pivot_longer().
Dalším krokem je podívat se na proměnnou indikátoru. 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
Kde SP.POP.GROW je populační růst, SP.POP.TOTL je celkový počet obyvatel a SP.URB. * totéž, ale pouze pro městské oblasti. Rozdělme tyto hodnoty do dvou proměnných: plocha - plocha (celková nebo městská) a proměnná obsahující skutečná data (počet obyvatel nebo růst):
Tabulování tohoto seznamu je poměrně obtížné, protože neexistuje žádná proměnná, která by identifikovala, která data patří ke kterému kontaktu. Můžeme to opravit tím, že si všimneme, že data každého nového kontaktu začínají „jménem“, takže můžeme vytvořit jedinečný identifikátor a zvýšit jej o jednu pokaždé, když sloupec pole obsahuje hodnotu „name“:
#> # 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
Nyní, když máme pro každý kontakt jedinečné ID, můžeme pole a hodnotu převést na sloupce:
#> # 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>
Závěr
Můj osobní názor je, že nový koncept pořádek skutečně intuitivnější a ve funkčnosti výrazně lepší než starší funkce spread() и gather(). Doufám, že vám tento článek pomohl se s tím vypořádat pivot_longer() и pivot_wider().