Initial commit
This commit is contained in:
parent
80a9282cb3
commit
17ef692d18
32
README.txt
Normal file
32
README.txt
Normal file
@ -0,0 +1,32 @@
|
||||
#######
|
||||
DATA REQUIREMENTS
|
||||
#######
|
||||
|
||||
- Add any new players to players.csv for import
|
||||
- Create directory in /data-input in format `XXXX Season Cardset`
|
||||
- Upload the following csv files:
|
||||
- baserunning-data.csv
|
||||
- https://www.baseball-reference.com/leagues/majors/2022-baserunning-batting.shtml
|
||||
- batter-stats.csv
|
||||
- https://www.fangraphs.com/leaders/splits-leaderboards
|
||||
- defense-X.csv (each position)
|
||||
- https://www.baseball-reference.com/leagues/majors/2022-specialpos_p-fielding.shtml
|
||||
- replace the `p` in `p-fielding` with 1b/2b/lf
|
||||
- defense-of.csv (don't forget combined OF)
|
||||
- https://www.baseball-reference.com/leagues/majors/2022-specialpos_p-fielding.shtml
|
||||
- replace the `p` in `p-fielding` with of
|
||||
- pitcher-data.csv
|
||||
- https://www.baseball-reference.com/leagues/majors/2022-standard-pitching.shtml
|
||||
- pitcher-stats.csv
|
||||
- https://www.fangraphs.com/leaders/splits-leaderboards
|
||||
|
||||
#######
|
||||
CARD CREATION PROCESS
|
||||
#######
|
||||
|
||||
1) Import new players for sba_id with `1. Import Players`
|
||||
2) Confirm cardset exists; if not, create now
|
||||
3) Create cards with `3. Card Creation`
|
||||
4) Generate csv output with `4. Card Output`
|
||||
5) Upload output files into Sheets for Component Studio import
|
||||
6) Upload ratings output files into Sheets for PD Ratings Guide
|
||||
255
calcs_batter.py
Normal file
255
calcs_batter.py
Normal file
@ -0,0 +1,255 @@
|
||||
from creation_helpers import mround
|
||||
|
||||
|
||||
def total_chances(chance_data):
|
||||
sum_chances = 0
|
||||
for key in chance_data:
|
||||
if key not in ['id', 'player_id', 'cardset_id', 'vs_hand', 'is_prep']:
|
||||
sum_chances += chance_data[key]
|
||||
|
||||
return mround(sum_chances)
|
||||
|
||||
|
||||
def bp_singles(all_singles):
|
||||
if all_singles < 6:
|
||||
return 0
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def wh_singles(rem_singles, hard_rate):
|
||||
if rem_singles == 0 or hard_rate < .2:
|
||||
return 0
|
||||
elif hard_rate > .4:
|
||||
return mround(rem_singles * .666)
|
||||
else:
|
||||
return mround(rem_singles * .333)
|
||||
|
||||
|
||||
def one_singles(rem_singles, ifh_rate, force_rem):
|
||||
if force_rem:
|
||||
return mround(rem_singles)
|
||||
elif rem_singles == 0 or ifh_rate < .05:
|
||||
return 0
|
||||
else:
|
||||
return mround(rem_singles * ifh_rate * 3)
|
||||
|
||||
|
||||
def all_homeruns(rem_hits, all_hits, hrs, hits, singles):
|
||||
if rem_hits == 0 or all_hits == 0 or hrs == 0 or hits - singles == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround(min(rem_hits, all_hits * ((hrs * 1.15) / hits)))
|
||||
|
||||
|
||||
def nd_homeruns(all_hr, hr_rate):
|
||||
if all_hr == 0 or hr_rate == 0:
|
||||
return 0
|
||||
elif hr_rate > .2:
|
||||
return mround(all_hr * .6)
|
||||
else:
|
||||
return mround(all_hr * .25)
|
||||
|
||||
|
||||
def triples(all_xbh, tr_count, do_count):
|
||||
if all_xbh == 0 or tr_count == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround(all_xbh * (tr_count / (tr_count + do_count)))
|
||||
|
||||
|
||||
def two_doubles(all_doubles, soft_rate):
|
||||
if all_doubles == 0 or soft_rate == 0:
|
||||
return 0
|
||||
elif soft_rate > .2:
|
||||
return mround(all_doubles / 2)
|
||||
else:
|
||||
return mround(all_doubles / 4)
|
||||
|
||||
|
||||
def hit_by_pitch(other_ob, hbps, walks):
|
||||
if hbps == 0 or other_ob * (hbps / (hbps + walks)) < 1:
|
||||
return 0
|
||||
else:
|
||||
return mround(other_ob * (hbps / (hbps + walks)), base=1.0)
|
||||
|
||||
|
||||
def strikeouts(all_outs, k_rate):
|
||||
if all_outs == 0 or k_rate == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround(all_outs * k_rate)
|
||||
|
||||
|
||||
def flyout_a(all_flyouts, hard_rate):
|
||||
if all_flyouts == 0 or hard_rate < .4:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def flyout_bq(rem_flyouts, soft_rate):
|
||||
if rem_flyouts == 0 or soft_rate < .1:
|
||||
return 0
|
||||
else:
|
||||
return mround(rem_flyouts * soft_rate * 3)
|
||||
|
||||
|
||||
def flyout_b(rem_flyouts, pull_rate, cent_rate):
|
||||
if rem_flyouts == 0 or pull_rate == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround(rem_flyouts * (pull_rate + cent_rate / 2))
|
||||
|
||||
|
||||
def popouts(rem_outs, iffb_rate):
|
||||
if rem_outs == 0 or iffb_rate * rem_outs < 1:
|
||||
return 0
|
||||
else:
|
||||
return mround(rem_outs * iffb_rate)
|
||||
|
||||
|
||||
def groundball_a(all_groundouts, gidps, abs):
|
||||
if all_groundouts == 0 or gidps == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround((min(gidps ** 2.5, abs) / abs) * all_groundouts)
|
||||
|
||||
|
||||
def groundball_c(rem_groundouts, med_rate):
|
||||
if rem_groundouts == 0 or med_rate < .4:
|
||||
return 0
|
||||
elif med_rate > .6:
|
||||
return mround(rem_groundouts)
|
||||
else:
|
||||
return mround(rem_groundouts * med_rate)
|
||||
|
||||
|
||||
def stealing(chances: int, sb2s: int, cs2s: int, sb3s: int, cs3s: int, season_pct: float):
|
||||
if chances == 0 or sb2s + cs2s == 0:
|
||||
return 0, 0, False, 0
|
||||
|
||||
total_attempts = sb2s + cs2s + sb3s + cs3s
|
||||
attempt_pct = total_attempts / chances
|
||||
|
||||
if attempt_pct >= .08:
|
||||
st_auto = True
|
||||
else:
|
||||
st_auto = False
|
||||
|
||||
# chance_odds = [x / 36 for x in range(1, 36)]
|
||||
st_jump = 0
|
||||
for x in range(1, 37):
|
||||
if attempt_pct * 1.5 <= x / 36:
|
||||
st_jump = x / 36
|
||||
break
|
||||
|
||||
st_high = mround(20 * (sb2s / (sb2s + cs2s + cs2s)))
|
||||
if sb3s + cs3s < (3 * season_pct):
|
||||
st_low = 3
|
||||
else:
|
||||
st_low = mround(16 * ((sb2s + sb3s) / (sb2s + sb3s + cs2s * 2 + cs3s * 2)))
|
||||
|
||||
if st_low >= st_high:
|
||||
if st_high == 0:
|
||||
st_low = 0
|
||||
elif st_high <= 3:
|
||||
st_low = 1
|
||||
else:
|
||||
st_low = st_high - 3
|
||||
|
||||
if ((st_high - 7) > st_low) and st_high > 7:
|
||||
st_low = st_high - 7
|
||||
|
||||
return round(st_low), round(st_high), st_auto, st_jump
|
||||
|
||||
|
||||
def stealing_line(steal_data: dict):
|
||||
sd = steal_data
|
||||
jump_chances = round(sd[3] * 36)
|
||||
|
||||
if jump_chances == 0:
|
||||
good_jump = '-'
|
||||
elif jump_chances <= 6:
|
||||
if jump_chances == 6:
|
||||
good_jump = 7
|
||||
elif jump_chances == 5:
|
||||
good_jump = 6
|
||||
elif jump_chances == 4:
|
||||
good_jump = 5
|
||||
elif jump_chances == 3:
|
||||
good_jump = 4
|
||||
elif jump_chances == 2:
|
||||
good_jump = 3
|
||||
elif jump_chances == 1:
|
||||
good_jump = 2
|
||||
elif jump_chances == 7:
|
||||
good_jump = '4,5'
|
||||
elif jump_chances == 8:
|
||||
good_jump = '4,6'
|
||||
elif jump_chances == 9:
|
||||
good_jump = '3-5'
|
||||
elif jump_chances == 10:
|
||||
good_jump = '2-5'
|
||||
elif jump_chances == 11:
|
||||
good_jump = '6,7'
|
||||
elif jump_chances == 12:
|
||||
good_jump = '4-6'
|
||||
elif jump_chances == 13:
|
||||
good_jump = '2,4-6'
|
||||
elif jump_chances == 14:
|
||||
good_jump = '3-6'
|
||||
elif jump_chances == 15:
|
||||
good_jump = '2-6'
|
||||
elif jump_chances == 16:
|
||||
good_jump = '2,5-6'
|
||||
elif jump_chances == 17:
|
||||
good_jump = '3,5-6'
|
||||
elif jump_chances == 18:
|
||||
good_jump = '4-6'
|
||||
elif jump_chances == 19:
|
||||
good_jump = '2,4-7'
|
||||
elif jump_chances == 20:
|
||||
good_jump = '3-7'
|
||||
elif jump_chances == 21:
|
||||
good_jump = '2-7'
|
||||
elif jump_chances == 22:
|
||||
good_jump = '2-7,12'
|
||||
elif jump_chances == 23:
|
||||
good_jump = '2-7,11'
|
||||
elif jump_chances == 24:
|
||||
good_jump = '2,4-8'
|
||||
elif jump_chances == 25:
|
||||
good_jump = '3-8'
|
||||
elif jump_chances == 26:
|
||||
good_jump = '2-8'
|
||||
elif jump_chances == 27:
|
||||
good_jump = '2-8,12'
|
||||
elif jump_chances == 28:
|
||||
good_jump = '2-8,11'
|
||||
elif jump_chances == 29:
|
||||
good_jump = '3-9'
|
||||
elif jump_chances == 30:
|
||||
good_jump = '2-9'
|
||||
elif jump_chances == 31:
|
||||
good_jump = '2-9,12'
|
||||
elif jump_chances == 32:
|
||||
good_jump = '2-9,11'
|
||||
elif jump_chances == 33:
|
||||
good_jump = '2-10'
|
||||
elif jump_chances == 34:
|
||||
good_jump = '3-11'
|
||||
elif jump_chances == 35:
|
||||
good_jump = '2-11'
|
||||
else:
|
||||
good_jump = '2-12'
|
||||
|
||||
return f'{"*" if sd[2] else ""}{good_jump}/- ({sd[1] if sd[1] else "-"}-{sd[0] if sd[0] else "-"})'
|
||||
|
||||
|
||||
def running(extra_base_pct: str):
|
||||
if extra_base_pct == '':
|
||||
return 8
|
||||
xb_pct = float(extra_base_pct.strip("%")) / 100
|
||||
|
||||
return round(8 + (10 * xb_pct))
|
||||
330
calcs_defense.py
Normal file
330
calcs_defense.py
Normal file
@ -0,0 +1,330 @@
|
||||
import math
|
||||
|
||||
|
||||
def range_pitcher(rs_value: int, season_pct: float):
|
||||
if rs_value >= (3 * season_pct):
|
||||
return 1
|
||||
elif rs_value >= (0 * season_pct):
|
||||
return 2
|
||||
elif rs_value >= (-1 * season_pct):
|
||||
return 3
|
||||
elif rs_value >= (-100 * season_pct):
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_catcher(rs_value: int, season_pct: float):
|
||||
if rs_value >= 7 * season_pct:
|
||||
return 1
|
||||
elif rs_value >= 3 * season_pct:
|
||||
return 2
|
||||
elif rs_value >= -1 * season_pct:
|
||||
return 3
|
||||
elif rs_value >= -5 * season_pct:
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_first_base(r_range: int, r_dp: int, season_pct: float):
|
||||
if (r_range + r_dp) >= (4 * season_pct):
|
||||
return 1
|
||||
elif (r_range + r_dp) >= (1 * season_pct):
|
||||
return 2
|
||||
elif (r_range + r_dp) >= (0 * season_pct):
|
||||
return 3
|
||||
elif (r_range + r_dp) >= (-2 * season_pct):
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_second_base(r_range: int, r_dp: int, season_pct: float):
|
||||
# print(f'r_range ({r_range}) + r_dp ({r_dp}) = {r_range + r_dp}')
|
||||
if (r_range + r_dp) >= (5 * season_pct):
|
||||
return 1
|
||||
elif (r_range + r_dp) >= (2 * season_pct):
|
||||
return 2
|
||||
elif (r_range + r_dp) >= (0 * season_pct):
|
||||
return 3
|
||||
elif (r_range + r_dp) >= (-2 * season_pct):
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_third_base(r_range: int, r_dp: int, season_pct: float):
|
||||
if (r_range + r_dp) >= (5 * season_pct):
|
||||
return 1
|
||||
elif (r_range + r_dp) >= (2 * season_pct):
|
||||
return 2
|
||||
elif (r_range + r_dp) >= (0 * season_pct):
|
||||
return 3
|
||||
elif (r_range + r_dp) >= (-2 * season_pct):
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_shortstop(r_range: int, r_dp: int, season_pct: float):
|
||||
if (r_range + r_dp) >= (6 * season_pct):
|
||||
return 1
|
||||
elif (r_range + r_dp) >= (2 * season_pct):
|
||||
return 2
|
||||
elif (r_range + r_dp) >= (0 * season_pct):
|
||||
return 3
|
||||
elif (r_range + r_dp) >= (-3 * season_pct):
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_center_field(r_range: int, season_pct: float):
|
||||
if r_range >= 9 * season_pct:
|
||||
return 1
|
||||
elif r_range >= 2 * season_pct:
|
||||
return 2
|
||||
elif r_range >= -2 * season_pct:
|
||||
return 3
|
||||
elif r_range >= -5 * season_pct:
|
||||
return 4
|
||||
else:
|
||||
return 5
|
||||
|
||||
|
||||
def range_left_field(r_range: int, season_pct: float):
|
||||
return range_center_field(r_range, season_pct)
|
||||
|
||||
|
||||
def range_right_field(r_range: int, season_pct: float):
|
||||
return range_center_field(r_range, season_pct)
|
||||
|
||||
|
||||
def valid_error_ratings(err_num: int, position: str) -> int:
|
||||
if position.lower() == 'p':
|
||||
valid_err = [
|
||||
0, 4, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 30, 31, 33, 34,
|
||||
35, 36, 38, 39, 40, 42, 43, 44, 46, 47, 48, 50, 51
|
||||
]
|
||||
elif position.lower() == 'c':
|
||||
valid_err = list(range(17))
|
||||
elif position.lower() == '1b':
|
||||
valid_err = list(range(31))
|
||||
elif position.lower() == '2b':
|
||||
valid_err = [
|
||||
0, 1, 2, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
32, 34, 37, 39, 41, 44, 47, 50, 53, 56, 59, 62, 65, 68, 71
|
||||
]
|
||||
elif position.lower() == '3b':
|
||||
valid_err = [
|
||||
0, 1, 2, 3, 4, 5, 6, 8, 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, 37, 39, 41, 44, 47, 50, 53, 56, 59, 62, 65
|
||||
]
|
||||
elif position.lower() == 'ss':
|
||||
valid_err = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 36, 38, 40, 42, 44, 48, 52, 56, 60, 64, 68, 72
|
||||
]
|
||||
# Outfielders
|
||||
else:
|
||||
valid_err = list(range(26))
|
||||
|
||||
if err_num in valid_err:
|
||||
return err_num
|
||||
elif err_num > valid_err[len(valid_err) - 1]:
|
||||
return valid_err[len(valid_err) - 1]
|
||||
else:
|
||||
for x in valid_err:
|
||||
if err_num <= x:
|
||||
return x
|
||||
|
||||
|
||||
def raw_error(errors: int, chances: int, season_pct: float, chance_max: int):
|
||||
if errors == 0 or chances == 0:
|
||||
return 0
|
||||
c_max = chance_max * season_pct
|
||||
return errors * c_max / chances
|
||||
|
||||
|
||||
def error_pitcher(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 50)), 'p')
|
||||
|
||||
|
||||
def error_catcher(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 500)), 'c')
|
||||
|
||||
|
||||
def error_first_base(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 1300)), '1b')
|
||||
|
||||
|
||||
def error_second_base(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 700)), '2b')
|
||||
|
||||
|
||||
def error_third_base(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 500)), '3b')
|
||||
|
||||
|
||||
def error_shortstop(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 700)), 'ss')
|
||||
|
||||
|
||||
def error_outfield(errors: int, chances: int, season_pct: float):
|
||||
return valid_error_ratings(int(raw_error(errors, chances, season_pct, 250)), 'of')
|
||||
|
||||
|
||||
def arm_outfield(all_arms: list):
|
||||
if not all_arms:
|
||||
return '+5'
|
||||
|
||||
if max(all_arms) > 8:
|
||||
return '-6'
|
||||
elif max(all_arms) > 4:
|
||||
return '-5'
|
||||
elif max(all_arms) < -4:
|
||||
return '+5'
|
||||
else:
|
||||
final_arm = max(all_arms) * -1
|
||||
return f'{"+" if final_arm >= 0 else ""}{final_arm}'
|
||||
|
||||
|
||||
def arm_catcher(cs_pct: str, raa: int, season_pct: float) -> str:
|
||||
if cs_pct == '':
|
||||
return '+3'
|
||||
cs_pct = float(cs_pct.strip("%")) / 100
|
||||
|
||||
if raa > 5 * season_pct:
|
||||
max_arm = -4
|
||||
elif raa > 2 * season_pct:
|
||||
max_arm = -2
|
||||
elif raa > -1 * season_pct:
|
||||
max_arm = 0
|
||||
elif raa > -2 * season_pct:
|
||||
max_arm = 3
|
||||
else:
|
||||
max_arm = 5
|
||||
|
||||
if cs_pct > .6:
|
||||
raw_arm = -5
|
||||
elif cs_pct > .5:
|
||||
raw_arm = -4
|
||||
elif cs_pct > .4:
|
||||
raw_arm = -3
|
||||
elif cs_pct > .3:
|
||||
raw_arm = -2
|
||||
elif cs_pct > .25:
|
||||
raw_arm = -1
|
||||
elif cs_pct > .2:
|
||||
raw_arm = 0
|
||||
elif cs_pct > .16:
|
||||
raw_arm = 1
|
||||
elif cs_pct > .12:
|
||||
raw_arm = 2
|
||||
elif cs_pct > .1:
|
||||
raw_arm = 3
|
||||
elif cs_pct > .05:
|
||||
raw_arm = 4
|
||||
else:
|
||||
raw_arm = 5
|
||||
|
||||
final_arm = min(max_arm, raw_arm)
|
||||
return f'{"+" if final_arm >= 0 else ""}{final_arm}'
|
||||
|
||||
|
||||
def pb_catcher(pb: int, innings: int, season_pct: float):
|
||||
if pb == 0 or innings == 0:
|
||||
return 0
|
||||
|
||||
return min(pb * 1000 * season_pct / innings, 20)
|
||||
|
||||
|
||||
def ot_catcher(errors: int, chances: int, season_pct: float):
|
||||
if errors == 0 or chances == 0:
|
||||
return 0
|
||||
|
||||
c_max = 3000 * season_pct
|
||||
return min(errors * c_max / chances / 3, 20)
|
||||
|
||||
|
||||
def hold_pitcher(raw_cs: str, picks: int, season_pct: float) -> str:
|
||||
if raw_cs == '':
|
||||
return '+9'
|
||||
|
||||
cs_pct = float(raw_cs.strip("%")) / 100
|
||||
if picks > 5 * season_pct:
|
||||
pick_cap = -3
|
||||
elif picks > 3 * season_pct:
|
||||
pick_cap = -2
|
||||
elif picks > 0 * season_pct:
|
||||
pick_cap = 5
|
||||
else:
|
||||
pick_cap = 9
|
||||
|
||||
if cs_pct > .667:
|
||||
hold_num = -5
|
||||
elif cs_pct > .6:
|
||||
hold_num = -4
|
||||
elif cs_pct > .48:
|
||||
hold_num = -3
|
||||
elif cs_pct > .34:
|
||||
hold_num = -2
|
||||
elif cs_pct > .26:
|
||||
hold_num = -1
|
||||
elif cs_pct > .22:
|
||||
hold_num = 0
|
||||
elif cs_pct > .2:
|
||||
hold_num = 1
|
||||
elif cs_pct > .18:
|
||||
hold_num = 3
|
||||
elif cs_pct > .16:
|
||||
hold_num = 4
|
||||
elif cs_pct > .14:
|
||||
hold_num = 5
|
||||
elif cs_pct > .12:
|
||||
hold_num = 6
|
||||
elif cs_pct > .1:
|
||||
hold_num = 7
|
||||
elif cs_pct > .06:
|
||||
hold_num = 8
|
||||
else:
|
||||
hold_num = 9
|
||||
|
||||
final_hold = min(pick_cap, hold_num)
|
||||
return f'{"+" if final_hold >= 0 else ""}{final_hold}'
|
||||
|
||||
|
||||
def pow_ratings(innings: float, gs: int, games: int) -> (int, int):
|
||||
if innings <= 1 or games <= 1:
|
||||
return 1, 1
|
||||
|
||||
s_innings = int(innings * gs / games)
|
||||
r_innings = int(innings * (games - gs) / games)
|
||||
|
||||
if gs == 0:
|
||||
s_pow = 1
|
||||
else:
|
||||
s_pow = round(s_innings / gs)
|
||||
|
||||
if r_innings == 0:
|
||||
r_pow = 1
|
||||
else:
|
||||
r_pow = round(r_innings / (games - gs))
|
||||
|
||||
if r_innings / max(s_innings, 1) < .1:
|
||||
r_pow = 1
|
||||
elif r_pow >= s_pow > 1:
|
||||
r_pow = s_pow - 1
|
||||
|
||||
return s_pow, r_pow
|
||||
|
||||
|
||||
def innings_float(innings: str) -> float:
|
||||
if '.' in innings:
|
||||
whole, decimal = innings.split('.')
|
||||
else:
|
||||
whole = innings
|
||||
decimal = "0"
|
||||
|
||||
return float(int(whole) + int(decimal) * .333)
|
||||
209
calcs_pitcher.py
Normal file
209
calcs_pitcher.py
Normal file
@ -0,0 +1,209 @@
|
||||
from creation_helpers import mround
|
||||
|
||||
|
||||
def total_chances(chance_data):
|
||||
sum_chances = 0
|
||||
for key in chance_data:
|
||||
if key not in ['id', 'player_id', 'cardset_id', 'vs_hand', 'is_prep']:
|
||||
sum_chances += chance_data[key]
|
||||
|
||||
return sum_chances
|
||||
|
||||
|
||||
def soft_rate(pct):
|
||||
if pct > .2:
|
||||
return 'high'
|
||||
elif pct < .1:
|
||||
return 'low'
|
||||
else:
|
||||
return 'avg'
|
||||
|
||||
|
||||
def med_rate(pct):
|
||||
if pct > .65:
|
||||
return 'high'
|
||||
elif pct < .4:
|
||||
return 'low'
|
||||
else:
|
||||
return 'avg'
|
||||
|
||||
|
||||
def hard_rate(pct):
|
||||
if pct > .4:
|
||||
return 'high'
|
||||
elif pct < .2:
|
||||
return 'low'
|
||||
else:
|
||||
return 'avg'
|
||||
|
||||
|
||||
def hr_per_fb_rate(pct):
|
||||
if pct > .18:
|
||||
return 'high'
|
||||
elif pct < .08:
|
||||
return 'low'
|
||||
else:
|
||||
return 'avg'
|
||||
|
||||
|
||||
def all_singles(row, hits_vl, hits_vr):
|
||||
tot_singles_vl = hits_vl * ((int(row[7]) - int(row[8]) - int(row[9]) - int(row[12]))
|
||||
/ int(row[7]))
|
||||
tot_singles_vr = hits_vr * ((int(row[40]) - int(row[41]) - int(row[42]) - int(row[45]))
|
||||
/ int(row[40]))
|
||||
|
||||
return mround(tot_singles_vl), mround(tot_singles_vr)
|
||||
|
||||
|
||||
def bp_singles(singles_vl, singles_vr):
|
||||
bpsi_vl = 5 if singles_vl >= 5 else 0
|
||||
bpsi_vr = 5 if singles_vr >= 5 else 0
|
||||
|
||||
return mround(bpsi_vl), mround(bpsi_vr)
|
||||
|
||||
|
||||
def wh_singles(rem_si_vl, rem_si_vr, hard_rate_vl, hard_rate_vr):
|
||||
if hard_rate_vl == 'low':
|
||||
whs_vl = 0
|
||||
else:
|
||||
whs_vl = rem_si_vl / 2
|
||||
|
||||
if hard_rate_vr == 'low':
|
||||
whs_vr = 0
|
||||
else:
|
||||
whs_vr = rem_si_vr / 2
|
||||
|
||||
return mround(whs_vl), mround(whs_vr)
|
||||
|
||||
|
||||
def one_singles(rem_si_vl, rem_si_vr, soft_rate_vl, soft_rate_vr):
|
||||
if soft_rate_vl == 'high':
|
||||
oss_vl = rem_si_vl
|
||||
else:
|
||||
oss_vl = 0
|
||||
|
||||
if soft_rate_vr == 'high':
|
||||
oss_vr = rem_si_vr
|
||||
else:
|
||||
oss_vr = 0
|
||||
|
||||
return mround(oss_vl), mround(oss_vr)
|
||||
|
||||
|
||||
def bp_homerun(hr_vl, hr_vr, hr_rate_vl, hr_rate_vr):
|
||||
if hr_rate_vl == 'low':
|
||||
bphr_vl = hr_vl
|
||||
elif hr_rate_vl == 'avg':
|
||||
bphr_vl = hr_vl * .75
|
||||
else:
|
||||
bphr_vl = hr_vl * .4
|
||||
|
||||
if hr_rate_vr == 'low':
|
||||
bphr_vr = hr_vr
|
||||
elif hr_rate_vr == 'avg':
|
||||
bphr_vr = hr_vr * .75
|
||||
else:
|
||||
bphr_vr = hr_vr * .4
|
||||
|
||||
return mround(bphr_vl), mround(bphr_vr)
|
||||
|
||||
|
||||
def triples(all_xbh_vl, all_xbh_vr, triple_rate_vl, triple_rate_vr):
|
||||
tr_vl = all_xbh_vl * triple_rate_vl if all_xbh_vl > 0 else 0
|
||||
tr_vr = all_xbh_vr * triple_rate_vr if all_xbh_vr > 0 else 0
|
||||
|
||||
return mround(tr_vl), mround(tr_vr)
|
||||
|
||||
|
||||
def two_doubles(all_doubles_vl, all_doubles_vr, soft_rate_vl, soft_rate_vr):
|
||||
two_doubles_vl = all_doubles_vl if soft_rate_vl == 'high' else 0
|
||||
two_doubles_vr = all_doubles_vr if soft_rate_vr == 'high' else 0
|
||||
|
||||
return mround(two_doubles_vl), mround(two_doubles_vr)
|
||||
|
||||
|
||||
def hbp_rate(hbp, bb):
|
||||
if hbp == 0:
|
||||
return 0
|
||||
elif bb == 0:
|
||||
return 1
|
||||
else:
|
||||
return hbp / bb
|
||||
|
||||
|
||||
def hbps(all_ob, this_hbp_rate):
|
||||
if all_ob == 0 or this_hbp_rate == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround(all_ob * this_hbp_rate)
|
||||
|
||||
|
||||
def xchecks(pos, all_chances=True):
|
||||
if pos.lower() == 'p':
|
||||
return 1 if all_chances else 0
|
||||
elif pos.lower() == 'c':
|
||||
return 3 if all_chances else 2
|
||||
elif pos.lower() == '1b':
|
||||
return 2 if all_chances else 1
|
||||
elif pos.lower() == '2b':
|
||||
return 6 if all_chances else 5
|
||||
elif pos.lower() == '3b':
|
||||
return 3 if all_chances else 2
|
||||
elif pos.lower() == 'ss':
|
||||
return 7 if all_chances else 6
|
||||
elif pos.lower() == 'lf':
|
||||
return 2 if all_chances else 1
|
||||
elif pos.lower() == 'cf':
|
||||
return 3 if all_chances else 2
|
||||
else:
|
||||
return 2 if all_chances else 1
|
||||
|
||||
|
||||
def oppo_fly(all_fly, oppo_rate):
|
||||
if all_fly == 0 or oppo_rate == 0:
|
||||
return 0
|
||||
else:
|
||||
return mround(all_fly * oppo_rate)
|
||||
|
||||
|
||||
def groundball_a(all_gb, dp_rate):
|
||||
if all_gb == 0 or dp_rate == 0:
|
||||
return 0
|
||||
elif dp_rate > .6:
|
||||
return all_gb
|
||||
else:
|
||||
return mround(all_gb * (dp_rate * 1.5))
|
||||
|
||||
|
||||
def balks(total_balks: int, innings: float, season_pct):
|
||||
if innings == 0:
|
||||
return 0
|
||||
return min(round((total_balks * 290 * season_pct) / innings), 20)
|
||||
|
||||
|
||||
def wild_pitches(total_wps: int, innings: float, season_pct):
|
||||
if innings == 0:
|
||||
return 0
|
||||
return min(round((total_wps * 200 * season_pct) / innings), 20)
|
||||
|
||||
|
||||
def closer_rating(gf: int, saves: int, games: int) -> str:
|
||||
if gf == 0 or games == 0 or saves == 0:
|
||||
return 'N'
|
||||
|
||||
if gf / games >= .875:
|
||||
return '6'
|
||||
elif gf / games >= .8:
|
||||
return '5'
|
||||
elif gf / games >= .7:
|
||||
return '4'
|
||||
elif gf / games >= .55:
|
||||
return '3'
|
||||
elif gf / games >= .4:
|
||||
return '2'
|
||||
elif gf / games >= .25:
|
||||
return '1'
|
||||
elif gf / games >= .1:
|
||||
return '0'
|
||||
else:
|
||||
return 'N'
|
||||
176
db_calls_card_creation.py
Normal file
176
db_calls_card_creation.py
Normal file
@ -0,0 +1,176 @@
|
||||
from peewee import *
|
||||
from playhouse.shortcuts import model_to_dict
|
||||
|
||||
db = SqliteDatabase(
|
||||
'storage/card_creation.db',
|
||||
pragmas={
|
||||
'journal_mode': 'wal',
|
||||
'cache_size': -1 * 64000,
|
||||
'synchronous': 0
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class BaseModel(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
class Cardset(BaseModel):
|
||||
set_title = CharField()
|
||||
set_subtitle = CharField(null=True)
|
||||
|
||||
|
||||
class Player(BaseModel):
|
||||
sba_id = IntegerField(primary_key=True)
|
||||
name = CharField()
|
||||
fg_id = IntegerField()
|
||||
br_id = CharField()
|
||||
offense_col = IntegerField()
|
||||
hand = CharField(default='R')
|
||||
|
||||
|
||||
db.create_tables([Cardset, Player])
|
||||
|
||||
|
||||
class BatterRatings(BaseModel):
|
||||
id = CharField(unique=True, primary_key=True)
|
||||
player = ForeignKeyField(Player)
|
||||
cardset = ForeignKeyField(Cardset)
|
||||
vs_hand = FloatField()
|
||||
is_prep = BooleanField()
|
||||
homerun = FloatField()
|
||||
bp_homerun = FloatField()
|
||||
triple = FloatField()
|
||||
double_three = FloatField()
|
||||
double_two = FloatField()
|
||||
double_pull = FloatField()
|
||||
single_two = FloatField()
|
||||
single_one = FloatField()
|
||||
single_center = FloatField()
|
||||
bp_single = FloatField()
|
||||
hbp = FloatField()
|
||||
walk = FloatField()
|
||||
strikeout = FloatField()
|
||||
lineout = FloatField()
|
||||
popout = FloatField()
|
||||
flyout_a = FloatField()
|
||||
flyout_bq = FloatField()
|
||||
flyout_lf_b = FloatField()
|
||||
flyout_rf_b = FloatField()
|
||||
groundout_a = FloatField()
|
||||
groundout_b = FloatField()
|
||||
groundout_c = FloatField()
|
||||
avg = FloatField(null=True)
|
||||
obp = FloatField(null=True)
|
||||
slg = FloatField(null=True)
|
||||
|
||||
|
||||
class PitcherRatings(BaseModel):
|
||||
id = CharField(unique=True, primary_key=True)
|
||||
player = ForeignKeyField(Player)
|
||||
cardset = ForeignKeyField(Cardset)
|
||||
vs_hand = CharField()
|
||||
is_prep = BooleanField()
|
||||
homerun = FloatField()
|
||||
bp_homerun = FloatField()
|
||||
triple = FloatField()
|
||||
double_three = FloatField()
|
||||
double_two = FloatField()
|
||||
double_cf = FloatField()
|
||||
single_two = FloatField()
|
||||
single_one = FloatField()
|
||||
single_center = FloatField()
|
||||
bp_single = FloatField()
|
||||
hbp = FloatField()
|
||||
walk = FloatField()
|
||||
strikeout = FloatField()
|
||||
fo_slap = FloatField()
|
||||
fo_center = FloatField()
|
||||
groundout_a = FloatField()
|
||||
groundout_b = FloatField()
|
||||
xcheck_p = FloatField()
|
||||
xcheck_c = FloatField()
|
||||
xcheck_1b = FloatField()
|
||||
xcheck_2b = FloatField()
|
||||
xcheck_3b = FloatField()
|
||||
xcheck_ss = FloatField()
|
||||
xcheck_lf = FloatField()
|
||||
xcheck_cf = FloatField()
|
||||
xcheck_rf = FloatField()
|
||||
avg = FloatField(null=True)
|
||||
obp = FloatField(null=True)
|
||||
slg = FloatField(null=True)
|
||||
|
||||
|
||||
db.create_tables([BatterRatings, PitcherRatings])
|
||||
|
||||
|
||||
class CardColumns(BaseModel):
|
||||
id = CharField(unique=True, primary_key=True)
|
||||
player = ForeignKeyField(Player)
|
||||
hand = CharField()
|
||||
b_ratings = ForeignKeyField(BatterRatings, null=True)
|
||||
p_ratings = ForeignKeyField(PitcherRatings, null=True)
|
||||
one_dice = CharField()
|
||||
one_results = CharField()
|
||||
one_splits = CharField()
|
||||
two_dice = CharField()
|
||||
two_results = CharField()
|
||||
two_splits = CharField()
|
||||
three_dice = CharField()
|
||||
three_results = CharField()
|
||||
three_splits = CharField()
|
||||
|
||||
|
||||
class Position(BaseModel):
|
||||
player = ForeignKeyField(Player)
|
||||
cardset = ForeignKeyField(Cardset)
|
||||
position = CharField()
|
||||
innings = IntegerField()
|
||||
range = IntegerField()
|
||||
error = IntegerField()
|
||||
arm = CharField(null=True)
|
||||
pb = IntegerField(null=True)
|
||||
overthrow = IntegerField(null=True)
|
||||
|
||||
|
||||
class BatterData(BaseModel):
|
||||
player = ForeignKeyField(Player)
|
||||
cardset = ForeignKeyField(Cardset)
|
||||
stealing = CharField()
|
||||
st_low = IntegerField()
|
||||
st_high = IntegerField()
|
||||
st_auto = BooleanField()
|
||||
st_jump = FloatField()
|
||||
bunting = CharField(null=True)
|
||||
hit_and_run = CharField(null=True)
|
||||
running = CharField()
|
||||
|
||||
|
||||
class PitcherData(BaseModel):
|
||||
player = ForeignKeyField(Player)
|
||||
cardset = ForeignKeyField(Cardset)
|
||||
balk = IntegerField(null=True)
|
||||
wild_pitch = IntegerField(null=True)
|
||||
hold = CharField()
|
||||
starter_rating = IntegerField()
|
||||
relief_rating = IntegerField()
|
||||
closer_rating = IntegerField(null=True)
|
||||
batting = CharField(null=True)
|
||||
|
||||
|
||||
db.create_tables([CardColumns, Position, BatterData, PitcherData])
|
||||
|
||||
|
||||
class CardOutput(BaseModel):
|
||||
name = CharField()
|
||||
hand = CharField()
|
||||
positions = CharField()
|
||||
stealing = CharField()
|
||||
bunting = CharField()
|
||||
hitandrun = CharField()
|
||||
running = CharField()
|
||||
|
||||
|
||||
db.close()
|
||||
42
import_players.py
Normal file
42
import_players.py
Normal file
@ -0,0 +1,42 @@
|
||||
import sys
|
||||
import csv
|
||||
|
||||
from db_calls_card_creation import *
|
||||
|
||||
|
||||
def clean_name(name):
|
||||
return name.replace('.', '').replace("'", '').replace('-', ' ').replace('í', 'i').replace('é', 'e')\
|
||||
.replace('ñ', 'n').replace('á', 'a')
|
||||
|
||||
|
||||
def main(argv):
|
||||
# Importing: https://docs.google.com/spreadsheets/d/1Gcgk4P8rJqX7mHO-OKPuM2FXBWNkDNZ8LNFA6DeaNck/edit#gid=72732162
|
||||
# Player Database from Columns tab
|
||||
file_name = 'data-input/players.csv'
|
||||
|
||||
with open(file_name, 'r') as file:
|
||||
reader = csv.reader(file)
|
||||
all_players = []
|
||||
|
||||
for row in reader:
|
||||
if row[0] != 'sba-id':
|
||||
all_players.append({
|
||||
'name': clean_name(row[3]),
|
||||
'fg_id': row[2],
|
||||
'br_id': row[1],
|
||||
'sba_id': row[0],
|
||||
'offense_col': row[4],
|
||||
'hand': row[5]
|
||||
})
|
||||
|
||||
# TODO: will want to update this to check for existing reocrd; get_or_create ?
|
||||
with db.atomic():
|
||||
for batch in chunked(all_players, 50):
|
||||
Player.insert_many(batch).on_conflict_ignore().execute()
|
||||
db.close()
|
||||
|
||||
print(f'Processed {len(all_players)} players')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
33
manual_updates.py
Normal file
33
manual_updates.py
Normal file
@ -0,0 +1,33 @@
|
||||
import asyncio
|
||||
import csv
|
||||
import datetime
|
||||
import logging
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
from db_calls import *
|
||||
|
||||
|
||||
async def main(argv):
|
||||
with open('manual-updates.csv', 'r') as file:
|
||||
reader = csv.reader(file)
|
||||
count = 0
|
||||
for row in reader:
|
||||
p_query = db_get('players', params=[('name', row[0]), ('cardset_id', 3)])
|
||||
if p_query:
|
||||
this_player = p_query['players'][0]
|
||||
|
||||
pos_1 = row[1]
|
||||
pos_2 = row[2] if row[2] else False
|
||||
pos_3 = row[3] if row[3] else False
|
||||
|
||||
db_patch('players', object_id=this_player['player_id'], params=[
|
||||
('pos_1', pos_1), ('pos_2', pos_2), ('pos_3', pos_3)
|
||||
])
|
||||
count += 1
|
||||
|
||||
print(f'Just updated {count} record{"s" if count != 1 else ""}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main(sys.argv[1:]))
|
||||
16
pit_chance_to_output.py
Normal file
16
pit_chance_to_output.py
Normal file
@ -0,0 +1,16 @@
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from db_calls_card_creation import *
|
||||
|
||||
date = f'{datetime.datetime.now().year}-{datetime.datetime.now().month}-{datetime.datetime.now().day}'
|
||||
log_level = logging.INFO
|
||||
logging.basicConfig(
|
||||
filename=f'{date}.log',
|
||||
format='%(asctime)s - create-pitchers - %(levelname)s - %(message)s',
|
||||
level=log_level
|
||||
)
|
||||
|
||||
|
||||
def process_pitcher_csv(filename: str, cardset: str, testing: bool = False):
|
||||
cardset = Cardset.get_or_none(Cardset.set_title == cardset)
|
||||
Loading…
Reference in New Issue
Block a user