<jupyter_start><jupyter_text>Estimating Pass Difficulty *<NAME>, March 2019* Using **Statsbomb** [Open Data][1] from *World Cup 2018* Tutorial will cover:- Downloading and Parsing Statsbomb JSON- Building Basic Logistic Regression Model- Evaluating Model using ROC Curve- Feature Engineering- Building Piecewise Model Dependencies Include:- pandas- numpy- scikit-learn- matplotlib[1]: https://github.com/statsbomb/open-data "Statsbomb Open Data Github Repository"<jupyter_code># Nice trick for high-resolution figures on retina screen
%config InlineBackend.figure_format = 'retina'
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Set random seed for reproducability
np.random.seed(42)<jupyter_output><empty_output><jupyter_text>Downloading and Parsing Statsbomb Data<jupyter_code>import requests
# Statsbomb's competition 43 is the World Cup
match_info_url = "https://raw.githubusercontent.com/statsbomb/open-data/master/data/matches/43.json"

# Pull data from url into python dictionary
games = requests.get(url=match_info_url).json()

# Use list comprehension to create a list of match ids
match_ids = [g['match_id'] for g in games]

print("There are {} games in this list".format(len(match_ids)))
data = []

# template URL at which each game's event data is stored
match_data_url = "https://raw.githubusercontent.com/statsbomb/open-data/master/data/events/{}.json"

# This takes some time, naturally
for match_id in match_ids:
    
    # Get all events from a particular game
    all_events = requests.get(url=match_data_url.format(match_id)).json()
        
    # Filter them by type-name Pass
    passes = [x for x in all_events if x['type']['name'] == "Pass"]
    
    for p in passes:
        attributes = {
            "x": p['location'][0],
            "y": p['location'][1],
            "end_x": p['pass']['end_location'][0],
            "end_y": p['pass']['end_location'][1],
            "length": p['pass']['length'],
            "angle": p['pass']['angle'],
            # Multiple other features can be collected here, such as pressure
            "outcome": 0 if "outcome" in p['pass'] else 1
        }
        
        data.append(attributes)
# How many passes are we dealing with?
print(len(data))
# Build Pandas Dataframe
df = pd.DataFrame(data=data, columns=["outcome", "x", "y", "end_x", "end_y", "length", "angle"])
df.head()<jupyter_output><empty_output><jupyter_text>Plot some Passes!<jupyter_code>for x,y,end_x,end_y,outcome in df[["x", "y", "end_x", "end_y", "outcome"]].head(200).values:
    
    plt.arrow(x, y, end_x-x, end_y-y,
              head_width=1,
              head_length=1,
              color='green' if outcome == 1 else 'red',
              alpha=0.5,
              length_includes_head=True)
    
plt.xlim(0,120)
plt.ylim(0,80)
plt.show()<jupyter_output><empty_output><jupyter_text>Baseline Accuracy<jupyter_code># What percentage of overall passes are completed?

baseline_accuracy = df['outcome'].mean()
print("{:.1f}%".format(baseline_accuracy*100))<jupyter_output>79.8%<jupyter_text>Basic Logistic Regression<jupyter_code>from sklearn.linear_model import LogisticRegression
basic_model = LogisticRegression()

features = df[['angle', 'length']]
labels = df['outcome']

basic_model.fit(features, labels)

# Make Predictions
predictions = basic_model.predict_proba(features)[:, 1]
print(predictions[:5])
# What percentage of these predictions are "correct"?
correct = 0

for i, prob in enumerate(predictions):
    pred = 1 if prob > 0.5 else 0
    
    if pred == labels[i]:
        correct += 1
        
print("{} of {} correct ({:.2f}%)".format(correct, len(labels), correct/len(labels)))<jupyter_output>50687 of 62871 correct (0.81%)<jupyter_text>Sensitivity and Specificity (Slides)  Confusion Matrix<jupyter_code>from sklearn.metrics import confusion_matrix

