11.1 Textové tabulární delimitované soubory

Základní způsob výměny dat jsou textové tabulární delimitované formáty, kde jednotlivé řádky odpovídají pozorováním a kde jsou sloupce (proměnné) odděleny nějakým jasně definovaným znakem, např. mezerou, čárkou, středníkem nebo dvojtečkou. Velká výhoda těchto formátů je, že je možné je načíst téměř do každého softwaru, který pracuje s daty a stejně tak je z něj i vypsat. Zároveň jsou data “čitelná lidmi” a pokud se něco pokazí, je často možné data nějak zachránit. Nevýhodou je, že tyto formáty mohou obsahovat pouze tabulární data (tabulky), jednoduché datové typy (tj. ne složitější objekty, jako jsou např. odhady modelů), nemohou obsahovat metadata (např. atributy), jejich načítání může trvat dlouho (pokud jsou data velká) a že data v těchto formátech na disku zabírají hodně místa (pokud nejsou komprimovaná).

Příkladem dat v textovém tabulárním formátu je např. soubor “bmi_data.csv.” Na disku vypadá takto:

id,height,weight,bmi
1,153,55,23.4952368747
2,207,97,22.6376344839
3,173,83,27.7322997761
4,181,92,28.0821708739
5,164,112,41.6418798334

Zde se podivame na to, jak data v textovem tabularnim formatu nacist a ulozit pomoci funkci z baliku readr. Oproti podobnym funkcim ze zakladniho R, tyto funkce nacitaji data rychleji, umoznuji presneji urcit, jak se maji data nacist a neprovadi nektere neprijemne upravy.4 Před použitím těchto funkcí musíme nejdříve načíst balík readr:

library(readr)

11.1.1 Načítání dat

Všechny funkce z balíku readr, které sloužící k načítání dat, jsou pojmenované read_XXX(), kde XXX je jméno načítaného formátu. Data ve formátu CSV tedy načítá funkce read_csv(). (Pozor! Funkce ze základního R určené k načtení těchto dat jsou pojmenované téměř stejně, jen místo podtržítka je jméno formátu oddělené tečkou. Obdobou funkce read_csv() je funkce read.csv() ze základního balíku. Přestože se funkce jmenují podobně, mají výrazně odlišný interface i chování.) Všechny funkce read_XXX() z balíku readr vracejí tabulku třídy tibble.

Základní funkcí k načtení textových delimitovaných tabulárních dat je funkce read_delim(). Při jejím použití je nezbytné zadat pouze jediný parametr: jméno načítaného souboru včetně cesty. Při takovém použití funkce odhadne, který znak odděluje jednotlivé sloupce (proměnné) v datech. Bezpečnější je však tento oddělovací znak zadat explicitně pomocí parametru delim. V našem případě nastavíme delim = ",":

bmi_data <- read_delim("data/reading_and_writing/bmi_data.csv", delim = ",")
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): id, height, weight, bmi
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
bmi_data
## # A tibble: 5 × 4
##      id height weight   bmi
##   <dbl>  <dbl>  <dbl> <dbl>
## 1     1    153     55  23.5
## 2     2    207     97  22.6
## 3     3    173     83  27.7
## 4     4    181     92  28.1
## 5     5    164    112  41.6

Textová tabulární data mohou mít jednotlivé proměnné oddělené různě. Existují však tři nejobvyklejší varianty, pro které má readr speciální varianty funkce read_delim():

  • read_csv() načítá klasický americký standard CSV, kde jsou jednotlivé proměnné oddělené čárkami a celou a desetinnou část čísla odděluje desetinná tečka,
  • read_csv2() načítá “evropskou” variantu CSV, kde jsou jednotlivé proměnné oddělené středníky a desetinná místa v číslech odděluje čárka a
  • read_tsv() načítá variantu formátu, kde jsou proměnné oddělené tabelátorem.

V těchto funkcích není potřeba nastavovat parametr delim; většinu ostatních parametrů mají tyto funkce stejné jako funkce read_delim().

