Rのifelse文が行う先行評価のせいで警告メッセージが出るときの対処法
放射能の測定結果の表し方にはお約束があって、検出下限値未満だったときは、検出下限値の数値の左側に不等号(小なり記号)をつけて表現します。
つまり
8.6
と書いてあれば検出値が8.6だったということですが、
<8.6
と書いてあれば、検出下限値が8.6という環境(測定器機の精度、試料の量、計測時間 etc.)で測定したが、値が小さいせいで検出されなかった、ということになります。
これを知らない人がやらかしてしまったのが、↓こちらの誤報だったりします。
新潟県:「女性セブン」誌の放射能関連記事に誤った内容が掲載されたため、訂正と謝罪を求める抗議文を送付しました。
「出典は厚生労働省のホームページであり、不等号(<)が付されている値が検出下限値であることを理解せず転記した」 by小学館
とのこと。記者一人が勘違いしたとしても、チェック機構はなかったんでしょうかね。不等号を見て、「これ何だろう?」って思わなかったんでしょうか。
で、話を戻しまして・・・
このような表記法となっておりますので、データはこんな感じになっていたりします。
> data
食品 結果
1 A <7.8
2 B 8.9
3 C <6.7
4 D 9.0
下限値未満をどう扱うかは分析の方針次第ですが、仮に「下限値未満はゼロとして扱う」としたかったとしましょう。その場合、不等号付きの結果を加工してやる必要があります。
また、data$結果はcharacter型かfactor型(因子型)になっているので、このままでは平均を出したり、ヒストグラムを書いたりはできません。
「結果」をnumeric型に変換した「結果2」という列を追加してみましょう。
for ( i in 1:nrow(data) ) {
if ( substr(data$結果[i], 1, 1) == "<" ) {
# 不等号付きのときは、ゼロにする
data$結果2[i] <- 0
} else {
# 不等号付きでないときは、そのまま数値にする
data$結果2[i] <- as.numeric(data$結果[i])
}
}
>data
食品 結果 結果2
1 A <7.8 0.0
2 B 8.9 8.9
3 C <6.7 0.0
4 D 9.0 9.0
結果2はnumeric型になっていますので、mean(data$結果2) とか、hist(data$結果2)とかすることも可能です。
が、このようにfor文とif文で処理する人はあまりいないと思います。Rにはifelseという便利な記法があり、しかもifelseは「ベクトル化された演算」(詳しくは「アート・オブ・Rプログラミング」参照)であるため、for文で記述するよりも高速に実行できるというメリットもあります。
先ほどのfor文によるループをifelseで書き直すと以下のようになります。
data$結果2 <- ifelse( substr(data$結果, 1, 1) == "<", 0, as.numeric(data$結果) )
ifelseは第1引数を評価して、真ならば第2引数を、偽ならば第3引数を返すという動きをします。引数としてベクトルを渡せば、すべての要素について処理を繰り返してくれます。
実行結果として生成される、データフレームの列は、先ほどのfor文の場合と同じになります。
が、しかし、上記の書き方だと、↓こんなwarningが出てしまうんですよね。
警告メッセージ:
In ifelse(substr(data$結果, 1, 1) == "<", 0, as.numeric(data$結果)) :
強制変換により NA が生成されました
結果の中にNAなどないし、強制変換しなきゃいけないような処理もしていないはずだし・・・、なぜでしょうか?
どうやらこれは、ifelseが「第1引数の真偽にかかわらず、第2引数・第3引数を評価する」という動きをしているのことに起因しているようです。分岐はせずにすべて評価しておいて、不要な結果は捨てるという動きをしているのでしょう。
つまり「<7.8」に対しても、as.numericしてしまい結果がNAとなる、でも第1引数の条件式は偽になるので、その結果は使われない。なので、最終結果としては問題なしです。
でも、警告が出るのが気持ち悪い。なんとかならないものか・・・
で、結論としては、下記のように書けばOKということが分かりました。(前置きが長くなりました・・・)
data$結果2 <- as.numeric( ifelse( substr(data$結果, 1, 1) == "<", 0, data$結果 ) )
最後にas.numericすればいいだけですね。0に対してもas.numericすることになってしまいますが、これについては警告は出ないので、こちらの方がベターかなと思います。
【重要な補足】
「結果」の列がcharacter型ではなく、factor型になっている場合があります。いやむしろデフォルトでCSVファイルを読み込むとそうなるので、そちらのケースが多いかもしれません。
その場合は・・・
data$結果2 <- as.numeric( ifelse( substr(data$結果, 1, 1) == "<", 0, as.character(data$結果) ) )
としてやる必要があります。
ファクタをそのまま as.numericすると文字列の表す数字ではなく、水準の値が返ってきてしまうんですよね。
水準というのは、"男"、"女"、"その他" というファクタがあったときに、内部的にはそれぞれに 1、2、3が割り当たっていたりするんですが、その数字のことです。
ファクタの水準値を使われてしまうと、数値が明らかにおかしくなるので、変換後のデータフレームの内容をちゃんとチェックしてから、次の処理を行いましょう。
つまり
8.6
と書いてあれば検出値が8.6だったということですが、
<8.6
と書いてあれば、検出下限値が8.6という環境(測定器機の精度、試料の量、計測時間 etc.)で測定したが、値が小さいせいで検出されなかった、ということになります。
これを知らない人がやらかしてしまったのが、↓こちらの誤報だったりします。
新潟県:「女性セブン」誌の放射能関連記事に誤った内容が掲載されたため、訂正と謝罪を求める抗議文を送付しました。
「出典は厚生労働省のホームページであり、不等号(<)が付されている値が検出下限値であることを理解せず転記した」 by小学館
とのこと。記者一人が勘違いしたとしても、チェック機構はなかったんでしょうかね。不等号を見て、「これ何だろう?」って思わなかったんでしょうか。
で、話を戻しまして・・・
このような表記法となっておりますので、データはこんな感じになっていたりします。
> data
食品 結果
1 A <7.8
2 B 8.9
3 C <6.7
4 D 9.0
下限値未満をどう扱うかは分析の方針次第ですが、仮に「下限値未満はゼロとして扱う」としたかったとしましょう。その場合、不等号付きの結果を加工してやる必要があります。
また、data$結果はcharacter型かfactor型(因子型)になっているので、このままでは平均を出したり、ヒストグラムを書いたりはできません。
「結果」をnumeric型に変換した「結果2」という列を追加してみましょう。
for ( i in 1:nrow(data) ) {
if ( substr(data$結果[i], 1, 1) == "<" ) {
# 不等号付きのときは、ゼロにする
data$結果2[i] <- 0
} else {
# 不等号付きでないときは、そのまま数値にする
data$結果2[i] <- as.numeric(data$結果[i])
}
}
>data
食品 結果 結果2
1 A <7.8 0.0
2 B 8.9 8.9
3 C <6.7 0.0
4 D 9.0 9.0
結果2はnumeric型になっていますので、mean(data$結果2) とか、hist(data$結果2)とかすることも可能です。
が、このようにfor文とif文で処理する人はあまりいないと思います。Rにはifelseという便利な記法があり、しかもifelseは「ベクトル化された演算」(詳しくは「アート・オブ・Rプログラミング」参照)であるため、for文で記述するよりも高速に実行できるというメリットもあります。
先ほどのfor文によるループをifelseで書き直すと以下のようになります。
data$結果2 <- ifelse( substr(data$結果, 1, 1) == "<", 0, as.numeric(data$結果) )
ifelseは第1引数を評価して、真ならば第2引数を、偽ならば第3引数を返すという動きをします。引数としてベクトルを渡せば、すべての要素について処理を繰り返してくれます。
実行結果として生成される、データフレームの列は、先ほどのfor文の場合と同じになります。
が、しかし、上記の書き方だと、↓こんなwarningが出てしまうんですよね。
警告メッセージ:
In ifelse(substr(data$結果, 1, 1) == "<", 0, as.numeric(data$結果)) :
強制変換により NA が生成されました
結果の中にNAなどないし、強制変換しなきゃいけないような処理もしていないはずだし・・・、なぜでしょうか?
どうやらこれは、ifelseが「第1引数の真偽にかかわらず、第2引数・第3引数を評価する」という動きをしているのことに起因しているようです。分岐はせずにすべて評価しておいて、不要な結果は捨てるという動きをしているのでしょう。
つまり「<7.8」に対しても、as.numericしてしまい結果がNAとなる、でも第1引数の条件式は偽になるので、その結果は使われない。なので、最終結果としては問題なしです。
でも、警告が出るのが気持ち悪い。なんとかならないものか・・・
で、結論としては、下記のように書けばOKということが分かりました。(前置きが長くなりました・・・)
data$結果2 <- as.numeric( ifelse( substr(data$結果, 1, 1) == "<", 0, data$結果 ) )
最後にas.numericすればいいだけですね。0に対してもas.numericすることになってしまいますが、これについては警告は出ないので、こちらの方がベターかなと思います。
【重要な補足】
「結果」の列がcharacter型ではなく、factor型になっている場合があります。いやむしろデフォルトでCSVファイルを読み込むとそうなるので、そちらのケースが多いかもしれません。
その場合は・・・
data$結果2 <- as.numeric( ifelse( substr(data$結果, 1, 1) == "<", 0, as.character(data$結果) ) )
としてやる必要があります。
ファクタをそのまま as.numericすると文字列の表す数字ではなく、水準の値が返ってきてしまうんですよね。
水準というのは、"男"、"女"、"その他" というファクタがあったときに、内部的にはそれぞれに 1、2、3が割り当たっていたりするんですが、その数字のことです。
ファクタの水準値を使われてしまうと、数値が明らかにおかしくなるので、変換後のデータフレームの内容をちゃんとチェックしてから、次の処理を行いましょう。
コメント
コメントを投稿