import pandas as pd
import numpy as np
import seaborn as sns
pd.options.display.max_columns = 999

fields = pd.read_csv('../data/fields.csv', index_col=0)
data2016 = pd.read_csv('../data/all2016.csv', names=fields.Header)

"""
Explanation: Run Expectancy
Ported from Analyzing Baseball Data with R, by Marchi, Albert and Baumer, Chapter 5
End of explanation
"""


data2016['RUNS'] = data2016.AWAY_SCORE_CT + data2016.HOME_SCORE_CT
data2016['HALF_INNING'] = (data2016.GAME_ID 
                            + data2016.INN_CT.astype(str) 
                            + data2016.BAT_HOME_ID.astype(str))
data2016['RUNS_SCORED'] = ((data2016.BAT_DEST_ID > 3).astype(int) 
                            + (data2016.RUN1_DEST_ID > 3).astype(int)
                            + (data2016.RUN2_DEST_ID > 3).astype(int)
                            + (data2016.RUN3_DEST_ID > 3).astype(int))

half_innings = data2016.groupby('HALF_INNING').agg(outs_inning=('EVENT_OUTS_CT',sum),
                                                    runs_inning=('RUNS_SCORED',sum),
                                                    runs_start=('RUNS',min))
half_innings['MAX_RUNS'] = half_innings.runs_inning + half_innings.runs_start

"""
Explanation: Runs scored in the remainder of the inning
End of explanation
"""


data2016 = data2016.join(half_innings, on='HALF_INNING', how='inner')
data2016['RUNS_ROI'] = data2016.MAX_RUNS - data2016.RUNS

assert not data2016.loc[data2016.MAX_RUNS < data2016.RUNS, ['HALF_INNING', 'RUNS_SCORED', 'RUNS', 'MAX_RUNS']].values

"""
Explanation: The runs from the event to the end of the inning is the difference between the total runs scored in the inning and the runs scored up until the event.
End of explanation
"""


data2016['STATE'] = ((~data2016.BASE1_RUN_ID.isnull()).astype(int).astype(str)
                    + (~data2016.BASE2_RUN_ID.isnull()).astype(int).astype(str)
                    + (~data2016.BASE3_RUN_ID.isnull()).astype(int).astype(str)
                    + data2016.OUTS_CT.astype(str))

data2016['STATE'].unique()

"""
Explanation: Run expectancy matrix
Create string variable for base occupancy state.
End of explanation
"""


data2016['NRUNNER1'] = ((data2016.RUN1_DEST_ID==1) | (data2016.BAT_DEST_ID==1)).astype(int)
data2016['NRUNNER2'] = ((data2016.RUN1_DEST_ID==2) | (data2016.RUN2_DEST_ID==2) 
                            | (data2016.BAT_DEST_ID==2)).astype(int)
data2016['NRUNNER3'] = ((data2016.RUN1_DEST_ID==3) | (data2016.RUN2_DEST_ID==3) 
                            | (data2016.RUN3_DEST_ID==3) | (data2016.BAT_DEST_ID==3)).astype(int)
data2016['NOUTS'] = data2016.OUTS_CT + data2016.EVENT_OUTS_CT
data2016['NEW_BASES'] = data2016[['NRUNNER1', 'NRUNNER2', 'NRUNNER3']].astype(int).astype(str).agg(''.join, axis=1)
data2016['NEW_STATE'] = data2016[['NEW_BASES', 'NOUTS']].astype(str).agg(''.join, axis=1)

data2016[['STATE', 'NEW_STATE']].head()

"""
Explanation: Create indicators for base occupancy following the play.
End of explanation
"""


data2016 = data2016.query('(STATE != NEW_STATE) | (RUNS_SCORED > 0)')

"""
Explanation: Condition on change between state and new state, or runs scored.
End of explanation
"""


data2016_complete = data2016.query('outs_inning==3')

"""
Explanation: Remove incomplete innings.
End of explanation
"""


expected_runs = data2016_complete.groupby('STATE').agg(mean_runs=('RUNS_ROI', 'mean')).reset_index()
expected_runs['base_state'] = expected_runs.STATE.str[:3]
expected_runs['outs'] = expected_runs.STATE.str[-1]

expected_runs.pivot(index='base_state', columns='outs', values='mean_runs').round(2)

"""
Explanation: Calculate run expectancy for base/outs states.
End of explanation
"""


expected_runs.head()

expected_runs.index

data2016.index

data2016 = (pd.merge(data2016, expected_runs, how='left', on='STATE')
    .rename({'mean_runs':'runs_state'}, axis=1)
    .merge(expected_runs, how='left', left_on='NEW_STATE', right_on='STATE')
    .rename({'mean_runs':'runs_new_state',
            'STATE_x':'STATE'}, axis=1)
    .fillna(value={'runs_new_state': 0})
    .drop('STATE_y', axis=1)
)

data2016['run_value'] = data2016.runs_new_state - data2016.runs_state + data2016.RUNS_SCORED

retroids = pd.read_csv('../data/retrosheetIDs.csv')
retroids.columns

data2016['BASES'] = data2016.STATE.str[:-1]

"""
Explanation: Estimating the success of a batting play
End of explanation
"""


altuve = (data2016.merge(retroids, left_on='BAT_ID', right_on=' ID')
            .query('(LAST=="Altuve") & (BAT_EVENT_FL=="T")'))
altuve[['STATE', 'NEW_STATE', 'run_value']]

ax = sns.stripplot(data=altuve, x="BASES", y="run_value", hue="OUTS_CT")
ax.set(ylabel="")

altuve.run_value

runs_altuve = altuve.groupby('BASES').agg(RUNS=('run_value', sum), PA=('run_value', 'count'))
runs_altuve

data2016b = data2016.query('BAT_EVENT_FL=="T"')

runs = data2016b.groupby('BAT_ID').agg(RE24=('run_value', sum),
                                PA=('run_value', 'count'),
                                RUNS_START=('runs_state', sum))

runs_400 = runs.query('PA>=400')

sns.regplot(x='RUNS_START', y='RE24', data=runs_400, lowess=True);

"""
Explanation: Example: Jose Altuve
End of explanation
"""


home_runs = data2016.query('EVENT_CD==23')

(home_runs.groupby('STATE').size().sort_values(ascending=False) / home_runs.shape[0]).round(3)

"""
Explanation: Run values of different hits
End of explanation
"""


home_runs.run_value.mean()

ax = sns.distplot(home_runs.run_value, kde=False)
ax.vlines(home_runs.run_value.mean(), ymin=0, ymax=ax.get_ylim()[1], color='r');

singles = data2016.query('EVENT_CD==20')

ax = sns.distplot(singles.run_value, kde=False)
ax.vlines(singles.run_value.mean(), ymin=0, ymax=ax.get_ylim()[1], color='r');

"""
Explanation: Run value of a home run
End of explanation
"""


stealing = data2016[data2016.EVENT_CD.isin([4, 6])]
stealing.EVENT_CD.value_counts()

stealing.groupby('STATE').size()

sns.displot(stealing, x='run_value', col='EVENT_CD');

stealing_1001 = stealing.query('STATE=="1001"').groupby('EVENT_CD').size()
stealing_1001 / stealing_1001.sum()

"""
Explanation: Value of base stealing
End of explanation
"""


stealing.query('STATE=="1001"').groupby('NEW_STATE').size()

stealing.query('STATE=="1001"').run_value.mean()

"""
Explanation: Sometimes the runner ends up on third.
End of explanation
"""


