【问题标题】:Using if else on a dataframe across multiple columns在跨多列的数据框上使用 if else
【发布时间】:2016-09-15 18:01:18
【问题描述】:

我有一个大型样本数据集,其中包含样本是否可行的描述符 - 它看起来(有点)像这样,其中“desc”是描述列,“空白”表示样本不可行:

     desc        x        y        z
1   blank 4.529976 5.297952 5.581013
2   blank 5.906855 4.557389 4.901660
3  sample 4.322014 4.798248 4.995959
4  sample 3.997565 5.975604 7.160871
5   blank 4.898922 7.666193 5.551385
6   blank 5.667884 5.195825 5.232072
7   blank 5.524773 6.726074 4.767475
8  sample 4.382937 5.926217 5.203737
9  sample 4.976908 3.079191 4.614121
10  blank 4.572954 4.772373 6.077195

我想使用 if else 语句将包含不可用数据的行设置为 NA。最终的数据集应该是这样的:

     desc        x        y        z
1   blank       NA       NA       NA
2   blank       NA       NA       NA
3  sample 4.322014 4.798248 4.995959
4  sample 3.997565 5.975604 7.160871
5   blank       NA       NA       NA
6   blank       NA       NA       NA
7   blank       NA       NA       NA
8  sample 4.382937 5.926217 5.203737
9  sample 4.976908 3.079191 4.614121
10  blank       NA       NA       NA 

我尝试了一个 for 循环,但我无法让 for 循环在一个循环中更改所有列。我的真实数据集有 40 列,所以我宁愿不必在单独的循环中处理它!这是一次更改一列的代码:

for(i in 1:length(desc)){
    if(dat$desc[i] =="blank"){
    dat$x[i] <- NA
    } 
    else {
    dat$x[i] <- dat$x[i]
    }
}

我用这个脚本制作了示例数据:

desc <- c("blank", "blank", "sample", "sample", "blank", "blank", "blank",    "sample", "sample", "blank")
x <-  rnorm(10, mean=5, sd=1)
y <-  rnorm(10, mean=5, sd=1)
z <-  rnorm(10, mean=5, sd=1)

dat <- data.frame(desc,x,y,z)

对不起,如果这是一个基本问题,我已经花了整个上午的时间在论坛上寻找解决方案。

非常感谢任何帮助!

