docs: roxygen, NEWS, and pkgdown for basket mode
Adds full @description, @details (algorithm), @examples on cog_gov_search(); examples on cog_basket_resolution() / cog_basket_unresolved(); NEWS.md entry covering the new mode and the pattern->name rename; pkgdown reference entries for the two new exports.
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
# uscogdata 0.1.0 (development)
|
||||||
|
|
||||||
|
## New features
|
||||||
|
|
||||||
|
* `cog_gov_search()` gains a **basket mode**: passing vector `name`
|
||||||
|
/ `state` / `type` arguments resolves multiple place names in one
|
||||||
|
call and returns a tibble of canonical rows in input order, ready
|
||||||
|
to pipe into `cog_spending()` / `cog_revenue()`. Per-row resolution
|
||||||
|
follows an exact-then-substring matching algorithm with deterministic
|
||||||
|
disambiguation; ambiguous and missing entries are surfaced via a
|
||||||
|
sidecar audit tibble plus a single console summary message.
|
||||||
|
* New exports `cog_basket_resolution()` and `cog_basket_unresolved()`
|
||||||
|
expose the basket sidecar for iterative query refinement.
|
||||||
|
|
||||||
|
## Breaking changes
|
||||||
|
|
||||||
|
* The first formal of `cog_gov_search()` was renamed from `pattern`
|
||||||
|
to `name`. All existing call sites in `cog_explorer/` and the
|
||||||
|
package itself use positional first-arg, so this rename is
|
||||||
|
non-breaking in practice. Callers that pass `pattern = ...` by name
|
||||||
|
must update to `name = ...`.
|
||||||
+17
@@ -86,6 +86,15 @@
|
|||||||
#' list-column (full-schema match candidates per input row). Default
|
#' list-column (full-schema match candidates per input row). Default
|
||||||
#' `FALSE`.
|
#' `FALSE`.
|
||||||
#' @return A tibble with the resolution audit trail.
|
#' @return A tibble with the resolution audit trail.
|
||||||
|
#' @examples
|
||||||
|
#' \dontrun{
|
||||||
|
#' basket <- cog_gov_search(
|
||||||
|
#' name = c("Broward", "San Diego", "Notarealplace"),
|
||||||
|
#' state = c("FL", "CA", "NY")
|
||||||
|
#' )
|
||||||
|
#' cog_basket_resolution(basket)
|
||||||
|
#' cog_basket_resolution(basket, expand_candidates = TRUE)
|
||||||
|
#' }
|
||||||
#' @export
|
#' @export
|
||||||
cog_basket_resolution <- function(x, expand_candidates = FALSE) {
|
cog_basket_resolution <- function(x, expand_candidates = FALSE) {
|
||||||
res <- attr(x, "resolution")
|
res <- attr(x, "resolution")
|
||||||
@@ -111,6 +120,14 @@ cog_basket_resolution <- function(x, expand_candidates = FALSE) {
|
|||||||
#'
|
#'
|
||||||
#' @param x A tibble returned by basket-mode [cog_gov_search()].
|
#' @param x A tibble returned by basket-mode [cog_gov_search()].
|
||||||
#' @return A tibble (subset of [cog_basket_resolution()]).
|
#' @return A tibble (subset of [cog_basket_resolution()]).
|
||||||
|
#' @examples
|
||||||
|
#' \dontrun{
|
||||||
|
#' basket <- cog_gov_search(
|
||||||
|
#' name = c("Broward", "San Diego", "Notarealplace"),
|
||||||
|
#' state = c("FL", "CA", "NY")
|
||||||
|
#' )
|
||||||
|
#' cog_basket_unresolved(basket)
|
||||||
|
#' }
|
||||||
#' @export
|
#' @export
|
||||||
cog_basket_unresolved <- function(x) {
|
cog_basket_unresolved <- function(x) {
|
||||||
res <- cog_basket_resolution(x, expand_candidates = TRUE)
|
res <- cog_basket_resolution(x, expand_candidates = TRUE)
|
||||||
|
|||||||
+70
-18
@@ -2,29 +2,81 @@
|
|||||||
|
|
||||||
#' Search for governments by name, state, and/or type
|
#' Search for governments by name, state, and/or type
|
||||||
#'
|
#'
|
||||||
#' Two modes:
|
#' Resolves human-readable place names into rows of `canonical_fips_xwalk`,
|
||||||
|
#' the cross-vintage canonical-government registry. Operates in two modes:
|
||||||
#'
|
#'
|
||||||
#' * **Utility mode** (single `name`): returns all rows from
|
#' * **Utility mode** (single `name`, the original behavior): returns all
|
||||||
#' `canonical_fips_xwalk` whose `gov_name` matches the regex
|
#' rows whose `gov_name` matches the regex case-insensitively, sorted by
|
||||||
#' case-insensitively, sorted by `population_acs` descending.
|
#' `population_acs` descending. Useful for exploratory lookups.
|
||||||
#' * **Basket mode** (`length(name) > 1`): resolves each input row to a
|
#' * **Basket mode** (`length(name) > 1`): resolves each input row to a
|
||||||
#' single canonical govid via exact-then-substring matching with
|
#' single canonical govid and returns a tibble in input order, suitable
|
||||||
#' deterministic disambiguation. Returns up to `length(name)` rows in
|
#' for piping straight into [cog_spending()] / [cog_revenue()] /
|
||||||
#' input order plus a `"resolution"` sidecar attribute. See
|
#' [cog_geographic_rollup()]. Carries an audit sidecar accessible via
|
||||||
#' [cog_basket_resolution()].
|
#' [cog_basket_resolution()] / [cog_basket_unresolved()].
|
||||||
|
#'
|
||||||
|
#' @details
|
||||||
|
#' **Basket-mode resolution algorithm** (per input row):
|
||||||
|
#' 1. Filter `canonical_fips_xwalk` by `state` and (if non-NA) `type`.
|
||||||
|
#' 2. **Exact pass:** case-insensitive equality against `gov_name`.
|
||||||
|
#' Single hit -> resolved. Multiple -> step 4.
|
||||||
|
#' 3. **Substring fallback:** case-insensitive regex against `gov_name`.
|
||||||
|
#' Single hit -> resolved (`match_method = "substring"`). Zero hits ->
|
||||||
|
#' `status = "no_match"`. Multiple hits -> step 4.
|
||||||
|
#' 4. **Disambiguation:** if matches share one `govs_type`, pick the
|
||||||
|
#' largest-population row (`status = "largest_pop"`). If they span >=2
|
||||||
|
#' types, no row is added (`status = "ambiguous"`); the user should
|
||||||
|
#' re-run with `type` specified.
|
||||||
|
#'
|
||||||
|
#' Resolved rows form the returned tibble in input order. Unresolved
|
||||||
|
#' inputs (`ambiguous` / `no_match`) appear only in the sidecar.
|
||||||
#'
|
#'
|
||||||
#' @param name Character vector of place name(s). Length 1 = utility mode;
|
#' @param name Character vector of place name(s). Length 1 = utility mode;
|
||||||
#' length >1 = basket mode.
|
#' length >1 = basket mode.
|
||||||
#' @param state Either a 2-letter USPS abbreviation, a FIPS integer, or
|
#' @param state 2-letter USPS abbreviation (e.g. `"FL"`), FIPS integer
|
||||||
#' `NULL`. Length 1 recycles across all entries in basket mode.
|
#' (e.g. `12`), or `NULL`. In basket mode, length 1 recycles across
|
||||||
#' @param type Government type: an integer in `0:3` or one of `"state"`,
|
#' all entries; otherwise must match `length(name)`.
|
||||||
#' `"county"`, `"city"`, `"township"`, or `NA` (per-row optional in
|
#' @param type Government type: integer in `0:3` or one of `"state"`,
|
||||||
#' basket mode). Passing `4`, `5`, `"special_district"`, or
|
#' `"county"`, `"city"`, `"township"`, or `NA`/`NULL`. Per-row optional
|
||||||
#' `"school_district"` emits an explanatory message and returns an
|
#' in basket mode (recycles from length 1). Excluded types `4`/`5` (or
|
||||||
#' empty tibble (v0.1 corpus excludes those types).
|
#' `"special_district"` / `"school_district"`) trigger an explanatory
|
||||||
#' @return Tibble from `canonical_fips_xwalk`. In utility mode, sorted by
|
#' message and an empty result.
|
||||||
#' `population_acs` descending (`NULL`s last). In basket mode, in input
|
#' @return A tibble of `canonical_fips_xwalk` rows. In utility mode, all
|
||||||
#' order, with `attr(result, "resolution")` set to the sidecar tibble.
|
#' matches sorted by `population_acs` desc. In basket mode, resolved
|
||||||
|
#' rows in input order, with `attr(., "resolution")` set to the
|
||||||
|
#' sidecar tibble.
|
||||||
|
#' @seealso [cog_basket_resolution()], [cog_basket_unresolved()],
|
||||||
|
#' [cog_spending()], [cog_revenue()].
|
||||||
|
#' @examples
|
||||||
|
#' \dontrun{
|
||||||
|
#' # Utility mode — exploratory regex lookup
|
||||||
|
#' cog_gov_search("broward", state = "FL")
|
||||||
|
#'
|
||||||
|
#' # Basket mode — resolve a known cohort
|
||||||
|
#' basket <- cog_gov_search(
|
||||||
|
#' name = c("BROWARD COUNTY", "SAN DIEGO CITY", "AUSTIN CITY"),
|
||||||
|
#' state = c("FL", "CA", "TX")
|
||||||
|
#' )
|
||||||
|
#' basket
|
||||||
|
#'
|
||||||
|
#' # Inspect resolution audit
|
||||||
|
#' cog_basket_resolution(basket)
|
||||||
|
#'
|
||||||
|
#' # Pipe into a spending query
|
||||||
|
#' library(dplyr)
|
||||||
|
#' basket |> cog_spending(years = 2019:2020, category = "Police")
|
||||||
|
#'
|
||||||
|
#' # Iteratively refine ambiguous matches
|
||||||
|
#' partial <- cog_gov_search(
|
||||||
|
#' name = c("Broward", "San Diego"), # San Diego is ambiguous
|
||||||
|
#' state = c("FL", "CA")
|
||||||
|
#' )
|
||||||
|
#' cog_basket_unresolved(partial)
|
||||||
|
#' refined <- cog_gov_search(
|
||||||
|
#' name = c("Broward", "San Diego"),
|
||||||
|
#' state = c("FL", "CA"),
|
||||||
|
#' type = c(NA, "city") # disambiguate
|
||||||
|
#' )
|
||||||
|
#' }
|
||||||
#' @export
|
#' @export
|
||||||
cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
|
cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
|
||||||
if (!is.null(type) && length(type) == 1L && .is_excluded_type(type)) {
|
if (!is.null(type) && length(type) == 1L && .is_excluded_type(type)) {
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ template:
|
|||||||
bootstrap: 5
|
bootstrap: 5
|
||||||
|
|
||||||
reference:
|
reference:
|
||||||
|
- title: Search & basket
|
||||||
|
desc: Resolve place names into canonical govids.
|
||||||
|
contents:
|
||||||
|
- cog_gov_search
|
||||||
|
- cog_basket_resolution
|
||||||
|
- cog_basket_unresolved
|
||||||
- title: Session
|
- title: Session
|
||||||
contents:
|
contents:
|
||||||
- has_keyword("internal")
|
- has_keyword("internal")
|
||||||
|
|||||||
@@ -23,3 +23,13 @@ Returns the resolution tibble attached to a basket-mode result of
|
|||||||
the `candidates` list-column is dropped for readable printing; pass
|
the `candidates` list-column is dropped for readable printing; pass
|
||||||
`expand_candidates = TRUE` to keep it.
|
`expand_candidates = TRUE` to keep it.
|
||||||
}
|
}
|
||||||
|
\examples{
|
||||||
|
\dontrun{
|
||||||
|
basket <- cog_gov_search(
|
||||||
|
name = c("Broward", "San Diego", "Notarealplace"),
|
||||||
|
state = c("FL", "CA", "NY")
|
||||||
|
)
|
||||||
|
cog_basket_resolution(basket)
|
||||||
|
cog_basket_resolution(basket, expand_candidates = TRUE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -18,3 +18,12 @@ Convenience wrapper that returns just the rows where `status` is
|
|||||||
refine before piping into a query verb. The `candidates` list-column
|
refine before piping into a query verb. The `candidates` list-column
|
||||||
is preserved so the user can drill into ambiguous match sets.
|
is preserved so the user can drill into ambiguous match sets.
|
||||||
}
|
}
|
||||||
|
\examples{
|
||||||
|
\dontrun{
|
||||||
|
basket <- cog_gov_search(
|
||||||
|
name = c("Broward", "San Diego", "Notarealplace"),
|
||||||
|
state = c("FL", "CA", "NY")
|
||||||
|
)
|
||||||
|
cog_basket_unresolved(basket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+78
-14
@@ -7,24 +7,88 @@
|
|||||||
cog_gov_search(name = NULL, state = NULL, type = NULL)
|
cog_gov_search(name = NULL, state = NULL, type = NULL)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
\item{name}{Character regex matched case-insensitively against
|
\item{name}{Character vector of place name(s). Length 1 = utility mode;
|
||||||
`gov_name`. `NULL` (default) means no name filter.}
|
length >1 = basket mode.}
|
||||||
|
|
||||||
\item{state}{Either a 2-letter USPS abbreviation (e.g. `"FL"`), a FIPS
|
\item{state}{2-letter USPS abbreviation (e.g. `"FL"`), FIPS integer
|
||||||
integer (e.g. `12`), or `NULL`.}
|
(e.g. `12`), or `NULL`. In basket mode, length 1 recycles across
|
||||||
|
all entries; otherwise must match `length(name)`.}
|
||||||
|
|
||||||
\item{type}{Government type: an integer in `0:3` or one of `"state"`,
|
\item{type}{Government type: integer in `0:3` or one of `"state"`,
|
||||||
`"county"`, `"city"`, `"township"`. Passing `4`, `5`,
|
`"county"`, `"city"`, `"township"`, or `NA`/`NULL`. Per-row optional
|
||||||
`"special_district"`, or `"school_district"` emits an explanatory
|
in basket mode (recycles from length 1). Excluded types `4`/`5` (or
|
||||||
message and returns an empty tibble (v0.1 corpus excludes those types).}
|
`"special_district"` / `"school_district"`) trigger an explanatory
|
||||||
|
message and an empty result.}
|
||||||
}
|
}
|
||||||
\value{
|
\value{
|
||||||
Tibble from `canonical_fips_xwalk` sorted by `population_acs`
|
A tibble of `canonical_fips_xwalk` rows. In utility mode, all
|
||||||
descending (`NULL`s last).
|
matches sorted by `population_acs` desc. In basket mode, resolved
|
||||||
|
rows in input order, with `attr(., "resolution")` set to the
|
||||||
|
sidecar tibble.
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
Returns rows from `canonical_fips_xwalk` matching the supplied filters.
|
Resolves human-readable place names into rows of `canonical_fips_xwalk`,
|
||||||
Intended as the entry point users call to resolve a human-readable place
|
the cross-vintage canonical-government registry. Operates in two modes:
|
||||||
name into one or more `canonical_govid` values before calling
|
}
|
||||||
[cog_spending()] / [cog_revenue()] / etc.
|
\details{
|
||||||
|
* **Utility mode** (single `name`, the original behavior): returns all
|
||||||
|
rows whose `gov_name` matches the regex case-insensitively, sorted by
|
||||||
|
`population_acs` descending. Useful for exploratory lookups.
|
||||||
|
* **Basket mode** (`length(name) > 1`): resolves each input row to a
|
||||||
|
single canonical govid and returns a tibble in input order, suitable
|
||||||
|
for piping straight into [cog_spending()] / [cog_revenue()] /
|
||||||
|
[cog_geographic_rollup()]. Carries an audit sidecar accessible via
|
||||||
|
[cog_basket_resolution()] / [cog_basket_unresolved()].
|
||||||
|
|
||||||
|
|
||||||
|
**Basket-mode resolution algorithm** (per input row):
|
||||||
|
1. Filter `canonical_fips_xwalk` by `state` and (if non-NA) `type`.
|
||||||
|
2. **Exact pass:** case-insensitive equality against `gov_name`.
|
||||||
|
Single hit -> resolved. Multiple -> step 4.
|
||||||
|
3. **Substring fallback:** case-insensitive regex against `gov_name`.
|
||||||
|
Single hit -> resolved (`match_method = "substring"`). Zero hits ->
|
||||||
|
`status = "no_match"`. Multiple hits -> step 4.
|
||||||
|
4. **Disambiguation:** if matches share one `govs_type`, pick the
|
||||||
|
largest-population row (`status = "largest_pop"`). If they span >=2
|
||||||
|
types, no row is added (`status = "ambiguous"`); the user should
|
||||||
|
re-run with `type` specified.
|
||||||
|
|
||||||
|
Resolved rows form the returned tibble in input order. Unresolved
|
||||||
|
inputs (`ambiguous` / `no_match`) appear only in the sidecar.
|
||||||
|
}
|
||||||
|
\examples{
|
||||||
|
\dontrun{
|
||||||
|
# Utility mode — exploratory regex lookup
|
||||||
|
cog_gov_search("broward", state = "FL")
|
||||||
|
|
||||||
|
# Basket mode — resolve a known cohort
|
||||||
|
basket <- cog_gov_search(
|
||||||
|
name = c("BROWARD COUNTY", "SAN DIEGO CITY", "AUSTIN CITY"),
|
||||||
|
state = c("FL", "CA", "TX")
|
||||||
|
)
|
||||||
|
basket
|
||||||
|
|
||||||
|
# Inspect resolution audit
|
||||||
|
cog_basket_resolution(basket)
|
||||||
|
|
||||||
|
# Pipe into a spending query
|
||||||
|
library(dplyr)
|
||||||
|
basket |> cog_spending(years = 2019:2020, category = "Police")
|
||||||
|
|
||||||
|
# Iteratively refine ambiguous matches
|
||||||
|
partial <- cog_gov_search(
|
||||||
|
name = c("Broward", "San Diego"), # San Diego is ambiguous
|
||||||
|
state = c("FL", "CA")
|
||||||
|
)
|
||||||
|
cog_basket_unresolved(partial)
|
||||||
|
refined <- cog_gov_search(
|
||||||
|
name = c("Broward", "San Diego"),
|
||||||
|
state = c("FL", "CA"),
|
||||||
|
type = c(NA, "city") # disambiguate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\seealso{
|
||||||
|
[cog_basket_resolution()], [cog_basket_unresolved()],
|
||||||
|
[cog_spending()], [cog_revenue()].
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user