An interactive GUI application for visualizing EPA (Estimated Performance Agility) distributions across FRC (FIRST Robotics Competition) team years. Built with Python and tkinter, it provides real-time exploration of team strength distributions with color-coded quartile displays.
- Per-Year Density Curves: Each year gets its own colored kernel density estimation (KDE) curve
- Interactive Year Selection: Checkbox controls to select/deselect years for analysis
- Normalized EPA Display: Automatically normalizes EPA values to 0-100% scale (percentage of max EPA)
- Percentile Lines: Optional vertical lines showing 25th (Q1), 50th (Q2/Median), and 75th (Q3) percentiles
- Real-Time Updates: Plot updates instantly as you change selections
- Data Trimming: Remove outliers by specifying top/bottom percentages (e.g., trim top 5% and bottom 5%)
- Select All/Deselect All: Quick buttons to manage year selections
- Always-Visible Quartiles: Color-coded spreadsheet showing Q1, Q2, Q3 values for each year
- Green (55+): Strong teams
- Yellow (27.5-55): Average teams
- Red (<27.5): Below average
- Hover Tooltips: Mouse over curves to see exact EPA%, percentile rank, and year information
- Per-Year Status: Real-time count of teams included when selections change
- Automatic Recalculation: Trimming and selections update statistics and plot instantly
- Full Source Code: Complete Python source for customization and cross-platform use
- Easy Setup: Simple pip install of dependencies
Requirements:
- Python 3.7+
- pandas
- numpy
- matplotlib
- scipy
- tkinter (included with Python)
Install dependencies:
pip install pandas numpy matplotlib scipyRun the application:
python EPADensityViewer.py- Load Data: The app automatically loads
all_team_years.jsonon startup - Select Years: Use checkboxes or "Select All"/"Deselect All" buttons
- Toggle Percentile Lines: Check boxes next to Q1, Q2, Q3 to show/hide them
- Adjust Trimming: Enter percentages to trim extreme values
- Review Quartiles: Check the spreadsheet on the left for per-year statistics
Comparing all-time trends:
- Click "Select All"
- Observe how distributions have changed across decades
Analyzing a single season:
- Uncheck all years except one
- Focus on Q1/Q2/Q3 values to understand that year's team spread
Removing outliers:
- Set "Trim top %": 5
- Set "Trim bottom %": 5
- View cleaner distribution with extreme teams excluded
Recent years comparison:
- Select only 2022, 2023, 2024, 2025, 2026
- Compare distribution shapes and see if EPA values are changing
The application expects all_team_years.json with this structure:
[
{
"team": 1234,
"year": 2022,
"epa_unitless": 1450.0,
"epa_norm": 1450.0,
"epa": 25.5
},
{
"team": 1235,
"year": 2022,
"epa_unitless": 1500.0,
"epa_norm": 1500.0,
"epa": 50.0
}
]Required fields: team, year, epa
Optional fields: epa_unitless, epa_norm
- EPA: Estimated Performance Agility - metric from Statbotics API measuring team strength
- KDE (Kernel Density Estimation): Smooth curve showing distribution shape
- Normalized EPA: Each year's EPA values scaled to 0-100% relative to that year's max
- Quartiles:
- Q1 (25th percentile): 25% of teams below this
- Q2 (50th percentile): Median - half above, half below
- Q3 (75th percentile): 75% of teams below this
- Trimming: Removes specified percentage from top/bottom before recalculating statistics
Left Panel:
- Year checkboxes with live team count
- Spreadsheet showing per-year Q1/Q2/Q3 values with color gradient
- Select All/Deselect All buttons
- Percentile line toggles (Q1, Q2, Q3)
- Trim percentage inputs (with 500ms debounce)
Right Panel:
- Interactive matplotlib plot showing density curves
- Legend with year labels and actual max EPA values
- Hover tooltips with detailed statistics
- Original EPA values from data
- Normalize to % of original year max
- Apply trimming (top/bottom percentages)
- Renormalize trimmed data to 0-100 scale
- Compute KDE on final normalized values
- X-axis always shows 0-100 for consistent comparison
- 55-100: Green (full saturation)
- 27.5-55: Yellow (transitional)
- 0-27.5: Red (low values)
- Efficient KDE computation with scipy
- Debounced trimming input (500ms) to prevent excessive updates
- Parallelizable density calculations
- Fast matplotlib rendering with FigureCanvasTkAgg
Use the companion script UpdateTeamYearsWeekly.py to fetch latest EPA data:
python UpdateTeamYearsWeekly.pyThis script:
- Fetches current year data from Statbotics API
- Merges with existing historical data
- Shows which teams were updated
- Displays EPA statistics for the year
- Uses efficient batch API calls (only 5 API calls vs 250+)
Example output:
EPA Team Years Weekly Update Script
Current Year: 2026
Timestamp: 2026-03-18 00:52:00
Fetching all 2026 team_years (approach 1: year param)...
Page 1: Retrieved 1000 records (total so far: 1000)
Page 2: Retrieved 1000 records (total so far: 2000)
Page 3: Retrieved 1000 records (total so far: 3000)
Page 4: Retrieved 763 records (total so far: 3763)
API calls made: 5
Fetched 3763 new records for 2026
TEAMS UPDATED IN 2026
Total teams with new data: 365
...
-
EPADensityViewer.py (485 lines): Main application with OOP design
- Single
EPADensityViewerclass handles UI, data, and visualization - Modular methods for data loading, plotting, and event handling
- Event-driven architecture with tkinter callbacks
- Single
-
UpdateTeamYearsWeekly.py (350 lines): Data update utility
- Efficient batch API fetching from Statbotics
- Incremental cache updates (no duplicates)
- Detailed progress reporting and statistics
- tkinter: Native GUI (included with Python)
- matplotlib: Plotting engine with tkinter backend
- pandas: Data manipulation and statistics
- numpy: Numerical computations
- scipy.stats.gaussian_kde: Kernel density estimation
Issue: Plot not updating after changing selections
- Ensure year checkboxes are properly tied to plot update callbacks
- Check that
update_plot()is called on all relevant events
Issue: Import errors
- Make sure all dependencies are installed:
pip install pandas numpy matplotlib scipy - Check Python version is 3.7 or higher:
python --version
- Startup: ~2 seconds (data loading and initial plot)
- Plot update: <100ms for interactive re-plotting
- Memory: ~50 MB for full dataset (48,000+ records)
- API update: ~30 seconds for complete year refresh (5 API calls)
[Specify your license - MIT, GPL, Apache 2.0, etc.]
Contributions welcome! Areas for enhancement:
- Export to PNG/PDF
- Side-by-side year comparisons
- Team filtering/search
- Statistical overlays (mean, stdev, etc.)
- Dark mode UI theme
EPA data sourced from Statbotics API
- Statbotics team for the EPA metric and API
- FIRST Robotics Community for the inspiration