поиск уникальных значений из списка - PullRequest
27 голосов
/ 07 октября 2010

Предположим, у вас есть список значений

x <- list(a=c(1,2,3), b = c(2,3,4), c=c(4,5,6))

Я хотел бы найти уникальные значения из всех элементов списка вместе взятых.Пока что следующий код добился цели

unique(unlist(x))

Кто-нибудь знает более эффективный способ?У меня есть здоровенный список с большим количеством значений, и я буду признателен за любое ускорение.

1 Ответ

41 голосов
/ 07 октября 2010

Это решение, предложенное Мареком, является лучшим ответом на первоначальный вопрос. См. Ниже обсуждение других подходов и почему Марек является наиболее полезным.

> unique(unlist(x, use.names = FALSE))
[1] 1 2 3 4 5 6

Обсуждение

Более быстрое решение состоит в том, чтобы сначала вычислить unique() для компонентов вашего x, а затем выполнить окончательное unique() для этих результатов.Это будет работать только в том случае, если компоненты списка имеют одинаковое количество уникальных значений, как в обоих примерах ниже.Например:

Сначала ваша версия, затем мой двойной уникальный подход:

> unique(unlist(x))
[1] 1 2 3 4 5 6
> unique.default(sapply(x, unique))
[1] 1 2 3 4 5 6

Мы должны вызвать unique.default, так как есть метод matrix для unique, который сохраняет одно полефиксированный;это нормально, поскольку матрицу можно рассматривать как вектор.

Марек в комментариях к этому ответу отмечает, что медленная скорость подхода unlist потенциально связана с names в списке,Решение Марека состоит в том, чтобы использовать аргумент use.names для unlist, что в случае его использования приводит к более быстрому решению, чем приведенная выше двойная уникальная версия.Для простого x сообщения Романа мы получаем

> unique(unlist(x, use.names = FALSE))
[1] 1 2 3 4 5 6

Решение Марека будет работать, даже если количество уникальных элементов отличается между компонентами.

Вот более крупный пример с некоторыми временамивсе три метода:

## Create a large list (1000 components of length 100 each)
DF <- as.list(data.frame(matrix(sample(1:10, 1000*1000, replace = TRUE), 
                                ncol = 1000)))

Вот результаты для двух подходов, использующих DF:

> ## Do the three approaches give the same result:
> all.equal(unique.default(sapply(DF, unique)), unique(unlist(DF)))
[1] TRUE
> all.equal(unique(unlist(DF, use.names = FALSE)), unique(unlist(DF)))
[1] TRUE
> ## Timing Roman's original:
> system.time(replicate(10, unique(unlist(DF))))
   user  system elapsed 
  12.884   0.077  12.966
> ## Timing double unique version:
> system.time(replicate(10, unique.default(sapply(DF, unique))))
   user  system elapsed 
  0.648   0.000   0.653
> ## timing of Marek's solution:
> system.time(replicate(10, unique(unlist(DF, use.names = FALSE))))
   user  system elapsed 
  0.510   0.000   0.512

Что показывает, что удвоение unique намного быстрее для применения unique() для отдельных компонентов, а затем unique() этих меньших наборов уникальных значений, но это ускорение обусловлено только names в списке DF.Если мы скажем unlist не использовать names, решение Марека будет несколько быстрее, чем двойное unique для этой проблемы.Поскольку решение Марека правильно использует правильный инструмент, и оно быстрее, чем обходной, оно является предпочтительным решением.

Большая проблема с двойным unique подходом заключается в том, что он будет работать только если , как в двух примерах, каждый компонент списка ввода (DF или x) имеет одинаковое количество уникальных значений.В таких случаях sapply упрощает результат до матрицы, которая позволяет нам применять unique.default.Если компоненты входного списка имеют различное количество уникальных значений, двойное уникальное решение завершится неудачей.

...