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.
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.
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.
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:00for 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.
Internal links
- Air quality indices explained — turning concentrations into bands
- How to download air quality station data in Python — AURN and EEA data in full
- ppb and µg/m³ mixed up — the fix guide for the unit error
- Air quality data gaps and negative values — capture and artefacts
- Satellite NO2 explained — the column is not a concentration
- PV yield off by a factor — the same family of errors in solar data
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.