/**
* [贝叶斯分类器](http://en.wikipedia.org/wiki/Naive_Bayes_classifier)
*
* 这是一个朴素贝叶斯分类器,它接受
* 仅包含一级嵌套的对象。
*
* @class
* @example
* var bayes = new BayesianClassifier();
* bayes.train({
* species: 'Cat'
* }, 'animal');
* var result = bayes.score({
* species: 'Cat'
* })
* // 结果
* // {
* // animal: 1
* // }
*/
class BayesianClassifier {
/*:: totalCount: number */
/*:: data: Object */
constructor() {
// 当前模型中已分类的项目总数
this.totalCount = 0;
// 记录所有已分类的项目
this.data = {};
}
/**
* 使用新项目训练分类器,该项目具有
* JavaScript 字面量键值对的单维属性。
*
* @param {Object} item 具有一级属性的对象
* @param {string} category 该项目所属的类别
* @return {undefined} 将项目添加到分类器中
*/
train(item, category) {
// 如果数据对象中尚未存储该类别的数据,
// 则为其创建一个新的对象。
if (!this.data[category]) {
this.data[category] = {};
}
// 遍历项目的每个键。
for (const k in item) {
const v = item[k];
// 初始化嵌套对象 `data[category][k][item[k]]`,
// 其中的键初始值均为 0。
if (this.data[category][k] === undefined) {
this.data[category][k] = {};
}
if (this.data[category][k][v] === undefined) {
this.data[category][k][v] = 0;
}
// 递增该键值对的计数。
this.data[category][k][v]++;
}
// 递增已分类的项目总数
this.totalCount++;
}
/**
* 计算该项目匹配所有可能类别的概率,
* 依据其属性进行分类评分。
*
* @param {Object} item 格式与 train 方法输入一致的对象
* @returns {Object} 返回该项目属于各个类别的概率分布
*/
score(item) {
// 初始化一个对象来存储每个类别的概率。
const odds = {};
let category;
// 遍历项目的每个键,
// 然后遍历所有已训练的类别。
for (const k in item) {
const v = item[k];
for (category in this.data) {
// 为当前类别创建一个对象,用于存储键值组合的概率。
odds[category] = {};
// 如果该类别中没有该属性,则该属性的概率为 0;
// 如果该类别包含该属性,则计算该属性的出现频率,
// 其值为该属性在类别中的出现次数除以总样本数。
if (this.data[category][k]) {
odds[category][k + "_" + v] =
(this.data[category][k][v] || 0) / this.totalCount;
} else {
odds[category][k + "_" + v] = 0;
}
}
}
// 初始化一个对象,用于存储每个类别的总概率。
const oddsSums = {};
for (category in odds) {
// 计算每个类别的所有键值组合概率之和,
// 不存在的类别不会影响最终得分。
oddsSums[category] = 0;
for (const combination in odds[category]) {
oddsSums[category] += odds[category][combination];
}
}
return oddsSums;
}
}
export default BayesianClassifier;