如何根据具有名称类型的变量在 R 中过滤和计数

Posted

技术标签:

【中文标题】如何根据具有名称类型的变量在 R 中过滤和计数【英文标题】:How to filter and count in R based on variables with a type of name 【发布时间】:2021-09-13 14:15:33 【问题描述】:

我在 R 中有如下教育数据:

df <- data.frame(
   "StudentID" = c(101, 102, 103, 104, 105, 106, 111, 112, 113, 114, 115, 116, 121, 122, 123, 124, 125, 126),
   "FedEthn" = c(1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 3, 3),
   "HIST.11.LEV" = c(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 5, 3, 3),
   "HIST.11.SCORE" = c(96, 95, 95, 97, 88, 99, 89, 96, 79, 83, 72, 95, 96, 93, 97, 98, 96, 87),
   "HIST.12.LEV" = c(2, 2, 1, 2, 1, 1, 2, 3, 2, 2, 2, 2, 4, 3, 3, 3, 3, 3),
   "SCI.9.LEV" = c(1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3),
   "SCI.9.SCORE" = c(91, 99, 82, 95, 65, 83, 96, 97, 99, 94, 95, 96, 89, 78, 96, 95, 97, 90),
   "SCI.10.LEV" = c(1, 2, 1, 2, 1, 1, 3, 3, 2, 2, 2, 3, 3, 3, 4, 3, 4, 3)
)

##    StudentID  FedEthn  HIST.11.LEV  HIST.11.SCORE  HIST.12.LEV  SCI.9.LEV  SCI.9.SCORE  SCI.10.LEV
## 1        101        1            1             96            2          1           91           1
## 2        102        1            1             95            2          1           99           2
## 3        103        2            1             95            1          1           82           1
## 4        104        2            1             97            2          1           95           2
## 5        105        3            1             88            1          1           65           1
## 6        106        3            1             99            1          1           83           1
## 7        111        1            2             89            2          2           96           3
## 8        112        1            2             96            3          2           97           3
## 9        113        2            2             79            2          2           99           2
## 10       114        2            2             83            2          2           94           2
## 11       115        3            2             72            2          2           95           2
## 12       116        3            2             95            2          2           96           3
## 13       121        1            3             96            4          3           89           3
## 14       122        1            3             93            3          3           78           3
## 15       123        2            3             97            3          3           96           4
## 16       124        2            3             98            3          3           95           3
## 17       125        3            3             96            3          3           97           4
## 18       126        3            3             87            3          3           90           3

HIST.11.LEV 代表学生在 11 年级历史课程中的学术水平。 (5 = 最高学术水平,1 = 最低学术水平。例如,5 可能是 AP 或 IB 课程。)HIST.11.SCORE 表示学生在课程中的分数。

当学生在一门课程中获得 95 分或更高分时,他们就有资格在下一年升读更高的学术水平(例如 HIST.12.LEV = 1 + HIST.11.LEV)。然而,这些符合条件的学生中只有一部分真正升迁,老师必须同意。我正在分析的是,符合条件的学生的升学率是否因报告的联邦种族而异。

到目前为止,我是这样实现的:

var.level <- 1
var.ethn <- 1

actual.move.ups <- 
  (df %>% filter(FedEthn==var.ethn,
                 HIST.11.LEV==var.level,
                 HIST.11.SCORE>94,
                 HIST.12.LEV==var.level+1) %>% 
     count) +
  (df %>% filter(FedEthn==var.ethn,
                 SCI.9.LEV==var.level,
                 SCI.9.SCORE>94,
                 SCI.10.LEV==var.level+1) %>% 
     count)

eligible.move.ups <- 
  (df %>% filter(FedEthn==var.ethn,
                 HIST.11.LEV==var.level,
                 HIST.11.SCORE>94) %>% 
     count) +
  (df %>% filter(FedEthn==var.ethn,
                 SCI.9.LEV==var.level,
                 SCI.9.SCORE>94) %>% 
     count)

这可行,我可以从 1:5 迭代 var.level 并从 1:7 迭代 var.ethnicity 并将结果存储在数据框中。但在我的实际数据中,这种方法需要 df %>% filter(...) %>% count 的 15 次迭代(我会将它们全部相加)。原因是,在我的实际数据中,有 15 个机会在 5 个科目(HIST、SCI、MATH、ENG、WL)和 4 个年级(9、10、11、12)之间升迁。

我的问题是,是否有一种更紧凑的方法来过滤和计算 COURSE.GRADE.LEV==i、COURSE.GRADE+1.LEV==i+1 和 COURSE.GRADE.SCORE>94 的所有实例键入/硬编码每个课程名称(HIST、SCI、MATH、ENG、WL)和每个年级(9、10、11、12)。而且,将结果存储在数据框中的最佳方式是什么?

