我有一个表,其中每一行代表一名学生在一门课程中的注册情况,与此类似,但要大得多:
学生 | 课程 |
---|---|
001 | PSYC101 |
001 | 化学102 |
002 | PSYC101 |
002 | SPAN101 |
002 | BIO101 |
003 | BIO101 |
003 | ENG201 |
003 | HIND101 |
003 | 化学102 |
004 | PSYC101 |
004 | 化学102 |
004 | HIND101 |
我想知道学生最常一起选修哪些课程组合。我从 2 道菜组合开始,但也可能想看看 3 道菜组合。
我完全不知道这个程序的名称。
我发现这个使用
dplyr
包的类似示例:https://stackoverflow.com/questions/61613192/r-how-to-find-the-most-frequent-combinations
但是,我不认为这正是我想要的。当某些学生修读了 2 门以上课程时,我想要所有可能的 2 门课程组合。例如,对于学生 3,他们将具有以下内容:
然后,我会找到所有学生中最常见的组合。
基本 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)
您可以过滤掉行数少于感兴趣的
m
组合的学生,使用 combn()
生成组合,然后将其转置并进行行排序,解压小标题,统计结果,然后降序排序。
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
另一种
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