How to check if any variable is defined in argument (...) in R? - r

If I have a function with argument (...) and want to check if a variable is defined in the argument. How can I do this? I have already looked at the solution provided at this link: How to check if object (variable) is defined in R?. However, it does not solve my problem.
# Scenario 1
exists("a")
# [1] FALSE
# Scenario 2
a <- 10
exists("a")
# [1] TRUE
# Define a function for remaining scenarios
f = function(...){exists("a", inherits = F)}
# Scenario 3
f()
# [1] FALSE
# Scenario 4
a <- 10
f()
# [1] FALSE
# Scenario 5
a <- 10
f(a = 5)
# [1] FALSE
I want the answer to be TRUE in Scenario 5.

Generally you use ... when you are passing parameters to other functions, not when you are using them in the function itself. It also makes a difference if you want to evaluate the parameter value or if you want to leave it unevaulated. If you need the latter, then you can do something like
f = function(...) {
mc <- match.call(expand.dots = TRUE)
"a" %in% names(mc)
}
This will return true for both
f(a = 4)
f(a = foo)
even when foo doesn't exist.

Does this suffice?
# Define a function for remaining scenarios
f = function(...){"a" %in% names(list(...))}
# Scenario 3
f()
# [1] FALSE
# Scenario 4
a <- 10
f()
# [1] FALSE
# Scenario 5
f(a = 5)
# [1] FALSE
f(a = 5)
[1] TRUE

Related

Is there a memory efficient way to change a higher level variable or counter in R with lapply?

I have a function in which a variable is initialized and then modified inside an lapply function.
f <- function() {
a <- 1
lapply(seq_len(1000000), function(i) {
print(paste0('a:', a))
b <- a + 1
a <- b
print(paste0('a:', a))
print(paste0('b:', b))
})
}
f()
# [1] "a:1"
# [1] "a:2"
# [1] "b:2"
# [1] "a:1"
# [1] "a:2"
# [1] "b:2"
...
However since lapply cannot modify variables in the parent scope, reassigning a inside the function does not carry throught every iteration giving me the wrong result. So I used the <<- operator which allows me to get the correct results, but at the cost of a much higher execution time on large datasets.
f2 <- function() {
a <- 1
lapply(seq_len(1000000), function(i) {
print(paste0('a:', a))
b <- a + 1
a <<- b
print(paste0('a:', a))
print(paste0('b:', b))
})
}
f2()
# [1] "a:1"
# [1] "a:2"
# [1] "b:2"
# [1] "a:2"
# [1] "a:3"
# [1] "b:3"
...
Is there a way to change a variable in this way without having to make a copy of it? The only solution I can think of is to use a data.table row to store my values and perform in-place changes.

Create expression test to see if `as.formula('X ~ 1')` in R contains an intercept?

In R, one can specify a formula:
F <- as.formula('X ~ 1')
I am trying to come up with a way to test if F above contains an intercept only, i.e., ~ 1. I was trying to use grepl to no avail. Is there a way to definitely test if the above formula contains only an intercept? i.e., I am hoping to come up with a method that would return true in the following different cases:
F <- as.formula('X~ 1')
F <- as.formula('X~1')
F <- as.formula('X ~1')
as well. Thanks!
attr(terms(x~y), 'intercept') will do what you want.
formula <- x~y
formula2 <- x~y-1 # no intercept
attr(terms(formula), 'intercept')
## [1] 1
attr(terms(formula2), 'intercept')
## [1] 0
EDIT: I initially misread the question. If you are looking for a specific example that will look for whether a formula contains only an intercept you could use:
f1 <- x ~ y
f2 <- x ~ y-1
f3 <- x ~ 1
f3 <- x ~ 0
onlyIntercept <- function(f){
return(attr(terms(f), 'intercept') & length(attr(terms(f), 'factors')) == 0)
}
# Examples on above, then on OPs examples:
onlyIntercept(f1)
## [1] FALSE
onlyIntercept(f2)
## [1] FALSE
onlyIntercept(f3)
## [1] TRUE
onlyIntercept(f4)
## [1] FALSE
onlyIntercept(as.formula('X~ 1'))
## [1] TRUE
onlyIntercept(as.formula('X~1'))
## [1] TRUE
onlyIntercept(as.formula('X ~1'))
## [1] TRUE
The onlyIntercept function I define here checks if the intercept attribute is 0 or 1 and checks if there are any additional factors(variables) that would normally be included in a model. If none are present this attribute has a length of 0 and can easily be checked.
You can use the lazyeval package:
> F <- as.formula('X ~ 1')
> lazyeval::f_rhs(F)
[1] 1
We can extract and check
F[[3]] == 1
because if we do as.list, the 3rd list element is 1
as.list(F)
#[[1]]
# `~`
#[[2]]
#X
#[[3]]
#[1] 1
it will return TRUE in all the 3 'F's in the OP"s post

