R Tools for Text Matrices, Embeddings, and Networks
Overview
This is a collection of functions optimized for working with various kinds of text matrices. Focusing on the text matrix as the primary object - represented either as a base R dense matrix or a 'Matrix' package sparse matrix - allows for a consistent and intuitive interface that stays close to the underlying mathematical foundation of computational text analysis. In particular, the package includes functions for working with word embeddings, text networks, and document-term matrices. Methods developed in Stoltz and Taylor (2019) doi:10.1007/s42001-019-00048-6, Taylor and Stoltz (2020) doi:10.1007/s42001-020-00075-8, Taylor and Stoltz (2020) doi:10.15195/v7.a23, and Stoltz and Taylor (2021) doi:10.1016/j.poetic.2021.101567.
Additions
- added
weight_ppmi()for Positive Pointwise Mutual Information weighting of term co-occurrence matrices; sparse-aware implementation operating ondgCMatrixslots directly, withsmoothparameter for count smoothing before marginal estimation - added
print.CMDist()/plot.CMDist()S3 methods (small-multiples plot sorted by score, with sensitivity-interval error bars when present)
Changes
CMDist()now returns a data frame with classc("CMDist", "data.frame")instead of plain"data.frame", so thatprint.CMDist()/plot.CMDist()dispatch on its output. This is backward compatible — the result is still a real data frame, and all normal data frame operations ([,cbind(), dplyr pipelines, etc.) work unchanged — but code that checksidentical(class(x), "data.frame")rather thaninherits(x, "data.frame")will now see the extra classClusterR(used only byget_regions()) andRSpectra(used only bydoc_centrality(method = "eigen")) moved from Imports to Suggests withrequireNamespace()checks, since neither is needed for the package's default/core code paths;text2vecandigraphremain hard Imports, since they're required byCMDist()'s andCoCA()'s default behavior respectively — an earlier attempt to make these Suggests too broke under R-hub'snosuggestscheck (missing example/test skip guards) and has been reverted- added
data-raw/to.gitignore - added
*.swp/*.swo/*.swnto.gitignore(replacing an overly narrow.Rprofile.swp-only entry) and removed a stale, tracked vim swap file - added CI jobs for
spelling::spell_check_package()and a coverage threshold (80%; current coverage ~92%) so the coverage job actually gates instead of only reporting a badge - added a
lintCI job with a deliberately curated linter set (.lintr:object_usage_linter,seq_linter,vector_logic_linter, and a few others) rather than lintr's full default set, which flags ~2,000 mostly stylistic issues (indentation, line length) on this codebase; addedLanguage: en-USandspelling/lintrtoSuggests - simplified a handful of real lint findings:
if (type == "compound" | type == "centroid")→||intest_anchors.relco()(safer for scalar conditions),seq_len(length(x))→seq_along(x),any(is.na(x))→anyNA(x), and two redundant== TRUE/!= TRUEcomparisons on logical vectors — no behavior change - batched
.prep_cmd_INPUT()'s vocab-growing step into a singlecbind()instead of one per missing concept word
Show all changes in 0.4.0Hide the rest of the changes in 0.4.0
- warnings across the package now use
cli::cli_warn(), matching existingcli::cli_abort()/cli::cli_inform()usage - all
stop()calls across the package now usecli::cli_abort()for consistent, better-formatted error messages (error text is unchanged where tests depend on specific wording) doc_centrality()anddtm_stats()now warn on degenerate input (documents with no ties; empty DTMs) instead of silently returningNaN- consolidated the four
.terms_stop_*internal helpers (dtm_stopper()'s term/document frequency and proportion rules) into one shared.terms_stop_filter(), removing ~100 lines of duplicated Inf-replacement/rounding/threshold logic; no behavior change dtm_builder()'s friendly error messages (empty last row; missing doc ID column) are now raised as a single error with the originalMatrix/dplyrerror preserved as the cause, instead of a separatemessage()immediately followed by the raw errorCoCA()now warns (previously just informed) when it overrides an explicitfilter_sig = TRUEbecause too few directions were supplied- added
RhpcBLASctlas an Import and pinned BLAS/OpenMP to a single thread around everytext2vec::RWMDcall (CMDist(),doc_similarity(method = "wmd"));text2vec's compiled Word Mover's Distance code can deadlock under a multi-threaded BLAS (e.g. OpenBLAS) due to nested OpenMP thread-pool contention, and this can't be worked around with an environment variable set at runtime since the thread pool is already initialized by the time any R code runs
Corrections
- fixed empty
dtm_statstest by adding missing assertions - fixed
dfmcbind warning: early-return now only for puredgCMatrix, not subclasses likedfm - suppress plot windows in tests with
pdf(NULL) - fixed RNG state leaking into the caller's session in
rancor_builder()/rancors_builder()whenseed = NULL - fixed
.Random.seednot found errors in a fresh R session inperm_tester(),test_anchors()(relco method), andplot.CoCA() - fixed
doc_centrality(method = "span")incorrectly requiring igraph - fixed
perm_tester()crashing withobject 'x' not foundwhen the model has exactly 2 coefficients (missingdrop = FALSEin column subset) - fixed
find_transformation(method = "align")crashing withnon-conformable argumentswhenwv/refhave different row counts (now validates upfront) - fixed
dtm_builder()producing a DTM with the wrong number of rows when the last document had zero tokens (Matrix::sparseMatrix()inferred nrow frommax(i)instead of actual document count; now passes explicit dims) - registered
relcoprint methods (tbl_sum.relco,tbl_format_footer.relco) as S3 methods; previously silently unused - fixed
plot.CoCA()crashing with an unhelpful error on invalid/out-of-rangemodule - fixed
dtm_builder(vocab =, chunk =)creating a spurious extra chunk row at exact chunk-size boundaries, and silently dropping (instead of bucketing) out-of-vocabulary tokens - fixed
.dtm_error_handler()no longer recognizing currentMatrixpackage error wording - fixed wrong argument name in
dtm_stopper(stop_termprop =)'s error message - fixed
CMDist()crashing on single-document DTMs - fixed
get_direction(method = "PCA")giving an unrelated error whenn_dirsexceeds available anchor pairs - fixed
get_direction()'s\examplescallingmethod = "PCA"with only one anchor pair, which failedR CMD checkafter the two-pair minimum above was added - added
.ontomics(a local tool directory, not part of the package) to.Rbuildignore; was triggering an R CMD check NOTE - fixed
find_transformation(method = "retrofit")giving an opaque error whenwv/refshare no vocabulary - fixed
perm_tester()giving an opaque error for an invalidstatisticargument - fixed
get_centroid()/get_direction()silently corrupting results whenmissing = "remove"dropped an out-of-vocabulary term: an in-placeanchors[] <-assignment recycled the shortened result back to the original length instead of shrinking, duplicating an earlier term inget_centroid()(double-weighting it) and crashing or corruptingget_direction()when the row count changed unevenly - fixed a typo in
find_transformation()'s documentation ("simplying" → "simply"; "et a." → "et al.") - fixed a flaky parallel-CMDist test that only failed under core-limited check environments (e.g.
_R_CHECK_LIMIT_CORES_, asR CMD checksets): the test requested more threads than its reduction logic could ever let survive the final row-count check - fixed
CoCA()silently computing nonsense (NaNs produced) instead of disablingfilter_sigwhen given exactly 2 directions: the guard comparedncol()of the CMD score data frame (which includes adoc_idcolumn) directly against 3, off by one relative to the actual direction count - fixed
CoCA()crashing with an opaquedim(X) must have a positive lengtherror when given exactly 1 direction; it now errors upfront with a clear message, since document correlations are undefined with fewer than 2 directions - fixed
CMDist(cw =, missing = "remove")crashing with an opaqueinvalid character indexingerror when a multi-wordcwphrase (e.g."critical thinking") had only some of its words missing from the embeddings:.check_term_in_embeddings()compared whole phrases against the list of missing individual words, so a partially-missing phrase was never actually dropped fromcwdespite the removal message saying otherwise; it now drops the whole phrase if any of its words is missing - fixed
CMDist(cv = <0-row matrix>)crashing with an opaque'names' must be a character vectorerror; it now errors upfront with a clear message - fixed
weight_ppmi(smooth > 0)triggering aMatrixpackage deprecation warning (as(<dgeMatrix>, "dgCMatrix")is deprecated); now coerces via"CsparseMatrix", matching the pattern used elsewhere in the package - fixed
CMDist(parallel = TRUE, sens_interval = TRUE)crashing withtask 1 failed - "invalid first argument"wheneverthreadsmade a worker's chunk exactly 1 document: a missingdrop = FALSEcollapsed that 1-row chunk to a bare vector before resampling - fixed
get_centroids(groups =)erroring with an opaquemissing value where TRUE/FALSE neededwhenevergroupscontainedNA; rows with a missing group label are now excluded from every centroid, with a message, matching how empty factor levels are already handled - fixed
find_projection()/find_rejection()silently returning a matrix ofNaNfor an all-zerovec; both now error with a clear message - fixed
test_anchors(method = "pairdir")crashing with an opaqueattempt to set 'rownames' on an object with no dimensionserror with exactly 1 anchor pair; it now errors upfront with a clear message, since pairwise cosine similarity is undefined with fewer than 2 pairs - fixed
find_transformation(method = "retrofit")crashing with an opaqueinherits(wv, "matrix") ... is not TRUEerror when the shared vocabulary betweenwvandrefwas smaller thanwv's embedding dimension; it now errors upfront with a clear message, since retrofitting needs at least as many shared terms as dimensions to recover - fixed
get_direction(method = "PCA")silently returning a meaningless unit vector with only 1 anchor pair (a single centered row degenerates inprcomp()); it now errors upfront with a clear message, since PCA needs at least 2 anchor pairs - fixed
get_regions(k_regions > nrow(wv))crashing with an opaqueinvalid 'data'error, several steps removed from the real cause (ClusterR::KMeans_armadoesn't raise an R condition on this input — it prints a message and returns a character string in place of a matrix); it now errors upfront with a clear message dtm_stopper(stop_termrank =)now warns when the threshold is at or above the DTM's term count, since this silently removed the entire vocabulary with no indicationdtm_builder()now errors with a clear message whentextordoc_idcontainsNA(previously theNAsilently became a real column/row name), and whenvocabis empty or contains duplicate terms (previously produced literal duplicate DTM columns, with later duplicates silently all-zero, or a silent 0-column DTM)doc_centrality(two_mode = FALSE)now errors with a clear message on a non-square input instead of silently computing meaningless row sums across mismatched columns (only affectedmethod = "degree";"between"/"eigen"/"span"already failed loudly via their underlying dependency)
From NEWS.md in the release
Install
Health
- OK13 OK2026-08-04
- NOTE12 OK, 1 NOTE2026-08-01
- OK13 OK2026-06-09
- ERROR12 OK, 1 ERROR2026-06-08
- OK14 OK2026-03-10
Documentation
- Help pages
- 37 (2 internal)includes a package overview page
- Examples that run
- 47%14 run in a plain check (1 of them under a condition) · 16 only when donttest examples are enabled (R CMD check --as-cran)
- Exported functions with examples
- 100%
- Documented parameters
- 100%
- Return-value docs
- 100%
- Help pages citing references
- 24% with a bibliography file
Downloads
Repository
- PRs merged (all time)
- 80%
- Median days to close, 50 most recently updated closed issues
- GitHub only
- Median days to close, 50 most recently updated closed PRs
- GitHub only
Repository gitlab.com/culturalcartography/text2map is linked, but repository-practices checks have not run for it yet. Checks are GitHub-only for now.
Dependencies
Installing text2map adds 36 packages (22 with compiled code, 39 MB of source tarballs). Heaviest: stringi 11 MB, data.table 5.7 MB, igraph 4.8 MB.
Code & Tests
Datasets
People & History
Pick two releases to compare their code metrics.
- 0.4.0Latest2026-09-15 · current release · diff ↗ ·
removed 1 export
Matrixadded 2 exports
get_centroids, weight_ppmi - RR 4.6.0 released · 2026-04-24
- 0.2.32026-02-11 · diff ↗
- RR 4.5.0 released · 2025-04-11
- RR 4.4.0 released · 2024-04-24
- 0.2.02024-04-11 · diff ↗
- 0.1.9
- unarchivedReturned to CRAN2024-01-15
- 0.1.8
- archivedRemoved from CRAN2024-01-11issues were not corrected in time
- 0.1.7
- RR 4.3.0 released · 2023-04-21
- 0.1.6
- 0.1.5
- RR 4.2.0 released · 2022-04-22
- 0.1.4
Package metadata
- First published
- 2021-08-24
- Total releases
- 12 / 5 yrs
- License
- MIT + file LICENSE OSI
- Minimum R
- ≥ 4.1.0
- Bundled data
- 400 KB / 5 files
- Download size
- 586 KB
Cite
Cite this package
Run in R for the authors' preferred citation:
citation("text2map")This package has no citation file, so citation() returns this entry built from DESCRIPTION.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-21, which the citation names so these numbers can be found later. More on citing and the projects behind them.