R non-standard evaluation - passing a list - r

How can I pass a character vector using NSE:
fun <- function(x){
x_ <-deparse(substitute(x))
print(x_)
}
fun_ <- function(x){
do_something(x)
}
For example,
fun(x= a, b, c)
should print interpret the argument to x as a vector c(a, b, c) and pass a character vector to fun_(x):
fun_(x=c("a", "b", "c"))
There are additional parameters as well.

You can't use commas within a parameter; commas separate parameters. Calling fun(x=a,b,c) would call fun() with three parameters, the first one named and the second two unnamed. If you just want to ignore the name x=, you can turn unquoted names to strings with
fun <- function(...){
x <- sapply(substitute(...()), deparse)
fun_(x)
}
fun_ <- function(x) {
print(x)
}
fun(a,b,c)
# [1] "a" "b" "c"
fun_(c("a","b","c"))
# [1] "a" "b" "c"

Related

How can I use an arguement supplied to a user defined function as both an input and a character string? [duplicate]

This question already has answers here:
In R, how to get an object's name after it is sent to a function?
(4 answers)
Closed 2 years ago.
I find I often am comparing two character vectors to see where they don't match up (typically columns in two different data frames). Because I'm doing this often, I want to write a function to make it easier. This is what I've come up with so far:
x <- c("A", "B", "C")
y <- c("B", "C", "D", "X")
check_mismatch <- function(vec1, vec2) {
vec1 <- unique(as.character(vec1))
vec2 <- unique(as.character(vec2))
missing_from_1 <- vec2[vec2 %notin% vec1]
missing_from_2 <- vec1[vec1 %notin% vec2]
print("Missing from vector 1")
print(missing_from_1)
print("Missing from vector 2")
print(missing_from_2)
}
check_mismatch(x,y)
[1] "Missing from vector 1"
[1] "D" "X"
[1] "Missing from vector 2"
[1] "A"
What I would really like is "Missing from x" instead of "Missing from vector 1". I would like the function to output the name of the actual argument that was entered. Another example of how I would like the function to work:
check_mismatch(all_polygons_df$Plot, sb_year$Plot)
[1] "Missing from all_polygons_df$Plot"
[1] "KWI-1314B"
[1] "Missing from sb_year$Plot"
character(0)
Any suggestions on how I could do this? I'm open to other ways of displaying the output too - perhaps some kind of table. But the output needs to be flexible to different lengths of output.
Up front, deparse(substitute(...)) is what you're asking for, and that is what makes your initial question a duplicate.
Some recommendations, however:
printing things to the console is a little off (IMO), since it prepends [1] to everything you print. Consider message (or cat). Since many R environments color things based on comments, etc, I have found it useful to prepend # before some text to break it out from other portions of the same text.
Your function is operating solely in side-effect, printing something to the console and then losing it forever. The function does happen to return a single object (the value of missing_from_2, accidentally), but it might be more useful if the function returned the mismatches.
With that, I offer an alternative:
check_mismatch <- function(vec1, vec2) {
nm1 <- deparse(substitute(vec1))
nm2 <- deparse(substitute(vec2))
vec1 <- unique(as.character(vec1))
vec2 <- unique(as.character(vec2))
missing_from_1 <- vec2[!vec2 %in% vec1]
missing_from_2 <- vec1[!vec1 %in% vec2]
setNames(list(missing_from_1, missing_from_2), c(nm1, nm2))
}
check_mismatch(x, y)
# $x
# [1] "D" "X"
# $y
# [1] "A"
One immediate benefit is that we can look for specific differences in one of the vectors immediately:
mis <- check_mismatch(x, y)
mis$x
# [1] "D" "X"
However, this uses the names of the variables presented to it. Realize that with non-standard evaluation comes responsibility and consequence. Consider:
mis <- check_mismatch(x, c("A", "B", "E"))
mis
# $x
# [1] "E"
# $`c("A", "B", "E")`
# [1] "C"
The name of the second element is atrocious. Fortunately, if all you care about is what the differences are for the second element, once can still use [[2]] to retrieve the character vector without issue. (This is mostly aesthetic.)
mis[[2]]
# [1] "C"
Also, one might want to repeat this for more than two vectors, so generalizing it might be useful (for "1 or more"):
check_mismatch_many <- function(...) {
dots <- list(...)
if (!length(dots)) {
out <- list()
} else {
nms <- as.character(match.call()[-1])
out <- lapply(seq_along(dots), function(i) {
b <- unique(unlist(dots[-i]))
b[!b %in% dots[[i]]]
})
out <- replace(out, sapply(out, is.null), list(dots[[1]][0]))
names(out) <- nms
}
out
}
z <- c("Y","Z")
check_mismatch_many()
# list()
check_mismatch_many(x)
# $x
# character(0)
check_mismatch_many(x, y)
# $x
# [1] "D" "X"
# $y
# [1] "A"
check_mismatch_many(x, y, z)
# $x
# [1] "D" "X" "Y" "Z"
# $y
# [1] "A" "Y" "Z"
# $z
# [1] "A" "B" "C" "D" "X"
And finally, if you want to be a little "personal" with the presentation on the console, you can go overboard and class it with an additional print.myclass S3 method.
check_mismatch_many <- function(...) {
dots <- list(...)
if (!length(dots)) {
out <- list()
} else {
nms <- as.character(match.call()[-1])
out <- lapply(seq_along(dots), function(i) {
b <- unique(unlist(dots[-i]))
b[!b %in% dots[[i]]]
})
out <- replace(out, sapply(out, is.null), list(dots[[1]][0]))
names(out) <- nms
}
class(out) <- c("mismatch", "list")
out
}
print.mismatch <- function(x, ...) {
cat("<Mismatch>\n")
cat(str(x, give.attr = FALSE, no.list = TRUE))
invisible(x)
}
mis <- check_mismatch_many(x, y)
mis
# <Mismatch>
# $ x: chr [1:2] "D" "X"
# $ y: chr "A"
(There are a lot more things you can do in the print.mismatch method, obviously. str is the major component of it, and it is the swiss-army-knife of depicting structure.)

Getting the names of inputted vectors to an R function

I was wondering how I could have my R function foo return the names of the vectors that are inputted to it?
In this example, I want foo to return "a" and "b".
Here is what I tried without success:
a = 1:30 ; b = 50:60 # the inputted vectors
foo <- function(...){ # the function
L <- list(...)
names(L)
}
# Example of use:
foo(a, b)
Using substitute as shown gives a pairlist of symbols and deparse applied individually to each element converts each to a character string:
foo <- function(...) sapply(substitute(...()), deparse)
foo(a, b)
## [1] "a" "b"
foo <- function(...) as.character(substitute((...)))[-1]
foo(a, b)
# [1] "a" "b"
Here is an option with match.call
foo <- function(...) sapply(as.list(match.call())[-1], as.character)
foo(a, b)
#[1] "a" "b"

Use to dictionary to relace certain specific values

I have the following ilst
list <- c("AB", "G", "H")
Now I have certain letters that should be replaced. So fe. B and H should be replaced.
So what I have not is:
replace_letter <- c("B", "H")
for(letter in replace_letter){
for (i in list){
print(i)
print(letter)
if(grepl(letter, i)){
new_value <- gsub(letter,"XXX",i)
print("yes")
}
else{
print("no")
}
}
}
However the XXX in my code should be replace by certain lookup values/.
So instead a B -> B+, in stead of H -> H**.
So I need some kind of dictionary function to replace the XXX with something specific.
Does anybody have suggestion how I can include this in the code above?
Data and dictionary
dictionary <- data.frame(From = LETTERS,
To = LETTERS[c(2:length(LETTERS), 1)], stringsAsFactors = F)
set.seed(1234)
data <- LETTERS[sample(length(LETTERS), 10, replace = T)]
Here is the replace-function
replace <- function(input, dictionary){
dictionary[which(input == dictionary$From),]$To
}
Apply it to data:
sapply(data, replace, dictionary = dictionary)
# C Q P Q W Q A G R N
# "D" "R" "Q" "R" "X" "R" "B" "H" "S" "O"
You just have to adjust your dictionary according to your needs.
I use the function plyr::mapvalues to do this. The function takes three arguments, the strings to do the replacement on, and two vectors from and to that define the replacement.
e.g.
plyr::mapvalues(letters[1:3], c("b", "c"), c("x", "y"))
# [1] "a" "x" "y"
I switched to the newer dplyr library, so I'll add another answer here:
In an interactive session I would enter the replacements in dplyr::recode directly:
dplyr::recode(letters[1:3], "b"="x", "c"="y")
# [1] "a" "x" "y"
Using a pre-defined dictionary, you'll have to use UQS to unquote the dictionary due to the tidy-eval semantics of dpylr:
dict <- c("b"="x", "c"="y")
dict
# b c
# "x" "y"
dplyr::recode(letters[1:3], UQS(dict))
# [1] "a" "x" "y"

Loop over named list of dfs, test condition on df's column, if true return name of list element

I have a named list of data.frame objects (training_data). Each data.frame object will be tested based on a "x" column and if the test is passed, the name of that data.frame object is supposed to be returned.
In the case below, "a" and "b" are supposed to be returned:
df <- data.frame(x=1:10, y=1:10)
df1 <- data.frame(x=11:20, y=11:20)
training_data <- list(df, df, df1, df1)
names(training_data) <- c("a", "b", "c", "d")
pos <- lapply(training_data, function(data) {
if(data$x==1)
["return the name of the data.frame object in hand"]
})
My question is how exactly one can determine the name of the object currently being processed within any iteration of lapply and how to return that name so that it goes to "pos" list.
Regards
No need to use lapply here, you can access your list for example :
training_data[['pos']]
This will give you acess to the data.frame named 'pos' or posxx...
EDIT after OP clarification
I use lapply in the names of the list , I and I use the same mode of acces above to the main list
pos <- lapply(names(training_data), function(data) {
if(training_data[[data]]$x==1) ## here your condition is not correct
data
})
[[1]]
[1] "a"
[[2]]
[1] "b"
[[3]]
NULL
[[4]]
NULL
You can remove the NULL elements using something like
unlist(pos)
[1] "a" "b"
but I think you will get better output here if you use sapply, which returns named list.

Can you pass a vector to a vararg?: Vector to sprintf

Let's say I have a function that takes variable arguments, such as sprintf(). I want something like:
sprintf("%s %s", "a", "b")
but I have "a" and "b" in a vector c("a", "b"). A call such as
sprintf("%s %s", c("a", "b"))
will produce an error for not enough arguments. Many languages provide a way to "flatten" a vector into a variable-length argument. But I cannot seem to find the syntax for doing so in R. Is there a way?
You can use do.call:
vec <- c("a","b")
do.call(sprintf, c(list("%s %s"), vec))
# [1] "a b"

Resources