← All field notes football statistics null zero api

Keep Missing Football Statistics Different from Zero

A team that took zero shots and a team whose shot count is unknown should not occupy the same point in your chart. The match statistics contract uses nullable values so unavailable measurements can remain explicit. Converting all false-like values to zero makes charts smoother at the cost of inventing football observations.

Zero is an observation; null is a gap

A team that took zero shots and a team whose shot count is unknown should not occupy the same point in your chart. The match statistics contract uses nullable values so unavailable measurements can remain explicit. Converting all false-like values to zero makes charts smoother at the cost of inventing football observations.

This problem often enters through convenience code: value or 0, a default database value, or a chart library that cannot handle null. Decide your missing-data policy before calculating averages. Preserve the raw value, carry the coverage flag, and render a visible gap or pending state when the required observation is absent.

Choose the denominator deliberately

An average of observed values divides by the number of known values, not the number of fixtures requested. If two of five matches have no shot count, an observed average uses three values and reports that coverage. A season total has a different problem: adding the known values gives only a partial total, which should not be labelled complete.

The example computes an observed average while returning known and unknown counts. It includes zero in the known set and returns a null average when nothing is known. That result lets a chart show both the statistic and its denominator instead of hiding gaps behind a rounded number.

Run the reproducible example

This Python 3 example uses deliberately synthetic inputs, not a downloaded match or a forecast for a real fixture. Save it as example.py and run python3 example.py. It uses only the standard library and its assertions check the behavior discussed here. Replace the fixture inputs with validated API records when connecting your application; demo identifiers are not production lookup values.

Complete offline example python
def observed_average(values):
    known = [v for v in values if v is not None]
    if any(type(v) not in (int,float) or v < 0 for v in known):
        raise ValueError("expected nonnegative counts")
    return {"average":sum(known)/len(known) if known else None,
            "observed":len(known),"missing":len(values)-len(known)}

result = observed_average([0,None,12,6,None])
assert result == {"average":6.0,"observed":3,"missing":2}
assert observed_average([None,None])["average"] is None
assert observed_average([]) == {"average":None,"observed":0,"missing":0}
assert observed_average([0])["observed"] == 1
print(result)

Read the result

The example reports an average of six from three observed values and two missing values. Including the genuine zero is essential: dropping it would incorrectly produce an average of nine. An entirely unknown sample remains unknown.

Preserve the measurement definition

Read the intended field from statistics: shots_total, shots_on_target and shots_off_target are different measurements. Do not replace an unavailable total with a guessed sum unless the contract and your chosen method justify the relationship. Likewise, percentages and counts use different denominators and cannot be pooled with the same aggregation formula.

Check coverage.statistics_available and the selected team's ID before accessing the measurement. The teams array is not a promise that element zero is always the home team. Join it to the match participants by stable ID so a reordered response cannot swap the labels on your visualization.

Make partial data understandable

A tooltip such as “average across 3 of 5 observed matches” tells the reader more than an unexplained dash or a fabricated zero. Keep excluded match IDs available for debugging and distinguish unsupported coverage from supported-but-empty observations. These states call for different product decisions and should not collapse into a generic request failure.

Test known zero, all-null input, mixed values and an empty sample. Include a correction case where null later becomes a number, then recompute the aggregate from its retained inputs. This makes the calculation repeatable and avoids incrementally adding the same match twice when the source fills a gap.

Connect this to your project

Use this policy in the chart adapter and in your storage schema so unknown values survive the full pipeline. Inspect the match statistics schema before selecting metrics, and keep raw retained responses available for review when a customer questions a chart.

Read the endpoint guide → Check the football API → Use the Python integration guide →