nesticot commited on
Commit
a0b73c2
·
verified ·
1 Parent(s): a853ece

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +148 -139
app.py CHANGED
@@ -408,151 +408,160 @@ def server(input, output, session):
408
  player_name = batter_name_id[batter_id]
409
  stats = [merged_dict[x]['stat_title'] for x in merged_dict.keys()]
410
 
411
- # Calculate percentiles and values
412
- percentiles = [int((1 - x) * 100) if merged_dict[stat]["percentile_flip"] else int(x * 100) for x, stat in zip(new_player_percentiles.select(merged_dict.keys()).to_numpy()[0], merged_dict.keys())]
413
- percentiles = np.clip(percentiles, 1, 100)
414
- values = [str(f'{x:{merged_dict[stat]["format"]}}').strip('%') for x, stat in zip(new_player_metrics.select(merged_dict.keys()).to_numpy()[0], merged_dict.keys())]
415
-
416
- # Get team logo URL
417
- try:
418
- logo_url = image_dict[team_dict[player_team_dict[batter_id]]]
419
- except KeyError:
420
- logo_url = "https://a.espncdn.com/combiner/i?img=/i/teamlogos/leagues/500/mlb.png&w=500&h=500"
421
-
422
- # Create a custom colormap
423
- color_list = ['#3661AD', '#B4CFD1', '#D82129']
424
- cmap = LinearSegmentedColormap.from_list("custom_cmap", color_list)
425
- norm = Normalize(vmin=0.1, vmax=0.9)
426
- norm_percentiles = norm(percentiles / 100)
427
- colors = [cmap(p) for p in norm_percentiles]
428
-
429
- # Figure setup
430
- num_stats = len(stats)
431
- bar_height = 4.5
432
- spacing = 1
433
- fig_height = (bar_height + spacing) * num_stats
434
- fig = plt.figure(figsize=(12, 12))
435
- gs = GridSpec(6, 5, height_ratios=[0.1, 1.5, 0.9, 0.9, 7.6, 0.1], width_ratios=[0.2, 1.5, 7, 1.5, 0.2])
436
 
