@willwade/aac-processors 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -267,6 +267,21 @@ export function spellingValidity(words, dictionary) {
267
267
  }
268
268
  return checked === 0 ? null : correct / checked;
269
269
  }
270
+ export function lexicalRichness(words) {
271
+ const n = words.length;
272
+ const freq = new Map();
273
+ for (const w of words)
274
+ freq.set(w, (freq.get(w) ?? 0) + 1);
275
+ const v = freq.size;
276
+ let v1 = 0;
277
+ for (const c of freq.values())
278
+ if (c === 1)
279
+ v1++;
280
+ const brunetsW = n > 0 && v > 0 ? n * Math.pow(v, -0.165) : null;
281
+ // Honoré's R is undefined when every word is a hapax (V1 = V) or N <= 1.
282
+ const honoresR = n > 1 && v > 0 && v1 < v ? (100 * Math.log(n)) / (1 - v1 / v) : null;
283
+ return { brunetsW, honoresR, hapax: v1, types: v, tokens: n };
284
+ }
270
285
  function distribution(values) {
271
286
  return {
272
287
  median: median(values),
@@ -381,6 +396,9 @@ export function analyzeTimeline(utterances, options = {}) {
381
396
  classifyInflection: resources.classifyInflection,
382
397
  });
383
398
  const spell = suppressed ? null : spellingValidity(stream, dictionary);
399
+ const rich = suppressed
400
+ ? { brunetsW: null, honoresR: null, hapax: 0, types: 0, tokens: 0 }
401
+ : lexicalRichness(stream);
384
402
  timeline.push({
385
403
  month: key,
386
404
  utterances: activity.utterances,
@@ -392,6 +410,7 @@ export function analyzeTimeline(utterances, options = {}) {
392
410
  syntacticDiversity: syn,
393
411
  morphologicalDiversity: mor,
394
412
  spellingValidity: spell,
413
+ lexicalRichness: rich,
395
414
  suppressed,
396
415
  suppressReason,
397
416
  });
@@ -118,6 +118,19 @@ export declare function morphologicalDiversity(words: WordStream, options?: Dive
118
118
  * Returns null (unavailable) if no dictionary is provided.
119
119
  */
120
120
  export declare function spellingValidity(words: WordStream, dictionary?: Set<string>): number | null;
121
+ export interface LexicalRichness {
122
+ /** Brunet's index W = N · V^(-0.165). Range ~10–30; LOWER = richer. */
123
+ brunetsW: number | null;
124
+ /** Honoré's statistic R = 100·ln N / (1 − V1/V). HIGHER = richer. */
125
+ honoresR: number | null;
126
+ /** Hapax legomena (words used exactly once) — count only. */
127
+ hapax: number;
128
+ /** Number of distinct words (V). */
129
+ types: number;
130
+ /** Number of tokens (N). */
131
+ tokens: number;
132
+ }
133
+ export declare function lexicalRichness(words: WordStream): LexicalRichness;
121
134
  export interface DistributionStats {
122
135
  median: number | null;
123
136
  mean: number | null;
@@ -151,6 +164,8 @@ export interface MonthBin {
151
164
  morphologicalDiversity: DiversityResult;
152
165
  /** Phonological — only when a dictionary is supplied. */
153
166
  spellingValidity: number | null;
167
+ /** Lexical richness indices (Brunet's W, Honoré's R) — always available. */
168
+ lexicalRichness: LexicalRichness;
154
169
  /** True when the month has too little data to trust the diversity figures. */
155
170
  suppressed: boolean;
156
171
  suppressReason: string | null;
@@ -42,6 +42,7 @@ exports.lexicalDiversity = lexicalDiversity;
42
42
  exports.syntacticDiversity = syntacticDiversity;
43
43
  exports.morphologicalDiversity = morphologicalDiversity;
44
44
  exports.spellingValidity = spellingValidity;
45
+ exports.lexicalRichness = lexicalRichness;
45
46
  exports.summarizeActivity = summarizeActivity;
46
47
  exports.analyzeTimeline = analyzeTimeline;
47
48
  /* ------------------------------------------------------------------ *
@@ -277,6 +278,21 @@ function spellingValidity(words, dictionary) {
277
278
  }
278
279
  return checked === 0 ? null : correct / checked;
279
280
  }
281
+ function lexicalRichness(words) {
282
+ const n = words.length;
283
+ const freq = new Map();
284
+ for (const w of words)
285
+ freq.set(w, (freq.get(w) ?? 0) + 1);
286
+ const v = freq.size;
287
+ let v1 = 0;
288
+ for (const c of freq.values())
289
+ if (c === 1)
290
+ v1++;
291
+ const brunetsW = n > 0 && v > 0 ? n * Math.pow(v, -0.165) : null;
292
+ // Honoré's R is undefined when every word is a hapax (V1 = V) or N <= 1.
293
+ const honoresR = n > 1 && v > 0 && v1 < v ? (100 * Math.log(n)) / (1 - v1 / v) : null;
294
+ return { brunetsW, honoresR, hapax: v1, types: v, tokens: n };
295
+ }
280
296
  function distribution(values) {
281
297
  return {
282
298
  median: median(values),
@@ -391,6 +407,9 @@ function analyzeTimeline(utterances, options = {}) {
391
407
  classifyInflection: resources.classifyInflection,
392
408
  });
393
409
  const spell = suppressed ? null : spellingValidity(stream, dictionary);
410
+ const rich = suppressed
411
+ ? { brunetsW: null, honoresR: null, hapax: 0, types: 0, tokens: 0 }
412
+ : lexicalRichness(stream);
394
413
  timeline.push({
395
414
  month: key,
396
415
  utterances: activity.utterances,
@@ -402,6 +421,7 @@ function analyzeTimeline(utterances, options = {}) {
402
421
  syntacticDiversity: syn,
403
422
  morphologicalDiversity: mor,
404
423
  spellingValidity: spell,
424
+ lexicalRichness: rich,
405
425
  suppressed,
406
426
  suppressReason,
407
427
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willwade/aac-processors",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
5
5
  "main": "dist/index.js",
6
6
  "browser": "dist/browser/index.browser.js",