如何识别组内最常见的值组合?

6
我有一张表,每一行代表一个学生在一门课程中的注册情况,类似于这个例子,但规模要大得多:
学生 课程
001 PSYC101
001 CHEM102
002 PSYC101
002 SPAN101
002 BIO101
003 BIO101
003 ENG201
003 HIND101
003 CHEM102
004 PSYC101
004 CHEM102
004 HIND101
我想知道学生们最常一起选修哪些课程的组合。我先从两门课程的组合开始,但也可能想看看三门课程的组合。
我完全不知道这个过程叫什么。
我找到了一个类似的例子,使用了`dplyr`包:https://stackoverflow.com/questions/61613192/r-how-to-find-the-most-frequent-combinations 然而,我不认为这正是我想要的。我想要所有可能的两门课程的组合,当一些学生选修了超过两门课程时。例如,对于学生3,他们会有以下组合:
- BIO101 & ENG201 - BIO101 & HIND101 - BIO101 & CHEM102 - ENG201 & HIND101 - ENG201 & CHEM102 - HIND101 & CHEM102
然后,我会找出所有学生中最常见的组合。

对于双向绑定,请查看此答案。学生就像沙拉,而课程就是其中的配料。 - undefined
然而,当我运行这段代码时,我收到一个错误消息。请问是什么错误?代码在哪里?请参考[mcve]获取更多信息。 - undefined
4个回答

7
一个基本的R方法是使用combn(选择2个元素)来创建每个特定学生课程的组合,然后使用table来查看这些对在整个人群中出现的频率。
我将在下面放置组合代码,然后通过将其分解为更可读/可理解的方式来解释其中的内容。
如果您想查看3个课程选项,请将combn(x$Course, 2)更改为combn(x$Course, 3)以选择三个组合。
table(apply(do.call(cbind, lapply(split(df, df$Student), \(x)
                                  combn(x$Course, 2))), 2, \(x) paste(sort(x), collapse = "_")))

输出

# BIO101_CHEM102   BIO101_ENG201  BIO101_HIND101 CHEM102_HIND101  ENG201_CHEM102  ENG201_HIND101 
#               1               1               1               1               1               1 

# HIND101_CHEM102  PSYC101_BIO101 PSYC101_CHEM102 PSYC101_HIND101 PSYC101_SPAN101  SPAN101_BIO101 
#               1               1               2               1               1               1 

首先,使用split将数据框(df)按照每个学生拆分成一个列表,然后找到课程的组合,再将组合合并并粘贴在一起,最后制作了一个表格。
# Split by student
ll <- split(df, df$Student)

# find combinations of courses
combs_list <- lapply(ll, \(x) combn(unique(x$Course), 2))

# combine it into a matrix
combs_combined <- do.call(cbind, combs_list)

# paste combinations together
apply_combined <- apply(combs_combined, 2, \(x) paste(sort(x), collapse = "_"))

#make a table
table(apply_combined)

请注意,如果您希望将其放入数据框中,只需将表格包装起来即可。
data.frame(tt)

# or to order by frequency:
data.frame(tt)[order(tt, decreasing = TRUE),]

#     apply_combined Freq
# 7  CHEM102_HIND101    2
# 8  CHEM102_PSYC101    2
# 1   BIO101_CHEM102    1
# 2    BIO101_ENG201    1
# 3   BIO101_HIND101    1
# ...

数据
df <- read.table(text = "Student    Course
001 PSYC101
001 CHEM102
002 PSYC101
002 SPAN101
002 BIO101
003 BIO101
003 ENG201
003 HIND101
003 CHEM102
004 PSYC101
004 CHEM102
004 HIND101", header = TRUE)

6
你可以过滤掉行数少于感兴趣的组合数 m 的学生,使用 combn() 生成组合,然后进行转置和行排序,展开 tibble,统计结果,最后按降序排序。
library(dplyr)
library(tidyr)

n <- 2

dat %>%
  filter(n() >= n, .by = Student) %>%
  reframe(x = t(combn(Course, n)) %>%
            {matrix(.[order(row(.), .)], ncol = ncol(.), byrow = TRUE)} %>%
            as_tibble(), .by = Student) |>
  unpack(x) |>
  count(across(-Student)) |>
  arrange(-n)

# A tibble: 11 × 3
   V1      V2          n
   <chr>   <chr>   <int>
 1 CHEM102 HIND101     2
 2 CHEM102 PSYC101     2
 3 BIO101  CHEM102     1
 4 BIO101  ENG201      1
 5 BIO101  HIND101     1
 6 BIO101  PSYC101     1
 7 BIO101  SPAN101     1
 8 CHEM102 ENG201      1
 9 ENG201  HIND101     1
10 HIND101 PSYC101     1
11 PSYC101 SPAN101     1

对于 n = 3,你将得到:
# A tibble: 6 × 4
  V1      V2      V3          n
  <chr>   <chr>   <chr>   <int>
1 BIO101  CHEM102 ENG201      1
2 BIO101  CHEM102 HIND101     1
3 BIO101  ENG201  HIND101     1
4 BIO101  PSYC101 SPAN101     1
5 CHEM102 ENG201  HIND101     1
6 CHEM102 HIND101 PSYC101     1

4
另一种使用`dplyr`和`combn`的方法:
library(dplyr)
df |> 
  arrange(Student, Course) |> 
  reframe(Course_comb = combn(seq_along(Student), 2, function(i) paste(Course[i], collapse = " & ")), .by = Student) |> 
  count(Course_comb, sort = TRUE)

#          Course_comb n
# 1  CHEM102 & PSYC101 2
# 2  CHEM102 & HIND101 2
# 3   BIO101 & PSYC101 1
# 4   BIO101 & SPAN101 1
# 5  PSYC101 & SPAN101 1
# 6   BIO101 & CHEM102 1
# 7    BIO101 & ENG201 1
# 8   BIO101 & HIND101 1
# 9   CHEM102 & ENG201 1
# 10  ENG201 & HIND101 1
# 11 HIND101 & PSYC101 1

我可以让它运行起来,但结果不是Course_comb,而是每一行都是<named list [1]>。我已经进行了一些搜索,但找不到任何修复这个问题的方法。有任何想法为什么它不显示实际的Course_comb文本? - undefined
1
好的。我进行了编辑,以避免这个问题,即在combn中使用了list - undefined
1
谢谢!这个选项对于3和4种组合效果很好。 - undefined

0
我更喜欢以图形为主的方法...
df <- scale(table(df[,2], df[,1]))
# calculate distance between Courses
d <- dist(df, method = "euclidean")
# Hierarchical clustering 
clustered <- hclust(d, method = "complete" )
# Plot dendrogram
plot(clustered)

enter image description here

# load sample data
df <- read.table(text = "Student    Course
001 PSYC101
001 CHEM102
002 PSYC101
002 SPAN101
002 BIO101
003 BIO101
003 ENG201
003 HIND101
003 CHEM102
004 PSYC101
004 CHEM102
004 HIND101", header = TRUE)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接