From a137ea37538642d5592913d7550cc23f94f001a1 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Tue, 21 Nov 2017 08:17:26 -0500 Subject: [PATCH 01/33] Add preliminary version of several new docs files --- docs/cookbook.htmx | 1070 +++++++++++++++++++++ docs/cookbook/ingredients/repeal_amt.json | 18 + docs/cookbook/recipe00.py | 43 + docs/make_cookbook.py | 255 +++++ 4 files changed, 1386 insertions(+) create mode 100644 docs/cookbook.htmx create mode 100644 docs/cookbook/ingredients/repeal_amt.json create mode 100644 docs/cookbook/recipe00.py create mode 100644 docs/make_cookbook.py diff --git a/docs/cookbook.htmx b/docs/cookbook.htmx new file mode 100644 index 000000000..a56e0df9e --- /dev/null +++ b/docs/cookbook.htmx @@ -0,0 +1,1070 @@ + + + + +Using Tax-Calculator + + + +
+ +

Using Tax-Calculator

+ +

This document tells you how to use Tax-Calculator, an open-source +federal income and payroll tax simulation model. The two ways of +using Tax-Calculator described here require no computer programming. +If you want to participate in the development of Tax-Calculator +— by asking a question, reporting a bug, improving the documentation +or making an enhancement to the Python source code — you should go to +the +developer website.

+ + +

Document Contents

+ +

TaxBrain GUI to use Tax-Calculator via an +interactive web application

+

tc CLI to use Tax-Calculator on your computer +via the command line

+

Policy Parameters that specify tax reforms

+

Input Variables that specify tax filing units

+

Output Variables that describe tax results

+

Response Parameters that specify reform responses

+ + +

TaxBrain GUI

+ +

You can use Tax-Calculator via a graphical user interface (GUI) +provided by the TaxBrain web +application. This approach requires only an Internet connection +and web browser on your computer. TaxBrain (TB) uses a micro dataset +to produce both aggregate and distributional reform estimates for each +of ten years for a tax reform you specify. The calculations take just +a couple of minutes and there is flexibility to show different kinds +of distributional results as well as aggregate revenue results.

+ + +

Section Contents

+ +

Specify Tax Reform

+

Initiate Static Analysis

+

View/Download Static Results

+

Initiate Dynamic Analysis

+ + +

Specify Tax Reform

+ +

The first step in using TaxBrain is to set the Start Year for the +tax calculations. Then specify the values of policy parameters that +you want to change in your reform.

+ +

By default the policy parameter boxes contain Start Year values. +Simply replace the current-law value with the Start Year reform value. +If some or all of your reform provisions are going to begin after the +Start Year, simply fill in the pre-reform years with asterisks. So, +for example, if Start Year is 2017 and your reform calls for raising +the social security payroll tax rate to 13 percent in 2018 and then +to 14 percent in 2020, you would enter in that box the following: +

*,0.13,*,0.14
+This parameter is not indexed, so the 0.13 value for 2018 will be +extrapolated to 2019, and the 0.14 value for 2020 will be in force +in all subsequent years.

+ +

Back to Section Contents

+ + +

Initiate Static Analysis

+ +

Simply click on the Show me the results! button to start the tax +calculations. That button will use the full sample and take a couple +of minutes to compute revenue and distributional results for each of +ten years beginning with the Start Year you specified.

+ +

There is also a Quick Calculation! button that produces results +only for the Start Year using a sub-sample of the full sample.

+ +

Back to Section Contents

+ + +

View/Download Static Results

+ +

After the tax calculations are completed, you are shown a results +page that presents various options: you can specify, view and download +a total liabilities table, your can specify, view and download a +distribution table, you can use your reform to conduct different types +of dynamic analysis (via the button labeled Link to Dynamic +Simulations), and finally, you can go back to the policy parameters +page to modify your reform (via the button labeled Edit Parameters).

+ +

There are three variants of the total liabilities table. +The variant shown initially is Change, meaning the amounts are the +difference between liabilities under your reform and the liabilities +projected for current-law policy. You can also elect to see the +reform and current-law levels.

+ +

There are many variants of the distribution table. Try +clicking on the options to the left to define the nature of the table +and the year for which it is constructed. Try clicking on the options +to the right to specify which columns are included in the table. An +easy way to uncheck all the column boxes is to double click on the +Select All box.

+ +

There are three download options for each of these two tables:

+ +

Print creates a new page in your browser that is suitable +for printing.

+ +

Copy puts the table on the clipboard of your local computer +so that you can paste it anywhere on your computer. The copy on the +clipboard uses invisible tab characters to separate the table's +columns.

+ +

CSV downloads to your computer a CSV-formatted file +containing the information in the table. CSV-formatted files can be +easily imported into any spreadsheet program.

+ +

Back to Section Contents

+ + +

Initiate Dynamic Analysis

+ +

After clicking on the Link to Dynamic Simulations on the static +results page, you are presented with a page that allows you to choose +three different types of dynamic simulations to conduct on your tax +reform:

+ +

Partial Equilibrium Simulation is described this way: +This approach answers the question, how would taxpayer behavior +(income and deductions) affect revenue if all other prices in the +economy could stay the same?

+ +

Overlapping Generations Simulation is described this way: +This approach answers the question, how does tax policy affect +macroeconomic aggregates and prices?

+ +

Macro Elasticity Simulation is described this way: +This approach harnesses econometric estimates of the historical +relationship between tax policy and the macro economy to predict the +effect of tax reforms on economic growth.

+ +Clicking on one of these choices will allow you to specify some +addition economic response parameters and initiate the dynamic +analysis using your response parameter assumptions. + +

Back to Section Contents   +Back to Document Contents

+ + +

tc CLI

+ +

You can use Tax-Calculator on your own computer via a command line +interface (CLI) called tc. This approach requires the installation of +Anaconda Python and the taxcalc package on your computer as well as +the use of a text editor to prepare simple files that are read by tc. +Computer programming knowledge is not required, but if you are +prevented from doing the easy installation or you are not willing to +work at the command line (Terminal on Mac or Command Prompt on +Windows) and use an editor (for example, TextEdit on Mac or Notepad on +Windows), then you should probably use the TaxBrain GUI.

+ +

If you are comfortable using an editor to prepare text files that +describe your tax reforms, but don't want to install Python and tc on +your computer, you can upload reform files to TaxBrain where all the +analysis is conducted (as described in the previous section). If you +are interested in this file-upload approach, skip the first part of +this section, read the next part on how to specify policy reform +files, and then skip to the last part of this section on how to upload +files to TaxBrain.

+ + +

Section Contents

+ +

Install/Test tc CLI

+

Specify Tax Reform

+

Specify Analysis Assumptions

+

Specify Filing Units

+

Initiate Reform Analysis

+

Tabulate Reform Results

+

Upload Files to TaxBrain

+ + +

Install/Test tc CLI

+ +

Installation of tc CLI involves several steps:

+ +

Install the free Anaconda Python distribution by going to +the Continuum Analytics +download page and selecting a version of Python. You can install +either Python 2.7 or Python 3.6 because we test Tax-Calculator with +both. You must do this installation even if you already +have Python installed on your computer because the Anaconda +distribution contains all the additional Python packages that +Tax-Calculator uses to conduct tax calculations (many of which are not +included in other Python installations). You can install the Anaconda +distribution without having administrative privileges on your computer +and the Anaconda distribution will not interfere with any Python +installation that came as part of your computer’s operating +system.

+ +

Check your Anaconda Python installation by entering the +following two commands at the operating system command prompt (which +is shown as $ here, but would be > on Windows) +in any directory: +

$ conda --version
+Expected output is something like conda 4.3.17 +
$ python --version
+Expected output should contain either the Python 2.7 or +the Python 3.6 phrase as well as the +Anaconda phrase, the presence of which confirm that the +installation went smoothly. + +

Install the free taxcalc package by entering the following: +

$ conda install -c ospc taxcalc
+Expected output should contain information about all the additional +packages that Tax-Calculator needs. After all that information has +been shown on the screen, press the y key to proceed with +the package installation.

+ +

Check your installation of tc (the CLI to Tax-Calculator) by +entering the following: +

$ tc --test
+Expected output (after about twenty seconds) is PASSED +TEST. If you get FAILED TEST, something went wrong +in the installation process. If the installation test fails, please +report your experience by creating a new issue at + +this website or by sending an email to one of the core maintainers +of Tax-Calculator listed at the bottom of the What is TaxBrain? +page at this website. + +

If your installation passes the test, you are ready to begin using +tc to analyze your tax reforms. Continue reading this section for +information about how to do that. But if you want a quick hint about +the range of tc capabilities, enter the following: +

$ tc --help
+

+ +

The basic idea of tc tax analysis is that each tax reform is +specified in a text file using a simple method to describe the details +of the reform. Read the next part of this section to see how policy +reform files are formatted.

+ +

Back to Section Contents

+ + +

Specify Tax Reform

+ +

The details of a tax reform are contained in a text file that you +write with a text editor. The reform is expressed by specifying which +tax policy parameters are changed from their current-law values by the +reform. The timing and magnitude of these policy parameter changes +are written in JSON, a simple and widely-used data-specification +language.

+ +

For several examples of reform files and the general rules for +writing JSON reform files, go +to this +page.

+ +

If you want to upload reform files to TaxBrain for static analysis +(rather than use the tc CLI on your computer for analysis), go to this +section's last part, which +discusses uploading files to TaxBrain. If you want to assume any +responses to your tax reform (that is, if you want to conduct +non-static analysis), read the next part before going to the last +part.

+ +

Back to Section Contents

+ + +

Specify Analysis Assumptions

+ +

This part explains how to specify response assumption files used in +non-static tax analysis. If you want to start out doing static +analysis, you can skip this part now and come back to read it whenever +you want to go beyond static analysis. +The next part of this section discusses +filing-unit input files.

+ +

The details of economic response assumptions are contained in a +text file that you write with a text editor. The assumptions are +expressed by specifying which response parameters are changed from +their default values, all of which are zero. The timing and magnitude of +these response parameter changes are written in JSON, a simple and +widely-used data-specification language.

+ +

For examples of response assumption files and the general rules for +writing JSON assumption files, go +to this +page.

+ +

If you want to upload policy reform and response assumption files +to TaxBrain, you can go directly to this +section's last part, which +discusses uploading files to TaxBrain. If you want to analyze your +tax reforms on your own computer, go to the next part on how to +specify tax filing units used in the analysis.

+ +

Back to Section Contents

+ + +

Specify Filing Units

+ +

The taxcalc package containing the tc CLI to Tax-Calculator does +not include the microsimulation sample used by TaxBrain. This is +because, unlike Census survey public-use files, the IRS-SOI Public Use +File (PUF) is proprietary. If you or your organization has paid IRS +to use the PUF version being used by TaxBrain, then it may be possible +for us to share with you our TaxBrain sample, which we +call puf.csv even though it contains CPS records that +represent non-filers. Otherwise, you have three choices.

+ +

First, you can easily create with an editor a CSV-formatted +file containing several filing units whose experience under your tax +reform is of interest to you. Much of the public discussion of tax +reforms is of this type: how is this family or that family affected by +a reform; how do they fare under different reforms; etc. Notice that +this kind of analysis of a few exemplary families is something you +cannot do on TaxBrain. The test conducted to check the tc +installation has left one such file. It is called test.csv +and contains two filing units with only wage and salary income: a +lower income family and a higher income family. You can use the +test.csv file as tc input to analyze your tax reforms. +Before creating your own input files be sure to read the short set of +guidelines that appear after this list of three choices.

+ +

Second, recently we made freely +available a public-use file containing only filing units derived from +several recent March CPS surveys. For several reasons, the results generated +by this cps.csv file are substantially different from the results +generated by the puf.csv file. The cps.csv file +contains a sample of the population while the puf.csv file +contains mostly a sample of income tax filers in which high-income +filing units are over represented. Also, the cps.csv file +has many income variables that are missing (and assumed to be zero by +Tax-Calculator), which causes an understating of total incomes, +especially for those with high incomes. All these differences +mean that the aggregate revenue and distributional results generated +when using the cps.csv file as input to Tax-Calculator can +be substantially different from the results generated when using the +puf.csv file as input. And this is particularly true when +analyzing reforms that change the tax treatment of high-income filers.

+ +

Third, when you want to estimate how your reform affects +total tax liabilities and/or the distribution of tax liabilities, you +can always upload your policy reform and response assumption files +to TaxBrain. Combining this option with the first option provides a +complete tax analysis capability. +The last part of this section +describes how to upload files to TaxBrain.

+ +

Input-File-Preparation Guidelines

+ +

The tc CLI to Tax-Calculator is flexible enough to read almost any +kind of CSV-formatted input data on filing units as long as the +variable names correspond to those expected by Tax-Calculator. The +only required input variables are RECID (a unique +filing-unit record identifier) and MARS (a positive-valued +filing-status indicator). Other variables in the input file must have +variable names that are listed in the Input +Variables section for them to affect the tax calculations. Any +variable listed in Input Variables that is not in an input file is +automatically set to zero for every filing unit. Variables +in the input file that are not listed in Input Variables are ignored +by Tax-Calculator.

+ +

However, there are important data-preparation issues related to the +fact that the payroll tax is a tax on individuals, not on income-tax +filing units. Tax-Calculator expects that the filing-unit total for +each of several earnings-related variables is split between the +taxpayer and the spouse. It is the responsibility of anyone preparing +data for Tax-Calculator input to do this earnings splitting. Here are +the relationships between the filing-unit variable and the taxpayer +(p) and spouse (s) variables expected by +Tax-Calculator: +

+e00200 = e00200p + e00200s
+e00900 = e00900p + e00900s
+e02100 = e02100p + e02100s
+
+Obviously, when MARS is not equal to 2 (married filing +jointly), the values of the three s variables are zero and +the value of each p variable is equal to the value of its +corresponding filing-unit variable. Note that the input file can +omit any one, or all, of these three sets variables. If the three +variables in one of these sets are omitted, the required relationship +will be satisfied because zero equals zero plus zero.

+ +

But when including one of these three sets of variables, it is up +to you to specify the taxpayer-spouse split. You will get an error +message from Tax-Calculator, and it will stop running, if you do not +split the filing-unit amount between taxpayer and spouse so that the +above equations hold for each filing unit in the input file.

+ +

In addition to this earnings-splitting data-preparation issue, +Tax-Calculator expects that the value of ordinary dividends +(e00600) will be no less than the value of qualified +dividends (e00650) for each filing unit. Again, it is your +responsibility to prepare input data in a way that ensures this +relationship is true for each filing unit.

+ +

Back to Section Contents

+ + +

Initiate Reform Analysis

+ +

The tc CLI to Tax-Calculator requires only an input file containing +one or more filing units. A policy reform file is optional; no reform +file implies you want to analyze current-law policy. A response +assumption file is also optional; no assumption file implies you want +to conduct static analysis.

+ +

Here we explain how to conduct tax analysis with the tc CLI by +presenting a series of examples and explaining what output is produced +in each example. There are several types of output that tc can +generate so there will be more than a few examples. The examples are +numbered in order to make it easier to refer to different examples. +All the examples assume that the input file is test.csv, +which was mentioned in previous part of this section.

+ +

+(1)$ tc test.csv 2020
+

+ +

Example (1) produces a minimal output file containing 2020 tax +liabilities for each filing unit assuming the income amounts in the +input file are amounts for 2020 and assuming current-law tax policy +projected to 2020. The name of the CSV-formatted output file is +test-20-#-#.csv. +The first # symbol indicates we did not specify a +policy reform file and the second # symbol indicates we did +not specify a response assumption file. The variables included +in the minimal output file include: +RECID (of filing unit in the input file), +YEAR (specified when executing tc CLI), +WEIGHT (which is same as s006), +INCTAX (which is same as iitax), +LSTAX (which is same as lumpsum_tax) and +PAYTAX (which is same as payroll_tax). +

+ +

Also, documentation of the reform is always written to a text file +ending in -doc.text, which in this example would be +named test-20-#-#-doc.text.

+ +

+(2)$ tc test.csv 2020 --dump
+

+ +

Example (2) produces a much more complete output file with the same +name test-20-#-#.csv as the minimal output file produced in +example (1). No other output is generated other than the +test-20-#-#-doc.text file. The +--dump option causes all the input variables (including the +one's understood by Tax-Calculator but not included +in test.csv, which are all zero) and all the output +variables calculated by Tax-Calculator to be included in the output +file. For a complete list of input variables, see +the Input Variables section. For a complete list +of output variables, see the Output Variables +section. Since Tax-Calculator ignores variables in the input file +that are not in the Input Variables section, the dump output file in +example (2) can be used as an input file and it will produce exactly +the same tax liabilities (apart from rounding errors of one or two +cents) as in the original dump output.

+ +

+(3)$ tc test.csv 2020 --sqldb
+

+ +

Example (3) produces the same dump output as example (2) except +that the dump output is written not to a CSV-formatted file, but to +the dump table in an SQLite3 database file, which is +called test-20-#-#.db in this example. Because +the --dump option is not used in example (3), minimal +output will be written to the test-20-#-#.csv file.

+ +

Pros and cons of putting dump output in a CSV file or an SQLite3 +database table: The CSV file is almost twice as large as the +database, but it can be easily imported into a wide range of +statistical packages and spreadsheets. The main advantage of the +SQLite3 database is that the Anaconda Python distribution includes +sqlite3 (or sqlite3.exe +on Windows), a command-line tool that can be used to tabulate dump +output using structured query language (SQL). SQL is a language that +you use to specify the tabulation you want and the SQL database +figures out the procedure for generating your tabulation and then +executes that procedure; there is no computer programming involved. +We will give an example of SQL tabulation of dump output in the next +part of this section.

+ +

+(4)$ tc test.csv 2020 --dump --sqldb
+

+ +

Example (4) shows that you can get dump output in the two different +formats from a single tc run.

+ +

The remaining examples use neither the --dump nor the +--sqldb option, and thus, produce minimal output for the +reform. But either or both of those options could be used in all the +subsequent examples to generate more complete output for the reform.

+ +

+(5)$ tc test.csv 2021 --reform ref3.json
+

+ +

Example (5) produces 2021 output for the filing units in the +test.csv file using the policy reform specified in +the ref3.json file. Because no --assump +option is used, the output results are produced using static analysis. +The name of the output file in this example is +test-21-ref3-#.csv.

+ +

+(6)$ tc test.csv 2021 --reform ref3.json --assump res1.json
+

+ +

Example (6) produces 2021 output for the filing units in the +test.csv file using the policy reform specified in +the ref3.json file and the response assumptions +specified in the res1.json file. The output results +produced by this non-static analysis are written to the +test-21-ref3-res1.csv file.

+ +

The following examples illustrate output options that work only if +each filing unit in the input file has a positive sampling weight +(s006). So, we are going to use the cps.csv +file in these examples along with the policy reform specified in +the ref3.json file, the content of which is: +

+// ref3.json raises personal exemption amount to 8000 in 2022,
+// after which it continues to be indexed to price inflation.
+{
+    "policy": {
+        "_II_em": {"2022": [8000]}
+    }
+}
+
+The output options illustrated in the following examples generate +tables of the post-reform level and the reform-induced change in tax +liability by income deciles as well as graphs of marginal and average +tax rates by income percentile. + +

