Every NBA box score is really a data point in a much larger story. Points, rebounds, shooting percentages, and minutes played form time series that, when modeled correctly, can reveal patterns invisible to the naked eye. What began as an offhand conversation after a departmental seminar β 'could we actually predict how a player performs in their next game using the tools we teach in our machine learning course?' β turned into a semester-long research collaboration between faculty and students that touched nearly every stage of the applied ML pipeline: data collection, feature engineering, model selection, evaluation, and, just as importantly, learning to be honest about what a model can and cannot claim to know.
The motivation was partly pedagogical and partly curiosity-driven. Sports statistics are messy in exactly the ways real-world data tends to be messy: incomplete records, players who miss games unpredictably, rule changes across seasons, and performances that are influenced by countless factors a spreadsheet will never capture β a nagging injury, a coaching change, or simply a hot shooting night. That messiness made it an ideal teaching dataset. Students got to practice the unglamorous eighty percent of machine learning work β cleaning, validating, and understanding data β before ever touching a model.
Framing the Problem
The team narrowed the scope early. Rather than attempting to predict every statistical category a player might produce, the project focused on a single, well-defined target: points scored in the next game, using only information that would have been available before that game was played. This distinction mattered more than it might sound. A recurring theme in the early weeks of the project was catching subtle forms of data leakage β accidentally letting information from the future slip into training data. For example, using a player's final season average to predict a game that happened in October, before that average even existed, would silently inflate accuracy in a way that would never hold up in a real deployment.
To avoid this, the team built a strict chronological data pipeline. For every training example, only statistics from games prior to the prediction date were used to compute features. This is a standard practice in time-series modeling, but it is one that is easy to get wrong, and walking students through a live example of a 'too good to be true' result caused by leakage β and then finding and fixing it β turned out to be one of the more memorable moments of the semester.
Building the Feature Set
With a clean pipeline in place, attention turned to feature engineering. The final feature set included rolling averages over the last three, five, and ten games; a measure of shooting variance to capture streakiness; opponent defensive rating as a proxy for game difficulty; rest days since the previous game; and a simple indicator for back-to-back games, which are well known to correlate with fatigue-related dips in performance. Students also experimented with home-versus-away splits and month-of-season indicators to see whether performance trended differently early versus late in a season.
Not every feature helped. Several that seemed intuitively useful β such as a player's career average against a specific opponent β added noise rather than signal once the sample sizes were examined closely, since most players face any given opponent only a handful of times per season. Removing them actually improved generalization, a small but useful lesson in resisting the urge to add every plausible feature just because it is available.
Figure 1: A sample comparison between actual and model-predicted scoring output across a 15-game stretch.
Choosing and Comparing Models
The team deliberately compared models across a spectrum of complexity: a simple linear regression baseline, a random forest, a gradient-boosted tree ensemble, and a small feed-forward neural network. Each was evaluated using mean absolute error and root mean squared error on a held-out test set, with performance validated through rolling-window cross-validation rather than a single random train/test split β a more realistic way to simulate how the model would actually be used, forecasting forward in time rather than filling in random gaps in the past.
"I expected the neural network to win by a wide margin. Watching a carefully tuned gradient boosting model with a dozen features consistently beat it taught me more about the bias-variance trade-off than any lecture slide could have."
β [ Hemant Maurya ], [MCA 2024]
The gradient boosting model ultimately produced the lowest test error, edging out both the random forest and the neural network, which struggled somewhat given the relatively modest size of the per-player dataset. This outcome lined up with a broader pattern seen across applied machine learning: deep models tend to shine when data is abundant, while tree-based ensembles often remain the pragmatic choice for small-to-medium structured datasets β a distinction the course now uses as a running example.
Challenges Along the Way
Not everything went smoothly, and part of the value of documenting this project is being honest about the friction points. Missing data was a constant issue β players who were traded mid-season, rookies with limited game history, and games that were postponed or rescheduled all required careful handling rather than simple deletion, which would have introduced bias by systematically removing certain types of players from the dataset.
Another challenge was communicating uncertainty. Early versions of the project reported a single predicted point total, which made the model feel more confident than it actually was. The team later shifted to reporting a predicted range along with the point estimate, which better reflected the underlying variance in player performance and proved far more useful in discussion β a small design change that made a large difference in how trustworthy the output felt.
What the Results Actually Mean
It is worth being explicit about the limits of this kind of model. The system is not a crystal ball, and it was never intended to be. On any given night, a player can have a performance well outside their typical range for reasons no dataset captures. What the model is good at is estimating a reasonable expectation given the available context β which turns out to be genuinely useful for tasks like flagging when a player's actual output diverges sharply from their recent form, a signal that itself can prompt deeper investigation rather than serve as a final answer.
"The goal was never to build something that 'knows' what will happen. The goal was to give students hands-on experience with the full lifecycle of a forecasting problem β the kind of problem they'll encounter constantly outside the basketball context, whether it's demand forecasting, risk modeling, or anomaly detection."
β [Dr. Surendra Yadav, Professor], Department of Computer Science & Applications
Building the Evaluation Framework
One of the less glamorous but most instructive parts of the project was building a proper evaluation harness before optimizing any model. Students implemented a rolling backtest: for each week of a simulated season, the model was retrained only on data available up to that point, then evaluated on the following week's games. This walk-forward structure is standard in financial forecasting but rarely covered in introductory ML courses, and it forced students to think carefully about how a model's usefulness would actually degrade or improve as a season progressed, rather than measuring performance against a single static snapshot.
The backtest also surfaced an unexpected finding: model accuracy was noticeably lower in the first few weeks of a simulated season, when recent-form features had little history to draw on, and stabilized as the season went on. This is intuitive in hindsight, but seeing it appear directly in the evaluation curves gave students a concrete, data-driven appreciation for why 'small sample size' warnings are not just a statistical clichΓ© β they show up as measurable degradation in exactly the features a model depends on.
A Note on Responsible Use
Because the project touches on individual player performance, the team spent time discussing how a system like this should and should not be used. A model like this is reasonable as an internal decision-support tool or an educational exercise; it is a poor foundation for anything resembling high-stakes judgments about a specific person, given how much of athletic performance depends on factors no statistical model can observe. That distinction β between a model as one input among many versus a model as an automated verdict β recurred throughout the semester and mirrors a broader conversation happening across applied machine learning about the appropriate scope of algorithmic decision-making.
Looking Ahead
The team plans to extend the project in two directions next semester. The first is incorporating player-tracking-derived features, such as shot quality and defensive pressure, to move from predicting raw point totals toward predicting scoring efficiency β a more nuanced and arguably more useful target. The second is exploring model interpretability techniques, such as SHAP values, so that predictions come with an explanation of which factors drove them, rather than a single opaque number. Both directions align closely with current best practice in applied machine learning, where explainability is increasingly treated as a first-class requirement rather than an afterthought.
For the students involved, the project offered something a textbook exercise rarely can: a dataset with real stakes, real messiness, and a domain enthusiastic enough that debugging a model at midnight didn't feel like a chore. For the department, it is one more example of how a familiar, engaging subject can be used to teach rigorous technical skills that transfer far beyond it.