R:将基于数据框中逻辑条件的值与 NA 值相乘
R: Multiply values values based on a logical conditionin in a data frame with NA values
如果你有一个完整的数据框,很容易根据逻辑条件乘以值:
df = data.frame(
var1 = c(1, 2, 3, 4, 5),
var2 = c(1, 2, 3, 2, 1),
var3 = c(5, 4, 3, 4, 5)
)
> df
var1 var2 var3
1 1 1 5
2 2 2 4
3 3 3 3
4 4 2 4
5 5 1 5
> df[df > 2] <- df[df > 2] * 10
> df
var1 var2 var3
1 1 1 50
2 2 2 40
3 30 30 30
4 40 2 40
5 50 1 50
但是,如果数据框中有 NA 值,操作将失败:
> df_na = data.frame(
var1 = c(NA, 2, 3, 4, 5),
var2 = c(1, 2, 3, 1, NA),
var3 = c(5, NA, 3, 4, 5)
)
> df_na
var1 var2 var3
1 NA 1 5
2 2 2 NA
3 3 3 3
4 4 1 4
5 5 NA 5
> df_na[df_na > 2] <- df_na[df_na > 2] * 10
Error in `[<-.data.frame`(`*tmp*`, df_na > 2, value = c(NA, 30, 40, 50, :
'value' is the wrong length
例如,我尝试了一些 na.omit()
策略,但无法奏效。我在 Stack Overflow 中也找不到合适的问题。
那么,我应该怎么做呢?
这行吗,使用基数 R:
df_na[] <- lapply(df_na, function(x) ifelse(!is.na(x) & x > 2, x * 10, x))
df_na
var1 var2 var3
1 NA 1 50
2 2 2 NA
3 30 30 30
4 40 1 40
5 50 NA 50
您可以添加 !is.na()
作为子集的附加逻辑参数:
df_na[df_na > 2 & !is.na(df_na)] <- df_na[df_na > 2 & !is.na(df_na)] * 10
# > df_na
# var1 var2 var3
# 1 NA 1 50
# 2 2 2 NA
# 3 30 30 30
# 4 40 1 40
# 5 50 NA 50
或者,dplyr
/ tidyverse
解决方案是:
library(dplyr)
df_na %>%
mutate_all(.funs = ~ ifelse(!is.na(.x) & .x > 2, .x * 10, .x))
根据 OP 评论添加:
如果您想根据基于 %in%
运算符的值进行子集化,请选择 dplyr
解决方案(%in%
运算符在这里的工作方式与在 post):
df_na %>%
mutate_all(.funs = ~ ifelse(!is.na(.x) & .x %in% c(3, 4), .x * 10, .x))
# var1 var2 var3
# 1 NA 1 5
# 2 2 2 NA
# 3 30 30 30
# 4 40 1 40
# 5 5 NA 5
这种方法通常适用于更复杂的操作任务。例如,您还可以借助 dplyr::case_when()
而不是 one-alternative ifelse
.
来定义其他条件
问题不在于乘法,而在于数组索引。
(df_na > 2
returns 不适用)。
如果您愿意,可以将下面的行转换为一行,
inds <- which(df_na > 2, arr.ind = TRUE)
df_na[inds] <- df_na[inds] * 10
如果你有一个完整的数据框,很容易根据逻辑条件乘以值:
df = data.frame(
var1 = c(1, 2, 3, 4, 5),
var2 = c(1, 2, 3, 2, 1),
var3 = c(5, 4, 3, 4, 5)
)
> df
var1 var2 var3
1 1 1 5
2 2 2 4
3 3 3 3
4 4 2 4
5 5 1 5
> df[df > 2] <- df[df > 2] * 10
> df
var1 var2 var3
1 1 1 50
2 2 2 40
3 30 30 30
4 40 2 40
5 50 1 50
但是,如果数据框中有 NA 值,操作将失败:
> df_na = data.frame(
var1 = c(NA, 2, 3, 4, 5),
var2 = c(1, 2, 3, 1, NA),
var3 = c(5, NA, 3, 4, 5)
)
> df_na
var1 var2 var3
1 NA 1 5
2 2 2 NA
3 3 3 3
4 4 1 4
5 5 NA 5
> df_na[df_na > 2] <- df_na[df_na > 2] * 10
Error in `[<-.data.frame`(`*tmp*`, df_na > 2, value = c(NA, 30, 40, 50, :
'value' is the wrong length
例如,我尝试了一些 na.omit()
策略,但无法奏效。我在 Stack Overflow 中也找不到合适的问题。
那么,我应该怎么做呢?
这行吗,使用基数 R:
df_na[] <- lapply(df_na, function(x) ifelse(!is.na(x) & x > 2, x * 10, x))
df_na
var1 var2 var3
1 NA 1 50
2 2 2 NA
3 30 30 30
4 40 1 40
5 50 NA 50
您可以添加 !is.na()
作为子集的附加逻辑参数:
df_na[df_na > 2 & !is.na(df_na)] <- df_na[df_na > 2 & !is.na(df_na)] * 10
# > df_na
# var1 var2 var3
# 1 NA 1 50
# 2 2 2 NA
# 3 30 30 30
# 4 40 1 40
# 5 50 NA 50
或者,dplyr
/ tidyverse
解决方案是:
library(dplyr)
df_na %>%
mutate_all(.funs = ~ ifelse(!is.na(.x) & .x > 2, .x * 10, .x))
根据 OP 评论添加:
如果您想根据基于 %in%
运算符的值进行子集化,请选择 dplyr
解决方案(%in%
运算符在这里的工作方式与在
df_na %>%
mutate_all(.funs = ~ ifelse(!is.na(.x) & .x %in% c(3, 4), .x * 10, .x))
# var1 var2 var3
# 1 NA 1 5
# 2 2 2 NA
# 3 30 30 30
# 4 40 1 40
# 5 5 NA 5
这种方法通常适用于更复杂的操作任务。例如,您还可以借助 dplyr::case_when()
而不是 one-alternative ifelse
.
问题不在于乘法,而在于数组索引。
(df_na > 2
returns 不适用)。
如果您愿意,可以将下面的行转换为一行,
inds <- which(df_na > 2, arr.ind = TRUE)
df_na[inds] <- df_na[inds] * 10