6 R-i tööpõhimõte
Iga kord, kui avate R studio, alustate R-i sessiooni, mis seisneb funktsioonide (väikeste programmijuppide) rakendamises andmetele eesmärgiga muuta tabelite struktuuri, transformeerida andmeid, genereerida jooniseid ja/või arvutada statistikuid. Kõik see toimub R-i töökeskkonnas. Tulemusi saab töökeskkonnast eksportida arvuti kõvakettale näiteks .pdf (joonised) või .csv (tabelid) formaadis. Teie sessiooni põhiline tulemus ei ole siiski eksporditavad asjad, vaid R-i kood (script), mida jooksutades on võimalik korrataval viisil algset andmetabelit manipuleerida. Kõik, mida te töökeskonnas teete, kajastub koodis ja on korratav.
R-i sessioon näeb üldjoontes välja niimoodi:
- Andmed ja funktsioonid (raamatukogude kujul) loetakse R-i töökeskkonda, (2) andmeid töödeldakse funktsioonide abil ja (3) tööproduktid eksporditakse/salvestatakse teistele programmidele kättesaadavasse vormi.
töökeskkond (workspace) - sisaldab üles laetud objekte
keskkond (environment) - sisaldab nimega seotud objekte
pakett e raamatukogu - sisaldab funktsioone, andmeid ja seletavaid faile
funktsioon - R-i alamprogramm
argument - funktsiooni tööd reguleeriv parameeter
objekt - funktsioon, andmestruktuur ja mida iganes saab töökeskkonda viia
nimi - objekt seotakse nimega <- abil, et see keskkonnas nähtavaks/taaskasutatavaks muuta
Andmetüübid:
character - tähemärgid
numeric (double) - ratsionaalarvud
integer - täisarvud
factor - muutuja, millel on loetud hulk nimedega “tasemeid” (levels), kus iga tase on sisemiselt kodeeritud täisarvuga alates 1st. On olemas nii järjestatud kui järjestamata tasemetega faktorid. Näit “mees”-naine on tüüpiliselt järjestamata tasemed, aga vähe-keskmiselt-palju peaks olema järjestatud.
logical - TRUE/FALSE. TRUE on sisemiselt kodeeritud kui 1 ja FALSE kui 0.
relatsioonilised operaatorid
, >= (on suurem või võrdne), <, <=,
== (võrdub),
!= (ei võrdu),
%in% (sisaldub),
& (and),
- (or)
1 == 2
#> [1] FALSE
1 != 2
#> [1] TRUE
1 %in% 1:5
#> [1] TRUE
1&4 %in% 1:5 #1 ja 4 sisalduvad vektoris c(1, 2, 3, 4, 5) --- TRUE
#> [1] TRUE
c(1, 8) %in% 1:5
#> [1] TRUE FALSE
1|8 %in% 1:5 #1 või 8 sisalduvad vektoris c(1, 2, 3, 4, 5) --- TRUE
#> [1] TRUE
6 %in% 1:5 #6 sisaldub vektoris c(1, 2, 3, 4, 5) --- see lause on FALSE
#> [1] FALSE
char.vector <- c("apple", "banana", "cantaloupe", "dragonfruit")
"apple" %in% char.vector
#> [1] TRUE
andmeklassid: (idee on andmed struktureerida hõlbustamaks edasisi tehteid nendega)
vektor - 1D järjestatud sama tüüpi andmed
list - kokku kogutud, järjestatud ja nimetatud objektid (vektorid, andmeraamid, mudelid, teised listid jms)
maatriks - 2D neljakandiline andmestruktuur, mis koosneb sama tüüpi andmetest
andmeraam (data frame, tibble) - 2D neljakandiline andmestruktuur, mis koosneb veeru kaupa kõrvuti asetatud ühepikkustest vektoritest (erinevad vektorid võivad sisaldada erinevaid tüüpi andmeid). Lähim asi nelinurksele tabelile (aga Exceli tabelis – erinevalt R-st – saab samasse veergu panna ka erinevat tüüpi andmeid).
Avades R Studio peaksite nägema tühja keskkonda (Enviroment tab, ülal paremas aknas). R-i tööpõhimõte on järgmine.
te laete võrgust alla ja installeerite oma kõvakettale vajalikud R-i alamprogrammid, mida kutsutakse pakettideks ehk raamatukogudeks. Paketid on funktsioonide (kuigi mitte ainult) kogud. Need funktsioonid ei ole aga veel R-s kasutamiseks kättesaadavad.
te loete R-i töökeskkonda (workspace) sisse vajalikud paketid (oma kõvakettalt) ja andmetabeli(d), mida soovite töödelda (oma kõvakettalt või otse võrgust), muutes need seega R-s kasutatavaks. Seda tuleb teha igal R-i sessioonil uuesti. Igat asja, mis on töökeskkonnas, kutsutakse objektiks. Objektid, mis on omistatud nimele, ilmuvad nähtavale Enviroment tab-i, koos oma struktuuri lühikirjeldsega.
Te sisestate töökeskkonnas olevatesse funtksioonidesse andmed ja argumendid. Sageli teeb üks funktsioon ühte toimingut, mistõttu koosneb teie töövoog funktsioonide rakendamisest üksteise järel sellisel moel, et eelmise funktsiooni väljund on sisend järgmisele funktsioonile.
Te ekspordite/salvestate oma kõvakettale need objektid (tabelid, joonised), mida soovite tulevikus avada teiste programmidega. Samuti salvestate oma põhilise töötulemuse - R-i scripti (näit .Rmd või .R laiendiga failina).
Järgmise sessiooni saate alustada juba oma salvestatud koodi baasilt — jooksutades algandmete peal olemasoleva koodi ning seejärel lisades uut koodi (andmetabeli ja raamatukogud tuleb iga sessiooni jaoks uuesti keskkonda sisse lugeda).
6.1 Funktsioon
Hea funktsioon teeb ühte asja. Näiteks funktsioon
t()
( t tähendab transpose) transponeerib maatriksi nii, et ridadest saavad veerud ja vastupidi, aga funktsioonc()
( c tähendab combine v concatenate ) moodustab sisestatud objektidest vektori.Funktsiooni nime taha käivad sulud. Ilma sulgudeta funktsiooni nime jooksutamine annab väljundina selle funktsiooni koodi.
Enamustel funktsioonidel on argumendid, mis käivad sulgude sisse ja on üksteisest komadega eraldatud. Kasutaja saab argumentidele anda väärtused, mis määravad andmed, millel funktsioon töötab, ja selle, mida funktsioon nende andmetega täpselt teeb. Funktsiooni iga argument täpsustab funktsiooni jaoks, mida teha.
Osadel argumentidel on vaikeväärtused, mida saab käsitsi muuta. Vaikeväärtused, nagu ka funktsiooni argumentide nimekirja ja kirjelduse, leiab
?funktsiooni_nimi
(ilma sulgudeta) abil. NB! Ära kasuta funktsioone, mille argumente sa ei tunne.Argumendid võivad olla kas kohustuslikud (ilma argumendi väärtust sisestamata funktsioon ei tööta), või mitte. Näiteks funktsioon
plot(x, y, ...)
argumendid on objekt nimega x, mis annab x teljele plotitud andmete koordinaadid, objekt nimega y, mis annab sama y teljele, ning lisaargumendid, mis võivad sõltuda x-i ja või y-i vormist. x on kohustuslik argument, aga y ei ole tingimata vajalik (kas y on vajalik või mitte, sõltub sisestatud x-i struktuurist).Kui argumendi väärtus sisestatakse teksti kujul, siis enamasti jutumärkides. Jutumärgid muudavad R-i jaoks teksti tähemärkide jadaks e stringiks, mille sees R numbreid ei tõlgenda arvudena.
Argumendid on järjestatud ja neil on nimed. Nimi trumpab järjekorra üle selles mõttes, et me võime argumentide nimed funktsiooni kirjutada suvalises järjekorras ilma, et funktsiooni töö sellest muutuks. Samas, kui me sisestame funktsiooni argumendid ilma nimedeta, siis on argumentide järjekord tähtis, sest need seostatakse vaikimisi nimedega vastavalt oma järjekorranumbrile. Oletame, et meil on vektorid
kaal <- c(2.3, 4.3, 3)
japikkus <- c(7, 5, 9)
. Me võime need funktsiooni sisestada nii:plot(x = kaal, y = pikkus)
,plot(y = pikkus, x = kaal)
japlot(kaal, pikkus)
teevad kõik identse scatterploti (agaplot(pikkus, kaal)
ei tee).Funktsiooni esimese argumendi saab enamasti sisestada ka alternatiivsel viisil, %>% pipe operaatori abil. Niimoodi jooksevad
fun(arg1, arg2)
jaarg1 %>% fun(arg2)
koodid enamasti identselt. Kumba koodi eelistada on seega “vaid” koodi loetavuse küsimus.arg1 %>% fun(arg2)
on sama, misarg1 %>% fun(., arg2)
, kus punkt “.” näitab vaikimisi, mitmenda argumendi kohale me oma arg1 sisse torutame. Seda teades on võimalik ka vormarg2 %>% fun(arg1,.)
, kus toru kaudu anname sisse 2. või ükskõik millise muu argumendi. Siin ei ole muud saladust kui, et peame punkti asukoha funktsioonis eksplitsiitselt ära näitama.
Ülesanne: uuri välja, mida määravad järgneva funktsiooni argumendid.
plot(table(rpois(100, 5)), type = "h", col = "red", lwd = 10, main = "rpois(100, lambda = 5)")
Pane tähele, et funktsiooni “plot” argumendid “table” ja “rpois” on ka ise funktsioonid, millel on kummagil oma argumendid.
6.2 Sama koodi saab kirjutada neljal erineval viisil
Idee on sooritada järjest operatsioone nii, et eelmise operatsiooni väljund (R-i objekt) oleks sisendiks järgmisele operatsioonile (funktsioonile). See on lihtne hargnemisteta analüüsiskeem.
Kui me muudame olemasolevat objekti, siis me kas jätame muudetud objektile vana objekti nime või me anname talle uue nime. Esimesel juhul läheb eelmine muutmata objekt töökeskkonnast kaduma, aga nimesid ei tule juurde ja säilib töövoo sujuvus. Teisel juhul jäävad analüüsi vaheobjektid meile alles ja nende juurde saab alati tagasi tulla. Aga samas tekib palju sarnaste nimedega objekte.
6.2.1 Esimene võimalus - anname järjest tekkinud objektid samale nimele.
a <- c(2, 3)
a <- sum(a)
a <- sqrt(a)
a <- round(a, 2)
a
#> [1] 2.24
6.2.2 Teine võimalus - uued nimed.
Nii saab tekkinud objekte hiljem kasutada.
a <- c(2, 3)
a1 <- sum(a)
a2 <- sqrt(a1)
a3 <- round(a2, 2)
a3
#> [1] 2.24
6.2.3 Kolmas võimalus on lühem variant esimesest.
Me nimelt ühendame etapid toru %>%
kaudu. Toru operaator ei ole siiski baas R-is kohe kättesaadav, vaid tuleb laadida kas magrittr või dplyr paketist (viimatinimetatu laadib selle funktsiooni ka vaikimisi esimesena nimetatud raamatukogust). Siin me võtame objekti “a” (nö. andmed), suuname selle funktsiooni sum()
, võtame selle funktsiooni väljundi ja suuname selle omakorda funktsiooni sqrt()
. Seejärel võtame selle funktsiooni outputi ja määrame selle nimele “result” (aga võime selle ka mõne teise nimega siduda). Kui mõni funktsioon võtab ainult ühe parameetri, mille me talle toru kaudu sisse sõõdame, siis pole selle funktsiooni taga isegi sulge vaja (R hea stiili juhised soovitavad siiski alati kasutada funktsiooni koos sulgudega).
See on hea lühike ja inimloetav viis koodi kirjutada, mis on masina jaoks identne esimese koodiga.
library(dplyr)
a <- c(2, 3)
result <- a %>% sum() %>% sqrt() %>% round(2)
result
#> [1] 2.24
6.2.4 Neljas võimalus, klassikaline baas R lahendus:
a <- c(2, 3)
result <- round(sqrt(sum(a)), 2)
result
#> [1] 2.24
Sellist koodi loetakse keskelt väljappoole ja kirjutatakse alates viimasest operatsioonist, mida soovitakse, et kood teeks. Masina jaoks pole vahet. Inimese jaoks on küll: 4. variant nõuab hästi pestud ajusid.
Koodi lühidus 4 –> 3 –> 1 –> 2 (pikem) Lollikindlus 2 –> 1 –> 3 –> 4 (vähem lollikindel) Loetavus 3 –> 2 –> 1 –> 4 (halvemini loetav)
See on teie otsustada, millist koodivormi te millal kasutate, aga te peaksite oskama lugeda neid kõiki.
6.3 objekt
R-i töökeskkonnas “workspace” asuvad objektid, millega me töötame. Igal objektil on nimi, mille abil saab selle objektiga opereerida (teda argumendina funktsioonidesse sisestada). Tüüpilised objektid on:
- Vektorid, maatriksid, listid ja andmeraamid.
- Statistiliste analüüside väljundid (mudeliobjektid, S3, S4 klass).
- Funktsioonid.
Funktsioon ls()
annab objektide nimed teie workspace-s.
rm(a)
eemaldab objekti nimega a töökeskkonnast.
Selleks, et salvestada töökeskkond faili, kasuta “Save” nuppu “Environment” akna servast või menüüst “Session” -> “Save Workspace As”.
Projekti sulgemisel salvestab RStudio vaikimisi töökeskkonna. Parema reprodutseeritavuse huvides pole siiski soovitav töökeskkonda peale töö lõppu projekti sulgemisel salvestada!. Lülitame automaatse salvestamise välja:
- Selleks mine “Tools” > “Global Options” > kõige ülemine, “R General” menüüs vali “Save workspace to .RData on exit” > “Never” ever!
- Võta ära linnuke “Restore .RData to workspace at startup” eest.
Kui on mingid kaua aega võtvad kalkulatsioonid või allalaadimised, salvesta need eraldi .rds faili ja laadi koodis vastavalt vajadusele: write_rds()
, read_rds()
.
6.3.1 Objekt ja nimi
Kui teil sünnib laps, annate talle nime. R-s on vastupidi: nimele antakse objekt
babe <- "beebi"
babe
#> [1] "beebi"
Siin on kõigepealt nimi (babe), siis assigneerimise sümbol <-
ja lõpuks objekt, mis on nimele antud (string “beebi”).
NB! Stringid on jutumärkides, nimed mitte. Nimi üksi evalueeritakse kui käsk: “print object”. Antud juhul trükitakse konsooli string “beebi”
Nüüd muudame objekti nime taga:
babe <- c("saatan", "inglike")
babe
#> [1] "saatan" "inglike"
Tulemuseks on sama nimi, mis tähistab nüüd midagi muud (vektorit, mis koosneb 2st stringist). Objekt “beebi” kaotas oma nime ja on nüüd workspacest kadunud. class()
annab meile objekti klassi.
class(babe)
#> [1] "character"
Antud juhul character.
Ainult need objektid, mis on assigneeritud nimele, lähevad workspace ja on sellistena kasutatvad edasises analüüsis.
apples <- 2
bananas <- 3
apples + bananas
#> [1] 5
Selle ekspressiooni tulemus trükitakse ainult R konsooli. Kuna teda ei määrata nimele, siis ei ilmu see ka workspace.
a <- 2
b <- 3
a <- a + b
# objekti nimega 'a' struktuur
str(a)
#> num 5
Nüüd on nimega a seostatud uus objekt, mis sisaldab numbrit 5 (olles ühe elemendiga vektor). Ja nimega a eelnevalt seostatud objekt, mis koosnes numbrist 2, on workspacest lahkunud.
6.3.1.1 Nimede vorm
- Nimed algavad ingliskeelse tähemärgiga, mitte numbriga ega $€%&/?~ˇöõüä
- Nimed ei sisalda tühikuid
- Tühiku asemel kasuta alakriipsu: näiteks eriti_pikk_nimi
- SUURED ja väiksed tähed on nimes erinevad
- Nimed peaksid kirjeldama objekti, mis on sellele nimele assigneeritud ja nad võivad olla pikad sest TAB klahv annab auto-complete.
- alt + - on otsetee
<-
jaoks
6.4 Andmete tüübid
- numeric / integer
- logical – 2 väärtust TRUE/FALSE
- character
- factor (ordered and unordered) - 2+ diskreetset väärtust, mis võivad olla järjestatud suuremast väiksemani (aga ei asu üksteisest võrdsel kaugusel). Faktoreid käsitleme põhjalikumalt hiljem.
Faktoritel on tasemed (level) ja sisemiselt on iga faktori tase tähistatud täisarvulise numbriga.
Andmete tüüpe saab üksteiseks konverteerida as.numeric()
, as.character()
, as.factor()
.
a <- 5:10
#vektor, mis koosneb 6st täisarvust 5st 10-ni
class(a)
#> [1] "integer"
a_char <- c("5", "6", "7")
#jutumärgid tähistavad tähemärki, mitte arvu.
class(a_char)
#> [1] "character"
a1 <- as.factor(a)
a1
#> [1] 5 6 7 8 9 10
#> Levels: 5 6 7 8 9 10
a2 <- as.numeric(a1)
#see ei tööta, sest faktori tasemed
#rekodeeritakse sisemiselt numbritena alates 1st.
a2
#> [1] 1 2 3 4 5 6
a3 <- as.numeric(as.character(a1))
#see töötab, taastab numbrid 5st 10-ni
#kõigepealt konverteerime faktori tasemed tähemärkideks
#(ignoreerides sisemisi rekodeeringuid).
#Seejärel konverteerime tähemärgid numbriteks.
a3
#> [1] 5 6 7 8 9 10
6.5 Objektide klassid
6.5.1 Vektor
Vektor on rida kindlas järjekorras arve, tähemärkide stringe või TRUE/FALSE loogilisi väärtusi. Iga vektor ja maatriks (mis on 2D vektor) sisaldab ainult ühte tüüpi andmeid. Vektor on elementaarüksus, millega me teeme tehteid. Andmetabelis ripuvad kõrvuti ühepikad vektorid (üks vektor = üks tulp) ja R-le meeldib arvutada vektori kaupa vasakult paremale (mis tabelis on ülevalt alla sest vektori algus on üleval tabeli peas). Pikema kui üheelemendise vektori loomiseks kasuta funktsiooni c()
– combine
Loome numbrilise vektori ja vaatame ta struktuuri:
minu_vektor <- c(1, 3, 4)
str(minu_vektor)
#> num [1:3] 1 3 4
Loome vektori puuduva väärtusega, vaatame vektori klassi:
minu_vektor <- c(1, NA, 4)
minu_vektor
#> [1] 1 NA 4
class(minu_vektor)
#> [1] "numeric"
Klass jääb numeric-uks.
Kui vektoris on segamini numbrid ja stringid, siis muudetakse numbrid ka stringideks:
minu_vektor <- c(1, "2", 2, 4, "joe")
minu_vektor
#> [1] "1" "2" "2" "4" "joe"
class(minu_vektor)
#> [1] "character"
Piisab ühest “tõrvatilgast meepotis”, et teie vektor ei sisaldaks enam numbreid.
Eelnevast segavektorist on võimalik numbrid päästa kasutades käsku as.numeric()
:
as.numeric(minu_vektor)
#> Warning: NAs introduced by coercion
#> [1] 1 2 2 4 NA
Väärtus “joe” muudeti NA-ks, kuna seda ei olnud võimalik numbriks muuta. Samuti peab olema tähelepanelik faktorite muutmisel numbriteks:
minu_vektor <- factor(c(9, "12", 12, 1.4, "joe"))
minu_vektor
#> [1] 9 12 12 1.4 joe
#> Levels: 1.4 12 9 joe
class(minu_vektor)
#> [1] "factor"
## Kui muudame faktori otse numbriks, saame faktori taseme numbri
as.numeric(minu_vektor)
#> [1] 3 2 2 1 4
Faktorite muutmisel numbriteks tuleb need kõigepealt stringideks muuta:
as.numeric(as.character(minu_vektor))
#> Warning: NAs introduced by coercion
#> [1] 9.0 12.0 12.0 1.4 NA
Järgneva trikiga saab stringidest kätte numbrid:
minu_vektor <- c(1, "A2", "$2", "joe")
## parse_number() is imported from tidyverse 'readr'
minu_vektor <- parse_number(minu_vektor) %>% as.vector()
#> Warning: 1 parsing failure.
#> row col expected actual
#> 4 -- a number joe
str(minu_vektor)
#> num [1:4] 1 2 2 NA
R säilitab vektori algse järjekorra. Sageli on aga vaja tulemusi näiteks vaatamiseks ja presenteerimiseks sorteerida suuruse või tähestiku järjekorras:
## sorts vector in ascending order
sort(x, decreasing = FALSE, ...)
Vektori unikaalsed väärtused saab kätte käsuga unique()
:
## returns a vector or data frame, but with duplicate elements/rows removed
unique(c(1,1,1,2,2,2,2,2,3,3,4,5,5))
#> [1] 1 2 3 4 5
Uus vektor automaatselt: seq()
ja rep()
seq annab kasvava või kahaneva rea. rep kordab väärtusi.
seq(2, 3, by = 0.5)
#> [1] 2.0 2.5 3.0
seq(2, 3, length.out = 5)
#> [1] 2.00 2.25 2.50 2.75 3.00
rep(1:2, times = 3)
#> [1] 1 2 1 2 1 2
rep(1:2, each = 3)
#> [1] 1 1 1 2 2 2
rep(c("a", "b"), each = 3, times = 2)
#> [1] "a" "a" "a" "b" "b" "b" "a" "a" "a" "b" "b" "b"
6.5.1.1 Tehted arvuliste vektoritega
Vektoreid saab liita, lahutada, korrutada ja jagada.
a <- c(1, 2, 3)
b <- 4
a + b
#> [1] 5 6 7
Kõik vektor a liikmed liideti arvuga 3 (kuna vektor b koosnes ühest liikmest, läks see kordusesse)
a <- c(1, 2, 3)
b <- c(4, 5)
a + b
#> Warning in a + b: longer object length is not a
#> multiple of shorter object length
#> [1] 5 7 7
Aga see töötab veateatega, sest vektorite pikkused ei ole üksteise kordajad 1 + 4; 2 + 5, 3 + 4
a <- c(1, 2, 3, 4)
b <- c(5, 6)
a + b
#> [1] 6 8 8 10
See töötab: 1 + 5; 2 + 6; 3 + 5; 4 + 6
a <- c(1, 2, 3, 4)
b <- c(5, 6, 7, 8)
a + b
#> [1] 6 8 10 12
Samuti see (ühepikkused vektorid — igat liiget kasutatakse üks kord)
a <- c(TRUE, FALSE, TRUE)
sum(a)
#> [1] 2
mean(a)
#> [1] 0.667
Mis siin juhtus? R kodeerib sisemiselt TRUE kui 1 ja FALSE kui 0-i. summa 1 + 0 + 1 = 2. Mean seevastu võtab ühtede summa (TRUE elementide arvu) suhte vektori elementide arvust ja annab seega TRUE väärtuste suhtarvu. Seda loogiliste väärtuste omadust õpime varsti praktikas kasutama.
6.5.2 List
List on objektitüüp, kuhu saab koondada kõiki teisi objekte, kaasa arvatud listid. R-i jaoks on list lihtsalt vektor, mille elemendid ei pean olema sama andmetüüpi (nagu tavalistel nn lihtsatel vektoritel).
Praktikas kasutatakse listi enamasti lihtsalt erinevate R-i objektide koos hoidmiseks ühes suuremas meta-objektis. List on nagu jõuluvana kingikott, kus kommid, sokipaarid ja muud kingid segamini kolisevad. Listidega töötamist vaatame lähemalt veidi hiljem.
Näiteks list, kus on 1 vektor nimega a, 1 tibble nimega b ja 1 list nimega c, mis omakorda sisaldab vektorit nimega d ja tibblet nimega e. Seega on meil tegu rekursiivse listiga.
# numeric vector a
a <- runif(5)
# data.frame
ab <- data.frame(a, b = rnorm(5))
# linear model
model <- lm(mpg ~ hp, data = mtcars)
# your grandma on bongos
grandma <- "your grandma on bongos"
# let's creat list
happy_list <- list(a, ab, model, grandma)
happy_list
#> [[1]]
#> [1] 0.458 0.208 0.613 0.481 0.936
#>
#> [[2]]
#> a b
#> 1 0.458 -1.033
#> 2 0.208 -2.106
#> 3 0.613 -0.734
#> 4 0.481 -2.005
#> 5 0.936 1.548
#>
#> [[3]]
#>
#> Call:
#> lm(formula = mpg ~ hp, data = mtcars)
#>
#> Coefficients:
#> (Intercept) hp
#> 30.0989 -0.0682
#>
#>
#> [[4]]
#> [1] "your grandma on bongos"
Võtame listist välja elemndi “ab”:
happy_list$ab
#> NULL
6.5.3 data frame ja tibble
Andmeraam on eriline list, mis koosneb ühepikkustest ja sama tüüpi vektoritest (listi iga element on vektor). Iga vektor on df-i veerg ja igas veerus on ainult ühte tüüpi andmed. Need vektorid ripuvad andmeraamis kõrvuti nagu tuulehaugid suitsuahjus, kusjuures vektori algus vastab tuulehaugi peale, mis on konksu otsas (konks vastab andmeraamis veeru nimele). Iga vektori nimi muutub sellises tabelis veeru nimeks.
R-s on 2 andmeraami tüüpi: data frame ja tibble, mis on väga sarnased. Tibble on uuem, veidi kaunima väljatrükiga ja pisut mugavam kasutada.
Erinevalt data frame-st saab tibblesse lisada ka list tulpasid, mis võimaldab sisuliselt suvalisi R objekte tibblesse paigutada. Põhimõtteliselt piisab ainult ühest andmestruktuurist – tibble, et R-is töötada. Kõik, mis juhtub tibbles, jääb tibblesse.
“Tidyverse” töötab tibblega veidi paremini kui data frame-ga, aga see vahe ei ole suur.
Siin on meil 3 vektorit: shop, apples ja oranges, millest me paneme kokku tibble nimega fruits
## loome kolm vektorit
shop <- c("maxima", "tesco", "lidl")
apples <- c(1, 4, 43)
oranges <- c(2, 32, NA)
vabakava <- list(letters, runif(10), lm(mpg ~ cyl, mtcars))
## paneme need vektorid kokku tibble-sse
fruits <- tibble(shop, apples, oranges, vabakava)
fruits
#> # A tibble: 3 x 4
#> shop apples oranges vabakava
#> <chr> <dbl> <dbl> <list>
#> 1 maxima 1 2 <chr [26]>
#> 2 tesco 4 32 <dbl [10]>
#> 3 lidl 43 NA <lm>
Siin ta on, ilusti meie workspace-s. Pange tähele viimast tulpa “vabakava”, mis sisaldab character vectorit, numbrilist vektorit ja lineaarse mudeli objekti.
Listi juba nii lihtsalt data.frame-i ei pane:
dfs <- try(data.frame(shop, apples, oranges, vabakava))
#> Error in as.data.frame.default(x[[i]], optional = TRUE, stringsAsFactors = stringsAsFactors) :
#> cannot coerce class '"lm"' to a data.frame
dfs
#> [1] "Error in as.data.frame.default(x[[i]], optional = TRUE, stringsAsFactors = stringsAsFactors) : \n cannot coerce class '\"lm\"' to a data.frame\n"
#> attr(,"class")
#> [1] "try-error"
#> attr(,"condition")
#> <simpleError in as.data.frame.default(x[[i]], optional = TRUE, stringsAsFactors = stringsAsFactors): cannot coerce class '"lm"' to a data.frame>
6.5.4 Matrix
Maatriks on 2-dimensionaalne vektor, sisaldab ainult ühte tüüpi andmeid – numbrid, stringid, faktorid.
Tip: me saame sageli andmeraami otse maatriksina kasutada kui me viskame sealt välja mitte-numbrilised tulbad. Aga saame ka andmeraame konverteerida maatriksiks, ja tagasi.
fruits <- as.matrix(fruits)
class(fruits)
6.6 Indekseerimine
Igale vektori, listi, andmeraami ja maatriksi elemendile vastab unikaalne postiindeks, mille abil saame just selle elemendi unikaalselt indentifitseerida, välja võtta ja töödelda. Seega on indeksi mõte väga lühikese käsuga välja võtta R-i objektide üksikuid elemente. R-s algab indeksi numeratsioon 1-st (mitte 0-st, nagu näiteks Pythonis).
6.6.1 Vektorid ja nende indeksid on ühedimensionaalsed
my_vector <- 2:5
my_vector
#> [1] 2 3 4 5
my_vector[1] #1. element ehk number 2
#> [1] 2
my_vector[c(1,3)] #1. ja 3. element
#> [1] 2 4
my_vector[-1] #kõik elemendid, v.a. 1. element
#> [1] 3 4 5
my_vector[c(-1, -3)] #kõik elemendid, v.a. 1. ja 3. element
#> [1] 3 5
my_vector[3:5] #elemendid 3, 4 ja 5 (element 5 on määramata, seega NA)
#> [1] 4 5 NA
my_vector[-(3:length(my_vector))] #1. ja 2. element
#> [1] 2 3
6.6.2 Andmeraamid ja maatriksid on kahedimensionaalsed, nagu ka nende indeksid
2D indeksi kuju on [rea_indeks, veeru_indeks].
dat <- tibble(colA = c("a", "b", "c"), colB = c(1, 2, 3))
dat
# üks andmepunkt: 1. rida, 2. veerg
dat[1, 2]
# 1. rida, kõik veerud
dat[1, ]
# 2. veerg, kõik read
dat[, 2]
# kõik read peale 1.
dat[-1, ]
# viskab välja 2. veeru
dat[, -2]
# 2 andmepunkti: 2. rida, 1. ja 2. veerg
dat[2, 1:2]
# 2 andmepunkti: 2. rida, 3. ja 4. veerg
dat[2, c(1, 2)]
#viskab välja 1. ja 2. rea
dat[-c(1, 2), ]
#veerg nimega colB, output on erandina vektor!
dat$colB
Kui me indekseerimisega tibblest veeru ehk vektori välja võtame, on output class: tibble. Kui me teeme sama data frame-st, siis on output class: vector.
Nüüd veidi keerulisemad konstruktsioonid, mis võimaldavad tabeli ühe kindla veeru väärtusi välja tõmmata teise veeru väärtuste järgi filteerides. Püüdke sellest koodist aru saada, et te hiljem ära tunneksite, kui midagi sellist vastu tuleb. Õnneks ei ole teil endil vaja sellist koodi kirjutada, me õpetame teile varsti lihtsama filtri meetodi.
dat <- tibble(colA = c("a", "b", "c"), colB = c(1, 2, 3))
dat$colB[dat$colA != "a" ]
#> [1] 2 3
#jätab sisse kõik vektori colB väärtused,
#kus samas tabeli reas olev colA väärtus ei
#ole "a". output on vektor!
dat$colA[dat$colB > 1]
#> [1] "b" "c"
#jätab sisse kõik vektori colA väärtused,
#kus samas tabeli reas olev colB väärtus >1.
#output on vektor.
6.6.3 Listide indekseerimine
Listi indekseerimisel kasutame kahte sorti nurksulge, “[ ]” ja “[[ ]]”, mis töötavad erinevalt.
Kui listi vaadata nagu objektide vanglat, siis kaksiksulgude [[ ]]
abil on võimalik üksikuid objekte vanglast välja päästa nii, et taastub nende algne kuju ehk class. Seevastu üksiksulud [ ]
tekitavad uue listi, kus on säilinud osad algse listi elemendid, ehk uue vangla vähemate vangidega.
Kaksiksulud “[[ ]]” päästavad listist välja ühe elemendi ja taastavad selle algse class-i (data.frame, vektor, list jms). Üksiksulud “[ ]” võtavad algsest listist välja teie poolt valitud elemendid aga jätavad uue objekti ikka listi kujule.
my_list <- list(a = tibble(colA = c("A", "B"), colB = c(1, 2)), b = c(1, NA, "s"))
## this list has two elements, a data frame called "a" and a character vector called "b".
str(my_list)
#> List of 2
#> $ a:Classes 'tbl_df', 'tbl' and 'data.frame': 2 obs. of 2 variables:
#> ..$ colA: chr [1:2] "A" "B"
#> ..$ colB: num [1:2] 1 2
#> $ b: chr [1:3] "1" NA "s"
Tõmbame listist välja tibble:
my_tibble <- my_list[[1]]
my_tibble
#> # A tibble: 2 x 2
#> colA colB
#> <chr> <dbl>
#> 1 A 1
#> 2 B 2
See ei ole enam list.
Nüüd võtame üksiksuluga listist välja 1. elemendi, mis on tibble, aga output ei ole mitte tibble, vaid ikka list. Seekord ühe elemendiga, mis on tibble.
aa <- my_list[1]
str(aa)
#> List of 1
#> $ a:Classes 'tbl_df', 'tbl' and 'data.frame': 2 obs. of 2 variables:
#> ..$ colA: chr [1:2] "A" "B"
#> ..$ colB: num [1:2] 1 2
aa1 <- my_list$a[2,] #class is df
aa1
#> # A tibble: 1 x 2
#> colA colB
#> <chr> <dbl>
#> 1 B 2
aa3 <- my_list[[1]][1,]
aa3
#> # A tibble: 1 x 2
#> colA colB
#> <chr> <dbl>
#> 1 A 1
Kõigepealt läksime kaksiksulgudega listi taseme võrra sisse ja võtsime välja objekti my_list 1. elemendi, tema algses tibble formaadis, (indeksi 1. dimensioon). Seejärel korjame sealt välja 1. rea, tibble formaati muutmata ja seega üksiksulgudes (indeksi 2. ja 3. dimensioon).
Pane tähele, et [[ ]]
lubab ainult ühe elemendi korraga listist välja päästa.