数据挖掘-基于Kmeans算法、MBSAS算法及DBSCAN算法的newsgroup18828文本聚类器的JAVA实现(上)...

(update 2012.12.28 关于本项目下载及运行的常见问题 FAQ见newsgroup18828文本分类器、文本聚类器、关联分析频繁模式挖掘算法的Java实现工程下载及运行FAQ)

本文要点如下:

对newsgroup文档集进行预处理,按照DF法及SVD分解法抽取特征词,实现降维

实现了K-Means,MBSAS,DBSCAN三种聚类算法
用weka工具进行newsgroup文档聚类

计算各种算法聚类的熵,进行算法评价

1、newsgroup文档集预处理

newsgroup是常用的数据挖掘实验数据。文本预处理主要包括单词分片、去除标点等无关符号、去停用词等等,相关详细介绍见我的另一篇博文数据挖掘-基于贝叶斯算法及KNN算法的newsgroup18828文本分类器的JAVA实现(上),此处只给出文本预处理和向量化不同的部分代码。

文本预处理类DataPreProcess.java

[java] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. packagecom.pku.yangliu;
  2. importjava.io.BufferedReader;
  3. importjava.io.File;
  4. importjava.io.FileReader;
  5. importjava.io.FileWriter;
  6. importjava.io.IOException;
  7. importjava.util.ArrayList;
  8. /**
  9. *Newsgroups文档集预处理类
  10. */
  11. publicclassDataPreProcess{
  12. /**输入文件调用处理数据函数
  13. *@paramstrDirnewsgroup文件目录的绝对路径
  14. *@throwsIOException
  15. */
  16. publicvoiddoProcess(StringstrDir)throwsIOException{
  17. FilefileDir=newFile(strDir);
  18. if(!fileDir.exists()){
  19. System.out.println("Filenotexist:"+strDir);
  20. return;
  21. }
  22. StringsubStrDir=strDir.substring(strDir.lastIndexOf('/'));
  23. StringdirTarget=strDir+"/../../processedSample_includeNotSpecial"+subStrDir;
  24. FilefileTarget=newFile(dirTarget);
  25. if(!fileTarget.exists()){//注意processedSample需要先建立目录建出来,否则会报错,因为母目录不存在
  26. fileTarget.mkdir();
  27. }
  28. File[]srcFiles=fileDir.listFiles();
  29. String[]stemFileNames=newString[srcFiles.length];
  30. for(inti=0;i<srcFiles.length;i++){
  31. StringfileFullName=srcFiles[i].getCanonicalPath();
  32. StringfileShortName=srcFiles[i].getName();
  33. if(!newFile(fileFullName).isDirectory()){//确认子文件名不是目录如果是可以再次递归调用
  34. System.out.println("Beginpreprocess:"+fileFullName);
  35. StringBuilderstringBuilder=newStringBuilder();
  36. stringBuilder.append(dirTarget+"/"+fileShortName);
  37. createProcessFile(fileFullName,stringBuilder.toString());
  38. stemFileNames[i]=stringBuilder.toString();
  39. }
  40. else{
  41. fileFullName=fileFullName.replace("\\","/");
  42. doProcess(fileFullName);
  43. }
  44. }
  45. //下面调用stem算法
  46. if(stemFileNames.length>0&&stemFileNames[0]!=null){
  47. Stemmer.porterMain(stemFileNames);
  48. }
  49. }
  50. /**进行文本预处理生成目标文件
  51. *@paramsrcDir源文件文件目录的绝对路径
  52. *@paramtargetDir生成的目标文件的绝对路径
  53. *@throwsIOException
  54. */
  55. privatestaticvoidcreateProcessFile(StringsrcDir,StringtargetDir)throwsIOException{
  56. //TODOAuto-generatedmethodstub
  57. FileReadersrcFileReader=newFileReader(srcDir);
  58. FileReaderstopWordsReader=newFileReader("F:/DataMiningSample/stopwords.txt");
  59. FileWritertargetFileWriter=newFileWriter(targetDir);
  60. BufferedReadersrcFileBR=newBufferedReader(srcFileReader);//装饰模式
  61. BufferedReaderstopWordsBR=newBufferedReader(stopWordsReader);
  62. Stringline,resLine,stopWordsLine;
  63. //用stopWordsBR够着停用词的ArrayList容器
  64. ArrayList<String>stopWordsArray=newArrayList<String>();
  65. while((stopWordsLine=stopWordsBR.readLine())!=null){
  66. if(!stopWordsLine.isEmpty()){
  67. stopWordsArray.add(stopWordsLine);
  68. }
  69. }
  70. while((line=srcFileBR.readLine())!=null){
  71. resLine=lineProcess(line,stopWordsArray);
  72. if(!resLine.isEmpty()){
  73. //按行写,一行写一个单词
  74. String[]tempStr=resLine.split("");//\s
  75. for(inti=0;i<tempStr.length;i++){
  76. if(!tempStr[i].isEmpty()){
  77. targetFileWriter.append(tempStr[i]+"\n");
  78. }
  79. }
  80. }
  81. }
  82. targetFileWriter.flush();
  83. targetFileWriter.close();
  84. srcFileReader.close();
  85. stopWordsReader.close();
  86. srcFileBR.close();
  87. stopWordsBR.close();
  88. }
  89. /**对每行字符串进行处理,主要是词法分析、去停用词和stemming
  90. *@paramline待处理的一行字符串
  91. *@paramArrayList<String>停用词数组
  92. *@returnString处理好的一行字符串,是由处理好的单词重新生成,以空格为分隔符
  93. *@throwsIOException
  94. */
  95. privatestaticStringlineProcess(Stringline,ArrayList<String>stopWordsArray)throwsIOException{
  96. //TODOAuto-generatedmethodstub
  97. //step1英文词法分析,去除数字、连字符、标点符号、特殊字符,所有大写字母转换成小写,可以考虑用正则表达式
  98. Stringres[]=line.split("[^a-zA-Z]");
  99. //这里要小心,防止把有单词中间有数字和连字符的单词截断了,但是截断也没事
  100. StringresString=newString();
  101. //step2去停用词
  102. //step3stemming,返回后一起做
  103. for(inti=0;i<res.length;i++){
  104. if(!res[i].isEmpty()&&!stopWordsArray.contains(res[i].toLowerCase())){
  105. resString+=""+res[i].toLowerCase()+"";
  106. }
  107. }
  108. returnresString;
  109. }
  110. /**
  111. *@paramargs
  112. *@throwsIOException
  113. */
  114. publicvoidBPPMain(String[]args)throwsIOException{
  115. //TODOAuto-generatedmethodstub
  116. DataPreProcessdataPrePro=newDataPreProcess();
  117. dataPrePro.doProcess("F:/DataMiningSample/orginSample");
  118. }
  119. }
