使用group_by搭配用户自定义函数

编程语言 2026-07-11

我写了一个函数来计算有多少学生升入高等教育(HE)的比例。现在我想以不同的方式筛选和分组我的数据,并计算每个分组中升入高等教育的比例。

同一个学生在列表中会出现多次,同时也有尚未参与的学生,这些人不应被计入。

example <- tibble(Student.ID = c("#001","#002","#003","#004","#005"),
               Student.Attended = c("Yes", "Yes", "No", "Yes", "Yes"), 
               entryby19 = c("Yes", "Yes", "Yes", "Yes", "No"),
               Type = c("Exhibition", "Exhibition", "Mentoring", "Mentoring", "Mentoring"))



progressionby19 <- function(.data) {

  total <- .data %>%  
    filter(Student.Attended == "Yes") %>% 
    summarise(count = n_distinct(Student.ID))

  progressed <- .data %>%  
    filter(Student.Attended == "Yes") %>% 
    filter(entryby19 == "Yes") %>% 
    summarise(count = n_distinct(Student.ID))

  (progressed/total)*100

} 

progressionby19(example)


example %>% 
  group_by(Type) %>% 
  summarise(learner_count = n_distinct(Student.ID),
            progression_rate = progressionby19(.))

然而,当我接着对数据进行分组时,出现了一个错误。我花了不少时间来寻找解决方法,并把下面的代码添加到了函数中(大致是直接从我找到的地方复制粘贴过来)。我并不百分之百理解它在做什么,但以这种方式运行时,几乎能工作——只是好像把进展率与其他部分分开分组了。

progressionby19 <- function(.data) {

  if (dplyr::is_grouped_df(.data)) {
    return(dplyr::do(.data, progressionby19(.)))
  }

  total <- .data %>%  
    filter(Student.Attended == "Yes") %>% 
    summarise(count = n_distinct(Student.ID))

  progressed <- .data %>%  
    filter(Student.Attended == "Yes") %>% 
    filter(entryby19 == "Yes") %>% 
    summarise(count = n_distinct(Student.ID))

  (progressed/total)*100

}

progressionby19(example)


example %>% 
  group_by(Type) %>% 
  summarise(learner_count = n_distinct(Student.ID),
            progression_rate = progressionby19(.))

有人能帮我把它理清楚吗?

解决方案

我不太清楚你期望的结果具体是怎样的,但也许这对你会有帮助。正如我在第一条注释中所建议的,dplyrgroup_xxxx 家族函数很可能是要走的方向。在这种情况下,group_modify 可能比 group_map 稍微高效一些。(group_map 仍然可以工作,但你需要在管道中添加 bind_rows 才能得到相同的结果。)

The group_xxxx 家族的函数将一个由 group_xxx 的参数定义的函数应用于分组数据框的每个分组。若数据框未分组,则该函数应用于整个数据框。

用户提供的函数需要定义两个参数,通常命名为 .x.y.x 是输入数据框的当前分组。.y 是一个包含当前分组定义的单行数据框。我认为这里不需要使用 .y,但我们仍然需要对它进行定义。

因此,在尽量少改动你的函数的前提下(虽然如果是我来写,还会做其他修改……),你可以写成

library(tidyverse)

progressionby19 <- function(.x, .y) {
  total <- .x %>%  
    filter(Student.Attended == "Yes") %>% 
    summarise(count = n_distinct(Student.ID))

  progressed <- .x %>%  
    filter(
      Student.Attended == "Yes",
      entryby19 == "Yes"
    ) %>% 
    summarise(count = n_distinct(Student.ID))

  total %>% mutate(progression_rate = (progressed$count/total$count)*100)
}

然后使用 group_modify 来应用该函数:

example %>% 
  group_by(Type) %>% 
  group_modify(progressionby19)

得到

# A tibble: 2 × 3
# Groups:   Type [2]
  Type       count progression_rate
  <chr>      <int>            <dbl>
1 Exhibition     2              100
2 Mentoring      2               50

这有点接近你想要的结果吗?如果不是,你确实需要提供更多信息……

附言

下面的函数给出的结果与上面的相同,但在我看来更易于理解。

progressionby19 <- function(.x, .y) {
  .x %>%  
    filter(Student.Attended == "Yes") %>% 
    summarise(
      count = n(),
      progression_rate = 100 * mean(entryby19 == "Yes")
    )
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章