8. Įvadas į Tidyverse

8.1 Apie Tidyverse

„Tidyverse“ yra „R“ paketų šeima, kuriuos vienija bendri principai. Įprastai išmokus naudoti vieną tam tikro tipo funkciją, lengvai suprantama, kaip naudoti kitas tokio paties tipo funkcijas.

„DataCamp“ pamokoje „Introduction to the Tidyverse“ būsite supažindinti su „Tidyverse“ sistemos pagrindais. Oficialus „Tidyverse“ aprašymas, nuorodos ir pavyzdžiai pateikti tinklapyje www.tidyverse.org . Kelis svarbius dalykus paminėsiu ir šiame skyriuje.

8.2 Tidyverse paketų užkrovimas

„Tidyverse“ sudaro pagrindiniai ir papildomi paketai. Pagridinius paketus galima užkrauti naudojant vieną komandą library(tidyverse). Visi kiti, jei jų reikia, užkraunami po vieną atskirai.

Parašius library(tidyverse), rezultatas bus maždaug toks:

-- Attaching packages --------------------------------------- tidyverse 1.3.0 --
v ggplot2 3.3.0     v purrr   0.3.3
v tibble  2.1.3     v dplyr   0.8.5
v tidyr   1.0.2     v stringr 1.4.0
v readr   1.3.1     v forcats 0.5.0
-- Conflicts ------------------------------------------ tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Skiltyje „Attaching packages“ nurodyta tidyverse versija 1.3.0 bei kad buvo užkrauti paketai ggplot2, tibble, tidyr, readr, purrr, dplyr, stringr, forcats bei nurodytos jų versijos (pvz., ggplot2 versija 3.3.0). Jei yra užkrauti keli paketai, kuriuose yra funkcijų vienodais pavadinimais, tai nurodoma skiltyje „Conflicts“. Pavyzdžiui, užkrovus paketą dplyr, funkcija filter() (žymima dplyr::filter()) maskuoja paketo stats funkciją analogišku pavadinimu stats::filter(). T. y., parašius filter() bus naudojama funkcija iš paketo dplyr, o ne stats.

8.3 Operatorius %>%

Viena iš pageidaujamų programos kodo savybių atliekant interaktyvią analizę – galimybė kuo greičiau suprasti, kas tuo kodu vykdoma. Šiam tikslui sukurtas operatorius %>%. Ilgasis jo pavadinimas – funkcijų jungimo į grandinę operatorius %>% (angliškai pipeline operator ar tiesiog pipe). Sutrumpintai jį vadinsime jungimo operatoriumi. Operatorius %>% yra paketo magrittr dalis. Taip pat užraunamas su daugeliu tidyverse šeimos paketų (pvz., dplyr ar tidyr). RStudio turi sparčiųjų klavišų kombinaciją (Ctrl+Shift+M) šiam operatoriui įterpti (žr. lentelėje (tab:hotkeys)).

Operatorius %>% yra pritaikytas dirbti su daugeliu tidyverse sistemos funkcijų.

# Užkraunamas paketas `magrittr`
library(tidyverse)
library(magrittr)
Užduotis 8.1 Parsisiųskite šį dokumentą su „R“ kodu . Skaitydami skyrių apie %>% operatorių, atlikite visas dokumente pateiktas užduotis.

8.3.1 Operatoriaus naudojimo principas

Tikrasis operatoriaus pavadinimas nusako jo paskirtį: funkcijų jungimo į grandinę operatorius. Tad naudojantis juo komanda:

funkcija(duomenys)

gali būti pakeista į:

duomenys %>% funkcija()

Arba:

# Ilgesnė komanda, kurioje funkcijos naudojamos įdėtiniu būdu:
funkcija_3(funkcija_2(funkcija_1(duomenys)))

# Ta pati komanda, užrašyta funkcijas jungiant į grandinę:
duomenys %>% funkcija_1() %>% funkcija_2() %>% funkcija_3()

Ką operatorius %>% padaro? Ogi kairėje operatoriaus pusėje esančius duomenis paverčia pirmu dešinėje pusėje esančios funkcijos argumentu. Jei grandinėje funkcijų yra daugiau, tai, tarkime, išraiškos duomenys %>% funkcija_1() rezultatas yra pradiniai duomenys funkcijai funkcija_2(). Šitaip „R“ programos kodas tampa žymiai lengviau suprantamas.