Looping through 2 vectors of different dimension in R

I have two character vectors a, b with different dimensions. I have to take each element in a and compare with all elements in b and note the element if there is a close match. For matching I'm using agrepl function.
Following is the sample data
a <- c("US","Canada","United States","United States of America")
b <- c("United States","U.S","United States","Canada", "America", "Spain")
Following is the code that I'm using to match. Please help me how to avoid for loop as my real data has more 900 and 5000 records respectively
for(i in 1:4)
{
for(j in 1:6)
{
bFlag <- agrepl(a[i],b[j], max.distance = 0.1,ignore.case = TRUE)
if(bFlag)
{
#Custom logic
}
else
{
#Custom logic
}
}
}
You don't need a double loop, since agrepl's second argument accepts vectors of length >= 1. So you could do something like:
lapply(a, function(x) agrepl(x, b, max.distance = 0.1, ignore.case = TRUE))
# [[1]]
# [1] TRUE TRUE TRUE FALSE FALSE TRUE
#
# [[2]]
# [1] FALSE FALSE FALSE TRUE FALSE FALSE
#
# [[3]]
# [1] TRUE FALSE TRUE FALSE FALSE FALSE
#
# [[4]]
# [1] FALSE FALSE FALSE FALSE FALSE FALSE
You can add some custom logic inside the lapply call if needed, but that's not specified in the question so I'll just leave the output as a list of logicals.
If you want indices (of TRUEs) instead of logicals, you can use agrep instead of agrepl:
lapply(a, function(x) agrep(x, b, max.distance = 0.1,ignore.case = TRUE))
# [[1]]
# [1] 1 2 3 6
#
# [[2]]
# [1] 4
#
# [[3]]
# [1] 1 3
#
# [[4]]
# integer(0)
If you only want the first TRUE index, you can use:
sapply(a, function(x) agrep(x, b, max.distance = 0.1,ignore.case = TRUE)[1])
# US Canada United States United States of America
# 1 4 1 NA

Extract function parameters and default values from any function

Is there a way to extract the parameters and their respective default values of any given function from outside the function?
For example, given:
myfunc <- function(a, b = 1) { print(c(a, b)) }
I'm looking for some function that will return:
list(a = NULL, b = 1)
or some variation thereof.
You are looking for formals().
formals(myfunc)
# $a
#
#
# $b
# [1] 1
If you needed NULL for a, you could do some checking. a will be of the "name" class and empty.
lapply(formals(myfunc), function(x) if(is.name(x) & !nzchar(x)) NULL else x)
# $a
# NULL
#
# $b
# [1] 1

How do I extract arguments in a function (written as a string) in R?

Let suppose I have defined a function by f <- function(x,y,z) {...}.
I would like to be able to transform an expression calling that function into a list of the parameters called by that function; it is the opposite of the do.call function.
For example, let us say I have such a function f, and I also have a string "f(2,1,3)".
How can I transform the string "f(2,1,3)" into the list of the parameters list(x=1,y=2,z=3)?
After you've parsed your character string into an R expression, use match.call() to match supplied to formal arguments.
f <- function(x,y,z) {}
x <- "f(1,2,3)"
ee <- parse(text = x)[[1]]
cc <- match.call(match.fun(ee[[1]]), ee)
as.list(cc)[-1]
# $x
# [1] 1
#
# $y
# [1] 2
#
# $z
# [1] 3
Alternatively:
f <- function(x,y,z) {...}
s <- "f(x = 2, y = 1, z = 3)"
c <- as.list(str2lang(s))
c[-1]
# $x
# [1] 2
#
# $y
# [1] 1
#
# $z
# [1] 3
I was looking for a solution to this a while ago in order to reconstruct a function call from a string. Hopefully this will be of use to someone who is looking for a solution to a similar problem.

Resources