Remove nested loops in R with map or apply - r

I have a list of vectors in R:
test_list <- list()
test_list[[1]] <- c(1,2,3,4,5,6,7)
test_list[[2]] <- c(1,2,3)
test_list[[3]] <- c(6,7)
test_list[[4]] <- c(9,10,11)
And I want to check the intersection of each vector with all the other vectors. A nested loop approach would look like this:
for(i in test_list) {
for(j in test_list) {
intersect(i, j)
}
}
And the results would look like this:
[1] 1 2 3 4 5 6 7
[1] 1 2 3
[1] 6 7
numeric(0)
[1] 1 2 3
[1] 1 2 3
numeric(0)
numeric(0)
[1] 6 7
numeric(0)
[1] 6 7
numeric(0)
numeric(0)
numeric(0)
numeric(0)
[1] 9 10 11
I have seen that I can remove one of the foor loops using map or apply:
get_overlap_cells <- function(x) {
for(i in test_list) {
overlaping_cells <- intersect(i, x)
}
}
r <- map(test_list, get_overlap_cells)
However, I would like to remove both loops, any ideas on how to achieve this?
Thank you!

combins <- expand.grid(seq_along(test_list), seq_along(test_list))
mapply( function(x,y) intersect(test_list[[x]],test_list[[y]]),
combins[,1], combins[,2])
[[1]]
[1] 1 2 3 4 5 6 7
[[2]]
[1] 1 2 3
[[3]]
[1] 6 7
[[4]]
numeric(0)
[[5]]
[1] 1 2 3
[[6]]
[1] 1 2 3
[[7]]
numeric(0)
[[8]]
numeric(0)
[[9]]
[1] 6 7
[[10]]
numeric(0)
[[11]]
[1] 6 7
[[12]]
numeric(0)
[[13]]
numeric(0)
[[14]]
numeric(0)
[[15]]
numeric(0)
[[16]]
[1] 9 10 11

Related

How to unlist nested lists while keeping vectors

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

Replacing values in a list based on a condition

I have a list of values called squares and would like to replace all values which are 0 to a 40.
I tried:
replace(squares, squares==0, 40)
but the list remains unchanged
If it is a list, then loop through the list with lapply and use replace
squares <- lapply(squares, function(x) replace(x, x==0, 40))
squares
#[[1]]
#[1] 40 1 2 3 4 5
#[[2]]
#[1] 1 2 3 4 5 6
#[[3]]
#[1] 40 1 2 3
data
squares <- list(0:5, 1:6, 0:3)
I think for this purpose, you can just treat it as if it were a vector as follows:
squares=list(2,4,6,0,8,0,10,20)
squares[squares==0]=40
Output:
[[1]]
[1] 2
[[2]]
[1] 4
[[3]]
[1] 6
[[4]]
[1] 40
[[5]]
[1] 8
[[6]]
[1] 40
[[7]]
[1] 10
[[8]]
[1] 20

Remove outliers based on a preceding value

How to remove outliers using a criterion that a value cannot be more than 2-fold higher then its preceding one.
Here is my try:
x<-c(1,2,6,4,10,20,50,10,2,1)
remove_outliers <- function(x, na.rm = TRUE, ...) {
for(i in 1:length(x))
x < (x[i-1] + 2*x)
x
}
remove_outliers(y)
expected outcome: 1,2,4,10,20,2,1
Thanks!
I think the first 10 should be removed in your data because 10>2*4. Here's a way to do what you want without loops. I'm using the dplyr version of lag.
library(dplyr)
x<-c(1,2,6,4,10,20,50,10,2,1)
x[c(TRUE,na.omit(x<=dplyr::lag(x)*2))]
[1] 1 2 4 20 10 2 1
EDIT
To use this with a data.frame:
df <- data.frame(id=1:10, x=c(1,2,6,4,10,20,50,10,2,1))
df[c(TRUE,na.omit(df$x<=dplyr::lag(df$x,1)*2)),]
id x
1 1 1
2 2 2
4 4 4
6 6 20
8 8 10
9 9 2
10 10 1
A simple sapply:
bool<-sapply(seq_along(1:length(x)),function(i) {ifelse(x[i]<2*x[i-1],FALSE,TRUE)})
bool
[[1]]
logical(0)
[[2]]
[1] TRUE
[[3]]
[1] TRUE
[[4]]
[1] FALSE
[[5]]
[1] TRUE
[[6]]
[1] TRUE
[[7]]
[1] TRUE
[[8]]
[1] FALSE
[[9]]
[1] FALSE
[[10]]
[1] FALSE
resulting in:
x[unlist(bool)]
[1] 1 2 4 10 20 1

Constructing vectors using (nested)loops in R

I've scanned quite some fora on the internet but couldn't find a clear answer to my problem, hence I decided to post it here. The program I am using is R.
The following problem is where I can't seem to find a solution. I am tasked with constructing a vector (1,2,2,3,3,3...,10,...,10) using a nested loop (so no rep()). So far I managed to construct a list of all elements but can't manage to convert it into the desired vector. I have tried quite some methods, like converting the data into a matrix and transposing it etc.
So far not a single method has worked, perhaps someone with more insight on this matter could help me.
This is what I've got so far:
for (i in 1:10){
for (j in 1:10)
if (j<=i)
{
x = c(i)
print(x)
}
}
which provides me with:
[1] 1
[1] 2
[1] 2
[1] 3
[1] 3
[1] 3
[1] 4
[1] 4
[1] 4
[1] 4
[1] 5
[1] 5
[1] 5
[1] 5
[1] 5
[1] 6
[1] 6
[1] 6
[1] 6
[1] 6
[1] 6
[1] 7
[1] 7
[1] 7
[1] 7
[1] 7
[1] 7
[1] 7
[1] 8
[1] 8
[1] 8
[1] 8
[1] 8
[1] 8
[1] 8
[1] 8
[1] 9
[1] 9
[1] 9
[1] 9
[1] 9
[1] 9
[1] 9
[1] 9
[1] 9
[1] 10
[1] 10
[1] 10
[1] 10
[1] 10
[1] 10
[1] 10
[1] 10
[1] 10
[1] 10
Thanks in advance!
If storing the result in a vector, instead of printing it, is the remaining problem, this can be done this way:
result <- vector(mode = "integer")
k <- 1
for (i in 1:10){
for (j in 1:10)
if (j<=i)
{
result[k] = c(i)
k <- k+1
}
}
head(result)

adding a field to each element of a list

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]]
} )

Resources