2 Muuttujat ja vektorit
2.1 Muuttujat
Muuttujat (variables) ovat yksi tärkeimmistä ohjelmointikielien rakenteista. Muuttujien tehtävä on säilyttää tietoa ja tuloksia edellisistä laskutoimituksista. Alla on yksinkertainen esimerkki muuttujien käytöstä R:ssä.
<- 3
x <- 5
y <- x + y
z
z
## [1] 8
Edellisessä esimerkissä sijoitetaan (assign) eli tallennetaan muuttujaan x
arvo 3 ja muuttujaan y
arvo 4. Sen jälkeen muuttujien x
ja y
summa sijoitetaan muuttujaan z
, jonka jälkeen tulostetaan muuttujan z
arvo. <-
on R:n sijoitusoperaattori (myös yhtä kuin-merkki =
toimii melkein aina, mutta <-
merkin käyttöä suositellaan vahvasti).
Mutta miten muuttujan z
arvo tulostui konsoliin, vaikka koodissa ei käytetty funktiota print
? R:n erikoisominaisuus moneen muuhun ohjelmointikieleen verrattuna on se, että print-käskyä ei tarvitse aina kirjoittaa, vaan pelkästään muuttujan (tai laskutoimituksen) kirjoittaminen tulostaa arvon konsoliin, kuten alla oleva koodi havainnollistaa:
zprint(z)
+ y
x print(x + y)
3 + 5
print(3 + 5)
Muuttujiin voi sijoittaa muutakin kuin yksittäisiä lukuja, kuten merkkijonoja (strings), vektoreita, tai paljon monimutkaisempiakin rakenteita.
<- "Hello world"
x x
## [1] "Hello world"
2.3 Vektorit
Nyt kun muuttujat ovat tuttuja, voimme siirtyä käsittelemään vektoreita (vector). R:n vektorit ovat yksinkertaisia järjestettyjä tietorakenteita, jotka koostuvat alkioista (elements), esimerkiksi desimaaliluvuista. Alla oleva esimerkki sijoittaa muuttujaan x
vektorin, joka sisältää 5 lukua.
<- c(1, 2, 7.4, 15, 0.2)
x x
## [1] 1.0 2.0 7.4 15.0 0.2
Yksinkertaisin tapa tehdä vektori R:ssä on käyttää c
-funktiota, joka luo vektorin, jossa on sille annetut arvot annetussa järjestyksessä. Monet R-kielen komennot ja funktiot luovat vektoreita, alla muutama esimerkki:
# Regular sequences
seq(1, 10)
## [1] 1 2 3 4 5 6 7 8 9 10
seq(0, 1, by = 0.2)
## [1] 0.0 0.2 0.4 0.6 0.8 1.0
seq_len(6)
## [1] 1 2 3 4 5 6
3:9
## [1] 3 4 5 6 7 8 9
# Repeat values
rep(1, 5)
## [1] 1 1 1 1 1
rep(c(1, 2), 3) # Repeat vector c(1, 2) 3 times
## [1] 1 2 1 2 1 2
rep(c(1, 2, 3), 3) # Repeat all values in vector c(1, 2, 3) 3 times
## [1] 1 2 3 1 2 3 1 2 3
2.3.1 Vektorilaskentaa
Vektoreilla laskeminen on usein hyvin intuitiivista (lisää vaaranpaikoista myöhemmin). Kun vektoriin kohdistetaan laskutoimintoja, sama operaatio tehdään kaikille vektorin alkioille (engl. vectorization).
<- c(1, 2, 3, 6, 10)
x * 2 x
## [1] 2 4 6 12 20
/ 2 + 1 x
## [1] 1.5 2.0 2.5 4.0 6.0
Entä jos vektoreita lisää toisiinsa, tai kertoo keskenään? Jos vektorit ovat samanpituisia, operaatio toteutetaan alkio kerrallaan. Jos vektorit ovat eripituisia, R yrittää kierrättää (recycle) lyhyempää vektoria niin, että siitä tulee yhtä pitkä kuin pidempi vektori. Tämän jälkeen operaatio suoritetaan alkio kerrallaan (itse asiassa näin tapahtui myös aiemmissa esimerkeissä, kun vektori kerrottiin yksittäisellä luvulla. R:ssä yksittäiset luvut ovat vektoreita, joiden pituus on 1). Jos kierrätys ei onnistu, eli pidemmän vektorin pituus ei ole jaollinen lyhyemmän pituudella, R antaa virheilmoituksen.
<- c(1, 2, 3, 6, 10, 2)
x <- c(1, 1, 1, 3, 3, 3) # or rep(c(1, 3), each = 3)
y <- c(2, 4)
z
+ y # Element-wise sum x
## [1] 2 3 4 9 13 5
* y # Element-wise multipliocation x
## [1] 1 2 3 18 30 6
+ z x
## [1] 3 6 5 10 12 6
R:ssä on myös paljon funktioita, joilla voi laskea vektoreista erilaisia tunnuslukuja, kuten keskiarvon, mediaanin, keskihajonnan jne.
<- c(1, 2, 3, 6, 10, 2)
x # Sample mean (average)
mean(x)
## [1] 4
# Standard deviation
sd(x)
## [1] 3.405877
# Sum
sum(x)
## [1] 24
2.3.2 Ei-numeeriset vektorit
2.3.2.1 Merkkijonovektorit
Vektorien ei ole pakko sisältää lukuja. Vektorit voivat sisältää esimerkiksi merkkijonoja, kuten alussa nähty “Hello, world!”. Merkkijonotyypin nimi R:ssä on character
.
<- c("Hello, world!", "R is the best", "I", "like", "programming", "!")
x x
## [1] "Hello, world!" "R is the best" "I" "like"
## [5] "programming" "!"
Merkkijonovektoreiden muokkausta varten on omia funktiota, tärkeimpinä paste
ja paste0
, jotka yhdistävät merkkijonoja toisiinsa. Myös numeerisia vektoreita voi antaa näille funktioille, ja ne muutetaan merkkijonoiksi.
<- c("Diana", "Peter", "Bruce")
first_names <- c("Prince", "Parker", "Wayne")
last_names paste(first_names, last_names)
## [1] "Diana Prince" "Peter Parker" "Bruce Wayne"
<- paste0("Student_", 1:5) students
2.3.2.2 Loogiset vektorit
Kolmas yleinen vektorityyppi on looginen vektori, joka sisältää arvoja TRUE
eli tosi tai FALSE
eli epätosi. Loogisia vektoreita käytetään yleensä joko merkitsemään binäärisiä muuttuja (esimerkiksi paastosiko koehenkilö ennen näytteenottoa) tai vektorien ja matriisien indeksoinnissa (tästä lisää pian). Tällöin loogisia vektoreita syntyy erilaisten loogisten operaattorien avulla:
<- c(1, 2, 3, 6, 10, 2)
x
> 3 # Is the element of x greater than 3? x
## [1] FALSE FALSE FALSE TRUE TRUE FALSE
>= 3 # Greater or equal to three= x
## [1] FALSE FALSE TRUE TRUE TRUE FALSE
== 6 # Equal to 6? x
## [1] FALSE FALSE FALSE TRUE FALSE FALSE
!= 2 # Not equal to 2? x
## [1] TRUE FALSE TRUE TRUE TRUE FALSE
2.3.2.3 Loogiset vektorit ja matematiikka
Jos loogiselle vektorille tekee operaation, joka odottaa numeerista vektoria, R muuttaa automaattisesti arvot TRUE
ykkösiksi ja arvot FALSE
nolliksi. Tämä on erityisen hyödyllistä käytettäessä funktiota sum
. Tällä tavalla saadaan helposti tietää esim. kuinka moni vektorin alkio täyttää tietyn ehdon:
<- c(1, 3, 5, 2, 19)
x <- x > 3
above_3
# Logical vector automatically converted to numeric
+ 1 x
## [1] 2 4 6 3 20
# how many elements of x are smaller than 10?
sum(x < 10)
## [1] 4
2.3.3 Vektorien indeksointi ja osajoukon valinta
Usein vektorista halutaan poimia vain tietyt arvot, esimerkiksi vain ensimmäiset 5 arvoa, tai vain arvot, jotka täyttävät tietyt ehdot. R:ssä vektorin indeksointiin käytetään hakasulkeita []
. Yleisin indeksointitapa on antaa hakasulkeiden sisään vektori kokonaislukuja, jotka vastaavat niiden alkioiden järjestyslukuja, jotka vektorista halutaan poimia (HUOM R:ssä indeksointi alkaa ykkösestä, ei nollasta!). Toinen vaihtoehto on käyttää loogista vektoria, jolloin vektorista poimitaan ne alkiot, joiden kohdalla loogisen vektorin arvo on TRUE. Tämä on yksinkertaisempaa kuin miltä se kuulostaa:
<- c(1, 2, 3, 6, 10, 2)
x
# Picking exact elements
2:3] # Second and third values x[
## [1] 2 3
c(4, 5, 1)] # Note that the order does not have to be increasing x[
## [1] 6 10 1
# Using logical vector as condition
> 3] x[x
## [1] 6 10
# The condition can be based on another vector
<- c("Yoda", "C-3PO", "Rey", "R2-D2", "Anakin", "Baby Yoda")
characters <- c(66, 175, 170, 109, 183, 40.5)
heights # Only characters shorter than 120 cm
< 120] characters[heights
## [1] "Yoda" "R2-D2" "Baby Yoda"
2.3.4 Puuttuvat arvot
Monessa tutkimusprojektissa törmätään syystä tai toisesta jossain vaiheessa puuttuviin arvoihin. Hyvä esimerkki ovat seurantatutkimukset, jossa usein seurannan lopussa on jäljellä vähemmän koehenkilöitä kuin alussa.
Puuttuvia arvoja merkitään R:ssä symbolilla NA
(not available). Puuttuvat arvot noudattavat yksinkertaista logiikkaa: mikä tahansa operaatio NA
:lle antaa tulokseksi NA
. Funktiot, jotka operoivat vektoreilla, kuten sum
tai mean
voidaan erikseen asettaa poistamaan puuttuvat arvot ennen summan, keskiarvon tms. laskemista.
<- c(1, 2, NA, 4, NA, 6)
missing <- seq(1, 6)
full
# Addition with NA returns NA
+ full missing
## [1] 2 4 NA 8 NA 12
# Sum of vector with NAs returns NA
sum(missing)
## [1] NA
# Removing NAs before summation
sum(missing, na.rm = TRUE)
## [1] 13
HUOM! Funktio is.na
tarkistaa, onko jokin arvo puuttuva. Perinteinen yhtäsuuruuden testaaminen ei siis toimi. Funktio complete.cases
muistuttaa is.na
funktiota, mutta sitä voidaan käyttää myös kokonaisille aineistoille, jolloin se palauttaa totuusarvon TRUE
niiden rivien kohdalla, jotka eivät sisällä lainkaan puuttuvaa tietoa yhdessäkään muuttujassa.
# Just returns NA
NA == NA
## [1] NA
# Returns a logical value as expected
is.na(NA)
## [1] TRUE
is.na(1)
## [1] FALSE
# is.na operates element-wise on a vector
<- c(1, 2, NA, 4, NA, 6)
missing is.na(missing)
## [1] FALSE FALSE TRUE FALSE TRUE FALSE
# complete.cases gives the data elements which do not have missing data.
# It can be used with data frames also.
complete.cases(missing)
## [1] TRUE TRUE FALSE TRUE FALSE TRUE
2.4 Extra: Alkeistietotyypit ja erikoisarvot
Tässä lisätieto-osiossa käsitellään asioita, joita et välttämättä tarvitse kurssista suoriutuaksesi. Mikäli käytät R:ää enemmän, niin vastaan tulee enemmin tai myöhemmin ongelmia, joissa tarvitsee näitä taitoja. Voit palata näihin myöhemmin koska tahansa, kun haluat syventää ymmärrystäsi R:stä.
R on rakennettu sisäisesti siten, että vektorin kukin elementti on jotain alkeistietotyyppiä. R:ssä on valmiina kuusi alkeistietotyyppiä:
- looginen (
logical
) - kokonaisluku (
integer
) - numeerinen (eli reaaliluku,
numeric
) - kompleksiluku (
complex
) - merkkijono (
character
) - bitti (
raw
)
Näistä tarpeellisimpia ovat numeerinen, looginen, ja merkkijono. Kompleksilukua tarvitsee vain joissain erityistapauksissa ja kokonaisluvut ovat nykyään lähes aina tallennettu numeerisina. Bittejä, eli raw
-tyypin vektoreita käytetään harvoin.
# Integers are usually stored as reals
<- c(1, 2, 3)
x class(x)
## [1] "numeric"
# You can create integers by adding capital L behind the number
<- c(1L, 2L, 3L)
x_int class(x_int)
## [1] "integer"
# Character strings (or just strings)
<- c("I", "have", "a", "cat.")
x_char class(x_char)
## [1] "character"
# Complex numbers
<- c(0i, 2 + 1i, 1 - 3i)
x_comp class(x_comp)
## [1] "complex"
# Logical vector
<- c(TRUE, FALSE)
x_logi class(x_logi)
## [1] "logical"
# Raw vector
<- as.raw(c(0, 1, 2))
x_raw class(x_raw)
## [1] "raw"
Joskus vektorin tiedot ovat väärässä muodossa, esim. merkkijonoina, mutta niitä haluttaisiin käsitellä numeroarvoina. Näihin operaatioihin on omat funktionsa. Tällöin voi kuitenkin tulla ongelmia, jos muutettava vektori ei ole helposti muutettavissa haluttuun muotoon.
# Chacter string to numeric
class(as.numeric(x_char))
## Warning: NAs introduced by coercion
## [1] "numeric"
# If character contains only values it is easy
<- c("0", "5", "6.5")
x_char2 as.numeric(x_char2)
## [1] 0.0 5.0 6.5
# Integer to numeric
<- as.numeric(x_int)
x_int_to_num x_int_to_num
## [1] 1 2 3
class(x_int_to_num)
## [1] "numeric"
# Numeric to integer
<- as.integer(x)
x_num_to_int x_num_to_int
## [1] 1 2 3
class(x_num_to_int)
## [1] "integer"
Vielä pari lisähuomiota puuttuvista arvoista (näitä ei tarvita usein) koskien niiden tietotyyppejä. Puuttuvalla arvolla on myös alkeistietotyyppi. Mikäli NA-arvon tietotyyppi tulee määritellä, niin sen voi tehdä seuraavasti. Jos luodaan muuttuja tai vektori, jossa on vain yksi arvo, joka on NA, niin se oletusarvoisesti looginen.
# Specify a numeric NA value
NA_real_
## [1] NA
# Specify a complex number NA value
NA_complex_
## [1] NA
# Specify a integer number NA value
NA_integer_
## [1] NA
# Specify a character NA value
NA_character_
## [1] NA
# NA gives a logical type when evaluated alone
class(NA)
## [1] "logical"
# NA_real_ is numeric
class(NA_real_)
## [1] "numeric"
2.4.1 Ääretön ja miinus ääretön
R:ssä on myös ääretön ja miinus ääretön. Ne on toteutettu samaan tapaan kuin puuttuva arvo, mutta niiden tarkasteluun on omat funktionsa. Ääretön ja miinus ääretön arvot syntyvät esimerkiksi silloin, kun nollasta poikkeavia lukuja jaetaan nollalla.
# You can type in infinity or minus infinity if needed
<- c(1, 2, Inf, 5, -Inf)
x # Use is.finite to determine if numbers are finite or not
is.finite(x)
## [1] TRUE TRUE FALSE TRUE FALSE
# Division by zero makes Inf or -Inf (unless 0/0)
<- c(1, 2, -3) / c(3, 0, 0)
x_div_zero x_div_zero
## [1] 0.3333333 Inf -Inf
is.finite(x_div_zero)
## [1] TRUE FALSE FALSE
2.4.2 Ei-numero
Mikäli R:ssä sattuu tekemään jonkin matemaattisen toimenpiteen, joka ei ole sallittu, esimerkiksi nollan jaon nollalla tai luvun -1 logaritmin, niin tämä tuottaa R:ssä tietotyypin, joka on NaN
(lyhenne sanoista Not a Number). Mikäli NaN
-arvoa tutkii funktiolla is.finite
tai is.na
, niin huomaa että NaN
ei ole äärellinen ja NaN
tulkitaan NA
:ksi.
<- 0/0
x_div_zero_by_zero
# Tests for NaN
is.nan(x_div_zero_by_zero)
## [1] TRUE
# Tests if it is finite
is.finite(x_div_zero_by_zero)
## [1] FALSE
# Tests if it is NA (missing)
is.na(x_div_zero_by_zero)
## [1] TRUE
2.2 Kommentit
Myöhemmin vastaan tulevassa koodissa käytetään kommentteja. Kommentit ovat koodin oheen kirjoitettua tekstiä, joka ei ole ohjelmointikieltä, ja joka ohitetaan koodia ajettaessa. Kommenttien tarkoitus on kuvailla koodin toimintaa. Oman koodin kommentointia on hyvä harjoitella alusta lähtien, vaikka ensimmäisten tehtävien koodi onkin hyvin yksinkertaista. R:ssä kommentit merkataan
#
-symbolilla. Edellinen esimerkki kommentoituna voisi näyttää jotakuinkin tältä: