Условное присвоение одной переменной значению одной из двух других переменных - программирование
Подтвердить что ты не робот

Условное присвоение одной переменной значению одной из двух других переменных

Я хочу создать новую переменную, которая равна значению одной из двух других переменных, обусловленной значениями других переменных. Вот пример игрушки с поддельными данными.

Каждая строка кадра данных представляет студента. Каждый студент может изучать до двух предметов (subj1 и subj2) и может проходить степень ( "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, которая равна значению subj1, если subj1 является основным основным учеником или значением subj2, если 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")
4b9b3361

Ответ 1

Исходный метод назначения не работает по крайней мере по двум причинам.

1) Проблема с индексированным присваиванием df$major[df$degree1 == "BA"] <-. Использование == может вызвать NA, что и вызвало ошибку. From ?"[<-": "При замене (с использованием индексации на lhs присваивания) NA не выбирает какой-либо элемент для замены. Поскольку существует двусмысленность относительно того, должен ли использоваться элемент rhs или нет, это разрешено только в том случае, если значение rhs имеет длину 1 (поэтому две интерпретации будут иметь одинаковый результат)". Есть много способов обойти это, но я предпочитаю использовать which:

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

Отличие состоит в том, что == возвращает TRUE, FALSE и NA, а 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")]

Я надеюсь, что это разъяснит, почему ваша первоначальная попытка вызвала ошибку.

Использование ifelse, как предложено @DavidRobinson, является хорошим способом выполнения этого типа назначения. Я беру на себя это:

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 другой подход может быть лучше для ваших реальных данных.


EDIT:

Я собирался написать третью причину неудачного первоначального кода (а именно, что df$major еще не назначен), но он работает для меня, не делая этого. Это была проблема, которую я помню в прошлом. Какую версию R вы используете? (2.15.0 для меня.) Этот шаг не требуется, если вы используете подход ifelse(). Ваше решение прекрасно подходит при использовании [, хотя я бы выбрал

df$major <- NA

Чтобы получить знаковые значения объектов, вместо индекса коэффициента, используйте as.character() (что для коэффициентов эквивалентно и вызывает 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))

Ответ 2

В общем случае функция ifelse является правильным выбором для этих ситуаций, например:

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

Однако его точное использование зависит от того, что вы делаете, если df$degree1 и df$degree2 являются "BA".