Protože dataset “bmi_data.csv” splňuje klasický formát CSV, můžeme jej pohodlněji načíst i pomocí funkce read_csv():

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv")
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): id, height, weight, bmi
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Všechny funkce read_XXX() berou první řádek datového souboru implicitně jako jména proměnných. Pokud soubor jména proměnných neobsahuje, je to třeba funkcím říct pomocí parametru col_names. Ten může nabývat jedné ze tří hodnot. Pokud má hodnotu TRUE, pak první řádek souboru chápe jako jména proměnných. Pokud má hodnotu FALSE, pak se předpokládá, že první řádek souboru jména proměnných neobsahuje; z řádku se načtou data a funkce sama jednotlivé proměnné pojmenuje X1, X2 atd. Třetí možností je zadat jména proměnných ručně. V tomto případě musí být parametr col_names zadán jako vektor řetězců, který obsahuje jména jednotlivých proměnných. Na rozdíl od funkcí ze základního R, funkce z balíku readr jména proměnných nijak nemodifikují. V důsledku toho nemusejí být jména proměnných platnými názvy proměnných v R. To je však vždy možné opravit dvěma různými způsoby: Jednou možností je dodatečně změnit jména ručně pomocí funkcí names() nebo setNames(). Druhou možností je pomocí parametru name_repair nastavit funkci, která jména sloupců opraví automaticky. Jednou z možností je zde použít funkci make_clean_names() z balíku janitor. Tato funkce odstraní divné znaky, písmena s háčky, čárkami a jinými akcenty zbaví těchto akcentů, mezery nahradí podtržítky apod. Volání funkce read_csv() pak bude vypadat takto:

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv",
                     name_repair = janitor::make_clean_names)

Ukážeme si to na příkladu. Soubor “bmi_data_headless.csv” obsahuje stejná data jako soubor “bmi_data.csv,” na prvním řádku však nejsou uvedena jména sloupců, ale dataset začíná přímo daty. Pokud bychom dataset načetli stejně jako výše, funkce read_csv() by první řádek použila k pojmenování proměnných (jména sloupců by navíc v našem případě byla syntakticky nepřípustná, takže bychom je museli používat spolu se zpětnými apostrofy):

read_csv("data/reading_and_writing/bmi_data_headless.csv")
## Rows: 4 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): 1, 153, 55, 23.4952368747
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 4 × 4
##     `1` `153`  `55` `23.4952368747`
##   <dbl> <dbl> <dbl>           <dbl>
## 1     2   207    97            22.6
## 2     3   173    83            27.7
## 3     4   181    92            28.1
## 4     5   164   112            41.6

Tomu můžeme zabránit tak, že nastavíme parametr col_names = FALSE, takže se první řádek souboru považuje za data:

read_csv("data/reading_and_writing/bmi_data_headless.csv",
         col_names = FALSE)
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): X1, X2, X3, X4
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 5 × 4
##      X1    X2    X3    X4
##   <dbl> <dbl> <dbl> <dbl>
## 1     1   153    55  23.5
## 2     2   207    97  22.6
## 3     3   173    83  27.7
## 4     4   181    92  28.1
## 5     5   164   112  41.6

V takovém případě se sloupce tabulky pojmenují X1, X2 atd. Alternativně můžeme pomocí parametru col_names přímo zadat jména jednotlivých sloupců, např. jako col_names = c("id", "výška", "váha", "bmi") (první řádek souboru se bude opět považovat za data):

read_csv("data/reading_and_writing/bmi_data_headless.csv",
         col_names = c("id", "výška", "váha", "bmi"))
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): id, výška, váha, bmi
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 5 × 4
##      id výška  váha   bmi
##   <dbl> <dbl> <dbl> <dbl>
## 1     1   153    55  23.5
## 2     2   207    97  22.6
## 3     3   173    83  27.7
## 4     4   181    92  28.1
## 5     5   164   112  41.6

Některé datové soubory obsahují v prvních řádcích nějaké balastní informace, např. údaje o pořízení dat, copyrightu, parametrech simulace apod. Tyto řádky je možné přeskočit pomocí parametru skip: např. skip = 5 znamená, že se má přeskočit prvních pět řádků. Podobně některé soubory obsahují mezi skutečnými daty komentáře. Pokud jsou tyto komentáře uvozeny nějakým jasným znakem, můžeme je při načítání dat přeskočit pomocí parametru comment: např. comment = "#" znamená, že se přeskočí všechny řádky, které začínají “křížkem.”

Opět si to ukážeme na příkladu. Soubor “bmi_data_comments.csv” opět obsahuje stejná data jako “bmi_data.csv,” na prvních dvou řádcích však obsahuje nějaké informace o experimentu. Na disku vypadá soubor takto:

Experiment no. 747.
Top secret
id,height,weight,bmi
1,153,55,23.4952368747
2,207,97,22.6376344839
3,173,83,27.7322997761
4,181,92,28.0821708739
5,164,112,41.6418798334

