From f62c13cc6e71705749c3c4cd2dda7242cc5efcba Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 14:18:05 -0700 Subject: [PATCH 1/7] fix: avoid cross join when combining correspondences aggregate_correspondences joined the per-step correspondences in order of size, so with three or more datasets consecutive tables could have no identifier in common and got cross joined. That triggered a dplyr deprecation warning and large intermediate tables. Tables now only get joined once they share an identifier with the ones joined so far. Co-Authored-By: Claude Fable 5.1 --- R/helpers.R | 26 +++++++++++++++----------- tests/testthat/test-helpers.R | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/R/helpers.R b/R/helpers.R index eb527c3..af3a0af 100644 --- a/R/helpers.R +++ b/R/helpers.R @@ -196,18 +196,22 @@ aggregate_correspondences <- function(correspondences){ select(!matches("Tongfen") | matches("TongfenMethod")) } # compute full correspondence, smallest table first to keep intermediate - # join results as small as possible - index_order <- correspondences %>% lapply(nrow) %>% unlist() %>% order() - - correspondence <- correspondences[[index_order[1]]] %>% - clean_correspondence_names() - if (length(correspondences)>1) for (index in index_order[-1]) { - c <- correspondences[[index]] %>% - clean_correspondence_names() - match_columns <- intersect(names(correspondence),names(c)) - match_columns <- match_columns[!grepl("TongfenMethod",match_columns)] - correspondence <- inner_join(correspondence,c,by=match_columns) %>% + # join results as small as possible, but only join tables that share an identifier + # with the tables joined so far, joining unrelated tables gives a cross join + remaining <- correspondences[order(vapply(correspondences,nrow,integer(1)))] %>% + lapply(clean_correspondence_names) + correspondence <- remaining[[1]] + remaining <- remaining[-1] + while (length(remaining)>0) { + match_columns <- lapply(remaining,function(c) { + match_columns <- intersect(names(correspondence),names(c)) + match_columns[!grepl("TongfenMethod",match_columns)] + }) + index <- which(lengths(match_columns)>0)[1] + if (is.na(index)) stop("Correspondences can't be combined, they don't share a common geographic identifier.") + correspondence <- inner_join(correspondence,remaining[[index]],by=match_columns[[index]]) %>% unique() + remaining <- remaining[-index] } method_columns <- names(correspondence)[grepl("TongfenMethod",names(correspondence))] diff --git a/tests/testthat/test-helpers.R b/tests/testthat/test-helpers.R index 2914fd9..60b2c78 100644 --- a/tests/testthat/test-helpers.R +++ b/tests/testthat/test-helpers.R @@ -192,6 +192,21 @@ test_that("aggregate_correspondences: uses every input correspondence exactly on expect_equal(nrow(result), 2L) }) +test_that("aggregate_correspondences: only joins correspondences sharing an identifier", { + # ordered by size the first two correspondences have no identifier in common + cl <- list( + tibble(A = c("a1", "a2"), B = c("b1", "b2"), TongfenMethod = "statcan"), + tibble(B = c("b1", "b2", "b3", "b4"), C = c("c1", "c2", "c3", "c4"), TongfenMethod = "statcan"), + tibble(C = c("c1", "c2", "c3"), D = c("d1", "d2", "d3"), TongfenMethod = "statcan") + ) + rlang::local_options(lifecycle_verbosity = "error") + result <- tongfen:::aggregate_correspondences(cl) + expect_equal(sort(names(result)), c("A", "B", "C", "D", "TongfenMethod")) + expect_equal(nrow(result), 2L) + + expect_error(tongfen:::aggregate_correspondences(cl[c(1, 3)]), "common geographic identifier") +}) + # ── summarize_geometry_by_group ─────────────────────────────────────────────── test_that("summarize_geometry_by_group: matches grouped st_union", { From fd04d32cdd230364230bd05c86fc623e72ebd956 Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 14:18:05 -0700 Subject: [PATCH 2/7] StatCan correspondence files from S3 mirror, v0.3.9 Statistics Canada put the correspondence files behind a browser check that blocks programmatic downloads, breaking method = "statcan". The files are now hosted as parquet on S3, built by data-raw/statcan_correspondence.R from manually downloaded originals. Cached files are checked against the mirror via ETag once per session and downloaded again if they changed. Co-Authored-By: Claude Fable 5.1 --- DESCRIPTION | 8 +- NEWS.md | 13 ++++ R/helpers.R | 43 +++++++++++ R/tongfen_ca.R | 54 ++++--------- README.md | 12 +-- data-raw/statcan_correspondence.R | 51 +++++++++++++ ...get_single_correspondence_ca_census_for.Rd | 5 ++ tests/testthat/test-cached-download.R | 75 +++++++++++++++++++ 8 files changed, 212 insertions(+), 49 deletions(-) create mode 100644 data-raw/statcan_correspondence.R create mode 100644 tests/testthat/test-cached-download.R diff --git a/DESCRIPTION b/DESCRIPTION index c014060..0d83429 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: tongfen Type: Package Title: Make Data Based on Different Geographies Comparable -Version: 0.3.8 +Version: 0.3.9 Authors@R: c( person("Jens", "von Bergmann", email = "jens@mountainmath.ca", role = c("aut", "cre"), comment = "creator and maintainer")) Description: Several functions to allow comparisons of data across different geographies, in particular for Canadian census data from different censuses. @@ -19,9 +19,10 @@ Imports: purrr, stringr, readr, + nanoparquet, + tools, utils, lifecycle -RoxygenNote: 7.3.3 Suggests: knitr, rmarkdown, @@ -34,7 +35,7 @@ Suggests: readxl, scales, microbenchmark, - testthat (>= 3.0.0) + testthat (>= 3.2.0) VignetteBuilder: knitr, rmarkdown URL: https://github.com/mountainMath/tongfen, https://mountainmath.github.io/tongfen/ BugReports: https://github.com/mountainMath/tongfen/issues @@ -42,3 +43,4 @@ Language: en-US RdMacros: lifecycle Depends: R (>= 4.1) +Config/roxygen2/version: 8.1.0 diff --git a/NEWS.md b/NEWS.md index e9babd4..efb766a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,16 @@ +# tongfen v.0.3.9 +## Major changes +- StatCan correspondence files are now downloaded as parquet files from a mirror, Statistics Canada + put the original files behind a browser check that blocks programmatic downloads, which broke + `method = "statcan"`. Cached files are checked against the mirror once per session and downloaded + again if they changed. The mirror location can be changed via the `tongfen.statcan_correspondence_url` + option. Previously cached `statcan_correspondence_*.csv` files in the tongfen cache directory are + no longer used and can be removed +## Minor changes +- combining correspondences across three or more datasets no longer runs into a cross join when the + correspondences happen to be ordered so that consecutive ones don't share a geographic identifier. + This gave a dplyr deprecation warning and needlessly large intermediate tables, results are unchanged + # tongfen v.0.3.8 ## Breaking changes - `get_tongfen_ca_census` now honours its `base_geo`, `na.rm`, `tolerance`, `crs` and diff --git a/R/helpers.R b/R/helpers.R index af3a0af..ca72e68 100644 --- a/R/helpers.R +++ b/R/helpers.R @@ -14,6 +14,49 @@ tongfen_cache_dir <- function(){ tempdir() } +tongfen_session <- new.env(parent=emptyenv()) + +# ETag of a remote file, NULL if it can't be determined, e.g. when offline +remote_etag <- function(url){ + headers <- tryCatch(suppressWarnings(curlGetHeaders(url)),error=function(e) NULL) + if (is.null(headers) || !identical(attr(headers,"status"),200L)) return(NULL) + etag <- grep("^etag:",headers,ignore.case=TRUE,value=TRUE) + if (length(etag)==0) return(NULL) + gsub("^etag:\\s*|\"|\\s+$","",etag[length(etag)],ignore.case=TRUE) +} + +# Download a remote file to the local path unless the local copy is still current. +# The ETag of the downloaded file is kept next to the cached file and compared to the remote ETag +# the first time the file is requested in a session, the file is only downloaded again if it changed. +cached_download <- function(url,path,refresh=FALSE){ + etag_path <- paste0(path,".etag") + cached <- file.exists(path) && !refresh + if (cached && isTRUE(tongfen_session[[url]])) return(path) + etag <- remote_etag(url) + if (cached) { + if (is.null(etag)) { + message(paste0("Could not check ",url," for updates, using cached version.")) + } + local_etag <- if (file.exists(etag_path)) readLines(etag_path,n=1,warn=FALSE) + if (is.null(etag) || identical(etag,local_etag)) { + tongfen_session[[url]] <- TRUE + return(path) + } + } + if (!dir.exists(dirname(path))) dir.create(dirname(path),recursive=TRUE) + tmp <- tempfile(tmpdir=dirname(path)) + on.exit(unlink(tmp)) + utils::download.file(url,tmp,mode="wb",quiet=TRUE) + # S3 ETags of files that were not uploaded in parts are the md5 checksum of the file + if (!is.null(etag) && grepl("^[0-9a-f]{32}$",etag) && !identical(unname(tools::md5sum(tmp)),etag)) { + stop(paste0("Download of ",url," is corrupted, please try again.")) + } + file.copy(tmp,path,overwrite=TRUE) + if (is.null(etag)) unlink(etag_path) else writeLines(etag,etag_path) + tongfen_session[[url]] <- TRUE + path +} + inner_join_tongfen_correspondence <- function(data,correspondence,link){ data %>% inner_join(correspondence %>% diff --git a/R/tongfen_ca.R b/R/tongfen_ca.R index 60caeb6..634e60b 100644 --- a/R/tongfen_ca.R +++ b/R/tongfen_ca.R @@ -1,13 +1,9 @@ -correspondence_ca_census_urls <- list( - "2006"=list("DB"="https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2006_92-156_DB_ID_txt.zip", - "DA"="https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2006_92-156_DA_AD_txt.zip"), - "2011"=list("DB"="https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2011_92-156_DB_ID_txt.zip", - "DA"="https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2011_92-156_DA_AD_txt.zip"), - "2016"=list("DB"="https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2016/2016_92-156_DB_ID_csv.zip", - "DA"="https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2016/2016_92-156_DA_AD_csv.zip"), - "2021"=list("DB"="https://www12.statcan.gc.ca/census-recensement/2021/geo/aip-pia/correspondence-correspondance/files-fichiers/2021_92-156-X_DB_ID.zip", - "DA"="https://www12.statcan.gc.ca/census-recensement/2021/geo/aip-pia/correspondence-correspondance/files-fichiers/2021_92-156-X_DA_AD.zip") -) +# StatCan correspondence files as parquet, built by data-raw/statcan_correspondence.R +correspondence_ca_census_url <- function(year,level){ + base_url <- nullify_blank(getOption("tongfen.statcan_correspondence_url")) %||% + "https://mountainmath.s3.ca-central-1.amazonaws.com/tongfen/statcan_correspondence/v1" + paste0(sub("/+$","",base_url),"/statcan_correspondence_",year,"_",level,".parquet") +} ca_census_base <- c("Population","Dwellings","Households") @@ -184,6 +180,11 @@ add_census_ca_base_variables <- function(meta){ #' @description #' \lifecycle{maturing} #' +#' The correspondence files are downloaded from a mirror of the Statistics Canada correspondence files +#' and cached in the tongfen cache directory. The cached files are checked against the mirror once per session +#' and get downloaded again if they changed. The location of the mirror can be changed via the +#' `tongfen.statcan_correspondence_url` option. +#' #' @param year census year, only 2006 through 2021 are supported #' @param level geographic level, DA or DB #' @param refresh reload the correspondence files, default is `FALSE` @@ -193,36 +194,9 @@ get_single_correspondence_ca_census_for <- function(year,level=c("DA","DB"),refr year=as.character(year)[1] if (!(level %in% c("DA","DB"))) stop("Level needs to be DA or DB") if (!(year %in% c("2006","2011","2016","2021"))) stop("Year needs to be 2006, 2011, 2016, or 2021") - new_field=paste0(level,"UID",year) - old_field=paste0(level,"UID",as.integer(year)-5) - path=file.path(tongfen_cache_dir(),paste0("statcan_correspondence_",year,"_",level,".csv")) - if (refresh || !file.exists(path)) { - url=correspondence_ca_census_urls[[year]][[level]] - tmp=tempfile() - utils::download.file(url,tmp) - exdir=file.path(tempdir(),paste0("correspondence_",year,"_",level)) - if (dir.exists(exdir)) unlink(exdir,recursive=TRUE) - dir.create(exdir,showWarnings = FALSE) - utils::unzip(tmp,exdir=exdir) - file=dir(exdir,"\\.txt|\\.csv") - if (length(file)==0) { - p<-dir(exdir)[1] - if (dir.exists(file.path(exdir,p))) { - exdir=file.path(exdir,p) - file=dir(exdir,"\\.txt|\\.csv") - } - } - if (level=="DB") headers=c(new_field,old_field,"flag") else headers=c(new_field,old_field,paste0("DBUID",year),"flag") - unwanted <- paste0(level,"UID",year) - d<-readr::read_csv(file.path(exdir,file),col_types = readr::cols(.default = "c"),col_names = headers) %>% - select(all_of(c(new_field,old_field,"flag"))) %>% - unique() %>% - filter(!grepl(unwanted,!!as.name(new_field))) - readr::write_csv(d,path) - unlink(tmp) - unlink(exdir,recursive = TRUE) - } - result <- readr::read_csv(path,col_types = readr::cols(.default = "c")) + path=file.path(tongfen_cache_dir(),paste0("statcan_correspondence_",year,"_",level,".parquet")) + cached_download(correspondence_ca_census_url(year,level),path,refresh=refresh) + result <- tibble::as_tibble(nanoparquet::read_parquet(path)) # manual corrections if (year=="2021" && level=="DB") { diff --git a/README.md b/README.md index 7a1ad3d..38e098e 100644 --- a/README.md +++ b/README.md @@ -29,13 +29,13 @@ library(tongfen) ### Caching correspondence files The `get_tongfen_ca_census` and `get_tongfen_correspondence_ca_census` methods make use of the StatCan correspondence -files when run with `method = "statcan"`. To speed up this process it is useful to permanently cache these files instead of having to download them repeatedly. If caching is desired, set either +files when run with `method = "statcan"`. Statistics Canada no longer allows programmatic downloads of these files, so the package downloads them from a mirror hosting the files in parquet format. To speed up this process it is useful to permanently cache these files instead of having to download them again in every session. If caching is desired, set either * `options("tongfen.cache_path"="")` * `Sys.setenv("tongfen.cache_path"="")` * `options("custom_data_path"="")` -in your `.Rprofile` or `.Renviron` file. +in your `.Rprofile` or `.Renviron` file. Cached files are checked against the mirror once per session and only get downloaded again if they changed, when offline the cached files are used as they are. ## General TongFen @@ -84,8 +84,8 @@ Methods to facilitate this are still under active development. If you wish to cite tongfen: - von Bergmann, J. (2024). tongfen: R package to - Make Data Based on Different Geographies Comparable. v0.3.7. + von Bergmann, J. (2026). tongfen: R package to + Make Data Based on Different Geographies Comparable. v0.3.9. DOI: 10.32614/CRAN.package.tongfen @@ -94,9 +94,9 @@ A BibTeX entry for LaTeX users is @Manual{tongfen, author = {Jens {von Bergmann}}, title = {tongfen: R package to Make Data Based on Different Geographies Comparable}, - year = {2024}, + year = {2026}, doi = {10.32614/CRAN.package.tongfen}, - note = {R package version 0.3.7}, + note = {R package version 0.3.9}, url = {https://mountainmath.github.io/tongfen/}, } ``` diff --git a/data-raw/statcan_correspondence.R b/data-raw/statcan_correspondence.R new file mode 100644 index 0000000..44e64d5 --- /dev/null +++ b/data-raw/statcan_correspondence.R @@ -0,0 +1,51 @@ +## code to prepare the StatCan DA and DB correspondence files hosted on S3 +## +## StatCan put the correspondence files behind a browser check, so they can't be downloaded +## programmatically any more. Download and extract the zip files manually from +## https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2006_92-156_DB_ID_txt.zip +## https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2006_92-156_DA_AD_txt.zip +## https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2011_92-156_DB_ID_txt.zip +## https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2011_92-156_DA_AD_txt.zip +## https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2016/2016_92-156_DB_ID_csv.zip +## https://www12.statcan.gc.ca/census-recensement/2011/geo/ref/files-fichiers/2016/2016_92-156_DA_AD_csv.zip +## https://www12.statcan.gc.ca/census-recensement/2021/geo/aip-pia/correspondence-correspondance/files-fichiers/2021_92-156-X_DB_ID.zip +## https://www12.statcan.gc.ca/census-recensement/2021/geo/aip-pia/correspondence-correspondance/files-fichiers/2021_92-156-X_DA_AD.zip +## into `input_dir`, then run this script and upload the parquet files in `output_dir` to S3. + +library(dplyr) + +input_dir <- "~/Downloads" +output_dir <- "~/Downloads/tongfen_statcan_correspondence" + +level_codes <- c(DA="DA_AD",DB="DB_ID") + +prepare_statcan_correspondence <- function(year,level){ + new_field <- paste0(level,"UID",year) + old_field <- paste0(level,"UID",year-5) + dirs <- dir(input_dir,paste0("^",year,"_92-156.*",level_codes[[level]]),full.names=TRUE) + dirs <- dirs[dir.exists(dirs)] + file <- dir(dirs,"\\.(txt|csv)$",full.names=TRUE,recursive=TRUE) + stopifnot(length(file)==1) + # DA files are at DB granularity and carry the DBUID in the third column, 2021 files have extra DGUID columns + headers <- if (level=="DB") c(new_field,old_field,"flag") else c(new_field,old_field,paste0("DBUID",year),"flag") + d <- readr::read_csv(file,col_types=readr::cols(.default="c"),col_names=FALSE) %>% + select(all_of(seq_along(headers))) %>% + setNames(headers) %>% + filter(grepl("^\\d+$",.data[[new_field]])) %>% # header row in the 2016 and 2021 files + select(all_of(c(new_field,old_field,"flag"))) %>% + unique() %>% + arrange(.data[[new_field]],.data[[old_field]]) + stopifnot(all(grepl("^\\d+$",d[[old_field]])),all(d$flag %in% as.character(1:4))) + d +} + +dir.create(output_dir,showWarnings=FALSE) +for (year in c(2006,2011,2016,2021)) { + for (level in c("DA","DB")) { + prepare_statcan_correspondence(year,level) %>% + nanoparquet::write_parquet(file.path(output_dir,paste0("statcan_correspondence_",year,"_",level,".parquet")), + compression="zstd", + # nanoparquet does not compress at its default zstd level + options=nanoparquet::parquet_options(compression_level=19)) + } +} diff --git a/man/get_single_correspondence_ca_census_for.Rd b/man/get_single_correspondence_ca_census_for.Rd index 3970fba..bd4c0e4 100644 --- a/man/get_single_correspondence_ca_census_for.Rd +++ b/man/get_single_correspondence_ca_census_for.Rd @@ -22,4 +22,9 @@ tibble with correspondence table` } \description{ \lifecycle{maturing} + +The correspondence files are downloaded from a mirror of the Statistics Canada correspondence files +and cached in the tongfen cache directory. The cached files are checked against the mirror once per session +and get downloaded again if they changed. The location of the mirror can be changed via the +`tongfen.statcan_correspondence_url` option. } diff --git a/tests/testthat/test-cached-download.R b/tests/testthat/test-cached-download.R new file mode 100644 index 0000000..de66488 --- /dev/null +++ b/tests/testthat/test-cached-download.R @@ -0,0 +1,75 @@ +# ── cached_download ─────────────────────────────────────────────────────────── + +# local file standing in for the remote file, with its md5 checksum as ETag like S3 +local_remote <- function(content) { + remote <- tempfile() + writeLines(content, remote) + url <- paste0("file://", normalizePath(remote, winslash = "/")) + rm(list = intersect(url, ls(tongfen:::tongfen_session)), envir = tongfen:::tongfen_session) + list(path = remote, url = url) +} + +test_that("cached_download: downloads the file and remembers the ETag", { + remote <- local_remote("a") + path <- file.path(tempfile(), "file.txt") + local_mocked_bindings(remote_etag = function(url) unname(tools::md5sum(remote$path))) + expect_equal(tongfen:::cached_download(remote$url, path), path) + expect_equal(readLines(path), "a") + expect_equal(readLines(paste0(path, ".etag")), unname(tools::md5sum(remote$path))) +}) + +test_that("cached_download: checks the remote file only once per session", { + remote <- local_remote("a") + path <- file.path(tempfile(), "file.txt") + checks <- 0 + local_mocked_bindings(remote_etag = function(url) { + checks <<- checks + 1 + unname(tools::md5sum(remote$path)) + }) + tongfen:::cached_download(remote$url, path) + tongfen:::cached_download(remote$url, path) + expect_equal(checks, 1) + tongfen:::cached_download(remote$url, path, refresh = TRUE) + expect_equal(checks, 2) +}) + +test_that("cached_download: downloads again only if the remote file changed", { + remote <- local_remote("a") + path <- file.path(tempfile(), "file.txt") + local_mocked_bindings(remote_etag = function(url) unname(tools::md5sum(remote$path))) + tongfen:::cached_download(remote$url, path) + + # new session, unchanged remote file, the cached file is left alone + rm(list = remote$url, envir = tongfen:::tongfen_session) + writeLines("local", path) + tongfen:::cached_download(remote$url, path) + expect_equal(readLines(path), "local") + + # new session, changed remote file + rm(list = remote$url, envir = tongfen:::tongfen_session) + writeLines("b", remote$path) + tongfen:::cached_download(remote$url, path) + expect_equal(readLines(path), "b") + expect_equal(readLines(paste0(path, ".etag")), unname(tools::md5sum(remote$path))) +}) + +test_that("cached_download: falls back to the cached file if the remote can't be checked", { + remote <- local_remote("a") + path <- file.path(tempfile(), "file.txt") + local_mocked_bindings(remote_etag = function(url) unname(tools::md5sum(remote$path))) + tongfen:::cached_download(remote$url, path) + + rm(list = remote$url, envir = tongfen:::tongfen_session) + writeLines("b", remote$path) + local_mocked_bindings(remote_etag = function(url) NULL) + expect_message(tongfen:::cached_download(remote$url, path), "using cached version") + expect_equal(readLines(path), "a") +}) + +test_that("cached_download: rejects downloads not matching the md5 ETag", { + remote <- local_remote("a") + path <- file.path(tempfile(), "file.txt") + local_mocked_bindings(remote_etag = function(url) strrep("0", 32)) + expect_error(tongfen:::cached_download(remote$url, path), "corrupted") + expect_false(file.exists(path)) +}) From b616b4783d4e3cd0aa8cc448b582a172f8419d33 Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 14:24:07 -0700 Subject: [PATCH 3/7] ci: drop master from R-CMD-check push trigger The master branch has been removed, main is the default branch. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/R-CMD-check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index bd2aadc..472ced2 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -2,7 +2,7 @@ # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: [main, master] + branches: [main] pull_request: schedule: - cron: '0 7 * * *' From 777f0baada4b1aa663a37d99e31323a05cf1b3a1 Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 15:56:09 -0700 Subject: [PATCH 4/7] fix: wrong results in reaggregation, estimation and averages, plus audit fixes Wrong results - proportional_reaggregate compared existing child values to the parent total across all categories instead of per category, and a missing child value discarded the values of its siblings - tongfen_estimate underestimated values when an intersection came back as a geometry collection, and picked up NA from source regions that only touch the target - averages were scaled by their parent more than once when the metadata lists the same variable for several datasets, tongfen_aggregate now only uses the metadata of the dataset being aggregated - averages aggregated with na.rm = TRUE kept the parent of regions with a missing value in the denominator - "Average to" variables sharing a parent overwrote each other's base Errors - estimate_tongfen_correspondence required the geometry column to be named geometry, and the robust option failed on more than one region - tongfen_estimate errored out when no target region overlaps the source, and gave an obscure error on clashing column names Behaviour - refresh is passed through to the StatCan correspondence download - tongfen_estimate_ca_census returns visibly - US relationship files are downloaded to a temporary file first and cached via the shared tongfen cache directory - fix function names in the get_tongfen_ca_census_ct_from_da deprecation Docs and housekeeping - missing \value sections, broken or misleading examples, README and get_tongfen_us_census docs - remove dead code in tongfen_ca.R - require dplyr >= 1.1.0, which the package already relied on - NEWS and cran-comments for 0.3.9 Co-Authored-By: Claude Fable 5.1 --- DESCRIPTION | 2 +- NEWS.md | 33 ++++ R/helpers.R | 14 +- R/tongfen.R | 114 ++++++++---- R/tongfen_ca.R | 31 +--- R/tongfen_ca_deprecated.R | 4 +- R/tongfen_ca_estimate.R | 9 +- R/tongfen_estimate.R | 55 +++--- R/tongfen_us.R | 58 +++---- README.md | 3 +- cran-comments.md | 70 ++------ man/get_tongfen_correspondence_us_census.Rd | 3 +- man/get_tongfen_us_census.Rd | 5 +- man/meta_for_ca_census_vectors.Rd | 2 +- man/tongfen_aggregate.Rd | 13 +- man/tongfen_ca_census_ct.Rd | 4 + man/tongfen_estimate.Rd | 4 +- man/tongfen_estimate_ca_census.Rd | 9 +- man/tongfen_tag_largest_overlap.Rd | 12 +- tests/testthat/test-aggregate.R | 162 ++++++++++++++++++ tests/testthat/test-cached-download.R | 34 ++++ tests/testthat/test-correspondence-estimate.R | 35 ++++ tests/testthat/test-estimate.R | 118 +++++++++++++ .../testthat/test-proportional-reaggregate.R | 60 +++++++ 24 files changed, 659 insertions(+), 195 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0d83429..2631df5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,7 +11,7 @@ ByteCompile: yes LazyData: true NeedsCompilation: no Imports: - dplyr (>= 1.0), + dplyr (>= 1.1.0), tidyr (>= 1.0), sf, tibble, diff --git a/NEWS.md b/NEWS.md index efb766a..6019538 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,39 @@ - combining correspondences across three or more datasets no longer runs into a cross join when the correspondences happen to be ordered so that consecutive ones don't share a geographic identifier. This gave a dplyr deprecation warning and needlessly large intermediate tables, results are unchanged +- fix `proportional_reaggregate` giving wrong results when the finer level data already has values + for the categories to reaggregate. The values were compared to the parent total across all + categories instead of per category, and a missing value in one child region discarded the + existing values of all its siblings +- fix `tongfen_estimate` underestimating values when the intersection of a source and a target + region is a geometry collection, i.e. several polygons joined by a shared boundary line +- `tongfen_estimate` with `na.rm = FALSE` no longer returns `NA` for target regions that only + share a boundary with a source region with missing values +- `tongfen_estimate` now returns `NA` for target regions that don't overlap the source instead of + erroring out when none of them do, and gives a clear error when `target` already has a column + named like one of the variables to estimate +- fix `tongfen_aggregate` and `aggregate_data_with_meta` scaling averages by their parent variable + more than once when the metadata lists the same variable name for several datasets, as is common + with US census data. `tongfen_aggregate` now only uses the metadata of the dataset being + aggregated, conflicting aggregation rules for the same variable are an error +- averages aggregated with `na.rm = TRUE` are now taken over the regions that have a value. The + parent variable of regions with a missing average still counted toward the total the average + was divided by, pulling the result toward zero. This affects `aggregate_data_with_meta`, + `tongfen_aggregate`, `tongfen_estimate` and the functions built on them +- fix "Average to" variables like percentage changes overwriting each other's base when several of + them share a parent variable. In that case the base columns in the result are named after the + variable (`base_`) instead of the parent +- `estimate_tongfen_correspondence` no longer requires the geometry column to be named `geometry` +- `refresh = TRUE` in `get_tongfen_correspondence_ca_census` and `get_tongfen_ca_census` now also + refreshes the cached StatCan correspondence files +- US Census Bureau relationship files are downloaded to a temporary file first, an interrupted + download no longer leaves a broken file in the cache. They are now cached in the same place as + the StatCan correspondence files, also honouring the `tongfen.cache_path` environment variable + and the `custom_data_path` option +- `tongfen_estimate_ca_census` returns its result visibly +- documentation fixes, among others the `tongfen_aggregate` example now passes a named list of + datasets matching the metadata +- requires dplyr 1.1.0 or newer, which the package already relied on # tongfen v.0.3.8 ## Breaking changes diff --git a/R/helpers.R b/R/helpers.R index ca72e68..cee0969 100644 --- a/R/helpers.R +++ b/R/helpers.R @@ -25,14 +25,22 @@ remote_etag <- function(url){ gsub("^etag:\\s*|\"|\\s+$","",etag[length(etag)],ignore.case=TRUE) } +# location of the cached US Census Bureau relationship files +us_cache_dir <- function(cache_path=NULL){ + file.path(nullify_blank(cache_path) %||% tongfen_cache_dir(),"us_data") +} + # Download a remote file to the local path unless the local copy is still current. # The ETag of the downloaded file is kept next to the cached file and compared to the remote ETag # the first time the file is requested in a session, the file is only downloaded again if it changed. -cached_download <- function(url,path,refresh=FALSE){ +# Files that never change don't need to be checked against the remote, with `check_remote=FALSE` +# the cached file is used as is. The download goes to a temporary file first so that an +# interrupted download does not leave a broken file in the cache. +cached_download <- function(url,path,refresh=FALSE,check_remote=TRUE){ etag_path <- paste0(path,".etag") cached <- file.exists(path) && !refresh - if (cached && isTRUE(tongfen_session[[url]])) return(path) - etag <- remote_etag(url) + if (cached && (!check_remote || isTRUE(tongfen_session[[url]]))) return(path) + etag <- if (check_remote) remote_etag(url) if (cached) { if (is.null(etag)) { message(paste0("Could not check ",url," for updates, using cached version.")) diff --git a/R/tongfen.R b/R/tongfen.R index 5640454..34a8e05 100644 --- a/R/tongfen.R +++ b/R/tongfen.R @@ -29,6 +29,21 @@ meta_for_additive_variables <- function(dataset,variables){ +# Averages are aggregated by scaling them by their parent variable, summing up, and dividing +# by the summed up parent again. The parent of regions where the average is missing must not +# count toward the total the sum gets divided by, so each average carries its own weight column. +average_weight_name <- function(variables) { + if (length(variables)==0) return(character(0)) + paste0("...weight_",variables) +} + +average_weight_exprs <- function(to_scale,parent_lookup) { + lapply(setNames(to_scale, average_weight_name(to_scale)), \(col) { + parent <- as.name(unname(parent_lookup[col])) + rlang::expr(replace(as.numeric(!!parent),is.na(!!as.name(col)),NA_real_)) + }) +} + cut_meta <- function(data,meta){ meta <- meta %>% filter(.data$variable %in% names(data)|.data$label %in% names(data)) %>% @@ -52,14 +67,16 @@ pre_scale <- function(data,meta,meta_var="data_var",quiet=FALSE) { if (length(to_scale) > 0) { + weight_exprs <- average_weight_exprs(to_scale,parent_lookup) scale_exprs <- lapply(setNames(to_scale, to_scale), \(col) rlang::expr(!!as.name(col) * !!as.name(unname(parent_lookup[col])))) - data <- data %>% mutate(!!!scale_exprs) + data <- data %>% mutate(!!!weight_exprs) %>% mutate(!!!scale_exprs) } data } +# divides by the weights added in `pre_scale` if they are still around, and by the parent otherwise post_scale <- function(data,meta,meta_var="data_var") { meta_name_lookup <- setNames(meta %>% pull(meta_var),meta$variable) meta$parent_name <- meta_name_lookup[meta$parent] @@ -67,9 +84,14 @@ post_scale <- function(data,meta,meta_var="data_var") { to_scale <- filter(meta,.data$rule %in% c("Median","Average")) %>% pull(meta_var) if (length(to_scale) > 0) { - scale_exprs <- lapply(setNames(to_scale, to_scale), \(col) - rlang::expr(!!as.name(col) / !!as.name(unname(parent_lookup[col])))) - data <- data %>% mutate(!!!scale_exprs) + scale_exprs <- lapply(setNames(to_scale, to_scale), \(col) { + weight <- average_weight_name(col) + if (!(weight %in% names(data))) weight <- unname(parent_lookup[col]) + rlang::expr(!!as.name(col) / !!as.name(weight)) + }) + data <- data %>% + mutate(!!!scale_exprs) %>% + select(-any_of(average_weight_name(to_scale))) } data @@ -102,6 +124,17 @@ post_scale <- function(data,meta,meta_var="data_var") { #'} aggregate_data_with_meta <- function(data,meta,geo=FALSE,na.rm=TRUE,quiet=FALSE){ meta <- meta %>% filter(.data$variable %in% names(data)) + # the same variable can be listed several times if meta spans several datasets, + # each variable must only be aggregated (and scaled) once + duplicates <- duplicated(meta$variable) + if (any(duplicates)) { + rules <- meta %>% select(any_of(c("variable","rule","parent","units"))) %>% unique() + ambiguous <- unique(rules$variable[duplicated(rules$variable)]) + if (length(ambiguous)>0) + stop(paste0("Conflicting aggregation rules in metadata for ",paste0(ambiguous,collapse = ", "), + ", please only pass the metadata for the dataset that is being aggregated.")) + meta <- meta[!duplicates,] + } grouping_var=groups(data) %>% as.character parent_lookup <- setNames(meta$parent,meta$variable) to_scale <- filter(meta,.data$rule %in% c("Median","Average"))$variable @@ -116,16 +149,25 @@ aggregate_data_with_meta <- function(data,meta,geo=FALSE,na.rm=TRUE,quiet=FALSE) message(paste0("Can't TongFen medians, will approximate by treating as averages: ",paste0(median_vars,collapse = ", "))) } + weight_variables <- average_weight_name(to_scale) if (length(to_scale) > 0) { + weight_exprs <- average_weight_exprs(to_scale,parent_lookup) scale_exprs <- lapply(setNames(to_scale, to_scale), \(col) rlang::expr(!!as.name(col) * !!as.name(unname(parent_lookup[col])))) - data <- data %>% mutate(!!!scale_exprs) + data <- data %>% mutate(!!!weight_exprs) %>% mutate(!!!scale_exprs) } + # the base an "Average to" variable gets scaled by depends on the variable, variables + # sharing a parent each need their own base column + scale_from_parents <- unname(parent_lookup[to_scale_from]) + shared_parent <- scale_from_parents %in% scale_from_parents[duplicated(scale_from_parents)] + base_vectors <- setNames(paste0("base_",ifelse(shared_parent,to_scale_from,scale_from_parents)), + to_scale_from) + base_variables <- c() for (x in to_scale_from) { scale_type <- meta %>% filter(.data$variable==x) %>% pull(units) %>% as.character() - base_vector <- paste0("base_",parent_lookup[x]) + base_vector <- unname(base_vectors[x]) base_variables <- c(base_variables,base_vector) if (scale_type=="Percentage ratio (0.0-1.0)") { data <- data %>% mutate(!!base_vector:=!!as.name(parent_lookup[x])/(!!as.name(x)+1)) @@ -144,22 +186,24 @@ aggregate_data_with_meta <- function(data,meta,geo=FALSE,na.rm=TRUE,quiet=FALSE) data <- left_join(summarize_geometry_by_group(data,grouping_var), data %>% sf::st_set_geometry(NULL) %>% - summarize_at(meta$variable,sum,na.rm=na.rm), + summarize_at(c(meta$variable,weight_variables),sum,na.rm=na.rm), by=grouping_var) } else { - data <- data %>% summarize_at(meta$variable,sum,na.rm=na.rm) + data <- data %>% summarize_at(c(meta$variable,weight_variables),sum,na.rm=na.rm) } if (length(to_scale) > 0) { scale_exprs <- lapply(setNames(to_scale, to_scale), \(col) - rlang::expr(!!as.name(col) / !!as.name(unname(parent_lookup[col])))) - data <- data %>% mutate(!!!scale_exprs) + rlang::expr(!!as.name(col) / !!as.name(average_weight_name(col)))) + data <- data %>% + mutate(!!!scale_exprs) %>% + select(-all_of(weight_variables)) } # Optimized: Vectorized division by base vectors if (length(to_scale_from) > 0) { for (x in to_scale_from) { - base_vector <- paste0("base_", parent_lookup[x]) + base_vector <- unname(base_vectors[x]) data[[x]] <- data[[x]] / data[[base_vector]] } } @@ -188,7 +232,10 @@ rename_with_meta <- function(data,meta,ds=NULL){ #' #' Aggregate variables secified in meta for several datasets according to correspondence. #' -#' @param data list of datasets to be aggregated +#' @param data named list of datasets to be aggregated. The names identify the datasets, they are +#' matched against the `geo_dataset` column in `meta` to pick the aggregation rules and labels +#' for each dataset. Without names, or with names not found in `meta`, the rules for all +#' datasets are applied and the variables keep their original names #' @param correspondence correspondence data for gluing up the datasets #' @param meta metadata containing aggregation rules as for example returned by `meta_for_ca_census_vectors` #' @param base_geo identifier for which data element to base the final geography on, @@ -200,17 +247,17 @@ rename_with_meta <- function(data,meta,ds=NULL){ #' @export #' #' @examples -#' # aggregate census tract level 2006 population data on common gepgraphy build through +#' # aggregate census tract level 2006 and 2016 population data on common geography built through #' # correspondence from 2006 and 2016 census tracts in the City of Vancouver. #' \dontrun{ #' regions <- list(CSD="5915022") #' geo1 <- cancensus::get_census("CA06",regions=regions,geo_format='sf',level='CT') #' geo2 <- cancensus::get_census("CA16",regions=regions,geo_format='sf',level='CT') -#' meta <- meta_for_additive_variables("CA06","Population") +#' meta <- meta_for_additive_variables(c("CA06","CA16"),"Population") #' correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=c('CA06','CA16'), #' regions=regions,level='CT') -#' result <- tongfen_aggregate(list(geo1 %>% rename(GeoUIDCA06=GeoUID), -#' geo2 %>% rename(GeoUIDCA16=GeoUID)),correspondence,meta) +#' result <- tongfen_aggregate(list(CA06=geo1 %>% rename(GeoUIDCA06=GeoUID), +#' CA16=geo2 %>% rename(GeoUIDCA16=GeoUID)),correspondence,meta) #'} tongfen_aggregate <- function(data,correspondence,meta=NULL, base_geo = NULL, na.rm = TRUE){ data <- ensure_names(data) @@ -239,7 +286,13 @@ tongfen_aggregate <- function(data,correspondence,meta=NULL, base_geo = NULL, na by=match_column) %>% group_by(.data$TongfenID,.data$TongfenUID) if (!is.null(meta)) { - d <- d %>% aggregate_data_with_meta(meta,na.rm=na.rm) + # only use the aggregation rules for this dataset if meta distinguishes datasets, + # the same variable name can come with different rules in different datasets + ds_meta <- meta + if (ds %in% as.character(meta$geo_dataset)) { + ds_meta <- meta %>% filter(as.character(.data$geo_dataset)==ds) + } + d <- d %>% aggregate_data_with_meta(ds_meta,na.rm=na.rm) } else { if ("sf" %in% class(d)) { d <- summarize_geometry_by_group(d,c("TongfenID","TongfenUID")) @@ -397,7 +450,9 @@ proportional_reaggregate <- function(data,parent_data,geo_match,categories,base= values_to="p_value") if (vt %in% c("numeric","integer","integer64")) { d_combined <- full_join(d_base,d_parent,by=c(geo_match,"category"="category")) %>% - mutate(s_value=sum(.data$value),.by=names(geo_match)) %>% + # the parent value gets compared to what the children of the same category + # already add up to, missing child values count as zero + mutate(s_value=sum(.data$value,na.rm=TRUE),.by=c(names(geo_match),"category")) %>% mutate(across(any_of(c("p_value","s_value")),\(x)coalesce(x,0))) %>% mutate(value=case_when(.data$agg_type=="additive" ~ coalesce(.data$value,0) + .data$weight*(.data$p_value-.data$s_value), is.na(.data$value) ~ .data$p_value, @@ -465,31 +520,32 @@ estimate_tongfen_single_correspondence <- function(geo1,geo2,geo1_uid,geo2_uid, if (robust) { - if (!st_is_valid(geo1)) geo1 <- geo1 %>% st_make_valid() - if (!st_is_valid(geo2)) geo2 <- geo2 %>% st_make_valid() + if (!isTRUE(all(st_is_valid(geo1)))) geo1 <- geo1 %>% st_make_valid() + if (!isTRUE(all(st_is_valid(geo2)))) geo2 <- geo2 %>% st_make_valid() } - robust_tolerance_buffer <- function(geo,geo_uid,tolerance,max_tries=20) { + # works on the geometries directly, the geometry column can go by any name + robust_tolerance_buffer <- function(geo,tolerance,max_tries=20) { t <- tolerance - d <- geo - d$geometry=st_buffer(geo$geometry,-t) + geometry <- st_geometry(geo) + buffered <- st_buffer(geometry,-t) count=0 - empties <- st_is_empty(d) + empties <- st_is_empty(buffered) while (sum(empties) > 0 & count 0) { stop("Unable to match within given tolerance, some geographies are too fine.") } - d + st_set_geometry(geo,buffered) } - cgeo1 <- geo1 %>% robust_tolerance_buffer(geo_uid = geo1_uid,tolerance = tolerance) - cgeo2 <- geo2 %>% robust_tolerance_buffer(geo_uid = geo2_uid,tolerance = tolerance) + cgeo1 <- geo1 %>% robust_tolerance_buffer(tolerance = tolerance) + cgeo2 <- geo2 %>% robust_tolerance_buffer(tolerance = tolerance) # Both intersections are necessary (buffered cgeo1 vs geo2, and cgeo2 vs geo1). The # sparse index list is turned into a tibble directly, `as.data.frame()` on an empty diff --git a/R/tongfen_ca.R b/R/tongfen_ca.R index 634e60b..6109e72 100644 --- a/R/tongfen_ca.R +++ b/R/tongfen_ca.R @@ -26,15 +26,6 @@ datasets_from_vectors <- function(vs){ ds } -GEO_DATASET_LOOKUP <- c( - setNames(rep("CA1996",1),paste0("TX",seq(2000,2000))), - setNames(rep("CA01",5),paste0("TX",seq(2001,2005))), - setNames(rep("CA06",6),paste0("TX",seq(2006,2011))), - setNames(rep("CA11",4),paste0("TX",seq(2012,2015))), - setNames(rep("CA16",5),paste0("TX",seq(2016,2020))), - setNames(rep("CA16",21),paste0("CA",seq(2000,2020),"RMS")) -) - geo_dataset_for_years <- function(years){ require_suggested("cancensus") dataset_list <- cancensus::list_census_datasets() @@ -50,7 +41,6 @@ geo_dataset_for_years <- function(years){ geo_dataset_from_dataset <- function(datasets){ require_suggested("cancensus") - if (TRUE) { # legacy until cancensus updates datasets <- datasets %>% gsub("^CA11[NF]$","CA11",.) %>% gsub("\\d{4}x","",.) dataset_list <- cancensus::list_census_datasets() lapply(datasets, function(ds){ @@ -60,16 +50,6 @@ geo_dataset_from_dataset <- function(datasets){ unique() }) %>% unlist() - } else { - result <- tibble(dataset=datasets,geo_dataset=GEO_DATASET_LOOKUP[datasets]) %>% - mutate(geo_dataset=ifelse(is.na(.data$geo_dataset),.data$dataset %>% - years_from_datasets() %>% - as.character() %>% - substr(3,4) %>% - paste0("CA",.), - .data$geo_dataset)) - result$geo_dataset - } } #' Generate metadata from Candian census vectors @@ -88,7 +68,7 @@ geo_dataset_from_dataset <- function(datasets){ #' @examples #' # Build metadata for vectors #' \dontrun{ -#' meta <- meta_for_ca_census_vectors("v_CA16_4836","v_CA16_4838","v_CA16_4899") +#' meta <- meta_for_ca_census_vectors(c("v_CA16_4836","v_CA16_4838","v_CA16_4899")) #'} meta_for_ca_census_vectors <- function(vectors){ require_suggested("cancensus") @@ -100,9 +80,6 @@ meta_for_ca_census_vectors <- function(vectors){ nn[nn==""]=vectors[nn==""] } - if (length(vectors)==0) { - meta <- tibble::tibble(variable=NA,label=NA,dataset=datasets_from_vectors(vectors)) - } meta <- tibble::tibble(variable=vectors,label=nn,dataset=datasets_from_vectors(vectors)) %>% mutate(type="Original", aggregation="0",units=NA) datasets <- meta$dataset %>% @@ -134,13 +111,13 @@ meta_for_ca_census_vectors <- function(vectors){ select(variable="parent","dataset") %>% mutate(type="Extra",aggregation="Additive",rule="Additive") %>% filter(!is.na(.data$variable),!.data$variable %in% meta$variable) %>% - filter(!duplicated(.data$variable,.data$dataset)) %>% + distinct(.data$variable,.data$dataset,.keep_all=TRUE) %>% mutate(label=.data$variable) if (nrow(extras)>0) { meta <- meta %>% bind_rows(extras) %>% - filter(!duplicated(.data$variable,.data$dataset)) + distinct(.data$variable,.data$dataset,.keep_all=TRUE) } meta <- meta %>% @@ -326,7 +303,7 @@ get_tongfen_correspondence_ca_census <- function(geo_datasets, regions, level="C correspondence_years=all_geo_years[-1] correspondence <- correspondence_years %>% lapply(function(year){ - c <- get_single_correspondence_ca_census_for(year,statcan_level) %>% + c <- get_single_correspondence_ca_census_for(year,statcan_level,refresh=refresh) %>% select(-"flag") previous_year <- all_geo_years[which(all_geo_years==year)-1] ds1 <- all_geo_datasets[all_geo_years==year] diff --git a/R/tongfen_ca_deprecated.R b/R/tongfen_ca_deprecated.R index 418ff0e..ee7551a 100644 --- a/R/tongfen_ca_deprecated.R +++ b/R/tongfen_ca_deprecated.R @@ -85,7 +85,7 @@ get_tongfen_census_da <- function(regions,vectors,geo_format=NA,use_cache=TRUE,n #' @return dataframe with variables on common geography #' @export get_tongfen_ca_census_ct_from_da <- function(regions,vectors,geo_format=NA,use_cache=TRUE,na.rm=TRUE,quiet=TRUE) { - lifecycle::deprecate_warn("0.2.0", "get_tongfen_census_da()", "get_tongfen_census_ca()") + lifecycle::deprecate_warn("0.2.0", "get_tongfen_ca_census_ct_from_da()", "get_tongfen_ca_census()") meta <- meta_for_ca_census_vectors(vectors) base_geo <- if (is.na(geo_format)) NULL else meta$geo_dataset %>% unique() %>% sort() %>% first() @@ -111,6 +111,8 @@ get_tongfen_ca_census_ct_from_da <- function(regions,vectors,geo_format=NA,use_c #' @param data2_sum_vars vector of variable names to by summed up when aggregating geographies #' @param data2_group_vars optional vector of grouping variables #' @param na.rm optional parameter to remove NA values when summing, default = `TRUE` +#' @return `data2` with the variables in `data2_sum_vars` aggregated to a common geography matching `data1`, +#' identified by the `GeoUID` of `data1` #' @export tongfen_ca_census_ct <- function(data1,data2,data2_sum_vars,data2_group_vars=c(),na.rm=TRUE) { lifecycle::deprecate_warn("0.2.0", "tongfen_ca_census_ct()", "tongfen_aggregate()") diff --git a/R/tongfen_ca_estimate.R b/R/tongfen_ca_estimate.R index 6fcaecb..bf24258 100644 --- a/R/tongfen_ca_estimate.R +++ b/R/tongfen_ca_estimate.R @@ -25,11 +25,12 @@ #' one of these for all variables. #' @param na.rm how to deal with NA values, default is \code{FALSE}. #' @param quiet suppress progress messages +#' @return `geometry` with the estimated values for the census variables specified by `meta` #' @export #' #' @examples -#' # Estimate a common geography for 2006 and 2016 dissemination areas in the City of Vancouver -#' # based on the geographic data and check estimation errors +#' # Estimate the 2016 population within 1 km of Toronto City Hall from dissemination area level +#' # census data #' \dontrun{ #' toronto_city_hall <- sf::st_point(c(-79.3839,43.6534)) %>% #' sf::st_sfc(crs=4326) %>% @@ -42,7 +43,7 @@ #' data <- tongfen_estimate_ca_census(toronto_city_hall,meta,level="DA",intersection_level="CT") #' #' print(paste0("Approximately ",scales::comma(data$Population,accuracy=100), -#' " people live within a 1 km radius of Toronto City.")) +#' " people live within a 1 km radius of Toronto City Hall.")) #' #'} tongfen_estimate_ca_census <- function(geometry, meta, level, @@ -85,7 +86,7 @@ tongfen_estimate_ca_census <- function(geometry, meta, level, census_data <- g } - result <- tongfen_estimate(target = geometry, source = census_data, meta = meta,na.rm = na.rm) + tongfen_estimate(target = geometry, source = census_data, meta = meta,na.rm = na.rm) } diff --git a/R/tongfen_estimate.R b/R/tongfen_estimate.R index 8342e84..75f63ce 100644 --- a/R/tongfen_estimate.R +++ b/R/tongfen_estimate.R @@ -13,7 +13,9 @@ #' @param meta metadata for variable aggregation, see `meta_for_additive_variables` and `meta_for_ca_census_vectors` for more information #' on how to construct metadata. #' @param na.rm remove NA values when aggregating, default is FALSE -#' @return `target` with estimated quantities from `source` as specified by `meta` +#' @return `target` with estimated quantities from `source` as specified by `meta`, regions in `target` +#' that don't overlap with `source` have `NA` values. Columns in `target` can't have the same name +#' as the variables to be estimated. #' @export #' #' @examples @@ -46,6 +48,12 @@ tongfen_estimate <- function(target,source,meta,na.rm=FALSE) { cut_meta(source,.) %>% mutate(var_name=paste0("v",row_number())) + clashes <- intersect(meta$data_var,names(target)) + if (length(clashes)>0) { + stop(paste0("Target already has columns named ",paste0(clashes,collapse=", "), + ", please rename or remove these before estimating.")) + } + # rename variables, st_interpolate_aw does not handle column names with special characters safe_rename_vars <- setNames(meta$data_var,meta$var_name) safe_rename_back <- setNames(meta$var_name,meta$data_var) @@ -54,30 +62,33 @@ tongfen_estimate <- function(target,source,meta,na.rm=FALSE) { i = suppressMessages(st_intersection(st_geometry(source), st_geometry(target))) idx = attr(i, "idx") - gc = which(st_is(i, "GEOMETRYCOLLECTION")) - i[gc] = st_collection_extract(i[gc], "POLYGON") + # the area only counts the polygonal parts of intersections, source regions that only + # touch a target region along a boundary don't overlap and must not contribute + area_st <- as.numeric(st_area(i)) + overlaps <- which(area_st > 0) + idx <- idx[overlaps,,drop=FALSE] + area_st <- area_st[overlaps] source <- source %>% rename(!!!safe_rename_vars) - source_area <- unclass(st_area(source)) + source_area <- as.numeric(st_area(source)) x_st <- source[idx[,1],, drop=FALSE] %>% + st_drop_geometry() %>% select(names(safe_rename_vars)) %>% - pre_scale(meta,meta_var = "var_name") %>% - mutate(...area_st = st_area(i) %>% unclass, - ...area_s = source_area[idx[,1]]) %>% - mutate(...factor = .data$...area_st/.data$...area_s) %>% - mutate(...partial = .data$...factor < 0.99) %>% - st_drop_geometry() + pre_scale(meta,meta_var = "var_name") - x_st[meta$var_name] <- lapply(x_st[meta$var_name], `*`, x_st$...factor) + # variables to sum up, including the weights for averages added when pre-scaling + sum_vars <- names(x_st) + x_st[sum_vars] <- lapply(x_st[sum_vars], `*`, area_st/source_area[idx[,1]]) - x_st <- stats::aggregate(x_st, list(idx[,2]), sum, na.rm=na.rm) + # target regions without overlap with the source don't show up here and end up as NA + x_st <- x_st %>% + mutate(!!unique_key:=idx[,2]) %>% + group_by(.data[[unique_key]]) %>% + summarize(across(all_of(sum_vars), \(x) sum(x,na.rm=na.rm)),.groups="drop") result <- target %>% - left_join(x_st %>% - select(-all_of(c("...factor", "...partial", "...area_s", "...area_st"))) %>% - rename(!!unique_key:="Group.1"), - by=unique_key) %>% + left_join(x_st,by=unique_key) %>% select(-all_of(unique_key)) %>% post_scale(meta,meta_var = "var_name") %>% rename(!!!safe_rename_back) @@ -117,17 +128,17 @@ tongfen_estimate <- function(target,source,meta,na.rm=FALSE) { #' @param target custom geography #' @param source input geography #' @param target_id name of the column in `target` table with unique id (character) -#' @return `source` with extra column with name `"target_id"` and column `...overlap_fraction` with -#' the proportion of overlap of the target geometry with the respective `target_id` +#' @return `source` with extra column with the name given by `target_id` and column `...overlap_fraction` with +#' the proportion of the area of the source region that overlaps with the region in `target` with that id #' @export #' #' @examples -#' # Estimate 2006 Populatino in the City of Vancouver dissemination ares on 2016 census geoographies +#' # Tag 2016 dissemination areas in the City of Vancouver by the 2006 census tract they overlap +#' # the most with #' \dontrun{ -#' geo1 <- cancensus::get_census("CA06",regions=list(CSD="5915022"),geo_format='sf',level='DA') +#' geo1 <- cancensus::get_census("CA06",regions=list(CSD="5915022"),geo_format='sf',level='CT') #' geo2 <- cancensus::get_census("CA16",regions=list(CSD="5915022"),geo_format='sf',level='DA') -#' meta <- meta_for_additive_variables("CA06","Population") -#' result <- tongfen_estimate(geo2 %>% rename(Population_2016=Population),geo1,meta) +#' result <- tongfen_tag_largest_overlap(geo2,geo1 %>% select(CT_2006=GeoUID),"CT_2006") #'} tongfen_tag_largest_overlap <- function(source, target, target_id) { target_geo_types <- target %>% sf::st_geometry_type() %>% unique diff --git a/R/tongfen_us.R b/R/tongfen_us.R index 19ecf35..4d426b3 100644 --- a/R/tongfen_us.R +++ b/R/tongfen_us.R @@ -59,14 +59,9 @@ get_us_ct_correspondence_path <- function(state,year){ # the blocks they are made up of get_us_ct_correspondence_2020 <- function(state,min_area_share=0.01, cache_path=getOption("tongfen.cache_path")) { - cache_path = file.path(cache_path %||% tempdir(),"us_data") - path <- get_us_ct_correspondence_path(state,2020) - local_path <- file.path(cache_path,basename(path)) - if (!file.exists(local_path)) { - if (!dir.exists(cache_path)) dir.create(cache_path, recursive = TRUE) - utils::download.file(path,local_path,quiet = TRUE) - } + local_path <- file.path(us_cache_dir(cache_path),basename(path)) + cached_download(path,local_path,check_remote=FALSE) blocks <- readr::read_delim(local_path,delim="|",progress=FALSE, col_types=readr::cols_only( STATE_2010="c",COUNTY_2010="c",TRACT_2010="c",BLK_2010="c", @@ -97,13 +92,8 @@ get_us_ct_correspondence_2020 <- function(state,min_area_share=0.01, get_us_ct_correspondence_2010 <- function(state,min_area_share=0.01, cache_path=getOption("tongfen.cache_path")){ path <- get_us_ct_correspondence_path(state,"2010") - file <- basename(path) - cache_path = file.path(cache_path %||% tempdir(),"us_data") - local_path <- file.path(cache_path,file) - if (!file.exists(local_path)) { - if (!dir.exists(cache_path)) dir.create(cache_path, recursive = TRUE) - utils::download.file(path,local_path,quiet=TRUE) - } + local_path <- file.path(us_cache_dir(cache_path),basename(path)) + cached_download(path,local_path,check_remote=FALSE) d<-readr::read_csv(local_path,progress=FALSE, col_names=c("STATE00","COUNTY00","TRACT00","GEOID00", "POP00","HU00","PART00","AREA00","AREALAND00", @@ -130,12 +120,8 @@ get_us_ct_correspondence_2010 <- function(state,min_area_share=0.01, get_us_ct_correspondence_2000 <- function(state,min_area_share=0.01, cache_path=getOption("tongfen.cache_path")){ path <- get_us_ct_correspondence_path(state,"2000") - cache_path = file.path(cache_path %||% tempdir(),"us_data") - local_path <- file.path(cache_path,basename(path)) - if (!file.exists(local_path)) { - if (!dir.exists(cache_path)) dir.create(cache_path, recursive = TRUE) - utils::download.file(path,local_path,quiet=TRUE) - } + local_path <- file.path(us_cache_dir(cache_path),basename(path)) + cached_download(path,local_path,check_remote=FALSE) d <- readr::read_fwf(local_path, readr::fwf_cols(STATE90=c(1,2),COUNTY90=c(3,5),TRACT90BASE=c(6,9), TRACT90SUF=c(10,11),PART90=c(12,12),POP90TRACT=c(13,21), @@ -202,15 +188,21 @@ get_us_ct_correspondence <- function(state, datasets, min_area_share=0.01, # the 2000 to 2010 county subdivision comparability file, covering all states get_us_county_subdivision_correspondence <- function(cache_path=getOption("tongfen.cache_path")){ require_suggested("readxl") - cache_path = file.path(cache_path %||% tempdir(),"us_data") + cache_path = us_cache_dir(cache_path) file <- "Cousub_comparability.xlsx" local_path <- file.path(cache_path,file) if (!file.exists(local_path)) { if (!dir.exists(cache_path)) dir.create(cache_path, recursive = TRUE) - tmp=tempfile(fileext = ".zip") + # download and unpack in a temporary directory, an interrupted download must not leave + # a broken file in the cache + tmp_dir <- tempfile() + dir.create(tmp_dir) + on.exit(unlink(tmp_dir,recursive=TRUE)) + tmp <- file.path(tmp_dir,"cousub_comparabilityxls.zip") path="https://www2.census.gov/geo/docs/maps-data/data/comp/cousub_comparabilityxls.zip" - utils::download.file(path,tmp,quiet=TRUE) - utils::unzip(tmp,exdir = cache_path) + utils::download.file(path,tmp,mode="wb",quiet=TRUE) + utils::unzip(tmp,files=file,exdir=tmp_dir) + file.copy(file.path(tmp_dir,file),local_path,overwrite=TRUE) } readxl::read_xlsx(local_path) } @@ -223,14 +215,10 @@ get_us_county_subdivision_correspondence <- function(cache_path=getOption("tongf # the share is taken over the larger of the two. get_us_county_subdivision_correspondence_2020 <- function(min_area_share=0.01, cache_path=getOption("tongfen.cache_path")){ - cache_path = file.path(cache_path %||% tempdir(),"us_data") path <- paste0("https://www2.census.gov/geo/docs/maps-data/data/rel2020/cousub/", "tab20_cousub20_cousub10_natl.txt") - local_path <- file.path(cache_path,basename(path)) - if (!file.exists(local_path)) { - if (!dir.exists(cache_path)) dir.create(cache_path, recursive = TRUE) - utils::download.file(path,local_path,quiet=TRUE) - } + local_path <- file.path(us_cache_dir(cache_path),basename(path)) + cached_download(path,local_path,check_remote=FALSE) d <- readr::read_delim(local_path,delim="|",progress=FALSE, col_types=readr::cols_only(GEOID_COUSUB_10="c",GEOID_COUSUB_20="c", AREALAND_COUSUB_10="d",AREAWATER_COUSUB_10="d", @@ -300,7 +288,8 @@ get_us_county_subdivision_correspondence_for <- function(state, datasets, min_ar #' geographies at the risk of separating regions that did change. No region is ever dropped, #' if all of its parts are slivers its largest part is kept. #' @param cache_path optional path to cache the relationship files in, defaults to the -#' `tongfen.cache_path` option and falls back to a temporary directory +#' `tongfen.cache_path` option. If that is not set the `tongfen.cache_path` environment variable +#' and the `custom_data_path` option are used, falling back to a temporary directory #' @return tibble with one row per census geography, a GEOID column for each requested census, #' and the common geography identified by `TongfenID` and `TongfenUID`. #' @export @@ -351,7 +340,7 @@ sumfile_for_dataset <- function(sumfile, ds){ unname(sumfile[[ds]]) } -#' Get US census data for 2000 and 2010 census on common census tract based geography +#' Get US census data for several censuses on a common geography #' #' @description #' \lifecycle{maturing} @@ -369,7 +358,8 @@ sumfile_for_dataset <- function(sumfile, ds){ #' @param meta metadata for variables to retrieve #' @param level aggregation level to return the data on. At this stage, the only valid levels are 'tract' and 'county subdivision'. #' @param survey survey to get data for, supported options is "census" -#' @param base_geo census year to use as base geography, default is `2010`. +#' @param base_geo dataset to use as base geography, for example `"dec2010"`, has to be one of +#' the datasets in `meta`. Default is `NULL`, which uses the first dataset in `meta`. #' @param min_area_share minimum share of area two geographies have to have in common to count #' as related, default is `0.01`, see \code{\link{get_tongfen_correspondence_us_census}}. #' @param sumfile summary file to read the variables from, either a single value used for all @@ -404,7 +394,7 @@ get_tongfen_us_census <- function(regions,meta,level='tract',survey="census", assert(base_geo %in% datasets,paste0("base_geo has to be one of the datasets ",paste0(datasets,collapse=", "))) invalid_datasets <- setdiff(datasets,names(valid_us_census_datasets)) assert(length(invalid_datasets)==0, paste0("Invalid datasets :",paste0(invalid_datasets,collapse = ", "))) - assert(level %in% c('tract','county subdivision'),"Only census tracts and counties are supported right now.") + assert(level %in% c('tract','county subdivision'),"Only census tracts and county subdivisions are supported right now.") assert(survey %in% c('census'),"Only census surveys are supported right now.") if (!is.null(sumfile)) { if (is.null(names(sumfile))) { diff --git a/README.md b/README.md index 38e098e..6f1e96b 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ The package is well-integrated to work with Canadian census data in two essentia ### US census data -* `get_tongfen_us_census` integrates the data acquisition (via the [**tidycensus** package](https://walker-data.com/tidycensus/index.html)) with TongFen, and adds the tongfen `method = "census.gov"` to use the US Census Bureau correspondence files for matching. +* `get_tongfen_us_census` integrates the data acquisition (via the [**tidycensus** package](https://walker-data.com/tidycensus/index.html)) with TongFen, using the US Census Bureau relationship files to build the common geography. +* `get_tongfen_correspondence_us_census` breaks out the correspondence generation from the US Census Bureau relationship files, to tongfen data that comes on census geographies but is obtained by other means. The relationship files are cached in the `us_data` folder of the cache path described above. ## Other implementations The `tongfen` package is open to add extensions for other specialized data sources, as well as extensions of existing ones. diff --git a/cran-comments.md b/cran-comments.md index c6fa36b..01aebb3 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,59 +1,21 @@ -# tongfen v.0.3.8 -## Breaking changes -- `get_tongfen_ca_census` now honours its `base_geo`, `na.rm`, `tolerance`, `crs` and - `data_transform` arguments, all of which were silently ignored -- removed the `area_mismatch_cutoff` argument from `get_tongfen_ca_census` and - `get_tongfen_correspondence_ca_census`, it never had any effect +# tongfen v.0.3.9 ## Major changes -- correspondence tables are now built via a vectorised connected components pass instead of - a row-by-row union-find, making tongfen on large geographies dramatically faster -- the "statcan" method no longer downloads census geometries it does not use -- dissolving geometries skips regions that don't need to be merged -- new `get_tongfen_correspondence_us_census`, US tract correspondence tables now reach back to - the 1990 census and county subdivisions forward to the 2020 census -- US correspondence tables no longer chain regions together over slivers, and no longer strip - leading zeros off 2020 census tract identifiers +- StatCan correspondence files are now downloaded as parquet files from a mirror, Statistics Canada + put the original files behind a browser check that blocks programmatic downloads, which broke + `method = "statcan"` ## Minor changes -- `get_tongfen_us_census` gained a `sumfile` argument, passed through to tidycensus -- `get_tongfen_correspondence_ca_census` gained a `crs` argument for the spatial intersections -- missing geographic identifiers no longer merge unrelated regions into one common geography -- fix crash when tongfen-ing census tracts across non-adjacent censuses -- US county subdivision data errors out up front on censuses it can't be matched across -- several fixes to the deprecated `get_tongfen_census_*` functions -- fix `proportional_reaggregate` ignoring all but the first base variable when `base` names a - different variable per category -- fix `estimate_tongfen_correspondence` with `method="identifier"` erroring out when every - geographic identifier matches -- packages in Suggests (`cancensus`, `tidycensus`, `readxl`) are now used conditionally, with an - actionable message when they are not installed -- faster `check_tongfen_areas` and `aggregate_correspondences` - -# tongfen v.0.3.7 -## Major changes -- accommodate factors in proportional_reaggregate -- sizable performance increases -- squish several edge case bugs - -# tongfen v.0.3.6 -## Major changs -- better downsampling that can also accommodate averages -- performance improvements -## Minor changes -- better documentation -- allow for datasets vartiables by census year for canadian data -- fix issue where some metadata might get duplicated - - -# Update v.0.3.3 -- Fix compatibility issue with changes in {sf} package -- More reliable GitHub action CRAN checks - -# Update v.0.3.2 -- Added `tongfen_estimate_ca_census` function for new CensusMapper endpoint, tying into new {cancensus} functionality. -- Custom impelementation of `tongfen_etimate` for finer control -- Fix compatibility issue with changes in {sf} package - -# Submission - v.0.3 +- combining correspondences across three or more datasets no longer runs into a cross join +- fix `proportional_reaggregate` giving wrong results when the finer level data already has values +- fix `tongfen_estimate` mishandling intersections that are geometry collections, regions that only + touch the target, and targets without overlap with the source +- fix averages getting scaled more than once in `tongfen_aggregate` when the metadata lists the same + variable name for several datasets +- fix averages with missing values being pulled toward zero when aggregating with `na.rm = TRUE`, + and "Average to" variables sharing a parent variable overwriting each other's base +- `estimate_tongfen_correspondence` no longer requires the geometry column to be named `geometry` +- `refresh = TRUE` now also refreshes the cached StatCan correspondence files +- US Census Bureau relationship files are downloaded to a temporary file before being moved to the cache +- added missing `\value` documentation for `tongfen_estimate_ca_census` and `tongfen_ca_census_ct` # Test environments * local macOS installation, R 4.6.0 diff --git a/man/get_tongfen_correspondence_us_census.Rd b/man/get_tongfen_correspondence_us_census.Rd index 71a3170..96f22eb 100644 --- a/man/get_tongfen_correspondence_us_census.Rd +++ b/man/get_tongfen_correspondence_us_census.Rd @@ -31,7 +31,8 @@ geographies at the risk of separating regions that did change. No region is ever if all of its parts are slivers its largest part is kept.} \item{cache_path}{optional path to cache the relationship files in, defaults to the -`tongfen.cache_path` option and falls back to a temporary directory} +`tongfen.cache_path` option. If that is not set the `tongfen.cache_path` environment variable +and the `custom_data_path` option are used, falling back to a temporary directory} } \value{ tibble with one row per census geography, a GEOID column for each requested census, diff --git a/man/get_tongfen_us_census.Rd b/man/get_tongfen_us_census.Rd index d6235cd..6fa8185 100644 --- a/man/get_tongfen_us_census.Rd +++ b/man/get_tongfen_us_census.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/tongfen_us.R \name{get_tongfen_us_census} \alias{get_tongfen_us_census} -\title{Get US census data for 2000 and 2010 census on common census tract based geography} +\title{Get US census data for several censuses on a common geography} \usage{ get_tongfen_us_census( regions, @@ -24,7 +24,8 @@ valid list is a vector of states, i.e. `regions = list(state=c("CA","OR"))``} \item{survey}{survey to get data for, supported options is "census"} -\item{base_geo}{census year to use as base geography, default is `2010`.} +\item{base_geo}{dataset to use as base geography, for example `"dec2010"`, has to be one of +the datasets in `meta`. Default is `NULL`, which uses the first dataset in `meta`.} \item{min_area_share}{minimum share of area two geographies have to have in common to count as related, default is `0.01`, see \code{\link{get_tongfen_correspondence_us_census}}.} diff --git a/man/meta_for_ca_census_vectors.Rd b/man/meta_for_ca_census_vectors.Rd index 3fbaa35..bd07480 100644 --- a/man/meta_for_ca_census_vectors.Rd +++ b/man/meta_for_ca_census_vectors.Rd @@ -22,6 +22,6 @@ Queries list_census_variables to obtain needed information and add in vectors ne \examples{ # Build metadata for vectors \dontrun{ -meta <- meta_for_ca_census_vectors("v_CA16_4836","v_CA16_4838","v_CA16_4899") +meta <- meta_for_ca_census_vectors(c("v_CA16_4836","v_CA16_4838","v_CA16_4899")) } } diff --git a/man/tongfen_aggregate.Rd b/man/tongfen_aggregate.Rd index e54da9d..87ce8e4 100644 --- a/man/tongfen_aggregate.Rd +++ b/man/tongfen_aggregate.Rd @@ -13,7 +13,10 @@ tongfen_aggregate( ) } \arguments{ -\item{data}{list of datasets to be aggregated} +\item{data}{named list of datasets to be aggregated. The names identify the datasets, they are +matched against the `geo_dataset` column in `meta` to pick the aggregation rules and labels +for each dataset. Without names, or with names not found in `meta`, the rules for all +datasets are applied and the variables keep their original names} \item{correspondence}{correspondence data for gluing up the datasets} @@ -35,16 +38,16 @@ aggregated dataset of class sf if base_geo is not NULL and data is of type sf or Aggregate variables secified in meta for several datasets according to correspondence. } \examples{ -# aggregate census tract level 2006 population data on common gepgraphy build through +# aggregate census tract level 2006 and 2016 population data on common geography built through # correspondence from 2006 and 2016 census tracts in the City of Vancouver. \dontrun{ regions <- list(CSD="5915022") geo1 <- cancensus::get_census("CA06",regions=regions,geo_format='sf',level='CT') geo2 <- cancensus::get_census("CA16",regions=regions,geo_format='sf',level='CT') -meta <- meta_for_additive_variables("CA06","Population") +meta <- meta_for_additive_variables(c("CA06","CA16"),"Population") correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=c('CA06','CA16'), regions=regions,level='CT') -result <- tongfen_aggregate(list(geo1 \%>\% rename(GeoUIDCA06=GeoUID), - geo2 \%>\% rename(GeoUIDCA16=GeoUID)),correspondence,meta) +result <- tongfen_aggregate(list(CA06=geo1 \%>\% rename(GeoUIDCA06=GeoUID), + CA16=geo2 \%>\% rename(GeoUIDCA16=GeoUID)),correspondence,meta) } } diff --git a/man/tongfen_ca_census_ct.Rd b/man/tongfen_ca_census_ct.Rd index 9d0ef11..0b12193 100644 --- a/man/tongfen_ca_census_ct.Rd +++ b/man/tongfen_ca_census_ct.Rd @@ -23,6 +23,10 @@ tongfen_ca_census_ct( \item{na.rm}{optional parameter to remove NA values when summing, default = `TRUE`} } +\value{ +`data2` with the variables in `data2_sum_vars` aggregated to a common geography matching `data1`, +identified by the `GeoUID` of `data1` +} \description{ \lifecycle{deprecated} diff --git a/man/tongfen_estimate.Rd b/man/tongfen_estimate.Rd index 77604ef..311f114 100644 --- a/man/tongfen_estimate.Rd +++ b/man/tongfen_estimate.Rd @@ -17,7 +17,9 @@ on how to construct metadata.} \item{na.rm}{remove NA values when aggregating, default is FALSE} } \value{ -`target` with estimated quantities from `source` as specified by `meta` +`target` with estimated quantities from `source` as specified by `meta`, regions in `target` +that don't overlap with `source` have `NA` values. Columns in `target` can't have the same name +as the variables to be estimated. } \description{ \lifecycle{maturing} diff --git a/man/tongfen_estimate_ca_census.Rd b/man/tongfen_estimate_ca_census.Rd index 6d2e5f7..0f70149 100644 --- a/man/tongfen_estimate_ca_census.Rd +++ b/man/tongfen_estimate_ca_census.Rd @@ -39,6 +39,9 @@ one of these for all variables.} \item{quiet}{suppress progress messages} } +\value{ +`geometry` with the estimated values for the census variables specified by `meta` +} \description{ \lifecycle{maturing} @@ -48,8 +51,8 @@ optionally with downsampling via `proportional_reaggregate`, to streamline estimating Canadian census data on custom geographies. } \examples{ -# Estimate a common geography for 2006 and 2016 dissemination areas in the City of Vancouver -# based on the geographic data and check estimation errors +# Estimate the 2016 population within 1 km of Toronto City Hall from dissemination area level +# census data \dontrun{ toronto_city_hall <- sf::st_point(c(-79.3839,43.6534)) \%>\% sf::st_sfc(crs=4326) \%>\% @@ -62,7 +65,7 @@ meta <- meta_for_additive_variables("CA16","Population") data <- tongfen_estimate_ca_census(toronto_city_hall,meta,level="DA",intersection_level="CT") print(paste0("Approximately ",scales::comma(data$Population,accuracy=100), - " people live within a 1 km radius of Toronto City.")) + " people live within a 1 km radius of Toronto City Hall.")) } } diff --git a/man/tongfen_tag_largest_overlap.Rd b/man/tongfen_tag_largest_overlap.Rd index f002efc..ac9313a 100644 --- a/man/tongfen_tag_largest_overlap.Rd +++ b/man/tongfen_tag_largest_overlap.Rd @@ -14,8 +14,8 @@ tongfen_tag_largest_overlap(source, target, target_id) \item{target_id}{name of the column in `target` table with unique id (character)} } \value{ -`source` with extra column with name `"target_id"` and column `...overlap_fraction` with -the proportion of overlap of the target geometry with the respective `target_id` +`source` with extra column with the name given by `target_id` and column `...overlap_fraction` with +the proportion of the area of the source region that overlaps with the region in `target` with that id } \description{ \lifecycle{maturing} @@ -23,11 +23,11 @@ the proportion of overlap of the target geometry with the respective `target_id` tags regions in `source` by `target_id` of region in `target` with the largest overlap } \examples{ -# Estimate 2006 Populatino in the City of Vancouver dissemination ares on 2016 census geoographies +# Tag 2016 dissemination areas in the City of Vancouver by the 2006 census tract they overlap +# the most with \dontrun{ -geo1 <- cancensus::get_census("CA06",regions=list(CSD="5915022"),geo_format='sf',level='DA') +geo1 <- cancensus::get_census("CA06",regions=list(CSD="5915022"),geo_format='sf',level='CT') geo2 <- cancensus::get_census("CA16",regions=list(CSD="5915022"),geo_format='sf',level='DA') -meta <- meta_for_additive_variables("CA06","Population") -result <- tongfen_estimate(geo2 \%>\% rename(Population_2016=Population),geo1,meta) +result <- tongfen_tag_largest_overlap(geo2,geo1 \%>\% select(CT_2006=GeoUID),"CT_2006") } } diff --git a/tests/testthat/test-aggregate.R b/tests/testthat/test-aggregate.R index 378192e..6934ce2 100644 --- a/tests/testthat/test-aggregate.R +++ b/tests/testthat/test-aggregate.R @@ -234,3 +234,165 @@ test_that("tongfen_aggregate: returns a plain tibble when no dataset has geometr result <- tongfen_aggregate(d$data, d$correspondence, meta) expect_false("sf" %in% class(result)) }) + +# ── same variable name in several datasets ─────────────────────────────────── + +make_average_meta <- function(ds) { + bind_rows( + make_meta("hh", "Additive"), + make_meta("avg", "Average", parent = "hh") + ) %>% + mutate(dataset = ds, geo_dataset = ds, + label = paste0(.data$variable, "_", ds)) +} + +test_that("aggregate_data_with_meta: variables listed for several datasets are only scaled once", { + data <- tibble( + group = c("A", "A"), + hh = c(10, 30), + avg = c(2, 4) + ) %>% group_by(.data$group) + + meta <- bind_rows(make_average_meta("X"), make_average_meta("Y")) + + result <- aggregate_data_with_meta(data, meta, quiet = TRUE) + expect_equal(result$hh, 40) + expect_equal(result$avg, (2 * 10 + 4 * 30) / 40) +}) + +test_that("aggregate_data_with_meta: conflicting rules for the same variable are an error", { + data <- tibble( + group = c("A", "A"), + hh = c(10, 30), + avg = c(2, 4) + ) %>% group_by(.data$group) + + meta <- bind_rows( + make_average_meta("X"), + make_meta("avg", "Additive") %>% mutate(geo_dataset = "Y") + ) + + expect_error(aggregate_data_with_meta(data, meta, quiet = TRUE), "Conflicting aggregation rules") +}) + +test_that("tongfen_aggregate: averages use the rules of their own dataset when variable names repeat", { + correspondence <- tibble( + GeoUIDX = c("x1", "x2"), + GeoUIDY = c("y1", "y1"), + TongfenID = c("x1", "x1"), + TongfenUID = c("u1", "u1") + ) + data <- list( + X = tibble(GeoUIDX = c("x1", "x2"), hh = c(10, 30), avg = c(2, 4)), + Y = tibble(GeoUIDY = "y1", hh = 50, avg = 5) + ) + meta <- bind_rows(make_average_meta("X"), make_average_meta("Y")) + + result <- tongfen_aggregate(data, correspondence, meta) + + expect_equal(nrow(result), 1L) + expect_equal(result$hh_X, 40) + expect_equal(result$avg_X, 3.5) + expect_equal(result$hh_Y, 50) + expect_equal(result$avg_Y, 5) + + # same variable name but a different rule in the second dataset + meta_mixed <- bind_rows( + make_average_meta("X"), + make_average_meta("Y") %>% mutate(rule = "Additive", parent = NA_character_) + ) + result <- tongfen_aggregate(data, correspondence, meta_mixed) + expect_equal(result$avg_X, 3.5) + expect_equal(result$avg_Y, 5) +}) + +# ── averages with missing values ───────────────────────────────────────────── + +test_that("aggregate_data_with_meta: missing averages don't count toward the weights with na.rm=TRUE", { + data <- tibble( + group = c("A", "A", "A", "B", "B"), + hh = c(10, 30, 60, 5, 15), + avg = c(2, NA, 4, NA, NA), + avg2 = c(1, 2, 3, 4, NA) + ) %>% group_by(.data$group) + + meta <- bind_rows( + make_meta("hh", "Additive"), + make_meta("avg", "Average", parent = "hh"), + make_meta("avg2", "Average", parent = "hh") + ) + + result <- aggregate_data_with_meta(data, meta, na.rm = TRUE, quiet = TRUE) + + expect_equal(names(result), c("group", "hh", "avg", "avg2")) + # the parent keeps counting all regions + expect_equal(result$hh, c(100, 20)) + # the average is taken over the regions that have a value, each average on its own + expect_equal(result$avg[1], (2 * 10 + 4 * 60) / 70) + expect_true(is.na(result$avg[2])) + expect_equal(result$avg2, c((1 * 10 + 2 * 30 + 3 * 60) / 100, 4)) + + kept <- aggregate_data_with_meta(data, meta, na.rm = FALSE, quiet = TRUE) + expect_true(all(is.na(kept$avg))) + expect_equal(kept$avg2[1], (1 * 10 + 2 * 30 + 3 * 60) / 100) + expect_true(is.na(kept$avg2[2])) +}) + +# ── "Average to" variables ─────────────────────────────────────────────────── + +make_average_to_meta <- function(variable, units = "Percentage (0-100)") { + make_meta(variable, "AverageTo", parent = "pop") %>% mutate(units = units) +} + +test_that("aggregate_data_with_meta: percentage change is averaged relative to its base", { + # population grew from 100 to 110 and from 100 to 220 + data <- tibble( + group = c("A", "A"), + pop = c(110, 220), + chg = c(10, 120) + ) %>% group_by(.data$group) + + meta <- bind_rows( + make_meta("pop", "Additive") %>% mutate(units = "Number"), + make_average_to_meta("chg") + ) + + result <- aggregate_data_with_meta(data, meta, quiet = TRUE) + expect_equal(result$pop, 330) + expect_equal(result$chg, 65) + expect_equal(result$base_pop, 200) +}) + +test_that("aggregate_data_with_meta: 'Average to' variables sharing a parent don't interfere", { + data <- tibble( + group = c("A", "A"), + pop = c(110, 220), + chg1 = c(10, 10), + chg2 = c(100, 100), + ratio = c(0.1, 1.2) + ) %>% group_by(.data$group) + + meta <- bind_rows( + make_meta("pop", "Additive") %>% mutate(units = "Number"), + make_average_to_meta("chg1"), + make_average_to_meta("chg2"), + make_average_to_meta("ratio", units = "Percentage ratio (0.0-1.0)") + ) + + result <- aggregate_data_with_meta(data, meta, quiet = TRUE) + expect_equal(result$chg1, 10) + expect_equal(result$chg2, 100) + expect_equal(result$ratio, 0.65) + # each variable comes with its own base + expect_equal(result$base_chg1, 300) + expect_equal(result$base_chg2, 165) + expect_equal(result$base_ratio, 200) + + # same results as when aggregating the variables one at a time + for (v in c("chg1", "chg2", "ratio")) { + single <- aggregate_data_with_meta(data, meta %>% filter(.data$variable %in% c("pop", v)), + quiet = TRUE) + expect_equal(single[[v]], result[[v]]) + expect_equal(single$base_pop, result[[paste0("base_", v)]]) + } +}) diff --git a/tests/testthat/test-cached-download.R b/tests/testthat/test-cached-download.R index de66488..abccf95 100644 --- a/tests/testthat/test-cached-download.R +++ b/tests/testthat/test-cached-download.R @@ -73,3 +73,37 @@ test_that("cached_download: rejects downloads not matching the md5 ETag", { expect_error(tongfen:::cached_download(remote$url, path), "corrupted") expect_false(file.exists(path)) }) + +test_that("cached_download: check_remote = FALSE uses the cached file without contacting the remote", { + remote <- local_remote("a") + path <- file.path(tempfile(), "file.txt") + local_mocked_bindings(remote_etag = function(url) stop("remote should not be checked")) + expect_equal(tongfen:::cached_download(remote$url, path, check_remote = FALSE), path) + expect_equal(readLines(path), "a") + expect_false(file.exists(paste0(path, ".etag"))) + # only the downloaded file ends up in the cache directory + expect_equal(list.files(dirname(path)), "file.txt") + + # the cached file is used as is, also in a new session and if the remote changed + writeLines("b", remote$path) + tongfen:::cached_download(remote$url, path, check_remote = FALSE) + expect_equal(readLines(path), "a") + + tongfen:::cached_download(remote$url, path, refresh = TRUE, check_remote = FALSE) + expect_equal(readLines(path), "b") +}) + +test_that("cached_download: failed downloads don't leave files in the cache", { + missing <- paste0("file://", normalizePath(tempdir(), winslash = "/"), "/does-not-exist.txt") + path <- file.path(tempfile(), "file.txt") + expect_error(suppressWarnings(tongfen:::cached_download(missing, path, check_remote = FALSE))) + expect_false(file.exists(path)) + expect_equal(list.files(dirname(path)), character(0)) +}) + +test_that("us_cache_dir: uses the given path and falls back to the tongfen cache directory", { + local_mocked_bindings(tongfen_cache_dir = function() "tongfen/cache") + expect_equal(tongfen:::us_cache_dir("some/path"), file.path("some/path", "us_data")) + expect_equal(tongfen:::us_cache_dir(NULL), file.path("tongfen/cache", "us_data")) + expect_equal(tongfen:::us_cache_dir(""), file.path("tongfen/cache", "us_data")) +}) diff --git a/tests/testthat/test-correspondence-estimate.R b/tests/testthat/test-correspondence-estimate.R index f1091d8..b237748 100644 --- a/tests/testthat/test-correspondence-estimate.R +++ b/tests/testthat/test-correspondence-estimate.R @@ -72,3 +72,38 @@ test_that("tongfen_tag_largest_overlap: tags each source region by its containin expect_equal(tagged$t, c("t1", "t1", "t2", "t2")) expect_equal(as.numeric(tagged[["...overlap_fraction"]]), rep(1, 4), tolerance = 1e-9) }) + +test_that("estimate_tongfen_correspondence: geometry column can go by any name", { + geo_a <- grid_4("idA") + geo_b <- st_sf(idB = c("b1", "b2"), + geometry = st_sfc(sq(0, 0, 2, 1), sq(0, 1, 2, 1), crs = 3347)) + expected <- estimate_tongfen_correspondence(list(geo_a, geo_b), c("idA", "idB"), + tolerance = 0.01) + + st_geometry(geo_a) <- "geom" + st_geometry(geo_b) <- "shape" + correspondence <- estimate_tongfen_correspondence(list(geo_a, geo_b), c("idA", "idB"), + tolerance = 0.01) + expect_equal(correspondence, expected) +}) + +test_that("estimate_tongfen_single_correspondence: robust option repairs invalid geometries", { + geo_a <- grid_4("idA") + geo_b <- st_sf(idB = c("b1", "b2"), + geometry = st_sfc(sq(0, 0, 2, 1), sq(0, 1, 2, 1), crs = 3347)) + expected <- tongfen:::estimate_tongfen_single_correspondence(geo_a, geo_b, "idA", "idB", + tolerance = 0.01) + + robust <- tongfen:::estimate_tongfen_single_correspondence(geo_a, geo_b, "idA", "idB", + tolerance = 0.01, robust = TRUE) + expect_equal(robust, expected) + + # same region as b1, but the ring crosses over itself along the bottom edge + bowtie <- st_polygon(list(cbind(c(0, 2, 2, 0, 0, 1, 0), c(0, 0, 1, 1, 0, 0, 0)))) + geo_invalid <- st_sf(idB = c("b1", "b2"), + geometry = st_sfc(bowtie, sq(0, 1, 2, 1), crs = 3347)) + expect_false(all(st_is_valid(geo_invalid))) + robust <- tongfen:::estimate_tongfen_single_correspondence(geo_a, geo_invalid, "idA", "idB", + tolerance = 0.01, robust = TRUE) + expect_equal(robust, expected) +}) diff --git a/tests/testthat/test-estimate.R b/tests/testthat/test-estimate.R index c50efe7..48d75a3 100644 --- a/tests/testthat/test-estimate.R +++ b/tests/testthat/test-estimate.R @@ -45,3 +45,121 @@ test_that("tongfen_estimate preserves missing-value aggregation semantics", { expect_true(is.na(keep_na$total)) expect_equal(drop_na$total, 5) }) + +test_that("tongfen_estimate ignores source regions that only touch the target", { + source <- sf::st_sf( + total = c(10, NA_real_), + geometry = sf::st_sfc(square(0, 1), square(1, 2), crs = 3347) + ) + target <- sf::st_sf( + geometry = sf::st_sfc(square(0, 1), crs = 3347) + ) + meta <- meta_for_additive_variables("synthetic", c(total = "total")) + + # the second source region shares a boundary with the target but does not overlap it + expect_equal(tongfen_estimate(target, source, meta, na.rm = FALSE)$total, 10) + expect_equal(tongfen_estimate(target, source, meta, na.rm = TRUE)$total, 10) +}) + +test_that("tongfen_estimate handles intersections that are geometry collections", { + poly <- function(...) { + coords <- matrix(c(...), ncol = 2, byrow = TRUE) + sf::st_polygon(list(rbind(coords, coords[1, ]))) + } + # comb shaped source, overlaps the target in two separate squares of area 0.5 + # and touches it along a line in between + comb <- poly(0,1, 0,0.5, 1,0.5, 1,1, 2,1, 2,0.5, 3,0.5, 3,1, 3,2, 0,2) + source <- sf::st_sf(total = 100, geometry = sf::st_sfc(comb, crs = 3347)) + target <- sf::st_sf(geometry = sf::st_sfc(poly(0,0, 3,0, 3,1, 0,1), crs = 3347)) + meta <- meta_for_additive_variables("synthetic", c(total = "total")) + + intersection <- sf::st_intersection(sf::st_geometry(source), sf::st_geometry(target)) + expect_equal(as.character(sf::st_geometry_type(intersection)), "GEOMETRYCOLLECTION") + + result <- tongfen_estimate(target, source, meta) + + expect_equal(nrow(result), 1L) + expect_equal(result$total, 100 * 1 / as.numeric(sf::st_area(source))) +}) + +test_that("tongfen_estimate returns NA for target regions without overlap", { + source <- sf::st_sf( + total = 10, + geometry = sf::st_sfc(square(0, 1), crs = 3347) + ) + target <- sf::st_sf( + region = c("inside", "outside"), + geometry = sf::st_sfc(square(0, 0.5), square(5, 6), crs = 3347) + ) + meta <- meta_for_additive_variables("synthetic", c(total = "total")) + + result <- tongfen_estimate(target, source, meta) + expect_equal(result$region, c("inside", "outside")) + expect_equal(result$total, c(5, NA_real_)) + + # no overlap at all + result <- tongfen_estimate(target[2, ], source, meta) + expect_s3_class(result, "sf") + expect_equal(nrow(result), 1L) + expect_true(is.na(result$total)) +}) + +test_that("tongfen_estimate estimates parent weighted averages", { + source <- sf::st_sf( + hh = c(10, 30), + avg = c(2, 4), + geometry = sf::st_sfc(square(0, 1), square(1, 2), crs = 3347) + ) + target <- sf::st_sf( + geometry = sf::st_sfc(square(0, 2), crs = 3347) + ) + meta <- tibble::tibble( + variable = c("hh", "avg"), dataset = "synthetic", label = c("hh", "avg"), + type = "Manual", aggregation = c("Additive", "Average of hh"), + rule = c("Additive", "Average"), geo_dataset = "synthetic", + parent = c(NA, "hh") + ) + + result <- tongfen_estimate(target, source, meta) + expect_equal(result$hh, 40) + expect_equal(result$avg, 3.5) +}) + +test_that("tongfen_estimate complains about target columns that clash with the estimates", { + source <- sf::st_sf( + total = 10, + geometry = sf::st_sfc(square(0, 1), crs = 3347) + ) + target <- sf::st_sf( + total = 1, + geometry = sf::st_sfc(square(0, 0.5), crs = 3347) + ) + meta <- meta_for_additive_variables("synthetic", c(total = "total")) + + expect_error(tongfen_estimate(target, source, meta), "already has columns named total") +}) + +test_that("tongfen_estimate takes averages over the regions that have a value with na.rm = TRUE", { + source <- sf::st_sf( + hh = c(10, 30, 60), + avg = c(2, NA, 4), + geometry = sf::st_sfc(square(0, 1), square(1, 2), square(2, 3), crs = 3347) + ) + target <- sf::st_sf( + geometry = sf::st_sfc(square(0, 3), square(0.5, 2), crs = 3347) + ) + meta <- tibble::tibble( + variable = c("hh", "avg"), dataset = "synthetic", label = c("hh", "avg"), + type = "Manual", aggregation = c("Additive", "Average of hh"), + rule = c("Additive", "Average"), geo_dataset = "synthetic", + parent = c(NA, "hh") + ) + + result <- tongfen_estimate(target, source, meta, na.rm = TRUE) + expect_equal(names(result), c("hh", "avg", "geometry")) + expect_equal(result$hh, c(100, 35)) + # the second target only gets a value from half of the first source region + expect_equal(result$avg, c((2 * 10 + 4 * 60) / 70, 2)) + + expect_true(all(is.na(tongfen_estimate(target, source, meta, na.rm = FALSE)$avg))) +}) diff --git a/tests/testthat/test-proportional-reaggregate.R b/tests/testthat/test-proportional-reaggregate.R index fc6a2ff..3f4a8a2 100644 --- a/tests/testthat/test-proportional-reaggregate.R +++ b/tests/testthat/test-proportional-reaggregate.R @@ -312,3 +312,63 @@ test_that("proportional_reaggregate: each category uses its own base variable", expect_equal(result$v1, c(50, 0), tolerance = 1e-9) expect_equal(result$v2, c(0, 50), tolerance = 1e-9) }) + +# ── Existing child values are compared to the parent per category ───────────── + +test_that("proportional_reaggregate: several categories with existing child values match their parent totals", { + parent <- tibble( + parent_id = "P1", + pop = 100, + x = 50, + y = 6 + ) %>% as_point_sf() + + child <- tibble( + child_id = c("C1", "C2", "C3"), + parent_id = "P1", + pop = c(20, 30, 50), + x = c(10, 15, 20), + y = c(1, 2, 3) + ) %>% as_point_sf() + + result <- proportional_reaggregate( + child, parent, + geo_match = c("parent_id" = "parent_id"), + categories = c("x", "y"), + base = "pop" + ) %>% + sf::st_drop_geometry() %>% + arrange(.data$child_id) + + # x is 5 short of the parent and gets topped up by population share, y already matches + expect_equal(result$x, c(11, 16.5, 22.5), tolerance = 1e-9) + expect_equal(result$y, c(1, 2, 3), tolerance = 1e-9) +}) + +test_that("proportional_reaggregate: missing child values are filled so that children sum to parent", { + parent <- tibble( + parent_id = "P1", + pop = 300, + x = 50 + ) %>% as_point_sf() + + child <- tibble( + child_id = c("C1", "C2", "C3"), + parent_id = "P1", + pop = c(100, 100, 100), + x = c(10, NA, 20) + ) %>% as_point_sf() + + result <- proportional_reaggregate( + child, parent, + geo_match = c("parent_id" = "parent_id"), + categories = "x", + base = "pop" + ) %>% + sf::st_drop_geometry() %>% + arrange(.data$child_id) + + # the 20 missing from the children get distributed evenly + expect_equal(result$x, c(10, 0, 20) + 20/3, tolerance = 1e-9) + expect_equal(sum(result$x), 50, tolerance = 1e-9) +}) From c96e995882031754f860b76d7de45493cc397110 Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 16:50:03 -0700 Subject: [PATCH 5/7] feat: detect and correct geocoding anomalies in timelines (#21) Geocoding varies between censuses, the same dwellings can get assigned to different neighbouring regions in different years. On a common geography this shows up as a surprising drop in one region that is offset by a jump in a neighbour. Adds experimental functions to check for this and to join the affected regions on demand, following the method from https://doodles.mountainmath.ca/posts/2024-07-26-geocoding-errors-in-aggregate-data/ - tongfen_detect_anomalies lists candidate regions with their best neighbour - tongfen_anomaly_joins iterates joins until no more regions qualify - tongfen_join_regions applies the joins to data on a common geography - tongfen_join_correspondence applies them to a correspondence for use with tongfen_aggregate The neighbour graph is contracted between rounds instead of recomputed from the joined geometries, so joined regions keep all neighbours of their parts. Missing values are never surprising and don't make up for a drop in a neighbour, and ties are broken by identifier so results don't depend on the order of the regions. Hoists intersects_pairs into helpers.R for reuse, adds a vignette and tests including a comparison against the original implementation. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01A4ixWqz6d5wpYg2DkoT4pm --- NAMESPACE | 4 + NEWS.md | 8 + R/helpers.R | 9 + R/tongfen.R | 12 +- R/tongfen_anomalies.R | 563 ++++++++++++++++++++++++++ man/tongfen_anomaly_joins.Rd | 93 +++++ man/tongfen_detect_anomalies.Rd | 102 +++++ man/tongfen_join_correspondence.Rd | 43 ++ man/tongfen_join_regions.Rd | 54 +++ pkgdown/_pkgdown.yml | 4 + tests/testthat/test-anomalies.R | 622 +++++++++++++++++++++++++++++ vignettes/tongfen_anomalies.Rmd | 202 ++++++++++ 12 files changed, 1706 insertions(+), 10 deletions(-) create mode 100644 R/tongfen_anomalies.R create mode 100644 man/tongfen_anomaly_joins.Rd create mode 100644 man/tongfen_detect_anomalies.Rd create mode 100644 man/tongfen_join_correspondence.Rd create mode 100644 man/tongfen_join_regions.Rd create mode 100644 tests/testthat/test-anomalies.R create mode 100644 vignettes/tongfen_anomalies.Rmd diff --git a/NAMESPACE b/NAMESPACE index 1ba1cc6..16f2796 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,9 +14,13 @@ export(meta_for_additive_variables) export(meta_for_ca_census_vectors) export(proportional_reaggregate) export(tongfen_aggregate) +export(tongfen_anomaly_joins) export(tongfen_ca_census_ct) +export(tongfen_detect_anomalies) export(tongfen_estimate) export(tongfen_estimate_ca_census) +export(tongfen_join_correspondence) +export(tongfen_join_regions) export(tongfen_tag_largest_overlap) import(dplyr) import(rlang) diff --git a/NEWS.md b/NEWS.md index 6019538..4aa5878 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,13 @@ # tongfen v.0.3.9 ## Major changes +- new experimental functions to detect and correct for likely geocoding anomalies in timelines on a + common geography, where the same dwellings got assigned to different neighbouring regions in + different years. This shows up as a surprising drop in one region that is offset by a jump in a + neighbouring region. `tongfen_detect_anomalies` lists the candidate regions, + `tongfen_anomaly_joins` determines the regions to join, `tongfen_join_regions` joins them in + data that already is on a common geography and `tongfen_join_correspondence` joins them in a + correspondence for use with `tongfen_aggregate`. See the new "Geocoding anomalies in TongFen + timelines" vignette for details - StatCan correspondence files are now downloaded as parquet files from a mirror, Statistics Canada put the original files behind a browser check that blocks programmatic downloads, which broke `method = "statcan"`. Cached files are checked against the mirror once per session and downloaded diff --git a/R/helpers.R b/R/helpers.R index cee0969..2e15131 100644 --- a/R/helpers.R +++ b/R/helpers.R @@ -191,6 +191,15 @@ assert <- function (expr, error) { if (! expr) stop(error, call. = FALSE) } +# Pairs of intersecting geometries as row indices into `x` and `y`. The sparse index +# list is turned into a tibble directly, `as.data.frame()` on an empty result drops +# the columns we join on. +intersects_pairs <- function(x, y) { + m <- sf::st_intersects(x, y, sparse = TRUE) + tibble(row.id = rep(seq_along(m), lengths(m)), + col.id = as.integer(unlist(m))) +} + # Dissolve the geometries of `data` by `grouping_var`, the geometric equivalent # of `summarize()`. Groups holding a single geometry - the bulk of the groups diff --git a/R/tongfen.R b/R/tongfen.R index 34a8e05..c8f2070 100644 --- a/R/tongfen.R +++ b/R/tongfen.R @@ -547,16 +547,8 @@ estimate_tongfen_single_correspondence <- function(geo1,geo2,geo1_uid,geo2_uid, cgeo1 <- geo1 %>% robust_tolerance_buffer(tolerance = tolerance) cgeo2 <- geo2 %>% robust_tolerance_buffer(tolerance = tolerance) - # Both intersections are necessary (buffered cgeo1 vs geo2, and cgeo2 vs geo1). The - # sparse index list is turned into a tibble directly, `as.data.frame()` on an empty - # result drops the columns we join on. - intersects_pairs <- function(x, y) { - m <- st_intersects(x, y, sparse = TRUE) - tibble(row.id = rep(seq_along(m), lengths(m)), - col.id = as.integer(unlist(m))) - } - - i1 <- intersects_pairs(cgeo1, geo2) %>% + # Both intersections are necessary (buffered cgeo1 vs geo2, and cgeo2 vs geo1). + i1 <-intersects_pairs(cgeo1, geo2) %>% left_join(id1, by = c("row.id" = "id1")) %>% left_join(id2, by = c("col.id" = "id2")) %>% select(-"row.id",-"col.id") diff --git a/R/tongfen_anomalies.R b/R/tongfen_anomalies.R new file mode 100644 index 0000000..e4fcd48 --- /dev/null +++ b/R/tongfen_anomalies.R @@ -0,0 +1,563 @@ +# Geocoding of census data varies over time, the same dwelling units can get assigned to +# different, neighbouring, regions in different years. In a timeline on a common geography +# this shows up as a surprising drop in one region that is offset by a jump in a neighbouring +# region. The functions in this file detect such patterns and join the affected regions, +# extending the idea behind TongFen from changing boundaries to data that got assigned across +# boundaries. See https://doodles.mountainmath.ca/posts/2024-07-26-geocoding-errors-in-aggregate-data/ +# for background, the implementation follows the method described there. + +anomaly_params <- function(rel_scale,abs_scale,p,surprise_cutoff,total_surprise_cutoff, + cutoff_fact,surprise_reduction_const,sum_fact) { + params <- list(rel_scale=rel_scale,abs_scale=abs_scale,p=p, + surprise_cutoff=surprise_cutoff,total_surprise_cutoff=total_surprise_cutoff, + cutoff_fact=cutoff_fact,surprise_reduction_const=surprise_reduction_const, + sum_fact=sum_fact) + valid <- vapply(params,\(x) is.numeric(x) && length(x)==1 && !is.na(x),logical(1)) + assert(all(valid),paste0("Need a single number for ",paste0(names(params)[!valid],collapse=", "),".")) + assert(rel_scale>0 && abs_scale>0 && p>0,"rel_scale, abs_scale and p have to be positive.") + params +} + +# Surprise of a change in a count, on a scale from 0 to 1. Only decreases are surprising, a +# relative decrease of `rel_scale` and an absolute decrease of `abs_scale` each are half way +# to full surprise, and it takes both for a change to be surprising. The relative change is +# taken with respect to `base`, which does not have to be the count the change started from. +anomaly_surprise <- function(change,base,rel_scale,abs_scale) { + decrease_surprise <- function(x,scale) { + r <- x + r[] <- 0 + decrease <- is.finite(x) & x<=0 + r[decrease] <- 1-0.5^(-x[decrease]/scale) + r + } + decrease_surprise(change/base,rel_scale)*decrease_surprise(change,abs_scale) +} + +# One round of looking for regions to join. `V` is a matrix of counts with a row per region +# and a column per year, `edges` a two column matrix with the row indices of neighbouring +# regions. Returns the candidate regions, most surprising first, with the neighbour that +# takes away most of the surprise when joined and if that is enough to join them. +anomaly_round <- function(V,edges,params) { + nt <- ncol(V) + change <- V[,-1,drop=FALSE]-V[,-nt,drop=FALSE] + base <- V[,-nt,drop=FALSE] + count_surprise <- \(S) rowSums(S>params$surprise_cutoff) + total_surprise <- \(S) rowSums(S^params$p)^(1/params$p) + + S <- anomaly_surprise(change,base,params$rel_scale,params$abs_scale) + count <- count_surprise(S) + total <- total_surprise(S) + + index <- which(count>0 & total>params$total_surprise_cutoff) + index <- index[order(-count[index],-total[index],-index)] + result <- tibble(index=index, + surprise_count=as.integer(count[index]), + surprise_total=total[index], + period=max.col(S[index,,drop=FALSE],ties.method="first"), + neighbour=NA_integer_, + surprise_total_joined=NA_real_, + join=FALSE) + + from <- c(edges[,1],edges[,2]) + to <- c(edges[,2],edges[,1]) + keep <- from %in% index + from <- from[keep] + to <- to[keep] + if (length(from)==0) return(result) + + # Joining a region with flat counts lowers the surprise just by growing the denominator + # of the relative change. To keep the surprise comparable the change of the joined region + # is measured against the counts of the candidate region alone. + # A neighbour with unknown change can't make up for a surprising change. + change_neighbour <- change[to,,drop=FALSE] + change_neighbour[is.na(change_neighbour)] <- 0 + S_joined <- anomaly_surprise(change[from,,drop=FALSE]+change_neighbour, + base[from,,drop=FALSE],params$rel_scale,params$abs_scale) + count_joined <- count_surprise(S_joined) + total_joined <- total_surprise(S_joined) + + o <- order(from,count_joined,total_joined,to) + best <- o[!duplicated(from[o])] + best <- best[match(result$index,from[best])] + + result$neighbour <- to[best] + result$surprise_total_joined <- total_joined[best] + join <- result$surprise_total_joined < params$cutoff_fact*result$surprise_total | + result$surprise_total-result$surprise_total_joined > params$surprise_reduction_const | + result$surprise_total_joined < params$cutoff_fact*params$sum_fact* + (result$surprise_total+total[result$neighbour]) + result$join <- !is.na(join) & join + result +} + +# Joins regions until there are no more pairs of regions left to join. Returns the group +# each region ended up in and the round in which it first got joined to another region. +anomaly_joins <- function(V,edges,params) { + membership <- seq_len(nrow(V)) + round_joined <- rep(NA_integer_,nrow(V)) + round <- 0L + repeat { + candidates <- anomaly_round(V,edges,params) + candidates <- candidates[candidates$join,] + # regions can only be part of one join per round, the most surprising ones go first + matched <- logical(nrow(V)) + target <- seq_len(nrow(V)) + for (r in seq_len(nrow(candidates))) { + i <- candidates$index[r] + j <- candidates$neighbour[r] + if (matched[i] || matched[j]) next + matched[c(i,j)] <- TRUE + target[max(i,j)] <- min(i,j) + } + if (!any(matched)) break + + round <- round+1L + round_joined[matched[membership] & is.na(round_joined)] <- round + + # contract the joined regions, the neighbours of a joined region are the neighbours + # of the regions it is made up of + target <- match(target,sort(unique(target))) + membership <- target[membership] + V <- rowsum(V,target) + edges <- cbind(target[edges[,1]],target[edges[,2]]) + edges <- edges[edges[,1]!=edges[,2],,drop=FALSE] + edges <- unique(cbind(pmin(edges[,1],edges[,2]),pmax(edges[,1],edges[,2]))) + } + list(membership=membership,round=round_joined) +} + +# Neighbouring regions as two column matrix of row indices into `ids`, each pair of +# neighbours is listed once. +anomaly_neighbours <- function(data,ids,neighbours) { + if (is.null(neighbours)) { + assert("sf" %in% class(data), + "Need data of class sf to determine neighbouring regions, alternatively specify the neighbours.") + geometry <- sf::st_geometry(data) + pairs <- intersects_pairs(geometry,geometry) + from <- pairs$row.id + to <- pairs$col.id + } else if (is.data.frame(neighbours)) { + assert(ncol(neighbours)>=2, + "Need neighbours to have two columns with the identifiers of neighbouring regions.") + from <- match(as.character(neighbours[[1]]),ids) + to <- match(as.character(neighbours[[2]]),ids) + } else if (is.list(neighbours)) { + # neighbours list like the ones from the spdep package, regions without neighbours hold a 0 + region_ids <- attr(neighbours,"region.id") %||% names(neighbours) %||% ids + assert(length(region_ids)==length(neighbours), + "Need neighbours to have an entry for each region.") + from <- rep(seq_along(neighbours),lengths(neighbours)) + to <- as.integer(unlist(neighbours)) + keep <- !is.na(to) & to>0 + from <- match(as.character(region_ids)[from[keep]],ids) + to <- match(as.character(region_ids)[to[keep]],ids) + } else { + stop("Don't know how to interpret neighbours, need a table with pairs of identifiers or a neighbours list.") + } + keep <- !is.na(from) & !is.na(to) & from!=to + unique(cbind(pmin(from[keep],to[keep]),pmax(from[keep],to[keep]))) +} + +anomaly_input <- function(data,variables,id,neighbours) { + assert(is.character(variables) && length(variables)>=2, + "Need at least two variables making up the timeline to look for anomalies.") + assert(is.character(id) && length(id)==1,"Need id to be the name of the identifier column.") + missing_columns <- setdiff(c(id,variables),names(data)) + assert(length(missing_columns)==0, + paste0("Did not find ",paste0(missing_columns,collapse=", ")," in data.")) + d <- data %>% ungroup() + if ("sf" %in% class(d)) d <- d %>% sf::st_drop_geometry() + not_numeric <- variables[!vapply(variables,\(v) is.numeric(d[[v]]),logical(1))] + assert(length(not_numeric)==0, + paste0("Variables have to be numeric, got ",paste0(not_numeric,collapse=", "),".")) + id_values <- d[[id]] + ids <- as.character(id_values) + assert(!anyNA(ids) && !anyDuplicated(ids), + paste0("Need ",id," to uniquely identify the regions in data.")) + + V <- matrix(as.numeric(unlist(d[variables],use.names=FALSE)),ncol=length(variables)) + edges <- anomaly_neighbours(data,ids,neighbours) + + # Ties are broken by the order of the regions, sort by identifier so that the result + # does not depend on the order the regions come in. + o <- order(ids,method="radix") + position <- order(o) + edges <- cbind(position[edges[,1]],position[edges[,2]]) + list(V=V[o,,drop=FALSE], + edges=cbind(pmin(edges[,1],edges[,2]),pmax(edges[,1],edges[,2])), + ids=ids[o], + id_values=id_values[o]) +} + + +#' Detect likely geocoding anomalies in timelines on a common geography +#' +#' @description +#' \lifecycle{experimental} +#' +#' TongFen is only as good as the geocoding that assigned the underlying data to geographic regions +#' in the first place. Geocoding varies over time, and the same dwelling units, and the people living +#' in them, can get assigned to different neighbouring regions in different years. In a timeline +#' on a common geography this shows up as a surprising drop in one region that is offset by +#' a corresponding jump in a neighbouring region. +#' +#' This function lists the candidate regions with surprising drops in the given count variable, +#' together with the neighbouring region that takes away most of the surprise when both are joined. +#' Use it to check for possible problems and to calibrate the parameters before joining regions with +#' `tongfen_anomaly_joins`. Not all surprising drops are due to geocoding problems, a drop that is +#' not complemented by a neighbouring region is likely real. +#' +#' The surprise of a change between two consecutive years ranges from 0 to 1. Only decreases are surprising, +#' the surprise is the product of the surprise of the relative and of the absolute decrease, so that it takes +#' a decrease that is large in both relative and absolute terms to be surprising. The total surprise of a +#' region is the `p`-norm of the surprises across all changes in the timeline. +#' +#' A candidate region and its neighbour are flagged for joining if joining reduces the total surprise of the +#' candidate region to below `cutoff_fact` times its total surprise, or reduces it by more than +#' `surprise_reduction_const`, or reduces it to below `cutoff_fact * sum_fact` times the sum of +#' the total surprises of both regions. To keep this comparable the surprise after joining is computed +#' from the change of the joined regions relative to the counts of the candidate region alone. +#' +#' @param data data on a common geography, with one row per region, for example as returned by +#' `tongfen_aggregate` or `get_tongfen_ca_census`. Needs to be of class sf unless `neighbours` is specified. +#' @param variables names of the columns holding the timeline of a count variable like population or +#' dwellings, in temporal order. Changes from or to a missing value are not surprising and don't make up for +#' surprising changes in neighbouring regions, and joined regions are missing a value if one of the regions they +#' are made up of is. Replace missing values by zero beforehand if they stand for regions where nothing got counted +#' @param id name of the column that uniquely identifies the regions, default is "TongfenID" +#' @param neighbours optional, neighbouring regions as a table with the identifiers of pairs of neighbouring +#' regions in the first two columns, or as a neighbours list like the ones returned by `spdep::poly2nb`. +#' By default all regions with intersecting geometries are neighbours, which can miss neighbours +#' if the geometries have been simplified and don't share their boundaries any more. +#' @param rel_scale relative decrease that is half way to full surprise, default is `0.25` for a 25\% drop +#' @param abs_scale absolute decrease that is half way to full surprise, default is `200` +#' @param p exponent of the norm used to combine the surprises across the timeline into the total surprise. +#' Large values focus on the most surprising change, 1 adds up the surprises of all changes, default is `4` +#' @param surprise_cutoff changes with larger surprise count as surprising, default is `0.15`. Only +#' regions with at least one surprising change are candidates +#' @param total_surprise_cutoff only regions with larger total surprise are candidates, default is `0.75` +#' @param cutoff_fact join regions if the total surprise after joining is lower than this share of the +#' total surprise of the candidate region, default is `0.6` +#' @param surprise_reduction_const join regions if joining lowers the total surprise by more than this, +#' default is `0.15` +#' @param sum_fact join regions if the total surprise after joining is lower than `cutoff_fact * sum_fact` +#' times the sum of the total surprises of both regions, default is `0.7` +#' @return A tibble with one row for each candidate region, most surprising first, with the identifier of the +#' region, the number of surprising changes `surprise_count`, the total surprise `surprise_total`, +#' the `period` with the most surprising change, the identifier of the `neighbour` that takes away most of +#' the surprise, the total surprise `surprise_total_joined` after joining both and `join` indicating if both +#' regions qualify to get joined. +#' @export +#' +#' @examples +#' # Check 2001 through 2021 dissemination area level population timelines in the +#' # City of Vancouver for possible geocoding problems +#' \dontrun{ +#' datasets <- c("CA01","CA06","CA11","CA16","CA21") +#' meta <- meta_for_additive_variables(datasets,"Population") +#' data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") +#' +#' anomalies <- tongfen_detect_anomalies(data,paste0("Population_",datasets)) +#' } +tongfen_detect_anomalies <- function(data,variables,id="TongfenID",neighbours=NULL, + rel_scale=0.25,abs_scale=200,p=4, + surprise_cutoff=0.15,total_surprise_cutoff=0.75, + cutoff_fact=0.6,surprise_reduction_const=0.15,sum_fact=0.7) { + params <- anomaly_params(rel_scale,abs_scale,p,surprise_cutoff,total_surprise_cutoff, + cutoff_fact,surprise_reduction_const,sum_fact) + input <- anomaly_input(data,variables,id,neighbours) + candidates <- anomaly_round(input$V,input$edges,params) + periods <- paste0(variables[-length(variables)],"-",variables[-1]) + + tibble(!!id:=input$id_values[candidates$index], + surprise_count=candidates$surprise_count, + surprise_total=candidates$surprise_total, + period=periods[candidates$period], + neighbour=input$id_values[candidates$neighbour], + surprise_total_joined=candidates$surprise_total_joined, + join=candidates$join) +} + + +#' Determine regions to join to correct for likely geocoding anomalies +#' +#' @description +#' \lifecycle{experimental} +#' +#' Looks for regions with surprising drops in the timeline of a count variable that are complemented by +#' a neighbouring region, as explained in `tongfen_detect_anomalies`, and joins them. This gets +#' repeated on the joined regions until there are no more regions left that qualify to get joined. +#' In each round a region only gets joined with one other region, the most surprising regions go first. +#' +#' Joining regions trades geographic detail for timelines that are consistent over time. The parameters +#' control how aggressively regions get joined and are best calibrated on the data at hand, erring on the side of +#' joining too few regions risks keeping geocoding problems, erring on the other side risks removing real +#' changes and needlessly coarsens the geography. +#' +#' The result can be used to join the regions via `tongfen_join_regions`, or to update a correspondence +#' via `tongfen_join_correspondence`. +#' +#' @inheritParams tongfen_detect_anomalies +#' @return A tibble with one row for each region that gets joined with other regions, with the identifier +#' of the region, the identifier of the joined region it becomes part of in the column named like the +#' identifier with suffix `_joined`, by default `TongfenID_joined`, and the `round` in which the region +#' first got joined to another region. The identifier of a joined region is the smallest +#' identifier of the regions it is made up of. +#' @export +#' +#' @examples +#' # Correct 2001 through 2021 dissemination area level population timelines in the +#' # City of Vancouver for likely geocoding problems +#' \dontrun{ +#' datasets <- c("CA01","CA06","CA11","CA16","CA21") +#' meta <- meta_for_additive_variables(datasets,"Population") +#' data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") +#' +#' joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +#' corrected_data <- tongfen_join_regions(data,joins,meta) +#' } +tongfen_anomaly_joins <- function(data,variables,id="TongfenID",neighbours=NULL, + rel_scale=0.25,abs_scale=200,p=4, + surprise_cutoff=0.15,total_surprise_cutoff=0.75, + cutoff_fact=0.6,surprise_reduction_const=0.15,sum_fact=0.7) { + params <- anomaly_params(rel_scale,abs_scale,p,surprise_cutoff,total_surprise_cutoff, + cutoff_fact,surprise_reduction_const,sum_fact) + input <- anomaly_input(data,variables,id,neighbours) + result <- anomaly_joins(input$V,input$edges,params) + + membership <- result$membership + joined <- tabulate(membership)[membership]>1 + # region with the smallest identifier in each group + o <- order(membership,input$ids,method="radix") + first <- !duplicated(membership[o]) + smallest <- integer(max(membership,0L)) + smallest[membership[o][first]] <- o[first] + + joins <- tibble(!!id:=input$id_values[joined], + !!paste0(id,"_joined"):=input$id_values[smallest[membership[joined]]], + round=result$round[joined]) + joins[order(input$ids[smallest[membership[joined]]],input$ids[joined],method="radix"),] +} + + +# Combine the TongfenUIDs of regions that get joined. A TongfenUID lists the identifiers +# making up a region as ":, :". +merge_tongfen_uids <- function(uids) { + uids <- unique(uids[!is.na(uids)]) + # the result must not depend on the order the regions come in + uids <- uids[order(uids,method="radix")] + parts <- unlist(strsplit(uids," ",fixed=TRUE)) + parts <- parts[nzchar(parts)] + split <- regexpr(":",parts,fixed=TRUE) + # not in the expected format, just string them together + if (length(parts)==0 || any(split<2)) return(paste0(uids,collapse=" ")) + columns <- substr(parts,1,split-1) + values <- strsplit(substring(parts,split+1),",",fixed=TRUE) + vapply(unique(columns),function(column) { + v <- unique(unlist(values[columns==column])) + paste0(column,":",paste0(v[order(v,method="radix")],collapse=",")) + },character(1)) %>% + paste0(collapse=" ") +} + +# metadata for aggregating data that already has been aggregated to a common geography, +# where variables are named by their label +meta_for_joining_regions <- function(data,meta) { + meta <- cut_meta(data,meta) + # the same variable can be part of several datasets, look up parents within the dataset + dataset <- if ("geo_dataset" %in% names(meta)) as.character(meta$geo_dataset) else rep("",nrow(meta)) + key <- paste(dataset,meta$variable,sep="\x1f") + parent <- meta$data_var[match(paste(dataset,meta$parent,sep="\x1f"),key)] + + needs_parent <- meta$rule %in% c("Average","Median","AverageTo") & is.na(parent) + if (any(needs_parent)) + stop(paste0("Can't join regions for ",paste0(unique(meta$data_var[needs_parent]),collapse=", "), + ", the data does not have the parent variables needed to aggregate them. ", + "Use `tongfen_join_correspondence` and `tongfen_aggregate` on the original data instead."), + call.=FALSE) + + meta %>% + mutate(variable=.data$data_var,parent=!!parent) %>% + select(any_of(c("variable","rule","parent","units","type"))) %>% + unique() +} + + +#' Join regions in data on a common geography +#' +#' @description +#' \lifecycle{experimental} +#' +#' Joins regions in data that has already been aggregated to a common geography, for example to correct +#' for likely geocoding anomalies as determined by `tongfen_anomaly_joins`. The data, and the geometries if +#' the data is of class sf, of the regions that get joined are aggregated, all other regions are left as they are. +#' +#' Variables are aggregated according to the metadata, numeric variables that are not part of the metadata +#' are assumed to be additive. Variables that are not additive, like averages, can only be aggregated if their +#' parent variable is part of the data. If that is not the case use `tongfen_join_correspondence` to update the +#' correspondence the data was built from and aggregate the original data again with `tongfen_aggregate`. +#' +#' @param data data on a common geography, with one row per region, for example as returned by +#' `tongfen_aggregate` or `get_tongfen_ca_census` +#' @param joins table with the regions to join as returned by `tongfen_anomaly_joins`, with the identifier +#' of the region and the identifier of the joined region it becomes part of in the column named like the +#' identifier with suffix `_joined` +#' @param meta optional metadata containing aggregation rules as for example returned by `meta_for_ca_census_vectors`, +#' variables are matched by their label. Numeric variables that are not part of the metadata are treated as +#' additive, if `NULL` (the default) that is the case for all numeric variables +#' @param id name of the column that uniquely identifies the regions, default is "TongfenID" +#' @param na.rm logical, determines how NA values should be treated when aggregating variables, +#' default is `TRUE` +#' @return The data with the regions joined. Joined regions take the place and the identifier of the +#' region with the smallest identifier among the regions they are made up of. Variables that are +#' not numeric and not part of the metadata are `NA` for joined regions. +#' @export +#' +#' @examples +#' # Correct 2001 through 2021 dissemination area level population timelines in the +#' # City of Vancouver for likely geocoding problems +#' \dontrun{ +#' datasets <- c("CA01","CA06","CA11","CA16","CA21") +#' meta <- meta_for_additive_variables(datasets,"Population") +#' data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") +#' +#' joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +#' corrected_data <- tongfen_join_regions(data,joins,meta) +#' } +tongfen_join_regions <- function(data,joins,meta=NULL,id="TongfenID",na.rm=TRUE) { + joined_id <- paste0(id,"_joined") + assert(id %in% names(data),paste0("Did not find ",id," in data.")) + assert(all(c(id,joined_id) %in% names(joins)), + paste0("Need joins to have columns ",id," and ",joined_id,".")) + data <- data %>% ungroup() + ids <- as.character(data[[id]]) + assert(!anyNA(ids) && !anyDuplicated(ids), + paste0("Need ",id," to uniquely identify the regions in data.")) + + match_index <- match(ids,as.character(joins[[id]])) + listed <- !is.na(match_index) + if (!any(listed)) return(data) + + new_id_values <- data[[id]] + new_id_values[listed] <- joins[[joined_id]][match_index[listed]] + # regions that other regions get joined to are part of the join, even if not listed themselves + affected <- listed | ids %in% as.character(new_id_values[listed]) + + is_sf <- "sf" %in% class(data) + geo_column <- if (is_sf) attr(data,"sf_column") else NULL + joined <- data[affected,] + joined[[id]] <- new_id_values[affected] + new_ids <- as.character(joined[[id]]) + + value_columns <- setdiff(names(data),c(id,"TongfenUID",geo_column)) + join_meta <- tibble(variable=character(0),rule=character(0),parent=character(0),type=character(0)) + if (!is.null(meta)) join_meta <- meta_for_joining_regions(joined,meta) + # results of tongfen calls can have count variables that are not part of the metadata, + # like the population, dwelling and household counts for Canadian census data + additive_columns <- setdiff(value_columns,join_meta$variable) + additive_columns <- additive_columns[vapply(additive_columns,\(v) is.numeric(joined[[v]]),logical(1))] + if (length(additive_columns)>0) { + message(paste0(ifelse(is.null(meta),"No metadata given, treating all numeric variables as additive: ", + "Treating numeric variables that are not part of the metadata as additive: "), + paste0(additive_columns,collapse=", "))) + join_meta <- bind_rows(join_meta, + tibble(variable=additive_columns,rule="Additive", + parent=NA_character_,type="Manual")) + } + dropped_columns <- setdiff(value_columns,join_meta$variable) + if (length(dropped_columns)>0) + message(paste0("Don't know how to aggregate ",paste0(dropped_columns,collapse=", "), + ", setting to NA for joined regions.")) + + aggregated <- joined %>% + select(all_of(c(id,intersect(join_meta$variable,names(joined)),geo_column))) %>% + group_by(!!as.name(id)) %>% + aggregate_data_with_meta(join_meta,na.rm=na.rm) %>% + ungroup() + + if ("TongfenUID" %in% names(data)) { + uids <- vapply(split(as.character(joined$TongfenUID),new_ids),merge_tongfen_uids,character(1)) + aggregated$TongfenUID <- unname(uids[as.character(aggregated[[id]])]) + } + + result <- bind_rows(data[!affected,],aggregated) + # joined regions take the place of the region they got their identifier from + result <- result[order(match(as.character(result[[id]]),ids)),names(data)] + if (is_sf) { + geometry_types <- unique(as.character(sf::st_geometry_type(result))) + if (length(geometry_types)>1 && all(geometry_types %in% c("POLYGON","MULTIPOLYGON"))) + result <- sf::st_cast(result,"MULTIPOLYGON") + } + result +} + + +#' Join regions in a correspondence +#' +#' @description +#' \lifecycle{experimental} +#' +#' Updates a correspondence so that the given regions are joined, for example to correct for likely geocoding +#' anomalies as determined by `tongfen_anomaly_joins`. The updated correspondence can be used in `tongfen_aggregate` +#' to aggregate data on the coarser common geography, which works for all variables `tongfen_aggregate` can +#' deal with and for data that was not part of detecting the anomalies. +#' +#' @param correspondence correspondence table with columns the unique geographic identifiers for each of the +#' geographies and the TongfenID and TongfenUID, as for example returned by `estimate_tongfen_correspondence` +#' or `get_tongfen_correspondence_ca_census` +#' @param joins table with the regions to join as returned by `tongfen_anomaly_joins`, with columns +#' `TongfenID` and `TongfenID_joined` +#' @return The correspondence with updated TongfenID and TongfenUID for the regions that got joined. If the +#' correspondence has a TongfenMethod column "anomaly" gets added to the method of the regions that got joined. +#' @export +#' +#' @examples +#' # Correct for likely geocoding problems in dissemination area level population timelines +#' # and use the updated correspondence to aggregate data on the corrected common geography +#' \dontrun{ +#' regions <- list(CSD="5915022") +#' datasets <- c("CA01","CA06","CA11","CA16","CA21") +#' meta <- meta_for_additive_variables(datasets,"Population") +#' data <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21") +#' joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +#' +#' correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets, +#' regions=regions,level="DA") %>% +#' tongfen_join_correspondence(joins) +#' } +tongfen_join_correspondence <- function(correspondence,joins) { + assert("TongfenID" %in% names(correspondence),"Did not find TongfenID in correspondence.") + assert(all(c("TongfenID","TongfenID_joined") %in% names(joins)), + "Need joins to have columns TongfenID and TongfenID_joined.") + ids <- as.character(correspondence$TongfenID) + missing_regions <- setdiff(as.character(joins$TongfenID),ids) + if (length(missing_regions)>0) + warning(paste0("Did not find ",length(missing_regions)," of the regions to join in the correspondence.")) + + match_index <- match(ids,as.character(joins$TongfenID)) + listed <- !is.na(match_index) + if (!any(listed)) return(correspondence) + + ids[listed] <- as.character(joins$TongfenID_joined)[match_index[listed]] + # regions that other regions get joined to are part of the join, even if not listed themselves + affected <- listed | ids %in% ids[listed] + new_ids <- ids[affected] + correspondence$TongfenID[affected] <- new_ids + if ("TongfenUID" %in% names(correspondence)) { + uids <- vapply(split(as.character(correspondence$TongfenUID[affected]),new_ids), + merge_tongfen_uids,character(1)) + correspondence$TongfenUID[affected] <- unname(uids[new_ids]) + } + if ("TongfenMethod" %in% names(correspondence)) { + method <- correspondence$TongfenMethod[affected] + tagged <- grepl("anomaly",method,fixed=TRUE) + method[!tagged] <- paste0(method[!tagged],", anomaly") + correspondence$TongfenMethod[affected] <- method + } + correspondence +} + +#' @import dplyr +#' @importFrom rlang .data +NULL +if(getRversion() >= "2.15.1") utils::globalVariables(c(".")) diff --git a/man/tongfen_anomaly_joins.Rd b/man/tongfen_anomaly_joins.Rd new file mode 100644 index 0000000..10fdc63 --- /dev/null +++ b/man/tongfen_anomaly_joins.Rd @@ -0,0 +1,93 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tongfen_anomalies.R +\name{tongfen_anomaly_joins} +\alias{tongfen_anomaly_joins} +\title{Determine regions to join to correct for likely geocoding anomalies} +\usage{ +tongfen_anomaly_joins( + data, + variables, + id = "TongfenID", + neighbours = NULL, + rel_scale = 0.25, + abs_scale = 200, + p = 4, + surprise_cutoff = 0.15, + total_surprise_cutoff = 0.75, + cutoff_fact = 0.6, + surprise_reduction_const = 0.15, + sum_fact = 0.7 +) +} +\arguments{ +\item{data}{data on a common geography, with one row per region, for example as returned by +`tongfen_aggregate` or `get_tongfen_ca_census`. Needs to be of class sf unless `neighbours` is specified.} + +\item{variables}{names of the columns holding the timeline of a count variable like population or +dwellings, in temporal order. Changes from or to a missing value are not surprising and don't make up for +surprising changes in neighbouring regions, and joined regions are missing a value if one of the regions they +are made up of is. Replace missing values by zero beforehand if they stand for regions where nothing got counted} + +\item{id}{name of the column that uniquely identifies the regions, default is "TongfenID"} + +\item{neighbours}{optional, neighbouring regions as a table with the identifiers of pairs of neighbouring +regions in the first two columns, or as a neighbours list like the ones returned by `spdep::poly2nb`. +By default all regions with intersecting geometries are neighbours, which can miss neighbours +if the geometries have been simplified and don't share their boundaries any more.} + +\item{rel_scale}{relative decrease that is half way to full surprise, default is `0.25` for a 25\% drop} + +\item{abs_scale}{absolute decrease that is half way to full surprise, default is `200`} + +\item{p}{exponent of the norm used to combine the surprises across the timeline into the total surprise. +Large values focus on the most surprising change, 1 adds up the surprises of all changes, default is `4`} + +\item{surprise_cutoff}{changes with larger surprise count as surprising, default is `0.15`. Only +regions with at least one surprising change are candidates} + +\item{total_surprise_cutoff}{only regions with larger total surprise are candidates, default is `0.75`} + +\item{cutoff_fact}{join regions if the total surprise after joining is lower than this share of the +total surprise of the candidate region, default is `0.6`} + +\item{surprise_reduction_const}{join regions if joining lowers the total surprise by more than this, +default is `0.15`} + +\item{sum_fact}{join regions if the total surprise after joining is lower than `cutoff_fact * sum_fact` +times the sum of the total surprises of both regions, default is `0.7`} +} +\value{ +A tibble with one row for each region that gets joined with other regions, with the identifier +of the region, the identifier of the joined region it becomes part of in the column named like the +identifier with suffix `_joined`, by default `TongfenID_joined`, and the `round` in which the region +first got joined to another region. The identifier of a joined region is the smallest +identifier of the regions it is made up of. +} +\description{ +\lifecycle{experimental} + +Looks for regions with surprising drops in the timeline of a count variable that are complemented by +a neighbouring region, as explained in `tongfen_detect_anomalies`, and joins them. This gets +repeated on the joined regions until there are no more regions left that qualify to get joined. +In each round a region only gets joined with one other region, the most surprising regions go first. + +Joining regions trades geographic detail for timelines that are consistent over time. The parameters +control how aggressively regions get joined and are best calibrated on the data at hand, erring on the side of +joining too few regions risks keeping geocoding problems, erring on the other side risks removing real +changes and needlessly coarsens the geography. + +The result can be used to join the regions via `tongfen_join_regions`, or to update a correspondence +via `tongfen_join_correspondence`. +} +\examples{ +# Correct 2001 through 2021 dissemination area level population timelines in the +# City of Vancouver for likely geocoding problems +\dontrun{ +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") + +joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +corrected_data <- tongfen_join_regions(data,joins,meta) +} +} diff --git a/man/tongfen_detect_anomalies.Rd b/man/tongfen_detect_anomalies.Rd new file mode 100644 index 0000000..f1b6758 --- /dev/null +++ b/man/tongfen_detect_anomalies.Rd @@ -0,0 +1,102 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tongfen_anomalies.R +\name{tongfen_detect_anomalies} +\alias{tongfen_detect_anomalies} +\title{Detect likely geocoding anomalies in timelines on a common geography} +\usage{ +tongfen_detect_anomalies( + data, + variables, + id = "TongfenID", + neighbours = NULL, + rel_scale = 0.25, + abs_scale = 200, + p = 4, + surprise_cutoff = 0.15, + total_surprise_cutoff = 0.75, + cutoff_fact = 0.6, + surprise_reduction_const = 0.15, + sum_fact = 0.7 +) +} +\arguments{ +\item{data}{data on a common geography, with one row per region, for example as returned by +`tongfen_aggregate` or `get_tongfen_ca_census`. Needs to be of class sf unless `neighbours` is specified.} + +\item{variables}{names of the columns holding the timeline of a count variable like population or +dwellings, in temporal order. Changes from or to a missing value are not surprising and don't make up for +surprising changes in neighbouring regions, and joined regions are missing a value if one of the regions they +are made up of is. Replace missing values by zero beforehand if they stand for regions where nothing got counted} + +\item{id}{name of the column that uniquely identifies the regions, default is "TongfenID"} + +\item{neighbours}{optional, neighbouring regions as a table with the identifiers of pairs of neighbouring +regions in the first two columns, or as a neighbours list like the ones returned by `spdep::poly2nb`. +By default all regions with intersecting geometries are neighbours, which can miss neighbours +if the geometries have been simplified and don't share their boundaries any more.} + +\item{rel_scale}{relative decrease that is half way to full surprise, default is `0.25` for a 25\% drop} + +\item{abs_scale}{absolute decrease that is half way to full surprise, default is `200`} + +\item{p}{exponent of the norm used to combine the surprises across the timeline into the total surprise. +Large values focus on the most surprising change, 1 adds up the surprises of all changes, default is `4`} + +\item{surprise_cutoff}{changes with larger surprise count as surprising, default is `0.15`. Only +regions with at least one surprising change are candidates} + +\item{total_surprise_cutoff}{only regions with larger total surprise are candidates, default is `0.75`} + +\item{cutoff_fact}{join regions if the total surprise after joining is lower than this share of the +total surprise of the candidate region, default is `0.6`} + +\item{surprise_reduction_const}{join regions if joining lowers the total surprise by more than this, +default is `0.15`} + +\item{sum_fact}{join regions if the total surprise after joining is lower than `cutoff_fact * sum_fact` +times the sum of the total surprises of both regions, default is `0.7`} +} +\value{ +A tibble with one row for each candidate region, most surprising first, with the identifier of the +region, the number of surprising changes `surprise_count`, the total surprise `surprise_total`, +the `period` with the most surprising change, the identifier of the `neighbour` that takes away most of +the surprise, the total surprise `surprise_total_joined` after joining both and `join` indicating if both +regions qualify to get joined. +} +\description{ +\lifecycle{experimental} + +TongFen is only as good as the geocoding that assigned the underlying data to geographic regions +in the first place. Geocoding varies over time, and the same dwelling units, and the people living +in them, can get assigned to different neighbouring regions in different years. In a timeline +on a common geography this shows up as a surprising drop in one region that is offset by +a corresponding jump in a neighbouring region. + +This function lists the candidate regions with surprising drops in the given count variable, +together with the neighbouring region that takes away most of the surprise when both are joined. +Use it to check for possible problems and to calibrate the parameters before joining regions with +`tongfen_anomaly_joins`. Not all surprising drops are due to geocoding problems, a drop that is +not complemented by a neighbouring region is likely real. + +The surprise of a change between two consecutive years ranges from 0 to 1. Only decreases are surprising, +the surprise is the product of the surprise of the relative and of the absolute decrease, so that it takes +a decrease that is large in both relative and absolute terms to be surprising. The total surprise of a +region is the `p`-norm of the surprises across all changes in the timeline. + +A candidate region and its neighbour are flagged for joining if joining reduces the total surprise of the +candidate region to below `cutoff_fact` times its total surprise, or reduces it by more than +`surprise_reduction_const`, or reduces it to below `cutoff_fact * sum_fact` times the sum of +the total surprises of both regions. To keep this comparable the surprise after joining is computed +from the change of the joined regions relative to the counts of the candidate region alone. +} +\examples{ +# Check 2001 through 2021 dissemination area level population timelines in the +# City of Vancouver for possible geocoding problems +\dontrun{ +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") + +anomalies <- tongfen_detect_anomalies(data,paste0("Population_",datasets)) +} +} diff --git a/man/tongfen_join_correspondence.Rd b/man/tongfen_join_correspondence.Rd new file mode 100644 index 0000000..76c8634 --- /dev/null +++ b/man/tongfen_join_correspondence.Rd @@ -0,0 +1,43 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tongfen_anomalies.R +\name{tongfen_join_correspondence} +\alias{tongfen_join_correspondence} +\title{Join regions in a correspondence} +\usage{ +tongfen_join_correspondence(correspondence, joins) +} +\arguments{ +\item{correspondence}{correspondence table with columns the unique geographic identifiers for each of the +geographies and the TongfenID and TongfenUID, as for example returned by `estimate_tongfen_correspondence` +or `get_tongfen_correspondence_ca_census`} + +\item{joins}{table with the regions to join as returned by `tongfen_anomaly_joins`, with columns +`TongfenID` and `TongfenID_joined`} +} +\value{ +The correspondence with updated TongfenID and TongfenUID for the regions that got joined. If the +correspondence has a TongfenMethod column "anomaly" gets added to the method of the regions that got joined. +} +\description{ +\lifecycle{experimental} + +Updates a correspondence so that the given regions are joined, for example to correct for likely geocoding +anomalies as determined by `tongfen_anomaly_joins`. The updated correspondence can be used in `tongfen_aggregate` +to aggregate data on the coarser common geography, which works for all variables `tongfen_aggregate` can +deal with and for data that was not part of detecting the anomalies. +} +\examples{ +# Correct for likely geocoding problems in dissemination area level population timelines +# and use the updated correspondence to aggregate data on the corrected common geography +\dontrun{ +regions <- list(CSD="5915022") +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21") +joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) + +correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets, + regions=regions,level="DA") \%>\% + tongfen_join_correspondence(joins) +} +} diff --git a/man/tongfen_join_regions.Rd b/man/tongfen_join_regions.Rd new file mode 100644 index 0000000..759ba26 --- /dev/null +++ b/man/tongfen_join_regions.Rd @@ -0,0 +1,54 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tongfen_anomalies.R +\name{tongfen_join_regions} +\alias{tongfen_join_regions} +\title{Join regions in data on a common geography} +\usage{ +tongfen_join_regions(data, joins, meta = NULL, id = "TongfenID", na.rm = TRUE) +} +\arguments{ +\item{data}{data on a common geography, with one row per region, for example as returned by +`tongfen_aggregate` or `get_tongfen_ca_census`} + +\item{joins}{table with the regions to join as returned by `tongfen_anomaly_joins`, with the identifier +of the region and the identifier of the joined region it becomes part of in the column named like the +identifier with suffix `_joined`} + +\item{meta}{optional metadata containing aggregation rules as for example returned by `meta_for_ca_census_vectors`, +variables are matched by their label. Numeric variables that are not part of the metadata are treated as +additive, if `NULL` (the default) that is the case for all numeric variables} + +\item{id}{name of the column that uniquely identifies the regions, default is "TongfenID"} + +\item{na.rm}{logical, determines how NA values should be treated when aggregating variables, +default is `TRUE`} +} +\value{ +The data with the regions joined. Joined regions take the place and the identifier of the +region with the smallest identifier among the regions they are made up of. Variables that are +not numeric and not part of the metadata are `NA` for joined regions. +} +\description{ +\lifecycle{experimental} + +Joins regions in data that has already been aggregated to a common geography, for example to correct +for likely geocoding anomalies as determined by `tongfen_anomaly_joins`. The data, and the geometries if +the data is of class sf, of the regions that get joined are aggregated, all other regions are left as they are. + +Variables are aggregated according to the metadata, numeric variables that are not part of the metadata +are assumed to be additive. Variables that are not additive, like averages, can only be aggregated if their +parent variable is part of the data. If that is not the case use `tongfen_join_correspondence` to update the +correspondence the data was built from and aggregate the original data again with `tongfen_aggregate`. +} +\examples{ +# Correct 2001 through 2021 dissemination area level population timelines in the +# City of Vancouver for likely geocoding problems +\dontrun{ +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") + +joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +corrected_data <- tongfen_join_regions(data,joins,meta) +} +} diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 950309d..54b1b72 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -25,6 +25,10 @@ articles: navbar: ~ contents: - tongfen_us + - title: "Geocoding anomalies" + navbar: ~ + contents: + - tongfen_anomalies authors: Jens von Bergmann: diff --git a/tests/testthat/test-anomalies.R b/tests/testthat/test-anomalies.R new file mode 100644 index 0000000..80a1217 --- /dev/null +++ b/tests/testthat/test-anomalies.R @@ -0,0 +1,622 @@ +library(dplyr) +library(sf) + +sq <- function(x0, y0, w, h) { + st_polygon(list(cbind(c(x0, x0 + w, x0 + w, x0, x0), + c(y0, y0, y0 + h, y0 + h, y0)))) +} + +# regions lined up in a row, each one only neighbours the one before and after +row_regions <- function(ids, values) { + geometry <- st_sfc(lapply(seq_along(ids), \(i) sq(i - 1, 0, 1, 1)), crs = 3347) + st_sf(bind_cols(tibble(TongfenID = ids), as_tibble(values)), geometry = geometry) +} + +years <- c("2001", "2006", "2011", "2016") + +# population moves from A to B between 2001 and 2006 while C stays flat, like dwellings +# getting geocoded to the neighbouring region +misallocation <- function() { + row_regions(c("A", "B", "C"), + rbind(c(1000, 400, 410, 420), + c(500, 1100, 1110, 1120), + c(800, 805, 810, 815)) %>% + `colnames<-`(years)) +} + +# population in A drops and no neighbour picks it up, like a redevelopment site getting cleared +real_drop <- function() { + row_regions(c("A", "B", "C"), + rbind(c(700, 710, 720, 730), + c(1000, 300, 310, 320), + c(800, 805, 810, 815)) %>% + `colnames<-`(years)) +} + +# A loses to B between 2001 and 2006, B loses to C between 2006 and 2011. A only matches +# up with its neighbour once B and C are joined. +chain <- function() { + row_regions(c("A", "B", "C", "D"), + rbind(c(1000, 600, 600, 600), + c(500, 900, 400, 400), + c(500, 500, 1000, 1000), + c(800, 800, 800, 800)) %>% + `colnames<-`(years)) +} + +# ── surprise ────────────────────────────────────────────────────────────────── + +test_that("anomaly_surprise: only decreases that are large in relative and absolute terms surprise", { + surprise <- \(change, base) tongfen:::anomaly_surprise(change, base, rel_scale = 0.25, abs_scale = 200) + + # half way to full surprise for both the relative and the absolute decrease + expect_equal(surprise(-200, 800), 0.25) + expect_equal(surprise(-400, 800), 0.75 * 0.75) + # large relative but small absolute decrease, and the other way around + expect_lt(surprise(-10, 20), 0.04) + expect_lt(surprise(-200, 100000), 0.01) + expect_equal(surprise(c(100, 0, NA, -50, 0), c(800, 800, 800, NA, 0)), rep(0, 5)) + # decrease from nothing + expect_equal(surprise(-50, 0), 0) + + m <- surprise(matrix(c(-200, 100, -400, NA), 2), matrix(800, 2, 2)) + expect_equal(m, matrix(c(0.25, 0, 0.75 * 0.75, 0), 2)) +}) + +# ── detection ───────────────────────────────────────────────────────────────── + +test_that("tongfen_detect_anomalies: complementary change in a neighbour qualifies for joining", { + anomalies <- tongfen_detect_anomalies(misallocation(), years, total_surprise_cutoff = 0.4) + + expect_equal(names(anomalies), + c("TongfenID", "surprise_count", "surprise_total", "period", "neighbour", + "surprise_total_joined", "join")) + expect_equal(nrow(anomalies), 1L) + expect_equal(anomalies$TongfenID, "A") + expect_equal(anomalies$surprise_count, 1L) + expect_equal(anomalies$surprise_total, (1 - 0.5^(0.6 / 0.25)) * (1 - 0.5^3)) + expect_equal(anomalies$period, "2001-2006") + expect_equal(anomalies$neighbour, "B") + expect_equal(anomalies$surprise_total_joined, 0) + expect_true(anomalies$join) +}) + +test_that("tongfen_detect_anomalies: drop without complementary change is listed but not joined", { + anomalies <- tongfen_detect_anomalies(real_drop(), years) + + expect_equal(anomalies$TongfenID, "B") + expect_false(anomalies$join) + expect_equal(anomalies$surprise_total_joined, anomalies$surprise_total, tolerance = 0.01) + expect_equal(nrow(tongfen_anomaly_joins(real_drop(), years)), 0L) +}) + +test_that("tongfen_detect_anomalies: no candidates gives an empty table", { + data <- misallocation() + anomalies <- tongfen_detect_anomalies(data, years[2:4]) + expect_equal(nrow(anomalies), 0L) + expect_equal(names(anomalies), + names(tongfen_detect_anomalies(data, years, total_surprise_cutoff = 0.4))) + expect_equal(nrow(tongfen_anomaly_joins(data, years[2:4])), 0L) +}) + +test_that("tongfen_detect_anomalies: candidates are ordered by surprise", { + anomalies <- tongfen_detect_anomalies(chain(), years, total_surprise_cutoff = 0.4) + expect_equal(anomalies$TongfenID, c("B", "A")) + expect_equal(anomalies$period, c("2006-2011", "2001-2006")) + expect_equal(anomalies$neighbour, c("C", "B")) + expect_equal(anomalies$join, c(TRUE, FALSE)) +}) + +test_that("tongfen_detect_anomalies: missing values are not surprising", { + data <- misallocation() + data$`2006`[1] <- NA + expect_equal(nrow(tongfen_detect_anomalies(data, years, total_surprise_cutoff = 0.4)), 0L) + expect_equal(nrow(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4)), 0L) + + # joined regions are missing the values their parts are missing + data <- chain() + data$`2016`[3] <- NA + joins <- tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4) + expect_equal(joins$TongfenID, c("A", "B", "C")) + # a missing value in a neighbour does not make up for a surprising change + data$`2006`[3] <- NA + anomalies <- tongfen_detect_anomalies(data, years, total_surprise_cutoff = 0.4) + expect_equal(anomalies$TongfenID, c("B", "A")) + expect_equal(anomalies$surprise_total_joined[1], anomalies$surprise_total[1]) + expect_equal(anomalies$join, c(FALSE, FALSE)) + expect_equal(nrow(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4)), 0L) +}) + +test_that("tongfen_detect_anomalies: candidate without neighbours", { + data <- misallocation() %>% st_drop_geometry() + anomalies <- tongfen_detect_anomalies(data, years, total_surprise_cutoff = 0.4, + neighbours = tibble(a = "B", b = "C")) + expect_equal(anomalies$TongfenID, "A") + expect_true(is.na(anomalies$neighbour)) + expect_true(is.na(anomalies$surprise_total_joined)) + expect_false(anomalies$join) +}) + +# ── joins ───────────────────────────────────────────────────────────────────── + +test_that("tongfen_anomaly_joins: joins regions with complementary changes", { + joins <- tongfen_anomaly_joins(misallocation(), years, total_surprise_cutoff = 0.4) + expect_equal(joins, tibble(TongfenID = c("A", "B"), TongfenID_joined = "A", round = 1L)) + + # too high a bar for regions to become candidates + expect_equal(nrow(tongfen_anomaly_joins(misallocation(), years, total_surprise_cutoff = 0.9)), 0L) +}) + +test_that("tongfen_anomaly_joins: joined regions keep getting joined", { + joins <- tongfen_anomaly_joins(chain(), years, total_surprise_cutoff = 0.4) + expect_equal(joins, tibble(TongfenID = c("A", "B", "C"), TongfenID_joined = "A", + round = c(2L, 1L, 1L))) +}) + +test_that("tongfen_anomaly_joins: joined regions are named after their smallest identifier", { + data <- chain() %>% mutate(TongfenID = c("59150010", "5915002", "59150003", "59150004")) + joins <- tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4) + # sorted like the identifiers of a correspondence, character by character + expect_equal(joins$TongfenID, c("59150003", "59150010", "5915002")) + expect_true(all(joins$TongfenID_joined == "59150003")) +}) + +test_that("tongfen_anomaly_joins: result does not depend on the order of the regions", { + # A drops and both neighbours make up for it in full + data <- row_regions(c("B", "A", "C"), + rbind(c(500, 1100, 1110, 1120), + c(1000, 400, 410, 420), + c(500, 1100, 1110, 1120)) %>% + `colnames<-`(years)) + expected <- tibble(TongfenID = c("A", "B"), TongfenID_joined = "A", round = 1L) + expect_equal(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4), expected) + expect_equal(tongfen_anomaly_joins(data[3:1, ], years, total_surprise_cutoff = 0.4), expected) + expect_equal(tongfen_detect_anomalies(data[3:1, ], years, total_surprise_cutoff = 0.4), + tongfen_detect_anomalies(data, years, total_surprise_cutoff = 0.4)) +}) + +test_that("tongfen_anomaly_joins: identifier column can go by any name and type", { + data <- chain() %>% st_drop_geometry() %>% mutate(TongfenID = c(40L, 30L, 20L, 10L)) %>% + rename(GeoUID = "TongfenID") + joins <- tongfen_anomaly_joins(data, years, id = "GeoUID", total_surprise_cutoff = 0.4, + neighbours = tibble(a = c(40L, 30L, 20L), b = c(30L, 20L, 10L))) + expect_equal(joins, tibble(GeoUID = c(20L, 30L, 40L), GeoUID_joined = 20L, + round = c(1L, 1L, 2L))) +}) + +test_that("tongfen_anomaly_joins: neighbours can be specified", { + data <- chain() + expected <- tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4) + data <- data %>% st_drop_geometry() + + neighbour_table <- tibble(id1 = c("A", "B", "C", "A", "X"), id2 = c("B", "C", "D", "A", "A")) + expect_equal(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4, + neighbours = neighbour_table), + expected) + + # neighbours list in the format of the spdep package + nb <- list(2L, c(1L, 3L), c(2L, 4L), 3L) + attr(nb, "region.id") <- data$TongfenID + class(nb) <- "nb" + expect_equal(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4, neighbours = nb), + expected) + + # in a different order than the data, and with a region without neighbours + nb <- list(0L, 3L, c(2L, 4L), c(3L, 5L), 4L) + attr(nb, "region.id") <- c("Z", rev(data$TongfenID)) + expect_equal(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4, neighbours = nb), + expected) + + # without the link between B and C nothing can get joined + expect_equal(nrow(tongfen_anomaly_joins(data, years, total_surprise_cutoff = 0.4, + neighbours = neighbour_table[-2, ])), 0L) +}) + +test_that("tongfen_anomaly_joins: checks inputs", { + data <- misallocation() + expect_error(tongfen_anomaly_joins(st_drop_geometry(data), years), "neighbours") + expect_error(tongfen_anomaly_joins(data, years, neighbours = "B"), "neighbours") + expect_error(tongfen_anomaly_joins(data, "2001"), "at least two") + expect_error(tongfen_anomaly_joins(data, c("2001", "2026")), "2026") + expect_error(tongfen_anomaly_joins(data, years, id = "GeoUID"), "GeoUID") + expect_error(tongfen_anomaly_joins(data %>% mutate(`2006` = as.character(.data$`2006`)), years), + "numeric") + expect_error(tongfen_anomaly_joins(data %>% mutate(TongfenID = c("A", "A", "B")), years), + "uniquely") + expect_error(tongfen_anomaly_joins(data, years, p = c(2, 4)), "single number") + expect_error(tongfen_detect_anomalies(data, years, abs_scale = 0), "positive") +}) + +# ── comparison with the original implementation ─────────────────────────────── + +# The method was first implemented in +# https://doodles.mountainmath.ca/posts/2024-07-26-geocoding-errors-in-aggregate-data/ +# This is the code from that post, with the neighbours derived via `st_intersects` instead +# of `spdep::poly2nb`. It works on timelines in columns named by year and names joined +# regions by stringing together their identifiers. +blog_rel_surprise <- function(x) { + r <- as.integer(x <= 0 & !is.na(x) & is.finite(x)) + r[r == 1] <- 1 - dexp(x[r == 1] * log(0.5) / 0.25, rate = 1) + r +} + +blog_abs_surprise <- function(x) { + r <- as.integer(x <= 0 & !is.na(x) & is.finite(x)) + r[r == 1] <- 1 - dexp(x[r == 1] * log(0.5) / 200, rate = 1) + r +} + +blog_add_surprise <- function(data, base_field = "value") { + data |> + arrange(Year) |> + mutate(`Absolute change` = value - lag(value, order_by = Year), + `Relative change` = `Absolute change` / lag(!!as.name(base_field), order_by = Year), + .by = TongfenID) |> + mutate(surprise = (blog_rel_surprise(`Relative change`)) * (blog_abs_surprise(`Absolute change`))) +} + +blog_get_match_list <- function(geo_data2, + cutoff_fact = 0.6, total_surprise_cutoff = 0.75, + surprise_reduction_const = 0.15, sum_fact = 0.7, p = 4) { + intersecting <- st_intersects(geo_data2) + data_nb <- lapply(seq_along(intersecting), \(i) geo_data2$TongfenID[setdiff(intersecting[[i]], i)]) |> + setNames(geo_data2$TongfenID) + + summarize_surprise <- function(data, fields = "surprise") { + data |> + summarize(across(all_of(fields), list(count = ~sum(.x > 0.15), + total = ~(sum(.x^p))^(1 / p))), + .by = TongfenID) |> + arrange(!!as.name(paste0(fields[1], "_count")), !!as.name(paste0(fields[1], "_total"))) + } + + long_data2 <- geo_data2 |> + st_drop_geometry() |> + select(TongfenID, matches("^\\d{4}$")) |> + tidyr::pivot_longer(matches("^\\d{4}$"), names_to = "Year") |> + blog_add_surprise() + + candidate_list <- long_data2 |> + summarize_surprise() |> + purrr::map_df(rev) |> + filter(surprise_count > 0, surprise_total > total_surprise_cutoff) + + match_list <- tibble(TongfenID = NA_character_, TongfenID_original = NA_character_) |> + slice(-1) + + if (nrow(candidate_list) == 0) return(match_list) + + for (i in 1:nrow(candidate_list)) { + id0 <- candidate_list$TongfenID[i] + + if (id0 %in% match_list$TongfenID_original) next + + neighbour_ids <- data_nb[[id0]] + + if (length(neighbour_ids) == 0) next + + od <- long_data2 |> + filter(TongfenID %in% c(id0)) + + original <- od |> + summarize_surprise() + + reductions <- long_data2 |> + filter(TongfenID %in% c(neighbour_ids)) |> + left_join(od |> select(Year, ov = value), by = "Year") |> + mutate(value = value + ov) |> + blog_add_surprise(base_field = "ov") |> + rename(surprise_ov = surprise) |> + blog_add_surprise() |> + summarize_surprise(c("surprise_ov", "surprise")) |> + slice(1) |> + rename(TongfenID2 = TongfenID) |> + mutate(TongfenID1 = original$TongfenID, .before = TongfenID2) |> + mutate(TongfenID = paste0(TongfenID1, "_", TongfenID2)) + + if (reductions$TongfenID2 %in% c(match_list$TongfenID_original)) next + + pre_sum <- long_data2 |> + filter(TongfenID %in% c(reductions$TongfenID1, reductions$TongfenID2)) |> + summarize_surprise() + + if (reductions$surprise_ov_total < cutoff_fact * original$surprise_total | + original$surprise_total - reductions$surprise_ov_total > surprise_reduction_const | + reductions$surprise_ov_total < cutoff_fact * sum_fact * sum(pre_sum$surprise_total)) { + match_list <- bind_rows(match_list, + reductions |> + tidyr::pivot_longer(c(TongfenID1, TongfenID2), + values_to = "TongfenID_original")) + } + } + match_list +} + +blog_iterate_geo_match_joins <- function(geo_data2, ...) { + stop_looking <- FALSE + + while (!stop_looking) { + match_list <- blog_get_match_list(geo_data2, ...) + + if (nrow(match_list) == 0) { + stop_looking <- TRUE + next + } + + g1 <- geo_data2 |> + rename(TongfenID_original = TongfenID) |> + inner_join(match_list |> select(TongfenID, TongfenID_original), + by = "TongfenID_original", + relationship = "many-to-one") |> + mutate(TongfenID = coalesce(TongfenID, TongfenID_original)) |> + select(-TongfenID_original) |> + group_by(TongfenID) |> + summarize(across(matches("\\d{4}"), sum), .groups = "drop") |> + st_make_valid() + + g2 <- geo_data2 |> + filter(!(TongfenID %in% match_list$TongfenID_original)) + + geo_data2 <- bind_rows(g1, g2) + } + geo_data2 +} + +# Regions on a grid with slowly declining counts, and counts that get moved between +# neighbouring regions for a couple of years. The counts are not rounded and all +# regions change in all periods to keep clear of ties, the two implementations +# order regions differently after the first round of joins. +random_timelines <- function(n, n_moves, n_drops, seed) { + set.seed(seed) + cells <- expand.grid(x = seq_len(n), y = seq_len(n)) + n_years <- 6 + values <- matrix(runif(n * n, 300, 1500), n * n, n_years) - + t(apply(matrix(runif(n * n * n_years, 1, 20), n * n, n_years), 1, cumsum)) + cell_index <- \(x, y) (y - 1) * n + x + + for (move in seq_len(n_moves)) { + from <- sample(n * n, 1) + step <- list(c(1, 0), c(-1, 0), c(0, 1), c(0, -1))[[sample(4, 1)]] + x <- cells$x[from] + step[1] + y <- cells$y[from] + step[2] + if (x < 1 || x > n || y < 1 || y > n) next + to <- cell_index(x, y) + start <- sample(2:n_years, 1) + moved_years <- start:sample(start:n_years, 1) + amount <- runif(1, 0.2, 0.8) * min(values[from, ]) + values[from, moved_years] <- values[from, moved_years] - amount + values[to, moved_years] <- values[to, moved_years] + amount + } + for (drop in seq_len(n_drops)) { + region <- sample(n * n, 1) + dropped_years <- sample(2:n_years, 1):n_years + values[region, dropped_years] <- values[region, dropped_years] * runif(1, 0.3, 0.7) + } + colnames(values) <- seq(1996, by = 5, length.out = n_years) + + geometry <- st_sfc(lapply(seq_len(n * n), \(i) sq(cells$x[i], cells$y[i], 1, 1)), crs = 3347) + st_sf(bind_cols(tibble(TongfenID = sprintf("r%03d", sample(n * n))), as_tibble(values)), + geometry = geometry) +} + +test_that("tongfen_anomaly_joins: agrees with the original implementation", { + rounds <- c() + for (seed in 1:4) { + data <- random_timelines(n = 7, n_moves = 30, n_drops = 5, seed = seed) + timeline <- names(data)[grepl("^\\d{4}$", names(data))] + + joins <- tongfen_anomaly_joins(data, timeline, total_surprise_cutoff = 0.4) + blog_ids <- blog_iterate_geo_match_joins(data, total_surprise_cutoff = 0.4)$TongfenID + blog_groups <- strsplit(blog_ids, "_", fixed = TRUE) + blog_groups <- blog_groups[lengths(blog_groups) > 1] %>% + vapply(\(g) paste0(sort(g), collapse = "_"), character(1)) + groups <- split(joins$TongfenID, joins$TongfenID_joined) %>% + vapply(\(g) paste0(sort(g), collapse = "_"), character(1)) + + expect_gt(length(groups), 3) + expect_setequal(unname(groups), blog_groups) + rounds <- c(rounds, max(joins$round)) + + # stricter parameters + joins <- tongfen_anomaly_joins(data, timeline, cutoff_fact = 0.3, surprise_reduction_const = 0.4, + sum_fact = 0.5, p = 2) + blog_ids <- blog_iterate_geo_match_joins(data, cutoff_fact = 0.3, surprise_reduction_const = 0.4, + sum_fact = 0.5, p = 2)$TongfenID + expect_equal(nrow(data) - n_distinct(joins$TongfenID) + n_distinct(joins$TongfenID_joined), + length(blog_ids)) + expect_setequal(joins$TongfenID, unlist(strsplit(blog_ids[grepl("_", blog_ids)], "_", fixed = TRUE))) + } + # some of the timelines need several rounds of joins + expect_gt(max(rounds), 1) +}) + +test_that("tongfen_anomaly_joins: random timelines don't depend on the order of the regions", { + data <- random_timelines(n = 6, n_moves = 25, n_drops = 4, seed = 11) + timeline <- names(data)[grepl("^\\d{4}$", names(data))] + joins <- tongfen_anomaly_joins(data, timeline, total_surprise_cutoff = 0.4) + expect_gt(nrow(joins), 6) + expect_equal(tongfen_anomaly_joins(data[sample(nrow(data)), ], timeline, total_surprise_cutoff = 0.4), + joins) +}) + +# ── joining regions ─────────────────────────────────────────────────────────── + +joins_ab <- tibble(TongfenID = c("A", "B"), TongfenID_joined = "A", round = 1L) + +test_that("tongfen_join_regions: aggregates joined regions and leaves the rest alone", { + data <- misallocation() %>% + mutate(TongfenUID = paste0("GeoUID16:", c("1", "2,3", "4"), " GeoUID21:", c("11,12", "13", "14")), + .after = "TongfenID") + + expect_message(result <- tongfen_join_regions(data, joins_ab), "additive") + expect_s3_class(result, "sf") + expect_equal(names(result), names(data)) + expect_equal(result$TongfenID, c("A", "C")) + expect_equal(result$TongfenUID, c("GeoUID16:1,2,3 GeoUID21:11,12,13", "GeoUID16:4 GeoUID21:14")) + expect_equal(result$`2001`, c(1500, 800)) + expect_equal(result$`2016`, c(1540, 815)) + expect_equal(as.numeric(st_area(result)), c(2, 1)) + expect_true(st_equals(st_geometry(result)[1], sq(0, 0, 2, 1), sparse = FALSE)[1, 1]) + expect_true(st_equals(st_geometry(result)[2], st_geometry(data)[3], sparse = FALSE)[1, 1]) + expect_equal(st_crs(result), st_crs(data)) + expect_equal(result %>% st_drop_geometry() %>% filter(.data$TongfenID == "C"), + data %>% st_drop_geometry() %>% filter(.data$TongfenID == "C")) + + # the timeline of the joined region does not have anything surprising left + expect_equal(nrow(tongfen_detect_anomalies(result, years, total_surprise_cutoff = 0.4)), 0L) +}) + +test_that("tongfen_join_regions: works on data without geometry and keeps the order of regions", { + data <- chain() %>% st_drop_geometry() %>% slice(c(4, 2, 3, 1)) + joins <- tibble(TongfenID = c("C", "B"), TongfenID_joined = "B") + + result <- suppressMessages(tongfen_join_regions(data, joins)) + expect_false(inherits(result, "sf")) + expect_equal(result$TongfenID, c("D", "B", "A")) + expect_equal(result$`2011`, c(800, 1400, 600)) + + # nothing to join + expect_equal(tongfen_join_regions(data, joins[0, ]), data) + expect_equal(tongfen_join_regions(data, tibble(TongfenID = "X", TongfenID_joined = "Y")), data) + # regions other regions get joined to don't have to be listed + result <- suppressMessages(tongfen_join_regions(data, joins[1, ])) + expect_equal(result$TongfenID, c("D", "B", "A")) + expect_equal(result$`2011`, c(800, 1400, 600)) +}) + +test_that("tongfen_join_regions: aggregates according to metadata", { + data <- tibble(TongfenID = c("A", "B", "C"), + Population_CA16 = c(100, 300, 50), + Income_CA16 = c(10, 20, 30), + Population_CA21 = c(300, 100, 60), + Income_CA21 = c(40, 20, 35), + name = c("a", "b", "c")) + meta <- tibble(variable = c("v_pop", "v_income", "v_pop", "v_income"), + label = c("Population_CA16", "Income_CA16", "Population_CA21", "Income_CA21"), + rule = c("Additive", "Average", "Additive", "Average"), + parent = c(NA, "v_pop", NA, "v_pop"), + type = "Original", + geo_dataset = c("CA16", "CA16", "CA21", "CA21")) + + expect_message(result <- tongfen_join_regions(data, joins_ab, meta), "name") + expect_equal(result$TongfenID, c("A", "C")) + expect_equal(result$Population_CA16, c(400, 50)) + expect_equal(result$Income_CA16, c((100 * 10 + 300 * 20) / 400, 30)) + expect_equal(result$Income_CA21, c((300 * 40 + 100 * 20) / 400, 35)) + expect_equal(result$name, c(NA, "c")) + + # count variables that are not part of the metadata, like the ones census calls add + expect_message(result <- tongfen_join_regions(data %>% mutate(Dwellings_CA16 = c(40, 110, 20)), + joins_ab, meta), + "not part of the metadata as additive: Dwellings_CA16") + expect_equal(result$Dwellings_CA16, c(150, 20)) + expect_equal(result$Income_CA16, c((100 * 10 + 300 * 20) / 400, 30)) + + # averages can't be aggregated without their parent + expect_error(tongfen_join_regions(data %>% select(-"Population_CA21"), joins_ab, meta), + "Income_CA21.*tongfen_join_correspondence") + # treating averages as additive is on the user + result <- suppressMessages(tongfen_join_regions(data, joins_ab)) + expect_equal(result$Income_CA16, c(30, 30)) +}) + +test_that("tongfen_join_regions: checks inputs", { + data <- misallocation() + expect_error(tongfen_join_regions(data, joins_ab %>% select(-"TongfenID_joined")), "TongfenID_joined") + expect_error(tongfen_join_regions(data, joins_ab, id = "GeoUID"), "GeoUID") + expect_error(tongfen_join_regions(bind_rows(data, data), joins_ab), "uniquely") +}) + +test_that("merge_tongfen_uids: combines the identifiers by geography", { + merge_uids <- tongfen:::merge_tongfen_uids + expect_equal(merge_uids(c("a:2,3 b:10", "a:1 b:9,10")), "a:1,2,3 b:10,9") + expect_equal(merge_uids(c("a:1 b:9,10", "a:2,3 b:10")), "a:1,2,3 b:10,9") + expect_equal(merge_uids("a:1 b:9"), "a:1 b:9") + # regions that are not part of all geographies + expect_equal(merge_uids(c("a:1 ", "a:2 b:3")), "a:1,2 b:3") + # not in the format of a TongfenUID + expect_equal(merge_uids(c("x", "y", "x")), "x y") +}) + +# ── joining correspondences ─────────────────────────────────────────────────── + +# two geographies on a grid of four regions, the second one has the top row in one piece +two_geographies <- function() { + ids <- c("a1", "a2", "a3", "a4") + geo_a <- st_sf(idA = ids, Population = c(1000, 400, 300, 500), + geometry = st_sfc(sq(0, 0, 1, 1), sq(1, 0, 1, 1), sq(0, 1, 1, 1), sq(1, 1, 1, 1), + crs = 3347)) + geo_b <- st_sf(idB = c("b1", "b2", "b3"), Population = c(400, 1050, 820), + geometry = st_sfc(sq(0, 0, 1, 1), sq(1, 0, 1, 1), sq(0, 1, 2, 1), crs = 3347)) + correspondence <- tibble(idA = ids, idB = c("b1", "b2", "b3", "b3")) %>% + tongfen:::get_tongfen_correspondence() + list(data = list(A = geo_a, B = geo_b), correspondence = correspondence, + meta = meta_for_additive_variables(c("A", "B"), "Population")) +} + +test_that("tongfen_join_correspondence: joins the regions in the correspondence", { + correspondence <- two_geographies()$correspondence + joins <- tibble(TongfenID = c("a1", "a2"), TongfenID_joined = "a1") + + result <- tongfen_join_correspondence(correspondence, joins) + expect_equal(names(result), names(correspondence)) + expect_equal(result$TongfenID, c("a1", "a1", "a3", "a3")) + expect_equal(result$TongfenUID, c(rep("idA:a1,a2 idB:b1,b2", 2), rep("idA:a3,a4 idB:b3", 2))) + expect_equal(result[3:4, ], correspondence[3:4, ]) + + # joining a region made up of several regions + result <- tongfen_join_correspondence(correspondence, tibble(TongfenID = c("a3", "a2"), + TongfenID_joined = "a2")) + expect_equal(result$TongfenID, c("a1", "a2", "a2", "a2")) + expect_equal(result$TongfenUID, c("idA:a1 idB:b1", rep("idA:a2,a3,a4 idB:b2,b3", 3))) + + expect_equal(tongfen_join_correspondence(correspondence, joins[0, ]), correspondence) + expect_warning(result <- tongfen_join_correspondence(correspondence, + bind_rows(joins, tibble(TongfenID = "x", + TongfenID_joined = "a1"))), + "Did not find 1") + expect_equal(result$TongfenID, c("a1", "a1", "a3", "a3")) + expect_error(tongfen_join_correspondence(correspondence, tibble(TongfenID = "a1")), + "TongfenID_joined") +}) + +test_that("tongfen_join_correspondence: tags the method of joined regions", { + correspondence <- two_geographies()$correspondence %>% + mutate(TongfenMethod = c("identifier", "identifier", "estimate", "estimate")) + joins <- tibble(TongfenID = c("a1", "a2"), TongfenID_joined = "a1") + + result <- tongfen_join_correspondence(correspondence, joins) + expect_equal(result$TongfenMethod, + c("identifier, anomaly", "identifier, anomaly", "estimate", "estimate")) + # joining again does not tag again + joins <- tibble(TongfenID = c("a1", "a3"), TongfenID_joined = "a1") + result <- tongfen_join_correspondence(result, joins) + expect_equal(result$TongfenID, rep("a1", 4)) + expect_equal(result$TongfenMethod, + c("identifier, anomaly", "identifier, anomaly", "estimate, anomaly", "estimate, anomaly")) + # the summary of the correspondence still works + expect_equal(nrow(check_tongfen_areas(two_geographies()$data, result)), 1L) +}) + +test_that("joining the aggregated data and aggregating with the joined correspondence agree", { + fixture <- two_geographies() + aggregated <- tongfen_aggregate(fixture$data, fixture$correspondence, fixture$meta, base_geo = "A") + expect_equal(aggregated$TongfenID, c("a1", "a2", "a3")) + + # a1 loses 600 that show up in a2 + joins <- tongfen_anomaly_joins(aggregated, c("Population_A", "Population_B"), + total_surprise_cutoff = 0.4) + expect_equal(joins$TongfenID, c("a1", "a2")) + + joined <- tongfen_join_regions(aggregated, joins, fixture$meta) + reaggregated <- tongfen_aggregate(fixture$data, + tongfen_join_correspondence(fixture$correspondence, joins), + fixture$meta, base_geo = "A") + + expect_equal(joined$TongfenID, c("a1", "a3")) + expect_equal(joined$Population_B, c(1450, 820)) + expect_equal(st_drop_geometry(joined)[names(st_drop_geometry(reaggregated))], + st_drop_geometry(reaggregated)) + expect_true(all(diag(st_equals(joined, reaggregated, sparse = FALSE)))) + expect_equal(as.character(st_geometry_type(joined)), as.character(st_geometry_type(reaggregated))) +}) diff --git a/vignettes/tongfen_anomalies.Rmd b/vignettes/tongfen_anomalies.Rmd new file mode 100644 index 0000000..fe2e89a --- /dev/null +++ b/vignettes/tongfen_anomalies.Rmd @@ -0,0 +1,202 @@ +--- +title: "Geocoding anomalies in TongFen timelines" +author: "Jens von Bergmann" +date: "`r Sys.Date()`" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Geocoding anomalies in TongFen timelines} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r setup, include = FALSE} +knitr::opts_chunk$set( + message = FALSE, + warning = FALSE, + collapse = TRUE, + eval = nzchar(Sys.getenv("COMPILE_VIG")), + comment = "#>" +) +``` + +TongFen makes data on different geographies comparable by aggregating it up to a common geography. The result is only as good as the geocoding that assigned the underlying data to geographic regions in the first place. Geocoding changes over time, and the same dwelling units, and the people living in them, can get assigned to different neighbouring regions in different years. In a timeline on a common geography this shows up as a surprising drop in one region that is offset by a jump in a neighbouring region. + +The fix is in the spirit of TongFen, joining the affected regions gives a slightly coarser geography on which the data is consistent over time. The functions in this vignette look for such patterns and join the regions on demand. The method is explained in more detail in a [blog post](https://doodles.mountainmath.ca/posts/2024-07-26-geocoding-errors-in-aggregate-data/), this vignette follows the example from that post. + +```{r} +library(dplyr) +library(tidyr) +library(ggplot2) +library(cancensus) +library(sf) +library(tongfen) +# cancensus::set_api_key("") +``` + +## Population timelines for Toronto + +As an example we take the population from the 1971 through 2011 censuses that Statistics Canada tabulated on 2016 dissemination areas, together with the 2016 population. All data comes on the same geography, so there is no need to TongFen, but the data for the earlier years is geocoded from the road network and block face of the time, which does not always match up with 2016 dissemination areas. + +```{r} +years <- c(1971,seq(1981,2011,5)) +vectors <- c(setNames(paste0("v_CA",years,"x16_1"),years),"2016"="v_CA16_1") +timeline <- names(vectors) + +toronto <- get_census("CA16CT",regions=list(CSD="3520005"),vectors=vectors, + level="DA",geo_format="sf",quiet=TRUE) %>% + select(GeoUID,all_of(timeline)) %>% + mutate(across(all_of(timeline),\(x) coalesce(x,0))) +``` + +Dissemination areas without population in a given year come back as missing values. Changes from or to a missing value are never considered surprising, so we set them to zero to mark these as areas where nobody got counted. + +The area around Crescent Town shows what the problem looks like. + +```{r fig.width=7, fig.height=3.5} +crescent_town <- c("35204370","35204765") + +plot_timelines <- function(data) { + data %>% + st_drop_geometry() %>% + pivot_longer(all_of(timeline),names_to="Year",values_to="Population") %>% + ggplot(aes(x=Year,y=Population,colour=GeoUID,group=GeoUID)) + + geom_line() + + geom_point() + + scale_y_continuous(labels=scales::comma,limits=c(0,NA)) +} + +toronto %>% + filter(GeoUID %in% crescent_town) %>% + plot_timelines() + + labs(title="Population in two neighbouring dissemination areas") +``` + +The population jumps back and forth between the two areas, while the sum of the two is fairly steady from 1981 on. People did not move back and forth, their homes got geocoded to a different dissemination area in different years. + +## Detecting anomalies + +`tongfen_detect_anomalies` lists the regions with surprising drops. Only decreases are surprising, and a decrease needs to be large in both relative and absolute terms. For each of these candidate regions it finds the neighbouring region that takes away most of the surprise when both are joined, and checks if that reduction is large enough to justify joining them. Our regions are identified by their `GeoUID` instead of the `TongfenID` the function looks for by default. + +```{r} +anomalies <- tongfen_detect_anomalies(toronto,timeline,id="GeoUID",total_surprise_cutoff=0.4) + +anomalies %>% filter(GeoUID %in% crescent_town) +``` + +Both areas are candidates, and each one is the neighbour that best explains the surprising drops of the other. Not all candidates find a neighbour to pair up with. Population does drop for real, for example when a site gets cleared for redevelopment, and such regions are left alone. + +```{r} +anomalies %>% count(join) +``` + +## Joining regions + +`tongfen_anomaly_joins` joins the regions that qualify and looks again, joined regions can have surprising drops that are complemented by another neighbour. This repeats until there are no more regions left to join. The result lists the regions that got joined together with the identifier of the joined region they are now part of and the round in which they first got joined. + +```{r} +joins <- tongfen_anomaly_joins(toronto,timeline,id="GeoUID",total_surprise_cutoff=0.4) + +joins %>% filter(GeoUID %in% crescent_town) +``` + +`tongfen_join_regions` applies the joins to the data, aggregating the variables and geometries of the regions that get joined and leaving all others as they are. + +```{r} +toronto_joined <- tongfen_join_regions(toronto,joins,id="GeoUID") + +c(original=nrow(toronto),joined=nrow(toronto_joined)) +``` + +```{r fig.width=7, fig.height=3.5} +toronto_joined %>% + filter(GeoUID %in% crescent_town) %>% + plot_timelines() + + labs(title="Population in the joined region") +``` + +The map shows the regions that got joined around Crescent Town. + +```{r fig.width=7, fig.height=5} +bbox <- toronto %>% filter(GeoUID %in% crescent_town) %>% st_buffer(1500) %>% st_bbox() + +ggplot(toronto_joined %>% mutate(joined=GeoUID %in% joins$GeoUID_joined)) + + geom_sf(aes(fill=joined),linewidth=0.1) + + geom_sf(data=toronto,fill=NA,linewidth=0.1,linetype="dotted") + + scale_fill_manual(values=c("TRUE"="steelblue","FALSE"="whitesmoke"),guide="none") + + coord_sf(datum=NA,xlim=bbox[c("xmin","xmax")],ylim=bbox[c("ymin","ymax")]) + + labs(title="Joined regions around Crescent Town", + caption="Joined regions in blue, original dissemination areas dotted") +``` + +## Tuning + +Joining regions trades geographic detail for consistency over time, and how to best make that trade depends on the data and the application. The parameters are documented in `tongfen_detect_anomalies`, the most important ones are + +* `rel_scale` and `abs_scale`, the relative and absolute decrease at which a change is half way to being fully surprising. The defaults of a 25% drop and a drop of 200 are tuned to population counts in regions of the size of dissemination areas. +* `total_surprise_cutoff`, how surprising the timeline of a region needs to be to become a candidate. The default of 0.75 is conservative, above we used 0.4 to also pick up less pronounced cases. +* `cutoff_fact`, `surprise_reduction_const` and `sum_fact` determine how much of the surprise a neighbour needs to take away for the regions to get joined. + +```{r} +c(0.4,0.6,0.75) %>% + lapply(\(cutoff) tibble(total_surprise_cutoff=cutoff, + regions_joined=tongfen_anomaly_joins(toronto,timeline,id="GeoUID", + total_surprise_cutoff=cutoff) %>% + nrow())) %>% + bind_rows() +``` + +Neighbours are by default determined by intersecting the geometries of the regions. This can miss neighbours if the geometries have been simplified, in that case the `neighbours` argument takes a table with the identifiers of neighbouring regions or a neighbours list from the **spdep** package. + +## Anomalies in TongFen data + +The functions work the same way on data on a common geography built by TongFen, where the regions are identified by their `TongfenID`. As an example we look at the dissemination area level population in the City of Vancouver for the 2001 through 2021 censuses. + +```{r} +regions <- list(CSD="5915022") +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") + +vancouver <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21",quiet=TRUE) + +joins <- tongfen_anomaly_joins(vancouver,paste0("Population_",datasets),total_surprise_cutoff=0.4) +joins +``` + +Passing the metadata to `tongfen_join_regions` makes sure the variables get aggregated the right way, numeric variables that are not part of the metadata are assumed to be additive. + +```{r} +vancouver_joined <- tongfen_join_regions(vancouver,joins,meta) +``` + +The `TongfenUID` of the joined regions lists all the dissemination areas they are made up of. + +```{r} +vancouver_joined %>% + st_drop_geometry() %>% + filter(TongfenID %in% joins$TongfenID_joined) %>% + select(TongfenID,TongfenUID,starts_with("Population")) +``` + +Variables that are not additive, like averages, can only be aggregated this way if the variable they are averaged over is part of the data. The alternative that always works is to join the regions in the correspondence the common geography was built from, and use the joined correspondence to aggregate the original data. This also is the way to use the joins for data other than the one that was used to detect the anomalies, for example to get average rents on the corrected geography. + +```{r} +correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets,regions=regions, + level="DA",quiet=TRUE) %>% + tongfen_join_correspondence(joins) + +rent_meta <- meta_for_ca_census_vectors(c(rent_2006="v_CA06_2050",rent_2016="v_CA16_4901")) + +rent_data <- c("CA06","CA16") %>% + lapply(\(ds) get_census(ds,regions=regions,level="DA",labels="short",quiet=TRUE, + vectors=rent_meta %>% filter(geo_dataset==ds) %>% pull(variable), + geo_format=if (ds=="CA16") "sf" else NA) %>% + rename(!!paste0("GeoUID",ds):="GeoUID")) %>% + setNames(c("CA06","CA16")) + +rents <- tongfen_aggregate(rent_data,correspondence,rent_meta,base_geo="CA16") + +rents %>% + st_drop_geometry() %>% + filter(TongfenID %in% joins$TongfenID_joined) %>% + select(TongfenID,rent_2006,rent_2016) +``` From 6702cbfdd178bb7c9974224d73b520df9ed9c668 Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 17:01:01 -0700 Subject: [PATCH 6/7] spelling --- NEWS.md | 6 +++--- R/tonfen_deprecated.R | 2 +- R/tongfen.R | 16 ++++++++-------- R/tongfen_ca.R | 18 +++++++++--------- R/tongfen_ca_deprecated.R | 6 +++--- R/tongfen_us.R | 2 +- README.md | 4 ++-- cran-comments.md | 6 +++++- man/add_census_ca_base_variables.Rd | 4 ++-- man/check_tongfen_areas.Rd | 4 ++-- man/check_tongfen_single_areas.Rd | 2 +- man/estimate_tongfen_correspondence.Rd | 2 +- man/estimate_tongfen_single_correspondence.Rd | 2 +- man/get_correspondence_ca_census_for.Rd | 2 +- man/get_tongfen_ca_census.Rd | 8 ++++---- man/get_tongfen_correspondence_ca_census.Rd | 4 ++-- man/get_tongfen_us_census.Rd | 2 +- man/meta_for_additive_variables.Rd | 4 ++-- man/meta_for_ca_census_vectors.Rd | 2 +- man/proportional_reaggregate.Rd | 2 +- man/tongfen_aggregate.Rd | 2 +- man/tongfen_ca_census_ct.Rd | 4 ++-- vignettes/tongfen-ca-estimate.Rmd | 2 +- vignettes/tongfen.Rmd | 8 ++++---- 24 files changed, 59 insertions(+), 55 deletions(-) diff --git a/NEWS.md b/NEWS.md index 4aa5878..2c97b3e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -122,12 +122,12 @@ - squish several edge case bugs # tongfen v.0.3.6 -## Major changs +## Major changes - better downsampling that can also accommodate averages - performance improvements ## Minor changes - better documentation -- allow for datasets vartiables by census year for canadian data +- allow for datasets variables by census year for Canadian data - fix issue where some metadata might get duplicated # tongfen 0.3.2 @@ -139,7 +139,7 @@ ## Major changes - Added `tongfen_estimate_ca_census` function for new CensusMapper endpoint, tying into new {cancensus} functionality. ## Minor changes -- Custom impelementation of `tongfen_etimate` for finer control +- Custom implementation of `tongfen_estimate` for finer control - Fix compatibility issue with changes in {sf} package # tongfen 0.3 diff --git a/R/tonfen_deprecated.R b/R/tonfen_deprecated.R index 9efe73c..372ed12 100644 --- a/R/tonfen_deprecated.R +++ b/R/tonfen_deprecated.R @@ -1,4 +1,4 @@ -#' Check geographic integrety +#' Check geographic integrity #' #' @description #' \lifecycle{deprecated} diff --git a/R/tongfen.R b/R/tongfen.R index c8f2070..f81c484 100644 --- a/R/tongfen.R +++ b/R/tongfen.R @@ -5,8 +5,8 @@ #' #' Generates metadata to be used in tongfen_aggregate. Variables need to be additive like counts. #' -#' @param dataset identifier for the dataset contianing the variable -#' @param variables (named) vecotor with additive variables +#' @param dataset identifier for the dataset containing the variable +#' @param variables (named) vector with additive variables #' @return a tibble to be used in tongfen_aggregate #' @export #' @@ -230,7 +230,7 @@ rename_with_meta <- function(data,meta,ds=NULL){ #' @description #' \lifecycle{maturing} #' -#' Aggregate variables secified in meta for several datasets according to correspondence. +#' Aggregate variables specified in meta for several datasets according to correspondence. #' #' @param data named list of datasets to be aggregated. The names identify the datasets, they are #' matched against the `geo_dataset` column in `meta` to pick the aggregation rules and labels @@ -344,7 +344,7 @@ tongfen_aggregate <- function(data,correspondence,meta=NULL, base_geo = NULL, na #' @param geo_match A named string informing on what column names to match data and parent_data #' @param categories Vector of column names to re-aggregate #' @param base Column name to use for proportional weighting when re-aggregating, or named vector with column name for each category. -#' Categries that should be re-aggregated as means should be set to NA and will only be reaggregated if the base data has NA values. +#' Categories that should be re-aggregated as means should be set to NA and will only be reaggregated if the base data has NA values. #' @return dataframe with downsampled variables from parent_data #' @keywords reaggregate proportionally wrt base variable #' @export @@ -477,7 +477,7 @@ proportional_reaggregate <- function(data,parent_data,geo_match,categories,base= select(-any_of(id)) } -#' Generate togfen correspondence for two geographies +#' Generate tongfen correspondence for two geographies #' #' @description #' \lifecycle{maturing} @@ -566,7 +566,7 @@ estimate_tongfen_single_correspondence <- function(geo1,geo2,geo1_uid,geo2_uid, correspondence } -#' Generate togfen correspondence for list of geographies +#' Generate tongfen correspondence for list of geographies #' #' @description #' \lifecycle{maturing} @@ -662,7 +662,7 @@ estimate_tongfen_correspondence <- function(data, -#' Check geographic integrety +#' Check geographic integrity #' #' @description #' \lifecycle{maturing} @@ -674,7 +674,7 @@ estimate_tongfen_correspondence <- function(data, #' simplified independently and differ in how water features are cut out, so a sizable area #' mismatch does not by itself mean the regions were matched up incorrectly. #' -#' @param data alist of geogrpahic data of class sf +#' @param data a list of geographic data of class sf #' @param correspondence Correspondence table with columns the unique geographic identifiers for each of the #' geographies and the TongfenID (and optionally TongfenUID and TongfenMethod) #' returned by `estimate_tongfen_correspondence`. diff --git a/R/tongfen_ca.R b/R/tongfen_ca.R index 6109e72..3d3d491 100644 --- a/R/tongfen_ca.R +++ b/R/tongfen_ca.R @@ -52,7 +52,7 @@ geo_dataset_from_dataset <- function(datasets){ unlist() } -#' Generate metadata from Candian census vectors +#' Generate metadata from Canadian census vectors #' #' @description #' \lifecycle{maturing} @@ -128,13 +128,13 @@ meta_for_ca_census_vectors <- function(vectors){ -#' Generate metadata from Candian census vectors +#' Generate metadata from Canadian census vectors #' #' @description #' \lifecycle{maturing} #' #' Add Population, Dwellings, and Household counts to metadata -#' @param meta ribble with metadata as for example provided by `meta_for_ca_census_vectors` +#' @param meta tibble with metadata as for example provided by `meta_for_ca_census_vectors` #' @return tibble with metadata add_census_ca_base_variables <- function(meta){ new_meta <- meta$geo_dataset %>% @@ -194,7 +194,7 @@ get_single_correspondence_ca_census_for <- function(year,level=c("DA","DB"),refr #' @description #' \lifecycle{maturing} #' -#' Get correspondence file for several Candian censuses on a common geography. Requires sf and cancensus package to be available +#' Get correspondence file for several Canadian censuses on a common geography. Requires sf and cancensus package to be available #' #' @param regions census region list, should be inclusive list of GeoUIDs across censuses #' @param geo_datasets vector of census geography dataset identifiers @@ -204,7 +204,7 @@ get_single_correspondence_ca_census_for <- function(year,level=c("DA","DB"),refr #' this method only works for "DB", "DA" and "CT" levels. #' * "estimate" uses `estimate_tongfen_correspondence` to build up the common geography from scratch based on geographies. #' * "identifier" assumes regions with identical geographic identifier are identical, and builds up the the correspondence for regions with unmatched geographic identifiers. -#' @param tolerance tolerance for `estimate_tongen_correspondence` in metres, default value is 50 metres, +#' @param tolerance tolerance for `estimate_tongfen_correspondence` in metres, default value is 50 metres, #' only used when method is 'estimate' or 'identifier' #' @param quiet suppress download progress output, default is `FALSE` #' @param refresh optional character, refresh data cache for this call, (default `FALSE`) @@ -347,15 +347,15 @@ get_tongfen_correspondence_ca_census <- function(geo_datasets, regions, level="C } -#' Togfen data from several Canadian censuses +#' Tongfen data from several Canadian censuses #' #' @description #' \lifecycle{maturing} #' -#' Get data from several Candian censuses on a common geography. Requires sf and cancensus package to be available +#' Get data from several Canadian censuses on a common geography. Requires sf and cancensus package to be available #' #' @param regions census region list, should be inclusive list of GeoUIDs across censuses -#' @param meta metadata for the census veraiables to aggregate, for example as returned +#' @param meta metadata for the census variables to aggregate, for example as returned #' by \code{meta_for_ca_census_vectors}. #' @param level aggregation level to return data on (default is "CT") #' @param method tongfen method, options are "statcan" (the default), "estimate", "identifier". @@ -367,7 +367,7 @@ get_tongfen_correspondence_ca_census <- function(geo_datasets, regions, level="C #' any geographic data #' @param na.rm logical, determines how NA values should be treated when aggregating variables, #' default is `FALSE` -#' @param tolerance tolerance for `estimate_tongen_correspondence` in metres, default value is 50 metres, +#' @param tolerance tolerance for `estimate_tongfen_correspondence` in metres, default value is 50 metres, #' only used when method is 'estimate' or 'identifier' #' @param quiet suppress download progress output, default is `FALSE` #' @param refresh optional character, refresh data cache for this call, (default `FALSE`) diff --git a/R/tongfen_ca_deprecated.R b/R/tongfen_ca_deprecated.R index ee7551a..f0cf13b 100644 --- a/R/tongfen_ca_deprecated.R +++ b/R/tongfen_ca_deprecated.R @@ -106,8 +106,8 @@ get_tongfen_ca_census_ct_from_da <- function(regions,vectors,geo_format=NA,use_c #' \lifecycle{deprecated} #' #' Aggregate variables to common CTs, returns data2 on new tiling matching data1 geography -#' @param data1 cancensus CT level datatset for year1 < year2 to serve as base for common geography -#' @param data2 cancensus CT level datatset for year2 to be aggregated to common geography +#' @param data1 cancensus CT level dataset for year1 < year2 to serve as base for common geography +#' @param data2 cancensus CT level dataset for year2 to be aggregated to common geography #' @param data2_sum_vars vector of variable names to by summed up when aggregating geographies #' @param data2_group_vars optional vector of grouping variables #' @param na.rm optional parameter to remove NA values when summing, default = `TRUE` @@ -161,7 +161,7 @@ tongfen_ca_census_ct <- function(data1,data2,data2_sum_vars,data2_group_vars=c() #' #' @description #' \lifecycle{deprecated} -#' Joins the StatCan correspodence files for several census years +#' Joins the StatCan correspondence files for several census years #' #' @param years list of census years #' @param level geographic level, DA or DB diff --git a/R/tongfen_us.R b/R/tongfen_us.R index 4d426b3..7e9790a 100644 --- a/R/tongfen_us.R +++ b/R/tongfen_us.R @@ -366,7 +366,7 @@ sumfile_for_dataset <- function(sumfile, ds){ #' censuses or a vector named by dataset, for example `c(dec2010="sf1", dec2020="dhc")`. Default #' is `NULL`, which leaves the choice to tidycensus. Note that tidycensus defaults the 2020 #' census to the PL 94-171 redistricting file, most 2020 variables need `sumfile="dhc"`. -#' @return sf object with (wide form) census variables with census year as suffix (separated by underdcore "_"). +#' @return sf object with (wide form) census variables with census year as suffix (separated by underscore "_"). #' @export #' #' @examples diff --git a/README.md b/README.md index 6f1e96b..d86e2eb 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ A convenience function to validate geographic TongFen fit via area comparison is Finding a common tiling of several different yet congruent geographies is only one part of the problem TongFen addresses, aggregating up the variables is the other part. The `tongfen` package deals with this using a *metadata* table that specifies how variables should be aggregated. In it's simplest form values are simply added up. The `meta_for_additive_variables` convenience function builds the metadata for additive variables. Metadata for non-additive variables like averages, ratios or percentages needs more care to build, it requires additional information on the **parent variable** that specifies the denominator of the average, ratio or percentage. Other data, like medians, can't be aggregated up, although `tongfen` can provide estimates of medians on aggregated geographies by treating them as averages. ### Packaged data -The package ships with a subset of [voting data from Elections Canada](https://www.elections.ca/content.aspx?section=ele&dir=pas&document=index&lang=e) for the 42nd and 43rd federal elections as well as the polling district geographies for the [42nd](https://open.canada.ca/data/en/dataset/6a78ccfd-6bba-4109-b040-87cb8c71ec35) and [43rd](https://open.canada.ca/data/en/dataset/e70e3263-8584-4f22-94cb-8c15b616cbfc). This facilitates running the example vignette on polling districts without having to download external data. Both are available as open data covered under the [Open Government Licence - Canda](https://open.canada.ca/en/open-government-licence-canada). +The package ships with a subset of [voting data from Elections Canada](https://www.elections.ca/content.aspx?section=ele&dir=pas&document=index&lang=e) for the 42nd and 43rd federal elections as well as the polling district geographies for the [42nd](https://open.canada.ca/data/en/dataset/6a78ccfd-6bba-4109-b040-87cb8c71ec35) and [43rd](https://open.canada.ca/data/en/dataset/e70e3263-8584-4f22-94cb-8c15b616cbfc). This facilitates running the example vignette on polling districts without having to download external data. Both are available as open data covered under the [Open Government Licence - Canada](https://open.canada.ca/en/open-government-licence-canada). ## Data-specific implementations The need for TongFen comes up frequently with certain types of geographies. Census geographies is one such example. In some cases these data sources come with their own correspondence files that go beyond geographic matchup but also join regions to alleviate data integrity problems like geocoding issues. @@ -75,7 +75,7 @@ The `tongfen` package is open to add extensions for other specialized data sourc ## Fixed target geography estimation -When geographies aren't sufficiently congruent or the target geography is fixed, we won't be able to use the `tongfen` methods to compute the data on a common geography but have to instead rely on estimates. The `tongfen_estimate` makes no assumption on the underlying geographies and returns estimates of the data on the target geography. It uses area-weighted interpolation to achieve this, and can be refined to dasymmetric estimates using the `proportional_reaggregate` function. +When geographies aren't sufficiently congruent or the target geography is fixed, we won't be able to use the `tongfen` methods to compute the data on a common geography but have to instead rely on estimates. The `tongfen_estimate` makes no assumption on the underlying geographies and returns estimates of the data on the target geography. It uses area-weighted interpolation to achieve this, and can be refined to dasymetric estimates using the `proportional_reaggregate` function. This method has the example that it works independent of the nature of the underlying geographies, but comes at the heavy price of only being an estimate. To be useful for research purposes we also need methods to estimate the errors this introduces and the effects this has on subsequent analysis results. diff --git a/cran-comments.md b/cran-comments.md index 01aebb3..298facd 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -1,5 +1,8 @@ # tongfen v.0.3.9 ## Major changes +- new experimental functions `tongfen_detect_anomalies`, `tongfen_anomaly_joins`, `tongfen_join_regions` + and `tongfen_join_correspondence` to detect and correct for likely geocoding anomalies in timelines + on a common geography, together with a new vignette - StatCan correspondence files are now downloaded as parquet files from a mirror, Statistics Canada put the original files behind a browser check that blocks programmatic downloads, which broke `method = "statcan"` @@ -16,10 +19,11 @@ - `refresh = TRUE` now also refreshes the cached StatCan correspondence files - US Census Bureau relationship files are downloaded to a temporary file before being moved to the cache - added missing `\value` documentation for `tongfen_estimate_ca_census` and `tongfen_ca_census_ct` +- fixed typos in the documentation # Test environments * local macOS installation, R 4.6.0 -* GitHub actions (windows-latest, macOS-latest, ubuntu-latest) on release, devel and oldrel +* GitHub actions: macOS-latest (release), windows-latest (release), ubuntu-latest (devel, release, oldrel-1) # R CMD check results 0 errors | 0 warnings | 0 notes diff --git a/man/add_census_ca_base_variables.Rd b/man/add_census_ca_base_variables.Rd index c1a5406..371400d 100644 --- a/man/add_census_ca_base_variables.Rd +++ b/man/add_census_ca_base_variables.Rd @@ -2,12 +2,12 @@ % Please edit documentation in R/tongfen_ca.R \name{add_census_ca_base_variables} \alias{add_census_ca_base_variables} -\title{Generate metadata from Candian census vectors} +\title{Generate metadata from Canadian census vectors} \usage{ add_census_ca_base_variables(meta) } \arguments{ -\item{meta}{ribble with metadata as for example provided by `meta_for_ca_census_vectors`} +\item{meta}{tibble with metadata as for example provided by `meta_for_ca_census_vectors`} } \value{ tibble with metadata diff --git a/man/check_tongfen_areas.Rd b/man/check_tongfen_areas.Rd index d03414a..99a35e6 100644 --- a/man/check_tongfen_areas.Rd +++ b/man/check_tongfen_areas.Rd @@ -2,12 +2,12 @@ % Please edit documentation in R/tongfen.R \name{check_tongfen_areas} \alias{check_tongfen_areas} -\title{Check geographic integrety} +\title{Check geographic integrity} \usage{ check_tongfen_areas(data, correspondence) } \arguments{ -\item{data}{alist of geogrpahic data of class sf} +\item{data}{a list of geographic data of class sf} \item{correspondence}{Correspondence table with columns the unique geographic identifiers for each of the geographies and the TongfenID (and optionally TongfenUID and TongfenMethod) diff --git a/man/check_tongfen_single_areas.Rd b/man/check_tongfen_single_areas.Rd index f20795f..ec7401d 100644 --- a/man/check_tongfen_single_areas.Rd +++ b/man/check_tongfen_single_areas.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/tonfen_deprecated.R \name{check_tongfen_single_areas} \alias{check_tongfen_single_areas} -\title{Check geographic integrety} +\title{Check geographic integrity} \usage{ check_tongfen_single_areas(geo1, geo2, correspondence) } diff --git a/man/estimate_tongfen_correspondence.Rd b/man/estimate_tongfen_correspondence.Rd index 0322925..56576a1 100644 --- a/man/estimate_tongfen_correspondence.Rd +++ b/man/estimate_tongfen_correspondence.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/tongfen.R \name{estimate_tongfen_correspondence} \alias{estimate_tongfen_correspondence} -\title{Generate togfen correspondence for list of geographies} +\title{Generate tongfen correspondence for list of geographies} \usage{ estimate_tongfen_correspondence( data, diff --git a/man/estimate_tongfen_single_correspondence.Rd b/man/estimate_tongfen_single_correspondence.Rd index 5a44c3e..a145363 100644 --- a/man/estimate_tongfen_single_correspondence.Rd +++ b/man/estimate_tongfen_single_correspondence.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/tongfen.R \name{estimate_tongfen_single_correspondence} \alias{estimate_tongfen_single_correspondence} -\title{Generate togfen correspondence for two geographies} +\title{Generate tongfen correspondence for two geographies} \usage{ estimate_tongfen_single_correspondence( geo1, diff --git a/man/get_correspondence_ca_census_for.Rd b/man/get_correspondence_ca_census_for.Rd index 90bec0d..25a37ec 100644 --- a/man/get_correspondence_ca_census_for.Rd +++ b/man/get_correspondence_ca_census_for.Rd @@ -18,5 +18,5 @@ tibble with correspondence table`spanning all years } \description{ \lifecycle{deprecated} -Joins the StatCan correspodence files for several census years +Joins the StatCan correspondence files for several census years } diff --git a/man/get_tongfen_ca_census.Rd b/man/get_tongfen_ca_census.Rd index b5d388c..4962ec0 100644 --- a/man/get_tongfen_ca_census.Rd +++ b/man/get_tongfen_ca_census.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/tongfen_ca.R \name{get_tongfen_ca_census} \alias{get_tongfen_ca_census} -\title{Togfen data from several Canadian censuses} +\title{Tongfen data from several Canadian censuses} \usage{ get_tongfen_ca_census( regions, @@ -21,7 +21,7 @@ get_tongfen_ca_census( \arguments{ \item{regions}{census region list, should be inclusive list of GeoUIDs across censuses} -\item{meta}{metadata for the census veraiables to aggregate, for example as returned +\item{meta}{metadata for the census variables to aggregate, for example as returned by \code{meta_for_ca_census_vectors}.} \item{level}{aggregation level to return data on (default is "CT")} @@ -38,7 +38,7 @@ any geographic data} \item{na.rm}{logical, determines how NA values should be treated when aggregating variables, default is `FALSE`} -\item{tolerance}{tolerance for `estimate_tongen_correspondence` in metres, default value is 50 metres, +\item{tolerance}{tolerance for `estimate_tongfen_correspondence` in metres, default value is 50 metres, only used when method is 'estimate' or 'identifier'} \item{quiet}{suppress download progress output, default is `FALSE`} @@ -56,7 +56,7 @@ dataframe with variables on common geography \description{ \lifecycle{maturing} -Get data from several Candian censuses on a common geography. Requires sf and cancensus package to be available +Get data from several Canadian censuses on a common geography. Requires sf and cancensus package to be available } \examples{ # Get rent data for census years 2001 through 2016 diff --git a/man/get_tongfen_correspondence_ca_census.Rd b/man/get_tongfen_correspondence_ca_census.Rd index ad8d6c8..5d0cb82 100644 --- a/man/get_tongfen_correspondence_ca_census.Rd +++ b/man/get_tongfen_correspondence_ca_census.Rd @@ -28,7 +28,7 @@ this method only works for "DB", "DA" and "CT" levels. * "estimate" uses `estimate_tongfen_correspondence` to build up the common geography from scratch based on geographies. * "identifier" assumes regions with identical geographic identifier are identical, and builds up the the correspondence for regions with unmatched geographic identifiers.} -\item{tolerance}{tolerance for `estimate_tongen_correspondence` in metres, default value is 50 metres, +\item{tolerance}{tolerance for `estimate_tongfen_correspondence` in metres, default value is 50 metres, only used when method is 'estimate' or 'identifier'} \item{quiet}{suppress download progress output, default is `FALSE`} @@ -44,7 +44,7 @@ dataframe with the multi-census correspondence file \description{ \lifecycle{maturing} -Get correspondence file for several Candian censuses on a common geography. Requires sf and cancensus package to be available +Get correspondence file for several Canadian censuses on a common geography. Requires sf and cancensus package to be available } \examples{ # Get correspondance files between CTs in 2006 and 2016 censuses in Vancouver CMA diff --git a/man/get_tongfen_us_census.Rd b/man/get_tongfen_us_census.Rd index 6fa8185..f093348 100644 --- a/man/get_tongfen_us_census.Rd +++ b/man/get_tongfen_us_census.Rd @@ -36,7 +36,7 @@ is `NULL`, which leaves the choice to tidycensus. Note that tidycensus defaults census to the PL 94-171 redistricting file, most 2020 variables need `sumfile="dhc"`.} } \value{ -sf object with (wide form) census variables with census year as suffix (separated by underdcore "_"). +sf object with (wide form) census variables with census year as suffix (separated by underscore "_"). } \description{ \lifecycle{maturing} diff --git a/man/meta_for_additive_variables.Rd b/man/meta_for_additive_variables.Rd index 468e53f..42ca9af 100644 --- a/man/meta_for_additive_variables.Rd +++ b/man/meta_for_additive_variables.Rd @@ -7,9 +7,9 @@ meta_for_additive_variables(dataset, variables) } \arguments{ -\item{dataset}{identifier for the dataset contianing the variable} +\item{dataset}{identifier for the dataset containing the variable} -\item{variables}{(named) vecotor with additive variables} +\item{variables}{(named) vector with additive variables} } \value{ a tibble to be used in tongfen_aggregate diff --git a/man/meta_for_ca_census_vectors.Rd b/man/meta_for_ca_census_vectors.Rd index bd07480..b911536 100644 --- a/man/meta_for_ca_census_vectors.Rd +++ b/man/meta_for_ca_census_vectors.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/tongfen_ca.R \name{meta_for_ca_census_vectors} \alias{meta_for_ca_census_vectors} -\title{Generate metadata from Candian census vectors} +\title{Generate metadata from Canadian census vectors} \usage{ meta_for_ca_census_vectors(vectors) } diff --git a/man/proportional_reaggregate.Rd b/man/proportional_reaggregate.Rd index 45d81ad..4ab5caf 100644 --- a/man/proportional_reaggregate.Rd +++ b/man/proportional_reaggregate.Rd @@ -22,7 +22,7 @@ proportional_reaggregate( \item{categories}{Vector of column names to re-aggregate} \item{base}{Column name to use for proportional weighting when re-aggregating, or named vector with column name for each category. -Categries that should be re-aggregated as means should be set to NA and will only be reaggregated if the base data has NA values.} +Categories that should be re-aggregated as means should be set to NA and will only be reaggregated if the base data has NA values.} } \value{ dataframe with downsampled variables from parent_data diff --git a/man/tongfen_aggregate.Rd b/man/tongfen_aggregate.Rd index 87ce8e4..4e0db33 100644 --- a/man/tongfen_aggregate.Rd +++ b/man/tongfen_aggregate.Rd @@ -35,7 +35,7 @@ aggregated dataset of class sf if base_geo is not NULL and data is of type sf or \description{ \lifecycle{maturing} -Aggregate variables secified in meta for several datasets according to correspondence. +Aggregate variables specified in meta for several datasets according to correspondence. } \examples{ # aggregate census tract level 2006 and 2016 population data on common geography built through diff --git a/man/tongfen_ca_census_ct.Rd b/man/tongfen_ca_census_ct.Rd index 0b12193..0475e70 100644 --- a/man/tongfen_ca_census_ct.Rd +++ b/man/tongfen_ca_census_ct.Rd @@ -13,9 +13,9 @@ tongfen_ca_census_ct( ) } \arguments{ -\item{data1}{cancensus CT level datatset for year1 < year2 to serve as base for common geography} +\item{data1}{cancensus CT level dataset for year1 < year2 to serve as base for common geography} -\item{data2}{cancensus CT level datatset for year2 to be aggregated to common geography} +\item{data2}{cancensus CT level dataset for year2 to be aggregated to common geography} \item{data2_sum_vars}{vector of variable names to by summed up when aggregating geographies} diff --git a/vignettes/tongfen-ca-estimate.Rmd b/vignettes/tongfen-ca-estimate.Rmd index ba3bcb9..bd73a86 100644 --- a/vignettes/tongfen-ca-estimate.Rmd +++ b/vignettes/tongfen-ca-estimate.Rmd @@ -24,7 +24,7 @@ library(ggplot2) The `tongfen_estimate_ca_census` function implements the full pipeline to estimate census data on custom geographies. -As an example, we estimate the share of people in low income in Vancouver's skytrain station neighbourhoods. The station neighbourhoods are available as part of the `cancenus` package. +As an example, we estimate the share of people in low income in Vancouver's skytrain station neighbourhoods. The station neighbourhoods are available as part of the `cancensus` package. ```{r} station_buffers <- cancensus::COV_SKYTRAIN_STATIONS diff --git a/vignettes/tongfen.Rmd b/vignettes/tongfen.Rmd index 27426c9..30846ba 100644 --- a/vignettes/tongfen.Rmd +++ b/vignettes/tongfen.Rmd @@ -48,7 +48,7 @@ data <- years %>% }) %>% setNames(years) ``` -Plotting the cenus tracts for our four census years shows how census tracts changed over the years. +Plotting the census tracts for our four census years shows how census tracts changed over the years. ```{r} data %>% @@ -60,9 +60,9 @@ data %>% labs(title="Vancouver census tracts",caption="StatCan Census 2001-2016") ``` -For this example we will estimate the correspondence between these regions from the geographic data using the `estimate_tongfen_correspondence` function. Unfortunately this is not an exact science, for example over the years census regions get adjusted to better align with the road network. Other harmless boundary adjustemens can happen along water boundaries, or re-jigging boundaries in unpopulated areas. +For this example we will estimate the correspondence between these regions from the geographic data using the `estimate_tongfen_correspondence` function. Unfortunately this is not an exact science, for example over the years census regions get adjusted to better align with the road network. Other harmless boundary adjustments can happen along water boundaries, or re-jigging boundaries in unpopulated areas. -We are going to impose a tolerance of 200m, where we are calling two census tract the same if they differ by no more than 200m. We are specifying that these calculations should be carried out in the Statistics Canada Lambert (EPSG:3347) refernce system with units metres. +We are going to impose a tolerance of 200m, where we are calling two census tract the same if they differ by no more than 200m. We are specifying that these calculations should be carried out in the Statistics Canada Lambert (EPSG:3347) reference system with units metres. ```{r} correspondence <- estimate_tongfen_correspondence(data, geo_identifiers, @@ -124,7 +124,7 @@ years %>% ``` ## Population change -It's time to go back to our original goal of mapping population change. For this we need to specify how to aggregate up the population data, which is by simply adding them up. The `meta_for_additive_variables` convenience function generates the appropriate metatdata that specifies how to deal with this data. +It's time to go back to our original goal of mapping population change. For this we need to specify how to aggregate up the population data, which is by simply adding them up. The `meta_for_additive_variables` convenience function generates the appropriate metadata that specifies how to deal with this data. ```{r} meta <- meta_for_additive_variables(years,"Population") From 5f304e09fc03e9186b3f10bd5916da0440550c07 Mon Sep 17 00:00:00 2001 From: Jens von Bergmann Date: Sun, 4 Oct 2026 17:14:21 -0700 Subject: [PATCH 7/7] pkgdown docs --- docs/404.html | 5 +- docs/LICENSE-text.html | 5 +- docs/LICENSE.html | 5 +- docs/articles/index.html | 12 +- docs/articles/index.md | 5 + docs/articles/polling_districts.html | 6 +- docs/articles/polling_districts.md | 1 + docs/articles/tongfen-ca-estimate.html | 7 +- docs/articles/tongfen-ca-estimate.md | 2 +- docs/articles/tongfen.html | 13 +- docs/articles/tongfen.md | 9 +- docs/articles/tongfen_anomalies.html | 367 ++++++++++++++++++ docs/articles/tongfen_anomalies.md | 311 +++++++++++++++ .../figure-html/unnamed-chunk-3-1.png | Bin 0 -> 85698 bytes .../figure-html/unnamed-chunk-8-1.png | Bin 0 -> 47963 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 0 -> 242301 bytes docs/articles/tongfen_ca.html | 7 +- docs/articles/tongfen_us.html | 5 +- docs/authors.html | 9 +- docs/authors.md | 4 +- ...=> 109cfdfecabbfbf5e2364aaceece5d12.woff2} | Bin ...=> 5cd426f27cf61d86c207d97f8db6cf8a.woff2} | Bin ...=> a8e2cb7ac3da2c4d38cd7c8c06ca5e46.woff2} | Bin ...=> c0540813382b64c1dde636c1fa0b592a.woff2} | Bin ...=> e0243c1a22ed284bb57c07b3ac886694.woff2} | Bin docs/index.html | 23 +- docs/index.md | 36 +- docs/llms.txt | 66 ++-- docs/news/index.html | 7 +- docs/news/index.md | 2 +- docs/pkgdown.yml | 5 +- .../add_census_ca_base_variables.html | 11 +- .../reference/add_census_ca_base_variables.md | 4 +- docs/reference/aggregate_data_with_meta.html | 5 +- docs/reference/check_tongfen_areas.html | 11 +- docs/reference/check_tongfen_areas.md | 4 +- .../reference/check_tongfen_single_areas.html | 9 +- docs/reference/check_tongfen_single_areas.md | 2 +- .../estimate_tongfen_correspondence.html | 9 +- .../estimate_tongfen_correspondence.md | 2 +- ...stimate_tongfen_single_correspondence.html | 9 +- .../estimate_tongfen_single_correspondence.md | 2 +- .../get_correspondence_ca_census_for.html | 11 +- .../get_correspondence_ca_census_for.md | 2 +- ...t_single_correspondence_ca_census_for.html | 19 +- ...get_single_correspondence_ca_census_for.md | 6 + docs/reference/get_tongfen_ca_census.html | 19 +- docs/reference/get_tongfen_ca_census.md | 8 +- .../get_tongfen_ca_census_ct_from_da.html | 5 +- docs/reference/get_tongfen_census_ct.html | 5 +- docs/reference/get_tongfen_census_da.html | 5 +- .../get_tongfen_correspondence_ca_census.html | 13 +- .../get_tongfen_correspondence_ca_census.md | 4 +- .../get_tongfen_correspondence_us_census.html | 8 +- .../get_tongfen_correspondence_us_census.md | 5 +- docs/reference/get_tongfen_us_census.html | 14 +- docs/reference/get_tongfen_us_census.md | 8 +- docs/reference/index.html | 49 ++- docs/reference/index.md | 25 +- .../meta_for_additive_variables.html | 9 +- docs/reference/meta_for_additive_variables.md | 4 +- .../reference/meta_for_ca_census_vectors.html | 11 +- docs/reference/meta_for_ca_census_vectors.md | 4 +- docs/reference/proportional_reaggregate.html | 7 +- docs/reference/proportional_reaggregate.md | 4 +- docs/reference/tongfen_aggregate.html | 24 +- docs/reference/tongfen_aggregate.md | 16 +- docs/reference/tongfen_anomaly_joins.html | 202 ++++++++++ docs/reference/tongfen_anomaly_joins.md | 139 +++++++ docs/reference/tongfen_ca_census_ct.html | 14 +- docs/reference/tongfen_ca_census_ct.md | 10 +- docs/reference/tongfen_detect_anomalies.html | 228 +++++++++++ docs/reference/tongfen_detect_anomalies.md | 152 ++++++++ docs/reference/tongfen_estimate.html | 9 +- docs/reference/tongfen_estimate.md | 4 +- .../reference/tongfen_estimate_ca_census.html | 15 +- docs/reference/tongfen_estimate_ca_census.md | 11 +- .../tongfen_join_correspondence.html | 122 ++++++ docs/reference/tongfen_join_correspondence.md | 55 +++ docs/reference/tongfen_join_regions.html | 144 +++++++ docs/reference/tongfen_join_regions.md | 77 ++++ .../tongfen_tag_largest_overlap.html | 17 +- docs/reference/tongfen_tag_largest_overlap.md | 14 +- .../vancouver_elections_data_2015.html | 5 +- .../vancouver_elections_data_2019.html | 5 +- .../vancouver_elections_geos_2015.html | 5 +- .../vancouver_elections_geos_2019.html | 5 +- docs/search.json | 2 +- docs/sitemap.xml | 5 + 89 files changed, 2244 insertions(+), 261 deletions(-) create mode 100644 docs/articles/tongfen_anomalies.html create mode 100644 docs/articles/tongfen_anomalies.md create mode 100644 docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-3-1.png create mode 100644 docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-8-1.png create mode 100644 docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-9-1.png rename docs/deps/bootstrap-5.3.8/fonts/{ee91700cdbf7ce16c054c2bb8946c736.woff2 => 109cfdfecabbfbf5e2364aaceece5d12.woff2} (100%) rename docs/deps/bootstrap-5.3.8/fonts/{c2f002b3a87d3f9bfeebb23d32cfd9f8.woff2 => 5cd426f27cf61d86c207d97f8db6cf8a.woff2} (100%) rename docs/deps/bootstrap-5.3.8/fonts/{07d40e985ad7c747025dabb9f22142c4.woff2 => a8e2cb7ac3da2c4d38cd7c8c06ca5e46.woff2} (100%) rename docs/deps/bootstrap-5.3.8/fonts/{1f5e011d6aae0d98fc0518e1a303e99a.woff2 => c0540813382b64c1dde636c1fa0b592a.woff2} (100%) rename docs/deps/bootstrap-5.3.8/fonts/{626330658504e338ee86aec8e957426b.woff2 => e0243c1a22ed284bb57c07b3ac886694.woff2} (100%) create mode 100644 docs/reference/tongfen_anomaly_joins.html create mode 100644 docs/reference/tongfen_anomaly_joins.md create mode 100644 docs/reference/tongfen_detect_anomalies.html create mode 100644 docs/reference/tongfen_detect_anomalies.md create mode 100644 docs/reference/tongfen_join_correspondence.html create mode 100644 docs/reference/tongfen_join_correspondence.md create mode 100644 docs/reference/tongfen_join_regions.html create mode 100644 docs/reference/tongfen_join_regions.md diff --git a/docs/404.html b/docs/404.html index d0348fd..008e97f 100644 --- a/docs/404.html +++ b/docs/404.html @@ -30,7 +30,7 @@ tongfen - 0.3.8 + 0.3.9 + + + + + +
+ + + + +
+
+ + + +

