Es gibt viele Dinge auf der Welt, über die man sich Sorgen machen muss, aber ich denke, das Wichtigste ist: "Wie alt sind Baseballspieler, wenn sie die meisten Heimbasen erreichen können?" Dieses Mal möchte ich die Trefferquote der Heimatbasis für das Alter jedes Schlägers nach Bayes'scher Schätzung aus den Daten der Hauptliga berechnen. Die wahre Definition ist anders, aber der Einfachheit halber definieren wir sie als "Home Base-Trefferquote = Home Base-Treffer / Treffer". Mit anderen Worten, die Trefferquote der Heimbasis ist der Wert dafür, wie wahrscheinlich es ist, dass ein Spieler eine Heimbasis trifft, wenn er auf dem Sitz steht (außer bei vier Bällen).
Das zu verwendende Werkzeug ist
ist.
Die beabsichtigten Leser sind diejenigen, die sich für Baseball, Bayes'sche Statistiken, Python und Stan interessieren oder diese studieren (fast niemand und umgekehrt, Sie müssen diesen Artikel nicht lesen. Es gibt Bedenken). Daher werde ich nicht jedes im Detail erklären, aber ich möchte gegebenenfalls einige gute Literatur auflisten.
Excuse
Da ich ein Statistiker und Bayesianer Schätzer und Stan Laie bin, kann der folgende Inhalt verschiedene Arten von Fehlern enthalten, von grundlegend bis geringfügig. Vielen Dank(?).
Lahman's Database ist eine überwältigende Website für alle Spieler in den wichtigsten Ligen der letzten 100 Jahre. , Die Anzahl der Home Base Hits ...) wird veröffentlicht, also werde ich es verwenden. Dieses Mal werden wir die Version 2016 der CSV-Datei herunterladen ("Version 2016 herunterladen" -> "Version 2016 - durch Kommas getrennte Version").
Wenn Sie die heruntergeladene Datei entpacken, verlieren Sie die Motivation, da es unendlich viele CSV-Dateien gibt, aber dieses Mal werde ich sie verwenden
--Master.csv (Basisinformationen der Spieler) --Batting.csv
Nur.
Importieren Sie die diesmal zu verwendende Bibliothek, lesen Sie die CSV-Datei und machen Sie sie zu pandas.DataFrame. Pandas ist eine wahnsinnig praktische Excel-ähnliche Bibliothek, die in Python (entsprechend) verwendet werden kann. Sie ist sehr nützlich, um Daten in Form einer Tabelle zu speichern und detaillierte Operationen an den Daten durchzuführen.
import numpy as np
import pandas as pd
import pystan
import matplotlib.pyplot as plt
bat = pd.read_csv('Batting.csv')
mas = pd.read_csv('Master.csv')
Eine Spalte in Batting.csv ist die Hitleistung eines Jahres mit einem Spieler. Mal sehen, wie es aussieht.
bat.tail()
Ich sehe, es fühlt sich gut an. Verschiedene Noten hängen vom Jahr und der ID des Spielers ab. Was ich diesmal jedoch wissen möchte, ist die Korrelation zwischen Alter und Noten, daher muss ich eine Altersspalte hinzufügen. Tatsächlich sind in Master.csv die ID des Spielers und das Geburtsjahr aufgereiht, also werde ich dies verwenden. Werfen wir einen Blick auf die Form von Master.csv.
mas.tail()
OK. Füge die beiden "DataFrame" mit der ID des Spielers als Schlüssel zusammen. Danach wird das Alter des Spielers berechnet, indem das Geburtsjahr des Spielers vom Saisonjahr abgezogen wird. In den wichtigsten Ligen wird jedoch das Alter der Spieler in einer bestimmten Saison ab dem 30. Juni offiziell verwendet. Für Spieler, die im Juli oder später geboren wurden, sollte das Geburtsjahr das folgende Jahr sein.
mas['birthYear'] = mas['birthYear'] + (mas['birthMonth'] >= 7)
bat = pd.merge(bat, mas[['playerID', 'birthYear']], on='playerID')
bat['age'] = bat['yearID'] - bat['birthYear']
Übrigens möchte ich die folgenden zwei Bücher erwähnen, die Tipps für die Erstellung von Baseballstatistiken enthalten, wie z. B. die Geschichte des oben genannten Alters und verschiedene Analyseergebnisse.
Lassen Sie es uns analysieren, aber es scheint nicht so gut zu sein, zu alte Daten oder Daten von Spielern zu verwenden, die nur wenig teilgenommen haben. Deshalb werde ich sie dieses Mal aus dem Datenrahmen herausschneiden. Insbesondere werden wir nur die Daten von Spielern verwenden, die nach 1965 geboren wurden und insgesamt mehr als 1000 Sitze haben. Dazu berechnen wir zuerst die Anzahl der Sitze (PA) und verwenden dann "groupby", um die Gesamtzahl der Sitze für diesen Spieler zu erhalten.
bat['PA'] = bat['AB'] + bat['BB'] + bat['HBP'] + bat['SH'] + bat['SF']
pa_c = bat.groupby('playerID').sum()[['PA']]
pa_c.columns = ['PA_Career']
bat = pd.merge(bat, pa_c, left_on='playerID', right_index=True)
Werfen wir einmal einen Blick auf den DataFrame
.
bat.head()
Rechts wurde eine Spalte mit dem Alter der Spieler hinzugefügt, was sehr schön ist. Lassen Sie uns nun eine tatsächliche Analyse durchführen.
Im Folgenden beginnen wir mit einem einfachen Modell und aktualisieren es schrittweise, um ein besseres Gefühl zu erzielen.
Zunächst möchte ich versuchen, die Anzahl der Sitze und Heimbasis-Treffer aller Spieler für jedes Alter zu addieren und zu teilen. Beispiel: "Wenn die Gesamtzahl der Treffer eines 30-jährigen Spielers in den Daten 100.000 beträgt und 3000 Treffer in der Heimatbasis erzielt werden, beträgt die Trefferquote in der Heimatbasis eines 30-jährigen Spielers 3000 / 100.000, was 0,03 entspricht." Machen. Diese Art der Verarbeitung kann sehr einfach mit der Funktion "Gruppieren nach" von "Pandas" durchgeführt werden.
HRdf = bat.groupby('age').sum()[['AB', 'HR']]
HRdf['HRR'] = HRdf['HR'] / HRdf['AB']
fig, ax = plt.subplots()
HRdf['HRR'].plot(kind='line', style='o-', c='black', ax=ax)
ax.set_ylabel('p')
Die horizontale Achse ist das Alter und die vertikale Achse ist die Home-Base-Trefferquote. Aha. Es ist ersichtlich, dass die Trefferquote in der Heimatbasis ab dem 19. Lebensjahr langsam ansteigt und im Alter von 29 Jahren am höchsten ist. Der Wert zu diesem Zeitpunkt beträgt 0,034, dh ungefähr 3,4%. Danach ging es langsam zurück und nach dem 40. Lebensjahr ging es stark zurück.
Es ist also in Ordnung, auf die Idee zu kommen, dass Sie froh sind, die Trefferquote der Heimatbasis für jedes Alter zu kennen, aber es scheint ein Problem zu geben, wenn Sie sich das obige Diagramm als "echte Trefferquote der Heimatbasis" vorstellen.
(a). ** Individuelle Unterschiede werden nicht berücksichtigt. ** In dieser Abbildung sollen Spieler im Alter von 37 und 8 Jahren genauso viele Heimbasen treffen wie Spieler Ende 20. Stimmt das? Wenn Sie ruhig denken, scheint dies daran zu liegen, dass in den späten 30ern die Spieler mit schlechter Schlagleistung in den Ruhestand gehen und nur die Spieler, die die Heimbasis erreichen können, übrig bleiben. Solche Spieler, die bis Ende 30 aktiv sein können, sollten Ende 20 mehr Heimbasen getroffen haben, aber das geht aus dieser Zahl nicht hervor. Dies liegt daran, dass individuelle Unterschiede nicht berücksichtigt werden, da alle Treffer gemittelt wurden.
(b). ** Laut. ** In der obigen Abbildung trifft ein 39-jähriger Schlagmann ungewöhnlich auf eine Heimbasis. Dies bedeutet jedoch nicht, dass die Fähigkeit eines Baseballspielers, eine Heimbasis zu treffen, im Alter von 39 Jahren plötzlich zunimmt. .. Ich kann mir keinen Grund vorstellen, warum ich plötzlich eine Heimatbasis erreichen könnte, nur weil ich 39 Jahre alt war. Wenn man normal denkt, sollten die wahren Fähigkeiten eines Baseballspielers eines 39-jährigen Spielers ungefähr gleich oder etwas geringer sein als die bei 37 und 38.
(c). ** Ich kenne die Zuverlässigkeit der Datenpunkte nicht. ** In den obigen Daten ist die Anzahl der 30-jährigen und 45-jährigen Schläger völlig unterschiedlich. Bei einer Verarbeitung, die wie diese Zeit den Gesamtdurchschnitt annimmt, steigt die Zuverlässigkeit des Werts mit zunehmender Anzahl von Stichproben, sodass die 45 Jahre alten Daten nahezu unzuverlässig sind (was einem Wert nahe dem wahren Wert entspricht). Es ist unwahrscheinlich, dass Sie dort sind. Trotzdem finde ich es seltsam, die 30 Jahre alten Daten würdevoll in Einklang zu bringen.
Aus den oben genannten Gründen möchte ich statistische Modelle erstellen. Das Obige ist meine (Amateur-) Idee. Wenn Sie also beispielsweise die Vorteile und Grundideen der statistischen Modellierung kennenlernen möchten, sollten Sie das folgende Buch lesen.
Lassen Sie uns das folgende Modell machen.
Ich habe plötzlich eine unverständliche Formel herausgegeben, deshalb werde ich sie auf Japanisch erklären. Angenommen, die Home-Base-Treffer eines Schlagmanns in einer Saison folgen einer Binomialverteilung, die durch die Treffer $ AB $ und die Home-Base-Trefferquote $ p $ bestimmt wird. Angenommen, die Home-Base-Trefferquote $ p $ wird durch eine Logistikfunktion dargestellt, die die Summe der Konstanten $ \ beta $ und der Variablen $ r_ {age} $, die durch das Alter des Spielers bestimmt wird, als Argument verwendet. Hier folgt der Wert von $ r_ {age} $ für jedes Alter einer Normalverteilung mit einem Mittelwert von 0 und einer Varianz von $ s_ {age} $. Von nun an werden wir PyStan verwenden, einen Wrapper für Python der freien Software Stan, den MCMC ausführen kann, um die Bayes'sche Schätzung jedes Parameters durchzuführen.
Was ist hier die Bayes'sche Schätzung, was ist MCMC, wie benutzt man Stan, was ist die Wahrscheinlichkeitsverteilung, warum leben Menschen überhaupt, wie wurde das menschliche Bewusstsein geboren, KI Ich werde es hier nicht erklären, weil es völlig dunkel sein wird und es viele Erklärungen von Menschen gibt, die anständiger sind als ich, wenn ich sage, ob ich Bewusstsein haben kann.
Für Bücher, die auf Japanisch relativ einfach zu lesen sind, sind die Grundlagen der Bayes'schen Schätzung und statistischen Modellierung oben aufgeführt "Einführung in die statistische Modellierung für die Datenanalyse". co.jp/%E3%83%87%E3%83%BC%E3%82%BF%E8%A7%A3%E6%9E%90%E3%81%AE%E3%81%9F%E3%82 % 81% E3% 81% AE% E7% B5% B1% E8% A8% 88% E3% 83% A2% E3% 83% 87% E3% 83% AA% E3% 83% B3% E3% 82% B0 % E5% 85% A5% E9% 96% 80 __% E4% B8% 80% E8% 88% AC% E5% 8C% 96% E7% B7% 9A% E5% BD% A2% E3% 83% A2% E3 % 83% 87% E3% 83% AB% E3% 83% BB% E9% 9A% 8E% E5% B1% A4% E3% 83% 99% E3% 82% A4% E3% 82% BA% E3% 83 % A2% E3% 83% 87% E3% 83% AB% E3% 83% BBMCMC-% E7% A2% BA% E7% 8E% 87% E3% 81% A8% E6% 83% 85% E5% A0% B1% E3% 81% AE% E7% A7% 91% E5% AD% A6-% E4% B9% 85% E4% BF% 9D-% E6% 8B% 93% E5% BC% A5 / dp / 400006973X / ref = pd_sim_14_9? _encoding = UTF8 & psc = 1 & refRID = TCFDQTQGKQEM42AERGJF), jedoch für Stan "Bayes statistische Modellierung mit Stan und R" Wenn Sie -22 /) lesen, können Sie es verstehen.
Der Code für PyStan ist unten.
AB = bat['AB'].values
HR = bat['HR'].values
age_map = {age: idx+1 for idx, age in enumerate(np.unique(bat['age'].values))}
age = bat['age'].map(age_map).values.astype(int)
data = dict(N=len(AB), N_age=len(np.unique(age)), HR=HR, AB=AB, age=age)
model_code = """
data {
int N;
int N_age;
int HR[N];
int AB[N];
int age[N];
}
parameters {
real beta;
real<lower=0> s_age;
vector[N_age] r_age;
}
transformed parameters {
vector[N] p;
for (i in 1:N)
p[i] = inv_logit(beta + r_age[age[i]]);
}
model {
r_age ~ normal(0, s_age);
HR ~ binomial(AB, p);
}
generated quantities {
real p_age[N_age];
for (i in 1:N_age)
p_age[i] = inv_logit(beta + r_age[i]);
}
"""
fit = pystan.stan(model_code=model_code, data=data, chains=4, iter=1000)
Wenn Sie den obigen Code ausführen, bemüht sich der Computer, die Verteilung verschiedener Parameter ($ \ beta, s_ {age}, r_ {age} $) zu ermitteln. Bei der Bayes'schen Schätzung ist der Wert des Parameters nicht auf einen Wert festgelegt, sondern wird als Verteilung erhalten, sodass er die Unsicherheit der Daten darstellen kann. Beispielsweise haben die 45 Jahre alten Daten weniger Stichproben und sind unsicherer als die 30 Jahre alten Daten, was zu einer breiteren Verteilung der Parameter führt. Mit anderen Worten wird das Problem (c) der Analyse nur durch Mitteln der oben erläuterten Daten gelöst.
Schließlich möchte ich die Home-Base-Trefferquote $ p $ für jedes Alter schätzen. Zeichnen wir sie also mit $ r_ {age} $ mit dem folgenden Code.
def plot_r_p(fit):
inv_age_map = {v: k for k, v in age_map.items()}
x_age = [inv_age_map[a] for a in np.unique(age)]
r_age_trace = fit.extract()['r_age']
p_age_trace = fit.extract()['p_age']
#Median und 50%・ 95%Berechnen Sie das Perzentil an den folgenden Punkten, um das vorhergesagte Intervall zu zeichnen
percents = [2.5, 25, 50, 75, 97.5]
r_age = []
p_age = []
for percent in percents:
r_age.append(np.percentile(r_age_trace, percent, axis=0))
p_age.append(np.percentile(p_age_trace, percent, axis=0))
fig, axs = plt.subplots(nrows=1, ncols=2, sharex=True, figsize=(12, 4))
# axs[0] : r_age
axs[0].plot(x_age, r_age[2], 'o-', color='blue') #Median
axs[0].fill_between(x_age, r_age[0], r_age[4], alpha=0.1, color='blue') # 95%Sektion
axs[0].fill_between(x_age, r_age[1], r_age[3], alpha=0.3, color='blue') # 50%Sektion
axs[0].set(xlabel='age', ylabel=r'$r_{age}$')
# axs[1] : p_age
axs[1].plot(x_age, p_age[2], 'o-', color='red')
axs[1].fill_between(x_age, p_age[0], p_age[4], alpha=0.1, color='red')
axs[1].fill_between(x_age, p_age[1], p_age[3], alpha=0.3, color='red')
axs[1].set(xlabel='age', ylabel=r'$p_{age}$')
return fig, axs
fig, axs = plot_r_p(fit)
HRdf['HRR'].plot(kind='line', style='o-', c='black', ax=axs[1]
Die blauen Daten auf der linken Seite sind $ r_ {age} $, die schwarzen Daten auf der rechten Seite sind der oben gezeigte einfache Durchschnitt und die roten Daten sind die geschätzte Trefferquote für jedes Alter $ p_ {age} $. In beiden Diagrammen repräsentiert die helle Hintergrundfarbe den vorhergesagten Abschnitt von 50% und die dunkle Hintergrundfarbe den vorhergesagten Abschnitt von 95%.
Wenn Sie sich die Ergebnisse ansehen, fühlt es sich wie hmm an. Ich denke, dass das Problem (c) teilweise gelöst wurde, aber nichts gegen (a) individuelle Unterschiede und (b) Rauschprobleme unternommen wurde. Zum Beispiel soll er wie üblich im Alter von 39 Jahren plötzlich eine Heimatbasis erreichen, und als neues Problem verbessert sich seine Fähigkeit nach seinen 40ern, die nur wenige Datenpunkte haben, was ein unrealistisches Ergebnis ist (dies). Ich denke, das liegt wahrscheinlich daran, dass die Wahrscheinlichkeit höher ist, wenn sie sich dem Durchschnitt aller Altersgruppen nähert.
Um die oben genannten Probleme zu lösen, werden wir die Zeitreihen betrachten. Dies bedeutet, dass das vorherige Modell altersspezifische Fähigkeiten völlig unabhängig behandelte (entsprechend der Gleichung von Modell 1, bei der $ r_ {age} $ einer Normalverteilung mit einem Durchschnitt von 0 folgt). Wenn Sie jedoch darüber nachdenken, sollte Ihre Fähigkeit im Alter von j nahe an Ihrer Fähigkeit im Alter (j-1) liegen. Wenn sich dies im Modell widerspiegelt, wird es meiner Meinung nach wie folgt aussehen.
In diesem Modell beträgt der Durchschnitt der Normalverteilung, der $ r_ {age} [j] $ folgt, $ r_ {age} [j-1] $. Dies stellt die Annahme dar, dass "die Fähigkeit im Alter j nahe an der Fähigkeit im Alter (j-1) liegen sollte".
AB = bat['AB'].values
HR = bat['HR'].values
age_map = {age: idx+1 for idx, age in enumerate(np.unique(bat['age'].values))}
age = bat['age'].map(age_map).values.astype(int)
data = dict(N=len(AB), N_age=len(np.unique(age)), HR=HR, AB=AB, age=age)
model_code = """
data {
int N;
int N_age;
int HR[N];
int AB[N];
int age[N];
}
parameters {
real beta;
real<lower=0> s_age;
vector[N_age] r_age;
}
transformed parameters {
vector[N] p;
for (i in 1:N)
p[i] = inv_logit(beta + r_age[age[i]]);
}
model {
r_age[1] ~ normal(-sum(r_age[2:N_age]), 0.001);
r_age[2:N_age] ~ normal(r_age[1:(N_age-1)], s_age);
HR ~ binomial(AB, p);
}
generated quantities {
real p_age[N_age];
for (i in 1:N_age)
p_age[i] = inv_logit(beta + r_age[i]);
}
"""
fit2 = pystan.stan(model_code=model_code, data=data, chains=4, iter=1000)
Die einzige Änderung gegenüber Modell 1 ist der Teil, in dem "r_age [2: N_age] ~ normal (r_age [1: (N_age-1)], s_age)" in "model" (Summe von $ r_ {age} $) festgelegt ist. Der Teil, in dem r_age [1] ~ normal (-sum (r_age [2: N_age]), 0.001)
auf 0 gesetzt ist, ist wirklich nicht sehr gut ...).
Das Ergebnis ist wie folgt aufgetragen.
Also das war's ~. Es gibt zwei Vorteile gegenüber Modell 1.
――Die Steigerung der Fähigkeiten im Alter von 39 Jahren wurde ein wenig unterdrückt. ――Die Fähigkeit in meinen 40ern hat von Jahr zu Jahr abgenommen
All dies sind Effekte, die Zeitreihen berücksichtigen und als realistische Ergebnisse angesehen werden. Ich habe also das Gefühl, dass sich das Rauschen in Problem (b) etwas verbessert hat. Lassen Sie uns abschließend ein Modell erstellen, das individuelle Unterschiede berücksichtigt.
Fügen Sie also einen Begriff $ r_ {id} $ hinzu, der individuelle Unterschiede zum Modell darstellt. Dies ist eine Variable, die den Unterschied in den Fähigkeiten jedes Spielers darstellt und einer Normalverteilung mit einem Durchschnitt von 0 folgen sollte.
id_map = {id: idx+1 for idx, id in enumerate(np.unique(bat['playerID'].values))}
id = bat['playerID'].map(id_map).values.astype(int)
data = dict(N=len(AB), N_age=len(np.unique(age)), N_id=len(np.unique(id)), HR=HR, AB=AB, id=id, age=age)
model_code = """
data {
int N;
int N_id;
int N_age;
int HR[N];
int AB[N];
int id[N];
int age[N];
}
parameters {
real beta;
real<lower=0> s_age;
real<lower=0> s_id;
vector[N_age] r_age;
vector[N_id] r_id;
}
transformed parameters {
vector[N] p;
for (i in 1:N)
p[i] = inv_logit(beta + r_age[age[i]] + r_id[id[i]]);
}
model {
r_id ~ normal(0, s_id);
r_age[1] ~ normal(-sum(r_age[2:N_age]), 0.001);
r_age[2:N_age] ~ normal(r_age[1:(N_age-1)], s_age);
HR ~ binomial(AB, p);
}
generated quantities {
real p_age[N_age];
for (i in 1:N_age)
p_age[i] = inv_logit(beta + r_age[i]);
}
"""
fit3 = pystan.stan(model_code=model_code, data=data, chains=4, iter=1000)
Mal sehen, das Ergebnis.
Also das war's ~. Ist das nicht ein ziemlich gutes Ergebnis? Das Alter ändert sich reibungslos mit jeder Fähigkeit und sieht ziemlich vernünftig aus. Hier ist der geschätzte Wert von $ p_ {age} $ viel kleiner als der gemessene Wert, da der geschätzte Wert "$ p_ {age} $ des durchschnittlichen Spielers nach Alter" darstellt. Ich denke. Ich denke, je erstaunlicher die Spieler sind, desto mehr Plätze haben sie in der tatsächlichen Messung. Wenn Sie also den Gesamtdurchschnitt nehmen, ist das Gewicht der erstaunlichen Spieler höher als das der Spieler, die nicht sehr schwer sind, und infolgedessen ist der Wert größer als das $ p_ {age} $ des durchschnittlichen Spielers. Ich werde herauskommen.
Zusammenfassend lässt sich sagen, dass 29-Jährige am ehesten die Heimatbasis erreichen, etwas mehr als 2,5% für den durchschnittlichen Spieler. Wenn Sie sich die Veränderungen der Fähigkeiten vor und nach dem 29. Lebensjahr ansehen, sehen Sie, dass das Wachstum vor dem 29. Lebensjahr schneller ist als der Rückgang nach dem 29. Lebensjahr. Zum Beispiel ist $ p_ {age} $ ungefähr 20 Jahre alt etwas höher als 1,5%, so dass Spieler um 30 Jahre durchschnittlich $ (2,5 / 1,5) \ simeq 1,7 sind als Spieler um 20 Jahre alt. Sie können damit rechnen, eine Heimatbasis zu erreichen, die etwa doppelt so hoch ist.
Ich denke, ich kann noch verschiedene Dinge tun, aber vorerst hier ...
Als Ergebnis der Bayes'schen Schätzung unter Verwendung von PyStan, wie alt ein Baseballspieler die Heimatbasis am meisten treffen kann, wurde festgestellt, dass der 29-jährige Spieler die Heimatbasis am meisten treffen kann.
das ist alles, vielen Dank.
Bonus: Ich habe die gleichen Untersuchungen für andere Indikatoren durchgeführt, aber ich bin der Meinung, dass die Auflistung außerhalb des Bereichs von Qiita liegt, daher ist dies anders (http://hoture6.hatenablog.com/entry/2017/06). / 08/212256). Wenn Sie interessiert sind, bitte.
Recommended Posts