Programering i R

Föreläsning 4: Funktioner och programmering

Josef Wilzén

STIMA, Linköpings universitet

Uppdaterad 2017-02-07 10:02:09

Föreläsning 4: Innehåll

  • Interaktiv del ~ 45 min
  • Mer om funktioner
  • Bra och effektiv programmering
  • *apply-funktioner
  • ROxygen
  • R-paket

Interaktiv del

Interaktiv del

(kommer snart)

Uppföljning av labb 3

  • Vid inlämning:
    • Spara er fil som labb1_josad732.R om det är labb 1.
    • Denna fil ska laddas upp på LISAM och ska inte innehålla något annat än de aktuella funktionerna, namn- och ID-variabler och ev kommentarer.
    • Tips: kommentera bort tester mm innan inlämning. ctrl+shif+c kommenterar/avkommenterar flera rader samtidigt.

Inför del 2 av kursen

  • Labbar ska göras i par.
  • På Slack finns en länk till ett dokument där ni ska fylla i er.
  • Ni blidar grupper själva.

Mer om funktioner

Kort repetition

En funktion i R består av:

  • ett funktionsnamn (ex. f)
  • en funktionsdefinition - function()
  • 0 eller flera argument / invärden (ex. x och y)
  • “curly bracers” {}
  • programkod / funktionen (ex. x + y)
  • returnera värde (ex. return())
  • utvärdet från funktionen (ex. f(3,2) = 5 )

  • en egen (lokal) miljö där beräkningar görs

Allmänt om funktioner

  • Funktioner är objekt
f <- function(x,y) {
  z <- x^2 - y
  return(z)
}
f
function(x,y) {
  z <- x^2 - y
  return(z)
}

Allmänt om funktioner

  • Funktioner har tre delar:
    • argument
    • funktionskropp
    • lokal miljö/föräldramiljö

Exempel

formals(f)
$x


$y
body(f)
{
    z <- x^2 - y
    return(z)
}

Exempel

environment(f)
<environment: R_GlobalEnv>

Allmänt om funktioner (forts.)

  • Funktioner kan tas som argument
square_function <- function(x) {
  return(x^2)
}
integrate(square_function, lower = 0, upper = 1)
0.3333333 with absolute error < 3.7e-15
  • Funktioner kan returnera funktioner (se ex. i RP:s video)

Allmänt om funktioner (forts. II)

  • Vi behöver inte namnge argumenten
f(1,5) # Samma som f(x = 1, y = 5)
[1] -4
  • Ordningen spelar ingen roll
f(y = 5, x = 1) # Samma som f(x = 1, y = 5)
[1] -4

Allmänt om funktioner (forts. III)

  • Behöver inte return() eller { } för små funktioner
f <- function(x) x^2 - 3
f(3)
[1] 6

Allmänt om funktioner (forts. IV)

  • Defaultvärden
f <- function(x = 10) x^2 - 3
f(3)
[1] 6
f()
[1] 97

Globala och lokala miljöer i R

Globala miljöer, lokala miljöer och namespaces

Objekt kan definieras/skapas i…

  • Den global miljön
  • Lokala miljöer (inne i funktioner)
  • Namespaces

Hur vet R vad vi menar?

  • objektnamn och objekt

  • R:s söklista:

    1. Lokala miljöer
    2. Den global miljön
    3. Vidare i den ordning namespaces är laddade

Fria variabler

f <- function(x) x + y

Objektet/värdet för “fria” variabler undersöks i den miljö funktionen var definierad/skapad.

  • Sedan söker R på samma sätt som i förra sliden

Exempel på fria variabler

f <- function(x) x + y
f(3)
Error in f(3) : object 'y' not found
y <- 2
f(3)
[1] 5

Hur vet R vad vi menar? II

  • Se R:s söklista: search()
  • Se var en funktion skapats: enviroment()
  • Anropa en funktion i en given namespace/miljö: ::

Exempel på search()

Undersöka laddade namespaces med search():

