perfect-postcode/pipeline/download/lsoa_population.py
Andras Schmelczer f59d01227b
Some checks failed
Build and publish Docker image / build-and-push (push) Failing after 15s
CI / Check (push) Failing after 1m58s
SPlit up
2026-06-12 21:51:37 +01:00

61 lines
1.9 KiB
Python

"""Download Census 2021 usual resident population by LSOA.
Source: NOMIS (ONS Census 2021 — TS001 dataset)
License: Open Government Licence v3.0
"""
import argparse
from pathlib import Path
import polars as pl
from pipeline.utils import ENGLAND_LSOA_COUNT_2021, download_nomis_csv
# NOMIS API: Census 2021 TS001 (usual residents) by LSOA 2021 (TYPE151)
# c2021_restype_3=0 selects "Total: All usual residents"
BASE_URL = "https://www.nomisweb.co.uk/api/v01/dataset/NM_2021_1.data.csv?date=latest&geography=TYPE151&measures=20100&c2021_restype_3=0&select=GEOGRAPHY_CODE,OBS_VALUE"
def download_and_convert(output_path: Path) -> None:
print("Downloading Census 2021 LSOA population from NOMIS...")
df = download_nomis_csv(BASE_URL)
print(f"Total rows: {df.height}")
result = df.rename(
{"GEOGRAPHY_CODE": "lsoa21", "OBS_VALUE": "population"}
).with_columns(
pl.col("population").cast(pl.UInt32),
)
# Filter to England only (E prefix)
result = result.filter(pl.col("lsoa21").str.starts_with("E"))
print(f"England LSOAs: {result.height}")
if result.height != ENGLAND_LSOA_COUNT_2021:
raise ValueError(
f"Expected {ENGLAND_LSOA_COUNT_2021} England LSOAs, "
f"got {result.height}: truncated NOMIS download?"
)
print(
f"Population range: {result['population'].min()} - {result['population'].max()}"
)
print(f"Mean population: {result['population'].mean():.0f}")
output_path.parent.mkdir(parents=True, exist_ok=True)
result.write_parquet(output_path, compression="zstd")
print(f"Saved to {output_path}")
def main() -> None:
parser = argparse.ArgumentParser(
description="Download Census 2021 population by LSOA"
)
parser.add_argument(
"--output", type=Path, required=True, help="Output parquet file path"
)
args = parser.parse_args()
download_and_convert(args.output)
if __name__ == "__main__":
main()