我试图弄清楚参加课程的学生人数,能够参加课程的学生,并非所有学校都提供计算机,不同的学校提供英语,能够学习计算和英语的学生会有所不同。例如。使用下面的测试数据,我们有:
df <- read.csv(text="school, student, course, result
URN1,stu1,comp,A
URN1,stu2,comp,B
URN1,stu3,comp,C
URN1,stu1,Eng,D
URN1,stu1,ICT,E
URN2,stu4,comp,A
URN1,stu1,ICT,B
URN2,stu5,comp,C
URN3,stu6,comp,D
URN3,stu6,ICT,E
URN4,stu7,Eng,E
URN4,stu8,ICT,E
URN4,stu8,Eng,E
URN5,stu9,comp,E
URN5,stu10,ICT,E")
[1]&#34;由58.3333333333333%的可能学生参加,#34;
[1]&#34; 33.333333333333333%的可能学生和#34;
[1]&#34;信息通信技术由38.4615384615385%的可能学生参加&#34;
我有以下循环(嘘!)来执行此操作:
library(magrittr)
library(dplyr)
for(c in unique(df$course)){
# c <- "comp"
#get URNs of schools offering each course
URNs <- df %>% filter(course == c) %>% distinct(school) %$% school
#get number of students in each school offering course c
num_possible <- df %>% filter(school %in% URNs) %>% summarise(n = n()) %$% n
#get number of students taking course c
num_actual <- df %>% filter(course == c) %>% summarise(n = n()) %$% n
# get % of students taking course from those who could theoretically take c
print(paste(c, "taken by", (100 * num_actual/num_possible), "% of possible students"))
}
但是想要将它全部矢量化,但是,我无法将num_possible与num_actual相同的函数:
df %>% group_by(course) %>% summarise(num_possible = somesubfunction(),
num_actual = n())
somesubfunction()应该返回可能参加课程的学生人数
答案 0 :(得分:5)
如果您热衷于尝试与dplyr不同的内容,可以尝试使用data.table:
library(data.table)
setDT(df)[, nb_stu:=.N, by=course] # how many students by course
df[, nb_stu_ec:=length(unique(student)), by=school] # how many students per school (!: Edited to avoid counting some students twice if they take multiple courses)
# finally compute the number of student for a course
# divided by the number of students in the schools that have this course (sprintf is only for formating the result):
df[, sprintf("%.2f", 100*first(nb_stu)/sum(nb_stu_ec[!duplicated(school)])), by=course]
# course V1
#1: comp 87.50
#2: Eng 60.00
#3: ICT 62.50
Nota Bene: 如果仅在最后一步计算每门课程的学生人数,则可以少一步实现同样的目标:
setDT(df)[, nb_stu_ec:=length(unique(student)), by=school]
df[, sprintf("%.2f", 100*(.N)/sum(nb_stu_ec[!duplicated(school)])), by=course]
# course V1
#1: comp 87.50
#2: Eng 60.00
#3: ICT 62.50
答案 1 :(得分:4)
您可以先创建辅助数据框,然后在其上进行映射以获取可能的学生数。考虑一下这个
school_students <- df %>%
group_by(school) %>%
summarise(students = n(), courses = paste0(unique(course), collapse = ", "))
df %>%
count(course) %>%
mutate(possible = map_int(as.character(course),
~sum(school_students[str_detect(school_students$courses, .), "students"]))) %>%
mutate(pct = n / possible * 100)
# A tibble: 3 x 4
course n possible pct
<fct> <int> <int> <dbl>
1 comp 7 12 58.3
2 Eng 3 9 33.3
3 ICT 5 13 38.5
答案 2 :(得分:4)
另一个简短的dplyr
答案。加入课程和学校概述,然后是一个简单的summarise
。
library(dplyr)
left_join(
count(df, course),
df %>% group_by(school) %>% transmute(s = n(), course) %>% distinct()
) %>%
group_by(course) %>%
summarise(actual = first(n),
total = sum(s),
perc = actual / total * 100)
这再现了你的答案:
# A tibble: 3 x 4 course actual total perc <fct> <int> <int> <dbl> 1 comp 7 12 58.3 2 Eng 3 9 33.3 3 ICT 5 13 38.5
但可能,你不想重复计算学生,所以寻找不同的学生:
left_join(
count(df, course),
df %>% group_by(school) %>% transmute(s = n_distinct(student), course) %>% distinct()
) %>%
group_by(course) %>%
summarise(actual = first(n),
total = sum(s),
perc = actual / total * 100)
# A tibble: 3 x 4 course actual total perc <fct> <int> <int> <dbl> 1 comp 7 8 87.5 2 Eng 3 5 60.0 3 ICT 5 8 62.5
答案 3 :(得分:3)
我首先要计算一个频率表 - 你不需要在这里使用原始数据:
false
然后,您可以使用ft <- with(df, as.matrix(table(school, course)))
# converting table to matrix to make it easier to handle
或for
或sapply
进行分割和乘法:
apply
或者(这是一个简单的问题,所以使用多行是浪费行):
sapply(1:ncol(ft), function(x) {
k <- ft[, x]
sum(k) / sum(ft[k!=0,])*100
})
答案 4 :(得分:2)
您正在总结不同的变量级别。可能的学生数量总结在学校层面,而实际学生的数量总结在课程级别,其中级别没有嵌套在其中。
因此,我发现创建两个不同的数据帧然后将它们连接在一起更容易,但我还将提供一个解决方案,在一个长调用中执行。
library(dplyr)
首先总结一下学校层面的数据:
df_school <- df %>%
group_by(school) %>%
summarise(n_students_school = n_distinct(student))
df_school
# A tibble: 5 x 2
# school n_students_school
# <fct> <int>
# 1 URN1 3
# 2 URN2 2
# 3 URN3 1
# 4 URN4 2
# 5 URN5 2
要为每所学校选择可能的课程,请使用原始df的left_join
,但仅使用distinct
和school
的{{1}}组合:
course
计算每门课程的实际学生人数:
df_possible <- df %>%
select(school, course) %>%
distinct() %>%
left_join(df_school, by = "school") %>%
group_by(course) %>%
summarise(n_possible = sum(n_students_school))
df_possible
# A tibble: 3 x 2
# course n_possible
# <fct> <int>
# 1 comp 8
# 2 Eng 5
# 3 ICT 8
将两个数据框加入到最终数据框中,计算出课程中学生的百分比:
df_actual <- df %>%
group_by(course) %>%
summarise(n_actual = n_distinct(student))
df_actual
# A tibble: 3 x 2
# course n_actual
# <fct> <int>
# 1 comp 7
# 2 Eng 3
# 3 ICT 4
或者在不同分组的长途电话中(积分转到@alistaire):
df_final <- left_join(df_possible, df_actual, by = "course") %>%
mutate(percentage = n_actual/n_possible)
df_final
# A tibble: 3 x 4
# course n_possible n_actual percentage
# <fct> <int> <int> <dbl>
# 1 comp 8 7 0.875
# 2 Eng 5 3 0.600
# 3 ICT 8 4 0.500
此处通过将df %>%
group_by(school) %>%
group_by(school, course, n_students_school = n_distinct(student)) %>%
summarise(n_students_course = n_distinct(student)) %>%
group_by(course) %>%
summarise(n_possible = sum(n_students_school),
n_actual = sum(n_students_course),
percentage = n_actual / n_possible)
添加到群组通话中,它会在每个课程的学生总结时创建,然后不会被删除。
答案 5 :(得分:1)
我知道这必须简化,但这是使用dplyr执行此操作的一种方法:
# load necessary packages
library( dplyr )
# calculate stats
df %>%
group_by( school ) %>%
summarise( Total_Students = n()
, Offer_Comp = "comp" %in% unique( course )
, Offer_English = "Eng" %in% unique( course )
, Offer_ICT = "ICT" %in% unique( course )
, Comp_Taken = ifelse( test = Offer_Comp == TRUE
, yes = length( course[ which( course == "comp" ) ] )
, no = NA
)
, English_Taken = ifelse( test = Offer_English == TRUE
, yes = length( course[ which( course == "Eng" ) ] )
, no = NA
)
, ICT_Taken = ifelse( test = Offer_ICT == TRUE
, yes = length( course[ which( course == "ICT" ) ] )
, no = NA
) ) %>%
summarise( Comp_Possible = sum( Total_Students[ which( Offer_Comp == TRUE ) ] )
, Comp_Taken_Count = sum( Comp_Taken, na.rm = TRUE )
, Comp_Taken_Per = Comp_Taken_Count / Comp_Possible * 100
, English_Possible = sum( Total_Students[ which( Offer_English == TRUE ) ] )
, English_Taken_Count = sum( English_Taken, na.rm = TRUE )
, English_Taken_Per = English_Taken_Count / English_Possible * 100
, ICT_Possible = sum( Total_Students[ which( Offer_ICT == TRUE ) ] )
, ICT_Taken_Count = sum( ICT_Taken, na.rm = TRUE )
, ICT_Taken_Per = ICT_Taken_Count / ICT_Possible * 100 )
# final output
# A tibble: 1 x 9
# Comp_Possible Comp_Taken_Count Comp_Taken_Per English_Possible English_Taken_Count English_Taken_Per ICT_Possible ICT_Taken_Count ICT_Taken_Per
# <int> <int> <dbl> <int> <int> <dbl> <int> <int> <int>
# 1 12 7 58.3 9 3 33.3 13 5 38.5
# end of script #
答案 6 :(得分:0)
这会重现您的预期输出,但可能会错误,因为您会多次计算学生:
df %>%
count(school,course) %>%
left_join(df %>% count(school),by="school") %>%
group_by(course) %>%
summarize(num_actual = sum(n.x), pc = num_actual/sum(n.y))
# # A tibble: 3 x 3
# course num_actual pc
# <fctr> <int> <dbl>
# 1 comp 7 0.5833333
# 2 Eng 3 0.3333333
# 3 ICT 5 0.3846154
这将是更正的答案:
df %>%
count(school,course) %>%
left_join(distinct(df[c("school","student")]) %>% count(school),by="school") %>%
group_by(course) %>%
summarize(num_actual = sum(n.x), pc = num_actual/sum(n.y))
# # A tibble: 3 x 3
# course num_actual pc
# <fctr> <int> <dbl>
# 1 comp 7 0.875
# 2 Eng 3 0.600
# 3 ICT 5 0.625