在R中向量化复杂的dplyr语句
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在R中向量化复杂的dplyr语句相关的知识,希望对你有一定的参考价值。
我正在努力计算参加课程的学生数量,从那些能够参加课程的学生,并非所有学校都提供计算机,不同的学校提供英语,那些能够学习计算和英语的学生会有所不同。例如。使用下面的测试数据,我们有:
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]“由58.3333333333333%可能的学生参加”
[1]“33.3333333333333%可能的学生参加了”
[1]“信息通信技术由38.4615384615385%的可能学生参加”
我有以下循环(嘘!)来做到这一点:
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()应该返回可能参加课程的学生人数c
您可以先创建一个辅助数据框,然后映射它以获得可能的学生数。考虑一下
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
如果你热衷于尝试与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
另一个短的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
我首先计算一个频率表 - 你不需要在这里使用原始数据:
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
})
或者(这是一个简单的问题,因此使用多行是浪费行):
apply(ft, 2, function(k) sum(k) / sum(ft[k!=0,]))*100
# comp Eng ICT
# 58.33333 33.33333 38.46154
您将总结不同的变量级别。可能的学生数量总结在学校层面,而实际学生的数量总结在课程级别,其中级别没有嵌套在其中。
因此,我发现创建两个不同的数据帧然后将它们连接在一起更容易,但我还将提供一个解决方案,在一个长调用中执行它。
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
的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
将两个数据框加入到最终数据框中,计算出课程中学生的百分比:
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
或者在不同分组的长途电话中(积分转到@alistaire):
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)
在这里,通过将n_students_school = n_distinct(student)
添加到组呼,它将被创建,然后在为每个课程的学生进行总结时不会被删除。
我知道这必须简化,但这是使用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 ) ] 以上是关于在R中向量化复杂的dplyr语句的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在没有 SSE4 的情况下在 VC++ 中向量化乘法?