14 Tidyverse
Tidyverse on osa R-i ökosüsteemist, kus kehtivad omad reeglid. Tidyverse raamatukogud lähtuvad ühtsest filosoofiast ja töötavad hästi koos. Tidyverse algab andmetabeli struktuurist ja selle funktsioonid võtavad reeglina sisse õige struktuuriga tibble ja väljastavad samuti tibble, mis sobib hästi järgmise tidyverse funktsiooni sisendiks. Seega on tidyverse hästi sobiv läbi torude %>%
laskmiseks. Tidyverse-ga sobib hästi kokku ka ggplot2 graafikasüsteem.
Laadime tidyverse metapaketi raamatukogud:
library(tidyverse)
Nagu näha laaditakse tidyverse raamatukoguga 8 paketti:
- ggplot2
- purrr
- tibble
- dplyr
- tidyr
- stringr
- readr
forcats
- tibble pakett sisaldab tidyverse-spetsiifilise andmeraami (data_frame) loomiseks ja manipuleerimiseks vajalike funktsioone. Erinevalt baas R-i andmeraamist (data.frame) iseloomustab tibble-t vaikimisi prindifunktsioon, kus vaikimisi näidataksegi ainult tabeli peast 10 esimest rida. Oluliseks erinevuseks on ka list tulpade toetus (data.frame tulbad saavad olla ainult vektorid). List tulbad võimaldavad andmeraami paigutada kõige erinevamaid objekte: näiteks vektoreid, andmeraame, lineaarseid mudeleid ja valgeid puudleid. Lisaks ei ole tibble tabelitel veerunimesid ja veidraid tulbanimesid ei muudeta vaikimisi/automaatselt.
- tidyr pakett sisaldab eelkõige funktsioone tibble-de kuju muutmiseks laiast formaadist pikka ja tagasi.
- readr paketi funktsioonid vastutavad andmete impordi eest tekstipõhistest failidest lähtuvalt tidyverse reeglitest ja asendavad vastavad baas R-i funktsioonid.
- purrr pakett sisaldab funktsioone töötamaks listidega ja asendavad baas R-i apply perekonna funktsioone.
- dplyr pakett sisaldab põhilisi andmetöötlusverbe.
stringr ja forcats paketid sisaldavad vastavalt tekstipõhiste ja kategooriliste andmetega töötamise funktsioone.
14.1 Tidy tabeli struktuur
- väärtus (value) — ühe mõõtmise tulemus (183 cm)
- muutuja (variable) — see, mida sa mõõdad (pikkus) või faktor (sex)
- andmepunkt (observation) — väärtused, mis mõõdeti samal katsetingimusel (1. subjekti pikkus ja kaal 3h ajapunktis)
- vaatlusühik (unit of measurement) — keda mõõdeti (subjekt nr)
- vaatlusühiku tüüp — inimene, hiir, jt
vaatlusühiku tüüp = tabel
muutuja = veerg
andmepunkt = rida
vaatlusühikute koodid on kõik koos ühes veerus
Veergude järjekord tabelis on 1. vaatlusühik, 2. faktor, mis annab katse-kontrolli erisuse, 3. kõik see, mida otse ei mõõdetud (sex, batch nr, etc.), 4. numbritega veerud (iga muutuja kohta üks veerg)
#> # A tibble: 2 x 6
#> subject drug sex time length weigth
#> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 1 exp F 3 168 88
#> 2 2 placebo M 3 176 91
Nii näeb välja tidy tibble. Kõik analüüsil vajalikud parameetrid tuleks siia tabelisse veeru kaupa sisse tuua. Näiteks, kui mõõtmised on sooritatud erinevates keskustes erinevate inimeste poolt kasutades sama ravimi erinevaid preparaate, oleks hea siia veel 3 veergu lisada (center, experimenter, batch).
14.1.1 Tabeli dimensioonide muutmine (pikk ja lai formaat)
Väga oluline osa tidyverses töötamisest on tabelite pika ja laia formaadi vahel viimine.
See on laias formaadis tabel df, mis ei ole tidy
#> # A tibble: 3 x 5
#> subject sex control experiment_1 experiment_2
#> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Tim M 23 34 40
#> 2 Ann F 31 38 42
#> 3 Jill F 30 36 44
Kõigepealt pikka formaati. key ja value argumendid on ainult uute veergude nimetamiseks, oluline on 3:ncol(dat) argument, mis ütleb, et “kogu kokku veerud alates 3. veerust”. Alternatiivne viis seda öelda: c(-subject, -sex).
dat_lng <- gather(dat, key = experiment, value = value, 3:ncol(dat))
# df_l3<-df %>% gather(experiment, value, 3:ncol(df)) works as well.
#df_l4<-df %>% gather(experiment, value, c(-subject, -sex)) works as well
dat_lng
#> # A tibble: 9 x 4
#> subject sex experiment value
#> <chr> <chr> <chr> <dbl>
#> 1 Tim M control 23
#> 2 Ann F control 31
#> 3 Jill F control 30
#> 4 Tim M experiment_1 34
#> 5 Ann F experiment_1 38
#> 6 Jill F experiment_1 36
#> # … with 3 more rows
Paneme selle tagasi algsesse laia formaati: ?spread
spread(dat_lng, key = experiment, value = value)
#> # A tibble: 3 x 5
#> subject sex control experiment_1 experiment_2
#> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Ann F 31 38 42
#> 2 Jill F 30 36 44
#> 3 Tim M 23 34 40
key viitab pika tabeli veerule, mille väärtustest tulevad laias tabelis uute veergude nimed. value viitab pika tabeli veerule, kust võetakse arvud, mis uues laias tabelis uute veergude vahel laiali jagatakse.
14.1.2 Tibble transpose — read veergudeks ja vastupidi
dat <- tibble(a = c("tim", "tom", "jill"), b1 = c(1, 2, 3), b2 = c(4, 5, 6))
dat
#> # A tibble: 3 x 3
#> a b1 b2
#> <chr> <dbl> <dbl>
#> 1 tim 1 4
#> 2 tom 2 5
#> 3 jill 3 6
Me kasutame selleks maatriksarvutuse funktsiooni t() — transpose. See võtab sisse ainult numbrilisi veerge, seega anname talle ette df miinus 1. veerg, mille sisu me konverteerime uue tablei veerunimedeks.
dat1 <- t(dat[,-1])
colnames(dat1) <- dat$a
dat1
#> tim tom jill
#> b1 1 2 3
#> b2 4 5 6
14.2 dplyr ja selle viis verbi
Need tuleb teil omale pähe ajada sest nende 5 verbiga (pluss gather ja spread) saab lihtsalt teha 90% andmeväänamisest, mida teil elus ette tuleb. NB! Check the data wrangling cheatsheet and dplyr help for further details. dplyr laetakse koos tidyverse-ga automaatselt teie workspace-i.
14.2.1 select()
columns
select()
selects, renames, and re-orders columns.
Select columns from sex to value:
iris
select(iris, Petal.Length:Species)
select(iris, -(Petal.Length:Species)) #selects everything, except those cols
To select 3 columns and rename subject to SUBJ and put liik as the 1st col:
select(iris, liik = Species, Sepal.Length, Sepal.Width) %>% dplyr::as_data_frame()
#> Warning: `as_data_frame()` is deprecated, use `as_tibble()` (but mind the new semantics).
#> This warning is displayed once per session.
#> # A tibble: 150 x 3
#> liik Sepal.Length Sepal.Width
#> <fct> <dbl> <dbl>
#> 1 setosa 5.1 3.5
#> 2 setosa 4.9 3
#> 3 setosa 4.7 3.2
#> 4 setosa 4.6 3.1
#> 5 setosa 5 3.6
#> 6 setosa 5.4 3.9
#> # … with 144 more rows
To select all cols, except sex and value, and rename the subject col:
select(iris, -Sepal.Length, -Sepal.Width, liik = Species)
helper functions you can use within select():
starts_with("abc")
: matches names that begin with “abc.”
ends_with("xyz")
: matches names that end with “xyz.”
contains("ijk")
: matches names that contain “ijk.”
matches("(.)\\1")
: selects variables that match a regular expression. This one matches any variables that contain repeated characters.
num_range("x", 1:3)
matches x1, x2 and x3.
iris <- as_tibble(iris)
select(iris, starts_with("Petal"))
#> # A tibble: 150 x 2
#> Petal.Length Petal.Width
#> <dbl> <dbl>
#> 1 1.4 0.2
#> 2 1.4 0.2
#> 3 1.3 0.2
#> 4 1.5 0.2
#> 5 1.4 0.2
#> 6 1.7 0.4
#> # … with 144 more rows
select(iris, ends_with("Width"))
#> # A tibble: 150 x 2
#> Sepal.Width Petal.Width
#> <dbl> <dbl>
#> 1 3.5 0.2
#> 2 3 0.2
#> 3 3.2 0.2
#> 4 3.1 0.2
#> 5 3.6 0.2
#> 6 3.9 0.4
#> # … with 144 more rows
# Move Species variable to the front
select(iris, Species, everything())
#> # A tibble: 150 x 5
#> Species Sepal.Length Sepal.Width Petal.Length
#> <fct> <dbl> <dbl> <dbl>
#> 1 setosa 5.1 3.5 1.4
#> 2 setosa 4.9 3 1.4
#> 3 setosa 4.7 3.2 1.3
#> 4 setosa 4.6 3.1 1.5
#> 5 setosa 5 3.6 1.4
#> 6 setosa 5.4 3.9 1.7
#> # … with 144 more rows, and 1 more variable:
#> # Petal.Width <dbl>
dat <- as.data.frame(matrix(runif(100), nrow = 10))
dat <- tbl_df(dat[c(3, 4, 7, 1, 9, 8, 5, 2, 6, 10)])
select(dat, V9:V6)
#> # A tibble: 10 x 5
#> V9 V8 V5 V2 V6
#> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1 0.149 0.166 0.878 0.776 0.634
#> 2 0.172 0.167 0.660 0.245 0.245
#> 3 0.0158 0.637 0.235 0.0986 0.709
#> 4 0.205 0.777 0.602 0.585 0.316
#> 5 0.645 0.460 0.652 0.260 0.444
#> 6 0.492 0.796 0.740 0.0179 0.925
#> # … with 4 more rows
select(dat, num_range("V", 9:6))
#> # A tibble: 10 x 4
#> V9 V8 V7 V6
#> <dbl> <dbl> <dbl> <dbl>
#> 1 0.149 0.166 0.774 0.634
#> 2 0.172 0.167 0.289 0.245
#> 3 0.0158 0.637 0.767 0.709
#> 4 0.205 0.777 0.991 0.316
#> 5 0.645 0.460 0.314 0.444
#> 6 0.492 0.796 0.350 0.925
#> # … with 4 more rows
# Drop variables with -
select(iris, -starts_with("Petal"))
#> # A tibble: 150 x 3
#> Sepal.Length Sepal.Width Species
#> <dbl> <dbl> <fct>
#> 1 5.1 3.5 setosa
#> 2 4.9 3 setosa
#> 3 4.7 3.2 setosa
#> 4 4.6 3.1 setosa
#> 5 5 3.6 setosa
#> 6 5.4 3.9 setosa
#> # … with 144 more rows
# Renaming -----------------------------------------
# select() keeps only the variables you specify
# rename() keeps all variables
rename(iris, petal_length = Petal.Length)
#> # A tibble: 150 x 5
#> Sepal.Length Sepal.Width petal_length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 5.1 3.5 1.4 0.2
#> 2 4.9 3 1.4 0.2
#> 3 4.7 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 5 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
#> # … with 144 more rows, and 1 more variable:
#> # Species <fct>
14.2.2 filter()
rows
Keep rows in Iris that have Species level “setosa” and Sepal.Length value <4.5.
filter(iris, Species=="setosa" & Sepal.Length < 4.5)
#> # A tibble: 4 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 4.4 2.9 1.4 0.2
#> 2 4.3 3 1.1 0.1
#> 3 4.4 3 1.3 0.2
#> 4 4.4 3.2 1.3 0.2
#> # … with 1 more variable: Species <fct>
Keep rows in Iris that have Species level “setosa” or Sepal.Length value <4.5.
filter(iris, Species=="setosa" | Sepal.Length < 4.5)
#> # A tibble: 50 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 5.1 3.5 1.4 0.2
#> 2 4.9 3 1.4 0.2
#> 3 4.7 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 5 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
#> # … with 44 more rows, and 1 more variable:
#> # Species <fct>
Keep rows in Iris that have Species level “not setosa” or Sepal.Length value <4.5.
filter(iris, Species !="setosa" | Sepal.Length < 4.5)
#> # A tibble: 104 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 4.4 2.9 1.4 0.2
#> 2 4.3 3 1.1 0.1
#> 3 4.4 3 1.3 0.2
#> 4 4.4 3.2 1.3 0.2
#> 5 7 3.2 4.7 1.4
#> 6 6.4 3.2 4.5 1.5
#> # … with 98 more rows, and 1 more variable:
#> # Species <fct>
Kui tahame samast veerust filtreerida “või” ehk “|” abil mitu väärtust, on meil valida kahe samaväärse variandi vahel (tegelikult töötab 2. variant ka ühe väärtuse korral)
filter(iris, Species =="setosa" | Species =="versicolor")
filter(iris, Species %in% c("setosa", "versicolor") )
Nagu näha, 2. variant on oluliselt lühem.
Filtreerime regulaarekspressiooniga: read, kus Species algab v tähega
library(stringr)
filter(iris, str_detect(Species, "^v"))
#> # A tibble: 100 x 5
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 7 3.2 4.7 1.4
#> 2 6.4 3.2 4.5 1.5
#> 3 6.9 3.1 4.9 1.5
#> 4 5.5 2.3 4 1.3
#> 5 6.5 2.8 4.6 1.5
#> 6 5.7 2.8 4.5 1.3
#> # … with 94 more rows, and 1 more variable:
#> # Species <fct>
eemalda NAdega read kahe veeru põhjal
filter(flights, !is.na(dep_delay), !is.na(arr_delay))
14.2.3 summarise()
Mitu rida summeeritakse üheks väärtuseks veeru kaupa. Kõigepealt summeerime kogu tabeli nii, et saame (1) keskmise Sepal-length-i, (2) standardhälbe samast, (3) tabeli ridade arvu ja (4) mitu erinevat Species-t on tabelis
summarise(iris,
MEAN = mean(Sepal.Length),
SD = sd(Sepal.Length),
N = n(),
n_species = n_distinct(Species))
#> # A tibble: 1 x 4
#> MEAN SD N n_species
#> <dbl> <dbl> <int> <int>
#> 1 5.84 0.828 150 3
n()
loeb üles, mitu väärtust läks selle summary statistic-u arvutusse,
n_distinct()
loeb üles, mitu unikaalset väärtust läks samasse arvutusse.
Summarise on kasulikum, kui teda kasutada koos järgmise verbi, group_by-ga.
14.2.4 group_by()
group_by()
grupeerib väärtused, nii et neid saab grupi kaupa summeerida või muteerida. Näiteks grupeerides Species kaupa, saame arvutada summaarsed statistikud igale liigile
iris_grouped <- group_by(iris, Species)
summarise(iris_grouped,
MEAN = mean(Sepal.Length),
SD = sd(Sepal.Length),
N = n(),
n_species = n_distinct(Species))
#> # A tibble: 3 x 5
#> Species MEAN SD N n_species
#> <fct> <dbl> <dbl> <int> <int>
#> 1 setosa 5.01 0.352 50 1
#> 2 versicolor 5.94 0.516 50 1
#> 3 virginica 6.59 0.636 50 1
summarise()
argumendid on indentsed eelmise näitega aga tulemus ei ole. Siin me rakendame summarise verbi mitte kogu tabelile, vaid 3-le virtuaalsele tabelile, mis on saadud algsest tabelist.
group_by()
-le saab anda järjest mitu grupeerivat muutujat. Siis ta grupeerib kõigepealt neist esimese järgi, seejärel lõõb saadud grupid omakorda lahku teise argumendi järgi ja nii edasi kuni teie poolt antud argumendid otsa saavad.
pro tip Kui tahad summaarseid statistikuid algse pika tabeli sisse uute veergudena (igale grupeeringu tasemele vastavad siis summeeriva statistiku identsed väärtused rea kaupa), kasuta peale group_by()
verbi mutate()
, mitte summarise()
.
mutate(iris_grouped,
MEAN = mean(Sepal.Length),
SD = sd(Sepal.Length))
#> # A tibble: 150 x 7
#> # Groups: Species [3]
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> <dbl> <dbl> <dbl> <dbl>
#> 1 5.1 3.5 1.4 0.2
#> 2 4.9 3 1.4 0.2
#> 3 4.7 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 5 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
#> # … with 144 more rows, and 3 more variables:
#> # Species <fct>, MEAN <dbl>, SD <dbl>
Anna igast grupist 3 kõrgeimat väärtust või 2 madalaimat väärtust. Samad numbrid erinevates ridades antakse kõik - selle pärast on meil tabelis rohkem ridu.
top_n(iris_grouped, 3, Sepal.Length)
top_n(iris_grouped, -2, Sepal.Length)
14.2.5 mutate()
Mutate põhikasutus on siiski uute veergude tekitamine, mis võtavad endale inputi rea kaupa. Seega tabeli ridade arv ei muutu.
tranformeeri tabeli “df” veerg “value” uueks veeruks “log_value”, kus on log2-transformeeritud numbrid: df %>% mutate(log_value = log2(value))
.
Uues veerus on vana veeru numbritest lahutatud konstant (näiteks vana veeru keskväärtus): df %>% mutate(centered_value = value - mean(value) )
.
Mutate() lisab veerge ja transmute()
kaotab ühtlasi ära vanad veerud
Uus veerg log-väärtustega, mis põhineb “value” veerul, millele anname nime “log_value”.
mutate(dat_lng, log_value = log(value))
#> # A tibble: 9 x 5
#> subject sex experiment value log_value
#> <chr> <chr> <chr> <dbl> <dbl>
#> 1 Tim M control 23 3.14
#> 2 Ann F control 31 3.43
#> 3 Jill F control 30 3.40
#> 4 Tim M experiment_1 34 3.53
#> 5 Ann F experiment_1 38 3.64
#> 6 Jill F experiment_1 36 3.58
#> # … with 3 more rows
Sama transmute() kasutades. Me säilitame lisaks “subject” veeru ja säilitame ning nimetame ümber “sex” veeru.
transmute(dat_lng, subject, gender = sex, log_value = log(value))
#> # A tibble: 9 x 3
#> subject gender log_value
#> <chr> <chr> <dbl>
#> 1 Tim M 3.14
#> 2 Ann F 3.43
#> 3 Jill F 3.40
#> 4 Tim M 3.53
#> 5 Ann F 3.64
#> 6 Jill F 3.58
#> # … with 3 more rows
Selekteerime veerud “year” kuni “day”, veerud, mille nimed lõppevad stringiga “delay”, veerud “distance” ja “air_time”. Seejärel loome uue veerud “gain” (kasutades selleks arr_delay ja dep_delay andmeid rea kaupa), “hours” (air_time jagatud konstandiga) ja “gain_per_hour”:
flights_sml <- select(flights,
year:day,
ends_with("delay"),
distance,
air_time) %>%
mutate(gain = arr_delay - dep_delay,
hours = air_time / 60,
gain_per_hour = gain / hours)
mutate_all()
, mutate_if()
and mutate_at()
and the three variants of transmute()
(transmute_all()
, transmute_if()
, transmute_at()
) make it easy to apply a transformation to a selection of variables. See help.
Kõigepealt grupeeri, siis muteeri. Konstandid Mean(value) ja sd(value) arvutatakse igale grupile eraldi selle grupi väärtuste pealt (siin grupeerime faktormuutuja “sex” kahe taseme järgi).
group_by(dat_lng, sex) %>%
mutate(norm_value = value / mean(value),
n2_val = value / sd(value))
#> # A tibble: 9 x 6
#> # Groups: sex [2]
#> subject sex experiment value norm_value n2_val
#> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Tim M control 23 0.711 2.67
#> 2 Ann F control 31 0.842 5.47
#> 3 Jill F control 30 0.814 5.29
#> 4 Tim M experiment_1 34 1.05 3.94
#> 5 Ann F experiment_1 38 1.03 6.70
#> 6 Jill F experiment_1 36 0.977 6.35
#> # … with 3 more rows
Võrdluseks ilma grupeerimata olukord, kus konstandil alati sama väärtus:
mutate(dat_lng,
norm_value = value / mean(value),
n2_val = value / sd(value))
#> # A tibble: 9 x 6
#> subject sex experiment value norm_value n2_val
#> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 Tim M control 23 0.651 3.48
#> 2 Ann F control 31 0.877 4.69
#> 3 Jill F control 30 0.849 4.54
#> 4 Tim M experiment_1 34 0.962 5.14
#> 5 Ann F experiment_1 38 1.08 5.75
#> 6 Jill F experiment_1 36 1.02 5.44
#> # … with 3 more rows
14.2.5.1 kahest veerust kolmanda tegemine nii, et NA-d esimeses veerus asendatakse numbritega teisest
y <- c(1, 2, 5, NA, 5)
z <- c(NA, NA, 7, 4, 5)
coalesce(z, y)
#> [1] 1 2 7 4 5
14.2.5.2 Summarise(), mutate(), transmute() ja filter() töötavad ka mitme veeru kaupa.
Need variandid sisaldavad suffikseid _if, _at ja _all.
_if võimaldab valida veerge teise funktsiooni, nagu näiteks is.numeric() või is.character() alusel.
_at võimaldab valida veerge sama süntaksiga, mis select().
_all valib kõik veerud.
summarise_all(df, mean)
teeb sama asja, mis colMeans().
summarise_all(df, funs(min, max))
võtab iga veeru min ja max väärtuse.
summarise_all(df, ~ sd(.) / mean(.))
arvutab iga veeru CV (pane tähele ~ kasutust)
summarise_all(df, funs(cv = sd(.) / mean(.), mean))
arvutab iga veeru CV ja keskmise (~ puudub, kui meil on >1 funktsiooni)
summarise_at(df, vars(-z), mean)
keskmine kõigist veergudest, v.a. z.
summarise_at(df, vars(x, y), funs(min, max))
kahe veeru min ja max.
summarise_if(is.numeric, mean, na.rm = TRUE)
ainult numbritega veerud
mutate_all(df, log10)
võta log10 kõikidest veergudest
mutate_all(df, ~ round(. * 25))
teeb kõik veerud täisarvulisteks ja korrutab 25-ga
mutate_all(df, funs(half = . / 2, double = . * 2))
rakendab 2 funktsiooni
transmute_all(df, funs(half = . / 2, double = . * 2))
jätab alles ainult uued veerud
filter_all(weather, any_vars(is.na(.)))
näitab ridu, mis sisaldavad NA-sid
filter_at(weather, vars(starts_with("wind")), all_vars(is.na(.)))
read, kus veerg, mis sisaldab wind, on NA.
14.2.5.3 Kasutame group_by %>% summarise toru, et arvutada rea kaupa statistik (p väärtused)
Näiteks t test tidy tabelist. Meil on 5 geeni, N=3, võrreldakse kahte tingimust (indeks veerg, “E” ja “C”).
library(tidyverse)
a <- tibble(gene= rep(1:5, each=6),
value= rnorm(30),
indeks= rep(c("E", "C"), each= 3, times=5))
head(a)
#> # A tibble: 6 x 3
#> gene value indeks
#> <int> <dbl> <chr>
#> 1 1 -0.485 E
#> 2 1 -0.863 E
#> 3 1 0.404 E
#> 4 1 2.56 C
#> 5 1 -0.0510 C
#> 6 1 -0.918 C
a %>% group_by(gene) %>% summarise(p = t.test(value~indeks)$p.value)
#> # A tibble: 5 x 2
#> gene p
#> <int> <dbl>
#> 1 1 0.512
#> 2 2 0.865
#> 3 3 0.646
#> 4 4 0.607
#> 5 5 0.457
14.2.5.4 Grupiviisiline filtreerimine
Säilita lennureiside sihtkohad, kuhu viib >365 lennu:
popular_dests <- flights %>%
group_by(dest) %>%
filter(n() > 365)
grupeeringu mahavõtmiseks, et töötada grupeerimata andmetega, kasuta ungroup()
.
14.3 separate()
üks veerg mitmeks
Siin on veel üks verb, mida aeg-ajalt kõigil vaja läheb. separate()
võtab ühe veeru sisu (mis peab olema character string) ning jagab selle laiali mitme uue veeru vahel. Kui teda kasutada vormis separate(df, old_Column, into=c("new_col1", "new_col2", "ja_nii_edasi"))
siis püüab programm ise ära arvata, kustkohalt veeru sisu hakkida (tühikud, komad, semikoolonid, koolonid jne). Aga te võite eksplitsiitselt ette anda separaatori sep = “”. sep = 2 tähendab “peale 2. tähemärki”. sep = -6 tähendab “enne tagantpoolt 6. tähemärki”
(dat <- tibble(country = c("Albania"), disease.cases = c("80/1000")))
#> # A tibble: 1 x 2
#> country disease.cases
#> <chr> <chr>
#> 1 Albania 80/1000
(df.sep <- dat %>% separate(disease.cases, into=c("cases", "thousand")))
#> # A tibble: 1 x 3
#> country cases thousand
#> <chr> <chr> <chr>
#> 1 Albania 80 1000
(df.sep <- dat %>% separate(disease.cases, into=c("cases", "thousand"), sep = "/"))
#> # A tibble: 1 x 3
#> country cases thousand
#> <chr> <chr> <chr>
#> 1 Albania 80 1000
(df.sep <- dat %>% separate(disease.cases, into=c("cases", "thousand"), sep = 2))
#> # A tibble: 1 x 3
#> country cases thousand
#> <chr> <chr> <chr>
#> 1 Albania 80 /1000
(df.sep <- dat %>% separate(disease.cases, into=c("cases", "thousand"), sep = -5))
#> # A tibble: 1 x 3
#> country cases thousand
#> <chr> <chr> <chr>
#> 1 Albania 80 /1000
(dat <- tibble(index = c(1, 2),
taxon = c("Procaryota; Bacteria; Alpha-Proteobacteria; Escharichia", "Eukaryota; Chordata")))
#> # A tibble: 2 x 2
#> index taxon
#> <dbl> <chr>
#> 1 1 Procaryota; Bacteria; Alpha-Proteobacteria; Es…
#> 2 2 Eukaryota; Chordata
(d1 <- dat %>% separate(taxon, c('riik', 'hmk', "klass", "perekond"), sep = '; ', extra = "merge", fill = "right"))
#> # A tibble: 2 x 5
#> index riik hmk klass perekond
#> <dbl> <chr> <chr> <chr> <chr>
#> 1 1 Procaryota Bacter… Alpha-Proteobact… Escharich…
#> 2 2 Eukaryota Chorda… <NA> <NA>
# some special cases:
(dat <- tibble(index = c(1, 2),
taxon = c("Prokaryota || Bacteria || Alpha-Proteobacteria || Escharichia", "Eukaryota || Chordata")))
(d1 <- dat %>% separate(taxon, c("riik", "hmk", "klass", "perekond"), sep = "\\|\\|", extra = "merge", fill = "right"))
dat <- tibble(index = c(1, 2),
taxon = c("Prokaryota.Bacteria.Alpha-Proteobacteria.Escharichia", "Eukaryota.Chordata"))
(d1 <- dat %>% separate(taxon, c('riik', 'hmk', "klass", "perekond"), sep = '[.]', extra = "merge", fill = "right"))
(dat <- tibble(index = c(1,2),
taxon = c("Prokaryota.Bacteria,Alpha-Proteobacteria.Escharichia", "Eukaryota.Chordata")))
(d1 <- dat %>% separate(taxon, c('riik', 'hmk', "klass", "perekond"), sep = '[,\\.]', extra = "merge", fill = "right"))
Anti-separate funktsioon on unite()
- vt. help.
Kuidas käituda siis, kui teil on veerus üks või mitu sissekannet, näiteks komadega eraldatud, ja te tahaksite need suruda mitte eraldi veergudesse (seda teeb separate()), vaid kõik ühte pikka veergu? Nüüd kasutame str_plit() ja unnest() funktsioone.
df1 <- tibble(a = c("w", "w, e, f", "g"), b=1:3)
df2 <- df1 %>% mutate(pikk_a = str_split(a, ",")) %>% select(pikk_a) %>% unnest()
#> Warning: `cols` is now required.
#> Please use `cols = c(pikk_a)`
df3 <- df1 %>% mutate(pikk_a = str_split(a, ",")) %>% unnest()
#> Warning: `cols` is now required.
#> Please use `cols = c(pikk_a)`
Pane tähele, et df3 on tabel, kus b veeru elemendid on duplitseeritud nii, et nad kataksid uue pikema pikk_a veeru vastavad elemndid. df2 on andmeraam, mis sisaldab ainult üht veergu.
14.4 Faktorid
Faktor on andmetüüp, mis oli ajalooliselt tähtsam kui ta praegu on. Sageli saame oma asja ära ajada character vectori andmetüübiga ja ei vaja faktorit. Aga siiski läheb faktoreid aeg-ajalt kõigil vaja.
Faktorite abil töötame kategooriliste muutujatega, millel on fikseeritud hulk võimalikke väärtusi, mida me kõiki teame.
Faktori väärtusi kutsutakse “tasemeteks” (levels). Näiteks: muutuja sex on 2 tasemega faktor (M, F)
NB! Faktoriks muutes saame character vectori liikmete järjekorra muuta mitte-tähestikuliseks
Me kasutame faktoritega töötamisel forcats paketti. Kõigepealt loome character vectori x1 nelja kuu nime ingliskeelse lühendiga.
library(forcats)
x1 <- c("Dec", "Apr", "Jan", "Mar")
Nüüd kujutlege, et vektor x1 sisaldab 10 000 elementi. Seda vektorit on raske sorteerida, ja trükivead on ka raskesti leitavad. Mõlema probleemi vastu aitab, kui me konverteerime x1-e faktoriks. Selleks, et luua uus faktor, peaks kõigepealt üles lugema selle faktori kõik võimalikud tasemed:
Nüüd loome uue faktori ehk muudame x1 character vektori y1 factor vektoriks. Erinevalt x1-st seostub iga y1 väärtusega faktori tase. Kui algses vektoris on mõni element, millele ei vasta näiteks trükivea tõttu ühtegi faktori taset, siis see element muudetakse NA-ks. Proovige see ise järele, viies trükivea sisse x1-e.
y1 <- factor(x1, levels = month.abb)
y1
#> [1] Dec Apr Jan Mar
#> 12 Levels: Jan Feb Mar Apr May Jun Jul Aug Sep ... Dec
NB! month.abb
on R objekt mis sisaldab kuude ingliskeelseid lühendeid.
Kui sa faktorile tasemeid ette ei anna, siis need tekivad andmetest automaatselt ja tähestikulises järjekorras.
Kui sa tahad, et faktori tasemed oleks samas järjekorras kui selle taseme esmakordne ilmumine teie andmetes siis:
f2 <- factor(x1) %>% fct_inorder()
f2
#> [1] Dec Apr Jan Mar
#> Levels: Dec Apr Jan Mar
levels()
annab faktori tasemed ja nende järjekorra
levels(f2)
#> [1] "Dec" "Apr" "Jan" "Mar"
Kui faktorid on tibbles oma veeruna, siis saab nende tasemed count()
kasutades:
gss_cat #tibble, mille veerg "race" on faktor.
#> # A tibble: 21,483 x 9
#> year marital age race rincome partyid relig denom
#> <int> <fct> <int> <fct> <fct> <fct> <fct> <fct>
#> 1 2000 Never … 26 White $8000 … Ind,ne… Prot… Sout…
#> 2 2000 Divorc… 48 White $8000 … Not st… Prot… Bapt…
#> 3 2000 Widowed 67 White Not ap… Indepe… Prot… No d…
#> 4 2000 Never … 39 White Not ap… Ind,ne… Orth… Not …
#> 5 2000 Divorc… 25 White Not ap… Not st… None Not …
#> 6 2000 Married 25 White $20000… Strong… Prot… Sout…
#> # … with 2.148e+04 more rows, and 1 more variable:
#> # tvhours <int>
gss_cat %>% count(race)
#> # A tibble: 3 x 2
#> race n
#> <fct> <int>
#> 1 Other 1959
#> 2 Black 3129
#> 3 White 16395
Nii saame ka teada, mitu korda iga faktori tase selles tabelis esineb.
14.4.1 tekitame faktortulba keerulisemal teel
dplyr::case_when(). Kui Sepal.Length on > 5.8 või Sepal.Width >4, siis uues veerus nimega fact ilmub tase “large”, kui Species = setosa, siis ilmub tase “I. setosa”, igal muul juhul ilmub
library(tidyverse)
i <- iris %>% mutate(
fact = case_when(
Sepal.Length > 5.8 | Sepal.Width > 4 ~ "large",
Species == "setosa" ~ "I. setosa",
TRUE ~ NA_character_
))
case_when() teeb loogilisi tehteid samas järjekorras, mis sa ette andsid. Seega kui mõni väärtus võiks minna mitmesse teie poolt spetsifitseeritud tingimusse, siis ta läheb tegelikult esimesena ette tulevasse tõesesse tingimusse.
14.4.2 droplevels()
viskab välja kasutamata faktori tasemed
df1$sex <- droplevels(df1$sex)
14.4.3 fct_recode()
rekodeerib faktori tasemed
gss_cat %>% count(partyid)
#> # A tibble: 10 x 2
#> partyid n
#> <fct> <int>
#> 1 No answer 154
#> 2 Don't know 1
#> 3 Other party 393
#> 4 Strong republican 2314
#> 5 Not str republican 3032
#> 6 Ind,near rep 1791
#> # … with 4 more rows
gss_cat %>%
mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat",
"Other" = "No answer",
"Other" = "Don't know",
"Other" = "Other party"
)) %>%
count(partyid)
#> # A tibble: 8 x 2
#> partyid n
#> <fct> <int>
#> 1 Other 548
#> 2 Republican, strong 2314
#> 3 Republican, weak 3032
#> 4 Independent, near rep 1791
#> 5 Independent 4119
#> 6 Independent, near dem 2499
#> # … with 2 more rows
fct_recode()
ei puuduta neid tasemeid, mida selle argumendis ei mainita. Lisaks saab mitu vana taset muuta üheks uueks tasemeks.
14.4.4 fct_collapse()
annab argumenti sisse vanade tasemete vektori, et teha vähem uusi tasemeid.
gss_cat %>%
mutate(partyid = fct_collapse(partyid,
other = c("No answer", "Don't know", "Other party"),
rep = c("Strong republican", "Not str republican"),
ind = c("Ind,near rep", "Independent", "Ind,near dem"),
dem = c("Not str democrat", "Strong democrat")
)) %>%
count(partyid)
14.4.5 fct_lump()
lööb kokku kõik vähem arv kordi esinevad tasemed.
n parameeter ütleb, mitu algset taset tuleb alles jätta:
gss_cat %>%
mutate(relig = fct_lump(relig, n = 5)) %>%
count(relig, sort = TRUE) %>%
print()
#> # A tibble: 6 x 2
#> relig n
#> <fct> <int>
#> 1 Protestant 10846
#> 2 Catholic 5124
#> 3 None 3523
#> 4 Other 913
#> 5 Christian 689
#> 6 Jewish 388
14.4.6 Rekodeerime pideva muutuja faktoriks
cut()
jagab meie muutuja väärtused intervallidesse ja annab igale intervallile faktori taseme.
cut(x, breaks, labels = NULL, ordered_result = FALSE, ...)
breaks - either a numeric vector of two or more unique cut points or a single number >1, giving the number of intervals into which x is to be cut. labels - labels for the levels of the resulting category. ordered_result - logical: should the result be an ordered factor?
z <- 1:10
z1 <- cut(z, breaks = c(0, 3, 6, 10), labels = c("A", "B", "C"))
z1
#> [1] A A A B B B C C C C
#> Levels: A B C
#Note that to include 1 in level “A” you need to start the first cut <1, while at the right side 3 is included in the 1st cut (in factor level “A”)
z2 <- cut(z, breaks = 3, labels = c("A", "B", "C"))
z2
#> [1] A A A A B B B C C C
#> Levels: A B C
car::recode
aitab rekodeerida
library(car)
x <- rep(1:3, 3)
x
#> [1] 1 2 3 1 2 3 1 2 3
recode(x, "c(1,2) = 'A'; else = 'B'")
#> [1] "A" "A" "B" "A" "A" "B" "A" "A" "B"
recode(x, "c(1,2) = NA")
#> [1] NA NA 3 NA NA 3 NA NA 3
recode(x, "1:2 = 'A'; 3 = 'B'")
#> [1] "A" "A" "B" "A" "A" "B" "A" "A" "B"
14.4.7 Muudame faktori tasemete järjekorda joonisel
## summeerime andmed
gsscat_sum <- group_by(gss_cat, relig) %>%
summarise(age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n())
## joonistame graafiku
p <- ggplot(gsscat_sum, aes(tvhours, fct_reorder(relig, tvhours))) +
geom_point()
p
14.4.8 fct_relevel()
tõstab joonisel osad tasemed teistest ettepoole
Argumendid on faktor f ja need tasemed (jutumärkides), mida sa tahad tõsta.
## täiendame eelmist graafikut ümberkorraldatud andmetega
p + aes(tvhours, fct_relevel(relig, "None", "Don't know"))
14.4.9 Joontega plotil saab fct_reorder2()
abil assotseerida y väärtused suurimate x väärtustega
See muudab ploti paremini jälgitavaks:
## summeerime andmed
gsscat_sum <- filter(gss_cat, !is.na(age)) %>%
group_by(age, marital) %>%
mutate(N=n())
## paneme andmed graafikule
ggplot(gsscat_sum, aes(age, N, colour = fct_reorder2(marital, age, N))) +
geom_line() +
labs(colour = "marital")
14.4.10 Tulpdiagrammide korral kasuta fct_infreq()
Loeme kokku erineva perekondliku staatusega isikud ja paneme need andmed tulpdiagrammi grupi suurusele vastupidises järjekorras st. väiksemad grupid tulevad enne.
mutate(gss_cat, marital = fct_infreq(marital) %>% fct_rev()) %>%
ggplot(aes(marital)) + geom_bar()