437
- # Define subplots
438
- ax_title = fig.add_subplot(gs[1, 2])
439
- ax_table = fig.add_subplot(gs[2, :])
440
- ax_fv_table = fig.add_subplot(gs[3, :])
441
- ax_fv_table.axis('off')
442
- ax = fig.add_subplot(gs[4, :])
443
- ax_logo = fig.add_subplot(gs[1, 3])
444
-
445
- ax.set_xlim(-1, 99)
446
- ax.set_ylim(-1, 99)
447
- ax.set_aspect("equal")
448
- ax.axis("off")
449
-
450
- # Draw each bar
451
- for i, (stat, percentile, value, color) in enumerate(zip(stats, percentiles, values, colors)):
452
- y = fig_height - (i + 1) * (bar_height + spacing)
453
- ax.add_patch(patches.Rectangle((0, y + bar_height / 4), 100, bar_height / 2, color="#C7DCDC", lw=0))
454
- ax.add_patch(patches.Rectangle((0, y), percentile, bar_height, color=color, lw=0))
455
- circle_y = y + bar_height - bar_height / 2
456
- circle = plt.Circle((percentile, circle_y), bar_height / 2, color=color, ec='white', lw=1.5, zorder=10)
457
- ax.add_patch(circle)
458
- fs = 14
459
- ax.text(percentile, circle_y, f"{percentile}", ha="center", va="center", fontsize=10, color='white', zorder=10, fontweight='bold')
460
- ax.text(-5, y + bar_height / 2, stat, ha="right", va="center", fontsize=fs)
461
- ax.text(115, y + bar_height / 2, str(value), ha="right", va="center", fontsize=fs, zorder=5)
462
- if i < len(stats) and i > 0:
463
- ax.hlines(y=y + bar_height + spacing / 2, color='#399098', linestyle=(0, (5, 5)), linewidth=1, xmin=-33, xmax=0)
464
- ax.hlines(y=y + bar_height + spacing / 2, color='#399098', linestyle=(0, (5, 5)), linewidth=1, xmin=100, xmax=115)
465
-
466
- # Draw vertical lines for 10%, 50%, and 90% with labels
467
- for x, label, align, color in zip([10, 50, 90], ["Poor", "Average", "Great"], ['center', 'center', 'center'], color_list):
468
- ax.axvline(x=x, ymin=0, ymax=1, color='#FFF', linestyle='-', lw=1, zorder=1, alpha=0.5)
469
- ax.text(x, fig_height + 4, label, ha=align, va='center', fontsize=12, fontweight='bold', color=color)
470
- triangle = patches.RegularPolygon((x, fig_height + 1), 3, radius=1, orientation=0, color=color, zorder=2)
471
- ax.add_patch(triangle)
472
-
473
- # # Title
474
- # ax_title.set_ylim(0, 1)
475
- # ax_title.text(0.5, 0.5, f"{player_name} - {player_position_dict[batter_id]}\nPercentile Rankings - 2024 AAA", ha="center", va="center", fontsize=24)
476
- # ax_title.axis("off")
477
- player_bio(batter_id, ax=ax_title, sport_id=sport_id, year_input=year_input)
478
-
479
- # Add team logo
480
- #response = requests.get(logo_url)
481
- if input.switch():
482
- response = requests.get(input.logo_select())
483
- else:
484
- response = requests.get(logo_url)
485
- img = Image.open(BytesIO(response.content))
486
- ax_logo.imshow(img)
487
- ax_logo.axis("off")
488
- ax.axis('equal')
489
-
490
- # Metrics data table
491
- metrics_data = {
492
- "Pitches": new_player_metrics['pitches'][0],
493
- "PA": new_player_metrics['pa'][0],
494
- "BIP": new_player_metrics['bip'][0],
495
- "HR": f"{new_player_metrics['home_run'][0]:.0f}",
496
- "AVG": f"{new_player_metrics['avg'][0]:.3f}",
497
- "OBP": f"{new_player_metrics['obp'][0]:.3f}",
498
- "SLG": f"{new_player_metrics['slg'][0]:.3f}",
499
- "OPS": f"{new_player_metrics['obp'][0] + new_player_metrics['slg'][0]:.3f}",
500
- }
501
- df_table = pd.DataFrame(metrics_data, index=[0])
502
- ax_table.axis('off')
503
- table = ax_table.table(cellText=df_table.values, colLabels=df_table.columns, cellLoc='center', loc='bottom', bbox=[0.07, 0, 0.86, 1])
504
- for key, cell in table.get_celld().items():
505
- if key[0] == 0:
506
- cell.set_text_props(fontweight='bold')
507
- table.auto_set_font_size(False)
508
- table.set_fontsize(12)
509
- table.scale(1, 1.5)
510
-
511
- # Additional subplots for spacing
512
- ax_top = fig.add_subplot(gs[0, :])
513
- ax_bot = fig.add_subplot(gs[-1, :])
514
- ax_top.axis('off')
515
- ax_bot.axis('off')
516
- ax_bot.text(0.05, 2, "By: Thomas Nestico (@TJStats)", ha="left", va="center", fontsize=14)
517
- ax_bot.text(0.95, 2, "Data: MLB, Fangraphs", ha="right", va="center", fontsize=14)
518
- fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
519
 
520
- # Player headshot
521
- ax_headshot = fig.add_subplot(gs[1, 1])
522
  try:
