【问题标题】:Create a new column using the non-blank value from each row using apply使用 apply 使用每行中的非空白值创建一个新列
【发布时间】:2019-01-17 01:33:07
【问题描述】:

我有一个由 3 列组成的数据框,每列代表受访者所属的组。受访者属于这些组之一,并负责在他们所属的组列中提供他们的数字回答。因此,对于给定的行,其他 2 列将是空白的。

我需要创建一个包含他们分数的列,无论他们属于哪个组。在 Stackoverflow 上,有一个与我类似的问题,但它是针对 Python 的(参见 here

以下是数据的样子和我所做的:

library(dplyr)

df <- data.frame(grp_A = c(13, NA, NA, NA, NA, 20, NA),
             grp_B = c(NA, 59, 66, NA, NA, NA, NA),
             grp_C = c(NA, NA, NA, 23, 42, NA, NA))

df$value <- apply(select(df, grp_A, grp_B, grp_C), 1,
              function(x) x[!is.na(x)])

由于某些行中缺少数据,R 错误地将新列转换为列表。我尝试使用as.data.frame 将其重新转换回数据框,但没有成功。

请告知如何防止新创建的列变成列表。

【问题讨论】:

    标签: r dplyr apply


    【解决方案1】:

    无需使用 apply,因为对于每一行,您只有一个非 NA 值,我们可以使用 max.col 获得该值,而无需担心关系。

    df$value <- df[cbind(1:nrow(df), max.col(!is.na(df)))]
    
    df
    #  grp_A grp_B grp_C value
    #1    13    NA    NA    13
    #2    NA    59    NA    59
    #3    NA    66    NA    66
    #4    NA    NA    23    23
    #5    NA    NA    42    42
    #6    20    NA    NA    20
    #7    NA    NA    NA    NA
    

    max.col 为我们提供每行的最大值的列号索引,由于我们将其包装在!is.na 中,它将为我们提供TRUE 的索引。

    max.col(!is.na(df))
    #[1] 1 2 2 3 3 1 2
    

    您的apply 不起作用的原因是因为您的最后一行包含所有NAs 和x[!is.na(x)] 失败。如果您删除该行并运行您的函数,那么它将起作用

    apply(df[-7, ], 1,function(x) x[!is.na(x)])
    # 1  2  3  4  5  6 
    #13 59 66 23 42 20 
    

    我们还可以通过删除NA 来找出每一行的max 值,但这将返回-Inf 用于所有NAs 的行

    apply(df, 1,max, na.rm = TRUE)
    #[1]   13   59   66   23   42   20 -Inf
    

    【讨论】:

      【解决方案2】:

      基础 R rowMeans

      df$new=rowMeans(df,na.rm=T)
      df
        grp_A grp_B grp_C new
      1    13    NA    NA  13
      2    NA    59    NA  59
      3    NA    66    NA  66
      4    NA    NA    23  23
      5    NA    NA    42  42
      6    20    NA    NA  20
      7    NA    NA    NA NaN
      

      【讨论】:

        【解决方案3】:

        Reducedplyr::coalesce 一起使用怎么样?

        library(dplyr)
        
        df <- data.frame(grp_A = c(13, NA, NA, NA, NA, 20, NA),
                         grp_B = c(NA, 59, 66, NA, NA, NA, NA),
                         grp_C = c(NA, NA, NA, 23, 42, NA, NA))
        
        mutate(df, value = Reduce(coalesce, df))
        

        结果:

          grp_A grp_B grp_C value
        1    13    NA    NA    13
        2    NA    59    NA    59
        3    NA    66    NA    66
        4    NA    NA    23    23
        5    NA    NA    42    42
        6    20    NA    NA    20
        7    NA    NA    NA    NA
        

        另一种选择是使用rowSums

        df$value <- rowSums(df, na.rm = T)
        
        df[df$value == 0, ] <- NA 
        

        此外,在性能方面,基本Reduce 解决方案似乎是最有效的:

        microbenchmark::microbenchmark(
          Reduce = Reduce(coalesce, df), 
          purrr = purrr::reduce(df, coalesce),
          rowMeans = rowMeans(df,na.rm=T), 
          rowSums = rowSums(df, na.rm = T), 
          cbind = df[cbind(1:nrow(df), max.col(!is.na(df)))],
          times = 1000
        )
        
        Unit: microseconds
             expr     min       lq     mean   median       uq       max neval cld
           Reduce  83.507 107.2095 145.4134 121.4320 137.8410 12190.845  1000  a 
            purrr 205.667 269.1175 357.5908 304.8540 342.4135 24316.051  1000   b
         rowMeans 129.089 159.3555 196.1438 174.4890 194.9095  5481.523  1000  a 
          rowSums 129.454 157.1680 197.2731 173.5775 196.0035  7685.874  1000  a 
            cbind 267.294 331.8385 408.3179 368.4860 410.2400  4533.050  1000   b
        

        【讨论】:

          猜你喜欢
          • 2015-12-30
          • 1970-01-01
          • 1970-01-01
          • 2021-06-13
          • 1970-01-01
          • 2020-08-27
          • 1970-01-01
          • 1970-01-01
          • 2021-05-21
          相关资源
          最近更新 更多