diff --git a/.gitignore b/.gitignore index 920958e..dbf043a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ .ipynb_checkpoints # Built Jupyter-Book documentation. -docs/_build \ No newline at end of file +docs/_build + +.vscode/settings.json diff --git a/docs/_toc.yml b/docs/_toc.yml index 76d3734..d8c8318 100644 --- a/docs/_toc.yml +++ b/docs/_toc.yml @@ -1,10 +1,12 @@ -- file: home +format: jb-article +root: home +sections: - file: examples sections: - - file: agg - - file: charts - - file: custom_taxes - - file: demo - - file: gini - - file: income_measures - - file: weighting + - file: agg + - file: charts + - file: custom_taxes + - file: demo + - file: gini + - file: income_measures + - file: weighting diff --git a/microdf/__init__.py b/microdf/__init__.py index 03d7165..3e9d327 100644 --- a/microdf/__init__.py +++ b/microdf/__init__.py @@ -44,6 +44,7 @@ deep_poverty_rate, poverty_gap, squared_poverty_gap, + deep_poverty_gap, ) from .style import AXIS_COLOR, DPI, GRID_COLOR, TITLE_COLOR, set_plot_style from .tax import mtr, tax_from_mtrs @@ -132,6 +133,7 @@ "deep_poverty_rate", "poverty_gap", "squared_poverty_gap", + "deep_poverty_gap", # style.py "AXIS_COLOR", "DPI", diff --git a/microdf/generic.py b/microdf/generic.py index b0906cf..a22e2b5 100644 --- a/microdf/generic.py +++ b/microdf/generic.py @@ -727,6 +727,22 @@ def poverty_gap(self, income: str, threshold: str) -> float: gaps = (threshold - income)[threshold > income] return gaps.sum() + @get_args_as_micro_series() + def deep_poverty_gap(self, income: str, threshold: str) -> float: + """Calculate deep poverty gap, i.e., the total gap between income and + half of poverty thresholds for all people in deep poverty. + + :param income: Column indicating income. + :type income: str + :param threshold: Column indicating threshold. + :type threshold: str + :return: Deep poverty gap. + :rtype: float + """ + deep_threshold = threshold / 2 + gaps = (deep_threshold - income)[deep_threshold > income] + return gaps.sum() + @get_args_as_micro_series() def squared_poverty_gap(self, income: str, threshold: str) -> float: """Calculate squared poverty gap, i.e., the total squared gap between diff --git a/microdf/poverty.py b/microdf/poverty.py index 6bc90f4..957ec0b 100644 --- a/microdf/poverty.py +++ b/microdf/poverty.py @@ -111,3 +111,27 @@ def squared_poverty_gap( if w is None: return sq_gap.sum() return (sq_gap * df[w]).sum() + + +def deep_poverty_gap( + df: pd.DataFrame, income: str, threshold: str, w: str = None +) -> float: + """Calculate deep poverty gap, i.e., the total gap between income and + halved poverty thresholds for all people in deep poverty. + + :param df: DataFrame with income, threshold, and possibly weight columns + for each household (data should represent households, not persons). + :type df: pd.DataFrame + :param income: Column indicating income. + :type income: str + :param threshold: Column indicating threshold. + :type threshold: str + :param w: Column indicating weight, defaults to None (unweighted). + :type w: str, optional + :return: Deep poverty gap. + :rtype: float + """ + gap = np.maximum((df[threshold] / 2) - df[income], 0) + if w is None: + return gap.sum() + return (gap * df[w]).sum() diff --git a/microdf/tests/test_poverty.py b/microdf/tests/test_poverty.py index f32ec20..68df0c9 100644 --- a/microdf/tests/test_poverty.py +++ b/microdf/tests/test_poverty.py @@ -53,7 +53,20 @@ def test_squared_poverty_gap(): # Weighted RES = 1 * (25 ** 2) + 2 * (10 ** 2) + 3 * (5 ** 2) assert np.allclose( - mdf.squared_poverty_gap(df, "income", "threshold", "weight"), - RES, + mdf.squared_poverty_gap(df, "income", "threshold", "weight"), RES, ) assert np.allclose(md.squared_poverty_gap("income", "threshold"), RES) + + +def test_deep_poverty_gap(): + # Unweighted + assert np.allclose( + mdf.deep_poverty_gap(df, "income", "threshold"), 17.5 + 5 + 0 + 0 + ) + # Weighted + RES = 17.5 * 1 + 5 * 2 + 0 * 3 + 0 * 4 + assert np.allclose( + mdf.deep_poverty_gap(df, "income", "threshold", "weight"), RES + ) + # Same in MicroDataFrame. + assert np.allclose(md.deep_poverty_gap("income", "threshold"), RES)