File size: 8,162 Bytes
fbbefa4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import polars as pl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from matplotlib.gridspec import GridSpec
import streamlit as st


# For help with plotting the pitch data, we will use the following dictionary to map pitch types to their corresponding colours
### PITCH COLOURS ###
pitch_colours = {
    ## Fastballs ##
    'FF': {'colour': '#FF007D', 'name': '4-Seam Fastball'},
    'FA': {'colour': '#FF007D', 'name': 'Fastball'},
    'SI': {'colour': '#98165D', 'name': 'Sinker'},
    'FC': {'colour': '#BE5FA0', 'name': 'Cutter'},

    ## Offspeed ##
    'CH': {'colour': '#F79E70', 'name': 'Changeup'},
    'FS': {'colour': '#FE6100', 'name': 'Splitter'},
    'SC': {'colour': '#F08223', 'name': 'Screwball'},
    'FO': {'colour': '#FFB000', 'name': 'Forkball'},

    ## Sliders ##
    'SL': {'colour': '#67E18D', 'name': 'Slider'},
    'ST': {'colour': '#1BB999', 'name': 'Sweeper'},
    'SV': {'colour': '#376748', 'name': 'Slurve'},

    ## Curveballs ##
    'KC': {'colour': '#311D8B', 'name': 'Knuckle Curve'},
    'CU': {'colour': '#3025CE', 'name': 'Curveball'},
    'CS': {'colour': '#274BFC', 'name': 'Slow Curve'},
    'EP': {'colour': '#648FFF', 'name': 'Eephus'},

    ## Others ##
    'KN': {'colour': '#867A08', 'name': 'Knuckleball'},
    'PO': {'colour': '#472C30', 'name': 'Pitch Out'},
    'UN': {'colour': '#9C8975', 'name': 'Unknown'},
}

# Create a dictionary mapping pitch types to their colors
dict_colour = dict(zip(pitch_colours.keys(), [pitch_colours[key]['colour'] for key in pitch_colours]))
dict_colour.update({'All': '#808080'})
# Create a dictionary mapping pitch types to their colors
dict_pitch = dict(zip(pitch_colours.keys(), [pitch_colours[key]['name'] for key in pitch_colours]))

# Create a dictionary mapping pitch types to their colors
dict_pitch_desc_type = dict(zip([pitch_colours[key]['name'] for key in pitch_colours],pitch_colours.keys()))


# Create a dictionary mapping pitch types to their colors
dict_pitch_name = dict(zip([pitch_colours[key]['name'] for key in pitch_colours], 
                           [pitch_colours[key]['colour'] for key in pitch_colours]))



required_pitch_types = ['All', 'FF', 'SI', 'FC', 'CH', 'FS','FO','SC','SL', 
                        'ST','SV' ,'CU', 'KC','KN']
# Create a mapping dictionary from the list
custom_order_dict = {pitch: index for index, pitch in enumerate(required_pitch_types)}

def tjstuff_plot(df:pl.DataFrame, 

                 pitcher_id:int,

                 position:str,

                 pitcher_name:str):
    sns.set_style("ticks")
    # Create the figure and GridSpec layout
    fig = plt.figure(figsize=(10, 8), dpi=450)
    gs = GridSpec(5, 3, height_ratios=[0.1, 10, 10, 2, 0.1], width_ratios=[1, 100, 1])
    gs.update(hspace=0.4, wspace=0.1)

    # Add subplots to the grid
    ax0 = fig.add_subplot(gs[1, 1]) 
    ax1 = fig.add_subplot(gs[2, 1])
    ax1_left = fig.add_subplot(gs[:, 0]) 
    ax1_right = fig.add_subplot(gs[:, 2]) 
    ax1_top = fig.add_subplot(gs[0, :])
    ax1_bot = fig.add_subplot(gs[4, 1])
    ax2 = fig.add_subplot(gs[3, 1])

    # Update color dictionary
    

 
    df = df.to_pandas()
    # Filter data for the specific pitcher
    pitcher_df = df[(df['pitcher_id'] == pitcher_id) &
                                    (df['pitches'] >= 10)]
    

    
    # Add a new column for the custom order
    pitcher_df['order'] = pitcher_df['pitch_type'].map(custom_order_dict)
    pitcher_df = pitcher_df.sort_values('order')
                         
    # Get unique pitch types for the pitcher
    pitcher_pitches = pitcher_df['pitch_type'].unique()
    pitcher_pitches = [x for x in required_pitch_types if x in pitcher_pitches]


                     
    # Plot tjStuff+ with swarmplot for all players in the same position
    sns.swarmplot(data=df[(df['pitches'] >= 10) &
                                            (df['position'] == position)].dropna(subset=['pitch_type']),
                x='pitch_type',
                y='tj_stuff_plus',
                palette=dict_colour,
                alpha=0.3,
                size=3,
                ax=ax0,
                order=pitcher_pitches)

    # Overlay swarmplot for the specific pitcher
    sns.swarmplot(data=df[(df['pitcher_id'] == pitcher_id) &
                                            (df['pitches'] >= 10)],
                x='pitch_type',
                y='tj_stuff_plus',
                palette=dict_colour,
                alpha=1,
                size=16,
                ax=ax0,
                order=pitcher_pitches,
                edgecolor='black',
                linewidth=1)

    # Annotate the median values on the plot
    for index, row in pitcher_df.reset_index(drop=True).iterrows():
        ax0.text(index, 
                row['tj_stuff_plus'], 
                f'{row["tj_stuff_plus"]:.0f}', 
                color='white', 
                ha="center", 
                va="center",
                fontsize=8,
                weight='bold',
                clip_on=False)

    # Customize ax0
    ax0.set_xlabel('')
    ax0.set_ylabel('tjStuff+')
    ax0.grid(False)
    ax0.set_ylim(70, 130)
    ax0.axhline(y=100, color='black', linestyle='--', alpha=0.2, zorder=0)

    # Plot pitch grade with swarmplot for all players in the same position
    sns.swarmplot(data=df[(df['pitches'] >= 10) &
                                            (df['position'] == position)].dropna(subset=['pitch_type']),
                x='pitch_type',
                y='pitch_grade',
                palette=dict_colour,
                alpha=0.3,
                size=3,
                ax=ax1,
                clip_on=False,
                order=pitcher_pitches)

    # Overlay swarmplot for the specific pitcher
    sns.swarmplot(data=df[(df['pitcher_id'] == pitcher_id) &
                                            (df['pitches'] >= 10)],
                x='pitch_type',
                y='pitch_grade',
                palette=dict_colour,
                alpha=1,
                size=16,
                ax=ax1,
                order=pitcher_pitches,
                edgecolor='black',
                clip_on=False,
                linewidth=1)

    # Annotate the median values on the plot
    for index, row in pitcher_df.reset_index(drop=True).iterrows():
        ax1.text(index, 
                row['pitch_grade'], 
                f'{row["pitch_grade"]:.0f}', 
                color='white', 
                ha="center", 
                va="center",
                fontsize=8,
                weight='bold',
                clip_on=False,
                zorder=1000)
        
    # Customize ax1
    ax1.set_xlabel('Pitch Type')
    ax1.set_ylabel('Pitch Grade')
    ax1.grid(False)
    ax1.set_ylim(20, 80)
    ax1.axhline(y=50, color='black', linestyle='--', alpha=0.2, zorder=0)

    # Hide axes for additional subplots
    ax2.axis('off')
    ax1_left.axis('off')
    ax1_right.axis('off')
    ax1_top.axis('off')
    ax1_bot.axis('off')

    # Add text annotations
    ax1_bot.text(s='By: @TJStats', x=0, y=1, fontsize=12, ha='left')
    ax1_bot.text(s='Data: MLB', x=1, y=1, fontsize=12, ha='right')

    ax1_top.text(0.5, 0, f'{pitcher_name} tjStuff+ 2024 Season - {position}',
                fontsize=24, ha='center', va='top')

    ax2.text(x=0.5, y=0.6, s='tjStuff+ calculates the Expected Run Value (xRV) of a pitch regardless of type\n'
                            'tjStuff+ is normally distributed, where 100 is the mean and Standard Deviation is 10\n'
                            'Pitch Grade is based off tjStuff+ and scales the data to the traditional 20-80 Scouting Scale for a given pitch type',
                            
            ha='center', va='top', fontname='Calibri', fontsize=10)

    # Adjust subplot layout
    fig.subplots_adjust(left=0.03, right=0.97, top=0.97, bottom=0.03)
    # fig.set_facecolor('#e0e0e0')
    st.pyplot(fig)