Skip to content

Spending

Access Data

Have more questions? Contact Vivek Sakhrani


Overview

Atlas AI’s Spending indicator is a proxy for per capita daily purchasing power measured in 2011 Purchasing Power Parity (international dollars), the same currency unit that the World Bank and IMF use to publish national poverty estimates.

Calibration

We calibrate our models so that our Spending measurements are not only consistent with the World Bank’s PovcalNet macro-economic distributions for the corresponding year and region, but also reflective of accumulated wealth and well-being as denoted by the Asset Wealth Index, another of our data products which is unitless. The Spending indicator (USD per person per day) is therefore a single indicator that can be used as a proxy for purchasing power, poverty levels, and more generally economic well-being.

Population weighting

The spending value of each pixel (i.e., raster grid cell) is the mean purchasing power of the entire population allocated to that pixel. To calculate zonal statistics for Spending, therefore, it is necessary to always weight the Spending value of each grid cell within the zone of interest (ZOI) by the size of the population within that grid cell.

For example, if we want to know the average household spending for a ZOI comprising two adjacent grid cells, the first of which having a population of 10 and the second having a population of 50, we will need to weight the second grid cell’s Spending value five times more highly than the Spending value of the first grid cell to calculate the zone’s population-weighted mean household spending. The formula to use is:

\[\frac{\sum\limits_{i=1}^{n} spending_i * population_i}{\sum\limits_{i=1}^{n} population_i}\]

Poverty estimates

In addition to spending estimates, this dataset includes estimates of the population living below three poverty thresholds (determined by mean daily household spending at the pixel level, where the pixel value is the mean of the log-normally distributed Spending values of all households in that pixel).

The three poverty thresholds are:

  • $1.90/day (Sub-Saharan Africa only)
  • $3.20/day
  • $5.50/day

Methods

For details on methods, input data sources, validation, and background citations, please consult:
https://docs.atlasai.co/economic%20well-being/spending/

Version information

This data is available in a single version in raster format and is updated annually for several geographic regions.

Version 2022

Data type: Float64
No-data value: 1.79769e+308
Min/max values: 0.10207427461962328 (min), 121.44679747158806 (max)
Time horizon: 2003–2021
Spatial representation: Raster grid
Spatial resolution: 1km x 1km
Extent: 43 countries in Sub-Saharan Africa (Angola, Burundi, Benin, Burkina Faso, Botswana, Central African Republic, Côte d'Ivoire, Cameroon, Democratic Republic of the Congo, Republic of the Congo, Eritrea, Ethiopia, Gabon, Ghana, Guinea, The Gambia, Guinea-Bissau, Equatorial Guinea, Kenya, Liberia, Lesotho, Madagascar, Mali, Mozambique, Mauritania, Malawi, Namibia, Niger, Nigeria, Rwanda, Senegal, Sierra Leone, Somalia, South Sudan, Sudan, Eswatini, Chad, Togo, Tanzania, Uganda, South Africa, Zambia, Zimbabwe)
National boundaries source: geoBoundaries

Version 2022 (beta)

Data type: Float32
No-data value: 3.40282e+38
Index mean: 0
Min/max values: 1.5675678 (min), 61.190014 (max)
Time horizon: 2015–2021
Spatial representation: Raster grid
Spatial resolution: 1km x 1km
Extent: Six countries in South Asia (Bangladesh, Bhutan, India, Nepal, Pakistan, Sri Lanka)
National boundaries source: geoBoundaries and Natural Earth (India POV)

Code samples

Below are two code samples for, first, generating color maps to display Spending data in Python and, second, calculating zonal statistics (mean spending) for an area of interest in Python.

Generate Spending color maps

import numpy as np
from matplotlib import colors


def hex2color_map(name, hex_list):
    color_tuples = list(map(_parse_hex, hex_list))
    color_tuples_normalized = [
        (e[0] / 255.0, e[1] / 255.0, e[2] / 255.0, 255.0) for e in color_tuples
    ]
    cmap = normalized_color_tuples2cmap(color_tuples_normalized)
    save_cmap(name, cmap)


def save_cmap(name, cmap):
    cmap_vals = cmap(np.linspace(0, 1, 255))
    cmap_uint8 = (cmap_vals * 255).astype("uint8")

    np.save(name + "_rgba.npy", cmap_uint8)


def normalized_color_tuples2cmap(color_tuples_normalized, interpolate=True):
    if not interpolate:
        # No color interpolation:
        return colors.ListedColormap(color_tuples_normalized, "woooooooooooo")

    ### With color interpolation:
    nc = len(color_tuples_normalized)
    c = np.zeros((3, nc, 3))
    rgb = ["red", "green", "blue"]
    for idx, e in enumerate(color_tuples_normalized):
        for ii in range(3):
            c[ii, idx, :] = [float(idx) / float(nc - 1), e[ii], e[ii]]

    cdict = dict(zip(rgb, c))
    cmap = colors.LinearSegmentedColormap("woooooooooooo", cdict)
    return cmap


def _parse_hex(color_code):
    return (
        int(color_code[1:3], 16),
        int(color_code[3:5], 16),
        int(color_code[5:7], 16),
    )


hex2color_map(
    "AtlasAI_Spending",
    [
        "#fff7ec",
        "#fee8c8",
        "#fdd49e",
        "#fdbb84",
        "#fc8d59",
        "#ef6548",
        "#d7301f",
        "#b30000",
        "#7f0000",
    ],
)

Calculate zonal statistics

import rasterio as rio
import geopandas as gpd
from rasterstats import zonal_stats

# convert boundaries file crs to raster crs (epsg:3857) and save as boundaries-3857.gpkg
gdf = gpd.read_file('boundaries.geojson')
gdf.to_crs(3857).to_file('boundaries-3857.gpkg')

# population-weight spending by multiplying sp and pop pixel values and save as POP-weighted-SP_Country_YYYY.tif
with rio.open('SP_Country_YYYY.tif') as src:
    sp = src.read(masked=True)
    profile = src.profile

with rio.open('POP_Country_YYYY.tif') as src:
    pop = src.read(masked=True)

sp = sp.astype('float128') #<--prevent 'RuntimeWarning: overflow encountered in multiply'
pop_weighted_sp = sp * pop

with rio.open('POP-weighted-SP_Country_YYYY.tif', 'w', **profile) as dst:
    dst.write(pop_weighted_sp)

# calculate zonal statistics (sum) for population and spending
pop_weighted_sp_sum = zonal_stats("boundaries-3857.gpkg",
                                  "POP-weighted-SP_Country_YYYY.tif",
                                  stats='sum')

pop_sum = zonal_stats("boundaries-3857.gpkg",
                      "POP_Country_YYYY.tif",
                      stats='sum')

# calculate the (population weighted) mean spending by dividing the population-weighted spending sum by the population sum
pop_weighted_mean_sp = pop_weighted_sp_sum[0]['sum'] / pop_sum[0]['sum']