首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >嵌套ifelse语句

嵌套ifelse语句
EN

Stack Overflow用户
提问于 2013-08-02 08:29:58
回答 9查看 242.6K关注 0票数 67

我仍在学习如何将SAS代码转换为R,并收到警告。我需要弄清楚我在哪里犯错误。我想要做的是创建一个变量来总结和区分一个人口的三个状态:大陆,海外,外国人。我有一个包含两个变量的数据库:

  • id国籍:idnat (法语、外国人)

如果idnat是法语,那么:

  • 出生地:idbp (内地、殖民地、海外)

我想将来自idnatidbp的信息归纳为一个名为idnat2的新变量

  • 地位:K(内地、海外、外国人)

所有这些变量都使用“字符类型”。

预期结果在idnat2栏中:

代码语言:javascript
复制
   idnat     idbp   idnat2
1  french mainland mainland
2  french   colony overseas
3  french overseas overseas
4 foreign  foreign  foreign

下面是我想在R中翻译的SAS代码:

代码语言:javascript
复制
if idnat = "french" then do;
   if idbp in ("overseas","colony") then idnat2 = "overseas";
   else idnat2 = "mainland";
end;
else idnat2 = "foreigner";
run;

以下是我在R中的尝试:

代码语言:javascript
复制
if(idnat=="french"){
    idnat2 <- "mainland"
} else if(idbp=="overseas"|idbp=="colony"){
    idnat2 <- "overseas"
} else {
    idnat2 <- "foreigner"
}

我收到这样的警告:

