WIP: Analytics: Support for Hierarchical Clustering for Variables (R package) - #1641
Draft
hidekoji wants to merge 1 commit into
Draft
WIP: Analytics: Support for Hierarchical Clustering for Variables (R package)#1641hidekoji wants to merge 1 commit into
hidekoji wants to merge 1 commit into
Conversation
…m#38161) New R/hclust_variables.R implements Variable Clustering -- hierarchical clustering of the SELECTED VARIABLES (columns) of a data frame, based on correlation, distinct from Row Clustering (tam#38157, not yet implemented -- see the paired tam PR's design doc for the full reuse-plan discussion). Pipeline: selected columns -> variable x variable correlation matrix (reuses the EXISTING do_cor.cols()/do_cor_internal() type-aware Pearson/Spearman/Kendall/Polychoric/Mixed/Auto correlation engine, so column-type gating stays consistent with do_cor()/Factor Analysis rather than being re-decided here) -> distance = 1 - correlation -> fastcluster::hclust() (falls back to stats::hclust() via requireNamespace() when the fastcluster package -- newly added to DESCRIPTION Imports -- isn't installed yet; a real deployment needs a requiredRPackagesReduced.json bump + package rebuild, an ops step out of scope for this PR) -> cutree() -> a dependency-free right-angle dendrogram segment table built directly from hc$merge/hc$height/ hc$order (hclust_dendrogram_segments()), matching the branch-geometry shape described in tam#38157's requirements doc so a future shared Row/Variable dendrogram renderer can consume it without a redesign. Key decisions (see the R file's own roxygen comments and the tam design doc for the full reasoning): - Distance is 1 - correlation, not 1 - |correlation| -- a strong negative correlation is a real, distinct relationship from a strong positive one and should not be treated as "close" the same way. - Ward's linkage is deliberately NOT offered/accepted (linkage_method only allows complete/average/single, and the function stop()s if ward.D2/ward is passed) -- Ward's method assumes an underlying Euclidean distance, and 1 - correlation is not Euclidean in general. - Zero-variance / all-NA columns are detected (a constant column's own correlation self-diagonal is NA, dropped by do_cor.cols' existing na.rm=TRUE) and excluded from clustering with a clear message, rather than letting an NA distance reach hclust() uncaught. - Cluster IDs are renumbered by left-to-right dendrogram display order (not cutree()'s internal numbering), per tam#38157 requirements doc section 29. - tidy.hclust_variables_exploratory() follows the existing tidy_rowwise(model, type=...) S3 convention with 5 branches: summary/analysis_conditions, cluster_membership, correlation_matrix, dendrogram_segments, dendrogram_leaves. NAMESPACE was hand-edited (export + S3method entries) since roxygen2 could not be run in this environment (no R/Rscript interpreter was available -- see caveat below). Tests: tests/testthat/test_hclust_variables.R covers basic structure, 1 - correlation distance semantics, zero-variance-column exclusion, the fewer-than-2-usable-variables error, the ward.D2 rejection, n_clusters bounds, all 3 linkage methods, all 4 correlation methods, pairwise-complete missing-value handling, the n=2 edge case, max_nrow sampling, and the canonical stress-test column name from .claude/rules/workflow.md rule 7 (spaces/multibyte/symbols), plus a standalone unit test of hclust_dendrogram_segments()'s geometry. CAVEAT: no R/Rscript binary was available in this environment (checked via `which`/`command -v` and a filesystem search), so these tests were written and carefully traced by hand against documented R semantics (cor() on a constant vector, hclust()'s merge/height/order contract, mat_to_df()'s na.rm behavior read directly from R/util.R) but were NEVER EXECUTED. This needs a real `devtools::test(filter="hclust_variables")` run before merge -- flagged explicitly, not silently skipped, per .claude/rules/testing.md and tam's global/testing.md#r-codegen-fix-live-execution-required. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kei51e
approved these changes
Aug 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
R-side companion to
exploratory-io/tam#38161("Analytics: Support for Hierarchical Clusteringfor Variables") — adds
exp_hclust_variables(), implementing hierarchical clustering of theSELECTED VARIABLES (columns) of a data frame, based on correlation, distinct from Row Clustering
(
tam#38157, which clusters observations/rows and is not yet implemented — see the pairedtam PR's design doc,
docs/plans/design/38161_variable_clustering_design.md, for the fullreuse-plan discussion and why this ships as an independent function rather than reusing shared
Row Clustering infrastructure that doesn't exist yet).
New file:
R/hclust_variables.R.Pipeline: selected columns → variable × variable correlation matrix (reuses the existing
do_cor.cols()/do_cor_internal()type-aware Pearson/Spearman/Kendall/Polychoric/Mixed/Autocorrelation engine, so column-type gating stays consistent with
do_cor()/Factor Analysis ratherthan being re-decided here) → distance =
1 - correlation→fastcluster::hclust()(falls backto
stats::hclust()viarequireNamespace()when thefastclusterpackage — newly added toDESCRIPTIONImports:— isn't installed yet; a real deployment additionally needs arequiredRPackagesReduced.jsonbump + package rebuild in thetamrepo, an ops step out of scopefor this PR) →
cutree()→ a dependency-free right-angle dendrogram segment table built directlyfrom
hc$merge/hc$height/hc$order(hclust_dendrogram_segments()), matching the branchgeometry shape described in
tam#38157's requirements doc so a future shared Row/Variabledendrogram renderer can consume it without a redesign.
Key decisions
1 - correlation, not1 - |correlation|— a strong negative correlation is areal, distinct relationship from a strong positive one and should not be treated as "close" the
same way.
linkage_methodonly allowscomplete/average/single, and the function
stop()s ifward.D2/wardis passed — Ward'smethod assumes an underlying Euclidean distance, and
1 - correlationis not Euclidean ingeneral.
column's own correlation self-diagonal is
NA, dropped bydo_cor.cols()'s existingna.rm = TRUE), rather than letting anNAdistance reachhclust()uncaught.cutree()'sinternal numbering), per
tam#38157requirements doc section 29.tidy.hclust_variables_exploratory()follows the existingtidy_rowwise(model, type=...)S3convention with 5 branches:
summary/analysis_conditions,cluster_membership,correlation_matrix,dendrogram_segments,dendrogram_leaves.NAMESPACEwas hand-edited (oneexport()+ oneS3method()entry) sinceroxygen2could notbe run in this environment — see the Checklist caveats below.
Checklist
tests/testthat/test_hclust_variables.R: basicstructure,
1 - correlationdistance semantics, zero-variance-column exclusion, thefewer-than-2-usable-variables error, the
ward.D2rejection,n_clustersbounds, all 3linkage methods, all 4+ correlation methods, pairwise-complete missing-value handling, the
n = 2edge case,max_nrowsampling, the canonical stress-test column name (spaces,multibyte characters, symbols — per this org's R-feature convention), and a standalone unit
test of
hclust_dendrogram_segments()'s geometry.devtools::check()— not run, no R interpreter available in this sandbox (see below).devtools::test()— not run, same reason.test_hclust_variables.Rwas written andcarefully hand-traced against documented R semantics (
cor()on a constant vector,hclust()'smerge/height/ordercontract,mat_to_df()'sna.rmbehavior readdirectly from
R/util.R) but never executed.Reason for the unchecked items: this PR was authored in a sandboxed environment with no
Ror
Rscriptbinary onPATHor found anywhere on the filesystem (which/command -vboth cameback empty, plus a broad filesystem search). This is flagged explicitly rather than silently
skipped — please run
devtools::check()/devtools::test(filter="hclust_variables")on amachine with R + this package's dependencies before merging, and treat this PR as ready for
design/code review, not for merge, until that happens.
🤖 Generated with Claude Code
Generated by Claude Code