Đây là bài đầu tiên trong loạt bài tôi dự định viết khi tôi học cách áp dụng các phương pháp Bayesian cho các chủ đề/vấn đề khác nhau mà tôi thấy thú vị. Trong bài đăng này, tôi sẽ giới thiệu cách sử dụng trình khởi động Bayesian để đo lường độ không chắc chắn cho số yard của tiền vệ NFL [QB] trên mỗi lần thử chuyền [YPA]
Bayesian Bootstrap là gì và chúng ta tính toán nó như thế nào?
Bootstrapping là một kỹ thuật lấy mẫu lại cho phép chúng tôi tính toán độ không chắc chắn cho một thống kê quan tâm nhất định [e. g. trung bình, trung bình, vv. ]. Trong bootstrap cổ điển, chúng tôi xây dựng các phép đo độ không đảm bảo này bằng cách trước tiên tạo nhiều bộ dữ liệu, được gọi là mẫu bootstrap, bằng cách lấy mẫu có thay thế từ dữ liệu gốc. Sau đó, đối với mỗi mẫu mới được tạo này, chúng tôi tính toán thống kê quan tâm và kết thúc bằng phép tính gần đúng về phân phối của nó
Dưới đây là một ví dụ về bootstrap cổ điển được sử dụng để xây dựng một khoảng xung quanh đường hồi quy
Trong 1]
from IPython.display import Video Video['//www.stat.auckland.ac.nz/~wild/BootAnim/movies/bootstrap5-1.mp4']
Ra[1]
Trình duyệt của bạn không hỗ trợ phần tử
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass0
Trong bootstrap cổ điển, lấy mẫu có thay thế có thể được coi là. Trong bootstrap Bayesian, thay vì áp dụng trọng số từ phân phối đa thức, chúng tôi áp dụng trọng số từ phân phối Dirichlet đồng nhất [phẳng]. [Đây là giải thích trực quan về bản phân phối Dirichlet cho những ai chưa biết nó là gì]. Để tạo các mẫu bootstrap Bayesian, chúng tôi lặp lại quy trình sau bao nhiêu lần tùy thích [càng lặp lại nhiều lần càng tốt]
- Vẽ các trọng số từ phân phối Dirichlet đồng nhất có cùng thứ nguyên với số lượng quan sát trong dữ liệu
- GHI CHÚ. Ở đây, phân phối Dirichlet thống nhất hoạt động như một thông tin trước đó không có thông tin
- Nếu có thể, hãy tính thống kê bằng cách sử dụng trọng số từ phân phối Dirichlet
- Mặt khác, nếu thống kê không trực tiếp sử dụng trọng số trong tính toán của nó, hãy làm như sau
- Lấy mẫu lại dữ liệu theo các trọng số được rút ra từ phân phối Dirichlet
- GHI CHÚ. Trong bước này, chúng tôi tạo ra một mẫu lớn nhất có thể. Nó phải lớn bằng tập dữ liệu gốc
- Sử dụng dữ liệu được lấy mẫu lại để tính toán thống kê
- Lấy mẫu lại dữ liệu theo các trọng số được rút ra từ phân phối Dirichlet
Phân phối cuối cùng được tạo bởi Bayesian bootstrap là phân phối sau của thống kê quan tâm
Để hiểu rõ hơn về quy trình trên, hãy mã hóa hàm khởi động Bayesian của riêng chúng ta
Trong 2]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]
Áp dụng Bayesian Bootstrap cho NFL
Bây giờ chúng ta đã có một số kiến thức về cách thức hoạt động của Bayesian bootstrap, hãy sử dụng nó để so sánh YPA trung bình của Drew Brees và Deshaun Watson từ mùa trước. Mặc dù chúng tôi đã tạo chức năng khởi động Bayesian của riêng mình, nhưng trong tương lai, chúng tôi sẽ sử dụng gói bayesian_bootstrap để phân tích
Trước tiên, hãy nhập các gói còn lại mà chúng tôi sẽ sử dụng
Trong 3]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext
Trong [4]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]
Sau đó, hãy thiết lập dữ liệu của chúng tôi. Dữ liệu play by play đến từ kho lưu trữ github dữ liệu nflscrapR tuyệt vời của Ron Yurko
Trong [5]
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass
Trong [6]
brees_df = qb_pass_df.loc[qb_pass_df.Player == 'Drew Brees'] watson_df = qb_pass_df.loc[qb_pass_df.Player == 'Deshaun Watson']
Trong [7]
# make sure only 1 player has the name Dree Brees brees_df.GSIS_ID.nunique[]
Ra[7]
________số 8
Trong [8]
# check Watson too watson_df.GSIS_ID.nunique[]
Ra[8]
________số 8
Bây giờ chúng tôi đã thiết lập dữ liệu, hãy tính YPA của mỗi người chơi cho mùa giải 2017
Trong [9]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]0
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]1
Để xây dựng các mẫu khởi động Bayesian của chúng tôi cho từng QB, chúng tôi chuyển số thước thu được trên mỗi lần thử vượt qua của chúng sang hàm
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass2 của
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass1 và đặt số lần sao chép mà chúng tôi muốn
Trong [10]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]2
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass2 trả lại cho chúng tôi một
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass4 với giá trị trung bình cho mỗi mẫu khởi động.
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass4 này là phân phối sau trên các giá trị trung bình có thể xảy ra của dữ liệu mà chúng tôi quan tâm
Trong [11]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]3
Ra[11]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]4
Trong [12]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]5
Ra[12]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]6
Khá đơn giản để vẽ phần sau với hàm
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass7 của
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass6
Trong [13]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]7
Chúng ta cũng có thể xây dựng một khoảng tin cậy.
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass1 cung cấp hai phương thức để thực hiện điều đó, hàm
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass9 và hàm
brees_df = qb_pass_df.loc[qb_pass_df.Player == 'Drew Brees'] watson_df = qb_pass_df.loc[qb_pass_df.Player == 'Deshaun Watson']0. Khi xây dựng các khoảng cho unimodal [i. e. nó có một đỉnh], phân phối đối xứng, cả hai phương pháp sẽ cung cấp cho bạn các khoảng tương tự. Nhưng nếu phân phối là đa phương thức [i. e. nó có nhiều đỉnh đại diện cho các chế độ được phân tách rõ ràng], hàm
# Read in and set up our data. pbp_df = pd.read_csv['data/pbp_2017.csv', low_memory=False] rosters_df = pd.read_csv['data/team_2017_rosters.csv'] # replace . with _ pbp_df.columns = pbp_df.columns.str.replace['.', '_'] # keep all qb pass attempt # first we keep the plays where a pass occured # then we get the passer's position [along with their full name and GSIS_ID] # in order to filter out all non-QB pass attempts qb_pass_df = [pbp_df.query['PassAttempt == 1'] .merge[rosters_df[['GSIS_ID', 'Player', 'Pos']], how='left', left_on='Passer_ID', right_on='GSIS_ID'] .query['Pos == "QB"']] # some plays are labeled as both a sack and a pass attempts, they should be # one or the other # For the 2017 pbp data I found 17 instances where this mislabeling occurs # I manually checked the description in another notebook, # they tend to be plays that were challenged and reversed # here I correct the issue sack_and_pass_mask = [qb_pass_df.Sack==1] & [qb_pass_df.PassAttempt==1] corrected_sack = np.array[[0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1]] corrected_pass = 1 - corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'Sack'] = corrected_sack qb_pass_df.loc[sack_and_pass_mask, 'PassAttempt'] = corrected_pass9 sẽ trả về một khoảng duy nhất trong khi hàm
brees_df = qb_pass_df.loc[qb_pass_df.Player == 'Drew Brees'] watson_df = qb_pass_df.loc[qb_pass_df.Player == 'Deshaun Watson']0 sẽ trả về nhiều khoảng rời rạc xoay quanh các chế độ phân phối. Một cuộc thảo luận tốt về hai phương pháp xây dựng các khoảng tin cậy này có thể được tìm thấy ở đây và ở đây. Ở đây chúng tôi sẽ sử dụng hàm
brees_df = qb_pass_df.loc[qb_pass_df.Player == 'Drew Brees'] watson_df = qb_pass_df.loc[qb_pass_df.Player == 'Deshaun Watson']0 để tính toán các khoảng tin cậy của chúng tôi
Trong [14]
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]8
import numpy as np def bayes_boot[X, statistic, n_samples1=1000, n_samples2=1000, weight_kwd=None, *args, **kwargs]: # draw our weights from the uniform dirichlet distribution # [1]*len[X] is the dimension of the distribution # n_samples1 represents the number of bootstrap replications to perform # from the bayesian perspective think of it as the number of draws from the # posterior distribution # in terms of the classical bootstrap this is the number times the data is # resampled weights = np.random.dirichlet[[1]*len[X], n_samples1] # if the statistic function accepts weights, use them to calculate the # bayesian bootstrap samples if weight_kwd is not None: samples = [statistic[X, *args, **{weight_kwd: w}, **kwargs] for w in weights] # otherwise we have to do a weighted resampling of the data, based on # the weights we drew from the dirichlet distribution else: samples = [] for w in weights: # resample the indexes using the dirchlet weights # the greater n_sample2 is, the better sample_idx = np.random.choice[range[len[X]], p=w, size=n_samples2] X_resample = X[sample_idx] # calculate the statistic on the resampled data and append it # to our samples list samples.append[statistic[X, *args, **kwargs]] return np.array[samples]9
Trong [15]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext0
Brees' 95% HDI nằm trong khoảng từ 7. 33 và 9. 04 YPA, có nghĩa là theo mô hình của chúng tôi [bạn có thể coi trình khởi động Bayesian là ] và dữ liệu chúng tôi quan sát được [Bree's 'pass], có 95% khả năng YPA có nghĩa là của Brees nằm trong khoảng từ 7. 33 YPA và 9. 04 YPA
Bây giờ chúng ta cũng hãy vẽ sơ đồ phân phối sau của Watson và so sánh các phân phối cho cả hai QB
Trong [16]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext1
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext2
Trong [17]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext3
Trong [18]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext4
Phân phối sau của Watson ngắn hơn và rộng hơn Brees', điều này cho thấy có nhiều sự không chắc chắn hơn đối với YPA trung bình của Watson
Chúng tôi cũng có thể đo lường mức độ không chắc chắn về sự khác biệt giữa YPA của người chơi bằng cách trừ đi những người đi sau của họ
Trong 19]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext5
Trong 20]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext6
Hết[20]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext7
Trong [21]
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext8
# import the rest of the packages we will use %matplotlib inline import matplotlib.pyplot as plt import seaborn as sns import pandas as pd import bayesian_bootstrap.bootstrap as bb from scipy import stats from astropy.utils import NumpyRNGContext9
Trong [22]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]0
Vì vậy, giá trị trung bình của phía sau là khoảng 0. 2 và 95% HDI khá rộng, dao động từ -1. 5 đến khoảng 2, cho chúng tôi thấy rằng chúng tôi thực sự không thể chắc chắn rằng YPA trung bình của hai người chơi có khác nhau ở mùa giải trước hay không
Chúng ta thực sự có thể tính xác suất mà YPA trung bình của Watson lớn hơn YPA trung bình của Brees bằng cách đo tỷ lệ các giá trị lớn hơn 0 trong phân phối trên
Trong [23]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]1
Hết[23]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]2
Vì vậy, có vẻ như khoảng 58% phân phối lớn hơn 0, điều này không khiến chúng tôi tin tưởng nhiều rằng YPA trung bình của Watson lớn hơn Brees'
So sánh nhiều người chơi
Ngoài Brees và Watson, chúng ta có thể sử dụng Bayesian bootstrap để so sánh các QB khác
Trong [24]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]3
Ra[24]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]4
Trong [25]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]5
Trong [26]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]6
Trong [27]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]7
Hết[27]
GSIS_IDPlayerest_mean_ypalower_cilower_ci_diffobs_mean_ypapass_attemptspass_yardsposteriorupper_ciupper_ci_diff000-0019596Tom Brady7. 8590857. 027669-0. 8240557. 8517245804554[8. 052497311890308, 8. 28356971609547, 7. 625991. 8. 6482030. 796479100-0020531Drew Brees8. 1949627. 321573-0. 8750568. 1966295344377[8. 440465203110161, 7. 603699584945726, 7. 68760. 9. 0300840. 833455200-0022803Eli Manning6. 1367535. 463517-0. 6730866. 1366025713504[6. 149504389328366, 6. 234419608989855, 7. 89640. 6. 8419150. 705312300-0023459Aaron Rodgers7. 0063605. 737744-1. 2664587. 0042022381667[6. 115763597383859, 6. 514784799147179, 6. 86876. 8. 2701971. 265995400-0026143Matt Ryan7. 8581817. 010996-0. 8464197. 8574145264133[8. 150744203165871, 7. 99024514648987, 7. 177652. 8. 6607070. 803292500-0029263Russell Wilson7. 2992516. 476802-0. 8226537. 2994565514022[7. 271371079500725, 7. 0028468601950715, 6. 8421. 8. 2010600. 901605600-0031280 Derek Carr 6. 8305515. 968901-0. 8679936. 8368935153521[6. 340925036310854, 6. 666601027037412, 6. 84111. 7. 7167060. 879813700-0031345 Jimmy Garoppolo 8. 8678237. 355245-1. 5083928. 8636361761560[9. 349730583896454, 9. 199047897675854, 7. 62247. 10. 4439381. 580302800-0033077Dak Prescott6. 8881486. 053135-0. 8423576. 8954924883365[6. 849487247884706, 6. 420418756772011, 6. 73932. 7. 8331110. 937620900-0033537Deshaun Watson8. 4010896. 847258-1. 5636338. 4108912021699[7. 523757238917812, 7. 607141473737741, 8. 29124. 9. 9804711. 569580
Chúng ta có thể so sánh hậu thế của mỗi người chơi bằng cách vẽ đồ thị 95% HDI của họ như vậy
Trong [28]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]8
Chúng tôi cũng có thể tạo các biểu đồ đường viền [trước đây gọi là biểu đồ điều khiển] để vẽ biểu đồ phân phối sau đầy đủ cho mỗi người chơi
Trong [29]
# set up the style for our plots sns.set[style='white', palette='colorblind', font_scale=1.3, rc={'figure.figsize':[12,9], "axes.facecolor": [0, 0, 0, 0]}]9
Hy vọng rằng bạn tìm thấy bài viết blog này hữu ích. Nếu bạn thấy bất kỳ sai lầm nào, có bất kỳ câu hỏi hoặc đề xuất nào [hoặc nếu bạn đang tuyển dụng. ]] bạn có thể gửi email cho tôi tại savvas. tjortjoglou@gmail. com, liên hệ với tôi trên Twitter @savvastj hoặc chỉ để lại nhận xét bên dưới
Nếu bạn thích bài đăng này và muốn hỗ trợ blog của tôi, bạn có thể xem trang patreon của tôi tại đây
Tài nguyên
Để tìm hiểu sâu hơn về bootstrap Bayesian, tôi khuyên bạn nên đọc loạt bài đăng trên blog của Rasmus Bååth về chủ đề này. Bài nói chuyện tại hội nghị của anh ấy cũng rất đáng xem. Chỉ mất 15 phút và anh ấy đã làm rất tốt việc giải thích cả bootstrap cổ điển và bootstrap Bayesian
Bài đăng trên blog của anh ấy
- Bootstrap phi tham số như một mô hình Bayesian
- Dễ dàng Bayesian Bootstrap trong R
- bayesboot. Gói R để thực hiện trình khởi động Bayesian
Video bài nói chuyện của anh ấy
- bayesboot. Gói R để dễ dàng khởi động Bayesian
Đây là các liên kết đến kho github cho gói R
brees_df = qb_pass_df.loc[qb_pass_df.Player == 'Drew Brees'] watson_df = qb_pass_df.loc[qb_pass_df.Player == 'Deshaun Watson']4 và gói Python `bayesian_bootstrap'