Interop with grattan and taxstats: costing a hypothetical reform

ato sits in a three-package constellation for Australian tax research:

ato provides ato_to_taxstats() to rename aggregate columns to the microdata schema, so analyses can move cleanly between views.

Schema map

library(ato)
head(ato_schema_map(), 15)

Costing a hypothetical reform

Suppose we want a first-pass cost of lifting the Medicare Levy Surcharge income threshold by AUD 10,000. Workflow:

  1. Get MLS taxpayer counts and amounts by income band from ATO aggregates.
  2. Check the total matches Budget Paper 1.
  3. Hand off to taxstats 2 per cent sample for counterfactual microsimulation.
  4. Use grattan::income_tax() for the new-regime liability.
library(ato)

ato_snapshot("2026-04-24")

mls <- ato_medicare_levy(year = "2022-23", component = "surcharge")

# Reconcile the published MLS total against FBO where available
# (MLS does not have a direct FBO line; rolled into individuals
# income tax net).
ind_total <- sum(ato_individuals(year = "2022-23")$tax_payable,
                 na.rm = TRUE)
ato_reconcile(ind_total, "2022-23", "individuals_income_tax_net")

Bridge to microdata

# Rename columns to taxstats schema
mls_ts <- ato_to_taxstats(mls)

# Now these columns are consistent with what `taxstats::taxstats1819`
# uses. You can write analysis code that works on both.

# Example: use grattan to compute the new regime's tax for the 2% sample
# library(taxstats)
# library(grattan)
# sample_2pc <- taxstats1819
# sample_2pc$new_tax <- income_tax(
#   income = sample_2pc$Taxable_Income,
#   fy.year = "2023-24",
#   ...
# )
# reform_cost <- sum(sample_2pc$new_tax - sample_2pc$Tax_assessed_amt,
#                    na.rm = TRUE) * 50  # 2% -> 100%

Summary

The three-package stack covers the full tax-research pipeline: ato for authoritative aggregates and provenance, taxstats for distributional microdata, grattan for policy simulation. Use ato_reconcile() at the boundary to confirm the microdata estimate lines up with the population total.