Leo Tolstoy: Sve srećne porodice liče jedna na drugu, svaka nesrećna porodica, nesrećna je na svoj način.

Hadley Wickham: Sve uredne baze podataka liče jedna na drugu, svaka neuredna baza, neuredna je na svoj način.

U ovom poglavlju ćemo se pozabaviti time kako da organizujemo podatke u R-u da budu “uredni”. Dobijanje podataka u ovom formatu zahteva dodatni posao, ali se dugoročno isplati. Jednom kada imamo uredne podatke i uredne alate koje obezbeđuju paketi iz tidyverse, potrošićemo mnogo manje vremena prevodeći podatke iz jedne reprezentacije u drugu, što će nam omogućiti da potrošimo više vremena na analitička pitanja.

Ovo poglavlje će nam dati praktičan uvod u uređivanje podataka i paket tidyr, paket koji pruža hrpu alata koji pomažu u uređivanju naših neurednih skupova podataka. Paket tidyr je deo klase tidyverse.

library(tidyverse)
library(tidyr)

Uređivanje podataka

Možemo predstaviti iste podatke na više različitih načina. Primer ispod pokazuje iste podatke organizovane na četiri različita načina. Svaka baza podataka prikazuje iste vrednosti četiri promenljive: country, year, population i cases, ali svaka baza organizuje vrednosti na drugačiji način.

table1
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583
table2
## # A tibble: 12 x 4
##    country      year type            count
##    <chr>       <int> <chr>           <int>
##  1 Afghanistan  1999 cases             745
##  2 Afghanistan  1999 population   19987071
##  3 Afghanistan  2000 cases            2666
##  4 Afghanistan  2000 population   20595360
##  5 Brazil       1999 cases           37737
##  6 Brazil       1999 population  172006362
##  7 Brazil       2000 cases           80488
##  8 Brazil       2000 population  174504898
##  9 China        1999 cases          212258
## 10 China        1999 population 1272915272
## 11 China        2000 cases          213766
## 12 China        2000 population 1280428583
table3
## # A tibble: 6 x 3
##   country      year rate             
## * <chr>       <int> <chr>            
## 1 Afghanistan  1999 745/19987071     
## 2 Afghanistan  2000 2666/20595360    
## 3 Brazil       1999 37737/172006362  
## 4 Brazil       2000 80488/174504898  
## 5 China        1999 212258/1272915272
## 6 China        2000 213766/1280428583
# Razdvojimo na 2 tibla
# cases
table4a
## # A tibble: 3 x 3
##   country     `1999` `2000`
## * <chr>        <int>  <int>
## 1 Afghanistan    745   2666
## 2 Brazil       37737  80488
## 3 China       212258 213766
#population
table4b
## # A tibble: 3 x 3
##   country         `1999`     `2000`
## * <chr>            <int>      <int>
## 1 Afghanistan   19987071   20595360
## 2 Brazil       172006362  174504898
## 3 China       1272915272 1280428583

Ovo su sve reprezentacije istih podataka, ali nisu sve jednako jednostavne za upotrebu. Jedna baza, uredna baza, biće mnogo lakša za rad u tidyverse.

Postoje tri međusobno povezana pravila koja čine skup podataka urednim:

  1. Svaka promenljiva mora da ima svoju kolonu.

  2. Svaka opservacija mora da ima svoj red.

  3. Svaka vrednost mora da ima svoju ćeliju.

Ova tri pravila su međusobno povezana jer je nemoguće zadovoljiti samo dva od tri. Taj međusobni odnos dovodi do jednostavnog niza praktičnih instrukcija:

  1. Smestimo svaku bazu podataka u tibl.

  2. Smestimo svaku promenljivu u kolonu.

U ovom primeru, samo table1 je uredna. To je jedina reprezentacija u kojoj svaka kolona predstavlja promenljivu.

Zašto želimo da osiguramo da su naši podaci uredni? Postoje dve glavne prednosti:

  1. Postoji opšta prednost odabira jednog konzistentnog načina skladištenja podataka. Ako imamo konzistentnu strukturu podataka, lakše je naučiti alate koje rade sa njima, jer imaju istu uniformnost.

  2. Postoji posebna prednost postavljanja promenljivih u kolone, jer dozvoljava vektorizovanoj prirodi R-a da zablista. Većina ugrađenih R funkcija radi sa vektorima vrednosti, pa to čini transformacije urednih podataka posebno prirodnim.

dplyr, ggplot2 i svi drugi paketi u tidyverse su dizajnirani da rade sa urednim podacima. Na primer, na table1 možemo primeniti dobro poznate funkcije:

table1 %>% 
  mutate(rate = cases / population)
## # A tibble: 6 x 5
##   country      year  cases population      rate
##   <chr>       <int>  <int>      <int>     <dbl>
## 1 Afghanistan  1999    745   19987071 0.0000373
## 2 Afghanistan  2000   2666   20595360 0.000129 
## 3 Brazil       1999  37737  172006362 0.000219 
## 4 Brazil       2000  80488  174504898 0.000461 
## 5 China        1999 212258 1272915272 0.000167 
## 6 China        2000 213766 1280428583 0.000167
table1 %>% 
  count(year, wt = cases)
## # A tibble: 2 x 2
##    year      n
##   <int>  <int>
## 1  1999 250740
## 2  2000 296920
library(ggplot2)
ggplot(table1, aes(year, cases)) + 
  geom_line(aes(group = country), colour = "grey50") + 
  geom_point(aes(colour = country))

Širenje i skupljanje

