RPR-Debugging
Debugging R
Keywords: Debugging with RStudio; the browser(), debug() and debugonce() commands; setting conditional breakpoints
Contents
This unit is under development. There is some contents here but it is incomplete and/or may change significantly: links may lead to nowhere, the contents is likely going to be rearranged, and objectives, deliverables etc. may be incomplete or missing. Do not work with this material until it is updated to "live" status.
Abstract
...
This unit ...
Prerequisites
You need to complete the following units before beginning this one:
Objectives
...
Outcomes
...
Deliverables
- Time management: Before you begin, estimate how long it will take you to complete this unit. Then, record in your course journal: the number of hours you estimated, the number of hours you worked on the unit, and the amount of time that passed between start and completion of this unit.
- Journal: Document your progress in your Course Journal. Some tasks may ask you to include specific items in your journal. Don't overlook these.
- Insights: If you find something particularly noteworthy about this unit, make a note in your insights! page.
Evaluation
Evaluation: NA
- This unit is not evaluated for course marks.
Contents
Debugging
When something goes wrong in your code, you need to look at intermediate values, as the code executes. Almost always sprinkling print()
statements all over your code to retrieve such intermediate values is the least efficient way to isolate problems. But what is worse, you are temporarily modifying your code, and there is a significant risk that that this will create problems later.
Right from the beginning of your programming trajectory, you should make yourself familiar with R's debug functions.
- At first, you may need to pin down approximately where an error occurs. Read the error message carefully, or perhaps do print out some intermediate values from a loop.
- Debugging is done by entering a "browser" mode that allows you to step through a function.
- To enter this browser mode ...
- Call
debug(function)
. When function() is next executed, R will enter the browser mode. Callundebug(function)
to clear the debugging mode. (Or use thedebugonce(function)
. ) - Alternatively insert
browser()
into your function code to enter the browser mode. This sets a breakpoint into your function; useif (condition) browser()
to insert a conditional breakpoint (or watchpoint). This is especially useful if the problem occurs only rarely, in a particular context.
- Call
- It should go without saying that you need to discover that problems exist in the first place: study the Testing learning unit and test, test, and test again.
Here is an example: let's write a rollDice-function, i.e. a function that creates a vector of n integers between 1 and MAX - the number of faces on your die.
rollDice <- function(len=1, MIN=1, MAX=6) {
v <- rep(0, len)
for (i in 1:len) {
x <- runif(1, min=MIN, max=MAX)
x <- as.integer(x)
v[i] <- x
}
return(v)
}
Lets try running this...
rollDice()
table(rollDice(1000))
Problem: we see only values from 1 to 5. Why? Lets flag the function for debugging...
debug(rollDice)
rollDice(10)
debugging in: rollDice(10)
debug at #1: {
v <- rep(0, len)
for (i in 1:len) {
x <- runif(1, min = MIN, max = MAX)
x <- as.integer(x)
v[i] <- x
}
return(v)
}
Browse[2]>
debug at #2: v <- rep(0, len)
Browse[2]>
debug at #3: for (i in 1:len) {
x <- runif(1, min = MIN, max = MAX)
x <- as.integer(x)
v[i] <- x
}
Browse[2]>
debug at #4: x <- runif(1, min = MIN, max = MAX)
Browse[2]>
debug at #5: x <- as.integer(x)
Browse[2]> x # Here we examine the current value of x
[1] 4.506351
Browse[2]>
debug at #6: v[i] <- x
Browse[2]>
debug at #4: x <- runif(1, min = MIN, max = MAX)
Browse[2]> v
[1] 4 # Aha: as.integer() truncates, but doesn't round!
Browse[2]> Q
undebug(rollDice)
We need to change the range of the random input values...
rollDice <- function(len=1, MIN=1, MAX=6) {
v <- rep(0, len)
for (i in 1:len) {
x <- runif(1, min=MIN, max=MAX+1)
x <- as.integer(x)
v[i] <- x
}
return(v)
}
table(rollDice(1000))
Now the output looks correct.
# Disclaimer 1: this function would be better
# written as ...
rollDice <- function(len=1, MIN=1, MAX=6) {
return(as.integer(runif(len, min=MIN, max=MAX+1)))
}
# Check the output:
table(rollDice(1000))
# This works, since runif() can return a vector of deviates,
# but if we write the function this way we can't check the value of
# individual trials.
# Disclaimer 2: the function relies on a side-effect of as.integer(), which is
# to drop the digits after the comma when it converts. More explicit and
# therefore clearer would be to use the function floor() instead. Here, the
# truncation is not a side effect, but the desired behaviour. This is
# actually important: there is no guarantee how as.integer() constructs an
# integer from a float, it could e.g. round, instead of truncating. But rounding
# would give a wrong distribution! An error that may be hard to spot. (You
# can easily try using the round() function and think about how the result is wrong.)
# A better alternative is thus to write:
rollDice <- function(len=1, MIN=1, MAX=6) {
return(floor(runif(len, min=MIN, max=MAX+1)))
}
# Disclaimer 3
# A base R function exists that already rolls dice in the required way: sample()
table(sample(1:6, 1000, replace=TRUE))
For the helpful debugging interface that comes with with RStudio, see here and here.
For a deeper excursion into R debugging, see this overview by Duncan Murdoch at UWO, and Roger Peng's introduction to R debugging tools.
Further reading, links and resources
Notes
Self-evaluation
If in doubt, ask! If anything about this learning unit is not clear to you, do not proceed blindly but ask for clarification. Post your question on the course mailing list: others are likely to have similar problems. Or send an email to your instructor.
About ...
Author:
- Boris Steipe <boris.steipe@utoronto.ca>
Created:
- 2017-08-05
Modified:
- 2017-08-05
Version:
- 0.1
Version history:
- 0.1 First stub
This copyrighted material is licensed under a Creative Commons Attribution 4.0 International License. Follow the link to learn more.