nesticot commited on
Commit
7205d15
·
verified ·
1 Parent(s): a2e0e2d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -122
app.py CHANGED
@@ -1,123 +1,153 @@
1
- import polars as pl
2
- import numpy as np
3
- import joblib
4
- from shiny import App, reactive, render, ui
5
- import matplotlib.pyplot as plt
6
- import matplotlib.ticker as tkr
7
- import seaborn as sns
8
- import adjustText
9
- sns.set_style('whitegrid')
10
-
11
-
12
- import matplotlib
13
- cmap_sum = matplotlib.colors.LinearSegmentedColormap.from_list("", ['#FFFFFF','#FFB000','#FE6100'])
14
-
15
- xwoba_model = joblib.load('joblib_model/xwoba_model.joblib')
16
-
17
- x = np.arange(-30,90.5,.5)
18
- y = np.arange(0,120.5,0.1)
19
-
20
- xx, yy = np.meshgrid(x, y)
21
-
22
- df = pl.DataFrame({'launch_angle': xx.ravel(), 'launch_speed': yy.ravel()})
23
-
24
- df = df.with_columns(
25
- pl.Series('xwoba', xwoba_model.predict_proba(df) @ [0, 0.883, 1.244, 1.569, 2.004])
26
- )
27
-
28
- app_ui = ui.page_sidebar(
29
- ui.sidebar(
30
- ui.markdown("""
31
- ### How to use this app
32
-
33
- 1. Click anywhere on the plot to select a point, or manually enter coordinates
34
- 2. The selected point's coordinates will update automatically
35
- 3. The xwOBA value will be calculated based on these coordinates
36
- """),
37
- ui.hr(),
38
- ui.input_numeric("x_select", "Launch Speed (mph)", value=110),
39
- ui.input_numeric("y_select", "Launch Angle (°)", value=30),
40
-
41
-
42
- ),
43
- ui.output_plot("plot",width='900px',height='900px', click=True)
44
- )
45
-
46
-
47
- def server(input, output, session):
48
- # Store the coordinates in reactive values
49
- x_coord = reactive.value(110)
50
- y_coord = reactive.value(30)
51
-
52
- @reactive.effect
53
- @reactive.event(input.plot_click)
54
- def _():
55
- # Update reactive values when plot is clicked
56
- click_data = input.plot_click()
57
- if click_data is not None:
58
- x_coord.set(click_data["x"])
59
- y_coord.set(click_data["y"])
60
- # Update the numeric inputs
61
- ui.update_numeric("x_select", value=round(click_data["x"],1))
62
- ui.update_numeric("y_select", value=round(click_data["y"],1))
63
-
64
- @reactive.effect
65
- @reactive.event(input.x_select, input.y_select)
66
- def _():
67
- # Update reactive values when numeric inputs change
68
- x_coord.set(round(input.x_select(),1))
69
- y_coord.set(round(input.y_select(),1))
70
-
71
-
72
- @render.plot
73
- def plot():
74
- fig, ax = plt.subplots(1, 1, figsize=(10, 10))
75
-
76
- h = ax.hexbin(df['launch_speed'],
77
- df['launch_angle'],
78
- C=df['xwoba'],
79
- gridsize=(40,25),
80
- cmap=cmap_sum,
81
- vmin=0.0,
82
- vmax=2.0,)
83
- bounds=[0.0,0.4,0.8,1.2,1.6,2.0]
84
- fig.colorbar(h, ax=ax, label='xwOBA',format=tkr.FormatStrFormatter('%.3f'),shrink=0.5,
85
- ticks=bounds)
86
-
87
-
88
-
89
- ax.set_xlabel('Launch Speed')
90
- ax.set_ylabel('Launch Angle')
91
- ax.set_title('Exit Velocity vs Launch Angle\nExpected Weighted On Base Average (xwOBA)\nBy: @TJStats, Data:MLB')
92
-
93
- ax.grid(False)
94
- ax.axis('square')
95
- ax.set_xlim(0, 120)
96
- ax.set_ylim(-30, 90)
97
-
98
- x_select = input.x_select()
99
- y_select = input.y_select()
100
-
101
-
102
- sns.scatterplot(x=[x_select],y=[y_select],color='#648FFF',s=50,ax=ax,edgecolor='k',zorder=100)
103
-
104
- xwoba_value = (xwoba_model.predict_proba([[y_select,x_select]]) @ [0, 0.883, 1.244, 1.569, 2.004])[0]
105
-
106
- texts = [ax.text(x_select+3, y_select+3, f'xwOBA: {xwoba_value:.3f}', color='black', fontsize=12, weight='bold',
107
- zorder=1000, bbox=dict(facecolor='white', alpha=0.5, edgecolor='black'))]
108
-
109
-
110
-
111
- adjustText.adjust_text(texts,
112
-
113
- arrowprops=dict(arrowstyle='->', color='#DC267F'),avoid_self=True,
114
- min_arrow_len =5)
115
- # xwoba_value =
116
-
117
- ax.axhline(y=y_select, color='k', linestyle='--',linewidth=1,alpha=0.5)
118
- ax.axvline(x=x_select, color='k', linestyle='--',linewidth=1,alpha=0.5)
119
-
120
- # ax.axis('square')
121
-
122
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  app = App(app_ui, server)
 
