<jupyter_start><jupyter_text>Lambda School Data Science*Unit 2, Sprint 3, Module 1*--- Define ML problemsYou will use your portfolio project dataset for all assignments this sprint. AssignmentComplete these tasks for your project, and document your decisions.- [ ] Choose your target. Which column in your tabular dataset will you predict?- [ ] Is your problem regression or classification?- [ ] How is your target distributed?    - Classification: How many classes? Are the classes imbalanced?    - Regression: Is the target right-skewed? If so, you may want to log transform the target.- [ ] Choose which observations you will use to train, validate, and test your model.    - Are some observations outliers? Will you exclude them?    - Will you do a random split or a time-based split?- [ ] Choose your evaluation metric(s).    - Classification: Is your majority class frequency > 50% and < 70% ? If so, you can just use accuracy if you want. Outside that range, accuracy could be misleading. What evaluation metric will you choose, in addition to or instead of accuracy?- [ ] Begin to clean and explore your data.- [ ] Begin to choose which features, if any, to exclude. Would some features "leak" future information?<jupyter_code>import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

pd.options.display.max_columns = 999

dat_df = pd.read_csv("../data/ufc/data.csv")
prep_dat_df = pd.read_csv("../data/ufc/preprocessed_data.csv")
raw_fighters_df = pd.read_csv("../data/ufc/raw_fighter_details.csv")
raw_dat_df = pd.read_csv("../data/ufc/raw_total_fight_data.csv", sep=";")

dat_df.head()
prep_dat_df.head()
raw_fighters_df.tail()
raw_dat_df.head()
# I'd like to have one row per fighter per fight. That is, 2 rows per fight, with a simple "is_winner" column.

r_fighter_cols = [col for col in dat_df.columns if col.startswith("R_")]
b_fighter_cols = [col for col in dat_df.columns if col.startswith("B_")]

other_cols = [col for col in dat_df.columns if col not in r_fighter_cols and col not in b_fighter_cols]
import re

def extractFighters(dat_df, fighter_cols, opp_cols, fighter_prefix, opp_prefix, fighter_color):

    fighters_df = dat_df[fighter_cols + other_cols].copy()
    fighters_df.columns = [col.replace(fighter_prefix, "") for col in fighters_df.columns]
    fighters_df["is_winner"] = fighters_df["Winner"] == fighter_color

    opp_df = dat_df[opp_cols].copy()
    opp_df.columns = [re.sub(r"^" + opp_prefix, "opponent_", col) for col in opp_df.columns]

    fighters_df = pd.concat([fighters_df, opp_df], axis=1)
    
    return fighters_df

r_fighters_df = extractFighters(dat_df, r_fighter_cols, b_fighter_cols, "R_", "B_", "Red")
b_fighters_df = extractFighters(dat_df, b_fighter_cols, r_fighter_cols, "B_", "R_", "Blue")
fighters_df = pd.concat([r_fighters_df, b_fighters_df], axis=0)
fighters_df.head()
for col in [col for col in fighters_df.select_dtypes(include="number").columns if col.startswith("opponent_")]:
    
    col2 = col
    col1 = col[9:]
    
    fighters_df["ratio_" + col1] = (fighters_df[col1] + 1) / (fighters_df[col2] + 1)

fighters_df["stance_config"] = fighters_df["Stance"] + "-" + fighters_df["opponent_Stance"]
# prevent leakage
fighters_df = fighters_df.drop(columns="Winner")
# clean up raw_fighters_df

inches_re = re.compile(r"\s*((?P<feet>\d+)')?\s*((?P<inches>\d+)\")\s*")

# tests
t1 = inches_re.match("6' 5\"")
print(t1.group("feet"), t1.group("inches"))

t2 = inches_re.match("58\"")
print(t2.group("feet"), t2.group("inches"))

def toInches(s):
    
    if s == np.NaN:
        return np.NaN
    
    m = inches_re.match(str(s))
    if not m:
        return np.NaN
    
    res = int(m.group("inches"))
    if m.group("feet") is not None:
        res += int(m.group("feet"))*12
        
    return res

raw_fighters_df["Height"] = raw_fighters_df["Height"].apply(toInches)
raw_fighters_df["Reach"] = raw_fighters_df["Reach"].apply(toInches)

raw_fighters_df.head()
raw_fighters_df.tail()
raw_fighters_df["DOB"] = pd.to_datetime(raw_fighters_df["DOB"])