Įprastinis operatoriaus naudojimas:

rezultatas <- 
    duomenys %>%
    pirmasis_veiksmas(papildomi_argumentai = "reikšmės") %>% 
    antrasis_veiksmas() %>% 
    treciasis_veiksmas()

Užrašas reiškia, kad pirma paimami duomenys „duomenys“, jiems atliekamas „pirmasis_veiksmas“ naudojant nurodytus argumentus. Šio veiksmo rezultatui – „antrasis_veiksmas“. Jo rezultatui – „treciasis_veiksmas“. Galiausiai paskutinis rezultatas išsaugomas kaip „rezultatas“.

Tiek naudojant %>%, tiek jo nenaudojant rezultatas turi būti identiškas.

Išnagrinėkime tolimesnius šio skyriaus pavyzdžius ir įsitikinsime tuo patys.

Paprastas pavyzdys su vektoriumi:

# Įprastinis užrašymas 
sum(c(1, 2, 3))
## [1] 6
# Naudojant %>% 
c(1, 2, 3) %>% sum()
## [1] 6

Dar keli pavyzdžiai:

summary(1:5)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       1       2       3       3       4       5

(1:5) %>% summary() # Reikia skliaustų (1:5)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       1       2       3       3       4       5

Prieš skliaustus iškeliame viską iki pirmojo kablelio, o kablelį ištriname:

x <- c(1:15, NA)

mean(x, na.rm = TRUE)
## [1] 8

x %>% mean(na.rm = TRUE)
## [1] 8
ats <- round(sqrt(mean(x, na.rm = TRUE)), digits = 3)
ats
## [1] 2.828

# Užrašymo būdas vienoje eilutėje:
ats <- x %>% mean(na.rm = TRUE) %>% sqrt() %>% round(digits = 3)
ats
## [1] 2.828

# Užrašymo būdas per kelias eilutes:
ats <- 
  x %>% 
  mean(na.rm = TRUE) %>%
  sqrt() %>%
  round(digits = 3)

ats
## [1] 2.828

Užrašius naudojant %>% aiškesnė veiksmų seka bei kam priklauso argumentas digits = 3.

Tad turbūt pastebėjote, kad, naudojant operatorių %>%, kodą galima užrašyti žmogui suprantamesniu būdu.

# Turime:
duomenys <- 1:10

# Iš užrašo įdėtiniu būdu (funkcija funkcijoje):
exp(mean(duomenys))
## [1] 244.6919

# Paverčiame į užrašą nuosekliu būdu:
duomenys %>% mean() %>% exp()
## [1] 244.6919

Nuoseklus užrašymas ypač patogus, kai turime žymiai sudėtingesnius veiksmus atliekančias funkcijas.


Paprastas pavyzdys su duomenų lentele:

# Įprastinis užrašymas 
head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
# Naudojant %>% 
iris %>% head()
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa


Pavyzdys, kai funkcijoje yra papildomų argumentų:

# Įprastinis užrašymas 
head(iris, 3)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
# Naudojant %>% 
iris %>% head(3)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa


Toliau bus pateiktas sudėtingas pavyzdys. Jame bus naudojamos funkcijos iš paketo dplyr. Nagrinėdami jį, galėsite įvertinti, ar operatorius %>% iš tiesų programos kodą padaro labiau suprantamą.

# Užkraunamas paketas `dplyr`
library(dplyr)
# Pavyzdys A: įprastinis užrašymas įdėtiniu (funkcija funkcijoje) būdu:
summarise(mutate(filter(iris, Species == "setosa"), 
                 Length = Sepal.Length + Petal.Length),
          mean = mean(Length))
##    mean
## 1 6.468

summarise(
    mutate(
        filter(iris, Species == "setosa"),   
        Length = Sepal.Length + Petal.Length
    ),
    mean = mean(Length)
)
##    mean
## 1 6.468


Jei įdėtiniu būdu užrašoma daug funkcijų, kurios taip pat turi argumentų, užrašas tampa sudėtingas suprasti. Be to padidėja tikimybė padaryti klaidą. Tad pažiūrėkime į kitą pavyzdį.

