Algoritmiek Algoritmiek Om een indruk te krijgen wat Algoritmiek en Programmeren inhoudt is het doorlezen van het eerste hoofdstuk van het boek over Python ook voor R een goede start, zolang je overal waar Python gedrukt staat gewoon R leest. Hoofdstuk 1 en hoofdstuk 2 van de [oude] Algoritmiek syllabus geven eveneens een inleiding met dezelfde strekking.
Page 1 of 96
Begin Begin Installeer R Start R Werkruimte Help in R
Page 2 of 96
Central script Script voor gemiddelden Als laatste voorbeeld van een script gebruiken we weer een statistische toepassing. We maken een script wat voor een vector met de naam x verschillende soorten gemiddelden uitrekent, nl het rekenkundig gemiddelde, de mediaan, het geometrisch gemiddelde en het harmonisch gemiddelde. Kopieer en plak de bovenstaande regels in een tekstfile (Notepad, of Textwrangler), en bewaar die file met naam central.r in de working directory van R. # 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") De functie cat doet ongeveer het zelfde als print, alleen kunnen we het format beter controleren, bijvoorbeeld door code voor een nieuwe regel ("\n") toe te voegen. 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 gaan we de verschillende gemiddelden van de variabele lichaam uitrekenen met ons zelf geschreven script central.r. Eerst zorgen we dat lichaam de juiste naam, x, krijgt om het script goed te laten werken. > x <- lichaam # daarna lezen we het script in > source("central.r") Mediaan = 179 Rekenkundig gemiddelde = 177.4470 Geometrisch gemiddelde = 177.1838 Harmonisch gemiddelde = 176.9154
Page 3 of 96
Controlestructuren Controlestructuren Zolang we alleen opeenvolging hebben als de manier waarop opdrachten in een script of functie kunnen worden gestructureerd, bestaat de uitvoering van een programma uit evenveel opdrachten als we achtereenvolgens hebben geformuleerd. Dat is eenvoudig in te zien door te kijken naar een voorbeeld zoals het SD script. Het script begint met de eerste regel, vervolgens wordt de 2e regel uitgevoerd, enzovoort, en we stoppen zodra de laatste regel is gedaan. Wat we ook willen kunnen is afhankelijk van voorwaarden bepaalde opdrachten wel uitvoeren en andere niet. We spreken dan van voorwaardelijke opdrachten. De opdrachten worden op uitvoering geselecteerd door middel van logische operatoren [zie logica]. In de tweede plaats moeten we over de mogelijkheid beschikken om opdrachten te herhalen (door middel van lussen), en wel om de volgende reden. Er zijn problemen die om heel veel (reken)opdrachten vragen om te kunnen worden opgelost. Het zijn ook precies dat soort problemen wat om het inzetten van computers vraagt. Die problemen zijn bewerkelijk omdat de gewenste toestandsveranderingen (= de oplossing) alleen voltrokken kunnen worden via een heel lang pad dat uit heel veel stapjes bestaat. Het is duidelijk dat het programmeren van een computer die, zeg, 1 miljoen opdrachten per sekonde uit kan voeren, onbegonnen werk is als we die 1 miljoen opdrachten eerst in de goede volgorde allemaal expliciet op zouden moeten schrijven, om het apparaat 1 sekonde bezig te houden. Wat we willen kunnen is om lange rekenprocessen in een kort programma, d.w.z. een korte serie opdrachten, op te schrijven. Zonder die mogelijkheid zouden lange (reken)processen niet te realiseren zijn. Een hulpmiddel om lange processen d.m.v. korte series opdrachten uitvoerbaar te maken is de logica. Zowel in de wiskunde als in de informatica wordt zeer veel gebruik gemaakt van logica. Met logica bedoelen we hier het gebruik van beweringen die óf waar óf niet waar zijn. Denk hierbij bijvoorbeeld aan: De temperatuur is 15 graden Celsius. Dit is óf waar óf niet waar - dit in tegenstelling tot de zin: Het is hier koud. Hier kan iedereen een eigen mening over hebben en men kan dus niet uitmaken of deze bewering waar of niet waar is. >> Logica >> Voorwaardelijke opdrachten >> Lussen Lees ook de webpagina over controlestructuren op de Quick-R webstek: [ http://www.statmethods.net/management/controlstructures.html ]
Page 4 of 96
Dataframe Dataframe Een dataframe lijkt op een matrix. Het is ook een verzameling gegevens in tabel vorm. Maar in tegenstelling tot een matrix kunnen de gegevens van een verschillend type zijn. Je kunt in een dataframe numerieke gegevens samen weergeven met teksten (karakters). In die zin lijkt een dataframe nog het meeste op een spreadsheet. Die tekst is dan bijvoorbeeld te vinden in de eerste rij, op de kop van de kolommen, in de vorm van de namen van de verschillende variabelen waarvan de getallen in de kolommen staan. Maar het is ook mogelijk om teksten te gebruiken als waarden voor de variabelen, bijvoorbeeld als het nominale [kleur, factor, merk] of ordinale [slecht, goed, uitstekend] variabelen betreft. Een dataframe kan binnen R gemaakt worden door vectoren aan elkaar te plakken tot een tabel, met behulp van de functie data.frame: > help(data.frame) > > > > > 1 2 3
x y z d d
<<<<-
c("lengte", "hoogte", "breedte") c(4, 5, 6) c(7, 8, 9) data.frame(x, y, z)
x lengte 4 7 hoogte 5 8 breedte 6 9
y z
LET OP dat de vectoren x, y, en z dus kolomsgewijs aan elkaar worden geplakt tot een dataframe. De elementen in de kolommen van een dataframe moeten van het zelfde type zijn [of tekst, of getallen]. Dus in de eerste kolom staan in dit voorbeeld alleen maar woorden, en in de overige kolommen alleen maar getallen. Je kunt in een en dezelfde kolom niet getallen en tekst mengen, om dezelfde reden waarom dat in een vector ook mis gaat [de getallen worden dan nl automatisch in tekst omgezet]! In de praktijk zul je binnen R niet vaak op de bovenstaande manier een dataframe maken om je gegevens in tabelvorm op te slaan. Je zult dat veel eerder doen met een programma. Dan kan bijv de data-editor van R zelf zijn, edit(). Een spreadsheet programma als Excel is een meer voor de liggende optie. We moeten dus leren hoe we gegevens inlezen uit een file, om die vervolgens binnen R in de vorm te gieten van een dataframe. File inlezen en bewaren in een dataframe: Verzamel je gegevens en sla ze op in een Excel spreadsheet. Gebruik de eerste regel van dat spreadsheet om de namen van de variabelen in op te slaan. Let op dat je hele woorden gebruikt als naam. Alle waarnemingen staan op de volgende regels van dat spreadsheet:
Save je spreadsheet in text format (bijv 'filenaam.txt'). Het is het handigst als je die file opslaat in de working directory van R. Met het commando dir kun je controleren welke files in je working directory aanwezig zijn: Page 5 of 96
Dataframe > dir() [1] "R Console.txt"
"cohort"
"gegevens.txt"
"gegevens.xls"
"gm.r"
Type nu de volgende opdracht op de commandoregel van R: dataset<-read.table("gegevens.txt", header=TRUE) Je kunt ook het volledige pad naar de file gebruiken: dataset<-read.table("C:\\directory\\subdirectory\\gegevens.txt", header=T) Let op de dubbele backslash ( \\ ) die hier gebruikt wordt om de namen van de directories te scheiden. Een enkele backslash wordt in R gebruikt als escape karakter, bijvoorbeeld om het commando voor nieuwe regel, "\n", of tab, "\t", aan te duiden. Let ook op het argument TRUE (of eenvoudigweg T) wat er op duidt dat de eerste regel van de in te lezen datamatrix de namen van de variabelen bevat. De data zullen door de functie read.table worden ingelezen als dataframe met de naam dataset. Je kunt zelf controleren of de variabele dataset bestaat door het commando ls in te gebruiken: > ls() Je ziet een lijst van objecten die op dit moment in het geheugen van R aanwezig zijn. De functie dim vertelt je uit hoeveel rijen en kolommen het dataframe met de naam dataset bestaat: > dim(dataset) [1] 66 6 Met het commando names kun je de namen van de variabelen in het dataframe te weten komen: > names(dataset) [1] "lichaam" "arm"
"pols"
"geslacht" "hand"
"ogen"
Vervolgens zorg je er met het commando attach voor dat die variabelen ook onder die namen te gebruiken zijn in verdere analyses en bewerkingen: > attach(dataset) Je kunt een samenvatting van de inhoud van dataset maken door het commando summary te gebruiken: > summary(dataset) Uit een dataframe zijn op dezelfde manier elementen te selecteren als bij een matrix. > dataset[,1] # selecteer eerste kolom > dataset[c(1,3,5), c(2,3)] # selecteer kolom 2 en 3 uit rij 1, 3, en 5
Page 6 of 96
Debug Debug Wat doen we als een zo juist door ons gemaakte functie niet [goed] blijkt te werken? We hebben klaarblijkelijk bij intypen van de programmacode een fout gemaakt. Of misschien een logische fout, als de functie wel werkt maar er komt een verkeerd antwoord uit. We vragen ons vertwijfeld af wat voor fout het is, en in welke regel van de functie hij precies is gemaakt. Stel dat de functie gewoon niet goed werkt. Hij stopt voor dat ie een antwoord geeft, en er verschijnt een foutboodschap op het scherm. We kunnen verschillende strategiën volgen om de fouten op te sporen. De eerste vraag waarmee we zitten is de volgende: is het een toevalllige fout, of is de fout makkelijk weer op te roepen? Van toevallige fouten, die zo maar lijken te gebeuren zonder regelmaat of patroon, is het heel lastig om de oorzaak op te sporen. Je bent al een stuk dichter bij de oplossing als je de fout herhaaldelijk weet op te roepen. Neem het volgende voorbeeld. We willen met behulp van een functie de variatiecoefficient berekenen van een variabele door de standaarddeviatie te delen door het gemiddelde. Wat blijkt? Als de waarden van de variable armlengte positief zijn werkt de functie naar behoren, maar zodra armlengte nul is gaat het fout : > vacov <- function(x) { + sd(x / mean(x)) + } > armlengte <- 0 > cv(armlengte) Error in var(x, na.rm = na.rm) : missing observations in cov/cor In de foutboodschap wordt gesproken over de functie var(), die we echter helemaal niet gebruiken in onze functie vacov. Om uit te vinden waar de aanroep van var() vandaan komt gebruiken we de functie traceback(). > traceback() 3: var(x, na.rm = na.rm) 2: sd(x / mean(x)) 1: cv(armlengte) Dit lijstje met stappen laat zien dat var() wordt aangeroepen door de functie sd, en dat vervolgens in var() een fout (missing observations) optreedt. Onze berekening x / mean(x) moet die missing observation hebben veroorzaakt. Het regel voor regel uitvoeren van de code kan nu helpen. Dus net doen of de programma-regels samen een script vormen, en die regels stuk voor stuk knippen/plakken achter de prompt en kijken of ze werken. Bij een eenvoudige functie zoals sd in dit voorbeeld is dat een goede benadering. > mean(armlengte) [1] 0 > armlengte / mean(armlengte) [1] NaN Aha! We kunnen wel het gemiddelde uitrekenen van het getal 0, maar nul delen door nul geeft een missing value. De functie sd werkt dus alleen met een invoer die uit positieve getallen bestaat. We kunnen vervolgens onze functie aanpassen aan deze bevinding: > vacov <- function(x) { + stopifnot(all(x>0)) + sd(x / mean(x)) + } > armlengte <- 0 > cv(armlengte) Page 7 of 96
Debug Error:
all(x>0) is not TRUE
Zit de functie ingewikkelder in elkaar, bijvoorbeeld met [veel] voorwaardelijke opdrachten en lussen, en je wilt toch regel voor regel controleren wat er gebeurt en of er wat gebeurt, dan kun je dat doen door gebruik te maken van de functie debug. De naam van de functie waar je de fout uit wilt halen [wilt debuggen] moet je als argument meegeven bij de aanroep van debug(), bijvoorbeeld zo: > debug(mijn_programma) > mijn_programme(input) Browse[1]> mijn_programma
# zet de debugger aan om mijn_programma te volgen # start mijn_programma met reguliere input # de debugger staat te wachten bij regel 1 van
Pas door een Enter te geven wordt de volgende stap van mijn_programma uitgevoerd. Na iedere stap kun je nu controleren wat de waarden zijn van de variabelen die je er van verdenkt je programma ergens te laten crashen.
Page 8 of 96
Dump Dump Met de functie dump kun je de inhoud van 1 variabele naar een file wegschrijven. Bijvoorbeeld zo: 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)
En vervolgens wegschrijven naar een file: > sumco <- summary(cohort) > dump("sumco", file="summary.txt") Het resultaat van dump is een file die je, mocht je hem in een volgende sessie weer nodig hebben, met het commando source in kunt lezen in het geheugen van R, en die de variabele die je in die file hebt bewaard [in dit geval sumco] weer teruggeeft. source("summary.txt")
Page 9 of 96
FAQ FAQ Veel gestelde vragen en hun antwoorden Hoe schrijf ik het resultaat van R-commando's naar een file? : zie File wegschrijven Hoe lees ik input door middel van een file? : zie File inlezen Hoe lees ik input via het toetstenbord? : zie Input via toetsenbord Wat zijn de meest gebruikte rekenkundige functies? : zie rekenkundige standaardfuncties Wat zijn de logische operatoren? : zie logische operatoren Hoe kan ik de elementen van een vector sorteren? : met de functie sort [voorbeeld in paragraaf over vector]
Page 10 of 96
File inlezen Gegevens inlezen uit een file, en bewaren in een dataframe Omdat dataframes nog het meeste weg hebben van een spreadsheet is het de meest voor de hand liggende manier om gegevens in R in te lezen uit een file, zoals bijv een Excel spreadsheet. Verzamel je gegevens en sla ze op in een Excel spreadsheet. Gebruik de eerste regel van dat spreadsheet om de namen van de variabelen in op te slaan. Let op dat je hele woorden gebruikt als naam. Alle waarnemingen staan op de volgende regels van dat spreadsheet:
Save je spreadsheet in text format (bijv 'gegevens.txt'). In files met dit format zijn de oorspronkelijke kolommen uit excel nu gescheiden door spaties. Het is het handigst als je die file opslaat in de working directory van R. Met het commando dir kun je controleren welke files in je working directory aanwezig zijn: > dir() [1] "R Console.txt"
"cohort"
"gegevens.txt"
"gegevens.xls"
"gm.r"
Type nu de volgende opdracht op de commandoregel van R: dataset<-read.table("gegevens.txt", header=TRUE) Je kunt ook het volledige pad naar de file gebruiken: dataset<-read.table("C:\\directory\\subdirectory\\gegevens.txt", header=T) Let op de dubbele backslash ( \\ ) die hier gebruikt wordt om de namen van de directories te scheiden. Een enkele backslash wordt in R gebruikt als escape karakter, bijvoorbeeld om het commando voor nieuwe regel, "\n", of tab, "\t", aan te duiden. Let ook op het argument TRUE (of eenvoudigweg T) wat er op duidt dat de eerste regel van de in te lezen datamatrix de namen van de variabelen bevat. In het geval dat je de te gebruiken file totaal ergens anders hebt opgeslagen, en je bent ook niet van plan om die file in je working directory te gaan bewaren, dan kun je gebruiki maken van de functie file.choose() om de file te zoeken: dataset<-read.table(file.choose(), header=TRUE) Je krijgt dan de bekende dialoogbox op je scherm te zien met behulp waarvan je door directories kunt bladeren om je file te zoeken. Je kunt een rekenblad uit Excel ook bewaren in het zg cvs format. CVS is een afkorting voor comma separated variables. Dergelijke files kun je in R inlezen met de functie read.csv in plaats van read.table. Samenvattend: als de kolommen van de tabel gescheiden zijn door komma's gebruik je read.csv om de file in te lezen in R; als de kolommen van de tabel gescheiden zijn door spaties gebruik je read.table dataset<-read.table("gegevens.txt", header=TRUE) dataset<-read.csv("gegevens.csv", header=TRUE) Page 11 of 96
File inlezen De data zullen door de functie read.table worden ingelezen als dataframe met de naam dataset. Je kunt zelf controleren of de variabele dataset bestaat door het commando ls of objects in te gebruiken: > ls() > objects() Je ziet een lijst van objecten die op dit moment in het geheugen van R aanwezig zijn. De functie dim vertelt je uit hoeveel rijen en kolommen het dataframe met de naam dataset bestaat: > dim(dataset) [1] 66 6 Met het commando names kun je de namen van de variabelen in het dataframe te weten komen: > names(dataset) [1] "lichaam" "arm"
"pols"
"geslacht" "hand"
"ogen"
Vervolgens zorg je er met het commando attach voor dat die variabelen ook onder die namen te gebruiken zijn in verdere analyses en bewerkingen: > attach(dataset) Je kunt een samenvatting van de inhoud van dataset maken door het commando summary te gebruiken: > summary(dataset) Zie ook de webpagina over input van externe gegevens op de Quick-R webstek: [ http://www.statmethods.net/input/importingdata.html ]
Page 12 of 96
File Hoe wegschrijven schrijf ik het resultaat van R-commando's naar een file? Met behulp van de functie sink kan het resultaat van berekeningen naar een file worden geschreven. 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)
En dan nu een analyse uitvoeren en de resultaten naar een file wegschrijven: > > > > > > > >
# bereken summary statistics en ... # schrijf het resultaat naar een file met de naam summary.out # open eerst de file waar de output naar toe moet worden geschreven. sink("summary.out") # bereken summary statistics van de variabelen in dataframe cohort. summary(cohort) # sluit de file waar de output naar toe is geschreven. sink()
Met sink kun je dus de output van een reeks van commando's opvangen en naar een file laten schrijven. Met de functie dump kun je de inhoud van 1 variabele naar een file wegschrijven. Bijvoorbeeld zo: > sumco <- summary(cohort) > dump("sumco", file="summary.txt") Het resultaat van dump is een file die je, mocht je hem in een volgende sessie weer nodig hebben, met het commando source in kunt lezen in het geheugen van R, en die de variabele die je in die file hebt bewaard [in dit geval sumco] weer teruggeeft. source("summary.txt") Er is nog een manier om het resultaat van berekeningen naar een file te schrijven. Die werkt met behulp van de functie write.table. Deze functie werkt op dataframes : > samenv <- summary(cohort) > write.table(samenv, file="samen.txt") > dir() [1] "R Console.txt" "gegevens.txt" "gegevens.xls" "samen.txt" "summary.out" Als je een matrix of vector weg wilt schrijven naar file dan kun je de functie write gebruiken. > x <- c(1:10) > write(x, file = "output", ncolumns = if(is.character(x)) 1 else length(x), append = FALSE, sep = " ") Als je write niet vertelt hoe de file heet waarnaar de inhoud van de variabele moet worden weggeschreven dan gebruikt write de naam data. In het voorbeeld wordt de inhoud van de variabele vec10 geschreven naar de file met de naam output. Als je het aantal kolommen (ncolumns) niet Page 13 of 96
File wegschrijven specificeert dan worden er standaard 5 kolommen gebruikt. Wel iets waar je even op moet letten! Als de inhoud van de variabele de vorm heeft van een matrix, ben je wel gedwongen om het aantal kolommen goed aan te geven om de inhoud van de file de juiste vorm te laten krijgen!: > m <- matrix(c(1:21), ncol=7) > write(m, file = "output", ncolumns = 7) Het resultaat van al deze functies is een zg platte tekstfile; een tekst in zg ASCII format. Gegevens kunnen ook naar file worden geschreven in het eigen format van R, het zg XDR format. Om files in dit format te saven moet de functie save worden gebruikt: > save (lichaam, file="lichaam.RData") Deze data kunnen op later tijdstip weer ingelezen worden in het geheugen van R door middel van de functie load: > load("lichaam.RData") Lees ook de webpagina over het wegschrijven van gegevens op de Quick-R webstek: [ http://www.statmethods.net/input/exportingdata.html ] R kan zijn resultaten ook wegschrijven in de vorm van een webpagina. Hoe dat precies in zijn werk gaat wordt beschreven op de Quick-R webstek: [ http://www.statmethods.net/interface/output.html ]
Page 14 of 96
for-loop voorbeeld Een voedselregulatie model Veel technologische feedbacksystemen worden gecontroleerd door een mechanisme dat een proces start of stopt. De thermostaat van een verwarming is hier een goed voorbeeld van. Wanneer het in een kamer te koud wordt dan zorgt de thermostaat ervoor dat de verwarming aanslaat. Wanneer het in een kamer te warm wordt dan zorgt de thermostaat dat de verwarming uit gaat. Een biologisch voorbeeld van een dergelijk controlesysteem is het voedingsgedrag van ratten. In een sterk vereenvoudigd model is in het voedsel systeem een regelmechanisme ingebouwd dat een 'switch' op aan zet als de rat gaat eten en de switch op uit zet als de rat niet eet. Van dit probleem is onderstaand diagram gemaakt.
De darm dient in dit model als voedsel opslagplaats. Via de darmwand vindt energie toevoer naar het lichaam plaats. De snelheid van energie transport naar het lichaam is evenredig met het oppervlak van de darmwand. In dit model nemen we aan dat dit evenredig is met de wortel van de darm inhoud: M = km √D Hier is M de snelheid van energie overdracht van de darm naar het lichaam, km is de opnamecoëfficiënt en D is de hoeveelheid energie van het voedsel in de darmen. km kan gedurende een etmaal variëren. Omdat de rat een nachtdier is zal de coëfficiënt 's nachts hoger zijn dan overdag. Opname van voedsel vindt snel plaats, terwijl energieverbruik langzaam gaat. De snelheid van voedselopname, F, wordt berekend door: F = eI waarbij I de eetsnelheid is en e de feedback coëfficiënt (het regel mechanisme). Als de rat eet, is e gelijk aan 1. De darm stroomt dan met snelheid I vol. Als de rat niet eet is e gelijk aan 0. In ons model wordt het regelmechanisme aan- en uitgezet door de hoeveelheid energie M. Als M onder een bepaald niveau komt, Ml, dan zal de rat gaan eten. Als M boven een maximum komt, Mh, dan stopt de rat met eten en wordt e op 0 gezet. In dit model wordt aangenomen dat het gewicht van de rat constant blijft. De energie kan niet worden opgeslagen, alleen energie uit de darmen wordt gebruikt. Voedsel is ten alle tijden aanwezig. De uiteindelijke formule voor het systeem is: D(t) = D(t-1) + F - M We maken een algoritme waarin we 24 uur (1440 minuten) van de rat simuleren. We gebruiken tijdstapjes van 5 minuten. Gegeven zijn de volgende startwaarden: Om de 8 uur verandert de km van de rat (variërend van 1 naar 2 naar 3), afhankelijk van ochtend, dag, of nacht Variabelen en hun startwaarden: Ml = 18 cal/min Mh = 60 cal/min I = 500 cal/min D = 4000 cal Page 15 of 96
for-loop voorbeeld Begintijd is 7 uur 's ochtends Dit levert ons het volgende algoritme: voedselregulatie <- function() { I <- 500 Ml <- 18 Mh <- 60 D <- 4000 km <- 1 #km op 'ochtendstand' e <- 0 M <- km * sqrt(D) for (tijd in 1:288) { #Doe 288 tijdstappen if (M < Ml) { e <- 1 # zet eetsysteem aan } if (M > Mh) { e <- 0 # zet eetsysteem uit } F <- e * I # snelheid van darm-vullen D <- D + F - M # bereken darminhoud M <- km * sqrt(D) # energie overdracht cat(c(F, D, M),"\n") # schrijf diverse model parameters if (tijd == 96) { #om de 8 uur veranderd km km <- 2 # km op 'dagstand' } if (tijd == 192) { km <- 3 # km op 'nachtstand' } } # einde for lus } # einde functie definitie ‘Prachtig hoor’, zul je zeggen, ‘maar hoe kom ik hier nu aan als ik het algoritme zelf moet schrijven ? Geef eens wat richtlijnen ! ‘ Hoe bouwen we het algoritme op ? De beginregel is het eenvoudigst. Dat is de programma-definitie regel. Op die regel wordt de naam van het programma vastgelegd en, als daarom is gevraagd, ook de mogelijkheid om input (= parameters) mee te geven. In dit geval wordt er niet gevraagd om parameters over te kunnen dragen. Dat betekent dat onze programma definitie regel er als volgt uit moet zien: voedselregulatie <- function() { De volgende stap is de initialisatie-fase, en bestaat er uit dat we ons afvragen hoeveel variabelen er in het probleem dat we moeten oplossen een rol spelen, van welk type ('getal' of 'teken') ze zijn, als ze het type 'getal' hebben welke vorm (dimensie) ze moeten hebben (scalair, vector, of matrix: zie gegevensstructuren), en welke startwaarde de variabelen bij het begin van het programma moeten krijgen. In dit geval wordt het aantal variabelen bepaald door de formule waarmee we de toestand van het metabolisme per tijdstap uitrekenen. In die formule spelen een zevental variabelen een rol. In de eerste plaats de eetsnelheid I, vervolgens de snelheid van energie overdracht M, het minimum- en maximum niveau, Ml en Mh, daarvan, de opname coëfficient km, de hoeveelheid energie D van het voedsel in de darm, en tot slot de feedback coëfficient e. Daarmee is het lijstje van variabelen kompleet. Alle zeven hebben ze aan het begin een startwaarde nodig; ze moeten worden geinitialiseerd anders kunnen we er verder op in het programma niet mee rekenen. Verder moeten we er rekening mee houden Page 16 of 96
for-loop voorbeeld dat de waarde van de variabele M niet direct bekend is maar afhankelijk is van de waarde van km en D. M kan dus pas worden uitgerekend nadat km en D hun waarde hebben gekregen ! Het initialisatie blok komt er dan als volgt uit te zien: I Ml Mh D km e M
<<<<<<<-
500 18 60 4000 1 0 km * sqrt(D)
#km op 'ochtendstand'
OK, de voorbereidende stappen zijn achter de rug en we zijn nu aan het echte werk toe. Wat is het probleem ? We moeten voor een vaststaande periode (24 uur met 1440 minuten) in een bepaalde regelmaat (om de 5 minuten) volgens bepaalde formules de waarden van variabelen bepalen. De kern van het programma moet dus een lus bevatten, want we moeten om de 5 minuten steeds dezelfde formules toepassen. En aangezien we precies weten hoeveel blokjes van 5 minuten er in 24 uur gaan (288) ligt het soort lus ook vast, nl de voor-lus ! De structuur van een voorlus is als volgt: for (tijdstip in 1:totaalAantalTijdstippen) { opdracht 1 opdracht 2 opdracht 3 ... opdracht n } In R wordt dat: for (tijd in 1: 288 ) {
#Doe 288 tijdstappen
We weten ook wat we in moeten vullen voor het blokje opdrachten, namelijk de tot geldige R expressies omgeschreven formules ! Dat ziet er als volgt uit: F D M
<<<-
e * I D + F - M km * sqrt(D)
# snelheid van darm-vullen # bereken darminhoud # energie overdracht
Eenmaal uitgerekend moet de gebruiker van het programma worden verteld wat nu de waarden van de verschillende variabelen is geworden. Dat kunnen we doen door die waarden naar het scherm te schrijven met een SCHRIJF opdracht: cat(c(F, D, M),"\n")
# schrijf diverse model parameters
Daarmee is de kous nog niet af ! De waarde van een aantal van de variabelen is namelijk niet afhankelijk van de formule maar van het tijdstip van de dag (km) of van een bepaalde limiet overschrijding (e). Dat betekent dat we nog een aantal voorwaardelijke opdrachten moeten formuleren waarin die wisselende omstandigheden tot uitdrukking worden gebracht. De eerste serie voorwaarden betreffen de snelheid van energie overdracht. Zodra we voor de eerste keer M hebben uitgerekend (in het initialisatie blok) moeten we controleren of de minimum waarde Ml dan wel de maximum waarde Mh van M is overschreden. In die gevallen moeten we namelijk de waarde van de feedback coëfficient e wijzigen. Direct na het begin van de voorlus, maar nog voor het formule blok, komen deze voorwaarden te staan: if
(M < Ml) { e <- 1 } if (M > Mh) {
# zet eetsysteem aan
Page 17 of 96
for-loope voorbeeld <- 0
# zet eetsysteem uit
} De andere variabele die voorwaardelijk verandert is km, de opnamecoëfficient. Deze verandert afhankelijk van het tijdstip. We weten dat we de simulatie om 7 uur ‘s ochtends beginnen, en dus weten we de begin toestand van km, nl de ochtendstand. Maar km moet veranderen zodra we 8 uur verder zijn, en weer een keer na nog eens 8 uur. Die voorwaardelijke veranderingen in de waarde van km komen na het formule blok. (maakt dat wat uit, of we km voor of na het formule blok controleren ?): if (tijd == km <} if (tijd == km <}
96) { 2
#om de 8 uur veranderd km # km op 'dagstand'
192) { 3 # km op 'nachtstand'
Hiermee zijn de bouwstenen van het algoritme kompleet. Nu alles nog in de goede volgorde zetten.
Page 18 of 96
Fout afhandeling Fout afhandeling R kent de return functie. Die kun je gebruiken om een zelf geschreven functie/programma direct met zijn werk te laten stoppen. Een van de manieren om de return functie toe te passen is bij de controle op de kwaliteit van de input van een programma. Als de input niet overeenkomt met de afspraak, bijvoorbeeld dat het een getal moet zijn en niet een tekst, dan kunnen we een if-statement samen met een return-statement gebruiken om het programma direct te laten stoppen. Bijvoorbeeld: mijn.func <- function(input) { if ("character" == mode(input)) { # controleer input cat("input moet numeriek zijn...!") return() } output <- 2 * input # output regel } Je kunt in dit geval ook de volgende if-statement gebruiken: if (is.character(input)) { # controleer input cat("input moet numeriek zijn...!") return() } Je kunt de foutboodschap ook als argument van return meegeven: if (is.character(input)) { # controleer input msg <- "input moet numeriek zijn...!" return(msg) } Nog een voorbeeld: Je moet controleren of het eerste argument van de functie groter is dan het tweede, en het tweede groter dan nul: mijn.func <- function(x,y) { if (! (x > y & y > 0)) { # controleer input cat("eerste argument moet groter zijn dan het tweede, en ...\n") cat("het tweede argument moet groter zijn dan nul. \n") return() } output <- x / y # output regel } Als je functie expliciete output moet leveren, bijvoorbeeld zo: > output <- mijn.func(input) dan gaat dat fout bij bovenstaande voorbeelden omdat er nog helemaal niets toe te wijzen valt aan de variabele output, omdat die nog niet door de functie is berekend. return doet de functie ogenblikkelijk stoppen, maar we kunnen tegelijkertijd zorgen dat de functie alsnog output genereert door die output als argument met return mee te geven. Bijvoorbeeld: mijnprogramma<-function(x,y,z) { # maak verstekwaarde voor output [lege vector] output <- NULL # invoer moeten getallen zijn, groter dan nul: Page 19 of 96
Fout afhandeling if (is.character(x)
| is.character(y) | is.character(z)) { print("alle argumenten moeten numeriek zijn") return(output) # klaar, maak uitvoer expliciet
} if(x<=0 | y<=0 | z<=0) { print("alle ingevoerde getallen moeten groter zijn dan 0") return(output) # klaar, maak uitvoer expliciet } output <- x / y * z # normale expliciete uitvoer
Page 20 of 96
Functie definitie 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 nadat we op de laatste regel met Page 21 of 96
Functie definitie een } de functie definitie 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. > bron <- "http://biology.leidenuniv.nl/~zandee/statistiek/data/ gegevens.txt" > cohort <- read.table(bron, 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! [Maar wat doen we nou als de functie niet blijkt te werken? -> debuggen !] 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! Page 22 of 96
Functie definitie 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) Page 23 of 96
Functie definitie 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. Zie ook de webpagina over functie definitie op de Quick-R webstek: [ http://www.statmethods.net/management/userfunctions.html ]
Page 24 of 96
Functies Functies Een van de grote voordelen van R is het beschikbaar zijn van een zeer groot aantal functies. Dat zijn kant en klare programma's die in R gebruikt kunnen worden voor het verrichten van allerlei taken en berekeningen. Er zijn bijvoorbeeld functies voor het uitvoeren van wiskundige berekeningen en statistische toetsen. Dat kunnen heel eenvoudige functies zijn zoals bijvoorbeeld het uitrekenen van de variantie van een steekproef, maar ook veel ingewikkelder zoals die voor het uitvoeren van multivariate analyses. Functies worden in R afhankelijk van de taken die ze kunnen uitvoeren gebundeld tot zg pakketten (packages). Zo zijn alle functies die nuttig en nodig zijn voor het doen van allerlei statistische analyses gebundeld in het pakket stats. Dat zijn er 494, en dus een beetje veel om hier de revue te laten passeren. En in het base pakket dat bij iedere start van R automatisch wordt geladen zitten 1046 functies! > ls("package:base") Dan blijkt dat bijvoorbeeld ook het symbool-paar '<-' een functie is! Functies lijken erg op scripts. Net als scripts bestaan functies uit een serie R opdrachten. Maar het grote verschil tussen functies en scripts is dat je bij het aanroepen van een functie een variabele mee kunt geven waarmee de functie moet werken, dus zo: functie(naam_van_variabele) De waarde van de variabele noemen we een parameter voor de functie. De variabele zelf is het argument van de functie. De functie heeft in dit voorbeeld zo gezegd dus 1 argument, nl die ene variabele. mean(x) en length(x) zijn voorbeelden van een R-functies die 1 argument nodig hebben, nl een vector. In dit voorbeeld heeft de vector de naam x. Andere functies hebben meerdere argumenten nodig bij hun aanroep. Dit is dan de algemene vorm: > functienaam(arg1,arg2,. . . ) De ronde haakjes zijn verplicht, zelfs als de functie geen argumenten behoeft. Zonder de haakjes reageert R met het listen van de definitie van de functie. > functienaam() # voer de functie functienaam uit (zonder argumenten) > functienaam # list de definitie van de functie functienaam Een ander belangrijk verschil tussen functies en scripts zit'm in het bereik, de zg scope, van de variabelen die in een functie of script worden gemaakt. In een script zijn alle variabelen globaal. Dat wil zeggen dat een script alle variabelen kan zien [en er dus ook mee kan werken] die in het geheugen van R aanwezig zijn, en ook dat na afloop van het script alle variabelen die in het script zijn gedefinieerd in het geheugen van R beschikbaar zijn gekomen. De scope van in een script gedefinieerde variabelen is dus heel breed - breder kan in feite niet. In een functie daarentegen hebben alle variabelen die in de functie worden gedefinieerd een lokaal Page 25 of 96
Functies karakter. Als een functie eenmaal werkt dan ziet hij niet meer de variabelen die in het geheugen van R aanwezig zijn, en die je met het commando ls kunt zien. Een functie kent na zijn start alleen de inhoud van de variabelen die als argument worden meegegeven, en verder alleen die variabelen die tijdens het 'draaien' van de functie in de functie zelf worden gemaakt. Is de functie eenmaal klaar met zijn werk dan zijn ook alle interne, lokale variabelen verdwenen en niet meer toegankelijk. De scope van lokale variabelen is dus heel beperkt. > + + +
printx <- function() { botje <- 12 print(botje) }
> printx() [1] 12 > botje Error: object "botje" not found In het bovenstaande voorbeeld wordt in de functie printx een waarde toegekend aan een variabele met de naam botje. De inhoud van botje wordt vervolgens geprint. Daarna is de functie afgelopen. Als we vervolgens vragen naar de inhoud van botje, dan kan botje niet gevonden worden in de werkruimte. Dat klopt, want botje is een lokale variabele van de functie printx: botje bestaat alleen zolang de functie printx aan het werk is. Daarna niet meer. Je kunt zorgen dat lokale variabelen toch globaal beschikbaar komen. Dat gebeurt met variabelen die nadrukkelijk als output van de functie worden gedefinieerd. Dergelijke output variabelen zorgen ervoor dat je het resultaat van de functie toe kunt kennen aan een variabele, dus zo: output <- functie(argument) >> Functie definitie Zie ook de webpagina met veel standaardfuncties van R op de Quick-R webstek: [ http://www.statmethods.net/management/functions.html ]
Page 26 of 96
Gegevensstructuren Gegevensstructuren Eén of meerdimensionele objecten waarin gegevens van 1 of meerdere types [numeriek, karkater] kunnen worden opgeslagen. (zie ook: wikipedia_nl, wikipedia_eng) Vector Lijst Matrix Dataframe Zie ook de webpagina over gegevensstructuren op de Quick-R webstek: [ http://www.statmethods.net/input/datatypes.html ]
Page 27 of 96
Grafieken Grafieken [bron: A.W. van der Vaart - Handleiding R] Het produceren van grafieken behoort tot de kern van het R-programma. Grafische output kan zowel op het scherm worden bekeken als op papier worden geprint. Wanneer een plotopdracht gegeven wordt zal een graphics window worden geopend . De output van plotopdrachten verschijnt automatisch in dit graphics window. Grootte en plaats van het graphics window kunnen op de gebruikelijke manier worden veranderd. Na verandering van de grootte wordt het plaatje opnieuw getekend, op zo’n manier dat de grafiek het gehele window vult. Er zijn twee soorten plotopdrachten in R. - High level plotfuncties produceren volledige plaatjes en wissen zonodig de output van een voorgaande plotopdracht uit. - Low level plotfuncties zijn bedoeld om grafische output aan een eerder gemaakt plaatje toe te voegen. Voor het maken van twee grafieken in één plaatje is derhalve één high level plotfunctie nodig, en één low level plotfunctie. De meest gebruikte plotfunctie is plot. Zonder opties levert deze een scatterplot van de elementen van de ene vector tegen die van een tweede vector. > x <- rnorm(50); y <- rnorm(50) > plot(x,y,main="figuur 1") Met de optie type="l" worden opeenvolgende punten verbonden door lijnstukjes. Dit maakt plot geschikt voor het plotten van een grafiek. > u <- seq(0,4*pi,by=0.05); v <- sin(u) > plot(u,v,type="l",xlab="",ylab="sin",main="figuur 2") Andere high level plotting functies maken meer specifieke plots. Plots van statistische betekenis zijn het histogram , de boxplot en de qq-plots . > > > > >
x <- rnorm(50) hist(x, main="figuur 3") text(0,5,"histogram") # print "histogram" in het punt (0,5) boxplot(x, main="figuur 4") qqnorm(x, main="figuur 5")
De range van de assen in een plot is gelijk aan de waarden in de vectoren xlim en ylim. Bij simpel gebruik is het handig om R default waarden voor deze vectoren te laten kiezen. Hetzelfde geldt voor de labels op de assen, en boven- en ondertitels. De tabel geeft een overzicht van enkele van zulke optionele argumenten. Vele andere aspecten van de plots kunnen worden gecontroleerd met par. Zie help voor nadere informatie. De functie lines is de low level versie van plot(. . .,type="l"). Deze functie kan worden gebruikt om een tweede grafiek over een eerste heen te tekenen. Beschouw bijvoorbeeld het plotten van een histogram en de ware dichtheid van een normale steekproef in één plaatje. > x <- rnorm(100) > range(x) [1] -3.226397 2.001791 # default wordt de range in hist(x): (-4,3) > hist(x,xlim=c(-4,4), xlab="", prob=T, main="figuur 6") > u <- seq(-4,4,by=0.1) > lines(u,dnorm(u)) Page 28 of 96
Grafieken Meerdere plaatjes kunnen naast en onder elkaar in één plot getekend worden met behulp van de functie par. > par(mfrow=c(r,k)) De eerst volgende (r,k) plots worden dan in één plaatje gezet, in respectievelijk r rijen en k kolommen. Sommige R-functies maken gebruik van de muis. Met de functie identify(x,y,labels) kun je erachter komen welk punt een gegeven punt in een puntenwolk is, bijvoorbeeld een uitbijter. > plot(x,y) > identify(x,y,labels) Klik vervolgens met de linker knop van de muis op de plaats van een punt. Als dit het punt (x[i],y[i]) is, verschijnt vervolgens op de plaats van het punt de i-de coördinaat van de character vector labels. Het default label is i. Je kunt dit herhalen. Een druk op de rechter knop van de muis beëindigt identify(). Het R-commando locator is handig als je de precieze coördinaten van een aantal punten op het scherm wilt weten, bijvoorbeeld om daar tekst af te drukken. Start met het typen van: > z <- locator(n) Klik vervolgens op n punten in het graphics window. De coördinaten van deze punten worden opgeslagen in de list z. Het is extra handig om locator te combineren met text. > text(locator(1),"Tekst op de juiste plaats") De tekst verschijnt nu op de plaats in de grafiek waar met de muis geklikt wordt. Door na locator(n=2,type="l") op twee punten in het graphics window te klikken kan een lijnstuk in de grafiek worden getekend. optienaam xlab="xlabel" ylab="ylabel" xlim=c(l,r) ylim=c(l,h) main="titel" sub="subtitel" lty=n
betekenis Label op de x-as Label op de y-as Range van de x-as van l tot r Range van de y-as van l tot h Titel boven de plot Titel onder de plot Lijntype: n=1: normaal; n=2, 3,. . .: gebroken
Enkele High Level Plotfuncties plot(x,y) plot(x,y,type="l") plot(x,y,type="n") plot(x,y,type="s") plot(mat) matplot(matx,maty) boxplot(x) qqnorm(x) qqverd qqplot(x,y) persp(z) hist(x)
Scatterplot de punten (x[i],y[i]). Plot de punten (x[i],y[i]) met opeenvolgende punten verbonden door een lijn. Plot de assen, maar niet de punten. Plot een trapfunctie. Plot de punten (mat[i,1],mat[i,2]). Plot kolommen van matrices tegen elkaar. Boxplot van vector x. qq-plot tegen de normale verdeling. qq-plot tegen de kansverdeling met code verd. Empirische qqplot van x tegen y. Quasi drie-dimensionale plot. Histogram van vector x. Page 29 of 96
Grafieken barplot(x)
Staafdiagram van elementen in vector x
Enkele Low Level Plotfuncties Plot van de punten (x[i],y[i]) met opeenvolgende punten verbonden door een rechte. title("titel","ondertitel") Print titels. text(x,y) Plot element-nummers op de punten (x[i],y[i]). text(x,y,label) Plot de karakter string label[i] op (x[i],y[i]). abline(a,b) Voeg de lijn y=a+bx toe. points(x,y) Voeg de punten (x[i],y[i]) toe. polygon(x,y) Veelhoek met hoekpunten (x[i],y[i]). mtext("tekst",side,line) Plot tekst in een kantlijn. legend() Voeg een legenda toe. lines(x,y)
Door het onderscheid in high- en low-level plotfuncties is het mogelijk om plaatjes in stappen op te bouwen. We maken een histogram op basis van 2000 random standaard normaal verdeelde getallen: > > > + +
par(las=1) # alle tekst langs de assen graag horizontaal afdrukken x <- rnorm(2000) # 2000 random standaard normaal verdeelde getallen hist(x, main="Dichtheid 2000 N(0,1) verdeelde random getallen", freq=FALSE, col = "grey", ylab="dichtheid", xlim=c(-5, 5), ylim=c(0,0.6))
Dichtheid 2000 N(0,1) verdeelde random getallen 0.6 0.5
dichtheid
0.4 0.3 0.2 0.1 0.0 −4
−2
0
2
4
x Vervolgens voegen we de theoretische dichtheidscurve toe aan de plot: > curve(dnorm, from=-5, to=5, add=TRUE, lwd=3, lty=2)
Page 30 of 96
Grafieken Dichtheid 2000 N(0,1) verdeelde random getallen 0.6 0.5
dichtheid
0.4 0.3 0.2 0.1 0.0 −4
−2
0
2
4
x Daarna doen we nog iets aan de legenda: > legend(-4.5, 0.6, legend=c("emp. dichth.", "theor. dichth."), + col=c("grey", "black"), lwd=5)
Dichtheid 2000 N(0,1) verdeelde random getallen 0.6
emp. dichth. theor. dichth.
0.5
dichtheid
0.4 0.3 0.2 0.1 0.0 −4
−2
0
2
4
x En tot slot voegen we nog een formule: Page 31 of 96
Grafieken > text(-5, + + > + +
0.45, adj=0, cex=1.3, expression(f(x)==frac(1, sigma*sqrt(2*pi)) ~~ e^{fraq(-(x-mu)^2, 2*sigma^2)})) text(-5, 0.3, adj=0, cex=1.3, expression(f(x)==frac(1, sigma*sqrt(2*pi)) ~~ e^{frac(-(x-mu)^2, 2*sigma^2)}))
en wat tekst toe aan de plot: > text(5, 0.45, adj=1,cex=1.3, + expression(paste("met ",mu==0, ", ", sigma==1)))
Dichtheid 2000 N(0,1) verdeelde random getallen 0.6
emp. dichth. theor. dichth.
0.5
f(x) = dichtheid
0.4
−(x− −µ)2 2σ σ2
1 σ 2π π
e
met µ = 0, σ = 1
0.3 0.2 0.1 0.0 −4
−2
0
2
4
x Het is mogelijk om in plot verschillende symbolen te gebruiken voor de punten die worden geplot. Plotsymbolen worden aangewezen door de parameter pch een geheel getal tussen de 1 en 25 te geven. Het is alleen lastig om te onthouden welk symbool precies bij welk getal hoort. Onderstaande tabel kan hierbij helpen.
Page 32 of 96
Grafieken
Referenties: Website van Paul Murrell's boek R Graphics, met heel veel voorbeelden van plots en R-code. Website van John Maindonald en John Braun's boek Data Analysis and Graphics Using R - An Example-Based Approach, met plot voorbeelden en R-code. Zie ook Plots in R (.pdf; Crawley) Grafieken wegschrijven in een file Zie Plot saven. Zie ook het hoofdstuk over grafieken op de Quick-R webstek: [ http://www.statmethods.net/graphs/index.html ]
Page 33 of 96
Help Help R beschikt over een uitgebreid systeem van Help files. Help kun je opvragen aan de prompt. Bijvoorbeeld: > help.start() # algemene Help voor R > help(foo) # helpfile voor de functie met de naam foo > ?foo # idem > example(foo) # laat een voorbeeld zien van het gebruik van de functie foo > # zoek naar foo in help manuals en archieven van mailing lists > RSiteSearch("foo")
Page 34 of 96
Impliciete lus Impliciete lus Een van de sterke punten van R is dat je in veel gevallen zonder lus constructie toch alle elementen in een vector of een matrix kan bewerken. Je kunt bijvoorbeeld in 1 keer een getal optellen bij een matrix of een vector, zonder dat je een lus moet programmeren waarin je alle elementen van de matrix of vector 1 voor 1 bezoekt om er vervolgens 1 bij op te kunnen tellen, of met 10 te vermenigvuldigen: > x <- 1:10 > y <- x * 10 > y [1] 10 20 30 40 50 60 70 80 90 100 Verder kent R nog de functie apply met behulp waarvan we een willekeurige functie (van R, of zelf geschreven) kunnen toepassen op de rijen of kolommen van een matrix. Stel we willen de getallen in de kolommen van matrix QQQ optellen. Het is niet nodig daarvoor een lus te schrijven. We kunnen apply gebruiken: # eerst even de data binnenhalen > QQQ<-read.table("http://biology.leidenuniv.nl/~zandee/ab07/data/ data.txt") # som van alle elementen in elke kolom > kolsom <- apply(QQQ, 2, sum) > kolsom V1 V2 V3 11711.5 4860.0 1064.1 # gemiddelde van alle elementen in elke kolom > kolgem <- apply(QQQ, 2, mean) kolgem V1 V2 V3 177.44697 73.63636 16.12273 Om de functie sum op de rijen toe te passen i.p.v. de kolommen wijzigen we de het argument 2 van apply in een 1 [de eerste index van een matrix geeft de rijen aan, de tweede de kolommen]. Er bestaat ook een functie lapply die het zelfde werkt als apply, maar dan op een lijst in plaats van een matrix. En dan is er nog de functie tapply die op vectoren werkt. De functie tapply heeft een argument nodig waarmee de elementen in de vector worden gegroepeerd, een zg factor. We willen bijvoorbeeld de gemiddelde lichaamslengte weten van personen gegroepeerd op oogkleur: # eerst even de data binnenhalen > QQQ<-read.table("http://biology.leidenuniv.nl/~zandee/ab07/data/ gegevens.txt") > attach(QQQ) > names(QQQ) [1] "lichaam" "arm" "pols" "geslacht" "hand" "ogen" > tapply(lichaam, ogen, mean) blauw bruin grijs groen zwart 175.7500 178.0769 176.0000 180.6190 154.0000 Het is dus niet nodig om een lus te schrijven waarin we de verschillende elementen in de vector lichaam bezoeken, en afhankelijk van hun waarde in de variabele ogen de gemiddelde lichaamslengte berekenen. Zoals bijvoorbeeld: > > + +
blauw<-bruin<-grijs<-groen<-zwart<-NULL for(teller in 1:length(lichaam)){ if(ogen[teller]=="blauw") blauw<-c(blauw,lichaam[teller]) if(ogen[teller]=="bruin") bruin<-c(bruin,lichaam[teller]) Page 35 of 96
Impliciete lus + if(ogen[teller]=="grijs")
grijs<-c(grijs,lichaam[teller]) + if(ogen[teller]=="groen") groen<-c(groen,lichaam[teller]) + if(ogen[teller]=="zwart") zwart<-c(zwart,lichaam[teller]) + } > c(mean(blauw),mean(bruin),mean(grijs),mean(groen),mean(zwart)) [1] 175.7500 178.0769 176.0000 180.6190 154.0000
Een ander bijzonder krachtig instrument om impliciet lussen mee te programmeren is de functie replicate. Replicate heeft 2 argumenten: het eerste moet een positief geheel getal x zijn. Het tweede argument is de expressie die x keer zal worden uitgevoerd. We willen bijvoorbeeld een histogram maken van de uitkomst van 1000 experimenten in elk waarvan we de gemiddelde waarde berekenen van 100 worpen met een dobbelsteen: hist(replicate(1000, mean(sample(1:6, 100, replace=TRUE)))) De functie sample trekt 100 keer random een getal uit de reeks 1, 2, 3, 4 , 5, 6, met teruglegging. We berekenen voor elke 100 trekkingen het gemiddelde, en dat berekenen van het gemiddelde over 100 trekkingen herhalen we 1000 keer.
Page 36 of 96
Index Inleiding Programmeren, met R. Introductie Dit college is er op gericht om je kennis van, en je vaardigheid in het gebruik van de taal R te verbreden en te verdiepen ivm de colleges Statistiek-1 in blok 3, 5 en 6, en tevens programmeervaardigheid bij te brengen die straks in het college Modelleren [blok 4, 5 en 6] goed van pas komt. Daarom is Inleiding Programmeren een naam die voor dit college in feite beter van toepassing is dan de oude naam Algoritmiek. Het hoofdoel van college en werkgroep is om de vertaalstap van biologisch probleem naar geprogrammeerde oplossing te leren maken. Het voordeel van R is dat we zowel bij het college Statistiek als bij Modelleren een en dezelfde [programmeer]taal R kunnen gebruiken. Er is wel een handicap. Een boek waar alles in staat wat je nodig hebt om met programmeren te starten is er niet voor R. Wel een grote serie tutorials. Heel veel info in heel veel verschillende bronnen. In dat opzicht is goed leren werken met R lastiger dan het leren van bijvoorbeeld Python (How to think like a computer scientist [pdf-versie]). Als de Duitse taal geen probleem voor je is kan ik je Programmieren mit R van Uwe Ligges aanraden. Veel onderwerpen die in het Python boek aan de orde komen zijn ook van toepassing op R. Dat zijn zaken die in algemene zin betrekking hebben op de kunst van het probleem oplossen met behulp van de computer, en niet met die van de daarbij gebruikte taal in het bijzonder. Daarom heeft dat boek ook als ondertitel: Learning with Python, en niet: Learning Python. Voor ons wordt dat nu dus Learning with R , vandaar de naam: Inleiding Programmeren, met R. Om een indruk te krijgen wat Algoritmiek en Programmeren inhoudt is het doorlezen van het eerste hoofdstuk van het boek over Python ook voor R een goede start, zolang je overal waar Python gedrukt staat gewoon R leest. Hoofdstuk 1 en hoofdstuk 2 van de [oude] Algoritmiek syllabus geven eveneens een inleiding met dezelfde strekking.
Page 37 of 96
Indexeren Indexeren Subscripting of Indexeren is het selecteren van elementen van een vector, matrix, lijst of dataframe. We laten hier alleen basale voorbeelden van het gebruik van een indices zien in vectoren. We maken eerst de variabelen waar we mee gaan werken: > x <- c(1,2,3,4,5,6,7,8,9,10) > y <- 3*x - 12 > y [1] -9 -6 -3 0 3 6 9 12 15 18 Om te indexeren gebruiken we rechte haken [ en ] > y[3] # het derde getal uit y [1] -3 > y[3:5] # het derde tot en met vijfde getal uit y [1] -3 0 3 > y[c(1,3,5)] # het eerste, derde, en vijfde getal uit y [1] -9 -3 3 Negatieve selectie kan ook > y[-3] # laat het derde element weg [1] -9 -6 0 3 6 9 12 15 18 > y[-c(1,3,5)] # laat elementen 1,3 en 5 weg [1] -6 0 6 9 12 15 18 We kunnen een index ook gebruiken om elementen van een vector te vervangen door andere: > y[c(1,3,5)] <- 0 # vervang het eerste, derde, en vijfde getal door 0 [1] 0 -6 0 0 0 6 9 12 15 18 We kunnen ook selecteren met behulp van een Boolese vector. We selecteren alle elementen van y die groter zijn dan 0 door: > y[y>0] [1] 3 6 9 12 15 18 > x <- 1:10 > x[x>3] <- 'hallo' > x [1] "1" "2" "3" "hallo"
"hallo" "hallo" "hallo" "hallo" "hallo" "hallo"
Je kunt in het laatste voorbeeld zien dat je in een vector dus geen getallen mag mengen met tekst. De getallen worden dan nl automatisch in tekst omgezet. Nog niet bestaande elementen van een vector kunnen we wel alvast aanwijzen met een index, om ze daarmee een waarde toe te kennen, zoals het volgende voorbeeld laat zien: > x <- 1:5 > x [1] 1 2 3 4 5 Page 38 of 96
Indexeren > x[7:9]
<- 7:9 # ken een waarde toe aan het 7e, 8e, en 9e element van x > x [1] 1 2 3 4 5 NA 7 8 9 # het 6e element is daarna dus NA (Not Available)
Dat kan ons van pas komen als we in een lus een vector willen vullen met waarden, waarbij we beginnen met een lege vector: > x <- NULL > x NULL > for(i in 1:10) {x[i] <- i} # vul x met de getallen 1 t/m 10 > x [1] 1 2 3 4 5 6 7 8 9 10 Samenvattend zijn er dus verschillende manieren om gegevens uit een vector te selecteren. Hier is een kort overzicht. Veronderstel dat x een vector met getallen is, bijvoorbeeld: > > > > > > > > > > > >
x <- 1:10 length(x) # hoeveel elementen zitten er in x? x[2] # het ide element, (i = 2) x[-2] # alle elementen, behalve het ide, (i = 2) x[1:5] # de eerste k elementen, (k = 5) x[(length(x)-5):length(x)] # laatste k elementen, (k = 5) x[c(1,3,5)] # bepaalde elementen (eerste, 3e en 5e) x[x>3] # alle waarden groter dan een bepaalde,(de waarde is 3) x[x>3]<- 0 # ... en vervang die door het getal 0 x[]<- 2 # vervang alle elementen van x door het getal 2 x[ x< -2 | x > 2] # groter dan of kleiner dan een bepaalde waarde which(x == max(x)) # op welke plaats staat het grootste getal?
Labels Aan de elementen in een vector kunnen labels worden gekoppeld. Dat gebeurt met behulp van de functie names. > z > x > x [1] > z [1]
<- c('orde', 'familie', 'geslacht', 'soort') <- 4:1 4 3 2 1 "orde"
"familie"
"geslacht" "soort"
> names(x) NULL > names(x)<-z > x orde familie geslacht 4 3 2
soort 1
De labels kunnen worden gebruikt om elementen uit de vector te selecteren: > x[c('orde','geslacht')] orde geslacht 4 2 Page 39 of 96
Indexeren De labels kunnen weer worden verwijderd door de waarde NULL er aan toe te kennen. > names(x)<-NULL > x [1] 4 3 2 1 Zie ook de webpagina over selectie op de Quick-R webstek: [ http://www.statmethods.net/management/subset.html ]
Page 40 of 96
Input via toetsenbord Input via toetsenbord R kent twee functies om input via het toetsenbord op te vangen en in een variabele op te bergen: Scan Readline
Page 41 of 96
Input-Output Input / Output File inlezen Input via toetsenbord File wegschrijven Output via toetsenbord Zie ook het hoofdstuk over Input-Output op de Quick-R webstek: [ http://www.statmethods.net/interface/io.html ]
Page 42 of 96
Installeer R Installeer R R is een gratis verkrijgbaar softwarepakket, wat voor vele platforms beschikbaar is, en vrij eenvoudig is te installeren. 1. Ga naar de website van R, en kies daar onder de kop Download and Install R het gewenste platform [Windows]
2. Kies in het daarop volgende venster voor: base - Binaries for base distribution
3. Klik in het daarop volgende venster met de rechter muisknop op de link met file naam [R-2.6.0Page 43 of 96
Installeer R win32.exe], en kies Save link as...
4. Kies Save in het daarop volgende dialoogvenster
5. Kies in het daarop volgende dialoogvenster een geschikte plek om deze file te saven [desktop?], en klik vervolgens Save.
Page 44 of 96
Installeer R
6. Ga [met behulp van Windows Explorer] naar de ge-downloade file, dubbelklik deze om de installatie te starten, en volg daarna de aanwijzingen van de installatie-wizard.
Page 45 of 96
Installeer R
7. Na afloop van de installatie heeft R een shortcut op de desktop geinstalleerd, waarmee het programma gestart kan worden [Start R]. Je kunt ook deze link gebruiken om R voor Windows te downloaden; doe daarna stap 6 en 7.
Page 46 of 96
Lijst Lijst Een lijst lijkt veel op een vector. Het is ook een verzameling gegevens in rij vorm. Maar in tegenstelling tot een vector kunnen de gegevens in een lijst van een verschillend type zijn. Je kunt in een lijst numerieke gegevens samen weergeven met teksten (karakters). Je kunt zelfs weer lijsten opbergen in een lijst. Een lijst kun je maken door de functie list te gebruiken: > help(lijst) > mijnlijst <- list(c(45, 34,21), 10:1, 1024) > mijnlijst # laat zien wat er in mijnlijst staat [[1]] [1] 45 34 21 [[2]] [1] 10
9
8
7
6
5
4
3
2
1
[[3]] [1] 1024 Wat direkt opvalt is dat er dubbele teksthaken worden gebruikt om de plaats van de verschillende objecten in mijnlijst aan te duiden: > mijnlijst[[1]] elementen) [1] 45 34 21
# laat het eerste object uit de lijst zien (vector met 3
Ik moet weer enkele teksthaken gebruiken om een selectie uit een selectie te doen, zoals bijv het derde element van de vector die zelf het eerste object in mijnlijst is: > mijnlijst[[1]][3] # laat van het eerste object het derde element zien [1] 21
Page 47 of 96
Links Links The R Project for Statistical Computing Download R R for Windows FAQ An Introduction to R [html] An Introduction to R [pdf] R-intro [Geyer and Jones, 2006] Simple R, by Verzani [pdf] Data import / export in R Quick-R
Page 48 of 96
Logica Logica Logica gebruiken we in algoritmen als niet alle opdrachten altijd uitgevoerd moeten worden (zoals bij opeenvolging wel altijd het geval is). We willen bepaalde opdrachten bijvoorbeeld alleen uit laten voeren als een bepaalde bewering waar of niet waar is. De beweringen die wij gaan gebruiken in voorwaardelijke opdrachten zijn over het algemeen vergelijkingen (bv. T=1, A
B TRUE FALSE TRUE FALSE
A en B TRUE FALSE FALSE FALSE
A of B TRUE TRUE TRUE FALSE
niet B FALSE TRUE FALSE TRUE
A xof B FALSE TRUE TRUE FALSE
Volgens de regels der logica is een zin als: Wil je koffie of thee? altijd met ja te beantwoorden (als je één van de twee wilt hebben). Welke van de twee je wilt hebben maakt dan niet uit (In welke betekenis wordt de OF hier gebruikt ?). Nog enkele voorbeelden: 2==2 geeft TRUE 3==2 geeft FALSE Page 49 of 96
Logica3>4 7>(5+1)
geeft FALSE geeft TRUE
Samenvattend kennen we de volgende logische operatoren: > < >= <= == != & && | || !
groter dan kleiner dan groter of gelijk dan kleiner of gelijk dan is gelijk aan is ongelijk aan logische EN logische EN; reduceert eventuele vector in antwoord tot 1 element logische OF logische OF; reduceert eventuele vector in antwoord tot 1 element ontkenning (niet ...)
en daarnaast nog de functie xor (= exclusive or). TRUE en FALSE heten boolse of booleaanse variabelen. In R kan je zowel TRUE en FALSE of 1 en 0 gebruiken, het effect is hetzelfde. We geven overigens de voorkeur aan TRUE en FALSE, om verwarring met de numerieke 0 en 1 te voorkomen, zoals in het volgende voorbeeld duidelijk wordt gemaakt. If Printer_On = 1
# Initialiseer de printer indien aangesloten
If Report_Selected = 1 # Print het rapport indien het het eerste is In het bovenste geval had om redenen van duidelijkheid beter TRUE gebruikt kunnen worden i.p.v. 1. Dat blijkt uit het onderscheid met het tweede geval waar echt om nummer 1 gevraagd wordt. Als we daar hadden willen vragen of het rapport ja/nee geselecteerd was, had er moeten staan: # Pak het rapport uit aangeboden stapel indien het geselecteerd is If Report_Selected = TRUE Om de volgorde van de opdrachten aan te geven kunnen we haakjes gebruiken. Bijvoorbeeld de opdracht: > 5 == 7 - 2 geeft dus TRUE (1) als antwoord. Met haakjes kunnen we de volgorde veranderen bijvoorbeeld: > (5 == 7) - 2 geeft dus als antwoord -2 (want eerst wordt '5 == 7' berekend en van dat resultaat wordt 2 afgetrokken). Net als de bekende rekenkundige operatoren (+ - * ** ^ /) kunnen ook de logische operatoren op vectoren werken: > c(TRUE, TRUE) & c(FALSE, TRUE) [1] FALSE TRUE > c(FALSE, FALSE) | c(FALSE, TRUE) [1] FALSE TRUE We krijgen dus als antwoord ook een vector terug, net als bij een rekenkundige bewerking: Page 50 of 96
Logica > c(1,2,3)
+ c(4,5,6)
[1] 5 7 9 Maar in tegenstelling tot rekenkundige operaties kunnen we bij logische operaties wel de vector reduceren tot 1 enkel element. Dat doen we door gebruik te maken van de operatoren && of || > c(TRUE, TRUE) && c(FALSE, TRUE) [1] FALSE > c(FALSE, FALSE) || c(FALSE, TRUE) [1] TRUE De vector die aanvankelijk resulteert als antwoord op de eerste & of | bewerking wordt dus nogmaals een keer aan die bewerking onderworpen, met als resultaat dat er 1 enkele TRUE of FALSE resulteert. Dit kan wel eens van pas komen bij opdrachten waarvan de uitvoering afhankelijk is van een samengestelde voorwaarde. R kent nog een aantal handige functies die werken op boolse vectoren, zoals any, all, en which. x <- c(2,9,3,1,6) # zo maar een vector enige <- x > 4 alle <- x > 0 > enige [1] FALSE
TRUE FALSE FALSE
TRUE
> alle [1] TRUE TRUE TRUE TRUE TRUE any(enige) # is er in de vector enige tenminste 1 element TRUE ? [1] TRUE > any(alle) # is er in de vector alle tenminste 1 element TRUE ? [1] TRUE > any(!alle) # is er bij ontkenning van alle tenminste 1 element TRUE? [1] FALSE > all(enige) # zijn alle elementen in enige TRUE? [1] FALSE > all(alle) # zijn alle elementen in alle TRUE? [1] TRUE > which(enige) # op welke plaatsen is enige TRUE? [1] 2 5 >> Voorwaardelijke opdrachten
Page 51 of 96
logische operatoren Logische operatoren > < >= <= == != & && | || !
groter dan kleiner dan groter of gelijk dan kleiner of gelijk dan is gelijk aan is ongelijk aan logische EN logische EN; reduceert vector logische OF logische OF; reduceert vector ontkenning (niet ...)
Logische functies xof() all(x) any(x) which(x)
exclusieve 'of' zijn alle elementen in x TRUE ? is er tenminste 1 element in x TRUE ? welke elementen in x zijn TRUE ?
Waarheidstafel : A TRUE TRUE FALSE FALSE
B TRUE FALSE TRUE FALSE
A en B TRUE FALSE FALSE FALSE
A of B TRUE TRUE TRUE FALSE
niet B FALSE TRUE FALSE TRUE
A xof B FALSE TRUE TRUE FALSE
zie ook de Quick-R webstek: http://www.statmethods.net/management/functions.html
Page 52 of 96
Lussen Lussen (loops) R kent 3 functies waarmee je lussen in scripts en functies kunt programmeren. Je gebruikt een lus structuur wanneer je opdrachten een [aan voorwaarden gebonden] aantal keren wilt herhalen. Die functies zijn for, while, en repeat, ofwel de voor, de zolang, en de herhaal-lus. Nota bene: Uit de voorgaande paragrafen (zoals vector) is duidelijk, dat R veel lus-iteraties impliciet uitvoert. Bijvoorbeeld, het commando sqrt(x) past automatisch de wortelfunctie toe op alle elementen van de vector x. Door hiervan handig gebruik te maken is het vaak mogelijk expliciete lus constructies te vermijden. Naast tot besparing van typwerk leidt dit in veel gevallen ook tot aanzienlijk sneller rekenwerk. De nu te bespreken expliciete lus constructies zijn in R relatief langzaam. De belangrijkste lus is de for constructie. > for (name in values) expr Het resultaat is het een voor een toekennen van de elementen van values aan name en het steeds evalueren van expr. Hierbij is values meestal een vector of een lijst. De variabele name is een dummy, die aan het eind van de for statement niet meer bestaat. > x <- y <- NULL # NULL is een leeg object > for (i in 1:10) {x <- c(x,i); y <-c(y,0)} > x [1] 1 2 3 4 5 6 7 8 9 10 > y [1] 0 0 0 0 0 0 0 0 0 0 >> for-loop voorbeeld Een alternatief is de while statement. > while (voorwaarde) expr De uitvoering begint met het testen van de voorwaarde. Als de uitkomst FALSE is, stopt de uitvoering; als de uitkomst TRUE is, dan wordt expr geëvalueerd en begint de uitvoering van voren af aan. > x <- numeric(10) > x [1] 0 0 0 0 0 0 0 0 0 0 > i <- 0 > + + +
while (i < 10) { i <- i+1 x[i]<- i }
> x [1] 1 2 3 4 5 6 7 8 9 10 >> while voorbeeld De repeat constructie wordt gebruikt in samenhang met break. De algemene vorm is: > i <- 0 Page 53 of 96
Lussen > x > + + + +
<- numeric(10)
repeat { i <- i+1 x[1] <- i if ( i > 9) break }
De opdracht break is het enige instrument wat we hebben om een repeat lus op tijd te stoppen, om zogezegd uit de lus te stappen. Een for lus en een while lus hebben van huis uit een voorwaarde die ze bij iedere stap controleren. Zodra niet meer aan de voorwaarde wordt voldaan dan stopt de for lus of de while lus. Bij de repeat lus is dat dus niet het geval. Daar moet met behulp van een voorwaardelijke opdracht (if) gekoppeld aan een break statement de mogelijkheid om uit de lus te kunnen stappen nadrukkelijk worden geprogrammeerd. >> repeat voorbeeld Nota bene: Als de break wordt vergeten waarschuwt R niet, zoals R bij het ontbreken van een conditie na de while wel zal doen (syntax error)! Een repeat lus zonder break levert geen syntax fout op. Daar moet je zelf maar op letten! Tussentijds stoppen Wanneer R break evalueert, verlaat het de binnenste for, while, of repeat loop waarin break bevat is. Naast break is het ook mogelijk next te gebruiken. De next zorgt ervoor dat de volgende iteratie van de loop onmiddelijk wordt begonnen, zonder de huidige af te ronden. Verder kent R de return functie. Die kun je gebruiken om een zelf geschreven functie/programma direct met zijn werk te laten stoppen. Een van de manieren om de return functie toe te passen is bij de controle op de kwaliteit van de input van een programma. Als de input niet overeenkomt met de afspraak, bijvoorbeeld dat het een numerieke vector met twee elementen moet zijn, dan kunnen we een ifstatement samen met een return-statement gebruiken om het programma direct te laten stoppen. Zie Fout afhandeling. Nota bene: voorgaande voorbeelden van for, while en repeat lussen zijn geen goed gebruik van R. Vergelijk het commando: > x <- 1:10 waarmee het zelfde resultaat wordt bereikt, maar dan zonder lus constructie! zie: Impliciete lus
Page 54 of 96
Matrix Matrix Een matrix is een gegevensstructuur in de vorm van een tabel, met rijen en kolommen. Er kan maar 1 type waarden gelijktijdig in opgeslagen worden: getallen of karakters, maar niet beide tezamen (gebruik een dataframe als je getallen en karakters samen in een tabel wilt zetten). Een matrix maken we met behulp van de functie matrix. > help(matrix) > # de getallen 1 t/m 12 in 4 rijen en 3 kolommen. > A<-matrix(1:12, nrow = 4, ncol = 3) > A [,1] [,2] [,3] [1,] 1 5 9 [2,] 2 6 10 [3,] 3 7 11 [4,] 4 8 12 Net als een vector beschikt ook een matrix over de eigenschap lengte: > length(A) [1] 12 Daarnaast heeft een matrix de eigenschap dimensie: > dim(A) [1] 4 3
# A heeft 4 rijen en drie kolommen, en dus 2 dimensies !
Nu we kennis hebben gemaakt met de functie dim kunnen we ook op de volgende manier een matrix maken: : > # de twee opdrachten .. > x <- 1:8 > dim(x) <- c(2,4) > # .. hebben hetzelfde resultaat als de ene opdracht > x <- matrix(1:8,2,4, byrow=F) > x [,1] [,2] [,3] [,4] [1,] 1 3 5 7 [2,] 2 4 6 8 Bij het gebruik van zowel de functie dim als de functie matrix wordt een matrix bij default kolomsgewijs opgevuld met de coordinaten van de vector. Bij gebruik van matrix kan deze default worden doorbroken door het argument byrow=T mee te geven. Van een matrix kunnen we door middel van een indexeren bepaalde onderdelen selecteren: > A[1,2] [1] 5
# pak eerste rij, tweede kolom
> A[1:2,2:3] [,1] [,2] [1,] 5 9 [2,] 6 10
# pak de eerste 2 rijen, en daarvan kolom 2 en 3
> A[2,] #selecteer de tweede rij Page 55 of 96
Matrix [1]
2 6 10
> A[,3] #selecteer de derde kolom [1] 9 10 11 12 En we kunnen door middel van indexeren ook elementen in een matrix vervangen door andere: > A[,3] <- 2 # vervang alle getallen in de derde kolom door het getal 2 > A[1,2] <- 5 # vervang het getal in de eerste rij en tweede kolom door het getal 5 > A[2,] <- c(23,34,45) # vervang alle elementen in rij 2 door 3 nieuwe getallen Labels Ook aan de objecten in een matrix kunnen labels worden gekoppeld, maar dat gebeurt niet aan de afzonderlijke elementen zoals bij een vector, maar aan de rijen en kolommen. We gebruiken daarvoor de functies colnames en rownames. > colnames(A) <- c('familie', 'geslacht', 'soort') > rownames(A) <- c('v1', 'v2', 'v3', 'v4') > A familie geslacht soort v1 1 5 9 v2 2 6 10 v3 3 7 11 v4 4 8 12 Die labels kunnen we net als bij vectoren gebruiken om delen uit de matrix te selecteren: > A[,'familie'] v1 v2 v3 v4 1 2 3 4
Page 56 of 96
matrix functies Matrix functies ncol(X) nrow(X) t(X) diag(X) col(X) row(X) cbind(. . .) rbind(. . .) apply(X,1,func) apply(X,2,func) X'%*%Y crossprod ((t(X)),Y) solve(X) solve(A,b) eigen(X)
aantal kolommen van een matrix aantal rijen van een matrix getransponeerde van een matrix maak een diagonaalmatrix uit een vector of vorm de diagonaalvector uit een matrix matrix van kolomnummers matrix van rijnummers combineer kolommen tot een matrix combineer rijen tot een matrix pas de functie func toe op de rijen van de matrix x pas de functie func toe op de kolommen van de matrix matrix vermenigvuldiging X'.Y inverse van een matrix oplossing van het stelsel Ax = b eigenwaarden en eigenvectoren ontleding in singuliere waarden
svd(X) Mijn eerste script... Laten we het bekende voorbeeld uit ieder 'inleiding programmeren' boek nemen, waarin de schrijver laat zien hoe eenvoudig het is om een programmaatje te schrijven in de taal XYZ [vul je eigen favoriete taal maar in]. Het enige wat je allereerste programma moet doen is de tekst 'Hello World!' op het scherm schrijven... Hoe pak ik dat aan in R? Om R in het console 'Hello World!' te laten printen is het voldoende om deze tekst: > "Hello World!" te typen op de commandoregel. Om dit door een R-script te laten doen is het dus voldoende om deze regel in te typen in een tekstfile en die te bewaren in de working directory van R. Open je platte tekstverwerker (zoals Notepad of Textwrangler), en type op de eerste en enige regel de tekst: "Hello World!" Save de file met als naam hw.r Ga nu naar het commando venster van R en type: > source("hw.r") en wat verschijnt er als output op het scherm? Niets! Nada! Zilch! En waarom verschijnt er niets op het scherm? Omdat de tekst in de file niet wordt voorafgegaan door het commando print. In het commando venster van R is dat ook niet nodig om de tekst geprint te krijgen, maar in een script of functie moet dat wel! Open het script hw.r met je teksteditor en wijzig de eerste regel in: print("Hello World!") en save de file. Als je nu > source("hw.r") intypt op de commandoregel van R zal wel de tekst "Hello World!" als output op het scherm verschijnen. Page 57 of 96
Mijn eerste script
Page 58 of 96
Output via toetsenbord Output via toetsenbord R kent drie functies om output, die is opgeborgen in een variabele, naar een file weg te schrijven: Sink Dump Write Er is nog een manier om het resultaat van berekeningen naar een file te schrijven. Die werkt met behulp van de functie write.table. Deze functie werkt op dataframes : > samenv <- summary(cohort) > write.table(samenv, file="samen.txt") > dir() [1] "R Console.txt" "gegevens.txt" "gegevens.xls" "samen.txt" "summary.out" Het resultaat van al deze functies is een zg platte tekstfile; een tekst in zg ASCII format (.txt). Gegevens kunnen ook naar file worden geschreven in het eigen format van R, het zg XDR format. Om files in dit format te saven moet de functie save worden gebruikt: > save (lichaam, file="lichaam.RData") Deze data kunnen op later tijdstip weer ingelezen worden in het geheugen van R door middel van de functie load: > load("lichaam.RData")
Page 59 of 96
Pakketten Pakketten Functies worden in R afhankelijk van de taken die ze kunnen uitvoeren gebundeld tot zg pakketten (packages). Zo zijn alle functies die nuttig en nodig zijn voor het doen van allerlei statistische analyses gebundeld in het pakket stats. Bij het installeren van R worden ook een groot aantal van dergelijke pakketten geinstalleerd. Het commando > library() geeft een overzicht van welke pakketten geinstalleerd zijn. Een aantal van deze pakketten en hun inhoud, zoals bijv stats, worden standaard geladen op het moment dat je het programma R opstart. Dat wil zeggen dat je vanaf de start van R de beschikking hebt over deze pakketten en de daarin opgeslagen functies en data. Om te zien welke pakketten geladen zijn gebruik je het commando > search() Andere pakketten uit je library kun je laden op het moment dat je ze nodig hebt. Dat kun je doen via het Packages menu van R, of door in R een commando in te typen. Het commando > library(survival) laadt alle objecten (functies en data) die zijn opgeslagen in het pakket survival. Het commando > ls("package:stats") geeft een overzicht van alle objecten (functies + data) in het pakket stats (494 stuks!).
Page 60 of 96
Plot saven Grafieken wegschrijven in een file Zoals we hebben gezien sturen grafische functies, zoals plot, barplot, boxplot, etc..., hun resultaten bij verstek naar een grafisch window. Het is ook mogelijk om de plaatjes naar een file te sturen. Er is een makkelijke manier om dat vanaf de commandoregel in R te doen. Je opent het toepasselijke graphics device, in dit geval dus een file, geeft de nodige commando's om de gewenste plaatjes te maken, en aan het eind sluit je het device, dwz de file, weer. Als je bijvoorbeeld een plaatje naar een JPEG file wil sturen dan geeft je de volgende commando's: dataset <- read.table("data.txt", header=T) # read data from working directory attach(dataset) # attach the names in first row as variable names jpeg(filename="barpl.jpg") # write results of next graphic command to a jpg file barplot(table(dataset[,2])) # make a barplot from tabulated second variable dev.off() # close the graphics device Je kunt de png functie (ipv jpeg) gebruiken als je een plaatje in .png format op wil slaan, of de postscript functie voor een plaatje in .eps format [zie: help(postscript)] Het algemene schema om een plaatje als postscript-file op te slaan is > postscript("filenaam.ps") > plotopdrachten > dev.off() Je kunt ook via het menu in R je plaatjes opslaan in een file. Als je op een Mac werkt gebruik je daarvoor het Save as... commando uit het File menu in de menu-balk, en bewaar je het plaatje in .pdf format. Als je op een PC met Windows werkt gebruik je ook het Save as... commando uit het File menu in de menubalk. Je kunt uit diverse formats kiezen:
Tot slot een voorbeeld waarbij het plaatje wordt bewaard als een pdf file. We maken daarbij gebruik van de mogelijkheid om transparantie in het plaatje aan te brengen. Daardoor is het mogelijk om in een plot met heel veel punten de indruk van gelaagdheid geven. Op plaatsen waar heel veel punten dicht bij elkaar of zelfs op elkaar liggen worden ze vanzelf donkerder afgebeeld. Page 61 of 96
Plot saven x <- rnorm(2000) # 2000 random standaard normaal verdeelde getallen y <- rnorm(2000) # idem pdf("transparant.pdf", version="1.4") plot(x,y,col=rgb(0,0,0,alpha=0.2),pch=16) dev.off()
−3
−2
−1
y
0
1
2
3
> > > > >
−3
−2
−1
0
1
2
3
x
Page 62 of 96
Plotsymbolen Plotsymbolen Het is mogelijk om in plot verschillende symbolen te gebruiken voor de punten die worden geplot. Plotsymbolen worden aangewezen door de parameter pch een geheel getal tussen de 1 en 25 te geven. Het is alleen lastig om te onthouden welk symbool precies bij welk getal hoort. Onderstaande tabel kan hierbij helpen.
Page 63 of 96
Readline Readline Een andere functie om via het keyboard input op te vangen is readline: readline(prompt = "") Als prompt kunt u een vraag aan de gebruiker invullen, zoals "Geef steeds 1 getal na elke :" in het scan voorbeeld. y <- readline(prompt = "Geef input: ") In tegenstelling tot scan die numerieke input oplevert, maakt readline variabelen van het type karakter. Daar kan dus pas mee gerekend worden nadat ze zijn omgezet tot het numerieke type. Dat omzetten doe je met de functie as.numeric . y <- readline(prompt = "Geef input: ") Geef input: hallo > y [1] "hallo" > inp <- as.numeric(y) Warning message: NAs introduced by coercion Als we, zoals in bovenstaand voorbeeld, karakter-input proberen om te zetten in getallen dan krijgen we een waarschuwing, en het resultaat van de omzetting is een NA [NA betekent: Not Available, en is dus de indicatie van een ontbrekende waarde (missing value)]. Aan de variabele inp is dus de 'waarde' NA toegekend. We kunnen dat als volgt controleren: > is.na(inp) [1] TRUE Als we een getal invoeren als antwoord op de prompt, en die input omzetten met behulp van as.numeric, gaat alles gewoon goed. > z <- readline(prompt = "Geef input: ") Geef input: 34 > z [1] "34" > as.numeric(z) [1] 34 Alles even samenvatten in een voorbeeld: totaal <- function() { # totaal vraagt om getal input, en rekent over de gegeven getallen de som uit som <- 0 # we geven de som alvast een waarde repeat{ input <- readline("Geef 1 getal: ") # lees 1 getal in x <- as.numeric(input) # zet input om in getallen ipv karakter-input if(is.na(x)){ # als x == NA, dan is input een letter of woord, en geen getal print("input is geen getal...") return(som) # stop, en geef som als output } if(x==0) {return(som)} # stop met lus als input gelijk is aan nul Page 64 of 96
Readlinesom
<- som + x cat("De som is nu gelijk aan ",som,"\n") }
}
Page 65 of 96
rekenkundige standaardfuncties Rekenkundige operatoren, functies, en waarden ^ * + %%
** / -
machtsverheffen vermenigvuldigen, delen optellen, aftrekken rest (bij deling) = modulo
sqrt() abs() sin() cos() tan() asin acos atan sinh cosh tanh asinh acosh atanh exp(), log() log10() log2() floor() ceiling() trunc() round() sign() sum() prod() max() min()
vierkantswortel absolute waarde goniometrische functies inverse goniometrische functies hyperbolische functies inverse hyperbolische functies exponentiele functie en natuurlijke logaritme logaritme met basis 10, 2 entier, boven-entier gehele deel, afronding teken som, produkt maximum, minimum
pi Inf, -Inf NA (not available) NULL
pi oneindig ontbrekende waarneming lege verzameling
Zie ook de Quick-R webstek: http://www.statmethods.net/management/functions.html
Page 66 of 96
Rekenmachine R als Rekenmachine We kunnen R gewoon als Rekenmachine gebruiken. Met behulp van een rekenmachine kunnen we waarden bewerken, zoals machtsverheffen (^), vermenigvuldigen (*), delen (/), optellen (+), en aftrekken (-), en dat gaat met R op vergelijkbare manier (zie: rekenkundige standaardfuncties) > 2+4 [1] 6 > 4-3 [1] 1 > 3*5 [1] 15 > 4/2 [1] 2 > 2^3 [1] 8 > 2^-1 # negatieve machten geven een reciprook getal [1] 0.5 De symbolen die de bewerking voorstellen noemen we ook wel operatoren. Ieder R-commando wordt afgesloten met een RETURN. Indien een commando bij het geven van een RETURN nog niet af is, antwoordt R met een vervolg-prompt : dit is bij default het plus teken ‘+’. Omgekeerd kunnen meerdere opdrachten op één regel gegeven worden door ze te scheiden met een puntkomma ‘;’. > 3+3; 1+1 [1] 6 [1] 2 > q( # onvolledig 'quit' commando + ) # vervolg prompt Soms is het antwoord van een berekening TRUE of FALSE. In zo'n berekening worden zg logische operatoren gebruikt. R kent de volgende: > groter dan < kleiner dan >= groter of gelijk dan <= kleiner of gelijk dan == is gelijk aan != is ongelijk aan & logische EN | logische OF > 2>4 # is 2 groter dan 4? [1] FALSE > 3==3 # let op de dubbele == [1] TRUE > 3!=3 # != staat voor ongelijkheid [1] FALSE Page 67 of 96
Rekenmachine > (3>2)&(3>4) # & staat voor het logische "EN" [1] FALSE > (3>2)|(3>4) # | staat voor het logische "OF" [1] TRUE In een uitdrukking, ook wel expressie genoemd, kunnen meerdere bewerkingen voorkomen die stuk voor stuk moeten worden uitgevoerd. Daarbij gelden de bekende voorrangsregels: haakjes gaan boven alles, vervolgens komt machtsverheffen, dan vermenigvuldigen samen met delen, en ten slotte optellen samen met aftrekken: > 3*4^2 [1] 48 > 4^2*3 [1] 48 > (3*4)^2 [1] 144 > 4^(2*3) [1] 4096 > 3*4/2 [1] 6 > 4/2*3 [1] 6 > 4/(2*3) [1] 0.6666667 > 4/2-1 [1] 1 > 4/(2-1) [1] 4 Net als rekenmachines kent R talrijke functies voor het uitvoeren van wiskundige en statistische berekeningen: > sqrt(9) [1] 3 > (sqrt(9))==9^0.5 [1] TRUE > sin(pi) # R kent pi [1] 1.224606e-16 # nou ja, eigenlijk 0! > log(10) [1] 2.302585 > exp(1) [1] 2.718282 Page 68 of 96
Rekenmachine Hier volgen enkele rekenkundige standaardfuncties: sqrt abs sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh exp log log10 floor ceiling trunc round sign %%
wortel absolute waarde goniometrische functies inverse goniometrische functies hyperbolische functies inverse hyperbolische functies exponentiele functie en natuurlijke logaritme logaritme met basis 10 entier, boven-entier en gehele deel afronding teken rest (bij deling)
R als Rekenmachine (.pdf; Crawley)
Page 69 of 96
repeat voorbeeld Herhaal lus We doen het zelfde probleem als bij de Zolang lus, om het verschil tusen de while en de repeat goed te laten zien. Inleiding: Elk jaar verliest een boom al z'n bladeren. Een boom begint op dag D met B bladeren. Op de eerste dag is voor elk blad dat aan de boom zit de kans op afvallen 0.03. Voor elke volgende dag wordt die kans met 0.03 verhoogd, en blijft voor alle bladeren identiek. Vraag: Schrijf een functie in R die • bij aanroep zowel: ⁃ het aantal bladeren (B) aan de boom, ⁃ als het aantal dagen (D) dat we naar de boom kijken, ⁃ als parameter mee krijgt, • stopt wanneer er geen blad meer aan de boom zit, of de dagen voorbij zijn, • voor elke dag het aantal overgebleven bladeren uitrekent en dat aantal samen met het nummer van de dag op het scherm schrijft. Antwoord: bladval <- function(blad,dag) { dagteller<- 1 repeat { # start van de lus afval <- round(blad * 0.03 * dagteller) # rekenen met hele bladeren blad <- blad - afval # restant aan bladeren dagteller <- dagteller + 1 # weer een dag voorbij dag <- dag - 1 # we gaan naar volgende dag cat("op dag",dag,"zitten er nog",blad,"bladeren aan de boom... \n") if ((blad <= 1) | (dag < 1)) { # stopvoorwaarde break } } } Waarin verschilt deze oplossing met de repeat lus nu met het while voorbeeld? • • • • • •
Let op de plaats van de stopvoorwaarde -> Niet aan het begin van de lus, maar aan het eind als de opdrachten tenminste 1 x gedaan zijn Let op de vorm van de stopvoorwaarde -> Er wordt gebruik gemaakt van de break functie om te kunnen stoppen Let op de inhoud van de stopvoorwaarde -> blad <= 1 ipv blad>=1 ; de | ipv de & ; dag<1 ipv dag>0
Ter oefening en aanvulling: • • • •
Wordt het aantal voorbije dagen wel logisch geteld en weergegeven...? Wat gebeurt er als er bij aanroep van de functie geen 2 argumenten worden gegeven? Kunnen we dat op tijd controleren...(fout afhandeling)? Stel dat we de afgesproken kans op afvallen van 0.03 procent voor ieder blad apart willen gebruiken om te bepalen of het blad al dan niet van de boom valt. Hoe zouden we dat kunnen aanpakken? Page 70 of 96
repeat voorbeeld
Page 71 of 96
RSiteSearch RSiteSearch Een zoekmachine speciaal voor R: R Site Search via http://search.r-project.org/ via http://www.r-project.org/search.html Ook te installeren als extensie in Firefox: http://addictedtor.free.fr/rsitesearch/
Page 72 of 96
Scan Scan R kent een aantal manieren om input op te vangen via het toetsenbord. In de eerste plaats met behulp van de functie scan. Een van de argumenten die scan daarbij gebruikt is n; het aantal waarden dat ingelezen gaat worden. Bijvoorbeeld: y <- scan(n=1) leest 1 numerieke waarde in en kent die waarde vervolgens toe aan de variabele y. En zo doe je er 30: > v<-scan(n=30) 1: 3 2 4 5 3 4 5 6 7 8 6 5 # ik doe Enter 13: 2 3 4 5 67 5 4 3 # we gaan verder vanaf getal 13 21: 4 5 6 7 5 5 # en verder vanaf 20 27: 4 5 56 30: 6 # en de laatste... Read 30 items > v [1] 3 2 4 5 3 4 5 [25] 5 5 4 5 56 6
6
7
8
6
5
2
3
4
5 67
5
4
3
4
5
6
7
Check: Wat gebeurt in als je in het bovenstaande voorbeeld meer dan 1 waarde intypt? Wat gebeurt in als je in het bovenstaande voorbeeld een woord intypt, bijv 'hallo' ? Als je scan geen argument geeft dan blijft deze functie getallen inlezen, totdat je met een lege regel eindigt: > x<-scan() 1: 23 2: 45 3: 34 4: 67 5: 89 6: 546 7: Read 6 items > x [1] 23 45 34
67
89 546
Die serie getallen type je zelf in, maar je kunt ze bijvoorbeeld ook met een copy [Ctrl-C] uit een kolom van een Excel spreadsheet hebben gehaald, waarna je met de cursor achter de 1: gaat staan en paste [Ctrl-V] doet. Als je in een zelf geschreven programma de gebruiker om input wil vragen (ipv die input als parameter bij de aanroep mee te geven), zou je daarvoor de volgende functie kunnen gebruiken: prmpt <- function(input) { # input geeft aan hoeveel getallen moeten worden ingelezen cat("Geef steeds 1 getal na elke : ","\n") out <- scan(n=input) out Page 73 of 96
Scan
}
Kopieer en plak de bovenstaande regels in een tekstfile (Notepad, of Textwrangler), en bewaar die file met naam prompt.r in de working directory van R. Wat in deze functie nog ontbreekt is een goede fout afhandeling, bijvoorbeeld als de gebruiker een woord ipv een getal intypt.
Page 74 of 96
Scripts Scripts Een script schrijven is de meest eenvoudige manier om een programma te maken in R. De andere manier is het schrijven van een functie. Een script bestaat uit 1 of meerdere R commando's die nodig zijn om een specifieke taak uit te voeren. De reden waarom je die commando's bundelt in een script is heel simpel. Die specifieke taak die je wilt laten uitvoeren komt regelmatig terug, en je hebt geen zin om iedere keer weer die commando's stuk voor stuk in te typen in het commando-venster (console) van R. Een computer is er juist voor gemaakt om het uitvoeren van dit soort regelmatig terugkerende taken zo makkelijk mogelijk voor je te maken, door je alle overbodige handelingen (= herhaaldelijk hetzelfde intypen) uit handen te nemen. Kies je favoriete editor (die van R zelf, of anders Notepad [of TextWrangler, als je een Mac gebruikt], maar zeker geen Word gebruiken !!!), en schrijf een lijstje met commando's dat je wilt laten uitvoeren. Sla die file met R-opdrachten op in de working directory van R. Zorg ervoor dat die file de extensie .r heeft. Met behulp van de functie source kun je vervolgens alle R opdrachten in de bewaarde file laten uitvoeren: > source("mijn opdrachten.r") Mijn eerste script...
Page 75 of 96
SDMijn script tweede script Het volgende voorbeeld van een script komt uit de opdrachten van Statistiek-1: Schrijf een instructie in R waarmee je de standaarddeviatie van een variabele kunt uitrekenen [gebruik R als rekenmachine, zonder gebruik te maken van de ingebakken functie sd] De variabele uit de opdracht geven we de naam x. We gaan er van uit dat x de vorm heeft van een numerieke vector. Om de standaarddeviatie van de waarden in x uit te rekenen zijn dan de volgende opdrachten nodig: # script: bereken standaardeviatie van vector x # bereken het gemiddelde van x = # de som van alle waarden in x gedeeld door het aantal waarden in x gem <- sum(x) / length(x) # bereken de som van de kwadraten van de afwijkingen van het gemiddelde kwasom <- sum((x-gem)^2) variantie <- kwasom/(length(x)-1) stdev <- variantie^0.5 print(stdev) Kopieer en plak de bovenstaande regels in een tekstfile (Notepad, of Textwrangler), en bewaar die file met naam standvar.r in de working directory van R. 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 gaan we de standaarddeviatie van de variabele lichaam uitrekenen met ons zelf geschreven script standvar.r. Eerst zorgen we dat lichaam de juiste naam, x, krijgt om het script goed te laten werken. > x <- lichaam Daarna lezen we het script in, waarna het direct werkt: > source("standvar.r") [1] 9.643303 We zien in dit voorbeeld dat we zelf moeten zorgen dat de input voor het script in de afgesproken vorm (vector) en met de afgesproken naam (x) klaar staat. Dit script werkt dus met een zg globale variabele als input: een variabele die in het geheugen van R beschikbaar is. Ook de variabelen die door het script tijdens zijn berekeningen worden gemaakt, zoals kwasom, variantie, en stdev komen gewoon in het geheugen van R terecht en worden zo dus globale variabelen. Ze blijven niet beperkt tot het domein van het script zelf. In dat geval, dus als variabelen beperkt zouden zijn tot het domein van het script, noemen we ze lokale variabelen. Dat wil zeggen: ze zijn er alleen zo lang het script aan het werk is, maar zodra het script klaar is en de output naar scherm of printer of file is geschreven, zijn de variabelen verdwenen. Nu is het zo dat scripts het fenomeen van dergelijke lokale variabelen niet kennen! Page 76 of 96
SDNota script Bene: Alle variabelen die in een script worden gebruikt of gemaakt zijn dus globaal. Dit in tegenstelling tot wat het geval is in functies.
Page 77 of 96
Sink Sink Met behulp van het commando sink kan het resultaat van berekeningen naar een file worden geschreven. 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)
En dan nu een analyse uitvoeren en de resultaten naar een file wegschrijven: > > > > > > > >
# bereken summary statistics en ... # schrijf het resultaat naar een file met de naam summary.out # open eerst de file waar de output naar toe moet worden geschreven. sink("summary.out") # bereken summary statistics van de variabelen in dataframe cohort. summary(cohort) # sluit de file waar de output naar toe is geschreven. sink()
Met sink kun je dus de output van een reeks van commando's opvangen en naar een file laten schrijven. Met de functie dump kun je de inhoud van 1 variabele naar een file wegschrijven.
Page 78 of 96
Start Starten met R Je kunt kiezen: 1. Nadat de installatie van R klaar is heb je een shortcut of alias op je desktop gekregen. Dubbelklik dat icoon om R op te starten. 2. Na installatie is er meestal ook een quick-start icoon geinstalleerd in je Startbalk:
3. Je kunt via de Start knop naar Programma's, en daar R opzoeken en klikken.
Wat R precies doet bij zijn opstart, en hoe je dat kunt manipuleren, staat beschreven op een Quick-R Page 79 of 96
Start webpagina: [ http://www.statmethods.net/interface/customizing.html ]
Page 80 of 96
Stop Stop Stoppen met je R-sessie is eenvoudig. Type quit: > quit() R vraagt vervolgens of je deze sessie wilt bewaren:
Klik Yes als je de workspace, met alles wat je daarin aan data en resultaten hebt gemaakt wilt bewaren voor een volgende sessie.
Page 81 of 96
Variabelen Variabelen 1. Waarde, Type, en Vorm 1.1 Waarde We kunnen R gewoon als Rekenmachine gebruiken. Met behulp van een rekenmachine kunnen we waarden bewerken, zoals machtsverheffen (^), vermenigvuldigen (*), delen (/), optellen (+), en aftrekken (-), en dat gaat met R op vergelijkbare manier: > 2+4 [1] 6 > 4-3 [1] 1 > 3*5 [1] 15 > 4/2 [1] 2 > 2^3 [1] 8 De symbolen die de bewerking voorstellen noemen we ook wel operatoren. De waarden kunnen we toekennen aan variabelen. Een waarde toekennen aan een variabele doen we met behulp van het pijltje <- . Een variabele krijgt daarbij tegelijk een naam. De naam bestaat in de regel uit letters, zoals bijvoorbeeld armlengte. Ook mogen getallen en - of _ worden gebruikt in de naam, zoals bijvoorbeeld lengte-poot-2, maar de naam mag niet met een getal beginnen. Er mogen geen spaties in de naam van een variabele voorkomen. # > > >
met dit teken # maak je in R kommentaar regels... a <- 2 # waarde 2 wordt toegekend aan variabele met naam a b <- 4 # waarde 4 wordt toegekend aan variabele met naam b armlengte <- 34
Als je vervolgens de inhoud van de variabele wil zien moet je de naam op de commandoregel van R intypen en RETURN geven: >varia <- 34 >varia [1] 34 Een beetje vreemd wellicht, dat we de naam van de variabele moeten intypen om de inhoud te zien te krijgen, want van een gewone expressie zoals > 4 * 3 [1] 12 krijgen we wel direct het resultaat onder ogen. Dat komt omdat R eigenlijk impliciet een print-opdracht voor zo'n expressie zet. Dus eigenlijk doet R dit: > print(4 * 3) [1] 12 Om dezelfde reden krijgen we als we de toekenning van waarden aan een variabele tussen haakjes Page 82 of 96
Variabelen zouden zetten, wel direct de inhoud te zien: > (varia <- 34) [1] 34 en dat komt weer omdat R in zo'n expressie er stiekem print voorzet: > print (varia <- 34) [1] 34 Je hebt de print opdracht dus nodig in alle gevallen dat je expliciet resultaten op je scherm afgedrukt wilt zien! Alleen bij gebruik van de commandoregel neemt R impliciet die taak van je over. Nadat een variabele d.m.v. een toekenning van 1 of meer waarden is voorzien kunnen we de variabelen gebruiken in de bewerkingen. > a+b [1] 6 > a-b [1] -2 > a/b [1] 0.5 > a*b [1] 8 > a^b [1] 16 In een uitdrukking, ook wel expressie genoemd, kunnen meerdere bewerkingen voorkomen die stuk voor stuk moeten worden uitgevoerd. Daarbij gelden de bekende voorrangsregels: haakjes gaan boven alles, vervolgens komt machtsverheffen, dan vermenigvuldigen samen met delen, en ten slotte optellen samen met aftrekken: > z <- a + 3 * b - (4 / a) ^ b [1] -2 > 3*4^2 [1] 48 > 4^2*3 [1] 48 > (3*4)^2 [1] 144 > 4^(2*3) [1] 4096 > 3*4/2 [1] 6 > 4/2*3 [1] 6 Page 83 of 96
Variabelen > 4/(2*3) [1] 0.6666667 1.2 Type We onderscheiden twee typen (R noemt dit mode) van variabelen: Getallen (en daar vallen de zg Boolese variabelen TRUE en FALSE ook onder) en Karakters. > > > > #
3 # dit is een getal 'a' # maar dit is een karakter a: het staat tussen aanhalingstekens '3' # dit is ook een karakter, nl het symbool voor het getal 3 a # dit is de naam van een variabele waar we al eerder een waarde aan hebben toegekend.
De twee typen van variabelen, getal en karakter, zijn incompatibel. Dat wil zeggen dat ze onderling niet te bewerken zijn. Je kunt niet een getal bij een karakter optellen, of een karakter door een getal delen. Met andere woorden: bij elk type hoort zijn eigen soort van bewerkingen [methoden]. Bij getallen horen alle rekenkundige bewerkingen. Maar met karakters kan R niet zoveel. (Je kunt er woorden en zinnen mee vormen, maar dat gaat niet door losse karakters met behulp van bijv de + of een ',' aan elkaar te plakken, maar gewoon door de worden en/of zinnen in hun geheel in te typen tussen aanhalingstekens). Dus dit werkt: > TRUE * 3 [1]3 Maar dit niet: > tekst <- 'goede morgen' > 23 * tekst Error in 23 * tekst: non-numeric argument to binary operator > woord <- 'hallo' > woord * tekst Error in woord * tekst : non-numeric argument to binary operator > '7' + '23' ??? Met behulp van de functie ls kunnen we aan R vragen welke variabelen in het geheugen aanwezig zijn: > ls() [1] "a"
"b"
"tekst" "woord"
Met de functie mode kunnen we vragen tot welk type een variabele behoort: > mode(a) "numeric" > mode(woord) "character" En met de functie str (= structure) krijgen we niet alleen het type als antwoord, maar ook de waarde: > str(a) num 2 We kunnen de functies ls en str ook bundelen tot ls.str. Dat werkt zo: > ls.str() Page 84 of 96
Variabelen a : num
2 b : num 4 tekst : chr "goede morgen" woord : chr "hallo"
1.3 Vorm Getallen kunnen op zich zelf staand voorkomen, zoals het getal 3. We spreken dan van een scalair. Getallen kunnen ook gegroepeerd voorkomen. Dat kan op twee manieren: als Vector, of als een Matrix. En eigenlijk kent R geen scalairen. Ook 1 enkel getal wordt door R al beschouwd als een vector, en wel een vector met een lengte die gelijk is aan 1. Alle gegevensstructuren in R bezitten de eigenschap lengte. Die eigenschap geeft weer uit hoeveel objecten een variabele bestaat. > length(a) [1] 1 > length(woord) [1] 1 > length(tekst) [1] 1 Daarnaast kent R nog twee vormen van zg samengestelde gegevensverzamelingen, de Lijst en het Dataframe. Een matrix en een dataframe hebben naast lengte bovendien de eigenschap dimensie.
Page 85 of 96
Vector Vector Voor het maken van een vector maken we gebruik van de functie c [c = 'Combine Values into a Vector or List'] of van de functie seq [seq = 'Sequence Generation'], of van de functie rep [rep = 'Replicate Elements of Vectors and Lists']. > > > >
help(vector) help(c) help(seq) help(rep)
Probeer de volgende voorbeelden: > x <- 1,3,2,6,0 Error: syntax error
# zo werkt het dus niet !!!
> x <- c(1,3,2,6,0) > x [1] 1 3 2 6 0 > x <- seq(1, 20, by = 3) > x [1] 1 4 7 10 13 16 19 > x <- seq(10, 1, by = -2) > x [1] 10 8 6 4 2 > x <- 1:10 > x [1] 1 2 3 4 5 6 7 8 9 10 > y <- 2.2:5.2 [1] 2.2 3.2 4.2 5.2 > x <- rep(3,10) > x [1] 3 3 3 3 3 3 3 3 3 3 > x <- rep(c(5,7), 2) [1] 5 7 5 7 > x <- rep(3:5, 1:3) [1] 3 4 4 5 5 5 We kunnen ook makkelijk getallen aan de vector x toevoegen: > v <- c(x,6,9,3) > v [1] 1 2 3 4 5 6 7 8 9 10 6 9 3 > z <- c(x,y) > z [1] 1 2 3 4 5 6 7 8 9 10 2.2 3.2 4.2 5.2 Een numerieke vector ter lengte n kun je maken met de functie numeric. Page 86 of 96
Vector > x
<- numeric(5) > x [1] 0 0 0 0 0
Vectoren hebben de eigenschap lengte die vertelt hoeveel objecten deel uitmaken van de vector. Met behulp van de functie length kunnen we aan R vragen wat de lengte van een bepaalde vector is: > x <- 1:10 > length(x) [1] 10 We kunnen rekenen met vectoren: > y <- 3*x - 12 > y [1] -9 -6 -3 0 3 6 9 12 15 18 > 3:5 - 1:6 [1] 2 2 2 -1 -1 -1
# 3-1, 4-2, 5-3, 3-4, 4-5, 5-6
> z <- (y<0) > z [1] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE Een logische vector, zoals z in bovenstaand voorbeeld, is permanent in een numerieke vector te veranderen met de functie mode. > mode(y) <- "numeric" > y [1] 1 1 1 0 0 0 0 0 0 0 En andersom kan natuurlijk ook: > mode(y) <- "logical" > y [1] TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE Een vector kan op grootte gesorteerd worden met behulp van sort. > x <- c(2, 6, 4, 5, 5, 8, 8, 1, 3, 0) > length(x) [1] 10 > sort(x) [1] 0 1 2 3 4 5 5 6 8 8 Met order krijg je de permutatie-index die nodig is om de vector op grootte gesorteerd te krijgen. Deze kan worden gebruikt om een tweede vector met een eerste te permuteren. > order(x) [1] 10 8 1 9 3 4 5 2 6 7 > y <- c(3, 6, 5, 7, 3, 5, 8, 0, 9, 12) > y [1] 3 6 5 7 3 5 8 0 9 12 > y[order(x)] Page 87 of 96
Vector [1]
12
0
3
9
5
7
3
6
5
8
De functie rev draait de volgorde van de coordinaten van een vector om. Derhalve is rev(sort(x)) de vector van order statistics in dalende volgorde. > rev(sort(x)) [1] 8 8 6 5 5 4 3 2 1 0 > rev(x) [1] 0 3 1 8 8 5 5 4 6 2 De functie rank(x) geeft de rangnummers van x. In het geval van ties in x worden gemiddelde rangnummers gegeven, zoals gebruikelijk in de statistiek. > rank(x) [1] 3.0 8.0 5.0 6.5 6.5 9.5 9.5 2.0 4.0 1.0 Met behulp van de functie duplicated kunnen we in een vector de elementen vinden die meer dan 1 keer voorkomen: > x <- c(3,4,3,2,3,4,6,5,6,6,7,7,6,5,5,5,4) > duplicated(x) [1] FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE TRUE TRUE
TRUE
TRUE FALSE
Met de functie unique kunnen die duplicaten vervolgens worden verwijderd: > unique(x) [1] 3 4 2 6 5 7 Rekenen met vectoren gaat meestal elementsgewijs, maar soms niet. Vergelijk: > x*y [1] -9 -12 -9 0 15 36 63 96 135 180 > x%*%y [,1] [1,] 495 De notatie x%*%y geeft het inproduct van de vectoren x en y. > help("%*%") Wat gebeurt er als je een vector probeert te maken waarin tekst en getallen door elkaar worden gebruikt? > probeer <- c(23, 'hallo', 34, 567, 'goede morgen', 123465) > probeer [1] ??? Dat werkt dus niet. Alle elementen blijken na deze operatie tot het type "character" te behoren. Voor dat doel, het mengen van zowel numerieke als karakter elementen is de lijst uitgevonden. >> Indexeren
Page 88 of 96
Voorwaardelijke opdrachten Voorwaardelijke opdrachten if... We gebruiken selectie door middel van logische operatoren [zie: logica] om bepaalde opdrachten soms wel en soms niet uit te voeren. Een voorbeeld: ALS het regent DAN neem een paraplu mee. In R gebruiken we de functie if om voorwaardelijk opdrachten te kunnen formuleren. Na de if komt de voorwaarde, vervolgens komen de opdrachten die uitgevoerd moet worden als de voorwaarde waar is. Als de voorwaarde niet waar is wordt die opdrachten overgeslagen en gaat het programma verder met de volgende opdrachten (of stopt als die er niet meer zijn). if(x>10) { opdracht 1 opdracht 2 opdracht 3 } # sluit if commando af opdracht 4 Bijvoorbeeld: # Dit is een eenvoudig if voorbeeld A <- 2 B <- 3 if (A < B) { print 'A is kleiner dan B' } C <- A * B Kopieer en plak de bovenstaande regels in een tekstfile (Notepad, of Textwrangler), en bewaar die file met naam test-if.r in de working directory van R. # daarna lezen we het script in > source("test-if.r") Als we het algoritme zo uitvoeren zal de tekst 'A is kleiner dan B' altijd op het scherm verschijnen. Veranderen we de eerste regel echter in A <- 10 dan zal er niets op het scherm geschreven worden. De laatste regel wordt wel uitgevoerd. C wordt dus in beide gevallen het produkt van A en B. if... else We kunnen de bovenstaande if... constructie uitbreiden met de else. Na het else-statement komen de opdrachten die uitgevoerd moeten worden als die na de if-statement niet uitgevoerd moeten worden. Het algoritme uit de vorige paragraaf kunnen we nu op de volgende manier uitbreiden: # Dit is een eenvoudig if ... else voorbeeld} A <- 2 B <- 3 if (A < B) { cat("A is kleiner dan B \n") Page 89 of 96
Voorwaardelijke } else { opdrachten cat("A is groter dan B \n") } C <- A * B Bij dit algoritme wordt dus altijd iets op het scherm geschreven (dus ook als A de waarde 10 heeft). Merk op dat in het geval van A=3 (dus A is gelijk aan B) er een logische fout in dit programma zit. We kunnen de if...else constructie net zo vaak gebruiken als we willen. We kunnen zelfs een selectie binnen een selectie maken. Kijk bijvoorbeeld naar het volgende algoritme: # Dit is een eenvoudig if ... else voorbeeld met nesting A <- 2 B <- 3 C <- 0 if (A < B) { cat("A is kleiner dan B \n") } else { if (A < C) { cat( 'A is kleiner dan C\n') } else { cat( 'A is groter dan C\n') } } C <- A * B Als A kleiner is dan B dan wordt eerst op het scherm geschreven: A is kleiner dan B. Daarna wordt gekeken of A kleiner is dan C. Dit is niet het geval en er wordt dus niets meer op het scherm geschreven. Daarna wordt het produkt van A en B toegekend aan C. Zo kunnen we de logische fout uit het eerste voorbeeld van deze paragraaf verbeteren door het algoritme als volgt uit te breiden: # Dit is een eenvoudig if ... else voorbeeld met nesting A <- 2 B <- 3 if (A < B) { cat("A is kleiner dan B \n") } else { if (A == B) { cat( 'A is gelijk aan B\n') } else { cat( 'A is groter dan B\n') } } C <- A * B cat("C is gelijk aan ", C) Je ziet dat we door middel van inspringen duidelijk maken welke opdrachten bij welke conditie horen. Inspringen is een tekstueel hulpmiddel; geen eis met betrekking tot de correctheid van het algoritme ! Ook wanneer de if... alleen voldoende lijkt om je probleem op te lossen, is het verstandig om je door het toevoegen van een ...else in te dekken tegen onverwachte uitzonderingen. Dat gaat er dan zo uitzien: if (voorwaarde) { opdracht Page 90 of 96
Voorwaardelijke }else { opdrachten cat('Hier had ik niet op gerekend') }
Page 91 of 96
Werkruimte Werkruimte Dit zie je als R is gestart:
Je bent in de zg workspace van R, en je cursor is zichtbaar achter de > , ook wel de prompt genoemd. Dit is de plek waar je je opdrachten voor R intypt. Door het geven van het commando getwd krijg je te zien welke directory op je PC door R wordt gebruikt om gegevens uit op te halen, of naar toe te schrijven: > getwd() [1] "C:/Program Files/R/R-2.4.0" Deze directory kun je wijzigen door in het File menu de optie Change dir... te kiezen en daarna een andere directory aan te wijzen of te maken [bijvoorbeeld een in de My Documents directory].
Page 92 of 96
Werkruimte
Je kunt ook het commando setwd gebruiken om een nieuwe working directory aan te wijzen: > setwd("c:/docs/mydir") windows
# let op het gebruik van / in plaats van \ in
Page 93 of 96
while voorbeeld Zolang lus Inleiding: Elk jaar verliest een boom al z'n bladeren. Een boom begint op dag D met B bladeren. Op de eerste dag is voor elk blad dat aan de boom zit de kans op afvallen 0.03. Voor elke volgende dag wordt die kans met 0.03 verhoogd, en blijft voor alle bladeren identiek. Vraag: Schrijf een functie in R die • bij aanroep zowel: ⁃ het aantal bladeren (B) aan de boom, ⁃ als het aantal dagen (D) dat we naar de boom kijken, ⁃ als parameter mee krijgt, • stopt wanneer er geen blad meer aan de boom zit, of de dagen voorbij zijn, • voor elke dag het aantal overgebleven bladeren uitrekent en dat aantal samen met het nummer van de dag op het scherm schrijft. Antwoord: bladval <- function(input) { blad <- input[1] dag <- input[2] dagteller<- 1 while ((blad >= 1) & (dag > 0)) { blad <- blad - blad * 0.03 * dagteller dagteller <- dagteller + 1 dag <- dag - 1 cat("op dag",dag,"zitten er nog",blad,"bladeren aan de boom... \n") } } Ter oefening en aanvulling: • • • • • •
Schrijf kommentaar, zodat duidelijk wordt in welke regels er wat en waarom gebeurt... Een boom heeft een geheel aantal bladeren -> zorg voor afronding van variabele blad... Wordt het aantal voorbije dagen wel logisch geteld en weergegeven...? Wat gebeurt er als de variabele input geen numerieke vector met twee getallen is...? Kunnen we dat op tijd controleren...(fout afhandeling)? Stel dat we de afgesproken kans op afvallen van 0.03 procent voor ieder blad apart willen gebruiken om te bepalen of het blad al dan niet van de boom valt. Hoe zouden we dat kunnen aanpakken?
Nog een voorbeeld: Een al heel oud algoritme is dat van Euclides voor het vinden van de grootste gemene deler van twee getallen. Het is een voorbeeld van een algoritme met een conditie in een lus. ggd <- function(A, while (A != B) if (A > B) A <- A } if (A < B) B <- B
B) { { { - B { - A Page 94 of 96
while voorbeeld } } cat( "Grootste gemene deler is: ", A) } Ter oefening en aanvulling: • • • •
Schrijf kommentaar, zodat duidelijk wordt in welke regels er wat en waarom gebeurt... Wat gebeurt er als de variabelen A en B geen getallen zijn...? Kunnen we dat op tijd controleren... (fout afhandeling)? Kunnen we de functie zo herschrijven dat hij expliciete output geeft in plaats van conversationeel.
Page 95 of 96
Write Write Als je een matrix of vector weg wilt schrijven naar file dan kun je de functie write gebruiken. > x <- c(1:10) > write(x, file = "output", + ncolumns = if(is.character(x)) 1 else length(x), + append = FALSE, sep = " ") Als je write niet vertelt hoe de file heet waarnaar de inhoud van de variabele moet worden weggeschreven dan gebruikt write de naam data. In het voorbeeld wordt de inhoud van de variabele vec10 geschreven naar de file met de naam output. Als je het aantal kolommen (ncolumns) niet specificeert dan worden er standaard 5 kolommen gebruikt. Wel iets waar je even op moet letten! Als de inhoud van de variabele de vorm heeft van een matrix, ben je wel gedwongen om het aantal kolommen goed aan te geven om de inhoud van de file de juiste vorm te laten krijgen!: > m <- matrix(c(1:21), ncol=7) > write(m, file = "output", ncolumns = 7)
Page 96 of 96