Задача: оптимизировать листинг [легко] - PullRequest
11 голосов
/ 25 октября 2010

Поскольку в последнее время ТАК немного медленный, я пишу простой вопрос. Я был бы признателен, если бы на этой скамье остались большие рыбы и дали бы новичкам шанс ответить.

Иногда у нас есть объекты, которые имеют смешное количество больших элементов списка (векторов). Как бы вы «разобрали» этот объект в один вектор? Показать доказательство того, что ваш метод быстрее, чем unlist().

Ответы [ 4 ]

10 голосов
/ 25 октября 2010

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

.Internal(unlist(your_list, FALSE, FALSE))

, я буду голосовать за все, что вы делаете на SO в течение следующего 1 года !!!

[Обновление: если требуются неуникальные имена и список не рекурсивный, вот версия, которая улучшается по сравнению с списком 100 раз

 myunlist <- function(l){
    names <- names(l)
    vec <- unlist(l, F, F)
    reps <- unlist(lapply(l, length), F, F)
    names(vec) <- rep(names, reps)
    vec
    }

 myunlist(list(a=1:3, b=2))
 a a a b 
 1 2 3 2 

 > tl <- list(a = 1:20000, b = 1:5000, c = 2:30)
 > system.time(for(i in 1:200) unlist(tl))
 user  system elapsed 
 22.97    0.00   23.00 

 > system.time(for(i in 1:200) myunlist(tl))
 user  system elapsed 
 0.2     0.0     0.2 

 > system.time(for(i in 1:200) unlist(tl, F, F))
 user  system elapsed 
 0.02    0.00    0.02 

]

[Обновление 2: Ответ на вызов Nr3 от Ричи Коттона.

bigList3 <- replicate(500, rnorm(1e3), simplify = F)

unlist_vit <- function(l){
    names(l) <- NULL
    do.call(c, l)
    }

library(rbenchmark)

benchmark(unlist = unlist(bigList3, FALSE, FALSE),
          rjc    = unlist_rjc(bigList3),
          vit    = unlist_vit(bigList3),
          order  = "elapsed",
          replications = 100,
          columns = c("test", "relative", "elapsed")
          )

    test  relative elapsed
1 unlist   1.0000    2.06
3    vit   1.4369    2.96
2    rjc   3.5146    7.24

]

PS: Я предполагаю, что "большая рыба" - это та, у которой больше репутации, чем у вас.Так что я здесь очень маленький :).

2 голосов
/ 08 декабря 2018

c() имеет логический аргумент recursive, который будет рекурсивно отменять список вектора при значении TRUE (по умолчанию, очевидно, FALSE).

l <- replicate(500, rnorm(1e3), simplify = F)

microbenchmark::microbenchmark(
  unlist = unlist(l, FALSE, FALSE),
  c = c(l, recursive = TRUE, use.names = FALSE)
)

# Unit: milliseconds
# expr      min       lq     mean   median       uq      max neval
# unlist 3.083424 3.121067 4.662491 3.172401 3.985668 27.35040   100
#      c 3.084890 3.133779 4.090520 3.201246 3.920646 33.22832   100
2 голосов
/ 25 октября 2010

Не-unlist() решение должно быть чертовски быстрым, чтобы победить unlist() не так ли? Здесь требуется менее двух секунд, чтобы удалить список из 2000 числовых векторов длиной 100 000 каждый.

> bigList2 <- as.list(data.frame(matrix(rep(rnorm(1000000), times = 200), 
+                                       ncol = 2000)))
> print(object.size(bigList2), units = "Gb")
1.5 Gb
> system.time(foo <- unlist(bigList2, use.names = FALSE))
   user  system elapsed 
  1.897   0.000   2.019

С bigList2 и foo в моей рабочей области R использует ~ 9 ГБ доступной памяти. Ключ use.names = FALSE. Без этого unlist() мучительно медленно. Как медленно я все еще жду, чтобы узнать ...

Мы можем немного ускорить это, установив recursive = FALSE, и тогда мы получим то же самое, что и ответ ВитошКа (два репрезентативных момента времени):

> system.time(foo <- unlist(bigList2, recursive = FALSE, use.names = FALSE))
   user  system elapsed 
  1.379   0.001   1.416
> system.time(foo <- .Internal(unlist(bigList2, FALSE, FALSE)))
   user  system elapsed 
  1.335   0.000   1.344

... наконец-то use.names = TRUE версия закончена ...:

> system.time(foo <- unlist(bigList2, use = TRUE))
    user   system  elapsed 
2307.839   10.978 2335.815

и он занимал все мои системы 16 ГБ ОЗУ, поэтому я сдался в этот момент ...

0 голосов
/ 25 октября 2010

Как рыба среднего размера, я использую решение с первой попытки, которое дает ориентир для ловли маленьких рыб.Это примерно в 3 раза медленнее, чем unlist.

Я использую уменьшенную версию тестового списка ucfagls.(Поскольку он лучше умещается в памяти.)

bigList3 <- as.list(data.frame(matrix(rep(rnorm(1e5), times = 200), ncol = 2000)))

Основная идея - создать один длинный вектор для хранения ответа, а затем перебрать элементы списка, копируя значения из списка.

unlist_rjc <- function(l)
{
  lengths <- vapply(l, length, FUN.VALUE = numeric(1), USE.NAMES = FALSE)
  total_len <- sum(lengths)
  end_index <- cumsum(lengths)
  start_index <- 1 + c(0, end_index)
  v <- numeric(total_len)
  for(i in seq_along(l))
  {
    v[start_index[i]:end_index[i]] <- l[[i]]
  }
  v
}

t1 <- system.time(for(i in 1:10) unlist(bigList2, FALSE, FALSE))
t2 <- system.time(for(i in 1:10) unlist_rjc(bigList2))
t2["user.self"] / t1["user.self"]  # 3.08

Проблемы для маленьких рыб:
1. Можете ли вы расширить его для работы с другими типами, кроме числовых?
2. Можете ли вы заставить его работать с рекурсией (вложенные списки)?
3. Можете ли вы сделатьэто быстрее?

Я буду голосовать всем, у кого меньше очков, чем у меня, чей ответ отвечает одному или нескольким из этих мини-вызовов.

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