<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Isolate Imputations
An inital approach to isolate imputations was to copy and paste from the related article on Fangraphs. This notebook serves as an analysis approach to derive the imputed values based on flagging if a certain exit velo/launch angle comprises more than a given percentage of the total dataset for a bb_type.
Step1:  Since there's a bit of variance year-to-year and especially difference in 2020 with Hawkeye, grab a month from each year
Step2:  Calculate the fraction per year a given ev/launch angle makes up, then put those together into one DF.
The topline threshold defines what fraction of total annual bb_type we use to raise a possible imputation.
Step3:  Group over years to see the cases where the threshold is passed
Step4:  Validate Results
Step5:  Output distributions look clean, so this set looks like a good start.
Also want to output top handful for each bb_type for each year. This repeats some code above - not the cleanest, but I don't want the key parts of the notebook buried under a huge number of tables.
<ASSISTANT_TASK:>
Python Code:
from pybaseball import statcast, utils
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pybaseball.plotting import plot_bb_profile
Explanation: Isolate Imputations
An inital approach to isolate imputations was to copy and paste from the related article on Fangraphs. This notebook serves as an analysis approach to derive the imputed values based on flagging if a certain exit velo/launch angle comprises more than a given percentage of the total dataset for a bb_type.
End of explanation
# Grab 1 month per year
dfs = []
for year in range(2015, 2021):
    print(f"Starting year {year}")
    dfs.append(statcast(start_dt=f'{year}-08-01', end_dt=f'{year}-09-01',verbose=False))
Explanation: Since there's a bit of variance year-to-year and especially difference in 2020 with Hawkeye, grab a month from each year
End of explanation
threshold = 0.002
summary = None
for year,df in zip(range(2015, 2021),dfs):
    for bb_type in dfs[0].bb_type.dropna().unique():
        # Isolate each bb_type
        i = df[df["bb_type"] == bb_type]
        # Sort by pairs of launch angle and speed
        i = i.groupby(["launch_angle", "launch_speed"]).size().reset_index(name="count").sort_values("count", ascending=False)
        # Derive fraction of total
        i["fraction"] = i["count"] / i["count"].sum()
        # Flagging as possibly imputed criterion
        i["flag"] = (i["fraction"] > threshold)
        i["bb_type"] = bb_type
        i["year"] = year
        flagged = i[i["flag"] == True]
        # Add to dataframe (or create on first iteration)
        if summary is not None:
            summary = summary.append(flagged[["launch_angle","launch_speed","count","bb_type","year"]])
        else:
            summary = flagged[["launch_angle","launch_speed","count","bb_type","year"]]
Explanation: Calculate the fraction per year a given ev/launch angle makes up, then put those together into one DF.
The topline threshold defines what fraction of total annual bb_type we use to raise a possible imputation.
End of explanation
# Print out the results
summary.groupby(["launch_angle", "launch_speed","bb_type"]).size().reset_index(name="Years above threshold")
Explanation: Group over years to see the cases where the threshold is passed
End of explanation
for i,year in enumerate(range(2015, 2021)):
    plot_bb_profile(dfs[i])
    plt.title(f'{year}')
    plt.xlabel("Launch Angle")
    plt.show()
for i,year in enumerate(range(2015, 2021)):
    skimmed_df = dfs[i].merge(summary.drop_duplicates(), how="left", on=["launch_angle","launch_speed","bb_type"],indicator=True)
    plot_bb_profile(skimmed_df[skimmed_df['_merge'] == 'left_only'])
    plt.title(f'{year}')
    plt.xlabel("Launch Angle")
    plt.show()
Explanation: Validate Results:
End of explanation
for year,df in zip(range(2015, 2021),dfs):
    for bb_type in dfs[0].bb_type.dropna().unique():
        # Isolate each bb_type
        i = df[df["bb_type"] == bb_type]
        # Sort by pairs of launch angle and speed
        i = i.groupby(["launch_angle", "launch_speed"]).size().reset_index(name="count").sort_values("count", ascending=False)
        # Derive fraction of total
        i["fraction"] = i["count"] / i["count"].sum()
        print(f"bb_type: {bb_type}, year: {year}")
        print(i.head(5))
Explanation: Output distributions look clean, so this set looks like a good start.
Also want to output top handful for each bb_type for each year. This repeats some code above - not the cleanest, but I don't want the key parts of the notebook buried under a huge number of tables.
End of explanation
<END_TASK>