Skip to content

Adjustment Functions

Functions for calculating pre-allocation responsibility, capability, and Gini adjustments.

Relative Adjustments

calculate_relative_adjustment

fair_shares.library.utils.calculate_relative_adjustment

Python
calculate_relative_adjustment(
    values: TimeseriesDataFrame | Series,
    functional_form: str = "asinh",
    exponent: float = 1.0,
    inverse: bool = True,
    normalize: bool = True,
) -> TimeseriesDataFrame | Series

Calculate relative adjustment factors for allocations.

Applies a transformation to input values (e.g., GDP per capita, cumulative emissions) to produce adjustment factors. See docs/science/allocations.md for theoretical grounding.

When normalize=True (default), values are divided by their median before transformation. This ensures unit invariance: multiplying all inputs by a constant (e.g., converting USD to EUR) produces identical adjustment factors, because the constant cancels in the ratio x / median(x).

Parameters:

Name Type Description Default
values TimeseriesDataFrame | Series

Input values (GDP per capita, cumulative emissions, etc.)

required
functional_form str

Transformation to apply. Default is "asinh" (inverse hyperbolic sine). arcsinh itself is defined on the full real line and preserves sign, but the subsequent ** (sign * exponent) step produces NaN on negative inputs and inf on zero for non-integer exponents — see the Notes section for caller responsibilities. "power" is the legacy form; values are clamped to a small epsilon for numerical safety.

'asinh'
exponent float

Controls how aggressively the adjustment squashes the range. With inverse=True: exponent=1.0 gives 1/transform(x), exponent=2.0 gives 1/transform(x)², exponent=0.5 gives 1/sqrt(transform(x)). Greater exponent = more squashing.

1.0
inverse bool

If True (default), higher values -> lower relative adjustment factor. If False, higher values -> higher relative adjustment factor.

True
normalize bool

If True (default), divide values by their median before transformation to achieve unit invariance. Set to False for legacy behaviour or when inputs are already dimensionless.

True

Returns:

Type Description
Adjustment factors with same structure as input
Notes

This function operationalizes both the Polluter Pays Principle (via historical responsibility adjustments over the window prior to the allocation year) and the Ability to Pay Principle (via economic capability adjustments, from the allocation year onwards).

The arcsinh function itself is defined on the full real line and preserves sign (it is odd), so negative inputs map to negative transformed values and zero maps to zero. However, the subsequent exponentiation step arcsinh(x) ** (sign * exponent) is not closed over the reals: for a non-integer real exponent (the typical case, since inverse=True is the default), numpy does not promote to complex, so negative bases yield NaN and a zero base with a negative exponent yields inf. In other words, asinh applies no clamping — callers that may pass negative values (e.g. cumulative CO2 including LULUCF sinks) or exact zeros must handle the sign upstream, either by flooring inputs at a small positive value or by using an emission basis that cannot be negative (e.g. fossil-fuel-and-industry only). The power form clamps to a small epsilon (1e-10) for numerical safety with negative exponents.

Gini Adjustments

calculate_gini_adjusted_gdp

fair_shares.library.utils.calculate_gini_adjusted_gdp

Python
calculate_gini_adjusted_gdp(
    total_gdps: ndarray,
    gini_coefficients: ndarray,
    income_floor: float,
    total_populations: ndarray,
    max_adjustment: float = 0.8,
) -> ndarray

Gini-adjusted GDP (capability) using the GDR development threshold.

Implements an interpretation of the Greenhouse Development Rights (GDR) framework's capability metric for entitlement allocation. Note: GDR was originally designed for burden-sharing; fair-shares adapts its capability calculation for use in an entitlement context. Only income above a development threshold counts as "ability to pay" for climate action.

For each individual, their first income_floor of annual income is exempt — it's needed for basic human development. Only the excess above the floor contributes to national capability.

Mathematical Foundation

Income is modelled as log-normal, parameterised by the Gini coefficient \(G\):

\[ \sigma = \sqrt{2} \cdot \Phi^{-1}\!\left(\frac{1 + G}{2}\right) \qquad \mu = \ln(\text{mean}) - \frac{\sigma^2}{2} \]

The proportion of the population below the floor is:

\[ p = \Phi\!\left(\frac{\ln(\text{floor}) - \mu}{\sigma}\right) \]

The income share of people below the floor (Lorenz curve) is:

\[ L(p) = \Phi\!\left(\Phi^{-1}(p) - \sigma\right) \]