对于我上面的示例数据,这是理想的输出。不过,数据框不需要具有这种精确的结构。

##    FedEthn  L1.Actual  L1.Eligible  L2.Actual  L2.Eligible  L3.Actual  L3.Eligible
## 1        1          3            3          3            3          1            1
## 2        2          2            3          0            1          1            3
## 3        3          0            1          1            3          1            2

*注意:我读过this helpful answer,但是对于我的变量名,年级(9、10、11、12)没有一致的字符串位置(例如,SCI。9 与 HIST。11)。此外,在某些情况下,我需要多次计算一行,因为一个学生可以在多个班级中升迁。也许解决方案是在执行计数之前将数据从宽调整为长?

【问题讨论】:

为了更清楚起见,我不只是获得每个种族的总总数/计数是有原因的。在某些学术水平(.LEV = 2 和 3)中,学生升迁的比例通常更高。而且种族分布在各个学术层次上也不尽相同。这就是为什么我需要在每个学术级别(.LEV = 1 到 5)重复计数。 【参考方案1】:

使用来自@akrun 的this great answer,我想出了一个解决方案。不过,我认为我仍然让它变得不必要地复杂,我希望接受其他人更简洁的答案。

course.names <- c("HIST.","SCI.")
grade.levels <- 9:11

tally.actual <- function(var.ethn, var.level)
  total.tally.actual <- NULL
  for(i in course.names)
    course.tally.actual <- NULL
    for(j in grade.levels)
      new.tally.actual <- df %>% filter(
        FedEthn == var.ethn,
        !!(rlang::sym(paste0(i,j,".LEV"))) == var.level,
        !!(rlang::sym(paste0(i,(j+1),".LEV"))) == (var.level+1),
        !!(rlang::sym(paste0(i,j,".SCORE"))) > 94
      ) %>% count
      course.tally.actual <- c(new.tally.actual, course.tally.actual)
    
    total.tally.actual <- c(total.tally.actual, course.tally.actual)
  
  return(sum(unlist(total.tally.actual)))


tally.eligible <- function(var.ethn, var.level)
  total.tally.eligible <- NULL
  for(i in course.names)
    course.tally.eligible <- NULL
    for(j in grade.levels)
      new.tally.eligible <- df %>% filter(
        FedEthn == var.ethn,
        !!(rlang::sym(paste0(i,j,".LEV"))) == var.level,
        !!(rlang::sym(paste0(i,j,".SCORE"))) > 94
      ) %>% count
      course.tally.eligible <- c(new.tally.eligible, course.tally.eligible)
    
    total.tally.eligible <- c(total.tally.eligible, course.tally.eligible)
  
  return(sum(unlist(total.tally.eligible)))


results <- data.frame("FedEthn" = 1:3, 
                      "L1.Actual" = NA, "L1.Eligible" = NA, 
                      "L2.Actual" = NA, "L2.Eligible" = NA, 
                      "L3.Actual" = NA, "L3.Eligible" = NA)

for(var.ethn in 1:3)
  for(var.level in 1:3)
    results[var.ethn,(var.level*2)] <- tally.actual(var.ethn,var.level)
    results[var.ethn,(var.level*2+1)] <- tally.eligible(var.ethn,var.level)
  

这种方法有效,但它要求 df 包含课程(SCI、MATH、HIST、ENG、WL)和年份(9、10、11、12)的所有组合。请参阅下文了解我如何添加到原始 df。包括所有可能的组合对我的实际数据来说不是问题,但我希望有一个解决方案不需要添加一堆用 NA 填充的列:

df$HIST.9.LEV = NA
df$HIST.9.SCORE = NA
df$HIST.10.LEV = NA
df$HIST.10.SCORE = NA
df$HIST.12.SCORE = NA
df$SCI.10.SCORE = NA
df$SCI.11.LEV = NA
df$SCI.11.SCORE = NA
df$SCI.12.LEV = NA
df$SCI.12.SCORE = NA

【讨论】:

以上是关于如何根据具有名称类型的变量在 R 中过滤和计数的主要内容,如果未能解决你的问题,请参考以下文章

R:根据OR条件创建具有多个级别的新变量[重复]

具有变量计数和类型参数的函数指针?

在R中,如何使用dplyr按数据类型过滤数据帧?

如何创建具有 1 个自变量和 3 个因变量的计数和百分比表和折线图

如何在 C 中取消设置变量以允许稍后使用具有不同数据类型的相同名称?

如何遍历具有相同名称形式的变量数组? [关闭]