R arrange list of vectors - r

Say I have a list of vectors like this:
list.of.vec <- data.frame(id = c(1,2,3,4)) %>%
mutate(a = list(list(c(1,2,3), c(2,3,4)), list(c(2,4,5), c(1,3,4)), list(c(1,2,3), c(4,3,4)), list(c(3,2,3), c(2,3,4))))
how can I arrange a such that in each pair of vectors, the one with the bigger first values goes first? E.g., if there was a list c(1,2,3), c(2,3,4), it should become c(2,3,4), c(1,2,3). And if first values are equal then it looks at the next value.

We can use rrapply
library(rrapply)
library(purrr)
library(dplyr)
list.of.vec <- list.of.vec %>%
mutate(a = map(a, ~ .x[order(-rrapply(.x, f = function(x) x[1],
how = 'unlist'))]))
-output
> list.of.vec
id a
1 1 2, 3, 4, 1, 2, 3
2 2 2, 4, 5, 1, 3, 4
3 3 4, 3, 4, 1, 2, 3
4 4 3, 2, 3, 2, 3, 4
> list.of.vec$a
[[1]]
[[1]][[1]]
[1] 2 3 4
[[1]][[2]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 2 4 5
[[2]][[2]]
[1] 1 3 4
[[3]]
[[3]][[1]]
[1] 4 3 4
[[3]][[2]]
[1] 1 2 3
[[4]]
[[4]][[1]]
[1] 3 2 3
[[4]][[2]]
[1] 2 3 4

You can use -
list.of.vec$a <- lapply(list.of.vec$a, function(x) {
inds <- match(TRUE, x[[1]] != x[[2]])
if(x[[1]][inds] > x[[2]][inds]) x else rev(x)
})
list.of.vec$a
#[[1]]
#[[1]][[1]]
#[1] 2 3 4
#[[1]][[2]]
#[1] 1 2 3
#[[2]]
#[[2]][[1]]
#[1] 2 4 5
#[[2]][[2]]
#[1] 1 3 4
#[[3]]
#[[3]][[1]]
#[1] 4 3 4
#[[3]][[2]]
#[1] 1 2 3
#[[4]]
#[[4]][[1]]
#[1] 3 2 3
#[[4]][[2]]
#[1] 2 3 4
inds would return the index for each list that we need to compare, an index where the value is different in both the vectors. If the first index has a higher value we return the list as it is without any change or else we return the reversed list.

We can try
lapply(
a,
function(x) {
x[order(-sapply(x, `[`, 1))]
}
)
which gives
[[1]]
[[1]][[1]]
[1] 2 3 4
[[1]][[2]]
[1] 1 2 3
[[2]]
[[2]][[1]]
[1] 2 4 5
[[2]][[2]]
[1] 1 3 4
[[3]]
[[3]][[1]]
[1] 4 3 4
[[3]][[2]]
[1] 1 2 3
[[4]]
[[4]][[1]]
[1] 3 2 3
[[4]][[2]]
[1] 2 3 4

Related

purrr - how to apply recursively a function with changing arguments

Ideally I would like to make use of purrr's accumulate function or similar.
Let's say I want to make use of utils::combn function iteratively, and get all the intermediate results (ideally put inside a list of lists).
In example below, initially, parameter x = 4, thus m will be also 4 (but (x, m) could be (5, 5), (6, 6), ...). Then, after first loop, x will be previous result, whilst m goes down by one, iteratively until m = 2.
n1 <- combn(x = 4, m = 4, simplify = FALSE)
n2 <- map(n1, ~ combn(.x, 3, simplify = FALSE))
n3 <- map(n2, ~ map(., ~ combn(.x, 2, simplify = FALSE)))
> n1
[[1]]
[1] 1 2 3 4
> n2
[[1]]
[[1]][[1]]
[1] 1 2 3
[[1]][[2]]
[1] 1 2 4
[[1]][[3]]
[1] 1 3 4
[[1]][[4]]
[1] 2 3 4
> n3
[[1]]
[[1]][[1]]
[[1]][[1]][[1]]
[1] 1 2
[[1]][[1]][[2]]
[1] 1 3
[[1]][[1]][[3]]
[1] 2 3
[[1]][[2]]
[[1]][[2]][[1]]
[1] 1 2
[[1]][[2]][[2]]
[1] 1 4
[[1]][[2]][[3]]
[1] 2 4
[[1]][[3]]
[[1]][[3]][[1]]
[1] 1 3
[[1]][[3]][[2]]
[1] 1 4
[[1]][[3]][[3]]
[1] 3 4
[[1]][[4]]
[[1]][[4]][[1]]
[1] 2 3
[[1]][[4]][[2]]
[1] 2 4
[[1]][[4]][[3]]
[1] 3 4
As you can imagine, I want to get all possible combinations, e.g.:
choose(4, 4) -> choose(result, 3) -> choose(result, 2).
Any help or ideas would be much appreciated.
You can use accumulate + map_depth:
combn_recur <- function(n) {
accumulate(c(n, 0:(n-2)),
~ map_depth(.x, .y, combn, m = n-.y, simplify = FALSE))[-1]
}
all.equal(combn_recur(4), c(n1, n2, n3))
# TRUE
combn_recur(3)
# [[1]]
# [1] 1 2 3
#
# [[2]]
# [[2]][[1]]
# [1] 1 2
#
# [[2]][[2]]
# [1] 1 3
#
# [[2]][[3]]
# [1] 2 3
combn_recur(2)
# [[1]]
# [1] 1 2
combn_recur(1)
# Error in .f(.x[[i]], ...) : n < m

Get subsets between one element and the previous same element

Consider a vector:
vec <- c(1, 3, 4, 3, 3, 1, 1)
I'd like to get, for each element of the vector, a subset of the values in between the nth element and its previous occurrence.
The expected output is:
f(vec)
# [[1]]
# [1] 1
#
# [[2]]
# [1] 3
#
# [[3]]
# [1] 4
#
# [[4]]
# [1] 3 4 3
#
# [[5]]
# [1] 3 3
#
# [[6]]
# [1] 1 3 4 3 3 1
#
# [[7]]
# [1] 1 1
We may loop over the sequence of the vector, get the index of the last match of the same element ('i1') from the previous elements of the vector and get the sequence (:) to subset the vector
lapply(seq_along(vec), function(i) {
i1 <- tail(which(vec[1:(i-1)] == vec[i]), 1)[1]
i1[is.na(i1)] <- i
vec[i1:i]
})
-output
[[1]]
[1] 1
[[2]]
[1] 3
[[3]]
[1] 4
[[4]]
[1] 3 4 3
[[5]]
[1] 3 3
[[6]]
[1] 1 3 4 3 3 1
[[7]]
[1] 1 1

From a list of numeric values, create a list of indices

I have a list of numeric vectors:
a <- list(c(2, 3, 4, 5, 6, 7), c(4, 5, 6, 7, 8), c(6, 7, 8, 9, 10))
> a
[[1]]
[1] 2 3 4 5 6 7
[[2]]
[1] 4 5 6 7 8
[[3]]
[1] 6 7 8 9 10
I want to create a list where each element corresponds to values from 1 to the max value in the original list "a". The values in each element of the new list are the indices in the original list containing the focal value.
For example, the first element in the result contains the indices in "a" with the value 1. Because no element contains 1, the result is NULL. The second element contains the indices in "a" with the value 2, i.e. the first element, 1. The value 4 is found in element 1 and 2.
> res
[[1]]
NULL
[[2]]
[1] 1
[[3]]
[1] 1
[[4]]
[2] 1 2
[[5]]
[2] 1 2
[[6]]
[3] 1 2 3
[[7]]
[3] 1 2 3
[[8]]
[2] 2 3
[[9]]
[1] 3
[[10]]
[1] 3
I tried this with nested loops, but it is taking too much time and growing lists within loops is very slow. I have 60,000 sublists in my main list, so is there vectorized solution for this ?
Thanks in Advance.
Here is a base R way.
lapply(seq.int(max(unique(unlist(a)))), \(i){
which(sapply(a, \(x) any(i == x)))
})
Another way:
searchInList <- function(list2search, e){
idx2search <- 1:length(list2search)
list2search2 <- lapply(list2search, `length<-`, max(lengths(list2search)))
output <- matrix(unlist(list2search2), ncol = length(list2search2[[1]]), byrow = TRUE)
idx <- apply(output, 1, function(x){ (e %in% x) } )
return(idx2search[idx])
}
result <- lapply(1:max(unlist(a)), function(x) { searchInList(a, x) } )
Here is one way using match and rapply.
apply(matrix(rapply(a, \(x) !is.na(match(1:max(unlist(a)), x))),,length(a)), 1, which)
# [[1]]
# integer(0)
#
# [[2]]
# [1] 1
#
# [[3]]
# [1] 1
#
# [[4]]
# [1] 1 2
#
# [[5]]
# [1] 1 2
#
# [[6]]
# [1] 1 2 3
#
# [[7]]
# [1] 1 2 3
#
# [[8]]
# [1] 2 3
#
# [[9]]
# [1] 3
#
# [[10]]
# [1] 3
Another solution using base R:
apply(sapply(a, `%in%`, x = seq_len(max(unlist(a)))), 1, which)
A tidyverse approach:
library(purrr)
a <- list(c(2, 3, 4, 5, 6, 7), c(4, 5, 6, 7, 8), c(6, 7, 8, 9, 10))
i = 1:10
map(i, ~map_int(imap(a, ~(..3 %in% .x)*.y, i), ~.x[.y], .x) %>% .[. != 0])
The logic is to get a list of positions of TRUE values, and multiple this by the list element index. Here, the first element of the first vector, first element of the second vector, third element of the third vector form all matches, and thus the first element in the target list.
imap(a, ~(..3 %in% .x)*.y, i)
[[1]]
[1] 0 1 1 1 1 1 1 0 0 0
[[2]]
[1] 0 0 0 2 2 2 2 2 0 0
[[3]]
[1] 0 0 0 0 0 3 3 3 3 3

Indexing table in [R]

I have a vector:
a = c(1, 3, 2, 2, 4, 1, 1, NA)
I want to generate something like:
[["1"]]
1, 6, 7
[["2"]]
3, 4
[["3"]]
2
[["5"]]
5
Mapping from value to index. Any help!
This is a split operation on the indexes of a:
split(seq_along(a), a)
#$`1`
#[1] 1 6 7
#
#$`2`
#[1] 3 4
#
#$`3`
#[1] 2
#
#$`4`
#[1] 5
setNames(lapply(unique(a), function(x) which(x == a)), unique(a))
# $`1`
# [1] 1 6 7
#
# $`3`
# [1] 2
#
# $`2`
# [1] 3 4
#
# $`4`
# [1] 5
#
# $<NA>
# integer(0)
This is slightly shorter using tidyverse
library(tidyverse)
map(unique(a) %>% setNames(.,.), ~which(.x == a))
Here is another option with unstack
unstack(list(i = seq_along(a), a), i ~ a)
#$`1`
#[1] 1 6 7
#$`2`
#[1] 3 4
#$`3`
#[1] 2
#$`4`
#[1] 5

How to delete specific elements in a list

I have a list:
aa <- list(c(12, 1, 5, 4), c(2, 3, 5), c(6, 4,1))
aa
#[[1]]
#[1] 12 1 5 4
#[[2]]
#[1] 2 3 5
#[[3]]
#[1] 6 4 1
I would like to delete all "4" and "5" in aa and be able to return the following list:
#[[1]]
#[1] 12 1
#[[2]]
#[1] 2 3
#[[3]]
#[1] 6 1
So is there a simple way of doing this? Thank you.
Here you have a simple solution using lapply:
lapply(aa, function(x) {x[!(x %in% c(4, 5))]})
Output:
[[1]]
[1] 12 1
[[2]]
[1] 2 3
[[3]]
[1] 6 1

Resources