11  Arbeidsprosess

Her er noen generelle kommentarer om hvordan prosjektene mine har vært organisert. Det vil gjøre det lettere for andre å ta over. Her er en grov oversikt over arbeidsprosessen min, etterfulgt av nøyere detaljer

Hovedenheten i arbeidsprosessen er et prosjekt. Hva et prosjekt er er et spørsmål jeg syns blir vanskeligere og vanskeligere å svare på desto lengre jeg holder på med prosjekter. Kort fortalt har de et felles tema, og noen få, relaterte output. Da kan man gjenbruke koder på tvers av relaterte skript. Eksempler på prosjekter er arbeidet med barnehagekapasitet, arbeidet med flyttestatistikk, prognoseevalueringa. Dette er en abstrakte inndelinga av arbeidet mitt, og det sammenfaller med den fysiske inndelinga i mapper og Rstudio-prosjekter. Hvert prosjekt inneholder (vanligvis): et hovedskript og en del støtteskript. De ligger lagra på Mine dokumenterC:, med oppdaterte sikkerhetskopier på M:\StatTK\R\sikkerhetskopier.

11.1 Rproj

Rstudio-prosjekter er en nyttig måte å organisere prosjekter i R. Hovednytten kommer i at alle filstier blir relative til prosjektets rotmappe. Rotmappa er der .Rproj-fila ligger. Hadde jeg måtte definere alle stier ut fra hvor de ligger på min PC ville det blitt vanskelgiere for dere å ta over. Pakka here er også nyttig for å gjøre stier enda mer robuste.

Når vi prater om .Rproj, her er en instilling dere burde endre på hvis du ikke alt har gjort det: Skru av lagring av workspace mellom sesjoner. Se beskrivelse i Section 4.1.

Hvis du lurer på om en mappe er et Rstudio-prosjekt kan se se etter en .Rproj-fil. Legg f.eks. merke til at i mappa apps inneholder tre undermapper og ingen .Rproj. Dette fordi hver app er sitt eget Rstudio-prosjekt.

11.2 main-filer og mappestruktur

Generelt etterstreber jeg en mappestruktur som er omtrent slik ut:

.
└── mitt_prosjekt
    ├── R
    │   ├── main.R
    │   ├── scratchpad.R
    │   └── functions.R
    ├── output
    │   ├── *.xlsx
    │   └── *.csv
    ├── data
    │   ├── *.xlsx
    │   └── *.csv
    ├── README.md
    └── .gitignore

Ingen prosjekter ser akkurat slik ut, men det er et mål. Elementene i mappa:

  • I mappa R ligger alle kodeskriptene.
  • main.R: dette er et lite skript som laster inn funksjoner fra andre skript, og som kun inneholder de få kodene som trengs for å lage outputen. Vanligvis putter jeg all funksjonalitet inn i funksjoner som jeg putter i ett eller flere egne skript.
  • scratchpad.R: jeg har ofte en slik fil hvor jeg skrive alle koder før jeg kopierer dem til rett skript. Dette dokumentet blir dermed gravplass for ikke-fungerende koder. Det er en nyttig måte å arbeide på, fordi jeg i ettertid kan ignorere alt som ligger her. Noen ganger er jeg flink til å slette inneholdet slik at jeg ikke forvirrer meg sjøl seinere. Noen gang blir det liggende. Dere kan alltid ignorere denne fila.
  • functions.R: Om det kun er ett skript, heter dette vanligvis functions.R. Blir det mange funksjoner og uoversiktelig med alle i ett skript, deler jeg dem opp i flere skript med mer eller mindre beskrivende navn som cleaning-functions.R, import-functions.R, etc.
  • I mappa output ligger alle de prosesserte filene. Ofte .csv eller .xlsx filer.
  • I mapppa data ligger rådatafilene. I noen prosjekter lasta jeg ned data fra nettet og la her. Andre plasser lasta jeg ned data via API, og lagre en kopi av dem her for å ikke beslaglegge API-en unødvendig. I andre kopierte jeg filer fra M:-disken og la her. Hvis jeg arbeida med filer fra M: pleide jeg vanligvis å oppgi filstien til fila direkte i funksjonen som importerte dataene til R.
  • Ideelt sett skulle alle prosjekter har en readMe.md (eller lesMeg.md på norsk). Denne fila (som noen gang blei printa som en pdf) forklarer hva prosjektet handler om.
  • .gitignore: denne fila brukes av Git (se ned), og du kan se bort fra den.

Shiny-appene har i tillegg en fil som heter app.R, som har samme hovedfunksjon som main.R. Her defineres sjølve appen. Alle skript som ligger i R blir automatisk kjørt når man kjører app.R, i motsetning til main.R hvor man manuelt må kjøre alle skript via source().

Mange av prosjekta er en eksentrisk blanding av engelsk og norsk, som kom av at jeg vanligvis koder på engelsk og dokumenterer på norsk. I enkelte prosjekter var jeg flinkere på være konsekvent enn i andre.

I noen main-filer har jeg kommentert vekk linjer som produserer data som blir lagra på disk. Dette fordi man kan komme i skade for å kjøre dem uforvarende og skrive over noe. F.eks. i appen fodte-dode har jeg gjort dette. Der ligger en funksjon som er tenkt å kjøres en gang i måneden for å hente nye dødetall via SSBs API.

11.3 Git

