65

SAS コードを R に変換する方法をまだ学習中で、警告が表示されます。どこで間違いを犯しているのかを理解する必要があります。私がやりたいことは、母集団の 3 つのステータス (本土、海外、外国人) を要約して区別する変数を作成することです。私は2つの変数を持つデータベースを持っています:

  • id 国籍: idnat(フランス人, 外国人),

idnatがフランス語の場合:

  • id 出身地: idbp(本土、植民地、海外)

と呼ばれる新しい変数からの情報idnatとそこへの情報を要約したいと思います。idbpidnat2

  • ステータス: k (本土、海外、外国人)

これらの変数はすべて「文字タイプ」を使用します。

列 idnat2 で期待される結果:

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

Rで翻訳したいSASコードは次のとおりです。

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 よりも大きいため、最初の括弧の間だけが考慮されます。申し訳ありませんが、この長さとここでの関係がわかりませんか? 私が間違っている場所を知っている人はいますか?

4

9 に答える 9

127

スプレッドシート アプリケーションを使用している場合は、次のif()構文を持つ基本的な関数があります。

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

ifelse()構文はRの場合とまったく同じです。

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

スプレッドシート アプリケーションとの唯一の違いif()は、Rifelse()がベクトル化されていることです (ベクトルを入力として取り、出力でベクトルを返します)。次の表計算アプリケーションと R の数式の比較を考えてみましょう。例として、a > b の場合に比較し、そうでない場合は 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 ドキュメンテーション

とは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 に精通している場合は、パッケージ内のCASE ステートメントも使用できます。sqldf

于 2013-08-02T12:27:37.383 に答える
13

Try something like the following:

# 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

Your confusion comes from how SAS and R handle if-else constructions. In R, if and else are not vectorized, meaning they check whether a single condition is true (i.e., if("french"=="french") works) and cannot handle multiple logicals (i.e., if(c("french","foreigner")=="french") doesn't work) and R gives you the warning you're receiving.

By contrast, ifelse is vectorized, so it can take your vectors (aka input variables) and test the logical condition on each of their elements, like you're used to in SAS. An alternative way to wrap your head around this would be to build a loop using if and else statements (as you've started to do here) but the vectorized ifelse approach will be more efficient and involve generally less code.

于 2013-08-02T08:47:40.963 に答える
8

ベクトルは とidnat2なしifで作成できますifelse

この関数を使用して、出現するreplaceすべてを で置き換えることができます。"colony""overseas"

idnat2 <- replace(idbp, idbp == "colony", "overseas")
于 2013-08-02T16:18:25.193 に答える
2

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
于 2016-09-19T09:22:52.337 に答える