TongFen makes data on different geographies comparable by aggregating +it up to a common geography. The result is only as good as the geocoding +that assigned the underlying data to geographic regions in the first +place. Geocoding changes over time, and the same dwelling units, and the +people living in them, can get assigned to different neighbouring +regions in different years. In a timeline on a common geography this +shows up as a surprising drop in one region that is offset by a jump in +a neighbouring region.

+

The fix is in the spirit of TongFen, joining the affected regions +gives a slightly coarser geography on which the data is consistent over +time. The functions in this vignette look for such patterns and join the +regions on demand. The method is explained in more detail in a blog +post, this vignette follows the example from that post.

+
+library(dplyr)
+library(tidyr)
+library(ggplot2)
+library(cancensus)
+library(sf)
+library(tongfen)
+# cancensus::set_api_key("<your cancensus API key>")
+
+

Population timelines for Toronto +

+

As an example we take the population from the 1971 through 2011 +censuses that Statistics Canada tabulated on 2016 dissemination areas, +together with the 2016 population. All data comes on the same geography, +so there is no need to TongFen, but the data for the earlier years is +geocoded from the road network and block face of the time, which does +not always match up with 2016 dissemination areas.

+
+years <- c(1971,seq(1981,2011,5))
+vectors <- c(setNames(paste0("v_CA",years,"x16_1"),years),"2016"="v_CA16_1")
+timeline <- names(vectors)
+
+toronto <- get_census("CA16CT",regions=list(CSD="3520005"),vectors=vectors,
+                      level="DA",geo_format="sf",quiet=TRUE) %>%
+  select(GeoUID,all_of(timeline)) %>%
+  mutate(across(all_of(timeline),\(x) coalesce(x,0)))
+

