Skip to content

Latest commit

 

History

History
441 lines (352 loc) · 14.3 KB

errorhandling.md

File metadata and controls

441 lines (352 loc) · 14.3 KB

Handling Errors in R Programming

Error Handling is a process in which we deal with unwanted or anomalous errors which may cause abnormal termination of the program during its execution. In R Programming, there are basically two ways in which we can implement an error handling mechanism. Either we can directly call the functions like stop() or warning(), or we can use the error options such as “warn” or “warning.expression”. The basic functions that one can use for error handling in the code :

  • stop(…): It halts the evaluation of the current statement and generates a message argument. The control is returned to the top level.
  • waiting(…): Its evaluation depends on the value of the error option warn. If the value of the warning is negative then it is ignored. In case the value is 0 (zero) they are stored and printed only after the top-level function completes its execution. If the value is 1 (one) then it is printed as soon as it has been encountered while if the value is 2 (two) then immediately the generated warning is converted into an error.
  • tryCatch(…): It helps to evaluate the code and assign the exceptions.

Condition Handling in R

Generally, if we encounter any unexpected errors while executing a program we need an efficient and interactive way to debug the error and know what went wrong. However, some errors are expected but sometimes the models fail to fit and throw an error. There are basically three methods to handle such conditions and errors in R :

  • try(): it helps us to continue with the execution of the program even when an error occurs.
  • tryCatch(): it helps to handle the conditions and control what happens based on the conditions.
  • withCallingHandlers():" it is an alternative to tryCatch() that takes care of the local handlers.

try-catch-finally in R

Unlike other programming languages such as Java, C++, and so on, the try-catch-finally statements are used as a function in R. The main two conditions to be handled in tryCatch() are “errors” and “warnings”.

Syntax:

check = tryCatch({
    expression
}, warning = function(w){
    code that handles the warnings
}, error = function(e){
    code that handles the errors
}, finally = function(f){
    clean-up code
})

Example:

# R program illustrating error handling
# Applying tryCatch
tryCatch(               
 
  # Specifying expression
  expr = {                     
    1 + 1
    print("Everything was fine.")
  },
  # Specifying error message
  error = function(e){         
    print("There was an error message.")
  },
  
  warning = function(w){      
    print("There was a warning message.")
  },
  
  finally = {            
    print("finally Executed")
  }
)

Output:

[1] "Everything was fine."
[1] "finally Executed"

withCallingHandlers() in R

In R, withCallingHandlers() is a variant of tryCatch(). The only difference is tryCatch() deals with exiting handlers while withCallingHandlers() deals with local handlers.

Example:

# R program illustrating error handling
 
# Evaluation of tryCatch
check <- function(expression){
 
withCallingHandlers(expression,
         
        warning = function(w){
        message("warning:\n", w)
        },
        error = function(e){
        message("error:\n", e)
        },
        finally = {
        message("Completed")
        })
}
 
check({10/2})
check({10/0})
check({10/'noe'})

error handling!

Condition Handling in R Programming

Decision handling or Condition handling is an important point in any programming language. Most of the use cases result in either positive or negative results. Sometimes there is the possibility of condition checking of more than one possibility and it lies with n number of possibilities. In this article let’s discuss how condition handling works in R language.

Communicating Potential Problems Developers always write good code to achieve good results. Not all problems are unexpected. Few expected potential problems are,

  • Giving the wrong type of input for a variable. In the place of numbers, we are giving alphanumeric values,
  • While uploading a file, non-existence of a file in the mentioned location,
  • The resultant output after calculating some value is expected as a numeric but the original output is either empty or null or invalid.

During these scenarios, there are possibilities of going wrong with R Code. And via errors, warnings, and messages they can be communicated. Fatal errors are raised by stop() and force all execution to terminate. Errors are used when there is no way for a function to continue. Warnings are generated by warning() and are used to display potential problems, such as when some elements of a vectorized input are invalid, like log(-1:2). Messages are generated by message() and are used to give informative output in a way that can easily be suppressed by the user.

