nesticot commited on
Commit
b4f092a
·
verified ·
1 Parent(s): 593a9a2

Update stuff_model/calculate_arm_angles.py

Browse files
Files changed (1) hide show
  1. stuff_model/calculate_arm_angles.py +120 -71
stuff_model/calculate_arm_angles.py CHANGED
@@ -1,72 +1,121 @@
1
- import polars as pl
2
- import numpy as np
3
- import requests
4
-
5
- def calculate_arm_angles(df: pl.DataFrame,pitcher_id:int) -> pl.DataFrame:
6
- df_arm_angle = pl.read_csv('stuff_model/pitcher_arm_angles_2024.csv')
7
- #pitcher_id = 489446
8
- df_filter = df.filter(pl.col("pitcher_id") == pitcher_id).drop_nulls(subset=["release_pos_x", "release_pos_z"])
9
- # data = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}').json()
10
-
11
- if pitcher_id not in df_arm_angle["pitcher"]:
12
-
13
- data = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}').json()
14
- height_in = data['people'][0]['height']
15
- height = int(height_in.split("'")[0]) * 12 + int(height_in.split("'")[1].split('"')[0])
16
- df_filter = (df_filter.with_columns(
17
- (pl.col("release_pos_x") * 12).alias("release_pos_x"),
18
- (pl.col("release_pos_z") * 12).alias("release_pos_z"),
19
- (pl.lit(height * 0.70)).alias("shoulder_pos"),
20
- )
21
- .with_columns(
22
- (pl.col("release_pos_z") - pl.col("shoulder_pos")).alias("Opp"),
23
- pl.col("release_pos_x").abs().alias("Adj"),
24
- )
25
- .with_columns(
26
- pl.struct(["Opp", "Adj"]).map_elements(lambda x: np.arctan2(x["Opp"], x["Adj"])).alias("arm_angle_rad")
27
- ))
28
-
29
- df_filter = (df_filter.with_columns(
30
-
31
- pl.col("arm_angle_rad").degrees().alias("arm_angle")
32
-
33
- #.drop(["Opp", "arm_angle_rad"])
34
- ))
35
-
36
- else:
37
- shoulder_x = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["relative_shoulder_x"][0]
38
- shoulder_z = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["shoulder_z"][0]
39
- rel_x = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["relative_release_ball_x"][0]
40
- rel_z = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["release_ball_z"][0]
41
-
42
-
43
- ball_angle = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["ball_angle"][0]
44
-
45
- hyp = np.sqrt((rel_x - shoulder_x)**2 + (rel_z - shoulder_z)**2)
46
-
47
- print(shoulder_x, shoulder_z)
48
-
49
- df_filter = (df_filter.with_columns(
50
-
51
- )
52
- .with_columns(
53
- (pl.col("release_pos_z") - shoulder_z).alias("Opp"),
54
- (pl.lit(hyp)).alias("Hyp"),
55
- )
56
- .with_columns(
57
- pl.struct(["Opp","Hyp"]).map_elements(lambda x: np.arcsin(x["Opp"] / x["Hyp"])).alias("arm_angle_rad")
58
- )
59
- .with_columns(
60
- pl.col("arm_angle_rad").degrees().alias("arm_angle")
61
- )
62
- #.drop(["Opp", "arm_angle_rad"])
63
- )
64
-
65
- df_filter = df_filter.with_columns(
66
- ((pl.col("arm_angle") * 0.5) + (ball_angle * 0.5)).alias("arm_angle")
67
- )
68
-
69
-
70
-
71
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  return df_filter
 
