119 lines
5.1 KiB
Python
119 lines
5.1 KiB
Python
import asyncio
|
|
from db_calls import db_get
|
|
import pandas as pd
|
|
import numpy as np
|
|
|
|
|
|
async def main():
|
|
"""Analyze OPS distribution for ALL pitchers to find proper thresholds"""
|
|
|
|
# Get cardset
|
|
c_query = await db_get('cardsets', params=[('name', '2025 Season')])
|
|
cardset_id = c_query['cardsets'][0]['id']
|
|
print(f'Analyzing all pitchers in: 2025 Season (ID: {cardset_id})\n')
|
|
|
|
# Get pitching cards
|
|
pc_query = await db_get('pitchingcards', params=[('cardset_id', cardset_id), ('short_output', True)])
|
|
pitching_cards = pd.DataFrame(pc_query['cards'])
|
|
|
|
vl_query = await db_get('pitchingcardratings', params=[('cardset_id', cardset_id), ('vs_hand', 'L'), ('short_output', True)], timeout=30)
|
|
vr_query = await db_get('pitchingcardratings', params=[('cardset_id', cardset_id), ('vs_hand', 'R'), ('short_output', True)], timeout=30)
|
|
|
|
vl = pd.DataFrame(vl_query['ratings'])
|
|
vr = pd.DataFrame(vr_query['ratings'])
|
|
|
|
# Calculate OPS for each hand
|
|
vl['ops_vL'] = vl['obp'] + vl['slg']
|
|
vr['ops_vR'] = vr['obp'] + vr['slg']
|
|
|
|
# Merge
|
|
pit_ratings = pd.merge(vl[['pitchingcard', 'ops_vL']], vr[['pitchingcard', 'ops_vR']], on='pitchingcard')
|
|
pit_ratings = pd.merge(pitching_cards[['id', 'player', 'starter_rating']], pit_ratings, left_on='id', right_on='pitchingcard')
|
|
|
|
# Calculate total_OPS using max (as in the code)
|
|
pit_ratings['total_OPS'] = (pit_ratings['ops_vR'] + pit_ratings['ops_vL'] + pit_ratings[['ops_vL', 'ops_vR']].max(axis=1)) / 3
|
|
|
|
# Split by starter/reliever
|
|
starters = pit_ratings[pit_ratings['starter_rating'] > 3]
|
|
relievers = pit_ratings[pit_ratings['starter_rating'] <= 3]
|
|
|
|
print("=" * 80)
|
|
print(f"STARTERS (n={len(starters)})")
|
|
print("=" * 80)
|
|
print(f"\nOPS-Against Distribution:")
|
|
print(f" Min: {starters['total_OPS'].min():.3f}")
|
|
print(f" 5th %: {starters['total_OPS'].quantile(0.05):.3f}")
|
|
print(f" 10th %: {starters['total_OPS'].quantile(0.10):.3f}")
|
|
print(f" 25th %: {starters['total_OPS'].quantile(0.25):.3f}")
|
|
print(f" Median: {starters['total_OPS'].quantile(0.50):.3f}")
|
|
print(f" 75th %: {starters['total_OPS'].quantile(0.75):.3f}")
|
|
print(f" 90th %: {starters['total_OPS'].quantile(0.90):.3f}")
|
|
print(f" 95th %: {starters['total_OPS'].quantile(0.95):.3f}")
|
|
print(f" Max: {starters['total_OPS'].max():.3f}")
|
|
|
|
print(f"\n\nCurrent Thresholds vs Reality:")
|
|
print(f" {'Rarity':<15} {'Threshold':<12} {'Count at/below':<20} {'% of Total':<12}")
|
|
print("-" * 80)
|
|
|
|
thresholds = [
|
|
('Hall of Fame', 0.4),
|
|
('Diamond', 0.475),
|
|
('Gold', 0.53),
|
|
('Silver', 0.6),
|
|
('Bronze', 0.675)
|
|
]
|
|
|
|
for name, thresh in thresholds:
|
|
count = len(starters[starters['total_OPS'] <= thresh])
|
|
pct = (count / len(starters)) * 100
|
|
print(f" {name:<15} <= {thresh:<9.3f} {count:<20} {pct:>5.1f}%")
|
|
|
|
print(f"\n\nSuggested Thresholds (using percentiles):")
|
|
print(f" Hall of Fame (top 5%): <= {starters['total_OPS'].quantile(0.05):.3f}")
|
|
print(f" Diamond (top 15%): <= {starters['total_OPS'].quantile(0.15):.3f}")
|
|
print(f" Gold (top 30%): <= {starters['total_OPS'].quantile(0.30):.3f}")
|
|
print(f" Silver (top 50%): <= {starters['total_OPS'].quantile(0.50):.3f}")
|
|
print(f" Bronze (top 70%): <= {starters['total_OPS'].quantile(0.70):.3f}")
|
|
|
|
print("\n" + "=" * 80)
|
|
print(f"RELIEVERS (n={len(relievers)})")
|
|
print("=" * 80)
|
|
print(f"\nOPS-Against Distribution:")
|
|
print(f" Min: {relievers['total_OPS'].min():.3f}")
|
|
print(f" 5th %: {relievers['total_OPS'].quantile(0.05):.3f}")
|
|
print(f" 10th %: {relievers['total_OPS'].quantile(0.10):.3f}")
|
|
print(f" 25th %: {relievers['total_OPS'].quantile(0.25):.3f}")
|
|
print(f" Median: {relievers['total_OPS'].quantile(0.50):.3f}")
|
|
print(f" 75th %: {relievers['total_OPS'].quantile(0.75):.3f}")
|
|
print(f" 90th %: {relievers['total_OPS'].quantile(0.90):.3f}")
|
|
print(f" 95th %: {relievers['total_OPS'].quantile(0.95):.3f}")
|
|
print(f" Max: {relievers['total_OPS'].max():.3f}")
|
|
|
|
print(f"\n\nCurrent Thresholds vs Reality:")
|
|
print(f" {'Rarity':<15} {'Threshold':<12} {'Count at/below':<20} {'% of Total':<12}")
|
|
print("-" * 80)
|
|
|
|
thresholds = [
|
|
('Hall of Fame', 0.325),
|
|
('Diamond', 0.4),
|
|
('Gold', 0.475),
|
|
('Silver', 0.55),
|
|
('Bronze', 0.625)
|
|
]
|
|
|
|
for name, thresh in thresholds:
|
|
count = len(relievers[relievers['total_OPS'] <= thresh])
|
|
pct = (count / len(relievers)) * 100
|
|
print(f" {name:<15} <= {thresh:<9.3f} {count:<20} {pct:>5.1f}%")
|
|
|
|
print(f"\n\nSuggested Thresholds (using percentiles):")
|
|
print(f" Hall of Fame (top 5%): <= {relievers['total_OPS'].quantile(0.05):.3f}")
|
|
print(f" Diamond (top 15%): <= {relievers['total_OPS'].quantile(0.15):.3f}")
|
|
print(f" Gold (top 30%): <= {relievers['total_OPS'].quantile(0.30):.3f}")
|
|
print(f" Silver (top 50%): <= {relievers['total_OPS'].quantile(0.50):.3f}")
|
|
print(f" Bronze (top 70%): <= {relievers['total_OPS'].quantile(0.70):.3f}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
asyncio.run(main())
|