【问题标题】:Getting only value (non-0, non-NA) from multiple columns从多列中仅获取值(非 0、非 NA)
【发布时间】:2020-09-15 05:02:49
【问题描述】:

这是我的数据示例:

df <- data.frame(Timing1 = c("Before", NA, 0, 0, 0, "Before"),
                 Timing2 = c(NA, "During", 0, "During", 0, NA),
                 Timing3 = c(0, NA, "After", "After", NA, 0))

我想创建一个名为Timing_combined 的新列,它仅从其他 3 列中获取字符串(非 NA、非 0)值,而忽略 NA 和 0。

我想要的输出是这样的:

 Timing1  Timing2    Timing3   Timing_combined
  Before     <NA>         0             Before
    <NA>   During      <NA>             During
       0        0     After              After
       0   During     After     During & After
       0        0      <NA>               <NA>
  Before     <NA>         0             Before

这是我目前的代码:

df <- df %>% 
  mutate(Timing_combined = apply(., 1, function(x) unlist(paste(x[!is.na(x) & x != 0], sep=" & "))))

这让我很接近,但还没有完全到那里。

我遇到的问题主要是:

  1. 没有字符串(即只有 NA 或 0)的行将在我的数据中作为 character(0) 而不是 NA
  2. 具有多个计时的行作为列表存储在我的数据框中,c("Before", "After"),而不是打印为"Before &amp; After"paste() 似乎不起作用,但是当我将其取出时,又出现了其他问题。

我在正确的轨道上吗?还是有其他方法可以更好地做到这一点?我想避免编写嵌套的 for/if 循环!

奖金:

我想我不太明白apply() 中的匿名function(x) 中的x 是如何由R 评估的。是否每次都通过函数传递,例如df$Timing1 ?还是按行排列,例如df$Timing1[1],然后转到df$Timing1[2] 等?因为我指定了MARGIN=1?如果有人能以愚蠢的方式向我解释这一点,我将不胜感激!我的实际数据集比这更复杂,所以我需要更好地理解这一点,以便我可以推断并将其应用(不是双关语)到我更广泛的上下文中。

谢谢!

【问题讨论】:

    标签: r dataframe apply


    【解决方案1】:

    我们可以将 0 值转换为 NA,然后使用 unitena.rm = TRUE 删除 NA 值。

    library(dplyr)
    library(tidyr)
    
    df %>%
      mutate(across(.fns = ~na_if(., 0))) %>%
      unite(Timing_combined, starts_with('Timing'), 
            na.rm = TRUE, remove = FALSE, sep = ' & ')
    
    #  Timing_combined Timing1 Timing2 Timing3
    #1          Before  Before    <NA>    <NA>
    #2          During    <NA>  During    <NA>
    #3           After    <NA>    <NA>   After
    #4  During & After    <NA>  During   After
    #5                    <NA>    <NA>    <NA>
    #6          Before  Before    <NA>    <NA>
    

    如果你想使用apply

    df$Timing_combined <- apply(df, 1, function(x) 
                                paste0(x[!is.na(x) & x != 0], collapse = ' & '))
    

    apply 中的匿名函数如何工作取决于您使用的 MARGIN。这里我们传递MARGIN = 1 意味着匿名函数中的第一次迭代x 将是第一行。对于第二个,x 将是第二行,依此类推。

    【讨论】:

    • 非常感谢!我使用的是 paste(sep) 而不是 paste0(collapse),所以我比我想象的更接近。 apply() 版本对我来说效果更好,因为我在单个 df 中有更多这些类型的列,但是这两种解决方案仍然对未来的情况有帮助。对于 x 的解释,你的意思是第一次迭代在我的示例中,x 本质上是 c("Before", NA, 0)
    • 是的,完全正确。如果您通过MARGIN = 2,它将是第一次迭代中x 的第一列。
    猜你喜欢
    • 1970-01-01
    • 2020-10-23
    • 2020-06-27
    • 1970-01-01
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    相关资源
    最近更新 更多