523
- url = f'https://img.mlbstatic.com/mlb-photos/image/upload/c_fill,g_auto/w_640/v1/people/{batter_id}/headshot/milb/current.png'
524
- response = requests.get(url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  img = Image.open(BytesIO(response.content))
526
- ax_headshot.set_xlim(0, 1)
527
- ax_headshot.set_ylim(0, 1)
528
- ax_headshot.imshow(img, extent=[1/6, 5/6, 0, 1], origin='upper')
529
- except PIL.UnidentifiedImageError:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
530
  ax_headshot.axis('off')
531
- #return
532
- ax_headshot.axis('off')
533
- ax_table.set_title('Season Summary', style='italic')
534
-
535
- # Fangraphs scouting grades table
536
- print(batter_id)
537
-
538
- if batter_id not in dict_mlb_fg.keys():
539
- ax_fv_table.text(x=0.5, y=0.5, s='No Scouting Data', style='italic', ha='center', va='center', fontsize=20, bbox=dict(facecolor='white', alpha=1, pad=10))
540
- return
541
- df_fv_table = df_prospects[(df_prospects['minorMasterId'] == dict_mlb_fg[batter_id])][['cFV', 'Hit', 'Game', 'Raw', 'Spd', 'Fld']].reset_index(drop=True)
542
- ax_fv_table.axis('off')
543
- if df_fv_table.empty:
544
- ax_fv_table.text(x=0.5, y=0.5, s='No Scouting Data', style='italic', ha='center', va='center', fontsize=20, bbox=dict(facecolor='white', alpha=1, pad=10))
545
- return
546
- df_fv_table.columns = ['FV', 'Hit', 'Game', 'Raw', 'Spd', 'Fld']
547
- table_fv = ax_fv_table.table(cellText=df_fv_table.values, colLabels=df_fv_table.columns, cellLoc='center', loc='bottom', bbox=[0.07, 0, 0.86, 1])
548
- for key, cell in table_fv.get_celld().items():
549
- if key[0] == 0:
550
- cell.set_text_props(fontweight='bold')
551
- table_fv.auto_set_font_size(False)
552
- table_fv.set_fontsize(12)
553
- table_fv.scale(1, 1.5)
554
- ax_fv_table.set_title('Fangraphs Scouting Grades', style='italic')
555
 
 
 
 
 
556
 
557
 
558
  #plt.show()
 
408
  player_name = batter_name_id[batter_id]
409
  stats = [merged_dict[x]['stat_title'] for x in merged_dict.keys()]
410
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
 
412
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
 
 
 
414
  try:
415
+
416
+ # Calculate percentiles and values
417
+ percentiles = [int((1 - x) * 100) if merged_dict[stat]["percentile_flip"] else int(x * 100) for x, stat in zip(new_player_percentiles.select(merged_dict.keys()).to_numpy()[0], merged_dict.keys())]
418
+ percentiles = np.clip(percentiles, 1, 100)
419
+ values = [str(f'{x:{merged_dict[stat]["format"]}}').strip('%') for x, stat in zip(new_player_metrics.select(merged_dict.keys()).to_numpy()[0], merged_dict.keys())]
420
+
421
+ # Get team logo URL
422
+ try:
423
+ logo_url = image_dict[team_dict[player_team_dict[batter_id]]]
424
+ except KeyError:
425
+ logo_url = "https://a.espncdn.com/combiner/i?img=/i/teamlogos/leagues/500/mlb.png&w=500&h=500"
426
+
427
+ # Create a custom colormap
428
+ color_list = ['#3661AD', '#B4CFD1', '#D82129']
429
+ cmap = LinearSegmentedColormap.from_list("custom_cmap", color_list)
430
+ norm = Normalize(vmin=0.1, vmax=0.9)
431
+ norm_percentiles = norm(percentiles / 100)
432
+ colors = [cmap(p) for p in norm_percentiles]
433
+
434
+ # Figure setup
435
+ num_stats = len(stats)
436
+ bar_height = 4.5
437
+ spacing = 1
438
+ fig_height = (bar_height + spacing) * num_stats
439
+ fig = plt.figure(figsize=(12, 12))
440
+ gs = GridSpec(6, 5, height_ratios=[0.1, 1.5, 0.9, 0.9, 7.6, 0.1], width_ratios=[0.2, 1.5, 7, 1.5, 0.2])
441
+
442
+ # Define subplots
443
+ ax_title = fig.add_subplot(gs[1, 2])
444
+ ax_table = fig.add_subplot(gs[2, :])
445
+ ax_fv_table = fig.add_subplot(gs[3, :])
446
+ ax_fv_table.axis('off')
447
+ ax = fig.add_subplot(gs[4, :])
448
+ ax_logo = fig.add_subplot(gs[1, 3])
449
+
450
+ ax.set_xlim(-1, 99)
451
+ ax.set_ylim(-1, 99)
452
+ ax.set_aspect("equal")
453
+ ax.axis("off")
454
+
455
+ # Draw each bar
456
+ for i, (stat, percentile, value, color) in enumerate(zip(stats, percentiles, values, colors)):
457
+ y = fig_height - (i + 1) * (bar_height + spacing)
458
+ ax.add_patch(patches.Rectangle((0, y + bar_height / 4), 100, bar_height / 2, color="#C7DCDC", lw=0))
459
+ ax.add_patch(patches.Rectangle((0, y), percentile, bar_height, color=color, lw=0))
460
+ circle_y = y + bar_height - bar_height / 2
461
+ circle = plt.Circle((percentile, circle_y), bar_height / 2, color=color, ec='white', lw=1.5, zorder=10)
462
+ ax.add_patch(circle)
463
+ fs = 14
464
+ ax.text(percentile, circle_y, f"{percentile}", ha="center", va="center", fontsize=10, color='white', zorder=10, fontweight='bold')
465
+ ax.text(-5, y + bar_height / 2, stat, ha="right", va="center", fontsize=fs)
466
+ ax.text(115, y + bar_height / 2, str(value), ha="right", va="center", fontsize=fs, zorder=5)
467
+ if i < len(stats) and i > 0:
468
+ ax.hlines(y=y + bar_height + spacing / 2, color='#399098', linestyle=(0, (5, 5)), linewidth=1, xmin=-33, xmax=0)
469
+ ax.hlines(y=y + bar_height + spacing / 2, color='#399098', linestyle=(0, (5, 5)), linewidth=1, xmin=100, xmax=115)
470
+
471
+ # Draw vertical lines for 10%, 50%, and 90% with labels
472
+ for x, label, align, color in zip([10, 50, 90], ["Poor", "Average", "Great"], ['center', 'center', 'center'], color_list):
473
+ ax.axvline(x=x, ymin=0, ymax=1, color='#FFF', linestyle='-', lw=1, zorder=1, alpha=0.5)
474
+ ax.text(x, fig_height + 4, label, ha=align, va='center', fontsize=12, fontweight='bold', color=color)
475
+ triangle = patches.RegularPolygon((x, fig_height + 1), 3, radius=1, orientation=0, color=color, zorder=2)
476
+ ax.add_patch(triangle)
477
+
478
+ # # Title
479
+ # ax_title.set_ylim(0, 1)
480
+ # ax_title.text(0.5, 0.5, f"{player_name} - {player_position_dict[batter_id]}\nPercentile Rankings - 2024 AAA", ha="center", va="center", fontsize=24)
481
+ # ax_title.axis("off")
482
+ player_bio(batter_id, ax=ax_title, sport_id=sport_id, year_input=year_input)
483
+
484
+ # Add team logo
485
+ #response = requests.get(logo_url)
486
+ if input.switch():
487
+ response = requests.get(input.logo_select())
488
+ else:
489
+ response = requests.get(logo_url)
490
  img = Image.open(BytesIO(response.content))
491
+ ax_logo.imshow(img)
492
+ ax_logo.axis("off")
493
+ ax.axis('equal')
494
+
495
+ # Metrics data table
496
+ metrics_data = {
497
+ "Pitches": new_player_metrics['pitches'][0],
498
+ "PA": new_player_metrics['pa'][0],
499
+ "BIP": new_player_metrics['bip'][0],
500
+ "HR": f"{new_player_metrics['home_run'][0]:.0f}",
501
+ "AVG": f"{new_player_metrics['avg'][0]:.3f}",
502
+ "OBP": f"{new_player_metrics['obp'][0]:.3f}",
503
+ "SLG": f"{new_player_metrics['slg'][0]:.3f}",
504
+ "OPS": f"{new_player_metrics['obp'][0] + new_player_metrics['slg'][0]:.3f}",
505
+ }
506
+ df_table = pd.DataFrame(metrics_data, index=[0])
507
+ ax_table.axis('off')
508
+ table = ax_table.table(cellText=df_table.values, colLabels=df_table.columns, cellLoc='center', loc='bottom', bbox=[0.07, 0, 0.86, 1])
509
+ for key, cell in table.get_celld().items():
510
+ if key[0] == 0:
511
+ cell.set_text_props(fontweight='bold')
512
+ table.auto_set_font_size(False)
513
+ table.set_fontsize(12)
514
+ table.scale(1, 1.5)
515
+
516
+ # Additional subplots for spacing
517
+ ax_top = fig.add_subplot(gs[0, :])
518
+ ax_bot = fig.add_subplot(gs[-1, :])
519
+ ax_top.axis('off')
520
+ ax_bot.axis('off')
521
+ ax_bot.text(0.05, 2, "By: Thomas Nestico (@TJStats)", ha="left", va="center", fontsize=14)
522
+ ax_bot.text(0.95, 2, "Data: MLB, Fangraphs", ha="right", va="center", fontsize=14)
523
+ fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
524
+
525
+ # Player headshot
526
+ ax_headshot = fig.add_subplot(gs[1, 1])
527
+ try:
528
+ url = f'https://img.mlbstatic.com/mlb-photos/image/upload/c_fill,g_auto/w_640/v1/people/{batter_id}/headshot/milb/current.png'
529
+ response = requests.get(url)
530
+ img = Image.open(BytesIO(response.content))
531
+ ax_headshot.set_xlim(0, 1)
532
+ ax_headshot.set_ylim(0, 1)
533
+ ax_headshot.imshow(img, extent=[1/6, 5/6, 0, 1], origin='upper')
534
+ except PIL.UnidentifiedImageError:
535
+ ax_headshot.axis('off')
536
+ #return
537
  ax_headshot.axis('off')
538
+ ax_table.set_title('Season Summary', style='italic')
539
+
540
+ # Fangraphs scouting grades table
541
+ print(batter_id)
542
+
543
+ if batter_id not in dict_mlb_fg.keys():
544
+ ax_fv_table.text(x=0.5, y=0.5, s='No Scouting Data', style='italic', ha='center', va='center', fontsize=20, bbox=dict(facecolor='white', alpha=1, pad=10))
545
+ return
546
+ df_fv_table = df_prospects[(df_prospects['minorMasterId'] == dict_mlb_fg[batter_id])][['cFV', 'Hit', 'Game', 'Raw', 'Spd', 'Fld']].reset_index(drop=True)
547
+ ax_fv_table.axis('off')
548
+ if df_fv_table.empty:
549
+ ax_fv_table.text(x=0.5, y=0.5, s='No Scouting Data', style='italic', ha='center', va='center', fontsize=20, bbox=dict(facecolor='white', alpha=1, pad=10))
550
+ return
551
+ df_fv_table.columns = ['FV', 'Hit', 'Game', 'Raw', 'Spd', 'Fld']
552
+ table_fv = ax_fv_table.table(cellText=df_fv_table.values, colLabels=df_fv_table.columns, cellLoc='center', loc='bottom', bbox=[0.07, 0, 0.86, 1])
553
+ for key, cell in table_fv.get_celld().items():
554
+ if key[0] == 0:
555
+ cell.set_text_props(fontweight='bold')
556
+ table_fv.auto_set_font_size(False)
557
+ table_fv.set_fontsize(12)
558
+ table_fv.scale(1, 1.5)
559
+ ax_fv_table.set_title('Fangraphs Scouting Grades', style='italic')
 
 
560
 
561
+ except ValueError:
562
+ fig = plt.figure(figsize=(26,26))
563
+ fig.text(x=0.1,y=0.9,s='No Statcast Data For This Batter',fontsize=36,ha='left')
564
+ return fig
565
 
566
 
567
  #plt.show()