5 Datan muokkaaminen

Aineisto ei tyypillisesti ole valmiiksi oikeassa muodossa. Voi olla että halutaan esimerkiksi käyttää vain jotain osajoukkoa aineistosta. Tällöin tarvitaan komentoja aineiston muokkaamiseksi.

Yleinen käytännön vinkki Aineiston muokkaaminen (data wrangling) on isojen tutkimusaineistojen kohdalla todella työlästä. Tällöin saatetaan joutua yhdistelemään aineistoja useista lähteistä, etsimään virheellisiä arvoja, muokkaamaan tekstimuotoisia (character) muuttujia eri muotoon ym. Mikäli halutaan muokata tekstimuotoisia vektoreita eri muotoon, niin ne kannattaa muuttaa faktoriksi vasta lopuksi, sillä muuttujan ei ole yleensä tarpeellista olla faktorimuodossa aineistoja muokatessa. Faktorit ovat tyypillisesti tarpeen vasta kun aineistoa aletaan todella analysoimaan.

5.1 Uuden muuttujan tai rivin luonti datakehikkoon

Uusi muuttuja voidaan luoda R:ssä joko perustuen aineiston muihin muuttujiin, tai muuttujan arvot voidaan syöttää vektorina aineistoon. Mikäli uusi muuttuja syötetään lukuina R-koodiin, tulee varmistua siitä, että havaintoja on sama määrä kuin aineistossa on rivejä. Muutoin aineisto tulee syötettyä virheellisesti ja tulokset eivät pidä paikkaansa.

Uuden sarakkeen luonti tapahtuu samalla tavalla kuin jo olemassa olevan sarakkeen muokkaaminen eli dollarisymbolilla, jossa dollarin jälkeen annetaan ensin uuden sarakkeen nimi ja tähän sijoitetaan halutut uuden muuttujan arvot.

# evaluate the number of rows and columns
dim(study_data)
## [1] 8 3
# there are 8 rows

# initiate a new variable called weight (imput data) with correct number of rows
study_data$weight <- c(78.2, 65.8, 49.2, 71.2, 58.3, 54.1, 74.2, 62.8)

# calculate a new variable based on existing variables
study_data$height_m <- study_data$height / 100 # height as metres
study_data$BMI <- study_data$weight / (study_data$height_m^2)
study_data
##   ID height gender weight height_m        BMI
## 1  1  189.8   male   78.2    1.898   21.70773
## 2  2  184.0 female   65.8    1.840   19.43526
## 3  3  173.8   male   49.2    1.738   16.28792
## 4  4  175.9   male   71.2    1.759   23.01168
## 5  5  169.0 female   58.3    1.690   20.41245
## 6  6  183.7   male   54.1    1.837   16.03168
## 7  7  181.8   male   74.2    1.818   22.44999
## 8  8   16.9 female   62.8    0.169 2198.80256

5.2 Datakehikon käsittely

Datakehikosta voidaan poimia sarakkeita joko niiden nimien tai niitä vastaavien indeksien perusteella, kuten matriisin tapauksessa. Yksittäisiä sarakkeita voidaan poimia ja muokata myös dollarisymbolin $ kautta.