lbs_re = re.compile(r"\s*(?P<lbs>\d+)\s*lbs\.")

def toLbs(s):
    
    if s == np.NaN:
        return np.NaN
    
    m = lbs_re.match(str(s))
    if not m:
        return np.NaN
    
    return int(m.group("lbs"))

raw_fighters_df["Weight"] = raw_fighters_df["Weight"].apply(toLbs)

raw_fighters_df.tail()
import category_encoders as ce
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import RandomForestRegressor

from sklearn.pipeline import make_pipeline

from sklearn.metrics import accuracy_score

pipeline = make_pipeline(
    ce.OneHotEncoder(),
    SimpleImputer(strategy="most_frequent"),
    StandardScaler(),
    RandomForestRegressor(n_jobs=-1, n_estimators=120)
)
from sklearn.model_selection import train_test_split

train, test = train_test_split(fighters_df)

target = "is_winner"
features = fighters_df.columns.drop([target] + ["age", "ratio_age", "opponent_age"])

X_train = train[features]
y_train = train[target].astype(np.int64)

X_test = test[features]
y_test = test[target].astype(np.int64)

pipeline.fit(X_train, y_train)
pipeline.score(X_test, y_test)
from sklearn.metrics import mean_absolute_error

y_pred = pipeline.predict(X_test)
mean_absolute_error(y_pred, y_test)
y_pred
import seaborn as sns
# How is your target distributed?
# total win-loss ratio is expected is to be 50-50 since each 

sns.distplot(fighters_df[target].astype(np.int64))
# Choose which observations you will use to train, validate, and test your model.
# I will use a random split. I don't want to split based on dates becaue UFC stats have changed over time.
# Choose your evaluation metric(s).
# I believe it would only make sense to do this based on accuracy.
# since we're essentially choosing one class out of two for each trial
fighters_df.head(5)
y_pred = pipeline.predict(X_test)

from sklearn.metrics import confusion_matrix

confusion_matrix(y_test.astype(np.int64), y_pred.astype(np.int64))
fighter_names = list(set(fighters_df["fighter"]))
fighter_cols = [col for col in fighters_df.select_dtypes(include="number").columns if not col.startswith("opponent_") and not col.startswith("ratio_")]
fighter_cols
model = pipeline.named_steps["randomforestregressor"]
encoder = pipeline.named_steps["onehotencoder"]

encoded_cols = encoder.transform(fighters_df[features]).columns
importances = pd.Series(model.feature_importances_, encoded_cols)
fig, ax = plt.subplots(figsize=(10, 40))
importances.sort_values()[len(importances)-30:].plot.barh(color="gray", ax=ax)

fig.show()
# this is to test the jupyterlab-dash extension

from plotly.offline import plot, iplot, init_notebook_mode
from plotly import graph_objs as go

init_notebook_mode(connected=True)

iplot({
    "data": [go.Scatter(x=np.random.randint(0, 100, 100), y=np.random.randint(0, 100, 100), mode="markers")],
    "layout": go.Layout(title="test chart")
})
not False

y_pred = pipeline.predict(fighters_df[features])
# this is going to be what we discussed about 2 rows per fight vs 1
y_pred
len(y_pred)
bool_pred = []
bool_pred2 = []

half = len(y_pred)//2
for ix in range(half):
    
    mean_pred = (y_pred[ix] + (1 - y_pred[half + ix]))/2
    pred = bool(round(mean_pred))
    
    bool_pred.append(pred)
    bool_pred2.append(not pred)

bool_pred += bool_pred2

accuracy_score(bool_pred, fighters_df[target])
fighters_df.columns = [col[9:] + "_opponent" if col.startswith("opponent_") else col for col in fighters_df.columns]
fighters_df.columns = [col[6:] + "_ratio" if col.startswith("ratio_") else col for col in fighters_df.columns]
fighters_df.to_csv("../data/ufc/fighters.csv")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12183789364997419, 'fraction_numerical': 0.010583376355188436, 'mean_word_length': 4.928844682478959, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '613358', 'license': 'MIT', 'n_tokens_mistral': 2793, 'n_tokens_neox': 2666, 'n_words': 701, 'path': 'module1/LS_DS_231_assignment.ipynb', 'repo_name': 'ekoly/DS-Unit-2-Applied-Modeling', 'size': '7748'}