【问题讨论】:

    标签: r if-statement for-loop dataframe


    【解决方案1】:

    使用你的第一个初始方法和循环我发现了这个:

        for(i in 1:nrow(dat)){
      if(dat[i, 1] =="blank"){
        dat[i, 2:4] <- NA
      } 
      else {
        dat[i,length(dat)] <- dat[i, length(dat)]
      }
    }
    

    我用您的数据对其进行了测试并且工作正常。希望这对处理带有条件的行和列中的循环的每个人都有用。

    【讨论】:

    • 太棒了,感谢您的评论 - 我相信有一天会有人发现这很有用:)
    【解决方案2】:

    这是一个使用来自data.tableset 的选项。它应该更快,因为避免了[.data.table 的开销。我们将'data.frame'转换为'data.table'(setDT(df1)),循环遍历'df1'的列名(不包括'desc'列'),将元素分配给逻辑条件的“NA”是否满足“我”。

    library(data.table)
    setDT(df1)
    for(j in names(df1)[-1]){
       set(df1, i= which(df1[["desc"]]=="blank"), j= j, value= NA)
    }
    df1
    #      desc        x        y        z
    # 1:  blank       NA       NA       NA
    # 2:  blank       NA       NA       NA
    # 3: sample 4.322014 4.798248 4.995959
    # 4: sample 3.997565 5.975604 7.160871
    # 5:  blank       NA       NA       NA
    # 6:  blank       NA       NA       NA
    # 7:  blank       NA       NA       NA
    # 8: sample 4.382937 5.926217 5.203737
    # 9: sample 4.976908 3.079191 4.614121
    #10:  blank       NA       NA       NA
    

    或其他选项(基于@dww 的评论)

    setDT(df1, key = "desc")["blank", names(df1)[-1] := NA][]
    

    【讨论】:

    • 或者,如果使用数据表,只需 df1[desc=="blank", c(2:NCOL(df1)):=NA, with=F] 即可。
    • @dww 可以,但我认为set 会很快
    • 对这些进行微基准测试,我评论中的版本似乎快了一个数量级。正如你所说,设置应该很快。会不会是 which(df1[[ 减慢了你的开销?
    • @dww 使用大型数据集进行微基准测试或 OP 显示的示例?
    • 我使用了 100,000 行,但只是 OP 的 4 列。
    【解决方案3】:

    这是另一个 dplyr 解决方案,带有一个小的自定义函数和mutate_each()

    library(dplyr)
    
    f <- function(x) if_else(dat$desc == "blank", NA_real_, x)
    dat %>% 
      mutate_each(funs(f), -desc)
    #>      desc        x        y        z
    #> 1   blank       NA       NA       NA
    #> 2   blank       NA       NA       NA
    #> 3  sample 3.624941 6.430955 5.486632
    #> 4  sample 3.236359 4.935453 4.319202
    #> 5   blank       NA       NA       NA
    #> 6   blank       NA       NA       NA
    #> 7   blank       NA       NA       NA
    #> 8  sample 5.058725 6.751650 4.750529
    #> 9  sample 5.837206 4.323562 4.914780
    #> 10  blank       NA       NA       NA
    

    【讨论】:

    • 感谢您的解决方案!我用了上面dww的单行解决方案,但这看起来也不错:)
    【解决方案4】:

    您可以使用 dplyr 和自定义函数在某些条件下改变值。

    `

    library(dplyr)
    mutate_cond <- function(.data, condition, ..., envir = parent.frame()) {
            condition <- eval(substitute(condition), .data, envir)
            .data[condition, ] <- .data[condition, ] %>% mutate(...)
            .data
    }
    data <- data %>% 
    mutate_cond( desc == "blank", x = NA, y = NA, z = NA)
    

    `

    【讨论】:

      【解决方案5】:

      对于您的示例数据集,这将起作用;

      选项 1,命名要更改的列:

      dat[which(dat$desc == "blank"), c("x", "y", "z")] <- NA
      

      在你实际有40列的数据中,如果你只想将最后39列设置为NA,那么下面的方法可能比命名每一列要更改更简单;

      选项2,使用范围选择列:

      dat[which(dat$desc == "blank"), 2:40] <- NA
      

      选项3,排除第一列:

      dat[which(dat$desc == "blank"), -1] <- NA
      

      选项 4,排除命名列:

      dat[which(dat$desc == "blank"), !names(dat) %in% "desc"] <- NA
      

      如您所见,执行此类操作的方法有很多(这远非完整列表),了解每个选项的工作原理将有助于您更好地理解该语言。

      【讨论】:

      • 非常感谢,我认为选项 2 将是最佳选择 :) 并感谢您提供更多示例!我以前没有遇到过which()。
      【解决方案6】:

      这应该可行。老实说,如果数据不可用,为什么不完全删除这些行呢?

      library(dplyr)
      
      blanks = 
        dat %>%
        filter(desc == "blank") %>%
        select(desc)
      
      dat %>%
        filter(desc == "sample") %>%
        bind_rows(blanks)
      

      【讨论】:

      • 非常感谢您花时间回答:) 我肯定需要更熟悉 dplyr,它似乎真的很有用。至于删除它,它是一个时间序列(以 0.5 秒为间隔),我认为如果我删除坏行,从长远来看,我的生活会更加艰难!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-30
      • 2015-12-16
      • 2017-06-25
      • 1970-01-01
      • 1970-01-01
      • 2021-12-31
      • 1970-01-01
      相关资源
      最近更新 更多