# Subscripting with variable names
study_data[, c("height", "gender")]
##   height gender
## 1  189.8   male
## 2  184.0 female
## 3  173.8   male
## 4  175.9   male
## 5  169.0 female
## 6  183.7   male
## 7  181.8   male
## 8   16.9 female
# Subscripting with brackets - as matrix (but I do not recommend this style!)
study_data[, 1:2]
##   ID height
## 1  1  189.8
## 2  2  184.0
## 3  3  173.8
## 4  4  175.9
## 5  5  169.0
## 6  6  183.7
## 7  7  181.8
## 8  8   16.9
# Rownames and colnames
colnames(study_data)
## [1] "ID"       "height"   "gender"   "weight"   "height_m" "BMI"
names(study_data)
## [1] "ID"       "height"   "gender"   "weight"   "height_m" "BMI"
# Individual columns can be accessed and added with dollar sign
# Let's say that we find out that the ID number 8 was typed in incorrectly. We can fix the entire height variables as follows
study_data$height <- c(189.8, 184.0, 173.8, 175.9, 169.0, 183.7, NA, 160.9)
study_data
##   ID height gender weight height_m        BMI
## 1  1  189.8   male   78.2    1.898   21.70773
## 2  2  184.0 female   65.8    1.840   19.43526
## 3  3  173.8   male   49.2    1.738   16.28792
## 4  4  175.9   male   71.2    1.759   23.01168
## 5  5  169.0 female   58.3    1.690   20.41245
## 6  6  183.7   male   54.1    1.837   16.03168
## 7  7     NA   male   74.2    1.818   22.44999
## 8  8  160.9 female   62.8    0.169 2198.80256
# It would have been possible to change value of only one cell e.g. like this
study_data$height[8] <- 161.9
study_data
##   ID height gender weight height_m        BMI
## 1  1  189.8   male   78.2    1.898   21.70773
## 2  2  184.0 female   65.8    1.840   19.43526
## 3  3  173.8   male   49.2    1.738   16.28792
## 4  4  175.9   male   71.2    1.759   23.01168
## 5  5  169.0 female   58.3    1.690   20.41245
## 6  6  183.7   male   54.1    1.837   16.03168
## 7  7     NA   male   74.2    1.818   22.44999
## 8  8  161.9 female   62.8    0.169 2198.80256

Uuden rivin lisäys datakehikkoon on hieman monimutkaisempaa kuin uuden rivin lisääminen matriisiin, sillä ensin pitää tehdä uusi datakehikko, jolla on samat sarakkeet kuin alkuperäisellä (samassa järjestyksessä), ja vasta sitten liittää se komennolla rbind. Käyttäjän tulee myös huolehtia siitä, että sarakkeet ovat samaa tyyppiä kuin alkuperäisessä datakehikossa.

new_row <- data.frame(ID = 11, height = 182, gender = "male", 
                      weight = 81.2, height_m = 1.82, BMI = 81.2 / 1.82^2)
rbind(study_data, new_row)
##   ID height gender weight height_m        BMI
## 1  1  189.8   male   78.2    1.898   21.70773
## 2  2  184.0 female   65.8    1.840   19.43526
## 3  3  173.8   male   49.2    1.738   16.28792
## 4  4  175.9   male   71.2    1.759   23.01168
## 5  5  169.0 female   58.3    1.690   20.41245
## 6  6  183.7   male   54.1    1.837   16.03168
## 7  7     NA   male   74.2    1.818   22.44999
## 8  8  161.9 female   62.8    0.169 2198.80256
## 9 11  182.0   male   81.2    1.820   24.51395

5.3 Osajoukkojen valinta

Aineistosta voi poimia osajoukon hakasulkujen avulla indeksoimalla. Osajoukon poimintaan tarvitaan usein vertailuoperattoreita, ja jos kriteerejä on useita, niin tarvitaan myös useita loogisia operaattoreita. Tarkemmin operaattoreita käsitellään luvussa Loogiset operaattorit. Voit käyttää kyseisen osion taulukkoa apuna jo tässä osiossa. Osajoukkoja voidaan poimia myös suoraan antamalla halutut indeksit esimerkiksi indeksivektorin avulla.

# Filter only females
study_data[study_data$gender == "female", ]
##   ID height gender weight height_m        BMI
## 2  2  184.0 female   65.8    1.840   19.43526
## 5  5  169.0 female   58.3    1.690   20.41245
## 8  8  161.9 female   62.8    0.169 2198.80256
# Filter individuals whose height is less than or equal to 175
study_data[study_data$height <= 175, ]
##    ID height gender weight height_m        BMI
## 3   3  173.8   male   49.2    1.738   16.28792
## 5   5  169.0 female   58.3    1.690   20.41245
## NA NA     NA   <NA>     NA       NA         NA
## 8   8  161.9 female   62.8    0.169 2198.80256
# Filter individuals whose height is not missing and is less than or equal to 175
study_data[!is.na(study_data$height) & study_data$height <= 175, ]
##   ID height gender weight height_m        BMI
## 3  3  173.8   male   49.2    1.738   16.28792
## 5  5  169.0 female   58.3    1.690   20.41245
## 8  8  161.9 female   62.8    0.169 2198.80256
# Use multiple filter criteria
study_data[study_data$height <= 175 & study_data$gender == "female", ]
##   ID height gender weight height_m        BMI
## 5  5  169.0 female   58.3    1.690   20.41245
## 8  8  161.9 female   62.8    0.169 2198.80256
# Select individuals (rows) 1,3, and 7 directly with a vector of indices
ind <- c(1, 3, 7)
study_data[ind,]
##   ID height gender weight height_m      BMI
## 1  1  189.8   male   78.2    1.898 21.70773
## 3  3  173.8   male   49.2    1.738 16.28792
## 7  7     NA   male   74.2    1.818 22.44999

