docs: drop the subtype argument from cog_balances()
balance is the only category_type whose subtype column is not orthogonal to
category. Measured against the crosswalk: 5 of 6 expenditure subtypes and 1 of
7 revenue subtypes span more than one category, but 0 of 5 balance subtypes do.
Balance is a strict tree -- Fund Balances = {general}, Retirement System
Holdings = {employee_retirement}, Insurance Trust Balances = the three trust
subtypes.
Exposing both arguments would admit no useful combination: of the 15 pairs, 3
are redundant and 12 are guaranteed empty for every government in every year,
failing as an empty tibble that reads as "holds none" rather than as a
contradiction.
Dropping it also keeps the verb aligned -- no uscogdata verb exposes a subtype
argument; the API layers its own subtype row filter on top, which cog-api#26
can do for /balances. #25's one-filter requirement is still met, since
category = "Fund Balances" is exactly W01/W31/W61.
Adds two tests: that one-filter equivalence, and an assertion that the
subtype -> category tree holds, so an upstream change making category lossy
fails here rather than in a user's analysis.
This commit is contained in:
@@ -84,9 +84,6 @@ see Out of scope.)
|
|||||||
|
|
||||||
```r
|
```r
|
||||||
cog_balances(govid, years,
|
cog_balances(govid, years,
|
||||||
subtype = NULL, # general | employee_retirement |
|
|
||||||
# unemployment_trust | workers_comp_trust |
|
|
||||||
# other_insurance_trust
|
|
||||||
category = NULL, # Fund Balances | Insurance Trust Balances |
|
category = NULL, # Fund Balances | Insurance Trust Balances |
|
||||||
# Retirement System Holdings
|
# Retirement System Holdings
|
||||||
per_capita = FALSE,
|
per_capita = FALSE,
|
||||||
@@ -96,7 +93,8 @@ cog_balances(govid, years,
|
|||||||
|
|
||||||
Returns a `tbl_df` with a `provenance` attribute, like every other verb.
|
Returns a `tbl_df` with a `provenance` attribute, like every other verb.
|
||||||
|
|
||||||
**Absent by design:** `expenditure_concept`, `revenue_concept`, `complete`.
|
**Absent by design:** `expenditure_concept`, `revenue_concept`, `complete`,
|
||||||
|
and `subtype` — see below.
|
||||||
|
|
||||||
**`per_capita` is offered.** Holdings per resident is a real measure (pension
|
**`per_capita` is offered.** Holdings per resident is a real measure (pension
|
||||||
assets per capita, fund balance per resident). The roxygen `@param` states
|
assets per capita, fund balance per resident). The roxygen `@param` states
|
||||||
@@ -110,6 +108,47 @@ with the money verbs (the API would otherwise special-case), and
|
|||||||
|
|
||||||
**`recipe` is deliberately omitted from v1.** See below.
|
**`recipe` is deliberately omitted from v1.** See below.
|
||||||
|
|
||||||
|
### No `subtype` argument: `category` is a strict coarsening
|
||||||
|
|
||||||
|
`balance` is the only `category_type` in which `category` and the subtype column
|
||||||
|
are **not** orthogonal. Measured against the published crosswalk:
|
||||||
|
|
||||||
|
| `category_type` | subtypes spanning more than one category |
|
||||||
|
|---|---|
|
||||||
|
| expenditure | 5 of 6 (`operations`, `capital`, `interest`, `assistance`, `intergovernmental`) |
|
||||||
|
| revenue | 1 of 7 (`own_source`) |
|
||||||
|
| **balance** | **0 of 5** |
|
||||||
|
|
||||||
|
For expenditure the two axes are a genuine cross-tab — *function* (Police, Fire)
|
||||||
|
× *economic character* (operations, capital) — so both earn their place. For
|
||||||
|
balance the relation is a strict tree:
|
||||||
|
|
||||||
|
```
|
||||||
|
Fund Balances = {general} W01 W31 W61
|
||||||
|
Retirement System Holdings = {employee_retirement} X21 X30 X42 X44 X47 Z77 Z78
|
||||||
|
Insurance Trust Balances = {unemployment_trust,
|
||||||
|
workers_comp_trust,
|
||||||
|
other_insurance_trust} Y07 Y08 Y21 Y61
|
||||||
|
```
|
||||||
|
|
||||||
|
Exposing both would therefore admit no useful combination. Of the 15 possible
|
||||||
|
pairs, 3 are redundant (the subtype already implies its category) and **12 are
|
||||||
|
guaranteed empty for every government in every year** — and an impossible query
|
||||||
|
would fail by returning an empty tibble, which reads as "this government holds
|
||||||
|
none" rather than "you asked a contradiction."
|
||||||
|
|
||||||
|
Dropping `subtype` also keeps the verb aligned with the rest of the package: no
|
||||||
|
uscogdata verb exposes a subtype argument. `subtype_col` is internal plumbing in
|
||||||
|
`.verb_spendrev()`, and the API layers its own `subtype` row filter on top
|
||||||
|
(`api/R/handlers_governments.R`). `cog-api#26` can do exactly that for
|
||||||
|
`/balances`.
|
||||||
|
|
||||||
|
`#25`'s hard requirement is still met — `category = "Fund Balances"` *is* the
|
||||||
|
`general` family, precisely `W01`/`W31`/`W61`, in one filter. The only loss is
|
||||||
|
isolating one of the three insurance funds in a single argument;
|
||||||
|
`balance_subtype` remains a returned column, so that is one `dplyr::filter()`
|
||||||
|
away.
|
||||||
|
|
||||||
## Decision point: `recipe=` deferred to v2
|
## Decision point: `recipe=` deferred to v2
|
||||||
|
|
||||||
The two holdings recipes are unusable from this verb today, and shipping the
|
The two holdings recipes are unusable from this verb today, and shipping the
|
||||||
@@ -168,6 +207,13 @@ throughout — so every test below runs offline.
|
|||||||
measured table above; the FY2002 valuation caveat fires only when the year
|
measured table above; the FY2002 valuation caveat fires only when the year
|
||||||
range crosses 2002 *and* touches `employee_retirement`.
|
range crosses 2002 *and* touches `employee_retirement`.
|
||||||
- **`per_capita`.** `amt_per_capita_nominal == amt_nominal / population`.
|
- **`per_capita`.** `amt_per_capita_nominal == amt_nominal / population`.
|
||||||
|
- **`category = "Fund Balances"` is the `general` family.** Returns exactly
|
||||||
|
`W01`/`W31`/`W61` and nothing else — `#25`'s one-filter requirement, asserted
|
||||||
|
rather than assumed.
|
||||||
|
- **The hierarchy holds.** Every `balance_subtype` in the crosswalk maps to
|
||||||
|
exactly one `category`. Asserted against the crosswalk so that an upstream
|
||||||
|
change breaking the tree — which would silently make `category` lossy —
|
||||||
|
fails here rather than in a user's analysis.
|
||||||
- **`recipe` rejection.** Passing `recipe` errors with a message naming the
|
- **`recipe` rejection.** Passing `recipe` errors with a message naming the
|
||||||
blocking issue.
|
blocking issue.
|
||||||
- **Gating.** `.require_balance_support()` errors cleanly on a corpus whose
|
- **Gating.** `.require_balance_support()` errors cleanly on a corpus whose
|
||||||
|
|||||||
Reference in New Issue
Block a user