この記事は Calendar for UdonTech Advent Calendar 2021 | Advent Calendar 2021 - Qiita の4日目の記事です。
私の担当日じゃないんですが、空いてるようなんで勝手に埋めときます :)
突然ですがみなさんガチャ回してますかー?
英霊だったり馬だったり刀剣だったり、世の中にはさまざまなガチャが溢れていますが、 我々ワシントンウィザーズファンは試合の度にベルターンスガチャを回しています!!
ワシントンウィザーズ(以下WASと表記)は、NBAバスケットボールチームのひとつです。 私は所詮、追いかけはじめて3年目のニワカなので、細かいことはwikipediaを見てください :)
このチームには、ラトビアが誇る3ポイントシューター ダービス・ベルターンス(Dāvis Bertāns)という選手が居ます。
彼の特徴を簡単にまとめると
- 顔がでかい
- ベンチスタートで、ちょろっとでてきて3pをばしばし打つ
- めっちゃクイックモーションで3P打てる
- 体勢が崩れながらでも、普通に打てる
- 去年から引き続きスランプ中
- 時々神がかった確率で決める
- 顔がでかい
という具合でエリートシューターなんですが、すごいムラのある選手です。今年は1回だけ大爆発した試合があってその時に某NBA youtuberさんに「ベルターンスガチャの引きが良かった」と評されてました。*1
さて、ガチャだということは排出率を表示する必要があります*2
ここからは、basketball-reference.comのデータを使って、つよつよベルターンスの排出率を求めてみましょう。
---- ここまで前フリ ----
データの入手
NBAの各プレイヤー、チームの統計情報は、公式が供給してくれてます。
が、ちょっとこのデータを取得するには、地道にコピペするしかないので、ここでは使いません。*3
代わりにbasketball-reference.comからデータをダウンロードしてきましょう。
こちらのページに、ベルターンス個人のスタッツがまとめられています。
このページの上の方にある "Dāvis Bertāns Overview" と書かれた行の"GameLogs"の部分にポインタを乗せると次のように各年の試合毎のスタッツ一覧のページへのリンクが表示されます。
今年のページへ移動してちょっと下へスクロールすると、"Regular Season"と書かれた横に "Share & Export"という表示が見えます。
またもや、マウスオーバーで下にリンクの一覧が表示されるので"Get as Excel Workbook"を選択するとxls形式でダウンロードできます。すぐ下にあるget table as CSVを選びたくなりますが、ここはグっと我慢してください。
これを1年毎に繰り返していけば、NBAでの全試合のスタッツがダウンロードできます。が、とりあえずWASに移籍してきた2019-2020シーズン以降の3ファイルだけ取ってきます。*4 ファイル名は全部固定で、"sportsref_download.xls"となっているので適当にリネームしておいた方が安心できます。
データの読み込み
excelファイルなんだら、pandas.read_excelで良かろうと思いきや、xlrdで読むとUnsupported format, or corrupt file: Expected BOF record;
と怒られます。
BOFでも先頭に付ければ解決するのかと思いつつ、一応エラーメッセージをぐぐって見たらpandas.read_htmlでhtmlとして読めとのこと
うちの環境では、別途lxmlパッケージをインストールする必要がありましたが、この方法で回避できました。
ただし、read_html(ファイル名)
とすると、0番目の要素にDataFrameが入ったリストが返ってくるので、全部取り出して連結する必要があります。
とまぁ日本語で書くと面倒なんですが、こんな感じのコードでカレントディレクトリにあるbasketlball-reference.comからダウンロードした全てのxlsファイルをまとめて1つのDataFrameにできます。
import pandas as pd import glob df=pd.concat([pd.read_html(f)[0] for f in glob.glob("*.xls")],ignore_index=True))
データ分析
さて、読み込んだDataFrameから色々と見てみましょう。
ベルターンスの場合、着目する情報は 3Pの確率("3P%"の列)、3Pの成功数("3P"の列)ぐらいのものです。3Pを高確率で決めてくれて、1試合あたりの本数も多ければ多いほど良いと考えます。*5
方針が決まったら、さくっとseabornでグラフを書いてみましょう。このコードは前のコードに続けて実行してください。( jupyter notebookでそれぞれのコードブロックをセルに書いて上から順に実行していく前提で書いてます)
import matplotlib.pyplot as plt import seaborn as sns sns.set(context='talk', style="ticks") df["3P"]=pd.to_numeric(df["3P"],errors='coerce') df["3P%"]=pd.to_numeric(df["3P%"],errors='coerce')*100 df=df[["3P","3P%"]].dropna() sns.jointplot(x="3P", y="3P%", data=df)
ふむ。
これを見ると、ベルターンスの成績は次のように分類できそうです。*6
- SSRベルターンス: 3P% >80 && 3P > 5
- SRベルターンス: 3P% >60 && 3P > 3
- Rベルターンス: 3P% >40 && 3P > 3
- Nベルターンス: その他
それでは、排出率を求めましょう。この期間中に彼が出場した試合数と、各ベルターンスの出現回数を数えます。
ちょっと直感的では無いコードですが、こんな感じで数えられます。df.count()
すると各列の行数を数えてくれるので、totalと矛盾が無いか確認しときましょう。
SSR=((df['3P%'] > 80) & (df['3P'] > 5)) SR=((df['3P%'] > 60) & (df['3P'] > 3)) R=((df['3P%'] > 40) & (df['3P'] > 3)) N=((df['3P%'] <= 40) | (df['3P'] <= 3)) print("SSR:",SSR.sum()) print("SR:",SR.sum()-SSR.sum()) print("R:",R.sum()-SR.sum()) print("N:",N.sum()) print("total:", R.sum()+N.sum())
結果はっぴょー
SSR: 1 SR: 11 R: 23 N: 86 total: 121
ここ3シーズンで、121回ガチャをまわして、SSRベルターンスは1回引けました。といわけでSSRの排出確率は1/121・・・ではありませんね。
SSRの排出率をpと置くと、121回試行して1回だけあたっているので
OK, Wolfram Alpha
これ他にも p<1を満たす解がある可能性はあると思うんですが、p<1を指定すると標準の計算時間を超えちゃうので、SSRベルターンスの排出率は5%ということにしましょう。
え?0.007%の方はどうするのかって?
人間は見たい数字しか見ないのです(キリ
まとめ
ベルターンスがんばれー :)
*1:この記事は12/2のWSH-MINを観ながら書いてたんですが、この試合でも活躍してたので、今年は既に2回ほどつよつよベルターンスを引けてます :)
*2:いやない
*4:ここはレギュラーシーズンの結果しか無いので、プレイオフの結果も含めたいならさらに下の方にある"Playoffs"のとこからもダウンロードが必要です。でもここ3シーズンだと去年の1回戦敗退の結果しか無いので、無視します :)
*5:この基準で行くと、ビシっと活躍してたのに、早々にケガで退場しちゃったとか、クォーター毎に無理目のブザービーターをトライして全部外したとかでも評価が下がっちゃいますが、まー彼の給料の査定をしてるわけでも無いので目をつむってもらいましょうw
*6:URベルターンスは入ってません :p