代码语言:javascript
复制
Warning message:
In if (idnat=="french") { :
  the condition has length > 1 and only the first element will be used

有人建议我使用“嵌套ifelse”来代替它的简单性,但会收到更多警告:

代码语言:javascript
复制
idnat2 <- ifelse (idnat=="french", "mainland",
        ifelse (idbp=="overseas"|idbp=="colony", "overseas")
      )
            else (idnat2 <- "foreigner")

根据警告消息,长度大于1,因此将只考虑第一个括号之间的内容。对不起,我不明白这个长度和这里有什么关系?有人知道我哪里错了吗?

EN

回答 9

Stack Overflow用户

发布于 2013-08-02 12:27:37

如果您正在使用任何电子表格应用程序,则有一个具有语法的基本函数if()

代码语言:javascript
复制
if(<condition>, <yes>, <no>)

在R中,ifelse()的语法完全相同:

代码语言:javascript
复制
ifelse(<condition>, <yes>, <no>)

在电子表格应用程序中,与if()的唯一区别是R ifelse()是矢量化的(将向量作为输入,在输出时返回向量)。考虑下面对电子表格应用程序中公式和R中公式的比较,例如,如果a>b,则返回1,如果不是,则返回0。

在电子表格中:

代码语言:javascript
复制
  A  B C
1 3  1 =if(A1 > B1, 1, 0)
2 2  2 =if(A2 > B2, 1, 0)
3 1  3 =if(A3 > B3, 1, 0)

在R:

代码语言:javascript
复制
> a <- 3:1; b <- 1:3
> ifelse(a > b, 1, 0)
[1] 1 0 0

ifelse()可以以多种方式嵌套:

代码语言:javascript
复制
ifelse(<condition>, <yes>, ifelse(<condition>, <yes>, <no>))

ifelse(<condition>, ifelse(<condition>, <yes>, <no>), <no>)

ifelse(<condition>, 
       ifelse(<condition>, <yes>, <no>), 
       ifelse(<condition>, <yes>, <no>)
      )

ifelse(<condition>, <yes>, 
       ifelse(<condition>, <yes>, 
              ifelse(<condition>, <yes>, <no>)
             )
       )

要计算列idnat2,可以:

代码语言:javascript
复制
df <- read.table(header=TRUE, text="
idnat idbp idnat2
french mainland mainland
french colony overseas
french overseas overseas
foreign foreign foreign"
)

with(df, 
     ifelse(idnat=="french",
       ifelse(idbp %in% c("overseas","colony"),"overseas","mainland"),"foreign")
     )

R文件

the condition has length > 1 and only the first element will be used是什么?让我们看看:

代码语言:javascript
复制
> # What is first condition really testing?
> with(df, idnat=="french")
[1]  TRUE  TRUE  TRUE FALSE
> # This is result of vectorized function - equality of all elements in idnat and 
> # string "french" is tested.
> # Vector of logical values is returned (has the same length as idnat)
> df$idnat2 <- with(df,
+   if(idnat=="french"){
+   idnat2 <- "xxx"
+   }
+   )
Warning message:
In if (idnat == "french") { :
  the condition has length > 1 and only the first element will be used
> # Note that the first element of comparison is TRUE and that's whay we get:
> df
    idnat     idbp idnat2
1  french mainland    xxx
2  french   colony    xxx
3  french overseas    xxx
4 foreign  foreign    xxx
> # There is really logic in it, you have to get used to it

我还能用if()吗?是的,你可以,但语法并不那么酷:)

代码语言:javascript
复制
test <- function(x) {
  if(x=="french") {
    "french"
  } else{
    "not really french"
  }
}

apply(array(df[["idnat"]]),MARGIN=1, FUN=test)

如果您熟悉SQL,也可以在CASE 套餐中使用sqldf 套餐

票数 135
EN

Stack Overflow用户

发布于 2013-08-02 08:47:40

尝试如下所示:

代码语言:javascript
复制
# some sample data
idnat <- sample(c("french","foreigner"),100,TRUE)
idbp <- rep(NA,100)
idbp[idnat=="french"] <- sample(c("mainland","overseas","colony"),sum(idnat=="french"),TRUE)

# recoding
out <- ifelse(idnat=="french" & !idbp %in% c("overseas","colony"), "mainland",
              ifelse(idbp %in% c("overseas","colony"),"overseas",
                     "foreigner"))
cbind(idnat,idbp,out) # check result

您的困惑来自SAS和R如何处理if-else结构。在R中,ifelse不是向量化的,这意味着它们检查单个条件是否为真(即if("french"=="french")工作),并且不能处理多个逻辑(即if(c("french","foreigner")=="french")不工作),并且R会给出您收到的警告。

相反,ifelse是向量化的,因此它可以接受向量(也称为输入变量),并测试每个元素的逻辑条件,就像您在SAS中所习惯的那样。解决这个问题的另一种方法是使用ifelse语句构建一个循环(就像您在这里开始做的那样),但是矢量化的ifelse方法将更高效,并且通常只涉及较少的代码。

票数 13
EN

Stack Overflow用户

发布于 2017-09-29 07:47:25

如果数据集包含许多行,则使用data.table而不是嵌套的ifelse()连接查找表可能更有效。

提供了下面的查找表

代码语言:javascript
复制
lookup

idnat idbp idnat2 1: french mainland mainland 2: french colony overseas 3: french overseas overseas 4: foreign foreign foreign

和一个样本数据集

代码语言:javascript
复制
library(data.table)
n_row <- 10L
set.seed(1L)
DT <- data.table(idnat = "french",
                 idbp = sample(c("mainland", "colony", "overseas", "foreign"), n_row, replace = TRUE))
DT[idbp == "foreign", idnat := "foreign"][]

idnat idbp 1: french colony 2: french colony 3: french overseas 4: foreign foreign 5: french mainland 6: foreign foreign 7: foreign foreign 8: french overseas 9: french overseas 10: french mainland

然后我们可以在加入时进行更新。

代码语言:javascript
复制
DT[lookup, on = .(idnat, idbp), idnat2 := i.idnat2][]

idnat idbp idnat2 1: french colony overseas 2: french colony overseas 3: french overseas overseas 4: foreign foreign foreign 5: french mainland mainland 6: foreign foreign foreign 7: foreign foreign foreign 8: french overseas overseas 9: french overseas overseas 10: french mainland mainland

票数 9
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18012222

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档