microdf-python 0.4.5__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
microdf/__init__.py CHANGED
@@ -1,156 +1,10 @@
1
- from .agg import agg, combine_base_reform, pctchg_base_reform
2
- from .concat import concat
3
- from .constants import (
4
- BENS,
5
- ECI_REMOVE_COLS,
6
- HOUSING_CASH_SHARE,
7
- MCAID_CASH_SHARE,
8
- MCARE_CASH_SHARE,
9
- MED_BENS,
10
- OTHER_CASH_SHARE,
11
- SNAP_CASH_SHARE,
12
- SSI_CASH_SHARE,
13
- TANF_CASH_SHARE,
14
- VET_CASH_SHARE,
15
- WIC_CASH_SHARE,
16
- )
17
- from .custom_taxes import (
18
- CARBON_TAX_INCIDENCE,
19
- FTT_INCIDENCE,
20
- VAT_INCIDENCE,
21
- add_carbon_tax,
22
- add_custom_tax,
23
- add_ftt,
24
- add_vat,
25
- )
26
- from .income_measures import cash_income, market_income, tpc_eci
27
- from .inequality import (
28
- bottom_50_pct_share,
29
- bottom_x_pct_share,
30
- gini,
31
- t10_b50,
32
- top_0_1_pct_share,
33
- top_1_pct_share,
34
- top_10_pct_share,
35
- top_50_pct_share,
36
- top_x_pct_share,
37
- )
38
- from .io import read_stata_zip
39
1
  from .microdataframe import MicroDataFrame, MicroDataFrameGroupBy
40
2
  from .microseries import MicroSeries, MicroSeriesGroupBy
41
- from .poverty import (
42
- deep_poverty_gap,
43
- deep_poverty_rate,
44
- fpl,
45
- poverty_gap,
46
- poverty_rate,
47
- squared_poverty_gap,
48
- )
49
- from .tax import mtr, tax_from_mtrs
50
- from .taxcalc import (
51
- add_weighted_metrics,
52
- calc_df,
53
- n65,
54
- recalculate,
55
- static_baseline_calc,
56
- )
57
- from .ubi import ubi_or_bens
58
- from .utils import (
59
- cartesian_product,
60
- dedup_list,
61
- flatten,
62
- listify,
63
- ordinal_label,
64
- )
65
- from .weighted import (
66
- add_weighted_quantiles,
67
- quantile_chg,
68
- weight,
69
- weighted_mean,
70
- weighted_median,
71
- weighted_quantile,
72
- weighted_sum,
73
- )
74
3
 
75
4
  name = "microdf"
76
5
  __version__ = "0.1.0"
77
6
 
