Repository: MaxGhenis/microdf

Issue:
Add function to calculate tax from a MTR schedule
Basic tax calculation:
```
def tax_from_mtrs(val, brackets, rates):
    # Args:
    #     val: Value to assess tax on, e.g. wealth or income (list or Series).
    #     brackets: Left side of each bracket (list or Series).
    #     rates: Rate corresponding to each bracket.
    df_tax = pd.DataFrame({'brackets': brackets, 'rates': rates})
    df_tax['base_tax'] = df_tax.brackets.\
        sub(df_tax.brackets.shift(fill_value=0)).\
        mul(df_tax.rates.shift(fill_value=0)).cumsum()
    rows = df_tax.brackets.searchsorted(val, side='right') - 1
    income_bracket_df = df_tax.loc[rows].reset_index(drop=True)
    return pd.Series(val).sub(income_bracket_df.brackets).\
        mul(income_bracket_df.rates).add(income_bracket_df.base_tax)
```

Discussion:

Patch:
diff --git a/microdf/__init__.py b/microdf/__init__.py
index 993a7dd..5033ad2 100644
--- a/microdf/__init__.py
+++ b/microdf/__init__.py
@@ -6,6 +6,7 @@ from .custom_taxes import *
 from .income_measures import *
 from .inequality import *
 from .style import *
+from .tax import *
 from .ubi import *
 from .utils import *
 
diff --git a/microdf/tax.py b/microdf/tax.py
new file mode 100644
index 0000000..5979a58
--- /dev/null
+++ b/microdf/tax.py
@@ -0,0 +1,21 @@
+import pandas as pd
+
+
+def tax_from_mtrs(val, brackets, rates):
+    # Calculates tax liability based on a marginal tax rate schedule.
+    #
+    # Args:
+    #     val: Value to assess tax on, e.g. wealth or income (list or Series).
+    #     brackets: Left side of each bracket (list or Series).
+    #     rates: Rate corresponding to each bracket.
+    #
+    # Returns:
+    #     Series of tax liabilities with the same size as val.
+    df_tax = pd.DataFrame({'brackets': brackets, 'rates': rates})
+    df_tax['base_tax'] = df_tax.brackets.\
+        sub(df_tax.brackets.shift(fill_value=0)).\
+        mul(df_tax.rates.shift(fill_value=0)).cumsum()
+    rows = df_tax.brackets.searchsorted(val, side='right') - 1
+    income_bracket_df = df_tax.loc[rows].reset_index(drop=True)
+    return pd.Series(val).sub(income_bracket_df.brackets).\
+        mul(income_bracket_df.rates).add(income_bracket_df.base_tax)


Test patch:
diff --git a/microdf/tests/test_tax.py b/microdf/tests/test_tax.py
new file mode 100644
index 0000000..f4cec43
--- /dev/null
+++ b/microdf/tests/test_tax.py
@@ -0,0 +1,13 @@
+import pytest
+import pandas as pd
+import microdf as mdf
+
+
+def test_tax():
+    # Consider a MTR schedule of 0% up to 10,000, then 10% after that.
+    BRACKETS = [0, 10e3]
+    RATES = [0, 0.1]
+    INCOME = [0, 5e3, 10e3, 10e3 + 1, 20e3]
+    EXPECTED = [0, 0, 0, 0.1, 1e3]
+    res = mdf.tax_from_mtrs(INCOME, BRACKETS, RATES)
+    pd.testing.assert_series_equal(res, pd.Series(EXPECTED))