如何仅在没有 NA 的情况下检查一组变量中是否存在某个值?
How to check for the existence of a certain value in a set of variables only when there is no NA?
我有一个包含数百个变量的数据框,按不同的因素(“Happy_”、“Sad_”等)分组,我想创建一组新变量来指示参与者是否将 4 的评级放在任何一个因素中的变量。但是,如果该因子中的任何变量为 NA,则新变量也将输出 NA。
我试过以下方法,但没有用:
library(tidyverse)
df <- data.frame(Subj = c("A", "B", "C", "D"),
Happy_1_Num = c(4,2,2,NA),
Happy_2_Num = c(4,2,2,1),
Happy_3_Num = c(1,NA,2,4),
Sad_1_Num = c(2,1,4,3),
Sad_2_Num = c(NA,1,2,4),
Sad_3_Num = c(4,2,2,1))
# Don't work
df <- df %>% mutate(Happy_Any4 = ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ is.na(.)), NA,
ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ . == 4),1,0)),
Sad_Any4 = ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ is.na(.)), NA,
ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ . == 4),1,0)))
我尝试了一种解决方法,首先生成一组变量来指示该因素是否有任何 NA,然后检查参与者是否给出了“4”的任何评级。有用;但由于我有很多因素,我想知道是否有更优雅的方法。
# workaround
df <- df %>% mutate(
NA_Happy = ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ is.na(.)), 1,0),
NA_Sad = ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ is.na(.)), 1,0))
df <- df %>% mutate(
Happy_Any4 = ifelse(NA_Happy == 1, NA,
ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ . == 4),1,0)),
Sad_Any4 = ifelse(NA_Sad == 1, NA,
ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ . == 4),1,0)))
这是另一种变通方法,它通过转置 data.frame 并在冒号上应用。我不确定它是否更优雅,但它在这里 ^^
tmp <- cbind(sub("^((Happy)|(Sad))(_.*_Num)$", "\1", colnames(df)), t(df))
Happy_Any4 <- apply(tmp[tmp[,1]== "Happy", -1], 2,
function(x) ifelse(any(is.na(x)), NA, length(grep("4", x))) )
Sad_Any4 <- apply(tmp[tmp[,1]== "Sad", -1], 2,
function(x) ifelse(any(is.na(x)), NA, length(grep("4", x))) )
df <- cbind(df, Happy_Any4 = Happy_Any4, Sad_Any4 = Sad_Any4)
编辑:上面是一个奇怪的测试,但现在这个工作更漂亮了!
这是因为任何有 NA 的东西的总和将 return NA。
df <- df %>% mutate(Happy_Any4 = apply(df[,grep("^Happy_.*_Num$", colnames(df))],
1, function(x) 1*(sum(x == 4) > 0)),
Sad_Any4 = apply(df[, grep("^Sad_.*_Num$", colnames(df))],
1, function(x) 1*(sum(x == 4) > 0)))
apply
将查找每一行,仅在我们在 colnames 中找到正确部分的列上查找(使用 grep
。然后它会找到每一次出现的 4,它们形成一个逻辑向量,并且sum
是出现的次数。NA
的存在将使总和达到 NA
。然后我只检查总和是否大于 0,1*
将将数字转换为逻辑。
这是使用 split.default
-
的基础 R 选项
tmp <- df[-1]
cbind(df, sapply(split.default(tmp, sub('_.*', '', names(tmp))),
function(x) as.integer(rowSums(x== 4) > 0)))
# Subj Happy_1_Num Happy_2_Num Happy_3_Num Sad_1_Num Sad_2_Num Sad_3_Num Happy Sad
#1 A 4 4 1 2 NA 4 1 NA
#2 B 2 2 NA 1 1 2 NA 0
#3 C 2 2 2 4 2 2 0 1
#4 D NA 1 4 3 4 1 NA 1
sub
将仅保留 "Happy"
或 "Sad"
部分名称,split.default
以此为基础拆分数据并使用 sapply
进行计算如果任何值 4 出现在一行中。
如果你有能力手动编写每个因素,你可以做到 -
library(dplyr)
df %>%
mutate(Happy = as.integer(rowSums(select(., starts_with('Happy')) == 4) > 0),
Sad = as.integer(rowSums(select(., starts_with('Sad')) == 4) > 0))
我有一个包含数百个变量的数据框,按不同的因素(“Happy_”、“Sad_”等)分组,我想创建一组新变量来指示参与者是否将 4 的评级放在任何一个因素中的变量。但是,如果该因子中的任何变量为 NA,则新变量也将输出 NA。
我试过以下方法,但没有用:
library(tidyverse)
df <- data.frame(Subj = c("A", "B", "C", "D"),
Happy_1_Num = c(4,2,2,NA),
Happy_2_Num = c(4,2,2,1),
Happy_3_Num = c(1,NA,2,4),
Sad_1_Num = c(2,1,4,3),
Sad_2_Num = c(NA,1,2,4),
Sad_3_Num = c(4,2,2,1))
# Don't work
df <- df %>% mutate(Happy_Any4 = ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ is.na(.)), NA,
ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ . == 4),1,0)),
Sad_Any4 = ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ is.na(.)), NA,
ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ . == 4),1,0)))
我尝试了一种解决方法,首先生成一组变量来指示该因素是否有任何 NA,然后检查参与者是否给出了“4”的任何评级。有用;但由于我有很多因素,我想知道是否有更优雅的方法。
# workaround
df <- df %>% mutate(
NA_Happy = ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ is.na(.)), 1,0),
NA_Sad = ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ is.na(.)), 1,0))
df <- df %>% mutate(
Happy_Any4 = ifelse(NA_Happy == 1, NA,
ifelse(if_any(matches("^Happy_") & matches("_Num$"), ~ . == 4),1,0)),
Sad_Any4 = ifelse(NA_Sad == 1, NA,
ifelse(if_any(matches("^Sad_") & matches("_Num$"), ~ . == 4),1,0)))
这是另一种变通方法,它通过转置 data.frame 并在冒号上应用。我不确定它是否更优雅,但它在这里 ^^
tmp <- cbind(sub("^((Happy)|(Sad))(_.*_Num)$", "\1", colnames(df)), t(df))
Happy_Any4 <- apply(tmp[tmp[,1]== "Happy", -1], 2,
function(x) ifelse(any(is.na(x)), NA, length(grep("4", x))) )
Sad_Any4 <- apply(tmp[tmp[,1]== "Sad", -1], 2,
function(x) ifelse(any(is.na(x)), NA, length(grep("4", x))) )
df <- cbind(df, Happy_Any4 = Happy_Any4, Sad_Any4 = Sad_Any4)
编辑:上面是一个奇怪的测试,但现在这个工作更漂亮了!
这是因为任何有 NA 的东西的总和将 return NA。
df <- df %>% mutate(Happy_Any4 = apply(df[,grep("^Happy_.*_Num$", colnames(df))],
1, function(x) 1*(sum(x == 4) > 0)),
Sad_Any4 = apply(df[, grep("^Sad_.*_Num$", colnames(df))],
1, function(x) 1*(sum(x == 4) > 0)))
apply
将查找每一行,仅在我们在 colnames 中找到正确部分的列上查找(使用 grep
。然后它会找到每一次出现的 4,它们形成一个逻辑向量,并且sum
是出现的次数。NA
的存在将使总和达到 NA
。然后我只检查总和是否大于 0,1*
将将数字转换为逻辑。
这是使用 split.default
-
tmp <- df[-1]
cbind(df, sapply(split.default(tmp, sub('_.*', '', names(tmp))),
function(x) as.integer(rowSums(x== 4) > 0)))
# Subj Happy_1_Num Happy_2_Num Happy_3_Num Sad_1_Num Sad_2_Num Sad_3_Num Happy Sad
#1 A 4 4 1 2 NA 4 1 NA
#2 B 2 2 NA 1 1 2 NA 0
#3 C 2 2 2 4 2 2 0 1
#4 D NA 1 4 3 4 1 NA 1
sub
将仅保留 "Happy"
或 "Sad"
部分名称,split.default
以此为基础拆分数据并使用 sapply
进行计算如果任何值 4 出现在一行中。
如果你有能力手动编写每个因素,你可以做到 -
library(dplyr)
df %>%
mutate(Happy = as.integer(rowSums(select(., starts_with('Happy')) == 4) > 0),
Sad = as.integer(rowSums(select(., starts_with('Sad')) == 4) > 0))