文本向量化表示主要基于TF-IDF值 ComputeWordsVector.java

[java] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. packagecom.pku.yangliu;
  2. importjava.io.BufferedReader;
  3. importjava.io.File;
  4. importjava.io.FileReader;
  5. importjava.io.FileWriter;
  6. importjava.io.IOException;
  7. importjava.util.HashSet;
  8. importjava.util.SortedMap;
  9. importjava.util.Map;
  10. importjava.util.Set;
  11. importjava.util.SortedSet;
  12. importjava.util.TreeMap;
  13. importjava.util.Iterator;
  14. importjava.util.TreeSet;
  15. /**计算文档的属性向量,将所有文档向量化
  16. *
  17. */
  18. publicclassComputeWordsVector{
  19. /**计算文档的TF-IDF属性向量,返回Map<文件名,Map<特征词,TF-IDF值>>
  20. *@paramtestSampleDir处理好的聚类样本测试样例集合
  21. *@returnMap<String,Map<String,Double>>所有测试样例的属性向量构成的map
  22. *@throwsIOException
  23. */
  24. publicMap<String,Map<String,Double>>computeTFMultiIDF(StringtestSampleDir)throwsIOException{
  25. Stringword;
  26. Map<String,Map<String,Double>>allTestSampleMap=newTreeMap<String,Map<String,Double>>();
  27. Map<String,Double>idfPerWordMap=computeIDF(testSampleDir);
  28. Map<String,Double>TFPerDocMap=newTreeMap<String,Double>();//计算每篇文档中含有各特征词数量
  29. File[]samples=newFile(testSampleDir).listFiles();
  30. System.out.println("thetotalnumberoftestfilesis"+samples.length);
  31. for(inti=0;i<samples.length;i++){
  32. TFPerDocMap.clear();
  33. FileReadersamReader=newFileReader(samples[i]);
  34. BufferedReadersamBR=newBufferedReader(samReader);
  35. DoublewordSumPerDoc=0.0;//计算每篇文档的总词数
  36. while((word=samBR.readLine())!=null){
  37. if(!word.isEmpty()){
  38. wordSumPerDoc++;
  39. if(TFPerDocMap.containsKey(word)){
  40. Doublecount=TFPerDocMap.get(word);
  41. TFPerDocMap.put(word,count+1.0);
  42. }
  43. else{
  44. TFPerDocMap.put(word,1.0);
  45. }
  46. }
  47. }
  48. DoublemaxCount=0.0,wordWeight;//记录出现次数最多的词出现的次数,用做归一化
  49. Set<Map.Entry<String,Double>>tempTF=TFPerDocMap.entrySet();
  50. for(Iterator<Map.Entry<String,Double>>mt=tempTF.iterator();mt.hasNext();){
  51. Map.Entry<String,Double>me=mt.next();
  52. if(me.getValue()>maxCount)maxCount=me.getValue();
  53. }
  54. for(Iterator<Map.Entry<String,Double>>mt=tempTF.iterator();mt.hasNext();){
  55. Map.Entry<String,Double>me=mt.next();
  56. DoubleIDF=Math.log(samples.length/idfPerWordMap.get(me.getKey()))/Math.log(10);
  57. wordWeight=(me.getValue()/maxCount)*IDF;
  58. TFPerDocMap.put(me.getKey(),wordWeight);
  59. }
  60. TreeMap<String,Double>tempMap=newTreeMap<String,Double>();
  61. tempMap.putAll(TFPerDocMap);
  62. allTestSampleMap.put(samples[i].getName(),tempMap);
  63. }
  64. //printTestSampleMap(allTestSampleMap);
  65. returnallTestSampleMap;
  66. }
  67. /**输出测试样例map内容,用于测试
  68. *@paramSortedMap<String,Double>属性词典
  69. *@throwsIOException
  70. */
  71. voidprintTestSampleMap(Map<String,Map<String,Double>>allTestSampleMap)throwsIOException{
  72. //TODOAuto-generatedmethodstub
  73. FileoutPutFile=newFile("F:/DataMiningSample/KmeansClusterResult/allTestSampleMap.txt");
  74. FileWriteroutPutFileWriter=newFileWriter(outPutFile);
  75. Set<Map.Entry<String,Map<String,Double>>>allWords=allTestSampleMap.entrySet();
  76. for(Iterator<Map.Entry<String,Map<String,Double>>>it=allWords.iterator();it.hasNext();){
  77. Map.Entry<String,Map<String,Double>>me=it.next();
  78. outPutFileWriter.append(me.getKey()+"");
  79. Set<Map.Entry<String,Double>>vecSet=me.getValue().entrySet();
  80. for(Iterator<Map.Entry<String,Double>>jt=vecSet.iterator();jt.hasNext();){
  81. Map.Entry<String,Double>ne=jt.next();
  82. outPutFileWriter.append(ne.getKey()+""+ne.getValue()+"");
  83. }
  84. outPutFileWriter.append("\n");
  85. outPutFileWriter.flush();
  86. }
  87. outPutFileWriter.close();
  88. }
  89. /**统计每个词的总的出现次数,返回出现次数大于n次的词汇构成最终的属性词典
  90. *@paramstrDir处理好的newsgroup文件目录的绝对路径
  91. *@throwsIOException
  92. */
  93. publicSortedMap<String,Double>countWords(StringstrDir,Map<String,Double>wordMap)throwsIOException{
  94. FilesampleFile=newFile(strDir);
  95. File[]sampleDir=sampleFile.listFiles();
  96. Stringword;
  97. for(intj=0;j<sampleDir.length;j++){
  98. File[]sample=sampleDir[j].listFiles();
  99. for(inti=0;i<sample.length;i++){
  100. if(sample[i].getName().contains("stemed")){
  101. FileReadersamReader=newFileReader(sample[i]);
  102. BufferedReadersamBR=newBufferedReader(samReader);
  103. while((word=samBR.readLine())!=null){
  104. if(!word.isEmpty()&&wordMap.containsKey(word)){
  105. doublecount=wordMap.get(word)+1;
  106. wordMap.put(word,count);
  107. }
  108. else{
  109. wordMap.put(word,1.0);
  110. }
  111. }
  112. }
  113. }
  114. }
  115. //去除停用词后,先用DF法选取特征词,后面再加入特征词的选取算法
  116. SortedMap<String,Double>newWordMap=newTreeMap<String,Double>();
  117. Set<Map.Entry<String,Double>>allWords=wordMap.entrySet();
  118. for(Iterator<Map.Entry<String,Double>>it=allWords.iterator();it.hasNext();){
  119. Map.Entry<String,Double>me=it.next();
  120. if(me.getValue()>100){//DF法降维
  121. newWordMap.put(me.getKey(),me.getValue());
  122. }
  123. }
  124. returnnewWordMap;
  125. }
  126. /**计算IDF,即属性词典中每个词在多少个文档中出现过
  127. *@paramtestSampleDir聚类算法测试样本所在目录
  128. *@return单词的IDFmap格式为SortedMap<String,Double>即<单词,包含该单词的文档数>
  129. *@throwsIOException
  130. */
  131. Map<String,Double>computeIDF(StringtestSampleDir)throwsIOException{
  132. //TODOAuto-generatedmethodstub
  133. Map<String,Double>IDFPerWordMap=newTreeMap<String,Double>();
  134. Set<String>alreadyCountWord=newHashSet<String>();//记下当前已经遇到过的该文档中的词
  135. Stringword;
  136. File[]samples=newFile(testSampleDir).listFiles();
  137. for(inti=0;i<samples.length;i++){
  138. alreadyCountWord.clear();
  139. FileReadertsReader=newFileReader(samples[i]);
  140. BufferedReadertsBR=newBufferedReader(tsReader);
  141. while((word=tsBR.readLine())!=null){
  142. if(!alreadyCountWord.contains(word)){
  143. if(IDFPerWordMap.containsKey(word)){
  144. IDFPerWordMap.put(word,IDFPerWordMap.get(word)+1.0);
  145. }
  146. elseIDFPerWordMap.put(word,1.0);
  147. alreadyCountWord.add(word);
  148. }
  149. }
  150. }
  151. returnIDFPerWordMap;
  152. }
  153. /**创建聚类算法的测试样例集,主要是过滤出只含有特征词的文档写到一个目录下
  154. *@paramStringsrcDir源目录,已经经过预处理但还没有过滤非特征词的文档目录
  155. *@paramStringdestDir目的目录,聚类算法的测试样例目录
  156. *@returnString[]创建测试样例集中特征词数组
  157. *@throwsIOException
  158. */
  159. String[]createTestSamples(StringsrcDir,StringdestDir)throwsIOException{
  160. //TODOAuto-generatedmethodstub
  161. SortedMap<String,Double>wordMap=newTreeMap<String,Double>();
  162. wordMap=countWords(srcDir,wordMap);
  163. System.out.println("specialwordsmapsizes:"+wordMap.size());
  164. Stringword,testSampleFile;
  165. File[]sampleDir=newFile(srcDir).listFiles();
  166. for(inti=0;i<sampleDir.length;i++){
  167. File[]sample=sampleDir[i].listFiles();
  168. for(intj=0;j<sample.length;j++){
  169. if(sample[j].getName().contains("stemed")){
  170. testSampleFile=destDir+sampleDir[i].getName()+"_"+sample[j].getName();
  171. FileReadersamReader=newFileReader(sample[j]);
  172. BufferedReadersamBR=newBufferedReader(samReader);
  173. FileWritertsWriter=newFileWriter(newFile(testSampleFile));
  174. while((word=samBR.readLine())!=null){
  175. if(wordMap.containsKey(word)){
  176. tsWriter.append(word+"\n");
  177. }
  178. }
  179. tsWriter.flush();
  180. tsWriter.close();
  181. }
  182. }
  183. }
  184. //返回属性词典
  185. String[]terms=newString[wordMap.size()];
  186. inti=0;
  187. Set<Map.Entry<String,Double>>allWords=wordMap.entrySet();
  188. for(Iterator<Map.Entry<String,Double>>it=allWords.iterator();it.hasNext();){
  189. Map.Entry<String,Double>me=it.next();
  190. terms[i]=me.getKey();
  191. i++;
  192. }
  193. returnterms;
  194. }
  195. /**评估函数根据聚类结果文件统计熵和混淆矩阵
  196. *@paramclusterResultFile聚类结果文件
  197. *@paramK聚类数目
  198. *@returndouble聚类结果的熵值
  199. *@throwsIOException
  200. */
  201. doubleevaluateClusterRes(StringclusterResultFile,intK)throwsIOException{
  202. //TODOAuto-generatedmethodstub
  203. Map<String,String>rightCate=newTreeMap<String,String>();
  204. Map<String,String>resultCate=newTreeMap<String,String>();
  205. FileReadercrReader=newFileReader(clusterResultFile);
  206. BufferedReadercrBR=newBufferedReader(crReader);
  207. String[]s;
  208. Stringline;
  209. while((line=crBR.readLine())!=null){
  210. s=line.split("");
  211. resultCate.put(s[0],s[1]);
  212. //再把s[0]用_分片
  213. rightCate.put(s[0],s[0].split("_")[0]);
  214. }
  215. returncomputeEntropyAndConfuMatrix(rightCate,resultCate,K);//返回熵
  216. }
  217. /**计算混淆矩阵并且输出,返回熵
  218. *@paramrightCate正确类目对应map
  219. *@paramresultCate聚类结果对应map
  220. *@returndouble返回聚类的熵
  221. *@throwsIOException
  222. */
  223. privatedoublecomputeEntropyAndConfuMatrix(Map<String,String>rightCate,
  224. Map<String,String>resultCate,intK){
  225. //TODOAuto-generatedmethodstub
  226. int[][]confusionMatrix=newint[K][20];//K行20列,[i,j]表示聚类i中属于类目j的文件数
  227. //首先求出类目对应的数组索引
  228. SortedSet<String>cateNames=newTreeSet<String>();
  229. Set<Map.Entry<String,String>>rightCateSet=rightCate.entrySet();
  230. for(Iterator<Map.Entry<String,String>>it=rightCateSet.iterator();it.hasNext();){
  231. Map.Entry<String,String>me=it.next();
  232. cateNames.add(me.getValue());
  233. }
  234. String[]cateNamesArray=cateNames.toArray(newString[0]);
  235. Map<String,Integer>cateNamesToIndex=newTreeMap<String,Integer>();
  236. for(inti=0;i<cateNamesArray.length;i++){
  237. cateNamesToIndex.put(cateNamesArray[i],i);
  238. }
  239. for(Iterator<Map.Entry<String,String>>it=rightCateSet.iterator();it.hasNext();){
  240. Map.Entry<String,String>me=it.next();
  241. confusionMatrix[Integer.parseInt(resultCate.get(me.getKey()))][cateNamesToIndex.get(me.getValue())]++;
  242. }
  243. //输出混淆矩阵
  244. double[]clusterSum=newdouble[K];//记录每个聚类的文件数
  245. double[]everyClusterEntropy=newdouble[K];//记录每个聚类的熵
  246. doubleclusterEntropy=0;
  247. System.out.print("");
  248. for(inti=0;i<20;i++){
  249. System.out.print(i+"");
  250. }
  251. System.out.println();
  252. for(inti=0;i<K;i++){
  253. System.out.print(i+"");
  254. for(intj=0;j<20;j++){
  255. clusterSum[i]+=confusionMatrix[i][j];
  256. System.out.print(confusionMatrix[i][j]+"");
  257. }
  258. System.out.println();
  259. }
  260. System.out.println();
  261. for(inti=0;i<K;i++){
  262. if(clusterSum[i]!=0){
  263. for(intj=0;j<20;j++){
  264. doublep=(double)confusionMatrix[i][j]/clusterSum[i];
  265. if(p!=0){
  266. everyClusterEntropy[i]+=-p*Math.log(p);
  267. }
  268. }
  269. clusterEntropy+=clusterSum[i]/(double)rightCate.size()*everyClusterEntropy[i];
  270. }
  271. }
  272. returnclusterEntropy;
  273. }
  274. }

