Programering i R

Föreläsning 4: Funktioner och programmering

Josef Wilzén

STIMA, Linköpings universitet

2021-02-15

Föreläsning 4: Innehåll

  • Intro
  • Inför del 2 i kursen.
  • Mer om funktioner
  • Bra och effektiv programmering
  • *apply-funktioner
  • roxygen2
  • R-paket

Labbar

  • Viktigt att göra övningsuppgifterna innan inlämningsuppfiterna.
    • Tentan utgår ifrån att ni gjort de flesta övningsuppgifter.
  • Utnyttja labbtiden, kom förberedd till labben.
  • Labbassar: har lite jour mellan labbtillfällen
  • Rätta med: mark_my_file()

Labbar

  • Tips: kommentera bort tester/kod mm innan inlämning. ctrl+shift+c kommenterar/avkommenterar flera rader samtidigt.
  • Andra användbara:
    • ctrl+L rensar konsolen
    • ctrl+1 flytta markören till skript
    • ctrl+2 flytta markören till konsol
    • F1 hjälp
  • För att se fler kommandon i RStudio tryck alt+shift+k

Labbar: Vid problem

  • Förstår jag uppgiften?

    • Kolla slides, kursbok, videos mm
    • Exempel? demokod, övningsuppgifter, kursbok
  • Dela upp problemet i små delar

    • Lös en del i taget (var utanför funktionen vid behov)
    • Sätt sen ihop allt
    • Kontrollera att allt fungerar tillsammans
    • Arbeta kontrollerat och metodiskt

Labbar: Error

  • Error i konsolen? (ofta syntaxfel)

    • Kolla så att alla ( [ { är stängda.
    • Kolla så att inte någon . , är på fel ställe
  • Kommentera bort stora delar av koden

    • testa att det funkar
    • sen avkommentera små delar i taget, börja uppifrån
  • Sök på felmeddelandet på internet

Labbar: Error

  • Använd debugging!

    • browser(), debug()
    • Skriv ut variabler till konsolen
    • Om det inte ens går att köra funktionen, kommentera bort kod tills det går
  • Fråga om hjälp!

if vs. if-else

När ska jag använda flera if-satser och när ska jag använda if-else?

  • Det beror på problemet!

  • Vid flera oberoende ja/nej-frågor: upprepade if-satser

  • Vid ja/nej-frågor där svarat beror på svaret på tidigare frågor: if-else-sats

    • Om vi vill täcka in alla möjliga svar/scenarion

if vs. if-else

Oberoende ja/nej-frågor:

  • Vill du ha hamburgare?
  • Vill du ha pommes?
  • Vill du ha läsk?

Beroende ja/nej-frågor

  • Vill du ha glass?
    • Om ja, vill du ha strössel?
      • Om ja, vill du chokladströssel?

Inför del 2 av kursen

  • Labbar ska göras i par
    • De som läser om kursen gör labbarna ensam eller tillsammans med någon annan som läser om kursen.
  • På Teams finns en länk till ett dokument där ni ska fylla i er.
  • Ni bildar grupper själva. Deadline lördag (20/2) kväll.

Mer om funktioner

Kort repetition

En funktion i R består av:

  • ett funktionsnamn (ex. f)
  • en funktionsdefinition med 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)
}
class(f)
[1] "function"

Allmänt om funktioner

  • Funktioner har tre delar:
    • argument
    • funktionskropp
    • lokal miljö och 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 (tänk R-paket)

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>

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 (används för att definera defaultargument 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
      • Se do.call() och ... (Ellipsis) i labben

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/statistiker spenderar stor del av sin tid med 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
  • Bra struktur och indentering
  • 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, spara ofta
  • Versionshantera din kod (överkurs)
    • Git finns implementerat direkt i R-Studio mer info
  • Det finns enkel versionshantering i vanliga molntjänster: OneDrive, Dropbox och Google drive etc

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!
    • browser(), debug()

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

  • Se till att ha en kod som löser ditt problem
  • Sen kan du fundera på om den behöver vara:
    • snabbare
    • mer minneseffektiv
    • mer användarvänlig
  • Använd profilers (överkurs)
    • i R: Rprof()
    • I RStudio: Klicka på knappen “Profile”
  • 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
    • (dock inte kod till inlämningsuppgifterna!)
  • Använd parprogrammering för att:
    • hjälpa kollegor in i projekt
    • hantera komplexa programmeringsproblem

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.013   0.000   0.013 

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 också 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)

Läsa in redan installerade R-paket

  • 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 4.0.3 (2020-10-10)"

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. Sök på nätet och se om andra använder paktet och till vad
    5. Mejla och fråga utvecklaren
    6. Kontrollräkna centrala funktioner
  • För att komma igång med nya paket: vignetter

Att hitta rätt paket

Installera paket

CRAN:

install.packages('lubridate')
install.packages('devtools')

GitHub:

library(devtools)
install_github('ropengov/pxweb')
# alt
devtools::install_github('ropengov/pxweb')

Läsa in paket

# läsa in:
library(lubridate)
# ta bort från aktuell session:
detach("package:lubridate", unload=TRUE)

Läsa in paket

SU-salar: Läsa in paket relaterade till kursen:

  • kör följande i en terminal:
    • module add prog/r-mega-edition/20.08
  • så kommer ni få tillgång till R, RStudio samt de R-paket som behövs i kursen.
  • Öppna en terminal genom att trycka ctrl+alt+T
  • Se mer info här, finns även på kurshemsidan

ROxygen

Dokumentera med ROxygen

  • roxygen2 ä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

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