2013-08-02 4 views
45

Я все еще учась переводить код SAS в R и получаю предупреждения. Мне нужно понять, где я делаю ошибки. То, что я хочу сделать, это создать переменную, которая суммирует и отличает 3 статуса населения: материк, за границей, иностранец. У меня есть база данных с 2-х переменных:Вложенный оператор ifelse

  • идентификатор национальности: idnat (французский, иностранец),

Если idnat является французский, то:

  • ID рождения: idbp (материк, колония, за границей)

Я хочу обобщить информацию о м idnat и idbp в новую переменную под названием idnat2:

  • статус: к (материк, за границей, иностранец)

Все тезисы переменные используют "тип символов".

Ожидаемые результаты в колонке idnat2:

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

Вот мой код SAS Я хочу перевести в R:

if idnat = "french" then do; 
    if idbp in ("overseas","colony") then idnat2 = "overseas"; 
    else idnat2 = "mainland"; 
end; 
else idnat2 = "foreigner"; 
run; 

Вот моя попытка в R:

if(idnat=="french"){ 
    idnat2 <- "mainland" 
} else if(idbp=="overseas"|idbp=="colony"){ 
    idnat2 <- "overseas" 
} else { 
    idnat2 <- "foreigner" 
} 

Я получаю это предупреждение:

Warning message: 
In if (idnat=="french") { : 
    the condition has length > 1 and only the first element will be used 

мне посоветовали использовать «вложенная ifelse» вместо своей легкости, но получить больше предупреждений:

idnat2 <- ifelse (idnat=="french", "mainland", 
     ifelse (idbp=="overseas"|idbp=="colony", "overseas") 
    ) 
      else (idnat2 <- "foreigner") 

Согласно сообщению Внимание длина больше, чем 1, то будет учитываться только то, что находится между первыми скобками. Извините, но я не понимаю, как эта длина связана с этим? Кто-нибудь знает, где я ошибаюсь?

+3

Не следует смешивать '' ifelse' и else'. – Roland

+0

@ Roland Вы правы, спасибо за совет, я только что положил результаты. Я хочу только в столбце idnat2, если это станет ясно. @KarlForner благодарит вас, это именно то, что я пытаюсь делать с простыми примерами, но я действительно борюсь с «R». Я попытался сделать то же самое в SPSS, и это было проще. – balour

+0

Могу сказать, что SO не является заменой для изучения языка. Есть много книг, учебников ... Вы должны публиковать здесь, когда вы застряли, и вы использовали все остальные ресурсы. Лучший. –

ответ

10

попробовать что-то вроде следующего:

# 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-то еще конструкции. В R if и else не являются векторизованными, что означает, что они проверяют, является ли одно условие истинным (т. Е. Работает if("french"=="french")) и не может обрабатывать несколько логических элементов (то есть if(c("french","foreigner")=="french") не работает), и R дает вам предупреждение, которое вы получаете.

В отличие от этого, ifelse векторизован, поэтому он может принимать ваши векторы (как входные переменные) и проверять логическое условие для каждого из их элементов, например, вы привыкли в SAS.Альтернативным способом обертывания вокруг этого будет создание цикла с использованием операторов if и else (как вы начали здесь делать), но векторный подход ifelse будет более эффективным и, как правило, будет содержать меньше кода.

+0

Здравствуйте, хорошо IF и ELSE в R не векторизованы, поэтому я получил предупреждение о длине> 1 и записал только 1-й ИСТИННЫЙ аргумент. Я собираюсь попробовать ваш намек на IFELSE, похоже, что он более эффективен, хотя также и Tomas greif. – balour

77

Если вы используете любое приложение электронной таблицы есть основная функция if() с синтаксисом:

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

Синтаксис является точно такой же для ifelse() в R:

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

Единственная разница if() в приложение электронной таблицы состоит в том, что R ifelse() векторизован (принимает векторы в качестве вектора ввода и возврата на выходе). Рассмотрим следующее сравнение формул в приложении электронных таблиц и в R для примера, где мы хотели бы сравнить, если a> b и return 1, если да и 0, если нет.

В таблице:

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:

> a <- 3:1; b <- 1:3 
> ifelse(a > b, 1, 0) 
[1] 1 0 0 

ifelse() может быть вложен во многих отношениях:

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 вы можете:

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 Documentation

Что такое the condition has length > 1 and only the first element will be used? Давайте посмотрим:

> # 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()? Да, вы можете, но синтаксис не так круто :)

test <- function(x) { 
    if(x=="french") { 
    "french" 
    } else{ 
    "not really french" 
    } 
} 

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

Если вы знакомы с SQL, вы можете также использовать CASEstatement в sqldfpackage.

6

Вы можете создать вектор idnat2 без if и ifelse.

Функция replace может быть использован, чтобы заменить все вхождения "colony" с "overseas":

idnat2 <- replace(idbp, idbp == "colony", "overseas") 
+1

более или менее одинаковые: 'df $ idnat2 <- df $ idbp; df $ idnat2 [df $ idnat == 'colony'] <- 'за границей' – Jaap

1

С data.table, решением является:

DT[, idnat2 := ifelse(idbp %in% "foreign", "foreign", 
     ifelse(idbp %in% c("colony", "overseas"), "overseas", "mainland"))] 

ifelse является векторизован. if-else нет.Здесь DT является:

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

Это дает:

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

imo лучшим способом будет: 'DT [, idnat2: = idbp] [idbp% in% c (' colony ',' international '), idnat2: =' за границей '] ' – Jaap

+2

или даже лучше:' DT [, idnat2: = idbp] [idbp ==' colony ', idnat2: =' за границей '] ' – Jaap

+0

Другой способ« data.table » присоединиться к таблице поиска: 'DT [lookup, on =. (idnat, idbp), idnat2: = i.idnat2] []' – Uwe

3

Используя оператор CASE, SQL с dplyr и sqldf пакетов:

данных

df <-structure(list(idnat = structure(c(2L, 2L, 2L, 1L), .Label = c("foreign", 
"french"), class = "factor"), idbp = structure(c(3L, 1L, 4L, 
2L), .Label = c("colony", "foreign", "mainland", "overseas"), class = "factor")), .Names = c("idnat", 
"idbp"), class = "data.frame", row.names = c(NA, -4L)) 

sqldf

library(sqldf) 
sqldf("SELECT idnat, idbp, 
     CASE 
      WHEN idbp IN ('colony', 'overseas') THEN 'overseas' 
      ELSE idbp 
     END AS idnat2 
     FROM df") 

dplyr

library(dplyr) 
df %>% 
mutate(idnat2 = case_when(.$idbp == 'mainland' ~ "mainland", 
          .$idbp %in% c("colony", "overseas") ~ "overseas", 
         TRUE ~ "foreign")) 

Выход

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

Если набор данных содержит множество строк, может быть более эффективным, чтобы присоединиться к справочной таблице, используя data.table вместо вложенной ifelse().

При условии, что таблицы поиска ниже

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

и образец набора данных

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 

, то мы можем сделать обновление при вступлении:

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 
Смежные вопросы