knitr::opts_chunk$set(echo = TRUE)

This page explains how to debug your code. There are many ways in which this can be done, so we've split them into levels ranging from basic to more advanced methods. First though:

Don't ignore warnings!

People often put statements into their code to reduce the number of warnings it generates to make it look prettier. This is always a terrible idea, as if new warnings are introduced by a mistake, you won't see them (because you suppressed them!), so please don't do any of the following:

suppressWarnings()
suppressPackageStartupMessages()
library(dplyr, warn.conflicts = FALSE)

Or anything else like that. We will never mark you down for generating warning messages in your code (unless they are real warnings about real problems in your code of course!).

Restart R and never save .RData files

Errors can often be transitory because of something weird in your current environment, and while that's not great it's just not worth worrying too much about, so it's always worth restarting R and trying again before you panic and try debugging the problem, which can take a while.

However, if you save and restore .RData files, then when you restart R you won't delete all of the mistakes you made in the last R session - the objects present in the last session will still be there after restarting - and your code will no longer be reproducible. As a result we strongly encourage you to never save or restore these files. To disable this, open the (global) preferences in RStudio, and untick "Restore .RData into workspace at startup" and set "Save workspace to .RData on exit:" to "Never".

knitr::include_graphics('images/debugging.png')

Once you've not ignoring the error messages, and you've tried restarting R and it hasn't helped, then you need to try debugging the code for real, we're afraid. Here's an approach you could try:

Watch the video on debugging

This would be a good time to watch this keynote from rstudio::conf 2020 by Jenny Bryan. It is an excellent keynote on errors in R and how to identify and fix them.

Level 1 - Check for typos

Read the error or warning message carefully and try to decipher it.

Level 2 - Tracking the error

If the problem is not immediately obvious from the error statement or a search for typos, you try running through the code line by line, or inserting print() statements.

If your code / function is short, it can be helpful to run the code line by line, viewing constructed objects as you go and confirming they match what is expected. This can be done by highlighting individual objects or lines of code and pressing Command + Enter on a Mac (Ctrl + Enter on Windows).

If your code is more complex -- if you have a number of functions that call on each other for example -- then running your code line by line might be too confusing. In this case you might want to try inserting print() or str() statements at key points in your code. This is especially useful if you have some idea where the error is occurring! This can be done by calling traceback() immediately after an error occurs.

a <- function(x) x + y
b <- function(x) a(x)

b(2)

traceback()

This function prints a list of functions (calls) that have lead to the error being triggered. This list is called the call stack (or stack trace).

Level 3 - Browsing the error

sfsdf

Examples

For each problem, try copying the code into an instance of RStudio and work from there. Accompanying solutions are provided in a separate tab.

Error: could not find function {.tabset}

Problem

Define a calculate() function, which calls add2() and multiplyby8() to perform the following calculation:

$$y = 8(x + 2)$$

multiplyby8 <- function(value) {
  value * 8
}

add2 <- function(value) {
  result <- value + 2
  multiplby8(result)
}

calculate <- function(value) {
  add2(value)
}

calculate(2)

Hints

In this problem, R couldn't find the function multiplby8():


The function calculate() calls add2(), which itself calls multiplby8(), which doesn't exist!

Solution

multiplyby8 <- function(value) {
  value * 8
}

add2 <- function(value) {
  result <- value + 2
  multiplyby8(result)   # fix the typo here!
}

calculate <- function(value) {
  add2(value)
}

calculate(2)

{-}

Error: Unexpected '}' in {.tabset}

Problem

The cats() function determines cat vocalisations based on their age.

cats <- data.frame(name = c("Cady", "Kitten", "Cleo"),
                   age = c(15, 14, 3))

for (i in 1:3) {

  cat <- cats[i, ]

  if (cat$age > 10) {

    for (j in seq_len(cat$age)) {
      meows <- rep("Meeooooww!", cat$age)
    }

  }
     msg <- paste(cat$name, "Meeooooww!")
  }

 } else {

   msg <- paste(cat$name, "Pew! Pew!")

 }

  print(msg)

}

Hints


There are too many brackets here. It's easier to see which opening and closing brackets match by highlighting the lines (or pressing Command + A on a Mac / Ctrl + A on Windows), then using the Reindent Lines shortcut (in the menu bar, select Code, then Reindent Lines; or pressing Command + I on a Mac / Ctrl + I on Windows).