Principi urednih podataka izgledaju tako jednostavni da se možemo zapitati da li ćemo ikada naići na skup podataka koji nije uredan. Međutim, većina podataka na koje ćemo naići, nažalost, nije uredna. Postoje dva glavna razloga:

  1. Većina ljudi nije upoznata sa principima urednih podataka.

  2. Podaci se često organizuju kako bi se olakšala neka druga upotreba koja nije analiza. Na primer, podaci se često organizuju kako bi unos bio lakši.

To znači da ćemo za većinu realnih analiza, morati da uradimo malo uređivanja. Prvi korak je uvek utvrditi šta su promenljive i opservacije. Ponekad je to lako, a ponekad ćemo morati da se konsultujemo sa ljudima koji su izvorno generisali podatke. Drugi korak je rešavanje jednog od dva uobičajena problema:

  1. Jedna promenljiva može biti raspoređena na više kolona.

  2. Jedna opservacija može biti raštrkana na više redova.

Obično će baza patiti samo od jednog od ova dva problema, a patiće od oba ako zaista nemamo sreće. Da bismo rešili ove probleme, biće nam potrebne dve najvažnije funkcije u tidyr: gather() i spread().

Skupljanje

Čest problem je baza podataka gde neka imena kolona nisu imena promenljivih, već vrednosti promenljive. Uzmimo table4a: nazivi kolona 1999 i 2000 predstavljaju vrednosti promenljive year, i svaki red predstavlja dve opservacije, ne jednu.

table4a
## # A tibble: 3 x 3
##   country     `1999` `2000`
## * <chr>        <int>  <int>
## 1 Afghanistan    745   2666
## 2 Brazil       37737  80488
## 3 China       212258 213766

Da bismo uredili ovakav skup podataka, potrebno je da skupimo te kolone u novi par promenljivih. Da bismo opisali tu operaciju, trebaju nam tri parametra:

Zajedno, ovi parametri generišu poziv za gather():

table4a %>% 
  gather(`1999`, `2000`, key = "year", value = "cases")
## # A tibble: 6 x 3
##   country     year   cases
##   <chr>       <chr>  <int>
## 1 Afghanistan 1999     745
## 2 Brazil      1999   37737
## 3 China       1999  212258
## 4 Afghanistan 2000    2666
## 5 Brazil      2000   80488
## 6 China       2000  213766

Kolone za skupljanje se specifikuju dplyr::select() stilom notacije. Ovde su samo dve kolone, tako da ih navodimo pojedinačno. Primetimo da su 1999 i 2000 nesintaksna imena (jer ne počinju slovom), pa ih moramo okružiti navodnicima (`).

U konačnom rezultatu se ne nalaze skupljene kolone, ali dobijamo nove key i value kolone. Odnosi između originalnih promenljivih su sačuvani. Vizuelno, to je prikazano na sledećoj slici:

Koristimo gather() da uredimo table4b u sličnom maniru. Jedina razlika je promenljiva smeštena u value ćelijama:

table4b %>% 
  gather(`1999`, `2000`, key = "year", value = "population")
## # A tibble: 6 x 3
##   country     year  population
##   <chr>       <chr>      <int>
## 1 Afghanistan 1999    19987071
## 2 Brazil      1999   172006362
## 3 China       1999  1272915272
## 4 Afghanistan 2000    20595360
## 5 Brazil      2000   174504898
## 6 China       2000  1280428583

Da bismo kombinovali uređene verzije table4a i table4b u jedan tibl, koristimo dplyr::left_join(), o čemu ćemo malo više pričati kasnije.

tidy4a = table4a %>% 
  gather(`1999`, `2000`, key = "year", value = "cases")
tidy4b = table4b %>% 
  gather(`1999`, `2000`, key = "year", value = "population")
left_join(tidy4a, tidy4b)
## Joining, by = c("country", "year")
## # A tibble: 6 x 4
##   country     year   cases population
##   <chr>       <chr>  <int>      <int>
## 1 Afghanistan 1999     745   19987071
## 2 Brazil      1999   37737  172006362
## 3 China       1999  212258 1272915272
## 4 Afghanistan 2000    2666   20595360
## 5 Brazil      2000   80488  174504898
## 6 China       2000  213766 1280428583

Širenje

Širenje je suprotno od skupljanja - koristimo ga kada su opservacije razbacane na više redova. Na primer, uzmimo table2: svaka opservacija se prostire na dva reda.

table2
## # A tibble: 12 x 4
##    country      year type            count
##    <chr>       <int> <chr>           <int>
##  1 Afghanistan  1999 cases             745
##  2 Afghanistan  1999 population   19987071
##  3 Afghanistan  2000 cases            2666
##  4 Afghanistan  2000 population   20595360
##  5 Brazil       1999 cases           37737
##  6 Brazil       1999 population  172006362
##  7 Brazil       2000 cases           80488
##  8 Brazil       2000 population  174504898
##  9 China        1999 cases          212258
## 10 China        1999 population 1272915272
## 11 China        2000 cases          213766
## 12 China        2000 population 1280428583

Da bismo ovo sredili, prvo analiziramo reprezentaciju na sličan način kao i za gather(). Ovog puta, međutim, potrebna su nam samo dva parametra:

Sada možemo koristiti spread(), kao što je prikazano programski ispod, a vizuelno i na slici dole.

table2 %>%
    spread(key = type, value = count)
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Kao što vidimo iz upotrebe key i value argumenata, spread() i gather() su komplementarne. gather() pravi široke tabele užim i dužim, a spread() pravi duge tabele kraćim i širim.