从多列中仅获取值(非 0、非 NA)
Getting only value (non-0, non-NA) from multiple columns
这是我的数据示例:
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=" & "))))
这让我接近了,但还没有完全实现。
我运行遇到的问题主要是:
- 没有字符串的行(即只有 NA 或 0)将在我的数据中作为
character(0)
而不是 NA
- 具有多个时间的行作为列表存储在我的数据框中,
c("Before", "After")
而不是打印为 "Before & After"
。 paste()
好像没有用,但是当我把它取出来的时候又出问题了。
我走在正确的轨道上吗?还是有其他方法可以更好地做到这一点?我想避免编写嵌套的 for/if 循环!
奖金:
我想我不太明白 apply()
中匿名 function(x)
中的 x
是如何被 R 评估的。是不是每次传递的每一列通过函数,比如 df$Timing1
?或者按行,比如 df$Timing1[1]
,然后移动到 df$Timing1[2]
等等?因为我指定了 MARGIN=1
?如果有人能以愚蠢的方式向我解释这一点,我将不胜感激!我的实际数据集比这更复杂,所以我需要更好地理解这一点,以便我可以推断并将(双关语)应用到我更广泛的上下文中。
谢谢!
我们可以将 0 值变为 NA
,然后使用 unite
和 na.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
它将是第二行,依此类推。
这是我的数据示例:
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=" & "))))
这让我接近了,但还没有完全实现。
我运行遇到的问题主要是:
- 没有字符串的行(即只有 NA 或 0)将在我的数据中作为
character(0)
而不是 NA - 具有多个时间的行作为列表存储在我的数据框中,
c("Before", "After")
而不是打印为"Before & After"
。paste()
好像没有用,但是当我把它取出来的时候又出问题了。
我走在正确的轨道上吗?还是有其他方法可以更好地做到这一点?我想避免编写嵌套的 for/if 循环!
奖金:
我想我不太明白 apply()
中匿名 function(x)
中的 x
是如何被 R 评估的。是不是每次传递的每一列通过函数,比如 df$Timing1
?或者按行,比如 df$Timing1[1]
,然后移动到 df$Timing1[2]
等等?因为我指定了 MARGIN=1
?如果有人能以愚蠢的方式向我解释这一点,我将不胜感激!我的实际数据集比这更复杂,所以我需要更好地理解这一点,以便我可以推断并将(双关语)应用到我更广泛的上下文中。
谢谢!
我们可以将 0 值变为 NA
,然后使用 unite
和 na.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
它将是第二行,依此类推。