cld-reducer reduces compact letter displays (CLDs) while preserving the pairwise statistical relationships they encode. It comes as an R package (cldreducer, at the root of this repository), a Python package (cld-reducer, in python/), and a JavaScript and TypeScript package (cld-reducer, in js/). All three follow one specification and return the same display for the same input. When the essential decisions alone cover every required relationship, a proof gives the unique optimum. Otherwise, the HiGHS solver finds the optimum.
Its optional CLD-sigma method implements the assignment-minimum clique covering problem introduced by Ennis, Fayle, and Ennis (2012): finding a CLD that uses the fewest possible individual letter-to-group assignments. The 2012 paper solves this problem with a backtracking algorithm (FIND-AM); this repository uses a binary mixed-integer model with HiGHS and an exact essential cover certificate. It is the implementation behind the CLD letter-reduction work presented at Sensometrics 2026.
See submission status for registry availability and release instructions for the release process. The commands below install from the repository.
Compact Letter Displays are useful because two products that share at least one letter are not significantly different, while products with no shared letters are significantly different.
For large sensory studies, standard maximal-clique CLD algorithms often assign more letters than are needed to preserve those relationships. A sample labeled ABC, for example, may only need AC if the removed B does not change any pairwise significance relationship.
The default CLD-C method minimizes distinct letters. The CLD-sigma option minimizes group-letter assignments. Both methods check that the display reconstructs the input relationship matrix.
# install.packages("remotes")
remotes::install_github("aigorahub/cld-reducer") # R
pip install "git+https://github.com/aigorahub/cld-reducer.git#subdirectory=python" # Python 3.10 or later
The #subdirectory=python part is needed because the Python package lives in python/. The JavaScript package is built from js/; see js/README.md.
The examples below work with version 0.3.3, published on PyPI and npm on 2026-10-15. Both packages default to CLD-C. Use assignment_minimum for CLD-sigma. CRAN still has version 0.2.0 under review and does not yet list the package. See submission status for the release records.
Give reduce_letters() the complete pairwise results: one row for each pair of groups, with a logical column that tells whether the pair differs significantly. Missing pairs are rejected, so an accidental omission is not treated as a significant difference.
library(cldreducer)
result <- reduce_letters(simple_abc_pairs, simple_abc_means)
result
#> Reduced compact letter display (letter_minimum)
#> group letters assignments
#> 1 A A
#> 2 AB A B
#> 3 ABC A B C
#> 4 BC B C
#> 5 C C
#> Assignments: 9 -> 9 (0.0% fewer). Letters: 3 -> 3. Relationships preserved: TRUE.
result$letters
#> 1 2 3 4 5
#> "A" "AB" "ABC" "BC" "C"
The default returns three distinct letters. Use method = "assignment_minimum" to reduce group 3 from ABC to AC. The means argument is optional; the means order the groups and the letters. If you already have the matrix of non-significant pairs, use reduce_from_adjacency().
The package also contains the wheat yield example of Piepho (2004), as tabulated in Table VIII of Ennis, Fayle, and Ennis (2012): 20 treatments and 190 comparisons.
wheat <- reduce_letters(piepho2004_wheat, method = "assignment_minimum")
wheat$stats[c("assignments_before", "assignments_after", "num_letters_after")]
#> $assignments_before
#> [1] 56
#>
#> $assignments_after
#> [1] 44
#>
#> $num_letters_after
#> [1] 4
The 56 assignments of the maximal display reduce to 44 (21.4% fewer) with the same 4 letters, as in the paper.
import pandas as pd
from cld_reducer import reduce_letters
# One row for each pair. True means the groups differ significantly.
pairs = pd.DataFrame(
[
("1", "2", False),
("1", "3", False),
("1", "4", True),
("1", "5", True),
("2", "3", False),
("2", "4", False),
("2", "5", True),
("3", "4", False),
("3", "5", False),
("4", "5", False),
],
columns=["group1", "group2", "significant"],
)
means = pd.DataFrame(
{"group": ["1", "2", "3", "4", "5"], "mean": [3.73, 3.57, 3.46, 3.33, 3.30]}
)
result = reduce_letters(pairs, means)
print(result.letters)
# {'1': 'A', '2': 'AB', '3': 'ABC', '4': 'BC', '5': 'C'}
This example works after package installation and needs no external files. The package also has a command line tool, cld-reduce. See python/README.md.
import { reduceFromAdjacency } from "cld-reducer";
const result = await reduceFromAdjacency(
[[1, 1, 0], [1, 1, 1], [0, 1, 1]],
{ groups: ["low", "mid", "high"] },
);
console.log(result.letters);
// { low: 'A', mid: 'AB', high: 'B' }
See js/README.md for the pairwise input, the options, and loading the WebAssembly solver in a browser.
dashboard/ builds one HTML file that runs the JavaScript package in a browser. Open the file; it needs no server and no network connection, and the data does not leave the computer. The page reads pairwise significance decisions as CSV, by paste or by file. It runs no significance tests on the user's data; Generate simulates decisions with a t test. It shows the letters before reduction, and Run puts the reduced letters next to them with the time of each solve. It solves with CLD-C or CLD-sigma, or with both for a comparison. A second stage then minimizes the other quantity, and the page shows that display under the name of the method; the R package has the same two orders as letter_then_assignment and assignment_then_letter. A second view follows the design of the simulation study of the 2012 paper in the browser. The results download as CSV and JSON.
cd js && npm ci
cd ../dashboard && npm ci && npm run build
open dist/cld-reducer-dashboard.html
The page is hosted at https://cld.aigora.ai and can also be built as an offline HTML file. The js.yaml workflow keeps the built file as the cld-reducer-dashboard artifact of each run. See dashboard/README.md for the input format, the limits, and the tests.
The default method is letter_minimum (CLD-C). Use assignment_minimum for CLD-sigma.
Both methods use the same pipeline.
The solver time limit covers the essential scan, certificate checks, and all canonical solves. Graph preparation and BIP compilation are outside that budget. Fallback retains the time left after the scan and proof attempt.
Exact assignment minimization can become expensive for dense or highly structured graphs. All three packages offer a time limit and a cap on the number of maximal cliques (10,000 by default); the call stops with a clear error beyond the cap.
docs/algorithm.md defines the input rules, errors, canonical order, solver settings, and APIs. The shared suite in conformance/ has 2,894 reduction cases: 1,422 for CLD-sigma, 1,442 for CLD-C, and 30 weighted reduction cases. It also has 65 error cases and 18 label cases. Expected displays come from exact search in a standard-library Python script. R, Python, and JavaScript run all cases in CI. Python and JavaScript repeat with HiGHS presolve off.
| Path | Contents |
|---|---|
DESCRIPTION, R/, man/, tests/, data/
|
R package cldreducer
|
python/ |
Python package cld-reducer (import name cld_reducer) |
js/ |
npm package cld-reducer
|
dashboard/ |
Browser page for the JavaScript package (private, not in any archive) |
docs/algorithm.md |
Specification |
conformance/ |
Fixtures, the generator, and the R runner |
If you use this software, please cite the foundational paper that introduced the assignment-minimum clique covering problem:
Ennis, J. M., Fayle, C. M., & Ennis, D. M. (2012). Assignment-Minimum Clique Coverings. ACM Journal of Experimental Algorithmics, 17, Article 1.5. https://doi.org/10.1145/2133803.2275596
You may additionally reference the talks that present this implementation:
Ennis, J. M. (2019). Computational Advances in the Production of Compact Letter Displays. Conference on Statistical Practice (CSP 2019), American Statistical Association, New Orleans, LA, February 14-16.
Ennis, J., Graham, C., Castro, L., Lampert, R., Jordan, R., & Rios de Souza, V. (2026). Too many letters? Cutting through the sensory clutter with letter reduction algorithms. Sensometrics 2026, Valencia, Spain.
The wheat example is from Piepho, H.-P. (2004). An algorithm for a letter-based representation of all-pairwise comparisons. Journal of Computational and Graphical Statistics, 13(2), 456-466. https://doi.org/10.1198/1061860043515
See CITATION.cff for machine-readable citation metadata.
MIT. See LICENSE.md.
Both methods merge vertices with identical closed neighborhoods before solving. The sigma model weights each merged vertex by its original group count. The result restores the original groups, labels, means, and assignment counts. This is the vertex reduction from Lemma 2.5 of the 2012 paper.
CLD-sigma (assignment_minimum, alias assignment-minimum) minimizes
letter-to-group assignments. The default CLD-C (letter_minimum, alias letter-minimum)
minimizes distinct letters by selecting full maximal cliques. It does not minimize
assignments as a second objective. Equal optima use the lexicographically greatest
binary selection vector in canonical clique order. Public method metadata uses
the underscore spelling.
The R package also has two staged methods. letter_then_assignment finds the
fewest letters and then the fewest assignments among displays with that many
letters (CLD-C, then CLD-sigma). assignment_then_letter finds the fewest
assignments and then the fewest letters among those displays (CLD-sigma, then
CLD-C). Each keeps its first minimum exactly; calling one method after the other
would not. Python and JavaScript do not have them yet. See section 15 of
docs/algorithm.md.
For CLD-sigma, objective equals the assignments after reduction. For CLD-C it equals
the number of letters after reduction. Assignment counts and reduction percentage
remain assignment measures in both methods. For the simple five-group example,
sigma uses 8 assignments and 3 letters; C uses 9 assignments and 3 letters.
result <- reduce_letters(simple_abc_pairs, simple_abc_means, method = "letter_minimum")
result$stats$objective # 3 letters
result$stats$assignments_after # 9 assignments
Version 0.3.0 is the merged candidate for these changes. Version 0.3.1 adds the browser page. Version 0.3.2 adds a second stage, simulated examples, and the letters before reduction to that page. Neither changes a package algorithm or the public API. Version 0.3.4 adds the staged methods to the R package; it is not published. The current development version is 0.3.5. Version 0.3.3 is published on npm and PyPI. The page is at https://cld.aigora.ai. The frozen 0.2.0 CRAN submission remains unchanged. See development status for implementation, review, version, and timing records.