def generic_5v5_log_graph_title(figtype, **kwargs):
    """
    Generates a figure title incorporating parameters from kwargs:

    [Fig type] for [player, or multiple players, or team]
    [date range]
    [rolling window, if applicable]
    [TOI range, if applicable]
    [TOI60 range, if applicable]

    Methods for individual graphs can take this list and arrange as necessary.

    :param figtype: str brief description, e.g. Rolling CF% or Lineup CF%
    :param kwargs: See get_and_filter_5v5_log

    :return: list of strings
    """

    titlestr = []
    line1help = ''
    if 'player' in kwargs:
        line1help = ' for {0:s}'.format(players.player_as_str(kwargs['team']))
    elif 'team' in kwargs:
        line1help = ' for {0:s}'.format(team_info.team_as_str(kwargs['team']))
    elif 'players' in kwargs:
        line1help = ' for multiple players'
    titlestr.append('{0:s}{1:s}'.format(figtype, line1help))
    titlestr.append(
        '{0:s} to {1:s}'.format(*get_startdate_enddate_from_kwargs(**kwargs)))
    if 'roll_len' in kwargs:
        titlestr.append('{0:d}-game moving window'.format(kwargs['roll_len']))
    elif 'roll_len' in kwargs:
        titlestr.append('{0:d}-day moving window'.format(
            kwargs['roll_len_days']))

    if 'min_toi' in kwargs and 'max_toi' in kwargs:
        titlestr.append('TOI range: {0:.1f}-{1:.1f} min'.format(
            kwargs['min_toi'], kwargs['max_toi']))
    elif 'min_toi' in kwargs:
        titlestr.append('TOI range: {0:.1f}+ min'.format(kwargs['min_toi']))
    elif 'min_toi' in kwargs:
        titlestr.append('TOI range: <= {0:.1f} min'.format(kwargs['max_toi']))

    if 'min_toi60' in kwargs and 'max_toi60' in kwargs:
        titlestr.append('TOI60 range: {0:.1f}-{1:.1f} min'.format(
            kwargs['min_toi60'], kwargs['max_toi60']))
    elif 'min_toi60' in kwargs:
        titlestr.append('TOI60 range: {0:.1f}+ min'.format(
            kwargs['min_toi60']))
    elif 'min_toi60' in kwargs:
        titlestr.append('TOI60 range: <= {0:.1f} min'.format(
            kwargs['max_toi60']))

    return titlestr
Example #2
0
def _get_rolling_f_title(gfcf, **kwargs):
    """
    Returns default title for this type of graph

    :param gfcf: str. Use 'G' for GF% and GF% Off and 'C' for CF% and CF% Off
    :param kwargs:

    :return: str, the title
    """

    title = 'Rolling {0:d}-game rolling {1:s}F% for {2:s}'.format(kwargs['roll_len'], gfcf,
                                                                  players.player_as_str(kwargs['player']))
    title += '\n{0:s} to {1:s}'.format(*(str(x) for x in vhelper.get_startdate_enddate_from_kwargs(**kwargs)))
    return title
Example #3
0
def _get_rolling_boxcars_title(**kwargs):
    """
    Returns suggested chart title for rolling boxcar graph given these keyword arguments

    :param kwargs:

    :return: str
    """

    title = 'Rolling {0:d}-game boxcar rates for {1:s}'.format(
        kwargs['roll_len'], players.player_as_str(kwargs['player']))
    title += '\n{0:s} to {1:s}'.format(
        *(str(x) for x in vhelper.get_startdate_enddate_from_kwargs(**kwargs)))
    return title
