【R言語】プロ野球データを例にして試みるコレスポンデンス分析の解釈の仕方ーデータ収集から視覚化までー
コレスポンデンス分析(Correspondence Analysis)とは、クロス集計表の表頭項目(列)と表側項目(行)の関係性を視覚的に表現する手法だそうです。クロス集計表を視覚化できるのは便利そうだと思ったのですが、列項目と行項目の両方をプロットした図(同時布置図)の解釈について色々と判断に迷うところがあったので、備忘録を兼ねてまとめておこうと思います。
目次
使用するパッケージ
> library(dplyr) > library(stringr) > library(magrittr) > library(ggplot2) > library(rvest) > library(ggrepel) > library(ca)
データセット
プロ野球データFreak(http://baseball-data.com/16/team/pitcher.html)さんから2016年シーズンの各チームの投手成績をスクレイピングして取得します。
> team_tbl <- read_html("http://baseball-data.com/16/team/pitcher.html") %>% html_nodes(xpath = '//*[@id="tbl-ce"]') %>% html_table() > sl <- team_tbl[[1]] # team_tblからセリーグ部分をslに > pl <- team_tbl[[2]] # team_tblからパリーグ部分をslに > team_seiseki <- rbind(sl,pl) # slとplを行結合 > pitcher <- team_seiseki %>% rename(セーブ=セlブ, ホールド=ホlルド) %>% select(-順位,-試合,-防御率, -勝利, -敗北,-引分,-WHIP,-DIPS,-失点平均,-被安平均) # 文字化けを修正して不要な表頭項目を削除する > print(pitcher) チーム セーブ ホールド 完投 完封勝 被安打 被本塁打 与四球 与死球 1 広島 37 99 7 19 1194 101 418 42 2 巨人 41 92 8 12 1248 119 395 49 3 DeNA 35 97 9 12 1253 143 419 31 4 阪神 31 81 8 15 1139 100 498 41 5 ヤクルト 31 70 7 3 1333 147 476 75 6 中日 25 91 8 10 1222 115 469 48 7 日本ハム 42 120 9 16 1129 89 455 48 8 ソフトバンク 46 89 7 14 1114 126 401 44 9 ロッテ 36 97 12 9 1308 104 428 43 10 西武 31 66 8 8 1249 78 524 76 11 楽天 30 87 3 8 1324 106 452 59 12 オリックス 34 82 7 10 1285 113 521 35 奪三振 失点 自責点 1 966 497 457 2 1061 543 492 3 1059 588 532 4 1166 546 479 5 872 694 663 6 980 573 519 7 1004 467 436 8 1158 479 443 9 855 582 524 10 956 618 543 11 1026 654 584 12 956 635 589
今回使用するデータセットは表側項目が球団名、表頭項目が球団成績のクロス表となっており、成績は各球団に所属する投手の合計(度数)となっております。防御率など度数として単純に表現できない項目については成績から除く処理をしています。というのも基本的にコレスポンデンス分析というのは基本的には名義尺度や順序尺度といった質的変数のクロス集計表を平面上にプロットして視覚化する手法であると私は理解しており、度数として表現できない防御率は除いたほうがいいのかなと判断しました(間違っていたらごめんなさい)。
コレスポンデンス分析と同時布置図
> rownames(pitcher) <- pitcher$チーム > pitcher.ca <- ca(pitcher[,-1]) > plot(pitcher.ca) #同時布置図
結果の解釈について
青色の〇が行の項目(球団名)を表しており、赤色の△が列の項目(成績)を表しています。ここで注意が必要なのは、上図では巨人〇と完投△が近くにプロットされていますが、ここから巨人の投手陣は他の球団と比べて完投する投手が多いという解釈はできません。その理由として斎藤・豊田(2004)は以下のように説明しています。
行要素内,列要素内で布置に表現される点の間の距離はユークリッド距離としては評価できるため,その間隔を測ることに意味はある.一方で,行要素と列要素の間では,布置に表現される点の距離はユークリッド距離として評価できないため,間隔を測ることに意味はない.
これは、行内の要素や列内の要素についてはユークリッド距離として評価できるため、距離の近い項目は強い対応関係があることを意味しています。たとえば、上図ではセーブ△とホールド△が隣接しており、これは両項目の関連性が高いと解釈できることになります。しかし、巨人〇と完投△の距離は近いですが、行要素と列要素の間では,布置に表現される点の距離はユークリッド距離として評価できないので、巨人と完投の対応関係は他の球団よりも強いと単純に解釈するのは少し危うい気がします。実際に完投数が最も多かった球団はロッテの12であり、次にDeNAと日本ハムが9でした。これは行項目と列項目の距離の近さは対応関係の強さを表していない証拠の1つといえるかもしれません*1。
では、分析結果の解釈はどのように行えばいいのでしょうか。今井・佐藤(1994)では1992年の大相撲6場所の幕内力士20人の勝ち数と決り手との関係をコレスポンデンス分析しており、結果の解釈をするにあたり、列項目の決まり手の布置図(第1(2)主成分ベクトルの係数の符号)から同時布置図の軸の評価を行っていました。なので、ここでも、まず同時布置図の軸の評価から行います。そのうえで、同時布置図にプロットされている12球団がどのように分布しているか(どのようなグループにまとめられるか)について検討していきます。
軸の評価
まず同時布置図の成績△がどのように分布しているかを軸別に見ていきます。横軸をみると、被安打、失点、自責点、与四球、与死球といった成績指標が正の値にプロットしており、完投、セーブ、ホールド、奪三振、完封勝が負の値にプロットされています。ここから、横軸(Dim1)は「勝ちにつながる投球」「負けにつながる投球」を表していると考えられる。次に縦軸をみると、与四球、与死球といった指標が大きな正の値を有しており、被本塁打が大きな負の値を有していることが分かる。このことから縦軸(Dim2)は「制球力」を表していると考えられる。
同時布置図の解釈
軸の評価をしたうえで、球団〇をみると2つのグループに分けることができると思ます(オレンジ色の円と緑色の円で囲んでいます)。オレンジ色の円に含まれて球団は広島(2016年セ1位)、巨人(セ2位)、DeNA(セ3位)、阪神(セ4位)ソフトバンク(パ1位)、日本ハム(パ2位)といった2016年シーズンの上位に位置する球団から構成されています。一方、緑色の円に含まれている球団はロッテ以外は下位の球団から構成されています。また、オレンジ色の円は横軸が負の値を有しているため、軸の評価より「勝ちにつながる投球」をした投手陣を要している球団であると考えられます。一方、緑色の円は横軸が正の値を有しているため「負けにつながる投球」をした投手陣を要した球団であると考えられます。
おわりに
今回の分析手順をおさらいすると、①表頭項目(成績)の布置図から軸の評価を行う、②表側項目(球団名)の布置図からグルーピングできるか考える、③同時布置図から全体的な解釈の妥当性を検討といった流れで分析を行いました。クロス集計表をグラフィカルに表現できるので便利そうだと思って分析を始めたものの、解釈の仕方で考え込んでしまいました。しかしながら、分析対象のドメイン知識を駆使して分析結果を解釈できれば役に立つ手法なのかなと思いました。
【参考・引用文献】
今井・佐藤.1994.「対応分析における布置図の信頼性の視覚的表示法」『計算機統計学』7(2):127-133.
斎藤・豊田.2004.「コレスポンデンス分析における布置の精度」『オペレーションズ・リサーチ』49(3):168-173.
おまけ
ggrepelパッケージとggplot2パッケージを使用して同時布置図を作成してみようと思い、以下のコードを書きました。
> team <- rownames(pitcher) > item<- c(rep("球団名",12), rep("成績",11)) > name <- c(team, colnames(pitcher[,-1])) > pitch_records <- data.frame( > name, item, rbind(pitcher.ca$rowcoord[,1:2], (pitcher.ca$colcoord[,1:2])) > ) > PointPlot <- ggplot(pitch_records, aes(x=Dim1, y=Dim2, label=name,color=item,fill=item)) + > geom_point(aes(colour=item, shape=item)) > PointPlot+ > geom_label_repel(aes(Dim1,Dim2,fill=factor(item),label=name), > fontface='bold',color='white', > box.padding=unit(0.35,"lines"), > point.padding=unit(0.5,"lines"), > segment.color='grey70')+ > geom_hline(yintercept=0, colour="gray70") + > geom_vline(xintercept=0, colour="gray70") + > xlab("Dim 1") + > ylab("Dim 2")
*1:単にサンプルサイズが小さいことが布置の精度に影響を及ぼしているのかもしれません。