1
+ import polars as pl
2
+ import numpy as np
3
+ import joblib
4
+ from shiny import App, reactive, render, ui
5
+ import matplotlib.pyplot as plt
6
+ import matplotlib.ticker as tkr
7
+ import seaborn as sns
8
+ import adjustText
9
+ sns.set_style('whitegrid')
10
+
11
+
12
+ import matplotlib
13
+ cmap_sum = matplotlib.colors.LinearSegmentedColormap.from_list("", ['#FFFFFF','#FFB000','#FE6100'])
14
+
15
+ xwoba_model = joblib.load('joblib_model/xwoba_model.joblib')
16
+
17
+ x = np.arange(-30,90.5,.5)
18
+ y = np.arange(0,120.5,0.1)
19
+
20
+ xx, yy = np.meshgrid(x, y)
21
+
22
+ df = pl.DataFrame({'launch_angle': xx.ravel(), 'launch_speed': yy.ravel()})
23
+
24
+ df = df.with_columns(
25
+ pl.Series('xwoba', xwoba_model.predict_proba(df) @ [0, 0.883, 1.244, 1.569, 2.004])
26
+ )
27
+
28
+ df = df.with_columns(
29
+ pl.Series('xslg', xwoba_model.predict_proba(df) @ [0, 1, 2, 3, 4])
30
+ )
31
+
32
+ app_ui = ui.page_sidebar(
33
+ ui.sidebar(
34
+ ui.markdown("""
35
+ ### How to use this app
36
+
37
+ 1. Click anywhere on the plot to select a point, or manually enter coordinates
38
+ 2. The selected point's coordinates will update automatically
39
+ 3. The xwOBA value will be calculated based on these coordinates
40
+ """),
41
+ ui.hr(),
42
+ ui.input_numeric("x_select", "Launch Speed (mph)", value=110),
43
+ ui.input_numeric("y_select", "Launch Angle (°)", value=30),
44
+ ui.input_switch("flip_stat", "xwOBA", value=False),
45
+
46
+
47
+ ),
48
+ ui.output_plot("plot",width='900px',height='900px', click=True)
49
+ )
50
+
51
+
52
+ def server(input, output, session):
53
+ # Store the coordinates in reactive values
54
+ x_coord = reactive.value(110)
55
+ y_coord = reactive.value(30)
56
+
57
+ @reactive.effect
58
+ @reactive.event(input.plot_click)
59
+ def _():
60
+ # Update reactive values when plot is clicked
61
+ click_data = input.plot_click()
62
+ if click_data is not None:
63
+ x_coord.set(click_data["x"])
64
+ y_coord.set(click_data["y"])
65
+ # Update the numeric inputs
66
+ ui.update_numeric("x_select", value=round(click_data["x"],1))
67
+ ui.update_numeric("y_select", value=round(click_data["y"],1))
68
+
69
+ @reactive.effect
70
+ @reactive.event(input.x_select, input.y_select)
71
+ def _():
72
+ # Update reactive values when numeric inputs change
73
+ x_coord.set(round(input.x_select(),1))
74
+ y_coord.set(round(input.y_select(),1))
75
+
76
+
77
+ @render.plot
78
+ def plot():
79
+ switch = input.flip_stat()
80
+ fig, ax = plt.subplots(1, 1, figsize=(9, 9))
81
+
82
+
83
+ if switch:
84
+ h = ax.hexbin(df['launch_speed'],
85
+ df['launch_angle'],
86
+ C=df['xwoba'],
87
+ gridsize=(40,25),
88
+ cmap=cmap_sum,
89
+ vmin=0.0,
90
+ vmax=2.0,)
91
+ bounds=[0.0,0.4,0.8,1.2,1.6,2.0]
92
+ fig.colorbar(h, ax=ax, label='xwOBA',format=tkr.FormatStrFormatter('%.3f'),shrink=0.5,
93
+ ticks=bounds)
94
+
95
+ if else:
96
+ h = ax.hexbin(df['launch_speed'],
97
+ df['launch_angle'],
98
+ C=df['xslg'],
99
+ gridsize=(40,25),
100
+ cmap=cmap_sum,
101
+ vmin=0.0,
102
+ vmax=2.0,)
103
+ bounds=[0.0,0.5,1,1.5,2,2.5,3,3.5,4]
104
+ fig.colorbar(h, ax=ax, label='xSLG',format=tkr.FormatStrFormatter('%.3f'),shrink=0.5,
105
+ ticks=bounds)
106
+
107
+
108
+
109
+ ax.set_xlabel('Launch Speed')
110
+ ax.set_ylabel('Launch Angle')
111
+ if switch:
112
+ ax.set_title('Exit Velocity vs Launch Angle\nExpected Weighted On Base Average (xwOBA)\nBy: @TJStats, Data:MLB')
113
+ else:
114
+ ax.set_title('Exit Velocity vs Launch Angle\nExpected Total Bases (xSLG)\nBy: @TJStats, Data:MLB')
115
+
116
+ ax.grid(False)
117
+ ax.axis('square')
118
+ ax.set_xlim(0, 120)
119
+ ax.set_ylim(-30, 90)
120
+
121
+ x_select = input.x_select()
122
+ y_select = input.y_select()
123
+
124
+
125
+ sns.scatterplot(x=[x_select],y=[y_select],color='#648FFF',s=50,ax=ax,edgecolor='k',zorder=100)
126
+
127
+
128
+ if switch:
129
+ xwoba_value = (xwoba_model.predict_proba([[y_select,x_select]]) @ [0, 0.883, 1.244, 1.569, 2.004])[0]
130
+ texts = [ax.text(x_select+3, y_select+3, f'xwOBA: {xwoba_value:.3f}', color='black', fontsize=12, weight='bold',
131
+ zorder=1000, bbox=dict(facecolor='white', alpha=0.5, edgecolor='black'))]
132
+
133
+ else switch:
134
+ xwoba_value = (xwoba_model.predict_proba([[y_select,x_select]]) @ [0, 1, 2, 3, 4])[0]
135
+ texts = [ax.text(x_select+3, y_select+3, f'xSLG: {xwoba_value:.3f}', color='black', fontsize=12, weight='bold',
136
+ zorder=1000, bbox=dict(facecolor='white', alpha=0.5, edgecolor='black'))]
137
+
138
+
139
+
140
+
141
+ adjustText.adjust_text(texts,
142
+
143
+ arrowprops=dict(arrowstyle='->', color='#DC267F'),avoid_self=True,
144
+ min_arrow_len =5)
145
+ # xwoba_value =
146
+
147
+ ax.axhline(y=y_select, color='k', linestyle='--',linewidth=1,alpha=0.5)
148
+ ax.axvline(x=x_select, color='k', linestyle='--',linewidth=1,alpha=0.5)
149
+
150
+ # ax.axis('square')
151
+
152
+
153
  app = App(app_ui, server)