14 Transformace proměnných
Transformace proměnných patří mezi nejběžnější operace při práci s daty. Od standardizace proměnných, přes jejich čištění, až po vytváření proměnných nových, obsah následující kapitoly budou denní chléb každého analytika.
Hlavním tahounem je zde funkce mutate()
z balíčku dplyr
. Přestože transformace proměnných lze provádět i bez ní, má tato funkce několik předností.
14.1 Jednoduché transformace
Funkce mutate()
přijímá jako svůj první argument dataframe, dalšími argumenty jsou poté jednotlivé transformace:
%>%
countries mutate(gdp_milliards = gdp / 1000,
poverty_risk = poverty_risk * 100) %>%
select(country, gdp, gdp_milliards, poverty_risk)
# A tibble: 38 × 4
country gdp gdp_milliards poverty_risk
<chr> <dbl> <dbl> <dbl>
1 Belgium 450506. 451. 20.3
2 Bulgaria 55182. 55.2 38.9
3 Czechia 207772. 208. 12.2
4 Denmark 298276. 298. 17.2
5 Germany 3386000 3386 19
6 Estonia 25657. 25.7 23.4
7 Ireland 324038. 324. 22.7
8 Greece 184714. 185. 34.8
9 Spain 1208248 1208. 26.6
10 France 2353090 2353. 17.1
# … with 28 more rows
Zde je vidět nejen aplikace funkce mutate()
, ale i její hlavní primární výhoda. Protože jejím výsledkem je dataframe s provedenými transformacemi, je možné na ní navázat dalšími funkcemi, jako je select()
nebo filter()
. Novým proměnným také můžeme jednoduše přiřadit jméno (viz Kapitola 5). Pokud uložíme výsledek transformace pod novým jménem, bude vytvořena nová proměnná (v našem případě gdp_milliards
). Pokud použijeme jméno již existující proměnné, bude přepsána hodnotami (poverty_risk
).
Jednou z analýz, která se v datasetu nabízí, je srovnání ekonomické produktivity zemí, a jednou z nejpopulárnějších metrik ekonomické produktivity je HDP. Čtenáři se znalostmi ekonomie ale již jistě tuší problém. HDP se silně odvíjí od počtu obyvatel a není tedy úplně smysluplné porovnávat obří Německo s maličkým Českem. Pro serióznější analýzu by proto bylo lepší využít standardizovanější míru, jakou je například HDP na hlavu. Tuto proměnnou náš dataset neobsahuje, nemusíme ale smutnit. Máme k dispozici jak HDP, tak počet obyvatel a od kýženého výsledku nás děli jedna matematická operace.
Pro rychlé srovnání zemí by také bylo vhodné převést data do standardizovaných jednotek. Takovou jednotkou jsou mimo jiné z skóry, získatelné odečtením průměru proměnné od každé naměřené hodnoty a vydělením rozdílu směrodatnou odchylkou, tedy \(z_i = \frac{x_i - \bar{x}}{sd(x)}\) . Protože se jedná o populární formu standardizace, R pro ni nabízí funkci scale()
:
%>%
countries mutate(gdp_pc = gdp / population,
gdp_pc_scaled = scale(gdp_pc)) %>%
select(country, gdp_pc, gdp_pc_scaled)
# A tibble: 38 × 3
country gdp_pc gdp_pc_scaled[,1]
<chr> <dbl> <dbl>
1 Belgium 0.0395 0.357
2 Bulgaria 0.00783 -1.06
3 Czechia 0.0196 -0.534
4 Denmark 0.0516 0.897
5 Germany 0.0409 0.419
6 Estonia 0.0194 -0.540
7 Ireland 0.0671 1.59
8 Greece 0.0172 -0.640
9 Spain 0.0259 -0.252
10 France 0.0352 0.162
# … with 28 more rows
Z transformované proměnné gdp_pc_scaled
je vidět, že z skór České republiky je -0.53, naše HDP na hlavu se tedy nachází zhruba půl směrodatné odchylky pod průměrem. Naopak Irsko se těší HDP na hlavu o 1.5 směrodatné odchylky vyšší, než je průměr všech zemí v datasetu.
14.2 Transformace po skupinách
Ve výše zmíněných příkladech byly transformace aplikovány na vybrané proměnné jako celek. Co když ale není naším cílem transformovat všechny hodnoty stejným způsobem?
Pro detailnější analýzu ekonomické produktivity zemí může být zajímavé zohlednit jejich politickou historii. Jak si například Česká republika vede ve srovnání s ostatními postsovětskými zeměmi? Pro zodpovězení této otázky je nutné aplikovat funkcí scale()
na každou skupinu proměnné postsoviet
zvlášť. Naštěstí pro nás, tato operace nemůže být jednoduší, a to díky funkci group_by()
, se kterou jsme se již setkali při řezání dataframů (Sekce 11.3):
%>%
countries group_by(postsoviet) %>%
mutate(gdp_pc = gdp / population,
gdp_pc_scaled = scale(gdp_pc)) %>%
ungroup() %>%
select(country, postsoviet, gdp_pc, gdp_pc_scaled)
# A tibble: 38 × 4
country postsoviet gdp_pc gdp_pc_scaled[,1]
<chr> <chr> <dbl> <dbl>
1 Belgium no 0.0395 -0.245
2 Bulgaria yes 0.00783 -0.781
3 Czechia yes 0.0196 0.522
4 Denmark no 0.0516 0.329
5 Germany yes 0.0409 2.89
6 Estonia yes 0.0194 0.507
7 Ireland no 0.0671 1.07
8 Greece no 0.0172 -1.31
9 Spain no 0.0259 -0.894
10 France no 0.0352 -0.453
# … with 28 more rows
Přestože tento dataframe na první pohled vypadá velmi podobně jako ten předchozí, hodnoty proměnné gdp_pc_scaled
jsou odlišné. Česká republika má nyní hodnotu 0.52. Nachází se tedy zhruba půl směrodatné odchylky nad průměrem ostatních postsovětských zemí. Naopak z skór Irska se snížil na 1.1, protože ve srovnání s ostatními západními zeměmi je jeho HDP na hlavu pouze jednu směrodatnou odchylku nad průměrem.
Tohoto srovnání jsme dosáhli právě tím, že jsme před aplikací funkce mutate()
rozdělili dataframe pomocí group_by()
a všechny následující operace tedy budou prováděny pro západní a postsovětské funkce zvlášť.
14.3 Řádkové operace
Přesuňme se teď od ekonomické produktivitě k palčivějším tématům. Jedním z ekonomicko-sociálních problémů, se kterými se musí každá země vypořádat, jsou obyvatelé ohrožení chudobou (proměnná poverty_risk
) a obyvatelé v materiální deprivaci (material_dep
). Naneštěstí pro nás nemáme k dispozici podíl obyvatel ohrožených alespoň jedním z těchto rizik, můžeme ale získat alespoň konzervativní odhad. Maximální možný podíl lidí ohrožených chudobou nebo v materiální deprivaci je možné získat jednoduše součtem obou hodnot pro každou zemi.
Tento krapet kostrbatý problém nám poslouží pro demonstraci řádkových (rowwise) transformací. R ve svém výchozím nastavení aplikuje funkce po sloupcích (columnwise). To s sebou přináší poněkud zákeřnou komplikaci při snaze sečíst dvě hodnoty na stejném řádku dataframu. Pokud chceme aplikovat funkci po řádcích, nikoliv po sloupcích, je nutné využít funkce rowwise()
. Ta funguje velmi obdobně jako group_by()
, a to včetně jejího “vypnutí” pomocí ungroup()
:
%>%
countries rowwise() %>%
mutate(poverty_or_dep = sum(poverty_risk, material_dep, na.rm = TRUE)) %>%
ungroup() %>%
select(country, poverty_or_dep)
# A tibble: 38 × 2
country poverty_or_dep
<chr> <dbl>
1 Belgium 0.316
2 Bulgaria 0.827
3 Czechia 0.22
4 Denmark 0.24
5 Germany 0.281
6 Estonia 0.35
7 Ireland 0.375
8 Greece 0.708
9 Spain 0.394
10 France 0.282
# … with 28 more rows
Pomocí funkce rowwise()
jsme získali součet podílu lidí ohrožených chudobou a lidí v materiální deprivaci pro každou ze zemí. Jak je vidět, alespoň do jedné z těchto kategorií v České republice spadá maximální 22 % obyvatel.
14.4 Podmíněné transformace
Jednou z myšlenkových operací, ve které počítače vynikají, je rigidní “pokud je splněna podmínka, udělej X”. Pojďme toho využít.
V předchozí sekci jsme porovnávali země na základě standardizovaného HDP na hlavu. Co kdybychom tuto analýzy chtěli vzít o krok dále a vytvořit novou kategoriální proměnnou, jejíž hodnota bude Above average
pro země s nadprůměrným HDP na hlavu, a Below average
pro země podprůměrné.
K tomu nám dobře poslouží funkce if_else()
, která má tři povinné argumenty. Tím prvním je podmínka, jejímž výsledkem musí být buď hodnota “pravda” (TRUE
) nebo “nepravda” (FALSE
). Druhým argumentem je operace, která bude provedena, pokud je zmíněná podmínka splněna, třetím argumentem poté nepřekvapivě operace provedené v případě nesplnění podmínky. Aplikace pro náš konkrétní případ by vypadala následovně:
%>%
countries mutate(gdp_pc_scaled = scale(gdp / population),
gdp_pc_cat = if_else(gdp_pc_scaled > 0,
true = "Above average",
false = "Below average")) %>%
select(country, gdp_pc_scaled, gdp_pc_cat)
# A tibble: 38 × 3
country gdp_pc_scaled[,1] gdp_pc_cat
<chr> <dbl> <chr>
1 Belgium 0.357 Above average
2 Bulgaria -1.06 Below average
3 Czechia -0.534 Below average
4 Denmark 0.897 Above average
5 Germany 0.419 Above average
6 Estonia -0.540 Below average
7 Ireland 1.59 Above average
8 Greece -0.640 Below average
9 Spain -0.252 Below average
10 France 0.162 Above average
# … with 28 more rows
Co kdybychom ale chtěli, aby výsledkem operace byly více než dvě hodnoty? Možná nám přijde, že klasifikovat země pouze jako nadprůměrné a podprůměrné je příliš redukcionistické (populární to výčitka mezi sociology). Země bychom místo toho raději rozdělili do čtyř kategorií:
below average
pro země se z skóre nižším než -1slightly below average
pro země v intervalu -1 až 0slightly above average
analogicky pro země mezi 0 a 1above average
pro ty se z skórem vyšším, než 1.
Jednou z možností je využít řadu na sebe navazujících if_else
funkcí. Tento postup by technicky fungoval, povede ale k mnoha slzám a frustracím přímo úměrným množství funkcí, které je třeba správně zřetězit. Elegantnějším řešením je využít funkci case_when()
, která byla vytvořena právě pro tento případ:
%>%
countries mutate(gdp_pc = scale(gdp / population),
gdp_pc_cat = case_when(gdp_pc < -1 ~ "Below average",
<= 0 ~ "Slightly below average",
gdp_pc <= 1 ~ "Slightly above average",
gdp_pc > 1 ~ "Above average",
gdp_pc TRUE ~ "Unknown")) %>%
select(country, gdp_pc, gdp_pc_cat)
# A tibble: 38 × 3
country gdp_pc[,1] gdp_pc_cat
<chr> <dbl> <chr>
1 Belgium 0.357 Slightly above average
2 Bulgaria -1.06 Below average
3 Czechia -0.534 Slightly below average
4 Denmark 0.897 Slightly above average
5 Germany 0.419 Slightly above average
6 Estonia -0.540 Slightly below average
7 Ireland 1.59 Above average
8 Greece -0.640 Slightly below average
9 Spain -0.252 Slightly below average
10 France 0.162 Slightly above average
# … with 28 more rows
Funkce case_when()
má oproti dosavadním funkcí atypickou syntax. Každá z logických podmínek je kondezovaná do formule podminka ~ vysledek
. První řádek v této funkci, gdp_pc < -1 ~ "Below average"
, tedy říká “pokud je hodnota proměnné gdp_pc menší než -1, vrať hodnotu Below average
”. Pokud tato podmínka splněná není, funkce zkontroluje podmínku následující. Podmínky jsou ověřovány jedna po druhé, přičemž podmínky na vyšších místech jsou ověřeny dříve. Speciální podmínkou je TRUE ~ vysledek
, která je je vždy splněna. To se hodí pokud jsou v datech přítomny hodnoty, které nesplňují žádnou z předchozích podmínek. Kdy se může stát, že hodnota nesplňuje žádnou z našich podmínek? Například, pokud se jedná o hodnotu chybějící:
%>%
countries mutate(gdp_pc = scale(gdp / population),
gdp_pc_cat = case_when(gdp_pc < -1 ~ "Below average",
<= 0 ~ "Slightly below average",
gdp_pc <= 1 ~ "Slightly above average",
gdp_pc > 1 ~ "Above average",
gdp_pc TRUE ~ "Unknown")) %>%
select(country, gdp_pc, gdp_pc_cat) %>%
filter(is.na(gdp_pc))
# A tibble: 4 × 3
country gdp_pc[,1] gdp_pc_cat
<chr> <dbl> <chr>
1 Liechtenstein NA Unknown
2 Montenegro NA Unknown
3 Turkey NA Unknown
4 Bosnia and Herzegovina NA Unknown