Dissemination areas without population in a given year come back as +missing values. Changes from or to a missing value are never considered +surprising, so we set them to zero to mark these as areas where nobody +got counted.

+

The area around Crescent Town shows what the problem looks like.

+
+crescent_town <- c("35204370","35204765")
+
+plot_timelines <- function(data) {
+  data %>%
+    st_drop_geometry() %>%
+    pivot_longer(all_of(timeline),names_to="Year",values_to="Population") %>%
+    ggplot(aes(x=Year,y=Population,colour=GeoUID,group=GeoUID)) +
+    geom_line() +
+    geom_point() +
+    scale_y_continuous(labels=scales::comma,limits=c(0,NA))
+}
+
+toronto %>%
+  filter(GeoUID %in% crescent_town) %>%
+  plot_timelines() +
+  labs(title="Population in two neighbouring dissemination areas")
+

+

The population jumps back and forth between the two areas, while the +sum of the two is fairly steady from 1981 on. People did not move back +and forth, their homes got geocoded to a different dissemination area in +different years.

+
+
+

Detecting anomalies +

+

tongfen_detect_anomalies lists the regions with +surprising drops. Only decreases are surprising, and a decrease needs to +be large in both relative and absolute terms. For each of these +candidate regions it finds the neighbouring region that takes away most +of the surprise when both are joined, and checks if that reduction is +large enough to justify joining them. Our regions are identified by +their GeoUID instead of the TongfenID the +function looks for by default.