2、K-means算法

K-means算法是非常经典的聚类算法。其算法思路是:先选K个初始聚类点作为初始中心点,然后计算其他所有点到K个聚类点的距离做聚类,将点分到最近的聚类,聚完类后中心点发生变化了,于是更新中心点。然后再计算其他所有点到这K个中心点的距离重新聚类,中心点又会发生变化,如此迭代下去。其伪代码如下:

K-means算法的实现有以下关键点:

初始点的选择策略:随机选、均匀抽样、最大最小法等
距离的度量 1-余弦相似度,欧式距离,1-向量内积,测试发现1-余弦相似度效果最好,而1-向量内积速度最快。
中心点的计算 向量各维取评价
算法停止条件 计算准则函数及设置最大迭代次数
空聚类的处理 注意空聚类导致的程序bug

K-means算法实现类KmeansCluster.java

[java] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. packagecom.pku.yangliu;
  2. importjava.io.FileWriter;
  3. importjava.io.IOException;
  4. importjava.util.Iterator;
  5. importjava.util.Map;
  6. importjava.util.Set;
  7. importjava.util.TreeMap;
  8. importjava.util.Vector;
  9. /**Kmeans聚类算法的实现类,将newsgroups文档集聚成10类、20类、30类
  10. *算法结束条件:当每个点最近的聚类中心点就是它所属的聚类中心点时,算法结束
  11. *
  12. */
  13. publicclassKmeansCluster{
  14. /**Kmeans算法主过程
  15. *@paramMap<String,Map<String,Double>>allTestSampleMap聚类算法测试样本map
  16. *@paramintK聚类的数量
  17. *@returnMap<String,Integer>聚类的结果即<文件名,聚类完成后所属的类别标号>
  18. *@throwsIOException
  19. */
  20. privateMap<String,Integer>doProcess(
  21. Map<String,Map<String,Double>>allTestSampleMap,intK){
  22. //TODOAuto-generatedmethodstub
  23. //0、首先获取allTestSampleMap所有文件名顺序组成的数组
  24. String[]testSampleNames=newString[allTestSampleMap.size()];
  25. intcount=0,tsLength=allTestSampleMap.size();
  26. Set<Map.Entry<String,Map<String,Double>>>allTestSampeleMapSet=allTestSampleMap.entrySet();
  27. for(Iterator<Map.Entry<String,Map<String,Double>>>it=allTestSampeleMapSet.iterator();it.hasNext();){
  28. Map.Entry<String,Map<String,Double>>me=it.next();
  29. testSampleNames[count++]=me.getKey();
  30. }
  31. //1、初始点的选择算法是随机选择或者是均匀分开选择,这里采用后者
  32. Map<Integer,Map<String,Double>>meansMap=getInitPoint(allTestSampleMap,K);//保存K个中心点
  33. double[][]distance=newdouble[tsLength][K];//distance[i][j]记录点i到聚类中心j的距离
  34. //2、初始化K个聚类
  35. int[]assignMeans=newint[tsLength];//记录所有点属于的聚类序号,初始化全部为0
  36. Map<Integer,Vector<Integer>>clusterMember=newTreeMap<Integer,Vector<Integer>>();//记录每个聚类的成员点序号
  37. Vector<Integer>mem=newVector<Integer>();
  38. intiterNum=0;//迭代次数
  39. while(true){
  40. System.out.println("IterationNo."+(iterNum++)+"----------------------");
  41. //3、计算每个点和每个聚类中心的距离
  42. for(inti=0;i<tsLength;i++){
  43. for(intj=0;j<K;j++){
  44. distance[i][j]=getDistance(allTestSampleMap.get(testSampleNames[i]),meansMap.get(j));
  45. }
  46. }
  47. //4、找出每个点最近的聚类中心
  48. int[]nearestMeans=newint[tsLength];
  49. for(inti=0;i<tsLength;i++){
  50. nearestMeans[i]=findNearestMeans(distance,i);
  51. }
  52. //5、判断当前所有点属于的聚类序号是否已经全部是其离得最近的聚类,如果是或者达到最大的迭代次数,那么结束算法
  53. intokCount=0;
  54. for(inti=0;i<tsLength;i++){
  55. if(nearestMeans[i]==assignMeans[i])okCount++;
  56. }
  57. System.out.println("okCount="+okCount);
  58. if(okCount==tsLength||iterNum>=10)break;
  59. //6、如果前面条件不满足,那么需要重新聚类再进行一次迭代,需要修改每个聚类的成员和每个点属于的聚类信息
  60. clusterMember.clear();
  61. for(inti=0;i<tsLength;i++){
  62. assignMeans[i]=nearestMeans[i];
  63. if(clusterMember.containsKey(nearestMeans[i])){
  64. clusterMember.get(nearestMeans[i]).add(i);
  65. }
  66. else{
  67. mem.clear();
  68. mem.add(i);
  69. Vector<Integer>tempMem=newVector<Integer>();
  70. tempMem.addAll(mem);
  71. clusterMember.put(nearestMeans[i],tempMem);
  72. }
  73. }
  74. //7、重新计算每个聚类的中心点!
  75. for(inti=0;i<K;i++){
  76. if(!clusterMember.containsKey(i)){//注意kmeans可能产生空聚类
  77. continue;
  78. }
  79. Map<String,Double>newMean=computeNewMean(clusterMember.get(i),allTestSampleMap,testSampleNames);
  80. Map<String,Double>tempMean=newTreeMap<String,Double>();
  81. tempMean.putAll(newMean);
  82. meansMap.put(i,tempMean);
  83. }
  84. }
  85. //8、形成聚类结果并且返回
  86. Map<String,Integer>resMap=newTreeMap<String,Integer>();
  87. for(inti=0;i<tsLength;i++){
  88. resMap.put(testSampleNames[i],assignMeans[i]);
  89. }
  90. returnresMap;
  91. }
  92. /**计算当前聚类新的中心,采用向量平均
  93. *@paramclusterM该点到所有聚类中心的距离
  94. *@paramallTestSampleMap所有测试样例的<文件名,向量>构成的map
  95. *@paramtestSampleNames所有测试样例文件名构成的数组
  96. *@returnMap<String,Double>新的聚类中心的向量
  97. *@throwsIOException
  98. */
  99. privateMap<String,Double>computeNewMean(Vector<Integer>clusterM,
  100. Map<String,Map<String,Double>>allTestSampleMap,
  101. String[]testSampleNames){
  102. //TODOAuto-generatedmethodstub
  103. doublememberNum=(double)clusterM.size();
  104. Map<String,Double>newMeanMap=newTreeMap<String,Double>();
  105. Map<String,Double>currentMemMap=newTreeMap<String,Double>();
  106. for(Iterator<Integer>it=clusterM.iterator();it.hasNext();){
  107. intme=it.next();
  108. currentMemMap=allTestSampleMap.get(testSampleNames[me]);
  109. Set<Map.Entry<String,Double>>currentMemMapSet=currentMemMap.entrySet();
  110. for(Iterator<Map.Entry<String,Double>>jt=currentMemMapSet.iterator();jt.hasNext();){
  111. Map.Entry<String,Double>ne=jt.next();
  112. if(newMeanMap.containsKey(ne.getKey())){
  113. newMeanMap.put(ne.getKey(),newMeanMap.get(ne.getKey())+ne.getValue());
  114. }
  115. else{
  116. newMeanMap.put(ne.getKey(),ne.getValue());
  117. }
  118. }
  119. }
  120. Set<Map.Entry<String,Double>>newMeanMapSet=newMeanMap.entrySet();
  121. for(Iterator<Map.Entry<String,Double>>jt=newMeanMapSet.iterator();jt.hasNext();){
  122. Map.Entry<String,Double>ne=jt.next();
  123. newMeanMap.put(ne.getKey(),newMeanMap.get(ne.getKey())/memberNum);
  124. }
  125. returnnewMeanMap;
  126. }
  127. /**找出距离当前点最近的聚类中心
  128. *@paramdouble[][]点到所有聚类中心的距离
  129. *@returni最近的聚类中心的序号
  130. *@throwsIOException
  131. */
  132. privateintfindNearestMeans(double[][]distance,intm){
  133. //TODOAuto-generatedmethodstub
  134. doubleminDist=10;
  135. intj=0;
  136. for(inti=0;i<distance[m].length;i++){
  137. if(distance[m][i]<minDist){
  138. minDist=distance[m][i];
  139. j=i;
  140. }
  141. }
  142. returnj;
  143. }
  144. /**计算两个点的距离
  145. *@parammap1点1的向量map
  146. *@parammap2点2的向量map
  147. *@returndouble两个点的欧式距离
  148. */
  149. privatedoublegetDistance(Map<String,Double>map1,Map<String,Double>map2){
  150. //TODOAuto-generatedmethodstub
  151. return1-computeSim(map1,map2);
  152. }
  153. /**计算两个文本的相似度
  154. *@paramtestWordTFMap文本1的<单词,词频>向量
  155. *@paramtrainWordTFMap文本2<单词,词频>向量
  156. *@returnDouble向量之间的相似度以向量夹角余弦计算或者向量内积计算(效果相当而速度更快)
  157. *@throwsIOException
  158. */
  159. privatedoublecomputeSim(Map<String,Double>testWordTFMap,
  160. Map<String,Double>trainWordTFMap){
  161. //TODOAuto-generatedmethodstub
  162. doublemul=0;//,testAbs=0,trainAbs=0;
  163. Set<Map.Entry<String,Double>>testWordTFMapSet=testWordTFMap.entrySet();
  164. for(Iterator<Map.Entry<String,Double>>it=testWordTFMapSet.iterator();it.hasNext();){
  165. Map.Entry<String,Double>me=it.next();
  166. if(trainWordTFMap.containsKey(me.getKey())){
  167. mul+=me.getValue()*trainWordTFMap.get(me.getKey());
  168. }
  169. //testAbs+=me.getValue()*me.getValue();
  170. }
  171. //testAbs=Math.sqrt(testAbs);
  172. /*Set<Map.Entry<String,Double>>trainWordTFMapSet=trainWordTFMap.entrySet();
  173. for(Iterator<Map.Entry<String,Double>>it=trainWordTFMapSet.iterator();it.hasNext();){
  174. Map.Entry<String,Double>me=it.next();
  175. trainAbs+=me.getValue()*me.getValue();
  176. }
  177. trainAbs=Math.sqrt(trainAbs);*/
  178. returnmul;///(testAbs*trainAbs);
  179. }
  180. /**获取kmeans算法迭代的初始点
  181. *@paramk聚类的数量
  182. *@paramMap<String,Map<String,Double>>allTestSampleMap所有测试样例的<文件名,向量>构成的map
  183. *@returnMap<Integer,Map<String,Double>>初始中心点的Map
  184. *@throwsIOException
  185. */
  186. privateMap<Integer,Map<String,Double>>getInitPoint(Map<String,Map<String,Double>>allTestSampleMap,intK){
  187. //TODOAuto-generatedmethodstub
  188. intcount=0,i=0;
  189. Map<Integer,Map<String,Double>>meansMap=newTreeMap<Integer,Map<String,Double>>();//保存K个聚类中心点向量
  190. System.out.println("本次聚类的初始点对应的文件为:");
  191. Set<Map.Entry<String,Map<String,Double>>>allTestSampleMapSet=allTestSampleMap.entrySet();
  192. for(Iterator<Map.Entry<String,Map<String,Double>>>it=allTestSampleMapSet.iterator();it.hasNext();){
  193. Map.Entry<String,Map<String,Double>>me=it.next();
  194. if(count==i*allTestSampleMapSet.size()/K){
  195. meansMap.put(i,me.getValue());
  196. System.out.println(me.getKey()+"mapsizeis"+me.getValue().size());
  197. i++;
  198. }
  199. count++;
  200. }
  201. returnmeansMap;
  202. }
  203. /**输出聚类结果到文件中
  204. *@paramkmeansClusterResultFile输出文件目录
  205. *@paramkmeansClusterResult聚类结果
  206. *@throwsIOException
  207. */
  208. privatevoidprintClusterResult(Map<String,Integer>kmeansClusterResult,StringkmeansClusterResultFile)throwsIOException{
  209. //TODOAuto-generatedmethodstub
  210. FileWriterresWriter=newFileWriter(kmeansClusterResultFile);
  211. Set<Map.Entry<String,Integer>>kmeansClusterResultSet=kmeansClusterResult.entrySet();
  212. for(Iterator<Map.Entry<String,Integer>>it=kmeansClusterResultSet.iterator();it.hasNext();){
  213. Map.Entry<String,Integer>me=it.next();
  214. resWriter.append(me.getKey()+""+me.getValue()+"\n");
  215. }
  216. resWriter.flush();
  217. resWriter.close();
  218. }
  219. publicvoidKmeansClusterMain(StringtestSampleDir)throwsIOException{
  220. //首先计算文档TF-IDF向量,保存为Map<String,Map<String,Double>>即为Map<文件名,Map<特征词,TF-IDF值>>
  221. ComputeWordsVectorcomputeV=newComputeWordsVector();
  222. int[]K={10,20,30};
  223. Map<String,Map<String,Double>>allTestSampleMap=computeV.computeTFMultiIDF(testSampleDir);
  224. for(inti=0;i<K.length;i++){
  225. System.out.println("开始聚类,聚成"+K[i]+"类");
  226. StringKmeansClusterResultFile="F:/DataMiningSample/KmeansClusterResult/";
  227. Map<String,Integer>KmeansClusterResult=newTreeMap<String,Integer>();
  228. KmeansClusterResult=doProcess(allTestSampleMap,K[i]);
  229. KmeansClusterResultFile+=K[i];
  230. printClusterResult(KmeansClusterResult,KmeansClusterResultFile);
  231. System.out.println("TheEntropyforthisClusteris"+computeV.evaluateClusterRes(KmeansClusterResultFile,K[i]));
  232. }
  233. }
  234. }