There are two extra curly brackets on lines 10 and 12.

To make the code easier to read, even more curly brackets can be removed. Since both the contents of the for loop and the contents of the else statement comprise only a single line.

Solution

cats <- data.frame(name = c("Cady", "Kitten", "Cleo"),
                   age = c(15, 14, 3))

for (i in 1:3) {

  cat <- cats[i, ]

  if (cat$age > 10) {

    for (j in seq_len(cat$age)) 
      meows <- rep("Meeooooww!", cat$age)

    msg <- paste(cat$name, "Meeooooww!")

  } else 
    msg <- paste(cat$name, "Pew! Pew!")

  print(msg)

}

{-}

Error: unexpected '=' in {.tabset}

Problem

Define a countdown() function, which counts down from a number before printing "Blast off!" to the console.

countdown <- function(number) {

  if (number < 0) {

    stop("Can't countdown from a negative time.")

  } else {

    timestep <- number

    for (i in 1:number) {
      print(timestep)
      timestep <- timestep - 1
      Sys.sleep(1)

      if (timestep = 0) 
        print("Blast off!")
    }

  }

}

countdown(3)

Hints

As the error stated, R has found an unexpected =:


Here, the code stops after hitting the first error. That's because of the way this website is generated. In RStudio, a number of unexpected '}' in ... errors follow this. The number of errors here might seem intimidating, but the important thing to note is that after we fix the first one, the remaining three will cease to occur. It's very important, therefore, to tackle errors in sequence.

So, returning to the unexpected =... the only = can be found on line 19. Now, recall that if statements are always followed by a condition. That is, a conditional test that results in TRUE or FALSE.

Solution

countdown <- function(number) {

  if (number < 0) {

    stop("Can't countdown from a negative time.")

  } else {

    timestep <- number

    for (i in 1:number) {
      print(timestep)
      timestep <- timestep - 1
      Sys.sleep(1)

      if (timestep == 0)   # fix conditional test
        print("Blast off!")
    }

  }

}

countdown(3)

{-}

Error: object not found {.tabset}

Problem

Given an acceleration from a point of rest and a time of travel, calculate the displacement of an object using Newton's second equation of motion:

$$s = ut + \frac{1}{2}at^2$$

displacement <- function(acceleration, time) {
  starting_speed <- 0
  initial_velocity * time + (0.5 * acceleration * (time ^ 2))
}

displacement(10, 18)

Hints

Here, an error is generated on line 3 when R tries to multiply initial_velocity by time, but can't find an initial_velocity object:


If an object is not defined earlier in the function, then it must be passed as an argument. It can't just magically appear out of thin air!

Solution

displacement <- function(acceleration, time) {
  initial_velocity <- 0   # initialise the object here!
  initial_velocity * time + (0.5 * acceleration * (time ^ 2))
}

displacement(10, 18)

{-}

Error: missing value where TRUE/FALSE needed {.tabset}

Problem

The checker() function checks whether a number is big (greater than 10) or small (less than or equal to 10) and prints out a descriptive statement to the console.

checker <- function(value) {

  if (value > 10) {

    print("Big number")

  } else {

    print("Small number")

  }

}

items <- list(2, 8, NA, 32)

for (i in items) {

  checker(i)

}

Hints

Here, R is returning an error on the third iteration of the for loop:


There if an error in if (value > 10) because a TRUE/FALSE value is expected. In this case, checker() is trying to determine whether the value, NA, is greater than 10. Since NA > 10 returns NA and if else statements only accept boolean values, an error is generated.

Solution

The solution here is to either remove NA from the items vector or check for non-numeric values in the function.

checker <- function(value) {

  if (!is.numeric(value)) {   # check for

    print("Not a number")     # non-numeric

  } else if (value > 10) {    # values here!

    print("Big number")

  } else {

    print("Small number")

  }

}

items <- list(2, 8, NA, 32)

for (i in items) {

  checker(i)

}

{-}

Warning: the condition has length > 1 and only the first element will be used {.tabset}

Problem

The function explode() takes a number between 0 and 11, otherwise NA is returned. Values between 0 and 10 are incremented by 1, whereas 11 is reset to 0.

explode <- function(value) {

  if (value > 11) {

    output <- NA

  } else if (value == 11) {

    output <- 0

  } else {

    output <- value + 1

  }

  output
}

vector <- c(2, 4, 11, 27, 15)
explode(vector)

