Build a dashboard to showcase Basketball data
To upload files, please first save the app
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from st_aggrid import AgGrid, GridOptionsBuilder, DataReturnMode, GridUpdateMode
from datetime import datetime, timedelta
import random
# Set page config
st.set_page_config(
page_title="Basketball Analytics Dashboard",
page_icon="🏀",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS
st.markdown("""
<style>
.main-header {
font-size: 3rem;
font-weight: bold;
text-align: center;
margin-bottom: 2rem;
background: linear-gradient(90deg, #FF6B35, #F7931E);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.metric-card {
background-color: #f0f2f6;
padding: 1rem;
border-radius: 10px;
border-left: 5px solid #FF6B35;
}
.sidebar .sidebar-content {
background-color: #f8f9fa;
}
</style>
""", unsafe_allow_html=True)
# Generate sample basketball data
@st.cache_data
def generate_basketball_data():
np.random.seed(42)
random.seed(42)
# Team names
teams = [
"Lakers", "Warriors", "Celtics", "Bulls", "Heat", "Nets", "Bucks", "76ers",
"Clippers", "Nuggets", "Suns", "Mavericks", "Jazz", "Hawks", "Knicks", "Pacers"
]
# Player positions
positions = ["PG", "SG", "SF", "PF", "C"]
# Generate player data
players_data = []
for i in range(200):
team = random.choice(teams)
position = random.choice(positions)
age = random.randint(19, 38)
games_played = random.randint(45, 82)
# Generate realistic stats based on position
if position == "PG":
points = round(np.random.normal(15, 5), 1)
assists = round(np.random.normal(8, 3), 1)
rebounds = round(np.random.normal(4, 2), 1)
elif position == "SG":
points = round(np.random.normal(18, 6), 1)
assists = round(np.random.normal(4, 2), 1)
rebounds = round(np.random.normal(5, 2), 1)
elif position == "SF":
points = round(np.random.normal(16, 5), 1)
assists = round(np.random.normal(5, 2), 1)
rebounds = round(np.random.normal(7, 3), 1)
elif position == "PF":
points = round(np.random.normal(14, 4), 1)
assists = round(np.random.normal(3, 2), 1)
rebounds = round(np.random.normal(9, 3), 1)
else: # Center
points = round(np.random.normal(13, 4), 1)
assists = round(np.random.normal(2, 1), 1)
rebounds = round(np.random.normal(11, 4), 1)
# Ensure positive values
points = max(0, points)
assists = max(0, assists)
rebounds = max(0, rebounds)
players_data.append({
"Player": f"Player_{i+1}",
"Team": team,
"Position": position,
"Age": age,
"Games": games_played,
"Points": points,
"Assists": assists,
"Rebounds": rebounds,
"FG%": round(np.random.uniform(0.35, 0.55), 3),
"3P%": round(np.random.uniform(0.25, 0.45), 3),
"FT%": round(np.random.uniform(0.65, 0.95), 3),
"Salary": random.randint(500000, 45000000)
})
# Generate team standings
team_data = []
for team in teams:
wins = random.randint(25, 65)
losses = 82 - wins
team_data.append({
"Team": team,
"Wins": wins,
"Losses": losses,
"Win%": round(wins / 82, 3),
"PPG": round(np.random.uniform(105, 125), 1),
"OPP_PPG": round(np.random.uniform(105, 125), 1),
"Diff": round(np.random.uniform(-10, 15), 1)
})
return pd.DataFrame(players_data), pd.DataFrame(team_data)
# Load data
players_df, teams_df = generate_basketball_data()
# Main title
st.markdown('<h1 class="main-header">🏀 Basketball Analytics Dashboard</h1>', unsafe_allow_html=True)
# Sidebar filters
st.sidebar.header("🔧 Filters & Controls")
# Team filter
selected_teams = st.sidebar.multiselect(
"Select Teams",
options=sorted(players_df['Team'].unique()),
default=sorted(players_df['Team'].unique())[:5]
)
# Position filter
selected_positions = st.sidebar.multiselect(
"Select Positions",
options=players_df['Position'].unique(),
default=players_df['Position'].unique()
)
# Age range
age_range = st.sidebar.slider(
"Age Range",
min_value=int(players_df['Age'].min()),
max_value=int(players_df['Age'].max()),
value=(20, 35)
)
# Filter data
filtered_players = players_df[
(players_df['Team'].isin(selected_teams)) &
(players_df['Position'].isin(selected_positions)) &
(players_df['Age'] >= age_range[0]) &
(players_df['Age'] <= age_range[1])
]
filtered_teams = teams_df[teams_df['Team'].isin(selected_teams)]
# Key Metrics Row
st.header("📊 Key Metrics")
col1, col2, col3, col4, col5 = st.columns(5)
with col1:
st.metric(
label="Total Players",
value=len(filtered_players),
delta=f"{len(filtered_players) - len(players_df)} vs All"
)
with col2:
avg_points = filtered_players['Points'].mean()
st.metric(
label="Avg Points",
value=f"{avg_points:.1f}",
delta=f"{avg_points - players_df['Points'].mean():.1f}"
)
with col3:
avg_assists = filtered_players['Assists'].mean()
st.metric(
label="Avg Assists",
value=f"{avg_assists:.1f}",
delta=f"{avg_assists - players_df['Assists'].mean():.1f}"
)
with col4:
avg_rebounds = filtered_players['Rebounds'].mean()
st.metric(
label="Avg Rebounds",
value=f"{avg_rebounds:.1f}",
delta=f"{avg_rebounds - players_df['Rebounds'].mean():.1f}"
)
with col5:
total_salary = filtered_players['Salary'].sum()
st.metric(
label="Total Salary",
value=f"${total_salary/1e6:.1f}M",
delta=f"${(total_salary - players_df['Salary'].sum())/1e6:.1f}M"
)
# Charts Row 1
st.header("📈 Performance Analytics")
col1, col2 = st.columns(2)
with col1:
st.subheader("Points by Position")
fig_box = px.box(
filtered_players,
x='Position',
y='Points',
color='Position',
title="Points Distribution by Position"
)
fig_box.update_layout(showlegend=False)
st.plotly_chart(fig_box, use_container_width=True)
with col2:
st.subheader("Age vs Performance")
fig_scatter = px.scatter(
filtered_players,
x='Age',
y='Points',
size='Salary',
color='Position',
hover_data=['Player', 'Team'],
title="Age vs Points (Size = Salary)"
)
st.plotly_chart(fig_scatter, use_container_width=True)
# Charts Row 2
col1, col2 = st.columns(2)
with col1:
st.subheader("Team Performance")
if not filtered_teams.empty:
fig_bar = px.bar(
filtered_teams.sort_values('Wins', ascending=False),
x='Team',
y=['Wins', 'Losses'],
title="Team Wins vs Losses",
barmode='stack'
)
fig_bar.update_xaxes(tickangle=45)
st.plotly_chart(fig_bar, use_container_width=True)
with col2:
st.subheader("Shooting Efficiency")
fig_shooting = px.scatter(
filtered_players,
x='FG%',
y='3P%',
size='Points',
color='Team',
hover_data=['Player', 'Position'],
title="Field Goal % vs 3-Point %"
)
st.plotly_chart(fig_shooting, use_container_width=True)
# Player Statistics Table
st.header("👥 Player Statistics")
st.subheader("Interactive Player Data Grid")
# Configure AgGrid
gb = GridOptionsBuilder.from_dataframe(filtered_players)
gb.configure_pagination(paginationAutoPageSize=True)
gb.configure_side_bar()
gb.configure_selection('multiple', use_checkbox=True)
gb.configure_default_column(
groupable=True,
value=True,
enableRowGroup=True,
editable=False,
sortable=True,
filterable=True
)
# Configure specific columns
gb.configure_column("Points", type=["numericColumn"], precision=1, aggFunc='avg')
gb.configure_column("Assists", type=["numericColumn"], precision=1, aggFunc='avg')
gb.configure_column("Rebounds", type=["numericColumn"], precision=1, aggFunc='avg')
gb.configure_column("Salary", type=["numericColumn"], valueFormatter="'$' + (value/1000000).toFixed(1) + 'M'")
gb.configure_column("FG%", type=["numericColumn"], precision=3)
gb.configure_column("3P%", type=["numericColumn"], precision=3)
gb.configure_column("FT%", type=["numericColumn"], precision=3)
gridOptions = gb.build()
grid_response = AgGrid(
filtered_players,
gridOptions=gridOptions,
data_return_mode=DataReturnMode.FILTERED_AND_SORTED,
update_mode=GridUpdateMode.MODEL_CHANGED,
fit_columns_on_grid_load=True,
enable_enterprise_modules=True,
height=400,
width='100%'
)
# Team Standings
st.header("🏆 Team Standings")
if not filtered_teams.empty:
# Sort teams by win percentage
standings = filtered_teams.sort_values('Win%', ascending=False).reset_index(drop=True)
standings['Rank'] = range(1, len(standings) + 1)
# Reorder columns
standings = standings[['Rank', 'Team', 'Wins', 'Losses', 'Win%', 'PPG', 'OPP_PPG', 'Diff']]
st.table(standings)
# Advanced Analytics
st.header("🔬 Advanced Analytics")
if len(grid_response['selected_rows']) > 0:
selected_players = pd.DataFrame(grid_response['selected_rows'])
col1, col2 = st.columns(2)
with col1:
st.subheader("Selected Players Comparison")
comparison_stats = selected_players[['Player', 'Points', 'Assists', 'Rebounds']].set_index('Player')
fig_radar = go.Figure()
for idx, player in enumerate(comparison_stats.index):
fig_radar.add_trace(go.Scatterpolar(
r=[comparison_stats.loc[player, 'Points'],
comparison_stats.loc[player, 'Assists'],
comparison_stats.loc[player, 'Rebounds']],
theta=['Points', 'Assists', 'Rebounds'],
fill='toself',
name=player
))
fig_radar.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, max(comparison_stats.max())]
)),
showlegend=True,
title="Player Performance Radar"
)
st.plotly_chart(fig_radar, use_container_width=True)
with col2:
st.subheader("Selected Players Stats")
st.table(selected_players[['Player', 'Team', 'Position', 'Points', 'Assists', 'Rebounds', 'FG%']])
else:
st.info("👆 Select players from the grid above to see advanced analytics")
# Footer
st.markdown("---")
st.markdown(
"""
<div style='text-align: center'>
<p>🏀 Basketball Analytics Dashboard | Built with Streamlit & AG-Grid</p>
<p><em>Data is simulated for demonstration purposes</em></p>
</div>
""",
unsafe_allow_html=True
)
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?