1
+ import polars as pl
2
+ import numpy as np
3
+ import requests
4
+ from io import StringIO
5
+
6
+ def calculate_arm_angles(df: pl.DataFrame,pitcher_id:int) -> pl.DataFrame:
7
+
8
+ url = f"https://baseballsavant.mlb.com/leaderboard/pitcher-arm-angles?batSide=&dateStart={df['game_date'][0]}&dateEnd={df['game_date'][-1]}&gameType=R&groupBy=&min=1&minGroupPitches=1&perspective=back&pitchHand=&pitchType=&season={int(df['game_date'][0][0:4])}&size=small&sort=ascending&team=&csv=true"
9
+
10
+ headers = {
11
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
12
+ }
13
+
14
+ response = requests.get(url, headers=headers)
15
+
16
+ old_data = False
17
+ daily_check = df['game_date'][0] == df['game_date'][-1]
18
+
19
+ # Assuming response.text contains the CSV formatted string
20
+ csv_data = response.text
21
+
22
+ # Use StringIO to convert the string into a file-like object
23
+ data = StringIO(csv_data)
24
+
25
+ # Read the CSV data into a DataFrame
26
+ df_arm_angle = pl.read_csv(data)
27
+
28
+ if pitcher_id not in df_arm_angle["pitcher"]:
29
+ old_data = True
30
+ df_arm_angle = pl.read_csv('stuff_model/pitcher_arm_angles_2024.csv')
31
+
32
+
33
+ #pitcher_id = 489446
34
+ df_filter = df.filter(pl.col("pitcher_id") == pitcher_id).drop_nulls(subset=["release_pos_x", "release_pos_z"])
35
+ # data = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}').json()
36
+
37
+ if pitcher_id not in df_arm_angle["pitcher"]:
38
+
39
+ data = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}').json()
40
+ height_in = data['people'][0]['height']
41
+ height = int(height_in.split("'")[0]) * 12 + int(height_in.split("'")[1].split('"')[0])
42
+ df_filter = (df_filter.with_columns(
43
+ (pl.col("release_pos_x") * 12).alias("release_pos_x"),
44
+ (pl.col("release_pos_z") * 12).alias("release_pos_z"),
45
+ (pl.lit(height * 0.70)).alias("shoulder_pos"),
46
+ )
47
+ .with_columns(
48
+ (pl.col("release_pos_z") - pl.col("shoulder_pos")).alias("Opp"),
49
+ pl.col("release_pos_x").abs().alias("Adj"),
50
+ )
51
+ .with_columns(
52
+ pl.struct(["Opp", "Adj"]).map_elements(lambda x: np.arctan2(x["Opp"], x["Adj"])).alias("arm_angle_rad")
53
+ ))
54
+
55
+ df_filter = (df_filter.with_columns(
56
+
57
+ pl.col("arm_angle_rad").degrees().alias("arm_angle")
58
+
59
+ #.drop(["Opp", "arm_angle_rad"])
60
+ ))
61
+
62
+ else:
63
+ shoulder_x = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["relative_shoulder_x"][0]
64
+ shoulder_z = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["shoulder_z"][0]
65
+ rel_x = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["relative_release_ball_x"][0]
66
+ rel_z = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["release_ball_z"][0]
67
+
68
+
69
+ ball_angle = df_arm_angle.filter(pl.col("pitcher") == pitcher_id)["ball_angle"][0]
70
+
71
+ hyp = np.sqrt((rel_x - shoulder_x)**2 + (rel_z - shoulder_z)**2)
72
+
73
+ print(shoulder_x, shoulder_z)
74
+
75
+ df_filter = (df_filter.with_columns(
76
+
77
+ )
78
+ .with_columns(
79
+ (pl.col("release_pos_z") - shoulder_z).alias("Opp"),
80
+ (pl.lit(hyp)).alias("Hyp"),
81
+ )
82
+ .with_columns(
83
+ pl.struct(["Opp","Hyp"]).map_elements(lambda x: np.arcsin(x["Opp"] / x["Hyp"])).alias("arm_angle_rad")
84
+ )
85
+ .with_columns(
86
+ pl.col("arm_angle_rad").degrees().alias("arm_angle")
87
+ )
88
+ #.drop(["Opp", "arm_angle_rad"])
89
+ )
90
+
91
+ if old_data:
92
+ df_filter = df_filter.with_columns(
93
+ ((pl.col("arm_angle") * 0.5) + (ball_angle * 0.5)).alias("arm_angle")
94
+ )
95
+
96
+ elif daily_check:
97
+ df_filter = df_filter.with_columns(
98
+ ((pl.col("arm_angle") * 0.25) + (ball_angle * 0.75)).alias("arm_angle")
99
+ )
100
+
101
+ else:
102
+ df_filter = df_filter.with_columns(
103
+ ((pl.col("arm_angle") * 0.0) + (ball_angle * 1)).alias("arm_angle")
104
+ )
105
+
106
+ valid_mean = df_filter["arm_angle"].fill_nan(None).drop_nulls().mean()
107
+
108
+
109
+ df_filter = df_filter.with_columns(
110
+ df_filter["arm_angle"]
111
+ .fill_nan(None) # Convert NaN to null
112
+ .fill_null(valid_mean) # Fill nulls with mean
113
+ )
114
+
115
+
116
+ #print([x for x in df_filter["arm_angle"]])
117
+
118
+
119
+
120
+
121
  return df_filter