Example #4
0
def _add_xy_names_for_dpair_graph(df, delta_small=0.25, delta_large=0.75):
    """
    X is CF60 and Y is CA60. Pushes PlayerID1 a little to the left and PlayerID2 a little to the right in X. Also
    adds player names.

    :param df: dataframe with CF60 and CA60. This df will be wide.
    :param delta_small: amount to move by, in data coordinates, for LL and RR pairs
    :param delta_large: amount to move by, in data coordinates, for LR pairs. Need two deltas because the plot is with
        triangles and the triangles plot so that the vertex across from the short side, and not the center of the short
        side, is at the xy specified.

    :return: dataframe with X and Y and names added on, melted version of original df
    """
    df = df.assign(PairIndex=1)
    df.loc[:, 'PairIndex'] = df.PairIndex.cumsum()

    melted = helper.melt_helper(
        df[['PlayerID1', 'PlayerID2', 'CF60', 'CA60', 'TOI', 'PairIndex']],
        id_vars=['CF60', 'CA60', 'TOI', 'PairIndex'],
        var_name='P1P2',
        value_name='PlayerID')

    handedness = players.get_player_ids_file().query('Pos == "D"')[[
        'ID', 'Hand'
    ]]
    deltadf = df[['PlayerID1', 'PlayerID2', 'PairIndex']] \
        .merge(handedness.rename(columns={'ID': 'PlayerID1', 'Hand': 'Hand1'}), how='left', on='PlayerID1') \
        .merge(handedness.rename(columns={'ID': 'PlayerID2', 'Hand': 'Hand2'}), how='left', on='PlayerID2')
    deltadf.loc[((deltadf.Hand1 == 'L') & (deltadf.Hand2 == 'R')),
                'DeltaReq'] = delta_large
    deltadf.loc[:, 'DeltaReq'] = deltadf.DeltaReq.fillna(delta_small)
    deltadf = deltadf[['PairIndex', 'DeltaReq']]

    melted = melted.merge(deltadf, how='left', on='PairIndex')

    melted.loc[:, 'Name'] = melted.PlayerID.apply(
        lambda x: players.player_as_str(x))

    temp1 = melted[melted.P1P2 == 'PlayerID1']
    temp2 = melted[melted.P1P2 == 'PlayerID2']

    temp1.loc[:, 'X'] = temp1.CF60 - temp1.DeltaReq
    temp2.loc[:, 'X'] = temp2.CF60 + temp2.DeltaReq

    melted = pd.concat([temp1, temp2])
    melted.loc[:, 'Y'] = melted.CA60

    return melted
Example #5
0
def team_lineup_cf_graph(team, **kwargs):
    """
    This method builds a 4x5 matrix of rolling CF% line graphs. The left 4x3 matrix are forward lines and the top-right
    3x2 are defense pairs.

    :param team: str or id, team to build this graph for
    :param kwargs: need to specify the following as iterables of names: l1, l2, l3, l4, p1, p2, p3.
        Three players for each of the 'l's and two for each of the 'p's.

    :return: figure, or nothing
    """
    allplayers = []
    if 'l1' in kwargs and 'l2' in kwargs and 'l3' in kwargs and 'l4' in kwargs and \
                    'p1' in kwargs and 'p2' in kwargs and 'p3' in kwargs:
        # Change all to IDs
        # Go on this strange order because it'll be the order of the plots below
        for key in ['l1', 'p1', 'l2', 'p2', 'l3', 'p3', 'l4']:
            kwargs[key] = [players.player_as_id(x) for x in kwargs[key]]
            allplayers += kwargs[key]
    else:
        # TODO Find most common lines
        # Edit get_line_combos etc from manip, and the method to get player order from game_h2h, to work at team level
        pass

    # Get data
    kwargs['add_missing_games'] = True
    kwargs['team'] = team
    kwargs['players'] = allplayers
    if 'roll_len' not in kwargs:
        kwargs['roll_len'] = 25
    data = vhelper.get_and_filter_5v5_log(**kwargs)
    df = pd.concat([
        data[['Season', 'Game', 'PlayerID']],
        rolling_cfgf._calculate_f_rates(data, 'C')
    ],
                   axis=1)
    col_dict = {
        col[col.index(' ') + 1:]: col
        for col in df.columns if '%' in col
    }

    # Set up figure to share x and y
    fig, axes = plt.subplots(4, 5, sharex=True, sharey=True, figsize=[12, 8])

    # Make chart for each player
    gamenums = df[['Season', 'Game']].drop_duplicates().assign(GameNum=1)
    gamenums.loc[:, 'GameNum'] = gamenums.GameNum.cumsum()
    df = df.merge(gamenums, how='left', on=['Season', 'Game'])

    axes = axes.flatten()
    for i in range(len(allplayers)):
        ax = axes[i]
        ax.set_title(players.player_as_str(allplayers[i]), fontsize=10)
        temp = df.query('PlayerID == {0:d}'.format(int(allplayers[i])))
        x = temp.GameNum.values
        y1 = temp[col_dict['CF%']].values
        y2 = temp[col_dict['CF% Off']].values
        ax.fill_between(x, y1, y2, where=y1 > y2, alpha=0.5)
        ax.fill_between(x, y1, y2, where=y2 > y1, alpha=0.5)
        ax.plot(x, y1)
        ax.plot(x, y2, ls='--')
        ax.plot(x, [0.5 for _ in range(len(x))], color='k')

    for i, ax in enumerate(axes):
        for direction in ['right', 'top', 'bottom', 'left']:
            ax.spines[direction].set_visible(False)
        ax.xaxis.set_ticks_position('none')
        ax.yaxis.set_ticks_position('none')

    # Set title and axis labels
    axes[0].set_ylim(0.35, 0.65)
    axes[0].set_yticks([0.4, 0.5, 0.6])
    axes[0].set_yticklabels(['40%', '50%', '60%'])
    axes[0].set_xlim(1, df.GameNum.max())

    plt.annotate('Game',
                 xy=(0.5, 0.05),
                 ha='center',
                 va='top',
                 xycoords='figure fraction')

    fig.suptitle(_team_lineup_cf_graph_title(**kwargs), fontsize=16, y=0.95)

    # Return
    return vhelper.savefilehelper(**kwargs)