聚类器主类ClusterMain.java

[java] view plain copy 在CODE上查看代码片 派生到我的代码片
  1. packagecom.pku.yangliu;
  2. importjava.io.IOException;
  3. importjava.text.SimpleDateFormat;
  4. /**聚类器主类,提供主函数入口
  5. *
  6. */
  7. publicclassClusterMain{
  8. /**
  9. *@paramargs
  10. *@throwsIOException
  11. */
  12. publicstaticvoidmain(String[]args)throwsIOException{
  13. //TODOAuto-generatedmethodstub
  14. DataPreProcessDataPP=newDataPreProcess();
  15. ComputeWordsVectorcomputeV=newComputeWordsVector();
  16. //KmeansSVDClusterkmeansCluster1=newKmeansSVDCluster();
  17. KmeansClusterkmeansCluster2=newKmeansCluster();
  18. DataPP.BPPMain(args);//数据预处理,注意如果已经完成数据预处理,此函数可以不执行
  19. //下面创建聚类算法的测试样例集合
  20. StringsrcDir="F:/DataMiningSample/processedSample_includeNotSpecial/";
  21. StringdestDir="F:/DataMiningSample/clusterTestSample/";
  22. SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddHH:mm:ss");
  23. StringbeginTime=sdf.format(newjava.util.Date());
  24. System.out.println("程序开始执行时间:"+beginTime);
  25. String[]terms=computeV.createTestSamples(srcDir,destDir);
  26. //kmeansCluster1.KmeansClusterMain(destDir,terms);
  27. kmeansCluster2.KmeansClusterMain(destDir);
  28. StringendTime=sdf.format(newjava.util.Date());
  29. System.out.println("程序结束执行时间:"+endTime);
  30. }
  31. }
3、K-means算法聚类结果

K-means算法对newsgroup文本聚类的结果用聚类结果的熵值来度量,熵值定义如下


对newsgroup文本聚类的结果混淆矩阵如下:


这是用DF法降维到6070词的聚类结果,熵值已经比较小了聚20类时只有1.144,特征词抽取降维是数据挖掘研究中的一个重要内容,我还尝试了用LSI中的SVD分解来进行特征降维,详细介绍实现和其他两种聚类算法的聚类结果对比见下一篇博文数据挖掘-基于Kmeans算法、MBSAS算法及DBSCAN算法的newsgroup18828文本聚类器的JAVA实现(下)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值