聚类算法之kmeans算法java版本
2014-04-05 02:32
351 查看
聚类的意思很明确,物以类聚,把类似的事物放在一起。
聚类算法是web智能中很重要的一步,可运用在社交,新闻,电商等各种应用中,我打算专门开个分类讲解聚类各种算法的java版实现。
首先介绍kmeans算法。
kmeans算法的速度很快,性能良好,几乎是应用最广泛的,它需要先指定聚类的个数k,然后根据k值来自动分出k个类别集合。
举个例子,某某教练在得到全队的数据后,想把这些球员自动分成不同的组别,你得问教练需要分成几个组,他回答你k个,ok可以开始了,在解决这个问题之前有必要详细了解自己需要达到的目的:根据教练给出的k值,呈现出k个组,每个组的队员是相似的。
首先,我们创建球员类。
viewsource
print?
@KmeanField这个注解是自定义的,用来标示这个属性是否是算法需要的维度。
代码如下
viewsource
print?
接下来看看最核心的kmeans算法,具体实现过程如下:
1,初始化k个聚类中心
2,计算出每个对象跟这k个中心的距离(相似度计算,这个下面会提到),假如x这个对象跟y这个中心的距离最小(相似度最大),那么x属于y这个中心。这一步就可以得到初步的k个聚类
3,在第二步得到的每个聚类分别计算出新的聚类中心,和旧的中心比对,假如不相同,则继续第2步,直到新旧两个中心相同,说明聚类不可变,已经成功
实现代码如下:
viewsource
print?
最后咱们测试一下:
viewsource
print?
结果如下
这个里面涉及到相似度算法,事实证明欧几里得距离算法的实践效果是最优的。
最后说说kmeans算法的不足:可以看到只能针对数字类型的属性(维),对于其他类型的除非选定合适的数值度量
聚类算法是web智能中很重要的一步,可运用在社交,新闻,电商等各种应用中,我打算专门开个分类讲解聚类各种算法的java版实现。
首先介绍kmeans算法。
kmeans算法的速度很快,性能良好,几乎是应用最广泛的,它需要先指定聚类的个数k,然后根据k值来自动分出k个类别集合。
举个例子,某某教练在得到全队的数据后,想把这些球员自动分成不同的组别,你得问教练需要分成几个组,他回答你k个,ok可以开始了,在解决这个问题之前有必要详细了解自己需要达到的目的:根据教练给出的k值,呈现出k个组,每个组的队员是相似的。
首先,我们创建球员类。
01 | package kmeans; |
02 |
03 | /** |
04 | *球员 |
05 | * |
06 | *@author阿飞哥 |
07 | * |
08 | */ |
09 | public class Player{ |
10 |
11 | private int id; |
12 | private Stringname; |
13 |
14 | private int age; |
15 |
16 | /*得分*/ |
17 | @KmeanField |
18 | privatedoublegoal; |
19 |
20 | /*助攻*/ |
21 | //@KmeanField |
22 | privatedoubleassists; |
23 |
24 | /*篮板*/ |
25 | //@KmeanField |
26 | privatedoublebackboard; |
27 |
28 | /*抢断*/ |
29 | //@KmeanField |
30 | private double steals; |
31 |
32 | public int getId(){ |
33 | return id; |
34 | } |
35 |
36 | public void setId( int id){ |
37 | this .id=id; |
38 | } |
39 |
40 | public StringgetName(){ |
41 | return name; |
42 | } |
43 |
44 | public void setName(Stringname){ |
45 | this .name=name; |
46 | } |
47 |
48 | public int getAge(){ |
49 | return age; |
50 | } |
51 |
52 | public void setAge( int age){ |
53 | this .age=age; |
54 | } |
55 |
56 | public double getGoal(){ |
57 | return goal; |
58 | } |
59 |
60 | public void setGoal( double goal){ |
61 | this .goal=goal; |
62 | } |
63 |
64 | public double getAssists(){ |
65 | return assists; |
66 | } |
67 |
68 | public void setAssists( double assists){ |
69 | this .assists=assists; |
70 | } |
71 |
72 | public double getBackboard(){ |
73 | return backboard; |
74 | } |
75 |
76 | public void setBackboard( double backboard){ |
77 | this .backboard=backboard; |
78 | } |
79 |
80 | public double getSteals(){ |
81 | return steals; |
82 | } |
83 |
84 | public void setSteals( double steals){ |
85 | this .steals=steals; |
86 | } |
87 |
88 |
89 | } |
代码如下
01 | package kmeans; |
02 |
03 | import
|
04 | import
|
05 | import
|
06 | import
|
07 |
08 | /** |
09 | *在对象的属性上标注此注释, |
10 | *表示纳入kmeans算法,仅支持数值类属性 |
11 | *@author阿飞哥 |
12 | */ |
13 | @Retention (RetentionPolicy.RUNTIME) |
14 | @Target (ElementType.FIELD) |
15 | public @interface KmeanField{ |
16 | } |
1,初始化k个聚类中心
2,计算出每个对象跟这k个中心的距离(相似度计算,这个下面会提到),假如x这个对象跟y这个中心的距离最小(相似度最大),那么x属于y这个中心。这一步就可以得到初步的k个聚类
3,在第二步得到的每个聚类分别计算出新的聚类中心,和旧的中心比对,假如不相同,则继续第2步,直到新旧两个中心相同,说明聚类不可变,已经成功
实现代码如下:
001 | package kmeans; |
002 |
003 | import
|
004 | import
|
005 | import
|
006 | import
|
007 | import
|
008 |
009 | /** |
010 | * |
011 | *@author阿飞哥 |
012 | * |
013 | */ |
014 | public class Kmeans<T>{ |
015 |
016 | /** |
017 | *所有数据列表 |
018 | */ |
019 | private List<T>players= new ArrayList<T>(); |
020 |
021 | /** |
022 | *数据类别 |
023 | */ |
024 | private Class<T>classT; |
025 |
026 | /** |
027 | *初始化列表 |
028 | */ |
029 | private List<T>initPlayers; |
030 |
031 | /** |
032 | *需要纳入kmeans算法的属性名称 |
033 | */ |
034 | private List<String>fieldNames= new ArrayList<String>(); |
035 |
036 | /** |
037 | *分类数 |
038 | */ |
039 | private int k= 1 ; |
040 |
041 | public Kmeans(){ |
042 |
043 | } |
044 |
045 | /** |
046 | *初始化列表 |
047 | * |
048 | *@paramlist |
049 | *@paramk |
050 | */ |
051 | public Kmeans(List<T>list, int k){ |
052 | this .players=list; |
053 | this .k=k; |
054 | Tt=list.get( 0 ); |
055 | this .classT=(Class<T>)t.getClass(); |
056 | Field[]fields= this .classT.getDeclaredFields(); |
057 | for ( int
0 ;i<fields.length;i++){ |
058 | AnnotationkmeansAnnotation=fields[i] |
059 | .getAnnotation(KmeanField. class ); |
060 | if (kmeansAnnotation!= null ){ |
061 | fieldNames.add(fields[i].getName()); |
062 | } |
063 |
064 | } |
065 |
066 | initPlayers= new ArrayList<T>(); |
067 | for ( int
0 ;i<k;i++){ |
068 | initPlayers.add(players.get(i)); |
069 | } |
070 | } |
071 |
072 | public List<T>[]comput(){ |
073 | List<T>[]results= new ArrayList[k]; |
074 |
075 | boolean centerchange= true ; |
076 | while (centerchange){ |
077 | centerchange= false ; |
078 | for ( int
0 ;i<k;i++){ |
079 | results[i]= new ArrayList<T>(); |
080 | } |
081 | for ( int
0 ;i<players.size();i++){ |
082 | Tp=players.get(i); |
083 | double []dists= new double [k]; |
084 | for ( int
0 ;j<initPlayers.size();j++){ |
085 | TinitP=initPlayers.get(j); |
086 | /*计算距离*/ |
087 | doubledist=distance(initP,p); |
088 | dists[j]=dist; |
089 | } |
090 |
091 | intdist_index=computOrder(dists); |
092 | results[dist_index].add(p); |
093 | } |
094 |
095 | for(inti=0;i<k;i++){ |
096 | Tplayer_new=findNewCenter(results[i]); |
097 | Tplayer_old=initPlayers.get(i); |
098 | if(!IsPlayerEqual(player_new,player_old)){ |
099 | centerchange=true; |
100 | initPlayers.set(i,player_new); |
101 | } |
102 |
103 | } |
104 |
105 | } |
106 |
107 | returnresults; |
108 | } |
109 |
110 | /** |
111 | *比较是否两个对象是否属性一致 |
112 | * |
113 | *@paramp1 |
114 | *@paramp2 |
115 | *@return |
116 | */ |
117 | publicbooleanIsPlayerEqual(Tp1,Tp2){ |
118 | if(p1==p2){ |
119 | returntrue; |
120 | } |
121 | if(p1==null||p2==null){ |
122 | returnfalse; |
123 | } |
124 |
125 |
126 |
127 | booleanflag=true; |
128 | try{ |
129 | for(inti=0;i<fieldNames.size();i++){ |
130 | StringfieldName=fieldNames.get(i); |
131 | StringgetName="get" |
132 | +fieldName.substring(0,1).toUpperCase() |
133 | +fieldName.substring(1); |
134 | Objectvalue1=invokeMethod(p1,getName,null); |
135 | Objectvalue2=invokeMethod(p2,getName,null); |
136 | if(!value1.equals(value2)){ |
137 | flag=false; |
138 | break; |
139 | } |
140 | } |
141 | }catch(Exceptione){ |
142 | e.printStackTrace(); |
143 | flag=false; |
144 | } |
145 |
146 | returnflag; |
147 | } |
148 |
149 | /** |
150 | *得到新聚类中心对象 |
151 | * |
152 | *@paramps |
153 | *@return |
154 | */ |
155 | publicTfindNewCenter(List<T>ps){ |
156 | try{ |
157 | Tt=classT.newInstance(); |
158 | if(ps==null||ps.size()==0){ |
159 | returnt; |
160 | } |
161 |
162 | double[]ds=newdouble[fieldNames.size()]; |
163 | for(Tvo:ps){ |
164 | for(inti=0;i<fieldNames.size();i++){ |
165 | StringfieldName=fieldNames.get(i); |
166 | StringgetName="get" |
167 | +fieldName.substring(0,1).toUpperCase() |
168 | +fieldName.substring(1); |
169 | Objectobj=invokeMethod(vo,getName,null); |
170 | Doublefv=(obj==null?0:Double.parseDouble(obj+"")); |
171 | ds[i]+=fv; |
172 | } |
173 |
174 | } |
175 |
176 | for(inti=0;i<fieldNames.size();i++){ |
177 | ds[i]=ds[i]/ps.size(); |
178 | StringfieldName=fieldNames.get(i); |
179 |
180 | /*给对象设值*/ |
181 | StringsetName="set" |
182 | +fieldName.substring(0,1).toUpperCase() |
183 | +fieldName.substring(1); |
184 |
185 | invokeMethod(t,setName,newClass[]{double.class},ds[i]); |
186 |
187 | } |
188 |
189 | returnt; |
190 | }catch(Exceptionex){ |
191 | ex.printStackTrace(); |
192 | } |
193 | returnnull; |
194 |
195 | } |
196 |
197 | /** |
198 | *得到最短距离,并返回最短距离索引 |
199 | * |
200 | *@paramdists |
201 | *@return |
202 | */ |
203 | publicintcomputOrder(double[]dists){ |
204 | doublemin=0; |
205 | intindex=0; |
206 | for(inti=0;i<dists.length-1;i++){ |
207 | doubledist0=dists[i]; |
208 | if(i==0){ |
209 | min=dist0; |
210 | index=0; |
211 | } |
212 | doubledist1=dists[i+1]; |
213 | if(min>dist1){ |
214 | min=dist1; |
215 | index=i+1; |
216 | } |
217 | } |
218 |
219 | returnindex; |
220 | } |
221 |
222 | /** |
223 | *计算距离(相似性)采用欧几里得算法 |
224 | * |
225 | *@paramp0 |
226 | *@paramp1 |
227 | *@return |
228 | */ |
229 | publicdoubledistance(Tp0,Tp1){ |
230 | doubledis=0; |
231 | try{ |
232 |
233 | for(inti=0;i<fieldNames.size();i++){ |
234 | StringfieldName=fieldNames.get(i); |
235 | StringgetName="get" |
236 | +fieldName.substring(0,1).toUpperCase() |
237 | +fieldName.substring(1); |
238 |
239 | Doublefield0Value=Double.parseDouble(invokeMethod(p0,getName,null)+""); |
240 | Doublefield1Value=Double.parseDouble(invokeMethod(p1,getName,null)+""); |
241 | dis+=Math.pow(field0Value-field1Value,2); |
242 | } |
243 |
244 | }catch(Exceptionex){ |
245 | ex.printStackTrace(); |
246 | } |
247 | returnMath.sqrt(dis); |
248 |
249 | } |
250 |
251 | /*------公共方法-----*/ |
252 | public ObjectinvokeMethod(Objectowner,StringmethodName,Class[]argsClass, |
253 | Object...args){ |
254 | ClassownerClass=owner.getClass(); |
255 | try { |
256 | Methodmethod=ownerClass.getDeclaredMethod(methodName,argsClass); |
257 | return method.invoke(owner,args); |
258 | } catch (SecurityExceptione){ |
259 | e.printStackTrace(); |
260 | } catch (NoSuchMethodExceptione){ |
261 | e.printStackTrace(); |
262 | } catch (Exceptionex){ |
263 | ex.printStackTrace(); |
264 | } |
265 |
266 | return null ; |
267 | } |
268 |
269 | } |
01 | package kmeans; |
02 |
03 | import
|
04 | import
|
05 | import
|
06 |
07 | public class TestMain{ |
08 |
09 | public static void
|
10 | List<Player>listPlayers= new ArrayList<Player>(); |
11 |
12 | for ( int i= 0 ;i< 15 ;i++){ |
13 |
14 | Playerp1= new Player(); |
15 | p1.setName( "afei-" +i); |
16 | p1.setAssists(i); |
17 | p1.setBackboard(i); |
18 |
19 | //p1.setGoal(newRandom(100*i).nextDouble()); |
20 | p1.setGoal(i* 10 ); |
21 | p1.setSteals(i); |
22 | //listPlayers.add(p1); |
23 | } |
24 |
25 | Playerp1= new Player(); |
26 | p1.setName( "afei1" ); |
27 | p1.setGoal( 1 ); |
28 | p1.setAssists( 8 ); |
29 | listPlayers.add(p1); |
30 |
31 | Playerp2= new Player(); |
32 | p2.setName( "afei2" ); |
33 | p2.setGoal( 2 ); |
34 | listPlayers.add(p2); |
35 |
36 | Playerp3= new Player(); |
37 | p3.setName( "afei3" ); |
38 | p3.setGoal( 3 ); |
39 | listPlayers.add(p3); |
40 |
41 | Playerp4= new Player(); |
42 | p4.setName( "afei4" ); |
43 | p4.setGoal( 7 ); |
44 | listPlayers.add(p4); |
45 |
46 | Playerp5= new Player(); |
47 | p5.setName( "afei5" ); |
48 | p5.setGoal( 8 ); |
49 | listPlayers.add(p5); |
50 |
51 | Playerp6= new Player(); |
52 | p6.setName( "afei6" ); |
53 | p6.setGoal( 25 ); |
54 | listPlayers.add(p6); |
55 |
56 | Playerp7= new Player(); |
57 | p7.setName( "afei7" ); |
58 | p7.setGoal( 26 ); |
59 | listPlayers.add(p7); |
60 |
61 | Playerp8= new Player(); |
62 | p8.setName( "afei8" ); |
63 | p8.setGoal( 27 ); |
64 | listPlayers.add(p8); |
65 |
66 | Playerp9= new Player(); |
67 | p9.setName( "afei9" ); |
68 | p9.setGoal( 28 ); |
69 | listPlayers.add(p9); |
70 |
71 |
72 | Kmeans<Player>kmeans= new Kmeans<Player>(listPlayers, 3 ); |
73 | List<Player>[]results=kmeans.comput(); |
74 | for ( int
0 ;i<results.length;i++){ |
75 | System.out.println( "===========类别" +(i+ 1 )+ "================" ); |
76 | List<Player>list=results[i]; |
77 | for (Playerp:list){ |
78 | System.out.println(p.getName()+ "--->" |
79 | +p.getGoal()+ "," +p.getAssists()+ "," |
80 | +p.getSteals()+ "," +p.getBackboard()); |
81 | } |
82 | } |
83 |
84 |
85 |
86 |
87 | } |
88 |
89 | } |
这个里面涉及到相似度算法,事实证明欧几里得距离算法的实践效果是最优的。
最后说说kmeans算法的不足:可以看到只能针对数字类型的属性(维),对于其他类型的除非选定合适的数值度量
相关文章推荐
- 解决IDEA自动重置LanguageLevel和JavaCompiler版本的问题
- 如何查找安装的 Java 版本(而不在 Windows 或 Mac 中运行小应用程序)?
- java版本的神经网络——开源框架JOONE实践
- 下压堆栈的链表实现 java版本
- 递归分治算法之二分搜索(Java版本)
- poi读取excel表格类型转换问题(java读取excel2007--2010版本)
- Maven中指定Java的编译版本和源文件编码方式
- Error:(142, 34) java: -source 1.6 中不支持 multi-catch 语句 (请使用 -source 7 或更高版本以启用 multi-catch 语句)
- leveldb java 版本
- 修改Eclipse对应的java版本
- java 中 poi解析Excel文件版本问题解决办法
- 版本问题:java.lang.UnsupportedClassVersionError: Bad version number in .class file
- 版本问题 Java:Unsupported major.minor version 51.0 (unable to load class . . .
- Java 平台的多个版本
- 查看Linux、Tomcat、JAVA版本信息
- 解决IDEA自动重置LanguageLevel和JavaCompiler版本的问题
- 自定义比较器JAVA和C#版本
- 修改jdk环境变量后,java版本不变 java -version
- 毛超帅一JAVA:JDK各版本分享与环境配置(java1)______软件开发-Tomcat
- 小叮咚 中文分词 发布 java 版本 , c# 版本 , c++ 版本