From 036a0fc4a65982fd5ae8e74f35de9f5ce9f4a3db Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 20 Dec 2017 12:37:57 -0500 Subject: [PATCH 1/4] Relax agg pufcsv test on Python 3.6 --- taxcalc/tests/test_pufcsv.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/taxcalc/tests/test_pufcsv.py b/taxcalc/tests/test_pufcsv.py index 0ee98a4dc..db11db7e0 100644 --- a/taxcalc/tests/test_pufcsv.py +++ b/taxcalc/tests/test_pufcsv.py @@ -16,6 +16,7 @@ # pylint --disable=locally-disabled test_pufcsv.py import os +import sys import json import difflib import pytest @@ -23,6 +24,7 @@ import pandas as pd # pylint: disable=import-error from taxcalc import Policy, Records, Calculator +from taxcalc import nonsmall_diff_line_list @pytest.mark.requires_pufcsv @@ -45,19 +47,27 @@ def test_agg(tests_path, puf_fullsample): taxes_fullsample = adt.loc["Combined Liability ($b)"] # convert adt results to a string with a trailing EOL character adtstr = adt.to_string() + '\n' - # generate differences between actual and expected results + # create actual and expected lists of diagnostic table lines actual = adtstr.splitlines(True) aggres_path = os.path.join(tests_path, 'pufcsv_agg_expect.txt') with open(aggres_path, 'r') as expected_file: txt = expected_file.read() expected_results = txt.rstrip('\n\t ') + '\n' # cleanup end of file txt - expected = expected_results.splitlines(True) - diff = difflib.unified_diff(expected, actual, - fromfile='expected', tofile='actual', n=0) - # convert diff generator into a list of lines: + expect = expected_results.splitlines(True) + # ensure actual and expect lines have differences less than "small" value + epsilon = 1e-6 + if sys.version_info.major == 2: + small = epsilon # tighter test for Python 2.7 + else: + small = 0.1 + epsilon # looser test for Python 3.6 diff_lines = list() - for line in diff: - diff_lines.append(line) + assert len(actual) == len(expect) + for actline, expline in zip(actual, expect): + if actline == expline: + continue + diffs = nonsmall_diff_line_list(actline, expline, small) + if diffs: + diff_lines.extend(diffs) # test failure if there are any diff_lines if diff_lines: new_filename = '{}{}'.format(aggres_path[:-10], 'actual.txt') From c7a809e580f065ca491bc4a5bda8265269ce6988 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 20 Dec 2017 17:38:36 -0500 Subject: [PATCH 2/4] Add cookbook/recipe01 files --- docs/cookbook.html | 24 +++++++++----- docs/cookbook/recipe01.py | 64 ++++++++++++++++++++++++++++++++++++++ docs/cookbook/recipe01.res | 43 +++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 docs/cookbook/recipe01.py create mode 100644 docs/cookbook/recipe01.res diff --git a/docs/cookbook.html b/docs/cookbook.html index e8feecfe8..37eff4e93 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -43,7 +43,9 @@

Cookbook Contents

Advanced Recipes

-

...coming soon...

+

Directly Comparing Two Reforms

+ +

...more coming soon...

Preliminaries: Kitchen Setup

@@ -202,24 +204,30 @@

Basic Recipe: Static Analysis of a Simple Reform

Back to Cookbook Contents

-

Advanced Recipe: ...

+

Advanced Recipe: Directly Comparing Two Reforms

This is an advanced recipe that should be followed only after -mastering the basic recipe.

