For loop: paste index into string - r

This may strike you as odd, but I want to exactly achieve the following: I want to get the index of a list pasted into a string containing a string reference to a subset of this list.
For illustration:
l1 <- list(a = 1, b = 2)
l2 <- list(a = 3, b = 4)
l <- list(l1,l2)
X_l <- vector("list", length = length(l))
for (i in 1:length(l)) {
X_l[[i]] = "l[[ #insert index number as character# ]]$l_1*a"
}
In the end, I want something like this:
X_l_wanted <- list("l[[1]]$l_1*a","l[2]]$l_1*a")

You can use sprintf/paste0 directly :
sprintf('l[[%d]]$l_1*a', seq_along(l))
#[1] "l[[1]]$l_1*a" "l[[2]]$l_1*a"
If you want final output as list :
as.list(sprintf('l[[%d]$l_1*a', seq_along(l)))
#[[1]]
#[1] "l[[1]]$l_1*a"
#[[2]]
#[1] "l[[2]]$l_1*a"
Using paste0 :
paste0('l[[', seq_along(l), ']]$l_1*a')

Try paste0() inside your loop. That is the way to concatenate chains. Here the solution with slight changes to your code:
#Data
l1 <- list(a = 1, b = 2)
l2 <- list(a = 3, b = 4)
l <- list(l1,l2)
#List
X_l <- vector("list", length = length(l))
#Loop
for (i in 1:length(l)) {
#Code
X_l[[i]] = paste0('l[[',i,']]$l_1*a')
}
Output:
X_l
[[1]]
[1] "l[[1]]$l_1*a"
[[2]]
[1] "l[[2]]$l_1*a"

Or you could do it with lapply()
library(glue)
X_l <- lapply(1:length(l), function(i)glue("l[[{i}]]$l_l*a"))
X_l
# [[1]]
# l[[1]]$l_l*a
# [[2]]
# l[[2]]$l_l*a

Related

How to create new list from nested lists in R