Jeg har brukt Git på alle prosjektene. Git er et version-control system, og nyttig når man koder. Jeg har brukt det for å unngå det vi ser i figuren på side 4 av denne artikkelen: mange versjoner av samme dokument med ulike navn som indikerer hvilken versjon det er snakk om. Kort fortalt sørger Git for at jeg har alle disse gamle versjonene av dokumentene tilgjengelig, men at de ikke vises og tar opp plass i mappestrukturen min. Dermed holder jeg lettere oversikt.

Dere trenger ikke vite noe om Git fordi jeg har sørga for at det alltid er siste versjon som ligger klar. Jeg nevner det likevel fordi dere kan lure på hvorfor det ligger noen filer som heter .git og .gitignore i prosjektene. Disse filene brukes av git. Det kan slette dem uten at det vil ødelegge prosjektene. (Men da vil all historikken gå tapt, og det er ikke lengre mulig å se eldre versjoner av skriptene mine).

Jeg brukte altså Git som en måte å holde orden i filene mine, men også til å sikkerhetskopiere til M-disken. M er som kjent en server vi kobler oss på trådløst. Her lagrer vi alt vi arbeider med (som ikke går på Google disk). Problemet med M er at det er en ekstern server. Dermed kan enkelte prosesser tar mye lengre tid når man arbeider med filer som ligger på M. Dessuten vil en del programmer frike ut dersom tilkoblinga til M blir borte i et mikrosekund, slik den plagsomt nok blir hvis skjermspareren kommer på eller hvis et atom nyser i feil retning. Min løsning blei å jobbe med alt på den lokale C-disken og kopiere over alt til M-disken på slutten av hver dag1. Dette høres tungvint ut, tenker du sikkert. Kopiere over alle filer til et prosjekt hver dag? Huff. Men det var ikke vanskeligere enn å skrive inn dette i en konsoll: git push.

11.3.1 Hva må du vite om Git

Ingen ting. Det holder å vite at jeg brukte Git, og det er derfor visse filer eksisterer i prosjektene. Det er smart å la disse ligge der i fall dere en gang får lyst å bruke Git sjøl. Og for å beholde historikken min (som jeg tviler på at dere noen gang vil ha nytte av).

Det som ligger i mappa R/sikkerhetskopier på M er essensielt det samme som ville ligget der dersom jeg hadde manuelt dratt filene over. Jeg antar at C-disken på pc-en min blir mind wiped snart, og da vil dette være de eneste kopiene av arbeidet mitt, så dere trenger ikke tenke på hva som er kopi av hva.

Min pc, snart.

11.4 Lagringslokasjon

Som jeg nevnt over arbeida jeg på den lokale C-disken, og opprettholdt sikkerhetskopier på M. For å utbrodere litt på dette: Noen ganger er det vanskelig å arbeide på filer direkte på en ekstern server. Det er ikke helt kosekvent hvordan dette utarter seg. I arbeidet med flytterater skal jeg skrive en excelfil til M-disken. Dette tar flere minutter. Å skrive den til C-disken tar ett sekund. Og å kopiere fra C til M tar sekunder. Dermed er det kjappere å skrive fila til C og så kopiere den til M. R lar oss manipulere filer direkte. Så i denne koden har jeg lagd en funksjon som kopierer det vi trenger fra M til C, og kopierer det ferdige produktet tilbake til M fra C. Vi kan gjøre dette relativt sømløst, slik at brukeren ikke trenger å tenke på hvor filer er lagra. Så lenge vi sjøl har kontroll, så klart.

Som nevnt tidligere over her og når pakka here omtales, i et prosjekt er alle filstier relative til prosjektets rotmappe. Dermed vil det ikke oppstå noen problemer når det kommer til filer som ligger i en undermappe av prosjektet. F.eks. legger jeg som nevnt datafiler i mappa data. Uansett hvor du kopierer et prosjekt til, så lenge du åpner prosjektet i Rstudio skal disse lenkene fungere (til og med om du åpner dem på en pc med macOS!). Noen filer vi bruker ligger på M. Her er det viktig at disse filstiene bevares eller oppdateres.

På statistikkmappene er det en konvensjon at filer med datasett er navngitt etter datoen de blei laga. Det vil si at folkemengdefila jeg henviser til i skriptet fra mars 2023 ikke er den oppdaterte folkemengdefila i mai 2024. Her må man gå inn og oppdatere koden med nytt navn. Hvis man føler seg fancy kan man lage en funksjon som gjetter seg fram til hva som er den nyeste versjonen av en fil.

11.5 Functional programming

Jeg har etterstreba et paradigme som kalles functional programming. Dette er en programmeringsstil hvor vi fokuserer på funksjoner som gjør alt av endringer på data. Resultatet er lettere å lese, forstå og debugge. Les mer om det i Modern R with the tidyverse. I praksis vil det si at jeg putter funksjoner sammen i andre funksjoner som jeg gir beskrivende navn. Dette gjør det lettere å gruppere sammen databehandlingsfunksjoner. Og dermed også å putte funksjoner i ulike skriptfiler. Jeg bytta nylig til denne praksisen, etter å tidligere ha arbeida med meterlange skript hvor objekter opprettes, endres, slettes, transformeres og lagres om hverandre. Så langt syns jeg dette er en stor forbedring. Utfordringa er å finne en god balanse mellom hvor mange skript man skal lage, og hvor mange delfunksjoner man trenger. Du vil se at jeg er inkonsekvent i de ulike prosjekta mine. Her finnes heller ingen one size fits all. Små prosjekter trenger ikke mer enn en main.R-fil. Store prosjekter kan ha mange skript fordelt over flere mapper.


  1. Jeg gjorde det ikke hver dag.↩︎