q <- lapply(1:3, function(x) x ** 2)
## returns nothing, because it is an assignment
# however, how you explain this?:
> lapply(list(1:3, 4:6, 7:9, 10:11), function(v) q <- lapply(v, function(x) x ** 2))
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 4
[[1]][[3]]
[1] 9
[[2]]
[[2]][[1]]
[1] 16
[[2]][[2]]
[1] 25
[[2]][[3]]
[1] 36
[[3]]
[[3]][[1]]
[1] 49
[[3]][[2]]
[1] 64
[[3]][[3]]
[1] 81
[[4]]
[[4]][[1]]
[1] 100
[[4]][[2]]
[1] 121
# while this gives the same but is logical (q is stated as return value).
> lapply(list(1:3, 4:6, 7:9, 10:11), function(v) {q <- lapply(v, function(x) x ** 2);q})
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 4
[[1]][[3]]
[1] 9
[[2]]
[[2]][[1]]
[1] 16
[[2]][[2]]
[1] 25
[[2]][[3]]
[1] 36
[[3]]
[[3]][[1]]
[1] 49
[[3]][[2]]
[1] 64
[[3]][[3]]
[1] 81
[[4]]
[[4]][[1]]
[1] 100
[[4]][[2]]
[1] 121
why in the second expression, although the inner lapply is just assigned to q but q not called at end of function, the value of the assignment
is returned to the outer lapply and thus collected?
Please, anybody has an explanation for this phenomenon?
It also works with =
lapply(list(1:3, 4:6, 7:9, 10:11), function(v) q = lapply(c(v), function(x) x ** 2))
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 4
[[1]][[3]]
[1] 9
[[2]]
[[2]][[1]]
[1] 16
[[2]][[2]]
[1] 25
[[2]][[3]]
[1] 36
[[3]]
[[3]][[1]]
[1] 49
[[3]][[2]]
[1] 64
[[3]][[3]]
[1] 81
[[4]]
[[4]][[1]]
[1] 100
[[4]][[2]]
[1] 121
The answer lies in the return value of an assignment operation. The assignment operator <- not only writes a value to a variable in the calling environment, it actually invisibly returns the assigned value itself to the caller.
Remember all operations in R are actually functions. When you do
x <- 3
You are actually doing
`<-`(x, 3)
Which not only creates the symbol "x" in the calling environment and assigns the value 3 to that symbol, but invisibly returns the value 3 to the caller. To see this, consider:
y <- 2
y
#> [1] 2
y <- `<-`(x, 3)
y
#> [1] 3
Or equivalently,
y <- (x <- 4)
y
#> [1] 4
And in fact, because of R's order of evaluation, we can even do:
y <- x <- 5
y
#> [1] 5
Which is a neat way of setting multiple variables to the same value on the same line.
Now consider the lambda function you use inside your lapply:
function(v) q <- lapply(v, function(x) x ** 2)
Look what happens when we consider this function as a stand-alone:
func <- function(v) q <- lapply(v, function(x) x ** 2)
func(1:3)
As predicted, nothing happens. But what happens when we do:
a <- func(1:3)
If func(1:3) doesn't return anything, then presumably a should be empty now.
But it isn't...
a
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 4
#>
#> [[3]]
#> [1] 9
Because the value of assignation was returned to the caller invisibly, we were able to assign it to a value in the calling scope. Therefore, doing
lapply(list(1:3, 4:6, 7:9, 10:11), function(v) q <- lapply(v, function(x) x ** 2))
assigns the value of your inner function applied to all the list elements to a new list. This list is not returned invisibly, but just returned as normal.
So this is expected behaviour.
Related
I am just practicing basic for loops to compare against their purrr::map() equivalent. However I am lost why the simple print function appears to double the output vs. its equivalent for loop.
#this simple for loop behaves as expected and gives us the numbers 1 through 10.
.x <- 1:10
for (i in .x){
print(i)
}
#result
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
#this doubles the output in an embedded list - I don't understand why
map(.x=.x,~print(.x))
#results below
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4
[[5]]
[1] 5
[[6]]
[1] 6
[[7]]
[1] 7
[[8]]
[1] 8
[[9]]
[1] 9
[[10]]
[1] 10
I would have though they would produce the same (however I know the map results would be in a list unless I specify the output (eg. map_chr or map_df).
According to R documentations print prints its argument and returns it invisibly (via invisible(x)).
So your map function is essentially doing
.x <- 1:10
funcy <- function() {
out = list()
for (i in .x){
out[[i]] = print(i)
}
return(out)
}
funcy()
The print function gets called every loop and when the loop ends the function returns the stored values in a list
The purrr library has a function specifically designed for tasks such as this: walk.
If you don't want to return anything and are only calling functions for the purpose of their downstream effects (print or write_csv), you can use walk instead of map.
walk(1:10, print)
# [1] 1
# [1] 2
# [1] 3
# [1] 4
# [1] 5
# [1] 6
# [1] 7
# [1] 8
# [1] 9
# [1] 10
I'd like to unlist a nested list with has some items as vectors. The problem is that unlist also splits up these vectors. How can I keep them as single items?
a) one level up (unlist parameter: recursive = F)
b) all levels (unlist parameter: recursive = T)
Here's the example:
list0 <- list(c(1,2),
list(3,
c(4,5)
)
)
> list0
[[1]]
[1] 1 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4 5
If we unlist one level:
list1 <- unlist(list0, recursive = F)
we get:
> list1
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
[[4]]
[1] 4 5
but, as I'd like to keep vectors as they are, I'd like to get:
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4 5
Maybe one way is with a for loop, but I guess that would be slow if the number of lists is high.
Could anyone give me some hints, please?
Thanks in advance
For your example, the code below gives the expected result.
f <- function(x){
if(is.atomic(x)){
list(x)
}else{
x
}
}
unlist(lapply(list0, f), recursive=FALSE)
But perhaps you need something which works with more nested levels, like:
f <- function(x){
if(is.atomic(x)){
list(x)
}else{
x
}
}
g <- function(L){
out <- unlist(lapply(L, f), recursive=FALSE)
while(any(sapply(out, is.list))){
out <- g(out)
}
out
}
list1 <- list(c(1,2),
list(3, c(4,5)),
list(6, list(c(7,8)))
)
list1_flattened <- g(list1)
which gives:
> list1
[[1]]
[1] 1 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4 5
[[3]]
[[3]][[1]]
[1] 6
[[3]][[2]]
[[3]][[2]][[1]]
[1] 7 8
> list1_flattened
[[1]]
[1] 1 2
[[2]]
[1] 3
[[3]]
[1] 4 5
[[4]]
[1] 6
[[5]]
[1] 7 8
I have a problem with R's grep() function apparently finding an "l" everywhere:
> l <- list(list(), list("a"), list("a","l"))
> grep("a",l)
[1] 2 3
> grep("l",l)
[1] 1 2 3
> grep("l",l,fixed=TRUE)
[1] 1 2 3
This problem seems to occur only with the letter "l". Does anyone have a hint on that?
Many thanks,
Cord
If you look at the documentation for the argument x in grep you'll see that it should be
a character vector where matches are sought, or an object which can be coerced by as.character to a character vector.
If you try that operation you'll see what goes wrong:
> as.character(l)
[1] "list()" "list(\"a\")" "list(\"a\", \"l\")"
so the same "problem" happens if you grep for i, s etc.
You could try the following instead
sapply(l, function(i) grep("l", i))
which produces
[[1]]
integer(0)
[[2]]
integer(0)
[[3]]
[1] 2
Interesting post, I never knew grep convert the x vector like this:
l <- list(list(), list("a"), list("a","l"))
l
#> [[1]]
#> list()
#>
#> [[2]]
#> [[2]][[1]]
#> [1] "a"
#>
#>
#> [[3]]
#> [[3]][[1]]
#> [1] "a"
#>
#> [[3]][[2]]
#> [1] "l"
Internally grep is converting l to a character vector
grep
#> function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
#> fixed = FALSE, useBytes = FALSE, invert = FALSE)
#> {
#> if (!is.character(x))
#> x <- structure(as.character(x), names = names(x))
#> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#> .Internal(grep(as.character(pattern), x, ignore.case, value,
#> perl, fixed, useBytes, invert))
#> }
#> <bytecode: 0x0000000012e18610>
#> <environment: namespace:base>
So now l is actually:
structure(as.character(l), names = names(l))
#> [1] "list()" "list(\"a\")" "list(\"a\", \"l\")"
Which has "l" in each.
You could unlist l first to get expected results:
ul <- unlist(l)
ul
#> [1] "a" "a" "l"
grep("a",l)
#> [1] 2 3
grep("a",ul)
#> [1] 1 2
grep("l",l)
#> [1] 1 2 3
grep("l",ul)
#> [1] 3
grep("l",l,fixed=TRUE)
#> [1] 1 2 3
grep("l",ul,fixed=TRUE)
#> [1] 3
This question has a nice solution of flattening lists while preserving their data types (which unlist does not):
flatten = function(x, unlist.vectors=F) {
while(any(vapply(x, is.list, logical(1)))) {
if (! unlist.vectors)
x = lapply(x, function(x) if(is.list(x)) x else list(x))
x = unlist(x, recursive=F)
}
x
}
If I give it the following list, it behaves as expected:
> a = list(c(1,2,3), list(52, 561), "a")
> flatten(a)
[[1]]
[1] 1 2 3
[[2]]
[1] 52
[[3]]
[1] 561
[[4]]
[1] "a"
Now I'd like to restructure the flat list like a. relist fails miserably:
> relist(flatten(a), skeleton=a)
[[1]]
[[1]][[1]]
[1] 1 2 3
[[1]][[2]]
[1] 52
[[1]][[3]]
[1] 561
[[2]]
[[2]][[1]]
[[2]][[1]][[1]]
[1] "a"
[[2]][[2]]
[[2]][[2]][[1]]
NULL
[[3]]
[[3]][[1]]
NULL
Now, I could of course do relist(unlist(b), a) but that loses data types again. What is a good way to restructure a flat list?
Bonus points if it handles the analogous attribute to unlist.vectors correctly.
One way to do it is:
relist2 = function(x, like, relist.vectors=F) {
if (! relist.vectors)
like = rapply(a, function(f) NA, how='replace')
lapply(relist(x, skeleton=like), function(e) unlist(e, recursive=F))
}
This retains the classes and distinguishes between lists and vectors:
> relist2(flatten(a), like=a)
[[1]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 52
[[2]][[2]]
[1] 561
[[3]]
[1] "a"
> relist2(flatten(a, unlist.vectors=T), like=a, relist.vectors=T)
[[1]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 52
[[2]][[2]]
[1] 561
[[3]]
[1] "a"
I have a list
> (mylist <- list(list(a=1),list(a=2),list(a=3)))
[[1]]
[[1]]$a
[1] 1
[[2]]
[[2]]$a
[1] 2
[[3]]
[[3]]$a
[1] 3
and I want to add field b to each sublist from 11:13 to get something like
> (mylist <- list(list(a=1,b=11),list(a=2,b=12),list(a=3,b=13)))
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 11
[[2]]
[[2]]$a
[1] 2
[[2]]$b
[1] 12
[[3]]
[[3]]$a
[1] 3
[[3]]$b
[1] 13
How do I do this?
(note that I have a large number of such relatively small lists, so this will be called in apply and has to be reasonably fast).
mylist <- list(list(a=1),list(a=2),list(a=3))
b.vals <- 11:13
mylist <- lapply(
1:length(mylist),
function(x) {
mylist[[x]]$b <- b.vals[[x]]
mylist[[x]]
} )