R-KORNERI

R-vaikeudet

Terveisiä taas Ärräkornerista! Edellisellä kerralla perustelimme, miksi jokaisen ekonomistiksi pyrkivän opiskelijan tulisi laajentaa osaamistaan Excelistä muihin analytiikkatyökaluihin, erityisesti R:än. Valtaosa taloustieteen opiskelijoista varmasti tiedostaa R:n olevan hyödyllinen työväline, mutta kuitenkin monen into R:n opetteluun lopahtaa aivan ensimetreillä. Tähän suurin yksittäinen syy lienee oppimiskäyrän jyrkkyys; yliopiston järjestämällä R:n peruskurssilla ehditään keskittymään ohjelmoinnin perusteiden opetteluun harmillisen pintapuolisesti. Ärräkornerin kirjoitustiimi suositteleekin taloustieteen opintojen täydentämistä Ohjelmoinnin perusteet -kurssilla, joka antaa hyvät perusvalmiudet kaikkeen ohjelmointiin. Ensihätään tarjoamme lukijoille lyhyen listan perusnikseistä, joita jokaisen R:ää käyttävän tulisi noudattaa. Tämänkertaisen Ärräkornerin teemana on aloittelevan koodarin muistilista


1. Ajattele kirjoittamaasi koodia ”to do” -listana tietokoneelle

Ohjelmoinnin syvin olemus on selkeiden ohjeiden kirjoittaminen tietokoneelle kielellä, jota kone ymmärtää. Tietokone käsittelee sille annettua ohjelistaa absoluuttisen orjallisesti: aloittaen ohjelmakoodin ylhäältä ja jatkaen listan lukemista rivi riviltä alaspäin siirtyen, ellei se saa käskyä toimia toisin. Ohjelmoijan tulisi aina olla selvillä siitä, mitä hän koneelta haluaa. Jokainen koodirivi on tehtävä, jossa koneelle ohjeistetaan tarvittavalla tarkkuudella, mitä sen halutaan tekevän. Mikäli käskyissä on virheitä, ohjelma ei toimi.

2.  Kirjoita siistiä koodia

Niin kuin mihin tahansa kieleen, myös koodin kirjoittamiseen pätee omat ”kielioppisääntönsä”. Näistä ihan ensimmäinen on koodin jäsentäminen eli kommenttien kirjoittaminen. R:ssä kommentin voi kirjoittaa laittamalla #-merkin lauseen eteen. Hyvässä koodissa kommentoidaan jokaista erillistä vaihetta, mutta tarkoitus ei kuitenkaan ole kommentoida jokaista riviä erikseen. Kommentointia voi verrata kappalejaon käyttöön kirjoitetussa tekstissä. Tarkoituksena on selventää, mitä alla olevassa kappaleessa tehdään.

Kommentit voi kirjoittaa ikään kuin työn vaiheita jollekin ulkopuoliselle selostaen, vaikka kirjoittaiskin koodia vain omaan käyttöön. Itsestään selvien asioiden selittäminen voi tuntua turhauttavalta, mutta on välttämätöntä ymmärrettävän koodin kirjoittamiseksi. Koodin jäsentäminen auttaa myös ymmärtämään, mitä on oikeastaan tekemässä.

Toinen keskeinen tekijä ymmärrettävän koodin kirjoittamisessa on muuttujien nimeäminen. Muuttujat tulee nimetä järkevästi. Hyvä nimi kertoo lukijalleen, mitä kyseinen muuttuja sisältää. Koodi saattaa näyttää toki ytimekkäältä, jos muuttujan nimeksi antaa vain yksittäisen kirjaimen, mutta siihen tämän menetelmän hyvät puolet jäävätkin. Vähänkään pidempää koodia kirjoittaessa on tärkeä muistaa, mitä mihinkin muuttujaan on tallennettu. Hyvin nimetyt muuttujat helpottavat huomattavasti keskeneräisen tai vanhan työn pariin palaamista, ja ovat lähes välttämättömiä, jos ulkopuolisen tulisi ymmärtää kirjoittamasi koodia.  Toisaalta sopivan nimen ei myöskään tulisi olla liian pitkä. Turhan pitkät nimet tuottavat helposti varsin pitkiä koodirivejä ja hankaloittavat koodin luettavuutta.

Muuttujan nimi ei saa sisältää välilyöntejä. Miten sitten useampisanaisen nimen voisi kirjoittaa niin, että siitäsaisijotainselvää? Tähän on kehitetty kaksi eri ratkaisua. Ensimmäinen ja suositellumpi tapa on ns. ”käärmemenetelmä” (engl. snake_case). Siinä sanat erotetaan toisistaan alaviivalla. Toinen tapa on ”kamelimenetelmä” (engl. CamelCase), jossa sanat erotetaan toisistaan isolla alkukirjaimella.

