Как узнать, сколько раз были куплены предметы и их комбинации? - PullRequest
3 голосов
/ 13 июня 2019

У меня есть data.table, показывающий, какие товары покупал клиент. Каждая строка представляет одного клиента, а каждый столбец - один элемент. Таблица имеет одинаковое количество столбцов для каждого клиента, а значения в столбцах item* равны 1 или 0 в зависимости от того, приобрел ли клиент данный товар. Простая версия таблицы выглядит так:

data.table(customerID = c(1,2,3,4,5),
           item1 = c(1,0,0,1,1),
           item2 = c(1,0,1,1,1),
           item3 = c(1,0,0,0,1),
           item4 = c(0,1,1,1,1))

В таблице указано, что покупатель 1 приобрел товары 1,2,3, а покупатель 3 - покупатели 1 и 5.

В реальном случае data.table имеет так много столбцов, что было бы нецелесообразно ссылаться на них по имени в коде, но было бы хорошо иметь вместо этого данные в длинном формате.

Мне нужно выяснить, сколько раз отдельные предметы были куплены и сколько раз были куплены их комбинации. В этом случае я хотел бы получить что-то вроде:

item1 3
item2 4
item3 2
item4 4
item1;item2 3
item1;item3 2
item1;item4 1
...
(same for other combinations of length 2)
...
item1;item2;item3 2
item1;item2;item4 1

...
up to combinations of 4 items.

Более того, для каждого покупателя мне понадобится таблица с указанием комбинаций продуктов, которые он или она приобрели.

Edit:

Благодаря трем очень полезным ответам я знаю, как ответить на первую часть вопроса - т.е. подсчитать, сколько клиентов приобрели определенную комбинацию. Однако вторая часть остается без ответа. Я хотел бы знать, какие клиенты купили какую комбинацию.

Ответы [ 3 ]

3 голосов
/ 13 июня 2019

Пошаговый подход с использованием baseR и data.table

пример данных

DT <- data.table(customerID = c(1,2,3,4,5),
           item1 = c(1,0,0,1,1),
           item2 = c(1,0,1,1,1),
           item3 = c(1,0,0,0,1),
           item4 = c(0,1,1,1,1))

код

#identify columns with items, grab their names
cols <- names(DT[,-1])

в приведенном ниже коде: установите 1:length(cols) на 1:n, если вы хотите комбинации максимум из n продуктов

#put all combinations of items in a list
combos <- unlist( lapply( 1:length(cols), combn, x = cols, simplify = FALSE ), recursive = FALSE )

#calculate number of sold items per combo
l <- lapply( combos, function(x) {
  nrow( DT[ rowSums( DT[, x, with = FALSE ] ) == length( x ), ] )
})

#name the list based on the combo
names(l) <- lapply( combos, paste0, collapse = ";")

выход

str( l )
List of 15
$ item1                  : int 3
$ item2                  : int 4
$ item3                  : int 2
$ item4                  : int 4
$ item1;item2            : int 3
$ item1;item3            : int 2
$ item1;item4            : int 2
$ item2;item3            : int 2
$ item2;item4            : int 3
$ item3;item4            : int 1
$ item1;item2;item3      : int 2
$ item1;item2;item4      : int 2
$ item1;item3;item4      : int 1
$ item2;item3;item4      : int 1
$ item1;item2;item3;item4: int 1

или создайте таблицу data.table

as.data.table( as.matrix( unlist(l), ncol = 2, nrow = length(l) ), keep.rownames = TRUE )
#                         rn V1
# 1:                   item1  3
# 2:                   item2  4
# 3:                   item3  2
# 4:                   item4  4
# 5:             item1;item2  3
# 6:             item1;item3  2
# 7:             item1;item4  2
# 8:             item2;item3  2
# 9:             item2;item4  3
#10:             item3;item4  1
#11:       item1;item2;item3  2
#12:       item1;item2;item4  2
#13:       item1;item3;item4  1
#14:       item2;item3;item4  1
#15: item1;item2;item3;item4  1
1 голос
/ 13 июня 2019

Это полностью базовая опция R, поэтому преобразование данных в фрейм данных

df <- data.frame(df)
unique_product <- names(df[-1])

stack(unlist(sapply(seq_along(unique_product), function(x) 
     combn(unique_product, x, FUN = function(y) 
           setNames(sum(rowSums(df[y] == 1) == length(y)), 
            paste0(y, collapse = ";")), simplify = FALSE))))


#   values                     ind
#1       3                   item1
#2       4                   item2
#3       2                   item3
#4       4                   item4
#5       3             item1;item2
#6       2             item1;item3
#7       2             item1;item4
#8       2             item2;item3
#9       3             item2;item4
#10      1             item3;item4
#11      2       item1;item2;item3
#12      2       item1;item2;item4
#13      1       item1;item3;item4
#14      1       item2;item3;item4
#15      1 item1;item2;item3;item4

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


Чтобы получить клиентов, которые разблокировали определенные комбинации, мы можем продолжить тот же подход

stack(unlist(sapply(seq_along(unique_product), function(x) 
     combn(unique_product, x, FUN = function(y) {
      inds <- rowSums(df[x] == 1) == length(x)
      setNames(df$customerID[inds], 
             rep(paste0(y, collapse = ";"), sum(inds)))
             }, simplify = FALSE))))

#   values                     ind
#1       1                   item1
#2       1                   item2
#3       1                   item3
#4       1                   item4
#5       1             item1;item2
#6       4             item1;item2
#7       5             item1;item2
#8       1             item1;item3
#9       4             item1;item3
#10      5             item1;item3
#....

При необходимости вы можете переименовать столбцы, но здесь values - это идентификатор клиента и ind - это комбинации, которые разблокировал соответствующий клиент.

1 голос
/ 13 июня 2019

Вот некоторый грязный код, который позволяет вам установить параметр n_items, который контролирует максимальный размер пакета:

library(magrittr)
DT_melt <- DT[, melt(.SD, id.vars = "customerID", variable.factor = FALSE)
              ][value == 1
                ][, variable := as.integer(sub("item", "", variable))]
n_items <- 4L
keep_track <- list()
for (i in seq_len(n_items)) {
  combs <- combn(seq_len(n_items), i)
  keep_track[[i]] <- apply(combs, 2, function(x)  DT_melt[, all(x %in% variable), by = customerID]) %>%
    lapply(function(x) sum(x[[2]])) %>% 
    setNames(apply(combs, 2, function(x) paste(paste0("item", x), collapse = ";")))
}
unlist(keep_track)

Возвращает именованный вектор отсчетов:

#                   item1                   item2 
#                       3                       4 
#                   item3                   item4 
#                       2                       4 
#             item1;item2             item1;item3 
#                       3                       2 
#             item1;item4             item2;item3 
#                       2                       2 
#             item2;item4             item3;item4 
#                       3                       1 
#       item1;item2;item3       item1;item2;item4 
#                       2                       2 
#       item1;item3;item4       item2;item3;item4 
#                       1                       1 
# item1;item2;item3;item4 
#                       1 
...