我有一个客户 ID 列表,每个 ID 都有一个他们使用的独特产品的列表。理论上最多可以有 150 种独特的产品。
df <- tibble(ID = c(1,1,1,2,2,3,3,4),
prod = c("Prod1", "Prod2", "Prod3", "Prod1", "Prod4", "Prod3", "Prod5", "Prod2"))
由此,我需要为每个 ID 获取所有可能的产品组合,而不仅仅是在最高级别(按 ID 分组)。也就是说,包括所有产品的组合,如 expand_grid() 所做的那样,但也包括 1,...,n 元素的所有组合,其中 n 是 ID 具有的唯一产品的数量。
最终数据集应该是这样的:
df_results <- tibble(ID = c(1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4),
combo = c("Prod1", "Prod2", "Prod3", "Prod1|Prod2", "Prod1|Prod3", "Prod2|Prod3", "Prod1|Prod2|Prod3",
"Prod1", "Prod4", "Prod1|Prod4",
"Prod3", "Prod5", "Prod3|Prod5",
"Prod2"))
library(dplyr)
df %>%
group_by(ID) %>%
reframe(combo = as.character(do.call(c, lapply(seq_along(prod), \(m) combn(x = prod, m = m, FUN = \(x) paste(x, collapse = "|"))))))
# A tibble: 14 × 2
ID combo
<dbl> <chr>
1 1 Prod1
2 1 Prod2
3 1 Prod3
4 1 Prod1|Prod2
5 1 Prod1|Prod3
6 1 Prod2|Prod3
7 1 Prod1|Prod2|Prod3
8 2 Prod1
9 2 Prod4
10 2 Prod1|Prod4
11 3 Prod3
12 3 Prod5
13 3 Prod3|Prod5
14 4 Prod2
或在基数R中:
stack(tapply(df$prod, df$ID,
\(prod) do.call(c, lapply(seq_along(prod), \(m) combn(prod, m, FUN = \(x) paste(x, collapse = "|"))))))[2:1]
这是另一个基本的 R 选项,使用
intToBits
将所有组合映射到整数索引的二进制表示
with(
df,
setNames(
rev(
stack(
by(
Prod, ID,
function(p) {
sapply(
seq(2^length(p) - 1),
function(k) paste0(p[which(intToBits(k) > 0)], collapse = "|")
)
}
)
)
), names(df)
)
)
这给
ID Prod
1 1 Prod1
2 1 Prod2
3 1 Prod1|Prod2
4 1 Prod3
5 1 Prod1|Prod3
6 1 Prod2|Prod3
7 1 Prod1|Prod2|Prod3
8 2 Prod1
9 2 Prod4
10 2 Prod1|Prod4
11 3 Prod3
12 3 Prod5
13 3 Prod3|Prod5
14 4 Prod2
如果你想探索使用
expand.grid
的可能性(但不推荐它,因为它相当低效),你可以尝试下面的代码
with(
df,
setNames(
rev(
stack(
lapply(
split(Prod, ID),
function(x) {
unique(
apply(
expand.grid(rep(list(x), length(x))),
1,
function(v) {
paste0(sort(unique(v)), collapse = "|")
}
)
)
}
)
)
), names(df)
)
)
这给
ID Prod
1 1 Prod1
2 1 Prod1|Prod2
3 1 Prod1|Prod3
4 1 Prod1|Prod2|Prod3
5 1 Prod2
6 1 Prod2|Prod3
7 1 Prod3
8 2 Prod1
9 2 Prod1|Prod4
10 2 Prod4
11 3 Prod3
12 3 Prod3|Prod5
13 3 Prod5
14 4 Prod2