Tento soubor musíme načíst s pomocí parametru skip; v opačném případě se věci opravdu pokazí (vyzkoušejte si to):

read_csv("data/reading_and_writing/bmi_data_comments.csv", skip = 2)
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): id, height, weight, bmi
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 5 × 4
##      id height weight   bmi
##   <dbl>  <dbl>  <dbl> <dbl>
## 1     1    153     55  23.5
## 2     2    207     97  22.6
## 3     3    173     83  27.7
## 4     4    181     92  28.1
## 5     5    164    112  41.6

Soubor “bmi_data_comments2.csv” obsahuje opět stejná data; nyní jsou však dvě množiny pozorování nadepsány komentáři a jeden řádek má také vlastní komentář:

id,height,weight,bmi
# the first set of observations
1,153,55,23.4952368747
2,207,97,22.6376344839
3,173,83,27.7322997761  # suspicious
# the second set of observations
4,181,92,28.0821708739
5,164,112,41.6418798334

V tomto případě musíme zadat znak, kterým začínají komentáře, pomocí parametru comment. Funkce read_XXX() pak ignorují vše od tohoto znaku do konce řádku:

read_csv("data/reading_and_writing/bmi_data_comments2.csv", comment = "#")
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): id, height, weight, bmi
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 5 × 4
##      id height weight   bmi
##   <dbl>  <dbl>  <dbl> <dbl>
## 1     1    153     55  23.5
## 2     2    207     97  22.6
## 3     3    173     83  27.7
## 4     4    181     92  28.1
## 5     5    164    112  41.6

Různé datové soubory kódují chybějící hodnoty různě. Standardně funkce read_XXX() očekávají, že chybějící hodnota je buď prázdná, nebo obsahuje řetězec “NA.” To je však možné změnit parametrem na. Do něj uložíte vektor všech hodnot, které má funkce read_XXX() považovat za NA. Pokud např. váš datový soubor kóduje chybějící hodnoty pomocí tečky, nastavíte na = ".". Pokud navíc může být zadaná chybějící hodnota i jako prázdný řetězec nebo řetězec “NA,” zadáte na = c("", ".", "NA"). (Funkce read_XXX() při načítání jednotlivých hodnot implicitně odstraní všechny úvodní a koncové mezery, takže našemu “tečkovému” pravidlu vyhoví jak řetězce “.” tak i " . ". Toto chování můžete změnit parametrem trim_ws.) Následující kód ukazuje příklad:

read_csv("data/reading_and_writing/bmi_data_na.csv", na = c("", ".", "NA"))
## Rows: 5 Columns: 4
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (4): id, height, weight, bmi
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## # A tibble: 5 × 4
##      id height weight   bmi
##   <dbl>  <dbl>  <dbl> <dbl>
## 1     1    153     55  23.5
## 2     2    207     97  22.6
## 3     3     NA     83  27.7
## 4     4    181     NA  28.1
## 5     5    164    112  41.6

Jak jste si asi všimli, funkce read_XXX() při načítání dat samy odhadnou, jaká data obsahují jednotlivé proměnné, a převedou je na daný datový typ. (Výsledná tabulka třídy tibble tyto typy vypisuje pod jmény proměnných.) Informace o tom, jaký typ funkce zvolila pro který sloupec, se vypíše při načítání: informace má tvar “dbl (4): id, height, weight, bmi” apod. V našem případě se všechny proměnné převedly na reálná čísla. Funkce read_XXX() odhadují typ proměnné na základě prvních 1 000 načtených řádků (toto číslo je možné změnit pomocí parametru guess_max). Nechat si datový typ odhadnout, je pohodlné, ale nepříliš bezpečné. Mnohem rozumnější je zadat datový typ jednotlivých sloupců ručně pomocí parametru col_types. Ten je mozne zadat tremi zpusoby. Zaprve, hodnota NULL indikuje, ze se typy promennych maji odhadnout. Zadruhe, datove typy jednotlivych sloupcu je mozne zadat plnymi jmeny ve funkci cols(). A zatreti, tato jmena muzeme zkratit do jednopismennych zkratek zadanych v jednom retezci. Seznam typu sloupcu a jejich zkratek uvadi tabulka 11.1. Rozdíl mezi funkcemi col_double() a col_number() spočívá v tom, že col_number() umožňuje mít před a za vlastním číslem nějaké nečíselné znaky, které zahodí. To se hodí např. při načítání peněžních údajů, kde col_number() zvládne převést na číslo i řetězce jako $15.30 nebo 753 Kč.

