What You’ll Learn in This Hour:
How to check the appropriateness of function inputs
How to return errors and warnings from a function
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.”
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.
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!
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:
Remove the negative values.
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.
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
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.
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.
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")
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")
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")
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
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
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")
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")
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)
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)
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. 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
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.
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?
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)"
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.