National capability (aggregate above-threshold income):

\[ \text{capability} = \underbrace{\text{GDP} \times (1 - L(p))}_{\text{income of above-threshold people}} \;-\; \underbrace{\text{floor} \times (1 - p) \times N}_{\text{their exempt income (up to floor)}} \]

Where \(N\) is the total population.

Effect of Inequality

When combined with an income floor, higher Gini always increases measured capability: inequality concentrates income in the upper tail, so more national income sits above the development threshold:

Scenario (floor = $7,500/yr) Gini 0.30 Gini 0.60 Direction
Rich (mean $20k) 63% of GDP 70% of GDP ↑ moderate
Poor (mean $3k) 1.6% of GDP 21.5% of GDP ↑ dramatic

For poor countries, inequality is the only mechanism that creates any above-threshold income at all. At perfect equality with mean $3k, nobody exceeds $7,500 so capability is near zero.

Parameters:

Name Type Description Default
total_gdps ndarray

Array of total GDP values for each country.

required
gini_coefficients ndarray

Array of Gini coefficients (0-1) for each country.

required
income_floor float

Development threshold in currency units per capita per year. Must use the price base of total_gdps. Baer (2013) gives $7,500 on a 2005 base and $8,500 on a 2010 base.

required
total_populations ndarray

Array of total population values for each country.

required
max_adjustment float

Maximum adjustment allowed as proportion of total GDP (default: 0.8). Prevents extreme reductions for countries with very high inequality. Note: with the GDR formula, higher inequality actually increases capability; extreme reductions are more likely for low-income, low-inequality countries where most people are near the threshold.

0.8

Returns:

Type Description
ndarray

Array of adjusted GDP values (capability) for each country.

apply_gini_adjustment

fair_shares.library.utils.apply_gini_adjustment

Python
apply_gini_adjustment(
    gdp_data: TimeseriesDataFrame | Series,
    population_data: TimeseriesDataFrame | Series,
    gini_lookup: dict[str, float],
    income_floor: float,
    max_gini_adjustment: float,
    group_level: str = "iso3c",
) -> TimeseriesDataFrame | Series

Apply Gini adjustment to GDP data for all groups.

Wrapper that applies calculate_gini_adjusted_gdp across all countries in a DataFrame or Series, handling MultiIndex grouping and Gini lookup.

Parameters:

Name Type Description Default
gdp_data TimeseriesDataFrame | Series

GDP data (DataFrame or Series)

required
population_data TimeseriesDataFrame | Series

Population data (DataFrame or Series)

required
gini_lookup dict[str, float]

Dictionary mapping country codes to Gini coefficients

required
income_floor float

Income floor value

required
max_gini_adjustment float

Maximum adjustment allowed as proportion of total GDP

required
group_level str

Level name for grouping (typically "iso3c")

'iso3c'

Returns:

Type Description
Gini-adjusted GDP data with same structure as input

create_gini_lookup_dict

fair_shares.library.utils.create_gini_lookup_dict

Python
create_gini_lookup_dict(
    gini_data_df: TimeseriesDataFrame,
) -> dict[str, float]

Create a simple lookup dict for Gini coefficients.

Assumes the common case: - Index: MultiIndex with level 'iso3c' (and possibly 'unit') - Column: 'gini'

Falls back to using 'iso3c' as a column if not found in the index.

Parameters:

Name Type Description Default
gini_data_df TimeseriesDataFrame

DataFrame containing Gini coefficient data

required

Returns:

Type Description
Dictionary mapping country codes to Gini coefficients

Deviation Constraints

apply_deviation_constraint

fair_shares.library.utils.apply_deviation_constraint

Python
apply_deviation_constraint(
    shares: TimeseriesDataFrame,
    population: TimeseriesDataFrame,
    max_deviation_sigma: float,
    group_level: str,
) -> TimeseriesDataFrame

Apply maximum deviation constraint from equal per capita baseline.

Constrains allocation shares to within a specified number of standard deviations from equal per capita distribution. See docs/science/allocations.md for theoretical basis.

Parameters:

Name Type Description Default
shares TimeseriesDataFrame

Current allocation shares

required
population TimeseriesDataFrame

Population data

required
max_deviation_sigma float

Maximum allowed deviation in standard deviations

required
group_level str

Level name for grouping (typically "iso3c")

required

Returns:

Type Description
Constrained allocation shares

See Also