Tabulka 11.1: Seznam jmen funkcí pro určení datového typu načítané proměnné.
funkce význam zkratka parametry
col_logical() logická proměnná “l”
col_integer() striktní celé číslo “i”
col_double() striktní reálné číslo “d”
col_number() flexibilní číslo “n”
col_character() řetězec “c”
col_date() datum (jen den bez hodin) “D” format
col_datetime() datum (den i hodina) “T” format
col_time() čas (jen hodiny, minuty a sekundy) “t” format
col_factor() faktor “f” levels, ordered, include_na
col_guess() typ odhadne R “?”
col_skip() proměnná se přeskočí a nenačte "_" nebo “-”

Řekněme, že proměnnou id chceme mít uloženou jako typ integer, zatímco ostatní hodnoty jako reálná čísla. Toho můžeme dosáhnout ručně takto (v tomto případě R nevypíše informaci o sloupcích):

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv",
                     col_types = cols(id = col_integer(),
                                      height = col_double(),
                                      weight = col_double(),
                                      bmi = col_double()))

nebo stručněji takto:

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv",
                     col_types = "iddd")

Specifikací sloupců můžeme samozřejmě změnit i typ sloupců. Někdy např. můžeme chtít načíst všechny proměnné jako řetězce, a pak si je zkonvertovat sami. To můžeme udělat třemi způsoby: 1) sloupce můžeme nastavit pomocí plného volání funkcí col_types = cols(id = col_character(), height = col_character(), weight = col_character(), bmi = col_character()), 2) je můžeme nastavit pomocí zkratkových písmen jako col_types = "cccc" nebo 3) nastavíme implicitní typ sloupce pomocí speciálního parametru .default buď plným voláním funkce col_types = cols(.default = col_character()) nebo pomocí zkratkového písmene:

read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = cols(.default = "c"))
## # A tibble: 5 × 4
##   id    height weight bmi          
##   <chr> <chr>  <chr>  <chr>        
## 1 1     153    55     23.4952368747
## 2 2     207    97     22.6376344839
## 3 3     173    83     27.7322997761
## 4 4     181    92     28.0821708739
## 5 5     164    112    41.6418798334

Nejjednodušší způsob, jak explicitně zadat datové typy sloupců tabulky, je nechat funkci read_XXX() odhadnout datový typ sloupců, a pak specifikaci cols() zkopírovat a případně upravit. Po vlastním načtení je možné specifikaci získat i pomocí funkce spec(), jejímž jediným argumentem je načtená tabulka. Specifikaci je možné zjednodušit pomocí funkce cols_condense(), která nejčastěji používaný datový typ shrne do parametru .default:

cols_condense(spec(bmi_data))
## cols(
##   .default = col_double(),
##   id = col_integer()
## )

Pokud bychom chtěli vypsat rychlý přehled o tom, v jakém formátu se data načetla, můžeme na výsledek funkce spec() spustit funkci summary(). Výsledek bude ve stejném formátu, v jakém tuto specifikaci vypíší funkce read_XXX():

summary(spec(bmi_data))
## ── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (3): height, weight, bmi
## int (1): id

Nektere funkce col_XXX umoznuji zadat parametry. Funkce pro nacitani data a casu umoznuji zadat format, ve kterem je datum nebo cas ulozen. Funkce pro nacitani faktoru umoznuji zadat platne urovne faktoru, to, zda je faktor ordinalni, a to, zda ma byt hodnota NA soucasti urovni faktoru. Uplny seznam parametru uvadi tabulka 11.1. Pokud bychom např. chtěli načíst proměnnou id jako faktor se třemi úrovněmi (“1,” “2” a “3”), můžeme to udělat takto (funkce read_XXX() nikdy nic nepřevádí na faktory samy od sebe – pokud chcete proměnnou převést na faktor, musíte o to požádat):

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv",
                     col_types = cols(id = col_factor(levels = as.character(1:3)),
                                      height = col_integer(),
                                      weight = col_integer(),
                                      bmi = col_double()))

Funkce vypíše varování, protože proměnná id obsahuje i jiné než povolené úrovně, a nepovolené úrovně nahradí hodnotami NA. (Pokud bychom chtěli nechat funkci read_XXX() odhadnout úrovně faktoru z dat, museli bychom zadat parametr levels = NULL.)

