Inleiding Programmeren + R

Functie definitie

» Start

Zelf een functie definieren


R heeft uitgebreide bibliotheken (zg pakketten) met functies voor allerlei toepassingen. Toch is het wel handig, en soms ook noodzakelijk om je eigen functies te kunnen schrijven.  Dat gaat in R vrij eenvoudig. Zoals gezegd bestaan functies, net zo als scripts, uit een bundeling van R opdrachten.  Voordat we zelf gemaakte functie kunnen gebruiken (= aanroepen) moeten we ze eerst definieren. Functies hebben de volgende algemene vorm:

    

    > mijn.functie <- function(argument) { # start functie definitie

        opdracht 1    # eerste opdracht

        opdracht 2    # enz...

        .

        .

        opdracht n    # laatste opdracht

        }            # afsluiting van functie definitie


    > mijn.functie(argument)    # aanroep van zelf gedefinieerde functie

    

We zullen voor het eerste voorbeeld van een zelf te schrijven functie een al eerder door ons gemaakt script als uitgangspunt kiezen, zodat het verschil tussen een script en een functie goed duidelijk wordt. Dit was het script:

    

    # mean, median, geometric mean, harmonic mean for vector x

    gm <- exp(mean(log(x)))

    hm <- 1/mean(1/x)

    cat("Mediaan = ", median(x),"\n")

    cat("Rekenkundig gemiddelde = ",mean(x),"\n")

    cat("Geometrisch gemiddelde =",gm,"\n")

    cat("Harmonisch gemiddelde = ",hm,"\n")

    

We willen dezelfde opdrachten nu in de vorm van een functie definieren. Het eerste waar we rekening mee moeten houden is dat een functie, net zoals een variabele, gedefinieerd moet worden door middel van een toekenning. Bij een script is dat niet het geval. In de tweede plaats moeten de variabelen die door de functie worden bewerkt bij de aanroep van de functie als parameter mee gegeven kunnen worden. Daarom wordt hij bij zijn definitie al voorzien van 1 of meerdere argumenten. Bij een script is dat niet het geval. 


In ons voorbeeld wordt een vector met de naam x bewerkt. Wil het script goed kunnen werken dan moet die vector per sé de naam x hebben! Het script werkt niet met een vector die een andere naam, bijv xyz, heeft. Wanneer we een functie gaan definieren die de zelfde taken als het voorbeeld script moet verrichten, is het niet nodig om die variabele van een vaste naam te voorzien. Het enige wat belangrijk is is dat de functie met een vector moet kunnen werken. Of de variabele waarin die vector is opgeslagen nu x heet, of y, of armlengte doet er helemaal niet toe.


    > mijn.central <- function(input) {

        # mean, median, geometric mean, harmonic mean for vector input

        x <- input

        gm <- exp(mean(log(x)))

        hm <- 1/mean(1/x)

        cat("Mediaan = ", median(x),"\n")

        cat("Rekenkundig gemiddelde = ",mean(x),"\n")

        cat("Geometrisch gemiddelde =",gm,"\n")

        cat("Harmonisch gemiddelde = ",hm,"\n")

        }


Je kunt deze regels stuk voor stuk intypen (copy/paste) op de commando regel van R. Na de eerste regel te hebben ingetypt zal R antwoorden met een + op een nieuwe regel. Pas na met de laatste regel } de functie definitie te hebben afgesloten, zal de R prompt (>) weer verschijnen. We kunnen de functie nu gaan gebruiken:


Eerst wat data binnen halen om te kunnen bewerken:

    

    > # lees een dataframe in.

    > cohort <- read.table("gegevens.txt", header=T)

    > # maak de namen in de eerste rij van cohort beschikbaar als namen van

    > # variabelen met bijbehorende inhoud (kolommen van dataframe)

    > attach(cohort).

    > # en laat zien om welke variabelen het gaat.

    > names(cohort) 

    [1] "lichaam"  "arm"  "pols"  "geslacht"  "hand"  "ogen"    


Vervolgens doen we:

    

    > mijn.central(lichaam)

    

en krijgen we het volgende antwoord:

    

    Mediaan =  179 

    Rekenkundig gemiddelde =  177.4470 

    Geometrisch gemiddelde = 177.1838 

    Harmonisch gemiddelde =  176.9154 

    

Hij doet het!

We willen de functie nu bewaren om bij een volgende R-sessie weer te kunnen gebruiken. We kunnen de functie saven met behulp van het command0 save. 

    

    > save(mijn.central, file="central.r")

    

Als we nu de functie uit het geheugen verwijderen met rm:

    

    > rm(mijn.central)

    

Even controleren:

    

    > ls()

    [1] "cohort"

    

