Air Pollutants and Their Units Explained

On this page

Problem statement

An air quality dataset arrives as a table of numbers labelled NO2, PM2.5, O3, with a unit column that says ugm-3, ppb, ug.m-3 (BAM) or nothing at all. The numbers are meaningless until three questions are answered: which substance, in which unit, averaged over which period. A nitrogen dioxide concentration of 41.6 is an annual mean over the legal limit if it is µg/m³ averaged over a year, a pleasant afternoon if it is an hourly value, and a different number altogether if it is parts per billion.

Every standard, every index and every map is defined in terms of all three. Getting any of them wrong produces a result that is plausible, publishable and off by a factor of two.

Quick answer

The pollutants that matter for most spatial work:

  • PM2.5 and PM10 — mass of particles below 2.5 or 10 µm per cubic metre of air, in µg/m³. Mass, not count, and measured by several methods that do not agree perfectly.
  • NO2 — nitrogen dioxide, mostly from combustion and above all from diesel traffic. µg/m³ in Europe, ppb in North America.
  • O3 — ozone, formed from other pollutants in sunlight; lowest next to traffic, where fresh NO destroys it.
import io, requests, pandas as pd

def defra(site, year=2023):
    """One UK AURN site-year: GMT, hour-ending, value/status/unit triplets."""
    text = requests.get(f"https://uk-air.defra.gov.uk/datastore/data_files/site_data/"
                        f"{site}_{year}.csv", timeout=120).text
    name = text.splitlines()[3].strip(",")
    d = pd.read_csv(io.StringIO(text), skiprows=4, low_memory=False)
    return name, d

WANT = {"Nitrogen dioxide": "NO2", "Ozone": "O3",
        "PM<sub>2.5</sub> particulate matter (Hourly measured)": "PM2.5",
        "PM<sub>10</sub> particulate matter (Hourly measured)": "PM10"}

for site in ("MY1", "KC1"):
    name, d = defra(site)
    print(name)
    for col, short in WANT.items():
        if col not in d:
            continue
        i = d.columns.get_loc(col)
        v = pd.to_numeric(d[col], errors="coerce")
        unit = d.iloc[:, i + 2].dropna().iloc[0]
        print(f"  {short:<6} annual mean {v.mean():5.1f} {unit:<16} "
              f"hourly max {v.max():6.1f}  data capture {v.notna().mean():.0%}")
London Marylebone Road
  NO2    annual mean  41.6 ugm-3            hourly max  145.5  data capture 95%
  O3     annual mean  29.1 ugm-3            hourly max  118.5  data capture 95%
  PM2.5  annual mean  11.6 ugm-3 (BAM)      hourly max   79.0  data capture 94%
  PM10   annual mean  18.3 ugm-3 (Ref.eq)   hourly max   90.8  data capture 94%
London N. Kensington
  NO2    annual mean  18.1 ugm-3            hourly max  106.9  data capture 99%
  O3     annual mean  55.0 ugm-3            hourly max  187.6  data capture 94%
  PM2.5  annual mean   7.7 ugm-3 (Ref.eq)   hourly max   98.0  data capture 100%
  PM10   annual mean  12.4 ugm-3 (FIDAS)    hourly max  112.6  data capture 100%

Marylebone Road is a kerbside site on one of London's busiest roads; North Kensington is an urban background site 4 km away. NO2 is more than twice as high at the kerb, and ozone almost half — the same chemistry seen from two sides.

Pollutant, method, unit, averaging period, capture

1. Identify the pollutant and the measurement method

PM2.5 by a reference gravimetric method, by a beta attenuation monitor (BAM) or by an optical instrument (FIDAS) are all "PM2.5" and can differ by 10% or more. The unit field often records the method; keep it.

2. Identify the unit

µg/m³ is a mass concentration; ppb is a volume mixing ratio. For gases, one converts to the other with the molecular weight and the temperature and pressure the standard assumes. For particles there is no ppb.

3. Identify the averaging period

Every limit value is defined for a period: one hour, eight hours (ozone), 24 hours, a calendar year. A value is only comparable with a limit defined over the same period.

4. Check data capture before averaging

An annual mean from a third of the year's hours is not an annual mean. EU rules require at least 90% of hours (allowing for maintenance) for a valid annual statistic; UK practice reports means below 75% capture with a flag.

5. Compare against the right standard

The WHO 2021 guidelines are health-based and strict (NO2 annual 10 µg/m³, PM2.5 annual 5). EU limit values are legal thresholds and looser (NO2 annual 40, PM2.5 annual 25, both tightening in 2030). A site can comply with one and exceed the other fourfold.

