26

我想创建一个新变量,该变量等于其他两个变量之一的值,条件是其他变量的值。这是一个带有假数据的玩具示例。

数据框的每一行代表一个学生。每个学生最多可以学习两门科目(subj1subj2),并且可以在每个科目中攻读学位(“BA”)或辅修(“MN”)。我的真实数据包括数千名学生,几种类型的学位,大约50个科目,学生最多可以有五个专业/辅修。

   ID  subj1 degree1  subj2 degree2
1   1    BUS      BA   <NA>    <NA>
2   2    SCI      BA    ENG      BA
3   3    BUS      MN    ENG      BA
4   4    SCI      MN    BUS      BA
5   5    ENG      BA    BUS      MN
6   6    SCI      MN   <NA>    <NA>
7   7    ENG      MN    SCI      BA
8   8    BUS      BA    ENG      MN
...

现在我想创建第六个变量 ,df$major它等于subj1if的值subj1是学生的主要专业,或者subj2if的值subj2是主要的专业。初级专业是学位等于“BA”的第一门学科。我尝试了以下代码:

df$major[df$degree1 == "BA"] = df$subj1
df$major[df$degree1 != "BA" & df$degree2 == "BA"] = df$subj2

不幸的是,我收到一条错误消息:

> df$major[df$degree1 == "BA"] = df$subj1
Error in df$major[df$degree1 == "BA"] = df$subj1 : 
  NAs are not allowed in subscripted assignments

我认为这意味着如果至少一行的赋值评估为 NA,则不能使用矢量化赋值。

我觉得我必须在这里遗漏一些基本的东西,但上面的代码似乎是显而易见的事情,我无法想出替代方案。

如果它有助于编写答案,这里是使用创建的示例数据,dput()格式与上面列出的假数据相同:

structure(list(ID = 1:20, subj1 = structure(c(3L, NA, 1L, 2L, 
2L, 3L, 2L, 1L, 2L, 2L, 1L, 2L, 1L, 1L, 1L, 3L, 3L, 1L, 2L, 1L
), .Label = c("BUS", "ENG", "SCI"), class = "factor"), degree1 = structure(c(2L, 
NA, 1L, 1L, 1L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L), .Label = c("BA", "MN"), class = "factor"), subj2 = structure(c(1L, 
2L, NA, NA, 1L, NA, 3L, 2L, NA, 2L, 2L, 1L, 3L, NA, 2L, 1L, 1L, 
NA, 2L, 2L), .Label = c("BUS", "ENG", "SCI"), class = "factor"), 
    degree2 = structure(c(2L, 2L, NA, NA, 2L, NA, 1L, 2L, NA, 
    2L, 1L, 1L, 2L, NA, 1L, 2L, 2L, NA, 1L, 2L), .Label = c("BA", 
    "MN"), class = "factor")), .Names = c("ID", "subj1", "degree1", 
"subj2", "degree2"), row.names = c(NA, -20L), class = "data.frame")
4

2 回答 2

35

您最初的分配方法失败至少有两个原因。

1) 下标赋值有问题df$major[df$degree1 == "BA"] <-。使用==can generate NA,这是提示错误的原因。来自?"[<-":“当替换(即在赋值的 lhs 上使用索引)时,NA 不会选择任何要替换的元素。由于是否应该使用 rhs 的元素存在歧义,只有在以下情况下才允许这样做rhs 值的长度为 1(因此两种解释将具有相同的结果)。” 有很多方法可以解决这个问题,但我更喜欢使用which

df$major[which(df$degree1 == "BA")] <-

不同之处在于==返回TRUE,FALSENA, 而which返回 TRUE 对象的索引

> df$degree1 == "BA"
 [1] FALSE    NA  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

> which(df$degree1 == "BA")
 [1]  3  4  5  8  9 10 11 12 13 14 15 16 17 18 19 20

2)当您执行下标赋值时,右侧需要明智地适合左侧(这是我的想法)。这可能意味着左右手边的长度相等,这就是您的示例所暗示的。因此,您还需要对赋值的右侧进行子集化:

df$major[which(df$degree1 == "BA")] <- df$subj1[which(df$degree1 == "BA")]

我希望能澄清您最初的尝试产生错误的原因。

正如@DavidRobinsonifelse所建议的那样,使用 是进行此类分配的好方法。我的看法:

df$major2 <- ifelse(df$degree1 == "BA", df$subj1, ifelse(df$degree2 == "BA",
  df$subj2,NA))

这相当于

df$major[which(df$degree1 == "BA")] <- df$subj1[which(df$degree1 == "BA")]
df$major[which(df$degree1 != "BA" & df$degree2 == "BA")] <- 
  df$subj2[which(df$degree1 != "BA" & df$degree2 == "BA")]

根据嵌套ifelse语句的深度,另一种方法可能更适合您的真实数据。


编辑:

我打算写原始代码失败的第三个原因(即df$major尚未分配),但它对我有用,而不必这样做。不过,这是我记得过去遇到的一个问题。您正在运行哪个版本的 R?ifelse()(对我来说是 2.15.0。)如果您使用该方法,则无需执行此步骤。使用时您的解决方案很好[,尽管我会选择

df$major <- NA

要获取主题的字符值,而不是因子级别索引,请使用as.character()(对于因子等效于 and 调用levels(x)[x]):

df$major[which(df$degree1 == "BA")] <- as.character(df$subj1)[which(df$degree1 == "BA")]
df$major[which(df$degree1 != "BA" & df$degree2 == "BA")] <- 
  as.character(df$subj2)[which(df$degree1 != "BA" & df$degree2 == "BA")]

方式相同ifelse()

df$major2 <- ifelse(df$degree1 == "BA", as.character(df$subj1),
  ifelse(df$degree2 == "BA", as.character(df$subj2), NA))
于 2012-05-07T22:24:56.083 回答
8

一般来说,ifelse函数是这些情况的正确选择,例如:

df$major = ifelse((!is.na(df$degree1) & df$degree1 == "BA") & (is.na(df$degree2) | df$degree1 != "BA"), df$subj1, df$subj2)

但是,它的精确使用取决于如果两者都是“BA” df$degree1,您会做什么。df$degree2

于 2012-05-07T21:52:50.220 回答