+
+anomalies <- tongfen_detect_anomalies(toronto,timeline,id="GeoUID",total_surprise_cutoff=0.4)
+
+anomalies %>% filter(GeoUID %in% crescent_town)
+#> # A tibble: 2 × 7
+#>   GeoUID   surprise_count surprise_total period  neighbour surprise_total_joined
+#>   <chr>             <int>          <dbl> <chr>   <chr>                     <dbl>
+#> 1 35204370              3          0.933 1991-1… 35204765                  0.616
+#> 2 35204765              2          1.03  1981-1… 35204370                  0.142
+#> # ℹ 1 more variable: join <lgl>
+

Both areas are candidates, and each one is the neighbour that best +explains the surprising drops of the other. Not all candidates find a +neighbour to pair up with. Population does drop for real, for example +when a site gets cleared for redevelopment, and such regions are left +alone.

+
+anomalies %>% count(join)
+#> # A tibble: 2 × 2
+#>   join      n
+#>   <lgl> <int>
+#> 1 FALSE   254
+#> 2 TRUE    288
+
+
+

Joining regions +

+

tongfen_anomaly_joins joins the regions that qualify and +looks again, joined regions can have surprising drops that are +complemented by another neighbour. This repeats until there are no more +regions left to join. The result lists the regions that got joined +together with the identifier of the joined region they are now part of +and the round in which they first got joined.