Siistin koodin kirjoittamiseen liittyy monia muitakin pikku niksejä, jotka lisäävät koodin luettavuutta huomattavasti. On esimerkiksi erittäin suositeltavaa lisätä välilyöntejä merkkien väliin, jokaisen rivin tulisi sisältää alle 80 merkkiä, funktioiden ja looppien sisällä olevat argumentit tulisi sisentää jne. Lisää siitin koodin kirjoittamisesta voi lukea tidyversen tyylioppaasta The tidyverse style guide

Lisävinkki: Siistin koodin kirjoittamiseen on kehitetty monia apuvälineitä. Eräs katsastamisen arvoinen paketti on lintr, joka sisältää komennon lint(”tiedoston nimi”). Tämä koodinpätkä ilmoittaa komentorivillä, onko koodissasi jotain korjattavaa ja ehdottaa parannuksia.  

3. Vähennä toisteisuutta

Toinen ja hieman edistyneempi askel ymmärrettävän koodin kirjoittamiseen on toisteisuuden eli copy-paste -koodin välttäminen. Kirjassaan R for Data Science R-gurut Hadley Wickham ja Garrett Grolemund määrittelevät copy-paste -koodin niin että se sisältää saman koodirivin ainakin kolme kertaa. Mikäli huomaa kirjoittavansa toisteista koodia, on syytä havahtua. Vaikka toimintojen kierrättäminen leikkaa-liimaa -menetelmällä on helppoa, se johtaa vaikeasti ymmärrettävään ja virheille alttiiseen ohjelmaan. Jonosta lähes identtisiä rivejä on vaikea havaita koodin punaista lankaa ja koodia korjatessa joka rivin argumentit täytyy muuttaa erikseen, jolloin virheitä syntyy helposti. Hyvä ja siisti koodi sisältää siis mahdollisimman vähän toistoa, ja paras keino sen vähentämiseen ovat funktiot ja loopit. 

Funktiot ovat olennainen osa R:ää. Tyypillisesti funktio on pieni ohjelma, joka suorittaa jonkin operaation käsiteltävälle aineistolle annettujen argumenttien mukaisesti. Esimerkiksi keskiarvon laskeva funktio mean() ottaa argumentiksi vektorin sekä miten vektorissa mahdollisesti olevat puuttuvat arvot otetaan huomioon. Tällaisia funktioita on R:ssä tarjolla laaja valikoima, ja näiden lisäksi on vielä lukematon määrä käyttäjien tekemien pakettien funktioita.

Toisteisuuden vähentämisen näkökulmasta tärkeämpiä ovat kuitenkin itse tehdyt funktiot, joiden luonti on R:ssä yksinkertaista. Koodari antaa funktiolle nimen, argumentit (yleensä aineisto ja operaation suorituksen yksityiskohdat) sekä toiminnallisuuden, eli sen mitä annetulle aineistolle tehdään. Esimerkistä käy funktio, joka laskee vektorin varianssin. Funktion nimeksi määritellään var, argumentiksi vektori ja toiminnallisuudeksi varianssin laskeminen sopivalla kaavalla sekä lasketun arvon palautus. Tuloksena on funktio, jota on helppo käyttää uudelleen.

Kierrätettävyyden lisäksi useasti toistuvien operaatioiden kirjoittaminen funktioiksi vähentää virheitä ja tekee koodista luettavampaa, varsinkin jos kyseessä on pidempi ja monimutkaisempi ongelma. Mikäli funktiota täytyy muokata, se onnistuu muuttamalla koodia ainoastaan yhdestä kohdasta. Lisäksi Wickhamin ja Grolemundin  mukaan käyttämällä havainnollistavasti nimettyjä funktioita koodin luettavuus paranee. Esimerkiksi var-funktion tarkoitus on helpompi hahmottaa kuin vastaavan koodinpätkän. Teoksen R for Data Science mukaan hyvä nyrkkisääntö funktioiden käytölle on, että kopioidessasi koodia kolmannen kerran kirjoita mieluummin funktio.

Lukuvinkki: Erinomainen aloittelijalle sopiva johdatus funktioiden luomiseen on edellä mainitun kirjan R for Data Science luku 19 tai Grolemundin Hands-On Programming with R. Edistyneemmälle ärräilijälle mainio lähde funktioiden saloihin on Wickhamin Advanced R. R for Data Science:stä ja Advanced R:stä löytyy ilmainen internet-versio.

4. Hyödynnä toistorakenteita