+mastering the basic recipe. This recipe +shows how to compare two reforms (instead of comparing a reform +to current-law policy) and also shows how to use the reform files +available on the Tax-Calculator website (instead of reform files +on your computer's disk).

Ingredients

-

...

+

No ingredients required because we read reform files from the +Tax-Calculator website.

Instructions

-

Step-by-step instructions included in the recipe01.py file.

+

Step-by-step +instructions in the recipe01.py file.

Results

-

Expected text results from executing -python recipe01.py > recipe01.out at the command prompt as -shown above.

+

Expected text +results from executing python recipe01.py > recipe01.out at +the command prompt as shown above.

Back to Cookbook Contents

diff --git a/docs/cookbook/recipe01.py b/docs/cookbook/recipe01.py new file mode 100644 index 000000000..6a241d8e8 --- /dev/null +++ b/docs/cookbook/recipe01.py @@ -0,0 +1,64 @@ +from __future__ import print_function # necessary only if using Python 2.7 +from taxcalc import * +import urllib as url_lib # on Python 3.6 use "import urllib.request as url_lib" + +# read two reform files from Tax-Calculator website +BASE_URL = ('https://raw.githubusercontent.com/' + 'open-source-economics/Tax-Calculator/master/taxcalc/reforms/') +reform1_name = 'TCJA_House_Amended.json' # TCJA as orginally passed by House +reform2_name = 'TCJA_Reconciliation.json' # TCJA conference committee bill +reform1_text = url_lib.urlopen(BASE_URL + reform1_name).read() +reform2_text = url_lib.urlopen(BASE_URL + reform2_name).read() + +# specify Policy object for static analysis of reform1 +reform1 = Calculator.read_json_param_objects(reform1_text, None) +policy1 = Policy() +policy1.implement_reform(reform1['policy']) +if policy1.reform_errors: + print(policy1.reform_errors) + exit(1) + +# specify Policy object for static analysis of reform2 +reform2 = Calculator.read_json_param_objects(reform2_text, None) +policy2 = Policy() +policy2.implement_reform(reform2['policy']) +if policy2.reform_errors: + print(policy2.reform_errors) + exit(1) + +# specify Calculator objects using policy1 and policy2 and calculate for 2018 +calc1 = Calculator(policy=policy1, records=Records.cps_constructor()) +calc1.advance_to_year(2018) +calc1.calc_all() +calc2 = Calculator(policy=policy2, records=Records.cps_constructor()) +calc2.advance_to_year(2018) +calc2.calc_all() + +# compare aggregate individual income tax revenue in 2018 +iitax_rev1 = calc1.weighted_total('iitax') +iitax_rev2 = calc2.weighted_total('iitax') + +# construct reform2-vs-reform1 difference table with results for income deciles +diff_table = calc1.difference_table(calc2, tax_to_diff='iitax') +assert isinstance(diff_table, pd.DataFrame) +diff_extract = pd.DataFrame() +dif_colnames = ['count', 'tax_cut', 'tax_inc', + 'tot_change', 'mean', 'pc_aftertaxinc'] +ext_colnames = ['funits(#m)', 'taxfall(#m)', 'taxrise(#m)', + 'agg_diff($b)', 'mean_diff($)', 'ataxinc_diff(%)'] +scaling_factors = [1e-6, 1e-6, 1e-6, 1e-9, 1e0, 1e0, 1e0] +for dname, ename, sfactor in zip(dif_colnames, ext_colnames, scaling_factors): + diff_extract[ename] = diff_table[dname] * sfactor + +# print total revenue estimates for 2018 +# (estimates in billons of dollars rounded to nearest tenth of a billion) +print('2018_REFORM1_iitax_rev($B)= {:.1f}'.format(iitax_rev1 * 1e-9)) +print('2018_REFORM2_iitax_rev($B)= {:.1f}'.format(iitax_rev2 * 1e-9)) +print('') + +print('Extract of 2018 income-tax difference table by expanded-income decile') +print('(taxfall is count of funits with cut in income tax in reform 2 vs 1)') +print('(taxrise is count of funits with rise in income tax in reform 2 vs 1)') +print(diff_extract) +print('Note: deciles are numbered 0-9 with top decile divided into bottom 5%,') +print(' next 4%, and top 1%, in the lines numbered 11-13, respectively') diff --git a/docs/cookbook/recipe01.res b/docs/cookbook/recipe01.res new file mode 100644 index 000000000..2cf15a221 --- /dev/null +++ b/docs/cookbook/recipe01.res @@ -0,0 +1,43 @@ +You loaded data for 2014. +Tax-Calculator startup automatically extrapolated your data to 2014. +You loaded data for 2014. +Tax-Calculator startup automatically extrapolated your data to 2014. +2018_REFORM1_iitax_rev($B)= 1219.4 +2018_REFORM2_iitax_rev($B)= 1138.4 + +Extract of 2018 income-tax difference table by expanded-income decile +(taxfall is count of funits with cut in income tax in reform 2 vs 1) +(taxrise is count of funits with rise in income tax in reform 2 vs 1) + funits(#m) taxfall(#m) taxrise(#m) agg_diff($b) mean_diff($) \ +0 16.99 1.11 0.08 -0.08 -4.88 +1 16.99 2.98 0.47 -0.28 -16.65 +2 16.99 3.23 4.03 0.12 6.86 +3 16.99 5.17 4.99 -1.07 -63.11 +4 16.99 6.56 6.34 -1.98 -116.32 +5 16.99 7.59 6.98 -2.77 -163.03 +6 16.99 7.54 8.64 -1.75 -103.23 +7 16.99 10.37 6.53 -5.45 -320.51 +8 16.99 9.81 7.17 -7.28 -428.31 +9 16.99 15.33 1.66 -60.45 -3,558.27 +10 169.89 69.69 46.88 -81.00 -476.75 +11 8.49 7.36 1.14 -11.99 -1,411.59 +12 6.79 6.42 0.38 -27.49 -4,046.07 +13 1.70 1.55 0.15 -20.97 -12,332.42 + + ataxinc_diff(%) +0 -1.57 +1 0.17 +2 -0.04 +3 0.26 +4 0.38 +5 0.41 +6 0.20 +7 0.47 +8 0.44 +9 1.67 +10 0.87 +11 1.04 +12 1.98 +13 1.96 +Note: deciles are numbered 0-9 with top decile divided into bottom 5%, + next 4%, and top 1%, in the lines numbered 11-13, respectively From b3759143faad7fedb54ac456ec475993bca62bb0 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 20 Dec 2017 17:43:29 -0500 Subject: [PATCH 3/4] Add recipe01 HTML files --- docs/recipe00.graph.html | 10 +++--- docs/recipe01.py.html | 66 ++++++++++++++++++++++++++++++++++++++++ docs/recipe01.res.html | 45 +++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 docs/recipe01.py.html create mode 100644 docs/recipe01.res.html diff --git a/docs/recipe00.graph.html b/docs/recipe00.graph.html index 0cc8551f6..c5be115dc 100644 --- a/docs/recipe00.graph.html +++ b/docs/recipe00.graph.html @@ -26,11 +26,11 @@
-
+
-