R - Есть ли векторизованный способ / готовая функция, которая быстро генерирует уникальные множества между двумя векторами? - PullRequest
0 голосов
/ 05 октября 2018

Итак, что я хочу - это информационный кадр, в котором комбинации двух случайных векторов представлены в строке.Я не хочу дублировать комбинации, как;1,2; 2,1.Всего 1 из них.Как и комбинация, НЕ являющаяся самоповторяющейся;1,1.

Прямо сейчас у меня есть этот простой цикл for, но он не идеален;

unique_combos <- function(v1, v2) {
    df <- data.frame(matrix(ncol=2))
    counter = 0
    for (name1 in v1) {
        for (name2 in v2) {
            if (name1 != name2){
                counter = counter + 1
                df[counter,] <- c(name1, name2)
            }
        }
    }
    return(df)
}

# example usage;
> v1 <- c(1,2,3,4)
> v2 <- c(3,4,5,6)
> unique_combos(v1, v2)
   X1 X2
1   1  3
2   1  4
3   1  5
4   1  6
5   2  3
6   2  4
7   2  5
8   2  6
9   3  4
10  3  5
11  3  6
12  4  3
13  4  5
14  4  6
> 

Любой векторизованный способ сделать это?Желательно также нацелены на производительность.Помимо этого я хотел отметить, что векторы могут быть любой длины и содержать случайные величины.

Edit1 - моя функция не работает должным образом! ;Мне не нужна комбинация 3-4 4-3.

Edit2 - мое окончательное решение от @Ryan и @Frank (спасибо, ребята!);

unique_combos <- function(v1, v2) {
  intermediate <- unique(CJ(v1, v2)[V1 > V2, c("V1", "V2") := .(V2, V1)])
  return(intermediate[V1 != V2])

* note;для этого используются пакеты data.table и plyr.

Ответы [ 3 ]

0 голосов
/ 05 октября 2018

Разница в скорости здесь, вероятно, не будет иметь никакого реального влияния, если ваши векторы не огромны, но, так как вы указываете «производительность» как тег, вот немного более быстрый метод.

library(data.table)
CJ(v1, v2)[V1 != V2]

Тест:

Примечание: CJ по умолчанию будет заказывать v1, а заказ по v1 в unique_combos2 занимает много времени, поэтому я удалилэта часть, поскольку не ясно, нужна ли она вам.

unique_combos2 <- function(v1, v2) {
  e <- expand.grid(v1, v2)
  e <- e[e[[1]] != e[[2]], ]
  e
}
unique_combos3 <- function(v1, v2) CJ(v1, v2)[V1 != V2]

w1 <- sample(200)
w2 <- sample(200)
mb2 <- microbenchmark(
  u2 = unique_combos2(w1, w2),
  u3 = unique_combos3(w1, w2)
)

# Unit: milliseconds
#  expr      min       lq      mean   median       uq        max neval cld
#    u2 5.513842 5.942765 10.969386 6.692507 8.158763 368.180211   100   b
#    u3 1.140513 1.443076  1.898202 1.711384 2.139075   8.397942   100  a 

Редактировать: чтобы удалить дублирующиеся пары независимо от порядка, используйте решение @ Frank в комментариях, которое эффективно сортирует все строки перед вызовом unique

unique(CJ(v1, v2)[V1 > V2, c("V1", "V2") := .(V2, V1)])
0 голосов
/ 05 октября 2018

Вот способ tidyverse, в основном с использованием purrr инструментов.(отредактировано для уточнения вопроса).Этот метод выполняет следующие действия:

  1. Получение списка наборов произведений векторов, фильтрация случаев, когда они равны,
  2. Преобразование элементов списка в отсортированные целочисленные векторы и отбрасываниелюбые дубликаты с unique,
  3. transpose возвращаются к структуре списка столбцов, simplify для преобразования столбцов в векторы и помещения обратно во фрейм данных.

Очень открыт, чтобы узнать, может ли кто-нибудь придумать способ сжать некоторые шаги!

v1 <- c(1,2,3,4)
v2 <- c(3,4,5,6)
library(tidyverse)
cross2(v1, v2, .filter = `==`) %>%
  map(~ sort(as.integer(.))) %>%
  unique %>%
  transpose(.names = c("x", "y")) %>%
  simplify_all %>%
  as_tibble()
#> # A tibble: 13 x 2
#>        x     y
#>    <int> <int>
#>  1     1     3
#>  2     2     3
#>  3     3     4
#>  4     1     4
#>  5     2     4
#>  6     1     5
#>  7     2     5
#>  8     3     5
#>  9     4     5
#> 10     1     6
#> 11     2     6
#> 12     3     6
#> 13     4     6

Создан в 2018-10-05 пакетом представлением (v0.2.0). * * тысяча двадцать-одна

0 голосов
/ 05 октября 2018

Циклы вообще не нужны.
Вы можете использовать expand.grid и иметь data.frame с повторениями в одной инструкции.Затем с логическим индексом сохраните только разные строки.

unique_combos2 <- function(v1, v2) {
  e <- expand.grid(v1, v2)
  e <- e[e[[1]] < e[[2]], ]
  e[order(e[[1]]), ]
}


u1 <- unique_combos(v1, v2)
u2 <- unique_combos2(v1, v2)

Теперь тесты скорости.Сначала с вашими данными, затем с большими векторами.Я буду загружать пакеты microbenchmark и ggplot2 для запуска тестов и визуализации результатов.

(Результаты не показаны.)

library(microbenchmark)
library(ggplot2)

mb1 <- microbenchmark(
  u1 = unique_combos(v1, v2),
  u2 = unique_combos2(v1, v2)
)

mb1
autoplot(mb1)

w1 <- 1:20
w2 <- sample(100, 30)

mb2 <- microbenchmark(
  u1 = unique_combos(w1, w2),
  u2 = unique_combos2(w1, w2)
)

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