scigantic-who
Query the WHO Global Health Observatory from Python: every indicator, from a public parquet mirror over DuckDB, with WHO's codes decoded. No API key, no download, no local database.
import scigantic_who as who
who.indicators("life expectancy")
df = who.data("WHOSIS_000001", countries=["JPN", "NGA"], dims={"SEX": who.BOTH_SEXES}, decode=True)
That runs against s3://scigantic-who over DuckDB's httpfs extension. Inside a Scigantic notebook with the archive mounted, the same call reads the mount instead.
Installation
$ pip install scigantic-who
Why this exists
WHO's GHO OData API serves one indicator per request and has no bulk download. Pulling the observatory means 3,099 requests and about 6 GB of JSON, and the API serves large indicators at roughly 1 MB/s. ghoclient (1.2.0, May 2026) is the existing Python client and is a good live wrapper: indicator search, country and region lists, one indicator at a time as a DataFrame. It has no bulk path, no decoding of the Dim1..Dim3 codes to labels, and no DuckDB.
This package adds the mirror. s3://scigantic-who/gho/ holds all 3,099 indicators (9.9 million observations) as parquet, rebuilt monthly, plus the lookup tables that turn every code into a label. One SQL statement over observations replaces thousands of API calls, and data() makes the pins a GHO query needs explicit. The live API is still here, in who.live, returning the same 25 columns so everything else works on a fresh pull too.
The shape of a GHO indicator, and the three mistakes this package prevents
A GHO indicator file holds every disaggregation and every spatial level at once: both sexes plus male plus female, every age group plus all ages, countries plus WHO regions plus income groups plus the world. Three things follow.
Pin the spatial level. data() keeps SpatialDimType = 'COUNTRY' by default; pass spatial_type=None to see regions and the world too.
Pin the disaggregations by type, not by slot. Dim1 is a sex code for one indicator and an age group for another. dims={"SEX": "SEX_BTSX"} matches whichever of Dim1..Dim3 carries SEX for this indicator.
who.data("MORT_100", countries="NGA", years=2021,
dims={"SEX": who.BOTH_SEXES, "AGEGROUP": "AGEGROUP_YEARS0-4"}, decode=True)
(MORT_100 is under-five deaths by cause; the cause sits in Dim3.) "All ages" alone is spelled three ways across indicators (AGEGROUP_AGEAll, AGEGROUP_YEARSALL, AGEGROUP_ALLAges, listed in who.ALL_AGES_CODES), so look the right one up in dimension_values for your indicator.
Decode on (dimension, code), never on code alone. The same code string can exist under different dimensions. decode=True and who.decode(df) join dimension_values on the pair.
Three more facts worth knowing. The GHE_* burden series (DALYs, YLLs, YLDs by cause) exist in the GHO only as global and GHE-region aggregates for 2000 and 2012; WHO's country-level Global Health Estimates are a separate download the API does not carry. Value is the published string (48.1 [45.4-50.8], or a category); NumericValue, Low and High are the numbers. And 612 of the 3,099 catalog entries have no data behind them; indicators() hides them unless you ask.
What is in the mirror
| path | what |
|---|---|
gho/indicators.parquet |
code, name, row_count, first and last year, number of spatial units, archived |
gho/dimensions.parquet |
the 202 dimension codes |
gho/dimension_values.parquet |
20,860 rows: every code in every dimension, with Title, ParentCode, ParentTitle |
gho/observations.parquet |
9,944,921 rows, every indicator, ordered by IndicatorCode |
gho/data/<IndicatorCode>.parquet |
the same rows, one file per indicator |
_MANIFEST.json |
when the mirror was pulled and the row counts; who.manifest() |
_MIRROR.txt |
WHO's terms and the attribution they require |
Raw SQL
con = who.connect() # views: indicators, dimensions, dimension_values, observations
con.sql("""
select o.SpatialDim, c.Title, o.TimeDim, o.NumericValue
from observations o
join dimension_values c on c.Dimension = 'COUNTRY' and c.Code = o.SpatialDim
where o.IndicatorCode = 'MDG_0000000001' and o.Dim1 = 'SEX_BTSX' and o.TimeDim = 2022
order by o.NumericValue desc limit 10
""").show()
who.query(sql) does the same and returns a DataFrame. observations.parquet is ordered by IndicatorCode, so a WHERE IndicatorCode = ... reads a few row groups, not the file.
Live API
fresh = who.live.fetch("WHOSIS_000001", odata_filter="SpatialDim eq 'JPN' and TimeDim ge 2015")
who.decode(fresh)
who.live.indicators() lists the catalog as WHO publishes it today, for spotting codes added since the last mirror run.
Where it reads from
In order: an explicit source= (a directory or an s3:// prefix), the SCIGANTIC_WHO_PATH environment variable, a Scigantic notebook mount ($SCIGANTIC_MOUNT_PATH, /mnt/archive, /home/jovyan/archive), then s3://scigantic-who.
Command line
$ scigantic-who search "under-five"
$ scigantic-who data MDG_0000000001 --country NGA --years 2010-2022 --dim SEX=SEX_BTSX --decode
$ scigantic-who query "select count(*) from observations"
$ scigantic-who info
Data terms
The data is WHO's. WHO's terms grant a royalty-free right to use, reproduce and distribute the datasets for public health purposes, require attribution in the form WHO, Global Health Observatory data, <year>, accessed <date> with acknowledgement of the reporting countries, and bar use in connection with the promotion of a commercial enterprise. The mirror changes storage format only; _MIRROR.txt carries the full notice. This package's own code is MIT-0.
Metadata
Release files for scigantic-who 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| scigantic_who-0.1.1.tar.gz | 19.4 kB | Without provenance | Details for scigantic_who-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| scigantic_who-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for scigantic_who-0.1.1-py3-none-any.whl |
Total release size: 36.3 kB
Release files / scigantic_who-0.1.1.tar.gz
| Download URL | scigantic_who-0.1.1.tar.gz |
|---|---|
| Size | 19.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
02b2ad96325adcbc1dffa1452ae8d0c5f99724d35ee9e6afea54fa4beed72528
|
|
BLAKE2b-256 checksum How to use checksums |
44acf6ff6ce3d4938eec954d6d0dd573e6f46ca3a6b351cb93e0a749570e3083
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / scigantic_who-0.1.1-py3-none-any.whl
| Download URL | scigantic_who-0.1.1-py3-none-any.whl |
|---|---|
| Size | 16.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ffda848f949966b69a4e39a73dacb4e816bdca15a8cdb0af0381944d67ad15b0
|
|
BLAKE2b-256 checksum How to use checksums |
0a50d20843e8233709c42f15bed3d8c819c78a24427ad4abfed69438f497c0c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Security for scigantic-who 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.