2017.06.23 Fri |
Kobe Bryant選手のShotデータ解析その1
今回はNBAのバスケットボール選手であったKobe Bryant選手が17歳からの20年間試合で放ったShot(ショットを放つ動作のことをシュートというみたいです。今回はShotで統一します)のデータを解析していきたいと思います。
僕はバスケットボールには詳しくありませんが、聞いたところによると、Kobe Bryant選手は、普通の選手の2-5倍くらいShotを入れていた怪物だそうです。
データはKaggleにある”Kobe Bryant Shot Selection”のものを使用します。
https://www.kaggle.com/c/kobe-bryant-shot-selection
ちなみに、Kobe Bryant選手以外のデータがほしければ、以下のwebsiteからとってこれるみたいです。
http://stats.nba.com/
それでは、データを読み込んでいきましょう。
とりあえず、特に目的なく、探索的データ解析を行ってみます。
#ファイル(data.csv)をワークディレクトリに配置
#データ読み込み
d=read.csv(“data.csv”,header=T)
#データの要約
str(d)
3万679本のShotのデータが入っているみたいです。各Shotにつき、その放たれた場所やどんなShotだったかなどの情報が格納されているみたいです。
それでは重要なものをひとつずつ見ていきましょう。
まずは一番重要な”shot_made_flag”変数を見ていきます。
Shotが入れば、1入らなければ0となっています。欠損値NAがあるのは、これを0か1か予測させるデータ提供者の意図によるものです。今回は削除してしまいましょう。
#shot_made_flag変数を集計
summary(as.factor(d$shot_made_flag))
テスト用のデータNAが5000個、外れたShotが14232個、入ったShotが11465個となっています。
それでは、NAが入っている行の削除とshot_made_flag変数のファクター化を行います。
#shot_made_flag変数にNAが入っている行の削除
d=d[complete.cases(d$shot_made_flag),]
#shot_made_flag変数のファクター化
d$shot_made_flag=as.factor(d$shot_made_flag)
#shot_made_flag変数を集計
summary(d$shot_made_flag)
うまく欠損値を取り除けたようです。
それでは、shot_made_flagと各変数の関係を一つずつ見ていきましょう。
まず、action_type変数から。
#action_type変数の中身を見てみる
unique(d$action_type)
どうやらこの変数はShotの種類を表しているようですね。
Shotの種類ごとに何回入ったか(1)入らなかったか(0)を見ていきましょう。
library(ggplot2)
ggplot(data=d)+
geom_bar(aes(x=action_type,fill=shot_made_flag))+
coord_flip()
※水色が成功したShot、赤が失敗したShotです。
圧倒的にJump Shotの数が多く、次に、Layup Shotが多いのがわかります。
他にも、Turn around jump shot、Running jump shot、Fadeaway jump shot、Driving Layup shotなどが、2000回以下なものの、それなりの数あります。
それでは、各Shotでの成功割合を見てみましょう。
#各Shotでの成功割合の可視化
ggplot(data=d)+
geom_bar(aes(x=action_type,fill=shot_made_flag),position = “fill”)+
coord_flip()
これで各Shotの成功率が可視化されました。
全体的に絶対数が少ないので、この可視化からいろいろ言うことは避けたいですが、とりあえず、絶対数が多い、Jump ShoとLayup Shotでは、Layup Shotの方が成功率が高いみたいですね。
次は、combined_shot_type変数を見ていきます。
#combined_shot_type変数の要約
summary(d$combined_shot_type)
Bank Shot:120
Dunk:1056
Hook Shot:127
Jump Shot: 19710
Layup:4532
Tip Shot:152
という結果です。先ほどの細かい57分類から、より大雑把な6分類になったものと思います。
先ほど同様に、Shotの種類ごとに何回入ったか(1)入らなかったか(0)を見ていきましょう。
ggplot(data=d)+
geom_bar(aes(x=combined_shot_type,fill=shot_made_flag))+
coord_flip()
やはり、LayupとJumpShotが多いですね。
さて今まで、action_type変数とcombined_shot_type変数を見てきましたが、この2変数の関係は、6種類のcombined_shot_type変数がそれぞれさらに細分化して、action_type変数になる、階層構造の関係がありました。この階層構造と各action_typeにおいて合計何回Shotが試みられたかを可視化してみます。
library(dplyr)
#各action_typeごとに何回Shotが試みられたかを表すNumShot変数の作成
t=d %>% group_by(combined_shot_type,action_type) %>% summarize(NumShot=length(shot_made_flag))
str(t)
#treemapパッケージのロード(まだ入れていない方はインストールしてください)
library(treemap)
treemap(t,index=c(“combined_shot_type”,”action_type”),vSize=”NumShot”,vColor=”combined_shot_type”)
さて次は、game_id変数を見ていきたいと思うのですが、この変数の使い方としては、各試合で何回Shotが入ったかでヒストグラムを作成してみたいと思います。
#game_id変数のファクター化
d$game_id=as.factor(d$game_id)
#game_idごとに集計して試合ごとのShot成功数でヒストグラム作成
library(dplyr)
d%>%filter(shot_made_flag==1)%>%group_by(game_id)%>%summarize(Total_shot_num=length(shot_made_flag))%>%ggplot()+geom_histogram(aes(x=Total_shot_num),binwidth = 1)
試合ごとのShot成功数は、だいたい0-20の間に収まるみたいで、たいていの場合4-12回くらいみたいですね。
次にlat変数,loc_x変数,loc_y変数とlon変数を見ていきます。
library(GGally)
ggpairs(d,c(5:8,15),mapping = ggplot2::aes(color = shot_made_flag), upper = list(continuous = wrap(“cor”, size=2)),lower = list(continuous = wrap(“points”, alpha = 0.3), combo = wrap(“dot”, alpha = 0.3)), diag = list(continuous =”barDiag”))
lat変数,loc_x変数,loc_y変数とlon変数は、
loc_xとlonが正の相関(相関係数1)、latとloc_yが負の相関(相関係数-1)ということで、内容が重複しているのが分かります。
latとlonが、latitudeとlongitude、
loc_xとloc_yが、location_xとlocation_y、の略であることが推測でき、
ここでは、loc_xとloc_yを使用することにします。
loc_xがコートを縦に配置したときの、コートのx座標、loc_yがコートのy座標です。
ではこの2変数で散布図を作っていきましょう。
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = shot_made_flag),alpha = 1 / 5)
バスケットコートにおいてどこでShotを放ったかを表しており、下の真ん中にバスケットゴールがあることが推測できます。
少し重なっていて見ずらいので、青と赤を別々にplotしてみましょう。
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = shot_made_flag),alpha = 1 / 5)+
facet_wrap(~shot_made_flag)
赤がShotを外した時、青がShotをいれたときです。
赤を見てみると、バスケットリングから遠いところから投げているものは、外れる確率が高いことが分かります。
青を見てみると、結構どこからでも打てていることが分かりますが、水色の点が少ないところもあることから、一部苦手な場所もあるようですね。
次に色づけを、combined_shot_typeでやってみましょう。
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = combined_shot_type),alpha = 1 / 5)+facet_wrap(~shot_made_flag)
重なっていてみづらいので、combined_shot_type変数とshot_made_flag変数で層別化してみましょう。
#combined_shot_type変数とshot_made_flag変数で層別化
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = combined_shot_type),alpha = 1 / 5)+facet_grid(combined_shot_type~shot_made_flag)
Shotが失敗するか(0)、成功するか(1)は、場所ごとの違いはほんの少しありますが、あまりなさそうです。。
最後にshot_zone_area変数と、shot_zone_basic変数とshot_zone_range変数で色付けしてプロットして今回は終わりにします。
まずshot_zone_area変数を見ていきます。
#質的変数であるshot_zone_area変数の集計結果
summary(d$shot_zone_area)
shot_zone_area変数はバスケットボールコートの部位の名前ですね。
#質的変数であるshot_zone_area変数で色づけ
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = shot_zone_area),alpha = 1 / 5)+facet_wrap(~shot_made_flag)
確かのバスケットボールコートの各部位で色付けされました。
左がShotに失敗した時の位置、右がShotに成功したときの位置、になっていますが、ぱっと見だとそれほど大きな違いはないように見えます。右のShotに成功したときの位置を見てみると、彼はどこからでもShotを打てることがわかりますね。
次にshot_zone_basic変数を見ていきます。
#質的変数であるshot_zone_basic変数の集計結果
summary(d$shot_zone_basic)
これも先ほど同様にバスケットボールコートの部位を表すことがわかります。
#質的変数であるshot_zone_basic変数での色付け
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = shot_zone_basic),alpha = 1 / 5)+facet_wrap(~shot_made_flag)
やはり、バスケットボールコートの区分でした。場所ごとにきれいに色分けされているのがわかります。ただShotが成功したとき(右)を失敗したとき(左)で大きな違いはほぼないですね。
次にshot_zone_range変数を見ていきます。
#shot_zone_range変数の要約
summary(d$shot_zone_range)
これは、バスケットボールリングの中心の真下からの距離を5水準で表しているようです。
実際に色付けしていきましょう。
#shot_zone_rage変数での色付け
ggplot(data = d)+
geom_point(aes(x = loc_x, y = loc_y, color = shot_zone_range),alpha = 1 / 5)+facet_wrap(~shot_made_flag)
確かに、shot_zone_rage変数はバスケットボールリングの中心の真下からの距離を表していました。左右のプロットを比較してみると、Shotが成功する(右図)か失敗する(左図)かにShotする場所はほとんど影響していないように見えますね。
ということで、バスケットボールコートの部位を表す、3変数についての解説を終了しました。
次回は、Shotの成功・不成功を予測するモデルを構築し、どの変数が、Shotの成功・不成功に影響しているかを、計算してみます。
鈴木瑞人
東京大学大学院新領域創成科学研究科 メディカル情報生命専攻 博士課程
東京大学機械学習勉強会 代表
NPO法人Bizjapan