The problem we all ignore
Football analysts continue to work with old data, as if the ball were a tennis ball from the 80s. Here's the thing: xG (expected goals) is not only a metric, it is a revolution that is being poorly implemented by the majority.
What is xG and why does it matter to you?
In a sentence: xG measures the probability of a shot ending in a goal. In a novel: combine angles, distance, defensive pressure and ball speed to paint the map of the quality of an opportunity. And here's the key: if your data model doesn't capture those variables accurately, you're giving away points to the competition.
The most common structural errors
First, flat boards. Saving each shot as a row without normalizing the position and context attributes creates redundancy and, worse, noise. Second, lack of timestamps. Without exact timestamps, the xG calculation loses the temporal dimension and becomes a static number. Third, use only distance. Do you think distance alone counts? Forget it, the defender's pressure and angle are as decisive as distance.
How to design a robust database
Start with an “Events” table containing ID, event type, timestamp, and reference to “Players”. Then, a “Shots” table that links to “Events” and adds fields: distance, angle, zone of the field, defensive pressure (number of opponents within 5 meters), ball speed. Finally, a table “xG_Calculated” that stores the value generated by the algorithm and the version of the model used.
This allows versioning of the algorithm and comparison of historical results without breaking data integrity. In addition, it facilitates queries such as: “How many xG did Messi generate in the 18-30 zone in the last 10 games?” without loading millions of unnecessary rows.
The calculation model: don't complicate things
Use logistic regression with standardized variables. Train with at least 10 thousand shots from different leagues to avoid bias. Add a “match context” factor (score status, minute) to refine the prediction. And, of course, validate with a test sample that has not seen the model.
Integration in the application
A REST API that returns JSON with the xG, eventid, and auxiliary metrics fields. Keep the answer light: just the essentials. Cache results per match to reduce load. And, above all, document each endpoint with clear examples, because without documentation the data science team will waste valuable time.
Practical example
Let's say you want to know the total xG of a match. Consult the “xG_Calculated” table filtering by MatchId. Add the values and that's it. If you want to compare with the historical average, join the “Matches” table and calculate the standard deviation. All this in less than a second if the database is well indexed.
The link you needed
To dive deeper into data architecture and see real examples, check out this resource on model data application xG. There you will find use cases and templates ready to copy.
Immediate action
Add the “defensive_pressure” column to your shooting table today and run a logistic regression test. You will see the difference in minutes, not weeks.