Handling Conditions Programmatically In the R language, there are three different tools that are there for handling conditions including errors programmatically. try() gives you the ability to continue execution even when an error occurs. Example:

success <- try(100 + 200) 
failure <- try("100" + "200")

Output: Error in "100" + "200" : non-numeric argument to binary operator

# Class of success
class(success) 

[1] "numeric"

# Class of failure
class(failure) 

[1] "try-error"

When we put the code inside the try block, code executes, even error occurs, and also for correct results, it will be the last result evaluated, and if a failure, it will give with “try-error”. tryCatch() specifies handler functions that control what happens when a condition is signaled. One can take different actions for warnings, messages, and interrupts.

Example:

# Using tryCatch()
display_condition <- function(inputcode)
{ 
  tryCatch(inputcode, 
           error = function(c) "Unexpected error occurred", 
           warning = function(c) "warning message, but
                                  still need to look into code", 
           message = function(c) "friendly message, but
                                   take precautions") 
}
 
# Calling the function
display_condition(stop("!")) 
display_condition(warning("?!")) 
display_condition(message("?")) 
display_condition(10000)
For Input:
display_condition(stop("!")) 
Output: 
[1] "Unexpected error occurred" 
For Input: 
display_condition(warning("?!")) 
Output: 
[1] "warning message, but still need to look into code" 
For Input: 
display_condition(message("?")) 
Output: 
[1] "friendly message, but take precautions" 
For Input: 
display_condition(10000) 
Output: 
[1] 10000 

withCallingHandlers() is an alternative to tryCatch(). It establishes local handlers, whereas tryCatch() registers existing handlers. This will be most useful to handle a message with withCallingHandlers() rather than tryCatch() since the latter will stop the program.

Example:

# Using tryCatch()
message_handler <- function(c) cat("Important message is caught!\n") 
    tryCatch(message = message_handler,
    { 
        message("1st value printed?") 
      message("Second value too printed!") 
    })

Output:

Important message is caught!

# Using withCallingHandlers()
message_handler <- function(c) cat("Important message is caught!\n") 
withCallingHandlers(message = message_handler,
    { 
      message("1st value printed?") 
      message("Second value too printed!") 
    })

Output:

Important message is caught!
1st value printed?
Important message is caught!
Second value too printed!

Custom Signal Classes One of the challenges of error handling in R is that most functions just call stop() with a string. For example, “expected” errors (like a model failing to converge for some input datasets) can be silently ignored, while unexpected errors (like no disk space available) can be propagated to the user. Example:

# R program to illustrate Custom signal classes
 
condition <- function(subclass, message,
                      call = sys.call(-1), ...) {
  structure(
    class = c(subclass, "condition"),
    list(message = message, call = call),
    ...
  )
}
 
is.condition <- function(x) inherits(x, "condition")
e <- condition(c("my_error", "error"),
               "Unexpected error occurred")
stop(e) # Output as Unexpected error occurred
 
# comment out stop(e)
w <- condition(c("my_warning", "warning"),
                  "Appropriate warning!!!!")
warning(w) # Output as Appropriate warning!!!!
           # as well as Important message to be noted!!
           # will be printed
 
m <- condition(c("my_message", "message"),
               "Important message to be noted!!!")
message(m) # Output as Important message to be noted!!!

Output:

Error: Unexpected error occurred 
Execution halted

But at the same time, if stop(e) line is commented out, both warning and message are printed.

Warning message: 
Appropriate warning!!!! 
Important message to be noted!! 

And if warning(w) is commented out, then

Important message to be noted!! 

So by using custom signal classes, we can differentiate the errors. We can see that in detail below

Example:

# R program to illustrate Custom signal classes
 
condition <- function(subclass, message,
                      call = sys.call(-1), ...) {
  structure(
    class = c(subclass, "condition"),
    list(message = message, call = call),
    ...
  )
}
is.condition <- function(x) inherits(x, "condition")
custom_stop <- function(subclass, message,
                        call = sys.call(-1),
                        ...) {
c <- condition(c(subclass, "error"), message,
               call = call, ...)
  stop(c)
}
 
check_log <- function(x) {
  if (!is.numeric(x))
    custom_stop("invalid_class",
                "check_log() needs numeric input")
  if (any(x < 0))
    custom_stop("invalid_value",
                "check_log() needs positive inputs")
  log(x)
}
 
tryCatch(
  check_log("ant"),  # As we pass "ant", we will
                     # get Numeric inputs
                     # only are allowed as output
   
  # for input, if we give with negative value
  # let us check what happens,
  # for that uncomment below line and see,
  # obviously you get Positive
  # numeric value need to be provided
  #check_log("-100"),
  invalid_class = function(c) "Numeric inputs
                               only are allowed",
  invalid_value = function(c) "Positive numeric value
                               need to be provided"
)

Output:

[1] "Numeric inputs only are allowed"

But at the same time, when we passed check_log(“100”),

[1] "Only positive values are allowed" 

Debugging in R Programming

Debugging is a process of cleaning a program code from bugs to run it successfully. While writing codes, some mistakes or problems automatically appears after the compilation of code and are harder to diagnose. So, fixing it takes a lot of time and after multiple levels of calls.

Debugging in R is through warnings, messages, and errors. Debugging in R means debugging functions. Various debugging functions are:

  • Editor breakpoint
  • traceback()
  • browser()
  • recover()

Editor Breakpoints Editor Breakpoints can be added in RStudio by clicking to the left of the line in RStudio or pressing Shift+F9 with the cursor on your line. A breakpoint is same as browser() but it doesn’t involve changing codes. Breakpoints are denoted by a red circle on the left side, indicating that debug mode will be entered at this line after the source is run.

editor breakpoints!

traceback() Function The traceback() function is used to give all the information on how your function arrived at an error. It will display all the functions called before the error arrived called the “call stack” in many languages, R favors calling traceback.

Example:

# Function 1
function_1 <- function(a){
 a + 5
}
  
# Function 2
function_2 <- function(b) {
 function_1(b)
}
  
# Calling function
function_2("s")
  
# Call traceback()
traceback()\

Output:

2: function_1(b) at #1
1: function_2("s")

traceback() function displays the error during evaluations. The call stack is read from the function that was run(at the bottom) to the function that was running(at the top). Also we can use traceback() as an error handler which will display error immediately without calling of traceback.

# Function 1
function_1 <- function(a){
 a + 5
}
  
# Function 2
function_2 <- function(b){
 function_1(b)
}
  
# Calling error handler
options(error = traceback)
function_2("s")

Output:

Error in a + 5 : non-numeric argument to binary operator
2: function_1(b) at #1
1: function_2("s")

browser() Function browser() function is inserted into functions to open R interactive debugger. It will stop the execution of function() and you can examine the function with the environment of itself. In debug mode, we can modify objects, look at the objects in the current environment, and also continue executing.

Example: browser function!

browser[1]> command in consoles confirms that you are in debug mode. Some commands to follow:

  • ls(): Objects available in current environment.
  • print(): To evaluate objects.
  • n: To examine the next statement.
  • s: To examine the next statement by stepping into function calls.
  • where: To print a stack trace.
  • c: To leave debugger and continue with execution.
  • C: To exit debugger and go back to R prompt.

Also, debug() statement automatically inserts browser() statement at the beginning of the function.

recover() Function recover() statement is used as an error handler and not like the direct statement. In recover(), R prints the whole call stack and lets you select which function browser you would like to enter. Then debugging session starts at the selected location.

Example:

# Calling recover
options(error = recover)
  
# Function 1
function_1 <- function(a){
 a + 5
}
  
# Function 2
function_2 <- function(b) {
 function_1(b)
}
  
# Calling function
function_2("s")

Output:

Enter a frame number, or 0 to exit   

1: function_2("s")
2: #2: function_1(b)

Selection: 

The debugging session starts at the selected location.