I have a some dataframes q1[[i]] and q2[[i]] contain some (i = 19) lists. For example:
q1
[[1]]
[1] 240.13777778 273.73777778 172.73555556 53.70444444 141.80000000 582.93333333
[[2]]
[1] 2.409867e+02 2.731156e+02 1.680622e+02 5.300222e+01 5.112444e+01 1.048476e+03
...
q2
[[1]]
[1] 70.29000000 69.57666667 48.82000000 22.19000000 31.44666667 143.34000000
[[2]]
[1] 70.2066667 69.5533333 47.9766667 22.0866667 14.0000000 270.3766667
I want to create list, contain such fragments:
qw1
[[1]]
[1] 240.13777778
[1] 70.29000000
[[1]]
[2] 273.73777778
[2] 69.57666667
qw2
[[2]]
[1] 2.409867e+02
[1] 70.2066667
[[2]]
[2] 2.731156e+02
[2] 69.5533333
...
and calculate norm for each block (for example)
qw2
[[2]]
[1] 2.409867e+02 -> norm
[1] 70.2066667
...
[[2]]
[2] 2.731156e+02 -> norm
[2] 69.5533333
and create new normlist for plotting (19 lists, insofar as i = 19).
I try to crete same list, but I get only last normlist:
for (i in 1:19){
q1[[i]] <- dfL_F[[assemble_normal[i]]]/0.000450
q2[[i]] <- dfL_RMF[[assemble_normal[i]]]/0.000300
q3[[i]] <- dfL_D[[assemble_normal[i]]]/0.001800
q4[[i]] <- dfL_RMD[[assemble_normal[i]]]/0.001200
length(q1[[i]])
length(q2[[i]])
length(q3[[i]])
length(q4[[i]])
qw1 <- lapply(q1[[i]], `[[`, 1)
qw2 <- lapply(q2[[i]], `[[`, 1)
qw3 <- lapply(q3[[i]], `[[`, 1)
qw4 <- lapply(q4[[i]], `[[`, 1)
nn <- list()
for (j in 1:length(q1[[i]])){
nn[[j]] <- c(qw1[j],qw2[j],qw3[j],qw4[j])
}
qnorm1 <- list()
for (k in 1:length(nn)){
qnorm1[[k]] <- norm(do.call(rbind, lapply(nn[k], as.numeric)),type = "i")
}
}
And I don't know how to get 19 lists contatin two fields for each lists q1[[i]] and q2[[i]], that form a block, there must be such blocks length (q1[[i]]) for each i (length (q1[[i]]) = length (q2[[i]]))?
Code reproducible:
dput(q1)
list(c(240.137777777778, 273.737777777778, 172.735555555556,
53.7044444444444, 141.8, 582.933333333333),c(240.986666666667, 273.115555555556, 168.062222222222, 53.0022222222222, 51.1244444444444, 1048.47555555556)
dput(q2)
list(c(70.29, 69.5766666666667, 48.82, 22.19, 31.4466666666667,
143.34),c(70.2066666666667, 69.5533333333333, 47.9766666666667, 22.0866666666667, 14, 270.376666666667)
dput(qnorm1)
list(305.738611111111, 365.616666666667, 666.443055555556, 608.981111111111, 393.538611111111, 142.288055555556)
But it's only last list qnorm, there should be 19 such lists and they need to be written in general list.
P.S. As a result, I got the required list, but I can't calculate the norm for each block, I get an empty list at the output... Why?
qw <- Map(
function(q1i, q2i) {
stopifnot(length(q1i) == length(q2i))
Map(c, q1i, q2i) # j elementh i block q1[[i]][j], q2[[i]][j]
},
q1, q2 # every block conatin q1[[i]], q2[[i]]
)
# list qw conatin blocks qw1, qw2
stopifnot(length(qw1) == length(qw2))
qnorm11 <- Map(
function(qw1, qw2, qw3, qw4)
{
stopifnot(length(qw1) == length(qw2))
Map(c, (norm(as.matrix(unlist(qw1),type = "1"))),
(norm(as.matrix(unlist(qw2),type = "1"))),
(norm(as.matrix(unlist(qw3),type = "1"))),
(norm(as.matrix(unlist(qw4),type = "1"))))
}, qw1, qw2, qw3, qw4)
Perhaps you can try this
list2env(
setNames(
Map(function(x, y) apply(rbind(x, y), 2, function(v) norm(t(v)), simplify = FALSE), q1, q2),
c("qw1", "qw2")
),
envir = .GlobalEnv
)

How do I create a 'for' loop to create a vector of columns which contain NAs in R?

I'm attempting to create a vector of column names which contain one or more NA values using a for loop, but am not having success.
hasnas <- c()
for (i in 1:length(data)){
if(sum(is.na(data[,i]))>0){
hasnas <- append(hasnas,names(data[,i]))
}
}
>hasnas
>NULL
Any help would be sincerely appreciated.
Couple of base R options :
#Option 1
hasnas <- names(data)[colSums(is.na(data)) > 0]
#Option 2
hasnas <- names(Filter(function(x) any(is.na(x)), data))
This doesn't use a loop, but R's apply function:
d <- data.frame(a = 1:2, b = c(1, NA), c = c(NA, NA), d = 1:2)
o <- apply(d, 2, function(x) any(is.na(x)))
names(o[sapply(o, isTRUE)])
[1] "b" "c"
You just need to change names(data[,i]) to names(data)[i]. See a full reprex below:
data <- iris
data[["Sepal.Length"]][sample(100, 10)] <- NA
data[["Species"]][sample(100, 10)] <- NA
hasnas <- c()
for (i in 1:length(data)) {
if(any(is.na(data[, i]))) {
hasnas <- append(hasnas, names(data)[i])
}
}
hasnas
#> [1] "Sepal.Length" "Species"
Created on 2021-03-25 by the reprex package (v1.0.0)
There's a concise (and elegant, I think) way you can achieve this by using purrr
data <- tibble(
a = rep(NA, 10),
b = rnorm(10),
c = rep(NA, 10)
)
Loading purrr package: library(purrr)
names(data)[map_lgl(data, ~any(is.na(.x)))]
[1] "a" "c"

reassign values in a list without looping

test <- list(a = list("first"= 1, "second" = 2),
b = list("first" = 3, "second" = 4))
In the list above, I would like to reassign the "first" elements to equal, let's say, five. This for loop works:
for(temp in c("a", "b")) {
test[[temp]]$first <- 5
}
Is there a way to do the same using a vectorized operation (lapply, etc)? The following extracts the values, but I can't get them reassigned:
lapply(test, "[[", "first")
Here is a vectorised one-liner using unlist and relist:
relist((function(x) ifelse(grepl("first",names(x)),5,x))(unlist(test)),test)
$a
$a$first
[1] 5
$a$second
[1] 2
$b
$b$first
[1] 5
$b$second
[1] 4
You can do it like this:
test <- lapply(test, function(x) {x$first <- 5; x})

Pass a comma separated string as a list

Say I have a string such as "x = 1, y = 'cat', z = NULL". I want to obtain the list created by the code list(x = 1, z = 'cat', z = NULL). Here is my first attempt, which I am aware is horrible:
parse_text <- function(x) parse(text = x)[[1]]
strsplit2 <- function(x, ...) strsplit(x, ...)[[1]]
trim_whitespace <- function (x) gsub("^\\s+|\\s+$", "", x)
# take 1
x <- "nk = 1, ncross = 1, pmethod = 'backward'"
x <- strsplit2(x, ",")
xs <- lapply(x, strsplit2, "=")
keys <- lapply(xs, function(x) trim_whitespace(x[1]))
vals <- lapply(xs, function(x) parse_text(x[2]))
setNames(vals, keys)
This is what I imagined a more canonical approach to look like:
# take 2
x <- "nk = 1, ncross = 1, pmethod = 'backward'"
x <- strsplit2(x, ",")
xs <- lapply(x, parse_text)
do.call(list, xs)
But this loses the names of the list. Any help much appreciated! Cheers
You can first create a string containing the expression that you want to execute (i.e. list('your string'), in this case "list( nk = 1, ncross = 1, pmethod = 'backward' )" ) with function paste to add list( and ), then parse the expression with parse function and finally evaluate it with eval function:
x <- "nk = 1, ncross = 1, pmethod = 'backward'" #your string
eval(parse(text=paste('list(',x,')'))) #create and returns the desired list
$nk
[1] 1
$ncross
[1] 1
$pmethod
[1] "backward"
As shown, this will returns you the correct named list.
I hope this will help you.
Here is another way, avoiding the dreaded parse & eval route (but IMHO entirely suitable for this use-case). It relies on the conformity of your tag=value pairings, delimited by ,.
x <- "nk = 1, ncross = 1, pmethod = 'backward'"
# Split into tag=value
vals <- strsplit( x , "," )[[1]]
# Split again and transform to matrix of tags and values
mat <- do.call( rbind , strsplit( vals , "=" ) )
# Return as a list
setNames( as.list( mat[,2] ) , mat[,1] )
#$`nk `
#[1] " 1"
#$` ncross `
#[1] " 1"
#$` pmethod `
#[1] " 'backward'"
Convert the commas to semicolons, source the string into environment e and convert e to a list:
source(textConnection(chartr(",", ";", s)), local = e <- new.env())
as.list(e)
giving:
$x
[1] 1
$y
[1] "cat"
$z
NULL

access plyr id variables within functions

I regulary have the problem that I need to access the actual id variable when using d*ply or l*ply. A simple (yet nonsense) example would be:
df1 <- data.frame( p = c("a", "a", "b", "b"), q = 1:4 )
df2 <- data.frame( m = c("a", "b" ), n = 1:2 )
d_ply( df1, "p", function(x){
actualId <- unique( x$p )
print( mean(x$q)^df2[ df2$m == actualId, "n" ] )
})
So in case of d*ply functions I can help myself with unique( x$p ). But when it comes to l*ply, I have no idea how to access the name of the according list element.
l_ply( list(a = 1, b = 2, c = 3), function(x){
print( <missing code> )
})
# desired output
[1] "a"
[1] "b"
[1] "c"
Any suggestions? Anything I am ignoring?
One way I've gotten around this is to loop over the index (names) and do the subsetting within the function.
l <- list(a = 1, b = 2, c = 3)
l_ply(names(l), function(x){
print(x)
myl <- l[[x]]
print(myl)
})
myl will then be the same as
l_ply(l, function(myl) {
print(myl)
})
Here's one idea.
l_ply( list(a = 1, b = 2, c = 3), function(x){
print(eval(substitute(names(.data)[i], parent.frame())))
})
# [1] "a"
# [1] "b"
# [1] "c"
(Have a look at the final code block of l_ply to see where I got the names .data and i.)
I'm not sure there's a way to do that, because the only argument to your anonymous function is the list element value, without its name :
l_ply( list(a = 1, b = 2, c = 3), function(x){
print(class(x))
})
[1] "numeric"
[1] "numeric"
[1] "numeric"
But if you get back the results of your command as a list or a data frame, the names are preserved for you to use later :
llply( list(a = 1, b = 2, c = 3), function(x){
x
})
$a
[1] 1
$b
[1] 2
$c
[1] 3
Aside from Josh solution, you can also pass both names and values of your list elements to a function with mapply or m*ply :
d <- list(a = 1, b = 2, c = 3)
myfunc <- function(value, name) {
print(as.character(name))
print(value)
}
mapply(myfunc, d, names(d))
m_ply(data.frame(value=unlist(d), name=names(d)), myfunc)

Resources