【问题标题】:R: using a for loop on a list of data framesR:在数据帧列表上使用 for 循环
【发布时间】:2016-12-02 02:39:55
【问题描述】:

我有一个循环,它重新编码列的值并在满足条件时中断。我想在具有相同格式的数据框列表上使用此循环或其基本概念。

样本数据:

Id <- as.factor(c(rep("01001", 11), rep("01043", 11), rep("01065", 11), rep("01069", 11)))
YearCode <- as.numeric(rep(1:11, 4))
Type <- c(NA,NA,NA,NA,NA,NA,NA,2,NA,NA,NA,NA,NA,NA,
          NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,
          NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2,NA)
test <- NA
sample_df <- data.frame(Id, YearCode, Type, test)

# A part of sample_df
one_df <- subset(sample_df, sample_df$Id=="01069")

这个 for 循环适用于一个数据帧:

# example for loop using example data frame "one_df"
for(i in seq(along=one_df$Id)){
if(is.na(one_df$Type[i])){  # if Type is NA, recode to 0
one_df$test[i] <- 0  
} else {   # Stop when Type is not NA, and leave remaining NAs that come after
break }
} 

但是,我在列表中有许多具有相同格式的数据框。我想将它们保留在列表中,并将此循环应用于整个列表。

# example list : split data frame into list by Id
sample_list <- split(sample_df, sample_df$Id, drop = TRUE)

我查看了其他帖子,例如 this one,但在尝试遍历列表中的每个数据帧或使用 lapply 编写类似的函数时,我遇到了困难。如何使用 for 循环、lapply 或其他方式修改此循环以在列表 (sample_list) 上工作?

任何提示将不胜感激,如果我需要澄清任何事情,请告诉我。谢谢!

【问题讨论】:

    标签: r for-loop lapply


    【解决方案1】:

    我认为以下内容可以完成您描述的工作。我所做的是以下。我首先使用if_else() 创建了一个名为test 的新列。如果 complete.cases(Type) 为 TRUE,则使用来自 Type 的值。否则使用 0。下一步是用 NA 替换一些特定的 0。因为您不希望在 Type 中第一个数值的行之后的行中有 0。例如,您不希望 Id == 01069 的第 10 行之后有 0。所以我创建了测试条件:row_number() &gt; which(complete.cases(Type))[1]。您可以将其理解为“行号是否大于第一个数值的行号”。使用此条件,我将 0 替换为 NA。我为sample_df 提供了部分结果。我希望这对您的工作有所帮助。

    library(dplyr)
    
    sample_df %>%
    group_by(Id) %>%
    mutate(test = if_else(complete.cases(Type), Type, 0),
           test = if_else(row_number() > which(complete.cases(Type))[1],
                          NA_real_, test)) -> out
    
    #       Id YearCode  Type  test
    #   <fctr>    <dbl> <dbl> <dbl>
    #1   01001        1    NA     0
    #2   01001        2    NA     0
    #3   01001        3    NA     0
    #4   01001        4    NA     0
    #5   01001        5    NA     0
    #6   01001        6    NA     0
    #7   01001        7    NA     0
    #8   01001        8     2     2
    #9   01001        9    NA    NA
    #10  01001       10    NA    NA
    #11  01001       11    NA    NA
    #------------------------------
    #34  01069        1    NA     0
    #35  01069        2    NA     0
    #36  01069        3    NA     0
    #37  01069        4    NA     0
    #38  01069        5    NA     0
    #39  01069        6    NA     0
    #40  01069        7    NA     0
    #41  01069        8    NA     0
    #42  01069        9    NA     0
    #43  01069       10     2     2
    #44  01069       11    NA    NA
    

    编辑

    根据他/她的评论,当 Type 仅包含 NA 时,OP 想要 0。以下将完成这项工作。

    sample_df %>%
    group_by(Id) %>%
    mutate(test = if_else(complete.cases(Type), Type, 0),
           test = if_else(row_number() > which(complete.cases(Type))[1],
                          NA_real_, test),
           foo = sum(Type, na.rm = TRUE),
           test = replace(test, which(foo == 0), 0)) %>%
    select(-foo) -> out
    
    # A part of the result
    #       Id YearCode  Type  test
    #   <fctr>    <dbl> <dbl> <dbl>
    #1   01001        1    NA     0
    #2   01001        2    NA     0
    #3   01001        3    NA     0
    #4   01001        4    NA     0
    #5   01001        5    NA     0
    #6   01001        6    NA     0
    #7   01001        7    NA     0
    #8   01001        8     2     2
    #9   01001        9    NA    NA
    #10  01001       10    NA    NA
    #11  01001       11    NA    NA
    #12  01043        1    NA     0
    #13  01043        2    NA     0
    #14  01043        3    NA     0
    #15  01043        4    NA     0
    #16  01043        5    NA     0
    #17  01043        6    NA     0
    #18  01043        7    NA     0
    #19  01043        8    NA     0
    #20  01043        9    NA     0
    #21  01043       10    NA     0
    #22  01043       11    NA     0
    

    【讨论】:

    • 感谢您的快速回复!还要感谢,这是一个有趣的解决方案。如果我错了,请纠正我,但我得到 NAs 而不是 0 的 ID,在 Type 列中没有任何内容。理想情况下,当该 ID 内的所有 Type 值均为 NA 时,所有 test 值都将为 0。
    • @mhd 是的,这是预期的结果。由于没有关于如何处理此类情况的信息,我决定编写上面的代码。我将添加另一种方法来完成附加任务。
    【解决方案2】:

    创建函数和使用 lapply 是否有问题?它似乎工作

    #rm(list=ls())
    Id <- as.factor(c(rep("01001", 11), rep("01043", 11), rep("01065", 11), rep("01069", 11)))
    YearCode <- as.numeric(rep(1:11, 4))
    Type <- c(NA,NA,NA,NA,NA,NA,NA,2,NA,NA,NA,NA,NA,NA,
              NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,
              NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,NA,2,NA)
    test <- NA
    sample_df <- data.frame(Id, YearCode, Type, test)
    
    # A part of sample_df
    one_df <- subset(sample_df, sample_df$Id=="01069")
    
    sample_list <- split(sample_df, sample_df$Id, drop = TRUE)
    
    ####################################
    
    # for loop as funciton   
    fnX<- function(myDF){
     for(i in seq(along=myDF$Id)){
       if(is.na(myDF$Type[i])){  # if Type is NA, recode to 0
        myDF$test[i] <- 0  
       } else {   # Stop and leave remaining NAs that come after
       break }
      } 
      myDF
     }
    
    #apply function 
    fnX(sample_list$`01069`)   
    
    lapply(sample_list,fnX)
    

    【讨论】:

    • 是的,就是这样。我错过了最后一个括号之前的 myDF 函数的最后一段,谢谢你帮助把它放在一起!
    • 返回位.. R 的便利性让出错更容易了,唉!很高兴它有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-16
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 2019-09-21
    • 1970-01-01
    相关资源
    最近更新 更多