search()
 [1] ".GlobalEnv"        "package:knitr"     "package:stats"    
 [4] "package:graphics"  "package:grDevices" "package:utils"    
 [7] "package:datasets"  "package:methods"   "Autoloads"        
[10] "package:base"     

Exempel på :: och environment()

f<-function(x) x^2
environment(f)
<environment: R_GlobalEnv>
environment(base::mean)
<environment: namespace:base>

Lite mer om att skapa objekt

Att skapa / definiera objekt kallas att tilldela (assign)

  • grunden är assign()
  • <- : tilldela i aktuell miljö
  • <<- : tilldela direkt till global miljö
  • = : kan användas, men avråds från (minska förvirring i funktioner)

Funktioner i funktioner

  • Ofta vill vi använda funktioner i funktioner
  • Att tänka på:
    • Var ska funktionen definieras/skapas?
    • Skicka information inom funktioner

Skicka information inom funktioner

  • Kan göras genom att “skicka vidare” argument hela vägen
  • Detta görs med “ellipsis” - symbolen ...
  • Detta läggs in som ett argument i funktionen, ex. min_fun <- function(...)
  • ... används där argumenten ska användas i funktionen
  • Argument efter ... måste namnges
min_mean <- function(...){ 
  print(mean(...))
}
min_mean(c(1,3,NA,5), na.rm = TRUE)
[1] 3

Skicka information inom funktioner

Ibland behövs mer flexibilitet än vad ... ger då kan do.call() användas. Argument:

  • what= en funktion som ska anropas
  • args= en lista med argument till funktionen i what
x<-c(NA,1,2,453,32,1,NA)
mylist<-list(x=x,na.rm=TRUE)
do.call(what = mean,args = mylist)
[1] 97.8

God (vetenskaplig) programmering

Bygger på artikeln Best practices for scientific computing som finns här

God (vetenskaplig) programmering

  • Kod är stor del av vetenskap/analys
  • Forskare spenderar 30% eller mer på att skriva kod
  • Det är enkelt att det blir fel

8 steg för god programmering

  1. Skriv kod för människor, inte datorer
  2. Låt datorn göra arbetet
  3. Arbeta i små steg
  4. Upprepa aldrig din (eller andras) kod (DRY - principle)
  5. Planera för misstag
  6. Optimera kod först när den fungerar korrekt
  7. Dokumentera
  8. Samarbeta

1. Skriv kod för människor, inte datorer

  • Läsare ska inte behöva hålla allt i huvudet - använd funktioner
  • Använd objektnamn som är tydliga och meningsfulla
  • Använd en kodstil (coding style):

2. Låt datorn göra arbetet

  • Låt datorn upprepa uppgifter
  • Återanvänd kod - använd funktioner
  • Automatisera arbetsflödet (one file to rule them all)

3. Arbeta i små steg

  • Skriv kod i små steg, testa och ändra löpande
  • Versionshantera din kod (överkurs)
    • Git finns implementerat direkt i R-Studio mer info
  • Versionshantera ALLT som inte skapas av koden (överkurs)
  • Det finns enkel versionshantering i Dropbox och Google drive
  • Om du inte versionshanterar: Spara ofta, vid större kodprojekt spara en ny fil vid större förändnirngar.

4. Upprepa aldrig din (eller andras) kod (DRY - principle)

  • All data ska bara finnas på ett ställe (ex. konstanter)
  • Skapa funktioner istället för att copy-paste
  • Återvinn (din och andras) kod istället för att skriva egen - R-paket!

5. Planera för misstag

  • Skapa kontroller av input och output: stop()
  • Det ska smälla högt och tidigt
  • Använd testpaket (överkurs)
    • i R: paketet testthat
  • Gör buggar till egna testfall
  • Använd debuggers!

6. Optimera kod först när den fungerar korrekt

  • Använd profilers (överkurs)
    • i R: Rprof()
  • Använd högnivåspråk, (använd R =) )

7. Dokumentera

  • Dokumentera syftet med koden, inte vad koden gör
  • Gör koden enkel att förstå
  • Kombinera kod och dokumentation

