Подмножество списка векторов по положению в векторизованном виде - PullRequest
1 голос
/ 26 апреля 2019

У меня есть список векторов, и я пытаюсь выбрать (например) 2-й и 4-й элемент в каждом векторе.Я могу сделать это, используя lapply:

list_of_vec <- list(c(1:10), c(10:1), c(1:10), c(10:1), c(1:10))
lapply(1:length(list_of_vec), function(i) list_of_vec[[i]][c(2,4)])

[[1]]
[1] 2 4

[[2]]
[1] 9 7

[[3]]
[1] 2 4

[[4]]
[1] 9 7

[[5]]
[1] 2 4

Но есть ли способ сделать это векторизованным способом - избегая одной из функций применения?Моя проблема в том, что мой фактический list_of_vec довольно длинный, поэтому lapply занимает некоторое время.

Ответы [ 2 ]

2 голосов
/ 26 апреля 2019

Решения:

Вариант 1 @ Умное решение Ате с использованием do.call?:

do.call(rbind, list_of_vec)[ ,c(2,4)]

Вариант 2 Более эффективное использование lapply:

lapply(list_of_vec, `[`, c(2, 4))

Вариант 3 Векторизованное решение:

starts <- c(0, cumsum(lengths(list_of_vec)[-1]))
matrix(unlist(list_of_vec)[c(starts + 2, starts + 4)], ncol = 2)

Вариант 4 решение lapply, которое вы хотели улучшить:

lapply(1:length(list_of_vec), function(i) list_of_vec[[i]][c(2,4)])

Данные:

И несколько наборов данных, на которых я буду их тестировать:

# The original data
list_of_vec <- list(c(1:10), c(10:1), c(1:10), c(10:1), c(1:10))

# A long list with short elements
list_of_vec2 <- rep(list_of_vec, 1e5)

# A long list with long elements
list_of_vec3 <- lapply(list_of_vec, rep, 1e3)
list_of_vec3 <- rep(list_of_vec3, 1e4)

Бенчмаркинг:

Исходный список :

Unit: microseconds
 expr   min     lq     mean median    uq      max neval cld
   o1 2.276 2.8450  3.00417  2.845 3.129   10.809   100   a
   o2 2.845 3.1300  3.59018  3.414 3.414   23.325   100   a
   o3 3.698 4.1250  4.60558  4.267 4.552   20.480   100   a
   o4 5.689 5.9735 17.52222  5.974 6.258 1144.606   100   a

Длинный список, короткие элементы :

Unit: milliseconds
 expr       min        lq      mean    median        uq       max neval  cld
   o1 146.30778 146.88037 155.04077 149.89164 159.52194 184.92028    10  b  
   o2 185.40526 187.85717 192.83834 188.42749 190.32103 213.79226    10   c 
   o3  26.55091  27.27596  28.46781  27.48915  28.84041  32.19998    10 a   
   o4 407.66430 411.58054 426.87020 415.82161 437.19193 473.64265    10    d

Длинный список, длинные элементы :

Unit: milliseconds
 expr        min         lq      mean     median        uq       max neval cld
   o1 4855.59146 4978.31167 5012.0429 5025.97619 5072.9350 5095.7566    10   c
   o2   17.88133   18.60524  103.2154   21.28613  195.0087  311.4122    10 a  
   o3  855.63128  872.15011  953.8423  892.96193 1069.7526 1106.1980    10  b 
   o4   37.92927   38.87704  135.6707  124.05127  214.6217  276.5814    10 a  

Резюме:

Похоже, векторизованное решение выигрывает, если список длинный, а элементы короткие, но lapply - явный победитель длинного списка с более длинными элементами. Некоторые параметры выводят список, другие - матрицу. Так что имейте в виду, что вы хотите, чтобы ваш вывод был. Удачи !!!

1 голос
/ 26 апреля 2019

Если ваш список состоит из векторов одинаковой длины, вы можете сначала преобразовать его в матрицу, а затем получить нужные столбцы.

matrix_of_vec <- do.call(rbind,list_of_vec)
matrix_of_vec[ ,c(2,4)]

В противном случае, боюсь, вам придетсяпридерживаться подходящей семьи.Наиболее эффективный способ сделать это - использовать параллельный пакет для параллельного вычисления (удивительно).

corenum <- parallel::detectCores()-1
cl<-parallel::makeCluster(corenum)
parallel::clusterExport(cl,"list_of_vec"))
parallel::parSapply(cl,list_of_vec, '[', c(2,4) )

В этом фрагменте кода '[' - это имя функции поднабора и c(2,4) аргумент, который вы используете.перейти к нему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...