Repository: PolicyEngine/policyengine-us (language: python)

Issue:
Delete `data` folder and replace with proper structure necessary to download, not build, datasets
This should now all be `policyengine-us-data`


Discussion / hints:
Because of changes to remove heavier dependencies, this now causes API updates ([link](https://github.com/PolicyEngine/policyengine-api/actions/runs/10902861343/job/30255693028)) and US package updates ([link](https://github.com/PolicyEngine/policyengine-us/actions/runs/10902763318/job/30255380873)) to fail.

Fix patch:
diff --git a/changelog_entry.yaml b/changelog_entry.yaml
index e69de29bb2d..f5581b1c29d 100644
--- a/changelog_entry.yaml
+++ b/changelog_entry.yaml
@@ -0,0 +1,6 @@
+- bump: minor
+  changes:
+    removed:
+    - Deprecated data folder
+    changed:
+    - Corrected connection with us-data repository
diff --git a/docs/_config.yml b/docs/_config.yml
index 9437f5d5476..fb6c2d048b3 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -3,8 +3,7 @@ author: PolicyEngine
 copyright: "2023"
 
 execute:
-  execute_notebooks: force
-  timeout: -1
+  execute_notebooks: off
 
 repository:
   url: https://github.com/policyengine/policyengine-us
diff --git a/policyengine_us/__init__.py b/policyengine_us/__init__.py
index 23b459372fc..cedf06bed4a 100644
--- a/policyengine_us/__init__.py
+++ b/policyengine_us/__init__.py
@@ -14,5 +14,3 @@
     Microsimulation,
     IndividualSim,
 )
-
-from policyengine_us.data import *
diff --git a/policyengine_us/data/__init__.py b/policyengine_us/data/__init__.py
deleted file mode 100644
index f06097efdf5..00000000000
--- a/policyengine_us/data/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-from policyengine_us_data.datasets import *
diff --git a/policyengine_us/data/datasets/README.md b/policyengine_us/data/datasets/README.md
deleted file mode 100644
index a6d057d7242..00000000000
--- a/policyengine_us/data/datasets/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Updating microdata
-
-If you are updating the microdata (e.g., modifying code in `cps.py`), you will need to regenerate the processed microdata file locally and add it as a new data release.
-
-Steps:
-
-1. Clear old microdata with `rm policyengine_us/data/storage/*.h5`
-2. Generate new microdata in Python with a command like this: `python -c "from policyengine_us.data import CPS_2022; CPS_2022().generate()"`
-3. Make a copy of the new microdata file with a name that includes the _bumped_ version number. For example, if PolicyEngine US is currently version 0.263.4 and you are updating the 2023 CPS in a minor-bump PR, run `cp policyengine_us/data/storage/cps_2022.h5 policyengine_us/data/storage/cps_2022_v0_263_5.h5`.
-4. Upload this new file to [github.com/PolicyEngine/policyengine-us/releases](https://github.com/PolicyEngine/policyengine-us/releases), both overwriting the existing unversioned file (delete the old one) and adding the versioned file as a new release.
-5. Update the `CPS_2022` class in `policyengine_us/data/cps.py` to point to the new versioned file, e.g. `new_url="release://policyengine/policyengine-us/cps-2023/cps_2022_v0_263_5.h5",`.
-6. Verify that this works by downloading it, e.g. `python -c "from policyengine_us.data import CPS_2022; CPS_2022().download()"`.
diff --git a/policyengine_us/data/datasets/__init__.py b/policyengine_us/data/datasets/__init__.py
deleted file mode 100644
index 389f8f0cca4..00000000000
--- a/policyengine_us/data/datasets/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from .cps import (
-    CPS_2019,
-    CPS_2020,
-    CPS_2021,
-    CPS_2022,
-    RawCPS_2020,
-    RawCPS_2021,
-    RawCPS_2022,
-    EnhancedCPS_2022,
-    CalibratedPUFExtendedCPS_2022,
-)
-
-from .puf import PUF_2022, PUF_2015
-
-from .poverty_tracker.poverty_tracker import PovertyTracker
-
-DATASETS = [
-    CPS_2019,
-    CPS_2020,
-    CPS_2021,
-    CPS_2022,
-    EnhancedCPS_2022,
-    CalibratedPUFExtendedCPS_2022,
-    PovertyTracker,
-    PUF_2022,
-    PUF_2015,
-]
diff --git a/policyengine_us/data/datasets/acs/__init__.py b/policyengine_us/data/datasets/acs/__init__.py
deleted file mode 100644
index 5656048f0d4..00000000000
--- a/policyengine_us/data/datasets/acs/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from policyengine_us.data.datasets.acs.raw_acs import RawACS
-from policyengine_us.data.datasets.acs.acs import ACS
diff --git a/policyengine_us/data/datasets/acs/acs.py b/policyengine_us/data/datasets/acs/acs.py
deleted file mode 100644
index e5b1646bee8..00000000000
--- a/policyengine_us/data/datasets/acs/acs.py
+++ /dev/null
@@ -1,118 +0,0 @@
-import logging
-from policyengine_core.data import PublicDataset
-import h5py
-from policyengine_us.data.datasets.acs.raw_acs import RawACS
-from policyengine_us.data.storage import policyengine_us_MICRODATA_FOLDER
-from pandas import DataFrame
-
-
-class ACS(PublicDataset):
-    name = "acs"
-    is_openfisca_compatible = True
-    label = "ACS"
-    folder_path = policyengine_us_MICRODATA_FOLDER
-
-    def generate(self, year: int) -> None:
-        """Generates the ACS dataset.
-
-        Args:
-            year (int): The year of the raw ACS to use.
-        """
-
-        # Prepare raw ACS tables
-        year = int(year)
-        if year in self.years:
-            self.remove(year)
-        if year not in RawACS.years:
-            RawACS.generate(year)
-
-        raw_data = RawACS.load(year)
-        acs = h5py.File(ACS.file(year), mode="w")
-
-        person, spm_unit, household = [
-            raw_data[entity] for entity in ("person", "spm_unit", "household")
-        ]
-        # Add primary and foreign keys
-
-        household.SERIALNO = household.SERIALNO.astype(int)
-        person.SERIALNO = person.SERIALNO.astype(int)
-        person.SPORDER = person.SPORDER.astype(int)
-        person.SPM_ID = person.SPM_ID.astype(int)
-        spm_unit.SPM_ID = spm_unit.SPM_ID.astype(int)
-
-        logging.info(
-            f"Persons with a linked household {person.SERIALNO.isin(household.SERIALNO).mean():.1%}"
-        )
-        person = person[person.SERIALNO.isin(household.SERIALNO)]
-        logging.info(
-            f"Households with a linked person {household.SERIALNO.isin(person.SERIALNO).mean():.1%}"
-        )
-        household = household[household.SERIALNO.isin(person.SERIALNO)]
-        logging.info(
-            f"SPM units with a linked person {spm_unit.SPM_ID.isin(person.SPM_ID).mean():.1%}"
-        )
-        spm_unit = spm_unit[spm_unit.SPM_ID.isin(person.SPM_ID)]
-
-        add_id_variables(acs, person, spm_unit, household)
-        add_person_variables(acs, person)
-        add_spm_variables(acs, spm_unit)
-        add_household_variables(acs, household)
-
-        raw_data.close()
-        acs.close()
-
-
-ACS = ACS()
-
-
-def add_id_variables(
-    acs: h5py.File,
-    person: DataFrame,
-    spm_unit: DataFrame,
-    household: DataFrame,
-) -> None:
-    """Add basic ID and weight variables.
-
-    Args:
-        acs (h5py.File): The ACS dataset file.
-        person (DataFrame): The person table of the ACS.
-        spm_unit (DataFrame): The SPM unit table created from the person table
-            of the ACS.
-        household (DataFrame): The household table of the ACS.
-    """
-    acs["person_id"] = person.SERIALNO * 1e2 + person.SPORDER
-    acs["person_spm_unit_id"] = person.SPM_ID
-    acs["spm_unit_id"] = spm_unit.SPM_ID
-    # ACS doesn't have tax units.
-    acs["tax_unit_id"] = spm_unit.SPM_ID
-    # Until we add a family table, we'll use the person table.
-    acs["family_id"] = spm_unit.SPM_ID
-    acs["person_household_id"] = person.SERIALNO
-    acs["person_tax_unit_id"] = person.SPM_ID
-    acs["person_family_id"] = person.SPM_ID
-    acs["household_id"] = household.SERIALNO
-
-    # TODO: add marital unit IDs - using person IDs for now
-    acs["person_marital_unit_id"] = person.SERIALNO
-    acs["marital_unit_id"] = person.SERIALNO.unique()
-
-    # Add weights
-    acs["person_weight"] = person.PWGTP
-    acs["household_weight"] = household.WGTP
-
-
-def add_person_variables(acs: h5py.File, person: DataFrame) -> None:
-    acs["age"] = person.AGEP
-    acs["employment_income"] = person.WAGP
-    acs["self_employment_income"] = person.SEMP
-    acs["total_income"] = person.PINCP
-
-
-def add_spm_variables(acs: h5py.File, spm_unit: DataFrame) -> None:
-    acs["spm_unit_net_income_reported"] = spm_unit.SPM_RESOURCES
-    acs["spm_unit_spm_threshold"] = spm_unit.SPM_POVTHRESHOLD
-
-
-def add_household_variables(acs: h5py.File, household: DataFrame) -> None:
-    acs["household_vehicles_owned"] = household.VEH
-    acs["state_fips"] = acs["household_state_fips"] = household.ST
diff --git a/policyengine_us/data/datasets/acs/raw_acs.py b/policyengine_us/data/datasets/acs/raw_acs.py
deleted file mode 100644
index 5104cd6d56a..00000000000
--- a/policyengine_us/data/datasets/acs/raw_acs.py
+++ /dev/null
@@ -1,198 +0,0 @@
-from io import BytesIO
-import logging
-from typing import List
-from zipfile import ZipFile
-import pandas as pd
-from policyengine_core.data import PublicDataset
-import requests
-from tqdm import tqdm
-from policyengine_us.data.storage import policyengine_us_MICRODATA_FOLDER
-
-
-logging.getLogger().setLevel(logging.INFO)
-
-PERSON_COLUMNS = [
-    "SERIALNO",  # Household ID
-    "SPORDER",  # Person number within household
-    "PWGTP",  # Person weight
-    "AGEP",  # Age
-    "CIT",  # Citizenship
-    "MAR",  # Marital status
-    "WAGP",  # Wage/salary
-    "SSP",  # Social security income
-    "SSIP",  # Supplemental security income
-    "SEX",  # Sex
-    "SEMP",  # Self-employment income
-    "SCHL",  # Educational attainment
-    "RETP",  # Retirement income
-    "PAP",  # Public assistance income
-    "OIP",  # Other income
-    "PERNP",  # Total earnings
-    "PINCP",  # Total income
-    "POVPIP",  # Income-to-poverty line percentage
-    "RAC1P",  # Race
-]
-
-HOUSEHOLD_COLUMNS = [
-    "SERIALNO",  # Household ID
-    "PUMA",  # PUMA area code
-    "ST",  # State code
-    "ADJHSG",  # Adjustment factor for housing dollar amounts
-    "ADJINC",  # Adjustment factor for income
-    "WGTP",  # Household weight
-    "NP",  # Number of persons in household
-    "BDSP",  # Number of bedrooms
-    "ELEP",  # Electricity monthly cost
-    "FULP",  # Fuel monthly cost
-    "GASP",  # Gas monthly cost
-    "RMSP",  # Number of rooms
-    "RNTP",  # Monthly rent
-    "TEN",  # Tenure
-    "VEH",  # Number of vehicles
-    "FINCP",  # Total income
-    "GRNTP",  # Gross rent
-]
-
-
-class RawACS(PublicDataset):
-    name = "raw_acs"
-    label = "Raw ACS"
-    is_openfisca_compatible = False
-    folder_path = policyengine_us_MICRODATA_FOLDER
-
-    def generate(self, year: int) -> None:
-        year = int(year)
-        if year in self.years:
-            self.remove(year)
-
-        spm_url = f"https://www2.census.gov/programs-surveys/supplemental-poverty-measure/datasets/spm/spm_{year}_pu.dta"
-        person_url = f"https://www2.census.gov/programs-surveys/acs/data/pums/{year}/1-Year/csv_pus.zip"
-        household_url = f"https://www2.census.gov/programs-surveys/acs/data/pums/{year}/1-Year/csv_hus.zip"
-
-        # The data dictionary for 2019 can be found here: https://www2.census.gov/programs-surveys/acs/tech_docs/pums/data_dict/PUMS_Data_Dictionary_2019.pdf
-
-        try:
-            with pd.HDFStore(RawACS.file(year)) as storage:
-                # Household file
-                logging.info(f"Downloading household file")
-                household = concat_zipped_csvs(
-                    household_url, "psam_hus", HOUSEHOLD_COLUMNS
-                )
-                # Remove group quarters (zero weight)
-                household = household[
-                    ~household.SERIALNO.str.contains("2019GQ")
-                ]
-                household["SERIALNO"] = household["SERIALNO"].apply(
-                    lambda x: int(x.replace("2019HU", ""))
-                )
-                storage["household"] = household
-                # Person file
-                logging.info(f"Downloading person file")
-                person = concat_zipped_csvs(
-                    person_url, "psam_pus", PERSON_COLUMNS
-                )
-                person = person[~person.SERIALNO.str.contains("2019GQ")]
-                person["SERIALNO"] = person["SERIALNO"].apply(
-                    lambda x: int(x.replace("2019HU", ""))
-                )
-                storage["person"] = person
-                # SPM unit file
-                logging.info(f"Downloading SPM unit file")
-                spm_person = pd.read_stata(spm_url).fillna(0)
-                spm_person.columns = spm_person.columns.str.upper()
-                create_spm_unit_table(storage, spm_person)
-        except Exception as e:
-            RawACS.remove(year)
-            logging.error(
-                f"Attempted to extract and save the CSV files, but encountered an error: {e}"
-            )
-            raise e
-
-
-RawACS = RawACS()
-
-
-def concat_zipped_csvs(
-    url: str, prefix: str, columns: List[str]
-) -> pd.DataFrame:
-    """Downloads the ACS microdata, which is a zip file containing two halves in CSV format.
-
-    Args:
-        url (str): The URL of the data server.
-        prefix (str): The prefix of the filenames, before a/b.csv.
-        columns (List[str]): The columns to filter (avoids hitting memory limits).
-
-    Returns:
-        pd.DataFrame: The concatenated DataFrame.
-    """
-    req = requests.get(url, stream=True)
-    with BytesIO() as f:
-        pbar = tqdm()
-        for chunk in req.iter_content(chunk_size=1024):
-            if chunk:  # filter out keep-alive new chunks
-                pbar.update(len(chunk))
-                f.write(chunk)
-        f.seek(0)
-        zf = ZipFile(f)
-        logging.info(f"Loading the first half of the dataset")
-        a = pd.read_csv(zf.open(prefix + "a.csv"), usecols=columns)
-        logging.info(f"Loading the second half of the dataset")
-        b = pd.read_csv(zf.open(prefix + "b.csv"), usecols=columns)
-    logging.info(f"Concatenating datasets")
-    res = pd.concat([a, b]).fillna(0)
-    res.columns = res.columns.str.upper()
-    return res
-
-
-def create_spm_unit_table(storage: pd.HDFStore, person: pd.DataFrame) -> None:
-    SPM_UNIT_COLUMNS = [
-        "CAPHOUSESUB",
-        "CAPWKCCXPNS",
-        "CHILDCAREXPNS",
-        "EITC",
-        "ENGVAL",
-        "EQUIVSCALE",
-        "FEDTAX",
-        "FEDTAXBC",
-        "FICA",
-        "GEOADJ",
-        "MEDXPNS",
-        "NUMADULTS",
-        "NUMKIDS",
-        "NUMPER",
-        "POOR",
-        "POVTHRESHOLD",
-        "RESOURCES",
-        "SCHLUNCH",
-        "SNAPSUB",
-        "STTAX",
-        "TENMORTSTATUS",
-        "TOTVAL",
-        "WCOHABIT",
-        "WICVAL",
-        "WKXPNS",
-        "WUI_LT15",
-        "ID",
-    ]
-    spm_table = (
-        person[["SPM_" + column for column in SPM_UNIT_COLUMNS]]
-        .groupby(person.SPM_ID)
-        .first()
-    )
-
-    original_person_table = storage["person"]
-    # Ensure that join keys are the same type.
-    JOIN_COLUMNS = ["SERIALNO", "SPORDER"]
-    original_person_table[JOIN_COLUMNS] = original_person_table[
-        JOIN_COLUMNS
-    ].astype(int)
-    person[JOIN_COLUMNS] = person[JOIN_COLUMNS].astype(int)
-    # Add SPM_ID from the SPM person table to the original person table.
-    combined_person_table = pd.merge(
-        original_person_table,
-        person[JOIN_COLUMNS + ["SPM_ID"]],
-        on=JOIN_COLUMNS,
-    )
-
-    storage["person"] = combined_person_table
-    storage["spm_unit"] = spm_table
diff --git a/policyengine_us/data/datasets/cps/README.md b/policyengine_us/data/datasets/cps/README.md
deleted file mode 100644
index 18ab1eea5f5..00000000000
--- a/policyengine_us/data/datasets/cps/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Current Population Survey March Supplement
-
-The Census Bureau administers the Current Population Survey March Supplement, also known as the Annual Social and Economic Supplement (ASEC).
-The March survey asks respondents about their economic circumstances over the prior calendar year.
-This is the core of the PolicyEngine enhanced dataset.
-
-* [Microdata page](https://www.census.gov/data/datasets/time-series/demo/cps/cps-asec.html)
-* [Latest codebook](https://www2.census.gov/programs-surveys/cps/datasets/2021/march/asec2021_ddl_pub_full.pdf)
diff --git a/policyengine_us/data/datasets/cps/__init__.py b/policyengine_us/data/datasets/cps/__init__.py
deleted file mode 100644
index a1cf44468b0..00000000000
--- a/policyengine_us/data/datasets/cps/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from .cps import (
-    CPS_2019,
-    CPS_2020,
-    CPS_2021,
-    CPS_2022,
-)
-
-from .raw_cps import RawCPS_2020, RawCPS_2021, RawCPS_2022
-
-from .enhanced_cps import (
-    EnhancedCPS_2022,
-    CalibratedPUFExtendedCPS_2022,
-)
diff --git a/policyengine_us/data/datasets/cps/cps.py b/policyengine_us/data/datasets/cps/cps.py
deleted file mode 100644
index a42169ceb0a..00000000000
--- a/policyengine_us/data/datasets/cps/cps.py
+++ /dev/null
@@ -1,565 +0,0 @@
-import logging
-from policyengine_core.data import Dataset
-from policyengine_us.data.storage import STORAGE_FOLDER
-import h5py
-from policyengine_us.data.datasets.cps.raw_cps import (
-    RawCPS_2018,
-    RawCPS_2019,
-    RawCPS_2020,
-    RawCPS_2021,
-    RawCPS_2022,
-    RawCPS,
-)
-from policyengine_us.data.datasets.cps.uprated_cps import UpratedCPS
-from policyengine_us.data.storage import STORAGE_FOLDER
-from pandas import DataFrame, Series
-import numpy as np
-import pandas as pd
-import os
-import yaml
-from typing import Type
-
-
-class CPS(Dataset):
-    name = "cps"
-    label = "CPS"
-    raw_cps: Type[RawCPS] = None
-    previous_year_raw_cps: Type[RawCPS] = None
-    data_format = Dataset.ARRAYS
-
-    def generate(self):
-        """Generates the Current Population Survey dataset for PolicyEngine US microsimulations.
-        Technical documentation and codebook here: https://www2.census.gov/programs-surveys/cps/techdocs/cpsmar21.pdf
-        """
-
-        raw_data = self.raw_cps(require=True).load()
-        cps = h5py.File(self.file_path, mode="w")
-
-        ENTITIES = ("person", "tax_unit", "family", "spm_unit", "household")
-        person, tax_unit, family, spm_unit, household = [
-            raw_data[entity] for entity in ENTITIES
-        ]
-
-        add_id_variables(cps, person, tax_unit, family, spm_unit, household)
-        add_personal_variables(cps, person)
-        add_personal_income_variables(cps, person, self.raw_cps.time_period)
-        add_previous_year_income(self, cps)
-        add_spm_variables(cps, spm_unit)
-        add_household_variables(cps, household)
-
-        raw_data.close()
-        cps.close()
-
-        cps = h5py.File(self.file_path, mode="a")
-        cps.close()
-
-
-def add_silver_plan_cost(self, cps: h5py.File, year: int):
-    """Adds the second-lowest silver plan cost for each tax unit, based on geography.
-
-    Args:
-        cps (h5py.File): The CPS dataset file.
-        year (int): The year of the data.
-    """
-    from policyengine_us import Microsimulation
-
-    sim = Microsimulation(dataset=self)
-    slspc = sim.calc("second_lowest_silver_plan_cost", year).values
-
-    cps["second_lowest_silver_plan_cost"] = slspc
-
-
-def add_id_variables(
-    cps: h5py.File,
-    person: DataFrame,
-    tax_unit: DataFrame,
-    family: DataFrame,
-    spm_unit: DataFrame,
-    household: DataFrame,
-) -> None:
-    """Add basic ID and weight variables.
-
-    Args:
-        cps (h5py.File): The CPS dataset file.
-        person (DataFrame): The person table of the ASEC.
-        tax_unit (DataFrame): The tax unit table created from the person table
-            of the ASEC.
-        family (DataFrame): The family table of the ASEC.
-        spm_unit (DataFrame): The SPM unit table created from the person table
-            of the ASEC.
-        household (DataFrame): The household table of the ASEC.
-    """
-    # Add primary and foreign keys
-    cps["person_id"] = person.PH_SEQ * 100 + person.P_SEQ
-    cps["family_id"] = family.FH_SEQ * 10 + family.FFPOS
-    cps["household_id"] = household.H_SEQ
-    cps["person_tax_unit_id"] = person.TAX_ID
-    cps["person_spm_unit_id"] = person.SPM_ID
-    cps["tax_unit_id"] = tax_unit.TAX_ID
-    cps["spm_unit_id"] = spm_unit.SPM_ID
-    cps["person_household_id"] = person.PH_SEQ
-    cps["person_family_id"] = person.PH_SEQ * 10 + person.PF_SEQ
-
-    # Add weights
-    # Weights are multiplied by 100 to avoid decimals
-    cps["person_weight"] = person.A_FNLWGT / 1e2
-    cps["family_weight"] = family.FSUP_WGT / 1e2
-
-    # Tax unit weight is the weight of the containing family.
-    family_weight = Series(
-        cps["family_weight"][...], index=cps["family_id"][...]
-    )
-    person_family_id = cps["person_family_id"][...]
-    persons_family_weight = Series(family_weight[person_family_id])
-    cps["tax_unit_weight"] = persons_family_weight.groupby(
-        cps["person_tax_unit_id"][...]
-    ).first()
-
-    cps["spm_unit_weight"] = spm_unit.SPM_WEIGHT / 1e2
-
-    cps["household_weight"] = household.HSUP_WGT / 1e2
-
-    # Marital units
-
-    marital_unit_id = person.PH_SEQ * 1e6 + np.maximum(
-        person.A_LINENO, person.A_SPOUSE
-    )
-
-    # marital_unit_id is not the household ID, zero padded and followed
-    # by the index within household (of each person, or their spouse if
-    # one exists earlier in the survey).
-
-    marital_unit_id = Series(marital_unit_id).rank(
-        method="dense"
-    )  # Simplify to a natural number sequence with repetitions [0, 1, 1, 2, 3, ...]
-
-    cps["person_marital_unit_id"] = marital_unit_id.values
-    cps["marital_unit_id"] = marital_unit_id.drop_duplicates().values
-
-
-def add_personal_variables(cps: h5py.File, person: DataFrame) -> None:
-    """Add personal demographic variables.
-
-    Args:
-        cps (h5py.File): The CPS dataset file.
-        person (DataFrame): The CPS person table.
-    """
-
-    # The CPS provides age as follows:
-    # 00-79 = 0-79 years of age
-    # 80 = 80-84 years of age
-    # 85 = 85+ years of age
-    # We assign the 80 ages randomly between 80 and 84.
-    # to avoid unrealistically bunching at 80.
-    cps["age"] = np.where(
-        person.A_AGE == 80,
-        # NB: randint is inclusive of first argument, exclusive of second.
-        np.random.randint(80, 85, len(person)),
-        person.A_AGE,
-    )
-    # A_SEX is 1 -> male, 2 -> female.
-    cps["is_female"] = person.A_SEX == 2
-    # "Is...blind or does...have serious difficulty seeing even when Wearing
-    #  glasses?" 1 -> Yes
-    cps["is_blind"] = person.PEDISEYE == 1
-    DISABILITY_FLAGS = [
-        "PEDIS" + i for i in ["DRS", "EAR", "EYE", "OUT", "PHY", "REM"]
-    ]
-    cps["is_disabled"] = (person[DISABILITY_FLAGS] == 1).any(axis=1)
-
-    def children_per_parent(col: str) -> pd.DataFrame:
-        """Calculate number of children in the household using parental
-            pointers.
-
-        Args:
-            col (str): Either PEPAR1 and PEPAR2, which correspond to A_LINENO
-            of the person's first and second parent in the household,
-            respectively.
-        """
-        return (
-            person[person[col] > 0]
-            .groupby(["PH_SEQ", col])
-            .size()
-            .reset_index()
-            .rename(columns={col: "A_LINENO", 0: "children"})
-        )
-
-    # Aggregate to parent.
-    res = (
-        pd.concat(
-            [children_per_parent("PEPAR1"), children_per_parent("PEPAR2")]
-        )
-        .groupby(["PH_SEQ", "A_LINENO"])
-        .children.sum()
-        .reset_index()
-    )
-    tmp = person[["PH_SEQ", "A_LINENO"]].merge(
-        res, on=["PH_SEQ", "A_LINENO"], how="left"
-    )
-    cps["own_children_in_household"] = tmp.children.fillna(0)
-
-    cps["has_marketplace_health_coverage"] = person.MRK == 1
-
-    cps["cps_race"] = person.PRDTRACE
-    cps["is_hispanic"] = person.PRDTHSP != 0
-
-    cps["is_widowed"] = person.A_MARITL == 4
-    cps["is_separated"] = person.A_MARITL == 6
-    # High school or college/university enrollment status.
-    cps["is_full_time_college_student"] = person.A_HSCOL == 2
-
-
-def add_personal_income_variables(
-    cps: h5py.File, person: DataFrame, year: int
-):
-    """Add income variables.
-
-    Args:
-        cps (h5py.File): The CPS dataset file.
-        person (DataFrame): The CPS person table.
-        year (int): The CPS year
-    """
-    # Get income imputation parameters.
-    yamlfilename = os.path.join(
-        os.path.abspath(os.path.dirname(__file__)),
-        "imputation_parameters.yaml",
-    )
-    with open(yamlfilename, "r", encoding="utf-8") as yamlfile:
-        p = yaml.safe_load(yamlfile)
-    assert isinstance(p, dict)
-
-    # Assign CPS variables.
-    cps["employment_income"] = person.WSAL_VAL
-
-    cps["weekly_hours_worked"] = person.HRSWK * person.WKSWORK / 52
-
-    cps["taxable_interest_income"] = person.INT_VAL * (
-        p["taxable_interest_fraction"]
-    )
-    cps["tax_exempt_interest_income"] = person.INT_VAL * (
-        1 - p["taxable_interest_fraction"]
-    )
-    cps["self_employment_income"] = person.SEMP_VAL
-    cps["farm_income"] = person.FRSE_VAL
-    cps["qualified_dividend_income"] = person.DIV_VAL * (
-        p["qualified_dividend_fraction"]
-    )
-    cps["non_qualified_dividend_income"] = person.DIV_VAL * (
-        1 - p["qualified_dividend_fraction"]
-    )
-    cps["rental_income"] = person.RNT_VAL
-    # Assign Social Security retirement benefits if at least 62.
-    MINIMUM_RETIREMENT_AGE = 62
-    cps["social_security_retirement"] = np.where(
-        person.A_AGE >= MINIMUM_RETIREMENT_AGE, person.SS_VAL, 0
-    )
-    # Otherwise assign them to Social Security disability benefits.
-    cps["social_security_disability"] = (
-        person.SS_VAL - cps["social_security_retirement"]
-    )
-    # Provide placeholders for other Social Security inputs to avoid creating
-    # NaNs as they're uprated.
-    cps["social_security_dependents"] = np.zeros_like(
-        cps["social_security_retirement"]
-    )
-    cps["social_security_survivors"] = np.zeros_like(
-        cps["social_security_retirement"]
-    )
-    cps["unemployment_compensation"] = person.UC_VAL
-    # Add pensions and annuities.
-    cps_pensions = person.PNSN_VAL + person.ANN_VAL
-    # Assume a constant fraction of pension income is taxable.
-    cps["taxable_private_pension_income"] = (
-        cps_pensions * p["taxable_pension_fraction"]
-    )
-    cps["tax_exempt_private_pension_income"] = cps_pensions * (
-        1 - p["taxable_pension_fraction"]
-    )
-    # Retirement account distributions.
-    RETIREMENT_CODES = {
-        1: "401k",
-        2: "403b",
-        3: "roth_ira",
-        4: "regular_ira",
-        5: "keogh",
-        6: "sep",  # Simplified Employee Pension plan
-        7: "other_type_retirement_account",
-    }
-    for code, description in RETIREMENT_CODES.items():
-        tmp = 0
-        # The ASEC splits distributions across four variable pairs.
-        for i in ["1", "2", "1_YNG", "2_YNG"]:
-            tmp += (person["DST_SC" + i] == code) * person["DST_VAL" + i]
-        cps[f"{description}_distributions"] = tmp
-    # Allocate retirement distributions by taxability.
-    for source_with_taxable_fraction in ["401k", "403b", "sep"]:
-        cps[f"taxable_{source_with_taxable_fraction}_distributions"] = (
-            cps[f"{source_with_taxable_fraction}_distributions"][...]
-            * p[
-                f"taxable_{source_with_taxable_fraction}_distribution_fraction"
-            ]
-        )
-        cps[f"tax_exempt_{source_with_taxable_fraction}_distributions"] = cps[
-            f"{source_with_taxable_fraction}_distributions"
-        ][...] * (
-            1
-            - p[
-                f"taxable_{source_with_taxable_fraction}_distribution_fraction"
-            ]
-        )
-        del cps[f"{source_with_taxable_fraction}_distributions"]
-
-    # Assume all regular IRA distributions are taxable,
-    # and all Roth IRA distributions are not.
-    cps["taxable_ira_distributions"] = cps["regular_ira_distributions"]
-    cps["tax_exempt_ira_distributions"] = cps["roth_ira_distributions"]
-    # Other income (OI_VAL) is a catch-all for all other income sources.
-    # The code for alimony income is 20.
-    cps["alimony_income"] = (person.OI_OFF == 20) * person.OI_VAL
-    # The code for strike benefits is 12.
-    cps["strike_benefits"] = (person.OI_OFF == 12) * person.OI_VAL
-    cps["child_support_received"] = person.CSP_VAL
-    # Assume all public assistance / welfare dollars (PAW_VAL) are TANF.
-    # They could also include General Assistance.
-    cps["tanf_reported"] = person.PAW_VAL
-    cps["ssi_reported"] = person.SSI_VAL
-    # Assume all retirement contributions are traditional 401(k) for now.
-    # Procedure for allocating retirement contributions:
-    # 1) If they report any self-employment income, allocate entirely to
-    #    self-employed pension contributions.
-    # 2) If they report any wage and salary income, allocate in this order:
-    #    a) Traditional 401(k) contributions up to to limit
-    #    b) Roth 401(k) contributions up to the limit
-    #    c) IRA contributions up to the limit, split according to administrative fractions
-    #    d) Other retirement contributions
-    # Disregard reported pension contributions from people who report neither wage and salary
-    # nor self-employment income.
-    # Assume no 403(b) or 457 contributions for now.
-    LIMIT_401K_2022 = 20_500
-    LIMIT_401K_CATCH_UP_2022 = 6_500
-    LIMIT_IRA_2022 = 6_000
-    LIMIT_IRA_CATCH_UP_2022 = 1_000
-    CATCH_UP_AGE_2022 = 50
-    retirement_contributions = person.RETCB_VAL
-    cps["self_employed_pension_contributions"] = np.where(
-        person.SEMP_VAL > 0, retirement_contributions, 0
-    )
-    remaining_retirement_contributions = np.maximum(
-        retirement_contributions - cps["self_employed_pension_contributions"],
-        0,
-    )
-    # Compute the 401(k) limit for the person's age.
-    catch_up_eligible = person.A_AGE >= CATCH_UP_AGE_2022
-    limit_401k = LIMIT_401K_2022 + catch_up_eligible * LIMIT_401K_CATCH_UP_2022
-    limit_ira = LIMIT_IRA_2022 + catch_up_eligible * LIMIT_IRA_CATCH_UP_2022
-    cps["traditional_401k_contributions"] = np.where(
-        person.WSAL_VAL > 0,
-        np.minimum(remaining_retirement_contributions, limit_401k),
-        0,
-    )
-    remaining_retirement_contributions = np.maximum(
-        remaining_retirement_contributions
-        - cps["traditional_401k_contributions"],
-        0,
-    )
-    cps["roth_401k_contributions"] = np.where(
-        person.WSAL_VAL > 0,
-        np.minimum(remaining_retirement_contributions, limit_401k),
-        0,
-    )
-    remaining_retirement_contributions = np.maximum(
-        remaining_retirement_contributions - cps["roth_401k_contributions"],
-        0,
-    )
-    cps["traditional_ira_contributions"] = np.where(
-        person.WSAL_VAL > 0,
-        np.minimum(remaining_retirement_contributions, limit_ira),
-        0,
-    )
-    remaining_retirement_contributions = np.maximum(
-        remaining_retirement_contributions
-        - cps["traditional_ira_contributions"],
-        0,
-    )
-    roth_ira_limit = limit_ira - cps["traditional_ira_contributions"]
-    cps["roth_ira_contributions"] = np.where(
-        person.WSAL_VAL > 0,
-        np.minimum(remaining_retirement_contributions, roth_ira_limit),
-        0,
-    )
-    # Allocate capital gains into long-term and short-term based on aggregate split.
-    cps["long_term_capital_gains"] = person.CAP_VAL * (
-        p["long_term_capgain_fraction"]
-    )
-    cps["short_term_capital_gains"] = person.CAP_VAL * (
-        1 - p["long_term_capgain_fraction"]
-    )
-    cps["receives_wic"] = person.WICYN == 1
-    cps["veterans_benefits"] = person.VET_VAL
-    cps["workers_compensation"] = person.WC_VAL
-    # Disability income has multiple sources and values split across two pairs
-    # of variables. Include everything except for worker's compensation
-    # (code 1), which is defined as WC_VAL.
-    WORKERS_COMP_DISABILITY_CODE = 1
-    disability_benefits_1 = person.DIS_VAL1 * (
-        person.DIS_SC1 != WORKERS_COMP_DISABILITY_CODE
-    )
-    disability_benefits_2 = person.DIS_VAL2 * (
-        person.DIS_SC2 != WORKERS_COMP_DISABILITY_CODE
-    )
-    cps["disability_benefits"] = disability_benefits_1 + disability_benefits_2
-    # Expenses.
-    # "What is the annual amount of child support paid?"
-    cps["child_support_expense"] = person.CHSP_VAL
-    cps["health_insurance_premiums_without_medicare_part_b"] = person.PHIP_VAL
-    cps["over_the_counter_health_expenses"] = person.POTC_VAL
-    cps["other_medical_expenses"] = person.PMED_VAL
-    cps["medicare_part_b_premiums"] = person.PEMCPREM
-
-
-def add_spm_variables(cps: h5py.File, spm_unit: DataFrame) -> None:
-    SPM_RENAMES = dict(
-        spm_unit_total_income_reported="SPM_TOTVAL",
-        snap_reported="SPM_SNAPSUB",
-        spm_unit_capped_housing_subsidy_reported="SPM_CAPHOUSESUB",
-        free_school_meals_reported="SPM_SCHLUNCH",
-        spm_unit_energy_subsidy_reported="SPM_ENGVAL",
-        spm_unit_wic_reported="SPM_WICVAL",
-        spm_unit_broadband_subsidy_reported="SPM_BBSUBVAL",
-        spm_unit_payroll_tax_reported="SPM_FICA",
-        spm_unit_federal_tax_reported="SPM_FEDTAX",
-        # State tax includes refundable credits.
-        spm_unit_state_tax_reported="SPM_STTAX",
-        spm_unit_capped_work_childcare_expenses="SPM_CAPWKCCXPNS",
-        spm_unit_spm_threshold="SPM_POVTHRESHOLD",
-        spm_unit_net_income_reported="SPM_RESOURCES",
-        spm_unit_pre_subsidy_childcare_expenses="SPM_CHILDCAREXPNS",
-    )
-
-    for openfisca_variable, asec_variable in SPM_RENAMES.items():
-        if asec_variable in spm_unit.columns:
-            cps[openfisca_variable] = spm_unit[asec_variable]
-
-    cps["reduced_price_school_meals_reported"] = (
-        cps["free_school_meals_reported"][...] * 0
-    )
-
-
-def add_household_variables(cps: h5py.File, household: DataFrame) -> None:
-    cps["state_fips"] = household.GESTFIPS
-    cps["county_fips"] = household.GTCO
-    state_county_fips = cps["state_fips"][...] * 1e3 + cps["county_fips"][...]
-    # Assign is_nyc here instead of as a variable formula so that it shows up
-    # as toggleable in the webapp.
-    # List county FIPS codes for each NYC county/borough.
-    NYC_COUNTY_FIPS = [
-        5,  # Bronx
-        47,  # Kings (Brooklyn)
-        61,  # New York (Manhattan)
-        81,  # Queens
-        85,  # Richmond (Staten Island)
-    ]
-    # Compute NYC by concatenating NY state FIPS with county FIPS.
-    # For example, 36061 is Manhattan.
-    NYS_FIPS = 36
-    nyc_full_county_fips = [
-        NYS_FIPS * 1e3 + county_fips for county_fips in NYC_COUNTY_FIPS
-    ]
-    cps["in_nyc"] = np.isin(state_county_fips, nyc_full_county_fips)
-
-
-def add_previous_year_income(self, cps: h5py.File) -> None:
-    if self.previous_year_raw_cps is None:
-        print(
-            "No previous year data available for this dataset, skipping previous year income imputation."
-        )
-        return
-
-    from survey_enhance.impute import Imputation
-
-    cps_current_year_data = self.raw_cps(require=True).load()
-    cps_previous_year_data = self.previous_year_raw_cps(require=True).load()
-    cps_previous_year = cps_previous_year_data.person.set_index(
-        cps_previous_year_data.person.PERIDNUM
-    )
-    cps_current_year = cps_current_year_data.person.set_index(
-        cps_current_year_data.person.PERIDNUM
-    )
-
-    previous_year_data = cps_previous_year[
-        ["WSAL_VAL", "SEMP_VAL", "I_ERNVAL", "I_SEVAL"]
-    ].rename(
-        {
-            "WSAL_VAL": "employment_income_last_year",
-            "SEMP_VAL": "self_employment_income_last_year",
-        },
-        axis=1,
-    )
-
-    previous_year_data = previous_year_data[
-        (previous_year_data.I_ERNVAL == 0) & (previous_year_data.I_SEVAL == 0)
-    ]
-
-    previous_year_data.drop(["I_ERNVAL", "I_SEVAL"], axis=1, inplace=True)
-
-    joined_data = cps_current_year.join(previous_year_data)[
-        [
-            "employment_income_last_year",
-            "self_employment_income_last_year",
-            "I_ERNVAL",
-            "I_SEVAL",
-        ]
-    ]
-    joined_data["previous_year_income_available"] = (
-        ~joined_data.employment_income_last_year.isna()
-        & ~joined_data.self_employment_income_last_year.isna()
-        & (joined_data.I_ERNVAL == 0)
-        & (joined_data.I_SEVAL == 0)
-    )
-    joined_data = joined_data.fillna(-1).drop(["I_ERNVAL", "I_SEVAL"], axis=1)
-
-    # CPS already ordered by PERIDNUM, so the join wouldn't change the order.
-    cps["employment_income_last_year"] = joined_data[
-        "employment_income_last_year"
-    ].values
-    cps["self_employment_income_last_year"] = joined_data[
-        "self_employment_income_last_year"
-    ].values
-    cps["previous_year_income_available"] = joined_data[
-        "previous_year_income_available"
-    ].values
-
-
-class CPS_2019(CPS):
-    name = "cps_2019"
-    label = "CPS 2019"
-    raw_cps = RawCPS_2019
-    previous_year_raw_cps = RawCPS_2018
-    file_path = STORAGE_FOLDER / "cps_2019.h5"
-    time_period = 2019
-
-
-class CPS_2020(CPS):
-    name = "cps_2020"
-    label = "CPS 2020"
-    raw_cps = RawCPS_2020
-    file_path = STORAGE_FOLDER / "cps_2020.h5"
-    time_period = 2020
-
-
-class CPS_2021(CPS):
-    name = "cps_2021"
-    label = "CPS 2021"
-    raw_cps = RawCPS_2021
-    file_path = STORAGE_FOLDER / "cps_2021.h5"
-    time_period = 2021
-
-
-class CPS_2022(CPS):
-    name = "cps_2022"
-    label = "CPS 2022"
-    raw_cps = RawCPS_2022
-    previous_year_raw_cps = RawCPS_2021
-    file_path = STORAGE_FOLDER / "cps_2022.h5"
-    time_period = 2022
-    url = "release://policyengine/policyengine-us/cps-2022/cps_2022.h5"
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/__init__.py b/policyengine_us/data/datasets/cps/enhanced_cps/__init__.py
deleted file mode 100644
index e57e858423a..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .puf_extended_cps import PUFExtendedCPS_2022
-from .enhanced_cps import EnhancedCPS_2022
-from .calibrated_cps import CalibratedPUFExtendedCPS_2022
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/calibrate.py b/policyengine_us/data/datasets/cps/enhanced_cps/calibrate.py
deleted file mode 100644
index c7f4e54f078..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/calibrate.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import torch
-import pandas as pd
-import numpy as np
-from tqdm import tqdm
-import numpy as np
-import pandas as pd
-from pathlib import Path
-from policyengine_us.data.datasets.cps.enhanced_cps.loss import (
-    generate_model_variables,
-)
-
-
-def aggregate(
-    adjusted_weights: torch.Tensor, values: pd.DataFrame
-) -> torch.Tensor:
-    broadcasted_weights = adjusted_weights.reshape(-1, 1)
-    weighted_values = torch.matmul(
-        broadcasted_weights.T, torch.tensor(values.values, dtype=torch.float32)
-    )
-    return weighted_values
-
-
-def calibrate(
-    dataset: str,
-    time_period: str = "2022",
-    training_log_path: str = "training_log.csv.gz",
-    learning_rate: float = 2e1,
-    epochs: int = 10_000,
-) -> np.ndarray:
-    (
-        household_weights,
-        weight_adjustment,
-        values_df,
-        targets,
-        targets_array,
-        equivalisation_factors_array,
-    ) = generate_model_variables(dataset, time_period)
-    household_weights = torch.tensor(household_weights, dtype=torch.float32)
-    weight_adjustment = torch.tensor(
-        weight_adjustment, dtype=torch.float32, requires_grad=True
-    )
-    targets_array = torch.tensor(targets_array, dtype=torch.float32)
-    equivalisation_factors_array = torch.tensor(
-        equivalisation_factors_array, dtype=torch.float32
-    )
-    training_log_path = Path(training_log_path)
-    if training_log_path.exists():
-        training_log_df = pd.read_csv(training_log_path, compression="gzip")
-    else:
-        training_log_df = pd.DataFrame()
-
-    progress_bar = tqdm(range(epochs), desc="Calibrating weights")
-    optimizer = torch.optim.Adam([weight_adjustment], lr=learning_rate)
-    for i in progress_bar:
-        adjusted_weights = torch.relu(household_weights + weight_adjustment)
-        result = (
-            aggregate(adjusted_weights, values_df)
-            / equivalisation_factors_array
-        )
-        target = targets_array / equivalisation_factors_array
-        loss = torch.mean(
-            ((result / target - 1) ** 2) * np.log2(np.abs(target))
-        )
-        optimizer.zero_grad()
-        loss.backward()
-        optimizer.step()
-        if i % 10 == 0:
-            current_loss = loss.item()
-            progress_bar.set_description_str(
-                f"Calibrating weights | Loss = {current_loss:,.3f}"
-            )
-        if i % 2_000 == 0:
-            current_aggregates = (
-                (result * equivalisation_factors_array).detach().numpy()[0]
-            )
-            training_log_df = pd.concat(
-                [
-                    training_log_df,
-                    pd.DataFrame(
-                        {
-                            "name": list(targets.keys()) + ["total"],
-                            "epoch": [i] * len(targets) + [i],
-                            "value": list(current_aggregates) + [current_loss],
-                            "target": list(targets.values()) + [0],
-                            "time_period": time_period,
-                        }
-                    ),
-                ]
-            )
-            training_log_df.to_csv(training_log_path, compression="gzip")
-
-    return adjusted_weights.detach().numpy()
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/calibrated_cps.py b/policyengine_us/data/datasets/cps/enhanced_cps/calibrated_cps.py
deleted file mode 100644
index e7787cf6781..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/calibrated_cps.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from policyengine_us.data.storage import STORAGE_FOLDER
-from policyengine_core.data import Dataset
-from .puf_extended_cps import PUFExtendedCPS_2022
-
-
-class CalibratedDataset(Dataset):
-    data_format = Dataset.TIME_PERIOD_ARRAYS
-    time_period = None
-    num_years: int = None
-    input_dataset = None
-
-    def generate(self):
-        from .calibrate import calibrate
-
-        new_data = {}
-        cps = self.input_dataset(require=True)
-        cps_data = cps.load()
-        for year in range(self.time_period, self.time_period + self.num_years):
-            year = str(year)
-            adjusted_weights = calibrate(
-                cps,
-                time_period=year,
-                training_log_path=STORAGE_FOLDER / "calibration_log.csv.gz",
-            )
-            for variable in cps.variables:
-                if variable not in new_data:
-                    new_data[variable] = {}
-                if variable == "household_weight":
-                    new_data[variable][year] = adjusted_weights
-                elif "_weight" not in variable and (
-                    (year == str(self.time_period)) or ("_id" in variable)
-                ):
-                    new_data[variable][year] = cps_data[variable][...]
-
-        self.save_dataset(new_data)
-
-
-class CalibratedPUFExtendedCPS_2022(CalibratedDataset):
-    name = "calibrated_puf_extended_cps_2022"
-    label = "Calibrated PUF-extended CPS (2022-34)"
-    input_dataset = PUFExtendedCPS_2022
-    time_period = 2022
-    num_years = 13
-    file_path = STORAGE_FOLDER / "calibrated_puf_extended_cps_2022.h5"
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/dashboard/app.py b/policyengine_us/data/datasets/cps/enhanced_cps/dashboard/app.py
deleted file mode 100644
index 1b7520f9424..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/dashboard/app.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import pandas as pd
-import plotly.express as px
-from policyengine_core.charts import *
-import streamlit as st
-from policyengine_us.data.storage import STORAGE_FOLDER
-
-st.set_page_config(layout="wide")
-
-st.title("PolicyEngine US microdata dashboard")
-
-df = pd.read_csv(STORAGE_FOLDER / "dataset_losses.csv.gz")
-
-df["abs_rel_error"] = (df.value / df.target - 1).abs()
-
-left, right = st.columns(2)
-with left:
-    metric = st.selectbox("Metric", df.name.unique())
-with right:
-    time_period = st.selectbox("Time period", df.time_period.unique())
-
-df.dataset = df.dataset.replace(
-    {
-        "cps_2022": "CPS (2022)",
-        "enhanced_cps_2022": "Enhanced CPS",
-        "puf_2023": "PUF (2015)",
-    }
-)
-
-
-def capitalise(string):
-    return string[0].upper() + string[1:]
-
-
-fig = px.bar(
-    df[(df.name == metric) & (df.time_period == time_period)],
-    x="dataset",
-    y="value",
-    color="dataset",
-    title=f"{capitalise(metric)} in {time_period}",
-    color_discrete_map={
-        "CPS (2022)": DARK_GRAY,
-        "Enhanced CPS": BLUE,
-        "PUF (2015)": GRAY,
-    },
-).update_layout(
-    yaxis_title="Absolute relative error",
-    xaxis_title="Dataset",
-    showlegend=False,
-)
-
-# Add dotted line for truth
-
-true_value = df[
-    (df.name == metric) & (df.time_period == time_period)
-].target.mean()
-fig.add_shape(
-    type="line",
-    x0=-0.5,
-    y0=true_value,
-    x1=2.5,
-    y1=true_value,
-    line=dict(
-        color=MEDIUM_DARK_GRAY,
-        width=3,
-        dash="dash",
-    ),
-)
-
-st.plotly_chart(format_fig(fig), use_container_width=True)
-
-st.dataframe(df, use_container_width=True)
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/enhanced_cps.py b/policyengine_us/data/datasets/cps/enhanced_cps/enhanced_cps.py
deleted file mode 100644
index 1be1407e1f6..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/enhanced_cps.py
+++ /dev/null
@@ -1,122 +0,0 @@
-from policyengine_us.data.storage import STORAGE_FOLDER
-from policyengine_core.data import Dataset
-from policyengine_us.data.datasets.cps.enhanced_cps.calibrated_cps import (
-    CalibratedPUFExtendedCPS_2022,
-)
-
-
-class EnhancedCPS(Dataset):
-    data_format = Dataset.TIME_PERIOD_ARRAYS
-    time_period = None
-    num_years: int = None
-    input_dataset = None
-
-    def generate(self):
-        self.remove()
-        new_data = {}
-        cps = self.input_dataset()
-        from policyengine_us.data.datasets.cps.cps import (
-            CPS_2019,
-        )  # Always use CPS 2019 for previous year imputations
-
-        cps_data = cps.load()
-        for variable in cps.variables:
-            new_data[variable] = {}
-            for time_period in cps_data[variable]:
-                new_data[variable][time_period] = cps_data[variable][
-                    time_period
-                ][...]
-
-        # Add imputation of prior year income
-        from policyengine_us import Microsimulation
-
-        sim = Microsimulation(dataset=CPS_2019)
-
-        VARIABLES = [
-            "previous_year_income_available",
-            "employment_income",
-            "self_employment_income",
-            "age",
-            "is_male",
-            "spm_unit_state_fips",
-            "dividend_income",
-            "interest_income",
-            "social_security",
-            "capital_gains",
-            "is_disabled",
-            "is_blind",
-            "is_married",
-            "tax_unit_children",
-            "pension_income",
-        ]
-
-        OUTPUTS = [
-            "employment_income_last_year",
-            "self_employment_income_last_year",
-        ]
-
-        df = sim.calculate_dataframe(
-            VARIABLES + OUTPUTS, 2019, map_to="person"
-        )
-        df_train = df[df.previous_year_income_available]
-
-        from survey_enhance import Imputation
-
-        income_last_year = Imputation()
-        X = df_train[VARIABLES[1:]]
-        y = df_train[OUTPUTS]
-
-        income_last_year.train(X, y)
-
-        sim = Microsimulation(dataset=cps)
-
-        df = sim.calculate_dataframe(
-            VARIABLES + OUTPUTS, self.time_period, map_to="person"
-        )
-
-        parameters = sim.tax_benefit_system.parameters
-
-        new_data["employment_income_last_year"] = {}
-        new_data["self_employment_income_last_year"] = {}
-
-        for time_period in range(
-            self.time_period, self.time_period + self.num_years
-        ):
-            projections = parameters(
-                f"{time_period - 1}-01-01"
-            ).calibration.gov.irs.soi
-            quantiles = income_last_year.solve_for_mean_quantiles(
-                [
-                    projections.employment_income,
-                    projections.self_employment_income,
-                ],
-                df[VARIABLES[1:]],
-                sim.calculate(
-                    "household_weight", time_period, map_to="person"
-                ).values,
-                max_iterations=6,
-            )
-
-            y_pred = income_last_year.predict(
-                df.drop(columns=OUTPUTS), mean_quantile=quantiles
-            )
-
-            df[OUTPUTS] = y_pred[OUTPUTS]
-            new_data["employment_income_last_year"][time_period] = df[
-                "employment_income_last_year"
-            ].values
-            new_data["self_employment_income_last_year"][time_period] = df[
-                "self_employment_income_last_year"
-            ].values
-
-        self.save_dataset(new_data)
-
-
-class EnhancedCPS_2022(EnhancedCPS):
-    name = "enhanced_cps_2022"
-    label = "Enhanced CPS (2022-25)"
-    input_dataset = CalibratedPUFExtendedCPS_2022
-    time_period = 2022
-    num_years = 4
-    file_path = STORAGE_FOLDER / "enhanced_cps_2022.h5"
-    url = "release://policyengine/policyengine-us/enhanced-cps-2022/enhanced_cps_2022.h5"
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/loss.py b/policyengine_us/data/datasets/cps/enhanced_cps/loss.py
deleted file mode 100644
index 9d7a36edd74..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/loss.py
+++ /dev/null
@@ -1,405 +0,0 @@
-import pandas as pd
-import numpy as np
-from policyengine_us import Microsimulation
-import numpy as np
-from policyengine_us import Microsimulation
-import pandas as pd
-from .process_puf import FINANCIAL_SUBSET as FINANCIAL_VARIABLES
-from typing import Tuple
-
-
-def generate_model_variables(
-    dataset: str, time_period: str = "2022", no_weight_adjustment: bool = False
-) -> Tuple:
-    """Generates variables needed for the calibration process.
-
-    Args:
-        dataset (str): The name of the dataset to use.
-        time_period (str, optional): The time period to use. Defaults to "2022".
-        no_weight_adjustment (bool, optional): Whether to skip the weight adjustment. Defaults to False.
-
-    Returns:
-        household_weights (np.ndarray): The household weights.
-        weight_adjustment (np.ndarray): The weight adjustment.
-        values_df (pd.DataFrame): A 2D array of values to transform household weights into statistical predictions.
-        targets (dict): A dictionary of names and target values for the statistical predictions.
-        targets_array (np.ndarray): A 1D array of target values for the statistical predictions.
-        equivalisation_factors_array (np.ndarray): A 1D array of equivalisation factors for the statistical predictions to normalise the targets.
-    """
-    simulation = Microsimulation(dataset=dataset)
-    simulation.default_calculation_period = time_period
-    parameters = simulation.tax_benefit_system.parameters.calibration(
-        f"{time_period}-01-01"
-    )
-
-    household_weights = simulation.calculate("household_weight").values
-    weight_adjustment = np.random.random(household_weights.shape) * 10
-
-    if no_weight_adjustment:
-        weight_adjustment = np.zeros_like(household_weights)
-
-    values_df = pd.DataFrame()
-    targets = {}
-    equivalisation = {}
-
-    # We need to normalise the targets. Common regression targets are often 1e1 to 1e3 (this informs the scale of the learning rate).
-    COUNT_HOUSEHOLDS = household_weights.sum().item()
-    FINANCIAL_EQUIVALISATION = COUNT_HOUSEHOLDS
-    POPULATION_EQUIVALISATION = COUNT_HOUSEHOLDS / 1e5
-
-    is_filer = simulation.calculate("tax_unit_is_filer").values
-    household_has_filers = (
-        simulation.map_result(is_filer, "tax_unit", "household") > 0
-    )
-
-    for variable_name in FINANCIAL_VARIABLES:
-        if variable_name not in parameters.gov.irs.soi:
-            continue
-        label = (
-            simulation.tax_benefit_system.variables[variable_name].label
-            + " (IRS SOI)"
-        )
-        values_df[label] = (
-            simulation.calculate(variable_name, map_to="household").values
-            * household_has_filers
-        )
-        targets[label] = parameters.gov.irs.soi[variable_name]
-        equivalisation[label] = FINANCIAL_EQUIVALISATION
-
-    # Program spending from CBO baseline projections
-
-    PROGRAMS = [
-        "income_tax",
-        "snap",
-        "social_security",
-        "ssi",
-        "unemployment_compensation",
-    ]
-
-    for variable_name in PROGRAMS:
-        label = (
-            simulation.tax_benefit_system.variables[variable_name].label
-            + " (CBO)"
-        )
-        values_df[label] = simulation.calculate(
-            variable_name, map_to="household"
-        ).values
-        targets[label] = parameters.gov.cbo[variable_name]
-        equivalisation[label] = FINANCIAL_EQUIVALISATION
-
-    snap_participation = parameters.gov.usda.snap.participation
-    ssi_participation = parameters.gov.ssa.ssi.participation
-    ss_participation = parameters.gov.ssa.social_security.participation
-
-    for program, participation in zip(
-        ["snap", "ssi", "social_security"],
-        [snap_participation, ssi_participation, ss_participation],
-    ):
-        label = simulation.tax_benefit_system.variables[program].label
-        entity_level = simulation.tax_benefit_system.variables[
-            program
-        ].entity.key
-        entity_level_value = simulation.calculate(program)
-        values_df[f"{label} participants"] = simulation.map_result(
-            entity_level_value > 0, entity_level, "household"
-        )
-        targets[f"{label} participants"] = participation
-        equivalisation[f"{label} participants"] = POPULATION_EQUIVALISATION
-
-    demographics_sim = Microsimulation(dataset="cps_2022")
-
-    # Total population
-    values_df["U.S. population"] = simulation.calculate(
-        "people", map_to="household"
-    ).values
-    targets["U.S. population"] = parameters.gov.census.populations.total
-    equivalisation["U.S. population"] = POPULATION_EQUIVALISATION
-
-    # Population by 10-year age group and sex
-    age_cps = demographics_sim.calculate("age").values
-    is_male_cps = demographics_sim.calculate("is_male")
-    age = simulation.calculate("age").values
-    is_male = simulation.calculate("is_male")
-    population_in_21 = demographics_sim.tax_benefit_system.parameters.calibration.gov.census.populations.total(
-        "2021-01-01"
-    )
-    population_growth_since_21 = (
-        parameters.gov.census.populations.total / population_in_21
-    )
-    cps_household_weights = demographics_sim.calculate(
-        "household_weight"
-    ).values
-    for lower_age_group in range(0, 90, 5):
-        for possible_is_male in (True, False):
-            in_age_range = (age >= lower_age_group) & (
-                age < lower_age_group + 5
-            )
-            in_sex_category = is_male == possible_is_male
-            count_people_in_range = simulation.map_result(
-                in_age_range * in_sex_category, "person", "household"
-            )
-            in_age_range_cps = (age_cps >= lower_age_group) & (
-                age_cps < lower_age_group + 5
-            )
-            in_sex_category_cps = is_male_cps == possible_is_male
-            count_people_in_range_cps = demographics_sim.map_result(
-                in_age_range_cps * in_sex_category_cps, "person", "household"
-            )
-            count_people_in_range = simulation.map_result(
-                in_age_range * in_sex_category, "person", "household"
-            )
-            sex_category = "male" if possible_is_male else "female"
-            name = f"{lower_age_group} to {lower_age_group + 5} and {sex_category} population"
-            values_df[name] = count_people_in_range
-            targets[name] = (
-                cps_household_weights * count_people_in_range_cps
-            ).sum() * population_growth_since_21
-            equivalisation[name] = POPULATION_EQUIVALISATION
-
-    values_df["Infants"] = simulation.map_result(
-        age < 1, "person", "household"
-    )
-
-    targets["Infants"] = 3_667_758
-
-    equivalisation["Infants"] = POPULATION_EQUIVALISATION
-
-    # Household population by number of adults and children
-
-    household_count_adults_cps = demographics_sim.map_result(
-        age_cps >= 18, "person", "household"
-    )
-    household_count_children_cps = demographics_sim.map_result(
-        age_cps < 18, "person", "household"
-    )
-
-    household_count_adults = simulation.map_result(
-        age >= 18, "person", "household"
-    )
-    household_count_children = simulation.map_result(
-        age < 18, "person", "household"
-    )
-
-    for count_adults in range(1, 3):
-        for count_children in range(0, 4):
-            in_criteria = (
-                (household_count_adults == count_adults)
-                * (household_count_children == count_children)
-                * 1.0
-            )
-            in_criteria_cps = (
-                (household_count_adults_cps == count_adults)
-                * (household_count_children_cps == count_children)
-                * 1.0
-            )
-            name = f"{count_adults}-adult, {count_children}-child household population"
-            values_df[name] = in_criteria
-            targets[name] = (
-                cps_household_weights * in_criteria_cps
-            ).sum() * population_growth_since_21
-            equivalisation[name] = POPULATION_EQUIVALISATION
-
-    # Number of tax returns by AGI category
-
-    # is_filer
-
-    agi = simulation.calculate("adjusted_gross_income").values
-
-    BOUNDS = [
-        -np.inf,
-        1,
-        5e3,
-        10e3,
-        15e3,
-        20e3,
-        25e3,
-        30e3,
-        40e3,
-        50e3,
-        75e3,
-        100e3,
-        200e3,
-        500e3,
-        1e6,
-        1.5e6,
-        2e6,
-        5e6,
-        10e6,
-        np.inf,
-    ]
-    COUNTS = [
-        4_098_522,
-        8_487_025,
-        8_944_908,
-        10_056_377,
-        9_786_580,
-        8_863_570,
-        8_787_576,
-        16_123_068,
-        12_782_334,
-        22_653_934,
-        14_657_726,
-        24_044_481,
-        9_045_567,
-        1_617_144,
-        376_859,
-        156_020,
-        233_838,
-        63_406,
-        45_404,
-    ]
-    VALUES = [
-        -171_836_364,
-        19_987_243,
-        67_651_359,
-        125_912_056,
-        170_836_129,
-        199_508_960,
-        241_347_179,
-        561_386_434,
-        573_155_378,
-        1_392_395_599,
-        1_271_699_391,
-        3_297_058_075,
-        2_619_188_471,
-        1_092_599_034,
-        454_552_875,
-        268_278_123,
-        698_923_219,
-        435_242_550,
-        1_477_728_359,
-        13_879_929_368,
-        -12_835_378,
-        451_204,
-        1_358_544,
-        14_362_205,
-        57_643_020,
-        101_727_915,
-        141_934_070,
-        382_385_416,
-        457_336_377,
-        1_238_178_360,
-        1_206_614_503,
-        3_252_746_502,
-        2_613_795_014,
-        1_091_571_914,
-        3_332_659_702,
-    ]
-
-    for i in range(len(BOUNDS) - 1):
-        lower_bound = BOUNDS[i]
-        upper_bound = BOUNDS[i + 1]
-        in_range = (agi >= lower_bound) * (agi < upper_bound) * is_filer
-        household_filers = simulation.map_result(
-            in_range, "tax_unit", "household"
-        )
-        if lower_bound == -np.inf:
-            lower_bound_str = "negative infinity"
-        else:
-            lower_bound_str = f"{lower_bound:,.0f}"
-        name = f"tax returns with AGI between ${lower_bound_str} and ${upper_bound:,.0f}"
-        values_df[name] = household_filers
-        targets[name] = COUNTS[i] * population_growth_since_21
-        equivalisation[name] = POPULATION_EQUIVALISATION
-
-        agi_in_range = agi * in_range
-        household_agi = simulation.map_result(
-            agi_in_range, "tax_unit", "household"
-        )
-        name = f"total AGI from tax returns with AGI between ${lower_bound_str} and ${upper_bound:,.0f}"
-        values_df[name] = household_agi
-        targets[name] = VALUES[i] * population_growth_since_21 * 1e3
-        equivalisation[name] = FINANCIAL_EQUIVALISATION
-
-    # Tax return counts by filing status
-
-    filing_status = (
-        simulation.calculate("filing_status")
-        .replace("SURVIVING_SPOUSE", "JOINT")
-        .values
-    )
-    for filing_status_value in [
-        "SINGLE",
-        "JOINT",
-        "HEAD_OF_HOUSEHOLD",
-        "SEPARATE",
-    ]:
-        parameter = parameters.gov.irs.soi.returns_by_filing_status[
-            filing_status_value
-        ]
-        in_filing_status = filing_status == filing_status_value
-        household_filers = simulation.map_result(
-            in_filing_status * is_filer, "tax_unit", "household"
-        )
-        labels = {
-            "SINGLE": "single",
-            "JOINT": "joint and widow(er)",
-            "HEAD_OF_HOUSEHOLD": "head of household",
-            "SEPARATE": "separate",
-        }
-        label = labels.get(filing_status_value) + " returns (IRS SOI)"
-        values_df[label] = household_filers
-        targets[label] = parameter
-        equivalisation[label] = POPULATION_EQUIVALISATION
-
-    targets_array = np.array(list(targets.values()))
-    equivalisation_factors_array = np.array(list(equivalisation.values()))
-
-    return (
-        household_weights,
-        weight_adjustment,
-        values_df,
-        targets,
-        targets_array,
-        equivalisation_factors_array,
-    )
-
-
-def aggregate_np(
-    adjusted_weights: np.ndarray, values: pd.DataFrame
-) -> np.ndarray:
-    broadcasted_weights = adjusted_weights.reshape(-1, 1)
-    weighted_values = np.matmul(broadcasted_weights.T, values.values)
-    return weighted_values
-
-
-def get_snapshot(
-    dataset: str,
-    time_period: str = "2022",
-) -> pd.DataFrame:
-    """Returns a snapshot of the training metrics without training the model.
-
-    Args:
-        dataset (str): The name of the dataset to use.
-        time_period (str, optional): The time period to use. Defaults to "2022".
-
-    Returns:
-        pd.DataFrame: A DataFrame containing the training metrics.
-    """
-    (
-        household_weights,
-        weight_adjustment,
-        values_df,
-        targets,
-        targets_array,
-        equivalisation_factors_array,
-    ) = generate_model_variables(
-        dataset, time_period, no_weight_adjustment=True
-    )
-    adjusted_weights = np.maximum(household_weights + weight_adjustment, 0)
-    result = (
-        aggregate_np(adjusted_weights, values_df)
-        / equivalisation_factors_array
-    )
-    target = targets_array / equivalisation_factors_array
-    current_aggregates = (result * equivalisation_factors_array)[0]
-    loss = np.mean(((result / target - 1) ** 2) * np.log2(np.abs(target)))
-    current_loss = loss.item()
-    return pd.DataFrame(
-        {
-            "name": list(targets.keys()) + ["total"],
-            "value": list(current_aggregates) + [current_loss],
-            "target": list(targets.values()) + [0],
-            "time_period": time_period,
-        }
-    )
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/process_puf.py b/policyengine_us/data/datasets/cps/enhanced_cps/process_puf.py
deleted file mode 100644
index 350076c5410..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/process_puf.py
+++ /dev/null
@@ -1,693 +0,0 @@
-import pandas as pd
-from microdf import MicroDataFrame
-from survey_enhance import Imputation
-from typing import Tuple
-from policyengine_us.system import system
-from policyengine_us import Microsimulation
-import numpy as np
-
-PUF_FILE_PATH = "~/Downloads/puf_2015.csv"
-PUF_DEMOGRAPHICS_FILE_PATH = "~/Downloads/demographics_2015.csv"
-
-# This is the full codebook, but we only use the variables in DEMOGRAPHIC_VARIABLES and FINANCIAL_SUBSET.
-
-codebook = {
-    "E00200": "employment_income",
-    "E00300": "taxable_interest_income",
-    "E00400": "tax_exempt_interest_income",
-    "E00600": "dividends_included_in_agi",
-    "E00650": "qualified_dividend_income",
-    "E00700": "state_income_tax_refunds",
-    "E00800": "alimony_income",
-    "E00900": "self_employment_income",
-    "E01000": "net_capital_gain_loss",
-    "E01100": "capital_gain_distributions",
-    "E01200": "other_net_gain",
-    "E01400": "taxable_ira_distributions",
-    "E01500": "total_pensions_annuities_received",
-    "E01700": "taxable_pension_income",
-    "E02000": "schedule_e_net_income_loss",
-    "E02100": "farm_income",
-    "E02300": "taxable_unemployment_compensation",
-    "E02400": "social_security",
-    "E02500": "taxable_social_security",
-    "E03150": "traditional_ira_contributions",
-    "E03210": "student_loan_interest",
-    "E03220": "educator_expense",
-    "E03230": "qualified_tuition_expenses",
-    "E03260": "self_employment_tax_deduction",
-    "E03270": "self_employed_health_insurance_deduction",
-    "E03240": "domestic_production_ald",
-    "E03290": "health_savings_account_ald",
-    # This includes SEP, SIMPLE, and other qualified plans.
-    "E03300": "self_employed_pension_contributions",
-    "E03400": "early_withdrawal_penalty",
-    "E03500": "alimony_expense",
-    "E00100": "adjusted_gross_income",
-    "P04470": "total_deductions_standard_itemized",
-    "E04600": "exemption_amount",
-    "E04800": "taxable_income",
-    "E05100": "tax_on_taxable_income",
-    "E05200": "computed_regular_tax",
-    "E05800": "income_tax_before_credits",
-    "E06000": "income_subject_to_tax",
-    "E06200": "marginal_tax_base",
-    "E06300": "tax_generated_tax_rate_tables",
-    "E09600": "alternative_minimum_tax",
-    "E07180": "child_dependent_care_credit",
-    "E07200": "elderly_disabled_credit",
-    "E07220": "child_tax_credit",
-    "E07230": "education_credits",
-    "E07240": "savers_credit",
-    "E07260": "residential_energy_credit",
-    "E07300": "foreign_tax_credit",
-    "E07400": "general_business_credit",
-    "E07600": "credit_prior_year_minimum_tax",
-    "P08000": "other_tax_credits",
-    "E07150": "total_tax_credit_soi",
-    "E06500": "total_income_tax",
-    "E08800": "income_tax_after_credits_soi",
-    "E09400": "self_employment_tax",
-    "E09700": "recapture_taxes",
-    "E09730": "total_additional_medicare_tax",
-    "E09740": "net_investment_income_tax",
-    "E09750": "health_care_individual_responsibility_payment",
-    "E09800": "social_security_tax_tip_income",
-    "E09900": "penalty_tax_ira",
-    "E10300": "total_tax_liability_soi",
-    "E10700": "income_tax_withheld",
-    "E10900": "estimated_tax_payments",
-    "E10960": "refundable_american_opportunity_credit",
-    "E59560": "earned_income_eic",
-    "E59680": "eic_offset_income_tax_before_credits",
-    "E59700": "eic_offset_other_taxes_except_advance_eic",
-    "E59720": "eic_refundable_portion",
-    "E11550": "refundable_prior_year_minimum_tax_credit",
-    "E11560": "net_premium_tax_credit",
-    "E11561": "net_premium_tax_credit_offset_income_tax_before_credits",
-    "E11562": "net_premium_tax_credit_offset_other_taxes",
-    "E11563": "net_premium_tax_credit_refundable_portion",
-    "E11070": "additional_child_tax_credit",
-    "E11100": "amount_paid_form_4868_request_extension",
-    "E11200": "excess_fica_rrta",
-    "E11300": "credit_federal_tax_special_fuels_oils",
-    "E11400": "regulated_investment_company_credit",
-    "E11601": "total_refundable_credits_offset_income_tax_before_credits",
-    "E11602": "total_refundable_credits_offset_other_taxes",
-    "E11603": "total_refundable_credits_refundable_parts",
-    "E10605": "total_tax_payments_soi",
-    "E11900": "balance_due_overpayment",
-    "E12000": "credit_elect",
-    "E12200": "predetermined_estimated_tax_penalty",
-    "E17500": "medical_dental_expenses_reduction_agi_limit",
-    "E18400": "state_local_taxes",
-    "E18500": "real_estate_taxes",
-    "E19200": "total_interest_paid_deduction",
-    "E19550": "qualified_mortgage_insurance_premiums",
-    "E19800": "charitable_cash_donations",
-    "E20100": "charitable_non_cash_donations",
-    "E19700": "contributions_deduction_total",
-    "E20550": "unreimbursed_employee_business_expense",
-    "E20600": "tax_preparation_fee",
-    "E20400": "misc_deduction",
-    "E20800": "net_limited_miscellaneous_deductions",
-    "E20500": "casualty_loss",
-    "E21040": "itemized_deduction_limitation",
-    "P22250": "short_term_capital_gains",
-    "E22320": "long_term_capital_gains_1",
-    "E22370": "schedule_d_capital_gain_distributions",
-    "P23250": "long_term_capital_gains",
-    "E24515": "unrecaptured_section_1250_gain",
-    "E24516": "capital_gain_less_investment_expense",
-    "E24518": "28_percent_rate_gain_loss",
-    "E24560": "non_schedule_d_tax",
-    "E24598": "schedule_d_15_percent_tax_amount",
-    "E24615": "schedule_d_25_percent_tax_amount",
-    "E24570": "capital_gains_28_percent_rate_gain",
-    "P25350": "total_rents_received",
-    "P25380": "rent_royalty_interest_expenses",
-    "E25550": "total_depreciation_depletion_all_property",
-    "P25700": "rental_income",
-    "E25820": "deductible_rental_loss",
-    "E25850": "rent_royalty_net_income",
-    "E25860": "rent_royalty_net_loss",
-    "E25940": "total_passive_income_partnerships",
-    "E25980": "total_non_passive_income_partnerships",
-    "E25920": "total_passive_loss_partnerships",
-    "E25960": "total_non_passive_loss_partnerships",
-    "E26110": "partnership_section_179_expense_deduction",
-    "E26170": "total_passive_income_small_business_corp",
-    "E26190": "total_non_passive_income_small_business_corp",
-    "E26160": "total_passive_loss_small_business_corp",
-    "E26180": "total_non_passive_loss_small_business_corp",
-    "E26270": "partnership_s_corp_income",
-    "E26100": "s_corp_section_179_expense_deduction",
-    "E26390": "total_income_estate_trust",
-    "E26400": "total_loss_estate_trust",
-    "E27200": "farm_rent_income",
-    "E30400": "self_employment_income_ss_tax_primary",
-    "E30500": "self_employment_income_ss_tax_secondary",
-    "E32800": "qualifying_individuals_expenses_form_2441",
-    "E33000": "expenses_limited_to_earned_income_form_2441",
-    "E53240": "work_opportunity_jobs_general_business_credit",
-    "E53280": "research_experimentation_general_business_credit",
-    "E53300": "low_income_housing_credit",
-    "E53317": "employer_credit_social_security_tax_tips",
-    "E58950": "total_investment_interest_expense_form_4952",
-    "E58990": "investment_income_elected_amount_form_4952",
-    "P60100": "net_operating_loss_tax_preference_adjustments_form_6251",
-    "P61850": "total_adjustments_preferences_form_6251",
-    "E60000": "taxable_income_amt_form_6251",
-    "E62100": "alternative_minimum_taxable_income",
-    "E62900": "alternative_tax_foreign_tax_credit",
-    "E62720": "alternative_minimum_schedule_d_less_investment_interest",
-    "E62730": "alternative_minimum_schedule_d_unrecaptured_section_1250_gain",
-    "E62740": "alternative_minimum_capital_gain_amount",
-    "P65300": "total_passive_net_income_form_8582",
-    "P65400": "total_passive_losses_form_8582",
-    "E68000": "total_losses_allowed_passive_activities",
-    "E82200": "carry_forward_minimum_tax_credit_form_8801",
-    "T27800": "farm_elected_income",
-    "S27860": "tentative_current_prior_year_tax_schedule_j",
-    "P27895": "actual_prior_year_tax_schedule_j",
-    "P87482": "american_opportunity_qualified_expenses_form_8863",
-    "E87521": "american_opportunity_credit",
-    "E87530": "lifetime_learning_total_qualified_expenses",
-    "E87550": "lifetime_learning_credit",
-    "P86421": "bond_purchase_amount_form_8888",
-    "E85050": "total_rental_real_estate_royalties_partnerships_s_corps_trusts",
-    "E85090": "total_net_gain_loss_disposition_property",
-    "E85120": "total_investment_income",
-    "E85180": "total_deductions_modifications",
-    "E85570": "dependents_modified_adjusted_gross_income_amount_form_8962",
-    "E85595": "annual_contribution_health_care_amount",
-    "E85600": "monthly_contribution_health_care_amount",
-    "E85770": "total_premium_tax_credit_amount",
-    "E85775": "advance_premium_tax_credit_amount",
-    "E85785": "excess_advance_payment_premium_tax_credit",
-    "E85790": "repayment_limitation_amount",
-    "RECID": "return_id",
-    "S006": "decimal_weight",
-    "S008": "sample_count",
-    "S009": "population_count",
-    "WSAMP": "sample_code",
-    "TXRT": "marginal_tax_rate",
-}
-
-# This update statement describes the second section of the first PUF codebook.
-
-codebook.update(
-    {
-        "AGIR1": "adjusted_gross_income_band",
-        "CLAIM8965": "health_coverage_exemptions",
-        "DSI": "dependent_status_indicator",
-        "EFI": "electronic_filing_indicator",
-        "EIC": "earned_income_credit_code",
-        "ELECT": "presidential_election_campaign_fund_boxes",
-        "FDED": "form_of_deduction_code",
-        "FLPDYR": "filing_accounting_period_year",
-        "FLPDMO": "filing_accounting_period_month",
-        "F2441": "form_2441_child_care_credit_qualified_individual",
-        "F3800": "form_3800_general_business_credit",
-        "F6251": "form_6251_alternative_minimum_tax",
-        "F8582": "form_8582_passive_activity_loss_limitation",
-        "F8606": "form_8606_nondeductible_ira_contributions",
-        "F8829": "form_8829_expenses_home_business_use",
-        "F8867": "form_8867_paid_preparer_earned_income_credit_checklist",
-        "F8949": "form_8949_sales_dispositions_capital_assets",
-        "F8959": "form_8959_additional_medicare_tax",
-        "F8960": "form_8960_net_investment_income_tax",
-        "F8962": "form_8962_premium_tax_credit",
-        "F8965": "form_8965_health_coverage_exemptions",
-        "IE": "itemized_deductions_election_indicator",
-        "MARS": "marital_filing_status",
-        "MIDR": "married_filing_separately_itemized_deductions_requirement_indicator",
-        "N24": "number_children_child_tax_credit",
-        "N25": "number_qualified_students_lifetime_learning_credit",
-        "N30": "number_qualified_students_american_opportunity_credit",
-        "PREP": "tax_preparer",
-        "PREMNTHS": "months_enrolled_health_insurance_marketplace",
-        "SCHB": "schedule_b_indicator",
-        "SCHCF": "schedule_c_or_f_indicator",
-        "SCHE": "schedule_e_indicator",
-        "TFORM": "form_of_return",
-        "TXST": "tax_status",
-        "XFPT": "primary_taxpayer_exemption",
-        "XFST": "secondary_taxpayer_exemption",
-        "XOCAH": "exemptions_children_living_at_home",
-        "XOCAWH": "exemptions_children_living_away_from_home",
-        "XOODEP": "exemptions_other_dependents",
-        "XOPAR": "exemptions_parents_living_at_away_from_home",
-        "XTOT": "total_exemptions",
-        "XTOT8962": "number_exemptions_form_8962",
-        "XTOT8965": "number_exemptions_form_8965",
-    }
-)
-
-# This update statement describes the demographic supplement.
-codebook.update(
-    {
-        "AGEDP1": "age_dependent_1",
-        "AGEDP2": "age_dependent_2",
-        "AGEDP3": "age_dependent_3",
-        "AGERANGE": "age_range_primary_filer",
-        "EARNSPLIT": "earnings_split_joint_returns",
-        "GENDER": "gender_primary_filer",
-        "RECID": "return_id",
-    }
-)
-
-CPS_DATASET = "cps_2022"
-
-
-DEMOGRAPHIC_VARIABLES = [
-    "age_dependent_1",
-    "age_dependent_2",
-    "age_dependent_3",
-    "age_range_primary_filer",
-    "earnings_split_joint_returns",
-    "gender_primary_filer",
-]
-
-FINANCIAL_VARIABLES = [
-    column
-    for column in codebook.values()
-    if column
-    not in DEMOGRAPHIC_VARIABLES
-    + [
-        "return_id",
-        "decimal_weight",
-        "sample_count",
-        "population_count",
-        "sample_code",
-    ]
-]
-
-FINANCIAL_SUBSET = [
-    "employment_income",
-    "self_employment_income",
-    "partnership_s_corp_income",
-    "farm_income",
-    "farm_rent_income",
-    "short_term_capital_gains",
-    "long_term_capital_gains",
-    "taxable_interest_income",
-    "tax_exempt_interest_income",
-    "rental_income",
-    "qualified_dividend_income",
-    "non_qualified_dividend_income",
-    "taxable_pension_income",
-    "social_security",
-    "taxable_unemployment_compensation",
-    "taxable_social_security",
-    "taxable_ira_distributions",
-    "casualty_loss",
-    "savers_credit",
-    "misc_deduction",
-    "interest_expense",
-    "unrecaptured_section_1250_gain",
-    "domestic_production_ald",
-    "charitable_cash_donations",
-    "charitable_non_cash_donations",
-    "other_net_gain",
-    # Note this is separate from the pre-tax HSA contributions,
-    # which would be made through payroll deductions.
-    "health_savings_account_ald",
-    "capital_gains_28_percent_rate_gain",
-    "foreign_tax_credit",
-    "alimony_expense",
-    "student_loan_interest",
-    "educator_expense",
-    "traditional_ira_contributions",
-    "early_withdrawal_penalty",
-    "self_employed_pension_contributions",
-    "real_estate_taxes",
-    "qualified_tuition_expenses",
-    "alimony_income",
-]
-
-
-def load_puf(
-    puf_file_path: str = None, puf_demographics_path: str = None
-) -> Tuple[pd.DataFrame, pd.DataFrame]:
-    """Load the PUF and demographics data.
-
-    Returns:
-        puf (pd.DataFrame): The PUF data.
-        puf_with_demographics (pd.DataFrame): The subset of the PUF data that has demographic information.
-    """
-    if puf_file_path is None:
-        puf_file_path = "~/Downloads/puf_2015.csv"
-    if puf_demographics_path is None:
-        puf_demographics_path = "~/Downloads/demographics_2015.csv"
-    puf = pd.read_csv(puf_file_path).fillna(0)
-    demographics = pd.read_csv(puf_demographics_path).fillna(0)
-
-    demographics = demographics.dropna()
-
-    puf.S006 = puf.S006 / 100
-
-    # Non-qualified dividend income =
-    # dividends included in AGI - qualified dividend income
-    puf["non_qualified_dividend_income"] = puf.E00600 - puf.E00650
-
-    puf = pd.concat([puf, puf.rename(columns=codebook)], axis=1)
-    # drop duplicate columns
-    puf = puf.loc[:, ~puf.columns.duplicated()]
-    puf.columns = puf.columns.str.lower()
-    demographics = demographics.rename(columns=codebook)
-
-    # Adjustments and derivations from the PUF
-
-    puf["interest_expense"] = puf["total_interest_paid_deduction"]
-    # Self-employed health insurance deduction is in reality capped at
-    # self-employment income. We do not currently impute total premiums
-    # for self-employed people.
-    puf["health_insurance_premiums"] = puf[
-        "self_employed_health_insurance_deduction"
-    ]
-    puf["long_term_capital_gains_on_collectibles"] = puf[
-        "capital_gains_28_percent_rate_gain"
-    ]  # Assume all 28p capital gains are on collectibles.
-
-    return puf, demographics
-
-
-def uprate_puf(puf: pd.DataFrame, time_period: str) -> pd.DataFrame:
-    gov = system.parameters.calibration.gov
-    soi = gov.irs.soi
-
-    # Uprate the financial subset
-
-    for variable_name in FINANCIAL_SUBSET:
-        if variable_name not in soi.children:
-            uprater = gov.cbo.income_by_source.adjusted_gross_income
-        else:
-            uprater = soi.children[variable_name]
-        value_in_2015 = uprater("2015-01-01")
-        value_now = uprater(f"{time_period}-01-01")
-        uprating_factor = value_now / value_in_2015
-        puf[variable_name] = puf[variable_name] * uprating_factor
-
-    return puf
-
-
-def impute_missing_demographics(
-    puf: pd.DataFrame,
-    demographics: pd.DataFrame,
-) -> pd.DataFrame:
-    """Impute missing demographic information from the PUF.
-
-    Args:
-        puf (pd.DataFrame): The PUF data.
-        puf_with_demographics (pd.DataFrame): The subset of the PUF data that has demographic information.
-
-    Returns:
-        puf_with_imputed_demographics (pd.DataFrame): The PUF data with imputed demographic information.
-    """
-
-    demographics_from_puf = Imputation()
-    puf_with_demographics = puf[
-        puf.return_id.isin(demographics.return_id)
-    ].merge(demographics, on="return_id")
-
-    demographics_from_puf.train(
-        puf_with_demographics[FINANCIAL_VARIABLES],
-        puf_with_demographics[DEMOGRAPHIC_VARIABLES],
-    )
-
-    puf_without_demographics = puf[
-        ~puf.return_id.isin(puf_with_demographics.return_id)
-    ].reset_index()
-    puf_without_demographics.marital_filing_status = (
-        puf_without_demographics.marital_filing_status.replace(
-            {
-                0: 1,
-            }
-        )
-    )  # Aggregated returns -> single
-    predicted_demographics = demographics_from_puf.predict(
-        puf_without_demographics[FINANCIAL_VARIABLES].fillna(0)
-    )
-    puf_with_imputed_demographics = pd.concat(
-        [puf_without_demographics, predicted_demographics], axis=1
-    )
-
-    weighted_puf_with_demographics = MicroDataFrame(
-        puf_with_demographics, weights="decimal_weight"
-    )
-    weighted_puf_with_imputed_demographics = MicroDataFrame(
-        puf_with_imputed_demographics, weights="decimal_weight"
-    )
-
-    puf_combined = pd.concat(
-        [
-            weighted_puf_with_demographics,
-            weighted_puf_with_imputed_demographics,
-        ]
-    )
-
-    return puf_combined
-
-
-def generate_puf_style_cps(time_period: str) -> pd.DataFrame:
-    """Generate a PUF-style table from the CPS.
-
-    Returns:
-        cps (pd.DataFrame): The CPS data.
-    """
-
-    sim = Microsimulation(dataset=f"cps_{time_period}")
-
-    cps_demographics = pd.DataFrame(index=sim.calculate("tax_unit_id").values)
-
-    df = sim.calculate_dataframe(
-        ["age", "tax_unit_id", "is_tax_unit_dependent"]
-    )
-    df = df[df.is_tax_unit_dependent]
-    df_sorted = df.sort_values(["tax_unit_id", "age"])
-    df_sorted["rank"] = df_sorted.groupby("tax_unit_id")["age"].rank()
-
-    df_sorted["age_dependent_1"] = np.where(
-        df_sorted["rank"] == 1, df_sorted["age"], -1
-    )
-    df_sorted["age_dependent_2"] = np.where(
-        df_sorted["rank"] == 2, df_sorted["age"], -1
-    )
-    df_sorted["age_dependent_3"] = np.where(
-        df_sorted["rank"] == 3, df_sorted["age"], -1
-    )
-
-    df_sorted_maxed = df_sorted.groupby("tax_unit_id").max()
-
-    cps_demographics["age_dependent_1"] = df_sorted_maxed["age_dependent_1"]
-    cps_demographics["age_dependent_2"] = df_sorted_maxed["age_dependent_2"]
-    cps_demographics["age_dependent_3"] = df_sorted_maxed["age_dependent_3"]
-
-    cps_demographics = cps_demographics.fillna(-1)
-
-    # Define the age bins and labels
-    bins = [-np.inf, -1, 4, 12, 16, 18, 23, np.inf]
-    labels = [0, 1, 2, 3, 4, 5, 6]
-
-    # Create AGEDP1, AGEDP2, AGEDP3 based on the categories
-    for col in ["age_dependent_1", "age_dependent_2", "age_dependent_3"]:
-        cps_demographics[col] = pd.cut(
-            cps_demographics[col], bins=bins, labels=labels, right=True
-        )
-
-    cps_demographics.reset_index(inplace=True)
-    cps_demographics = cps_demographics[
-        ["age_dependent_1", "age_dependent_2", "age_dependent_3"]
-    ]
-
-    cps_demographics["age_range_primary_filer"] = sim.calculate(
-        "age_head"
-    ).values
-
-    bins_head = [-np.inf, -1, 25, 34, 44, 54, 64, np.inf]
-    labels_head = [0, 1, 2, 3, 4, 5, 6]
-
-    cps_demographics["age_range_primary_filer"] = pd.cut(
-        cps_demographics["age_range_primary_filer"],
-        bins=bins_head,
-        labels=labels_head,
-        right=True,
-    )
-
-    is_male = sim.calculate("is_male")
-    is_head = sim.calculate("is_tax_unit_head")
-    male_head = sim.map_result(is_male * is_head, "person", "tax_unit")
-    tax_unit_filer_gender = np.where(male_head, 1, 2)
-
-    cps_demographics["gender_primary_filer"] = tax_unit_filer_gender
-
-    filer_earned = sim.calculate("head_earned")
-    spouse_earned = sim.calculate("spouse_earned")
-    filing_status = sim.calculate("filing_status")
-
-    def determine_earning_split_value(
-        filer: float, spouse: float, filing_status: str
-    ) -> int:
-        if filing_status != "JOINT":
-            return 0
-        if filer + spouse <= 0:
-            return 1
-        ratio_filer = filer / (filer + spouse)
-        if ratio_filer >= 0.75:
-            return 1
-        elif ratio_filer >= 0.25:
-            return 2
-        else:
-            return 3
-
-    cps_demographics["earnings_split_joint_returns"] = np.vectorize(
-        determine_earning_split_value
-    )(filer_earned, spouse_earned, filing_status)
-    cps_demographics["tax_unit_weight"] = sim.calculate(
-        "tax_unit_weight"
-    ).values
-    return pd.DataFrame(cps_demographics)
-
-
-def impute_puf_financials_to_cps(
-    cps_demographics: pd.DataFrame,
-    puf: pd.DataFrame,
-):
-    """Impute PUF financials to the CPS.
-
-    Args:
-        cps_demographics (pd.DataFrame): The CPS data with demographic information.
-        puf (pd.DataFrame): The PUF data.
-
-    Returns:
-        cps_imputed (pd.DataFrame): The CPS data with imputed financial information.
-    """
-    income_from_demographics = Imputation()
-
-    income_from_demographics.train(
-        puf[DEMOGRAPHIC_VARIABLES],
-        puf[FINANCIAL_SUBSET],
-        sample_weight=puf.decimal_weight,
-    )
-
-    cps_financial_predictions = income_from_demographics.predict(
-        cps_demographics[DEMOGRAPHIC_VARIABLES],
-        mean_quantile=[0.8] + [0.5] * (len(FINANCIAL_SUBSET) - 1),
-    )
-    cps_imputed = pd.concat(
-        [cps_demographics, cps_financial_predictions], axis=1
-    )
-    cps_imputed = MicroDataFrame(
-        cps_imputed, weights=cps_demographics.tax_unit_weight
-    )
-
-    return cps_imputed
-
-
-def project_tax_unit_cps_to_person_level(
-    puf_style_cps: pd.DataFrame, time_period: str
-) -> pd.DataFrame:
-    """Project tax unit CPS to person level.
-
-    Args:
-        puf_style_cps (pd.DataFrame): The CPS data with imputed financial information.
-
-    Returns:
-        person_df (pd.DataFrame): The CPS data with imputed financial information projected to the person level.
-    """
-    sim = Microsimulation(dataset=f"cps_{time_period}")
-    person_df = pd.DataFrame(
-        dict(
-            person_id=sim.calculate("person_id").values,
-            tax_unit_id=sim.calculate("person_tax_unit_id").values,
-        )
-    )
-
-    tax_unit_df = pd.DataFrame(
-        dict(
-            tax_unit_id=sim.calculate("tax_unit_id").values,
-        ),
-    )
-
-    person_is_tax_filer_head = sim.calculate("is_tax_unit_head").values
-
-    for variable in FINANCIAL_SUBSET:
-        if sim.tax_benefit_system.variables[variable].entity.key == "tax_unit":
-            tax_unit_df[variable] = puf_style_cps[variable].values
-        else:
-            cps_original_value = sim.calculate(variable).values
-            cps_tax_unit_original_total = sim.map_result(
-                sim.map_result(cps_original_value, "person", "tax_unit"),
-                "tax_unit",
-                "person",
-            )
-            cps_share_of_tax_unit_original_total = (
-                cps_original_value / cps_tax_unit_original_total
-            )
-            cps_share_of_tax_unit_original_total = np.where(
-                np.isnan(cps_share_of_tax_unit_original_total),
-                person_is_tax_filer_head,
-                cps_share_of_tax_unit_original_total,
-            )
-            mapped_down_imputed_values = sim.map_result(
-                puf_style_cps[variable].values, "tax_unit", "person"
-            )
-            person_df[variable] = (
-                cps_share_of_tax_unit_original_total
-                * mapped_down_imputed_values
-            )
-
-    person_df = person_df.fillna(0)
-    tax_unit_df = tax_unit_df.fillna(0)
-    return person_df, tax_unit_df
-
-
-def puf_imputed_cps_person_level(
-    verbose: bool = True,
-    time_period: str = "2022",
-) -> pd.DataFrame:
-    """Generate a PUF-imputed CPS at the person level.
-
-    Args:
-        verbose (bool): Whether to print progress statements.
-        time_period (str): The time period to uprate the PUF to.
-
-    Returns:
-        person_level_puf_imputed_cps (pd.DataFrame): The PUF-imputed CPS at the person level.
-    """
-    if verbose:
-        print("Loading PUF and demographics")
-    puf, demographics = load_puf()
-
-    puf = uprate_puf(puf, time_period)
-
-    if verbose:
-        print("Imputing missing demographics")
-    puf = impute_missing_demographics(puf, demographics)
-
-    if verbose:
-        print("Generating PUF-style CPS")
-    puf_style_cps = generate_puf_style_cps(time_period)
-
-    if verbose:
-        print("Imputing PUF financials to CPS")
-    puf_imputed_cps = impute_puf_financials_to_cps(puf_style_cps, puf)
-
-    if verbose:
-        print("Projecting tax unit CPS to person level")
-    (
-        person_level_puf_imputed_cps,
-        tax_unit_level_puf_imputed_cps,
-    ) = project_tax_unit_cps_to_person_level(
-        puf_imputed_cps,
-        time_period,
-    )
-
-    if verbose:
-        print("Done")
-    return person_level_puf_imputed_cps, tax_unit_level_puf_imputed_cps
-
-
-if __name__ == "__main__":
-    puf_imputed_cps_person_level(verbose=True)
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/puf_extended_cps.py b/policyengine_us/data/datasets/cps/enhanced_cps/puf_extended_cps.py
deleted file mode 100644
index 36b70e374ef..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/puf_extended_cps.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from policyengine_core.data import Dataset
-from policyengine_us.data.datasets.cps.cps import CPS_2022
-from policyengine_us.data.storage import STORAGE_FOLDER
-import numpy as np
-
-
-class PUFExtendedCPS(Dataset):
-    data_format = Dataset.ARRAYS
-    cps = None
-
-    def generate(self):
-        from .process_puf import (
-            FINANCIAL_SUBSET as FINANCIAL_VARIABLES,
-            puf_imputed_cps_person_level,
-        )
-        from policyengine_us.system import system
-        from policyengine_us import Microsimulation
-
-        person_df, tax_unit_df = puf_imputed_cps_person_level(
-            time_period=self.time_period
-        )
-        new_data = {}
-        cps = self.cps()
-        input_data_sim = Microsimulation(dataset=self.cps)
-        cps_data = cps.load()
-        for variable in list(set(cps.variables) | set(FINANCIAL_VARIABLES)):
-            if "_id" in variable:
-                # Append on a copy multiplied by 10
-                new_data[variable] = np.concatenate(
-                    [cps_data[variable][...], cps_data[variable][...] + 1e8]
-                )
-            elif "_weight" in variable:
-                # Append on a zero-weighted copy
-                new_data[variable] = np.concatenate(
-                    [
-                        cps_data[variable][...],
-                        np.zeros_like(cps_data[variable][...]),
-                    ]
-                )
-            else:
-                # Append on a copy
-                if variable in FINANCIAL_VARIABLES:
-                    if variable not in cps.variables:
-                        entity = system.variables[variable].entity.key
-                        original_values = np.zeros_like(
-                            cps_data[f"{entity}_id"][...]
-                        )
-                    else:
-                        original_values = input_data_sim.calculate(
-                            variable, self.time_period
-                        ).values
-                    imputed_data = (
-                        person_df[variable].values
-                        if variable in person_df
-                        else tax_unit_df[variable].values
-                    )
-                    new_data[variable] = np.concatenate(
-                        [original_values, imputed_data]
-                    )
-                else:
-                    new_data[variable] = np.concatenate(
-                        [cps_data[variable][...], cps_data[variable][...]]
-                    )
-
-        # Flag if record is from imputed from PUF or straight from CPS.
-        new_data["is_imputed_from_puf"] = np.concatenate(
-            [
-                np.zeros_like(cps_data["age"][...]),
-                np.ones_like(cps_data["age"][...]),
-            ]
-        ).astype(bool)
-
-        self.save_dataset(new_data)
-
-
-class PUFExtendedCPS_2022(PUFExtendedCPS):
-    time_period = 2022
-    name = "puf_extended_cps_2022"
-    label = "PUF-extended CPS (2022)"
-    cps = CPS_2022
-    file_path = STORAGE_FOLDER / "puf_extended_cps_2022.h5"
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/run_calibration.py b/policyengine_us/data/datasets/cps/enhanced_cps/run_calibration.py
deleted file mode 100644
index d3e0f4205c3..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/run_calibration.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from policyengine_us.data.datasets.cps.enhanced_cps.loss import (
-    get_snapshot,
-)
-from policyengine_us.data.storage import STORAGE_FOLDER
-import pandas as pd
-
-from policyengine_us.data import CalibratedPUFExtendedCPS_2022
-
-
-def main():
-    CalibratedPUFExtendedCPS_2022().generate()
-
-    for file in STORAGE_FOLDER.glob("*.csv.gz"):
-        file.unlink()
-
-    YEARS = ["2022", "2023", "2024", "2025"]
-    DATASETS = ["cps_2022", "calibrated_puf_extended_cps_2022"]
-
-    dfs = []
-
-    for year in YEARS:
-        for dataset in DATASETS:
-            print(f"Evaluating {dataset} for {year}")
-            df = get_snapshot(dataset, year)
-            df["time_period"] = year
-            df["dataset"] = dataset
-            dfs.append(df)
-
-            df = pd.concat(dfs, ignore_index=True)
-
-            df.to_csv(
-                STORAGE_FOLDER / "dataset_losses.csv.gz",
-                index=False,
-                compression="gzip",
-            )
-
-
-if __name__ == "__main__":
-    main()
diff --git a/policyengine_us/data/datasets/cps/enhanced_cps/update_dashboard.py b/policyengine_us/data/datasets/cps/enhanced_cps/update_dashboard.py
deleted file mode 100644
index 95238a611b1..00000000000
--- a/policyengine_us/data/datasets/cps/enhanced_cps/update_dashboard.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from policyengine_us.data.datasets.cps.enhanced_cps.loss import (
-    get_snapshot,
-)
-from policyengine_us.data.storage import STORAGE_FOLDER
-import pandas as pd
-
-
-def main():
-    YEARS = ["2022", "2023", "2024", "2025"]
-    DATASETS = ["puf_2022", "cps_2022", "calibrated_puf_extended_cps_2022"]
-
-    dfs = []
-
-    for year in YEARS:
-        for dataset in DATASETS:
-            print(dataset, year)
-            df = get_snapshot(dataset, year)
-            df["time_period"] = year
-            df["dataset"] = dataset
-            dfs.append(df)
-
-    df = pd.concat(dfs, ignore_index=True)
-
-    df.to_csv(
-        STORAGE_FOLDER / "dataset_losses.csv.gz",
-        index=False,
-        compression="gzip",
-    )
-
-
-if __name__ == "__main__":
-    main()
diff --git a/policyengine_us/data/datasets/cps/imputation_parameters.yaml b/policyengine_us/data/datasets/cps/imputation_parameters.yaml
deleted file mode 100644
index 895edf0a110..00000000000
--- a/policyengine_us/data/datasets/cps/imputation_parameters.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-# Personal income variable imputation parameters used
-# in the cps.py add_personal_income_variables function.
-
-# SOI 2020 data
-taxable_interest_fraction: 0.680
-
-# SOI 2018 data
-qualified_dividend_fraction: 0.448
-
-# no SOI data, so arbitrary assumption
-taxable_pension_fraction: 1.0
-taxable_401k_distribution_fraction: 1.0
-taxable_403b_distribution_fraction: 1.0
-taxable_ira_distribution_fraction: 1.0
-taxable_sep_distribution_fraction: 1.0
-
-# SOI 2012 data
-long_term_capgain_fraction: 0.880  
diff --git a/policyengine_us/data/datasets/cps/raw_cps.py b/policyengine_us/data/datasets/cps/raw_cps.py
deleted file mode 100644
index dbaeb0c8620..00000000000
--- a/policyengine_us/data/datasets/cps/raw_cps.py
+++ /dev/null
@@ -1,289 +0,0 @@
-from io import BytesIO
-from zipfile import ZipFile
-from policyengine_core.data import Dataset
-import pandas as pd
-import requests
-from tqdm import tqdm
-from policyengine_us.data.storage import STORAGE_FOLDER
-
-TAX_UNIT_COLUMNS = [
-    "ACTC_CRD",
-    "AGI",
-    "CTC_CRD",
-    "EIT_CRED",
-    "FEDTAX_AC",
-    "FEDTAX_BC",
-    "MARG_TAX",
-    "STATETAX_A",
-    "STATETAX_B",
-    "TAX_INC",
-]
-
-SPM_UNIT_COLUMNS = [
-    "ACTC",
-    "BBSUBVAL",
-    "CAPHOUSESUB",
-    "CAPWKCCXPNS",
-    "CHILDCAREXPNS",
-    "CHILDSUPPD",
-    "EITC",
-    "ENGVAL",
-    "EQUIVSCALE",
-    "FAMTYPE",
-    "FEDTAX",
-    "FEDTAXBC",
-    "FICA",
-    "GEOADJ",
-    "HAGE",
-    "HHISP",
-    "HMARITALSTATUS",
-    "HRACE",
-    "MEDXPNS",
-    "NUMADULTS",
-    "NUMKIDS",
-    "NUMPER",
-    "POOR",
-    "POVTHRESHOLD",
-    "RESOURCES",
-    "SCHLUNCH",
-    "SNAPSUB",
-    "STTAX",
-    "TENMORTSTATUS",
-    "TOTVAL",
-    "WCOHABIT",
-    "WEIGHT",
-    "WFOSTER22",
-    "WICVAL",
-    "WKXPNS",
-    "WNEWHEAD",
-    "WNEWPARENT",
-    "WUI_LT15",
-    "ID",
-]
-SPM_UNIT_COLUMNS = ["SPM_" + column for column in SPM_UNIT_COLUMNS]
-PERSON_COLUMNS = [
-    "PH_SEQ",
-    "PF_SEQ",
-    "P_SEQ",
-    "TAX_ID",
-    "SPM_ID",
-    "A_FNLWGT",
-    "A_LINENO",
-    "A_SPOUSE",
-    "A_AGE",
-    "A_SEX",
-    "PEDISEYE",
-    "MRK",
-    "WSAL_VAL",
-    "INT_VAL",
-    "SEMP_VAL",
-    "FRSE_VAL",
-    "DIV_VAL",
-    "RNT_VAL",
-    "SS_VAL",
-    "UC_VAL",
-    "ANN_VAL",
-    "PNSN_VAL",
-    "OI_OFF",
-    "OI_VAL",
-    "CSP_VAL",
-    "PAW_VAL",
-    "SSI_VAL",
-    "RETCB_VAL",
-    "CAP_VAL",
-    "WICYN",
-    "VET_VAL",
-    "WC_VAL",
-    "DIS_VAL1",
-    "DIS_VAL2",
-    "CHSP_VAL",
-    "PHIP_VAL",
-    "MOOP",
-    "POTC_VAL",
-    "PMED_VAL",
-    "PEMCPREM",
-    "PEDISDRS",
-    "PEDISEAR",
-    "PEDISOUT",
-    "PEDISPHY",
-    "PEDISREM",
-    "PEPAR1",
-    "PEPAR2",
-    "DIS_SC1",
-    "DIS_SC2",
-    "DST_SC1",
-    "DST_SC2",
-    "DST_SC1_YNG",
-    "DST_SC2_YNG",
-    "DST_VAL1",
-    "DST_VAL2",
-    "DST_VAL1_YNG",
-    "DST_VAL2_YNG",
-    "PRDTRACE",
-    "PRDTHSP",
-    "A_MARITL",
-    "PERIDNUM",
-    "I_ERNVAL",
-    "I_SEVAL",
-    "A_HSCOL",
-    "HRSWK",
-    "WKSWORK",
-]
-
-
-class RawCPS(Dataset):
-    name = "raw_cps"
-    label = "Raw CPS"
-    time_period = None
-    data_format = Dataset.TABLES
-
-    def generate(self) -> pd.DataFrame:
-        """Generates the raw CPS dataset."""
-        # Files are named for a year after the year the survey represents.
-        # For example, the 2020 CPS was administered in March 2021, so it's
-        # named 2021.
-        file_year = int(self.time_period) + 1
-        file_year_code = str(file_year)[-2:]
-
-        CPS_URL_BY_YEAR = {
-            2018: "https://www2.census.gov/programs-surveys/cps/datasets/2019/march/asecpub19csv.zip",
-            2019: "https://www2.census.gov/programs-surveys/cps/datasets/2020/march/asecpub20csv.zip",
-            2020: "https://www2.census.gov/programs-surveys/cps/datasets/2021/march/asecpub21csv.zip",
-            2021: "https://www2.census.gov/programs-surveys/cps/datasets/2022/march/asecpub22csv.zip",
-            2022: "https://www2.census.gov/programs-surveys/cps/datasets/2023/march/asecpub23csv.zip",
-        }
-
-        if self.time_period not in CPS_URL_BY_YEAR:
-            raise ValueError(
-                f"No raw CPS data URL known for year {self.time_period}."
-            )
-
-        url = CPS_URL_BY_YEAR[self.time_period]
-
-        spm_unit_columns = SPM_UNIT_COLUMNS
-        if self.time_period <= 2020:
-            spm_unit_columns = [
-                col for col in spm_unit_columns if col != "SPM_BBSUBVAL"
-            ]
-
-        response = requests.get(url, stream=True)
-        total_size_in_bytes = int(
-            response.headers.get("content-length", 200e6)
-        )
-        progress_bar = tqdm(
-            total=total_size_in_bytes,
-            unit="iB",
-            unit_scale=True,
-            desc="Downloading ASEC",
-        )
-        if response.status_code == 404:
-            raise FileNotFoundError(
-                "Received a 404 response when fetching the data."
-            )
-        try:
-            with BytesIO() as file, pd.HDFStore(
-                self.file_path, mode="w"
-            ) as storage:
-                content_length_actual = 0
-                for data in response.iter_content(int(1e6)):
-                    progress_bar.update(len(data))
-                    content_length_actual += len(data)
-                    file.write(data)
-                progress_bar.set_description("Downloaded ASEC")
-                progress_bar.total = content_length_actual
-                progress_bar.close()
-                zipfile = ZipFile(file)
-                if file_year_code == "19":
-                    # In the 2018 CPS, the file is within prod/data/2019
-                    # instead of at the top level.
-                    file_prefix = "cpspb/asec/prod/data/2019/"
-                else:
-                    file_prefix = ""
-                with zipfile.open(
-                    f"{file_prefix}pppub{file_year_code}.csv"
-                ) as f:
-                    storage["person"] = pd.read_csv(
-                        f,
-                        usecols=PERSON_COLUMNS
-                        + spm_unit_columns
-                        + TAX_UNIT_COLUMNS,
-                    ).fillna(0)
-                    person = storage["person"]
-                with zipfile.open(
-                    f"{file_prefix}ffpub{file_year_code}.csv"
-                ) as f:
-                    person_family_id = person.PH_SEQ * 10 + person.PF_SEQ
-                    family = pd.read_csv(f).fillna(0)
-                    family_id = family.FH_SEQ * 10 + family.FFPOS
-                    family = family[family_id.isin(person_family_id)]
-                    storage["family"] = family
-                with zipfile.open(
-                    f"{file_prefix}hhpub{file_year_code}.csv"
-                ) as f:
-                    person_household_id = person.PH_SEQ
-                    household = pd.read_csv(f).fillna(0)
-                    household_id = household.H_SEQ
-                    household = household[
-                        household_id.isin(person_household_id)
-                    ]
-                    storage["household"] = household
-                storage["tax_unit"] = RawCPS._create_tax_unit_table(person)
-                storage["spm_unit"] = RawCPS._create_spm_unit_table(
-                    person, self.time_period
-                )
-        except Exception as e:
-            raise ValueError(
-                f"Attempted to extract and save the CSV files, but encountered an error: {e} (removed the intermediate dataset)."
-            )
-
-    @staticmethod
-    def _create_tax_unit_table(person: pd.DataFrame) -> pd.DataFrame:
-        tax_unit_df = person[TAX_UNIT_COLUMNS].groupby(person.TAX_ID).sum()
-        tax_unit_df["TAX_ID"] = tax_unit_df.index
-        return tax_unit_df
-
-    @staticmethod
-    def _create_spm_unit_table(
-        person: pd.DataFrame, time_period: int
-    ) -> pd.DataFrame:
-        spm_unit_columns = SPM_UNIT_COLUMNS
-        if time_period <= 2020:
-            spm_unit_columns = [
-                col for col in spm_unit_columns if col != "SPM_BBSUBVAL"
-            ]
-        return person[spm_unit_columns].groupby(person.SPM_ID).first()
-
-
-class RawCPS_2018(RawCPS):
-    time_period = 2018
-    name = "raw_cps_2018"
-    label = "Raw CPS 2018"
-    file_path = STORAGE_FOLDER / "raw_cps_2018.h5"
-
-
-class RawCPS_2019(RawCPS):
-    time_period = 2019
-    name = "raw_cps_2019"
-    label = "Raw CPS 2019"
-    file_path = STORAGE_FOLDER / "raw_cps_2019.h5"
-
-
-class RawCPS_2020(RawCPS):
-    time_period = 2020
-    name = "raw_cps_2020"
-    label = "Raw CPS 2020"
-    file_path = STORAGE_FOLDER / "raw_cps_2020.h5"
-
-
-class RawCPS_2021(RawCPS):
-    time_period = 2021
-    name = "raw_cps_2021"
-    label = "Raw CPS 2021"
-    file_path = STORAGE_FOLDER / "raw_cps_2021.h5"
-
-
-class RawCPS_2022(RawCPS):
-    time_period = 2022
-    name = "raw_cps_2022"
-    label = "Raw CPS 2022"
-    file_path = STORAGE_FOLDER / "raw_cps_2022.h5"
diff --git a/policyengine_us/data/datasets/cps/uprated_cps.py b/policyengine_us/data/datasets/cps/uprated_cps.py
deleted file mode 100644
index b4d15188976..00000000000
--- a/policyengine_us/data/datasets/cps/uprated_cps.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from policyengine_core.data import Dataset
-from typing import Type
-from policyengine_us.data.storage import STORAGE_FOLDER
-
-
-class UpratedCPS(Dataset):
-    data_format = Dataset.ARRAYS
-
-    @staticmethod
-    def from_dataset(
-        dataset: Type[Dataset],
-        out_year: int = 2022,
-        new_name: str = None,
-        new_label: str = None,
-        new_file_path: str = None,
-        new_url: str = None,
-    ):
-        class UpratedCPSFromDataset(UpratedCPS):
-            name = new_name or f"{dataset.name}_uprated_{out_year}"
-            label = new_label or f"{dataset.label}"
-            input_dataset = dataset
-            time_period = out_year
-            file_path = new_file_path or (
-                STORAGE_FOLDER / f"{dataset.name}_uprated_{out_year}.h5"
-            )
-            url = new_url
-
-        return UpratedCPSFromDataset
-
-    def generate(self):
-        from policyengine_us import Microsimulation
-
-        input_dataset = self.input_dataset()
-        simulation = Microsimulation(dataset=self.input_dataset)
-
-        data = {}
-        for variable in input_dataset.variables:
-            try:
-                data[variable] = simulation.calculate(
-                    variable, period=self.output_year
-                ).values
-            except:
-                data[variable] = input_dataset.load(variable)
-
-        self.save_dataset(data)
diff --git a/policyengine_us/data/datasets/create.py b/policyengine_us/data/datasets/create.py
deleted file mode 100644
index 79c2547c6cf..00000000000
--- a/policyengine_us/data/datasets/create.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from policyengine_us.data import (
-    CPS_2019,
-    CPS_2020,
-    CPS_2021,
-    CPS_2022,
-    CalibratedPUFExtendedCPS_2022,
-    EnhancedCPS_2022,
-)
-from policyengine_us.data.storage import STORAGE_FOLDER
-
-datasets = [
-    CPS_2019,
-    CPS_2020,
-    CPS_2021,
-    CPS_2022,
-    CalibratedPUFExtendedCPS_2022,
-    EnhancedCPS_2022,
-]
-
-for dataset in datasets[5:]:
-    print(f"Generating {dataset.name}...")
-    dataset().generate()
diff --git a/policyengine_us/data/datasets/poverty_tracker/poverty_tracker.py b/policyengine_us/data/datasets/poverty_tracker/poverty_tracker.py
deleted file mode 100644
index 6fabfbb72b3..00000000000
--- a/policyengine_us/data/datasets/poverty_tracker/poverty_tracker.py
+++ /dev/null
@@ -1,247 +0,0 @@
-from policyengine_core.data import Dataset
-from typing import Type
-import pandas as pd
-from pathlib import Path
-from policyengine_us.data.storage import STORAGE_FOLDER
-import os
-
-
-class PovertyTracker(Dataset):
-    name = "poverty_tracker"
-    label = "Poverty Tracker"
-    data_format = Dataset.ARRAYS
-    time_period = "2023"
-    file_path = STORAGE_FOLDER / "poverty_tracker.h5"
-
-    # Fetch the secret URL from the environment variable.
-    # Replace with a valid URL if running locally.
-    POVERTY_TRACKER_URL = os.environ.get("POVERTYTRACKER_RAW_URL")
-
-    def generate(self):
-        household_df = self.load_pt_data()
-        situations = household_df.apply(get_hh_situtation, axis=1)
-
-        if not self.POVERTY_TRACKER_URL:
-            raise ValueError(
-                "The POVERTY_TRACKER_URL environment variable is not set!"
-            )
-
-        person_ids = []
-        person_group_ids = []
-        group_ids = []
-
-        data = {
-            "age": [],
-            "employment_income": [],
-        }
-
-        current_person_id = 0
-        current_group_id = 0
-        group_ids = household_df["public_id"].values
-        for situation in situations:
-            for person in situation["people"]:
-                person_ids.append(current_person_id)
-                person_group_ids.append(group_ids[current_group_id])
-
-                age = situation["people"][person]["age"]
-                employment_income = situation["people"][person][
-                    "employment_income"
-                ]
-
-                # Clarify why age is sometimes directly in situation["people"][person]["age"] and sometimes in situation["people"][person]["age"][TAX_YEAR_STR]
-                data["age"].append(
-                    age.get(TAX_YEAR_STR) if isinstance(age, dict) else age
-                )
-                data["employment_income"].append(
-                    employment_income.get(TAX_YEAR_STR)
-                    if isinstance(employment_income, dict)
-                    else employment_income
-                )
-
-                current_person_id += 1
-
-            current_group_id += 1
-
-        data["in_nyc"] = [True] * len(group_ids)
-        data["state_name"] = ["NY"] * len(group_ids)
-
-        group_id_names = [
-            "tax_unit",
-            "marital_unit",
-            "spm_unit",
-            "household",
-            "family",
-        ]
-
-        for group_id_name in group_id_names:
-            data[group_id_name + "_id"] = group_ids
-            data["person_" + group_id_name + "_id"] = person_group_ids
-
-        data["person_id"] = person_ids
-
-        self.save_dataset(data)
-
-    def load_pt_data(
-        self,
-        url: str = POVERTY_TRACKER_URL,
-    ) -> pd.DataFrame:
-        """
-        Loads household-level raw PovertyTracker dataset.
-        """
-        # Download raw data from PovertyTracker website.
-        pt = pd.read_csv(url)
-
-        # Filter to only 2019 and 2020 (else q16surveyyear is NaN).
-        pt = pt[pt["q16surveyyear"].notnull()].reset_index(drop=True)
-
-        # Other adult and child household income.
-        pt["other_household_income"] = pt[
-            [
-                "imp_q16incothhh_adult_tc",
-                "imp_q16incothhh_child_tc",
-                "imp_q16incothhh_tc",
-            ]
-        ].sum(axis=1)
-
-        # Rename columns.
-        COLUMNS_TO_RENAME = {
-            "q16a3_age_tc": "age",
-            "imp_q16earnhd_tc": "household_head_income",
-            "imp_q16earnsp_tc": "spouse_income",
-            "imp_q16moop_tc": "medical_out_of_pocket_expenses",
-            "imp_q16chwoop_tc": "childcare_and_work_expenses",
-            "imp_q16incsnap_tc": "snap_income",
-            "imp_q16incret_tc": "retirement_income",
-            "imp_q16incdis_tc": "disability_income",
-            "imp_q16incui_tc": "unemployment_income",
-            "q16a4": "respondent_lives_with_others",  # 1/2/97/98/99 Yes/No/NoAnswer/Don't know/Refused
-            # "qopmres": "income",
-        }
-        for i in range(1, 10):
-            COLUMNS_TO_RENAME[f"q16a4_rel{i}"] = f"relationship_to_relative{i}"
-            COLUMNS_TO_RENAME[f"q16a4_age{i}_tc"] = f"age_of_relative{i}"
-
-        pt.rename(columns=COLUMNS_TO_RENAME, inplace=True)
-
-        # Replace 970 (unknown) with 40 (default age)
-        pt["age"] = pt["age"].replace(970, 40)
-
-        return pt.rename(columns=COLUMNS_TO_RENAME)
-
-
-def extract_hh_data(observation: pd.Series) -> dict:
-    """
-    Takes in a row of the PovertyTracker dataframe and extracts
-    information on relatives living with the household head.
-    """
-    relatives_dict = {}
-
-    if observation["respondent_lives_with_others"] != 1:
-        return relatives_dict
-
-    var_number = 1
-    relative_var = f"relationship_to_relative{var_number}"
-    dependent_counter = 1
-    while pd.notnull(relative_var) and var_number < 10:
-        if observation[relative_var] == 1:  # Spouse
-            partner_age = observation[f"age_of_relative{var_number}"]
-            relatives_dict["your partner"] = {
-                "age": partner_age if partner_age != 970 else 40,
-                "employment_income": {
-                    TAX_YEAR_STR: observation["spouse_income"]
-                },
-            }
-
-        elif observation[relative_var] in [
-            3,
-            4,
-            5,
-            7,
-        ]:  # child, stepchild, grandchild, or foster child
-            relative_age = observation[f"age_of_relative{var_number}"]
-            if relative_age < 18:  # assume only children are dependents
-                relatives_dict[f"dependent{dependent_counter}"] = {
-                    "age": relative_age if relative_age != 970 else 40
-                }
-            dependent_counter += 1
-
-        var_number += 1
-
-    return relatives_dict
-
-
-TAX_YEAR = 2023
-TAX_YEAR_STR = str(TAX_YEAR)
-
-
-def get_hh_situtation(row: pd.Series):
-    situation_dict = {
-        "people": {
-            "you": {
-                "age": {TAX_YEAR_STR: row["age"]},
-                "employment_income": {
-                    TAX_YEAR_STR: row["household_head_income"]
-                },
-                "medical_out_of_pocket_expenses": {
-                    TAX_YEAR_STR: row["medical_out_of_pocket_expenses"]
-                },
-            }
-        },
-        "families": {"your family": {"members": ["you"]}},
-        "marital_units": {},
-        "tax_units": {"your tax unit": {"members": ["you"]}},
-        "spm_units": {
-            "your household": {
-                "members": [
-                    "you",
-                ],
-                "childcare_expenses": {
-                    TAX_YEAR_STR: row["childcare_and_work_expenses"]
-                },
-                "snap": {TAX_YEAR_STR: row["snap_income"]},
-            }
-        },
-        "households": {
-            "your household": {
-                "members": ["you"],
-                "state_name": {TAX_YEAR_STR: "NY"},
-                "in_nyc": True,
-            }
-        },
-    }
-
-    hh_data = extract_hh_data(row)
-    situation_dict["people"].update(hh_data)
-
-    # Add spouse and dependents to families, marital_units, tax_units, spm_units
-    family_members = ["you"]
-    marital_unit_members = ["you"]
-    for member, member_data in hh_data.items():
-        family_members.append(member)
-        marital_unit_members.append(member)
-        situation_dict["people"][member].update(
-            {"employment_income": {TAX_YEAR_STR: 0}}
-        )
-        situation_dict["tax_units"]["your tax unit"]["members"].append(member)
-        situation_dict["spm_units"]["your household"]["members"].append(member)
-        situation_dict["households"]["your household"]["members"].append(
-            member
-        )
-
-        # marital_unit_id = "{}'s marital unit".format(member)
-        # situation_dict["marital_units"][marital_unit_id] = {
-        #     "members": [member]
-        # }
-
-        # if member.startswith("dependent"):
-        #     situation_dict["marital_units"][marital_unit_id]["marital_unit_id"] = {
-        #         TAX_YEAR_STR: dependent_counter
-        #     }
-        #     dependent_counter += 1
-
-    situation_dict["families"]["your family"]["members"] = family_members
-    situation_dict["marital_units"]["your marital unit"] = {
-        "members": marital_unit_members
-    }
-
-    return situation_dict
diff --git a/policyengine_us/data/datasets/puf/__init__.py b/policyengine_us/data/datasets/puf/__init__.py
deleted file mode 100644
index 519ef9fc0e7..00000000000
--- a/policyengine_us/data/datasets/puf/__init__.py
+++ /dev/null
@@ -1,1 +0,0 @@
-from .puf import PUF_2022, PUF_2015
diff --git a/policyengine_us/data/datasets/puf/puf.py b/policyengine_us/data/datasets/puf/puf.py
deleted file mode 100644
index c1475572057..00000000000
--- a/policyengine_us/data/datasets/puf/puf.py
+++ /dev/null
@@ -1,288 +0,0 @@
-from policyengine_core.data import Dataset
-import numpy as np
-from policyengine_us.data.storage import STORAGE_FOLDER
-from tqdm import tqdm
-
-EXTRA_PUF_VARIABLES = [
-    "e02000",
-    "e26270",
-    "e19200",
-    "e18500",
-    "e19800",
-    "e20400",
-    "e20100",
-    "e00700",
-    "e03270",
-    "e24515",
-    "e03300",
-    "e07300",
-    "e62900",
-    "e32800",
-    "e87530",
-    "e03240",
-    "e01100",
-    "e01200",
-    "e24518",
-    "e09900",
-    "e27200",
-    "e03290",
-    "e58990",
-    "e03230",
-    "e07400",
-    "e11200",
-    "e07260",
-    "e07240",
-    "e07600",
-    "e03220",
-    "p08000",
-    "e03400",
-    "e09800",
-    "e09700",
-    "e03500",
-    "e87521",
-]
-
-
-class PUF(Dataset):
-    time_period = None
-    data_format = Dataset.ARRAYS
-
-    def generate(
-        self, puf_file_path: str = None, puf_demographics_path: str = None
-    ):
-        # First pass: single person tax units.
-        from policyengine_us.data.datasets.cps.enhanced_cps.process_puf import (
-            load_puf,
-            impute_missing_demographics,
-            uprate_puf,
-        )
-
-        puf, demographics = load_puf(
-            puf_file_path=puf_file_path, puf_demographics_path=None
-        )
-        puf = uprate_puf(puf, self.time_period)
-        puf = impute_missing_demographics(puf, demographics)
-
-        VARIABLES = [
-            "person_id",
-            "tax_unit_id",
-            "marital_unit_id",
-            "spm_unit_id",
-            "family_id",
-            "household_id",
-            "person_tax_unit_id",
-            "person_marital_unit_id",
-            "person_spm_unit_id",
-            "person_family_id",
-            "person_household_id",
-            "age",
-            "employment_income",
-            "household_weight",
-            "is_male",
-        ] + list(FINANCE_VARIABLE_RENAMES.values())
-
-        self.holder = {variable: [] for variable in VARIABLES}
-
-        i = 0
-        for _, row in tqdm(puf.iterrows(), total=len(puf)):
-            i += 1
-            tax_unit_id = i
-            self.add_tax_unit(row, tax_unit_id)
-            self.add_filer(row, tax_unit_id)
-            if row["marital_filing_status"] == 2:
-                self.add_spouse(row, tax_unit_id)
-
-            DEPENDENT_COLUMNS = [
-                "exemptions_children_living_at_home",
-                "exemptions_children_living_away_from_home",
-                "exemptions_other_dependents",
-            ]
-            count_dependents = int(sum(row[dep] for dep in DEPENDENT_COLUMNS))
-
-            for j in range(count_dependents):
-                self.add_dependent(row, tax_unit_id, j)
-
-        groups_assumed_to_be_tax_unit_like = [
-            "family",
-            "spm_unit",
-            "household",
-        ]
-
-        for group in groups_assumed_to_be_tax_unit_like:
-            self.holder[f"{group}_id"] = self.holder["tax_unit_id"]
-            self.holder[f"person_{group}_id"] = self.holder[
-                "person_tax_unit_id"
-            ]
-
-        for key in self.holder:
-            self.holder[key] = np.array(self.holder[key]).astype(float)
-
-        self.save_dataset(self.holder)
-
-    def add_tax_unit(self, row, tax_unit_id):
-        self.holder["tax_unit_id"].append(tax_unit_id)
-
-    def add_filer(self, row, tax_unit_id):
-        person_id = int(tax_unit_id * 1e2 + 1)
-        self.holder["person_id"].append(person_id)
-        self.holder["person_tax_unit_id"].append(tax_unit_id)
-        self.holder["person_marital_unit_id"].append(person_id)
-        self.holder["marital_unit_id"].append(person_id)
-
-        self.holder["age"].append(
-            decode_age_filer(row["age_range_primary_filer"])
-        )
-
-        earnings_split = row["earnings_split_joint_returns"]
-        if earnings_split > 0:
-            SPLIT_DECODES = {
-                1: 0,
-                2: 0.25,
-                3: 0.75,
-                4: 1,
-            }
-            lower = SPLIT_DECODES[earnings_split]
-            upper = SPLIT_DECODES[earnings_split + 1]
-            earnings_percentage = 1 - np.random.uniform(lower, upper)
-        else:
-            earnings_percentage = 1
-
-        self.holder["employment_income"].append(
-            row["employment_income"] * earnings_percentage
-        )
-
-        self.holder["household_weight"].append(row["decimal_weight"])
-
-        self.holder["is_male"].append(row["gender_primary_filer"] == 1)
-
-        for key in FINANCE_VARIABLE_RENAMES:
-            self.holder[FINANCE_VARIABLE_RENAMES[key]].append(row[key])
-
-    def add_spouse(self, row, tax_unit_id):
-        person_id = int(tax_unit_id * 1e2 + 2)
-        self.holder["person_id"].append(person_id)
-        self.holder["person_tax_unit_id"].append(tax_unit_id)
-        self.holder["person_marital_unit_id"].append(person_id - 1)
-
-        self.holder["age"].append(
-            self.holder["age"][-1]
-        )  # Assume same age as filer for now
-
-        earnings_split = row["earnings_split_joint_returns"]
-        if earnings_split > 0:
-            SPLIT_DECODES = {
-                1: 0,
-                2: 0.25,
-                3: 0.75,
-                4: 1,
-            }
-            lower = SPLIT_DECODES[earnings_split]
-            upper = SPLIT_DECODES[earnings_split + 1]
-            earnings_percentage = np.random.uniform(lower, upper)
-        else:
-            earnings_percentage = 0
-
-        self.holder["employment_income"].append(
-            row["employment_income"] * earnings_percentage
-        )
-
-        # 96% of joint filers are opposite-gender
-
-        is_opposite_gender = np.random.uniform() < 0.96
-        opposite_gender_code = 0 if row["gender_primary_filer"] == 1 else 1
-        same_gender_code = 1 - opposite_gender_code
-        self.holder["is_male"].append(
-            opposite_gender_code if is_opposite_gender else same_gender_code
-        )
-
-        for key in FINANCE_VARIABLE_RENAMES:
-            self.holder[FINANCE_VARIABLE_RENAMES[key]].append(0)
-
-    def add_dependent(self, row, tax_unit_id, dependent_id):
-        person_id = int(tax_unit_id * 1e2 + 3 + dependent_id)
-        self.holder["person_id"].append(person_id)
-        self.holder["person_tax_unit_id"].append(tax_unit_id)
-        self.holder["person_marital_unit_id"].append(person_id)
-        self.holder["marital_unit_id"].append(person_id)
-
-        self.holder["age"].append(
-            decode_age_dependent(row[f"age_dependent_{dependent_id + 1}"])
-        )
-        self.holder["employment_income"].append(0)
-
-        for key in FINANCE_VARIABLE_RENAMES:
-            self.holder[FINANCE_VARIABLE_RENAMES[key]].append(0)
-
-        self.holder["is_male"].append(np.random.choice([0, 1]))
-
-
-class PUF_2022(PUF):
-    label = "PUF (2022)"
-    name = "puf_2022"
-    time_period = 2022
-    file_path = STORAGE_FOLDER / "puf_2022.h5"
-    url = "release://policyengine/non-public-microdata/puf-2022/puf_2022.h5"
-
-
-class PUF_2015(PUF):
-    label = "PUF (2015)"
-    name = "puf_2015"
-    time_period = 2015
-    file_path = STORAGE_FOLDER / "puf_2015.h5"
-
-
-FINANCE_VARIABLE_RENAMES = dict(
-    self_employment_income="self_employment_income",
-    taxable_interest_income="taxable_interest_income",
-    tax_exempt_interest_income="tax_exempt_interest_income",
-    qualified_dividend_income="qualified_dividend_income",
-    non_qualified_dividend_income="non_qualified_dividend_income",
-    taxable_pension_income="taxable_pension_income",
-    social_security="social_security",
-    long_term_capital_gains="long_term_capital_gains",
-    short_term_capital_gains="short_term_capital_gains",
-    rental_income="rental_income",
-    farm_rent_income="farm_rent_income",
-    farm_income="farm_income",
-    partnership_s_corp_income="partnership_s_corp_income",
-    schedule_e_net_income_loss="schedule_e_net_income",
-)
-
-FINANCE_VARIABLE_RENAMES.update(
-    {variable: variable for variable in EXTRA_PUF_VARIABLES}
-)
-
-
-def decode_age_filer(age_range):
-    if age_range == 0:
-        return 40
-    AGERANGE_FILER_DECODE = {
-        1: 0,
-        2: 26,
-        3: 35,
-        4: 45,
-        5: 55,
-        6: 65,
-        7: 80,
-    }
-    lower = AGERANGE_FILER_DECODE[age_range]
-    upper = AGERANGE_FILER_DECODE[age_range + 1]
-    return np.random.randint(lower, upper)
-
-
-def decode_age_dependent(age_range):
-    if age_range == 0:
-        return 0
-    AGERANGE_DEPENDENT_DECODE = {
-        0: 0,
-        1: 0,
-        2: 5,
-        3: 13,
-        4: 17,
-        5: 19,
-        6: 25,
-        7: 30,
-    }
-    lower = AGERANGE_DEPENDENT_DECODE[age_range]
-    upper = AGERANGE_DEPENDENT_DECODE[age_range + 1]
-    return np.random.randint(lower, upper)
diff --git a/policyengine_us/data/geography/README.md b/policyengine_us/data/geography/README.md
deleted file mode 100644
index b2c3407d1ca..00000000000
--- a/policyengine_us/data/geography/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# data/geography
-
-This folder contains scripts and datasets used for geographic mappings and imputations.
-
-## `create_zip_code_dataset.py`
-
-This script generates the dataset `zip_codes.csv.gz`, which contains a row for every ZIP code, providing its:
-* ZIP code tabulation area
-* County
-* Population
-* State
-
-The dataset is pre-generated and stored in the repo, so it'd only need to be updated when new ACS 5-year estimates come out.
diff --git a/policyengine_us/data/geography/create_zip_code_dataset.py b/policyengine_us/data/geography/create_zip_code_dataset.py
deleted file mode 100644
index 157d773043a..00000000000
--- a/policyengine_us/data/geography/create_zip_code_dataset.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import pandas as pd
-
-# Per-ZCTA population dataset
-# ACS 5-year estimates, download URL: https://data.census.gov/cedsci/table?q=DP05%3A%20ACS%20DEMOGRAPHIC%20AND%20HOUSING%20ESTIMATES&g=0100000US%248600000&tid=ACSDP5Y2020.DP05
-
-zcta = pd.read_csv(
-    "zcta_2020_population.csv",
-    low_memory=False,
-    usecols=["DP05_0001E", "NAME"],
-    skiprows=1,
-    header=0,
-    names=["population", "zcta"],
-)
-zcta.zcta = zcta.zcta.apply(lambda x: x.split(" ")[1])
-zcta.zcta = zcta.zcta.astype(int)
-zcta.population = zcta.population.astype(int)
-
-# ZCTA-county dataset
-# 2020 ZCTA to County Relationship File, download URL: https://www.census.gov/geographies/reference-files/time-series/geo/relationship-files.html#zcta
-zcta_to_county = pd.read_csv(
-    "zcta_2020_to_county_2020.csv",
-    delimiter="|",
-    usecols=["GEOID_ZCTA5_20", "NAMELSAD_COUNTY_20"],
-    names=["zcta", "county"],
-)
-zcta_to_county = zcta_to_county.dropna()
-zcta_to_county.zcta = zcta_to_county.zcta.astype(int)
-# Some ZCTAs have more than one county - select a random one
-zcta_to_county = zcta_to_county.groupby("zcta").apply(lambda x: x.sample(1))
-
-# ZIP code-ZCTA dataset
-# Download URL: https://udsmapper.org/zip-code-to-zcta-crosswalk/
-zip_code = pd.read_csv(
-    "zip_code_to_zcta.csv",
-    usecols=["ZIP_CODE", "ZCTA", "STATE"],
-    names=["zip_code", "zcta", "state"],
-)
-zip_code.zip_code = zip_code.zip_code.astype(int)
-zip_code = zip_code[zip_code.zcta != "No ZCTA"]
-zip_code.zcta = zip_code.zcta.astype(int)
-zip_code = zip_code[zip_code.zcta.isin(zcta.zcta)]
-zip_code = zip_code[zip_code.zcta.isin(zcta_to_county.zcta)]
-
-# ZCTAs have multiple ZIP codes - split each ZCTA population equally into its component ZIP codes
-zip_code["population"] = (
-    zcta.set_index("zcta").population[zip_code.zcta].values
-    / zip_code.groupby("zcta").zip_code.count()[zip_code.zcta].values
-)
-zip_code["county"] = (
-    zcta_to_county.set_index("zcta").county[zip_code.zcta].values
-)
-zip_code.to_csv("zip_codes.csv", compression="gzip")
diff --git a/policyengine_us/data/geography/zip_codes.csv.gz b/policyengine_us/data/geography/zip_codes.csv.gz
deleted file mode 100644
index 2007b6edaab..00000000000
Binary files a/policyengine_us/data/geography/zip_codes.csv.gz and /dev/null differ
diff --git a/policyengine_us/data/storage/README.md b/policyengine_us/data/storage/README.md
deleted file mode 100644
index d924c05f24b..00000000000
--- a/policyengine_us/data/storage/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# PolicyEngine US microdata
-
-This folder will contain microdata files (.h5) to support PolicyEngine US's microsimulation runs.
diff --git a/policyengine_us/data/storage/__init__.py b/policyengine_us/data/storage/__init__.py
deleted file mode 100644
index 111a9735fd6..00000000000
--- a/policyengine_us/data/storage/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from pathlib import Path
-
-STORAGE_FOLDER = Path(__file__).parent
diff --git a/policyengine_us/data/parameters/README.md b/policyengine_us/params_on_demand/README.md
similarity index 100%
rename from policyengine_us/data/parameters/README.md
rename to policyengine_us/params_on_demand/README.md
diff --git a/policyengine_us/data/parameters/gov/aca/ca/gra.yaml b/policyengine_us/params_on_demand/gov/aca/ca/gra.yaml
similarity index 100%
rename from policyengine_us/data/parameters/gov/aca/ca/gra.yaml
rename to policyengine_us/params_on_demand/gov/aca/ca/gra.yaml
diff --git a/policyengine_us/data/parameters/gov/aca/ca/slspc.yaml b/policyengine_us/params_on_demand/gov/aca/ca/slspc.yaml
similarity index 100%
rename from policyengine_us/data/parameters/gov/aca/ca/slspc.yaml
rename to policyengine_us/params_on_demand/gov/aca/ca/slspc.yaml
diff --git a/policyengine_us/data/parameters/gov/hhs/medicaid/geography/medicaid_rating_area.yaml b/policyengine_us/params_on_demand/gov/hhs/medicaid/geography/medicaid_rating_area.yaml
similarity index 100%
rename from policyengine_us/data/parameters/gov/hhs/medicaid/geography/medicaid_rating_area.yaml
rename to policyengine_us/params_on_demand/gov/hhs/medicaid/geography/medicaid_rating_area.yaml
diff --git a/policyengine_us/data/parameters/gov/hhs/medicaid/geography/second_lowest_silver_plan_cost.yaml b/policyengine_us/params_on_demand/gov/hhs/medicaid/geography/second_lowest_silver_plan_cost.yaml
similarity index 100%
rename from policyengine_us/data/parameters/gov/hhs/medicaid/geography/second_lowest_silver_plan_cost.yaml
rename to policyengine_us/params_on_demand/gov/hhs/medicaid/geography/second_lowest_silver_plan_cost.yaml
diff --git a/policyengine_us/system.py b/policyengine_us/system.py
index b7d618ed067..cb3b7f7dd40 100644
--- a/policyengine_us/system.py
+++ b/policyengine_us/system.py
@@ -9,7 +9,6 @@
     Microsimulation as CoreMicrosimulation,
     IndividualSim as CoreIndividualSim,
 )
-from policyengine_us_data import DATASETS, CPS_2024
 from policyengine_us.variables.household.demographic.geographic.state.in_state import (
     create_50_state_variables,
 )
@@ -28,6 +27,7 @@
     uprate_parameters,
 )
 from .tools.default_uprating import add_default_uprating
+from policyengine_us_data import DATASETS, CPS_2024
 
 COUNTRY_DIR = Path(__file__).parent
 
@@ -189,7 +189,6 @@ class IndividualSim(CoreIndividualSim):  # Deprecated
     tax_benefit_system = CountryTaxBenefitSystem
     entities = {entity.key: entity for entity in entities}
     default_dataset = CPS_2024
-
     default_roles = dict(
         tax_unit="member",
         spm_unit="member",
diff --git a/policyengine_us/tools/general.py b/policyengine_us/tools/general.py
index 113c2322df3..84701fc131b 100644
--- a/policyengine_us/tools/general.py
+++ b/policyengine_us/tools/general.py
@@ -5,12 +5,6 @@
 import pandas as pd
 from policyengine_us.typing import Formula
 
-ZIP_CODE_DATASET_PATH = (
-    Path(__file__).parent.parent / "data" / "geography" / "zip_codes.csv.gz"
-)
-
-ZIP_CODE_DATASET = pd.read_csv(ZIP_CODE_DATASET_PATH, compression="gzip")
-
 USD = "currency-USD"
 
 
diff --git a/policyengine_us/variables/gov/aca/slspc/person_aca_slspc_ca.py b/policyengine_us/variables/gov/aca/slspc/person_aca_slspc_ca.py
index b6903aeb519..076795e146c 100644
--- a/policyengine_us/variables/gov/aca/slspc/person_aca_slspc_ca.py
+++ b/policyengine_us/variables/gov/aca/slspc/person_aca_slspc_ca.py
@@ -17,12 +17,7 @@ def formula(person, period, parameters):
         ptree = person.simulation.tax_benefit_system.parameters
         if not hasattr(ptree.gov.aca, "ca_"):
             aca_params = ParameterNode(
-                directory_path=REPO
-                / "data"
-                / "parameters"
-                / "gov"
-                / "aca"
-                / "ca"
+                directory_path=REPO / "params_on_demand" / "gov" / "aca" / "ca"
             )
             aca_params = homogenize_parameter_structures(
                 aca_params,
diff --git a/policyengine_us/variables/household/demographic/geographic/county/county.py b/policyengine_us/variables/household/demographic/geographic/county/county.py
index 596449de9dd..592138bb4fc 100644
--- a/policyengine_us/variables/household/demographic/geographic/county/county.py
+++ b/policyengine_us/variables/household/demographic/geographic/county/county.py
@@ -2,6 +2,7 @@
 from policyengine_us.variables.household.demographic.geographic.county.county_enum import (
     County,
 )
+from policyengine_us_data import ZIP_CODE_DATASET
 
 
 class county(Variable):
diff --git a/policyengine_us/variables/household/demographic/geographic/slspc.py b/policyengine_us/variables/household/demographic/geographic/slspc.py
index 98bea788d36..42dba8c358c 100644
--- a/policyengine_us/variables/household/demographic/geographic/slspc.py
+++ b/policyengine_us/variables/household/demographic/geographic/slspc.py
@@ -27,8 +27,7 @@ def formula(tax_unit, period, parameters):
         if not hasattr(parameter_tree.gov.hhs.medicaid, "geography"):
             medicaid_parameters = ParameterNode(
                 directory_path=REPO
-                / "data"
-                / "parameters"
+                / "params_on_demand"
                 / "gov"
                 / "hhs"
                 / "medicaid"
diff --git a/policyengine_us/variables/household/demographic/geographic/zip_code/zip_code.py b/policyengine_us/variables/household/demographic/geographic/zip_code/zip_code.py
index 1b933485460..d8f51e16c22 100644
--- a/policyengine_us/variables/household/demographic/geographic/zip_code/zip_code.py
+++ b/policyengine_us/variables/household/demographic/geographic/zip_code/zip_code.py
@@ -1,4 +1,5 @@
 from policyengine_us.model_api import *
+from policyengine_us_data import ZIP_CODE_DATASET
 
 
 class zip_code(Variable):
diff --git a/policyengine_us/variables/input/geography.py b/policyengine_us/variables/input/geography.py
index 2c676b5360e..a47696a0cf0 100644
--- a/policyengine_us/variables/input/geography.py
+++ b/policyengine_us/variables/input/geography.py
@@ -100,8 +100,7 @@ def formula(household, period, parameters):
         if not hasattr(parameter_tree.gov.hhs.medicaid, "geography"):
             medicaid_parameters = ParameterNode(
                 directory_path=REPO
-                / "data"
-                / "parameters"
+                / "params_on_demand"
                 / "gov"
                 / "hhs"
                 / "medicaid"
diff --git a/setup.py b/setup.py
index 45c62488cfd..4b6fbcf8719 100644
--- a/setup.py
+++ b/setup.py
@@ -35,13 +35,15 @@
     ],
     install_requires=[
         "policyengine-core>=3.6.5",
-        "policyengine-us-data>=1.1.0",
+        "policyengine-us-data>=1.4.1",
         "microdf-python",
         "tqdm",
     ],
     extras_require={
         "dev": [
             "setuptools",
+            "jupyter-book",
+            "furo",
         ],
     },
     python_requires=">=3.10,<3.13",


Test patch:
diff --git a/policyengine_us/tests/microsimulation/calibration/test_calibration.py b/policyengine_us/tests/microsimulation/calibration/test_calibration.py
index 6b6d6e37415..a97a394deee 100644
--- a/policyengine_us/tests/microsimulation/calibration/test_calibration.py
+++ b/policyengine_us/tests/microsimulation/calibration/test_calibration.py
@@ -17,7 +17,7 @@
     [(dataset, year, limit) for dataset, year, limit in LIMITS],
 )
 def test_calibration(dataset, year, limit):
-    from policyengine_us.data.datasets.cps.enhanced_cps.calibrate import (
+    from policyengine_us_data.datasets.cps.enhanced_cps.calibrate import (
         get_snapshot,
     )
 
diff --git a/policyengine_us/tests/microsimulation/data/cps/test_cps.py b/policyengine_us/tests/microsimulation/data/cps/test_cps.py
index 0af389dd1d0..ab64a9c446d 100644
--- a/policyengine_us/tests/microsimulation/data/cps/test_cps.py
+++ b/policyengine_us/tests/microsimulation/data/cps/test_cps.py
@@ -1,4 +1,4 @@
-from policyengine_us.data import CPS_2021, CPS_2022
+from policyengine_us_data import CPS_2021, CPS_2022
 import pytest
 from policyengine_us import Microsimulation
 import warnings
diff --git a/policyengine_us/tests/microsimulation/test_against_taxsim.py b/policyengine_us/tests/microsimulation/test_against_taxsim.py
index b350dc64704..e8495bfa3c6 100644
--- a/policyengine_us/tests/microsimulation/test_against_taxsim.py
+++ b/policyengine_us/tests/microsimulation/test_against_taxsim.py
@@ -1,6 +1,6 @@
 import os
 from policyengine_us import Microsimulation
-from policyengine_us.data import CPS_2022
+from policyengine_us_data import CPS_2022
 from policyengine_us.tools.taxsim.generate_taxsim_tests import TaxSim35
 import numpy as np
 import pytest
diff --git a/policyengine_us/tools/taxsim/generate_taxsim_tests.py b/policyengine_us/tools/taxsim/generate_taxsim_tests.py
index 17c33d0a0c3..2df9e73c570 100644
--- a/policyengine_us/tools/taxsim/generate_taxsim_tests.py
+++ b/policyengine_us/tools/taxsim/generate_taxsim_tests.py
@@ -5,7 +5,7 @@
 import subprocess
 import stat
 from io import StringIO
-from policyengine_us_data import CPS
+from policyengine_us_data import CPS_2024
 from policyengine_core.data import Dataset
 from policyengine_core.taxbenefitsystems import TaxBenefitSystem
 from policyengine_us import Microsimulation
@@ -300,6 +300,6 @@ def generate_from_microsimulation(
     taxsim = TaxSim35()
     taxsim.OUTPUT_VARIABLES = args.outputs
     result = taxsim.generate_from_microsimulation(
-        CPS, args.year, number=args.num
+        CPS_2024, args.year, number=args.num
     )
     print(result)