5.4 Datakehikon ja vektorin järjestäminen

Yhden vektorin arvot voidaan asettaa nousevaan tai laskevaan järjestykseen funktiolla sort. Funktiota voidaan soveltaa niin numeerisiin kuin merkkitietoa sisältäviin vektoreihinkin. Oletusarvoisesti järjestys on nouseva, eli numeeriset arvot järjestetään pienimmästä suurimpaan ja merkkitieto aakkosjärjestykseen. Järjestyksen voi kääntää laskevaksi argumentilla decreasing = TRUE.

nums <- c(3, 1, 7, 8, 5, 4)
chars <- c("ab", "ca", "ac", "bb", "ba", "cb")

# Ascending order
sort(nums)
## [1] 1 3 4 5 7 8
sort(chars)
## [1] "ab" "ac" "ba" "bb" "ca" "cb"
# Descending order
sort(nums, decreasing = TRUE)
## [1] 8 7 5 4 3 1
sort(chars, decreasing = TRUE)
## [1] "cb" "ca" "bb" "ba" "ac" "ab"

Jossain tilanteissa on haluttavaa järjestää datakehikon rivit jonkin muuttujan tai muuttujien suhteen. Tähän tarkoitukseen voi käyttää funktiota order, joka palauttaa yhden tai useamman argumentin alkioiden järjestysluvut (rank). Seuraavassa esimerkissä aineiston rivit järjestetään koehenkilöiden pituuden suhteen nouseevaan suuruusjärjestykseen.

study_data[order(study_data$height),]
##   ID height gender weight height_m        BMI
## 8  8  161.9 female   62.8    0.169 2198.80256
## 5  5  169.0 female   58.3    1.690   20.41245
## 3  3  173.8   male   49.2    1.738   16.28792
## 4  4  175.9   male   71.2    1.759   23.01168
## 6  6  183.7   male   54.1    1.837   16.03168
## 2  2  184.0 female   65.8    1.840   19.43526
## 1  1  189.8   male   78.2    1.898   21.70773
## 7  7     NA   male   74.2    1.818   22.44999

Järjestäminen voidaan tehdä usean muuttujan suhteen, esimerkiksi pituuden ja painon. Tämä tarkoittaa nousevassa järjestyksessä sitä, että jos kahdella koehenkilöllä on täsmälleen sama pituus, valitaan heidän keskinäinen järjestyksensä painon perusteella.

5.5 Faktorit

R:n numeeriset vektorit ovat lähtökohtaisesti jatkuva-asteikollisia. Olet ehkä ihmetellytkin, miten kategorinen muuttuja määritellään. Kategorista muuttujaa sanotaan R:ssä faktoriksi. Numeerisen tai tekstimuotoisen muuttujan tai vektorin voi muuttaa faktori-muotoiseksi muuttujaksi factor -funktiolla.

# Let's change gender from character string to a factor and rename it as fgender
study_data$fgender <- factor(study_data$gender)

# Let's now compare the printing of gender and fgender
study_data$gender
## [1] "male"   "female" "male"   "male"   "female" "male"   "male"   "female"
study_data$fgender
## [1] male   female male   male   female male   male   female
## Levels: female male

Huomataan, että faktori tulostaa faktorin tasot eli kaikkien mahdollisten luokkien nimet faktorin perässä: Levels: female male.

Usein vastaan tulee myös tilanne, jossa faktorin eri tasoja vastaavat kokonaislukuarvot, kuten tässä esimerkissä luvut 1, 2 ja 3. Tällaisessa tilanteessa faktorin tasojen merkitys on usein annettu jossain dokumenttitiedostossa. Tällöin faktorin tasot ja niiden kuvaukset (labels) tulee määrittää käsin.

# Create a data for this example
wall_dat <- data.frame(building_ID = c(1, 2, 3, 4, 5, 6), building_material = c(1, 1, 2, 2, 3, 3))

