Hour 8. Writing Functions: Part II


What You’ll Learn in This Hour:

Image How to check the appropriateness of function inputs

Image How to return errors and warnings from a function

Image How to use function “ellipsis”


In the last hour, you saw how to create a number of simple R functions. This included the definition of function inputs, the creation of the function body, and the management of results back to the user. You also saw how to control the overall “flow” through a function with the if/else structure. This hour will look at a range of advanced function writing topics, such as returning error messaging, checking whether inputs are appropriate to our functions, and the use of function “ellipsis.”

Errors and Warnings

On occasion, we may wish to return errors or warnings to the users of our functions. This allows us to inform our users of unexpected behavior and communicate the resulting impact on the execution of the functions (for example, stop processing or continue with some assumption).

First, let’s consider a simple function. Here’s an example that causes unexpected behavior:

> logRange <- function(X) {
+   logX <- log(X)           # Takes logs of X
+   round(range(logX), 2)    # Return (rounded) range of values
+ }
> logRange(1:5)  # Only positive integers
[1] 0.00 1.61
> logRange(-2:2) # Positive and negative integers
[1] NaN NaN
Warning message:
In log(X) : NaNs produced

When we execute our logRange function with a vector of positive integers, the function executes correctly. However, when we introduce negative integers, the function produces unexpected results: two NaN values are returned, and a warning message is produced.


Note: Adding the na.rm Argument

We could, of course, fix this function by removing missing values (with is.na) or calculating the range without missing values (using the na.rm argument to range). However, we’ll instead use error and warning messages to illustrate the behavior of these features.


Error Messages

It could be that we want to return an error message when we find negative integers in the input data and halt the execution of the function. We can achieve this with the stop function, which accepts an error message to return:

> logRange <- function(X) {
+   stop("Negative Values found!")  # Return an error message
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:5)  # Only positive integers
Error in logRange(1:5) : Negative Values found!
> logRange(-2:2) # Positive and negative integers
Error in logRange(-2:2) : Negative Values found!

In this case, we can see that an error message is returned to the user. However, the error message is returned regardless of whether negative values are found. Let’s update our function to return an error only when a particular condition is met, using the if/else structure from the last hour:

> logRange <- function(X) {
+   if (any(X <= 0))  stop("Negative Values found!")
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:5)  # Only positive integers
[1] 0.00 1.61
> logRange(-2:2) # Positive and negative integers
Error in logRange(-2:2) : Negative Values found!

Now the error message is only returned if there are any values of X less than or equal to 0, and we’ve provided a (slightly) more informative error message to the user. Note that the function stops executing at this point and no value is returned. This can be further illustrated by introducing an artificial message using cat:

> logRange <- function(X) {
+   if (any(X <= 0))  stop("Negative Values found!")
+   cat("Made it this far!! ")
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:5)  # Only positive integers
Made it this far!!
[1] 0.00 1.61
> logRange(-2:2) # Positive and negative integers
Error in logRange(-2:2) : Negative Values found!

Warning Messages

In the last example, we halted the flow of the function under a specific condition (that is, if any negative values exist). We sometimes want to warn the user that something has happened, inform them of how we’re going to continue, and then execute the rest of the function. For example, we may want to check for any negative values, and if there are any, we want to do the following:

Image Remove the negative values.

Image Inform the user that we’re continuing without these values.

We can achieve this using the warning function, which, as with the stop function, accepts a message to display to the user:

> logRange <- function(X) {
+   if (any(X <= 0)) {
+     warning("Some values were <= 0. We will remove them")
+     X <- X [ X > 0 ]
+   }
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:5)  # Only positive integers
[1] 0.00 1.61
> logRange(-2:2) # Positive and negative integers
[1] 0.00 0.69
Warning message:
In logRange(-2:2) : Some values were <= 0. We will remove them

Note that, in both instances, the function continues and a result is provided. However, when negative integers are found, the user is warned.

We could extend this further to inform the user of the number of values we have removed:

> logRange <- function(X) {
+   lessTest <- X <= 0              # Test for values <= 0
+   if (any(lessTest)) {
+     nLess <- sum(lessTest)        # How many values
+     outMessage <- paste(nLess, "values were <= 0. We will remove them")
+     warning(outMessage)
+     X <- X [ X > 0 ]
+   }
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:5)  # Only positive integers
[1] 0.00 1.61
> logRange(-2:2) # Positive and negative integers
[1] 0.00 0.69
Warning message:
In logRange(-2:2) : 3 values were <= 0. We will remove them