8. Samarbeta

  • Låt andra titta på/kolla din kod
  • Använd parprogrammering för att:
    • hjälpa kollegor in i projekt
    • hantera komplexa programmeringsproblem
  • Använd issue-trackers (överkurs)

högnivå-funktioner

*apply-functioner

  • “High level functions”
  • Ett (snabbare) alternativ till loopar
  • Internt i R: loop i C-kod
  • Funtioner:
    • lapply() : loopar över element i en lista
    • tapply() : loopar över ett index (ex. aggregate())
    • apply() : loopar över marginaler (ex. colSums())
    • det finns fler… se här

Exempel på lapply()

lapply() har tre argument:

  • X listan vi vill loopa över
  • FUN funktionen att aplicera
  • ... argument till funktionen
myList <- list(x = 1:10, y = c(NA,12:20))
str(lapply(X=myList, FUN=mean, na.rm=TRUE))
List of 2
 $ x: num 5.5
 $ y: num 16

Mäta körtid i R

  • Ibland vill vi mäta hur lång tid kod tar att köra: system.time()
system.time(expr=mean(rep(c(1,2),1000000)))
   user  system elapsed 
  0.032   0.004   0.036 

ROxygen

Dokumentera med ROxygen

  • ROxygen är standard för dokumentation
  • Samma som JavaDoc
  • Skapar automatiskt .Rd i paket
  • Använder #'

Dokumentera med ROxygen

ROxygendel Innehåll
@title Anger titel för dokumentet
@description En beskrivning vad funktionen gör
@details Detaljer om funktionen
@param Argument till funktionen
@return Vad funktionen returnerar
@references Eventuella referenser av intresse
@seealso Andra funktioner som kan vara aktuella
@examples Exempel på hur funktionen kan användas

Exempel

#' @title f
#' @description
#' En funktion som kvadrerar argumenten i x och y och summerar dem. 
#' @param x
#' Den numeriska variabel x som ska kvadreras
#' @param y
#' Den numeriska variabel y som ska kvadreras
#'
#' @return
#' Funktionen returnerar en numerisk vektor
#'
f <- function(x, y) x^2 + y^2

R-paket

Vad är R-paket?

  • R:s största styrka!
  • En samling funktioner
  • MÅNGA utvecklare
  • Finns två huvudsakliga “arkiv” för paket:
  • Det är enkelt att bidra med egna paket!
  • Det är finns oxkså mycket skräp därute!
  • Glöm inte att citera med citation()

Läsa in redan installerade R-paket

  • Läsa in paket görs med library()
  • Anropa funktioner (utan att ladda paket görs med ::)
    • base::mean()
  • Installera paket:
    • install.packages() (CRAN)
    • devtools::install_github() (GitHub)
  • En del paket följer med R
  • ls("package:MASS") visar vilka funktioner som finns i paketet MASS.
  • I Rstudio finns en flik för pakethantering
  • Lista alla inlästa paket: sessionInfo()

Semantic versioning

  • Ett versionsnummer består av tre delar: [MAJOR].[MINOR].[PATCH]
    • Major: Stora förändringar som inte är bakåtkompatibla
    • Minor: Mindre förändringar som är bakåtkompatibla
    • Patch: Buggkorrigeringar
  • En majorversion som är 0 innebär “utveckling pågår”, större färändringar kan ske.
R.version.string
[1] "R version 3.3.2 (2016-10-31)"

Att hitta rätt paket

  • Alla paket håller inte samma kvaliet. Följande tips för att granska:

    1. Kommer paketet med R eller någon från R Core Team?
    2. När kom senaste uppdateringen?
    3. Är paketet en utvecklingsversion?
    4. Mejla och fråga utvecklaren
    5. Kontrollräkna centrala funktioner
  • För att komma igång med nya paket: vignetter

Installera paket

CRAN:

install.packages("lubridate")

GitHub:

devtools::install_github(repo="sweSCB", username="rOpenGov")

Läsa in paket

library(lubridate)