【问题标题】:Using a for-loop to change a column in a dataframe使用 for 循环更改数据框中的列
【发布时间】:2019-08-20 13:50:04
【问题描述】:

我是 R 的初级用户,我编写了一个我相信可以用 for 循环缩短的代码。问题是我不知道如何编写循环。

我有一个包含“TestGrade”列的数据框,其中包含“Grade 1”或“Kindergarten”等值。我正在尝试将该列更改为只是一个数值。例如,“幼儿园”将更改为 0,“1 年级”将更改为 1。我将在下面提供示例数据框的代码,以及我如何在没有循环的情况下解决问题。

任何指导将不胜感激!

##Sample Data
FirstInitial <- c("A", "D", "M", "C", "J", "S", "K", "L", "M", "K", "G", "B", "F")
LastInitial <- c("S", "M", "T", "M", "A", "B", "H", "M", "S", "W", "L", "Z", "P")
TestGrade <- c('Kindergarten', 'Grade 1','Grade 2', 'Grade 3','Grade 4', 'Grade 5', 'Grade 6','Grade 7','Grade 8', 'Grade 9', 'Grade 10', 'Grade 11','Grade 12')

df <- data.frame(FirstInitial, LastInitial, TestGrade)

##The codes current function
if(any(df$TestGrade == 'Kindergarten')){
  df$TestGrade <- gsub('Kindergarten', '0', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 1')){
  df$TestGrade <- gsub('Grade 1', '1', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 2')){
  df$TestGrade <- gsub('Grade 2', '2', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 3')){
  df$TestGrade <- gsub('Grade 3', '3', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 4')){
  df$TestGrade <- gsub('Grade 4', '4', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 5')){
  df$TestGrade <- gsub('Grade 5', '5', df$TestGrade)
}

if(any(df$TestGrade == 'Grade 6')){
  df$TestGrade <- gsub('Grade 6', '6', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 7')){
  df$TestGrade <- gsub('Grade 7', '7', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 8')){
  df$TestGrade <- gsub('Grade 8', '8', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 9')){
  df$TestGrade <- gsub('Grade 9', '9', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 10')){
  df$TestGrade <- gsub('Grade 10', '10', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 11')){
  df$TestGrade <- gsub('Grade 11', '11', df$TestGrade)
}
if(any(df$TestGrade == 'Grade 12')){
  df$TestGrade <- gsub('Grade 12', '12', df$TestGrade)
}

【问题讨论】:

  • 我反复提出的切题建议是使用显式循环作为 R 中的最后选择。应用函数和相关包旨在提高编码效率。

标签: r dataframe for-loop


【解决方案1】:

我们可以使用ifelse,为“幼儿园”分配0,并从其他人中删除“等级”

as.numeric(ifelse(df$TestGrade == "Kindergarten", 0, 
          sub("Grade ", "", df$TestGrade)))

#[1]  0  1  2  3  4  5  6  7  8  9 10 11 12

【讨论】:

    【解决方案2】:

    我们可以使用case_when

    library(dplyr)
    library(readr)
    df %>%
      mutate(TestGrade = case_when(as.character(TestGrade) == "Kindergarten"~ 0,
                                   TRUE ~ parse_number(TestGrade)))
    
    #   FirstInitial LastInitial TestGrade
    #1             A           S         0
    #2             D           M         1
    #3             M           T         2
    #4             C           M         3
    #5             J           A         4
    #6             S           B         5
    #7             K           H         6
    #8             L           M         7
    #9             M           S         8
    #10            K           W         9
    #11            G           L        10
    #12            B           Z        11
    #13            F           P        12
    

    【讨论】:

      【解决方案3】:

      第一次缩短:你不需要任何if(any(...))gsub 很聪明,就像查找/替换一样。命令gsub('Grade 9', '9', df$TestGrade) 会将'Grade 9' 替换为'9',并且不会触及其他任何内容。所以删除你所有的if 语句,我们得到:

      df$TestGrade <- gsub('Kindergarten', '0', df$TestGrade)
      df$TestGrade <- gsub('Grade 1', '1', df$TestGrade)
      df$TestGrade <- gsub('Grade 2', '2', df$TestGrade)
      df$TestGrade <- gsub('Grade 3', '3', df$TestGrade)
      df$TestGrade <- gsub('Grade 4', '4', df$TestGrade)
      df$TestGrade <- gsub('Grade 5', '5', df$TestGrade)
      df$TestGrade <- gsub('Grade 6', '6', df$TestGrade)
      df$TestGrade <- gsub('Grade 7', '7', df$TestGrade)
      df$TestGrade <- gsub('Grade 8', '8', df$TestGrade)
      df$TestGrade <- gsub('Grade 9', '9', df$TestGrade)
      df$TestGrade <- gsub('Grade 10', '10', df$TestGrade)
      df$TestGrade <- gsub('Grade 11', '11', df$TestGrade)
      df$TestGrade <- gsub('Grade 12', '12', df$TestGrade)
      

      下一个改进,我们可以做一个循环。这和上面的代码完全一样,只是少了一些输入。

      pattern = c("Kindergarten", paste("Grade", 1:12))
      replacement = as.character(0:12)
      
      for (i in seq_along(pattern)) {
        df$TestGrade <- gsub(pattern[i], replacement[i], df$TestGrade)
      }
      

      更好的是,我们可以更聪明,让幼儿园成为一个特例,然后从其他所有内容中删除 "Grade ",就像 Juian 和 Ronak 的回答一样。另一个变体是这样的:

      df$TestGrade = as.character(df$TestGrade) # needed only if it is a factor
      df$TestGrade[df$TestGrade == "Kindergarten"] = 0
      df$TestGrade = sub("Grade ", "", df$TestGrade)
      df$TestGrade = as.numeric(df$TestGrade) # if needed
      

      如果我们真的想要花哨,我们可以在sub() 中设置fixed = TRUE。这告诉sub 我们只想要完全匹配,我们不尝试使用正则表达式。这将使代码运行得更快,但除非您有 大量 数据,否则您不会注意到差异。如果你有 100,000+ 行,这个方法会很快:

      # optimized
      df$TestGrade = as.character(df$TestGrade) # needed only if it is a factor
      df$TestGrade[df$TestGrade == "Kindergarten"] = 0
      df$TestGrade = as.integer(sub("Grade ", "", df$TestGrade, fixed = TRUE))
      

      【讨论】:

        【解决方案4】:

        这可以在不需要使用两行代码的for 循环的情况下完成。我还建议您在运行这些行之前在您的data.frame 命令中添加stringsAsFactors = F

        df$TestGrade[df$TestGrade == "Kindergarten"] = 0
        df$TestGrade <- gsub("Grade ", "", df$TestGrade)
        
        > df
           FirstInitial LastInitial TestGrade
        1             A           S         0
        2             D           M         1
        3             M           T         2
        4             C           M         3
        5             J           A         4
        6             S           B         5
        7             K           H         6
        8             L           M         7
        9             M           S         8
        10            K           W         9
        11            G           L        10
        12            B           Z        11
        13            F           P        12
        

        【讨论】:

          【解决方案5】:

          您可以编写一个键并将成绩设置为一个因素。即使成绩的格式发生变化,这也将起作用。

          key <- c('Kindergarten',
                   'Grade 1',
                   'Grade 2',
                   'Grade 3',
                   'Grade 4',
                   'Grade 5',
                   'Grade 6',
                   'Grade 7',
                   'Grade 8',
                   'Grade 9',
                   'Grade 10',
                   'Grade 11',
                   'Grade 12')
          dat <- c('Grade 3', 'Grade 5', 'Grade 2')
          dat <- factor(dat, levels = key)
          dat <- as.numeric(dat) - 1
          dat
          

          我们在最后减去 1,因为因子从 1 开始,而您希望幼儿园设置为 0。

          【讨论】:

            【解决方案6】:

            这解决了你的问题:

            df$TestGrade <- sapply(df$TestGrade,function(el)
              {
              if(el == "Kindergarten") return(0)
              else return(as.numeric(sub("Grade ","",el)))
            }
            

            【讨论】:

              猜你喜欢
              • 2020-09-19
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-08-01
              • 2016-08-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多