Hints

Here we have a warning that has been triggered by if (value > 11). In this case, it's particularly dangerous because a Warning doesn't stop your code like an Error does, and here, erroneous values are returned!


The warnings are triggered on lines 3 and 7, where vector (a five element vector) is passed into explode() as the value argument.

The problem is that the condition (if (condition)) must have a length of 1 and therefore only the first element of the vector is being used in the comparison. That is, value is input as c(2, 4, 11, 27, 15), but the condition value > 11 tests on 2, returning FALSE. Then the condition value == 11 tests on 2, again returning FALSE. So then output <- value + 1 returns c(3, 5, 12, 28, 16).

Solution

The solution here is to either (1) ensure the function can only take a single input value; or (2) wrap the if else statements in a for loop, so that each element of the vector can be checked and transformed individually. Here we do the former:

explode <- function(value) {

  if (length(value) > 1)                   # add a check
    stop("Input must be a single value")   # here

  if (value > 11) {

    output <- NA

  } else if (value == 11) {

    output <- 0

  } else {

    output <- value + 1

  }

  output
}

vector <- c(2, 4, 11, 27, 15)

for (i in vector) {                        # and loop

  explode(i)                               # through the

}                                          # vector here

{-}

Error: unexpected symbol in {.tabset}

Problem

The quadratic() function is used to solve quadratic equations.

quadratic <- function(a, b, c) {

  root <- sqrt(b^2 - 4ac)
  solution1 <- -b + root / 2a
  solution2 <- -b - root / 2a
  list(solution1 solution2)

}

quadratic(1, -5, 6)

Hints


An unexpected symbol can often be avoided by keeping good code hygiene and making sure everything is neat and tidy. A good way to do this is to make good use of white space (blank lines between sections of code) as well as making a habit of selecting all of your code (Command + A on Mac / Ctrl + A on Windows), then reindenting your lines (Command + I on Mac / Ctrl + I on Windows).

In this case, that won't help because each error occurs on a single line. There are 4 syntax errors -- errors in the grammar of code -- in total, see if you can find them all.

Solution

quadratic <- function(a, b, c) {

  root <- sqrt(b^2 - 4 * a * c)    # here,
  solution1 <- -b + root / 2 * a   # here,
  solution2 <- -b - root / 2 * a   # here,
  list(solution1, solution2)       # and here!

}

quadratic(1, -5, 6)

{-}

Error: replacement has, data has {.tabset}

Problem

There are no functions here. We only wish to add a new column to a dataframe.

data <- data.frame(colour = c("teal", "mustard yellow", "leaf green", "dark",
                              "candyfloss pink", "quite a nice brown"), 
                   number = c(3328476, 9832, 23992611, 213987, 487621, 6328974))

data$time <- seq(0, 9, 2)

Hints

This error is a little difficult to read:


The key point to note here is that the replacement has 5 rows but the data has 6... and the replacement here is time.

Solution

data <- data.frame(colour = c("teal", "mustard yellow", "leaf green", "dark",
                              "candyfloss pink", "quite a nice brown"), 
                   number = c(3328476, 9832, 23992611, 213987, 487621, 6328974))

data$time <- seq(0, 9, 1.67)

{-}

Error: object of type 'closure' is not subsettable {.tabset}

Problem

The force() function calculates Newton's second law of motion.

force <- function(mass, acceleration) {

  mass * acceleration

}

dat <- data.frame(mass = c(20, 23, 27), acceleration = c(30, 40, 62))

for (i in seq_len(nrow(dat))) {

  mean[i] <- force(dat$mass[i], dat$acceleration[i])

}

mean

Hints

This is a tricky one that might require an internet search (for Stack Overflow) to solve.

Reading the error message, we know that the error is triggered on line 11 by mean[i] <- force(dat$mass[i], dat$acceleration[i]):


The problem is that "object of type 'closure' is not subsettable". What does this mean?

Solution

force <- function(mass, acceleration) {

  mass * acceleration

}

dat <- data.frame(mass = c(20, 23, 27), acceleration = c(30, 40, 62))

results <- c()   # define results object first and don't give an object
                 # the same name as a function!

for (i in seq_len(nrow(dat))) {

  results[i] <- force(dat$mass[i], dat$acceleration[i])

}

results

{-}



IBAHCM/RPiR documentation built on Jan. 12, 2023, 7:41 p.m.