Compare commits
4 Commits
edeadd547a
...
minor-css-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6b07ccb56 | ||
|
|
8e79e78006 | ||
| 19d4a82c90 | |||
|
|
6f09b6ecdc |
14
README.md
14
README.md
@@ -30,14 +30,18 @@ Average silhouette width is a widely-used measure of clustering quality. Higher
|
||||
|
||||
2. How well-separated each cluster is from its nearest neighboring cluster.
|
||||
|
||||
Although the coefficient is mathematically bounded by [−1, 1], cosine distance between high-dimensional text embeddings is compressed: unrelated notes sit close to orthogonal, so both the within-cluster and nearest-cluster distances land near 0.8. Because silhouette divides the gap between them by the larger of the two, the practical range on embedding data is roughly [−0.05, 0.10] rather than the full interval.
|
||||
|
||||
The bands below are therefore calibrated against that observed range. On the seed board, the five ideal thematic clusters score 0.09; swapping a few notes between clusters drops it to 0.06; a scrambled assignment falls below zero.
|
||||
|
||||
The score appears above the results with a plain-language band:
|
||||
|
||||
- **0.70 and above** — Strong
|
||||
- **0.40 to 0.69** — Moderate
|
||||
- **0.10 to 0.39** — Weak
|
||||
- **Below 0.10** — Poor
|
||||
- **0.07 and above** — Strong
|
||||
- **0.04 to 0.06** — Moderate
|
||||
- **0.01 to 0.03** — Weak
|
||||
- **Below 0.01** — Poor
|
||||
|
||||
Silhouette values are archetypically bounded below 1.0 for real-world data, so the number is best read as a relative measure. See Hugo Sträng, Tai Dinh. An upper bound on the silhouette evaluation metric for clustering. Pattern Recognition, Volume 178, 2026, 113402, ISSN 0031-3203.
|
||||
A score near 0.00 means the grouping is no better than chance. Bands are specific to `voyage-3` cosine distance and would need recalibration behind a different embedding model. See Hugo Sträng, Tai Dinh. An upper bound on the silhouette evaluation metric for clustering. Pattern Recognition, Volume 178, 2026, 113402, ISSN 0031-3203.
|
||||
|
||||
## Organizing clusters, exporting to workflow software
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@ import Sticky from './sticky';
|
||||
import Button from './button';
|
||||
import '../styles/stickies.css';
|
||||
|
||||
// In practice, silhouette on cosine distance between text embeddings occupies roughly
|
||||
// [-0.05, 0.10], not strict theoretical [-1, 1]: near-orthogonal vectors put both the within- and
|
||||
// nearest-cluster distances close to 0.8, and the coefficient divides their gap
|
||||
// by the larger. These bands are calibrated to that range for voyage-3. see README, Reading the cohesion score
|
||||
const scoreLabel = (score: number): string => {
|
||||
if (score >= 0.7) return 'Strong';
|
||||
if (score >= 0.4) return 'Moderate';
|
||||
if (score >= 0.1) return 'Weak';
|
||||
if (score >= 0.07) return 'Strong';
|
||||
if (score >= 0.04) return 'Moderate';
|
||||
if (score >= 0.01) return 'Weak';
|
||||
return 'Poor';
|
||||
};
|
||||
|
||||
@@ -83,7 +87,7 @@ const Stickies = () => {
|
||||
dragIndex.current = null;
|
||||
setDragOverIndex(null);
|
||||
};
|
||||
|
||||
console.log(score?.toFixed(2))
|
||||
return (
|
||||
<div className="stickies-container">
|
||||
<Button onClick={handleCluster} isLoading={isPending} label="Group Stickies By Topic" />
|
||||
@@ -91,7 +95,7 @@ const Stickies = () => {
|
||||
<div className="clusters-container">
|
||||
{score != null && (
|
||||
<div className="cohesion-score">
|
||||
Cluster cohesion: <strong>{score.toFixed(2)}</strong> — {scoreLabel(score)}
|
||||
Cluster cohesion: <strong>{scoreLabel(score)}</strong>
|
||||
</div>
|
||||
)}
|
||||
{rankedClusters.map((group, index) => (
|
||||
|
||||
@@ -40,10 +40,12 @@
|
||||
}
|
||||
|
||||
.cluster-header {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.cluster-rank {
|
||||
@@ -69,13 +71,18 @@
|
||||
}
|
||||
|
||||
.cluster-label {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
max-width: 50%;
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
font-weight: 600;
|
||||
flex: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cluster-drag-handle {
|
||||
margin-left: auto;
|
||||
font-size: 1.4em;
|
||||
color: #6dd6f4;
|
||||
opacity: 0.4;
|
||||
|
||||
@@ -14,7 +14,7 @@ const MOCK_CLUSTER_RESPONSE = {
|
||||
{ label: 'Auth Issues', noteIds: ['note_001'] },
|
||||
{ label: 'Export Issues', noteIds: ['note_002'] },
|
||||
],
|
||||
score: 0.74,
|
||||
score: 0.09,
|
||||
};
|
||||
|
||||
let fetchMock: ReturnType<typeof vi.fn>;
|
||||
|
||||
Reference in New Issue
Block a user