# Pavyzdys B: naudojamas %>% operatorius
iris  %>% 
    filter(Species == "setosa")  %>% 
    mutate(Length = Sepal.Length + Petal.Length)  %>% 
    summarise(mean = mean(Length))
##    mean
## 1 6.468


Abiejų pavyzdžių rezultatas tas pats. Nors kol kas jums galbūt dar neaišku, ką daro funkcijos, tačiau pavyzdyje B bent jau suprantama taikomų funkcijų eiga (pirma filter(), po to mutate(), dar po to summarise()) ir kurie argumentai kuriai funkcijai priklauso. Nors atidžiau pažiūrėję gal netgi ir numanysite, ką pateiktas programos kodas daro. O atveju A – tai būtų žymiai sudėtingesnė užduotis. (Kodas: pasirenkama tik setosa rūšies narius, sudedami taurėlapių ir vainiklapių ilgiai ir apskaičiuojamas jų vidurkis.)

Beje, galimas ir dar vienas užrašymo būdas, bet tam naudojami tarpiniai kintamieji.

# Pavyzdys c (naudojami tarpiniai kintamieji)
tik_setosa     <- filter(iris, Species == "setosa")
tik_setosa2    <- mutate(tik_setosa, Length = Sepal.Length + Petal.Length) 
ilgio_vidurkis <- summarise(tik_setosa2, mean = mean(Length))
ilgio_vidurkis
##    mean
## 1 6.468

Daugiau apie šį bei panašius operatorius:


Užduotis 8.2 Šias „R“ kodo eilutes užrašykite naudodami operatorių %>%:

  1. summary(iris);
  2. mean(23:30);
  3. tail(iris, n = 20);
  4. summary(tail(data.table::fread("http://calcnet.mth.cmich.edu/org/spss/V16_materials/DataSets_v16/Diseaseoutbreak.txt")));
  5. mean(c(log(20*pi, base = 16), NA, 14, 24), na.rm = TRUE).

8.3.2 Įrankis ViewPipeSteps

Įrankis „ViewPipeSteps“ skirtas pademonstruoti, kas su duomenimis vyksta kiekvienoje jungimo operatoriumi %>% sujungtų funkcijų grandyje.

Diegimas:

# install.packages("devtools")
devtools::install_github("daranzolin/ViewPipeSteps")

Įrankis naudojamas kaip „RStudio“ įskiepis (add-in). Daugiau informacijos ir pavyzdžių įrankio svetainėje .

8.4 Kodo rašymo stilius

Kodo rašymo stilius – tai taisyklės, kurias sugalvojo tam tikra programa dirbantys žmonės dėl to, kad būtų lengviau dirbti su programos kodu. Tai kaip geros skyrybos taisyklės – galime išsiversti ir be jų, betjosdarokodąlabiausuprantamą irdarbassutokiukodumažiauvargina. (Ar nebūtų lengviau skaityti, jei būtų tarpai?)

Stiliaus gidą, kaip pagal „Tidyverse“ reikalavimus rašyti programos kodą, jo dokumentaciją, galite rasti tinklapyje style.tidyverse.org . Šiuo metu mums svarbiausi 1-4 skyriai. Kitos sistemos, programavimo kalbos ar net ir darbo grupės gali turėsi savo stiliaus gidą, kuris nesutampa su pasiūlytuoju šiame skyriuje. Tačiau šio kurso metu paprašysiu, kad kodą rašytumėte pagal „Tidyverse“ stilių.

