10万PVになりました。ありがとうございます。
今回は、このブログを見てくださった皆様へのお礼です。
100,000PV
100,000PVは、このブログを始めた時、そして始めてしばらくの間には考えもつかなかった数字です。しかも2014年11月に10,000pv達成だったので、1年1〜2ヶ月の間に指数的にpvが伸びています。割と思いついたこと、書きたいこと、書いておいた方が良いと思うことを自由に書いています。いわゆるブロガーを志しているわけでもないですし、アフィリエイトなども一切やっていません。そう考えると、100,000pvはできすぎな数字と言えます。
どんな人が見てくださったのか?
これは10,000pv達成した時にも書きましたが、平日のアクセスと休日のアクセスの落差がかなりあります。休日は平日の半分以下のアクセスです。内容としては、基本的な内容を検索で見つけた人が多いように思います。本ブログのタイトルは「webbeginner」なのでその趣旨にかなりあった結果となっています。嬉しい限りです。
そろそろ「ビギナー」ではないのでは?
確かに、「ビギナー」とは言えないなと思う部分(例えばR言語の使い方)とまだまだ初心者同然の部分(例えばD3,p5などのJavaScriptライブラリ)が混在しています。しかし、R言語の使い方については、自分が初学者ということではなく、自分に関わりのある初学者の皆様がウェブから検索で偶然拾ってくれないかなという淡い期待を込めつつ記事を投稿しています。なので「初心者むけ」というポリシーに広い意味で合致していると思っています。
1,000,000pvは現実的にありえないので
あと一桁増やすためには、どう考えても増やすための努力が必要です。最初に書いたようにプロブロガーを目指してこのブログを運営しているわけではなく、このペースが保たれれば十分だと考えています。今後は、少し込み入った内容についても、本業に支障のでない限りで書いてみたいと思っています。そのような内容は、「初心者向け」ではないので、投稿先をqiitaに変えるかもしれません。というわけで、以下が私のqiitaへのリンクです。こちらもぜひよろしくお願いします。
R言語でサイズの大きいファイルの読み込み
今回は、ファイルの入力に関するヒントを書きます。
ファイルからの入力はread.table関数
R言語では、baseパッケージの中にread.table関数、read.csv関数などがあります。これを利用すれば、簡単にテキストファイルとして保存されているデータを読み込めます。しかし、read.table関数で作成されるdata.frameオブジェクトは容量制限が結構きびしく、20変数で100万行あるようなデ−タだと容量ぎりぎりになります。問題は、ファイルを中までしか読んでいなくても警告しか出ないことです。そのままコードが走ってしまうので、きちんと確認しないと実はファイルがうまく読み込めていなかったということになりかねません。
data.tableパッケージの利用
そこで、data.tableパッケージを利用します。data.tableオブジェクトは、data.frameよりも大きなデータを高速に読み込むことができます。
data.tableオブジェクトに入力するにはfread関数を用います。
#data.tableパッケージの起動 library(data.table) #ファイルの読み込み dat <- fread("filename.txt", sep = "\t")
なぜ分散分析には自由度が2つあるのか?
今回は、分散分析という解析方法の紹介をします。
平均値の差の検定です。
分散分析は、3つ以上の条件やグループがあるときの平均値の条件差・グループ間の差を比較するときに使います。確率の考え方を取り入れることで、平均の差が偶然と呼べる程度の差なのか、それとも偶然からはありえないほど大きな差になっているのかを明らかにできます。分散分析という名前(英語だとANalysis Of VArianceで大文字の部分をとってANOVAとも言われます )から想像すると、分散の大きさの比較なのかなと思うかもしれませんが、実は平均の比較をしています。
平均の差の大きさをどうやって評価するのか。
条件やグループの平均の差は、同一条件、同一グループのばらつきに対する相対的な大きさとして評価します。統計的仮説検定としての分散分析は、条件、グループの平均値の差が0であることを帰無仮説*1にします。つまり、観測された条件間のばらつきは偶然得られたに過ぎず、その大きさは、条件内・グループ内の変動と等しいとなります。これを比で示したのがF値という統計量です。
そしてこのF値という統計量には2つの自由度が伴います。
なぜ2つの自由度があるのか
2つの自由度はそれぞれ「分子の自由度」と「分母の自由度」と呼ばれることがあります。実は、これが上記の式に対応しており、分子の自由度は条件・グループの平均の自由度、分母の自由度はグループ内のデータのばらつきの自由度を示しています。以下では3つの条件を比較する1要因分散分析の場合で考えてみます。
条件・グループの平均の自由度
では、分子の自由度の意味を少し考えてみます。分子の自由度は
です。例えば3つのグループを比較するときの自由度は2になります。自由度というのは、値の取り方の自由度です。グループを考慮に入れない全体の平均値がわかっているとき、3つのうち2つのグループの平均値がわかると残り一つのグループの平均値は自然に決まります。
図式的に考えれば以下のようになります。
各グループの全てがグループの平均値と一致している状態です。データがたくさんあっても、データの値の取り方は限られており、全体平均がわかっているなら、あと2つの値は自由にとることができます。
条件内・グループ内のばらつきの自由度
次は、条件内・グループ内のデータのばらつきです。ここでも自由度の値の取り方は決まっています。グループの平均値がもとまっていれば、グループ内に含まれるデータの最後の1つは、他の値がわかると自然に決まります。
これを全てのグループについて足し合わせるので、以下のようになります。
図式的に考えれば、以下のように、各データのグループ平均からのズレを考えていることになります。
Rを使ってF値を求める
ではR言語でF値を計算してみます。基本的には以下のlm関数またはaov関数を利用すれば簡単に計算可能です。
lm関数/ aov関数の利用
特に1つの被験者間要因分散分析ならlm関数かaov関数かで計算結果が変わらないので、どちらでも好きな方を使ってください。
#サンプルデータ dat <- data.frame(dv = c(3.188957, 3.188957, 3.188957, 3.188957, 3.188957, 6.375809, 6.375809, 6.375809, 6.375809, 6.375809, -9.016713, -9.016713, -9.016713, -9.016713, -9.016713), iv = c("red", "red", "red", "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue")) #lm関数の場合 res_lm <- lm(dv ~ iv, data =dat) anova(res_lm) #aov関数の場合 res_aov <- aov(dv ~ iv, data =dat) summary(res_aov)
条件間・グループ間の平均値の差と条件内・グループ内のデータのばらつきを比較する
次に、条件間・グループ間の平均値の差をグループ内のデータのばらつきを比較するすることを具体的な数値計算とともに考えてみましょう。
仮想のデータセットをつくる
ここでは、本来全く差がない3つのグループを考えます。R言語のrnorm関数では、任意の平均値と分散のデータをランダムに作成できるので、これを利用しましょう。
dat <- data.frame(dv = c(rnorm(5),rnorm(5),rnorm(5)),iv = c("red", "red", "red", "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue"))
このデータにはred,orange,blueの3つのグループがあり、それぞれ平均0、分散1の分布から5つのデータがサンプルとして取られています*2。
条件間・グループ間の平均値のばらつきの分布
条件間・グループ間の平均値が、全体の平均に比べてどの程度ばらつくのかは、平方和という値で考えます。各データが、グループの平均値に一致しているとみなして、それが全体の平均からのズレを計算し、二乗します。それを足し合わせたものが平方和です。二乗は平方とも呼ばれるので、二乗したものの合計という意味で平方和という言い方をしています。
R言語を利用するなら例えば以下のように計算できます。
#データ全体の平均値を計算する mean_dat <- mean(dat$dv) #各グループの平均値を計算する mean_red <- mean(dat[dat[,"iv"]== "red","dv"]) mean_orange <- mean(dat[dat[,"iv"]== "orange","dv"]) mean_blue <- mean(dat[dat[,"iv"]== "blue","dv"]) #平方和を求める MSS_group <- ((mean_red - mean_dat)^2*length(dat[dat[,"iv"]== "red","dv"]) + (mean_orange - mean_dat)^2*length(dat[dat[,"iv"]== "orange","dv"]) + (mean_blue - mean_dat)^2*length(dat[dat[,"iv"]== "blue","dv"]))
これを何度も繰り返すと、以下のように平方和の分布を求めることができます。
#上述の平方和を求める数式を関数にする getSS_between <- function(){ dat <- data.frame(dv = c(rnorm(5),rnorm(5),rnorm(5)),iv = c("red", "red", "red", "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue")) #データ全体の平均値を計算する mean_dat <- mean(dat$dv) #各グループの平均値を計算する mean_red <- mean(dat[dat[,"iv"]== "red","dv"]) mean_orange <- mean(dat[dat[,"iv"]== "orange","dv"]) mean_blue <- mean(dat[dat[,"iv"]== "blue","dv"]) #平方和を求める SS_between <- ((mean_red - mean_dat)^2*length(dat[dat[,"iv"]== "red","dv"]) + (mean_orange - mean_dat)^2*length(dat[dat[,"iv"]== "orange","dv"]) + (mean_blue - mean_dat)^2*length(dat[dat[,"iv"]== "blue","dv"])) return(MSS_group) } #100000回繰り返す res <- replicate(100000,getSS_between())
実際にやってみたところ、このような分布ができました。
理論的には、自由度2のカイ二乗分布となります。
条件内・グループ内のデータのばらつき
では、次に、条件内・グループ内のデータのばらつきを考えてみましょう。先ほどのグループ間の計算と同じように、平均値からのズレを二乗したものを合計して平方和を求めます。
#グループ内の平方和を求める関数 getSS_within <- function(){ dat <- data.frame(dv = c(rnorm(5),rnorm(5),rnorm(5)),iv = c("red", "red", "red", "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue")) #各グループの平均値を計算する mean_red <- mean(dat[dat[,"iv"]== "red","dv"]) mean_orange <- mean(dat[dat[,"iv"]== "orange","dv"]) mean_blue <- mean(dat[dat[,"iv"]== "blue","dv"]) #平方和を求める SS_within <- sum((dat[dat[,"iv"] == "red","dv"] - mean_red)^2) + sum((dat[dat[,"iv"] == "orange","dv"] - mean_orange)^2) + sum((dat[dat[,"iv"] == "blue","dv"] - mean_blue)^2) return(SS_within) } #100000回繰り返す res <- replicate(100000,getSS_within())
実際に100000回繰り返して分布を描き出すと以下のようになります。
これは理論として自由度12(データ数15から3を引いた)のカイ二乗分布に一致しました。
F分布
F分布は、2つの平方和のそれぞれを自由度で割った値(平均平方和)の比で求めます。分布の形状としては、分子であるグループ間の平均値のばらつきを示す平方和の分布形状に影響を受けやすく、類似します。
まとめ
以上、長くなりましたが、分散分析の2つの自由度は、条件間・グループ間の平均値の差に関する自由度と条件内・グループ内のデータのばらつきについての自由度をあらわてしていることをみてきました。途中で示したように、lm関数やaov関数を利用すればあっという間にF値とp値が求まるのですが、たまにはこうやって計算を分解して見ながら考えると、統計量をより深く理解できますね。
卒論でよく言われる「まだ検討されていない」研究テーマについて思うこと
大学の卒業論文の季節ですね。今回は、卒業論文でよく見られる表現について考えたことを書きます。
科学が目指すもの
科学が目指すものは「新たな知の獲得」です*1。今まで誰も知らなかったことを調べてみて、面白い結果が得られたらそれを皆で共有します。さて、「今まで誰も知らなかったこと」と聞いて、これだけ人類の歴史があって、科学者もたくさんいたんだからまだわかっていないことなんて、ほとんどないのではないかと思うかもしれません。しかし、実際には、問題すら把握されていないような現象がまだまだたくさんあります。また、ある研究結果と別の研究結果を結びつける橋渡しのような研究もどんどん行われなければなりません。意外にもわかっていることは少ないんです。そこで、大学の学部生の卒業論文であったとしても、「研究」として新規な発想・方法・考察で新たな知見が望まれます。
「まだやられていない」理由はいろいろある
上記のような背景があって、卒業論文の目的には「こういうことはわかっているが、〜の内容はまだ検討されていないので、これを検討する」としばしばかかれます。しかし、よく考えてみると、この「〜はまだ検討されていない」は、「なぜ検討されていない」を考える必要がありそうです。少なくとも「〜はまだ検討されていない」には3つの理由があると思います。
理論・仮説の成り行き上重要であるが、まだ行われていないため
これが本来の「まだ検討されていない」の理由となるべきものです。あるテーマにおける理論にとって、重要でありつつも未だ踏み込めていない問題を提起し、それを検討することで、学術的に貢献しようというわけです。
理論・仮説の成り行き上重要だが、検討が困難ではっきりした証拠を見出せないため
実証研究でよくある話だと思います。基本的に、学術論文ははっきりとした証拠が伴わなければ審査を通りません。すると、誰もが重要であると認めているが、精度の高い実験や調査が困難で決定的な証拠がないため、刊行された論文が存在しないことがあります。これは、表面的に見れば、その分野の研究がないと言えます*2。検討の難しさを何も知らない学生がデータベースを検索しても、何もひっかからない。そこで、理論・仮説的に大事だと思えば、そのようなテーマを選ぶ可能性はかなり高いでしょう。しかし、既に多くの研究者が証拠を見つけることに失敗している現象を、学部の卒論で扱うのは失敗リスクが高すぎます。序論のテンションの高い議論が、後半の考察で苦し紛れの反省文に変わってしまうといった卒業論文はぜひ避けてもらいたいです。
理論・仮説の成り行きから乖離しているため
これも学部学生が思いつきだけで行っている研究にありがちなパターンのように思います。確かに誰もやっていないのですが、それは「やっても大した意味がないから」であって、たとえ明瞭な証拠が伴った主張であっても「だから何?」と言われる可能性があります。
ではどうすればいい?
月並みな意見ですが、指導教授や類似のテーマを扱っている先生、大学院生に相談したほうがいいでしょう。かなり研究を進めて、データも散々取ってから相談しても、文字通り後の祭りなので、早めの相談をお勧めします*3。
大学での授業ノートを貸し借りについて思うこと
今回も、日常的なテーマで思うことを書いてみます。今回のテーマは、一部の大学生にとって悩みの種になるようなもので、検索するといろいろな意見が飛び交っていて面白いです。例えば
上記のような悩み相談が数え切れないほどあります。なので、本当にどこにでもある人間関係の悩みのようです。以下では、借りる側、貸す側、教員としての3つの立場で思いついたことを書いてみます。ちなみにこれは妄想で、フィクションです。実話ではないので悪しからずご了承ください。
借りる側として
他人のノートを借りなければならない事態を避けるべきなのは間違いないとしても、「危機状態(なければテストが受けられない・レポートが書けない)」なのでどうしようもない。誰のノートでも構わないから、いつも授業に出ていることがわかっている友達に頼むしかないだろう。言ってみれば「溺れるものは藁をも掴む」状態。でも信頼性のある情報を取るという観点に立てば、「たまたま借りた人のノートをどこまで信じられるか」不明。できることなら「複数名のノートを借りてノートの信頼性の誤差を知りたい」ところ。それとやはり「ただでもらおう」という都合のよい考えは捨てるべき。情報やそのまとめに対する対価は支払って今後の人間関係の継続に支障をきたさないように。現金でないにしても、食事なりおやつなりご馳走するくらいのことはせめてものマナーとしておきたい。
貸す側として
他人にノートを貸す時には心情的に「借りた人はずるい」と思う。だってそれは、私が苦労してとったノートを楽々と手に入れているから。でも、先々までの人間関係を考えると断りにくい。何回も当たり前のように頼んでくる友達がいたらさすがにそれは断るべきか。でもやはり断りにくい。なので多分また貸してしまうだろう。
教員として
授業ノートを人に借りること事態はそんなに悪くないだろう。しかし、ろくに授業も出ていない学生が他人のノートで勉強したことにたいして単位認定していいものかどうか。欠席・遅刻・早退が多ければそれなりのペナルティは受けているので構わないか。それにしてもどうせ一部の学生のノートが出回るのなら、「正解のノート」が出回ってくれればいいのに。ちょっとありえないようなおかしなミスが、複数の学生の答案に出てきたときに複雑な気持ちになる。
ちょっと極論:俯瞰的にみたら
それぞれの立場を無視して、何が起きているかだけを観察するつもりになってみます。本来授業コンテンツは、教員の配布した資料(一次資料)と教員の説明で完結しており、それが手に入れば、自学自習できます。しかし、授業を欠席した学生はどちらも手に入っていないかもしれないので、これをすべて他人のノート(二次資料)で補おうとしているわけです。
ここで興味深い事実は、二次資料を作成したのが学生であることです。つまり、ここで資料コンテンツの創作・配布側と受け取り・消費側という2種類の学生がいることを意味します。ノートを貸す側は、たいてい人間関係にヒビを入れたくないという動機を持っています。ある意味、従う側にまわっています。しかし、実は、彼らはコンテンツを制作・配布する側であり、情報的に強い立場にいます。こういった優位性を自分自身が持っていることに気づけば、ノートを貸すにしても貸さないにしてもノートをとった本人の気持ちはある程度納得いくのかもしれないと思います。
もっと極論:貸すだけじゃなくて教えてみる
ノートをとった人は、ただそれを貸すだけじゃなくて、いっそのこと、ノートを一緒にみながらコーチしてみたらどうでしょうか。試験前・レポート提出前に時間がないという制約は確かに大きいのですが、人に説明することで自分の理解を確認し、不十分な知識を補う機会にできます。また、他人に自分の言いたいことを伝えるにはどのように話し、見せるべきなのかを考える良い機会にもなるでしょう*1。
*1:タイトルにもあるようにあくまで極論です。あとは、ノートだけならただで(どうせ減るもんじゃないし)という考えの人にも、積極的にコーチすれば、その対価が支払われる可能性が高いでしょう。周りにも一目置かれる存在になるかもしれないし、そういうのが面倒という人から声がかからなくなります
統計学を通して見える世界
今回は、統計学に基づいた研究の意義や世界の見え方について思うことを書きます。
研究にとっての統計的仮説検定というツール
研究では、何らかのアイデア(仮説と呼びます)が、実際に何かの現象をうまく説明できたり、何かに役立ったりすることなどを検証します。その際、多くの実証研究では統計的仮説検定という手続きを経て、主張の説得力(研究上の言葉で言えば妥当性)を高めようとします。統計的仮説検定では、帰無仮説と対立仮説という二つの仮説を立てます。
帰無仮説:研究者が主張したいこととは逆の内容にあたります。例えば、商品Aと商品Bは売れ方が違うと思っている時に、あえて商品Aと商品Bの売れ方は等しいと仮定するような仮説です。
対立仮説:研究者が主張したい仮説はこちらです。
統計的仮説検定は、主張したい内容と逆の帰無仮説を立てて、収集したデータの観測値が帰無仮説を前提にするとありえないことを示し、帰無仮説を取り下げます(棄却すると言います)。これはすなわち対立仮説を採用するということにあたります。
似たようなロジックの立て方に、数学の背理法があります。背理法も、ある仮定から式展開を始めて、明らかな矛盾が得られた時に、最初の仮定を取り下げることで、その逆の内容を採用するという手続きですね。
実証研究における統計的仮説検定と数学の背理法の違いは、ありえないこと(つまり矛盾)の示し方です。数学の背理法では、絶対的な矛盾を示しますが、実証研究の統計的仮説検定では「確率」を利用して、確率的に非常に低い、確率的にありえないということを示します。
第1種の過誤と第2種の過誤
上記で述べたように、統計的仮説検定では、確率の概念の導入が肝になっています。ここで確率で考えるということの意味を考えてみましょう。数学の絶対的な矛盾とことなり、統計的仮説検定では「ある事象の生起の可能性が非常に低い」という議論になります。しかし、「非常に低い」とはいえ、0ではないので、ここで主張されるのは絶対的な矛盾ではありません。そして、常に結論に誤りが生じる可能性を残すことになります。誤りには2種類あり以下のような名前がついています。
真の状態 | 正しい判断 | 誤った判断 |
帰無仮説が正しい | 帰無仮説を保持する | 帰無仮説を棄却する(第1種の過誤) |
対立仮説が正しい | 帰無仮説を棄却する | 帰無仮説を保持する(第2種の過誤) |
上の表にあるように第1種の過誤とは帰無仮説が正しいにもかかわらず、誤って棄却してしまうことです。最初に述べた例で考えれば、商品Aと商品Bの売り上げに差がないにもかかわらず、誤って差があるとしてしまうことにあたります。
第2種の過誤は、第1種の過誤の逆です。対立仮説が正しいにもかかわらず、帰無仮説を棄却しない場合にあたります。例で考えれば、商品Aと商品Bの売り上げに差があるにもかかわらず、そのような差を見つけられない場合にあたります。
第1種の過誤と第2種の過誤のトレードオフ
第1種の過誤と第2種の過誤。どちらもできるだけその可能性を低めたいのですが、これらの過誤の間にはトレードオフの関係があることが知られています。つまり、「ないものをある(第1種の過誤)」可能性を低めようとすることは、「あるものを見落とす(第2種の過誤)」可能性を高めることになります。なので、研究者は常にこの二つの過誤のバランスをとりながら研究の精度をたかめようとします。研究の場合、「ないものをある(第1種の過誤)」というのは危険なので、「あるものを見落とす(第2種の過誤)」を犠牲にしながら、その可能性を低めています。
統計的仮説検定の意味
もう一度、上記の表について考えます。あらゆる研究で得られた結果は、この表の4つの場合のどれかに当てはまります。そして大事なポイントとして、真実は常に4つのセルの外側にあるということです。つまり科学者は真実を突き止めるのではなく、真実とは異なる結論をなるべくしないような努力をしているんだと思います。統計的仮説検定というか人間の限界みたいなものを感じます。
統計学を通してみる世界
上で考えたことは、世界の見方としてもう少し一般的な事柄にも当てはまりそうです。例えば、病気の検診の結果、陰性か陽性が伝えられますが、それがすなわちその人が病に冒されていることを意味しません。検査で陽性でも最終的に病気ではないことはあります。また、刑事裁判を例に考えれば、真犯人を見逃す可能性や冤罪の存在があげられます。きちんとした検証を行っていたとしても、真実を知ることは難しいわけです。そして科学的な検証が済んだ事実(例えば学校教科書に書かれているようなこと)でも、それが覆る可能性は0ではありません。こんな風に考えると、人間はいかにあやふやな世界に生きていているのかと思います。そして、それが安定していると考えられる不思議な存在でもあるなと思います。
R言語で複数の変数を集計(2)
今回は、前回の内容の続きで、複数の変数の内容を1つの変数にまとめる方法を紹介します。
複数の変数に別れた条件名を1つにまとめたい
以下のデータフレームでは、ある条件に該当するかどうかが複数の項目への解答として記録されています。
参加者 | 条件A | 条件B | 条件C |
1 | 1 | 0 | 0 |
2 | 0 | 0 | 1 |
3 | 0 | 1 | 0 |
4 | NA | NA | NA |
5 | NA | 0 | 1 |
このデータをよく見ると、各参加者は条件A,B,Cのいずれか1つにしか該当しないようです。このような場合、どの条件に該当するかを1つの変数で表現することができます。たとえば、条件Aは0、条件Bは1、条件Cは2というようにです。
参加者 | 条件A | 条件B | 条件C | |
1 | 1 | 0 | 0 | 0 |
2 | 0 | 0 | 1 | 2 |
3 | 0 | 1 | 0 | 1 |
4 | NA | NA | NA | NA |
5 | NA | 0 | 1 | NA |
これと同じことをR言語を利用して行うのならifelse関数が便利です。まず条件名を示すための列を作成し、それに-1を代入します。これであとから、1度も代入されていないセルがあるかどうかを確認できます。
dat <- data.frame(id = c(1,2,3,4,5), VarA = c(1,0,0,NA,NA), VarB = c(0,0,1,NA,0), VarC = c(0,1,0,NA,1)) print(dat) dat$Var <- rep(-1,nrow(dat)) print(dat)
ではifelse関数を利用して、どの条件に該当するかを調べてみましょう。
#条件Aに該当するときは0 dat$VarA2 <- ifelse(dat$VarA == 1,0,-1) #条件Bに該当するときは1 dat$VarB2 <- ifelse(dat$VarB == 1, 1,-1) #条件Cに該当するときは2 dat$VarC2 <- ifelse(dat$VarC == 1, 2, -1) for (i in 1:nrow(dat)){ dat[i,"Var"] <- max(c(dat[i,"Var"],dat[i,"VarA2"],dat[i,"VarB2"],dat[i,"VarC2"])) } print(dat)
少しややこしいのですが、こうすると1つでもNAが含まれている参加者についてNAを戻すことができます。そうではなく、NAがあったとしても全てでなければ埋まっている項目の中から選びたいという場合にはna.rm = TRUEをmax関数の引数として加えてください。