Funkcím read_XXX() je možné sdělit i to, které proměnné z tabulky vyloučit. K tomu slouží funkce col_skip(). Řekněme, že bychom chtěli náši tabulku anonymizovat tím, že z dat odstraníme identifikátory id. Toho můžeme dosáhnout např. takto:

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv",
                     col_types = cols(id = col_skip(),
                                      height = col_integer(),
                                      weight = col_integer(),
                                      bmi = col_double()))
bmi_data
## # A tibble: 5 × 3
##   height weight   bmi
##    <int>  <int> <dbl>
## 1    153     55  23.5
## 2    207     97  22.6
## 3    173     83  27.7
## 4    181     92  28.1
## 5    164    112  41.6

Pokud bychom chtěli načíst jen některé proměnné, stačí nahradit funkci cols() funkcí cols_only() a v ní uvést pouze proměnné, které se mají načíst. Následující kód načte pouze proměnné height a bmi:

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv",
                     col_types = cols_only(height = col_integer(),
                                           bmi = col_double()))
bmi_data
## # A tibble: 5 × 2
##   height   bmi
##    <int> <dbl>
## 1    153  23.5
## 2    207  22.6
## 3    173  27.7
## 4    181  28.1
## 5    164  41.6

K vyberu sloupcu, ktere se maji nacist, je mozne pouzit i sofistikovanejsi parametr col_select, ktera pouziva stejnou syntaxi jako funkce select() z baliku dplyr, viz oddil 16.1.2. Vybrané sloupce je možné vyjmenovat (bez uvozovek) nebo zavolat pořadím sloupce:

read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = "iddd",
         col_select = c(height, bmi))
## # A tibble: 5 × 2
##   height   bmi
##    <dbl> <dbl>
## 1    153  23.5
## 2    207  22.6
## 3    173  27.7
## 4    181  28.1
## 5    164  41.6
read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = "iddd",
         col_select = c(2, 4))
## # A tibble: 5 × 2
##   height   bmi
##    <dbl> <dbl>
## 1    153  23.5
## 2    207  22.6
## 3    173  27.7
## 4    181  28.1
## 5    164  41.6

Pomocí symbolu : je možné vybrat souvisle sloupce od vybraného počátečního po vybraný koncový:

read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = "iddd",
         col_select = height:bmi)
## # A tibble: 5 × 3
##   height weight   bmi
##    <dbl>  <dbl> <dbl>
## 1    153     55  23.5
## 2    207     97  22.6
## 3    173     83  27.7
## 4    181     92  28.1
## 5    164    112  41.6

Pomocí symbolu - je možné sloupce vypouštět:

read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = "iddd",
         col_select = -c(height, bmi))
## # A tibble: 5 × 2
##      id weight
##   <int>  <dbl>
## 1     1     55
## 2     2     97
## 3     3     83
## 4     4     92
## 5     5    112

Speciální funkce starts_with(), ends_with(), contains(), matches() a num_range() umožňují vybírat sloupce podle části jejich názvu. Sloupce, jejichž název končí na “ht” je např. možné načíst takto:

read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = "iddd",
         col_select = ends_with("ht"))
## # A tibble: 5 × 2
##   height weight
##    <dbl>  <dbl>
## 1    153     55
## 2    207     97
## 3    173     83
## 4    181     92
## 5    164    112

Tyto možnosti je samozřejmě možné i kombinovat:

read_csv("data/reading_and_writing/bmi_data.csv",
         col_types = "iddd",
         col_select = c(id, ends_with("ht")))
## # A tibble: 5 × 3
##      id height weight
##   <int>  <dbl>  <dbl>
## 1     1    153     55
## 2     2    207     97
## 3     3    173     83
## 4     4    181     92
## 5     5    164    112

Jak jsme viděli výše, pokud se při načítání jednotlivých sloupců tabulky něco pokazí, funkce read_XXX() o tom vypíší varování a příslušnou “buňku” tabulky doplní hodnotou NA. K tomu může dojít z různých důvodů: Datový typ, který jste zadali (nebo který R odhadlo na základě prvních 1 000 řádků) není dost obecný pro všechny řádky tabulky. První řádky např. obsahují jen celá čísla, zatímco pozdější řádky i reálná čísla. Faktor obsahuje i jiné hodnoty, než zadané platné úrovně atd. Pokud je varování hodně, zobrazí se jen několik prvních varování. Všechna varování je možné vypsat funkcí problems(), jejímž jediným argumentem je načtená tabulka.