# Name is 'building_material' very long, I want to rename it
names(wall_dat) <- c("building_ID", "build_mat")

# We know from some kind of documentation that 1 stands for "wood", 2 is "steel" and 3 is "brick".
wall_dat$fbuild_mat <- factor(wall_dat$build_mat,levels = c(1, 2, 3), labels = c("wood", "steel", "brick"))

str(wall_dat)
## 'data.frame':    6 obs. of  3 variables:
##  $ building_ID: num  1 2 3 4 5 6
##  $ build_mat  : num  1 1 2 2 3 3
##  $ fbuild_mat : Factor w/ 3 levels "wood","steel",..: 1 1 2 2 3 3

5.6 Extra: Lääketutkimusesimerkki

R:ssä on aiemmin nähtyjen numeric, character ja logical-vektorien lisäksi muitakin vektoriluokkia, tärkeimpänä näistä factor. Factor-vektoreihin tallennetaan kategorisia muuttujia, kuten tutkimuksessa määrättyjä ryhmiä, aikapisteitä tms. Luodaan esimerkiksi factor-vektori, jossa on kuvitteellisen lääketutkimuksen osallistujien ryhmätiedot:

groups <- as.factor(c("drug1", "drug2", "control", "drug1", "control",
                      "drug2", "drug2", "control", "control", "drug1"))
groups
##  [1] drug1   drug2   control drug1   control drug2   drug2   control control
## [10] drug1  
## Levels: control drug1 drug2

Factoreita voi luoda muista vektoreista funktioilla factor tai as.factor. as.factor muuntaa vektorin automaattisesti ja nopeasti factoriksi, ja säilyttää myös jo valmiiksi factor-luokan vektorien tasojen järjestyksen (tästä lisää pian).

Kuten tulosteesta nähdään, factor-vektorin tulostus tulostaa factorin alkiot (HUOM: ei lainausmerkkejä) sekä factorin tasot. Factorit ovat pinnan alla kokonaisluku- eli integer-vektoreita, joissa on päällä “kerros”, joka määrittää factorin tasot. Edellä nähty vektori groups näyttää siis tältä:

Factorien tasoille annetaan siis lukuarvot ykkösestä eteenpäin. Oletuksena ensimmäinen taso eli taso 1 on aakkosissa ensimmäinen arvo, tai pienin lukuarvo jos factori tehdään numeerisista muuttujista. Lukuarvot saa näkyville muuntamalla factorin numeeriseksi vektoriksi:

as.numeric(groups)
##  [1] 2 3 1 2 1 3 3 1 1 2

Tasojen järjestyksen voi myös päättää itse. Tämä on tärkeää, sillä kuten pian nähdään, factorin ensimmäinen taso on monissa tilastollisissa testeissä ns. referenssitaso, johon muita tasoja verrataan. Usein esiintyvä tapaus ovat tutkimukset, joissa on ryhmät nimeltä case ja control. Koska case on aakkosissa ennen controllia, R käyttää oletuksen case-ryhmää referenssitasona, ja testaa miten control-ryhmä poikkeaa tästä tasosta, vaikka haluaisimme päinvastaisen tuloksen. Tasot voi itse määrittää näin:

study_groups <- factor(c("case", "control", "control", "case", "case"),
                       levels = c("control", "case"))
study_groups
## [1] case    control control case    case   
## Levels: control case

Nyt tasot ovat oikeassa järjestyksessä!

Kuten aiemmin mainittiin, factoreita voi tehdä myös numeerisista vektoreista. HUOM: muista, että as.numeric() palauttaa factorin kokonaislukuarvot, ei alkuperäisiä lukuja. Alkuperäiset luvut saa käyttämällä ensin as.character-funktiota, joka muuttaa factorin tasot merkkijonovektoriksi.

time_points <- as.factor(c(0, 0, 1, 1, 5, 5, 1, 0, 5))
time_points
## [1] 0 0 1 1 5 5 1 0 5
## Levels: 0 1 5
# Probably not what you expect
as.numeric(time_points)
## [1] 1 1 2 2 3 3 2 1 3
# First to character, then to numeric
as.numeric(as.character(time_points))
## [1] 0 0 1 1 5 5 1 0 5