2、遍历每个词,得到每个词在所有文档里的IDF值,和在本聚类内出现的次数(TF)相乘的值
3、用一个字典(key是词,value是TF*IDF权重)来保存所有的词信息,然后按value对字典排序,最后取权重排名靠前的几个词作为关键词
测试输入如下
================================
a 奥运 拳击 入场券 基本 分罄 邹市明 夺冠 对手 浮出 水面
a 股民 要 清楚 自己 的 目的
a 印花税 之 股民 四季
a ASP.NET 自定义 控件 复杂 属性 声明 持久性 浅析
a 运动员 行李 将 “后 上 先 下” 奥运 相关 人员 行李 实名制
a asp.net 控件 开发 显示 控件 内容
a 奥运 票务 网上 成功 订票 后 应 及时 到 银行 代售 网点 付款
a 某 心理 健康 站 开张 后 首 个 咨询 者 是 位 新 股民
a 残疾 女 青年 入围 奥运 游泳 比赛 创 奥运 历史 两 项 第一
a 介绍 一 个 ASP.net MVC 系列 教程
a 在 asp.net 中 实现 观察者 模式 ,或 有 更 好 的 方法 (续)
a 输 大钱 的 股民 给 我们 启迪
a Asp.Net 页面 执行 流程 分析
a 杭州 股民 放 鞭炮 庆祝 印花税 下调
//4、初始化k-means算法,第一个参数表示输入数据,第二个参数表示要聚成几个类 WawaKMeans kmeans = new WawaKMeans(data, K); //5、开始迭代 kmeans.Start(); //6、获取聚类结果并输出 WawaCluster[] clusters = kmeans.Clusters; StringBuilder sb = new StringBuilder(); foreach (WawaCluster cluster in clusters) { Listmembers = cluster.CurrentMembership; //获取该聚类的关键词并打印 IEnumerable keywords = tf.GetKeyword(cluster.CurrentMembership, 2); StringBuilder sbTemp = new StringBuilder(); sbTemp.Append("---------"); foreach (string s in keywords) { sbTemp.AppendFormat("{0},", s); } sbTemp.Append("-------\r\n"); Console.WriteLine(sbTemp); //打印该聚类的成员 sb.Append(sbTemp.ToString()); foreach (int i in members) { Console.WriteLine(docs[i]); sb.AppendFormat("{0}\r\n", docs[i]); } }
再看GetKeyword方法的实现
/**/////// 获取某组文档的关键词 /// /// /// ///public IEnumerable GetKeyword(List arr, int count) { //1、给每个文档分词并保存在一个列表里 List allWords = new List (); foreach (int i in arr) { //这里把一个文档里出现的多个词进行消重 allWords.AddRange(GetDistinctWords(_tokenizer.Partition(_docs[i]))); } //2、用一个字典保存词的词频,key是词,value是重复次数 Dictionary tfDict = SortByDuplicateCount(allWords); //3、遍历已排序的词频字典,并获取每个词的IDF值,并把更新后的结果放入一个tfidfDict词典 //该词典的key是词,value是tfidf值 Dictionary tfidfDict = new Dictionary (tfDict.Count); foreach (KeyValuePair pair in tfDict) { int tremIndex; if(_tremIndex.TryGetValue(pair.Key,out tremIndex)) { float idf = GetInverseDocumentFrequency(tremIndex); tfidfDict.Add(pair.Key, pair.Value * idf); } } //4、给tfidf字典俺权重排序 tfidfDict = GetSortByValueDict(tfidfDict); //5、更新要提取的关键词数量 int keywordCount = count; if (keywordCount > tfidfDict.Count) keywordCount = tfidfDict.Count; //6、用一个数组保存tfidf字典的keys,这些key已排序 string[] keywordArr = new string[tfidfDict.Count]; tfidfDict.Keys.CopyTo(keywordArr,0); //7、在关键词数组里取出前几个关键词返回给调用者 List result = new List (keywordCount); int tempCount = 0; foreach (string str in keywordArr) { tempCount++; result.Add(str); if(tempCount >=keywordCount) break; } return result; }
这里面用到一个SortByDuplicateCount方法,是对一个集合里的元素按重复次数排序,输出一个字典,字典的key是原始元素,value是出现次数,并按出现次数从大到小排序,像 { "abcd", "ab", "b", "a", "abcd", "ab", "ab", "ab", "cd", "cd", "cd" }这样一个集合应该输入如下结果。
ab-4
cd-3
abcd-2
b-1
a-1
原理是先用一个字典计算每个元素的出现次数,然后把该字典按value的大小排序,下面是实现代码
/// 把一个集合按重复次数排序 /// ////// /// public static Dictionary SortByDuplicateCount (IList inputList) { //用于计算每个元素出现的次数,key是元素,value是出现次数 Dictionary distinctDict = new Dictionary (); for (int i = 0; i < inputList.Count; i++) { //这里没用trygetvalue,会计算两次hash if (distinctDict.ContainsKey(inputList[i])) distinctDict[inputList[i]]++; else distinctDict.Add(inputList[i], 1); } Dictionary sortByValueDict = GetSortByValueDict(distinctDict); return sortByValueDict; }
这里用到一个把一个字典按值的大小排序的方法GetSortByValueDict,代码如下,是泛型的
/**/////// 把一个字典俺value的顺序排序 /// ////// /// /// public static Dictionary GetSortByValueDict (IDictionary distinctDict) { //用于给tempDict.Values排序的临时数组 V[] tempSortList = new V[distinctDict.Count]; distinctDict.Values.CopyTo(tempSortList, 0); Array.Sort(tempSortList); //给数据排序 Array.Reverse(tempSortList);//反转 //用于保存按value排序的字典 Dictionary sortByValueDict = new Dictionary (distinctDict.Count); for (int i = 0; i < tempSortList.Length; i++) { foreach (KeyValuePair pair in distinctDict) { //比较两个泛型是否相当要用Equals,不能用==操作符 if (pair.Value.Equals(tempSortList[i]) && !sortByValueDict.ContainsKey(pair.Key)) sortByValueDict.Add(pair.Key, pair.Value); } } return sortByValueDict; }
对一个文章内出现的多个词进行消重是因为如果一个文章里堆叠关键词会影响本聚类关键词提取的准确性,所以要排重,算法如下,也是泛型的
/**/////// 对一个数组进行排重 /// /// ///public static IEnumerable GetDistinctWords (IEnumerable scanKeys) { T temp = default(T); if (scanKeys.Equals(temp)) return new T[0]; else { Dictionary fixKeys = new Dictionary (); foreach (T key in scanKeys) { fixKeys[key] = key; } T[] result = new T[fixKeys.Count]; fixKeys.Values.CopyTo(result, 0); return result; } }
最后效果如下
Iteration 0...
Iteration 1...
---------asp,net,-------
a ASP.NET 自定义 控件 复杂 属性 声明 持久性 浅析
a asp.net 控件 开发 显示 控件 内容
a 介绍 一 个 ASP.net MVC 系列 教程
a 在 asp.net 中 实现 观察者 模式 ,或 有 更 好 的 方法 (续)
a Asp.Net 页面 执行 流程 分析
---------股民,印花税,-------
a 股民 要 清楚 自己 的 目的
a 印花税 之 股民 四季
a 某 心理 健康 站 开张 后 首 个 咨询 者 是 位 新 股民
a 输 大钱 的 股民 给 我们 启迪
a 杭州 股民 放 鞭炮 庆祝 印花税 下调
---------奥运,拳击,-------
a 奥运 拳击 入场券 基本 分罄 邹市明 夺冠 对手 浮出 水面
a 运动员 行李 将 “后 上 先 下” 奥运 相关 人员 行李 实名制
a 奥运 票务 网上 成功 订票 后 应 及时 到 银行 代售 网点 付款
a 残疾 女 青年 入围 奥运 游泳 比赛 创 奥运 历史 两 项 第一
可以看到,提取的关键字还是准确的,并没把a当成关键字。