Вызов: оптимизировать unlisting [easy]
Потому что в последнее время SO немного медленнее, я задаю простой вопрос. Я был бы признателен, если бы большие рыбы оставались на скамейке для этого и давали новичкам возможность ответить.
Иногда у нас есть объекты, которые имеют смехотворное количество больших элементов списка (векторов). Как бы вы "перечислили" этот объект в один вектор. Покажите, что ваш метод быстрее, чем unlist()
.
Ответы
Ответ 1
Если вам не нужны имена, а ваш список - один уровень глубины, то если вы можете победить
.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
Решение, отличное от 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 использует ~ 9Gb моей доступной памяти. Ключ use.names = FALSE
. Без него unlist()
мучительно медленно. Точно как медленно я все еще жду, чтобы узнать...
Мы можем немного ускорить это, установив recursive = FALSE
, а затем мы получим то же самое, что и ответ VitoshKa (два репрезентативных тайминга):
> 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 ГБ оперативной памяти, поэтому я сдался в этот момент...
Ответ 3
Как рыба среднего размера, я прыгаю с помощью решения первой попытки, которое дает ориентир для маленьких рыб, чтобы победить. Это примерно в 3 раза медленнее, чем список.
Я использую меньшую версию тестового списка 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. Можете ли вы сделать это быстрее?
Я буду перенаправлять всех с меньшим количеством очков, чем я, чей ответ отвечает одному или нескольким из этих мини-задач.
Ответ 4
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