【问题标题】:How to use R ifelse statements with multiple conditions?如何使用具有多个条件的 R ifelse 语句?
【发布时间】:2016-07-18 16:18:08
【问题描述】:

我是 R 新手,但很高兴学习它,我认为这可能是一个很好的机会。 我有两个盐度测量值(uS 和 mS.m_1.5)。我根据它们的值为每种测量类型(uSClass 和 mS.m_1.5Class)创建了 3 个类(1、2、3)。对于许多观察结果,我只有一种测量类型。 我想基于这两个类创建一个新类(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。我尝试了其他几种方法运营商,但那些也没有工作。 感谢您的帮助!

【问题讨论】:

  • 使用正式的 if/else 语句比将 ifelse 调用串在一起更好。

标签: r if-statement logical-operators


【解决方案1】:

您可能正在寻找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])})

【讨论】:

  • 但是,在某些情况下,例如,观察值可能是 Var1 的第 1 类和 Var2 的第 3 类,我需要为这些冲突的类分配 NA。
  • 查看编辑。只需删除不是原件之一的手段
  • mean(c(1, 3)) == 2 -- 解决方案的好主意,但如果给出的类别是 1 和 3,您将得到不正确的类别 2。
  • 请添加测速验证
  • 感谢您的回复!看起来这对我也有用。
【解决方案2】:

rowMeans 方法在这种情况下效果很好,并且很难在速度方面被击败。对于更通用的方法,您所做的大部分工作是在一系列列中查找非缺失值。这通常称为 “coalesce”,它内置于 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{},并且对mapply 的外部调用获得了很大的速度提升(尽管rowMeans 仍然是最快的):

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 

在大数据(1e5 行)上运行,rowMeans 绝对是最快的。 Coalesce 做得很好,矢量化的ifelse 仍然比一次一行的版本快一个数量级。值得注意的是,如果涉及更多列,rowMeansadvantage 可能会增长,并且它也将是到目前为止最容易编码的。

【讨论】:

  • 干得好。价值观似乎非常不同。 std 你用的是什么尺寸的?
  • 基准值。对于 10,000 行数据集,我平均看到大约 10 毫秒。基准测试使用了多大的数据集?
  • 所做的编辑 - 现在看起来(rowMeans 仍在顶部 :)
  • 我想我已经完成了这个答案的编辑,但是我用ifelse 替换为dplyr::if_else() 的coalesce 解决方案运行了一个基准测试——结果正好在当前@ 之间的中间987654337@ 计时和rowMeans 计时 - 令人印象深刻!
  • 这很有趣——我假设ifelse 速度较慢,并且将它们串在一起通常是不好的做法。谢谢你的课!
【解决方案3】:

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

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

【讨论】:

  • 谢谢!不能说我现在完全掌握了它,但它奏效了!
  • mapply 非常令人困惑。我会读到这个:stackoverflow.com/q/3505701/1623354
  • 您实际上并未调用基准测试中的函数。您忘记添加括号 f1()f2()。您的代码要慢几个数量级。
  • 已删除。愚蠢的错误——对不起。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 2019-05-06
相关资源
最近更新 更多