Example #6
0
def parallel_usage_chart(**kwargs):
    """

    :param kwargs: Defaults to take last month of games for all teams.

    :return: nothing, or figure
    """
    if 'startdate' not in kwargs and 'enddate' not in kwargs and \
                    'startseason' not in kwargs and 'endseason' not in kwargs:
        kwargs['last_n_days'] = 30

    qocqot = vhelper.get_and_filter_5v5_log(**kwargs)
    qocqot = qocqot[['PlayerID', 'TOION', 'TOIOFF',
                     'FCompSum', 'FCompN', 'DCompSum', 'DCompN',
                     'FTeamSum', 'FTeamN', 'DTeamSum', 'DTeamN']] \
        .groupby('PlayerID').sum().reset_index()
    qocqot.loc[:, 'FQoC'] = qocqot.FCompSum / qocqot.FCompN
    qocqot.loc[:, 'FQoT'] = qocqot.FTeamSum / qocqot.FTeamN
    qocqot.loc[:, 'DQoC'] = qocqot.DCompSum / qocqot.DCompN
    qocqot.loc[:, 'DQoT'] = qocqot.DTeamSum / qocqot.DTeamN
    qocqot.loc[:, 'TOI60'] = qocqot.TOION / (qocqot.TOION + qocqot.TOIOFF)
    qocqot = qocqot.dropna().sort_values('TOI60', ascending=False)  # In case I have zeroes

    qocqot.loc[:, 'PlayerName'] = qocqot.PlayerID.apply(lambda x: helpers.get_lastname(players.player_as_str(x)))
    qocqot.loc[:, 'PlayerInitials'] = qocqot.PlayerID.apply(lambda x: helpers.get_lastname(players.player_as_str(x)))
    qocqot.loc[:, 'Position'] = qocqot.PlayerID.apply(lambda x: players.get_player_position(x))
    qocqot.drop({'FCompSum', 'FCompN', 'DCompSum', 'DCompN', 'FTeamSum', 'FTeamN', 'DTeamSum', 'DTeamN',
                 'PlayerID'}, axis=1, inplace=True)

    # Reorder columns for the parallel coordinates plot
    qocqot = qocqot[['FQoT', 'FQoC', 'DQoC', 'DQoT', 'TOION', 'TOIOFF', 'TOI60', 'PlayerName', 'PlayerInitials',
                     'Position']] \
        .sort_values('TOION', ascending=False) \
        .drop({'TOION', 'TOION', 'TOIOFF', 'TOI60'}, axis=1)

    fig, axes = plt.subplots(2, 2, sharex=True, sharey=True, figsize=[11, 7])

    forwards = qocqot.query('Position != "D"')
    centers = forwards.query('Position == "C"').drop('Position', axis=1).iloc[:6, :]
    wingers = forwards.query('Position != "C"').drop('Position', axis=1).iloc[:6, :]
    forwards.drop('Position', axis=1, inplace=True)
    vhelper.parallel_coords(forwards, centers, 'PlayerInitials', 'PlayerName', axes.flatten()[0])
    vhelper.parallel_coords(forwards, wingers, 'PlayerInitials', 'PlayerName', axes.flatten()[1])

    alldefense = qocqot.query('Position == "D"').drop('Position', axis=1)
    defense = alldefense.iloc[:6, :]
    vhelper.parallel_coords(alldefense, defense, 'PlayerInitials', 'PlayerName', axes.flatten()[2])

    other_players = pd.concat([qocqot.drop('Position', axis=1), centers, wingers, defense]) \
        .drop_duplicates(keep=False).iloc[:6, :]
    vhelper.parallel_coords(pd.concat([forwards, defense]), other_players, 'PlayerInitials', 'PlayerName', axes.flatten()[3])

    fig.text(0.5, 0.04, 'Statistic (based on TOI/60)', ha='center')
    fig.text(0.04, 0.5, 'Minutes', va='center', rotation='vertical')
    axes.flatten()[0].set_title('Top centers')
    axes.flatten()[1].set_title('Top wingers')
    axes.flatten()[2].set_title('Top defense')
    axes.flatten()[3].set_title('Others')

    fig.suptitle(_parallel_usage_chart_title(**kwargs))

    return vhelper.savefilehelper(**kwargs)