R:将基于逻辑条件的值与NA值相乘

问题描述

如果您有完整的数据框,则可以很容易地根据逻辑条件将值相乘:

df = data.frame(
    var1 = c(1,2,3,4,5),var2 = c(1,1),var3 = c(5,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,1,NA),NA,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()策略,但无法使其起作用。在堆栈溢出中,我也找不到合适的问题。

那我该怎么办?

解决方法

您可以通过以下方式将!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%运算符在这里的工作方式与{{ 3}}帖子):

df_na %>% 
  mutate_all(.funs = ~ ifelse(!is.na(.x) & .x %in% c(3,4),.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()的帮助下定义其他条件,而不是一个替代的ifelse

,

使用基数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
 
,

问题不在于乘法,而是数组索引。 (df_na > 2返回NA)。

如果愿意,您可以将下面的行转换为一行,

inds <- which(df_na > 2,arr.ind = TRUE)
df_na[inds] <- df_na[inds] * 10