如何使用具有多个条件的R ifelse语句?

时间:2016-07-18 16:18:09

标签: r if-statement logical-operators

我是R的新手,但我很高兴能够学习它,我认为这可能是个好机会。 我有两个盐度测量值(uS和mS.m_1.5)。我根据它们的值为每个测量类型(uSClass和mS.m_1.5Class)创建了3个类(1,2,3)。对于许多观察,我只有1种测量类型。 我想基于这两个类创建一个新类(SClass)。

任何对uSClass = 1和mS.m_1.5Class = 1的观察都应该是SClass 1。

对uSClass = 1和mS.m_1.5Class = NA的任何观察都应该是SClass 1。

对uSClass = NA和mS.m_1.5Class = 1的任何观察都应该是SClass 1.等等......

任何有冲突类的观察(例如uSClass = 1和mS.m_1.5Class = 2)都不应该被分配一个类(NA)。 这是我的代码:

    std$SClass <- ifelse(std$uSClass == 1 & std$mS.m_1.5Class == 1, 1, 
                     ifelse(std$uSClass == 1 & is.na(std$mS.m_1.5Class), 1,
                        ifelse(is.na(std$uSClass) & std$mS.m_1.5Class == 1, 1,
                  ifelse(std$uSClass == 2 & std$mS.m_1.5Class == 2, 2,
                     ifelse(std$uSClass == 2 & is.na(std$mS.m_1.5Class), 2,
                        ifelse(is.na(std$uSClass) & std$mS.m_1.5Class == 2, 2,
                  ifelse(std$uSClass == 3 & std$mS.m_1.5Class == 3, 3,
                     ifelse(std$uSClass == 3 & is.na(std$mS.m_1.5Class), 3,
                        ifelse(is.na(std$uSClass) & std$mS.m_1.5Class == 3, 3, NA)))))))))

这对我来说是合乎逻辑的,但它一定不正确。唯一有效的分类是uSClass和mS.m_1.5Class都有值的分类。如果我运行整个代码,大多数观察都会被指定为NA。我尝试了其他几种方法运营商,但那些也没有工作。 感谢您的帮助!

3 个答案:

答案 0 :(得分:2)

您可能正在寻找rowMeans作为逻辑捷径。

rowMeans(mydata, na.rm=TRUE)

示例

#Create example with all possible combinations
std <- expand.grid(c(1:3,NA), c(1:3,NA))
ind <- apply(std, 1, function(x) anyDuplicated(x) | any(is.na(x)))

mydata <- std[ind,]
mydata
#    Var1 Var2
# 1     1    1
# 4    NA    1
# 6     2    2
# 8    NA    2
# 11    3    3
# 12   NA    3
# 13    1   NA
# 14    2   NA
# 15    3   NA
# 16   NA   NA

示例已设置完毕。这里结合1到3和NA的所有可能方式。我们使用rowMeans来解决问题:

mydata$SClass <- rowMeans(mydata, na.rm=TRUE)
mydata
#    Var1 Var2 SClass
# 1     1    1      1
# 4    NA    1      1
# 6     2    2      2
# 8    NA    2      2
# 11    3    3      3
# 12   NA    3      3
# 13    1   NA      1
# 14    2   NA      2
# 15    3   NA      3
# 16   NA   NA    NaN

修改

如果也存在一些不匹配,则没有区别。我们可以补充:

r <- rowMeans(std, na.rm=TRUE)
is.na(r) <- !r %in% 1:3 | std[,1] != std[,2]

#Verification
cbind(std, r)
   Var1 Var2  r
1     1    1  1
2     2    1 NA
3     3    1 NA
4    NA    1  1
5     1    2 NA
6     2    2  2
7     3    2 NA
8    NA    2  2
9     1    3 NA
10    2    3 NA
11    3    3  3
12   NA    3  3
13    1   NA  1
14    2   NA  2
15    3   NA  3
16   NA   NA NA

在上面验证所有可能的组合都是正确的。

速度测试

怀疑者的东西。快5000%

Unit: milliseconds
       expr        min         lq      mean    median        uq       max neval cld
 plafortune   7.370385   9.246964  10.44307  10.10766  11.55795  18.72463   100  a 
      dayne 443.972804 506.965996 555.80049 550.91229 582.45713 831.18534   100   b

数据

std <- data.frame(x=sample(c(1:3,NA), 1e5, T), y=sample(c(1:3,NA), 1e5, T))

getClass <- function(c1, c2) {
  if (!is.na(c1) && !is.na(c2)) {
    return(NA)
  } else {
    return(ifelse(is.na(c1), c2, c1))
  }
  NA
}

