UNPKG

cefr-analyzer

Version:

分析英文文本中各CEFR级别(A1-C2)单词的数量

288 lines (287 loc) 10.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.cefrAnalyzer = exports.CEFRTextAnalyzer = void 0; const wink_nlp_1 = __importDefault(require("wink-nlp")); const wink_eng_lite_web_model_1 = __importDefault(require("wink-eng-lite-web-model")); const wink_lemmatizer_1 = __importDefault(require("wink-lemmatizer")); const vocabulary_1 = require("../vocabulary"); /* istanbul ignore next */ function fixLemma(word, pos) { switch (pos) { case 'NOUN': case 'PROPN': return wink_lemmatizer_1.default.noun(word); case 'VERB': return wink_lemmatizer_1.default.verb(word); case 'ADJ': case 'ADV': return wink_lemmatizer_1.default.adjective(word); // ADV、PROPN 和 PRON 通常保留原型(专有名词/代词),可选择直接返回 default: return word; } } /** * 基于wink-nlp的CEFR文本分析器 * 用于分析文本中各CEFR级别单词的分布 */ class CEFRTextAnalyzer { /** * 创建分析器实例 */ constructor() { // 初始化wink-nlp this.nlp = (0, wink_nlp_1.default)(wink_eng_lite_web_model_1.default); // 确保词汇表已初始化 vocabulary_1.vocabularyManager.initialize(); } /** * 分析文本中的CEFR级别单词分布 * @param text 要分析的文本 * @param options 分析选项 * @returns 分析结果 */ analyze(text, options) { const defaultOptions = { caseSensitive: false, includeUnknownWords: true, analyzeByPartOfSpeech: false, }; const mergedOptions = { ...defaultOptions, ...options }; // 使用wink-nlp处理文本 const doc = this.nlp.readDoc(text); // 提取所有单词(过滤掉标点符号和数字) const tokens = doc.tokens().filter((token) => { return token.out(this.nlp.its.type) === 'word'; }); // 处理每个单词,获取其CEFR级别 const processedWords = []; const uniqueWords = new Set(); const levelCounts = { a1: 0, a2: 0, b1: 0, b2: 0, c1: 0, c2: 0, }; const unknownWordsList = new Set(); // 初始化各级别单词列表 const wordsAtLevel = { a1: [], a2: [], b1: [], b2: [], c1: [], c2: [], }; tokens.each((token) => { let word = token.out(); word = mergedOptions.caseSensitive ? word : word.toLowerCase(); const lemma = token.out(this.nlp.its.lemma); // 获取单词的词性 const pos = token.out(this.nlp.its.pos); // console.log(`Word: ${word}, Lemma: ${lemma}, Pos: ${pos}`); if (word.trim() === '') return; // 跳过空单词 const normalizedWord = lemma.toLowerCase(); const uniqueKey = mergedOptions.analyzeByPartOfSpeech ? `${word}${pos})` : word; // 如果已处理过该单词,则跳过 if (uniqueWords.has(uniqueKey)) { return; } uniqueWords.add(uniqueKey); // 查询单词的CEFR级别 let cefrLevel; if (mergedOptions.analyzeByPartOfSpeech) { // 根据词性查询CEFR级别 // 注意:这里需要将wink-nlp的词性映射到我们的词性类型 const mappedPos = this.mapPartOfSpeech(pos); cefrLevel = mappedPos ? vocabulary_1.vocabularyManager.getCEFRLevel(normalizedWord, mappedPos) || vocabulary_1.vocabularyManager.getCEFRLevel(normalizedWord) // fallback : undefined; } else { // 不考虑词性,直接查询CEFR级别 cefrLevel = vocabulary_1.vocabularyManager.getCEFRLevel(normalizedWord); } // 记录处理结果 processedWords.push({ original: word, normalized: normalizedWord, cefrLevel, partOfSpeech: pos, }); if (!cefrLevel) { const lemma = fixLemma(word, pos); if (word !== lemma) { // 稍微补救一下,但是一些派生词是识别不了的 if (mergedOptions.analyzeByPartOfSpeech) { const mappedPos = this.mapPartOfSpeech(pos); cefrLevel = vocabulary_1.vocabularyManager.getCEFRLevel(lemma, mappedPos) || vocabulary_1.vocabularyManager.getCEFRLevel(lemma); // fallback } else { cefrLevel = vocabulary_1.vocabularyManager.getCEFRLevel(lemma); } } } // 更新统计数据 if (cefrLevel) { levelCounts[cefrLevel]++; // 将单词添加到对应级别的列表中 wordsAtLevel[cefrLevel].push({ word, lemma: normalizedWord, pos: pos, }); } else if (mergedOptions.includeUnknownWords) { unknownWordsList.add(uniqueKey); } }); // 计算总单词数和未知单词数 const totalWords = uniqueWords.size; const unknownWords = unknownWordsList.size; // 计算各级别单词占比 const levelPercentages = { a1: 0, a2: 0, b1: 0, b2: 0, c1: 0, c2: 0, }; Object.keys(levelCounts).forEach(level => { const cefrLevel = level; const count = totalWords - unknownWords; levelPercentages[cefrLevel] = count > 0 ? (levelCounts[cefrLevel] / count) * 100 : 0; }); // 返回分析结果 return { totalWords, levelCounts, levelPercentages, unknownWords, unknownWordsList: mergedOptions.includeUnknownWords ? [...unknownWordsList] : [], wordsAtLevel, }; } /** * 获取文本中指定CEFR级别的单词列表 * @param text 要分析的文本 * @param level CEFR级别 * @param options 分析选项 * @returns 指定级别的单词列表(包含词性) */ getWordsAtLevel(text, level, options) { // 通过analyze方法获取分析结果,确保includeUnknownWords选项为false,因为我们只关心特定级别的单词 const analysisResult = this.analyze(text, { ...options, includeUnknownWords: false, // 不需要未知单词列表 }); // 直接返回指定级别的单词列表 return analysisResult.wordsAtLevel[level]; } /** * 获取文本的CEFR级别分布统计 * @param text 要分析的文本 * @param options 分析选项 * @returns 各级别单词数量的统计 */ getLevelDistribution(text, options) { const result = this.analyze(text, options); return result.levelPercentages; } /** * 将wink-nlp的词性映射到我们的词性类型 * @param winkPos wink-nlp的词性标记 * @returns 映射后的词性,如果无法映射则返回undefined */ mapPartOfSpeech(winkPos) { // wink-nlp使用通用词性标签 (Universal POS tags) // 这里将其映射到我们的词性类型 const posMapping = { // 基本词性映射 NOUN: 'noun', PROPN: 'noun', VERB: 'verb', ADJ: 'adjective', ADV: 'adverb', DET: 'determiner', PRON: 'pronoun', ADP: 'preposition', CCONJ: 'conjunction', SCONJ: 'conjunction', INTJ: 'interjection', // 扩展词性映射 AUX: 'auxiliary verb', NUM: 'number', // 特殊动词类型 'AUX-MD': 'modal verb', 'AUX-BE': 'be-verb', 'AUX-DO': 'do-verb', 'AUX-HV': 'have-verb', // 其他特殊类型 PART: 'infinitive-to', // 其他通用标签没有直接映射到我们的词性类型 // PUNCT (标点), SYM (符号), X (其他), SPACE (空格) // Penn Treebank POS Tags 映射 // 名词类 NN: 'noun', NNS: 'noun', NNP: 'noun', NNPS: 'noun', // 动词类 VB: 'verb', VBD: 'verb', VBG: 'verb', VBN: 'verb', VBP: 'verb', VBZ: 'verb', // 形容词类 JJ: 'adjective', JJR: 'adjective', JJS: 'adjective', // 副词类 RB: 'adverb', RBR: 'adverb', RBS: 'adverb', WRB: 'adverb', // 代词类 PRP: 'pronoun', PRP$: 'pronoun', WP: 'pronoun', WP$: 'pronoun', // 限定词类 DT: 'determiner', PDT: 'determiner', WDT: 'determiner', // 介词类 IN: 'preposition', // 连词类 CC: 'conjunction', // 数词类 CD: 'number', // 特殊类 MD: 'modal verb', TO: 'infinitive-to', EX: 'pronoun', FW: 'noun', LS: 'number', POS: 'noun', RP: 'adverb', SYM: 'noun', UH: 'interjection', // 感叹词 }; return posMapping[winkPos]; } } exports.CEFRTextAnalyzer = CEFRTextAnalyzer; // 导出分析器实例 exports.cefrAnalyzer = new CEFRTextAnalyzer();