【问题标题】:Manipulating a column based on other column values with R dplyr package使用 R dplyr 包根据其他列值操作列
【发布时间】:2020-10-01 06:45:55
【问题描述】:

我想为每个学生选择最好的 2 个测验考试结果(最高分和最高出勤率),并消除 3 个测验考试中最弱的测验。我们可能会说,我想从每行的 3 列中选择最好的 2 列。然后新建一个数据框有StudentID, ExamQuiz1, ExamQuiz2, ExamMidterm and ExamFinal。我可以通过循环遍历表格来处理它,这在我认为 R 中效率太低了。处理 dplyr 包问题的有效方法是什么?

极简数据

伪数据框放置在底部。 “G”表示学生没有参加考试,所以我想保留该值而不是将其替换为 0。例如,如果他使用 G (ExamQuiz1)、0 (ExamQuiz2 ), 10 (ExamQuiz3),我必须选择 0 作为ExamQuiz1 和 10 作为ExamQuiz2 用于测验输入。因为出勤方面,0 比 G 好。如果有结果(带数字),则表示学生已经参加。 ExamQuiz1, ExamQuiz2, ExamMidterm and ExamFinal 列下的每个单元格都可能有数字(考试结果)或字符值(“G”> 未参加)。我不会触及 ExamMidterm 和 ExamFinal 列的任何值。主要思想只与ExamQuiz1, ExamQuiz2, and ExamQuiz3的列有关。

   StudentID  ExamQuiz1  ExamQuiz2  ExamQuiz3  ExamMidterm  ExamFinal
1      11111          0          G          G            G          G
2      22222          0          G         43           71         18
3      33333          0          G          G            G          G
4      44444          0          G          G            G          G
5      55555         60         38          G           64         27
6      66666          0          G          G            G          G

编辑:仍然有一些评论者不断指出数据不整洁。正如我在 cmets 上解释的那样,这样做的原因或您提供的整理方法对我来说没有意义。出于这个原因,我在问题主体上放了更多的解释,而不改变数据的结构。

【问题讨论】:

  • 出勤率最高是什么意思?每个学生要么参加测验,要么没有参加测验,那么每个学生的出勤率如何才能达到最高呢?您的数据不是tidy。虽然对于像这样的玩具示例来说这并不重要,但保持所有数据整洁是一个好习惯。从长远来看,它将为您节省时间!
  • @Limey 如果您仔细阅读,您会发现“G”表示学生没有参加。出于这个原因,如果他得到这个场景有 10、G、0,我必须选择 10 和 0 作为测验输入。因为出勤方面0比G好。如果有结果(带数字),则表示学生已经参加。很简单,数据也很干净!
  • 嗨 @ozturkib,光顾试图在 cmets 中帮助您的人不太可能导致您的问题得到回答。
  • @IanCampbell 我一点也不光顾任何人。我只是在解释每一个细节,而不是接受带有给定理由的评论。就是这样。
  • @ChuckP 澄清一下,每一列都可能有数字(考试结果)或字符值(“G” > 未参加)。

标签: r dataframe join merge dplyr


【解决方案1】:

基础 R 解决方案

cbind(df[-(2:4)], t(apply(df[2:4], 1, function(x){
  c(x[x == "G"], sort(x[x != "G"]))[-1]
})))

#   StudentID Midterm Final  1  2
# 1     11111       G     G  G  0
# 2     22222      71    18  0 43
# 3     33333       G     G  G  0
# 4     44444       G     G  G  0
# 5     55555      64    27 38 60
# 6     66666       G     G  G  0

在您的规则中,G 应放在任何数字前面。因此,首先我将所有现有的G 放在向量的开头并附加排序分数。删除向量中的第一个元素后,将保留前 2 个分数。

【讨论】:

  • 是的,它已解决,但我需要在接下来的一周内继续尝试。然后,我将其标记为答案,抱歉耽搁了。
【解决方案2】:

这是dplyr的新across(版本1.0.0或更高版本)的方法:

假设没有人可以得到负分并且缺席比得零更糟糕,我们可以将G设置为-1

library(dplyr)
data %>% 
  mutate(across(-StudentID, ~case_when(. == "G" ~ -1,
                                       TRUE ~ as.numeric(.)))) %>%
  rowwise() %>%
  mutate(TopQuiz = max(c_across(starts_with("Quiz"))),
         SecondQuiz = sort(c_across(starts_with("Quiz")),
                           decreasing = TRUE)[2]) %>%
  dplyr::select(StudentID, TopQuiz, SecondQuiz, Midterm, Final) %>%
  mutate(across(-StudentID, ~case_when(. == -1 ~ "G",
                                       TRUE ~ as.character(.))))
##A tibble: 6 x 5
## Rowwise: 
#  StudentID TopQuiz SecondQuiz Midterm Final
#      <int> <chr>   <chr>      <chr>   <chr>
#1     11111 0       G          G       G    
#2     22222 43      0          71      18   
#3     33333 0       G          G       G    
#4     44444 0       G          G       G    
#5     55555 60      38         64      27   
#6     66666 0       G          G       G     

【讨论】:

    【解决方案3】:

    应用 dplyrstringr 的方式略有不同,通过让 G NA 进行数学运算,然后将 NA 放回 G 并返回字符。

    library(dplyr)
    library(stringr)
    
    
    newgrades <- grades %>% 
      mutate(across(starts_with("Quiz"), ~ str_replace(., "G", NA_character_))) %>%
      mutate(across(starts_with("Quiz"), as.numeric)) %>%
      rowwise() %>%
      mutate(TopQuiz = max(c_across(starts_with("Quiz")), na.rm = TRUE),
             NextBestQuiz = sort(c_across(starts_with("Quiz")),
                                 decreasing = TRUE)[2]) %>%
      mutate(across(ends_with("Quiz"), as.character)) %>%
      mutate(across(ends_with("Quiz"), ~ str_replace_na(., replacement = "G"))) %>%
      select(id, TopQuiz, NextBestQuiz, Midterm, Final)
    
    newgrades
    #> # A tibble: 6 x 5
    #> # Rowwise: 
    #>      id TopQuiz NextBestQuiz Midterm Final
    #>   <int> <chr>   <chr>        <chr>   <chr>
    #> 1     1 0       G            G       G    
    #> 2     2 43      0            71      18   
    #> 3     3 0       G            G       G    
    #> 4     4 0       G            G       G    
    #> 5     5 60      38           64      27   
    #> 6     6 0       G            G       G
    

    您的数据

    grades <- data.frame(
      id = c(1:6),
      Quiz1 = c("0","0","0","0","60","0"),
      Quiz2 = c("G","G","G","G","38","G"),
      Quiz3 = c("G","43","G","G","G","G"),
      Midterm = c("G","71","G","G","64","G"),
      Final = c("G","18","G","G","27","G")
    )
    `
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-03
      相关资源
      最近更新 更多