library(microbenchmark)
microbenchmark(plafortune={r <- rowMeans(std, na.rm=TRUE)
is.na(r) <- !r %in% 1:3 | std[,1] != std[,2]},
dayne = {mapply(getClass, c1 = std[,1], c2 = std[,2])})

答案 1 :(得分:2)

rowMeans方法在这种情况下运作良好,并且很难以速度方式击败。对于更通用的方法,您正在做的大部分工作是在一系列列中查找非缺失值。这通常称为&#34; coalesce&#34; ,它内置于dplyr包(以及其他)。

如果您没有不匹配,那么您的操作可以简化为此(使用Pierre的良好共享数据):

with(mydata, dplyr::coalesce(Var1, Var2))
#    Var1 Var2  r
# 1     1    1  1
# 4    NA    1  1
# 6     2    2  2
# 8    NA    2  2
# 11    3    3  3
# 12   NA    3  3
# 13    1   NA  1
# 14    2   NA  2
# 15    3   NA  3
# 16   NA   NA NA

如果不匹配,我们需要单独检查:

std$r = with(std, ifelse(Var1 != Var2 & !is.na(Var1) & !is.na(Var2), NA,
                         coalesce(Var1, Var2)))
#    Var1 Var2  r
# 1     1    1  1
# 2     2    1 NA
# 3     3    1 NA
# 4    NA    1  1
# 5     1    2 NA
# 6     2    2  2
# 7     3    2 NA
# 8    NA    2  2
# 9     1    3 NA
# 10    2    3 NA
# 11    3    3  3
# 12   NA    3  3
# 13    1   NA  1
# 14    2   NA  2
# 15    3   NA  3
# 16   NA   NA NA

我们也可以回到ifelse寻找一个很好的矢量化解决方案。我已将其包含在@ dayne的答案中,但我已使用向量化的ifelse而不是if(){}else{}以及对{{1}的外部调用获得了很大的速度提升(尽管mapply仍然是最快的):

rowMeans

运行大数据(1e5行),getClass3 <- function(c1, c2) { ifelse((!is.na(c1) & !is.na(c2)), ifelse(c1 == c2, c1, NA), ifelse(is.na(c1), c2, c1)) } microbenchmark(plafortune = { r <- rowMeans(std, na.rm = TRUE) is.na(r) <- !r %in% 1:3 | std[, 1] != std[, 2] }, dayne = { mapply(getClass2, c1 = std[, 1], c2 = std[, 2]) }, coal = { ifelse(std[, 1] != std[, 2] & !is.na(std[, 1]) & !is.na(std[, 2]), NA, coalesce(std[, 1], std[, 2])) }, getClass_ifelse = { getClass3(std[, 1], std[, 2]) } ) # Unit: milliseconds # expr min lq mean median uq max neval cld # plafortune 10.09130 10.49593 18.95146 12.31516 14.46738 194.7095 100 a # dayne 466.60288 499.47639 552.12454 529.53229 573.53311 823.2745 100 d # coal 20.70184 24.10026 40.87038 26.22795 31.20252 217.3142 100 b # getClass_ifelse 50.90161 56.41823 96.69930 64.78723 95.32416 262.2016 100 c 绝对是最快的。 Coalesce表现相当不错,矢量化rowMeans仍然比1-line-at-time版本快一个数量级。值得注意的是,如果涉及更多列,ifelse优势可能会增长,而且到目前为止最容易鳕鱼。

答案 2 :(得分:0)

我认为这可以满足您的要求:

getClass <- function(c1, c2) {
  if (!is.na(c1) && !is.na(c2)) {
    return(NA)
  } else {
    return(ifelse(is.na(c1), c2, c1))
  }
  NA
}

c1 <- c(1,  2, NA, 3, NA, NA,  2, NA,  1)
c2 <- c(NA, NA, 1, 2,  1,  3, NA, NA, NA)
mapply(getClass, c1 = c1, c2 = c2)
 # [1]  1  2  1 NA  1  3  2 NA  1

修改

如果您希望值具有相同的类来返回该类,只需修改第一个if语句:

getClass2 <- function(c1, c2) {
  if (!is.na(c1) && !is.na(c2) && c1 != c2) {
    return(NA)
  } else {
    return(ifelse(is.na(c1), c2, c1))
  }
  NA
}
c1 <- c(1,  2, NA, 3, NA, NA,  2, NA,  1, 1, 2, 3)
c2 <- c(NA, NA, 1, 2,  1,  3, NA, NA, NA, 1, 2, 3)
mapply(getClass2, c1 = c1, c2 = c2)
# [1]  1  2  1 NA  1  3  2 NA  1  1  2  3