How to use character as variable name in arguments? [R] - r

I am wondering how to tell R the string I used in the arguments of a function stands for a variable instead of the string itself. Specifically, I am dealing with 'Dict' package and run the following code as a trial.
library(Dict)
x = c(1,2)
d = dict(x = 1)
d$keys
# 'x'
What I want is c(1,2) to be the key instead of 'x'. The same problem goes for 'list', for example
y = 'happy'
l = list(y = 1)
names(l)
The result would be 'y'. Moreover, one can solve this situation by
names(l) <- y, but I don't think this is very efficient and clean.
Anyone has any idea how to do this in a one-line code? Thanks!

We could use setNames
out <- do.call(dict, setNames(rep(list(1), length(x)), x))
out$keys
[1] "1" "2"
Or we may use invoke or exec
library(purrr)
out <- invoke(dict, setNames(rep(1, length(x)), x))
out <- exec(dict, !!!setNames(rep(1, length(x)), x))
For the second case, also setNames works
setNames(list(1), y)
$happy
[1] 1
or we can use dplyr::lst
dplyr::lst(!! y := 1)
$happy
[1] 1

Related

how to extract indices of a vector for missing values in R [duplicate]

In matlab there is a way to find the values in one vector but not in the other.
for example:
x <- c(1,2,3,4)
y <- c(2,3,4)
is there any function that would tell me that the value in x that's not in y is 1?
you can use the setdiff() (set difference) function:
> setdiff(x, y)
[1] 1
Yes. For vectors you can simply use the %in% operator or is.element() function.
> x[!(x %in% y)]
1
For a matrix, there are many difference approaches. merge() is probably the most straight forward. I suggest looking at this question for that scenario.
The help file in R for setdiff, union, intersect, setequal, and is.element provides information on the standard set functions in R.
setdiff(x, y) returns the elements of x that are not in y.
As noted above, it is an asymmetric difference.
So for example:
> x <- c(1,2,3,4)
> y <- c(2,3,4,5)
>
> setdiff(x, y)
[1] 1
> setdiff(y, x)
[1] 5
> union(setdiff(x, y), setdiff(y, x))
[1] 1 5
x[is.na(match(x,y))]
setdiff() is a tricky function because the output is dependent on the order of the input. You can instead write a simple function as such that does the exact opposite of intersect. This is far better.
>difference <- function(x, y) {
c(setdiff(x, y), setdiff(y, x))
}
#Now lets test it.
>x <- c(1,2,3,4)
>y <- c(2,3,4,5)
>difference(x,y)
[1] 1 5
If:
x <- c(1,2,3,4)
y <- c(2,3,4)
Any of these expressions:
setdiff(x, y)
x[!(x %in% y)]
x[is.na(match(x,y))]
x[!(is.element(x,y))]
will give you the right answer [1] 1, if the goal is to find the values/characters in x, that is not present in y.
However, applying the above expressions can be tricky and can give undesirable results depending on the nature of the vector, and the position of x and y in the expression. For instance, if:
x <- c(1,1,2,2,3,4)
y <- c(2,3,4)
and the goal is just to find the unique values/characters in x, that is not present in y or vice-versa. Applying any of these expressions will still give the right answer [1] 1:
union(setdiff(x, y), setdiff(y, x))
Thanks to contribution of Jeromy Anglim
OR:
difference <- function(x, y) {
c(setdiff(x, y), setdiff(y, x))
}
difference(y,x)
Thanks to contribution of Workhouse

how to replace all but one character in a set of identifcal strings (different character stays not replaced in each string)?

I have a character vector of identical elements:
vec <- c("AXAXAXA", "AXAXAXA", "AXAXAXA")
I would like to replace "X" with "Y" but leave one "X" behind in each string, different one in each string, so I get
vec_res <- c("AXAYAYA", "AYAXAYA", "AYAYAXA")
The strings are always the same and the number of elements (strings) in the vector is the same as the number of "X" in each string
I am new to sringr but I suspect there could be a smart way to do this.
Another one
x <- 'AXAXAXA'
y <- grepRaw('X', x, all = TRUE)
x <- chartr('X', 'Y', rep(x, length(y)))
substr(x, y, y + 1) <- 'X'
x
# [1] "AXAYAYA" "AYAXAYA" "AYAYAXA"
One option utilizing purrr could be:
map2_chr(.x = grepRaw("X", vec[1], all = TRUE),
.y = vec,
~ `substr<-`(gsub("X", "Y", .y), .x, .x, "X"))
[1] "AXAYAYA" "AYAXAYA" "AYAYAXA"
The same thing with base R:
mapply(function(x, y) `substr<-`(gsub("X", "Y", y), x, x, "X"),
x = grepRaw("X", vec[1], all = TRUE),
y = vec)
I'm not sure this is a very common operator or that stringr will help much here. Here's a base R function. Since the repetition of the input vector is redundant, this function expects a single value and will generate an output value for each of the X's
swap_expand <- function(x) {
stopifnot(length(x)==1)
m <- gregexpr("X", x)
regmatches(x, m) <- "Y" #set all X's to Y
m <- m[[1]]
r <- rep(x, length(m)) #repeat for each X
substr(r, m, m)<-"X" #replace a different Y each time
r
}
swap_expand("AXAXAXA")
# [1] "AXAYAYA" "AYAXAYA" "AYAYAXA"

