【Juliaでセイバーメトリクス】2020年のパ・リーグ4強打者について比較

Julia
LINEで送る
Pocket

はじめに

こんにちは。
最近はJuliaについて勉強しています。PythonやRと比較して日本語の情報は少ないように感じますが、Juliaの思想にあるようにそれぞれの言語のいいとこ取りをしているような印象があります。実際、速度も早く面白いなと感じましたので、今後も継続して勉強したいなと思っている言語です。

そこで実際のデータを利用して、練習をしていこうと思っています。今回もいつものように野球のデータを利用して、データフレームの操作や可視化の練習をしていきます。

ただ、今回JuliaでWebスクレイピングやそこからテーブルを取得することが難しかったので(実際できるようですが、著者には困難でした。)、Pythonでcsvを作成して、Juliaで読み込むところからはじめています。

今回の流れ

  • パ・リーグで2020年10月現在で打撃タイトル争いをしている柳田悠岐選手、吉田正尚選手、浅村栄斗選手、中田翔選手の4選手のここ5年の打撃成績を比較し、そのすごさを考える。
  • 今回比較する打撃成績は以前内川聖一選手の際に算出したように、OPS、BB/K、wOBA、IsoP、IsoDとする。
    • OPS:OPSは打席あたりで得点増加に有効な打撃をしているかどうかを表す指標。つまり打席での貢献度。平均は.730前後で、.1000を越えればリーグ最高レベルとのこと。
    • BB/K:四球と三振の割合から打者の選球眼を見る指標。1前後が望ましく、理想は1.5〜2とのこと。
    • wOBA:打者が打席あたりにどれだけチームの得点増に貢献する打撃をしているかを評価する指標。出塁の価値を全て均一とみなす出塁率よりも打撃の貢献を総合的に表し、加重が統計的な根拠に基づいていることからOPSよりも適切に打撃の価値を評価する。出塁率に合うように設計されているため、平均的な打者で.330程度になるようです。
    • IsoP:打者が長打(二塁打以上)を放つ能力を測る指標。長打率から単打の要素を除くことでより純粋な長打力を測ることが可能。平均は.130程度。
    • IsoD:四死球によってどの程度出塁したかを測るための指標。平均は.060程度。
  • その中で少しJuliaにおけるデータフレーム操作や可視化について体験する。

ちなみに記事執筆当時のパ・リーグ打撃タイトル3傑は

打率本塁打打点
1吉田正尚 .355浅村栄斗 31中田翔 102
2柳田悠岐 .345中田翔 30浅村栄斗 99
3近藤健介 .339柳田悠岐 28柳田悠岐 80

以下、今回の分析手順

  1. Pythonでスクレイピング、csvデータ作成
  2. Juliaでcsv読み込み
  3. JuliaでOPS、BB/K、wOBA、IsoP、IsoDを計算、データフレームに列追加
  4. Juliaで可視化

Pythonでスクレイピング、csvデータ作成

まずはpandasの準備。表示行列を増やします。

import pandas as pd

# 表示行・列を増やす(30列、10行)
pd.get_option("display.max_columns", 30)
pd.get_option("display.max_rows", 10)

おなじみNPB公式サイトから各選手のデータをスクレイピングしていきます。4選手全員分を行いますが、ここでは柳田悠岐選手に絞ってコードを記載します。

#柳田悠岐選手の個人年度別成績(npb.jp)
url = 'https://npb.jp/bis/players/31835133.html' 

#Tableタグをスクレイピング。
yana = pd.read_html(url) 

#不使用となるデータを捨てて、別のデータフレーム作成
yanagita = yana[1].drop([0, 1, 2, 3, 4, 10], axis=0)  
# カラム名を付与する(野球英語の略称)
yanagita.columns = ['year', 'team', 'g', 'pa', 'ab', 'r', 'h', '_2b', '_3b', 'hr', 'tb', 
'rbi', 'sb', 'cs', 'sh', 'sf', 'bb', 'hbp', 'so', 'dp', 'ba', 'slg', 'obp']

# 各カラムのデータ型を整える
import numpy as np
yanagita['year'] = yanagita['year'].astype(np.float64)
yanagita['g'] = yanagita['g'].astype(np.float64)
yanagita['pa'] = yanagita['pa'].astype(np.float64)
yanagita['ab'] = yanagita['ab'].astype(np.float64)
yanagita['r'] = yanagita['r'].astype(np.float64)
yanagita['h'] = yanagita['h'].astype(np.float64)
yanagita['_2b'] = yanagita['_2b'].astype(np.float64)
yanagita['_3b'] = yanagita['_3b'].astype(np.float64)
yanagita['hr'] = yanagita['hr'].astype(np.float64)
yanagita['tb'] = yanagita['tb'].astype(np.float64)
yanagita['rbi'] = yanagita['rbi'].astype(np.float64)
yanagita['sb'] = yanagita['sb'].astype(np.float64)
yanagita['cs'] = yanagita['cs'].astype(np.float64)
yanagita['sh'] = yanagita['tb'].astype(np.float64)
yanagita['sf'] = yanagita['sf'].astype(np.float64)
yanagita['bb'] = yanagita['bb'].astype(np.float64)
yanagita['hbp'] = yanagita['hbp'].astype(np.float64)
yanagita['so'] = yanagita['so'].astype(np.float64)
yanagita['dp'] = yanagita['dp'].astype(np.float64)
yanagita['ba'] = yanagita['ba'].astype(np.float64)
yanagita['slg'] = yanagita['slg'].astype(np.float64)
yanagita['obp'] = yanagita['obp'].astype(np.float64) 