+(7)$ tc cps.csv 2022 --reform ref3.json --tables
+You loaded data for 2014.
+Tax-Calculator startup automatically extrapolated your data to 2022.
+
+(7)$ ls cps-22*
+cps-22-ref3-#-doc.text	cps-22-ref3-#-tab.text	cps-22-ref3-#.csv
+
+(7)$ cat cps-22-ref3-#-tab.text
+Weighted Tax Reform Totals by Baseline Expanded-Income Decile
+    Returns    ExpInc    IncTax    PayTax     LSTax    AllTax
+       (#m)      ($b)      ($b)      ($b)      ($b)      ($b)
+ 0    17.93      -3.6      -2.1       3.6       0.0       1.5
+ 1    17.93     202.8     -15.4      13.7       0.0      -1.7
+ 2    17.93     353.5     -19.2      25.3       0.0       6.0
+ 3    17.93     513.8     -16.9      47.0       0.0      30.1
+ 4    17.93     696.3      -1.2      72.3       0.0      71.2
+ 5    17.93     928.0      24.0     104.3       0.0     128.2
+ 6    17.93    1246.3      59.9     147.5       0.0     207.4
+ 7    17.93    1723.5     121.0     215.3       0.0     336.3
+ 8    17.93    2497.1     224.8     317.9       0.0     542.6
+ 9    17.93    5949.7    1099.9     517.8       0.0    1617.8
+ A   179.26   14107.4    1474.8    1464.8       0.0    2939.6
+
+Weighted Tax Differences by Baseline Expanded-Income Decile
+    Returns    ExpInc    IncTax    PayTax     LSTax    AllTax
+       (#m)      ($b)      ($b)      ($b)      ($b)      ($b)
+ 0    17.93      -3.6       0.0       0.0       0.0       0.0
+ 1    17.93     202.8      -0.2       0.0       0.0      -0.2
+ 2    17.93     353.5      -1.8       0.0       0.0      -1.8
+ 3    17.93     513.8      -4.2       0.0       0.0      -4.2
+ 4    17.93     696.3      -7.3       0.0       0.0      -7.3
+ 5    17.93     928.0     -11.1       0.0       0.0     -11.1
+ 6    17.93    1246.3     -17.5       0.0       0.0     -17.5
+ 7    17.93    1723.5     -22.7       0.0       0.0     -22.7
+ 8    17.93    2497.1     -31.2       0.0       0.0     -31.2
+ 9    17.93    5949.7     -26.3       0.0       0.0     -26.3
+ A   179.26   14107.4    -122.3       0.0       0.0    -122.3
+

+ +

Example (7) produces 2022 static output for the filing units in the +cps.csv file using the policy reform specified in +the ref3.json file. Notice that Tax-Calculator knows to +extrapolate (or age) filing unit data in the cps.csv +file to the specified tax year. It knows to do that because of the +special input file name cps.csv. The tables produced by +this static analysis are written to the +cps-22-ref3-#-tab.text file. Note that on Windows +you would use dir instead of ls and +type instead of cat.

+ +

+(8)$ tc cps.csv 2024 --reform ref3.json --graphs
+You loaded data for 2014.
+Tax-Calculator startup automatically extrapolated your data to 2024.
+
+(8)$ ls cps-24*
+cps-24-ref3-#-atr.html	cps-24-ref3-#-mtr.html
+cps-24-ref3-#-doc.text	cps-24-ref3-#.csv
+

+ +

Example (8) is like example (7) except we ask for 2024 static +output and for graphs instead of tables, although we could ask for +both. The HTML files containing the graphs can be viewed in your +browser.

+ +

Here is what the average tax rate graph +in cps-24-ref3-#-atr.html looks like.

+ +

atr graph

+ +

Here is what the marginal tax rate graph +in cps-24-ref3-#-mtr.html looks like:

+ +

mtr graph

+ +

There is yet another tc CLI output option that writes to the screen +results from a normative welfare analysis of the specified policy +reform. This --ceeu option produces experimental results +that make sense only with input files that contain representative +samples of the population such as the cps.csv file. The +name of this option stands for certainty-equivalent expected utility. +If you want to use this output option, you should read the commented +Python source code for the ce_aftertax_income function in +the taxcalc/utils.py file at the + +developer website.

+ +

Back to Section Contents

+ + +

Tabulate Reform Results

+ +

Given that tc CLI output can be written to either CSV-formatted +files or SQLite3 database files, there is an enormous range of +software tools that can be used to tabulate the output. You can use +SAS or R, Stata or MATLAB, or even import output into a spreadsheet +(but this would seem to be the least useful option). If you just +want to compare the contents of two output files, you can use your +favorite graphical diff program to view the two files side by side +with highlighting of numbers that are different. The main point +is to use a software tool that is available to you and that you have +experience using.

+ +

Here we give some examples of using the sqlite3 +command-line tool that is part of the Anaconda distribution (so it is +always available when using Tax-Calculator). The first step, of +course, is to use the --sqldb option when running tc. Then +you can use the sqlite3 tool interactively or use it to +execute SQL scripts you have saved in a text file. We'll provide +examples of both those approaches. There are many online tutorials on +the SQL select command; if you want to learn more, search the +Internet.

+ +

First, we provide a simple example of using sqlite3 +interactively. This approach is ideal for exploratory data analysis. +Our example uses the cps.csv file as input, but you can do +the following with the output from any input file that has weights +(s006). Also, we specify no policy reform file, so the +output is for current-law policy. What you cannot see from the +following record of the analysis is that the sqlite3 tool +keeps a command history, so pressing the up-arrow key will bring up +the prior command for editing. This feature reduces substantially the +amount of typing required to conduct exploratory data analysis. +

+$ tc cps.csv 2016 --sqldb
+You loaded data for 2014.
+Tax-Calculator startup automatically extrapolated your data to 2016.
+$ sqlite3 cps-16-#-#.db
+SQLite version 3.13.0 2016-05-18 10:57:30
+Enter ".help" for usage hints.
+sqlite> select count(*) from dump;
+456465
+sqlite> select count(*),sum(s006) from dump;
+456465|165398918.379996
+sqlite> select count(*),round(sum(s006)*1e-6,3) from dump;
+456465|165.399
+sqlite> select round(sum(s006)*1e-6,3) from dump where MARS=2;
+68.234
+sqlite> select MARS,round(sum(s006)*1e-6,3) from dump group by MARS;
+1|87.385
+2|68.234
+4|9.781
+sqlite> .quit
+$
+
+

+ +

Second, we provide a simple example of using sqlite3 +with SQL commands stored in a text file. This approach is useful +if you want to tabulate many different output files in the same way. +This second example assumes that the first example has already +been done. Note that on Windows you should replace cat +with type. +

+
+$ cat tab.sql
+-- tabulate weight of those with negative marginal income tax rates
+select "weighted count of those with negative MTR",
+        round(sum(s006)*1e-6,3)
+from dump
+where mtr_inctax < 0;
+
+-- construct marginal income tax rate histogram with bin width 10
+select "bin number|weighted count|average MTR in bin";
+select round((mtr_inctax-5)/10) as mtr_bin, -- histogram bin number
+       round(sum(s006)*1e-6,3), -- weight of those in bin (m#)
+       -- weighted average marginal income tax rate on taxpayer earnings in bin:
+       round(sum(mtr_inctax*s006)/sum(s006),2)
+from dump
+where mtr_inctax >= 0  -- include only those with positive MTR
+group by mtr_bin
+order by mtr_bin;
+
+-- tabulate weight of all filing units
+select "weighted count of all filing units",
+        round(sum(s006)*1e-6,3)
+from dump;
+
+$ cat tab.sql | sqlite3 cps-16-#-#.db
+weighted count of those with negative MTR|22.434
+bin number|weighted count|average MTR in bin
+-1.0|35.34|0.0
+0.0|2.308|7.27
+1.0|56.887|14.11
+2.0|32.926|25.39
+3.0|14.636|32.18
+4.0|0.786|43.0
+5.0|0.069|54.52
+6.0|0.012|66.75
+weighted count of all filing units|165.399
+
+The cat command writes the contents of +the tab.sql file to stdout. We do nothing but that in the +first command in order to show you the file contents. The second +command pipes the contents of the tab.sql file into +the sqlite3 tool, which executes the SQL statements and +writes the tabulation results to stdout. (If you're wondering about +the validity of those high marginal tax rates, rest assured that all +filing units with marginal income tax rates greater than seventy +percent have been checked by hand and are valid: most are +caught in the rapid phase-out of non-refundable education credits. +The negative +marginal tax rates are caused by refundable credits, primarily the +earned income tax credit.)

+ +

If you want to use the sqlite3 tool to tabulate the +changes caused by a reform, use the tc CLI to generate two database +dump files (one for current-law policy and the other for your reform) +and then use the SQLite3 ATTACH command to make both database files +available in your SQLite tabulation session.

+ + +

Upload Files to TaxBrain

+ +

Any policy reform or response assumption files you can use with tc +can be uploaded to trigger a TaxBrain run. As mentioned above the +main advantages of doing this is getting access to the proprietary +puf.csv input file, getting fast execution of both reform +and current-law policy runs for ten years and getting the flexibility +to specify a variety of distributional tables. All the TaxBrain results +can be downloaded to your computer for storage or for additional +analysis.

+ +

The files on your computer can be uploaded +at the TaxBrain +file-upload page.

+ +

An extended example of how preliminary tax analysis on your +computer can be combined with TaxBrain file-uploading is +here.

+ +

Back to Section Contents   +Back to Document Contents

+ + +

Policy Parameters

+ +

This section contains documentation of policy parameters in a +format that is easy to search and print. The policy parameters are +grouped here as they are are on TaxBrain (TB). Parameters understood +by Tax-Calculator and the tc CLI, but not available in the TaxBrain +GUI, are placed in an Other Parameters group at the end of the +section.

+ + +

Section Contents

+ +

Parameter Indexing

+

Payroll Taxes

+

Social Security Taxability

+

Above The Line Deductions

+

Personal Exemptions

+

Standard Deduction

+

Nonrefundable Credits

+

Itemized Deductions

+

Capital Gains And Dividends

+

Personal Income

+

Other Taxes

+

Refundable Credits

+

Surtaxes

+

Universal Basic Income

+

Other Parameters

+ + +

Parameter Indexing

+ + + +

Back to Section Contents

+ + +

Payroll Taxes

+ + + + + + + +

Back to Section Contents

+ + +

Social Security Taxability

+ + + + + +

Back to Section Contents

+ + +

Above The Line Deductions

+ + + + + + + +

Back to Section Contents

+ + +

Personal Exemptions

+ + + + + + + +

Back to Section Contents

+ + +

Standard Deduction

+ + + + + +

Back to Section Contents

+ + +

Nonrefundable Credits

+ + + + + + + + + +

Back to Section Contents

+ + +

Itemized Deductions

+ + + + + + + + + + + + + + + + + + + + + + + +

Back to Section Contents

+ + +

Capital Gains And Dividends

+ + + + + + + +

Back to Section Contents

+ + +

Personal Income

+ + + + + + + +

Back to Section Contents

+ + +

Other Taxes

+ + + +

Back to Section Contents

+ + +

Refundable Credits

+ + + + + + + + + +

Back to Section Contents

+ + +

Surtaxes

+ + + + + + + +

Back to Section Contents

+ + +

Universal Basic Income

+ + + + + +

Back to Section Contents

+ + +

Other Parameters

+ + + +

Back to Section Contents   +Back to Document Contents

+ + +

Input Variables

+ +

This section contains documentation of input variables in a format +that is easy to search and print. The input variables are ordered +alphabetically by name. There are no subsections, just a long list +of input variables that Tax-Calculator is programmed to use in its +calculations. The Availability information indicates which input +data files contain the variable.

+ + + +

Back to Section Contents   +Back to Document Contents

+ + +

Output Variables

+ +

This section contains documentation of output variables in a format +that is easy to search and print. The output variables are ordered +alphabetically by name. There are no subsections, just a long list of +output variables that Tax-Calculator is programmed to calculate.

+ + + +

Back to Section Contents   +Back to Document Contents

+ + +

Response Parameters

+ +

This section contains documentation of several sets of parameters +that characterize responses to a tax reform. Consumption +parameters are used to compute marginal tax rates. Behavior +parameters are used to compute changes in input variables caused by a +tax reform in a partial-equilibrium setting. Growdiff parameters are +used to specify baseline differences and/or reform responses in the +annual rate of growth in economic variables.

+ +

All the response parameters can be used by the tc CLI, but only +those with a TB Name appear in the TaxBrain GUI. All these +dynamic response parameters control advanced features of +Tax-Calculator, so understanding the +source +code that uses them is essential. Each default parameter value is +zero and is projected into the future at that value, which implies no +response to the reform. Using the default no-reform-response +assumption for all the response parameters generates a static analysis +of the tax reform.

+ + +

Section Contents

+ +

Consumption Parameters

+

Behavior Parameters

+

Growdiff Parameters

+ + +

Consumption Parameters

+ + + +

Back to Section Contents

+ + +

Behavior Parameters

+ + + +

Back to Section Contents

+ + +

Growdiff Parameters

+ + + +

Back to Section Contents   +Back to Document Contents

+ + +
+ + diff --git a/docs/cookbook/ingredients/repeal_amt.json b/docs/cookbook/ingredients/repeal_amt.json new file mode 100644 index 000000000..29a937b65 --- /dev/null +++ b/docs/cookbook/ingredients/repeal_amt.json @@ -0,0 +1,18 @@ +// Repeals AMT and raises regular tax rates in the top three brackets +// from (0.33, 0.35, 0.396) to (0.35, 0.37, 0.42) beginning in 2018. +{ + "policy": { + // repeal AMT by setting AMT tax rates to zero + "_AMT_rt1": {"2018": [0.0]}, + "_AMT_rt2": {"2018": [0.0]}, + // raise tax rates in the top three brackets + // ... raise non-AMT rates on non-pass-through income + "_II_rt5": {"2018": [0.350]}, + "_II_rt6": {"2018": [0.370]}, + "_II_rt7": {"2018": [0.420]}, + // ... raise non-AMT rates on pass-through income + "_PT_rt5": {"2018": [0.350]}, + "_PT_rt6": {"2018": [0.370]}, + "_PT_rt7": {"2018": [0.420]} + } +} diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py new file mode 100644 index 000000000..c27945c70 --- /dev/null +++ b/docs/cookbook/recipe00.py @@ -0,0 +1,43 @@ +""" +RECIPE00.PY +""" + +from __future__ import print_function # necessary only if using Python 2.7 + +from taxcalc import * + + +# use publicly-available CPS input file +recs = Records.cps_constructor() + +# specify Calculator object for static analysis of current-law policy +pol = Policy() +calc1 = Calculator(policy=pol, records=recs) + +# read JSON reform file and use default (that is, static) analysis assumptions +reform_filename = './ingredients/repeal_amt.json' +params = Calculator.read_json_param_objects(reform=reform_filename, + assump=None) + +# specify Calculator object for static analysis of reform policy +pol.implement_reform(params['policy']) +if pol.reform_errors != '': # check for reform error messages + print(pol.reform_errors) + exit(1) +calc2 = Calculator(policy=pol, records=recs) + +# calculate tax liabilities for 2018 +calc1.advance_to_year(2018) +calc1.calc_all() +calc2.advance_to_year(2018) +calc2.calc_all() + +# compute total income tax liability under current law and the reform +# (records.iitax contains each filing unit's individual income tax liability +# and records.s006 contains each filing units's sampling weight) +itax_rev1 = (calc1.records.iitax * calc1.records.s006).sum() +itax_rev2 = (calc2.records.iitax * calc2.records.s006).sum() + +# print total revenue estimates for 2018 in whole billons of dollars +print('2018_CLP_itax_rev($B)= {:.0f}'.format(itax_rev1 * 1e-9)) +print('2018_REF_itax_rev($B)= {:.0f}'.format(itax_rev2 * 1e-9)) diff --git a/docs/make_cookbook.py b/docs/make_cookbook.py new file mode 100644 index 000000000..a5fff6c09 --- /dev/null +++ b/docs/make_cookbook.py @@ -0,0 +1,255 @@ +""" +Reads skeletal index.htmx file and writes fleshed-out index.html file +containing information from several JSON files. +""" +# CODING-STYLE CHECKS: +# pep8 --ignore=E402 make_index.py +# pylint --disable=locally-disabled make_index.py + +import os +import sys +import json +from collections import OrderedDict +CUR_PATH = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(os.path.join(CUR_PATH, '..')) +# pylint: disable=import-error,wrong-import-position +from taxcalc import Policy + + +INPUT_FILENAME = 'index.htmx' +OUTPUT_FILENAME = 'index.html' + +CURDIR_PATH = os.path.abspath(os.path.dirname(__file__)) +TAXCALC_PATH = os.path.join(CURDIR_PATH, '..', 'taxcalc') + +INPUT_PATH = os.path.join(CURDIR_PATH, INPUT_FILENAME) +POLICY_PATH = os.path.join(TAXCALC_PATH, 'current_law_policy.json') +IOVARS_PATH = os.path.join(TAXCALC_PATH, 'records_variables.json') +CONSUMPTION_PATH = os.path.join(TAXCALC_PATH, 'consumption.json') +BEHAVIOR_PATH = os.path.join(TAXCALC_PATH, 'behavior.json') +GROWDIFF_PATH = os.path.join(TAXCALC_PATH, 'growdiff.json') +OUTPUT_PATH = os.path.join(CURDIR_PATH, OUTPUT_FILENAME) + + +def main(): + """ + Contains high-level logic of the script. + """ + # read INPUT file into text variable + with open(INPUT_PATH, 'r') as ifile: + text = ifile.read() + + # augment text variable with do-not-edit warning + old = '' + new = ('\n' + '') + text = text.replace(old, new) + + # augment text variable with information from JSON files + text = policy_params(POLICY_PATH, text) + text = io_variables('read', IOVARS_PATH, text) + text = io_variables('calc', IOVARS_PATH, text) + text = response_params('consumption', CONSUMPTION_PATH, text) + text = response_params('behavior', BEHAVIOR_PATH, text) + text = response_params('growdiff', GROWDIFF_PATH, text) + + # write text variable to OUTPUT file + with open(OUTPUT_PATH, 'w') as ofile: + ofile.write(text) + + # normal return code + return 0 +# end of main function code + + +def policy_param_text(pname, param): + """ + Extract info from param for pname and return as HTML string. + """ + # pylint: disable=too-many-branches,len-as-condition + sec1 = param['section_1'] + if len(sec1) > 0: + txt = '

{} — {}'.format(sec1, param['section_2']) + else: + txt = '

{} — {}'.format('Other Parameters', + 'Not in TaxBrain GUI') + txt += '
tc Name: {}'.format(pname) + if len(sec1) > 0: + txt += '
TB Name: {}'.format(param['long_name']) + else: + txt += '
Long Name: {}'.format(param['long_name']) + txt += '
Description: {}'.format(param['description']) + if len(param['notes']) > 0: + txt += '
Notes: {}'.format(param['notes']) + if param['cpi_inflated']: + txt += '
Inflation Indexed: True' + else: + txt += '
Inflation Indexed: False' + if param['integer_value']: + txt += '     Integer Value: True' + else: + txt += '     Integer Value: False' + if param['boolean_value']: + txt += '     Boolean Value: True' + else: + txt += '     Boolean Value: False' + txt += '
Known Values:' + if len(param['col_label']) > 0: + cols = ', '.join(param['col_label']) + txt += '
   for: [{}]'.format(cols) + for cyr, val in zip(param['row_label'], param['value']): + final_cyr = cyr + final_val = val + txt += '
{}: {}'.format(cyr, val) + if not param['cpi_inflated']: + fcyr = int(final_cyr) + if fcyr < Policy.LAST_KNOWN_YEAR: + # extrapolate final_val thru Policy.LAST_KNOWN_YEAR if not indexed + for cyr in range(fcyr + 1, Policy.LAST_KNOWN_YEAR + 1): + txt += '
{}: {}'.format(cyr, final_val) + txt += '
Valid Range:' + if param['range']['min'] == 'default': + minval = 'known_value' + else: + minval = param['range']['min'] + if param['range']['max'] == 'default': + maxval = 'known_value' + else: + maxval = param['range']['max'] + txt += ' min = {} and max = {}'.format(minval, maxval) + txt += '
Out-of-Range Action: {}'.format( + param['out_of_range_action']) + txt += '

' + return txt + + +def policy_params(path, text): + """ + Read policy parameters from path, integrate them into text, and + return the integrated text. + """ + with open(path) as pfile: + params = json.load(pfile, object_pairs_hook=OrderedDict) + assert isinstance(params, OrderedDict) + # construct section dict containing sec1_sec2 titles + concat_str = ' @ ' + section = OrderedDict() + using_other_params_section = False + for pname in params: + param = params[pname] + sec1_sec2 = '{}{}{}'.format(param['section_1'], + concat_str, + param['section_2']) + if sec1_sec2 == concat_str: + using_other_params_section = True + elif sec1_sec2 not in section: + section[sec1_sec2] = 0 + if using_other_params_section: + section[concat_str] = 0 + # construct parameter text for each sec1_sec2 in section + for sec1_sec2 in section: + split_list = sec1_sec2.split(concat_str) + sec1 = split_list[0] + sec2 = split_list[1] + ptext = '' + for pname in params: + param = params[pname] + if sec1 == param['section_1'] and sec2 == param['section_2']: + ptext += policy_param_text(pname, param) + # integrate parameter text into text + old = ''.format(sec1_sec2) + text = text.replace(old, ptext) + return text + + +def var_text(vname, iotype, variable): + """ + Extract info from variable for vname of iotype + and return info as HTML string. + """ + if iotype == 'read': + txt = '

Input Variable Name: {}'.format(vname) + if 'required' in variable: + txt += '
Required Input Variable' + else: + txt = '

Output Variable Name: {}'.format(vname) + txt += '
Description: {}'.format(variable['desc']) + txt += '
Datatype: {}'.format(variable['type']) + if iotype == 'read': + txt += '
Availability: {}'.format(variable['availability']) + txt += '
IRS Form Location:' + formdict = variable['form'] + for yrange in sorted(formdict.keys()): + txt += '
{}: {}'.format(yrange, formdict[yrange]) + txt += '

' + return txt + + +def io_variables(iotype, path, text): + """ + Read variables for iotype ('read' for input or 'calc' for output) + from path, integrate them into text, and return the integrated text. + """ + with open(path) as vfile: + variables = json.load(vfile) + assert isinstance(variables, dict) + # construct variable text + vtext = '' + for vname in sorted(variables[iotype].keys()): + vtext += var_text(vname, iotype, variables[iotype][vname]) + # integrate variable text into text + old = ''.format(iotype) + text = text.replace(old, vtext) + return text + + +def response_param_text(pname, ptype, param): + """ + Extract info from param for pname of ptype and return as HTML string. + """ + # pylint: disable=len-as-condition + sec1 = param['section_1'] + if len(sec1) > 0: + txt = '

{} — {}'.format(sec1, param['section_2']) + else: + txt = '

{} — {}'.format('Response Parameter', + ptype.capitalize()) + txt += '
tc Name: {}'.format(pname) + if len(sec1) > 0: + txt += '
TB Name: {}'.format(param['long_name']) + else: + txt += '
Long Name: {}'.format(param['long_name']) + txt += '
Description: {}'.format(param['description']) + if len(param['notes']) > 0: + txt += '
Notes: {}'.format(param['notes']) + txt += '
Default Value:' + if len(param['col_label']) > 0: + cols = ', '.join(param['col_label']) + txt += '
   for: [{}]'.format(cols) + for cyr, val in zip(param['row_label'], param['value']): + txt += '
{}: {}'.format(cyr, val) + txt += '

' + return txt + + +def response_params(ptype, path, text): + """ + Read response parameters of ptype from path, integrate them into text, + and return the integrated text. + """ + with open(path) as pfile: + params = json.load(pfile, object_pairs_hook=OrderedDict) + assert isinstance(params, OrderedDict) + # construct parameter text for each param + ptext = '' + for pname in params: + param = params[pname] + ptext += response_param_text(pname, ptype, param) + # integrate parameter text into text + old = ''.format(ptype) + text = text.replace(old, ptext) + return text + + +if __name__ == '__main__': + sys.exit(main()) From 6ea5277dc70584ebe1fac95dae9a837ced4f2b53 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Tue, 21 Nov 2017 09:44:09 -0500 Subject: [PATCH 02/33] Revise cookbook/recipe00.py and add recipe00.res file --- docs/cookbook/recipe00.py | 36 +++++++++++++------------ docs/cookbook/recipe00.res | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 docs/cookbook/recipe00.res diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index c27945c70..3105c175e 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -1,7 +1,3 @@ -""" -RECIPE00.PY -""" - from __future__ import print_function # necessary only if using Python 2.7 from taxcalc import * @@ -14,30 +10,36 @@ pol = Policy() calc1 = Calculator(policy=pol, records=recs) -# read JSON reform file and use default (that is, static) analysis assumptions +# NOTE: calc1 now contains a PRIVATE COPY of pol and a PRIVATE COPY of recs, +# so we can continue to use pol and recs in this script without any +# concern about side effects from Calculator method calls. + +# calculate aggregate current-law income tax liabilities for 2018 +calc1.advance_to_year(2018) +calc1.calc_all() +itax_rev1 = calc1.weighted_total('iitax') + +# read JSON reform file and use (the default) static analysis assumptions reform_filename = './ingredients/repeal_amt.json' params = Calculator.read_json_param_objects(reform=reform_filename, assump=None) # specify Calculator object for static analysis of reform policy pol.implement_reform(params['policy']) -if pol.reform_errors != '': # check for reform error messages +if pol.reform_errors: # check for reform error messages print(pol.reform_errors) exit(1) calc2 = Calculator(policy=pol, records=recs) -# calculate tax liabilities for 2018 -calc1.advance_to_year(2018) -calc1.calc_all() +# calculate reform income tax liabilities for 2018 calc2.advance_to_year(2018) calc2.calc_all() +itax_rev2 = calc2.weighted_total('iitax') -# compute total income tax liability under current law and the reform -# (records.iitax contains each filing unit's individual income tax liability -# and records.s006 contains each filing units's sampling weight) -itax_rev1 = (calc1.records.iitax * calc1.records.s006).sum() -itax_rev2 = (calc2.records.iitax * calc2.records.s006).sum() +# print reform documentation +print(Calculator.reform_documentation(params)) -# print total revenue estimates for 2018 in whole billons of dollars -print('2018_CLP_itax_rev($B)= {:.0f}'.format(itax_rev1 * 1e-9)) -print('2018_REF_itax_rev($B)= {:.0f}'.format(itax_rev2 * 1e-9)) +# print total revenue estimates for 2018 +# (estimates in billons of dollars rounded to nearest tenth of a billion) +print('2018_CLP_itax_rev($B)= {:.1f}'.format(itax_rev1 * 1e-9)) +print('2018_REF_itax_rev($B)= {:.1f}'.format(itax_rev2 * 1e-9)) diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res new file mode 100644 index 000000000..034fac3af --- /dev/null +++ b/docs/cookbook/recipe00.res @@ -0,0 +1,55 @@ +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. +REFORM DOCUMENTATION +Baseline Growth-Difference Assumption Values by Year: +none: using default baseline growth assumptions +Policy Reform Parameter Values by Year: +2018: + _AMT_rt1 : 0.0 + name: AMT rate 1 + desc: The tax rate applied to the portion of AMT taxable income below the + surtax threshold, AMT bracket 1. + baseline_value: 0.26 + _AMT_rt2 : 0.0 + name: Additional AMT rate for AMT taxable income above AMT bracket 1 + desc: The additional tax rate applied to the portion of AMT income above the + AMT bracket 1. + baseline_value: 0.02 + _II_rt5 : 0.35 + name: Personal income (regular/non-AMT/non-pass-through) tax rate 5 + desc: The third highest tax rate, applied to the portion of taxable income + below tax bracket 5 and above tax bracket 4. + baseline_value: 0.33 + _II_rt6 : 0.37 + name: Personal income (regular/non-AMT/non-pass-through) tax rate 6 + desc: The second higher tax rate, applied to the portion of taxable income + below tax bracket 6 and above tax bracket 5. + baseline_value: 0.35 + _II_rt7 : 0.42 + name: Personal income (regular/non-AMT/non-pass-through) tax rate 7 + desc: The tax rate applied to the portion of taxable income below tax + bracket 7 and above tax bracket 6. + baseline_value: 0.396 + _PT_rt5 : 0.35 + name: Pass-through income tax rate 5 + desc: The third highest tax rate, applied to the portion of income from sole + proprietorships, partnerships and S corporations below tax bracket 5 + and above tax bracket 4. + baseline_value: 0.33 + _PT_rt6 : 0.37 + name: Pass-through income tax rate 6 + desc: The second higher tax rate, applied to the portion of income from sole + proprietorships, partnerships and S corporations below tax bracket 6 + and above tax bracket 5. + baseline_value: 0.35 + _PT_rt7 : 0.42 + name: Pass-through income tax rate 7 + desc: The highest tax rate, applied to the portion of income from sole + proprietorships, partnerships and S corporations below tax bracket 7 + and above tax bracket 6. + baseline_value: 0.396 + +2018_CLP_itax_rev($B)= 1287.5 +2018_REF_itax_rev($B)= 1283.1 From 654049fa75f6d9685a199e4bbe34dce3845ba3a6 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Tue, 21 Nov 2017 12:15:17 -0500 Subject: [PATCH 03/33] Add new docs/cookbook/test_recipes.py file --- docs/cookbook/test_recipes.py | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 docs/cookbook/test_recipes.py diff --git a/docs/cookbook/test_recipes.py b/docs/cookbook/test_recipes.py new file mode 100644 index 000000000..2cd6ee182 --- /dev/null +++ b/docs/cookbook/test_recipes.py @@ -0,0 +1,56 @@ +""" +Tests all recipes by executing each recipeNN.py script and comparing +the script output with the expected output in the recipeNN.res file. +""" +# CODING-STYLE CHECKS: +# pep8 --ignore=E402 test_recipes.py +# pylint --disable=locally-disabled test_recipes.py + +from __future__ import print_function +from datetime import datetime +import os +import glob +import string +import subprocess +import difflib + + +# print start time +print('{}'.format(datetime.now())) + +# make list of recipeNN.py filenames +CURDIR_PATH = os.path.abspath(os.path.dirname(__file__)) +RECIPES = glob.glob('./recipe[0-9][0-9].py') + +# execute each recipe in RECIPES list and compare output with expected output +for recipe in RECIPES: + out_filename = string.replace(recipe, '.py', '.out') + if os.path.isfile(out_filename): + os.remove(out_filename) + try: + out = subprocess.check_output(['python', recipe], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + print('{} FAIL with error rtncode={}'.format(recipe, err.returncode)) + continue # to next recipe + with open(string.replace(recipe, '.py', '.res'), 'r') as resfile: + exp = resfile.read() + # check for differences between out and exp results + actual = out.splitlines(True) + expected = exp.splitlines(True) + diff_lines = list() + diff = difflib.unified_diff(expected, actual, + fromfile='expected', tofile='actual', n=0) + for line in diff: + diff_lines.append(line) + # write actual output to file if any differences; else report PASS + if diff_lines: + print('{} FAIL with output differences'.format(recipe)) + outfilename = string.replace(recipe, '.py', '.out') + with open(outfilename, 'w') as outfile: + outfile.write(out) + else: + print('{} PASS'.format(recipe)) + +# print finish time +print('{}'.format(datetime.now())) From 630e21bed0efc773598d5fc8bf4cb19f90a7b05c Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Sun, 26 Nov 2017 10:30:37 -0500 Subject: [PATCH 04/33] Initial outline of cookbook.htmx --- docs/cookbook.htmx | 1065 ++------------------------------------------ 1 file changed, 44 insertions(+), 1021 deletions(-) diff --git a/docs/cookbook.htmx b/docs/cookbook.htmx index a56e0df9e..62ffe79d1 100644 --- a/docs/cookbook.htmx +++ b/docs/cookbook.htmx @@ -2,7 +2,7 @@ -Using Tax-Calculator +Tax-Calculator Cookbook @@ -10,1059 +10,82 @@ div { max-width: 19cm }
-

Using Tax-Calculator

+

A Cookbook of Tested Recipes for Python Programming with +Tax-Calculator

This document tells you how to use Tax-Calculator, an open-source -federal income and payroll tax simulation model. The two ways of -using Tax-Calculator described here require no computer programming. -If you want to participate in the development of Tax-Calculator -— by asking a question, reporting a bug, improving the documentation -or making an enhancement to the Python source code — you should go to -the -developer website.

+federal income and payroll tax simulation model, in Python scripts +that you can run on your own computer. For other ways of using +Tax-Calculator, see the + +user documentation.

+

Cookbook Contents

-

Document Contents

+

Preliminaries

-

TaxBrain GUI to use Tax-Calculator via an -interactive web application

-

tc CLI to use Tax-Calculator on your computer -via the command line

-

Policy Parameters that specify tax reforms

-

Input Variables that specify tax filing units

-

Output Variables that describe tax results

-

Response Parameters that specify reform responses

+

Kitchen Setup on how to setup your computer to +follow these recipes

+

Recipe Techniques on rules to follow when +modifying the recipes in this cookbook

-

TaxBrain GUI

+

Recipe Ingredients on how get the +ingredients required for these recipes in your kitchen

-

You can use Tax-Calculator via a graphical user interface (GUI) -provided by the TaxBrain web -application. This approach requires only an Internet connection -and web browser on your computer. TaxBrain (TB) uses a micro dataset -to produce both aggregate and distributional reform estimates for each -of ten years for a tax reform you specify. The calculations take just -a couple of minutes and there is flexibility to show different kinds -of distributional results as well as aggregate revenue results.

+

Recipe Feedback on how request a new recipe +be added to this cookbook or
report problems encountered when +following an existing recipe

+

Basic Recipes

-

Section Contents

+

Static Analysis of a Simple Reform

-

Specify Tax Reform

-

Initiate Static Analysis

-

View/Download Static Results

-

Initiate Dynamic Analysis

+

Advanced Recipes

+

coming soon

-

Specify Tax Reform

-

The first step in using TaxBrain is to set the Start Year for the -tax calculations. Then specify the values of policy parameters that -you want to change in your reform.

+

Preliminaries: Kitchen Setup

-

By default the policy parameter boxes contain Start Year values. -Simply replace the current-law value with the Start Year reform value. -If some or all of your reform provisions are going to begin after the -Start Year, simply fill in the pre-reform years with asterisks. So, -for example, if Start Year is 2017 and your reform calls for raising -the social security payroll tax rate to 13 percent in 2018 and then -to 14 percent in 2020, you would enter in that box the following: -

*,0.13,*,0.14
-This parameter is not indexed, so the 0.13 value for 2018 will be -extrapolated to 2019, and the 0.14 value for 2020 will be in force -in all subsequent years.

+

Text goes here.

-

Back to Section Contents

+

Back to Cookbook Contents

-

Initiate Static Analysis

+

Preliminaries: Recipe Techniques

-

Simply click on the Show me the results! button to start the tax -calculations. That button will use the full sample and take a couple -of minutes to compute revenue and distributional results for each of -ten years beginning with the Start Year you specified.

+

Text goes here.

-

There is also a Quick Calculation! button that produces results -only for the Start Year using a sub-sample of the full sample.

+

Back to Cookbook Contents

-

Back to Section Contents

+

Preliminaries: Recipe Ingredients

-

View/Download Static Results

+

Text goes here.

-

After the tax calculations are completed, you are shown a results -page that presents various options: you can specify, view and download -a total liabilities table, your can specify, view and download a -distribution table, you can use your reform to conduct different types -of dynamic analysis (via the button labeled Link to Dynamic -Simulations), and finally, you can go back to the policy parameters -page to modify your reform (via the button labeled Edit Parameters).

+

Back to Cookbook Contents

-

There are three variants of the total liabilities table. -The variant shown initially is Change, meaning the amounts are the -difference between liabilities under your reform and the liabilities -projected for current-law policy. You can also elect to see the -reform and current-law levels.

-

There are many variants of the distribution table. Try -clicking on the options to the left to define the nature of the table -and the year for which it is constructed. Try clicking on the options -to the right to specify which columns are included in the table. An -easy way to uncheck all the column boxes is to double click on the -Select All box.

+

Preliminaries: Recipe Feedback

-

There are three download options for each of these two tables:

+

If you want to request a recipe that makes a new dish, +create a new issue +here +providing details on what you want to make and why the existing +recipes cannot be easily modified to make what you want.

-

Print creates a new page in your browser that is suitable -for printing.

+

Also, please report, in the same way, any problems you experience +following an existing recipe.

-

Copy puts the table on the clipboard of your local computer -so that you can paste it anywhere on your computer. The copy on the -clipboard uses invisible tab characters to separate the table's -columns.

+

Back to Cookbook Contents

-

CSV downloads to your computer a CSV-formatted file -containing the information in the table. CSV-formatted files can be -easily imported into any spreadsheet program.

-

Back to Section Contents

+

Basic Recipes: Static Analysis of a Simple Reform

+

Text goes here.

-

Initiate Dynamic Analysis

- -

After clicking on the Link to Dynamic Simulations on the static -results page, you are presented with a page that allows you to choose -three different types of dynamic simulations to conduct on your tax -reform:

- -

Partial Equilibrium Simulation is described this way: -This approach answers the question, how would taxpayer behavior -(income and deductions) affect revenue if all other prices in the -economy could stay the same?

- -

Overlapping Generations Simulation is described this way: -This approach answers the question, how does tax policy affect -macroeconomic aggregates and prices?

- -

Macro Elasticity Simulation is described this way: -This approach harnesses econometric estimates of the historical -relationship between tax policy and the macro economy to predict the -effect of tax reforms on economic growth.

- -Clicking on one of these choices will allow you to specify some -addition economic response parameters and initiate the dynamic -analysis using your response parameter assumptions. - -

Back to Section Contents   -Back to Document Contents

- - -

tc CLI

- -

You can use Tax-Calculator on your own computer via a command line -interface (CLI) called tc. This approach requires the installation of -Anaconda Python and the taxcalc package on your computer as well as -the use of a text editor to prepare simple files that are read by tc. -Computer programming knowledge is not required, but if you are -prevented from doing the easy installation or you are not willing to -work at the command line (Terminal on Mac or Command Prompt on -Windows) and use an editor (for example, TextEdit on Mac or Notepad on -Windows), then you should probably use the TaxBrain GUI.

- -

If you are comfortable using an editor to prepare text files that -describe your tax reforms, but don't want to install Python and tc on -your computer, you can upload reform files to TaxBrain where all the -analysis is conducted (as described in the previous section). If you -are interested in this file-upload approach, skip the first part of -this section, read the next part on how to specify policy reform -files, and then skip to the last part of this section on how to upload -files to TaxBrain.

- - -

Section Contents

- -

Install/Test tc CLI

-

Specify Tax Reform

-

Specify Analysis Assumptions

-

Specify Filing Units

-

Initiate Reform Analysis

-

Tabulate Reform Results

-

Upload Files to TaxBrain

- - -

Install/Test tc CLI

- -

Installation of tc CLI involves several steps:

- -

Install the free Anaconda Python distribution by going to -the Continuum Analytics -download page and selecting a version of Python. You can install -either Python 2.7 or Python 3.6 because we test Tax-Calculator with -both. You must do this installation even if you already -have Python installed on your computer because the Anaconda -distribution contains all the additional Python packages that -Tax-Calculator uses to conduct tax calculations (many of which are not -included in other Python installations). You can install the Anaconda -distribution without having administrative privileges on your computer -and the Anaconda distribution will not interfere with any Python -installation that came as part of your computer’s operating -system.

- -

Check your Anaconda Python installation by entering the -following two commands at the operating system command prompt (which -is shown as $ here, but would be > on Windows) -in any directory: -

$ conda --version
-Expected output is something like conda 4.3.17 -
$ python --version
-Expected output should contain either the Python 2.7 or -the Python 3.6 phrase as well as the -Anaconda phrase, the presence of which confirm that the -installation went smoothly. - -

Install the free taxcalc package by entering the following: -

$ conda install -c ospc taxcalc
-Expected output should contain information about all the additional -packages that Tax-Calculator needs. After all that information has -been shown on the screen, press the y key to proceed with -the package installation.

- -

Check your installation of tc (the CLI to Tax-Calculator) by -entering the following: -

$ tc --test
-Expected output (after about twenty seconds) is PASSED -TEST. If you get FAILED TEST, something went wrong -in the installation process. If the installation test fails, please -report your experience by creating a new issue at - -this website or by sending an email to one of the core maintainers -of Tax-Calculator listed at the bottom of the What is TaxBrain? -page at this website. - -

If your installation passes the test, you are ready to begin using -tc to analyze your tax reforms. Continue reading this section for -information about how to do that. But if you want a quick hint about -the range of tc capabilities, enter the following: -

$ tc --help
-

- -

The basic idea of tc tax analysis is that each tax reform is -specified in a text file using a simple method to describe the details -of the reform. Read the next part of this section to see how policy -reform files are formatted.

- -

Back to Section Contents

- - -

Specify Tax Reform

- -

The details of a tax reform are contained in a text file that you -write with a text editor. The reform is expressed by specifying which -tax policy parameters are changed from their current-law values by the -reform. The timing and magnitude of these policy parameter changes -are written in JSON, a simple and widely-used data-specification -language.

- -

For several examples of reform files and the general rules for -writing JSON reform files, go -to this -page.

- -

If you want to upload reform files to TaxBrain for static analysis -(rather than use the tc CLI on your computer for analysis), go to this -section's last part, which -discusses uploading files to TaxBrain. If you want to assume any -responses to your tax reform (that is, if you want to conduct -non-static analysis), read the next part before going to the last -part.

- -

Back to Section Contents

- - -

Specify Analysis Assumptions

- -

This part explains how to specify response assumption files used in -non-static tax analysis. If you want to start out doing static -analysis, you can skip this part now and come back to read it whenever -you want to go beyond static analysis. -The next part of this section discusses -filing-unit input files.

- -

The details of economic response assumptions are contained in a -text file that you write with a text editor. The assumptions are -expressed by specifying which response parameters are changed from -their default values, all of which are zero. The timing and magnitude of -these response parameter changes are written in JSON, a simple and -widely-used data-specification language.

- -

For examples of response assumption files and the general rules for -writing JSON assumption files, go -to this -page.

- -

If you want to upload policy reform and response assumption files -to TaxBrain, you can go directly to this -section's last part, which -discusses uploading files to TaxBrain. If you want to analyze your -tax reforms on your own computer, go to the next part on how to -specify tax filing units used in the analysis.

- -

Back to Section Contents

- - -

Specify Filing Units

- -

The taxcalc package containing the tc CLI to Tax-Calculator does -not include the microsimulation sample used by TaxBrain. This is -because, unlike Census survey public-use files, the IRS-SOI Public Use -File (PUF) is proprietary. If you or your organization has paid IRS -to use the PUF version being used by TaxBrain, then it may be possible -for us to share with you our TaxBrain sample, which we -call puf.csv even though it contains CPS records that -represent non-filers. Otherwise, you have three choices.

- -

First, you can easily create with an editor a CSV-formatted -file containing several filing units whose experience under your tax -reform is of interest to you. Much of the public discussion of tax -reforms is of this type: how is this family or that family affected by -a reform; how do they fare under different reforms; etc. Notice that -this kind of analysis of a few exemplary families is something you -cannot do on TaxBrain. The test conducted to check the tc -installation has left one such file. It is called test.csv -and contains two filing units with only wage and salary income: a -lower income family and a higher income family. You can use the -test.csv file as tc input to analyze your tax reforms. -Before creating your own input files be sure to read the short set of -guidelines that appear after this list of three choices.

- -

Second, recently we made freely -available a public-use file containing only filing units derived from -several recent March CPS surveys. For several reasons, the results generated -by this cps.csv file are substantially different from the results -generated by the puf.csv file. The cps.csv file -contains a sample of the population while the puf.csv file -contains mostly a sample of income tax filers in which high-income -filing units are over represented. Also, the cps.csv file -has many income variables that are missing (and assumed to be zero by -Tax-Calculator), which causes an understating of total incomes, -especially for those with high incomes. All these differences -mean that the aggregate revenue and distributional results generated -when using the cps.csv file as input to Tax-Calculator can -be substantially different from the results generated when using the -puf.csv file as input. And this is particularly true when -analyzing reforms that change the tax treatment of high-income filers.

- -

Third, when you want to estimate how your reform affects -total tax liabilities and/or the distribution of tax liabilities, you -can always upload your policy reform and response assumption files -to TaxBrain. Combining this option with the first option provides a -complete tax analysis capability. -The last part of this section -describes how to upload files to TaxBrain.

- -

Input-File-Preparation Guidelines

- -

The tc CLI to Tax-Calculator is flexible enough to read almost any -kind of CSV-formatted input data on filing units as long as the -variable names correspond to those expected by Tax-Calculator. The -only required input variables are RECID (a unique -filing-unit record identifier) and MARS (a positive-valued -filing-status indicator). Other variables in the input file must have -variable names that are listed in the Input -Variables section for them to affect the tax calculations. Any -variable listed in Input Variables that is not in an input file is -automatically set to zero for every filing unit. Variables -in the input file that are not listed in Input Variables are ignored -by Tax-Calculator.

- -

However, there are important data-preparation issues related to the -fact that the payroll tax is a tax on individuals, not on income-tax -filing units. Tax-Calculator expects that the filing-unit total for -each of several earnings-related variables is split between the -taxpayer and the spouse. It is the responsibility of anyone preparing -data for Tax-Calculator input to do this earnings splitting. Here are -the relationships between the filing-unit variable and the taxpayer -(p) and spouse (s) variables expected by -Tax-Calculator: -

-e00200 = e00200p + e00200s
-e00900 = e00900p + e00900s
-e02100 = e02100p + e02100s
-
-Obviously, when MARS is not equal to 2 (married filing -jointly), the values of the three s variables are zero and -the value of each p variable is equal to the value of its -corresponding filing-unit variable. Note that the input file can -omit any one, or all, of these three sets variables. If the three -variables in one of these sets are omitted, the required relationship -will be satisfied because zero equals zero plus zero.

- -

But when including one of these three sets of variables, it is up -to you to specify the taxpayer-spouse split. You will get an error -message from Tax-Calculator, and it will stop running, if you do not -split the filing-unit amount between taxpayer and spouse so that the -above equations hold for each filing unit in the input file.

- -

In addition to this earnings-splitting data-preparation issue, -Tax-Calculator expects that the value of ordinary dividends -(e00600) will be no less than the value of qualified -dividends (e00650) for each filing unit. Again, it is your -responsibility to prepare input data in a way that ensures this -relationship is true for each filing unit.

- -

Back to Section Contents

- - -

Initiate Reform Analysis

- -

The tc CLI to Tax-Calculator requires only an input file containing -one or more filing units. A policy reform file is optional; no reform -file implies you want to analyze current-law policy. A response -assumption file is also optional; no assumption file implies you want -to conduct static analysis.

- -

Here we explain how to conduct tax analysis with the tc CLI by -presenting a series of examples and explaining what output is produced -in each example. There are several types of output that tc can -generate so there will be more than a few examples. The examples are -numbered in order to make it easier to refer to different examples. -All the examples assume that the input file is test.csv, -which was mentioned in previous part of this section.

- -

-(1)$ tc test.csv 2020
-

- -

Example (1) produces a minimal output file containing 2020 tax -liabilities for each filing unit assuming the income amounts in the -input file are amounts for 2020 and assuming current-law tax policy -projected to 2020. The name of the CSV-formatted output file is -test-20-#-#.csv. -The first # symbol indicates we did not specify a -policy reform file and the second # symbol indicates we did -not specify a response assumption file. The variables included -in the minimal output file include: -RECID (of filing unit in the input file), -YEAR (specified when executing tc CLI), -WEIGHT (which is same as s006), -INCTAX (which is same as iitax), -LSTAX (which is same as lumpsum_tax) and -PAYTAX (which is same as payroll_tax). -

- -

Also, documentation of the reform is always written to a text file -ending in -doc.text, which in this example would be -named test-20-#-#-doc.text.

- -

-(2)$ tc test.csv 2020 --dump
-

- -

Example (2) produces a much more complete output file with the same -name test-20-#-#.csv as the minimal output file produced in -example (1). No other output is generated other than the -test-20-#-#-doc.text file. The ---dump option causes all the input variables (including the -one's understood by Tax-Calculator but not included -in test.csv, which are all zero) and all the output -variables calculated by Tax-Calculator to be included in the output -file. For a complete list of input variables, see -the Input Variables section. For a complete list -of output variables, see the Output Variables -section. Since Tax-Calculator ignores variables in the input file -that are not in the Input Variables section, the dump output file in -example (2) can be used as an input file and it will produce exactly -the same tax liabilities (apart from rounding errors of one or two -cents) as in the original dump output.

- -

-(3)$ tc test.csv 2020 --sqldb
-

- -

Example (3) produces the same dump output as example (2) except -that the dump output is written not to a CSV-formatted file, but to -the dump table in an SQLite3 database file, which is -called test-20-#-#.db in this example. Because -the --dump option is not used in example (3), minimal -output will be written to the test-20-#-#.csv file.

- -

Pros and cons of putting dump output in a CSV file or an SQLite3 -database table: The CSV file is almost twice as large as the -database, but it can be easily imported into a wide range of -statistical packages and spreadsheets. The main advantage of the -SQLite3 database is that the Anaconda Python distribution includes -sqlite3 (or sqlite3.exe -on Windows), a command-line tool that can be used to tabulate dump -output using structured query language (SQL). SQL is a language that -you use to specify the tabulation you want and the SQL database -figures out the procedure for generating your tabulation and then -executes that procedure; there is no computer programming involved. -We will give an example of SQL tabulation of dump output in the next -part of this section.

- -

-(4)$ tc test.csv 2020 --dump --sqldb
-

- -

Example (4) shows that you can get dump output in the two different -formats from a single tc run.

- -

The remaining examples use neither the --dump nor the ---sqldb option, and thus, produce minimal output for the -reform. But either or both of those options could be used in all the -subsequent examples to generate more complete output for the reform.

- -

-(5)$ tc test.csv 2021 --reform ref3.json
-

- -

Example (5) produces 2021 output for the filing units in the -test.csv file using the policy reform specified in -the ref3.json file. Because no --assump -option is used, the output results are produced using static analysis. -The name of the output file in this example is -test-21-ref3-#.csv.

- -

-(6)$ tc test.csv 2021 --reform ref3.json --assump res1.json
-

- -

Example (6) produces 2021 output for the filing units in the -test.csv file using the policy reform specified in -the ref3.json file and the response assumptions -specified in the res1.json file. The output results -produced by this non-static analysis are written to the -test-21-ref3-res1.csv file.

- -

The following examples illustrate output options that work only if -each filing unit in the input file has a positive sampling weight -(s006). So, we are going to use the cps.csv -file in these examples along with the policy reform specified in -the ref3.json file, the content of which is: -

-// ref3.json raises personal exemption amount to 8000 in 2022,
-// after which it continues to be indexed to price inflation.
-{
-    "policy": {
-        "_II_em": {"2022": [8000]}
-    }
-}
-
-The output options illustrated in the following examples generate -tables of the post-reform level and the reform-induced change in tax -liability by income deciles as well as graphs of marginal and average -tax rates by income percentile. - -

-(7)$ tc cps.csv 2022 --reform ref3.json --tables
-You loaded data for 2014.
-Tax-Calculator startup automatically extrapolated your data to 2022.
-
-(7)$ ls cps-22*
-cps-22-ref3-#-doc.text	cps-22-ref3-#-tab.text	cps-22-ref3-#.csv
-
-(7)$ cat cps-22-ref3-#-tab.text
-Weighted Tax Reform Totals by Baseline Expanded-Income Decile
-    Returns    ExpInc    IncTax    PayTax     LSTax    AllTax
-       (#m)      ($b)      ($b)      ($b)      ($b)      ($b)
- 0    17.93      -3.6      -2.1       3.6       0.0       1.5
- 1    17.93     202.8     -15.4      13.7       0.0      -1.7
- 2    17.93     353.5     -19.2      25.3       0.0       6.0
- 3    17.93     513.8     -16.9      47.0       0.0      30.1
- 4    17.93     696.3      -1.2      72.3       0.0      71.2
- 5    17.93     928.0      24.0     104.3       0.0     128.2
- 6    17.93    1246.3      59.9     147.5       0.0     207.4
- 7    17.93    1723.5     121.0     215.3       0.0     336.3
- 8    17.93    2497.1     224.8     317.9       0.0     542.6
- 9    17.93    5949.7    1099.9     517.8       0.0    1617.8
- A   179.26   14107.4    1474.8    1464.8       0.0    2939.6
-
-Weighted Tax Differences by Baseline Expanded-Income Decile
-    Returns    ExpInc    IncTax    PayTax     LSTax    AllTax
-       (#m)      ($b)      ($b)      ($b)      ($b)      ($b)
- 0    17.93      -3.6       0.0       0.0       0.0       0.0
- 1    17.93     202.8      -0.2       0.0       0.0      -0.2
- 2    17.93     353.5      -1.8       0.0       0.0      -1.8
- 3    17.93     513.8      -4.2       0.0       0.0      -4.2
- 4    17.93     696.3      -7.3       0.0       0.0      -7.3
- 5    17.93     928.0     -11.1       0.0       0.0     -11.1
- 6    17.93    1246.3     -17.5       0.0       0.0     -17.5
- 7    17.93    1723.5     -22.7       0.0       0.0     -22.7
- 8    17.93    2497.1     -31.2       0.0       0.0     -31.2
- 9    17.93    5949.7     -26.3       0.0       0.0     -26.3
- A   179.26   14107.4    -122.3       0.0       0.0    -122.3
-

- -

Example (7) produces 2022 static output for the filing units in the -cps.csv file using the policy reform specified in -the ref3.json file. Notice that Tax-Calculator knows to -extrapolate (or age) filing unit data in the cps.csv -file to the specified tax year. It knows to do that because of the -special input file name cps.csv. The tables produced by -this static analysis are written to the -cps-22-ref3-#-tab.text file. Note that on Windows -you would use dir instead of ls and -type instead of cat.

- -

-(8)$ tc cps.csv 2024 --reform ref3.json --graphs
-You loaded data for 2014.
-Tax-Calculator startup automatically extrapolated your data to 2024.
-
-(8)$ ls cps-24*
-cps-24-ref3-#-atr.html	cps-24-ref3-#-mtr.html
-cps-24-ref3-#-doc.text	cps-24-ref3-#.csv
-

- -

Example (8) is like example (7) except we ask for 2024 static -output and for graphs instead of tables, although we could ask for -both. The HTML files containing the graphs can be viewed in your -browser.

- -

Here is what the average tax rate graph -in cps-24-ref3-#-atr.html looks like.

- -

atr graph

- -

Here is what the marginal tax rate graph -in cps-24-ref3-#-mtr.html looks like:

- -

mtr graph

- -

There is yet another tc CLI output option that writes to the screen -results from a normative welfare analysis of the specified policy -reform. This --ceeu option produces experimental results -that make sense only with input files that contain representative -samples of the population such as the cps.csv file. The -name of this option stands for certainty-equivalent expected utility. -If you want to use this output option, you should read the commented -Python source code for the ce_aftertax_income function in -the taxcalc/utils.py file at the - -developer website.

- -

Back to Section Contents

- - -

Tabulate Reform Results

- -

Given that tc CLI output can be written to either CSV-formatted -files or SQLite3 database files, there is an enormous range of -software tools that can be used to tabulate the output. You can use -SAS or R, Stata or MATLAB, or even import output into a spreadsheet -(but this would seem to be the least useful option). If you just -want to compare the contents of two output files, you can use your -favorite graphical diff program to view the two files side by side -with highlighting of numbers that are different. The main point -is to use a software tool that is available to you and that you have -experience using.

- -

Here we give some examples of using the sqlite3 -command-line tool that is part of the Anaconda distribution (so it is -always available when using Tax-Calculator). The first step, of -course, is to use the --sqldb option when running tc. Then -you can use the sqlite3 tool interactively or use it to -execute SQL scripts you have saved in a text file. We'll provide -examples of both those approaches. There are many online tutorials on -the SQL select command; if you want to learn more, search the -Internet.

- -

First, we provide a simple example of using sqlite3 -interactively. This approach is ideal for exploratory data analysis. -Our example uses the cps.csv file as input, but you can do -the following with the output from any input file that has weights -(s006). Also, we specify no policy reform file, so the -output is for current-law policy. What you cannot see from the -following record of the analysis is that the sqlite3 tool -keeps a command history, so pressing the up-arrow key will bring up -the prior command for editing. This feature reduces substantially the -amount of typing required to conduct exploratory data analysis. -

-$ tc cps.csv 2016 --sqldb
-You loaded data for 2014.
-Tax-Calculator startup automatically extrapolated your data to 2016.
-$ sqlite3 cps-16-#-#.db
-SQLite version 3.13.0 2016-05-18 10:57:30
-Enter ".help" for usage hints.
-sqlite> select count(*) from dump;
-456465
-sqlite> select count(*),sum(s006) from dump;
-456465|165398918.379996
-sqlite> select count(*),round(sum(s006)*1e-6,3) from dump;
-456465|165.399
-sqlite> select round(sum(s006)*1e-6,3) from dump where MARS=2;
-68.234
-sqlite> select MARS,round(sum(s006)*1e-6,3) from dump group by MARS;
-1|87.385
-2|68.234
-4|9.781
-sqlite> .quit
-$
-
-

- -

Second, we provide a simple example of using sqlite3 -with SQL commands stored in a text file. This approach is useful -if you want to tabulate many different output files in the same way. -This second example assumes that the first example has already -been done. Note that on Windows you should replace cat -with type. -

-
-$ cat tab.sql
--- tabulate weight of those with negative marginal income tax rates
-select "weighted count of those with negative MTR",
-        round(sum(s006)*1e-6,3)
-from dump
-where mtr_inctax < 0;
-
--- construct marginal income tax rate histogram with bin width 10
-select "bin number|weighted count|average MTR in bin";
-select round((mtr_inctax-5)/10) as mtr_bin, -- histogram bin number
-       round(sum(s006)*1e-6,3), -- weight of those in bin (m#)
-       -- weighted average marginal income tax rate on taxpayer earnings in bin:
-       round(sum(mtr_inctax*s006)/sum(s006),2)
-from dump
-where mtr_inctax >= 0  -- include only those with positive MTR
-group by mtr_bin
-order by mtr_bin;
-
--- tabulate weight of all filing units
-select "weighted count of all filing units",
-        round(sum(s006)*1e-6,3)
-from dump;
-
-$ cat tab.sql | sqlite3 cps-16-#-#.db
-weighted count of those with negative MTR|22.434
-bin number|weighted count|average MTR in bin
--1.0|35.34|0.0
-0.0|2.308|7.27
-1.0|56.887|14.11
-2.0|32.926|25.39
-3.0|14.636|32.18
-4.0|0.786|43.0
-5.0|0.069|54.52
-6.0|0.012|66.75
-weighted count of all filing units|165.399
-
-The cat command writes the contents of -the tab.sql file to stdout. We do nothing but that in the -first command in order to show you the file contents. The second -command pipes the contents of the tab.sql file into -the sqlite3 tool, which executes the SQL statements and -writes the tabulation results to stdout. (If you're wondering about -the validity of those high marginal tax rates, rest assured that all -filing units with marginal income tax rates greater than seventy -percent have been checked by hand and are valid: most are -caught in the rapid phase-out of non-refundable education credits. -The negative -marginal tax rates are caused by refundable credits, primarily the -earned income tax credit.)

- -

If you want to use the sqlite3 tool to tabulate the -changes caused by a reform, use the tc CLI to generate two database -dump files (one for current-law policy and the other for your reform) -and then use the SQLite3 ATTACH command to make both database files -available in your SQLite tabulation session.

- - -

Upload Files to TaxBrain

- -

Any policy reform or response assumption files you can use with tc -can be uploaded to trigger a TaxBrain run. As mentioned above the -main advantages of doing this is getting access to the proprietary -puf.csv input file, getting fast execution of both reform -and current-law policy runs for ten years and getting the flexibility -to specify a variety of distributional tables. All the TaxBrain results -can be downloaded to your computer for storage or for additional -analysis.

- -

The files on your computer can be uploaded -at the TaxBrain -file-upload page.

- -

An extended example of how preliminary tax analysis on your -computer can be combined with TaxBrain file-uploading is -here.

- -

Back to Section Contents   -Back to Document Contents

- - -

Policy Parameters

- -

This section contains documentation of policy parameters in a -format that is easy to search and print. The policy parameters are -grouped here as they are are on TaxBrain (TB). Parameters understood -by Tax-Calculator and the tc CLI, but not available in the TaxBrain -GUI, are placed in an Other Parameters group at the end of the -section.

- - -

Section Contents

- -

Parameter Indexing

-

Payroll Taxes

-

Social Security Taxability

-

Above The Line Deductions

-

Personal Exemptions

-

Standard Deduction

-

Nonrefundable Credits

-

Itemized Deductions

-

Capital Gains And Dividends

-

Personal Income

-

Other Taxes

-

Refundable Credits

-

Surtaxes

-

Universal Basic Income

-

Other Parameters

- - -

Parameter Indexing

- - - -

Back to Section Contents

- - -

Payroll Taxes

- - - - - - - -

Back to Section Contents

- - -

Social Security Taxability

- - - - - -

Back to Section Contents

- - -

Above The Line Deductions

- - - - - - - -

Back to Section Contents

- - -

Personal Exemptions

- - - - - - - -

Back to Section Contents

- - -

Standard Deduction

- - - - - -

Back to Section Contents

- - -

Nonrefundable Credits

- - - - - - - - - -

Back to Section Contents

- - -

Itemized Deductions

- - - - - - - - - - - - - - - - - - - - - - - -

Back to Section Contents

- - -

Capital Gains And Dividends

- - - - - - - -

Back to Section Contents

- - -

Personal Income

- - - - - - - -

Back to Section Contents

- - -

Other Taxes

- - - -

Back to Section Contents

- - -

Refundable Credits

- - - - - - - - - -

Back to Section Contents

- - -

Surtaxes

- - - - - - - -

Back to Section Contents

- - -

Universal Basic Income

- - - - - -

Back to Section Contents

- - -

Other Parameters

- - - -

Back to Section Contents   -Back to Document Contents

- - -

Input Variables

- -

This section contains documentation of input variables in a format -that is easy to search and print. The input variables are ordered -alphabetically by name. There are no subsections, just a long list -of input variables that Tax-Calculator is programmed to use in its -calculations. The Availability information indicates which input -data files contain the variable.

- - - -

Back to Section Contents   -Back to Document Contents

- - -

Output Variables

- -

This section contains documentation of output variables in a format -that is easy to search and print. The output variables are ordered -alphabetically by name. There are no subsections, just a long list of -output variables that Tax-Calculator is programmed to calculate.

- - - -

Back to Section Contents   -Back to Document Contents

- - -

Response Parameters

- -

This section contains documentation of several sets of parameters -that characterize responses to a tax reform. Consumption -parameters are used to compute marginal tax rates. Behavior -parameters are used to compute changes in input variables caused by a -tax reform in a partial-equilibrium setting. Growdiff parameters are -used to specify baseline differences and/or reform responses in the -annual rate of growth in economic variables.

- -

All the response parameters can be used by the tc CLI, but only -those with a TB Name appear in the TaxBrain GUI. All these -dynamic response parameters control advanced features of -Tax-Calculator, so understanding the -source -code that uses them is essential. Each default parameter value is -zero and is projected into the future at that value, which implies no -response to the reform. Using the default no-reform-response -assumption for all the response parameters generates a static analysis -of the tax reform.

- - -

Section Contents

- -

Consumption Parameters

-

Behavior Parameters

-

Growdiff Parameters

- - -

Consumption Parameters

- - - -

Back to Section Contents

- - -

Behavior Parameters

- - - -

Back to Section Contents

- - -

Growdiff Parameters

- - - -

Back to Section Contents   -Back to Document Contents

+

Back to Cookbook Contents

From 166de1ff9326db47c0c687e4c2ce938c6caad9fc Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Sun, 26 Nov 2017 13:38:40 -0500 Subject: [PATCH 05/33] More revisions to cookbook.htmx file --- docs/cookbook.htmx | 21 +++++++++++++++++---- docs/make_cookbook.py | 23 +++++++++-------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/docs/cookbook.htmx b/docs/cookbook.htmx index 62ffe79d1..d4099aa93 100644 --- a/docs/cookbook.htmx +++ b/docs/cookbook.htmx @@ -48,21 +48,21 @@ following an existing recipe

Preliminaries: Kitchen Setup

-

Text goes here.

+

coming soon

Back to Cookbook Contents

Preliminaries: Recipe Techniques

-

Text goes here.

+

coming soon

Back to Cookbook Contents

Preliminaries: Recipe Ingredients

-

Text goes here.

+

coming soon

Back to Cookbook Contents

@@ -83,7 +83,20 @@ following an existing recipe.

Basic Recipes: Static Analysis of a Simple Reform

-

Text goes here.

+

The is the recipe you should follow first. Mastering this recipe +is a prerequiste for all the other recipes in this cookbook.

+ +

Ingredients

+ +

AMT-repeal-plus-higher-tax-rates reform

+ +

Instructions

+ +

Step-by-step instructions

+ +

Results

+ +

Expected results

Back to Cookbook Contents

diff --git a/docs/make_cookbook.py b/docs/make_cookbook.py index a5fff6c09..5929f7ac3 100644 --- a/docs/make_cookbook.py +++ b/docs/make_cookbook.py @@ -1,10 +1,10 @@ """ -Reads skeletal index.htmx file and writes fleshed-out index.html file -containing information from several JSON files. +Reads skeletal cookbook.htmx file and writes fleshed-out cookbook.html file +containing information from recipe and ingredient files. """ # CODING-STYLE CHECKS: -# pep8 --ignore=E402 make_index.py -# pylint --disable=locally-disabled make_index.py +# pep8 --ignore=E402 make_cookbook.py +# pylint --disable=locally-disabled make_cookbook.py import os import sys @@ -16,18 +16,13 @@ from taxcalc import Policy -INPUT_FILENAME = 'index.htmx' -OUTPUT_FILENAME = 'index.html' +INPUT_FILENAME = 'cookbook.htmx' +OUTPUT_FILENAME = 'cookbook.html' CURDIR_PATH = os.path.abspath(os.path.dirname(__file__)) -TAXCALC_PATH = os.path.join(CURDIR_PATH, '..', 'taxcalc') +COOKBOOK_PATH = os.path.join(CURDIR_PATH, 'cookbook') INPUT_PATH = os.path.join(CURDIR_PATH, INPUT_FILENAME) -POLICY_PATH = os.path.join(TAXCALC_PATH, 'current_law_policy.json') -IOVARS_PATH = os.path.join(TAXCALC_PATH, 'records_variables.json') -CONSUMPTION_PATH = os.path.join(TAXCALC_PATH, 'consumption.json') -BEHAVIOR_PATH = os.path.join(TAXCALC_PATH, 'behavior.json') -GROWDIFF_PATH = os.path.join(TAXCALC_PATH, 'growdiff.json') OUTPUT_PATH = os.path.join(CURDIR_PATH, OUTPUT_FILENAME) @@ -42,10 +37,10 @@ def main(): # augment text variable with do-not-edit warning old = '' new = ('\n' - '') + '') text = text.replace(old, new) - # augment text variable with information from JSON files + # augment text variable with recipie ingredients, instructions, results text = policy_params(POLICY_PATH, text) text = io_variables('read', IOVARS_PATH, text) text = io_variables('calc', IOVARS_PATH, text) From 4dd463c195a666e83744471238f3526db3893b5e Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Sun, 26 Nov 2017 13:49:18 -0500 Subject: [PATCH 06/33] Eliminate make_cookbook.py script --- docs/{cookbook.htmx => cookbook.html} | 6 +- docs/make_cookbook.py | 250 -------------------------- 2 files changed, 3 insertions(+), 253 deletions(-) rename docs/{cookbook.htmx => cookbook.html} (89%) delete mode 100644 docs/make_cookbook.py diff --git a/docs/cookbook.htmx b/docs/cookbook.html similarity index 89% rename from docs/cookbook.htmx rename to docs/cookbook.html index d4099aa93..17e9af676 100644 --- a/docs/cookbook.htmx +++ b/docs/cookbook.html @@ -88,15 +88,15 @@

Basic Recipes: Static Analysis of a Simple Reform

Ingredients

-

AMT-repeal-plus-higher-tax-rates reform

+

AMT-repeal-plus-higher-tax-rates reform in the ingredients/repeal_amt.json file

Instructions

-

Step-by-step instructions

+

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

Results

-

Expected results

+

Expected results from executing python recipe00.py at the command prompt

Back to Cookbook Contents

diff --git a/docs/make_cookbook.py b/docs/make_cookbook.py deleted file mode 100644 index 5929f7ac3..000000000 --- a/docs/make_cookbook.py +++ /dev/null @@ -1,250 +0,0 @@ -""" -Reads skeletal cookbook.htmx file and writes fleshed-out cookbook.html file -containing information from recipe and ingredient files. -""" -# CODING-STYLE CHECKS: -# pep8 --ignore=E402 make_cookbook.py -# pylint --disable=locally-disabled make_cookbook.py - -import os -import sys -import json -from collections import OrderedDict -CUR_PATH = os.path.abspath(os.path.dirname(__file__)) -sys.path.append(os.path.join(CUR_PATH, '..')) -# pylint: disable=import-error,wrong-import-position -from taxcalc import Policy - - -INPUT_FILENAME = 'cookbook.htmx' -OUTPUT_FILENAME = 'cookbook.html' - -CURDIR_PATH = os.path.abspath(os.path.dirname(__file__)) -COOKBOOK_PATH = os.path.join(CURDIR_PATH, 'cookbook') - -INPUT_PATH = os.path.join(CURDIR_PATH, INPUT_FILENAME) -OUTPUT_PATH = os.path.join(CURDIR_PATH, OUTPUT_FILENAME) - - -def main(): - """ - Contains high-level logic of the script. - """ - # read INPUT file into text variable - with open(INPUT_PATH, 'r') as ifile: - text = ifile.read() - - # augment text variable with do-not-edit warning - old = '' - new = ('\n' - '') - text = text.replace(old, new) - - # augment text variable with recipie ingredients, instructions, results - text = policy_params(POLICY_PATH, text) - text = io_variables('read', IOVARS_PATH, text) - text = io_variables('calc', IOVARS_PATH, text) - text = response_params('consumption', CONSUMPTION_PATH, text) - text = response_params('behavior', BEHAVIOR_PATH, text) - text = response_params('growdiff', GROWDIFF_PATH, text) - - # write text variable to OUTPUT file - with open(OUTPUT_PATH, 'w') as ofile: - ofile.write(text) - - # normal return code - return 0 -# end of main function code - - -def policy_param_text(pname, param): - """ - Extract info from param for pname and return as HTML string. - """ - # pylint: disable=too-many-branches,len-as-condition - sec1 = param['section_1'] - if len(sec1) > 0: - txt = '

{} — {}'.format(sec1, param['section_2']) - else: - txt = '

{} — {}'.format('Other Parameters', - 'Not in TaxBrain GUI') - txt += '
tc Name: {}'.format(pname) - if len(sec1) > 0: - txt += '
TB Name: {}'.format(param['long_name']) - else: - txt += '
Long Name: {}'.format(param['long_name']) - txt += '
Description: {}'.format(param['description']) - if len(param['notes']) > 0: - txt += '
Notes: {}'.format(param['notes']) - if param['cpi_inflated']: - txt += '
Inflation Indexed: True' - else: - txt += '
Inflation Indexed: False' - if param['integer_value']: - txt += '     Integer Value: True' - else: - txt += '     Integer Value: False' - if param['boolean_value']: - txt += '     Boolean Value: True' - else: - txt += '     Boolean Value: False' - txt += '
Known Values:' - if len(param['col_label']) > 0: - cols = ', '.join(param['col_label']) - txt += '
   for: [{}]'.format(cols) - for cyr, val in zip(param['row_label'], param['value']): - final_cyr = cyr - final_val = val - txt += '
{}: {}'.format(cyr, val) - if not param['cpi_inflated']: - fcyr = int(final_cyr) - if fcyr < Policy.LAST_KNOWN_YEAR: - # extrapolate final_val thru Policy.LAST_KNOWN_YEAR if not indexed - for cyr in range(fcyr + 1, Policy.LAST_KNOWN_YEAR + 1): - txt += '
{}: {}'.format(cyr, final_val) - txt += '
Valid Range:' - if param['range']['min'] == 'default': - minval = 'known_value' - else: - minval = param['range']['min'] - if param['range']['max'] == 'default': - maxval = 'known_value' - else: - maxval = param['range']['max'] - txt += ' min = {} and max = {}'.format(minval, maxval) - txt += '
Out-of-Range Action: {}'.format( - param['out_of_range_action']) - txt += '

' - return txt - - -def policy_params(path, text): - """ - Read policy parameters from path, integrate them into text, and - return the integrated text. - """ - with open(path) as pfile: - params = json.load(pfile, object_pairs_hook=OrderedDict) - assert isinstance(params, OrderedDict) - # construct section dict containing sec1_sec2 titles - concat_str = ' @ ' - section = OrderedDict() - using_other_params_section = False - for pname in params: - param = params[pname] - sec1_sec2 = '{}{}{}'.format(param['section_1'], - concat_str, - param['section_2']) - if sec1_sec2 == concat_str: - using_other_params_section = True - elif sec1_sec2 not in section: - section[sec1_sec2] = 0 - if using_other_params_section: - section[concat_str] = 0 - # construct parameter text for each sec1_sec2 in section - for sec1_sec2 in section: - split_list = sec1_sec2.split(concat_str) - sec1 = split_list[0] - sec2 = split_list[1] - ptext = '' - for pname in params: - param = params[pname] - if sec1 == param['section_1'] and sec2 == param['section_2']: - ptext += policy_param_text(pname, param) - # integrate parameter text into text - old = ''.format(sec1_sec2) - text = text.replace(old, ptext) - return text - - -def var_text(vname, iotype, variable): - """ - Extract info from variable for vname of iotype - and return info as HTML string. - """ - if iotype == 'read': - txt = '

Input Variable Name: {}'.format(vname) - if 'required' in variable: - txt += '
Required Input Variable' - else: - txt = '

Output Variable Name: {}'.format(vname) - txt += '
Description: {}'.format(variable['desc']) - txt += '
Datatype: {}'.format(variable['type']) - if iotype == 'read': - txt += '
Availability: {}'.format(variable['availability']) - txt += '
IRS Form Location:' - formdict = variable['form'] - for yrange in sorted(formdict.keys()): - txt += '
{}: {}'.format(yrange, formdict[yrange]) - txt += '

' - return txt - - -def io_variables(iotype, path, text): - """ - Read variables for iotype ('read' for input or 'calc' for output) - from path, integrate them into text, and return the integrated text. - """ - with open(path) as vfile: - variables = json.load(vfile) - assert isinstance(variables, dict) - # construct variable text - vtext = '' - for vname in sorted(variables[iotype].keys()): - vtext += var_text(vname, iotype, variables[iotype][vname]) - # integrate variable text into text - old = ''.format(iotype) - text = text.replace(old, vtext) - return text - - -def response_param_text(pname, ptype, param): - """ - Extract info from param for pname of ptype and return as HTML string. - """ - # pylint: disable=len-as-condition - sec1 = param['section_1'] - if len(sec1) > 0: - txt = '

{} — {}'.format(sec1, param['section_2']) - else: - txt = '

{} — {}'.format('Response Parameter', - ptype.capitalize()) - txt += '
tc Name: {}'.format(pname) - if len(sec1) > 0: - txt += '
TB Name: {}'.format(param['long_name']) - else: - txt += '
Long Name: {}'.format(param['long_name']) - txt += '
Description: {}'.format(param['description']) - if len(param['notes']) > 0: - txt += '
Notes: {}'.format(param['notes']) - txt += '
Default Value:' - if len(param['col_label']) > 0: - cols = ', '.join(param['col_label']) - txt += '
   for: [{}]'.format(cols) - for cyr, val in zip(param['row_label'], param['value']): - txt += '
{}: {}'.format(cyr, val) - txt += '

' - return txt - - -def response_params(ptype, path, text): - """ - Read response parameters of ptype from path, integrate them into text, - and return the integrated text. - """ - with open(path) as pfile: - params = json.load(pfile, object_pairs_hook=OrderedDict) - assert isinstance(params, OrderedDict) - # construct parameter text for each param - ptext = '' - for pname in params: - param = params[pname] - ptext += response_param_text(pname, ptype, param) - # integrate parameter text into text - old = ''.format(ptype) - text = text.replace(old, ptext) - return text - - -if __name__ == '__main__': - sys.exit(main()) From f652b5b90b15aa1048e26aef764129d141153086 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Sun, 26 Nov 2017 14:19:47 -0500 Subject: [PATCH 07/33] Add up-to-date test for docs/cookbook.html file --- taxcalc/tests/test_docs.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/taxcalc/tests/test_docs.py b/taxcalc/tests/test_docs.py index 10ba5583e..9a124e7f9 100644 --- a/taxcalc/tests/test_docs.py +++ b/taxcalc/tests/test_docs.py @@ -6,11 +6,12 @@ # pylint --disable=locally-disabled test_docs.py import os +import glob import pytest @pytest.mark.local_test -def test_docs_up_to_date(tests_path): +def test_docs_index_up_to_date(tests_path): """ Check that index.html timestamp is greater than the timestamp of each of its dependencies, where a timestamp is the time a file @@ -38,3 +39,35 @@ def test_docs_up_to_date(tests_path): msg += ' $ python make_index.py \n' msg += 'AND INCLUDE UPDATED index.html IN NEXT COMMIT \n' raise ValueError(msg) + + +@pytest.mark.local_test +def test_docs_cookbook_up_to_date(tests_path): + """ + Check that cookbook.html timestamp is greater than the timestamp + of each of its dependencies, where a timestamp is the time a file + was last modified. + """ + docs_path = os.path.join(tests_path, '..', '..', 'docs') + target = os.path.join(docs_path, 'cookbook.html') + recipes = glob.glob(os.path.join(docs_path, 'recipes', '*')) + ingredients = glob.glob(os.path.join(docs_path, 'recipes', + 'ingredients', '*')) + dependencies = recipes + ingredients + + target_timestamp = os.path.getmtime(target) + target_up_to_date = True + for dep in dependencies: + if os.path.getmtime(dep) > target_timestamp: + target_up_to_date = False + if not target_up_to_date: + msg = 'Tax-Calculator/docs/cookbook.html IS NOT UP-TO-DATE: \n' + msg += 'EDIT cookbook.html SO THAT IT REFERENCES \n' + msg += '- ALL cookbook/recipe*py FILES, \n' + msg += '- ALL cookbook/recipe*res FILES, \n' + msg += '- ALL cookbook/ingredients FILES. \n' + msg += 'AND INCLUDE THE UPDATED cookbook.html IN NEXT COMMIT. \n' + msg += ' \n' + msg += 'IF NO SUBSTATIVE CHANGES IN cookbook.html ARE RQUIRED,\n' + msg += 'SAVE THE UNCHANGED FILE SO IT HAS A CURRENT TIMESTAMP.\n' + raise ValueError(msg) From 631c271169d034337d905542e1eb6b7b95f20401 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Sun, 26 Nov 2017 16:40:21 -0500 Subject: [PATCH 08/33] Add more results info to cookbook recipe00 --- docs/cookbook/recipe00.py | 44 ++++++++++++++++++++--- docs/cookbook/recipe00.res | 73 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 6 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 3105c175e..ee1b1bccc 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -12,7 +12,7 @@ # NOTE: calc1 now contains a PRIVATE COPY of pol and a PRIVATE COPY of recs, # so we can continue to use pol and recs in this script without any -# concern about side effects from Calculator method calls. +# concern about side effects from Calculator method calls on calc1. # calculate aggregate current-law income tax liabilities for 2018 calc1.advance_to_year(2018) @@ -40,6 +40,42 @@ print(Calculator.reform_documentation(params)) # print total revenue estimates for 2018 -# (estimates in billons of dollars rounded to nearest tenth of a billion) -print('2018_CLP_itax_rev($B)= {:.1f}'.format(itax_rev1 * 1e-9)) -print('2018_REF_itax_rev($B)= {:.1f}'.format(itax_rev2 * 1e-9)) +# (estimates in billons of dollars rounded to nearest hundredth of a billion) +print('2018_CLP_itax_rev($B)= {:.2f}'.format(itax_rev1 * 1e-9)) +print('2018_REF_itax_rev($B)= {:.2f}'.format(itax_rev2 * 1e-9)) +print('') + +# generate several other standard results tables: + +# aggregate diagnostic tables for 2018 +# read source code at following URL for details +# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_diagnostic_table +clp_diagnostic_table = create_diagnostic_table(calc1) +ref_diagnostic_table = create_diagnostic_table(calc2) + +# income-tax difference table extract for 2018 by expanded-income decile +# read following source code for details: +# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_difference_table +diff_table = create_difference_table(calc1.records, calc2.records, + groupby='weighted_deciles', + income_measure='expanded_income', + tax_to_diff='iitax') +assert isinstance(diff_table, pd.DataFrame) +diff_extract = pd.DataFrame() +dif_colnames = ['count', 'tot_change', 'mean', 'pc_aftertaxinc'] +ext_colnames = ['funits(#m)', 'total_diff($b)', 'mean_diff($)', 'aftertaxinc_diff(%)'] +scaling_factors = [1e-6, 1e-9, 1e0, 1e0] +for dname, ename, sfactor in zip(dif_colnames, ext_colnames, scaling_factors): + diff_extract[ename] = diff_table[dname] * sfactor + +print('CLP diagnostic table for 2018:') +print(clp_diagnostic_table) +print('') +print('REF diagnostic table for 2018:') +print(ref_diagnostic_table) +print('') +print('Extract of 2018 income-tax difference table by expanded-income decile:') +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/recipe00.res b/docs/cookbook/recipe00.res index 034fac3af..191abedce 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -51,5 +51,74 @@ Policy Reform Parameter Values by Year: and above tax bracket 6. baseline_value: 0.396 -2018_CLP_itax_rev($B)= 1287.5 -2018_REF_itax_rev($B)= 1283.1 +2018_CLP_itax_rev($B)= 1287.51 +2018_REF_itax_rev($B)= 1283.12 + +CLP diagnostic table for 2018: + 2018 +Returns (#m) 169.89 +AGI ($b) 10,503.83 +Itemizers (#m) 67.99 +Itemized Deduction ($b) 1,500.94 +Standard Deduction Filers (#m) 83.86 +Standard Deduction ($b) 771.37 +Personal Exemption ($b) 1,222.60 +Taxable Income ($b) 7,421.92 +Regular Tax ($b) 1,369.71 +AMT Income ($b) 9,664.54 +AMT Liability ($b) 18.02 +AMT Filers (#m) 4.22 +Tax before Credits ($b) 1,387.73 +Refundable Credits ($b) 76.61 +Nonrefundable Credits ($b) 32.93 +Reform Surtaxes ($b) 0.00 +Other Taxes ($b) 9.32 +Ind Income Tax ($b) 1,287.51 +Payroll Taxes ($b) 1,233.56 +Combined Liability ($b) 2,521.07 +With Income Tax <= 0 (#m) 72.00 +With Combined Tax <= 0 (#m) 48.60 + +REF diagnostic table for 2018: + 2018 +Returns (#m) 169.89 +AGI ($b) 10,503.83 +Itemizers (#m) 68.03 +Itemized Deduction ($b) 1,501.75 +Standard Deduction Filers (#m) 83.82 +Standard Deduction ($b) 771.17 +Personal Exemption ($b) 1,222.60 +Taxable Income ($b) 7,421.27 +Regular Tax ($b) 1,383.33 +AMT Income ($b) 9,665.24 +AMT Liability ($b) 0.00 +AMT Filers (#m) nan +Tax before Credits ($b) 1,383.33 +Refundable Credits ($b) 76.62 +Nonrefundable Credits ($b) 32.91 +Reform Surtaxes ($b) 0.00 +Other Taxes ($b) 9.32 +Ind Income Tax ($b) 1,283.12 +Payroll Taxes ($b) 1,233.56 +Combined Liability ($b) 2,516.68 +With Income Tax <= 0 (#m) 72.03 +With Combined Tax <= 0 (#m) 48.62 + +Extract of 2018 income-tax difference table by expanded-income decile: + funits(#m) total_diff($b) mean_diff($) aftertaxinc_diff(%) +0 16.99 0.00 0.00 0.00 +1 16.99 -0.00 -0.16 0.00 +2 16.99 -0.01 -0.64 0.00 +3 16.99 -0.03 -1.56 0.01 +4 16.99 -0.06 -3.57 0.01 +5 16.99 -0.08 -4.89 0.01 +6 16.99 -0.06 -3.31 0.01 +7 16.99 -0.08 -4.65 0.01 +8 16.99 -0.11 -6.36 0.01 +9 16.99 -3.96 -233.19 0.21 +10 169.89 -4.39 nan nan +11 8.49 -0.35 -40.65 0.03 +12 6.79 -7.43 -1,093.70 0.47 +13 1.70 3.82 2,243.60 0.07 +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 caf95e2ee26d71a3c56aad795290c262954072c8 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Sun, 26 Nov 2017 19:01:58 -0500 Subject: [PATCH 09/33] Add extract of distribution tables to recipe00.py --- docs/cookbook/recipe00.py | 35 +++++++++++++++++++++++++++++++---- docs/cookbook/recipe00.res | 19 +++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index ee1b1bccc..93bf094f6 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -53,8 +53,28 @@ clp_diagnostic_table = create_diagnostic_table(calc1) ref_diagnostic_table = create_diagnostic_table(calc2) +# income-tax distribution for 2018 with CLP and REF results side-by-side +# read source code at following URL for details +# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_distribution_table +dist_table1 = create_distribution_table(calc1.records, + groupby='weighted_deciles', + income_measure='expanded_income', + result_type='weighted_sum') +dist_table2 = create_distribution_table(calc2.records, + groupby='weighted_deciles', + income_measure='expanded_income', + result_type='weighted_sum') +assert isinstance(dist_table1, pd.DataFrame) +assert isinstance(dist_table2, pd.DataFrame) +dist_extract = pd.DataFrame() +dist_extract['funits(#m)'] = dist_table1['s006'] * 1e-6 +dist_extract['itax1($b)'] = dist_table1['iitax'] * 1e-9 +dist_extract['itax2($b)'] = dist_table2['iitax'] * 1e-9 +dist_extract['aftertax_inc1($b)'] = dist_table1['aftertax_income'] * 1e-9 +dist_extract['aftertax_inc2($b)'] = dist_table2['aftertax_income'] * 1e-9 + # income-tax difference table extract for 2018 by expanded-income decile -# read following source code for details: +# read source code at following URL for details # http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_difference_table diff_table = create_difference_table(calc1.records, calc2.records, groupby='weighted_deciles', @@ -62,9 +82,9 @@ tax_to_diff='iitax') assert isinstance(diff_table, pd.DataFrame) diff_extract = pd.DataFrame() -dif_colnames = ['count', 'tot_change', 'mean', 'pc_aftertaxinc'] -ext_colnames = ['funits(#m)', 'total_diff($b)', 'mean_diff($)', 'aftertaxinc_diff(%)'] -scaling_factors = [1e-6, 1e-9, 1e0, 1e0] +dif_colnames = ['count', 'tot_change', 'mean', 'pc_aftertaxinc', 'perc_aftertax'] +ext_colnames = ['funits(#m)', 'total_diff($b)', 'mean_diff($)', 'aftertaxinc_diff(%)', 'perc_aftertax(%)'] +scaling_factors = [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 @@ -74,6 +94,13 @@ print('REF diagnostic table for 2018:') print(ref_diagnostic_table) print('') + +print('Extract of 2018 distribution tables by expanded-income decile:') +print(dist_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') +print('') + print('Extract of 2018 income-tax difference table by expanded-income decile:') print(diff_extract) print('Note: deciles are numbered 0-9 with top decile divided into bottom 5%,') diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index 191abedce..4cb01772c 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -104,6 +104,25 @@ Combined Liability ($b) 2,516.68 With Income Tax <= 0 (#m) 72.03 With Combined Tax <= 0 (#m) 48.62 +Extract of 2018 distribution tables by expanded-income decile: + funits(#m) itax1($b) itax2($b) aftertax_inc1($b) aftertax_inc2($b) +0 16.99 -1.65 -1.65 -5.30 -5.30 +1 16.99 -12.65 -12.65 168.67 168.68 +2 16.99 -15.20 -15.21 287.66 287.67 +3 16.99 -12.74 -12.77 402.40 402.43 +4 16.99 2.69 2.63 520.45 520.51 +5 16.99 27.14 27.06 665.73 665.81 +6 16.99 63.42 63.37 861.52 861.57 +7 16.99 119.61 119.53 1,153.42 1,153.50 +8 16.99 212.26 212.15 1,629.28 1,629.39 +9 16.99 904.62 900.66 3,599.33 3,603.29 +10 169.89 1,287.51 1,283.12 9,283.16 9,287.55 +11 8.49 463.66 461.14 1,796.20 1,798.71 +12 6.80 270.40 269.76 1,199.98 1,200.62 +13 1.70 170.57 169.77 603.16 603.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 + Extract of 2018 income-tax difference table by expanded-income decile: funits(#m) total_diff($b) mean_diff($) aftertaxinc_diff(%) 0 16.99 0.00 0.00 0.00 From 2fe5ab1ac23a6890913d76da8459c11597fad2cd Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 08:07:12 -0500 Subject: [PATCH 10/33] Updata recipe00 results --- docs/cookbook/recipe00.res | 46 +++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index 4cb01772c..eee97af06 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -124,20 +124,36 @@ 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 Extract of 2018 income-tax difference table by expanded-income decile: - funits(#m) total_diff($b) mean_diff($) aftertaxinc_diff(%) -0 16.99 0.00 0.00 0.00 -1 16.99 -0.00 -0.16 0.00 -2 16.99 -0.01 -0.64 0.00 -3 16.99 -0.03 -1.56 0.01 -4 16.99 -0.06 -3.57 0.01 -5 16.99 -0.08 -4.89 0.01 -6 16.99 -0.06 -3.31 0.01 -7 16.99 -0.08 -4.65 0.01 -8 16.99 -0.11 -6.36 0.01 -9 16.99 -3.96 -233.19 0.21 -10 169.89 -4.39 nan nan -11 8.49 -0.35 -40.65 0.03 -12 6.79 -7.43 -1,093.70 0.47 -13 1.70 3.82 2,243.60 0.07 + funits(#m) total_diff($b) mean_diff($) aftertaxinc_diff(%) \ +0 16.99 0.00 0.00 -0.00 +1 16.99 -0.00 -0.16 0.00 +2 16.99 -0.01 -0.64 0.00 +3 16.99 -0.03 -1.56 0.01 +4 16.99 -0.06 -3.57 0.01 +5 16.99 -0.08 -4.89 0.01 +6 16.99 -0.06 -3.31 0.01 +7 16.99 -0.08 -4.65 0.01 +8 16.99 -0.11 -6.36 0.01 +9 16.99 -3.96 -233.19 0.11 +10 169.89 -4.39 nan nan +11 8.49 -0.35 -40.65 0.03 +12 6.79 -7.43 -1,093.70 0.54 +13 1.70 3.82 2,243.60 -0.36 + + perc_aftertax(%) +0 -0.00 +1 -0.00 +2 -0.00 +3 -0.01 +4 -0.01 +5 -0.01 +6 -0.01 +7 -0.01 +8 -0.01 +9 -0.11 +10 nan +11 -0.03 +12 -0.54 +13 0.36 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 0354b25cd2515f27d7d71738660104fe97deec45 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 09:57:43 -0500 Subject: [PATCH 11/33] Update cookbook/recipe00.res --- docs/cookbook/recipe00.res | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index eee97af06..27605e73e 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -125,7 +125,7 @@ Note: deciles are numbered 0-9 with top decile divided into bottom 5%, Extract of 2018 income-tax difference table by expanded-income decile: funits(#m) total_diff($b) mean_diff($) aftertaxinc_diff(%) \ -0 16.99 0.00 0.00 -0.00 +0 16.99 0.00 0.00 0.00 1 16.99 -0.00 -0.16 0.00 2 16.99 -0.01 -0.64 0.00 3 16.99 -0.03 -1.56 0.01 @@ -135,7 +135,7 @@ Extract of 2018 income-tax difference table by expanded-income decile: 7 16.99 -0.08 -4.65 0.01 8 16.99 -0.11 -6.36 0.01 9 16.99 -3.96 -233.19 0.11 -10 169.89 -4.39 nan nan +10 169.89 -4.39 nan 0.05 11 8.49 -0.35 -40.65 0.03 12 6.79 -7.43 -1,093.70 0.54 13 1.70 3.82 2,243.60 -0.36 @@ -151,7 +151,7 @@ Extract of 2018 income-tax difference table by expanded-income decile: 7 -0.01 8 -0.01 9 -0.11 -10 nan +10 -0.05 11 -0.03 12 -0.54 13 0.36 From 3f34c990bc8d22fece281b29718fe9e92b2ae7bc Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 11:42:42 -0500 Subject: [PATCH 12/33] Update recipe00.* files --- docs/cookbook/recipe00.py | 18 +++++++++++++----- docs/cookbook/recipe00.res | 1 + 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 93bf094f6..1f5b71938 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -37,6 +37,7 @@ itax_rev2 = calc2.weighted_total('iitax') # print reform documentation +print('') print(Calculator.reform_documentation(params)) # print total revenue estimates for 2018 @@ -54,15 +55,23 @@ ref_diagnostic_table = create_diagnostic_table(calc2) # income-tax distribution for 2018 with CLP and REF results side-by-side -# read source code at following URL for details +# read source code at following URLs for details +# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#results # http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_distribution_table -dist_table1 = create_distribution_table(calc1.records, +res1 = results(calc1.records) +assert isinstance(res1, pd.DataFrame) +res2 = results(calc2.records) +assert isinstance(res2, pd.DataFrame) +dist_table1 = create_distribution_table(res1, groupby='weighted_deciles', income_measure='expanded_income', result_type='weighted_sum') -dist_table2 = create_distribution_table(calc2.records, +res2['expanded_income_baseline'] = res1['expanded_income'] +exp_inc_baseline = 'expanded_income_baseline' +dist_table2 = create_distribution_table(res2, groupby='weighted_deciles', - income_measure='expanded_income', + # so can compare the two dist tables: + income_measure=exp_inc_baseline, result_type='weighted_sum') assert isinstance(dist_table1, pd.DataFrame) assert isinstance(dist_table2, pd.DataFrame) @@ -105,4 +114,3 @@ 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/recipe00.res b/docs/cookbook/recipe00.res index 27605e73e..7d77a9350 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -2,6 +2,7 @@ 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. + REFORM DOCUMENTATION Baseline Growth-Difference Assumption Values by Year: none: using default baseline growth assumptions From 5c46bfb6be90744e4aae60b5effe164497879a26 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 16:12:02 -0500 Subject: [PATCH 13/33] Update distribution table extract info --- docs/cookbook/recipe00.res | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index 7d77a9350..3c43d9d4a 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -118,9 +118,9 @@ Extract of 2018 distribution tables by expanded-income decile: 8 16.99 212.26 212.15 1,629.28 1,629.39 9 16.99 904.62 900.66 3,599.33 3,603.29 10 169.89 1,287.51 1,283.12 9,283.16 9,287.55 -11 8.49 463.66 461.14 1,796.20 1,798.71 -12 6.80 270.40 269.76 1,199.98 1,200.62 -13 1.70 170.57 169.77 603.16 603.96 +11 8.49 463.66 200.46 1,796.20 1,150.57 +12 6.80 270.40 310.72 1,199.98 1,394.11 +13 1.70 170.57 389.48 603.16 1,058.61 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 3ae923c4dcdb66fc947a5fcee2e97039e23c8f9e Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 17:35:23 -0500 Subject: [PATCH 14/33] Change reform used in cookbook/recipe00 --- ...l_amt.json => raise_rates_and_stdded.json} | 9 +- docs/cookbook/recipe00.py | 22 ++- docs/cookbook/recipe00.res | 133 ++++++++---------- 3 files changed, 76 insertions(+), 88 deletions(-) rename docs/cookbook/ingredients/{repeal_amt.json => raise_rates_and_stdded.json} (60%) diff --git a/docs/cookbook/ingredients/repeal_amt.json b/docs/cookbook/ingredients/raise_rates_and_stdded.json similarity index 60% rename from docs/cookbook/ingredients/repeal_amt.json rename to docs/cookbook/ingredients/raise_rates_and_stdded.json index 29a937b65..c4480ec94 100644 --- a/docs/cookbook/ingredients/repeal_amt.json +++ b/docs/cookbook/ingredients/raise_rates_and_stdded.json @@ -1,10 +1,11 @@ -// Repeals AMT and raises regular tax rates in the top three brackets +// Raises standard deduction and raises regular tax rates in top three brackets // from (0.33, 0.35, 0.396) to (0.35, 0.37, 0.42) beginning in 2018. { "policy": { - // repeal AMT by setting AMT tax rates to zero - "_AMT_rt1": {"2018": [0.0]}, - "_AMT_rt2": {"2018": [0.0]}, + // raise standard deduction and eliminate Dep and Aged differentials + "_STD": {"2018": [[12000, 24000, 12000, 18000, 24000]]}, + "_STD_Dep": {"2018": [0]}, + "_STD_Aged": {"2018": [[0, 0, 0, 0, 0]]}, // raise tax rates in the top three brackets // ... raise non-AMT rates on non-pass-through income "_II_rt5": {"2018": [0.350]}, diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 1f5b71938..e217d7d99 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -20,7 +20,7 @@ itax_rev1 = calc1.weighted_total('iitax') # read JSON reform file and use (the default) static analysis assumptions -reform_filename = './ingredients/repeal_amt.json' +reform_filename = './ingredients/raise_stdded_and_rates.json' params = Calculator.read_json_param_objects(reform=reform_filename, assump=None) @@ -55,20 +55,16 @@ ref_diagnostic_table = create_diagnostic_table(calc2) # income-tax distribution for 2018 with CLP and REF results side-by-side -# read source code at following URLs for details -# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#results +# read source code at following URL for details # http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_distribution_table -res1 = results(calc1.records) -assert isinstance(res1, pd.DataFrame) -res2 = results(calc2.records) -assert isinstance(res2, pd.DataFrame) -dist_table1 = create_distribution_table(res1, +dist_table1 = create_distribution_table(calc1.records, groupby='weighted_deciles', income_measure='expanded_income', result_type='weighted_sum') -res2['expanded_income_baseline'] = res1['expanded_income'] +setattr(calc2.records, 'expanded_income_baseline', + getattr(calc1.records, 'expanded_income')) exp_inc_baseline = 'expanded_income_baseline' -dist_table2 = create_distribution_table(res2, +dist_table2 = create_distribution_table(calc2.records, groupby='weighted_deciles', # so can compare the two dist tables: income_measure=exp_inc_baseline, @@ -91,8 +87,10 @@ tax_to_diff='iitax') assert isinstance(diff_table, pd.DataFrame) diff_extract = pd.DataFrame() -dif_colnames = ['count', 'tot_change', 'mean', 'pc_aftertaxinc', 'perc_aftertax'] -ext_colnames = ['funits(#m)', 'total_diff($b)', 'mean_diff($)', 'aftertaxinc_diff(%)', 'perc_aftertax(%)'] +dif_colnames = ['count', 'tot_change', 'mean', + 'pc_aftertaxinc'] +ext_colnames = ['funits(#m)', 'agg_diff($b)', 'mean_diff($)', + 'aftertaxinc_diff(%)'] scaling_factors = [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 diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index 3c43d9d4a..b3769d994 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -8,16 +8,6 @@ Baseline Growth-Difference Assumption Values by Year: none: using default baseline growth assumptions Policy Reform Parameter Values by Year: 2018: - _AMT_rt1 : 0.0 - name: AMT rate 1 - desc: The tax rate applied to the portion of AMT taxable income below the - surtax threshold, AMT bracket 1. - baseline_value: 0.26 - _AMT_rt2 : 0.0 - name: Additional AMT rate for AMT taxable income above AMT bracket 1 - desc: The additional tax rate applied to the portion of AMT income above the - AMT bracket 1. - baseline_value: 0.02 _II_rt5 : 0.35 name: Personal income (regular/non-AMT/non-pass-through) tax rate 5 desc: The third highest tax rate, applied to the portion of taxable income @@ -51,9 +41,24 @@ Policy Reform Parameter Values by Year: proprietorships, partnerships and S corporations below tax bracket 7 and above tax bracket 6. baseline_value: 0.396 + _STD : [12000, 24000, 12000, 18000, 24000] + ['single', 'joint', 'separate', 'headhousehold', 'widow'] + name: Standard deduction amount + desc: Amount filing unit can use as a standard deduction. + baseline_value: [6492.88, 12985.75, 6492.88, 9560.38, 12985.75] + _STD_Aged : [0, 0, 0, 0, 0] + ['single', 'joint', 'separate', 'headhousehold', 'widow'] + name: Additional standard deduction for blind and aged + desc: To get the standard deduction for aged or blind individuals, taxpayers + need to add this value to regular standard deduction. + baseline_value: [1584.88, 1278.13, 1278.13, 1584.88, 1584.88] + _STD_Dep : 0 + name: Standard deduction for dependents + desc: This is the maximum standard deduction for dependents. + baseline_value: 1073.63 2018_CLP_itax_rev($B)= 1287.51 -2018_REF_itax_rev($B)= 1283.12 +2018_REF_itax_rev($B)= 1234.66 CLP diagnostic table for 2018: 2018 @@ -84,77 +89,61 @@ REF diagnostic table for 2018: 2018 Returns (#m) 169.89 AGI ($b) 10,503.83 -Itemizers (#m) 68.03 -Itemized Deduction ($b) 1,501.75 -Standard Deduction Filers (#m) 83.82 -Standard Deduction ($b) 771.17 +Itemizers (#m) 34.58 +Itemized Deduction ($b) 1,010.91 +Standard Deduction Filers (#m) 117.28 +Standard Deduction ($b) 1,958.91 Personal Exemption ($b) 1,222.60 -Taxable Income ($b) 7,421.27 -Regular Tax ($b) 1,383.33 -AMT Income ($b) 9,665.24 -AMT Liability ($b) 0.00 -AMT Filers (#m) nan -Tax before Credits ($b) 1,383.33 -Refundable Credits ($b) 76.62 -Nonrefundable Credits ($b) 32.91 +Taxable Income ($b) 6,993.08 +Regular Tax ($b) 1,317.86 +AMT Income ($b) 9,945.40 +AMT Liability ($b) 16.87 +AMT Filers (#m) 4.40 +Tax before Credits ($b) 1,334.73 +Refundable Credits ($b) 81.41 +Nonrefundable Credits ($b) 27.99 Reform Surtaxes ($b) 0.00 Other Taxes ($b) 9.32 -Ind Income Tax ($b) 1,283.12 +Ind Income Tax ($b) 1,234.66 Payroll Taxes ($b) 1,233.56 -Combined Liability ($b) 2,516.68 -With Income Tax <= 0 (#m) 72.03 -With Combined Tax <= 0 (#m) 48.62 +Combined Liability ($b) 2,468.22 +With Income Tax <= 0 (#m) 79.01 +With Combined Tax <= 0 (#m) 49.54 Extract of 2018 distribution tables by expanded-income decile: funits(#m) itax1($b) itax2($b) aftertax_inc1($b) aftertax_inc2($b) -0 16.99 -1.65 -1.65 -5.30 -5.30 -1 16.99 -12.65 -12.65 168.67 168.68 -2 16.99 -15.20 -15.21 287.66 287.67 -3 16.99 -12.74 -12.77 402.40 402.43 -4 16.99 2.69 2.63 520.45 520.51 -5 16.99 27.14 27.06 665.73 665.81 -6 16.99 63.42 63.37 861.52 861.57 -7 16.99 119.61 119.53 1,153.42 1,153.50 -8 16.99 212.26 212.15 1,629.28 1,629.39 -9 16.99 904.62 900.66 3,599.33 3,603.29 -10 169.89 1,287.51 1,283.12 9,283.16 9,287.55 -11 8.49 463.66 200.46 1,796.20 1,150.57 -12 6.80 270.40 310.72 1,199.98 1,394.11 -13 1.70 170.57 389.48 603.16 1,058.61 +0 16.99 -1.65 -1.65 -5.30 -5.31 +1 16.99 -12.65 -12.82 168.67 168.85 +2 16.99 -15.20 -17.23 287.66 289.69 +3 16.99 -12.74 -17.18 402.40 406.84 +4 16.99 2.69 -4.50 520.45 527.65 +5 16.99 27.14 17.77 665.73 675.10 +6 16.99 63.42 50.65 861.52 874.29 +7 16.99 119.61 107.46 1,153.42 1,165.57 +8 16.99 212.26 201.22 1,629.28 1,640.32 +9 16.99 904.62 910.94 3,599.33 3,593.01 +10 169.89 1,287.51 1,234.66 9,283.16 9,336.02 +11 8.49 463.66 197.05 1,796.20 1,153.97 +12 6.80 270.40 317.48 1,199.98 1,387.35 +13 1.70 170.57 396.41 603.16 1,051.69 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 Extract of 2018 income-tax difference table by expanded-income decile: - funits(#m) total_diff($b) mean_diff($) aftertaxinc_diff(%) \ -0 16.99 0.00 0.00 0.00 -1 16.99 -0.00 -0.16 0.00 -2 16.99 -0.01 -0.64 0.00 -3 16.99 -0.03 -1.56 0.01 -4 16.99 -0.06 -3.57 0.01 -5 16.99 -0.08 -4.89 0.01 -6 16.99 -0.06 -3.31 0.01 -7 16.99 -0.08 -4.65 0.01 -8 16.99 -0.11 -6.36 0.01 -9 16.99 -3.96 -233.19 0.11 -10 169.89 -4.39 nan 0.05 -11 8.49 -0.35 -40.65 0.03 -12 6.79 -7.43 -1,093.70 0.54 -13 1.70 3.82 2,243.60 -0.36 - - perc_aftertax(%) -0 -0.00 -1 -0.00 -2 -0.00 -3 -0.01 -4 -0.01 -5 -0.01 -6 -0.01 -7 -0.01 -8 -0.01 -9 -0.11 -10 -0.05 -11 -0.03 -12 -0.54 -13 0.36 + funits(#m) agg_diff($b) mean_diff($) aftertaxinc_diff(%) +0 16.99 0.00 0.27 0.09 +1 16.99 -0.17 -10.24 0.10 +2 16.99 -2.03 -119.71 0.71 +3 16.99 -4.43 -260.96 1.10 +4 16.99 -7.19 -423.46 1.38 +5 16.99 -9.37 -551.70 1.41 +6 16.99 -12.77 -751.77 1.48 +7 16.99 -12.15 -715.03 1.05 +8 16.99 -11.05 -650.18 0.68 +9 16.99 6.32 371.95 -0.18 +10 169.89 -52.85 nan 0.57 +11 8.49 -3.75 -441.16 0.33 +12 6.79 -0.67 -98.94 0.05 +13 1.70 10.74 6,315.38 -1.01 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 280a0121f8b43b34fe3cbf4a6cba6c7dc3b10f8f Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 18:48:20 -0500 Subject: [PATCH 15/33] Update cookbook/recipe00.res file --- docs/cookbook/recipe00.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index b3769d994..e6150ddd5 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -141,7 +141,7 @@ Extract of 2018 income-tax difference table by expanded-income decile: 7 16.99 -12.15 -715.03 1.05 8 16.99 -11.05 -650.18 0.68 9 16.99 6.32 371.95 -0.18 -10 169.89 -52.85 nan 0.57 +10 169.89 -52.85 -311.09 0.57 11 8.49 -3.75 -441.16 0.33 12 6.79 -0.67 -98.94 0.05 13 1.70 10.74 6,315.38 -1.01 From 3bfb14d1ab0714c92bc26835208616bfedec4404 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 18:53:06 -0500 Subject: [PATCH 16/33] Update cookbook/recipe00.res file again --- docs/cookbook/recipe00.py | 2 +- docs/cookbook/recipe00.res | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index e217d7d99..6e145bf13 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -102,7 +102,7 @@ print(ref_diagnostic_table) print('') -print('Extract of 2018 distribution tables by expanded-income decile:') +print('Extract of 2018 distribution tables by baseline expanded-income decile:') print(dist_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/recipe00.res b/docs/cookbook/recipe00.res index e6150ddd5..dd285db1a 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -110,7 +110,7 @@ Combined Liability ($b) 2,468.22 With Income Tax <= 0 (#m) 79.01 With Combined Tax <= 0 (#m) 49.54 -Extract of 2018 distribution tables by expanded-income decile: +Extract of 2018 distribution tables by baseline expanded-income decile: funits(#m) itax1($b) itax2($b) aftertax_inc1($b) aftertax_inc2($b) 0 16.99 -1.65 -1.65 -5.30 -5.31 1 16.99 -12.65 -12.82 168.67 168.85 From a25c39668ba63ad676414b6a16a11ec6f517b158 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Mon, 27 Nov 2017 18:57:54 -0500 Subject: [PATCH 17/33] Cosmetic changes to cookbook/recipe00.py file --- docs/cookbook/recipe00.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 6e145bf13..bf226639a 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -1,8 +1,6 @@ from __future__ import print_function # necessary only if using Python 2.7 - from taxcalc import * - # use publicly-available CPS input file recs = Records.cps_constructor() From 01409948ee0c24199b3b4f698c537daffd805584 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Tue, 28 Nov 2017 10:52:25 -0500 Subject: [PATCH 18/33] Eliminate dist-vs-diff table discrepancies --- docs/cookbook/recipe00.py | 11 +++++------ docs/cookbook/recipe00.res | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index bf226639a..508dc8ffd 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -55,17 +55,16 @@ # income-tax distribution for 2018 with CLP and REF results side-by-side # read source code at following URL for details # http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_distribution_table +expinc = 'expanded_income_baseline' +setattr(calc1.records, expinc, getattr(calc1.records, 'expanded_income')) +setattr(calc2.records, expinc, getattr(calc1.records, 'expanded_income')) dist_table1 = create_distribution_table(calc1.records, groupby='weighted_deciles', - income_measure='expanded_income', + income_measure=expinc, result_type='weighted_sum') -setattr(calc2.records, 'expanded_income_baseline', - getattr(calc1.records, 'expanded_income')) -exp_inc_baseline = 'expanded_income_baseline' dist_table2 = create_distribution_table(calc2.records, groupby='weighted_deciles', - # so can compare the two dist tables: - income_measure=exp_inc_baseline, + income_measure=expinc, result_type='weighted_sum') assert isinstance(dist_table1, pd.DataFrame) assert isinstance(dist_table2, pd.DataFrame) diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index dd285db1a..158a89650 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -123,9 +123,9 @@ Extract of 2018 distribution tables by baseline expanded-income decile: 8 16.99 212.26 201.22 1,629.28 1,640.32 9 16.99 904.62 910.94 3,599.33 3,593.01 10 169.89 1,287.51 1,234.66 9,283.16 9,336.02 -11 8.49 463.66 197.05 1,796.20 1,153.97 -12 6.80 270.40 317.48 1,199.98 1,387.35 -13 1.70 170.57 396.41 603.16 1,051.69 +11 8.49 200.80 197.05 1,150.22 1,153.97 +12 6.79 318.15 317.48 1,386.68 1,387.35 +13 1.70 385.67 396.41 1,062.43 1,051.69 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 db2f85483fc880a4d557010c1c79c8e3f4fc6b84 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Tue, 28 Nov 2017 20:41:59 -0500 Subject: [PATCH 19/33] Update create_di*_table calls --- docs/cookbook/recipe00.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 508dc8ffd..7ad833f3d 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -58,11 +58,11 @@ expinc = 'expanded_income_baseline' setattr(calc1.records, expinc, getattr(calc1.records, 'expanded_income')) setattr(calc2.records, expinc, getattr(calc1.records, 'expanded_income')) -dist_table1 = create_distribution_table(calc1.records, +dist_table1 = create_distribution_table(calc1, groupby='weighted_deciles', income_measure=expinc, result_type='weighted_sum') -dist_table2 = create_distribution_table(calc2.records, +dist_table2 = create_distribution_table(calc2, groupby='weighted_deciles', income_measure=expinc, result_type='weighted_sum') @@ -78,7 +78,7 @@ # income-tax difference table extract for 2018 by expanded-income decile # read source code at following URL for details # http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_difference_table -diff_table = create_difference_table(calc1.records, calc2.records, +diff_table = create_difference_table(calc1, calc2, groupby='weighted_deciles', income_measure='expanded_income', tax_to_diff='iitax') From c0d78de165ba888663ce0598a111e43b68e73f42 Mon Sep 17 00:00:00 2001 From: Martin Holmer Date: Wed, 29 Nov 2017 09:55:19 -0500 Subject: [PATCH 20/33] Update cookbook recipe00 --- .../ingredients/raise_rates_and_stdded.json | 2 +- docs/cookbook/recipe00.py | 20 ++++++++++++------- docs/cookbook/recipe00.res | 2 ++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/docs/cookbook/ingredients/raise_rates_and_stdded.json b/docs/cookbook/ingredients/raise_rates_and_stdded.json index c4480ec94..836619c1b 100644 --- a/docs/cookbook/ingredients/raise_rates_and_stdded.json +++ b/docs/cookbook/ingredients/raise_rates_and_stdded.json @@ -1,4 +1,4 @@ -// Raises standard deduction and raises regular tax rates in top three brackets +// Raise standard deduction and raise regular tax rates in top three brackets // from (0.33, 0.35, 0.396) to (0.35, 0.37, 0.42) beginning in 2018. { "policy": { diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 436f0b5c2..11c26896b 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -18,7 +18,7 @@ itax_rev1 = calc1.weighted_total('iitax') # read JSON reform file and use (the default) static analysis assumptions -reform_filename = './ingredients/raise_stdded_and_rates.json' +reform_filename = './ingredients/raise_rates_and_stdded.json' params = Calculator.read_json_param_objects(reform=reform_filename, assump=None) @@ -55,18 +55,24 @@ # income-tax distribution for 2018 with CLP and REF results side-by-side # read source code at following URL for details # http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_distribution_table -expincbase = 'expanded_income_baseline' -calc1.add_records_variable(expincbase, calc1, 'expanded_income') -calc2.add_records_variable(expincbase, calc1, 'expanded_income') dist_table1 = create_distribution_table(calc1, groupby='weighted_deciles', - income_measure=expincbase, + income_measure='expanded_income', result_type='weighted_sum') +assert isinstance(dist_table1, pd.DataFrame) +import numpy +if numpy.allclose(calc2.records.expanded_income, + calc1.records.expanded_income): + print("******** EXPANDED_INCOME IS SAME ********\n") + income_measure = 'expanded_income' +else: + print("******** EXPANDED_INCOME DIFFERS ********\n") + income_measure = 'expanded_income_baseline' + calc2.add_records_variable(income_measure, calc1, 'expanded_income') dist_table2 = create_distribution_table(calc2, groupby='weighted_deciles', - income_measure=expincbase, + income_measure=income_measure, result_type='weighted_sum') -assert isinstance(dist_table1, pd.DataFrame) assert isinstance(dist_table2, pd.DataFrame) dist_extract = pd.DataFrame() dist_extract['funits(#m)'] = dist_table1['s006'] * 1e-6 diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index 158a89650..c4d93f517 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -60,6 +60,8 @@ Policy Reform Parameter Values by Year: 2018_CLP_itax_rev($B)= 1287.51 2018_REF_itax_rev($B)= 1234.66 +******** EXPANDED_INCOME IS SAME ******** + CLP diagnostic table for 2018: 2018 Returns (#m) 169.89 From 1e2dfa4a2a616c4b09bac0b99f3a7ca7adf57992 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Sat, 2 Dec 2017 08:23:24 -0500 Subject: [PATCH 21/33] Update recipie00.py and recipe00.res --- docs/cookbook/recipe00.py | 33 ++++----------------------------- docs/cookbook/recipe00.res | 2 -- taxcalc/utils.py | 6 +++--- 3 files changed, 7 insertions(+), 34 deletions(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 11c26896b..3c937a57d 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -47,32 +47,12 @@ # generate several other standard results tables: # aggregate diagnostic tables for 2018 -# read source code at following URL for details -# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_diagnostic_table -clp_diagnostic_table = create_diagnostic_table(calc1) -ref_diagnostic_table = create_diagnostic_table(calc2) +clp_diagnostic_table = calc1.diagnostic_table(1) +ref_diagnostic_table = calc2.diagnostic_table(1) # income-tax distribution for 2018 with CLP and REF results side-by-side -# read source code at following URL for details -# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_distribution_table -dist_table1 = create_distribution_table(calc1, - groupby='weighted_deciles', - income_measure='expanded_income', - result_type='weighted_sum') +dist_table1, dist_table2 = calc1.distribution_tables(calc2) assert isinstance(dist_table1, pd.DataFrame) -import numpy -if numpy.allclose(calc2.records.expanded_income, - calc1.records.expanded_income): - print("******** EXPANDED_INCOME IS SAME ********\n") - income_measure = 'expanded_income' -else: - print("******** EXPANDED_INCOME DIFFERS ********\n") - income_measure = 'expanded_income_baseline' - calc2.add_records_variable(income_measure, calc1, 'expanded_income') -dist_table2 = create_distribution_table(calc2, - groupby='weighted_deciles', - income_measure=income_measure, - result_type='weighted_sum') assert isinstance(dist_table2, pd.DataFrame) dist_extract = pd.DataFrame() dist_extract['funits(#m)'] = dist_table1['s006'] * 1e-6 @@ -82,12 +62,7 @@ dist_extract['aftertax_inc2($b)'] = dist_table2['aftertax_income'] * 1e-9 # income-tax difference table extract for 2018 by expanded-income decile -# read source code at following URL for details -# http://taxcalc.readthedocs.io/en/latest/_modules/taxcalc/utils.html#create_difference_table -diff_table = create_difference_table(calc1, calc2, - groupby='weighted_deciles', - income_measure='expanded_income', - tax_to_diff='iitax') +diff_table = calc1.difference_table(calc2, tax_to_diff='iitax') assert isinstance(diff_table, pd.DataFrame) diff_extract = pd.DataFrame() dif_colnames = ['count', 'tot_change', 'mean', diff --git a/docs/cookbook/recipe00.res b/docs/cookbook/recipe00.res index c4d93f517..158a89650 100644 --- a/docs/cookbook/recipe00.res +++ b/docs/cookbook/recipe00.res @@ -60,8 +60,6 @@ Policy Reform Parameter Values by Year: 2018_CLP_itax_rev($B)= 1287.51 2018_REF_itax_rev($B)= 1234.66 -******** EXPANDED_INCOME IS SAME ******** - CLP diagnostic table for 2018: 2018 Returns (#m) 169.89 diff --git a/taxcalc/utils.py b/taxcalc/utils.py index c2e635b77..18fbe88f7 100644 --- a/taxcalc/utils.py +++ b/taxcalc/utils.py @@ -576,18 +576,18 @@ def diagnostic_table_odict(recs): agi = vdf['c00100'] odict['AGI ($b)'] = (agi * wghts).sum() * in_billions # number of itemizers - num = (wghts[(vdf['c04470'] > 0.) * (agi > 0.)].sum()) + num = (wghts[(vdf['c04470'] > 0.) & (agi > 0.)].sum()) odict['Itemizers (#m)'] = num * in_millions # itemized deduction ided1 = vdf['c04470'] * wghts val = ided1[vdf['c04470'] > 0.].sum() odict['Itemized Deduction ($b)'] = val * in_billions # number of standard deductions - num = wghts[(vdf['standard'] > 0.) * (agi > 0.)].sum() + num = wghts[(vdf['standard'] > 0.) & (agi > 0.)].sum() odict['Standard Deduction Filers (#m)'] = num * in_millions # standard deduction sded1 = recs.standard * wghts - val = sded1[(vdf['standard'] > 0.) * (agi > 0.)].sum() + val = sded1[(vdf['standard'] > 0.) & (agi > 0.)].sum() odict['Standard Deduction ($b)'] = val * in_billions # personal exemption val = (vdf['c04600'] * wghts)[agi > 0.].sum() From 77bed2524d19903e0129973579859f278a2a70cf Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 6 Dec 2017 08:17:49 -0500 Subject: [PATCH 22/33] Edit comment in recipe00.py --- docs/cookbook/recipe00.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 3c937a57d..5361ae480 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -61,7 +61,7 @@ dist_extract['aftertax_inc1($b)'] = dist_table1['aftertax_income'] * 1e-9 dist_extract['aftertax_inc2($b)'] = dist_table2['aftertax_income'] * 1e-9 -# income-tax difference table extract for 2018 by expanded-income decile +# income-tax difference table by expanded-income decile for 2018 diff_table = calc1.difference_table(calc2, tax_to_diff='iitax') assert isinstance(diff_table, pd.DataFrame) diff_extract = pd.DataFrame() From c3cd3fd93940b1db85300ed7fc50ccff0dd4df8b Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 6 Dec 2017 09:24:10 -0500 Subject: [PATCH 23/33] Add kitchen setup text --- docs/cookbook.html | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/cookbook.html b/docs/cookbook.html index 17e9af676..80cf2ecfe 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -37,18 +37,34 @@

Cookbook Contents

be added to this cookbook or
report problems encountered when following an existing recipe

-

Basic Recipes

+

Basic Recipe

Static Analysis of a Simple Reform

-

Advanced Recipes

+

Other Recipes

coming soon

Preliminaries: Kitchen Setup

-

coming soon

+

You need to setup your computer in certain ways in order to follow +these recipes. First, follow +these instructions on installing Anaconda and a taxcalc package on +your computer. And second, install the recipes and ingredients in this +cookbook on your computer. There are several ways to do that. If you +have cloned the Tax-Calculator repository (see the + +Contributor Guide), then the recipes are located in the +Tax-Calculator/docs/cookbook directory. If you haven't +cloned the repository, you have a couple of choices. You can download +the source code as a zip file by clicking on the green Clone or +download button on + +this page. Or you can copy and paste the recipes and ingredients +to your local computer. If you follow this last option, you may need +to edit file paths in the recipes depending on how you've organized +the copy-and-pasted files.

Back to Cookbook Contents

@@ -81,10 +97,10 @@

Preliminaries: Recipe Feedback

Back to Cookbook Contents

-

Basic Recipes: Static Analysis of a Simple Reform

+

Basic Recipe: Static Analysis of a Simple Reform

The is the recipe you should follow first. Mastering this recipe -is a prerequiste for all the other recipes in this cookbook.

+-is a prerequisite for all the other recipes in this cookbook.

Ingredients

From f716d82d94a17f9b0458e962d6637a3df3848999 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 6 Dec 2017 11:42:27 -0500 Subject: [PATCH 24/33] Finish adding text to cookbook.html file --- docs/cookbook.html | 78 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/docs/cookbook.html b/docs/cookbook.html index 80cf2ecfe..7b40d24f5 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -52,7 +52,7 @@

Preliminaries: Kitchen Setup

these recipes. First, follow these instructions on installing Anaconda and a taxcalc package on your computer. And second, install the recipes and ingredients in this -cookbook on your computer. There are several ways to do that. If you +cookbook on your computer. There are several ways to do this. If you have cloned the Tax-Calculator repository (see the Contributor Guide), then the recipes are located in the @@ -61,24 +61,75 @@

Preliminaries: Kitchen Setup

the source code as a zip file by clicking on the green Clone or download button on -this page. Or you can copy and paste the recipes and ingredients -to your local computer. If you follow this last option, you may need -to edit file paths in the recipes depending on how you've organized -the copy-and-pasted files. +this page. This choice has the advantage of replicating the +directory structure and file names we use in our test kitchen. Or you +can copy and paste the recipes and ingredients, as needed, to your +local computer. If you make this second choice, you may need to edit +file paths in the recipes depending on how you've organized and named +the copied-and-pasted files.

Back to Cookbook Contents

Preliminaries: Recipe Techniques

-

coming soon

+

As with any cookbook, the best approach is to follow a recipe +exactly the first time and then, if needed, modify the recipe to get +the exactly the dish you want. Remember to copy a recipe file +and give it a new file name before you start to modify the recipe.

+ +

The Calculator object is the central object in Tax-Calculator and +it is created by passing four secondary objects (a Policy object, a +Records object, a Behavior object, and a Consumption object) to the +Calculator class constructor. When modifying a recipe, following a +few rules will minimize the chance of running into problems.

+ +

Fully specify Consumption, Behavior, Records, and Policy objects +before passing them to the Calculator class constructor.

+ +

After initializing a Calculator object, manipulate it using only +Calculator class methods.

+ +

Following these two rules means avoiding the manipulation of a +Calculator object's private internal objects. You should definitely +avoid trying to change those internal Calculator objects. And if you +find yourself wanting to read those internal objects, look for a way +to do that using public Calculator methods. If no Calculator methods +allow you to get out of the Calculator object the information you +need, create +a new issue asking for a Tax-Calculator enhancement.

+ +

The recipes in this cookbook are Python scripts that +can be executed from the command line like this: +

+python recipe00.py > recipe00.out
+diff recipe00.out recipe00.res
+
+Your kitchen setup would be validated if the above diff +command yields no differences. Of course, you can substitute your +favorite graphical diff tool for diff to get an easier +to read set of differences.

+ +

Some people like to use Tax-Calculator inside an interactive +notebook. You should be able to load a recipe into an empty notebook +and execute it there. If you want to work that way, the recipes may +require some modification to show results interactively. But if you +are a notebook user, you will know how to make a recipe work in a +notebook.

Back to Cookbook Contents

Preliminaries: Recipe Ingredients

-

coming soon

+

All the ingredients needed for the recipes are included in +the Tax-Calculator/docs/cookbook/ingredients directory. If +you organize the recipes and ingredients in a different way than they +are organized in our test kitchen, you will need to change the file +path for each ingredient in each recipe.

+ +

Just like with recipe modification, copy and rename an ingredient +file before you make modifications to it.

Back to Cookbook Contents

@@ -99,12 +150,15 @@

Preliminaries: Recipe Feedback

Basic Recipe: Static Analysis of a Simple Reform

-

The is the recipe you should follow first. Mastering this recipe --is a prerequisite for all the other recipes in this cookbook.

+

This is the recipe you should follow first. Mastering this recipe +is a prerequisite for all the other recipes in this cookbook.

Ingredients

-

AMT-repeal-plus-higher-tax-rates reform in the ingredients/repeal_amt.json file

+ +

Policy +reform in the ingredients/raise_rates_and_stdded.json +file

Instructions

@@ -112,7 +166,9 @@

Basic Recipe: Static Analysis of a Simple Reform

Results

-

Expected results from executing python recipe00.py at the command prompt

+

Expected results from +executing python recipe00.py at the command prompt as shown +above.

Back to Cookbook Contents

From c6d3e8d912db8fe72245f17712cb4340e9205106 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 6 Dec 2017 12:32:38 -0500 Subject: [PATCH 25/33] Minor edits of index.htmx and cookbook.html files --- docs/cookbook.html | 8 ++++---- docs/index.htmx | 18 +++++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/cookbook.html b/docs/cookbook.html index 7b40d24f5..48702ff51 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -1,5 +1,4 @@ - Tax-Calculator Cookbook @@ -96,8 +95,9 @@

Preliminaries: Recipe Techniques

find yourself wanting to read those internal objects, look for a way to do that using public Calculator methods. If no Calculator methods allow you to get out of the Calculator object the information you -need, create -a new issue asking for a Tax-Calculator enhancement.

+need, + +create a new issue asking for a Tax-Calculator enhancement.

The recipes in this cookbook are Python scripts that can be executed from the command line like this: @@ -107,7 +107,7 @@

Preliminaries: Recipe Techniques

Your kitchen setup would be validated if the above diff command yields no differences. Of course, you can substitute your -favorite graphical diff tool for diff to get an easier +favorite graphical diff program for diff to get an easier to read set of differences.

Some people like to use Tax-Calculator inside an interactive diff --git a/docs/index.htmx b/docs/index.htmx index a817c76b9..ecd0fdca7 100644 --- a/docs/index.htmx +++ b/docs/index.htmx @@ -15,14 +15,18 @@ div { max-width: 19cm }

This document tells you how to use Tax-Calculator, an open-source federal income and payroll tax simulation model. The two ways of using Tax-Calculator described here require no computer programming. -If you want to participate in the development of Tax-Calculator -— by asking a question, reporting a bug, improving the documentation -or making an enhancement to the Python source code — you should go to +If you are interested in Python programming with Tax-Calculator, you +should read the tested recipes in our + +Tax-Calculator Cookbook. If you want to participate in the +development of Tax-Calculator — by asking a question, reporting +a bug, improving the documentation or making an enhancement to the +Python source code — you should go to the developer website.

-

Please cite the source of your analysis as "Tax-Calculator (Version -#.#.#)" and link to this site.

+

Please cite the source of your analysis as Tax-Calculator +(version #.#.#) and link to this site.

Document Contents

@@ -639,8 +643,8 @@ that make sense only with input files that contain representative samples of the population such as the cps.csv file. The name of this option stands for certainty-equivalent expected utility. If you want to use this output option, you should read the commented -Python source code for the ce_aftertax_income function in -the taxcalc/utils.py file at the +Python source code for the ce_aftertax_expanded_income +function in the taxcalc/utils.py file at the developer website.

From 8620dfdba2a2a6441ea8003cf3e0c42e9eb9b0b8 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Wed, 6 Dec 2017 17:12:57 -0500 Subject: [PATCH 26/33] Add graph output to cookbook basic recipe --- docs/cookbook.html | 19 ++++++++++++++++--- docs/cookbook/recipe00-graph.png | Bin 0 -> 50023 bytes docs/cookbook/recipe00.py | 9 +++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 docs/cookbook/recipe00-graph.png diff --git a/docs/cookbook.html b/docs/cookbook.html index 48702ff51..688de246a 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -117,6 +117,10 @@

Preliminaries: Recipe Techniques

are a notebook user, you will know how to make a recipe work in a notebook.

+

After writing an HTML graph file to disk, you can view it in your +favorite browser. The easiest way to do that varies by operating +system.

+

Back to Cookbook Contents

@@ -166,9 +170,18 @@

Basic Recipe: Static Analysis of a Simple Reform

Results

-

Expected results from -executing python recipe00.py at the command prompt as shown -above.

+

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

+ +

Expected graph (located +in the same directory as recipe00.py and named +recipe00-graph.html) from executing +python recipe00.py > recipe00.out at the command prompt as +shown above. To view the HTML graph file +generated when you follow the recipe, open it in your favorite +browser. For example, on a Mac, you enter at the command prompt +open recipe00-graph.html.

Back to Cookbook Contents

diff --git a/docs/cookbook/recipe00-graph.png b/docs/cookbook/recipe00-graph.png new file mode 100644 index 0000000000000000000000000000000000000000..18083cfc5c084aaa07e5df8617bafae37f11f6ce GIT binary patch literal 50023 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 literal 0 HcmV?d00001 diff --git a/docs/cookbook/recipe00.py b/docs/cookbook/recipe00.py index 5361ae480..bada4c8ee 100644 --- a/docs/cookbook/recipe00.py +++ b/docs/cookbook/recipe00.py @@ -3,6 +3,11 @@ # use publicly-available CPS input file recs = Records.cps_constructor() +# NOTE: if you have access to the restricted-use IRS-SOI PUF-based input file +# and you have that file (named 'puf.csv') located in the directory +# where this script is located, then you can substitute the following +# statement for the prior statement: +# recs = Records() # specify Calculator object for static analysis of current-law policy pol = Policy() @@ -73,6 +78,10 @@ for dname, ename, sfactor in zip(dif_colnames, ext_colnames, scaling_factors): diff_extract[ename] = diff_table[dname] * sfactor +# generate a graph and save in an HTML file +fig = calc1.decile_graph(calc2) +write_graph_file(fig, 'recipe00-graph.html', 'recipe00 graph') + print('CLP diagnostic table for 2018:') print(clp_diagnostic_table) print('') From 295472af79976c4de25c046fb1489d62c50fa765 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Thu, 7 Dec 2017 16:16:43 -0500 Subject: [PATCH 27/33] Minor edits to cookbook.html file --- docs/cookbook.html | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/cookbook.html b/docs/cookbook.html index 688de246a..2efba38a5 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -105,10 +105,11 @@

Preliminaries: Recipe Techniques

python recipe00.py > recipe00.out diff recipe00.out recipe00.res -Your kitchen setup would be validated if the above diff -command yields no differences. Of course, you can substitute your -favorite graphical diff program for diff to get an easier -to read set of differences.

+Your kitchen setup and ability to follow a recipe and produce the same +dish as we produce in our test kitchen would be validated if +the above diff command yields no differences. Of course, +you can substitute your favorite graphical diff program +for diff to get an easier to read set of differences.

Some people like to use Tax-Calculator inside an interactive notebook. You should be able to load a recipe into an empty notebook @@ -180,8 +181,10 @@

Basic Recipe: Static Analysis of a Simple Reform

python recipe00.py > recipe00.out at the command prompt as shown above. To view the HTML graph file generated when you follow the recipe, open it in your favorite -browser. For example, on a Mac, you enter at the command prompt -open recipe00-graph.html.

+browser. For example, on a Mac, you would enter at the command prompt +the following +
open recipe00-graph.html
+to start your default browser showing the graph.

Back to Cookbook Contents

From 440773ba37eca4e0ab89a45d9fbed7d841afc688 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Mon, 11 Dec 2017 16:07:18 -0500 Subject: [PATCH 28/33] Add distributed processing tip to index.htmx docs --- docs/index.htmx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/index.htmx b/docs/index.htmx index 0a6a65d47..d0a1bd421 100644 --- a/docs/index.htmx +++ b/docs/index.htmx @@ -504,7 +504,7 @@ cents) as in the original dump output.

This full dump output can be useful for debugging and is small when using a number of stylized filing units as input. But when using large samples as input (for example, the cps.csv input -file), the size of the dump output can be quite large. Beginning with +file), the size of the dump output becomes quite large. Beginning with Tax-Calculator version 0.14.0, there is a way to specify a partial dump that includes only variables of interest. To have tc do a partial dump, create a file named tcdumpvars in the @@ -683,6 +683,22 @@ function in the taxcalc/utils.py file at the developer website.

+

In all the examples in this section, we have executed one tc run at +at time. But what if you want to execute many tc runs because +you want results for many years and/or for several different reforms. +Unless you are asking for full-dump output, a single tc run should +take no more than one minute on your computer (even if you are using +the large cps.csv input file). The easiest way to speed up +the execution of many tc runs is to split them into groups of runs and +execute each group of runs in a different command-prompt window. On +most modern computers that have four CPU cores and a fast disk drive, +executing four runs in different windows will take not much more time +than executing a single tc run. If you have more than one run in each +group, put them in a Unix/Mac bash script or a Windows batch file, and +execute one script in each command-prompt window. If it still takes +too long, consider splitting the tc runs across more than one +computer.

+

Back to Section Contents

From 3ae854ae4d68be2724cec00e3f4328f1b4ebd8e2 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Mon, 11 Dec 2017 16:31:10 -0500 Subject: [PATCH 29/33] Edits of cookbook.html --- docs/cookbook.html | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/docs/cookbook.html b/docs/cookbook.html index 2efba38a5..24d0fe835 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -14,8 +14,9 @@

A Cookbook of Tested Recipes for Python Programming with

This document tells you how to use Tax-Calculator, an open-source federal income and payroll tax simulation model, in Python scripts -that you can run on your own computer. For other ways of using -Tax-Calculator, see the +that you can run on your own computer. Note that these recipes +require Tax-Calculator release 0.14.0 or higher. For other ways of +using Tax-Calculator, see the user documentation.

@@ -61,11 +62,14 @@

Preliminaries: Kitchen Setup

download button on this page. This choice has the advantage of replicating the -directory structure and file names we use in our test kitchen. Or you -can copy and paste the recipes and ingredients, as needed, to your -local computer. If you make this second choice, you may need to edit -file paths in the recipes depending on how you've organized and named -the copied-and-pasted files.

+directory structure and file names that we use in our test kitchen. +To comfirm your unzipped installation is valid, execute +python test_recipes.py in the +Tax-Calculator/docs/cookbook directory. Or you can copy +and paste the recipes and ingredients, as needed, to your local +computer. If you make this second choice, you may need to edit file +paths in the recipes depending on how you've organized and named the +copied-and-pasted files.

Back to Cookbook Contents

@@ -171,6 +175,16 @@

Basic Recipe: Static Analysis of a Simple Reform

Results

+

+When following the recipe as shown below, you will get three instances +of the same ignored error message from deep inside the Pandas +library that is being used by Tax-Calculator. After conferring with +the Pandas developers, our expectation is these error messages will go +away when we upgrade to Pandas version 0.22.0, which is scheduled to +be released in January 2018, and which fixes a bug in the Pandas +library. Meanwhile, the error messages are annoying but harmless. +

+

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

From 8ca12437a1e107fd94db5c343874b411050e128a Mon Sep 17 00:00:00 2001 From: martinholmer Date: Mon, 11 Dec 2017 16:46:13 -0500 Subject: [PATCH 30/33] Do not include stderr in test_recipes output --- docs/cookbook.html | 11 ++++++----- docs/cookbook/test_recipes.py | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/cookbook.html b/docs/cookbook.html index 24d0fe835..123511e42 100644 --- a/docs/cookbook.html +++ b/docs/cookbook.html @@ -65,11 +65,12 @@

Preliminaries: Kitchen Setup

directory structure and file names that we use in our test kitchen. To comfirm your unzipped installation is valid, execute python test_recipes.py in the -Tax-Calculator/docs/cookbook directory. Or you can copy -and paste the recipes and ingredients, as needed, to your local -computer. If you make this second choice, you may need to edit file -paths in the recipes depending on how you've organized and named the -copied-and-pasted files.

+Tax-Calculator/docs/cookbook directory to make sure all the +recipes PASS. Or you can copy and paste the recipes and +ingredients, as needed, to your local computer. If you make this +second choice, you may need to edit file paths in the recipes +depending on how you've organized and named the copied-and-pasted +files.

Back to Cookbook Contents

diff --git a/docs/cookbook/test_recipes.py b/docs/cookbook/test_recipes.py index 2cd6ee182..250c80911 100644 --- a/docs/cookbook/test_recipes.py +++ b/docs/cookbook/test_recipes.py @@ -28,8 +28,7 @@ if os.path.isfile(out_filename): os.remove(out_filename) try: - out = subprocess.check_output(['python', recipe], - stderr=subprocess.STDOUT) + out = subprocess.check_output(['python', recipe]) except subprocess.CalledProcessError as err: print('{} FAIL with error rtncode={}'.format(recipe, err.returncode)) continue # to next recipe From da7a7a9f7c59c69a8ed3c04affff1f0772d39e9b Mon Sep 17 00:00:00 2001 From: martinholmer Date: Mon, 11 Dec 2017 17:00:41 -0500 Subject: [PATCH 31/33] Add link to cookbook in README.md file --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c5b3b98b7..21e514190 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,11 @@ you should begin by reading the [user documentation](http://open-source-economics.github.io/Tax-Calculator/) that describes how to use Tax-Calculator on your own computer (without doing any programming) and how to use the Tax-Calculator web application -called [TaxBrain](http://www.ospc.org/taxbrain/). +called [TaxBrain](http://www.ospc.org/taxbrain/). If you want the most +flexibility in using Tax-Calculator on your own computer, start +by reading our [Cookbook of Tested Recipes for Python Programming with +Tax-Calculator](http://open-source-economics.github.io/Tax-Calculator/cookbook.html). + What is Tax-Calculator? ----------------------- From ce4f633f6824fe37ce69b45b44c3e6bb00730549 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Mon, 11 Dec 2017 17:10:17 -0500 Subject: [PATCH 32/33] Update RELEASES.md info for 0.14.0 release --- RELEASES.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 770190cc3..77e49e161 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,10 +4,10 @@ Go [here](https://github.com/open-source-economics/Tax-Calculator/pulls?q=is%3Ap for a complete commit history. -2017-12-?? Release 0.14.0 +2017-12-11 Release 0.14.0 ------------------------- (last merged pull request is -[#xxxx](https://github.com/open-source-economics/Tax-Calculator/pull/xxxx)) +[#1742](https://github.com/open-source-economics/Tax-Calculator/pull/1742)) **API Changes** - Add several Calculator table methods and revise table utilities to not use Calculator object(s) @@ -30,6 +30,9 @@ for a complete commit history. - Add ability to specify partial customized CLI `tc --dump` output [[#1735](https://github.com/open-source-economics/Tax-Calculator/pull/1735) by Martin Holmer as suggested by Sean Wang] +- Add *Cookbook of Tested Recipes for Python Programming with Tax-Calculator* + [[#1740](https://github.com/open-source-economics/Tax-Calculator/pull/1740) + by Martin Holmer] - Add calculation of two values on the ALL row of the difference table [[#1741](https://github.com/open-source-economics/Tax-Calculator/pull/1741) by Martin Holmer] From 5cd451655a7a6633cc7c1e24118bc7f9601d92b4 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Mon, 11 Dec 2017 17:27:08 -0500 Subject: [PATCH 33/33] Update docs/index.html file --- docs/index.html | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/index.html b/docs/index.html index dec381996..ea2e4788a 100644 --- a/docs/index.html +++ b/docs/index.html @@ -505,7 +505,7 @@

Initiate Reform Analysis

This full dump output can be useful for debugging and is small when using a number of stylized filing units as input. But when using large samples as input (for example, the cps.csv input -file), the size of the dump output can be quite large. Beginning with +file), the size of the dump output becomes quite large. Beginning with Tax-Calculator version 0.14.0, there is a way to specify a partial dump that includes only variables of interest. To have tc do a partial dump, create a file named tcdumpvars in the @@ -684,6 +684,22 @@

Initiate Reform Analysis

developer website.

+

In all the examples in this section, we have executed one tc run at +at time. But what if you want to execute many tc runs because +you want results for many years and/or for several different reforms. +Unless you are asking for full-dump output, a single tc run should +take no more than one minute on your computer (even if you are using +the large cps.csv input file). The easiest way to speed up +the execution of many tc runs is to split them into groups of runs and +execute each group of runs in a different command-prompt window. On +most modern computers that have four CPU cores and a fast disk drive, +executing four runs in different windows will take not much more time +than executing a single tc run. If you have more than one run in each +group, put them in a Unix/Mac bash script or a Windows batch file, and +execute one script in each command-prompt window. If it still takes +too long, consider splitting the tc runs across more than one +computer.

+

Back to Section Contents