Use paste0 to create multiple object names with a for loop - r

I would like to create multiple object names with a for loop. I have tried the following which fails horribly:
somevar_1 = c(1,2,3)
somevar_2 = c(4,5,6)
somevar_3 = c(7,8,9)
for (n in length(1:3)) {
x <- as.name(paste0("somevar_",[i]))
x[2]
}
The desired result is x being somevar_1, somevar_2, somevar_3 for the respective iterations, and x[2] being 2, 5 and 8 respectively.
How should I do this?

somevar_1 = c(1,2,3)
somevar_2 = c(4,5,6)
somevar_3 = c(7,8,9)
for (n in 1:3) {
x <- get(paste0("somevar_", n))
print(x[2])
}
Result
[1] 2
[1] 5
[1] 8

We can use mget to get all the required objects in a list and use sapply to subset 2nd element from each of them.
sapply(mget(paste0("somevar_", 1:3)), `[`, 2)
#somevar_1 somevar_2 somevar_3
# 2 5 8

Related

concatenation sublists of two different lists [duplicate]

I have two lists
first = list(a = 1, b = 2, c = 3)
second = list(a = 2, b = 3, c = 4)
I want to merge these two lists so the final product is
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Is there a simple function to do this?
If lists always have the same structure, as in the example, then a simpler solution is
mapply(c, first, second, SIMPLIFY=FALSE)
This is a very simple adaptation of the modifyList function by Sarkar. Because it is recursive, it will handle more complex situations than mapply would, and it will handle mismatched name situations by ignoring the items in 'second' that are not in 'first'.
appendList <- function (x, val)
{
stopifnot(is.list(x), is.list(val))
xnames <- names(x)
for (v in names(val)) {
x[[v]] <- if (v %in% xnames && is.list(x[[v]]) && is.list(val[[v]]))
appendList(x[[v]], val[[v]])
else c(x[[v]], val[[v]])
}
x
}
> appendList(first,second)
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Here are two options, the first:
both <- list(first, second)
n <- unique(unlist(lapply(both, names)))
names(n) <- n
lapply(n, function(ni) unlist(lapply(both, `[[`, ni)))
and the second, which works only if they have the same structure:
apply(cbind(first, second),1,function(x) unname(unlist(x)))
Both give the desired result.
Here's some code that I ended up writing, based upon #Andrei's answer but without the elegancy/simplicity. The advantage is that it allows a more complex recursive merge and also differs between elements that should be connected with rbind and those that are just connected with c:
# Decided to move this outside the mapply, not sure this is
# that important for speed but I imagine redefining the function
# might be somewhat time-consuming
mergeLists_internal <- function(o_element, n_element){
if (is.list(n_element)){
# Fill in non-existant element with NA elements
if (length(n_element) != length(o_element)){
n_unique <- names(n_element)[! names(n_element) %in% names(o_element)]
if (length(n_unique) > 0){
for (n in n_unique){
if (is.matrix(n_element[[n]])){
o_element[[n]] <- matrix(NA,
nrow=nrow(n_element[[n]]),
ncol=ncol(n_element[[n]]))
}else{
o_element[[n]] <- rep(NA,
times=length(n_element[[n]]))
}
}
}
o_unique <- names(o_element)[! names(o_element) %in% names(n_element)]
if (length(o_unique) > 0){
for (n in o_unique){
if (is.matrix(n_element[[n]])){
n_element[[n]] <- matrix(NA,
nrow=nrow(o_element[[n]]),
ncol=ncol(o_element[[n]]))
}else{
n_element[[n]] <- rep(NA,
times=length(o_element[[n]]))
}
}
}
}
# Now merge the two lists
return(mergeLists(o_element,
n_element))
}
if(length(n_element)>1){
new_cols <- ifelse(is.matrix(n_element), ncol(n_element), length(n_element))
old_cols <- ifelse(is.matrix(o_element), ncol(o_element), length(o_element))
if (new_cols != old_cols)
stop("Your length doesn't match on the elements,",
" new element (", new_cols , ") !=",
" old element (", old_cols , ")")
}
return(rbind(o_element,
n_element,
deparse.level=0))
return(c(o_element,
n_element))
}
mergeLists <- function(old, new){
if (is.null(old))
return (new)
m <- mapply(mergeLists_internal, old, new, SIMPLIFY=FALSE)
return(m)
}
Here's my example:
v1 <- list("a"=c(1,2), b="test 1", sublist=list(one=20:21, two=21:22))
v2 <- list("a"=c(3,4), b="test 2", sublist=list(one=10:11, two=11:12, three=1:2))
mergeLists(v1, v2)
This results in:
$a
[,1] [,2]
[1,] 1 2
[2,] 3 4
$b
[1] "test 1" "test 2"
$sublist
$sublist$one
[,1] [,2]
[1,] 20 21
[2,] 10 11
$sublist$two
[,1] [,2]
[1,] 21 22
[2,] 11 12
$sublist$three
[,1] [,2]
[1,] NA NA
[2,] 1 2
Yeah, I know - perhaps not the most logical merge but I have a complex parallel loop that I had to generate a more customized .combine function for, and therefore I wrote this monster :-)
merged = map(names(first), ~c(first[[.x]], second[[.x]])
merged = set_names(merged, names(first))
Using purrr. Also solves the problem of your lists not being in order.
In general one could,
merge_list <- function(...) by(v<-unlist(c(...)),names(v),base::c)
Note that the by() solution returns an attributed list, so it will print differently, but will still be a list. But you can get rid of the attributes with attr(x,"_attribute.name_")<-NULL. You can probably also use aggregate().
We can do a lapply with c(), and use setNames to assign the original name to the output.
setNames(lapply(1:length(first), function(x) c(first[[x]], second[[x]])), names(first))
$a
[1] 1 2
$b
[1] 2 3
$c
[1] 3 4
Following #Aaron left Stack Overflow and #Theo answer, the merged list's elements are in form of vector c.
But if you want to bind rows and columns use rbind and cbind.
merged = map(names(first), ~rbind(first[[.x]], second[[.x]])
merged = set_names(merged, names(first))
Using dplyr, I found that this line works for named lists using the same names:
as.list(bind_rows(first, second))

Dynamically appending n objects in a list

I am working in R.
I have n objects all named x followed by a number j = 1,..., n.
eg, for n = 5:
x1, x2, x3, x4, x5
I want to be able to list them all together dynamically depending on the n value:
list(x1,x2,x3,x4,x5)
In other words, I need to write a function that returns a list of those similarly-named objects automatically recognizing at what value of n to stop.
I tried this:
l <- vector()
for (k in (1:n)){
if (k != n){
u <- paste0("x",k, ",")
} else {
u <- paste0("x",k)
}
l <- append(l,u)
}
But obviously returns a list of characters...
Does anyone have an idea of how to do that?
Many thanks for your help.
mget gets a list of objects from their names. Construct the names using paste (vectorized), give it to mget (also vectorized) and you have your list:
l <- mget(paste0("x", 1:n))
I'd suggest trying to use lists from the start, rather than creating a bunch of objects then gathering them into a list. My answer at How to make a list of data frames has examples and discussion about this.
If you want to write a function:
> x1 <- 1:2
> x2 <- 1:3
> x3 <- 2:5
>
> make_list <- function(n){
+ l <- list()
+ for(i in 1:n){
+ l[[i]] <- get(paste0('x',i))
+ }
+ l
+ }
> make_list(3)
[[1]]
[1] 1 2
[[2]]
[1] 1 2 3
[[3]]
[1] 2 3 4 5
> make_list(1)
[[1]]
[1] 1 2
>

Sum of all vectors of variables with common prefix

Is it possible to sum all vector variables with a common prefix ?
Exemple:
x1 <- c(1,2,3)
x2 <- c(4,5,6)
.
.
.
xn <- c(n,n,n)
y = x1 + x2 + ... xn
The number of variables xn (ie with prefix x) is only known at runtime.
Assuming your y has the same dimension as x, you could try capturing all the variables into the list and apply a summation operation.
> x2 <- c(4,5,6)
> x1 <- c(1,2,3)
> ls(pattern = "^x\\d+$") # this is regex for finding "x" and "digits",
# ^ is start of string, $ is end of string
[1] "x1" "x2"
> sapply(ls(pattern = "^x\\d+$"), get, simplify = FALSE)
$x1
[1] 1 2 3
$x2
[1] 4 5 6
> out <- sapply(ls(pattern = "^x\\d+$"), get, simplify = FALSE)
> Reduce("+", out)
[1] 5 7 9
You can also use mget as suggested by #LyzandeR's, especially if fancy one-liners.
Reduce("+", mget(ls(pattern = "^x\\d+$")))
You can check an example:
xx <- 1
xx2 <- 2
xx3 <- 3
#get the names of the variables containing xx
vars <- ls(pattern = 'xx')
#mget will get the variables from the names, unlist will add them in an atomic vector
sum(unlist(mget(vars)))
#[1] 6
A very naive solution could be:
# first 2 vectors are of interest
x1 <- c(1,2,3)
x2 <- c(4,5,6)
# answer doesn't need to have z sum in it
z <- c(7,8,9)
# create a dummy answer vector, initialize it will all 0; length will be the length of single vector that we are adding
answer<-rep(0,length(x1))
# loop through each variable in current environment
for (var in ls()){
# see if variable name begins with x
if (startsWith(var,'x')){
# add it to our answer
answer = answer + get(var)
}
}
# print the answer
print(answer)

Chaining list of vectors

If I have a list of vectors such as below
list.x <- list(1:2, 1:3, 3:4, 5, 5:6)
Is there a way to replace each list element with an element that includes all the other values that the element can be paired with?
For example the first element (list.x[[1]]) would be replace with 1:4 because element 2 (list.x[[2]]) shows that 2, is also paired with 3, and element 3 shows that 3 is also paired with 4.
The final result I would like to achieve would be this list
final.list <- list(1:4, 1:4, 1:4, 5:6, 5:6)
I needed a change of pace today, so I decided to try to answer the question using base R. Here it goes:
First, I created a function that unions two vectors if they intersect, and if not, simply returns the first vector:
expand.if.toucing <- function(vector1, vector2) {
i = intersect(vector1, vector2);
if (NROW(i) > 0)
union(vector1, vector2)
else
vector1;
}
Then I made a function that merges one element in the list of vectors with another:
list.reduce <- function (lst) {
for(v1 in 1:NROW(lst))
for (v2 in 1:NROW(lst)) {
if (v1 == v2)
next;
prevLength <- NROW(lst[[v1]]);
lst[[v1]] <- expand.if.toucing(lst[[v1]], lst[[v2]]);
newLength <- NROW(lst[[v1]]);
if (newLength == prevLength)
next;
return(lst[-v2]);
}
lst;
}
After this, I made a function that merges all vectors in the list that can be merged. This is sort of a proto cluster analysis, so I called it clusterize:
clusterize <- function (lst) {
reduced = TRUE;
while(reduced) {
prevLength <- NROW(lst);
lst <- list.reduce(lst);
newLength <- NROW(lst);
reduced <- prevLength != newLength;
}
lst;
}
Now it's just a matter of replacing each element in the original list with its associated cluster:
replace.with.clusters <- function(lst, clusters) {
for(l in 1:NROW(lst))
for(c in 1:NROW(clusters)) {
lst[[l]] <- expand.if.toucing(lst[[l]], clusters[[c]]);
next;
}
lst;
}
You're good to go. The two main functions are clusterize and replace.with.cluster. Use them like this:
list.x <- list(1:2, 1:3, 3:4, 5, 5:6)
clusters <- clusterize(list.x);
replace.with.clusters(list.x, clusters);
# Outputs the following:
#
# [[1]]
# [1] 1 2 3 4
#
# [[2]]
# [1] 1 2 3 4
#
# [[3]]
# [1] 3 4 1 2
#
# [[4]]
# [1] 5 6
#
# [[5]]
# [1] 5 6
The third element is in a different order than your list, but from the way you describe the problem, order is not truly relevant.

treat string as object name in a loop in R

I want to create a string in a loop and use this string as object in this loop. Here is a simplified example:
for (i in 1:2) {
x <- paste("varname",i, sep="")
x <- value
}
the loop should create varname1, varname2. Then I want to use varname1, varname2 as objects to assign values. I tried paste(), print() etc.
Thanks for help!
You could create the call() to <- and then evaluate it. Here's an example,
value <- 1:5
for (i in 1:2) {
x <- paste("varname",i, sep="")
eval(call("<-", as.name(x), value))
}
which creates the two objects varname1 and varname2
varname1
# [1] 1 2 3 4 5
varname2
# [1] 1 2 3 4 5
But you should really try to avoid assigning to the global environment from with in a method/function. We could use a list along with substitute() and then we have the new variables together in the same place.
f <- function(aa, bb) {
eval(substitute(a <- b, list(a = as.name(aa), b = bb)))
}
Map(f, paste0("varname", 1:2), list(1:3, 3:6))
# $varname1
# [1] 1 2 3
#
# $varname2
# [1] 3 4 5 6
assign("variableName", 5)
would do that.
For example if you have variable names in array of strings you can set them in loop as:
assign(varname[1], 2 + 2)
More and more information
https://stat.ethz.ch/R-manual/R-patched/library/base/html/assign.html
#MahmutAliÖZKURAN has answered your question about how to do this using a loop. A more "R-ish" way to accomplish this might be:
mapply(assign, <vector of variable names>, <vector of values>,
MoreArgs = list(envir = .GlobalEnv))
Or, as in the case you specified above:
mapply(assign, paste0("varname", 1:2), <vector of values>,
MoreArgs = list(envir = .GlobalEnv))
I had the same issue and for some reason my apply's weren't working (lapply, assign directly, or my preferred goto, mclapply)
But this worked
vectorXTS <- mclapply(symbolstring,function(x)
{
df <- symbol_data_set[symbol_data_set$Symbol==x,]
return(xts(as.data.frame(df[,-1:-2]),order.by=as.POSIXct(df$Date)))
})
names(symbolstring) <- symbolstring
names(vectorXTS) <- symbolstring
for(i in symbolstring) assign(symbolstring[i],vectorXTS[i])

Resources