78
7
  __all__ = [
79
- # agg.py
80
- "combine_base_reform",
81
- "pctchg_base_reform",
82
- "agg",
83
- # concat.py
84
- "concat",
85
- # constants.py
86
- "BENS",
87
- "ECI_REMOVE_COLS",
88
- "HOUSING_CASH_SHARE",
89
- "MCAID_CASH_SHARE",
90
- "MCARE_CASH_SHARE",
91
- "MED_BENS",
92
- "OTHER_CASH_SHARE",
93
- "SNAP_CASH_SHARE",
94
- "SSI_CASH_SHARE",
95
- "TANF_CASH_SHARE",
96
- "VET_CASH_SHARE",
97
- "WIC_CASH_SHARE",
98
- # custom_taxes.py
99
- "CARBON_TAX_INCIDENCE",
100
- "FTT_INCIDENCE",
101
- "VAT_INCIDENCE",
102
- "add_custom_tax",
103
- "add_vat",
104
- "add_carbon_tax",
105
- "add_ftt",
106
- # income_measures.py
107
- "cash_income",
108
- "tpc_eci",
109
- "market_income",
110
- # inequality.py
111
- "gini",
112
- "top_x_pct_share",
113
- "bottom_x_pct_share",
114
- "bottom_50_pct_share",
115
- "top_10_pct_share",
116
- "top_1_pct_share",
117
- "top_0_1_pct_share",
118
- "top_50_pct_share",
119
- "t10_b50",
120
- # io.py
121
- "read_stata_zip",
122
- # poverty.py
123
- "fpl",
124
- "poverty_rate",
125
- "deep_poverty_rate",
126
- "poverty_gap",
127
- "squared_poverty_gap",
128
- "deep_poverty_gap",
129
- # tax.py
130
- "mtr",
131
- "tax_from_mtrs",
132
- # taxcalc.py
133
- "static_baseline_calc",
134
- "add_weighted_metrics",
135
- "n65",
136
- "calc_df",
137
- "recalculate",
138
- # ubi.py
139
- "ubi_or_bens",
140
- # utils.py
141
- "ordinal_label",
142
- "dedup_list",
143
- "listify",
144
- "flatten",
145
- "cartesian_product",
146
- # weighted.py
147
- "weight",
148
- "weighted_sum",
149
- "weighted_mean",
150
- "weighted_quantile",
151
- "weighted_median",
152
- "add_weighted_quantiles",
153
- "quantile_chg",
154
8
  # microseries.py
155
9
  "MicroSeries",
156
10
  "MicroSeriesGroupBy",
@@ -105,24 +105,6 @@ def test_multiple_groupby() -> None:
105
105
  assert (df.groupby(["x", "y"]).z.sum() == np.array([5, 6])).all()
106
106
 
107
107
 
108
- def test_concat() -> None:
109
- df1 = mdf.MicroDataFrame({"x": [1, 2]}, weights=[3, 4])
110
- df2 = mdf.MicroDataFrame({"y": [5, 6]}, weights=[7, 8])
111
- # Verify that pd.concat returns DataFrame (probably no way to fix this).
112
- pd_long = pd.concat([df1, df2])
113
- assert isinstance(pd_long, pd.DataFrame)
114
- assert not isinstance(pd_long, mdf.MicroDataFrame)
115
- # Verify that mdf.concat works.
116
- mdf_long = mdf.concat([df1, df2])
117
- assert isinstance(mdf_long, mdf.MicroDataFrame)
118
- # Weights should be preserved.
119
- assert mdf_long.weights.equals(pd.concat([df1.weights, df2.weights]))
120
- # Verify it works horizontally too (take the first set of weights).
121
- mdf_wide = mdf.concat([df1, df2], axis=1)
122
- assert isinstance(mdf_wide, mdf.MicroDataFrame)
123
- assert mdf_wide.weights.equals(df1.weights)
124
-
125
-
126
108
  def test_set_index() -> None:
127
109
  d = mdf.MicroDataFrame(dict(x=[1, 2, 3]), weights=[4, 5, 6])
128
110
  assert d.x.__class__ == MicroSeries
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: microdf-python
3
+ Version: 1.0.0
4
+ Summary: Weighted pandas DataFrames and Series for survey microdata
5
+ Author-email: Max Ghenis <max@ubicenter.org>
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: numpy
11
+ Requires-Dist: pandas
12
+ Provides-Extra: dev
13
+ Requires-Dist: codecov; extra == "dev"
14
+ Requires-Dist: flake8; extra == "dev"
15
+ Requires-Dist: flake8-pyproject; extra == "dev"
16
+ Requires-Dist: black; extra == "dev"
17
+ Requires-Dist: docformatter; extra == "dev"
18
+ Requires-Dist: isort; extra == "dev"
19
+ Requires-Dist: linecheck; extra == "dev"
20
+ Requires-Dist: pytest; extra == "dev"
21
+ Requires-Dist: pytest-cov; extra == "dev"
22
+ Requires-Dist: setuptools; extra == "dev"
23
+ Provides-Extra: docs
24
+ Requires-Dist: jupyter_book; extra == "docs"
25
+ Dynamic: license-file
26
+
27
+ [![Build](https://github.com/PolicyEngine/microdf/workflows/Pull%20request/badge.svg)](https://github.com/PolicyEngine/microdf/actions)
28
+ [![Codecov](https://codecov.io/gh/PolicyEngine/microdf/branch/master/graph/badge.svg)](https://codecov.io/gh/PolicyEngine/microdf)
29
+
30
+ # microdf
31
+ Weighted pandas DataFrames and Series for survey microdata analysis.
32
+
33
+ ## Overview
34
+ microdf provides `MicroDataFrame` and `MicroSeries` classes that extend pandas functionality with integrated weighting support, essential for accurate survey data analysis.
35
+
36
+ ## Key Features
37
+ - **MicroDataFrame**: A pandas DataFrame with an integrated weight column
38
+ - **MicroSeries**: A pandas Series with integrated weights
39
+ - **Weighted operations**: All aggregations (sum, mean, median, etc.) automatically use weights
40
+ - **Inequality metrics**: Built-in Gini coefficient calculation
41
+ - **Poverty analysis**: Integrated poverty rate and gap calculations
42
+
43
+ ## Installation
44
+ Install with:
45
+
46
+ pip install microdf-python
47
+
48
+ Or for development:
49
+
50
+ pip install git+https://github.com/PolicyEngine/microdf.git
51
+
52
+ ## Usage
53
+ ```python
54
+ import microdf as mdf
55
+ import pandas as pd
56
+
57
+ # Create sample data with weights
58
+ df = pd.DataFrame({
59
+ 'income': [10_000, 20_000, 30_000, 40_000, 50_000],
60
+ 'weights': [1, 2, 3, 2, 1]
61
+ })
62
+
63
+ # Create a MicroDataFrame
64
+ mdf_df = mdf.MicroDataFrame(df, weights='weights')
65
+
66
+ # All operations are weight-aware
67
+ print(mdf_df.income.mean()) # Weighted mean
68
+ print(mdf_df.income.gini()) # Gini coefficient
69
+ ```
70
+
71
+ ## Questions
72
+ Contact the maintainer, Max Ghenis (max@policyengine.org).
73
+
74
+ ## Citation
75
+ You may cite the source of your analysis as "microdf release #.#.#, author's calculations."
@@ -0,0 +1,10 @@
1
+ microdf/__init__.py,sha256=ldRvhE8t4cD7qlxQ5yLndMnWEUYpDw4m-jCDV_psXkY,319
2
+ microdf/microdataframe.py,sha256=5jYQ76PYjndACJO6hsJCaCL0g0clAsQbWltVcPtJ7yU,18202
3
+ microdf/microseries.py,sha256=URFT9bqry5Q_qezl3ge9lW8FSbQ6qg0XRaFQwN0IkwQ,22130
4
+ microdf/tests/conftest.py,sha256=u-EMyX1-u_nM-YO0RJYCzYHQDXxUI2WQE6GkyJlErqg,150
5
+ microdf/tests/test_microseries_dataframe.py,sha256=QSxdJ0ZfXO1XZgER9LRqDt5SGpVGyME03gefixH6spk,8855
6
+ microdf_python-1.0.0.dist-info/licenses/LICENSE,sha256=uPs-ASYnzlldpf2z8jeRgQFeEH3FLhSuX0rw0OKWoDU,1067
7
+ microdf_python-1.0.0.dist-info/METADATA,sha256=mC0MB2zQwVaqrPXfmh9mIc1KX5lmSO1zS9ilCvZQQg0,2483
8
+ microdf_python-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ microdf_python-1.0.0.dist-info/top_level.txt,sha256=T2WFPTygQQMdS3GF8YpZ12DKfMGrspbZ3r7z-e3KfiM,8
10
+ microdf_python-1.0.0.dist-info/RECORD,,
microdf/_optional.py DELETED
@@ -1,85 +0,0 @@
1
- import distutils.version
2
- import importlib
3
- import types
4
- import warnings
5
- from typing import Optional, Union
6
-
7
- # Adapted from:
8
- # https://github.com/pandas-dev/pandas/blob/master/pandas/compat/_optional.py
9
-
10
- VERSIONS = {
11
- "taxcalc": "2.0.0",
12
- }
13
-
14
-
15
- def _get_version(module: types.ModuleType) -> str:
16
- """
17
-
18
- :param module: types.ModuleType:
19
- :param module: types.ModuleType:
20
-
21
- """
22
- version = getattr(module, "__version__", None)
23
- if version is None:
24
- # xlrd uses a capitalized attribute name
25
- version = getattr(module, "__VERSION__", None)
26
-
27
- if version is None:
28
- raise ImportError(f"Can't determine version for {module.__name__}")
29
- return version
30
-
31
-
32
- def import_optional_dependency(
33
- name: str,
34
- extra: Optional[str] = "",
35
- raise_on_missing: Optional[bool] = True,
36
- on_version: Optional[str] = "raise",
37
- ) -> Union[types.ModuleType, None]:
38
- """Import an optional dependency. By default, if a dependency is missing an
39
- ImportError with a nice message will be raised. If a dependency is present,
40
- but too old, we raise.
41
-
42
- :param name: The module name. This should be top-level only, so that the
43
- version may be checked.
44
- :type name: str
45
- :param extra: Additional text to include in the ImportError message.
46
- :type extra: str
47
- :param raise_on_missing: Whether to raise if the optional dependency is
48
- not found. When False and the module is not present, None is returned.
49
- :type raise_on_missing: bool, default True
50
- :param on_version: What to do when a dependency's version is too old.
51
- * raise : Raise an ImportError
52
- * warn : Warn that the version is too old. Returns None
53
- * ignore: Return the module, even if the version is too old.
54
- It's expected that users validate the version locally when
55
- :type on_version: str {'raise', 'warn'}
56
- """
57
- msg = (
58
- f"Missing optional dependency '{name}'. {extra} "
59
- f"Use pip or conda to install {name}."
60
- )
61
- try:
62
- module = importlib.import_module(name)
63
- except ImportError:
64
- if raise_on_missing:
65
- raise ImportError(msg) from None
66
- else:
67
- return None
68
-
69
- minimum_version = VERSIONS.get(name)
70
- if minimum_version:
71
- version = _get_version(module)
72
- if distutils.version.LooseVersion(version) < minimum_version:
73
- assert on_version in {"warn", "raise", "ignore"}
74
- msg = (
75
- f"microdf requires version '{minimum_version}' or newer of "
76
- f"'{name}' "
77
- f"(version '{version}' currently installed)."
78
- )
79
- if on_version == "warn":
80
- warnings.warn(msg, UserWarning)
81
- return None
82
- elif on_version == "raise":
83
- raise ImportError(msg)
84
-
85
- return module
microdf/agg.py DELETED
@@ -1,90 +0,0 @@
1
- from typing import Optional
2
-
3
- import pandas as pd
4
-
5
- import microdf as mdf
6
-
7
-
8
- def combine_base_reform(
9
- base: pd.DataFrame,
10
- reform: pd.DataFrame,
11
- base_cols: Optional[list] = None,
12
- cols: Optional[list] = None,
13
- reform_cols: Optional[list] = None,
14
- ) -> pd.DataFrame:
15
- """Combine base and reform with certain columns.
16
-
17
- :param base: Base DataFrame. Index must match reform.
18
- :type base: pd.DataFrame
19
- :param reform: Reform DataFrame. Index must match base.
20
- :type reform: pd.DataFrame
21
- :param base_cols: Columns in base to keep.
22
- :type base_cols: list, optional
23
- :param cols: Columns to keep from both base and reform.
24
- :type cols: list, optional
25
- :param reform_cols: Columns in reform to keep.
26
- :type reform_cols: list, optional
27
- :returns: DataFrame with columns for base ("_base") and reform ("_reform").
28
- :rtype: pd.DataFrame
29
- """
30
- all_base_cols = mdf.listify([base_cols] + [cols])
31
- all_reform_cols = mdf.listify([reform_cols] + [cols])
32
- return base[all_base_cols].join(
33
- reform[all_reform_cols], lsuffix="_base", rsuffix="_reform"
34
- )
35
-
36
-
37
- def pctchg_base_reform(combined: pd.DataFrame, metric: str) -> pd.Series:
38
- """Calculates the percentage change in a metric for a combined dataset.
39
-
40
- :param combined: Combined DataFrame with _base and _reform columns.
41
- :type combined: pd.DataFrame
42
- :param metric: String of the column to calculate the difference. Must exist
43
- as metric_m_base and metric_m_reform in combined.
44
- :type metric: str
45
- :returns: Series with percentage change.
46
- :rtype: pd.Series
47
- """
48
- return combined[metric + "_m_reform"] / combined[metric + "_m_base"] - 1
49
-
50
-
51
- def agg(
52
- base: pd.DataFrame,
53
- reform: pd.DataFrame,
54
- groupby: str,
55
- metrics: list,
56
- base_metrics: Optional[list] = None,
57
- reform_metrics: Optional[list] = None,
58
- ) -> pd.DataFrame:
59
- """Aggregates differences between base and reform.
60
-
61
- :param base: Base DataFrame. Index must match reform.
62
- :type base: pd.DataFrame
63
- :param reform: Reform DataFrame. Index must match base.
64
- :type reform: pd.DataFrame
65
- :param groupby: Variable in base to group on.
66
- :type groupby: str
67
- :param metrics: List of variables to agg and calculate the % change of.
68
- These should have associated weighted columns ending in _m in base and
69
- reform.
70
- :type metrics: list
71
- :param base_metrics: List of variables from base to sum.
72
- :type base_metrics: Optional[list]
73
- :param reform_metrics: List of variables from reform to sum.
74
- :type reform_metrics: Optional[list]
75
- :returns: DataFrame with groupby and metrics, and _pctchg metrics.
76
- :rtype: pd.DataFrame
77
- """
78
- metrics = mdf.listify(metrics)
79
- metrics_m = [i + "_m" for i in metrics]
80
- combined = combine_base_reform(
81
- base,
82
- reform,
83
- base_cols=mdf.listify([groupby, base_metrics]),
84
- cols=mdf.listify(metrics_m),
85
- reform_cols=mdf.listify(reform_metrics),
86
- )
87
- grouped = combined.groupby(groupby).sum()
88
- for metric in metrics:
89
- grouped[metric + "_pctchg"] = pctchg_base_reform(grouped, metric)
90
- return grouped
microdf/concat.py DELETED
@@ -1,29 +0,0 @@
1
- import inspect
2
-
3
- import pandas as pd
4
-
5
- import microdf as mdf
6
- from microdf.microdataframe import MicroDataFrame
7
-
8
-
9
- def concat(*args, **kwargs) -> "MicroDataFrame":
10
- """Concatenates MicroDataFrame objects, preserving weights. If
11
- concatenating horizontally, the first set of weights are used. All args and
12
- kwargs are passed to pd.concat.
13
-
14
- :return: MicroDataFrame with concatenated weights.
15
- :rtype: mdf.MicroDataFrame
16
- """
17
- # Extract args with respect to pd.concat.
18
- pd_args = inspect.getcallargs(pd.concat, *args, **kwargs)
19
- objs = pd_args["objs"]
20
- axis = pd_args["axis"]
21
- # Create result, starting with pd.concat.
22
- res = mdf.MicroDataFrame(pd.concat(*args, **kwargs))
23
- # Assign weights depending on axis.
24
- if axis == 0:
25
- res.weights = pd.concat([obj.weights for obj in objs])
26
- else:
27
- # If concatenating horizontally, use the first set of weights.
28
- res.weights = objs[0].weights
29
- return res
microdf/constants.py DELETED
@@ -1,40 +0,0 @@
1
- # Constants for share of each benefit that is cash.
2
- HOUSING_CASH_SHARE = 0.0
3
- MCAID_CASH_SHARE = 0.0
4
- MCARE_CASH_SHARE = 0.0
5
- # https://github.com/open-source-economics/taxdata/issues/148
6
- # https://docs.google.com/spreadsheets/d/1g_YdFd5idgLL764G0pZBiBnIlnCBGyxBmapXCOZ1OV4
7
- OTHER_CASH_SHARE = 0.35
8
- SNAP_CASH_SHARE = 0.0
9
- SSI_CASH_SHARE = 1.0
10
- TANF_CASH_SHARE = 0.25
11
- # https://github.com/open-source-economics/C-TAM/issues/62.
12
- VET_CASH_SHARE = 0.48
13
- WIC_CASH_SHARE = 0.0
14
-
15
- # Columns to remove from expanded_income to approximate TPC's Expanded Cash
16
- # Income.
17
- ECI_REMOVE_COLS = [
18
- "wic_ben",
19
- "housing_ben",
20
- "vet_ben",
21
- "mcare_ben",
22
- "mcaid_ben",
23
- ]
24
-
25
- # Benefits.
26
- BENS = [
27
- "housing_ben",
28
- "mcaid_ben",
29
- "mcare_ben",
30
- "vet_ben",
31
- "other_ben",
32
- "snap_ben",
33
- "ssi_ben",
34
- "tanf_ben",
35
- "wic_ben",
36
- "e02400", # Social Security (OASDI).
37
- "e02300", # Unemployment insurance.
38
- ]
39
-
40
- MED_BENS = ["mcaid_ben", "mcare_ben", "vet_ben"]
microdf/custom_taxes.py DELETED
@@ -1,174 +0,0 @@
1
- """Functions and data for estimating taxes outside the income tax system.
2
-
3
- Examples include value added tax, financial transaction tax, and carbon tax.
4
- """
5
-
6
- from typing import Optional
7
-
8
- import numpy as np
9
- import pandas as pd
10
-
11
- import microdf as mdf
12
-
13
- # Source:
14
- # https://www.taxpolicycenter.org/briefing-book/who-would-bear-burden-vat
15
- VAT_INCIDENCE = pd.Series(
16
- index=[-1, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 99.9],
17
- data=[3.9, 3.9, 3.6, 3.6, 3.6, 3.6, 3.6, 3.4, 3.4, 3.2, 2.8, 2.5, 2.5],
18
- )
19
- VAT_INCIDENCE /= 100
20
-
21
- # Source: Table 5 in
22
- # https://www.treasury.gov/resource-center/tax-policy/tax-analysis/Documents/WP-115.pdf
23
- CARBON_TAX_INCIDENCE = pd.Series(
24
- index=[-1, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 99.9],
25
- data=[0.8, 1.2, 1.4, 1.5, 1.6, 1.7, 1.8, 1.8, 1.8, 1.8, 1.6, 1.4, 0.7],
26
- )
27
- CARBON_TAX_INCIDENCE /= 100
28
-
29
- # Source: Figure 1 in
30
- # https://www.taxpolicycenter.org/sites/default/files/alfresco/publication-pdfs/2000587-financial-transaction-taxes.pdf
31
- FTT_INCIDENCE = pd.Series(
32
- index=[-1, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 99.9],
33
- data=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.3, 0.4, 0.8, 1.0],
34
- )
35
- FTT_INCIDENCE /= 100
36
-
37
-
38
- def add_custom_tax(
39
- df: pd.DataFrame,
40
- segment_income: str,
41
- w: str,
42
- base_income: str,
43
- incidence: pd.Series,
44
- name: str,
45
- total: Optional[float] = None,
46
- ratio: Optional[float] = None,
47
- verbose: Optional[bool] = True,
48
- ) -> None:
49
- """Add a custom tax based on incidence analysis driven by percentiles.
50
-
51
- :param df: DataFrame.
52
- :param segment_income: Income measure used to segment tax units into
53
- quantiles.
54
- :param w: Weight used to segment into quantiles (either s006 or XTOT_m).
55
- :param base_income: Income measure by which incidence is multiplied to
56
- estimate liability.
57
- :param incidence: pandas Series indexed on the floor of an income
58
- percentile, with values for the tax rate.
59
- :param name: Name of the column to add.
60
- :param total: Total amount the tax should generate. If not provided,
61
- liabilities are calculated only based on the incidence schedule.
62
- (Default value = None)
63
- :param ratio: Ratio to adjust the tax by, compared to the original tax.
64
- This acts as a multiplier for the incidence argument. (Default value =
65
- None)
66
- :param verbose: Whether to print the tax adjustment factor if needed.
67
- Defaults to True.
68
- :returns: Nothing. Adds the column name to df representing the tax
69
- liability. df is also sorted by segment_income.
70
- """
71
- if ratio is not None:
72
- incidence = incidence * ratio
73
- assert total is None, "ratio and total cannot both be provided."
74
- df.sort_values(segment_income, inplace=True)
75
- income_percentile = 100 * df[w].cumsum() / df[w].sum()
76
- tu_incidence = incidence.iloc[
77
- pd.cut(
78
- income_percentile,
79
- # Add a right endpoint. Should be 100 but sometimes a decimal
80
- # gets added.
81
- bins=incidence.index.tolist() + [101],
82
- labels=False,
83
- )
84
- ].values
85
- df[name] = np.maximum(0, tu_incidence * df[base_income])
86
- if total is not None:
87
- initial_total = mdf.weighted_sum(df, name, "s006")
88
- if verbose:
89
- print(
90
- "Multiplying tax by "
91
- + str(round(total / initial_total, 2))
92
- + "."
93
- )
94
- df[name] *= total / initial_total
95
-
96
-
97
- def add_vat(
98
- df: pd.DataFrame,
99
- segment_income: Optional[str] = "tpc_eci",
100
- w: Optional[str] = "XTOT_m",
101
- base_income: Optional[str] = "aftertax_income",
102
- incidence: Optional[pd.Series] = VAT_INCIDENCE,
103
- name: Optional[str] = "vat",
104
- **kwargs,
105
- ) -> None:
106
- """Add value added tax based on incidence estimate from Tax Policy Center.
107
-
108
- :param df: DataFrame with columns for tpc_eci, XTOT_m, and aftertax_income.
109
- :param Other: arguments: Args to add_custom_tax with VAT defaults.
110
- :param segment_income: Default value = "tpc_eci")
111
- :param w: Default value = "XTOT_m")
112
- :param base_income: Default value = "aftertax_income")
113
- :param incidence: Default value = VAT_INCIDENCE)
114
- :param name: Default value = "vat") :param **kwargs: Other arguments passed
115
- to add_custom_tax().
116
- :returns: Nothing. Adds vat to df. df is also sorted by tpc_eci.
117
- """
118
- add_custom_tax(
119
- df, segment_income, w, base_income, incidence, name, **kwargs
120
- )
121
-
122
-
123
- def add_carbon_tax(
124
- df: pd.DataFrame,
125
- segment_income: Optional[str] = "tpc_eci",
126
- w: Optional[str] = "XTOT_m",
127
- base_income: Optional[str] = "aftertax_income",
128
- incidence: Optional[pd.Series] = CARBON_TAX_INCIDENCE,
129
- name: Optional[str] = "carbon_tax",
130
- **kwargs,
131
- ) -> None:
132
- """Add carbon tax based on incidence estimate from the US Treasury
133
- Department.
134
-
135
- :param df: DataFrame with columns for tpc_eci, XTOT_m, and aftertax_income.
136
- :param Other: arguments: Args to add_custom_tax with carbon tax defaults.
137
- :param segment_income: Default value = "tpc_eci")
138
- :param w: Default value = "XTOT_m")
139
- :param base_income: Default value = "aftertax_income")
140
- :param incidence: Default value = CARBON_TAX_INCIDENCE)
141
- :param name: Default value = "carbon_tax") :param **kwargs: Other arguments
142
- passed to add_custom_tax().
143
- :returns: Nothing. Adds carbon_tax to df. df is also sorted by tpc_eci.
144
- """
145
- add_custom_tax(
146
- df, segment_income, w, base_income, incidence, name, **kwargs
147
- )
148
-
149
-
150
- def add_ftt(
151
- df: pd.DataFrame,
152
- segment_income: Optional[str] = "tpc_eci",
153
- w: Optional[str] = "XTOT_m",
154
- base_income: Optional[str] = "aftertax_income",
155
- incidence: Optional[pd.Series] = FTT_INCIDENCE,
156
- name: Optional[str] = "ftt",
157
- **kwargs,
158
- ) -> None:
159
- """Add financial transaction tax based on incidence estimate from Tax
160
- Policy Center.
161
-
162
- :param df: DataFrame with columns for tpc_eci, XTOT_m, and aftertax_income.
163
- :param Other: arguments: Args to add_custom_tax with FTT defaults.
164
- :param segment_income: Default value = "tpc_eci")
165
- :param w: Default value = "XTOT_m")
166
- :param base_income: Default value = "aftertax_income")
167
- :param incidence: Default value = FTT_INCIDENCE)
168
- :param name: Default value = "ftt") :param **kwargs: Other arguments passed
169
- to add_custom_tax().
170
- :returns: Nothing. Adds ftt to df. df is also sorted by tpc_eci.
171
- """
172
- add_custom_tax(
173
- df, segment_income, w, base_income, incidence, name, **kwargs
174
- )