Hij is echt weg! 

We kunnen de functie weer in het geheugen laden m.b.v. het commando load:

    

    > load("central.r")

    

Even controleren:

    

    > ls()

    [1] "cohort"       "mijn.central"

    

Hij is er weer!


We hebben nu een functie mijn.central gedefinieerd die zg conversationele output geeft. Dat wil zeggen dat de functie het resultaat van zijn berekeningen op het scherm schrijft. Het enige wat we met die output kunnen doen is direct printen, of overschrijven, of copy/pasten naar een note, voor dat we het scherm sluiten. 

Wat natuurlijk veel handiger zou zijn is als de functie het resultaat van zijn berekeningen opslaat in een variabele waarmee we na afloop verder kunnen werken (rekenen). We spreken dan van een functie met expliciete output. Om dat te bereiken moeten we nog een regel aan mijn.central toevoegen. Daarbij moeten we rekening houden met het feit dat alleen de waarde van de laatste regel van de functie wordt doorgegeven aan het hoofdprogramma (= de R-sessie).:


    mijn.central2 <- function(input) {

        # mean, median, geometric mean, harmonic mean for vector input

        x <- input

        gm <- exp(mean(log(x)))

        hm <- 1/mean(1/x)

        cat("Mediaan = ", median(x),"\n")

        cat("Rekenkundig gemiddelde = ",mean(x),"\n")

        cat("Geometrisch gemiddelde =",gm,"\n")

        cat("Harmonisch gemiddelde = ",hm,"\n")

        out <- c(median(x), mean(x), gm, hm)

        out

        }


Het is ook mogelijk om het programma op andere regels dan de laatste te laten stoppen en output naar de buitenwereld door te geven. Daarvoor dient de functie return. Met de opdracht:

    

    return(out)

    

stopt de functie direct, en geeft de inhoud van de variabele out als expliciete output.


    mijn.central2 <- function(input) {

        # mean, median, geometric mean, harmonic mean for vector input

        x <- input

        gm <- exp(mean(log(x)))

        hm <- 1/mean(1/x)

        cat("Mediaan = ", median(x),"\n")

        cat("Rekenkundig gemiddelde = ",mean(x),"\n")

        cat("Geometrisch gemiddelde =",gm,"\n")

        cat("Harmonisch gemiddelde = ",hm,"\n")

        out <- c(median(x), mean(x), gm, hm)

        return(out)

        }


Kopieer en plak de bovenstaande regels in een tekstfile (Notepad, of Textwrangler), en bewaar die file met naam central2.r in de working directory van R.

    

    # daarna lezen we de nieuwe functie in

    > source("central2.r")

    

We nemen een kijkje in het geheugen:

    

    > ls()

    [1] "cohort"           "mijn.central"     "mijn.central2"

    

Hij is er... onze nieuwe functie ! Gelijk uitproberen:

    

    > mijn.central2(lichaam)

    Mediaan =  179 

    Rekenkundig gemiddelde =  177.4470 

    Geometrisch gemiddelde = 177.1838 

    Harmonisch gemiddelde =  176.9154 

    [1]  179.0000  177.4470 177.1838 176.9154

    

De laatste regel wijst er op dat er ook expliciete output is in de vorm van een vector met een aantal gemiddelden. We zouden onze nieuwe functie dus ook als volgt kunnen aanroepen:

    

    > mids<-mijn.central2(lichaam)

    Mediaan =  179 

    Rekenkundig gemiddelde =  177.4470 

    Geometrisch gemiddelde = 177.1838 

    Harmonisch gemiddelde =  176.9154 

    

Om daarna te kijken wat er in de variabele mids zit:

    

    > mids

    [1]  179.0000  177.4470 177.1838 176.9154


Daar kunnen we dus verder mee spelen...


Overigens is het niet noodzakelijk dat de functie return pas op de laatste regel wordt gebruikt om te zorgen dat de functie expliciete output krijgt, zoals het volgende voorbeeld laat zien:

    

    printLog <- function(x) {

        if (any(x <= 0)) { # controleer het domein van x: moet > 0 zijn!

            print ("Positive numbers only, please.")

            return(NULL)   # expliciete output is leeg als x niet OK is...

            }

        result <- log(x)   # bereken log(x) indien domein van x is OK

        return(result)     # en geef expliciete output

        }

    

De eerste keer dat de functie return wordt aangeroepen is dat om te zorgen voor expliciete output (een lege vector) nadat gebleken is dat de input x (1 of meer getallen) niet voldoet aan de voorwaarde van groter zijn dan nul. De tweede aanroep van return zorgt voor de expliciete output van de log's van de getallen in x.