Taloustieteilijä joutuu usein empiiristä tutkimusta tehdessään estimoimaan useita erilaisia regressiomalleja eri spesifikaatioilla. Suoraviivaisin tapa on kirjoittaa jokainen lineaarinen malli erikseen ja tallentaa jokainen malli omaan muuttujaansa. Mikäli malleja on vähän, tämä toimii hyvin, mutta jos malleja on enemmän, näpyttely käy ankeaksi ja koodista tulee toisteista. Juuri tällaisia tilanteita varten loopit ovat olemassa. Looppi toistaa saman koodiblokin komennot usealle eri syötteelle, esimerkiksi jokaiselle vektorin alkiolle. Näin on mahdollista toistaa sama operaatio useita kertoja ilman toisteisuuden kiroja. Estimointiesimerkissä loopin avulla voi antaa lineaariselle mallille syötteeksi vuorotellen kuhunkin spesifikaatioon kuuluvat muuttujat ja tallentaa estimoidut mallit listaan. Toisteisuus vähenee ja urakasta selviää vähemmällä koodilla.

Kirjassa R for Data Science Wickhamin ja Grolemundin mukaan tavat, joilla looppaaminen voidaan R:ssä suorittaa, on mahdollista jakaa kahteen eri ryhmään. Ensimmäiseen kuuluvat esimerkiksi R-ohjelmoinnin peruskurssilta tutut for ja while -loopit. Näistä on hyvä aloittaa, sillä ne ovat melko yksinkertaisia ja niistä käy selkeästi ilmi, mitä looppauksen aikana tapahtuu. Toisaalta nämä loopit voivat joskus vaatia paljonkin koodia suhteellisen yksinkertaisen ongelman ratkaisemiseksi. 

Toinen ryhmä, joka kuuluu funktionaalisen ohjelmoinnin alle, yhdistää loopit ja funktiot. Tällöin ajatuksena on, että toisteisuutta loopin sisällä pyritään vähentämään luomalla toisteisesta koodista funktioita. Tähän ryhmään kuuluvat erilaiset applyt ja ja Purrr -paketin funktiot. Erona esimerkiksi for-looppiin on, että näille loop-funktioille annetaan toistettavaksi jokin funktio toiminnallisuuden eksplisiittisen koodaamisen sijaan. Esimerkiksi apply-funktioperheen jäsenille annetaan argumentiksi funktio ja jokin syöte: esimerkiksi vektori, jonka jokaiselle arvolle funktio ajetaan erikseen. Funktionaalinen lähestymistapa mahdollistaa ongelmien ratkaisemisen one-linereilla ja vähemmillä virheillä, joten sen hallitsemisesta on ehdottomasti hyötyä.

Vinkki: Looppien opettelu kannattaa aloittaa helpommista for ja while -loopeista ja siirtyä funktionaaliseen lähestymistapaan, kun edelliset ovat hyvin hallussa. Wickhamin ja Gormundin R for Data Sciencen 21. kappale on erinomainen johdatus looppaamiseen R:ssä.

5. Älä panikoidu virheilmoituksista ja käytä tarvittaessa internettiä apuna

Virhe ohjelmakoodissa, riippumatta siitä johtaako se virheilmoitukseen vai vääränlaiseen toimintaan, johtuu lähes poikkeuksetta käyttäjän tekemästä virheestä. Kun ohjelma toimii epätoivotulla tavalla, on koneelle annettu “to do” -lista sisältänyt osia, jotka ovat olleet koneelle epäselviä. Usein ongelma johtuu väärin käytetyistä tai vajavaisesti määritetyistä funktioista, väärässä muodossa olevista muuttujista, puuttuvista paketeista tai puhtaista kirjoitusvirheistä. 

Ongelmakohdan löytäminen on yleensä yksinkertaista, vaikkakin pidempää ja monimutkaisempaa koodia käsiteltäessä se voi viedä aikaa. Takuuvarma menetelmä ongelman paikantamiseksi on aloittaa ohjelmakoodin alusta ja ajaa sitä rivi riviltä siinä järjestyksessä, missä kone koodia lukee, kunnes koodi ei enää toimi halutulla tavalla. Yleensä ongelmat ovat verrattain selkeitä ja korjattavissa lyhyen pohdinnan jälkeen, mutta erittäin usein ohjelmoija pääsee hyödyntämään maailmanlaajuisen verkon lähes rajatonta kirjastoa ongelman ratkaisemiseksi. Googlaamalla R:n antaman virheilmoituksen tai sen osan ohjelmoija löytää itsensä yleensä Stack Overflow -sivustolta, jossa vastaavaa ongelmaa on jo aiemmin puitu, ja johon etevämmät koodaajat ovat antaneet omat ratkaisuehdotuksensa. Learning by googling!

TEKSTI: Meeri Seppä, Juho Lähteenmaa ja Tuomas Markkula

Jatka keskustelua: