Tidy data: jak si ušetřit mnoho zbytečných starostí

Na motivy Hadley Wickham: Tidy data, Journal of Statistical Software, August 2014, Volume 59, Issue 10.

K této kapitole jsou zpracovány velice hezké „taháky“: http://www.rstudio.com/resources/cheatsheets/ (Data Import Cheat Sheet a Data Transformation Cheat Sheet), případně Data Manipulation with dplyr, tidyr dostupný přímo z RStudia z menu Help, Cheatsheets.

Pokud ještě nejsou knihovny dplyr, tidyr a readr nainstalovány, učiníme tak příkazy (stačí provést jednou)

install.packages("tidyr")
install.packages("dplyr")
install.packages("readr")

Před použitím knihoven je potřeba je nejdříve zavést příkazem (nutné vždy po startu R)

library(tidyr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(readr)

Tidy Data je obecný koncept nezávislý na použitých programech.

Raw Data → Processing Script → Tidy data + Codebook → Data Analysis → Data Communication.

Raw Data: surová data, zaznamenaná v jakémkoliv prvotním (často neuspořádaném) formátu (klidně i jen poznámky na papíře).

Reprodukovatelnost: celý řetězec zpracování jednoznačně popsán skripty – na první pohled je jasný postup a lze ho jednoduše zopakovat. V případě ručních mezikroků detailně zaznamenat (verze programu, nastavení, jak co pojmenovat a jak uložit).

Codebook: informace o proměnných, jejich jednotkách a možných hodnotách. Slouží k tomu, aby se v datech vyznal a mohl s nimi pracovat i ten, kdo nebyl u jejich pořízení.

Tidy data

  1. Každá proměnná v samostatném sloupci.
  2. Každé pozorování v samostatném řádku.
  3. Každý druh pozorované záležitosti v samostatné tabulce.

Poznámka k bodu 3: pokud máme např. tabulku s průběžnými body studentů během jejich studia, jedná se o jednu záležitost. Osobní informace o jednotlivých studentech (datum narození, adresa, telefon) uložíme do samostatné tabulky.

V případě více tabulek je nutný sloupec, dle kterého je lze propojit – ID pozorování – identifikační číslo, řetězec apod.

Čitelné pojmenování sloupců – raději AgeAtDiagnosis než AgeDx.

Uložení dat: co tabulka, to jeden soubor (záložkám v Excelu se vyhýbat). A rozhodně neukládat do jedné záložky více tabulek! Zbytečně si tím v extrémní míře znesnadňujeme následné automatické zpracování.

Messy data: vše ostatní.

  1. Hlavičky sloupců jsou hodnoty, nikoliv jména proměnných.
  2. Více proměnných v rámci jednoho sloupce.
  3. Proměnné uloženy jak v řádcích, tak ve sloupcích.
  4. Více pozorovaných záležitostí uložených ve stejné tabulce.
  5. Jedna pozorovaná záležitost uložena ve více tabulkách.

Pokud jsou data v tidy formátu, můžeme využívat efektivních knihoven pro jejich další zpracování, například dplyr pro souhrnné statistiky a manipulace podle různých kritérií (viz dále v této kapitole) nebo ggplot2 pro efektivní tvorbu pokročilých grafů (viz kapitola Grammar of Graphics (ggplot2)).

Nyní se podívejme na data v nevhodném formátu a jak je převést na tidy data.

Messy data a jejich převod na tidy data pomocí knihovny tidyr

Příklad 1

Tabulka obsahuje průměrnou hodnotu základní frekvence kmitání hlasivek (F0) u 3 žen a 3 mužů při 2 různých úkolech. Čím jsou porušena pravidla tidy data?

data <- read.table(header = TRUE, text = '
task male female
1    119  203
1    143  219
1    94   245
2    114  144
2    127  259
2    95   244
')

„Hlavičky některých sloupců jsou hodnoty, nikoliv jména proměnných.“

Hlavičky sloupců male a female ve skutečnosti nejsou 2 různé proměnné, ale jen 2 hodnoty téže proměnné (sex). Že to skutečně není vhodný formát tabulky, by bylo ještě zřejmější, pokud by se výzkumu zůčastnilo např. 5 žen a 3 muži.

Máme tedy 3 proměnné: task, sex a F0_mean. Opravu (sloučení sloupců) provedem pomocí funkce gather. Význam jejích parametrů je následující.

    1. parametr: proměnná s daty (data.frame)
    1. parametr: které sloupce sloučit (pokud všechny, není potřeba tento parametr vyplňovat)
    1. parametr: jak nazvat sloupec hodnot ze sloučených sloupců
    1. parametr: jak nazvat sloupec, jehož hodnoty se vyplní podle názvů slučovaných sloupců
dataTidy <- gather(data, sex, F0_mean, c(male, female))

Tímto zápisem říkáme: vezmi tabulku data, slučovat se budou sloupce male a female, čísla z těchto sloupců se převedou do nového sloupce s názvem F0_mean a z názvů slučovaných sloupců male a female se udělají hodnoty nového sloupce sex.

dataTidy
##    task    sex F0_mean
## 1     1   male     119
## 2     1   male     143
## 3     1   male      94
## 4     2   male     114
## 5     2   male     127
## 6     2   male      95
## 7     1 female     203
## 8     1 female     219
## 9     1 female     245
## 10    2 female     144
## 11    2 female     259
## 12    2 female     244

A máme krásný tidy data formát. Můžeme pak např. jednoduše pomocí grafické knihovny ggplot2 říct, ať zobrazí frekvence u jednotlivých úloh (task) a rozdělí se do skupin podle hodnot proměnné sex. Ale o tom až v další kapitole…

Poznámka: pro výběr slučovaných sloupců lze využít i zápisu se symbolem - značícím „vše kromě“, tedy sloučit všechny sloupce kromě sloupce task:

dataTidy <- gather(data, sex, F0_mean, -task)

Příklad 2

Opět máme data z předchozího příkladu, ale v jiném tvaru. Čísla úkolů jsou součástí názvů sloupečků (_1 a _2). Čím jsou porušena pravidla tidy data?

data2 <- read.table(header = TRUE, text = '
male_1 male_2 female_1 female_2
119    114    203      144
143    127    219      259
94     95     245      244
')

Opět „hlavičky některých sloupců jsou hodnoty, nikoliv jména proměnných“, ale k tomu ještě „více proměnných v rámci jednoho sloupce“ (sex a task).

data2Tidy <- gather(data2, sex_task, F0_mean) %>%
    separate(sex_task, c("sex", "task"))

První funkci gather jsme již poznali (4. parametr nevyplněn, slučujeme všechny sloupce), vytváříme dočasný sloupec se dvěma proměnnými sex_task (zkuste si spustit samostatně jen tuto operaci a podívejte se na výsledek).

##    sex_task F0_mean
## 1    male_1     119
## 2    male_1     143
## 3    male_1      94
## 4    male_2     114
## 5    male_2     127
## 6    male_2      95
## 7  female_1     203
## 8  female_1     219
## 9  female_1     245
## 10 female_2     144
## 11 female_2     259
## 12 female_2     244

Operátor %>% se nazývá pipeline (potrubí, spojení) a posílá výsledek z jedné funkce do druhé, takže není potřeba ukládat mezivýsledky do proměnných. Pokud je tento operátor použit, následující funkci nezadáváme 1. parametr (název proměnné s daty), protože data jsou jí právě přeposlána tímto operátorem.

Funkce separate rozdělí řetězec na více částí podle nealfanumerických znaků (tedy např. podtržítka, mezery apod.) a výsledek uloží do nových sloupců, jejichž název jsme zvolili sex a task.

data2Tidy
##       sex task F0_mean
## 1    male    1     119
## 2    male    1     143
## 3    male    1      94
## 4    male    2     114
## 5    male    2     127
## 6    male    2      95
## 7  female    1     203
## 8  female    1     219
## 9  female    1     245
## 10 female    2     144
## 11 female    2     259
## 12 female    2     244

Příklad 3

Tabulka obsahuje trvání (duration) a průměrnou F0 u pěti různých úkolů. Ne všichni ale absolvovali všechny úkoly, proto tu máme spoustu chybějících hodnot NA (not available). Čím tabulka porušuje pravidla tidy data?

data3 <- read.table(header = TRUE, text = '
name   parameter task1 task2 task3 task4 task5
Eva    duration  4.8   NA    12.3  NA    NA
Eva    F0_mean   230   NA    222   NA    NA
Jiří   duration  NA    7.0   NA    5.9   NA
Jiří   F0_mean   NA    110   NA    113   NA
Zbyněk duration  NA    6.7   NA    NA    15.1
Zbyněk F0_mean   NA    127   NA    NA    118
Monika duration  NA    NA    10.9  6.2   NA
Monika F0_mean   NA    NA    240   235   NA
Patrik duration  5.1   NA    NA    NA    16.3
Patrik F0_mean   98    NA    NA    NA    95
')

„Proměnné uloženy jak v řádcích, tak ve sloupcích.“

Name je o.k., ale task1 až task5 jsou jen hodnoty jedné proměnné task (5 různých úkolů), duration a F0_mean jsou dvě proměnné související s konkrétním jedním pokusem, tudíž by měly tvořit 2 sloupce vždy pro stejný řádek.

data3_krok1 <- gather(data3, task, value, task1:task5, na.rm=TRUE)

V prvním kroku vezmeme tabulku data3, všechny názvy sloupců v rozsahu task1 až task5 budou hodnoty nové proměnné task a příslušné hodnoty ze sloupců uložíme do nové proměnné value. Všechny hodnoty NA (not available) zahodíme.

##      name parameter  task value
## 1     Eva  duration task1   4.8
## 2     Eva   F0_mean task1 230.0
## 9  Patrik  duration task1   5.1
## 10 Patrik   F0_mean task1  98.0
## 13   Jiří  duration task2   7.0
## 14   Jiří   F0_mean task2 110.0
## 15 Zbyněk  duration task2   6.7
## 16 Zbyněk   F0_mean task2 127.0
## 21    Eva  duration task3  12.3
## 22    Eva   F0_mean task3 222.0
## 27 Monika  duration task3  10.9
## 28 Monika   F0_mean task3 240.0
## 33   Jiří  duration task4   5.9
## 34   Jiří   F0_mean task4 113.0
## 37 Monika  duration task4   6.2
## 38 Monika   F0_mean task4 235.0
## 45 Zbyněk  duration task5  15.1
## 46 Zbyněk   F0_mean task5 118.0
## 49 Patrik  duration task5  16.3
## 50 Patrik   F0_mean task5  95.0
data3_krok2 <- mutate(data3_krok1, task = parse_number(task))

Z hodnot proměnné task (které jsou task1, task2 atd.) extrahujeme jen číslo (funkcí parse_number z balíčku readr), to nám stačí.

##      name parameter task value
## 1     Eva  duration    1   4.8
## 2     Eva   F0_mean    1 230.0
## 3  Patrik  duration    1   5.1
## 4  Patrik   F0_mean    1  98.0
## 5    Jiří  duration    2   7.0
## 6    Jiří   F0_mean    2 110.0
## 7  Zbyněk  duration    2   6.7
## 8  Zbyněk   F0_mean    2 127.0
## 9     Eva  duration    3  12.3
## 10    Eva   F0_mean    3 222.0
## 11 Monika  duration    3  10.9
## 12 Monika   F0_mean    3 240.0
## 13   Jiří  duration    4   5.9
## 14   Jiří   F0_mean    4 113.0
## 15 Monika  duration    4   6.2
## 16 Monika   F0_mean    4 235.0
## 17 Zbyněk  duration    5  15.1
## 18 Zbyněk   F0_mean    5 118.0
## 19 Patrik  duration    5  16.3
## 20 Patrik   F0_mean    5  95.0
data3Tidy <- spread(data3_krok2, parameter, value)

Z hodnot proměnné parameter vytvoříme nové proměnné (nové sloupce), jejichž hodnoty naplníme z proměné value (v nové tabulce tím tedy proměnné parameter a value zaniknou, o konkrétní hodnoty samozřejme nepřijdeme, jen budou přemístěny do jiného formátu).

data3Tidy
##      name task duration F0_mean
## 1     Eva    1      4.8     230
## 2     Eva    3     12.3     222
## 3    Jiří    2      7.0     110
## 4    Jiří    4      5.9     113
## 5  Monika    3     10.9     240
## 6  Monika    4      6.2     235
## 7  Patrik    1      5.1      98
## 8  Patrik    5     16.3      95
## 9  Zbyněk    2      6.7     127
## 10 Zbyněk    5     15.1     118

Celý postup by samozřejmě bylo možné zapsat najednou pomocí operátoru pipeline %>%. Zkuste si to.

Příklad 4

Čím jsou porušena pravidla tidy data?

data4 <- read.table(header = TRUE, text = '
id  name    sex  task  duration F0_mean
168 Eva     F    1     4.8      230
168 Eva     F    3     12.3     222
588 Jiří    M    2     7.0      110
588 Jiří    M    4     5.9      113
710 Monika  F    3     10.9     240
710 Monika  F    4     6.2      235
731 Patrik  M    1     5.1      98
731 Patrik  M    5     16.3     95
908 Zbyněk  M    2     6.7      127
908 Zbyněk  M    5     15.1     118
')

„Více pozorovaných záležitostí uložených ve stejné tabulce.“

Osobní informace o lidech (id, name, sex) – vše je 2x, to je jasná známka redundance (a ještě je tu riziko překlepu).

Data je vhodné rozdělit do 2 tabulek: idInfo a results. Obě tabulky budou propojeny přes id číslo.

idInfo <- data4 %>% select(id, name, sex) %>% unique

Z tabulky jsme funkcí select vybrali první 3 sloupce. Funkcí unique jsme zanechali jen unikátní řádky. Pokud se řádky opakují, ve výsledku budou zahrnuty pouze jednou.

idInfo
##    id   name sex
## 1 168    Eva   F
## 3 588   Jiří   M
## 5 710 Monika   F
## 7 731 Patrik   M
## 9 908 Zbyněk   M
results <- data4 %>% select(id, task, duration, F0_mean)

A opět vybíráme jen některé sloupce.

results
##     id task duration F0_mean
## 1  168    1      4.8     230
## 2  168    3     12.3     222
## 3  588    2      7.0     110
## 4  588    4      5.9     113
## 5  710    3     10.9     240
## 6  710    4      6.2     235
## 7  731    1      5.1      98
## 8  731    5     16.3      95
## 9  908    2      6.7     127
## 10 908    5     15.1     118

Mít takto oddělené záznamy je praktické. V případě potřeby je pak možné informace kdykoliv spojit.

right_join(idInfo, results, by = "id")
##     id   name sex task duration F0_mean
## 1  168    Eva   F    1      4.8     230
## 2  168    Eva   F    3     12.3     222
## 3  588   Jiří   M    2      7.0     110
## 4  588   Jiří   M    4      5.9     113
## 5  710 Monika   F    3     10.9     240
## 6  710 Monika   F    4      6.2     235
## 7  731 Patrik   M    1      5.1      98
## 8  731 Patrik   M    5     16.3      95
## 9  908 Zbyněk   M    2      6.7     127
## 10 908 Zbyněk   M    5     15.1     118

Pro další způsoby spojování tabulek viz tahák dostupný přímo z RStudia z menu Help, Cheatsheets, Data Manipulation v dplyr, tidyr. Druhá strana, sekce Combine Data Sets.

Příklad 5

Čím jsou zde porušena pravidla tidy data?

male <- read.table(header = TRUE, text = '
name   task duration F0_mean
Jiří   2    7        110
Jiří   4    5.9      113
Patrik 1    5.1      98
Patrik 5    16.3     95
Zbyněk 2    6.7      127
Zbyněk 5    15.1     118
')

female <- read.table(header = TRUE, text = '
name   task duration F0_mean
Eva    1    4.8      230
Eva    3    12.3     222
Monika 3    10.9     240
Monika 4    6.2      235
')

„Jedna pozorovaná záležitost uložena ve více tabulkách.“

Pokud se jedná o stejný experiment, obě tabulky má smysl sloučit a z male a female vytvořit jednu proměnnou sex.

male <- male %>% mutate(sex = "M")
female <- female %>% mutate(sex = "F")

Tímto jsme do tabulek přidali novou proměnnou sex s příslušnou hodnotou ve všech řádcích.

data5Tidy <- bind_rows(male, female)
## Warning in bind_rows_(x, .id): Unequal factor levels: coercing to character

A obě tabulky spojíme pod sebe.

data5Tidy
##      name task duration F0_mean sex
## 1    Jiří    2      7.0     110   M
## 2    Jiří    4      5.9     113   M
## 3  Patrik    1      5.1      98   M
## 4  Patrik    5     16.3      95   M
## 5  Zbyněk    2      6.7     127   M
## 6  Zbyněk    5     15.1     118   M
## 7     Eva    1      4.8     230   F
## 8     Eva    3     12.3     222   F
## 9  Monika    3     10.9     240   F
## 10 Monika    4      6.2     235   F

Untidy

Občas se při zpracování dat dostaneme do situace, kdy je potřeba tidy tabulku záměrně předělat do „messy“ tvaru. V následujícím příkladu jsme nahráli 3 osoby, každá přečetla jednu větu ve dvou stylech fonace, a sice normální (modal) a třepené (creaky). Vše bylo čteno dvakrát (sloupec order). Vypočteno bylo trvání a průměrná hodnota F0.

data <- read.table(header = TRUE, text = '
speaker phonation order duration F0_mean
1       modal     1     5.7      127
1       modal     2     5.3      135
1       creaky    1     8.5      110
1       creaky    2     7.4      105
2       modal     1     6.3      243
2       creaky    1     9.2      145
2       modal     2     5.8      219
2       creaky    2     9.3      162
3       modal     1     5.1      140
3       creaky    1     6.0      93
3       creaky    2     5.8      95
')

Rádi bychom vytvořili sloupce s jednotlivými vypočtenými parametry doplněné o index 1 nebo 2 odpovídající hodnotě ve sloupci order, aby pak bylo možné jednoduše vypočítat rozdíl mezi hodnotami prvního a druhého čtení té samé položky tou samou fonací.

messy <- data %>%
    gather(temp, xx, -c(speaker, phonation, order)) %>%   # výčet všech sloupců sloužících k identifikaci
                                                          # položky (tedy nejsou vypočtenými parametry)
    unite(temp1, temp, order, sep = ".") %>%        # 3. parametr "order": podle něho se očíslují sloupce
                                                    # s vypočtenými parametry
    spread(temp1, xx)
##   speaker phonation duration.1 duration.2 F0_mean.1 F0_mean.2
## 1       1    creaky        8.5        7.4       110       105
## 2       1     modal        5.7        5.3       127       135
## 3       2    creaky        9.2        9.3       145       162
## 4       2     modal        6.3        5.8       243       219
## 5       3    creaky        6.0        5.8        93        95
## 6       3     modal        5.1         NA       140        NA

Je to veliký trik, který používá v mezivýpočtech dočasné proměnné temp, temp1 a xx.

Hadley Wickham, autor balíčků tidyr a dplyr, k tomuto postupu: „the goal of tidyr is to make your data tidy, so you shouldn’t expect doing the opposite to be easier.“

Nyní můžeme vypočítat rozdíly parametrů mezi 1. a 2. čtením. Všimněme si, že bylo správně rozpoznáno, že u třetí osoby chybí druhé čtení v modální fonaci. Ani prohozené pořadí řádků u druhé osoby (vůči logice zbylých dvou osob) převod tabulky nijak nerozhodilo. Kdybychom tyto úpravy dělali ručně, riziko chyby by bylo vysoké.

tab <- messy %>% mutate(deltaDuration = duration.2 - duration.1, deltaF0_mean = F0_mean.2 - F0_mean.1) %>% 
    select(speaker, phonation, deltaDuration, deltaF0_mean)
##   speaker phonation deltaDuration deltaF0_mean
## 1       1    creaky          -1.1           -5
## 2       1     modal          -0.4            8
## 3       2    creaky           0.1           17
## 4       2     modal          -0.5          -24
## 5       3    creaky          -0.2            2
## 6       3     modal            NA           NA

Rozdíly jsou bezchybně určeny a správně chybí modální fonace třetí osoby, jelikož v datech není druhé čtení.

Kontingenční tabulky

Kontingenční tabulka, neboli tabulka se dvěma vstupy a souhrnnými počty také z principu odporuje formátu tidy data. Přesto je velmi užitečná právě pro zobrazení souhrnných počtů nebo pro statistické testy porovnávající četnosti.

Primární formát uchování dat by měl být tidy data (např. co řádek tabulky, to jeden náhodný výběr ze základního souboru) a převod na kontingenční tabulku pak již snadno provedeme funkcí table(), jak je uvedeno na příkladech v kapitole 15. Testy četností.

Souhrnné statistiky a manipulace pomocí knihovny dplyr

Nyní přichází ta zábavnější část. Předpokládejme, že máme tabulku ve správném formátu tidy data. Pak je radost s ní pracovat. Nejlepší je samozřejmě data hned od začátku ukládat do formátu tidy data, ušetříme si tím starosti. Pakliže ale tento formát nesplňují, musíme je na tidy data nejdříve převést, o čemž byla horní část této stránky.

Funkce v dplyr jsou určeny pro jednodušší a efektivnější spravování dat. Zápis těchto operací je oproti dřívějším knihovnám velice zjednodušen a implementace je vskutku bleskurychlá. Některé funkce jsme již poznali výše.

Knihovna dplyr implementuje „gramatiku“ pro manipulaci s daty (konkrétně slovesa).

  • rename: přejmenování názvů vybraných sloupců
  • arrange: seřadí řádky (podle velikosti jedné či více proměnných), pro sestupné řazení se doplní název proměnné funkcí desc
  • mutate: přidá novou proměnnou (sloupec) nebo transformuje existující proměnnou
  • select: vybere sloupce
  • filter: vybere řádky na základě logických podmínek
  • group_by: rozdělení řádků do skupin podle hodnot nějaké proměnné či kombinace hodnot více proměnných
  • summarize: generuje sumární statistiky různých proměnných, a to ve vrstvách podle skupin vytvořených funkcí group_by
  • ungroup: jestliže jsme vytvořili sumární statistiky funkcí summarize pro tabulku rozdělenou do více skupin a chceme výsledek například řadit, je nutné nejdříve pomocí ungroup zrušit „skupinový“ charakter

Dále můžeme používat operátor %>% (pipeline), který jsme již poznali výše.

Příklady

Budeme pracovat s interním dataframe mtcars, seznamte se s ním proto pomocí funkcí View(mtcars) a nápovědy ?mtcars.

Všimněme si, že názvy automobilů jsou uloženy jen jako názvy řádků tabulky. Uděláme z nich „oficiální proměnnou“, tedy nový sloupec.

mtcars$car <- rownames(mtcars)

Seřadíme tabulku podle sloupce car.

mtcars <- mtcars %>% arrange(car)
mtcars
##     mpg cyl  disp  hp drat    wt  qsec vs am gear carb                 car
## 1  15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2         AMC Javelin
## 2  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4  Cadillac Fleetwood
## 3  13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4          Camaro Z28
## 4  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1          Datsun 710
## 5  15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2    Dodge Challenger
## 6  14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4          Duster 360
## 7  19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6        Ferrari Dino
## 8  32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1            Fiat 128
## 9  27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1           Fiat X1-9
## 10 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4      Ford Pantera L
## 11 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2         Honda Civic
## 12 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1      Hornet 4 Drive
## 13 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2   Hornet Sportabout
## 14 14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4   Chrysler Imperial
## 15 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4 Lincoln Continental
## 16 30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2        Lotus Europa
## 17 15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8       Maserati Bora
## 18 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4           Mazda RX4
## 19 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4       Mazda RX4 Wag
## 20 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2            Merc 230
## 21 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2           Merc 240D
## 22 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4            Merc 280
## 23 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4           Merc 280C
## 24 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3          Merc 450SE
## 25 17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3          Merc 450SL
## 26 15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3         Merc 450SLC
## 27 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2    Pontiac Firebird
## 28 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2       Porsche 914-2
## 29 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1      Toyota Corolla
## 30 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1       Toyota Corona
## 31 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1             Valiant
## 32 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2          Volvo 142E

Používá to defaultní Locale našeho systému, což bývá např. Czech. Takže Chrysler je až a Hornet.

Sys.getlocale(category = "LC_ALL")   # zjisti nastavení Locale
## [1] "LC_COLLATE=Czech_Czech Republic.1250;LC_CTYPE=Czech_Czech Republic.1250;LC_MONETARY=Czech_Czech Republic.1250;LC_NUMERIC=C;LC_TIME=Czech_Czech Republic.1250"
Sys.setlocale(category = "LC_COLLATE", locale = "English")   # nastav Anglické Locale pro řazení
## [1] "English_United States.1252"
mtcars <- mtcars %>% arrange(car)
mtcars
##     mpg cyl  disp  hp drat    wt  qsec vs am gear carb                 car
## 1  15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2         AMC Javelin
## 2  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4  Cadillac Fleetwood
## 3  13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4          Camaro Z28
## 4  14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4   Chrysler Imperial
## 5  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1          Datsun 710
## 6  15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2    Dodge Challenger
## 7  14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4          Duster 360
## 8  19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6        Ferrari Dino
## 9  32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1            Fiat 128
## 10 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1           Fiat X1-9
## 11 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4      Ford Pantera L
## 12 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2         Honda Civic
## 13 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1      Hornet 4 Drive
## 14 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2   Hornet Sportabout
## 15 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4 Lincoln Continental
## 16 30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2        Lotus Europa
## 17 15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8       Maserati Bora
## 18 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4           Mazda RX4
## 19 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4       Mazda RX4 Wag
## 20 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2            Merc 230
## 21 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2           Merc 240D
## 22 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4            Merc 280
## 23 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4           Merc 280C
## 24 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3          Merc 450SE
## 25 17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3          Merc 450SL
## 26 15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3         Merc 450SLC
## 27 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2    Pontiac Firebird
## 28 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2       Porsche 914-2
## 29 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1      Toyota Corolla
## 30 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1       Toyota Corona
## 31 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1             Valiant
## 32 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2          Volvo 142E

Nyní je Chrysler před Datsun.

Seřadíme ji podle cyl (sestupně) a v případě více shodných hodnot budeme řadit ještě podle gear (normálně vzestupně).

mtcars <- mtcars %>% arrange(desc(cyl), gear)
mtcars
##     mpg cyl  disp  hp drat    wt  qsec vs am gear carb                 car
## 1  15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2         AMC Javelin
## 2  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4  Cadillac Fleetwood
## 3  13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4          Camaro Z28
## 4  14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4   Chrysler Imperial
## 5  15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2    Dodge Challenger
## 6  14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4          Duster 360
## 7  18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2   Hornet Sportabout
## 8  10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4 Lincoln Continental
## 9  16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3          Merc 450SE
## 10 17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3          Merc 450SL
## 11 15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3         Merc 450SLC
## 12 19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2    Pontiac Firebird
## 13 15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4      Ford Pantera L
## 14 15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8       Maserati Bora
## 15 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1      Hornet 4 Drive
## 16 18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1             Valiant
## 17 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4           Mazda RX4
## 18 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4       Mazda RX4 Wag
## 19 19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4            Merc 280
## 20 17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4           Merc 280C
## 21 19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6        Ferrari Dino
## 22 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1       Toyota Corona
## 23 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1          Datsun 710
## 24 32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1            Fiat 128
## 25 27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1           Fiat X1-9
## 26 30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2         Honda Civic
## 27 22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2            Merc 230
## 28 24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2           Merc 240D
## 29 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1      Toyota Corolla
## 30 21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2          Volvo 142E
## 31 30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2        Lotus Europa
## 32 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2       Porsche 914-2

Přejmenujeme sloupec am na razeni.

mtcars <- mtcars %>% rename(razeni = am)
head(mtcars)
##    mpg cyl disp  hp drat    wt  qsec vs razeni gear carb
## 1 15.2   8  304 150 3.15 3.435 17.30  0      0    3    2
## 2 10.4   8  472 205 2.93 5.250 17.98  0      0    3    4
## 3 13.3   8  350 245 3.73 3.840 15.41  0      0    3    4
## 4 14.7   8  440 230 3.23 5.345 17.42  0      0    3    4
## 5 15.5   8  318 150 2.76 3.520 16.87  0      0    3    2
## 6 14.3   8  360 245 3.21 3.570 15.84  0      0    3    4
##                  car
## 1        AMC Javelin
## 2 Cadillac Fleetwood
## 3         Camaro Z28
## 4  Chrysler Imperial
## 5   Dodge Challenger
## 6         Duster 360

Vybereme jen sloupce car a mpg (dojezd v mílích na gallon).

prehled <- mtcars %>% select(car, mpg)
head(prehled)
##                  car  mpg
## 1        AMC Javelin 15.2
## 2 Cadillac Fleetwood 10.4
## 3         Camaro Z28 13.3
## 4  Chrysler Imperial 14.7
## 5   Dodge Challenger 15.5
## 6         Duster 360 14.3

Vybereme jen řádky, kde vs = 0 a carb >= 4, výsledek seřadíme podle názvu.

vyber <- mtcars %>% filter(vs == 0, carb >= 4) %>% arrange(car)
vyber
##     mpg cyl disp  hp drat    wt  qsec vs razeni gear carb
## 1  10.4   8  472 205 2.93 5.250 17.98  0      0    3    4
## 2  13.3   8  350 245 3.73 3.840 15.41  0      0    3    4
## 3  14.7   8  440 230 3.23 5.345 17.42  0      0    3    4
## 4  14.3   8  360 245 3.21 3.570 15.84  0      0    3    4
## 5  19.7   6  145 175 3.62 2.770 15.50  0      1    5    6
## 6  15.8   8  351 264 4.22 3.170 14.50  0      1    5    4
## 7  10.4   8  460 215 3.00 5.424 17.82  0      0    3    4
## 8  15.0   8  301 335 3.54 3.570 14.60  0      1    5    8
## 9  21.0   6  160 110 3.90 2.620 16.46  0      1    4    4
## 10 21.0   6  160 110 3.90 2.875 17.02  0      1    4    4
##                    car
## 1   Cadillac Fleetwood
## 2           Camaro Z28
## 3    Chrysler Imperial
## 4           Duster 360
## 5         Ferrari Dino
## 6       Ford Pantera L
## 7  Lincoln Continental
## 8        Maserati Bora
## 9            Mazda RX4
## 10       Mazda RX4 Wag

Zavedeme novou proměnnou (sloupec) udávající, zda mají auta nadprůměrně nízkou spotřebu.

mtcars <- mtcars %>% mutate(usporne = (mpg > mean(mpg)))
mtcars
##     mpg cyl  disp  hp drat    wt  qsec vs razeni gear carb
## 1  15.2   8 304.0 150 3.15 3.435 17.30  0      0    3    2
## 2  10.4   8 472.0 205 2.93 5.250 17.98  0      0    3    4
## 3  13.3   8 350.0 245 3.73 3.840 15.41  0      0    3    4
## 4  14.7   8 440.0 230 3.23 5.345 17.42  0      0    3    4
## 5  15.5   8 318.0 150 2.76 3.520 16.87  0      0    3    2
## 6  14.3   8 360.0 245 3.21 3.570 15.84  0      0    3    4
## 7  18.7   8 360.0 175 3.15 3.440 17.02  0      0    3    2
## 8  10.4   8 460.0 215 3.00 5.424 17.82  0      0    3    4
## 9  16.4   8 275.8 180 3.07 4.070 17.40  0      0    3    3
## 10 17.3   8 275.8 180 3.07 3.730 17.60  0      0    3    3
## 11 15.2   8 275.8 180 3.07 3.780 18.00  0      0    3    3
## 12 19.2   8 400.0 175 3.08 3.845 17.05  0      0    3    2
## 13 15.8   8 351.0 264 4.22 3.170 14.50  0      1    5    4
## 14 15.0   8 301.0 335 3.54 3.570 14.60  0      1    5    8
## 15 21.4   6 258.0 110 3.08 3.215 19.44  1      0    3    1
## 16 18.1   6 225.0 105 2.76 3.460 20.22  1      0    3    1
## 17 21.0   6 160.0 110 3.90 2.620 16.46  0      1    4    4
## 18 21.0   6 160.0 110 3.90 2.875 17.02  0      1    4    4
## 19 19.2   6 167.6 123 3.92 3.440 18.30  1      0    4    4
## 20 17.8   6 167.6 123 3.92 3.440 18.90  1      0    4    4
## 21 19.7   6 145.0 175 3.62 2.770 15.50  0      1    5    6
## 22 21.5   4 120.1  97 3.70 2.465 20.01  1      0    3    1
## 23 22.8   4 108.0  93 3.85 2.320 18.61  1      1    4    1
## 24 32.4   4  78.7  66 4.08 2.200 19.47  1      1    4    1
## 25 27.3   4  79.0  66 4.08 1.935 18.90  1      1    4    1
## 26 30.4   4  75.7  52 4.93 1.615 18.52  1      1    4    2
## 27 22.8   4 140.8  95 3.92 3.150 22.90  1      0    4    2
## 28 24.4   4 146.7  62 3.69 3.190 20.00  1      0    4    2
## 29 33.9   4  71.1  65 4.22 1.835 19.90  1      1    4    1
## 30 21.4   4 121.0 109 4.11 2.780 18.60  1      1    4    2
## 31 30.4   4  95.1 113 3.77 1.513 16.90  1      1    5    2
## 32 26.0   4 120.3  91 4.43 2.140 16.70  0      1    5    2
##                    car usporne
## 1          AMC Javelin   FALSE
## 2   Cadillac Fleetwood   FALSE
## 3           Camaro Z28   FALSE
## 4    Chrysler Imperial   FALSE
## 5     Dodge Challenger   FALSE
## 6           Duster 360   FALSE
## 7    Hornet Sportabout   FALSE
## 8  Lincoln Continental   FALSE
## 9           Merc 450SE   FALSE
## 10          Merc 450SL   FALSE
## 11         Merc 450SLC   FALSE
## 12    Pontiac Firebird   FALSE
## 13      Ford Pantera L   FALSE
## 14       Maserati Bora   FALSE
## 15      Hornet 4 Drive    TRUE
## 16             Valiant   FALSE
## 17           Mazda RX4    TRUE
## 18       Mazda RX4 Wag    TRUE
## 19            Merc 280   FALSE
## 20           Merc 280C   FALSE
## 21        Ferrari Dino   FALSE
## 22       Toyota Corona    TRUE
## 23          Datsun 710    TRUE
## 24            Fiat 128    TRUE
## 25           Fiat X1-9    TRUE
## 26         Honda Civic    TRUE
## 27            Merc 230    TRUE
## 28           Merc 240D    TRUE
## 29      Toyota Corolla    TRUE
## 30          Volvo 142E    TRUE
## 31        Lotus Europa    TRUE
## 32       Porsche 914-2    TRUE

A nyní by mě zajímalo, kolik průměrně váží auta s nízkou spotřebou a kolik ta s vysokou.

mtcars %>% group_by(usporne) %>% summarize(wtPrumer = mean(wt))
## # A tibble: 2 × 2
##   usporne wtPrumer
##     <lgl>    <dbl>
## 1   FALSE 3.838833
## 2    TRUE 2.418071

Tak to vypadá, že úsporná auta jsou spíše ta lehčí (opět připomeňme, že souvislost neznamená nutně kauzalitu).

Zvládli bychom vypočítat i 95% konfidenční intervaly tohoto odhadu? V jednom sloupci může být vždy jen jedno číslo, takže interval bude ve dvou sloupcích (spodní a horní hodnota).

mtcars %>% group_by(usporne) %>% summarize(wtPrumer = mean(wt),
    confInt1 = t.test(wt)$conf.int[1], confInt2 = t.test(wt)$conf.int[2])
## # A tibble: 2 × 4
##   usporne wtPrumer confInt1 confInt2
##     <lgl>    <dbl>    <dbl>    <dbl>
## 1   FALSE 3.838833 3.467848 4.209819
## 2    TRUE 2.418071 2.085181 2.750962

A pojďme vymyslet složitější skupiny. Rozdělíme data podle převodovky (razeni), válců (cyl) a počtu karburátorů (carb). V těchto skupinách vypočteme průměr a směrodatnou odchylku z mpg a wt. Výsledky seřadíme od největší průměrné mpg po nejmenší.

mtcars %>% group_by(razeni, cyl, carb) %>% summarize(mpgPrumer = mean(mpg), mpgSD = sd(mpg),
            wtPrumer = mean(wt), wtSD = sd(wt)) %>% ungroup %>% arrange(desc(mpgPrumer))
## # A tibble: 13 × 7
##    razeni   cyl  carb mpgPrumer     mpgSD wtPrumer       wtSD
##     <dbl> <dbl> <dbl>     <dbl>     <dbl>    <dbl>      <dbl>
## 1       1     4     1     29.10 5.0616203   2.0725 0.22570261
## 2       1     4     2     27.05 4.3000000   2.0120 0.58103873
## 3       0     4     2     23.60 1.1313708   3.1700 0.02828427
## 4       0     4     1     21.50       NaN   2.4650        NaN
## 5       1     6     4     21.00 0.0000000   2.7475 0.18031223
## 6       0     6     1     19.75 2.3334524   3.3375 0.17324116
## 7       1     6     6     19.70       NaN   2.7700        NaN
## 8       0     6     4     18.50 0.9899495   3.4400 0.00000000
## 9       0     8     2     17.15 2.0920484   3.5600 0.19395017
## 10      0     8     3     16.30 1.0535654   3.8600 0.18357560
## 11      1     8     4     15.80       NaN   3.1700        NaN
## 12      1     8     8     15.00       NaN   3.5700        NaN
## 13      0     8     4     12.62 2.0897368   4.6858 0.90252324

Vidíme, že některé skupiny byly asi zastoupeny jen jednou položkou, a proto nebylo možné vypočítat směrodatnou odchylku.


© 13. 5. 2015, 12. 5. 2017 Tomáš Bořil, borilt@gmail.com