We can use append function to add element to list. For example like blow.
a_list <- list()
a_list <- append(a_list, "a")
But I want do to like this. The append_new don't return but change the a_list.
a_list <- list()
append_new(a_list, "a")
It can be used by eval function to do this.
a_list <- list()
eval(parse(text="a_list[[1]]<-a"))
a_list
But if I want to write the function add_element_to_list.
a_list <- list()
add_element_to_list(a_list, "a")
a_list ## same as list("a")
How to write the function? This function like assign but more powerful.
The post use eval(parse(text="")) but it can not write in the custom function append_new.
Simpler:
`append<-` <- function(x, value) {
c(x, value)
}
x <- as.list(1:3)
y <- as.list(1:3)
append(x) <- y
append(x) <- "a"
print(x)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 1
[[5]]
[1] 2
[[6]]
[1] 3
[[7]]
[1] "a"
Using evil parse:
append_new <- function(x, y){
eval(parse(text = paste0(x, "[ length(", x, ") + 1 ]<<- '", y, "'")))
}
a_list <- list()
append_new(x = "a_list", y = "a")
a_list
# [[1]]
# [1] "a"
append_new(x = "a_list", y = "b")
a_list
# [[1]]
# [1] "a"
#
# [[2]]
# [1] "b"
Perhaps something like this?
add_element_to_list <- function(this, that)
{
if(typeof(this) != "list") stop("append_new requires a list as first argument")
assign(deparse(substitute(this)),
append(this, that),
envir = parent.frame(),
inherits = TRUE)
}
a_list <- list()
add_element_to_list(a_list, "a")
a_list
#> [[1]]
#> [1] "a"
add_element_to_list(a_list, "b")
a_list
#> [[1]]
#> [1] "a"
#>
#> [[2]]
#> [1] "b"
I would be very cautious in using something like this in a package though, since it is not idiomatic R. In general, R users expect functions not to modify existing objects but to return new objects.
Of course there are some notable exceptions...
Related
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
)
Is it possible to move variables that reside in the global environment into a separate environment to declutter the global namespace? I understand how to create variables in a separate environment (with(env, ...)) but is there an efficient way to move them after creation in the global environment. I suppose it would be possible to copy them into a separate environment and then remove them from the global environment, but wanted to know if there was a more efficient manner.
Maybe:
library(purrr)
a <- 111
b <- 'hello'
my_envir <- new.env()
names(.GlobalEnv) %>%
walk(~ assign(.x, get(.x), envir = my_envir))
eapply(my_envir, function(x) x)
#> $my_envir
#> <environment: 0x7fed59e56dc8>
#>
#> $a
#> [1] 111
#>
#> $b
#> [1] "hello"
Or
library(purrr)
a <- 111
b <- 'hello'
my_envir <- new.env()
eapply(.GlobalEnv, function(x) x) %>%
discard(is.environment) %>%
{walk2(., names(.), ~{
assign(.y, .x, envir = my_envir)
exec('rm', .y, envir = .GlobalEnv)}
)}
eapply(my_envir, function(x) x)
#> $a
#> [1] 111
#>
#> $b
#> [1] "hello"
Created on 2021-12-31 by the reprex package (v2.0.1)
Not sure if this is a good idea but you can attach them to the search path. Starting with a fresh vanilla R session try this.
a <- 1
b <- 2
attach(as.list(.GlobalEnv), name = "myenv")
rm(a, b)
ls("myenv")
ls()
a
b
Using rlang
library(rlang)
a <- 111
b <- "hello"
my_envir <- env(!!! as.list(.GlobalEnv))
-checking
> ls(my_envir)
[1] "a" "b"
> my_envir$a
[1] 111
You may use multiple lines in the with.
e1 <- new.env()
e2 <- new.env()
with(e1, {
k <- l <- m <- 0L
x <- 1
fo <- y ~ x
fun <- function(x) x^2
})
The objects are created in e1,
ls(e1)
# [1] "fo" "fun" "k" "l" "m" "x"
e2 stays empty,
ls(e2)
# character(0)
and in .GlobalEnv only the environments exist so far.
ls(.GlobalEnv)
# [1] "e1" "e2"
To work with objects, also use with or $.
with(e1, fun(2))
# [1] 4
e1$fun(2)
# [1] 4
I would like to extract list elements and their indices in R while removing items with 0 length. Let's say I have the following list in R:
l1 <- character(0)
l2 <- c("a","b")
l3 <- c("c","d","e")
list1 <- list(l1, l1, l2, l1, l3)
Then list1 returns the following:
[[1]]
character(0)
[[2]]
character(0)
[[3]]
[1] "a" "b"
[[4]]
character(0)
[[5]]
[1] "c" "d" "e"
I would like to somehow extract an object that displays the index/position for each non-empty element, as well as the contents of that element. So something that looks like this:
[[3]]
[1] "a" "b"
[[5]]
[1] "c" "d" "e"
The closest I've come to doing this is by removing the empty elements, but then I lose the original index/position of the remaining elements:
list2 <- list1[lapply(list1, length) > 0]
list2
[[1]]
[1] "a" "b"
[[2]]
[1] "c" "d" "e"
keep, will keep elements matching a predicate. negate(is_empty) creates a function that returns TRUE if a vector is not empty.
library("purrr")
names(list1) <- seq_along(list1)
keep(list1, negate(is_empty))
#> $`3`
#> [1] "a" "b"
#>
#> $`5`
#> [1] "c" "d" "e"
Overview
Keeping the indices required me to name each element in the list. This answer uses which() to set the condition that I apply to list1 to keep non-zero length elements.
# load data
l1 <- character(0)
l2 <- c("a","b")
l3 <- c("c","d","e")
list1 <- list( l1, l1, l2, l1, l3)
# name each element in the list
names( list1 ) <- as.character( 1:length( list1 ) )
# create a condition that
# keeps only non zero length elements
# from list1
non.zero.length.elements <-
which( lapply( X = list1, FUN = length ) != 0 )
# apply the condition to list1
# to view the non zero length elements
list1[ non.zero.length.elements ]
# $`3`
# [1] "a" "b"
#
# $`5`
# [1] "c" "d" "e"
# end of script #
I'm not sure exactly what 'extract an object that displays' means, but if you just want to print you can use this modified print.
I just slightly edited print.listof (it's not recursive! zero length subelements will be displayed):
print2 <- function (x, ...)
{
nn <- names(x)
ll <- length(x)
if (length(nn) != ll)
nn <- paste0("[[", seq.int(ll),"]]")
for (i in seq_len(ll)[lengths(x)>0]) {
cat(nn[i], "\n")
print(x[[i]], ...)
cat("\n")
}
invisible(x)
}
print2(list1)
[[3]]
[1] "a" "b"
[[5]]
[1] "c" "d" "e"
A very simple solution is to provide names to the elements of your list and then run your function again. There are several ways to name your elements.
l1 <- character(0)
l2 <- c("a","b")
l3 <- c("c","d","e")
list1 <- list(e1=l1, e2=l1, e3=l2, e4=l1, e5=l3)
list1
names(list1)<-paste0("element",seq(length(list1)))
list1[lapply(list1, length) > 0]
I tried to get the list of names and the expression in ... in a function composition. Let's suppose a function:
g <- function(...) {
print(as.list(match.call(expand.dots = FALSE))$...)
}
And if we call:
g(rnorm(5), par = "a", 4 + 4)
We get:
[[1]]
rnorm(5)
$par
[1] "a"
[[3]]
4 + 4
And it's nice: we can get the expression call for every argument and validate. But I need this but in a function composition:
f <- function(...) g(...)
f(rnorm(5), par = "a", 4 + 4)
But I get:
[[1]]
..1
$par
[1] "a"
[[3]]
..3
I'm reading some chapters http://adv-r.had.co.nz/Expressions.html but I can't find the solution yet. I know, I need kepp studying.
Any tips? Thanks in advance.
If you just want the parameters, you don't need the entire call. Just use substitute() to access the ... rather than match.call
g <- function(...) {
print(substitute(...()))
}
f <- function(...) g(...)
f(rnorm(5), par = "a", 4 + 4)
# [[1]]
# rnorm(5)
#
# $par
# [1] "a"
#
# [[3]]
# 4 + 4
There's also Hadley's recommendation of
g <- function(...) {
print( eval(substitute(alist(...))))
}
Recently I've stumbled upon this bit of code:
y <- NULL
y[cbind(1:2, 1:2)] <- list( list(1,2), list(2,3))
From the second answer here.
But it doesn't seem to differ from y <- list(...), as the comparisons below show:
> identical(y, list( list(1,2), list(2,3)))
[1] TRUE
> identical(y, y[cbind(1:2, 1:2)])
[1] FALSE
What is going on in the bracket assignment here? Why it doesn't throw an error? And why is it different from the non-assigment version in the last line of code?
Matrix indexing only applies when y has dim. Combine this with standard R recycling and the fact that all matrices are actually vectors, and this behavior makes sense.
When you initialize y to NULL, you ensure it has no dim. Therefore, when you index y by a matrix, say ind, you get the same results as having called y[as.vector(ind)]
identical(y[ind], y[as.vector(ind)])
# [1] TRUE
If there are repeat values in ind and you are also assigning, then for each index, only the last value assigned ot it will remain. For example Lets assume we are executing
y <- NULL; y[cbind(1:2, 2:1)] <- list( list(1,2), list(3,4) )
# y has no dimension, so `y[cbind(1:2, 2:1)]`
# is the equivalent of `y[c(1:2, 2:1)]`
When you assign y[c(1, 2, 2, 1)] <- list("A", "B") , in effect what happens is analogous to:
y[[1]] <- "A"
y[[2]] <- "B"
y[[2]] <- "B" # <~~ 'Overwriting' previous value
y[[1]] <- "A" # <~~ 'Overwriting' previous value
Here is a further look at the indexing that occurs: (Notice how the first two letters are being repeated)
ind <- cbind(1:2, 1:2)
L <- as.list(LETTERS)
L[ind]
# [[1]]
# [1] "A"
#
# [[2]]
# [1] "B"
#
# [[3]]
# [1] "A"
#
# [[4]]
# [1] "B"
Here is the same thing, now with assignment. Notice how only the 3rd and 4th values being assigned have been kept.
L[ind] <- c("FirstWord", "SecondWord", "ThirdWord", "FourthWord")
L[ind]
# [[1]]
# [1] "ThirdWord"
#
# [[2]]
# [1] "FourthWord"
#
# [[3]]
# [1] "ThirdWord"
#
# [[4]]
# [1] "FourthWord"
Try a different index for further clarity:
ind <- cbind(c(3, 2), c(1, 3)) ## will be treated as c(3, 2, 1, 3)
L <- as.list(LETTERS)
L[ind] <- c("FirstWord", "SecondWord", "ThirdWord", "FourthWord")
L[1:5]
# [[1]]
# [1] "ThirdWord"
#
# [[2]]
# [1] "SecondWord"
#
# [[3]]
# [1] "FourthWord"
#
# [[4]]
# [1] "D"
#
# [[5]]
# [1] "E"
L[ind]
# [[1]]
# [1] "FourthWord"
#
# [[2]]
# [1] "SecondWord"
#
# [[3]]
# [1] "ThirdWord"
#
# [[4]]
# [1] "FourthWord"
Edit regarding #agstudy's questions:
Looking at the src for [ we have the following comments:
The special [ subscripting where dim(x) == ncol(subscript matrix)
is handled inside VectorSubset. The subscript matrix is turned
into a subscript vector of the appropriate size and then
VectorSubset continues.
Looking at the function static SEXP VectorSubset(SEXP x, SEXP s, SEXP call) the relevant check is the following:
/* lines omitted */
attrib = getAttrib(x, R_DimSymbol);
/* lines omitted */
if (isMatrix(s) && isArray(x) && ncols(s) == length(attrib)) {
/* lines omitted */
...