ここまで4選手分の処理をしたら、csvファイルに書き出します。

#データフレームの書き出し
yanagita.to_csv("yanagita.csv")
yoshida.to_csv("yoshida.csv")
asamura.to_csv("asamura.csv")
nakata.to_csv("nakata.csv")

Juliaでcsv読み込み

Pythonでcsvファイルを作成したら、Juliaでそのファイルを読み込みます。

#必要なパッケージの準備
import Pkg
Pkg.add("DataFrames")
Pkg.add("CSV")
using DataFrames
using CSV

#csvファイルの読み込み
ya = CSV.read("yanagita.csv")
yo = CSV.read("yoshida.csv")
asa = CSV.read("asamura.csv")
na = CSV.read("nakata.csv")

JuliaでOPS、BB/K、wOBA、IsoP、IsoDを計算、データフレームに列追加

csvファイルを読み込めたら、OPS、BB/K、wOBA、IsoP、IsoDを計算し、それぞれをデータフレームに追加します。

データフレームを計算するときにはDataFramesMetaパッケージを利用します。DataFramesMetaはR言語のdplyrパッケージのようにデータフレームを操作できます。パイプ連結子|>を利用して、複数の処理をつなぐことができます。具体的には以下の対応表を参考にしてください。

Juliadplyr
行の抽出@wherefilter
列の追加@transformmutate
グループ化+計算@by
グループ化@groupbygroup_by
要約@based_onsummarise
並び替え@orderbyarrange
列の選択@selectSelect

実際の利用の際にははじめに@linqを描くことで、以降は@なく繋ぐことができます。

詳しくは公式のgithubを参照してください。

ではDataFramesMetaを使って計算してみましょう。まずは柳田選手。

#必要なパッケージの準備
Pkg.add("DataFramesMeta")
using DataFramesMeta
#列の指定はsymbol型。:+列名で指定可能。
#除算、乗算のときは明示的に.が必要。
ya2 = @linq ya |>
    transform(ops = :obp + :slg) |>
    transform(bb_k = :bb./:so) |>
    transform(woba = (0.7.*(:bb + :hbp) + 0.9.*:h + 1.3.*:_2b + 1.6.*:_3b + 2.0.*:hr )
./( :ab + :bb + :hbp + :sf )) |> 
    transform(isop = :slg - :ba) |>
    transform(isod = :obp - :ba)
ya2[[:ops, :bb_k, :woba, :isop, :isod]]

柳田

同様に他の選手の成績を計算します。上のコードのyaをそれぞれyoasanaに変えるだけです。

吉田選手
吉田

浅村選手
浅村

中田選手
中田

4選手のOPS、BB/K、wOBA、IsoP、IsoDが計算できました。
ではこれを可視化して見やすくしましょう。

Juliaで可視化

Julia言語の可視化には様々な方法があるようですが、今回可視化にはPlotsパッケージを利用しました。バックエンドも色々種類があるようですが今回はGRを利用しています。

それぞれの選手のグラフを重ねましたが、重ねるときは!をつけて破壊的にする必要があるようです。

では可視化していきます。まずはOPSから。

#必要なパッケージの準備
Pkg.add("Plots")
using Plots
gr()

#OPSの可視化。それぞれの結果を重ねる。
plot([2016:2020],ya2[:ops], xlabel="year", ylabel="OPS", title="OPS", 
label="Yanagita", linecolor=:black)
plot!([2016:2020],yo2[:ops], label="Yoshida", linecolor=:blue)
plot!([2016:2020],asa2[:ops], label="Asamura", linecolor=:red)
plot!([2016:2020],na2[:ops], label="Nakata", linecolor=:orange)
savefig("ops.png")

同様にして、BB/K、wOBA、IsoP、IsoDも可視化していきます。

BB/K

wOBA

IsoP

IsoD

以上のような可視化ができました。これらのことから何がわかるか考察してみましょう。

考察

  • どの指標を見ても毎年高水準の成績を残している柳田悠岐選手。年毎のムラが大きいのは気になりますが、すごい成績です。長打力も抜群。今年は2018年を超える成績を残すかもしれません。
  • 今年の成績が特徴的な吉田正尚選手。長打の割合(IsoP)はガクンと減っていますが、三振が極端に少ない(BB/K)ことが見て取れます。単打が中心でも、打ちまくっているとOPSやwOBAは変わらないことがわかりました。
  • 全体的に右肩上がりの成績の浅村栄斗選手。長打力(IsoP)も選球眼(IsoD)も年々向上していると推測できます。2019年に西武から楽天に移籍して、役割が変わったことも関係あるかもしれません。
  • 全体的に浅村栄斗選手と似たグラフの中田翔選手。違いはIsoD(四球での出塁)の割合が少ないことかなと思います。なんとなくこの4人の中では、昔ながらのホームランバッターという印象です。

まとめ

以上、Julia言語を使ってパ・リーグの4打者の成績を分析してみました。全員高水準のバッターですが、それぞれの特徴が見られました。皆ほぼ同年代で、今後のさらなる活躍が楽しみです。

また、Juliaに関しては、後発の言語ということもあって色々使いやすく開発されたんだろうなということを感じました。さらなるパッケージの発展や、日本語でのドキュメントなどがたくさん増えることで、どんどん使いやすくなっていくのかなと感じました。今後も勉強していきます。

今回は以上です。

参考サイト・文献

LINEで送る
Pocket

コメント

タイトルとURLをコピーしました