> inch.to.cm <- function(){
+ cm <- 1*2.54
+ cat(cm)
+ }
> inch.to.cm()
2.54
> inch.to.cm <- function(inch){
+ cm <- inch*2.54
+ cat(inch,"inch is equal to",cm,"cm\n")
+ }
> inch.to.cm(2)
2 inch is equal to 5.08 cm
> inch.to.cm(4)
4 inch is equal to 10.16 cm
To set some default value for the arguments when no argument values are assigned in function-
> inch.to.cm <- function(inch=1){
+ cm <- inch*2.54
+ cat(inch,"inch is equal to",cm,"cm\n")
+ }
Here we have used inch=1 as defualt value. Now if we use the function with no arguments we will still get an answer for the default value-
> inch.to.cm()
1 inch is equal to 2.54 cm
If we run the function without () we will see the code behind it-
> inch.to.cm
function(inch=1){
cm <- inch*2.54
cat(inch,"inch is equal to",cm,"cm\n")
}
The substitute function with as.character can be used to take the given argument as a character in a variable for further usage -
> sf <- function(name){
+ name <- as.character(substitute(name))
+ return(paste0("Hello ",name,"!"))
+ }
> sf(Himel)
[1] "Hello Himel!"
> class(sf(Himel))
[1] "character"
To know about different printing methods visit here.
Extras: Difference between quote and substitute -
> f <- function(arg) {
+ list(quote = quote(arg),
+ substitute = substitute(arg),
+ argument = arg)
+ }
>
> value <- 90
> f(arg=value)
$quote
arg
$substitute
value
$argument
[1] 90
Get help with your R and Statistics assignments.
If you want something further usable in return from the function then
use return-
> inch.to.cm <- function(inch=1){
+ cm <- inch*2.54
+ return(cm)
+ }
The output of the function will give us a value in [1] so that can be used for further calculations and assignments-
> inch.to.cm(2)
[1] 5.08
> inch.to.cm(2)*2 + inch.to.cm(5)
[1] 22.86
An example function to find the area and circumference of a circle from the given value of radius-
> area.cir <- function(r){
+ area <- pi*r**2
+ circum <- 2*pi*r
+ return(c(area = area, circumference = circum))
+ }
Now let’s use the function-
> area.cir(2)
area circumference
12.56637 12.56637
Let’s calculate it for multiple radius-
> x <- c(4,6,15,30)
> area.cir(x)
area1 area2 area3 area4 circumference1
50.26548 113.09734 706.85835 2827.43339 25.13274
circumference2 circumference3 circumference4
37.69911 94.24778 188.49556
The result shown is so messy and pretty unusable. So let’s use list instead of c in return-
> area.cir <- function(r){
+ area <- pi*r**2
+ circum <- 2*pi*r
+ return(list(area = area, circumference = circum))
+ }
> x <- c(4,6,15,30)
> area.cir(x)
$area
[1] 50.26548 113.09734 706.85835 2827.43339
$circumference
[1] 25.13274 37.69911 94.24778 188.49556
Now the areas can be called using area.cir(x)$area or
area.cir(x)[[1]] and similarly for circumference.
> area.cir(x)$area[3] # 3rd output from area
[1] 706.8583
Data frame can also be used instead of list to make the result more understandable-
> area.cir <- function(r){
+ area <- pi*r**2
+ circum <- 2*pi*r
+ return(data.frame(Radius = r, Area = area, Circumference = circum))
+ }
> x <- c(4,6,15,30)
> area.cir(x)
Radius Area Circumference
1 4 50.26548 25.13274
2 6 113.09734 37.69911
3 15 706.85835 94.24778
4 30 2827.43339 188.49556
> res <- area.cir(x)
> res$Area
[1] 50.26548 113.09734 706.85835 2827.43339
> area.cir <- function(r){
+ area <- pi*r**2
+ circum <- 2*pi*r
+ return(plot(r, area,
+ xlab="Radius", ylab="Area"))
+ }
> x <- c(4,6,15,20,27,30)
> area.cir(x)
Local variables are those inside the function or local environment. These cannot be called outside of the function-
> cm <- 10
> inch.to.cm <- function(inch=1){
+ cm <- inch*2.54
+ return(cm)
+ }
> inch.to.cm()
[1] 2.54
> cm
[1] 10
In the example cm variable is called. But the value is not from the function. The value assigned to cm before the function was called here, which is global.
In the above example we’ve seen that the value of cm wasn’t changed. We can use <<- to change any variables value of the preceding scope from inside the function -
> cm <- 10
> inch.to.cm <- function(inch=1){
+ cm <<- inch*2.54
+ return(cm)
+ }
> inch.to.cm()
[1] 2.54
> cm
[1] 2.54
So now the variable’s value of the global environment’s has changed. Details on this can be found in this stackover flow answer https://stackoverflow.com/questions/32623856/difference-between-and?noredirect=1&lq=1
Here is a code that utilizes while loop to find prime factors -
> prime_factors_Loop <- function(x){
+ factors = c()
+ i = 2
+ while(x >= i){
+ if(! x %% i) {
+ factors <- c(factors, i)
+ x <- x/i
+ i <- i - 1
+ }
+ i <- i + 1
+ }
+ return(factors)
+ }
> prime_factors_Loop(2364)
[1] 2 2 3 197
If you don’t want to load a output after calculation, and only to assign, then use invisible() instead of return().
> trial_func_ret <- function(x){
+ res <- mean(x)
+ return(res)
+ }
>
> trial_func_inv <- function(x){
+ res <- mean(x)
+ invisible(res)
+ }
>
> vals <- c(4,5,2,6,9)
> trial_func_ret(x = vals)
[1] 5.2
> trial_func_inv(x = vals)
The returned result can only be shown after assigning to an object:
> a <- trial_func_inv(x = vals)
> a
[1] 5.2
There are mainly three types of function:
To see the available primitive function in R -
> names(methods:::.BasicFunsList)
[1] "$" "$<-" "["
[4] "[<-" "[[" "[[<-"
[7] "%*%" "xtfrm" "c"
[10] "all" "any" "sum"
[13] "prod" "max" "min"
[16] "range" "is.matrix" ">="
[19] "cosh" "cummax" "dimnames<-"
[22] "as.raw" "log2" "tan"
[25] "dim" "as.logical" "^"
[28] "is.finite" "sinh" "log10"
[31] "as.numeric" "dim<-" "is.array"
[34] "tanpi" "gamma" "atan"
[37] "as.integer" "Arg" "signif"
[40] "cumprod" "cos" "length"
[43] "!=" "digamma" "exp"
[46] "floor" "acos" "seq.int"
[49] "abs" "length<-" "sqrt"
[52] "!" "acosh" "is.nan"
[55] "Re" "tanh" "names"
[58] "cospi" "&" "anyNA"
[61] "trunc" "cummin" "levels<-"
[64] "*" "Mod" "|"
[67] "names<-" "+" "log"
[70] "lgamma" "as.complex" "asinh"
[73] "-" "sin" "/"
[76] "as.environment" "<=" "as.double"
[79] "is.infinite" "is.numeric" "rep"
[82] "round" "sinpi" "dimnames"
[85] "asin" "as.character" "%/%"
[88] "is.na" "<" ">"
[91] "Im" "%%" "trigamma"
[94] "==" "cumsum" "atanh"
[97] "sign" "ceiling" "Conj"
[100] "as.call" "log1p" "expm1"
[103] "(" ":" "="
[106] "@" "{" "~"
[109] "&&" ".C" "baseenv"
[112] "quote" "::" "<-"
[115] "is.name" "if" "||"
[118] "attr<-" "untracemem" ".cache_class"
[121] "substitute" "interactive" "is.call"
[124] "switch" "function" "is.single"
[127] "is.null" "is.language" "is.pairlist"
[130] ".External.graphics" "globalenv" "class<-"
[133] ".Primitive" "is.logical" "enc2utf8"
[136] "UseMethod" ".subset" "proc.time"
[139] "enc2native" "repeat" ":::"
[142] "<<-" "@<-" "missing"
[145] "nargs" "isS4" ".isMethodsDispatchOn"
[148] "forceAndCall" ".primTrace" "storage.mode<-"
[151] ".Call" "unclass" "gc.time"
[154] ".subset2" "environment<-" "emptyenv"
[157] "seq_len" ".External2" "is.symbol"
[160] "class" "on.exit" "is.raw"
[163] "for" "is.complex" "list"
[166] "invisible" "is.character" "oldClass<-"
[169] "is.environment" "attributes" "break"
[172] "return" "attr" "tracemem"
[175] "next" ".Call.graphics" "standardGeneric"
[178] "is.atomic" "retracemem" "expression"
[181] "is.expression" "call" "is.object"
[184] "pos.to.env" "attributes<-" ".primUntrace"
[187] "...length" ".External" "oldClass"
[190] ".Internal" ".Fortran" "browser"
[193] "is.double" ".class2" "while"
[196] "nzchar" "is.list" "lazyLoadDBfetch"
[199] "...elt" "...names" "is.integer"
[202] "is.function" "is.recursive" "seq_along"
[205] "unlist" "as.vector" "lengths"
For example sum is a primitive function -
> sum
function (..., na.rm = FALSE) .Primitive("sum")
We can see it by the word .Primitive("sum") in the
output.
Check if a function is primitive -
> is.primitive(mean)
[1] FALSE
> is.primitive(sum)
[1] TRUE
> is.primitive(is.primitive)
[1] FALSE
> is.primitive(is.integer)
[1] TRUE
Primitive functions has the type = “builtin” -
> typeof(sum)
[1] "builtin"
Whereas -
> typeof(mean)
[1] "closure"
Infix functions are those functions in which the function name comes in between its arguments, and hence have two arguments. R comes with a number of built-in infix operators such as :, ::, :::, $, @, ^, *, /, +, -, >, >=, <, <=, ==, !=, !, &, &&, |, ||, ~, <-, and <<-. One can create his own infix functions that start and end with %. The name of an infix function is more flexible as it can contain any sequence of characters except %. There are some predefined infix operators in R programming. Source
Examples:
You can create your own infix function Source -
> # number in between two values
> "%><%" <- function(x, rng) x > rng[1] & x < rng[2]
>
> # number in between two values including those
> "%>=<%" <- function(x, rng) x >= rng[1] & x <= rng[2]
>
> x = 1:7
> x
[1] 1 2 3 4 5 6 7
> x[x %><% c(2,5)]
[1] 3 4
> x[x %>=<% c(2,5)]
[1] 2 3 4 5
Replacement functions modify their arguments in place(modifying an R object usually creates a copy). The name of replacement functions are always succeeded by <. They must have arguments named x and value, and return the modified object.
> "replace_last<-" <- function(x, value)
+ {
+ x[length(x)] = value
+ x
+ }
Run the function as: replace_last(x) <- value
> x <- 1:6
> x
[1] 1 2 3 4 5 6
> replace_last(x) <- 999
> # it will replace the last value as defined in the function
> print(x)
[1] 1 2 3 4 5 999
Find more discussion on this in here.
Source1
Source2
Source3
For many generic functions the function body is quite short, for example
-
> mean
function (x, ...)
UseMethod("mean")
<bytecode: 0x00000273f1d64500>
<environment: namespace:base>
The presence of UseMethod indicates this is a generic function. To see what methods are available we can use methods() -
> methods(mean)
[1] mean.Date mean.default mean.difftime mean.POSIXct mean.POSIXlt
[6] mean.quosure*
see '?methods' for accessing help and source code
Non-visible functions are asterisked.
To read the non-visible functions we can utilize the
getAnywhere() function -
> getAnywhere(mean.quosure)
A single object matching 'mean.quosure' was found
It was found in the following places
registered S3 method for mean from namespace rlang
namespace:rlang
with value
function (x, na.rm = TRUE, ...)
{
abort_quosure_op("Summary", "mean")
}
<bytecode: 0x00000273f4559930>
<environment: namespace:rlang>
It can also be used for others -
> getAnywhere(mean.default)
A single object matching 'mean.default' was found
It was found in the following places
package:base
registered S3 method for mean from namespace base
namespace:base
with value
function (x, trim = 0, na.rm = FALSE, ...)
{
if (!is.numeric(x) && !is.complex(x) && !is.logical(x)) {
warning("argument is not numeric or logical: returning NA")
return(NA_real_)
}
if (isTRUE(na.rm))
x <- x[!is.na(x)]
if (!is.numeric(trim) || length(trim) != 1L)
stop("'trim' must be numeric of length one")
n <- length(x)
if (trim > 0 && n) {
if (is.complex(x))
stop("trimmed means are not defined for complex data")
if (anyNA(x))
return(NA_real_)
if (trim >= 0.5)
return(stats::median(x, na.rm = FALSE))
lo <- floor(n * trim) + 1
hi <- n + 1 - lo
x <- sort.int(x, partial = unique(c(lo, hi)))[lo:hi]
}
.Internal(mean(x))
}
<bytecode: 0x00000273f18dbd30>
<environment: namespace:base>
> getAnywhere(Filter)
A single object matching 'Filter' was found
It was found in the following places
package:base
namespace:base
with value
function (f, x)
{
f <- match.fun(f)
ind <- as.logical(unlist(lapply(x, f)))
x[which(ind)]
}
<bytecode: 0x00000273f11d63d0>
<environment: namespace:base>
getS3method function can be used too -
> getS3method(f = "mean", class = "default")
function (x, trim = 0, na.rm = FALSE, ...)
{
if (!is.numeric(x) && !is.complex(x) && !is.logical(x)) {
warning("argument is not numeric or logical: returning NA")
return(NA_real_)
}
if (isTRUE(na.rm))
x <- x[!is.na(x)]
if (!is.numeric(trim) || length(trim) != 1L)
stop("'trim' must be numeric of length one")
n <- length(x)
if (trim > 0 && n) {
if (is.complex(x))
stop("trimmed means are not defined for complex data")
if (anyNA(x))
return(NA_real_)
if (trim >= 0.5)
return(stats::median(x, na.rm = FALSE))
lo <- floor(n * trim) + 1
hi <- n + 1 - lo
x <- sort.int(x, partial = unique(c(lo, hi)))[lo:hi]
}
.Internal(mean(x))
}
<bytecode: 0x00000273f18dbd30>
<environment: namespace:base>
Another easy way is to use the body function if you know the class of the function -
> body(mean.default)
{
if (!is.numeric(x) && !is.complex(x) && !is.logical(x)) {
warning("argument is not numeric or logical: returning NA")
return(NA_real_)
}
if (isTRUE(na.rm))
x <- x[!is.na(x)]
if (!is.numeric(trim) || length(trim) != 1L)
stop("'trim' must be numeric of length one")
n <- length(x)
if (trim > 0 && n) {
if (is.complex(x))
stop("trimmed means are not defined for complex data")
if (anyNA(x))
return(NA_real_)
if (trim >= 0.5)
return(stats::median(x, na.rm = FALSE))
lo <- floor(n * trim) + 1
hi <- n + 1 - lo
x <- sort.int(x, partial = unique(c(lo, hi)))[lo:hi]
}
.Internal(mean(x))
}
Arguments can be obtained using -
> args(mean.default)
function (x, trim = 0, na.rm = FALSE, ...)
NULL
If you use edit(), a prompt will open up with the
functions code -
> edit(lm.fit)
SO MUCH TO LEARN, SO LITTLE TIME :-)