2012-05-07 3 views
16

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

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

Есть строки, в которых оба degree1 и являются BA степени 2. Каким должен быть главный столбец в этих случаях? –

+0

Если deg1 = "BA", то subj1 является основным основным. Если deg1! = "BA" & deg2 == "BA", то subj2 является основным основным. С точки зрения проблемы, с которой я столкнулся, конкретные условия для выбора основного основного не так важны, как причина неудачи этого метода назначения. – eipi10

ответ

26

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

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

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

Разница заключается в том, что == возвращается TRUE, FALSE и NA, а which возвращает индексы объекта, истинны

> 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) Когда вы выполняете подстрочное задание, правая сторона должна сместиться в левую сторону разумно (так я думаю об этом). Это может означать левую и правую стороны равной длины, что что, кажется, подразумевает ваш пример. erefore, вам нужно будет подмножество в правую часть задания, а также:

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)) 
+0

+ 1- хороший ответ –

+0

Во-первых, спасибо для подробных объяснений! Очень полезно. – eipi10

+1

Во-вторых, когда я попробовал «какой» метод, я получил результат, которого я не ожидал. Вместо того, чтобы df $ major был установлен в соответствующие значения df $ subj1, df $ major присваивалось число, либо 1, 2, либо 3. Сначала я думал, что они соответствуют факторам в df $ subj1, но не было рифмы или причина, по которой числа в df $ major пошли с соответствующим значением df $ subj1. Вот что я побежал (по данным в моем вопросе): df1 $ major [which (df1 $ degree1 == "BA")] <- df1 $ subj1 [which (df1 $ degree1 == "BA")]. – eipi10

7

В общем, функция 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».

+0

Дэвид, в моем фактическом наборе данных, студенты также могут иметь «пре-майоры», если они официально не были допущены к майору. Если у ученика есть только пре-майор, я бы назвал его основным. Но если у них есть как майор, так и майор, я бы назначил майора в качестве основного. Могут ли выражения 'ifelse' быть вложенными для обработки сложных возможностей? – eipi10

+0

Да: типичный вложенный пример выглядел бы как 'ifelse (A, B, ifelse (C, D))' –

+0

(Если вы предоставите больше в своем примере, я мог бы показать вам) –

Смежные вопросы