Většinou je moudré ladit zadání typů sloupců ve specifikaci cols() tak dlouho, až R nevypisuje žádná varování. To je užitečné zejména v případě, že načítáte různé datové soubory se stejnou strukturou. Když se potom u některého souboru objeví varování, znamená to, že se v datech něco změnilo. Pokud chcete být opravdu důkladně varováni, že k tomu došlo, můžete použít funkci stop_for_problems(). Jejím jediným argumentem je načtená tabulka. Pokud při jeho načtení došlo k nějakým varováním, funkce stop_for_problems() zastaví běh skriptu.

Tabularni datove soubory jsou obycejne textove soubory bez specialniho formatovani. Proto cela rada prvku techto souboru neni standardizovana. Jednotlive datove soubory se mohou lisit kodovanim, znakem pouzitym pro oddeleni celych a desetinnych cisel, znakem pouzitym pro oddeleni tisicu, jmeny mesicu a dnu v tydnu apod. Balik readr implicitne pouziva americkou konvenci- kodovani je UTF-8, desetinna mista oddeluje tecka, tisice carka a veskera jmena jsou anglicka. Toto chovani muzete zmenit pomoci parametru locale, do ktereho vlozite objekt vytvoreny funkci locale(). Jeji zakladni parametry uvadi tabulka 11.2.

Tabulka 11.2: Seznam jmen funkcí pro určení datového typu načítané proměnné.
parametr význam impl. hodnota
date_names řetězec pro jména datumů (česká “cs,” slovenská “sk”) “en”
date_format formát data “%AD”
time_format formát času “%AT”
decimal_mark znak pro oddělení desetinných míst “.”
grouping_mark znak pro oddělení tisíců “,”
tz časová zóna “UTC”
encoding kódování souboru “UTF-8”
asciify jestli soubor odstranil diakritiku ze jmen datumů FALSE

Nastavení ve funkci locale si můžete vyzkoušet tak, že je necháte vypsat do konzoly. Typické české nastavení data vytvořená z LibreOffice na Linuxu s formátem data typu 31. 12. 2017 by vypadalo takto:

locale("cs",
       date_format = "%d.%*%m.%*%Y",
       decimal_mark = ",", grouping_mark = ".",
       tz = "Europe/Prague")
## <locale>
## Numbers:  123.456,78
## Formats:  %d.%*%m.%*%Y / %AT
## Timezone: Europe/Prague
## Encoding: UTF-8
## <date_names>
## Days:   neděle (ne), pondělí (po), úterý (út), středa (st), čtvrtek (čt), pátek
##         (pá), sobota (so)
## Months: ledna (led), února (úno), března (bře), dubna (dub), května (kvě),
##         června (čvn), července (čvc), srpna (srp), září (zář), října
##         (říj), listopadu (lis), prosince (pro)
## AM/PM:  dopoledne/odpoledne

Složitější nastavení locale, např. jak nastavit jména dnů a měsíců v nepodporovaném jazyce nebo jak zjistit časovou zónu, najdete v příslušné vinětě balíku readr.

Největší oříšek je v našich končinách zjistit, jaké kódování má daný soubor. K tomu může pomoci funkce guess_encoding(), jejímž jediným parametrem je jméno souboru, jehož kódování chceme odhadnout. Funkce vypíše seznam několika kódování a u nich pravděpodobnost, kterou tomuto kódování přikládá. Pokud funkci vyzkoušíme na našem testovacím souboru, dostaneme následující výsledek:

guess_encoding("data/reading_and_writing/bmi_data.csv")
## # A tibble: 1 × 2
##   encoding confidence
##   <chr>         <dbl>
## 1 ASCII             1

Protože náš soubor neobsahuje žádnou diakritiku, je si funkce guess_encoding() zcela jistá, že se jedná o ASCII soubor. (Protože ASCII je podmnožinou UTF-8, nemusíme kódování explicitně deklarovat.)

11.1.2 Načítání velkých souborů

Načítání velkých souborů funguje stejně jako načítání malých souborů – se dvěma specifiky: trvá déle a zabírá více operační paměti počítače. Balík readr zde pomáhá dvěma způsoby. Předně implicitně načítá data do paměti “lenivě.” To znamená, že když načtete datový soubor, funkce read_XXX() ve skutečnosti jen projde strukturu datového souboru (zjistí počet řádků a sloupců, typy sloupců apod.), ale žádná data ve skutečnosti do paměti počítače nenačte. To udělá teprve ve chvíli, kdy o tato data skutečně požádáte, tj. pokusíte si je vypsat, něco s nimi spočítat apod. Přitom se načtou pouze ta data, o která požádáte. To např. znamená, že když po načtení souboru vypíšete jen několik prvních nebo posledních řádků pomocí funkce head() nebo tail(), načtou se pouze tyto řádky.