Svarbiausi pastebėjimai iš stiliaus gido:

  • Objektų pavadinimai:
    1. objektų pavadinimai turėtų būti prasmingi (pvz., zuvys, o ne x), bet pakankamai trumpi;
    2. objektų pavadinimuose naudojamos tik mažosios raidės, skaičiai ir apatiniai brūkšniai;
    3. pirmasis simbolis objekto pavadinime – raidė;
    4. jei pavadinimą sudaro keli žodžiai, jie jungiami apatiniu brūkšniu, pvz., objektas_a;
    5. funkcijų pavadinimai turėtų būti formuluojami kaip veiksmažodžiai (liepiamąja nuosaka), pvz., parsiųsk_dokumentą();
  • Tarpai – elementų atskyrimas ir sujungimas:
    1. naudojant kablelį, prieš jį tarpas nerašomas, po kablelio – privalomas, pvz., mean(x, na.rm = TRUE);
    2. naudojant operatorius =, ==, !=, +, -, <-, <, >, %>% ir panašius – iš abiejų pusių reikia palikti tarpus, pvz., a < b;
    3. naudojant operatorių ^, aplink jį tarpų įprastai nepaliekami, pvz., x^2;
    4. naudojant operatorius :, ::, ::: – tarpų palikti negalima, pvz., 1:5, dplyr::mutate();
    5. naudojant dvipusę modelio formulę, operatorius ~ išskiriamas tarpais iš abiejų pusių, pvz., a ~ b;
    6. naudojant vienpusę modelio formulę, tarp operatorius ~ ir objekto pavadinimo tarpo nereikia, pvz., ~c;
    7. tarp funkcijos pavadinimo ir atidarymo skliaustelio tarpo privalo nebūti, pvz., mean(x);
    8. išimtis taikoma if sakiniams ir ciklams (for, while ir kt.), po kurių pavadinimų ir atidarymo skliaustelio rašomas tarpas, pvz., if (a < b) {"mažiau"}.
  • Nauja eilutė:
    1. viena eilutė – viena mintis, todėl įprastai vienoje eilutėje naudojama tik viena pagrindinė funkcija;
    2. vienos eilutės ilgis turi neviršyti 80 simbolių; („RStudio“ galima nusistatyti, kad ties 80 simbolių riba būtų vertikali linija);
    3. po jungimo operatoriaus %>% bei ggplot2 funkcijų jungimo operatoriaus +, kitą funkciją įprastai rašome iš naujos eilutės;
    4. tęsiant funkcijų grandinę iš naujos eilutės, prieš rašant kodą, eilutės pradžioje paliekami 2 tarpai. („RStudio“ kelis tarpus įterpia automatiškai. Kai kurie vartotojai dėl aiškumo įterpia ne 2, o 4 tarpus.)

Plačiau apie objektų pavadinimus rašoma skyriuje 4.2.3. Daugiau stiliaus pavyzdžių – rekomenduojamų ir nerekomenduojamų – „Tydiverse“ stiliaus gide. Kodo rašymo stiliaus patarimai iš vadovėlio „YaRrr! The Pirate’s Guide to R“ .

8.4.1 Kodo diagnostika

„RStudio“ turi integruotus įrankius, skirtus kodo sintaksės klaidų bei stiliaus diagnostikai . Pabraukimas mėlyna spalva būtent ir signalizuoja stiliaus klaidas.

8.4.2 Kodo stiliaus taisymo įrankiai

„RStudio“ turi greituosius klavišus, kurie gali būti naudojami kai kurioms stiliaus klaidoms pataisyti. Pirmiausia pele reikia pažymėti koreguotiną programos kodo vietą, tada galima naudoti sparčiuosius klavišus:

  • Ctrl+Shift+A – pagal „R“ stiliaus taisykles pataiso kai kuriuos stiliaus aspektus;
  • Ctrl+I (angl. Indent) – pagal „R“ stiliaus taisykles nuo krašto atitraukia pažymėtas „R“ kodo eilutes.

Pagal „Tidyveerse“ stiliaus taisykles kodą gali pataisyti paketo styler „RStudio“ įskiepiai (angl. addins, žr. skyriuje 6.9). Šie įskiepiai atlieka tik tokius taisymus, kurie pagražina kodą, bet jo nesugadina, pvz., gali įterpti tarpus, jei jų trūksta, bet objektų nepervadina, nors ir pavadinimai neatitinka „Tidyverse“ taisyklių.


Užduotis 8.3

  1. Įsidiekite paketą styler.
  2. Naudodamiesi šio paketo įskiepiu „Style selection“ bei greitaisiais „RStudio“ klavišais pataisykite šias kodo eilutes:
    • tidyverse_style=mean(c(log(20*pi,base=16),NA,14,24,25,1),na.rm=TRUE)
    • sum(15,2);"lint"

Klausimai ir komentarai