NEWS


uscogdata 0.4.0

Cohorts can be named by predicate, not just by id

cog_spending(), cog_revenue() and cog_balances() gain optional state and type arguments. Both default to NULL, so every existing call behaves exactly as before.

Passing them expresses the cohort as a subquery against canonical_fips_xwalk inside each statement, instead of round-tripping the ids through R and rendering them back into a literal IN list:

# before: resolve 20,106 ids in R, then embed them in every statement
ids <- cog_gov_search(NULL, state = "CA", type = "city")$canonical_govid
cog_spending(ids, years = 2022)

# now: the cohort never leaves the database
cog_spending(years = 2022, state = "CA", type = "city")

Measured against the production corpus, same FY2022 aggregate over the 20,106-government type = "city" cohort:

| cohort expressed as | time | |---|---:| | IN (20,106 literals) | 449 ms | | join against a temp cohort table | 99 ms | | predicate on canonical_fips_xwalk | 94 ms | | no cohort filter at all (the floor) | 88 ms |

4.8x, within 7% of the floor. The rendered IN list was 301,591 characters and was re-parsed in 5-8 separate statements per call, so the cost was paid repeatedly; the predicate's size is constant in the cohort.

state and type use the same vocabulary and the same internal coercion as cog_gov_search() -- state is a postal abbreviation ("WI") even though the crosswalk column holds a FIPS code ("55").

Supplying govid and state/type intersects them: the governments in govid that also match the predicate. Naming no cohort at all now aborts with class uscogdata_no_cohort rather than R's "argument is missing" error.

When the cohort is named by predicate there is no id list to report, so provenance$scope$govids_found/govids_missing are empty and provenance$scope$cohort carries state, type and n_governments instead. A govid-named cohort's provenance is unchanged.

cog_gov_search() and cog_balances() gain limit/offset

Pagination arrived on cog_spending()/cog_revenue() in 0.3.0; the other two verbs were left materializing everything and slicing in R. Both now take limit/offset with the same semantics: NULL default, the page applied in SQL behind a deterministic ORDER BY, and the unpaginated count returned as a total_rows attribute computed by COUNT(*) OVER() in the same scan rather than a second query.

cog_gov_search() had no LIMIT at all, which made it the one verb that returns the entire 40,336-row crosswalk when called with no filter.

Two refusals rather than silent surprises:

DuckDB's resource budget is configurable

USCOGDATA_DUCKDB_THREADS and USCOGDATA_DUCKDB_MEMORY_LIMIT (with matching options(uscogdata.duckdb_threads = ) / options(uscogdata.duckdb_memory_limit = ) spellings) cap the DuckDB connection the package opens. Both follow the same env-var > option > default precedence as USCOGDATA_URL.

Unset, no pragma is issued at all and DuckDB's own defaults apply exactly as before -- every visible core. That is right for one interactive session on a dedicated machine and wrong for a server: where several readers share a host, each otherwise claims the whole machine and they contend. Capping measured ~5% on a single-government all-years query (502 ms at 2 threads vs 475 ms uncapped on 16 cores), which is cheap enough that a server should always cap.

This replaces a workaround in which a consumer reached into the package namespace at boot -- getFromNamespace(".ensure_session", "uscogdata")() followed by a manual SET threads -- depending both on a private name and on the session already being open.

Documentation: the corpus-access table is re-measured and honest

The README's "two ways to read the corpus" table carried figures taken before the corpus was re-chunked into row groups (cog_pipeline#93, published 2026-08-09) and reported the mirrored column as "local speed" with no number at all. Re-measured 2026-08-10 against the published corpus (pipeline_commit 3d28ddd), fresh R session per arm:

Fixes

uscogdata 0.3.0

First public release.

uscogdata provides curated R verbs over the Civilytics US Census of Governments finance corpus: unit-level financial profiles, geographic rollups and peer comparisons, with auditable provenance on every result.

What it covers

Government types 0-3 (state, county, municipality, township), FY1967-FY2024 -- 56 fiscal years, 46,148,034 rows, 190.6 MB. There is no source data for FY1968 or FY1969. Special districts (type 4) and school districts (type 5) are out of scope pending validation.

The verbs

cog_spending(), cog_revenue() and cog_balances() for flows and holdings; cog_gov_search() to resolve place names (including basket mode for many at once); cog_find_peers() and cog_peer_compare() for cohorts; cog_geographic_rollup() for aggregates; cog_categories(), cog_recipes(), cog_manifest() and cog_explain() for metadata and provenance; and cog_mirror() for a local copy of the corpus.

Reading the corpus now works out of the box

Four things to know before your first query

Known limits

uscogdata 0.2.0

New features

Documentation