Líné načítání dat může někdy způsobit určité problémy. Načítaný soubor je např. otevřený do té doby, dokud není do paměti načtený celý. Ve Windows není možné otevřený soubor smazat. Některé operace mohou (aspoň v současné implementaci) vést k tomu, že se data načtou několikrát. Línému načítání dat je možné zabránit pomocí parametru lazy = FALSE.

Zadruhé, R dokáže pracovat pouze s proměnnými, které má v operační paměti. Pokud se pokusíte načíst data, která se do paměti počítače nevejdou, R spadne. Pokud tedy máte opravdu hodně velký datový soubor, je užitečné dopředu zjistit, kolik paměti zabere. Objem dat v paměti můžete přibližně odhadnout následujícím způsobem:

  1. zjistíte, kolik řádků má vaše tabulka (v Linuxu to můžete udělat pomocí prográmku wc),
  2. načtete prvních 1 000 řádků tabulky (počet řádků omezíte pomocí parametru n_max); přitom odladíte datové typy jednotlivých sloupců,
  3. zjistíte, kolik paměti tabulka zabírá pomocí funkce object.size() a
  4. vypočítáte, kolik paměti zabere celý datový soubor: vydělíte velikost vzorku dat tisícem a vynásobíte ji počtem řádků souboru.

Balík readr umožňuje do jisté míry omezení proměnných na operační paměť počítače obejít. K tomu slouží speciální funkce read_XXX_chunked() (ekvivalentem funkce read_delim() je funkce read_delim_chunked()). Tyto funkce čtou datový soubor po kusech. Nad každým kusem provedou nějakou agregační operaci (např. vyberou jen řádky, které splňují určitou podmínku, spočítají nějaké agregátní statistiky apod.) a do paměti ukládají jen výsledky těchto operací. Na detaily použití těchto funkcí se podívejte do jejich dokumentace.

Naštěstí pro vás, valná většina dat, se kterou budete v blízké budoucnosti pracovat, bude nejspíše tak malá, že nic z těchto triků nebudete muset používat. Například tabulka s milionem řádků a 20 sloupci, které všechny obsahují reálná čísla bude v paměti počítač zabírat 1 000  000 \(\times\) 20 \(\times\)\(=\) 160 000 000 bytů, tj. asi jen 153 MB.

11.1.3 Ukládání dat do textových tabulárních delimitovaných souborů

K zapsání dat do tabulárních formátů slouží funkce write_delim(), write_csv(), write_excel_csv() a write_tsv(). Prvním parametrem všech těchto funkcí je vždy tabulka, který se má zapsat na disk, druhým je cesta, kam se mají data zapsat. Dále je možné nastavit pomocí parametru na znak pro uložení hodnoty NA a určit, zda se mají data připsat na konec existujícího souboru. Funkce read_delim() umožňuje navíc nastavit pomocí parametru delim znak, který bude oddělovat jednotlivé sloupce tabulky. Funkce write_excel_csv() přidá do souboru kromě běžného CSV i znak, podle kterého MS Excel pozná, že soubor je v kódování UTF-8.

Tyto funkce bohužel neumožňují nastavit locale, takže výsledkem je vždy soubor v kódování UTF-8 s desetinnými místy oddělenými pomocí tečky, tisící oddělenými pomocí čárky atd. Pokud potřebujete něco jiného. podívejte se do dokumentace na odpovídající funkce ze základního R (tj. např. funkci write.csv()).

11.1.4 Spojení (connections)

Když R čte nebo zapisuje data, nepracuje přímo se soubory, ale se “spojeními” (connections). Spojení zobecňuje myšlenku souboru – spojení může být lokální soubor, soubor komprimovaný algoritmem gzip, bzip2 apod., URL a další.

Funkce read_XXX() vytvareji spojeni pro cteni dat automaticky. To znamena, ze jmeno souboru muze obsahovat jak lokalni soubor, tak URL. Internetovy odkaz funkce pozna podle uvodniho "http://," "https://," "ftp://” nebo “ftps://.” V takovém případě vzdálený soubor automaticky stáhne. Funkce umí číst i některý typy komprimovaných souborů. Ty pozná podle koncovky “.gz,” “.bz2,” .“xz” nebo “.zip.” Takový soubor funkce automaticky dekomprimuje.

Předpokládejme, že náš datový soubor je pro úsporu místa na disku komprimovaný pomocí programu gzip. Pak data načteme jednoduše takto:

bmi_data <- read_csv("data/reading_and_writing/bmi_data.csv.gz",
                     col_types = "iiid")
bmi_data
## # A tibble: 5 × 4
##      id height weight   bmi
##   <int>  <int>  <int> <dbl>
## 1     1    153     55  23.5
## 2     2    207     97  22.6
## 3     3    173     83  27.7
## 4     4    181     92  28.1
## 5     5    164    112  41.6

Podobne je mozne nacist i data z internetu, staci nahradit jmeno souboru jeho URL. Rekneme, ze v promenne estat_gdp_adr mate ulozenou cestu k jedne z datovych tabulek Eurostatu (zde jedna z tabulek s hodnotami GDP- "https://ec.europa.eu/eurostat/estat-navtree-portlet-prod/BulkDownloadListing?file=data/namq_10_gdp.tsv.gz”). Pak můžete tato data načít do R takto:

gdp <- read_tsv(estat_gdp_adr, show_col_types = FALSE)
dim(gdp)
## [1] 51429   187

Na rozdíl od funkcí ze základního R umí funkce write_XXX() i přímo zapisovat do komprimovaných souborů bez toho, aby bylo potřeba connection explicitně deklarovat (např. pomocí funkce gzfile()). Stačí uvést jméno souboru ukončené odpovídající koncovkou. Pokud tedy chceme uložit naši tabulku do komprimovaného souboru, můžeme to provést např. takto:

write_csv(bmi_data, "moje_data.csv.gz")

Vic detailu ke spojenim najdete napr. v (Spector 2008, s. 23-25) nebo (Peng 2016, s. 33–35).

11.1.5 Načtení více souborů najednou

Někdy máme data se stejnou strukturou rozdělená do více dílčích souborů. To vzniká typicky v situaci, kdy data zapisuje nějaký senzor nebo robot. Funkce read_XXX() umožňují načíst takové soubory naráz a automaticky je spojit do jedné tabulky. Stačí místo cesty k souboru zadat vektor řetězců, který obsahuje cesty ke všem načítaným souborům:

files <- fs::dir_ls(path = "rawdata", glob = "data*.tsv")
rawdata <- readr::read_tsv(files, id = "path")

Funkce dir_ls() z balíku fs najde všechny soubory v adresáři rawdata, která odpovídají zadané masce, tj. např. soubory data0001.tsv, data0002.tsv atd. a absolutní cesty k těmto souborům vrátí jako vektor řetězců. Následně funkce read_tsv() všechny tyto soubory načte a spojí do jedné tabulky rawdata. Pokud je zadán parametr id, pak funkce k vlastním datům přidá i sloupec, do kterého uloží zadanou cestu k načítaným souborům. Jméno tohoto sloupce určí parametr id; zde se tedy bude jmenovat “path.” Přidání tohoto sloupce je užitečné např. v případě, že vlastní data neobsahují všechny proměnné a chybějící proměnné jsou součástí jména datového souboru nebo cesty k němu.

11.1.6 Načtení tabulárních dat v RStudiu

Pri interaktivni praci je mozne vyuzit toho, ze RStudio umi nacist tabularni textova data. V zalozce Environment kliknete na Import Dataset a zvolte From Text (readr)..., viz leva cast obrazku 11.1. Nastroj odhadne vetsinu potrebnych parametru a zbytek (vcetne locale) je mozne konfigurovat, viz prava cast obrazku 11.1. Příjemné je, že nástroj vygeneruje i potřebný kód, který můžete překopírovat do svého skriptu. Nástroj vám tedy umožní vizuálně odladit základní nastavení načítání dat, detaily můžete doladit později ve skriptu.

Načtení dat z tabulárního formátu v RStudiu.

Obrázek 11.1: Načtení dat z tabulárního formátu v RStudiu.

References

Peng, Roger D. 2016. R Programming for Data Science. LeanPub. https://bookdown.org/rdpeng/rprogdatascience/.
Spector, Phil. 2008. Data Manipulation with R. 1st ed. Use R! Springer.

  1. Od verze 2.0 neumi funkce z baliku readr nacitat soubory, ktere maji radky oddelene pouze pomoci tzv. carriage return (znaku \r). Prestoze zadny operacni system by uz od roku 2001 nemel takove soubory vytvaret, muzete je v divocine obcas dosud najit. Nejjednodussi zpusob, jak takovy soubor nacist, je pouzit funkce ze zakladniho R.↩︎