2013-08-02 43 views
45

Tôi vẫn đang học cách dịch mã SAS thành R và tôi nhận được cảnh báo. Tôi cần phải hiểu nơi tôi đang phạm sai lầm. Những gì tôi muốn làm là tạo ra một biến mà tóm tắt và phân biệt 3 trạng thái của một dân số: đại lục, ở nước ngoài, người nước ngoài. Tôi có một cơ sở dữ liệu với 2 biến:Tuyên bố ifelse lồng nhau

  • id quốc tịch: idnat (Pháp, người nước ngoài),

Nếu idnat là tiếng Pháp thì:

  • id nơi sinh: idbp (đất liền, thuộc địa, ở nước ngoài)

Tôi muốn tóm tắt thông tin fro m idnatidbp vào một biến mới gọi là idnat2:

  • status: k (đất liền, ở nước ngoài, người nước ngoài)

Tất cả các đề tài biến sử dụng "loại nhân vật".

Kết quả dự kiến ​​trong cột idnat2:

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

Đây là mã SAS của tôi, tôi muốn dịch trong R:

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

Đây là nỗ lực của tôi trong R:

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

tôi nhận được cảnh báo này:

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

tôi được khuyên là nên sử dụng một "lồng nhau ifelse" thay cho yên thân của mình nhưng có thêm cảnh báo:

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

Theo thông điệp cảnh báo độ dài lớn hơn 1 vì vậy chỉ có những gì giữa các dấu ngoặc đầu tiên sẽ được đưa vào tài khoản. Xin lỗi nhưng tôi không hiểu những gì chiều dài này đã làm với ở đây? Có ai biết tôi sai ở đâu không?

+3

Bạn không nên trộn 'ifelse' và' else'. – Roland

+0

@ Roland Bạn cảm ơn vì lời khuyên, tôi vừa mới đưa ra kết quả. Những gì tôi muốn là chỉ trong cột idnat2 nếu nó làm cho nó rõ ràng. @ KarlForner cảm ơn bạn đó chính xác là những gì tôi đang cố gắng làm với các ví dụ đơn giản tuy nhiên tôi đang thực sự đấu tranh với "R". Tôi đã cố gắng làm tương tự trên SPSS và nó đơn giản hơn. – balour

+0

Điểm của tôi là SO không phải là sự thay thế cho việc học một ngôn ngữ. Có rất nhiều sách, hướng dẫn ... Bạn nên đăng ở đây khi bạn bị kẹt và bạn đã sử dụng tất cả các tài nguyên khác. Tốt. –

Trả lời

10

Hãy thử một cái gì đó như sau:

# 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 

nhầm lẫn của bạn xuất phát từ cách SAS và R xử lý if-else công trình xây dựng. Trong R, ifelse không được vector hóa, nghĩa là chúng kiểm tra xem điều kiện có đúng hay không (tức là, if("french"=="french") hoạt động) và không thể xử lý nhiều lôgic (tức là, if(c("french","foreigner")=="french") không hoạt động) và R cung cấp cho bạn cảnh báo bạn nhận được.

Ngược lại, ifelse được vector hóa, vì vậy nó có thể lấy các vectơ của bạn (còn gọi là biến đầu vào) và kiểm tra điều kiện logic trên từng phần tử của chúng, như bạn đã từng sử dụng trong SAS.Một cách khác để bao quanh đầu của bạn là xây dựng một vòng lặp sử dụng các câu lệnh ifelse (như bạn đã bắt đầu làm ở đây) nhưng cách tiếp cận ifelse được vectorized sẽ hiệu quả hơn và thường có ít mã hơn.

+0

Xin chào, được rồi IF và ELSE trong R không được vectơ vì vậy đó là lý do tại sao tôi nhận được cảnh báo về độ dài> 1 và chỉ ghi lại đối số TRUE thứ nhất. Tôi sẽ thử gợi ý của bạn về IFELSE có vẻ như nó hiệu quả hơn mặc dù Tomas Greif cũng vậy. – balour

77

Nếu bạn đang sử dụng bất kỳ ứng dụng bảng tính có một chức năng cơ bản if() với cú pháp:

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

Cú pháp là giống hệt nhau cho ifelse() trong R:

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

Sự khác biệt duy nhất if() trong ứng dụng bảng tính là R ifelse() được vectơ hóa (lấy các vectơ làm đầu vào và trả về vectơ trên đầu ra). Hãy xem xét so sánh sau đây của các công thức trong ứng dụng bảng tính và trong R cho một ví dụ mà chúng tôi muốn so sánh nếu a> b và trả về 1 nếu có và 0 nếu không.

Trong bảng tính:

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) 

Trong R:

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

ifelse() có thể được lồng vào nhau theo nhiều cách:

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

Để tính cột idnat2 bạn có thể:

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 là gì? Hãy xem:

> # 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 

Tôi vẫn có thể sử dụng if()? Vâng, bạn có thể, nhưng cú pháp không phải là quá mát mẻ :)

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

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

Nếu bạn đã quen thuộc với SQL, bạn cũng có thể sử dụng CASEstatement trong sqldfpackage.

6

Bạn có thể tạo véc tơ idnat2 mà không cần ififelse.

Chức năng replace có thể được sử dụng để thay thế tất cả các lần xuất hiện của "colony" với "overseas":

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

nhiều hơn hoặc ít hơn: 'df $ idnat2 <- df $ idbp; df $ idnat2 [df $ idnat == 'thuộc địa'] <- 'abroad'' – Jaap

1

Với data.table, các giải pháp là:

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

Các ifelse được vector hóa. if-else thì không.Ở đây, DT là:

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

Điều này cho phép:

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

imo một cách tốt hơn sẽ là: 'DT [, idnat2: = idbp] [idbp% trong% c (' thuộc địa ',' ở nước ngoài '), idnat2: =' abroad '] ' – Jaap

+2

hoặc thậm chí tốt hơn:' DT [, idnat2: = idbp] [idbp ==' thuộc địa ', idnat2: =' ở nước ngoài '] ' – Jaap

+0

Cách' dữ liệu khác' sẽ được tham gia với một bảng tra cứu: 'DT [tra cứu, trên =. (idnat, idbp), idnat2: = i.idnat2] []' – Uwe

3

Sử dụng câu lệnh CASE SQL với dplyr và sqldf gói:

liệu

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")) 

Output

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

Nếu bộ dữ liệu chứa nhiều hàng nó có thể hiệu quả hơn để tham gia với một bảng tra cứu sử dụng data.table thay vì lồng nhau ifelse().

Cung cấp các bảng tra cứu dưới đây

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

và dữ liệu mẫu thiết

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 

sau đó chúng ta có thể làm một bản cập nhật khi tham gia:

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