The three questions every air quality value needs answered before it can be compared with anything. a number becomes an air quality valuewhich pollutantand which methodwhich unitµg/m³ or ppbwhich period1 h, 8 h, 24 h, yearcomparablewith a standard of thesame kindMarylebone Road NO2 2023: 41.6 µg/m³ as an annual mean — about 22 in ppb spatialworkflow.io
A number with no pollutant, unit and period attached is not an air quality value.

Units and averaging periods on AURN data

The data used on this page

Hourly measurements from the UK's Automatic Urban and Rural Network, downloaded by the page's own code from DEFRA's site-data archive: London Bloomsbury (CLL2), London Westminster (HORS), London North Kensington (KC1), London Marylebone Road (MY1). The files are ratified data; the hour in each row is the end of the measurement hour, in GMT.

import requests

print("CLL2_2023.csv:", requests.head("https://uk-air.defra.gov.uk/datastore/data_files/site_data/CLL2_2023.csv", timeout=60, allow_redirects=True).status_code)
print("HORS_2023.csv:", requests.head("https://uk-air.defra.gov.uk/datastore/data_files/site_data/HORS_2023.csv", timeout=60, allow_redirects=True).status_code)
print("KC1_2023.csv:", requests.head("https://uk-air.defra.gov.uk/datastore/data_files/site_data/KC1_2023.csv", timeout=60, allow_redirects=True).status_code)
print("MY1_2023.csv:", requests.head("https://uk-air.defra.gov.uk/datastore/data_files/site_data/MY1_2023.csv", timeout=60, allow_redirects=True).status_code)
CLL2_2023.csv: 200
HORS_2023.csv: 200
KC1_2023.csv: 200
MY1_2023.csv: 200

Example 1 — ppb and µg/m³

MOLAR_MASS = {"NO2": 46.01, "O3": 48.00, "SO2": 64.07, "CO": 28.01}

def ppb_to_ugm3(ppb, gas, temp_c=20.0, pressure_hpa=1013.25):
    """Convert a volume mixing ratio to a mass concentration at given conditions."""
    molar_volume = 22.414 * (273.15 + temp_c) / 273.15 * 1013.25 / pressure_hpa  # L/mol
    return ppb * MOLAR_MASS[gas] / molar_volume

for gas in ("NO2", "O3"):
    print(f"1 ppb {gas} = {ppb_to_ugm3(1, gas):.3f} µg/m³ at 20 °C "
          f"(EU reference), {ppb_to_ugm3(1, gas, 25):.3f} at 25 °C (US)")
print(f"\nMarylebone Road NO2 annual mean 41.6 µg/m³ = {41.6 / ppb_to_ugm3(1, 'NO2'):.1f} ppb")
1 ppb NO2 = 1.913 µg/m³ at 20 °C (EU reference), 1.881 at 25 °C (US)
1 ppb O3 = 1.995 µg/m³ at 20 °C (EU reference), 1.962 at 25 °C (US)

Marylebone Road NO2 annual mean 41.6 µg/m³ = 21.7 ppb

A factor of about two for NO2, and a few percent more depending on which reference temperature the standard uses. Comparing a ppb value with a µg/m³ limit is the single most common unit error in air quality data.

Example 2 — the averaging period decides the verdict

name, d = defra("MY1")
no2 = pd.to_numeric(d["Nitrogen dioxide"], errors="coerce")
print(f"{name}, NO2 2023")
print(f"  annual mean: {no2.mean():.1f} µg/m³  (EU limit 40, WHO guideline 10)")
print(f"  hours above 200 µg/m³: {(no2 > 200).sum()}  (EU allows 18 a year)")
daily = no2.groupby(d["Date"]).mean()
print(f"  days with a 24-h mean above 25 µg/m³: {(daily > 25).sum()}  "
      f"(WHO: no more than 3–4 a year)")
London Marylebone Road, NO2 2023
  annual mean: 41.6 µg/m³  (EU limit 40, WHO guideline 10)
  hours above 200 µg/m³: 0  (EU allows 18 a year)
  days with a 24-h mean above 25 µg/m³: 313  (WHO: no more than 3–4 a year)

The same year of data complies with the EU hourly limit, exceeds the EU annual limit, and exceeds the WHO daily guideline on most days. "Does this site meet the standard" has no answer until the standard and its period are named.

Example 3 — method matters for particles

