Access and Analysis of Brazilian CNEFE Address Data
Overview
Download, cache and read municipality-level address data from the Cadastro Nacional de Enderecos para Fins Estatisticos (CNEFE) of the 2022 Brazilian Census, published by the Instituto Brasileiro de Geografia e Estatistica (IBGE) https://ftp.ibge.gov.br/Cadastro_Nacional_de_Enderecos_para_Fins_Estatisticos/. Beyond data access, provides spatial aggregation of addresses, computation of land-use mix indices, and dasymetric interpolation of census tract variables using CNEFE dwelling points as ancillary data. Results can be produced on 'H3' hexagonal grids or user-supplied polygons, and heavy operations leverage a 'DuckDB' backend with extensions for fast, in-process execution.
Breaking changes
- cnefetools now requires **R (>= 4.4.0)**, raised from 4.1.0. duckspatial (>= 1.1.0) calls the null-coalescing operator
%||%without importing it, so it resolves only against the base R version added in 4.4.0. On older R the failure surfaced from geobr as a misleading "a file must have been corrupted during download" message. Declaring the requirement turns a confusing runtime error into a clear message at install time (#78). - cnefetools now requires **geobr (>= 2.0.0)**. The data server behind geobr 1.x no longer responds:
read_municipality()on geobr 1.9.1 returnsNULLfor every year tested (2020, 2022 and 2024), while geobr 2.0.1 serves them normally. Since the failure is a silentNULLrather than an error, leaving the dependency unpinned meant users on geobr 1.x hit an obscure downstream error instead of a clear one (#87). This supersedes the note under #74 below, which stated that any geobr version works.
Show all changes in 0.3.0Hide the rest of the changes in 0.3.0
- The H3 grid is now built from the municipality boundary of the **CNEFE data year** instead of a hardcoded 2024 boundary, affecting
cnefe_counts(),compute_lumi()andtracts_to_h3()inpolygon_type = "hex"mode. IBGE geocoded the CNEFE records against the territorial base of their own year, and the census tracts of that year nest into the matching municipal mesh, so the grid now shares the reference frame of the data aggregated onto it. In principle the set of border hexagons can differ from previous releases for municipalities whose limits changed between the two bases, though at resolution 9 the grids come out identical for Lauro de Freitas-BA (550 cells) and Fortaleza-CE (2651 cells), the municipalities used in the package articles (#81). polygon_typeis deprecated incnefe_counts()andcompute_lumi(). The aggregation mode is now inferred frompolygon: leave itNULLfor an H3 grid, or pass ansfobject for user polygons. Existing code that passespolygon_typekeeps working and emits a deprecation warning. Passingpolygon_type = "user"with nopolygonremains an error. The previous behaviour, where supplying apolygonprinted three alert lines about switching mode automatically, is gone: the inference is now silent (#90).
New features
- New
cnefetools.duckdb_configoption, a named list of DuckDB settings applied to every connection the package opens. Unset, DuckDB sizes itself against the whole machine, taking one thread per logical core and 80% of installed RAM, and there was no way to hold it back on a shared node, a laptop running other work, or a CI runner: ``r options(cnefetools.duckdb_config = list(threads = 4, memory_limit = "4GB"))`Names are passed to DuckDB'sSETverbatim, so any setting DuckDB accepts works. Exceedingmemory_limit` makes DuckDB spill to disk rather than fail, so a low value costs time, not correctness (#80). - The pure-R backends of
cnefe_counts()andcompute_lumi()now push theirtransmute()andfilter()calls down to the Arrow table and collect last, instead of callingas.data.frame()first. The old order materialised all 34 columns as an R data frame before keeping three of them. Measured on Fortaleza (1.19M rows), peak memory falls from 108.4 MB to 68.3 MB, a 37% reduction, at the cost of about 0.2s (#88). - The download cache now stores a **gzipped CSV** instead of the published ZIP. DuckDB decompresses gzip natively, so the community
zipfsextension is no longer used, which removes a dependency on a community extension (these have been reported to add code-signing delays of 4 to 5 seconds per load on macOS). Measured indata-raw/bench_gz_vs_zip.R, on Fortaleza a DuckDB read is 2.29x faster from.csv.gzthan throughzipfs, at the same size on disk (27.6 MB either way). Raw CSV would be 4.86x faster but needs 6.7x the disk, so it is not a sensible cache format. The conversion is done once, on first download, and is streamed in chunks so peak memory does not scale with the file. Caches written by earlier versions are not read, so each municipality is downloaded again once, andclear_cache_muni()removes the old ZIPs (#93). - The download cache is now segregated by CNEFE edition, at
<cache>/<year>/. The ZIP names IBGE publishes carry no year, so2919207_LAURO_DE_FREITAS.zipfrom a future census would be indistinguishable from the 2022 one, and a cached 2022 file could be served silently to someone who asked for another edition.clear_cache_muni()andclear_cache_tracts()gain ayearargument, withNULLclearing every edition as before. Caches created by earlier versions are ignored and re-downloaded once (#81). - Census tract assets now come from release
sc-assets-v3, which is reproducible fromdata-raw/sc_assets_build.R. The data is unchanged: verified against the previous release across all 27 states, 468,097 tracts and 3,144,868 values, with every value matching exactly.sc-assets-v2remains published, so earlier versions of the package keep working (#80). - New
cnefe_export()writes a municipality to a persistent, optimised file at a location of your choosing, as Parquet (default), CSV or gzipped CSV. The package cache is transient by design and lives in a directory the package manages, which is the wrong place for an analysis that must still run in a year (#93). read_cnefe()gains afileargument that reads a CNEFE file already on disk, skipping the download entirely. It accepts.zipas published by IBGE,.csv,.csv.gzand.parquet, so data obtained by any means can be read without the IBGE server being reachable.code_muniandfileare mutually exclusive, and both paths share the same output code, so they return the same object for the same data (#93).- Downloads now recover from an upstream layout change. If the URL held in the internal index returns 404, the package scans the published IBGE directory listing to reconstruct the correct URL and retries once. The scan reads directory pages, never data files: recovering any municipality costs roughly 170 KB. It handles both a renamed file and a renamed UF directory, and it does not mutate the internal index, so a successful scan repairs one call rather than changing package state. If the scan finds nothing, the error says so and points at the issue tracker (#92).
- Downloads now run a pre-flight availability check that tells two failures apart. If the IBGE server cannot be reached, the message points at connectivity. If the server answers but the file is missing (HTTP 404), the message explains that the upstream directory layout has most likely changed, which is a package problem rather than a user problem, and points at the issue tracker. The check also aborts before the retry ladder, which previously spent 300, 600 and 1800 seconds on a URL that could never resolve (#91).
- All functions that download or read cached data gain a
cache_dirargument:read_cnefe(),cnefe_counts(),compute_lumi(),tracts_to_h3(),tracts_to_polygon(),clear_cache_muni()andclear_cache_tracts(). The cache location resolves from the argument first, then theCNEFETOOLS_CACHE_DIRenvironment variable, thentools::R_user_dir("cnefetools", "cache")as before. CNEFE ZIPs reach 901 MB uncompressed, and the location was previously fixed to the user's primary partition (#89).
Bug fixes
- A DuckDB extension that has no usable build for the running platform now fails with a message that says so, instead of a raw
dlopen()dump. The case that prompted this is live: since extension v1.5.5 the DuckDB community repository serves an arm64 binary under theosx_amd64path forh3, so the DuckDB backend cannot run on Intel macOS at all. The error now names the upstream packaging problem and, incnefe_counts()andcompute_lumi(), points atbackend = "r".tracts_to_h3()andtracts_to_polygon()get the same diagnosis without the suggestion, since they have no pure-R path by design (#99). clear_cache_muni()works again. Since the cache moved to gzipped CSV it had been matching.ziponly, so it silently deleted nothing: it reported "No cached CNEFE ZIP files found" with a full cache and returned an empty vector, leaving users no working way to clear downloads. It now matches both the current.csv.gzentries and the.ziparchives left by versions before 0.3.0, and leaves census tract Parquet files toclear_cache_tracts(), which was never affected. The existing tests missed this because their fixtures seeded the one format that still worked (#93).- DuckDB connections are no longer leaked when something fails between connecting and the first query.
on.exit()was registered only after the whole connect-and-load block had run, so a failed extension install, a SQL error or a user interrupt left the connection and its file handles behind. Cleanup is now registered throughwithr::defer()immediately afterdbConnect()returns, guarded byDBI::dbIsValid()so a connection that died earlier cannot raise a second error during cleanup (#84). - DuckDB error messages are no longer swallowed. The old pattern wrapped the queries in a nested
utils::capture.output()that captured both output and message streams, which also absorbed the text of genuine errors and left users with a failure they could not diagnose. The replacement muffles messages only, so errors propagate with their message intact (#57). cnefe_counts(),compute_lumi()andtracts_to_polygon()now give a clear error whenpolygonhas zero features. An emptysfobject previously survived validation and failed much later with an unrelated message, becausest_union()on zero rows yields an empty geometry,st_centroid()of that yields an empty point, andst_coordinates()returns no rows (#71).compute_lumi()no longer returnsNULLwhen no hexagon survives filtering. Both backends now return a zero-rowsfcarrying the documented columns, so downstream code can pipe the result without aNULLcheck (#85).read_cnefe(output = "sf")now reports, underverbose, how many rows were dropped for missing coordinates. They were previously discarded silently, which hid data quality problems (#85).compute_lumi()now excludesCOD_ESPECIE == 7(buildings under construction or renovation) in **all** code paths. The DuckDB backend for user-supplied polygons was missing that filter, so every index it returned (p_res,ei,hhi,bal,ice,hhi_adp,bgbi) was computed over a denominator that still contained type 7, contradicting both the documented behaviour and the other three code paths. Sincebackend = "duckdb"is the default, this affected most users ofpolygon_type = "user". On Lauro de Freitas-BA, where type 7 is 3.7% of addresses, the error reached 0.13 inp_resand 0.32 inbgbi. The two backends now agree exactly (#96).tracts_to_h3()andtracts_to_polygon()no longer write a text progress bar into knitted documents. The first download of a census tract asset passedverbosestraight topiggyback::pb_download(), so rendering an R Markdown or Quarto file, or running underRscript, printed several hundred lines of bar into the output (342 in each of two package articles). No chunk option suppresses it. The bar now appears only in interactive sessions, and the message announcing the download is kept everywhere (#108).tracts_to_h3()andtracts_to_polygon()no longer fail withGitHub API error (401): Bad credentialswhen an expired or invalid GitHub token is present in the environment. The census tract assets live in public GitHub releases and need no credential, butghsends whatever token it finds inGITHUB_PAT,GITHUB_TOKEN,GH_TOKENor the git credential store, and GitHub then rejects the request instead of serving it anonymously. The download now retries once without a token after an authentication failure. A valid token is still tried first, so authenticated rate limits are preserved, and if the anonymous retry also fails the error names the broken credential as a likely cause and points atgitcreds::gitcreds_delete()(#79).cnefe_counts(),compute_lumi(), andtracts_to_polygon()no longer fail in the DuckDB backend when the user-suppliedsfpolygon has a geometry column not named"geom"(e.g. the sf default"geometry"). The geometry column is now normalized before writing to DuckDB, restoring compatibility with duckspatial (>= 1.1.0) (#70).- cnefetools now works with geobr 2.0.0. geobr 2.0.0 reads boundaries lazily through duckspatial, which under a fixed RNG seed (e.g. R CMD check examples) could trigger a DuckDB temporary-table name collision (
Table dbplyr_<...> already exists). The RNG state is now isolated around the geobr call in the H3-grid path (#74). Note that geobr is nonetheless pinned to (>= 2.0.0), for the unrelated reason given above.
Documentation
- New article, "Cache and exported copies". It covers where the cache lives and how to move it (
cache_dirandCNEFETOOLS_CACHE_DIR), how to clear it, and how to keep a permanent copy withcnefe_export()and read it back withread_cnefe(file = ...). It also points out that onlyread_cnefe()reads an exported file (#110). - The Index of Concentration at Extremes is now credited to Massey (2001), the chapter that proposes it, instead of Booth & Crouter (2001), who edited the book. Affects
?compute_lumiand thecnefe_countsarticle. The description of?compute_luminow also cites the 2025 preprint alongside the 2026 article, since the adapted HHI appears only in the preprint (#110). ?read_cnefeno longer says that the cache holds the published ZIP and always lives intools::R_user_dir()(#110).?compute_lumino longer says that the BGBI is the only index using the citywide share P. The Balance Index uses it too, through r = P / (1 - P) (#112).?tracts_variables_refnow says that its variable codes are the ones used by censobr, which the census tract assets take their attributes from, and not the IBGE file codes (#112).
From NEWS.md in the release
Install
Health
- OK13 OK2026-09-29
- ERROR12 OK, 1 ERROR2026-09-04
- OK13 OK2026-08-05
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-06-09
Show 6 earlier changesHide 6 earlier changes
- ERROR12 OK, 1 ERROR2026-06-08
- OK13 OK2026-05-24
- ERROR12 OK, 1 ERROR2026-05-17
- OK13 OK2026-05-15
- ERROR12 OK, 1 ERROR2026-05-13
- OK14 OK2026-03-10
Documentation
Documentation signals are not tracked yet for this package.
Downloads
Repository
- PRs merged (all time)
- 96%
- Median days to close, 50 most recently updated closed issues
- 12
- Median days to close, 50 most recently updated closed PRs
- 0
9 development-tooling and community-health practices detected across 5 families in the upstream repository
Checks run against github.com/pedreirajr/cnefetools on 2026-10-11.
The R CMD check workflow runs on Linux, macOS and Windows
GitHub Pages site last deployed 2026-10-02 · The repository has a site deploy workflow
pkgdown site last built 2026-10-02
Show all practicesHide 1 practice
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-01-15Named in .Rbuildignore: .claude, CLAUDE.mdNamed in .gitignore: .claude, CLAUDE.mdPull request it wrote: opened by a project memberCommits: at least 4 crediting it in the message
- Claude Opus 54 commits · Sep 2026
Dependencies
Installing cnefetools adds 68 packages (45 with compiled code, 60 MB of source tarballs). Heaviest: stringi 11 MB, sf 9.3 MB, duckspatial 4.7 MB.
Code & Tests
Datasets
People & History
- 0.3.0Latest
- RR 4.6.0 released · 2026-04-24
- unarchivedReturned to CRAN2026-04-03
- 0.2.52026-04-03 · diff ↗
- archivedRemoved from CRAN2026-03-30issues were not corrected in time
- 0.2.3
- unarchivedReturned to CRAN2026-02-27
- 0.2.22026-02-27 · diff ↗
- archivedRemoved from CRAN2026-02-26issues were not corrected in time
- 0.2.12026-02-13 · diff ↗
- 0.2.02026-02-11
- RR 4.5.0 released · 2025-04-11
Package metadata
- First published
- 2026-02-11
- Total releases
- 6 / 1 yrs
- License
- MIT + file LICENSE OSI
- Download size
- 2.2 MB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("cnefetools")This is what citation() produces when a package has no citation file of its own. If it prints something else, use that.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-19, which the citation names so these numbers can be found later. More on citing and the projects behind them.