+
+joins <- tongfen_anomaly_joins(toronto,timeline,id="GeoUID",total_surprise_cutoff=0.4)
+
+joins %>% filter(GeoUID %in% crescent_town)
+#> # A tibble: 2 × 3
+#>   GeoUID   GeoUID_joined round
+#>   <chr>    <chr>         <int>
+#> 1 35204370 35204370          1
+#> 2 35204765 35204370          1
+

tongfen_join_regions applies the joins to the data, +aggregating the variables and geometries of the regions that get joined +and leaving all others as they are.

+
+toronto_joined <- tongfen_join_regions(toronto,joins,id="GeoUID")
+
+c(original=nrow(toronto),joined=nrow(toronto_joined))
+#> original   joined 
+#>     3702     3430
+
+toronto_joined %>%
+  filter(GeoUID %in% crescent_town) %>%
+  plot_timelines() +
+  labs(title="Population in the joined region")
+

+

The map shows the regions that got joined around Crescent Town.

+
+bbox <- toronto %>% filter(GeoUID %in% crescent_town) %>% st_buffer(1500) %>% st_bbox()
+
+ggplot(toronto_joined %>% mutate(joined=GeoUID %in% joins$GeoUID_joined)) +
+  geom_sf(aes(fill=joined),linewidth=0.1) +
+  geom_sf(data=toronto,fill=NA,linewidth=0.1,linetype="dotted") +
+  scale_fill_manual(values=c("TRUE"="steelblue","FALSE"="whitesmoke"),guide="none") +
+  coord_sf(datum=NA,xlim=bbox[c("xmin","xmax")],ylim=bbox[c("ymin","ymax")]) +
+  labs(title="Joined regions around Crescent Town",
+       caption="Joined regions in blue, original dissemination areas dotted")
+

