Tidy Data (suom. siisti, puhdas) on aineiston muoto, jonka tilastotieteilijä Hadley Wickham on tehnyt tunnetuksi. Tidy -muotoa kannattaa useimmiten pitää ensimmäisenä tavoitteena aineiston puhdistuksessa. Kun aineisto on siistissä muodossa, on sitä helpompi lukea, muokata sekä analysoida. Tidy Data pohjautuu kolmeen periaatteeseen:
- Jokainen muuttuja on omassa sarakkeessaan
- Jokainen havainto on omalla rivillään
- Jokainen arvo on omassa solussaan.
Kun näistä periaatteista noudattaa vähintään kahta, niin kolmannen tulisi toteutua itsestään. Näiden perusperiaatteiden lisäksi jokainen yksittäinen aineisto tulisi olla omassa taulukossaan.
Vaikka periaatteet kuulostavat itsestäänselvyyksiltä, niin reaalimaailman aineistot voivat useammallakin tavalla rikkoa näitä periaatteita. Yleisimmät virheet ovat:
- Sarakkeen nimet ovat havaintoja, eivätkä muuttujia
- Useampi muuttuja on samassa sarakkeessa
- Muuttujia on sekä riveillä että sarakkeissa
- Useampi eri aineisto on samassa taulukossa
- Yksi aineisto on useammassa eri taulukossa
Seuraavaksi käymme esimerkkejä sotkuisista aineistoista (untidy data) ja käymme läpi ratkaisuesimerkkejä tapauskohtaisesti.
Otetaan esimerkkiaineisto tilastokeskuksen vapaasti saatavilla olevista tilastoista. Aineistoksi valitaan keskiväkiluku, KOKO MAA, vuodet 2010-2019, miehet ja naiset, sekä ikäryhmät yhteensä. Tilastokeskuksen palvelusta saa kyllä muokattua aineiston puhtaaseen muotoon, mutta harjoitusteknisesti katsomme kuinka sama asia onnistuu R:llä.
Tallennetaan aineisto ja ladataan se R-ympäristöön tarkastelua varten. Huom, ensimmäinen rivi kannattaa jättää pois (skip=1
) jotta miehet ja naiset saadaan sarakkeiden nimiksi:
Nyt aineisto on sopivan sotkuinen harjoittelua varten. Ensinnäkin aineistossa on paljon tyhjiä soluja (NA), sekä sarakkeiden nimet ovat muuttujia, eivätkä niiden nimiä (Miehet ja Naiset). Ensin aineisto pitää putsata niin, että tyhjät solut ja ylimääräiset tekstit poistetaan. Ensimmäinen sarake ei sisällä olennaista informaatiota, joten se voidaan poistaa edellisen artikkelin oppien mukaisesti funktiolla select()
. Funktioon kirjoitetaan ensin miinus (-
), koska valittu sarake tulee poistaa, ja koska kyseinen sarake on järjestykseltään ensimmänen, tulee select()
funktioon ainoaksi argumentiksi -1
(poistetaan ensimmäinen sarake).
library(readxl)
library(tidyverse)
df <- read_excel("026_11s1_2019.xlsx", skip=1)
df %>%
select(-1)
# A tibble: 54 x 3
...2 Miehet Naiset
<chr> <chr> <chr>
1 NA Yhteensä Yhteensä
2 NA Keskiväkiluku Keskiväkiluku
3 2010 2631741.5 2731610
4 2011 2645475 2742796.5
5 2012 2659578 2754392.5
6 2013 2673493 2765479
7 2014 2686113.5 2775398
8 2015 2696676.5 2782854
9 2016 2706908.5 2788394
10 2017 2715729 2792484.5
# … with 44 more rows
Seuraavaksi riveiltä pitää suodattaa ne havainnot pois, joissa ei ole meille tarpeellista informaatiota. Kaksi ensimmäistä riviä, sekä viimeiset 42 riviä aineistossa on tyhjiä soluja (NA). NA-arvojen poistamiseen ja korvaamiseen on olemassa useita vaihtoehtoja esim. paketissa tidyr
. Lunttilappu (Cheat sheet) paketin funktioihin, sekä muuta hyödyllistä tietoa paketista löytyy Tidyversen
sivulta: https://tidyr.tidyverse.org.
Voimme suodattaa muuttujan …2
sisältämät NA-arvot funktiolla drop_na()
, joka poistaa kaikki havainnot (rivit), jotka sisältävät NA-arvoja:
df %>%
select(-1) %>%
drop_na(...2)
# A tibble: 10 x 3
...2 Miehet Naiset
<chr> <chr> <chr>
1 2010 2631741.5 2731610
2 2011 2645475 2742796.5
3 2012 2659578 2754392.5
4 2013 2673493 2765479
5 2014 2686113.5 2775398
6 2015 2696676.5 2782854
7 2016 2706908.5 2788394
8 2017 2715729 2792484.5
9 2018 2721210.5 2794314
10 2019 2725776 2795829.5
Puuttuvat arvot voisi myös jossain tilanteessa korvata viereisten solujen arvoilla (fill()
) tai itse valitsemilla arvoilla (replace_na()
).
df %>%
select(-1) %>%
mutate(...2 = replace_na(...2, 99))
# A tibble: 54 x 3
...2 Miehet Naiset
<chr> <chr> <chr>
1 99 Yhteensä Yhteensä
2 99 Keskiväkiluku Keskiväkiluku
3 2010 2631741.5 2731610
4 2011 2645475 2742796.5
5 2012 2659578 2754392.5
6 2013 2673493 2765479
7 2014 2686113.5 2775398
8 2015 2696676.5 2782854
9 2016 2706908.5 2788394
10 2017 2715729 2792484.5
# … with 44 more rows
Tässä tilanteessa tavoitteena on NA-arvojen pois jättäminen, joten jatketaan aineistolla, josta ...2
sarakkeessa olleet NA-arvoja sisältävät rivit on poistettu. Nyt enää muuttujan ...2
nimi tulee muuttaa vastaamaan sarakkeen sisältöä (Vuosi
). Lisäksi aineisto tallennetaan tässä vaiheessa nimellä df1
:
df1 <- df %>%
select(-1) %>%
drop_na(...2) %>%
rename(Vuosi=...2)
# A tibble: 10 x 3
Vuosi Miehet Naiset
<chr> <chr> <chr>
1 2010 2631741.5 2731610
2 2011 2645475 2742796.5
3 2012 2659578 2754392.5
4 2013 2673493 2765479
5 2014 2686113.5 2775398
6 2015 2696676.5 2782854
7 2016 2706908.5 2788394
8 2017 2715729 2792484.5
9 2018 2721210.5 2794314
10 2019 2725776 2795829.5
Nyt aineisto ei enää sisällä tyhjiä soluja, mutta se on edelleen rakenteeltaan oppikirjaesimerkki virheistä 1 ja 3, sillä arvot Miehet ja Naiset ovat havaintoja (sukupuoli), joten ne tulisi olla omilla riveillään. Lisäksi jokainen muuttuja pitäisi olla omassa sarakkeessa (periaate 1), mutta keskiväkiluku on nyt jaettu kahteen sarakkeeseen (Miehet ja Naiset).
Aineiston kääntäminen (Pivoting)
Tämän vuoksi tutustumme yhteen tärkeimmistä tekniikoista aineiston rakenteen muuttamiseksi, eli sen kääntämiseen (pivoting).
Long vs Wide
Tätä varten on hyvä käsittää kaksi eri aineiston muotoa, joissa aineisto voi esiintyä: wide (leveä) ja long (pitkä). Wide tarkoittaa aineiston muotoa, jossa havaintoarvoja on esitetty sarakkeissa, jolloin sarakkeita on tarpeellista enemmän ja aineisto levenee. Esimerkkinä alla olevassa kuvassa vasemmalla Miehet ja Naiset ovat oikeasti havaintoarvoja, mutta ne ovat taulukossa sarakkeiden niminä). Long puolestaan tarkoittaa muotoa, jossa kaikki havaintoarvot on esitetty riveillä, jolloin rivejä on enemmän ja aineisto pitenee. Esimerkiksi alla olevassa kuvassa oikealla Miehet ja Naiset ovat omalla paikallaan muiden havaintoarvojen kanssa riveillä. Nyt aineistoon on nimetty (vihreällä) sarakkeet Sukupuoli ja Keskiväkiluku sarakkessa olevien havaintoarvojen mukaan.
Long ja wide aineistomuodoista on saatavilla kattavasti lisätietoa mm. open access kirjassa R for Data Science. Tällä hetkellä esimerkkiaineisto on leveässä muodossa, koska väestön keskiluku on nyt levitetty kahteen eri sarakkeeseen yhden sijasta. Seuraavissa esimerkeissä käytettävät funktiot ovat paketista tidyr
.
Long-wide rakenteen ymmärtäminen auttaa koodin kirjoittamista, sillä R:ssä olevat funktiot ongelman korjaamiseksi on nimetty datalle tehtävän toimenpiteen mukaan: pivot_longer()
ja pivot_wider()
. Tässä tilanteessa meidän pitää tehdä kääntö: wide -> long, joten käytämme pivot_longer()
funktiota.
Pivot_longer()
kääntää cols (columns) kohtaan määritellyt sarakkeet pitkään muotoon, eli siirtää miehet ja naiset erikseen omale rivilleen joka vuoden kohdalle. Lisäksi funktioon tulee määritellä uusien sarakkeiden nimet, names_to
kohtaan käännettävät sarakkeet ja uuden, syntyvän sarakkeesen nimi kohtaan values_to
.
df1 %>%
pivot_longer(cols=Miehet:Naiset,
names_to="Sukupuoli",
values_to="Keskiväkiluku")
# A tibble: 20 x 3
Vuosi Sukupuoli Keskiväkiluku
<chr> <chr> <chr>
1 2010 Miehet 2631741.5
2 2010 Naiset 2731610
3 2011 Miehet 2645475
4 2011 Naiset 2742796.5
5 2012 Miehet 2659578
6 2012 Naiset 2754392.5
7 2013 Miehet 2673493
8 2013 Naiset 2765479
9 2014 Miehet 2686113.5
10 2014 Naiset 2775398
11 2015 Miehet 2696676.5
12 2015 Naiset 2782854
13 2016 Miehet 2706908.5
14 2016 Naiset 2788394
15 2017 Miehet 2715729
16 2017 Naiset 2792484.5
17 2018 Miehet 2721210.5
18 2018 Naiset 2794314
19 2019 Miehet 2725776
20 2019 Naiset 2795829.5
Nyt kun tarkastamme Tidy Datan perusperiaatteet, voimme todeta, että jokainen muuttuja on omassa sarakkeessaan, jokainen havainto omalla rivillään ja jokainen on solu on yksi arvo. Nyt kun aineisto on puhdistetussa muodossa, joka on tallennettu objektiksi df2
, pystymme laskemaan kätevästi naisten ja miesten yhteenlasketun keskiväkiluku funktioilla group_by()
ja summarise()
.
df2 %>%
group_by(Vuosi) %>%
summarise(Keskiväkiluku=sum(as.numeric(Keskiväkiluku)))
# A tibble: 10 x 2
Vuosi Keskiväkiluku
<chr> <dbl>
1 2010 5363352.
2 2011 5388272.
3 2012 5413970.
4 2013 5438972
5 2014 5461512.
6 2015 5479530.
7 2016 5495302.
8 2017 5508214.
9 2018 5515524.
10 2019 5521606.
Lisäharjoituksena voimme seuraavaksi kokeilla palauttaa nyt tidyyn muotoon saatu aineisto wide-muotoon käyttämällä funktiota pivot_wider()
.
Pivot_wider()
toimii täysin vastaavasti pivot_longer():n kanssa, mutta tekee käännön vastakkaiseen suuntaan. Nimetään pivot_wider()
:n names_from
käskyyn muuttuja Sukupuoli
, josta saamme nimet uusille sarakkeille, ja kohtaan values_from
muuttuja Keskiväkiluku
, joka nimensä mukaan valitsee uusiin sarakkeisiin arvot Keskiväkiluku-sarakkeesta:
df2 %>%
pivot_wider(names_from=Sukupuoli,
values_from=Keskiväkiluku)
# A tibble: 10 x 3
Vuosi Miehet Naiset
<chr> <chr> <chr>
1 2010 2631741.5 2731610
2 2011 2645475 2742796.5
3 2012 2659578 2754392.5
4 2013 2673493 2765479
5 2014 2686113.5 2775398
6 2015 2696676.5 2782854
7 2016 2706908.5 2788394
8 2017 2715729 2792484.5
9 2018 2721210.5 2794314
10 2019 2725776 2795829.5
Näin saamme aineiston takaisin alkuperäiseen, leveään muotoon.
Tässä käytetyt funktiot toimivat vastaavissa aineiston kääntämisissä, mutta niiden intuitiivinen käyttö vaatii harjoittelua. Varsinkin alussa näitä funktioita käyttäessä kannattaa pitää help-tiedosto auki (esim. ?pivot_wider()
). On myös hyvä ymmärtää, että tietyissä tilanteessa kannattaa tidyssa muodossa oleva aineisto muuttaa long tai wide muotoon analysoinnin ja visualisoinnin helpottamiseksi ja mahdollistamiseksi.
Muuttujien yhdistäminen ja erottaminen (separate ja unite)
Seuraavaksi käsittelemme yhtä yleisimmistä virheistä: Useampi muuttuja on samassa sarakkeessa (virhe 2). Tämä virhe on onneksi useimmiten yksinkertaista korjata. Tyypillinen esimerkki usean arvon sisältävästä muuttujasta on päivämäärät. Vaikka tidyverse sisältää erittäin käytännöllisen paketin päivämäärien muokkaukseen (lubridate
), harjoittelemme muuttujien yhdistämistä ja erottamista päivämäärien avulla.
Esimerkkiaineisto luodaan funktioilla tibble()
ja sample()
, joka luo satunnaisen aineiston (tibble
:n) haluamillamme kriteereillä (10 satunnaista päivää 1.1.2019 ja 1.1.2020 väliltä:
df_pvm <- tibble(pvm=sample(seq(as.Date('2019/01/01'),
as.Date('2020/01/01'),
by="day"), 10))
df_pvm
# A tibble: 10 x 1
pvm
<date>
1 2019-05-30
2 2019-12-16
3 2019-11-05
4 2019-10-02
5 2019-11-13
6 2019-09-26
7 2019-08-22
8 2019-08-12
9 2019-02-09
10 2019-09-13
Nyt meillä on tallennettu aineisto objektiksi df_date
, joka sisältää yhden sarakkeen nimeltä pvm, jossa on kymmenen päivämäärää. Haluaisimme kuitenkin erottaa tästä muuttujasta omiin sarakkeisiinsa päivän, kuukauden, sekä vuoden.
Tätä varten käytämme funktiota separate()
, joka nimensä mukaisesti erottelee halutun muuttujan useammaksi muuttujaksi. Ensimmäiseksi määritellään sarake col
, joka halutaan erotella. Lisäksi määritellään uudet sarakkeet joihin eroteltu muuttuja jaetaan argumentilla into
. Koska muuttujasta tulee useampi uusi muuttuja, tulee ne määritellä vektoriksi (c()
). Lopuksi määritellään, että minkä välimerkin perusteella jako (sep
) tehdään (-
).
df_pvm %>%
separate(col=pvm, into = c("vuosi", "kuukausi", "päivä"), sep="-")
# A tibble: 10 x 3
vuosi kuukausi päivä
<chr> <chr> <chr>
1 2019 05 30
2 2019 12 16
3 2019 11 05
4 2019 10 02
5 2019 11 13
6 2019 09 26
7 2019 08 22
8 2019 08 12
9 2019 02 09
10 2019 09 13
Näin saamme aineiston muuttujan erotettua kolmeen uuteen muuttujaan. Mikäli alkuperäisen muuttujan haluaa säästää, kannattaa se aluksi monistaa (pvm
-> pvm1
) ja erotella monistettu muuttuja:
df_pvm %>%
mutate(pvm1=pvm) %>%
separate(col=pvm1, into = c("vuosi", "kuukausi", "päivä"), sep="-")
# A tibble: 10 x 4
pvm vuosi kuukausi päivä
<date> <chr> <chr> <chr>
1 2019-05-30 2019 05 30
2 2019-12-16 2019 12 16
3 2019-11-05 2019 11 05
4 2019-10-02 2019 10 02
5 2019-11-13 2019 11 13
6 2019-09-26 2019 09 26
7 2019-08-22 2019 08 22
8 2019-08-12 2019 08 12
9 2019-02-09 2019 02 09
10 2019-09-13 2019 09 13
Mikäli aineistossa on taas päinvastainen tilanne, eli yksi muuttuja on useammassa sarakkeessa, voidaan se yhdistää yhdeksi muuttujaksi funktiolla unite()
. Tätä havainnollistamaan voimme hyödyntää edellisessä esimerkissä luotua eroteltua pvm aineistoa, joka tallennetaan uudeksi objeksiksi df_separated
.
df_separated <- df_pvm %>%
separate(col=pvm, into = c("vuosi", "kuukausi", "päivä"), sep="-")
Funktioon unite()
määritellään ensin uuden muuttujan nimi (col), seuraavaksi sarakkeet jotka halutaan yhdistää (vuosi:päivä
), sekä välimerkki joka lisätään yhdistettyjen sarakkeiden väliin (-
).
df_separated %>%
unite(col=pvm, vuosi:päivä, sep="-")
# A tibble: 10 x 1
pvm
<chr>
1 2019-05-30
2 2019-12-16
3 2019-11-05
4 2019-10-02
5 2019-11-13
6 2019-09-26
7 2019-08-22
8 2019-08-12
9 2019-02-09
10 2019-09-13
Näin saamme kolmen eroitellun sarakkeen tiedot takaisin yhteen sarakkeeseen.
Mikäli muuttujasta halutaan poimia vain tietty osa, voidaan se ensin muuttaa tekstiksi (string) ja valita tarvittava osa funktiolla substring()
. Tämä tehdään käyttäen mutate()
:a, jonka avulla luomme uuden muuttujan nimelta vuosi. Funktioon substring()
määritellään ensin muuttuja, josta poiminta tehdään, ja sen jälkeen eroitellaan pilkuin, monennesta merkistä poiminta alkaa ja monenteen se loppuu. Eli tässä tilanteessa pvm muuttujassa ensimmäiset 4 (1-4) merkkiä ovat vuosi, joten funktioon määritellään poiminnaksi 1,4
.
df_pvm %>%
mutate(vuosi=substr(pvm,1,4))
# A tibble: 10 x 2
pvm vuosi
<date> <chr>
1 2019-05-30 2019
2 2019-12-16 2019
3 2019-11-05 2019
4 2019-10-02 2019
5 2019-11-13 2019
6 2019-09-26 2019
7 2019-08-22 2019
8 2019-08-12 2019
9 2019-02-09 2019
10 2019-09-13 2019
Näin saamme poimittua uuteen muuttujaan ainoastaan vuosiluvun. Samalla tavalla voimme poimia myös kuukauden sekä päivän:
df_pvm %>%
mutate(vuosi=substr(pvm,1,4),
kuukausi=substr(pvm,6,7),
päivä=substr(pvm,9,10))
# A tibble: 10 x 4
pvm vuosi kuukausi päivä
<date> <chr> <chr> <chr>
1 2019-05-30 2019 05 30
2 2019-12-16 2019 12 16
3 2019-11-05 2019 11 05
4 2019-10-02 2019 10 02
5 2019-11-13 2019 11 13
6 2019-09-26 2019 09 26
7 2019-08-22 2019 08 22
8 2019-08-12 2019 08 12
9 2019-02-09 2019 02 09
10 2019-09-13 2019 09 13
Aineistojen yhdistäminen (Joining)
Yleisimmistä virheistä on enää käsittelemättä 4. (Useampi eri aineisto on samassa taulukossa) ja 5. (Yksi aineisto on useammassa eri taulukossa). Tämän vuoksi seuraavaksi käsitellään funktioryhmää join
, joka myös kuuluu tidyversen
dplyr
– pakettiin. Kyseisten funktioiden avulla voimme yhdistää useamman aineiston samaan taulukkoon.
Join
-funktioita on useampia, ja oikean funktion valinta riippuu halutusta lopputuloksesta. Funktioon määritellään aina yksi tai useampi sarake, jonka perusteella yhdistäminen tehdään, käyttäen argumenttia by
.
Left_join(x,y, by=z)
sisällyttää aineiston x (koodissa vasemmanpuoleinen) kokonaisuudessaan ja yhdistää y-aineistosta havainnot, joille löytyy vastaava arvo aineiston x sarakkeesta z.
Right_join(x,y, by=z)
sisällyttää aineiston y (koodissa oikeanpuoleinen) kokonaisuudessaan, ja yhdistää aineistosta x havainnot, joille löytyy vastaava arvo aineiston y sarakkeesta z.
Inner_join(x,y, by=z)
sisällyttää molemmista aineistosta kaikki havainnot, joille löytyy vastaava arvo molempien aineistojen sarakkeesta z.
Full_join(x,y, by=z)
sisällyttää molemmista aineistosta kaikki havainnot, ja yhdistää ne, joille löytyy vastaava arvo molempien aineistojen sarakkeesta z.
Semi_join(x,y, by=z)
sisällyttää aineistosta x kaikki havainnot, joille löytyy vastaava arvo aineiston y sarakkeesta z, mutta ei liitä aineiston y arvoja mukaan aineistoon x.
Anti_join(x,y, by=z)
sisällyttää molemmista aineistoista kaikki havainnot, joille ei löydy vastaavaa arvoa molempien aineistojen sarakkeesta z.
Näin ollen, vaihtoehtoja aineistojen yhdistämiseen löytyy useita ja varsinkin alussa tulee olla tarkkana, että käyttää oikeaa vaihtoehtoa. Join
-funktioiden ymmärtämistä varten kannattaa tutustua mm. Data Transformation Cheatsheetiin.
Tutustutaan seuraavaksi join
-funktioihin esimerkkien kautta.
Luodaan kuvitteellinen aineisto, joka kuvaa jonkin taudin tapausmääriä n Suomessa molemmilla sukupuolilla vuosien
df_join <- tibble(Vuosi=as.character(seq(2010, 2019, 1)),
Miehet=sample(1000:2000, 10),
Naiset=sample(1000:2000, 10)) %>%
pivot_longer(cols=Miehet:Naiset,
names_to="Sukupuoli",
values_to="N")
df_join
# A tibble: 20 x 3
Vuosi Sukupuoli N
<chr> <chr> <int>
1 2010 Miehet 1579
2 2010 Naiset 1121
3 2011 Miehet 1240
4 2011 Naiset 1997
5 2012 Miehet 1903
6 2012 Naiset 1386
7 2013 Miehet 1527
8 2013 Naiset 1745
9 2014 Miehet 1815
10 2014 Naiset 1739
11 2015 Miehet 1647
12 2015 Naiset 1424
13 2016 Miehet 1489
14 2016 Naiset 1035
15 2017 Miehet 1811
16 2017 Naiset 1231
17 2018 Miehet 1227
18 2018 Naiset 1575
19 2019 Miehet 1117
20 2019 Naiset 1205
Nyt aineisto on samassa muodossa kuin ensimmäisen esimerkin Tidy-muotoon muutettu aineisto df2
. Aineistoja tarkastellessa huomaamme, että molemmista aineistoista löytyy muuttujat Vuosi ja Sukupuoli, mutta aineistossa df_join
on muuttuja N
, joka kuvaa tautitapausten määrää ja aineistossa df2
on Kokonaisväkiluku
. Koska haluaisimme laskea näistä ilmaantuvuuden molemmille sukupuolille, täytyisi aineistot yhdistää ensin.
Nyt yhdistämisen suhteen on ideaalinen tilanne, koska samat muuttujat löytyvät molemmista aineistoista, ja ne ovat vieläpä saman nimiset. Lisäksi havaintoja on yhtä paljon. Näin ollen, yhdistämisen voisi tehdä useammalla eri funktiolla, mutta käytämme tässä left_join
:ia, jota kannattaa käyttää tilanteessa, jossa alkuperäinen aineisto halutaan pitää koskemattomana ja ainoastaan lisätä siihen muuttujia toisesta aineistosta.
Esimerkissä otetaan aineisto df2
, johon liitetään aineisto df_join
.
df2 %>%
left_join(df_join)
# A tibble: 20 x 4
Vuosi Sukupuoli Keskiväkiluku N
<chr> <chr> <chr> <int>
1 2010 Miehet 2631741.5 1579
2 2010 Naiset 2731610 1121
3 2011 Miehet 2645475 1240
4 2011 Naiset 2742796.5 1997
5 2012 Miehet 2659578 1903
6 2012 Naiset 2754392.5 1386
7 2013 Miehet 2673493 1527
8 2013 Naiset 2765479 1745
9 2014 Miehet 2686113.5 1815
10 2014 Naiset 2775398 1739
11 2015 Miehet 2696676.5 1647
12 2015 Naiset 2782854 1424
13 2016 Miehet 2706908.5 1489
14 2016 Naiset 2788394 1035
15 2017 Miehet 2715729 1811
16 2017 Naiset 2792484.5 1231
17 2018 Miehet 2721210.5 1227
18 2018 Naiset 2794314 1575
19 2019 Miehet 2725776 1117
20 2019 Naiset 2795829.5 1205
Koska muuttujat Vuosi
ja Sukupuoli
ovat identtiset molemmissa aineistossa, funktio left_join()
tunnistaa ne automaattisesti ja tekee yhdistämisen ilman että yhdistävää muuttujaa tarvitsee määritellä. Nyt saamme laskettua ilmaantuvuuden yksinkertaisesti mutate()
:lla. Ensin korjataan muuttujat Keskiväkiluku ja N numeerisiksi funktiolla as.numeric()
, ja sitten lasketaan ilmaantuvuus jakamalla tautitapaukset (N) Keskiväkiluvulla ja kertomalla suhde 100 000:lla, jotta saamme vuosittaisen ilmaantuvuuden 100 000 henkilöä kohden:
df2 %>%
left_join(df_join) %>%
mutate(N = as.numeric(N),
Keskiväkiluku=as.numeric(Keskiväkiluku),
ilmaantuvuus=N/Keskiväkiluku*100000)
# A tibble: 20 x 5
Vuosi Sukupuoli Keskiväkiluku N ilmaantuvuus
<chr> <chr> <dbl> <dbl> <dbl>
1 2010 Miehet 2631742. 1579 60.0
2 2010 Naiset 2731610 1121 41.0
3 2011 Miehet 2645475 1240 46.9
4 2011 Naiset 2742796. 1997 72.8
5 2012 Miehet 2659578 1903 71.6
6 2012 Naiset 2754392. 1386 50.3
7 2013 Miehet 2673493 1527 57.1
8 2013 Naiset 2765479 1745 63.1
9 2014 Miehet 2686114. 1815 67.6
10 2014 Naiset 2775398 1739 62.7
11 2015 Miehet 2696676. 1647 61.1
12 2015 Naiset 2782854 1424 51.2
13 2016 Miehet 2706908. 1489 55.0
14 2016 Naiset 2788394 1035 37.1
15 2017 Miehet 2715729 1811 66.7
16 2017 Naiset 2792484. 1231 44.1
17 2018 Miehet 2721210. 1227 45.1
18 2018 Naiset 2794314 1575 56.4
19 2019 Miehet 2725776 1117 41.0
20 2019 Naiset 2795830. 1205 43.1
Useimmiten tilanne ei kuitenkaan ole näin yksinkertainen, vaan yhdistettävät sarakkeet ovat eri nimisiä ja havaintoja on myös muilta vuosilta kuin halutuilta. Tämän vuoksi luomme uuden kuvitteellisen aineiston, jossa on tiedot vain vuosilta 2000-2015, ja muuttujien nimet on englanniksi.
df_join2 <- tibble(Year=as.character(seq(2005, 2014, 1)),
Miehet=sample(1000:2000, 10),
Naiset=sample(1000:2000, 10)) %>%
pivot_longer(cols=Miehet:Naiset,
names_to="Gender",
values_to="N")
df_join2
# A tibble: 20 x 3
Year Gender N
<chr> <chr> <int>
1 2005 Miehet 1113
2 2005 Naiset 1879
3 2006 Miehet 1521
4 2006 Naiset 1472
5 2007 Miehet 1557
6 2007 Naiset 1975
7 2008 Miehet 1483
8 2008 Naiset 1624
9 2009 Miehet 1512
10 2009 Naiset 1721
11 2010 Miehet 1680
12 2010 Naiset 1686
13 2011 Miehet 1749
14 2011 Naiset 1398
15 2012 Miehet 1011
16 2012 Naiset 1417
17 2013 Miehet 1813
18 2013 Naiset 1486
19 2014 Miehet 1787
20 2014 Naiset 1534
Nyt kun käytämme funktiota left_join()
saamme N ja Keskiväkiluvut niille vuosille jotka ovat ensimmäisessä aineistossa, sekä aineistoon jäävät mukaan vuodet jotka ovat vain ensimmäisessä aineistossa. Nyt täytyy kutenkin määritellä muuttujat, minkä perusteella yhdistäminen tehdään. Koska muuttujien nimet ovat erilaiset, tulee vastaavat muuttujat määritellä vektoriksi.
df2 %>%
left_join(df_join2, by = c("Vuosi" = "Year",
"Sukupuoli" = "Gender"))
# A tibble: 20 x 4
Vuosi Sukupuoli Keskiväkiluku N
<chr> <chr> <chr> <int>
1 2010 Miehet 2631741.5 1680
2 2010 Naiset 2731610 1686
3 2011 Miehet 2645475 1749
4 2011 Naiset 2742796.5 1398
5 2012 Miehet 2659578 1011
6 2012 Naiset 2754392.5 1417
7 2013 Miehet 2673493 1813
8 2013 Naiset 2765479 1486
9 2014 Miehet 2686113.5 1787
10 2014 Naiset 2775398 1534
11 2015 Miehet 2696676.5 NA
12 2015 Naiset 2782854 NA
13 2016 Miehet 2706908.5 NA
14 2016 Naiset 2788394 NA
15 2017 Miehet 2715729 NA
16 2017 Naiset 2792484.5 NA
17 2018 Miehet 2721210.5 NA
18 2018 Naiset 2794314 NA
19 2019 Miehet 2725776 NA
20 2019 Naiset 2795829.5 NA
Nyt aineistossa df2
on mukana kaikki sen alun perin sisältämä tieto, mutta siihen on yhdistetty aineisto df_join2
mukaan niiltä vuosilta, joille molemmista aineistoista löytyi vastaavuus.
Mikäli haluaisimme sisällyttää kaikki df_join2
aineiston tiedot, ja yhdistää aineistosta df2
vain ne vuodet, jotka löytyvät molemmista aineistoista, voimme käyttää funktiota right_join()
.
df2 %>%
right_join(df_join2, by = c("Vuosi" = "Year",
"Sukupuoli" = "Gender"))
# A tibble: 20 x 4
Vuosi Sukupuoli Keskiväkiluku N
<chr> <chr> <chr> <int>
1 2010 Miehet 2631741.5 1680
2 2010 Naiset 2731610 1686
3 2011 Miehet 2645475 1749
4 2011 Naiset 2742796.5 1398
5 2012 Miehet 2659578 1011
6 2012 Naiset 2754392.5 1417
7 2013 Miehet 2673493 1813
8 2013 Naiset 2765479 1486
9 2014 Miehet 2686113.5 1787
10 2014 Naiset 2775398 1534
11 2005 Miehet NA 1113
12 2005 Naiset NA 1879
13 2006 Miehet NA 1521
14 2006 Naiset NA 1472
15 2007 Miehet NA 1557
16 2007 Naiset NA 1975
17 2008 Miehet NA 1483
18 2008 Naiset NA 1624
19 2009 Miehet NA 1512
20 2009 Naiset NA 1721
Tässä tilanteessa olisikin ideaalisinta käyttää funktiota inner_join()
joka sisällyyttää vain havainnot, joille löytyy molemmista aineistoista vastaavat arvot sarakkeista Vuosi/Year ja Sukupuoli/Gender:
df2 %>%
inner_join(df_join2, by = c("Vuosi" = "Year",
"Sukupuoli" = "Gender"))
# A tibble: 10 x 4
Vuosi Sukupuoli Keskiväkiluku N
<chr> <chr> <chr> <int>
1 2010 Miehet 2631741.5 1680
2 2010 Naiset 2731610 1686
3 2011 Miehet 2645475 1749
4 2011 Naiset 2742796.5 1398
5 2012 Miehet 2659578 1011
6 2012 Naiset 2754392.5 1417
7 2013 Miehet 2673493 1813
8 2013 Naiset 2765479 1486
9 2014 Miehet 2686113.5 1787
10 2014 Naiset 2775398 1534
Yhteenvetona
Tässä artikkelissa käsittelimme yleisimpiä tilanteita sotkuisesta aineistosta. Useimmiten sotkuinen aineisto on lukuisilla eri tavoilla sotkuinen, jolloin putsaaminen vaatii useita eri menetelmiä. On tärkeää pitää mielessä Tidy Datan perusperiaatteet, ja aina ennen analysoinnin aloittamista käydä läpi mielessä seuraavat kysymykset:
- Onko aineisto tidy?
- Jos ei, niin mikä siitä tekee untidyn?
- Mitä pitäisi tehdä että aineisto olisi tidy?
- Millä funktioilla muutokset pystyisi tekemään?
Vaikka tässä artikkelissa pyrittiin käymään läpi yleisimmät sotkuisen aineiston virheet, on olemassa lukematon määrä tapoja, joilla aineisto voi olla sotkuinen, joten jokaisen aineiston puhdistaminen on aina erilaista. Tästä johtuen Tidy Dataan ja sen korjaamiseen kannattaa paneutua lisää englanninkielisten artikkeleiden kautta:
Kirjoittanut Ville Ponkilainen, vertaisarvioinut Aleksi Reito ja Mikko Uimonen.