Select an existing variable by using readline

I'd like to allow the user of my script to pick an existing object (a vector).
I thought something like this
...
message("Select a vector of y values")
nwd <- readLines(n = 1)
return(mean(nwd))
...
but the result is NA because nwd is seen as a character.
How can I solve?
Thanks.
A bit safer than eval(parse(...)):
x <- 1:10
message("Select a vector of y values")
nwd <- readLines(n = 1)
#input x
mean(get(nwd))
#[1] 5.5

R - Repetitions of an array in other array

From a dataframe I get a new array, sliced from a dataframe.
I want to get the amount of times a certain repetition appears on it.
For example
main <- c(A,B,C,A,B,V,A,B,C,D,E)
p <- c(A,B,C)
q <- c(A,B)
someFunction(main,p)
2
someFunction(main,q)
3
I've been messing around with rle but it counts every subrepetion also, undersirable.
Is there a quick solution I'm missing?
You can use one of the regular expression tools in R since this is really a pattern matching exercise, specifically gregexpr for this question. The p and q vectors represent the search pattern and main is where we want to search for those patterns. From the help page for gregexpr:
gregexpr returns a list of the same length as text each element of which is of
the same form as the return value for regexpr, except that the starting positions
of every (disjoint) match are given.
So we can take the length of the first list returned by gregexpr which gives the starting positions of the matches. We'll first collapse the vectors and then do the searching:
someFunction <- function(haystack, needle) {
haystack <- paste(haystack, collapse = "")
needle <- paste(needle, collapse = "")
out <- gregexpr(needle, haystack)
out.length <- length(out[[1]])
return(out.length)
}
> someFunction(main, p)
[1] 2
> someFunction(main, q)
[1] 3
Note - you also need to throw "" around your vector main, p, and q vectors unless you have variables A, B, C, et al defined.
main <- c("A","B","C","A","B","V","A","B","C","D","E")
p <- c("A","B","C")
q <- c("A","B")
I'm not sure if this is the best way, but you can simply do that work by:
f <- function(a,b)
if (length(a) > length(b)) 0
else all(head(b, length(a)) == a) + Recall(a, tail(b, -1))
Someone may or may not find a built-in function.
Using sapply:
find_x_in_y <- function(x, y){
sum(sapply(
seq_len(length(y)-length(x)),
function(i)as.numeric(all(y[i:(i+length(x)-1)]==x))
))
}
find_x_in_y(c("A", "B", "C"), main)
[1] 2
find_x_in_y(c("A", "B"), main)
[1] 3
Here's a way to do it using embed(v,n), which returns a matrix of all n-length sub-sequences of vector v:
find_x_in_y <- function(x, y)
sum( apply( embed( y, length(x)), 1,
identical, rev(x)))
> find_x_in_y(p, main)
[1] 2
> find_x_in_y(q, main)
[1] 3

Creation of an object inside names<-() gives an error. How to explain?

This
x <- list(12, 13)
names(y <- x) <- c("a", "b")
gives the error:
Error in names(y <- x) <- c("a", "b") : object 'y' not found
Can anyone explain why?
According to R's rules of evaluation y <- x should be evaluated inside the parent frame of names<-. So y should be created in global environment.
Thanks.
[update] If object y is already present in the global environment, then the error is:
Error in names(y <- x) <- c("a", "b") : could not find function "<-<-"
[update2] Here it is, another construct, which I encountered today.
(X <- matrix(0, nrow = 10, ncol = 10))[1:3] <- 3:5
Error during wrapup: object 'X' not found
This is related to the way that <- recursively transforms the LHS, appending "<-" to the names of functions to get the replacement form. The first argument is treated specially. Note the difference between the last two:
x <- a <- 1
`f<-` <- function(x, a, value) x
f(x, a <- 2) <- 2
f(x <- 2, a) <- 2
# Error in f(x <- 2, a) <- 2 : could not find function "<-<-"
For what you're trying to do, I'd use setNames anyway.
This is probably due to lazy evaluation. There is little guarentee what order things will be done in when doing multiple tasks in one line. Apparently in this case it tries to find y before evaluating the assignment. If you just ask for the names, then y is assigned.
It is best to do these types of things in 2 steps so you can be assured that the first is done before the second needs the results.

Resources