+
+
+

Tuning +

+

Joining regions trades geographic detail for consistency over time, +and how to best make that trade depends on the data and the application. +The parameters are documented in tongfen_detect_anomalies, +the most important ones are

+
    +
  • +rel_scale and abs_scale, the relative and +absolute decrease at which a change is half way to being fully +surprising. The defaults of a 25% drop and a drop of 200 are tuned to +population counts in regions of the size of dissemination areas.
  • +
  • +total_surprise_cutoff, how surprising the timeline of a +region needs to be to become a candidate. The default of 0.75 is +conservative, above we used 0.4 to also pick up less pronounced +cases.
  • +
  • +cutoff_fact, surprise_reduction_const and +sum_fact determine how much of the surprise a neighbour +needs to take away for the regions to get joined.
  • +
+
+c(0.4,0.6,0.75) %>%
+  lapply(\(cutoff) tibble(total_surprise_cutoff=cutoff,
+                          regions_joined=tongfen_anomaly_joins(toronto,timeline,id="GeoUID",
+                                                               total_surprise_cutoff=cutoff) %>%
+                            nrow())) %>%
+  bind_rows()
+#> # A tibble: 3 × 2
+#>   total_surprise_cutoff regions_joined
+#>                   <dbl>          <int>
+#> 1                  0.4             477
+#> 2                  0.6             273
+#> 3                  0.75            152
+

