Как перебирать значения столбцов, чтобы найти все возможные комбинации в R?

Предположим, у вас есть фрейм данных с идентификаторами и элементами, прописанными для каждого идентификатора. Например:

example <- data.frame(id = c(1,1,1,1,1,2,2,2,3,4,4,4,4,4,4,4,5,5,5,5),
                      vals = c("a","b",'c','d','e','a','b','d','c',
                                 'd','f','g','h','a','k','l','m', 'a',
                                 'b', 'c'))

Я хочу найти все возможные парные комбинации. Основная проблема здесь не в функционале языка R, который я могу использовать, а в логике. Как я могу перебрать все элементы и найти закономерности? Например, a был выбран с b 3 раза в моем примере фрейма данных. Но исходный фрейм данных содержит более 30 тыс. Строк, поэтому я не могу подсчитать эти комбинации вручную. Как мне автоматизировать процесс определения количества выбранных элементов для каждого элемента?

Я думал о том, чтобы расширить свой df с помощью pivot_wider, а затем использовать map_lgl для поиска совпадений. Затем я столкнулся с проблемой, что мне потребуется много времени, чтобы найти все возможные комбинации, применяя map_lgl для каждой пары элементов.

Я задавал почти тот же вопрос меньше чем месяц назад, другие пользователи ответили на него, но результат мне не нужен.

У вас есть идеи, как создать фрейм данных со всеми возможными комбинациями значений для всех идентификаторов?


person k1rgas    schedule 04.03.2021    source источник
comment
Вы можете показать нам ожидаемый результат?   -  person user2974951    schedule 04.03.2021
comment
Какие комбинации пар вам нужны: id и vals или между vals? И я не уверен, что вы имеете в виду под a, было выбрано с b 3 раза.   -  person yh6    schedule 04.03.2021
comment
@ yh6 между валсами, да.   -  person k1rgas    schedule 04.03.2021
comment
@ k1rgas Так, например, для id = 1 у нас есть a, b, c, d, e как vals, затем мы создаем все возможные парные комбинации между этими 5 символами. И мы повторяем эту процедуру по всем идентификаторам и, наконец, подсчитываем количество шаблонов пар. Это правильно?   -  person yh6    schedule 04.03.2021
comment
@ yh6 что-то вроде этого, но мы хотим подсчитать количество шаблонов для всех идентификаторов, а не только для id1, id2 и т. д. Например, id == 1 выбрал a с b, c, d. Сколько других идентификаторов выбрали такие же варианты?   -  person k1rgas    schedule 04.03.2021
comment
@ k1rgas Да, именно это я имел в виду, говоря о количестве пар паттернов. Например, пара (a, b) появляется с идентификаторами 1, 2 и 5, поэтому вы хотите получить 3 для этой пары, не так ли?   -  person yh6    schedule 04.03.2021
comment
@ yh6 да, вы все правильно поняли. Я просто уточнял наверняка   -  person k1rgas    schedule 04.03.2021
comment
Я видел, что arules были предложены в комментарии к ваш предыдущий пост. В сообщении, на которое я ссылался, вы обнаружите, что код arules довольно простой и быстрый (по крайней мере, по сравнению с другими ответами, предоставленными там). Удачи! Ваше здоровье   -  person Henrik    schedule 04.03.2021
comment
Также для пар: m = crossprod(table(example)); m[lower.tri(m, diag = TRUE)] = NA; na.omit(data.frame(as.table(m))); Пересекайте все возможные комбинации элементов списка   -  person Henrik    schedule 05.03.2021
comment
@Henrik да, спасибо, но apriori не работает с моим исходным набором данных с более чем 9 тыс. Строк. R просто не хватает памяти.   -  person k1rgas    schedule 06.03.2021
comment
Хорошо, тогда должны быть некоторые особенности ваших данных / желаемого анализа. Как вы видите по ссылке, я запустил 10000 клиентов, предлагая до 10 товаров каждый за 60 миллисекунд ... В любом случае: удачи!   -  person Henrik    schedule 06.03.2021
comment
@Henrik да, я вижу, что ваш код очень быстрый. Но у меня не получается выделить вектор большого размера (64 Мб). И это происходит, когда я пытаюсь преобразовать результат функции apriori в фрейм данных. У вас есть идеи, как с этим справиться?   -  person k1rgas    schedule 09.03.2021
comment
В моем примере я установил значение минимальной поддержки элемента равным 0: support = 0, а затем удалил наборы элементов с нулевым счетчиком после, который я принудил к фрейму данных. Если вы установите для support, скажем, значение по умолчанию 0,1, вы исключите все наборы элементов с поддержкой 0 уже на этапе apriori. Это, конечно, уменьшит размер результатов. Вы можете попробовать. См. Также красивую виньетку. Удачи!   -  person Henrik    schedule 09.03.2021


Ответы (2)


Это будет (не может) быть быстрым для многих идентификаторов. Если он слишком медленный, вам нужно распараллелить или реализовать его на скомпилированном языке (например, с помощью Rcpp).

Сортируем vals. Затем мы можем создать всю комбинацию из двух элементов, сгруппированных по идентификатору. Мы исключаем идентификаторы с 1 предметом. Наконец, мы заносим в таблицу результат.

library(data.table)
setDT(example)
setorder(example, id, vals)
example[, if (.N > 1) split(combn(vals, 2), 1:2), by = id][, .N, by = c("1", "2")]
#    1 2 N
# 1: a b 3
# 2: a c 2
# 3: a d 3
# 4: a e 1
# 5: b c 2
# 6: b d 2
# 7: b e 1
#<...>
person Roland    schedule 04.03.2021
comment
Похоже, твой совет верен. Но я действительно не понимаю, как работает этот код. Объясните, пожалуйста, что мы делаем в первых и вторых квадратных скобках? Мол, если id выбрал более одного варианта, мы разбиваем все варианты попарно и видим их комбинации? - person k1rgas; 04.03.2021
comment
Я считаю, что мой ответ объясняет, что делает код. combn возвращает матрицу (в данном случае с двумя строками). Я разделяю строки так, чтобы [.data.table получил список, который он автоматически рассматривал как столбцы. Возможно, вам потребуется изучить хотя бы вводную часть таблицы data.table. .N - это количество строк в каждой группе, как определено в by. - person Roland; 04.03.2021

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

library(tidyverse)
example <- data.frame(
  id = c(1,1,1,1,1,2,2,2,3,4,4,4,4,4,4,4,5,5,5,5),
  vals = c("a","b",'c','d','e','a','b','d','c','d','f','g','h','a','k','l','m','a','b', 'c')
)
example %>% nest(dataset=-id) %>% mutate(dataset=map(dataset, function(dataset){
  if(nrow(dataset)>1){
    dataset %>% .$vals %>% combn(., 2) %>% t() %>% as_tibble(.name_repair=~c("val1", "val2")) %>% return()
  }else{
    return(NULL)
  }
})) %>% unnest(cols=dataset) %>% group_by(val1, val2) %>% summarize(n=n(), .groups="drop") %>% arrange(desc(n), val1, val2)
#> # A tibble: 34 x 3
#>    val1  val2      n
#>    <chr> <chr> <int>
#>  1 a     b         3
#>  2 a     c         2
#>  3 a     d         2
#>  4 b     c         2
#>  5 b     d         2
#>  6 a     e         1
#>  7 a     k         1
#>  8 a     l         1
#>  9 b     e         1
#> 10 c     d         1
#> # … with 24 more rows

Создано 2021-03-04 пакетом REPEX (v1.0.0)

person yh6    schedule 04.03.2021