Metadata-Version: 2.4
Name: taxsim-py
Version: 0.1.0
Summary: A dataframe-oriented Python implementation of NBER TAXSIM
License-Expression: MIT
Project-URL: Homepage, https://github.com/jrothbaum/taxsim_py
Project-URL: Documentation, https://jrothbaum.github.io/taxsim_py/
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: polars>=1.28.1
Requires-Dist: pyyaml>=6.0.3
Provides-Extra: readstat
Requires-Dist: polars-readstat>=0.20.2; extra == "readstat"
Dynamic: license-file

THIS IS STILL IN DEVELOPMENT AND NOT ON PYPI YET

# taxsim-py

`taxsim-py` is a dataframe-oriented Python implementation of NBER TAXSIM,
independent of and not affiliated with NBER. It calculates federal income tax,
payroll tax, and state income tax for household records, using Polars
`DataFrame` or `LazyFrame` inputs and clear variable names (TAXSIM's `v1`-style
names only on request).

**[Documentation](https://jrothbaum.github.io/taxsim_py/)** ·
**[Try the calculator in your browser](https://jrothbaum.github.io/taxsim_py/calculator/)**

## Install

Python 3.9 or newer: `pip install taxsim-py` or `uv add taxsim-py`. From a
checkout: `uv sync --group dev --group test`.

## Basic use

Every row needs `mstat` and `state` (TAXSIM codes, or Census FIPS with
`state_id_type="fips"`). Include `year` in the data or pass `year=`.

```python
import polars as pl

from taxsim_py import calculate_taxes

households = pl.DataFrame(
    {
        "year": [2021, 2021],
        "state": [6, 36],       # California, New York
        "mstat": [1, 2],        # single, married filing jointly
        "page": [45, 50],
        "pwages": [50_000, 80_000],
        "swages": [0, 40_000],
        "depx": [0, 2],
    }
)

taxes = calculate_taxes(households)
print(taxes.select("fiitax", "fica", "siitax"))
```

The result is the input plus `fiitax` (federal income tax), `fica` (payroll
taxes), `siitax` (state income tax) and other outputs, and `frate`/`srate`
(marginal rates) when requested. Missing inputs default to zero (`dep13`,
`dep17` and `dep18` default to `depx`). `idtl=2` adds detailed federal and state
worksheets, `taxsim_names=True` renames them to TAXSIM's labels, and
`keep_intermediate=True` keeps every intermediate column for auditing.

## Calculation modes

`statutory` (default) uses the canonical parameter tables and reviewed
corrections to TAXSIM. `calculation_mode="taxsim"` reproduces the compiled
TAXSIM, for replication and comparison; it is not recommended for new analysis.

## In the browser

`web/index.html` is a calculator that runs taxsim-py in the browser through Pyodide:
a form for one household, and a CSV upload for many. Nothing is sent to a server.

```bash
uv run python scripts/build_web.py      # builds the wheel into web/
python -m http.server -d web            # then open http://localhost:8000
```

From JavaScript or Python, `taxsim_py.calculate_row({...})` takes a dict of TAXSIM
inputs and returns a dict of inputs plus results.

## Command line

```bash
taxsim-py households.csv taxes.parquet
taxsim-py households.dta                 # CSV on standard output
taxsim-py households.csv taxes.csv --mode taxsim --batch-rows 50000 --workers 4
```

File types come from the extensions: `csv`, `tsv`, `parquet`, `arrow`, `ndjson`,
and Stata (`dta`), SPSS (`sav`, `zsav`) and SAS (`sas7bdat`, read only) with the
optional reader (`pip install "taxsim-py[readstat]"`, which adds
[polars-readstat](https://github.com/jrothbaum/polars_readstat)). Use
`--input-format`/`--output-format` when the extension is not useful and
`--lowercase` for SAS files with uppercase names. The same readers are
`taxsim_py.io.tables.read_table` and `write_table`.

## What is supported

- **Years:** federal tax 1960-2025 and state tax 1977-2025, all actual law. Other
  years raise an error.
- **States:** all 50 states and DC (TAXSIM codes 1-51; 0 means no state). States
  without an income tax return 0, except Washington's Working Families credit in
  statutory mode.
- **Inputs:** TAXSIM's 35 inputs, with the same meanings and units, plus the
  optional `children_under_3`, `children_under_4` and `children_under_7`. Two
  conventions: `psemp`/`ssemp` get no qualified business income deduction (use
  `pbusinc`/`pprofinc`), and `pensions` is the kind of pension each state exempts.
- **Accuracy:** in `taxsim` mode, to the cent against the compiled TAXSIM on the
  validation matrix (`scripts/validate_federal.py`, `scripts/validate_states.py`),
  apart from logged TAXSIM errors. `statutory` mode follows the law where TAXSIM is
  wrong ([Statutory corrections](docs/statutory_corrections.md)); its remaining
  differences from PolicyEngine-US (2022-2025) are in the
  [PolicyEngine comparison](docs/policyengine_recent_state_comparison.md).
- **Not modelled:** items TAXSIM has no input for, such as 2025 deductions for tips,
  overtime and car-loan interest, and Washington's capital gains tax.
- **Speed:** on a mixed 42-state batch, 1,000,000 rows including marginal rates
  take about 7.4 s and 2.8 GiB. The compiled TAXSIM takes 17.2 s for the same
  calculation.
  `batch_rows` and `max_year_workers` limit memory. See [Performance](docs/performance.md).

## Tests

`uv run pytest -q` runs the test suite, `uv run scripts/validate_all.py` the full
validation matrix, and `uv run scripts/compare_cps.py PATH/TO/cps_2011 --tax-year
2021` a CPS comparison with the compiled TAXSIM (from the `policyengine-taxsim`
test dependency).

## Documentation

Start with the [user guide](docs/index.md) (build it with `uvx --with mkdocs-material mkdocs serve`). Reference and working notes: [Architecture](docs/architecture.md),
[Statutory corrections](docs/statutory_corrections.md),
[Performance](docs/performance.md),
[PolicyEngine comparison](docs/policyengine_recent_state_comparison.md),
[Pending issues](docs/pending_issues.md) and
[Parameter tables](parameters/README.md).

## License

MIT; see [LICENSE](LICENSE).