Neighbours are by default determined by intersecting the geometries +of the regions. This can miss neighbours if the geometries have been +simplified, in that case the neighbours argument takes a +table with the identifiers of neighbouring regions or a neighbours list +from the spdep package.

+
+
+

Anomalies in TongFen data +

+

The functions work the same way on data on a common geography built +by TongFen, where the regions are identified by their +TongfenID. As an example we look at the dissemination area +level population in the City of Vancouver for the 2001 through 2021 +censuses.

+
+regions <- list(CSD="5915022")
+datasets <- c("CA01","CA06","CA11","CA16","CA21")
+meta <- meta_for_additive_variables(datasets,"Population")
+
+vancouver <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21",quiet=TRUE)
+
+joins <- tongfen_anomaly_joins(vancouver,paste0("Population_",datasets),total_surprise_cutoff=0.4)
+joins
+#> # A tibble: 4 × 3
+#>   TongfenID TongfenID_joined round
+#>   <chr>     <chr>            <int>
+#> 1 59150762  59150762             1
+#> 2 59153181  59150762             1
+#> 3 59150765  59150765             1
+#> 4 59150770  59150765             1
+

Passing the metadata to tongfen_join_regions makes sure +the variables get aggregated the right way, numeric variables that are +not part of the metadata are assumed to be additive.

+
+vancouver_joined <- tongfen_join_regions(vancouver,joins,meta)
+

The TongfenUID of the joined regions lists all the +dissemination areas they are made up of.

+
+vancouver_joined %>%
+  st_drop_geometry() %>%
+  filter(TongfenID %in% joins$TongfenID_joined) %>%
+  select(TongfenID,TongfenUID,starts_with("Population"))
+#> # A tibble: 2 × 7
+#>   TongfenID TongfenUID           Population_CA21 Population_CA01 Population_CA06
+#>   <chr>     <chr>                          <int>           <dbl>           <dbl>
+#> 1 59150762  GeoUIDCA01:59150762…            1412            1148            1266
+#> 2 59150765  GeoUIDCA01:59150765…            4082            1895            2499
+#> # ℹ 2 more variables: Population_CA11 <dbl>, Population_CA16 <dbl>
+

Variables that are not additive, like averages, can only be +aggregated this way if the variable they are averaged over is part of +the data. The alternative that always works is to join the regions in +the correspondence the common geography was built from, and use the +joined correspondence to aggregate the original data. This also is the +way to use the joins for data other than the one that was used to detect +the anomalies, for example to get average rents on the corrected +geography.

