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