Reordering elements of a list - r

I'm trying to create a function in R that takes a list as an input and reorder its elements based on the mean of each element (for example, the first element will be the one with the largest mean value, etc.) I'm trying to find the easiest way to do it without having to use any loops. I tried to sort the mean like below but couldn't figure out how to have the elements associated with the mean to move along. Any suggestions or advice would be appreciated.
function1 <- function(x){
return(sort(mean(x), decreasing = T))
}
function2 <- function(x) {
return(lapply(function1, x))
}
testlist <- list(c(10, 5, 1), c(3, 2), c(77, 90, 1), c(23, 34), c(2, 35, 22))
function2(testlist)

Here's one way that is really just testlist[order(sapply(testiest, mean))], but put inside a function. The idea is that sapply() returns a vector that gives the mean of each list element, order() gives the element numbers in order of the mean. Then, you are giving that ordered element numbers based on the mean to index the values of the list.
testlist <- list(c(10, 5, 1), c(3, 2), c(77, 90, 1), c(23, 34), c(2, 35, 22))
function1 <- function(x){
x[order(sapply(x, mean))]
}
function1(testlist)
#> [[1]]
#> [1] 3 2
#>
#> [[2]]
#> [1] 10 5 1
#>
#> [[3]]
#> [1] 2 35 22
#>
#> [[4]]
#> [1] 23 34
#>
#> [[5]]
#> [1] 77 90 1
Created on 2023-02-18 by the reprex package (v2.0.1)

You can use sapply function and then order the elements in decreasing ordedr with the first element with the highest mean :
function2 <- function(z) {
order_ind <- order(sapply(z, mean), decreasing = TRUE)
z[order_ind]
}
sapply() computes the mean value of each element and
z[order_ind] outputs the ordered list using the ordered indexes
function2(testlist)
the results:
[[1]]
[1] 77 90 1
[[2]]
[1] 23 34
[[3]]
[1] 2 35 22
[[4]]
[1] 10 5 1
[[5]]
[1] 3 2

Using fmean from collapse
library(collapse)
testlist[order(fmean(testlist))]
-output
[[1]]
[1] 3 2
[[2]]
[1] 10 5 1
[[3]]
[1] 2 35 22
[[4]]
[1] 23 34
[[5]]
[1] 77 90 1
data
testlist <- list(c(10, 5, 1), c(3, 2), c(77, 90, 1), c(23, 34), c(2, 35,
22))

Related

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

Group a vector of numbers by range

I have a sorted vector:
c(1, 10, 31, 80, 100, 120, 160)
I would like to split its elements into several groups by range, which I set to 31 in this example.
The result is like this:
[[1]] 1, 10, 31
[[2]] 80, 100
[[3]] 100, 120
[[4]] 160
The ranges in each group is less than 31. I have tried a loop, but I do not like it. Also, I tried the outer function, where I calculated all pairwise differences:
res <- outer(vec, vec, "-")
Then filter each column by the condition > 0 and < 31.
apply(res, 2, function(x) x[x > 0 & x < 31])
The result is not good enough though...
I think this will serve your purpose finally
First list will extract items fulfilling your condition of range, whereas final_list will remove items that are actually contained in some other items.
vec <- c(1, 10, 31, 80, 100, 120, 160)
first_list <- unique(apply(outer(vec, vec, "-"), 1, function(x){vec[(x < 31 & x >= 0)] }))
final_list <- first_list[!sapply(seq_along(first_list), function(i) max(sapply(first_list[-i],function(L) all(first_list[[i]] %in% L))))]
> final_list
[[1]]
[1] 1 10 31
[[2]]
[1] 80 100
[[3]]
[1] 100 120
[[4]]
[1] 160
Here's a neat solution
x <- c(1, 10, 31, 80, 100, 120, 160)
y <- findInterval(x+30, x)
lapply(seq_along(x)[!duplicated(y)], function(z) x[z:y[z]])
#> [[1]]
#> [1] 1 10 31
#>
#> [[2]]
#> [1] 80 100
#>
#> [[3]]
#> [1] 100 120
#>
#> [[4]]
#> [1] 160

Return a list containing all values greater than N of each matrix row in R

Given a matrix in R like the following:
[,1] [,2] [,3]
[1,] 2 0 8
[2,] 7 1 9
[3,] 9 13 5
I would like to get a list with numbers that are greater than a given N by row.
For the example of N=7, the result should be:
[[1]]
[1] 8
[[2]]
[1] 9
[[3]]
[1] 9 13
I don't know if it can be done efficiently using functions like apply, lapply, sapply... or if I should use conventional loops (for, while).
apply with MARGIN=1 will apply a function across all the rows of the matrix.
m <- matrix(c(2, 7, 9, 0, 1, 13, 8, 9, 5), ncol = 3)
N <- 7
apply(m, 1, function(r) r[r>N])
#> [[1]]
#> [1] 8
#>
#> [[2]]
#> [1] 9
#>
#> [[3]]
#> [1] 9 13

creating one new vector from a list with 3 vectors in it, but eliminate duplicate values

I have a list that contains multiple lists of three vectors
That prints:
[[1]]
[[1]][[1]]
[1] 1 2
[[1]][[2]]
[1] 1 3
[[1]][[3]]
[1] 2 3
[[2]]
[[2]][[1]]
[1] 4 5
[[2]][[2]]
[1] 5 6
[[2]][[3]]
[1] 4 6
I want to create two vectors:
c(1, 2, 3), c(4, 5, 6)
I have also tried to unique(unlist(x)) which works but loses the references to the positions within the list. I have multiple lists within my lists, so when I apply unique(unlist(list(x))) it just creates one string. I want to keep my vector c(1, 2, 3) separate from the next list of c(4, 5, 6)
You are almost there. You just need to apply your solution to all the lists separately.
lapply(lst, function(x) unique(unlist(x)))
#[[1]]
#[1] 1 2 3
#[[2]]
#[1] 4 5 6
data
lst <- list(list(c(1, 2), c(1, 3), c(2, 3)), list(c(4,5), c(5, 6), c(4, 6)))

Combine vectors of list in R

I have a list contains number of vectors with different length as below:
lst <- list(c(1,2), c(1,2), c(4,5,10,11,12,13), c(7,8,9))
lst
[[1]]
[1] 1 2
[[2]]
[1] 1 2
[[3]]
[1] 4 5 10 11 12 13
[[4]]
[1] 7 8 9
[[5]]
[1] 7 8 9
how can I combine and remove repeated vectors to be similar to the list below:
[[1]]
[1] 1 2
[[2]]
[1] 4 5 10 11 12 13
[[3]]
[1] 7 8 9
for repeated vectors I can use unique function.
Here is one option. We unlist the list, get a logical vector with duplicated, relist it to a list having the same skeleton as the original list, subset the 'l1' based on the logical list with Map and Filter out the list elements having 0 elements.
Filter(length, Map(`[`, l1, relist(!duplicated(unlist(l1)), skeleton = l1)))
#[[1]]
#[1] 1 2
#[[2]]
#[1] 4 5 10 11 12 13
#[[3]]
#[1] 7 8 9
#[[4]]
#[1] 14 15
#[[5]]
#[1] 19 20
data
l1 <- list(1:2, 1:2, c(4, 5, 10, 11, 12, 13), 7:9, 7:9, 10:13, 10:13,
c(4, 10, 11, 12, 13), 14:15, 19:20)
I have edited your answer to give a reproducible example. Please let me know if this does not correctly illustrate your problem anymore. If it does, this is trivial:
unique(lst)

Resources