+
+correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets,regions=regions,
+                                                       level="DA",quiet=TRUE) %>%
+  tongfen_join_correspondence(joins)
+
+rent_meta <- meta_for_ca_census_vectors(c(rent_2006="v_CA06_2050",rent_2016="v_CA16_4901"))
+
+rent_data <- c("CA06","CA16") %>%
+  lapply(\(ds) get_census(ds,regions=regions,level="DA",labels="short",quiet=TRUE,
+                          vectors=rent_meta %>% filter(geo_dataset==ds) %>% pull(variable),
+                          geo_format=if (ds=="CA16") "sf" else NA) %>%
+           rename(!!paste0("GeoUID",ds):="GeoUID")) %>%
+  setNames(c("CA06","CA16"))
+
+rents <- tongfen_aggregate(rent_data,correspondence,rent_meta,base_geo="CA16")
+
+rents %>%
+  st_drop_geometry() %>%
+  filter(TongfenID %in% joins$TongfenID_joined) %>%
+  select(TongfenID,rent_2006,rent_2016)
+#> # A tibble: 2 × 3
+#>   TongfenID rent_2006 rent_2016
+#>   <chr>         <dbl>     <dbl>
+#> 1 59150762       452.      674.
+#> 2 59150765       474.      913.
+
+
+
+ + + + +
+ + + + + + + diff --git a/docs/articles/tongfen_anomalies.md b/docs/articles/tongfen_anomalies.md new file mode 100644 index 0000000..4e1c12f --- /dev/null +++ b/docs/articles/tongfen_anomalies.md @@ -0,0 +1,311 @@ +# Geocoding anomalies in TongFen timelines + +TongFen makes data on different geographies comparable by aggregating it +up to a common geography. The result is only as good as the geocoding +that assigned the underlying data to geographic regions in the first +place. Geocoding changes over time, and the same dwelling units, and the +people living in them, can get assigned to different neighbouring +regions in different years. In a timeline on a common geography this +shows up as a surprising drop in one region that is offset by a jump in +a neighbouring region. + +The fix is in the spirit of TongFen, joining the affected regions gives +a slightly coarser geography on which the data is consistent over time. +The functions in this vignette look for such patterns and join the +regions on demand. The method is explained in more detail in a [blog +post](https://doodles.mountainmath.ca/posts/2024-07-26-geocoding-errors-in-aggregate-data/), +this vignette follows the example from that post. + +``` r + +library(dplyr) +library(tidyr) +library(ggplot2) +library(cancensus) +library(sf) +library(tongfen) +# cancensus::set_api_key("") +``` + +## Population timelines for Toronto + +As an example we take the population from the 1971 through 2011 censuses +that Statistics Canada tabulated on 2016 dissemination areas, together +with the 2016 population. All data comes on the same geography, so there +is no need to TongFen, but the data for the earlier years is geocoded +from the road network and block face of the time, which does not always +match up with 2016 dissemination areas. + +``` r + +years <- c(1971,seq(1981,2011,5)) +vectors <- c(setNames(paste0("v_CA",years,"x16_1"),years),"2016"="v_CA16_1") +timeline <- names(vectors) + +toronto <- get_census("CA16CT",regions=list(CSD="3520005"),vectors=vectors, + level="DA",geo_format="sf",quiet=TRUE) %>% + select(GeoUID,all_of(timeline)) %>% + mutate(across(all_of(timeline),\(x) coalesce(x,0))) +``` + +Dissemination areas without population in a given year come back as +missing values. Changes from or to a missing value are never considered +surprising, so we set them to zero to mark these as areas where nobody +got counted. + +The area around Crescent Town shows what the problem looks like. + +``` r + +crescent_town <- c("35204370","35204765") + +plot_timelines <- function(data) { + data %>% + st_drop_geometry() %>% + pivot_longer(all_of(timeline),names_to="Year",values_to="Population") %>% + ggplot(aes(x=Year,y=Population,colour=GeoUID,group=GeoUID)) + + geom_line() + + geom_point() + + scale_y_continuous(labels=scales::comma,limits=c(0,NA)) +} + +toronto %>% + filter(GeoUID %in% crescent_town) %>% + plot_timelines() + + labs(title="Population in two neighbouring dissemination areas") +``` + +![](tongfen_anomalies_files/figure-html/unnamed-chunk-3-1.png) + +The population jumps back and forth between the two areas, while the sum +of the two is fairly steady from 1981 on. People did not move back and +forth, their homes got geocoded to a different dissemination area in +different years. + +## Detecting anomalies + +`tongfen_detect_anomalies` lists the regions with surprising drops. Only +decreases are surprising, and a decrease needs to be large in both +relative and absolute terms. For each of these candidate regions it +finds the neighbouring region that takes away most of the surprise when +both are joined, and checks if that reduction is large enough to justify +joining them. Our regions are identified by their `GeoUID` instead of +the `TongfenID` the function looks for by default. + +``` r + +anomalies <- tongfen_detect_anomalies(toronto,timeline,id="GeoUID",total_surprise_cutoff=0.4) + +anomalies %>% filter(GeoUID %in% crescent_town) +#> # A tibble: 2 × 7 +#> GeoUID surprise_count surprise_total period neighbour surprise_total_joined +#> +#> 1 35204370 3 0.933 1991-1… 35204765 0.616 +#> 2 35204765 2 1.03 1981-1… 35204370 0.142 +#> # ℹ 1 more variable: join +``` + +Both areas are candidates, and each one is the neighbour that best +explains the surprising drops of the other. Not all candidates find a +neighbour to pair up with. Population does drop for real, for example +when a site gets cleared for redevelopment, and such regions are left +alone. + +``` r + +anomalies %>% count(join) +#> # A tibble: 2 × 2 +#> join n +#> +#> 1 FALSE 254 +#> 2 TRUE 288 +``` + +## Joining regions + +`tongfen_anomaly_joins` joins the regions that qualify and looks again, +joined regions can have surprising drops that are complemented by +another neighbour. This repeats until there are no more regions left to +join. The result lists the regions that got joined together with the +identifier of the joined region they are now part of and the round in +which they first got joined. + +``` r + +joins <- tongfen_anomaly_joins(toronto,timeline,id="GeoUID",total_surprise_cutoff=0.4) + +joins %>% filter(GeoUID %in% crescent_town) +#> # A tibble: 2 × 3 +#> GeoUID GeoUID_joined round +#> +#> 1 35204370 35204370 1 +#> 2 35204765 35204370 1 +``` + +`tongfen_join_regions` applies the joins to the data, aggregating the +variables and geometries of the regions that get joined and leaving all +others as they are. + +``` r + +toronto_joined <- tongfen_join_regions(toronto,joins,id="GeoUID") + +c(original=nrow(toronto),joined=nrow(toronto_joined)) +#> original joined +#> 3702 3430 +``` + +``` r + +toronto_joined %>% + filter(GeoUID %in% crescent_town) %>% + plot_timelines() + + labs(title="Population in the joined region") +``` + +![](tongfen_anomalies_files/figure-html/unnamed-chunk-8-1.png) + +The map shows the regions that got joined around Crescent Town. + +``` r + +bbox <- toronto %>% filter(GeoUID %in% crescent_town) %>% st_buffer(1500) %>% st_bbox() + +ggplot(toronto_joined %>% mutate(joined=GeoUID %in% joins$GeoUID_joined)) + + geom_sf(aes(fill=joined),linewidth=0.1) + + geom_sf(data=toronto,fill=NA,linewidth=0.1,linetype="dotted") + + scale_fill_manual(values=c("TRUE"="steelblue","FALSE"="whitesmoke"),guide="none") + + coord_sf(datum=NA,xlim=bbox[c("xmin","xmax")],ylim=bbox[c("ymin","ymax")]) + + labs(title="Joined regions around Crescent Town", + caption="Joined regions in blue, original dissemination areas dotted") +``` + +![](tongfen_anomalies_files/figure-html/unnamed-chunk-9-1.png) + +## Tuning + +Joining regions trades geographic detail for consistency over time, and +how to best make that trade depends on the data and the application. The +parameters are documented in `tongfen_detect_anomalies`, the most +important ones are + +- `rel_scale` and `abs_scale`, the relative and absolute decrease at + which a change is half way to being fully surprising. The defaults of + a 25% drop and a drop of 200 are tuned to population counts in regions + of the size of dissemination areas. +- `total_surprise_cutoff`, how surprising the timeline of a region needs + to be to become a candidate. The default of 0.75 is conservative, + above we used 0.4 to also pick up less pronounced cases. +- `cutoff_fact`, `surprise_reduction_const` and `sum_fact` determine how + much of the surprise a neighbour needs to take away for the regions to + get joined. + +``` r + +c(0.4,0.6,0.75) %>% + lapply(\(cutoff) tibble(total_surprise_cutoff=cutoff, + regions_joined=tongfen_anomaly_joins(toronto,timeline,id="GeoUID", + total_surprise_cutoff=cutoff) %>% + nrow())) %>% + bind_rows() +#> # A tibble: 3 × 2 +#> total_surprise_cutoff regions_joined +#> +#> 1 0.4 477 +#> 2 0.6 273 +#> 3 0.75 152 +``` + +Neighbours are by default determined by intersecting the geometries of +the regions. This can miss neighbours if the geometries have been +simplified, in that case the `neighbours` argument takes a table with +the identifiers of neighbouring regions or a neighbours list from the +**spdep** package. + +## Anomalies in TongFen data + +The functions work the same way on data on a common geography built by +TongFen, where the regions are identified by their `TongfenID`. As an +example we look at the dissemination area level population in the City +of Vancouver for the 2001 through 2021 censuses. + +``` r + +regions <- list(CSD="5915022") +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") + +vancouver <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21",quiet=TRUE) + +joins <- tongfen_anomaly_joins(vancouver,paste0("Population_",datasets),total_surprise_cutoff=0.4) +joins +#> # A tibble: 4 × 3 +#> TongfenID TongfenID_joined round +#> +#> 1 59150762 59150762 1 +#> 2 59153181 59150762 1 +#> 3 59150765 59150765 1 +#> 4 59150770 59150765 1 +``` + +Passing the metadata to `tongfen_join_regions` makes sure the variables +get aggregated the right way, numeric variables that are not part of the +metadata are assumed to be additive. + +``` r + +vancouver_joined <- tongfen_join_regions(vancouver,joins,meta) +``` + +The `TongfenUID` of the joined regions lists all the dissemination areas +they are made up of. + +``` r + +vancouver_joined %>% + st_drop_geometry() %>% + filter(TongfenID %in% joins$TongfenID_joined) %>% + select(TongfenID,TongfenUID,starts_with("Population")) +#> # A tibble: 2 × 7 +#> TongfenID TongfenUID Population_CA21 Population_CA01 Population_CA06 +#> +#> 1 59150762 GeoUIDCA01:59150762… 1412 1148 1266 +#> 2 59150765 GeoUIDCA01:59150765… 4082 1895 2499 +#> # ℹ 2 more variables: Population_CA11 , Population_CA16 +``` + +Variables that are not additive, like averages, can only be aggregated +this way if the variable they are averaged over is part of the data. The +alternative that always works is to join the regions in the +correspondence the common geography was built from, and use the joined +correspondence to aggregate the original data. This also is the way to +use the joins for data other than the one that was used to detect the +anomalies, for example to get average rents on the corrected geography. + +``` r + +correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets,regions=regions, + level="DA",quiet=TRUE) %>% + tongfen_join_correspondence(joins) + +rent_meta <- meta_for_ca_census_vectors(c(rent_2006="v_CA06_2050",rent_2016="v_CA16_4901")) + +rent_data <- c("CA06","CA16") %>% + lapply(\(ds) get_census(ds,regions=regions,level="DA",labels="short",quiet=TRUE, + vectors=rent_meta %>% filter(geo_dataset==ds) %>% pull(variable), + geo_format=if (ds=="CA16") "sf" else NA) %>% + rename(!!paste0("GeoUID",ds):="GeoUID")) %>% + setNames(c("CA06","CA16")) + +rents <- tongfen_aggregate(rent_data,correspondence,rent_meta,base_geo="CA16") + +rents %>% + st_drop_geometry() %>% + filter(TongfenID %in% joins$TongfenID_joined) %>% + select(TongfenID,rent_2006,rent_2016) +#> # A tibble: 2 × 3 +#> TongfenID rent_2006 rent_2016 +#> +#> 1 59150762 452. 674. +#> 2 59150765 474. 913. +``` diff --git a/docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-3-1.png b/docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-3-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f4533c8834308f5d5fdb341e9e6be492d302b2a2 GIT binary patch literal 85698 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
(7_ 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 Spag@w3N0=#mVN)xi~j`^Jt4aQ literal 0 HcmV?d00001 diff --git a/docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-8-1.png b/docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-8-1.png new file mode 100644 index 0000000000000000000000000000000000000000..12a93f2bceb9b59b98308cbeb5ef8462b208dc2f GIT binary patch literal 47963 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 literal 0 HcmV?d00001 diff --git a/docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-9-1.png b/docs/articles/tongfen_anomalies_files/figure-html/unnamed-chunk-9-1.png new file mode 100644 index 0000000000000000000000000000000000000000..eea41289280e46207b5f42f4d1ecd2a1e2cabacb GIT binary patch literal 242301 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≫t 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¥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 - 0.3.8 + 0.3.9 + +

+ + + +
+
+
+ +
+

[Experimental]

+

Looks for regions with surprising drops in the timeline of a count variable that are complemented by +a neighbouring region, as explained in `tongfen_detect_anomalies`, and joins them. This gets +repeated on the joined regions until there are no more regions left that qualify to get joined. +In each round a region only gets joined with one other region, the most surprising regions go first.

+

Joining regions trades geographic detail for timelines that are consistent over time. The parameters +control how aggressively regions get joined and are best calibrated on the data at hand, erring on the side of +joining too few regions risks keeping geocoding problems, erring on the other side risks removing real +changes and needlessly coarsens the geography.

+

The result can be used to join the regions via `tongfen_join_regions`, or to update a correspondence +via `tongfen_join_correspondence`.

+
+ +
+

Usage

+
tongfen_anomaly_joins(
+  data,
+  variables,
+  id = "TongfenID",
+  neighbours = NULL,
+  rel_scale = 0.25,
+  abs_scale = 200,
+  p = 4,
+  surprise_cutoff = 0.15,
+  total_surprise_cutoff = 0.75,
+  cutoff_fact = 0.6,
+  surprise_reduction_const = 0.15,
+  sum_fact = 0.7
+)
+
+ +
+

Arguments

+ + +
data
+

data on a common geography, with one row per region, for example as returned by +`tongfen_aggregate` or `get_tongfen_ca_census`. Needs to be of class sf unless `neighbours` is specified.

+ + +
variables
+

names of the columns holding the timeline of a count variable like population or +dwellings, in temporal order. Changes from or to a missing value are not surprising and don't make up for +surprising changes in neighbouring regions, and joined regions are missing a value if one of the regions they +are made up of is. Replace missing values by zero beforehand if they stand for regions where nothing got counted

+ + +
id
+

name of the column that uniquely identifies the regions, default is "TongfenID"

+ + +
neighbours
+

optional, neighbouring regions as a table with the identifiers of pairs of neighbouring +regions in the first two columns, or as a neighbours list like the ones returned by `spdep::poly2nb`. +By default all regions with intersecting geometries are neighbours, which can miss neighbours +if the geometries have been simplified and don't share their boundaries any more.

+ + +
rel_scale
+

relative decrease that is half way to full surprise, default is `0.25` for a 25% drop

+ + +
abs_scale
+

absolute decrease that is half way to full surprise, default is `200`

+ + +
p
+

exponent of the norm used to combine the surprises across the timeline into the total surprise. +Large values focus on the most surprising change, 1 adds up the surprises of all changes, default is `4`

+ + +
surprise_cutoff
+

changes with larger surprise count as surprising, default is `0.15`. Only +regions with at least one surprising change are candidates

+ + +
total_surprise_cutoff
+

only regions with larger total surprise are candidates, default is `0.75`

+ + +
cutoff_fact
+

join regions if the total surprise after joining is lower than this share of the +total surprise of the candidate region, default is `0.6`

+ + +
surprise_reduction_const
+

join regions if joining lowers the total surprise by more than this, +default is `0.15`

+ + +
sum_fact
+

join regions if the total surprise after joining is lower than `cutoff_fact * sum_fact` +times the sum of the total surprises of both regions, default is `0.7`

+ +
+
+

Value

+

A tibble with one row for each region that gets joined with other regions, with the identifier +of the region, the identifier of the joined region it becomes part of in the column named like the +identifier with suffix `_joined`, by default `TongfenID_joined`, and the `round` in which the region +first got joined to another region. The identifier of a joined region is the smallest +identifier of the regions it is made up of.

+
+ +
+

Examples

+
# Correct 2001 through 2021 dissemination area level population timelines in the
+# City of Vancouver for likely geocoding problems
+if (FALSE) { # \dontrun{
+datasets <- c("CA01","CA06","CA11","CA16","CA21")
+meta <- meta_for_additive_variables(datasets,"Population")
+data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21")
+
+joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets))
+corrected_data <- tongfen_join_regions(data,joins,meta)
+} # }
+
+
+
+ + +
+ + + + + + + diff --git a/docs/reference/tongfen_anomaly_joins.md b/docs/reference/tongfen_anomaly_joins.md new file mode 100644 index 0000000..5e39412 --- /dev/null +++ b/docs/reference/tongfen_anomaly_joins.md @@ -0,0 +1,139 @@ +# Determine regions to join to correct for likely geocoding anomalies + +**\[experimental\]** + +Looks for regions with surprising drops in the timeline of a count +variable that are complemented by a neighbouring region, as explained in +\`tongfen_detect_anomalies\`, and joins them. This gets repeated on the +joined regions until there are no more regions left that qualify to get +joined. In each round a region only gets joined with one other region, +the most surprising regions go first. + +Joining regions trades geographic detail for timelines that are +consistent over time. The parameters control how aggressively regions +get joined and are best calibrated on the data at hand, erring on the +side of joining too few regions risks keeping geocoding problems, erring +on the other side risks removing real changes and needlessly coarsens +the geography. + +The result can be used to join the regions via \`tongfen_join_regions\`, +or to update a correspondence via \`tongfen_join_correspondence\`. + +## Usage + +``` r +tongfen_anomaly_joins( + data, + variables, + id = "TongfenID", + neighbours = NULL, + rel_scale = 0.25, + abs_scale = 200, + p = 4, + surprise_cutoff = 0.15, + total_surprise_cutoff = 0.75, + cutoff_fact = 0.6, + surprise_reduction_const = 0.15, + sum_fact = 0.7 +) +``` + +## Arguments + +- data: + + data on a common geography, with one row per region, for example as + returned by \`tongfen_aggregate\` or \`get_tongfen_ca_census\`. Needs + to be of class sf unless \`neighbours\` is specified. + +- variables: + + names of the columns holding the timeline of a count variable like + population or dwellings, in temporal order. Changes from or to a + missing value are not surprising and don't make up for surprising + changes in neighbouring regions, and joined regions are missing a + value if one of the regions they are made up of is. Replace missing + values by zero beforehand if they stand for regions where nothing got + counted + +- id: + + name of the column that uniquely identifies the regions, default is + "TongfenID" + +- neighbours: + + optional, neighbouring regions as a table with the identifiers of + pairs of neighbouring regions in the first two columns, or as a + neighbours list like the ones returned by \`spdep::poly2nb\`. By + default all regions with intersecting geometries are neighbours, which + can miss neighbours if the geometries have been simplified and don't + share their boundaries any more. + +- rel_scale: + + relative decrease that is half way to full surprise, default is + \`0.25\` for a 25% drop + +- abs_scale: + + absolute decrease that is half way to full surprise, default is + \`200\` + +- p: + + exponent of the norm used to combine the surprises across the timeline + into the total surprise. Large values focus on the most surprising + change, 1 adds up the surprises of all changes, default is \`4\` + +- surprise_cutoff: + + changes with larger surprise count as surprising, default is \`0.15\`. + Only regions with at least one surprising change are candidates + +- total_surprise_cutoff: + + only regions with larger total surprise are candidates, default is + \`0.75\` + +- cutoff_fact: + + join regions if the total surprise after joining is lower than this + share of the total surprise of the candidate region, default is + \`0.6\` + +- surprise_reduction_const: + + join regions if joining lowers the total surprise by more than this, + default is \`0.15\` + +- sum_fact: + + join regions if the total surprise after joining is lower than + \`cutoff_fact \* sum_fact\` times the sum of the total surprises of + both regions, default is \`0.7\` + +## Value + +A tibble with one row for each region that gets joined with other +regions, with the identifier of the region, the identifier of the joined +region it becomes part of in the column named like the identifier with +suffix \`\_joined\`, by default \`TongfenID_joined\`, and the \`round\` +in which the region first got joined to another region. The identifier +of a joined region is the smallest identifier of the regions it is made +up of. + +## Examples + +``` r +# Correct 2001 through 2021 dissemination area level population timelines in the +# City of Vancouver for likely geocoding problems +if (FALSE) { # \dontrun{ +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") + +joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +corrected_data <- tongfen_join_regions(data,joins,meta) +} # } +``` diff --git a/docs/reference/tongfen_ca_census_ct.html b/docs/reference/tongfen_ca_census_ct.html index cc9ebcc..090d3f2 100644 --- a/docs/reference/tongfen_ca_census_ct.html +++ b/docs/reference/tongfen_ca_census_ct.html @@ -9,7 +9,7 @@ tongfen - 0.3.8 + 0.3.9 + + + + + +
+
+
+ +
+

[Experimental]

+

TongFen is only as good as the geocoding that assigned the underlying data to geographic regions +in the first place. Geocoding varies over time, and the same dwelling units, and the people living +in them, can get assigned to different neighbouring regions in different years. In a timeline +on a common geography this shows up as a surprising drop in one region that is offset by +a corresponding jump in a neighbouring region.

+

This function lists the candidate regions with surprising drops in the given count variable, +together with the neighbouring region that takes away most of the surprise when both are joined. +Use it to check for possible problems and to calibrate the parameters before joining regions with +`tongfen_anomaly_joins`. Not all surprising drops are due to geocoding problems, a drop that is +not complemented by a neighbouring region is likely real.

+

The surprise of a change between two consecutive years ranges from 0 to 1. Only decreases are surprising, +the surprise is the product of the surprise of the relative and of the absolute decrease, so that it takes +a decrease that is large in both relative and absolute terms to be surprising. The total surprise of a +region is the `p`-norm of the surprises across all changes in the timeline.

+

A candidate region and its neighbour are flagged for joining if joining reduces the total surprise of the +candidate region to below `cutoff_fact` times its total surprise, or reduces it by more than +`surprise_reduction_const`, or reduces it to below `cutoff_fact * sum_fact` times the sum of +the total surprises of both regions. To keep this comparable the surprise after joining is computed +from the change of the joined regions relative to the counts of the candidate region alone.

+
+ +
+

Usage

+
tongfen_detect_anomalies(
+  data,
+  variables,
+  id = "TongfenID",
+  neighbours = NULL,
+  rel_scale = 0.25,
+  abs_scale = 200,
+  p = 4,
+  surprise_cutoff = 0.15,
+  total_surprise_cutoff = 0.75,
+  cutoff_fact = 0.6,
+  surprise_reduction_const = 0.15,
+  sum_fact = 0.7
+)
+
+ +
+

Arguments

+ + +
data
+

data on a common geography, with one row per region, for example as returned by +`tongfen_aggregate` or `get_tongfen_ca_census`. Needs to be of class sf unless `neighbours` is specified.

+ + +
variables
+

names of the columns holding the timeline of a count variable like population or +dwellings, in temporal order. Changes from or to a missing value are not surprising and don't make up for +surprising changes in neighbouring regions, and joined regions are missing a value if one of the regions they +are made up of is. Replace missing values by zero beforehand if they stand for regions where nothing got counted

+ + +
id
+

name of the column that uniquely identifies the regions, default is "TongfenID"

+ + +
neighbours
+

optional, neighbouring regions as a table with the identifiers of pairs of neighbouring +regions in the first two columns, or as a neighbours list like the ones returned by `spdep::poly2nb`. +By default all regions with intersecting geometries are neighbours, which can miss neighbours +if the geometries have been simplified and don't share their boundaries any more.

+ + +
rel_scale
+

relative decrease that is half way to full surprise, default is `0.25` for a 25% drop

+ + +
abs_scale
+

absolute decrease that is half way to full surprise, default is `200`

+ + +
p
+

exponent of the norm used to combine the surprises across the timeline into the total surprise. +Large values focus on the most surprising change, 1 adds up the surprises of all changes, default is `4`

+ + +
surprise_cutoff
+

changes with larger surprise count as surprising, default is `0.15`. Only +regions with at least one surprising change are candidates

+ + +
total_surprise_cutoff
+

only regions with larger total surprise are candidates, default is `0.75`

+ + +
cutoff_fact
+

join regions if the total surprise after joining is lower than this share of the +total surprise of the candidate region, default is `0.6`

+ + +
surprise_reduction_const
+

join regions if joining lowers the total surprise by more than this, +default is `0.15`

+ + +
sum_fact
+

join regions if the total surprise after joining is lower than `cutoff_fact * sum_fact` +times the sum of the total surprises of both regions, default is `0.7`

+ +
+
+

Value

+

A tibble with one row for each candidate region, most surprising first, with the identifier of the +region, the number of surprising changes `surprise_count`, the total surprise `surprise_total`, +the `period` with the most surprising change, the identifier of the `neighbour` that takes away most of +the surprise, the total surprise `surprise_total_joined` after joining both and `join` indicating if both +regions qualify to get joined.

+
+ +
+

Examples

+
# Check 2001 through 2021 dissemination area level population timelines in the
+# City of Vancouver for possible geocoding problems
+if (FALSE) { # \dontrun{
+datasets <- c("CA01","CA06","CA11","CA16","CA21")
+meta <- meta_for_additive_variables(datasets,"Population")
+data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21")
+
+anomalies <- tongfen_detect_anomalies(data,paste0("Population_",datasets))
+} # }
+
+
+
+ + +
+ + + + + + + diff --git a/docs/reference/tongfen_detect_anomalies.md b/docs/reference/tongfen_detect_anomalies.md new file mode 100644 index 0000000..6ba66b3 --- /dev/null +++ b/docs/reference/tongfen_detect_anomalies.md @@ -0,0 +1,152 @@ +# Detect likely geocoding anomalies in timelines on a common geography + +**\[experimental\]** + +TongFen is only as good as the geocoding that assigned the underlying +data to geographic regions in the first place. Geocoding varies over +time, and the same dwelling units, and the people living in them, can +get assigned to different neighbouring regions in different years. In a +timeline on a common geography this shows up as a surprising drop in one +region that is offset by a corresponding jump in a neighbouring region. + +This function lists the candidate regions with surprising drops in the +given count variable, together with the neighbouring region that takes +away most of the surprise when both are joined. Use it to check for +possible problems and to calibrate the parameters before joining regions +with \`tongfen_anomaly_joins\`. Not all surprising drops are due to +geocoding problems, a drop that is not complemented by a neighbouring +region is likely real. + +The surprise of a change between two consecutive years ranges from 0 +to 1. Only decreases are surprising, the surprise is the product of the +surprise of the relative and of the absolute decrease, so that it takes +a decrease that is large in both relative and absolute terms to be +surprising. The total surprise of a region is the \`p\`-norm of the +surprises across all changes in the timeline. + +A candidate region and its neighbour are flagged for joining if joining +reduces the total surprise of the candidate region to below +\`cutoff_fact\` times its total surprise, or reduces it by more than +\`surprise_reduction_const\`, or reduces it to below \`cutoff_fact \* +sum_fact\` times the sum of the total surprises of both regions. To keep +this comparable the surprise after joining is computed from the change +of the joined regions relative to the counts of the candidate region +alone. + +## Usage + +``` r +tongfen_detect_anomalies( + data, + variables, + id = "TongfenID", + neighbours = NULL, + rel_scale = 0.25, + abs_scale = 200, + p = 4, + surprise_cutoff = 0.15, + total_surprise_cutoff = 0.75, + cutoff_fact = 0.6, + surprise_reduction_const = 0.15, + sum_fact = 0.7 +) +``` + +## Arguments + +- data: + + data on a common geography, with one row per region, for example as + returned by \`tongfen_aggregate\` or \`get_tongfen_ca_census\`. Needs + to be of class sf unless \`neighbours\` is specified. + +- variables: + + names of the columns holding the timeline of a count variable like + population or dwellings, in temporal order. Changes from or to a + missing value are not surprising and don't make up for surprising + changes in neighbouring regions, and joined regions are missing a + value if one of the regions they are made up of is. Replace missing + values by zero beforehand if they stand for regions where nothing got + counted + +- id: + + name of the column that uniquely identifies the regions, default is + "TongfenID" + +- neighbours: + + optional, neighbouring regions as a table with the identifiers of + pairs of neighbouring regions in the first two columns, or as a + neighbours list like the ones returned by \`spdep::poly2nb\`. By + default all regions with intersecting geometries are neighbours, which + can miss neighbours if the geometries have been simplified and don't + share their boundaries any more. + +- rel_scale: + + relative decrease that is half way to full surprise, default is + \`0.25\` for a 25% drop + +- abs_scale: + + absolute decrease that is half way to full surprise, default is + \`200\` + +- p: + + exponent of the norm used to combine the surprises across the timeline + into the total surprise. Large values focus on the most surprising + change, 1 adds up the surprises of all changes, default is \`4\` + +- surprise_cutoff: + + changes with larger surprise count as surprising, default is \`0.15\`. + Only regions with at least one surprising change are candidates + +- total_surprise_cutoff: + + only regions with larger total surprise are candidates, default is + \`0.75\` + +- cutoff_fact: + + join regions if the total surprise after joining is lower than this + share of the total surprise of the candidate region, default is + \`0.6\` + +- surprise_reduction_const: + + join regions if joining lowers the total surprise by more than this, + default is \`0.15\` + +- sum_fact: + + join regions if the total surprise after joining is lower than + \`cutoff_fact \* sum_fact\` times the sum of the total surprises of + both regions, default is \`0.7\` + +## Value + +A tibble with one row for each candidate region, most surprising first, +with the identifier of the region, the number of surprising changes +\`surprise_count\`, the total surprise \`surprise_total\`, the +\`period\` with the most surprising change, the identifier of the +\`neighbour\` that takes away most of the surprise, the total surprise +\`surprise_total_joined\` after joining both and \`join\` indicating if +both regions qualify to get joined. + +## Examples + +``` r +# Check 2001 through 2021 dissemination area level population timelines in the +# City of Vancouver for possible geocoding problems +if (FALSE) { # \dontrun{ +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") + +anomalies <- tongfen_detect_anomalies(data,paste0("Population_",datasets)) +} # } +``` diff --git a/docs/reference/tongfen_estimate.html b/docs/reference/tongfen_estimate.html index e9eeac4..603f34c 100644 --- a/docs/reference/tongfen_estimate.html +++ b/docs/reference/tongfen_estimate.html @@ -13,7 +13,7 @@ tongfen - 0.3.8 + 0.3.9 + + + + + +
+
+
+ +
+

[Experimental]

+

Updates a correspondence so that the given regions are joined, for example to correct for likely geocoding +anomalies as determined by `tongfen_anomaly_joins`. The updated correspondence can be used in `tongfen_aggregate` +to aggregate data on the coarser common geography, which works for all variables `tongfen_aggregate` can +deal with and for data that was not part of detecting the anomalies.

+
+ +
+

Usage

+
tongfen_join_correspondence(correspondence, joins)
+
+ +
+

Arguments

+ + +
correspondence
+

correspondence table with columns the unique geographic identifiers for each of the +geographies and the TongfenID and TongfenUID, as for example returned by `estimate_tongfen_correspondence` +or `get_tongfen_correspondence_ca_census`

+ + +
joins
+

table with the regions to join as returned by `tongfen_anomaly_joins`, with columns +`TongfenID` and `TongfenID_joined`

+ +
+
+

Value

+

The correspondence with updated TongfenID and TongfenUID for the regions that got joined. If the +correspondence has a TongfenMethod column "anomaly" gets added to the method of the regions that got joined.

+
+ +
+

Examples

+
# Correct for likely geocoding problems in dissemination area level population timelines
+# and use the updated correspondence to aggregate data on the corrected common geography
+if (FALSE) { # \dontrun{
+regions <- list(CSD="5915022")
+datasets <- c("CA01","CA06","CA11","CA16","CA21")
+meta <- meta_for_additive_variables(datasets,"Population")
+data <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21")
+joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets))
+
+correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets,
+                                                       regions=regions,level="DA") %>%
+  tongfen_join_correspondence(joins)
+} # }
+
+
+
+ + +
+ + + + + + + diff --git a/docs/reference/tongfen_join_correspondence.md b/docs/reference/tongfen_join_correspondence.md new file mode 100644 index 0000000..772d411 --- /dev/null +++ b/docs/reference/tongfen_join_correspondence.md @@ -0,0 +1,55 @@ +# Join regions in a correspondence + +**\[experimental\]** + +Updates a correspondence so that the given regions are joined, for +example to correct for likely geocoding anomalies as determined by +\`tongfen_anomaly_joins\`. The updated correspondence can be used in +\`tongfen_aggregate\` to aggregate data on the coarser common geography, +which works for all variables \`tongfen_aggregate\` can deal with and +for data that was not part of detecting the anomalies. + +## Usage + +``` r +tongfen_join_correspondence(correspondence, joins) +``` + +## Arguments + +- correspondence: + + correspondence table with columns the unique geographic identifiers + for each of the geographies and the TongfenID and TongfenUID, as for + example returned by \`estimate_tongfen_correspondence\` or + \`get_tongfen_correspondence_ca_census\` + +- joins: + + table with the regions to join as returned by + \`tongfen_anomaly_joins\`, with columns \`TongfenID\` and + \`TongfenID_joined\` + +## Value + +The correspondence with updated TongfenID and TongfenUID for the regions +that got joined. If the correspondence has a TongfenMethod column +"anomaly" gets added to the method of the regions that got joined. + +## Examples + +``` r +# Correct for likely geocoding problems in dissemination area level population timelines +# and use the updated correspondence to aggregate data on the corrected common geography +if (FALSE) { # \dontrun{ +regions <- list(CSD="5915022") +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=regions,meta=meta,level="DA",base_geo="CA21") +joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) + +correspondence <- get_tongfen_correspondence_ca_census(geo_datasets=datasets, + regions=regions,level="DA") %>% + tongfen_join_correspondence(joins) +} # } +``` diff --git a/docs/reference/tongfen_join_regions.html b/docs/reference/tongfen_join_regions.html new file mode 100644 index 0000000..4aff240 --- /dev/null +++ b/docs/reference/tongfen_join_regions.html @@ -0,0 +1,144 @@ + +Join regions in data on a common geography — tongfen_join_regions • tongfen + Skip to contents + + +
+
+
+ +
+

[Experimental]

+

Joins regions in data that has already been aggregated to a common geography, for example to correct +for likely geocoding anomalies as determined by `tongfen_anomaly_joins`. The data, and the geometries if +the data is of class sf, of the regions that get joined are aggregated, all other regions are left as they are.

+

Variables are aggregated according to the metadata, numeric variables that are not part of the metadata +are assumed to be additive. Variables that are not additive, like averages, can only be aggregated if their +parent variable is part of the data. If that is not the case use `tongfen_join_correspondence` to update the +correspondence the data was built from and aggregate the original data again with `tongfen_aggregate`.

+
+ +
+

Usage

+
tongfen_join_regions(data, joins, meta = NULL, id = "TongfenID", na.rm = TRUE)
+
+ +
+

Arguments

+ + +
data
+

data on a common geography, with one row per region, for example as returned by +`tongfen_aggregate` or `get_tongfen_ca_census`

+ + +
joins
+

table with the regions to join as returned by `tongfen_anomaly_joins`, with the identifier +of the region and the identifier of the joined region it becomes part of in the column named like the +identifier with suffix `_joined`

+ + +
meta
+

optional metadata containing aggregation rules as for example returned by `meta_for_ca_census_vectors`, +variables are matched by their label. Numeric variables that are not part of the metadata are treated as +additive, if `NULL` (the default) that is the case for all numeric variables

+ + +
id
+

name of the column that uniquely identifies the regions, default is "TongfenID"

+ + +
na.rm
+

logical, determines how NA values should be treated when aggregating variables, +default is `TRUE`

+ +
+
+

Value

+

The data with the regions joined. Joined regions take the place and the identifier of the +region with the smallest identifier among the regions they are made up of. Variables that are +not numeric and not part of the metadata are `NA` for joined regions.

+
+ +
+

Examples

+
# Correct 2001 through 2021 dissemination area level population timelines in the
+# City of Vancouver for likely geocoding problems
+if (FALSE) { # \dontrun{
+datasets <- c("CA01","CA06","CA11","CA16","CA21")
+meta <- meta_for_additive_variables(datasets,"Population")
+data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21")
+
+joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets))
+corrected_data <- tongfen_join_regions(data,joins,meta)
+} # }
+
+
+
+ + +
+ + + + + + + diff --git a/docs/reference/tongfen_join_regions.md b/docs/reference/tongfen_join_regions.md new file mode 100644 index 0000000..40d63d0 --- /dev/null +++ b/docs/reference/tongfen_join_regions.md @@ -0,0 +1,77 @@ +# Join regions in data on a common geography + +**\[experimental\]** + +Joins regions in data that has already been aggregated to a common +geography, for example to correct for likely geocoding anomalies as +determined by \`tongfen_anomaly_joins\`. The data, and the geometries if +the data is of class sf, of the regions that get joined are aggregated, +all other regions are left as they are. + +Variables are aggregated according to the metadata, numeric variables +that are not part of the metadata are assumed to be additive. Variables +that are not additive, like averages, can only be aggregated if their +parent variable is part of the data. If that is not the case use +\`tongfen_join_correspondence\` to update the correspondence the data +was built from and aggregate the original data again with +\`tongfen_aggregate\`. + +## Usage + +``` r +tongfen_join_regions(data, joins, meta = NULL, id = "TongfenID", na.rm = TRUE) +``` + +## Arguments + +- data: + + data on a common geography, with one row per region, for example as + returned by \`tongfen_aggregate\` or \`get_tongfen_ca_census\` + +- joins: + + table with the regions to join as returned by + \`tongfen_anomaly_joins\`, with the identifier of the region and the + identifier of the joined region it becomes part of in the column named + like the identifier with suffix \`\_joined\` + +- meta: + + optional metadata containing aggregation rules as for example returned + by \`meta_for_ca_census_vectors\`, variables are matched by their + label. Numeric variables that are not part of the metadata are treated + as additive, if \`NULL\` (the default) that is the case for all + numeric variables + +- id: + + name of the column that uniquely identifies the regions, default is + "TongfenID" + +- na.rm: + + logical, determines how NA values should be treated when aggregating + variables, default is \`TRUE\` + +## Value + +The data with the regions joined. Joined regions take the place and the +identifier of the region with the smallest identifier among the regions +they are made up of. Variables that are not numeric and not part of the +metadata are \`NA\` for joined regions. + +## Examples + +``` r +# Correct 2001 through 2021 dissemination area level population timelines in the +# City of Vancouver for likely geocoding problems +if (FALSE) { # \dontrun{ +datasets <- c("CA01","CA06","CA11","CA16","CA21") +meta <- meta_for_additive_variables(datasets,"Population") +data <- get_tongfen_ca_census(regions=list(CSD="5915022"),meta=meta,level="DA",base_geo="CA21") + +joins <- tongfen_anomaly_joins(data,paste0("Population_",datasets)) +corrected_data <- tongfen_join_regions(data,joins,meta) +} # } +``` diff --git a/docs/reference/tongfen_tag_largest_overlap.html b/docs/reference/tongfen_tag_largest_overlap.html index c580c9c..4d4ff1a 100644 --- a/docs/reference/tongfen_tag_largest_overlap.html +++ b/docs/reference/tongfen_tag_largest_overlap.html @@ -9,7 +9,7 @@ tongfen - 0.3.8 + 0.3.9