Of course, if we removed all of the negatives there may not be any left, so perhaps we should mix both the “error” and “warn” approaches:

> logRange <- function(X) {
+   lessTest <- X <= 0                              # Test for values <= 0
+   if (all(lessTest)) stop("All values are <= 0")  # Stop if all <= 0
+   if (any(lessTest)) {
+     nLess <- sum(lessTest)        # How many values
+     outMessage <- paste(nLess, "values were <= 0. We will remove them")
+     warning(outMessage)
+     X <- X [ X > 0 ]
+   }
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:5)    # Only positive integers
[1] 0.00 1.61
> logRange(-2:2)   # Positive and negative integers
[1] 0.00 0.69
Warning message:
In logRange(-2:2) : 3 values were <= 0. We will remove them
> logRange(-(1:5)) # All negative integers
Error in logRange(-(1:5)) : All values are <= 0


Caution: Missing Values

We should also consider missing values in the preceding example, but we will leave it at this now.


Checking Inputs

In the last example, we checked whether the values of X were less than or equal to 0, and informed the user of the impact (with either an error or warning message). However, in this case, we are assuming the input to the function is a numeric object. Consider, instead, if we pass a character vector to the logRange function:

> logRange <- function(X) {
+   if (any(X <= 0))  stop("Negative Values found!")
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(LETTERS)  # A Character vector
Error in log(X) : non-numeric argument to mathematical function

Because we often write functions to expect a particular type of data structure, we commonly want to check whether these assumptions hold at the start of the function. To achieve this, R contains a large suite of functions that start with “is.”:

> apropos("^is\.") # Show all objects starting with "is."
 [1] "is.array"                "is.atomic"               "is.call"
 [4] "is.character"            "is.complex"              "is.data.frame"
 [7] "is.double"               "is.element"              "is.empty.model"
[10] "is.environment"          "is.expression"           "is.factor"
[13] "is.finite"               "is.function"             "is.infinite"
[16] "is.integer"              "is.language"             "is.leaf"
[19] "is.list"                 "is.loaded"               "is.logical"
[22] "is.matrix"               "is.mts"                  "is.na"
...

The “is.” functions take an object and return a TRUE or FALSE value, depending on whether the object matches the mode or class we’re testing for. Let’s look at some examples:

> letters        # The letters vector
[1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o"
[16] "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
> mode(letters)  # It's a character vector
[1] "character"

> is.vector(letters)     # Is it a vector?
[1] TRUE
> is.character(letters)  # Is it a character?
[1] TRUE
> is.matrix(letters)     # Is it a matrix?
[1] FALSE
> is.numeric(letters)    # Is it numeric?
[1] FALSE

We can introduce these functions to check the mode and class of our inputs before continuing:

> logRange <- function(X) {
+   if (!is.numeric(X) | !is.vector(X)) stop("Need a numeric vector!")
+   if (any(X <= 0)) stop("Negative Values found!")
+   logX <- log(X)                  # Takes logs of X
+   round(range(logX), 2)           # Return (rounded) range of values
+ }
> logRange(1:10)        # A Numeric vector
[1] 0.0 2.3
> logRange(LETTERS)     # A Character vector
Error in logRange(LETTERS) : Need a numeric vector!
> logRange(airquality)  # A Data Frame
Error in logRange(airquality) : Need a numeric vector!


Note: Converting Objects

In addition to the suite of “is.” functions to check our object’s mode and class, there is an equivalent suite of “as.” functions, which will (attempt to) convert an object from one mode/class to another. Here is an example:

> charNums <- c("1.65", "2.03", "9.88", "3.51")  # Create character vector
> charNums
[1] "1.65" "2.03" "9.88" "3.51"
> is.numeric(charNums)                           # Is it numeric?
[1] FALSE
> convertNums <- as.numeric(charNums)            # Convert to numeric
> is.numeric(convertNums)                        # Is it numeric now?
[1] TRUE
> is.matrix(convertNums)                         # Is it a matrix?
[1] FALSE
> matNums <- as.matrix(convertNums)              # Convert to matrix
> is.matrix(matNums)                             # Is it a matrix now?
[1] TRUE
> matNums                                        # Print the matrix
     [,1]
[1,] 1.65
[2,] 2.03
[3,] 9.88
[4,] 3.51


The Ellipsis

As discussed in Hour 6, “Common R Utility Functions,” we can use the args function to check the inputs to a function. Let’s consider two examples: the runif function (which creates samples from a Uniform distribution) and the paste function (which concatenates strings). First, let’s use the runif function:

> args(runif)                          # Arguments of runif
function (n, min = 0, max = 1)
NULL
> runif(n = 10, min = 1, max = 100)    # Call runif
 [1] 84.95420 51.39096 66.54084 91.43757 88.51552 66.70264 45.44668
 [8] 19.76205 82.41349 36.74277

As you can see, we’ve specified the n, min, and max inputs to generate some random numbers. Now let’s consider an example using the paste function:

> fruits <- c("apples", "bananas", "pears", "peaches")
> paste("I like", fruits[1])
[1] "I like apples"
> paste("I like", fruits[1], "and", fruits[2])
[1] "I like apples and bananas"
> paste("I like", fruits[1], "and", fruits[2], "and", fruits[3])
[1] "I like apples and bananas and pears"
> paste("I like", fruits[1], "and", fruits[2], "and", fruits[3], "and", fruits[4])
[1] "I like apples and bananas and pears and peaches"

You can see that the paste function accepts any number of inputs that are simply “pasted” together. Given that we can pass “any number of inputs,” what do the arguments of paste look like? Let’s find out:

> args(paste)
function (..., sep = " ", collapse = NULL)
NULL

The first argument for paste is “...”, which is referred to as an “ellipsis.” The ellipsis here refers to “one or more inputs,” and the help file describes what the function will do with these inputs. In the case of the paste function, the inputs are described as follows:

...   one or more R objects, to be converted to character vectors.
sep         a character string to separate the terms. Not NA_character_.
collapse    an optional character string to separate the results. Not NA_character_.

Therefore, we can pass “one or more R objects” as the ellipsis.

Using the Ellipsis

We can use the ellipsis in our function definitions by specifying them in the arguments and then specifying where in the function body the inputs should be passed. Consider the following example, which allows the user to generate random samples from one of three different distributions:

> genRandoms <- function(N, dist, mean = 0, sd = 1, lambda, min, max) {
+   switch(dist,
+     "norm" = rnorm(N, mean = mean, sd = sd),
+     "pois" = rpois(N, lambda = lambda),
+     "unif" = runif(N, min = min, max = max))
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 4.071533 5.212119 5.610405 6.527552 4.519315 4.333632 4.518676
 [8] 5.242985 3.050987 5.969838
> genRandoms(10, "unif", min = 1, max = 10)
 [1] 2.830932 8.213797 5.294915 1.089826 4.190719 9.482410 2.877680
 [8] 1.398005 9.294324 9.313718

Here, we define many arguments that are parameters to the distribution functions (mean, sd, lambda, min, and max) and then pass them directly into function calls with syntax such as mean = mean, sd = sd.

Instead of defining the inputs in this way, we could use the ellipsis, as follows:

> genRandoms <- function(N, dist, ...) {
+   switch(dist,
+          "norm" = rnorm(N, ...),
+          "pois" = rpois(N, ...),
+          "unif" = runif(N, ...))
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 4.812319 4.330495 5.369091 4.205875 5.072567 4.029603 5.116522
 [8] 4.163062 6.231766 5.481158
> genRandoms(10, "unif", min = 1, max = 10)
 [1] 2.141485 5.552706 5.114769 2.800839 9.396432 8.006636 3.249285
 [8] 7.320116 4.525931 9.238757


Tip: Switching Flow

Note the use of the switch function in the preceding example. This function allows for a number of alternative flows to be executed, depending on the outcome of an initial expression. See the help file (?switch) for more details.


Passing Graphical Parameters Using the Ellipsis

We see many examples of the ellipsis with graphic functions. Consider the hist function, which (as you’ll see later) produces a simple histogram. The col and main arguments to the hist function control the color of the plot and the main title, respectively. Let’s see an example of producing a histogram with 1,000 samples from a Normal distribution. The output is seen in Figure 8.1.

> hist(rnorm(1000), main = "Nice Red Histogram", col = "red")

Image

FIGURE 8.1 Histogram of samples from a normal distribution


Note: Graphics

We will cover the use of functions such as hist to create graphics in Hour 13, “Graphics,” but they are useful to illustrate the ellipsis at this point. As such, in this section, do not worry too much about the uses of graphic functions, but look at the way in which the ellipsis is used.


Now let’s see the inputs to the hist function using args:

> args(hist)
function (x, ...)
NULL

From the help file, we can see that the col and main inputs are passed via the ellipsis, and are considered “further arguments and graphical parameters passed to plot.histogram.”

If we wanted to create a function that draws a specific graphic, we could also use the ellipsis to pass graphical parameters in the same way. Consider the following example, where we define a function histFun which creates a histogram and (optionally) adds a vertical line at the median. The output from this function can be seen in Figure 8.2.

> histFun <- function(X, addLine = TRUE, col = "lightblue", main = "Histogram") {
+   hist(X, col = col, main = main)
+   if (addLine) abline(v = median(X), lwd = 2)
+ }
> histFun(rnorm(1000), main = "New Title")

Image

FIGURE 8.2 Output from histFun: a histogram of samples from a normal distribution

We could represent many graphic parameters in this way, but we would need to specify them as inputs before our users can control those aspects of the graphic. This is another case where the ellipsis can add value. In this example, we’ve updated the histFun function with the ellipsis, then passed those inputs directly to the call to hist. The output from this example can be seen in Figure 8.3.

> histFun <- function(X, addLine = TRUE, ...) {
+   hist(X, ...)
+   if (addLine) abline(v = median(X), lwd = 2)
+ }
> histFun(rnorm(1000), col = "plum", xlab = "X AXIS LABEL")

Image

FIGURE 8.3 Plum-colored histogram created with histFun


Caution: Shortened Argument Calling

Earlier you saw that we can shorten the name of the input when calling a function as follows:

> aFunction <- function(x, inputWithLongName) {
+   x + inputWithLongName
+ }
> aFunction(x = 1, i = 2)
[1] 3

When there is an ellipsis in the argument definition, we can only use this approach for inputs defined before the ellipsis, as shown here:

> aFunction <- function(x, inputWithLongName, ...) {
+   x + inputWithLongName
+ }
> aFunction(x = 1, i = 2)
[1] 3
> aFunction <- function(..., x, inputWithLongName) {
+   x + inputWithLongName
+ }
> aFunction(x = 1, i = 2)
Error in aFunction(x = 1, i = 2) :
  argument "inputWithLongName" is missing, with no default


Checking Multivalue Inputs

In the previous section we defined a function called genRandoms that generates random numbers based on three possible distributions. We specify the distribution using the dist argument as follows:

> genRandoms <- function(N, dist, ...) {
+   switch(dist,
+          "norm" = rnorm(N, ...),
+          "pois" = rpois(N, ...),
+          "unif" = runif(N, ...))
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 4.152562 4.330108 6.580539 5.708272 5.872492 4.533635 4.295672
 [8] 5.654961 3.838976 4.474047
> genRandoms(10, "Normal", mean = 5)

Note that, for the last example, we specified the distribution as “Normal,” which isn’t an option in the switch function. As such, no tasks are performed—but this isn’t very intuitive.

We could improve the messaging to the users by specifying a last, unnamed option to the switch function:

> genRandoms <- function(N, dist, ...) {
+   switch(dist,
+          "norm" = rnorm(N, ...),
+          "pois" = rpois(N, ...),
+          "unif" = runif(N, ...),
+          stop(paste0("Distribution "", dist, "" not recognized")))
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 3.213303 5.564620 4.029048 6.004051 4.965648 3.395951 5.754919
 [8] 5.019788 5.627128 4.528970
> genRandoms(10, "Normal", mean = 5)
Error in genRandoms(10, "Normal", mean = 5) :
  Distribution "Normal" not recognized

This produces an error message stating that the input “Normal” is not recognized. Alternatively, we could use the match.arg function, which provides a neat mechanism for checking that an input is one of a list of “valid” inputs. The simplest way to use match.arg is to place a value in a first argument to be matched against a vector of possible values as the second argument:

> match.arg("norm", choices = c("norm", "pois", "unif"))
[1] "norm"
> match.arg("NORM", choices = c("norm", "pois", "unif"))
Error in match.arg("NORM", choices = c("norm", "pois", "unif")) :
  'arg' should be one of "norm", "pois", "unif"

We could include this approach to check whether an input is valid:

> genRandoms <- function(N, dist, ...) {
+   dist <- match.arg(dist, choices = c("norm", "pois", "unif"))  # Check dist
+   switch(dist,
+          "norm" = rnorm(N, ...),
+          "pois" = rpois(N, ...),
+          "unif" = runif(N, ...))
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 4.503535 4.971087 3.758512 4.580493 6.297477 2.688116 5.637076
 [8] 4.921771 4.408372 4.484797
> genRandoms(10, "Normal", mean = 5)
Error in match.arg(dist, choices = c("norm", "pois", "unif")) :
  'arg' should be one of "norm", "pois", "unif"

We can alternatively use match.arg in “one-argument form,” which matches our input against choices set in the argument statement:

> genRandoms <- function(N, dist = c("norm", "pois", "unif"), ...) {
+   dist <- match.arg(dist)  # Check validity if "dist" input
+   switch(dist,
+          "norm" = rnorm(N, ...),
+          "pois" = rpois(N, ...),
+          "unif" = runif(N, ...))
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 6.243477 4.173172 6.449329 3.768405 5.283295 4.849446 5.190646
 [8] 4.464281 6.497654 3.584767
> genRandoms(10, "Normal", mean = 5)
Error in match.arg(dist) : 'arg' should be one of "norm", "pois", "unif"


Tip: Getting a Function

If we wanted to, we could write this function more concisely using the get function, which returns a function, given its name as a character string. Therefore, the function could be rewritten as follows:

> genRandoms <- function(N, dist = c("norm", "pois", "unif"), ...) {
+   dist <- match.arg(dist)            # Check validity if "dist" input
+   randFun <- get(paste0("r", dist))  # Get the function
+   randFun(N, ...)                    # Run the function
+ }
> genRandoms(10, "norm", mean = 5)
 [1] 5.698743 5.463239 6.596608 4.385926 5.288524 6.200866 5.537720
 [8] 3.854999 4.781841 5.588260
> genRandoms(10, "pois", lambda = 3)
 [1] 5 3 1 1 2 2 3 2 2 1


Using Input Definition

Consider the following code, which plots two variables as a scatter plot. The output can be seen in Figure 8.4.

> Day <- 1:7
> Sales <- c(100, 120, 150, 130, 160, 210, 120)
> plot(Day, Sales, type = "o")

Image

FIGURE 8.4 Simple line plot of Sales versus Day

Note that the X axis is labeled “Day” and the Y axis is labeled “Sales.” This occurs because R is able to access the argument definitions and use them as the labels. This can be further illustrated using a modified example, the result of which can be seen in Figure 8.5.

> plot(Day - 1, log(Sales), type = "o")

Image

FIGURE 8.5 Simple line plot of log(Sales) versus “Day – 1”

As you can see, the labels reflect the modified inputs. This ability to capture not just the input values but also the definition that was used can be very useful. Consider, for example, if we create a function based on this plot, and use it to create a graph of our Sales data, shown in Figure 8.6.

> nicePlot <- function(X, Y) {
+   plot(X, Y, type = "o")
+ }
> nicePlot(Day, Sales)

Image

FIGURE 8.6 Simple line plot of Y versus X

In this example, the plot function uses the calling inputs X and Y for the axes. What if we instead want to capture the input definitions (Day and Sales) and use those for the axis labels?

To do this we use two functions together: substitute and deparse. The substitute function performs the action of capturing the definition, and the deparse function then converts this to characters:

> x <- 1 + 2                        # Add 2 numbers
> substitute(x <- 1 + 2)            # Capture the call
x <- 1 + 2
> deparse(substitute(x <- 1 + 2))   # Convert this to character
[1] "x <- 1 + 2"

We can use this approach to capture the inputs to our functions, then use the inputs to provide better labels to our plots. An example of this can be seen here, with the output seen in Figure 8.7.

> nicePlot <- function(X, Y) {
+   xLab <- deparse(substitute(X))   # Capture X input
+   yLab <- deparse(substitute(Y))   # Capture Y input
+   plot(X, Y, type = "o", xlab = xLab, ylab = yLab)
+ }
> nicePlot(Day, Sales)

Image

FIGURE 8.7 Simple line plot of Sales versus Day, with correct axis labels

Summary

In this hour, we looked at some more approaches that can enrich our R functions. In particular, we focused on ways in which we can check the inputs to a function, providing feedback to the function user if the inputs are not appropriate. In the next hour, we’ll look at how to perform tasks in a repetitive manner using loop structures, and how to extend into frameworks that allow us to apply functions to structures in more complex ways.

Q&A

Q. Is it possible to simplify the error messages by removing the “call”?

A. By default, the call made is included in the error message. See the inclusion of “in logFun(-2:2)” in the following error message:

> logFun <- function(X) stop("Your Error Message here!")
> logFun(-2:2)
Error in logFun(-2:2) : Your Error Message here!

You can remove the call itself from the error using the call. argument, which accepts a single logical value. (Note the period character in this argument name!) This argument can be used in both stop and warning functions.

> logFun <- function(X) stop("Your Error Message here!", call.=F)
> logFun(-2:2)
Error: Your Error Message here!

Q. What is the “environment” tag I see when I print out (some) functions?

A. Every function (with the exception of low-level “primitive” functions) has an “environment,” which is the active environment when the function was created.

Q. When is a warning message printed?

A. By default, a warning message is printed after a function completes; therefore, warnings are collated on the last line(s) of output:

> addFun <- function(x, y) {
+   warning("This is a warning!")
+   x + y
+ }
> addFun(1, 2)
[1] 3
Warning message:
In addFun(1, 2) : This is a warning!

We could, instead, issue warnings immediately using the immediate. argument to warning:

> addFun <- function(x, y) {
+   warning("This is a warning!", immediate. = T)
+   x + y
+ }
> addFun(1, 2)
Warning in addFun(1, 2) : This is a warning!
[1] 3

For more control over the behavior of warning messages, see the details for the warn option in the getOption function help file.

Q. Can the ellipsis be used in multiple places within the function body?

A. Yes, although care has to be taken to ensure the inputs in the ellipsis are applicable to all the functions we pass the ellipsis to.

Q. Can I capture the inputs contained in the ellipsis?

A. Yes, you can directly capture the input values using a line such as X <- list(...) and then process them in any manner you wish. Here’s an example:

> getDots <- function(...) {
+   list(...)
+ }
> getDots(1, 2)
[[1]]
[1] 1

[[2]]
[1] 2

> getDots(x = 1, y = 2)
$x
[1] 1

$y
[1] 2

Workshop

The workshop contains quiz questions and exercises to help you solidify your understanding of the material covered. Try to answer all questions before looking at the “Answers” section that follows.

Quiz

1. What’s the difference between stop and warning?

2. How would you check whether an input to a function is a character matrix?

3. What is the difference between is.data.frame and as.data.frame?

4. How many dots make up the ellipsis?

5. What are the two ways you’ve seen for using match.arg?

6. What do the deparse and substitute functions do?

Answers

1. The stop and warning functions both issue messages to the user. The primary difference is that the stop function causes the execution of the function to halt, whereas the warning function continues to execute after a warning is reported—unless controlled explicitly with getOption("warn").

2. You can use is.character & is.matrix as a condition.

3. The is.data.frame function takes an object and returns a TRUE value if the object is a data frame. The as.data.frame function takes an object and attempts to convert to a data frame.

4. The ellipsis is represented by exactly three dots.

5. You can call match.arg with the input as the first argument and a vector of possible “choices” as the second argument. Alternatively, you can use match.arg in one-argument mode, where you pass only an input to the function with the “choices” defined in the input definition. Here’s an example:

> genRandoms <- function(N, dist = c("norm", "pois", "unif"), ...) {
+   dist <- match.arg(dist)  # Check validity if "dist" input
+   dist
+ }

6. The substitute function returns the call that was made to create an input. The deparse function converts the output from substitute to character format. By using them together, you can access the call made to define an argument in a suitable (character) format:

> theCall <- function(x) {
+   deparse(substitute(x))
+ }
> theCall(x = mean(Sales))
[1] "mean(Sales)"

Activities

1. Create a function that accepts a vector input, X, and returns the mean and median of X.

2. Update your function so that a warning is issued if any missing values exist in X.

3. Update your function so that an error is returned if all values of X are missing.

4. Update your function to ensure that X is, actually, a numeric vector, and return an error if not.

5. Add an argument to your function called funs, and ensure the input is either mean, median, sd, min, or max. When called, the selected function defined in funs should be used to summarize X.

6. Look at the several.ok argument to match.arg. Update your function so that multiple summaries (that is, multiple values of funs) are returned from the function.

7. Update your function so the input definition of X (that is, the call used to define the X input) is printed (via a called to cat) before the summaries are returned.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset