I want to vectorize deparse(substitute(x)).
f <- function(...){
deparse(substitute(...))
}
f(a, b, c)
This gives only the first element, "a", but I await "a", "b", "c". By accident, I found this
f2 <- function(...){
deparse(substitute(...()))
}
f2(ab, b, c)
This gives "pairlist(ab, b, c)". Now I could delete all the stuff I do not need to obtain "a", "b", "c". But this seems not elegant to me. Is there a way to vectorize deparse(substitute(x))?
I know there is a question with a similar issue but the answer does not include deparse(substitute(x)).
match.call is a good starting point. I'd encourage you to explore all what you can do with it. I believe this gets you where you want though:
f <- function(...){
as.character(match.call(expand.dots = FALSE)[[2]])
}
and an example of using it...
f(hey, you)
[1] "hey" "you"
We can use match.call
f <- function(...) sapply(as.list(match.call())[-1], as.character)
f(a)
#[1] "a"
f(a, b)
#[1] "a" "b"
f(a, b, c)
#[1] "a" "b" "c"
Or using substitute
f <- function(...) sapply(substitute(...()), deparse)
f(a)
#[1] "a"
f(a, b)
#[1] "a" "b"
f(a, b, c)
#[1] "a" "b" "c"
Related
I want to append data to my list only if it is distinct from previously stored data.
data <- c("A","B","C")
My code so far:
x<- function(...){
data <- ifelse(... %in% data, append(data, ""),append(data, as.character(...)))
return(data)
}
For instance, if I want to append "D," my desired output is:
data
[1] "A" "B" "C" "D"
However, I received this:
data
[1] "A"
x <- c("A","B","C")
y <- c("D", "A")
union(x, y)
# [1] "A" "B" "C" "D"
ifelse function cannot give vector as a result. See ifelse function documentation.
Instead, you should use if - else statement
x<- function(...){
data <- if (... %in% data) {append(data, "")} else {append(data, as.character(...))}
return(data)
}
x("D")
[1] "A" "B" "C" "D"
Here is my solution:
x <- c("A","B","C")
y <- c("D", "A")
unique(c(x, y))
[1] "A" "B" "C" "D"
I have 2 vectors "x" and "y". I would like to take all letters that are in "x" only if they are also in "y".
x<- c(a, b, c, d, e)
y<- c(a, z, m, d, e, g)
result
r <- c(a, d, e)
We can use intersect
intersect(x, y)
#[1] "a" "d" "e"
A useful short-hand for this problem is to subset x according to x %in% y. This will return every value where x is one of the values in y. unique is used to remove duplicate elements.
x <- c("a", "b", "c", "a")
y <- c("a", "d", "f")
print(x %in% y)
#> [1] TRUE FALSE FALSE TRUE
unique(x[x %in% y])
#> [1] "a"
Created on 2021-03-09 by the reprex package (v0.3.0)
I'm still a little green to R. So bear with me.
I have a list of vectors and I would like to compare each vector in the list and then tack on the matching list to the end of the match one. I am looking for robust repeatable solution, regardless of number of vectors in the list.
So if I have a list (lst) made of vectors:
lst <- list(c("a", "b"), c("b", "c"), c("e", "f"), c("c", "g"))
I want to get a list of vectors like this as a result:
[[1]]
[1] "a" "b" "c" "g"
[[2]]
[1] "e" "f"
So I've been able to make this work for a singular instance:
if(any(lst[[1]] %in% lst[[2]])){
c(lst[[1]], lst[[2]])
}
but now I'm trying to loop it over the entire list and this is what I have so far, but I'm a little stuck:
endmembers <- lapply(seq_along(lst), function(i,j){
x <- lst[[i]]
x2 <- lst[[j]]
if(any(x %in% x2)){
c(x, x2)
}
})
I would use a recursive function to stick all the components together, then remove list items that are contained within other items:
#### helper functions ----
# Recursive function to stick list items together
fun <- function(x, d) {
i <- which(sapply(d, function(y) y[1]) == tail(x, 1))
if (length(i) > 0) {
y <- d[[i[1]]]
x <- c(x, y[2:length(y)])
x <- fun(x, d)
}
x
}
# is vector inside another vector? - must be in the same sequence and order
inside <- function(x, y) {
if ( isTRUE(all.equal(x, y)) )
return(FALSE)
if ( length(x) > length(y) )
return(FALSE)
if ( !any(x %in% y))
return(FALSE)
!is.unsorted( sapply(x, function(a, b) which(a == b), b = y), strictly = TRUE )
}
#### analysis ----
# Stick vectors together if last == first
d <- lapply(lst, fun, d = lst)
# remove list items that are inside other list items - there might be a more
# elegant solution to this, I'm confused by it.
d[!apply(
sapply(d,
function(x, y) sapply(y, function(x, y) inside(x, y), y = x),
y = d),
1,
any)]
An easy option is using igraph
library(igraph)
u <- cluster_infomap(graph_from_data_frame(as.data.frame(do.call(rbind,lst))))
out <- split(u$names,u$membership)
which gives
> out
$`1`
[1] "a" "b" "c" "g"
$`2`
[1] "e" "f"
If you want base R solution with for loops, here is one version
out <- lst[1]
for (v in lst) {
flag <- 1
for (k in 1:length(out)) {
if (any(v %in% out[[k]])) {
out[[k]] <- union(out[[k]], v)
flag <- 0
break
}
}
if (flag) out[[length(out) + 1]] <- v
}
such that
> out
[[1]]
[1] "a" "b" "c" "g"
[[2]]
[1] "e" "f"
In case anyone wants to know what I did, I followed the code in merging sets which have even one element in common R that was commented.
m <- sapply(lst, function(x) sapply(lst, function(y) (any(x %in% y))))
#determine the groups of the graph constructed from m
groups <- groups(components(graph_from_adjacency_matrix(m)))
#Get the unique elements of each group
endmembers <- lapply(groups,function(x) sort(unique(unlist(lst[x]))))
Is there a keyboard shortcut for converting something like rm("a", "b", "c", "d") into rm(a, b, c, d)?
Not an RStudio shortcut but you can do, ctrl+F, check the Regex box and replace \"(.*?)\" by \1
Coming back at it later, here are two functions that operate in both directions,
You could build an addin from those and trigger the execution with hotkeys
quote_vars <- function(expr) {
expr <- substitute(expr)
vars <- all.vars(expr)
vars <- setNames(as.list(vars), vars)
do.call(substitute, list(expr, vars))
}
unquote_strings <- function(expr) {
expr <- deparse(substitute(expr))
expr <- gsub("\"(.*?)\"", "\\1", expr)
parse(text= expr)[[1]]
}
quote_vars(rm(a, b, c, d))
#> rm("a", "b", "c", "d")
unquote_strings(rm("a", "b", "c", "d"))
#> rm(a, b, c, d)
Created on 2019-07-05 by the reprex package (v0.3.0)
For viceversa conversions, an option is
f1 <- function(...) {
v1 <- rlang::enexprs(...)
if(is.character(v1[[1]])) {
rlang::syms(v1)
} else purrr::map(v1, ~ rlang::as_name(.x))
}
-testing
f1("a", "b", "c", "d") # changes to symbol
#[[1]]
#a
#[[2]]
#b
#[[3]]
#c
#[[4]]
#d
f1(a, b, c, d) # changes to character
#[[1]]
#[1] "a"
#[[2]]
#[1] "b"
#[[3]]
#[1] "c"
#[[4]]
#[1] "d"
NOTE: Returning a list to have consistent behavior
With rm, we can use do.call
out <- f1("a", "b", "c", "d")
do.call("rm", out)
a
#Error: object 'a' not found
b
#Error: object 'b' not found
data
a <- b <- c <-d <- 1:5
I have the vector
x <- c("A", "B", "C", "D", "E", "F")
that I split in the following manner:
split(x, 1:2)
It comes out as (a, c, e) and (b, d, f), yet I want (a, b, c) and (d, e, f). Any way of changing it to a horizontal split rather than a vertical one?
You can do:
split(x, rep(1:2, each = length(x)/2))
which gives:
$`1`
[1] "A" "B" "C"
$`2`
[1] "D" "E" "F"
We can also use gl
split(x, as.numeric(gl(length(x), 3, length(x))))