いとマニアックな備忘録というかもはや作業メモでしかない系記事
Motivation
Football LABの各試合の詳細データ(例:川崎vs浦和)だとスタッツとして記載があるのは、シュート数/枠内シュート数/シュート数のみであり、シュート数の位置まではわからない。
だから、全シュートのうち「PA内から打たれたシュート数」がどれだけか、とかを数値として取得してくるのは不可能である。
しかし、同ページには、以下のようなシュート位置のマップが示されている
この図から座標情報を間接的にわかるんじゃね?と考えて推定していく。
座標の指定方法をしらべる
Chromeの開発ツールで、上のグラフのシュートの位置の起点となる黒丸をクリックしてみると、以下のように座標が指定されていることがわかる。
それで、このcyとかcxを動かしながら*1、ピッチの端などキーとなる座標をおぼえておく。
結果は、
という感じでした。
ピッチの広さに関する知識
上のサイトに各寸法があったので、メモしてく。
自分も学生時代はサッカーをしていたので、練習試合を自校でやるときとか石灰を出す台車*2をつかったりしてラインも引いてたはずなんだけど、意外とほとんどおぼえてないもんで。
座標変換のロジックをつくる
とりあえず、元のcx/cyの座標を変換してくための計算式を用意しなければならない
私たちが知りたいのは、
- PA内から打たれたシュートか?
- GA内から打たれたシュートか?
という二点。
だから、PAの横幅/縦幅、GAの縦幅
PAの高さ16.5mがLABのサイトのcx/cy平面では80-8 = 72となっているとするなら、だいたい概算して16.5/72≒0.223mが1cxに相当する
ちなみに横幅は302-8 = 294だが、この倍率を用いて検算すると横68mのピッチが想定されていることがわかる。だから68/294が1cyに相当する。
この計算をもとに、cx/cyの座標をリアル座標に変換する関数をかく。
x座標は中心から左なら負の値、右なら正の値をとるようにしてある。
labToRealXY <- function(cx, cy , PA_height = 71.5 , pitch_width=294){ scl_y <- 16.5/ PA_height #調整係数の計算 scl_x <- 68 /pitch_width y_val <- (cy - 8) * scl_y x_val <- (cx - 155) * scl_y return( c( x_val , y_val)) } #LabToRealXY
リアル座標(labToRealXY()の戻り値)のほうをもとに、PA内かGA内か、それともPA外かを判定する関数も実装する
judgeArea <- function( x, y){ if( abs(x ) > 40.32/2 || y > 16.5){ return("Outside_PA") }else if(abs(x) <= 18.32/2 && y <= 5.5){ return("Inside_GA") }else{ return("Inside_PA") } #if }#func
試合ページから取得できるようにする
あとは仕上げの工程。とか思いきや他の部分は静的に生成していたFootball LABはこの部分だけ動的に生成していた(なぜだ)ので、RSeleniumの出番だった。
まぁクライアント立ち上げてちょっと待ってからgetPageSource()した内容を結局rvestに渡すだけなので、あんまりやること変わらないんだけど、実行時間は結構かかった。
codeは省略するが、とりあえず日付とクラブさえわかれば紐づけができるため、日付/クラブ/X座標/Y座標/エリア判定結果を変数としてもったdata frameをページから取得できるようにする。
これをすべての試合のURLについて行って、タテにつなげて統合する。
集計結果(やっぱ川崎すげー)
2020年に放たれた全8031本のシュートをクラブ別に集計して、その内訳をみると以下になる。
◆シュート総数
◆シュートの内訳(%)
川崎はシュート数が1位なだけじゃなくて、ゴールエリア内で打ってる率も1位というやばいことになっている。
翻って名古屋は、シュート数が少ないだけじゃなくてPA外から打ってるのが多い。
うーん...
※追記(21/3/18)
2020年のクラブ別の集計結果詳細は以下の通り
クラブ名 | シュート総数 | PA内シュート数 | うちGA内シュート数 | PA内シュート率(%) | GA内シュート率(%) |
---|---|---|---|---|---|
C大阪 | 434 | 263 | 24 | 60.6 | 5.5 |
FC東京 | 444 | 255 | 39 | 57.4 | 8.8 |
G大阪 | 463 | 281 | 30 | 60.7 | 6.5 |
広島 | 473 | 290 | 32 | 61.3 | 6.8 |
川崎F | 643 | 420 | 65 | 65.3 | 10.1 |
鹿島 | 597 | 356 | 40 | 59.6 | 6.7 |
柏 | 463 | 294 | 22 | 63.5 | 4.8 |
神戸 | 482 | 274 | 32 | 56.8 | 6.6 |
名古屋 | 381 | 200 | 29 | 52.5 | 7.6 |
大分 | 347 | 235 | 32 | 67.7 | 9.2 |
札幌 | 494 | 295 | 28 | 59.7 | 5.7 |
仙台 | 381 | 226 | 22 | 59.3 | 5.8 |
清水 | 458 | 279 | 25 | 60.9 | 5.5 |
湘南 | 315 | 194 | 30 | 61.6 | 9.5 |
鳥栖 | 409 | 252 | 25 | 61.6 | 6.1 |
浦和 | 404 | 227 | 29 | 56.2 | 7.2 |
横浜FC | 366 | 219 | 19 | 59.8 | 5.2 |
横浜FM | 534 | 339 | 31 | 63.5 | 5.8 |
ちなみにリーグ平均のPA内シュート率は60.6%で、GA内シュート率は6.8%である。
ちなみに、LABの試合情報ページの表に掲載されている「シュート数」を集計すると2020シーズンの総シュート数は全8088本で、上のやりかたで座標が取得可能なシュートは8031本である。
つまり、マップ上から座標を取得できるシュートのほうが若干少ない。
これは、シュートマップに収まらないくらいの超ロングシュートの存在が影響していると推測している。
比率でいうとは0.63%くらいなので、なんとなく直感的にも納得がいく。