for site in ("MY1", "KC1", "CLL2", "HORS"):
    name, d = defra(site)
    col = "PM<sub>2.5</sub> particulate matter (Hourly measured)"
    if col in d:
        unit = d.iloc[:, d.columns.get_loc(col) + 2].dropna().iloc[0]
        v = pd.to_numeric(d[col], errors="coerce")
        print(f"{name:<28} {unit:<16} mean {v.mean():5.1f}  negative hours {(v < 0).sum()}")
London Marylebone Road       ugm-3 (BAM)      mean  11.6  negative hours 7
London N. Kensington         ugm-3 (Ref.eq)   mean   7.7  negative hours 0
London Bloomsbury            ugm-3 (Ref.eq)   mean   7.8  negative hours 0
London Westminster           ugm-3 (BAM)      mean   7.8  negative hours 159

Beta attenuation monitors report small negative values in clean air — an instrument artefact that is kept in ratified data because deleting it would bias the mean upwards. Optical and reference-equivalent instruments behave differently at low concentrations, which matters most at exactly the levels the WHO guideline is set at.

WHO 2021 guideline values and EU limit values for NO2, PM2.5, PM10 and ozone. health-based and legal, µg/m³pollutantperiodWHO 2021EU limitNO2year1040 (20 from 2030)NO21 hour—200, 18 times a yearPM2.5year525 (10 from 2030)PM10year1540PM1024 hours4550, 35 days a yearozone8 hours100120 (target value)a site can meet the EU limit and exceed the WHO guideline fourfoldAlways name the standard and its averaging period spatialworkflow.io
Two sets of numbers for the same pollutants: one health-based, one legal.

NO2 and ozone, mass and count, reference conditions

Why NO2 and ozone move in opposite directions

Traffic emits mostly nitric oxide (NO), which reacts with ozone to make NO2 and oxygen. Next to a busy road, fresh NO consumes the ozone; downwind and in the suburbs, sunlight drives the reaction back and ozone recovers. That is why the kerbside site has the highest NO2 and the lowest ozone, and why ozone is often worst in the countryside.

Why particle mass is not particle count

PM2.5 is a mass per volume, dominated by the largest particles in the range. Ultrafine particles from traffic are numerous and weigh almost nothing, so they barely register in PM2.5 — a site can have a modest PM2.5 and a very high particle number. The standards are written in mass because that is what the epidemiology used.

Why reference conditions differ

Gas concentrations in µg/m³ depend on the air's density, which depends on temperature and pressure. The EU fixes 20 °C and 101.3 kPa for gases; US conversions often use 25 °C. The difference is 1.7% — small against the factor of two between ppb and µg/m³, and still worth stating when comparing across continents.

Annual means of NO2, ozone and PM2.5 at the Marylebone Road kerbside site and the North Kensington background site in 2023. kerbside against background, µg/m³, 2023NO2, Marylebone Road41.6NO2, North Kensington18.1ozone, Marylebone Road29.1 — consumed by fresh NOozone, North Kensington55.0PM2.5, Marylebone Road11.6 (BAM)PM2.5, North Kensington7.7 (reference-equivalent)03060µg/m³NO2 up, ozone down at the kerb: the same chemistry seen from two sides spatialworkflow.io
Twice the NO2 and half the ozone at the kerb, four kilometres from the background site.

NOx, PM10, hour-ending labels and ratification

  • NOx is reported "as NO2" — the NO fraction converted with NO2's molecular weight — so NOx in µg/m³ is not NO + NO2 in their own units.
  • PM10 includes PM2.5; the coarse fraction is the difference.
  • Hour-ending timestamps: DEFRA labels each hour by its end, with 24:00 for midnight.
  • Ratified vs provisional data: recent months are provisional and can change when ratified.
  • Satellite NO2 is a column in mol/m², not a concentration — another unit, another guide.

FAQ

What is the difference between PM2.5 and PM10?

PM2.5 is the mass of particles smaller than 2.5 µm per cubic metre; PM10 includes everything below 10 µm, so it contains PM2.5.

How do I convert ppb to µg/m³?

Multiply by the molecular weight and divide by the molar volume at the reference temperature: 1 ppb NO2 is about 1.91 µg/m³ at 20 °C.

What are the WHO air quality guidelines?

Health-based values from 2021: annual NO2 10 µg/m³, annual PM2.5 5 µg/m³, among others. They are stricter than most legal limits.

What is the EU annual limit for NO2?

40 µg/m³ as an annual mean, falling to 20 µg/m³ from 2030.

Why are some PM2.5 values negative?

Instrument noise at low concentrations, mainly from beta attenuation monitors. Ratified data keeps them to avoid biasing the mean.

Why is ozone lower next to busy roads?

Nitric oxide from exhausts reacts with ozone, consuming it. Ozone recovers downwind.