cefr-analyzer
Version:
分析英文文本中各CEFR级别(A1-C2)单词的数量
288 lines (287 loc) • 10.3 kB
JavaScript
;
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();