Skip to content

laser.core.demographics.spatialpops

laser.core.demographics.spatialpops

laser.core.demographics.spatialpops.distribute_population_skewed(tot_pop, num_nodes, frac_rural=0.3)

Calculate the population distribution across a number of nodes based on a total population, the number of nodes, and the fraction of the population assigned to rural nodes.

The function generates a list of node populations distributed according to a simple exponential random distribution, with adjustments to ensure the sum matches the total population and the specified fraction of rural population is respected.

Parameters:

Name Type Description Default
tot_pop int

The total population to be distributed across the nodes.

required
num_nodes int

The total number of nodes among which the population will be distributed.

required
frac_rural float

The fraction of the total population to be assigned to rural nodes (value between 0 and 1). Defaults to 0.3. The 0 node is the single urban node and has (1 - frac_rural) of the population.

0.3

Returns:

Type Description
list[int]

A list of integers representing the population at each node. The sum of the list equals tot_pop.

Notes
  • The population distribution is weighted using an exponential random distribution to create heterogeneity among node populations.
  • Adjustments are made to ensure the total fraction assigned to rural nodes adheres to frac_rural.
Examples

from laser.core.demographics.spatialpops import distribute_population_skewed np.random.seed(42) # For reproducibility tot_pop = 1000 num_nodes = 5 frac_rural = 0.3 distribute_population_skewed(tot_pop, num_nodes, frac_rural) [700, 154, 64, 54, 28]

tot_pop = 500 num_nodes = 3 frac_rural = 0.4 distribute_population_skewed(tot_pop, num_nodes, frac_rural) [300, 136, 64]

laser.core.demographics.spatialpops.distribute_population_tapered(tot_pop, num_nodes)

Distribute a total population heterogeneously across a given number of nodes.

The distribution follows a logarithmic-like decay pattern where the first node (Node 0) receives the largest share of the population, approximately half the total population. Subsequent nodes receive progressively smaller populations, ensuring that even the smallest node has a non-negligible share.

The function ensures the sum of the distributed populations matches the tot_pop exactly by adjusting the largest node if rounding introduces discrepancies.

Parameters:

Name Type Description Default
tot_pop int

The total population to distribute. Must be a positive integer.

required
num_nodes int

The number of nodes to distribute the population across. Must be a positive integer.

required

Returns:

Name Type Description
ndarray ndarray

A 1D array of integers where each element represents the population assigned to a specific node. The length of the array is equal to num_nodes.

Raises:

Type Description
ValueError

If tot_pop or num_nodes is not greater than 0.

Notes
  • The logarithmic-like distribution ensures that Node 0 has the highest population, and subsequent nodes receive progressively smaller proportions.
  • The function guarantees that the sum of the returned array equals tot_pop.

Examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Distribute a total population of 1000 across 5 nodes::

    >>> from laser.core.demographics.spatialpops import distribute_population_tapered
    >>> distribute_population_tapered(1000, 5)
    array([500, 250, 125, 75, 50])

Distribute a total population of 1200 across 3 nodes::

    >>> distribute_population_tapered(1200, 3)
    array([600, 400, 200])

Handling a small total population with more nodes::

    >>> distribute_population_tapered(10, 4)
    array([5, 3, 2, 0])

Ensuring the distribution adds up to the total population::

    >>> pop = distribute_population_tapered(1000, 5)
    >>> pop.sum()
    1000