threshold = 0.5
confusion_matrix(labels, [1 if prob > threshold else 0 for prob in predictions])

# How to Read Confusion Matrix:

# True Negatives, False Positives
# False Negatives, True Positives<jupyter_output><empty_output><jupyter_text>Evaluating Predictions using ROCROC = Receiver Operating CharacteristicAUC = Area Under Curve<jupyter_code>from sklearn import metrics
def draw_roc(labels, predictions):
    fpr, tpr, thresholds = metrics.roc_curve(labels, predictions, pos_label=1)
    auc = metrics.roc_auc_score(labels, predictions)
    plt.plot(fpr,tpr,label="AUC: {:.3f}".format(auc))
    plt.plot([0,1], [0,1], 'r--')
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")
    plt.legend(loc=4)
    plt.show()
# Perfect ROC
draw_roc(labels, labels)
# ROC for regression predictions
draw_roc(labels, predictions)<jupyter_output><empty_output><jupyter_text>Feature Engineering<jupyter_code># Angle is provided in radians and ranges from -pi to +pi
# An angle of 0 is directly up the field

bins = np.linspace(-3.14,3.14,31)
df['angle_bin'] = pd.cut(df['angle'], bins=bins)

ax = df[['angle_bin', 'outcome']].groupby('angle_bin').mean()['outcome'].plot()
# Create new column in data frame
df['abs_angle'] = abs(df['angle'])
df.head()
# Build another logistic regression model using new features
adjusted_model = LogisticRegression()

features = df[['abs_angle', 'length']]
labels = df['outcome']
basic_model.fit(features,labels)

predictions = basic_model.predict_proba(features)[:, 1]

draw_roc(labels, predictions)<jupyter_output><empty_output><jupyter_text>Build Piecewise Model<jupyter_code>from sklearn.cluster import KMeans
# Cluster all passes into 50 different types
cluster_model = KMeans(n_clusters=50).fit(df[['x','y','end_x','end_y']])

# store cluster classification as an additional column in data frame
df['cluster'] = cluster_model.predict(df[['x','y','end_x','end_y']])
# Plot all clusters
for i, (x, y, end_x, end_y) in enumerate(cluster_model.cluster_centers_):
    plt.arrow(x, y, end_x-x, end_y-y,
              head_width=1,
              head_length=1,
              color='red',
              alpha=0.5,
              length_includes_head=True)
    
    plt.text((x+end_x)/2, (y+end_y)/2, str(i+1))
    
plt.xlim(0,120)
plt.ylim(0,80)
plt.show()
df.head()
# Build an individualized logistic regression model for each pass type
models = {}

for i, e in enumerate(cluster_model.cluster_centers_):
    passes_in_cluster = df[df['cluster'] == i]
    
    models[i] = LogisticRegression()
    
    features = passes_in_cluster[['abs_angle', 'length']]
    labels = passes_in_cluster['outcome']
    
    models[i].fit(features,labels)
def get_prediction(cluster, abs_angle, length):
    v = np.array([abs_angle, length]).reshape(1,-1)    
    return models[cluster].predict_proba(v)[:, 1]
# vectorize get prediction funtion for fast results
get_prediction = np.vectorize(get_prediction)

# create additional column in data frame to store predictions
df['predictions'] = get_prediction(df['cluster'], df['abs_angle'], df['length'])
df.head()
# Draw ROC of final piecewise model
draw_roc(df['outcome'], df['predictions'])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13395904436860068, 'fraction_numerical': 0.015358361774744027, 'mean_word_length': 4.599522292993631, 'pattern_counts': {'":': 7, '<': 36, '<?xml version=': 0, '>': 38, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '295840', 'license': 'MIT', 'n_tokens_mistral': 2411, 'n_tokens_neox': 2303, 'n_words': 579, 'path': 'Estimating Pass Difficulty.ipynb', 'repo_name': 'devinpleuler/applied-examples', 'size': '7039'}