K-均值算法

总是有废话要先说~~

二妹原创,转载请注明出处,大家讨论~

上次面试,写自我评价的缺点,我写的比较胖~~然后拿offer了,人啊 就是要看得见的实诚!

 

----------------------------------------我是废话分割线 -------------------------------------------------------------

 

web上大量的数据,希望对这些数据进行聚类,而事先并不知道该怎么聚类,k-均值算法则是将大数据聚为k类。

关键要素:

1:用户事先要确定K的值,这个可能需要大量的测试优化k值。k值代表将数据聚为k类,那么将会产生k个簇的质心(均值所在的位置)。

2:聚类的的数据节点之间必须是可以计算平均值。K均值的思想就是某一堆数据都在一个均值的附近,那么将这一堆数据划为一簇。

3:适用于球状分布的数据。例如如果数据是环形分布的,直观上应该将同一环形的数据归为一类,而用k均值则可能分为扇形簇。

4:初始的质心选择得不同,则得到的聚类也会不同。

例如如下图,当k为3的时候,下面红黄蓝是正确的分类,使用k均值算法可以正确的分类第一类,而第二类则会出现错误。

 

以Person为例,现在有一堆人的集合,将他们分为三类,按年龄相近来分类。

 

Person对象,相当于集合中的节点。

package com.zyp.learn.datamining.algorithm;

public class Person {
	private int age;
	private String name;
	
	public Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

 

算法实现类:

    package com.zyp.learn.datamining.algorithm;  
      
    import java.util.ArrayList;  
    import java.util.HashMap;  
    import java.util.HashSet;  
    import java.util.List;  
    import java.util.Map;  
    import java.util.Set;  
      
      
    /** 
     * K均值算法 
     * @author 卓二妹 2012/10/31 
     * 
     */  
    public class KAverageAlgorithm {  
          
        public static void main(String[] args){  
            //生成集合,年龄随机生成0-99的数字  
//            Set<Person> personSet= initSet(5);  
    
            //生成固定值集合,便于调试        	 
               Set<Person> personSet= initSet();  
              
            System.out.print("集合元素:");  
            for(Person p : personSet){  
                System.out.print(p.getAge()+" ");  
            }  
            System.out.println();  
            // 调用无监督学习的k均值算法,将这50人按年龄分为3类,如老、中、青  
            Person[] centerArray = cluster(3,personSet);   
              
            System.out.print("质心:");  
            for(Person center : centerArray){  
                System.out.print(center.getAge()+" ");  
            }  
              
            //标记元素所属质心  
            Map<String,List> map = new HashMap();  
              
            //集合中的某个元素所属的质心,加到该质心对应的数组上  
            for(Person p: personSet){  
                int index = nearestIndex(centerArray,p);//p属于centerArray[index]质心  
                List<Person> lst = map.get(centerArray[index].getAge()+"");  
                if(lst==null){  
                    lst = new ArrayList();  
                    map.put(centerArray[index].getAge()+"",lst);  
                }  
                lst.add(p);  
            }  
              
            System.out.println("\n质心包含的元素:");  
            for(Person center : centerArray){  
                System.out.print(center.getAge()+" :");  
                List<Person> lst = map.get(center.getAge()+"");  
                if(lst!=null){  
                    for(Person p : lst){  
                        System.out.print(p.getAge()+" ");  
                    }  
                }  
                System.out.println("\n");  
            }  
        }  
          
        /** 
         * 将person集合分类,按照person的年龄维度来分类。 
         * 分为k个年龄段,而每个年龄段是事前并不知道。 
         * 用k-均值算法的硬盘版本,只需要遍历一次全集。 
         * @param k:簇的数量 
         * @param personSet:要划分簇的全集 
         * @return 质心的数组 
         */  
        public static Person[] cluster(int k,final Set<Person> personSet){  
            Person[] centerArray = new Person[k];   
            //由于集合是无序的,因此在集合中选前k个点做初始的质心(任意k个点)  
            int i = 0;  
            for(Person p:personSet){  
                centerArray[i++] = p;  
                if(i==k){  
                    break;  
                }  
            }  
              
            //进行一遍执行的调整,本次调整中,改变了所属簇的节点的数量为chanedPointNum  
            int chanedPointNum = -1;  
            //直到没有任何person所属簇都不再变化后,结束分类  
            boolean isExit = false;
            while(isExit){  
            	Person[] newCenterArray = adjustCenteroid(centerArray,personSet);  
            	isExit = exit(newCenterArray,centerArray,personSet);
            }  
              
            return centerArray;  
        }  
          
        /** 
         * 进行一遍质心的调整 
         * @param centerArray 质心数组 
         * @param personSet 全集 
         * return 返回本轮调整质心,改变了所属簇的Person的个数 
         */  
        public static Person[] adjustCenteroid(final Person[] centerArray,final Set<Person> personSet){  
            int changedPointNum = 0;//本次调整质心,改变了所属的簇的节点的个数  
            int oldIndex = -1;  
            int newIndex = -1;  
            int k = centerArray.length;  
            //distArray[i]代表该划划分到第i类的年龄的总和  
            int[] distArray = new int[k];  
              
            //numberArray[i]代表划分到第i类的年龄有多少个  
            int[] numberArray = new int[k];  
              
            //待返回的新质心
            Person[] newCenterArray  = new Person[k];
            
            //将数组元素初始化为0  
            initArray(distArray);  
            initArray(numberArray);  
              
            for(Person tempPerson:personSet){  
                //计算该tempPerson到centerArray的第index质心最近  
                int index = nearestIndex(centerArray,tempPerson);  
                //则该tempPerson被划为第index质心代表的簇中  
                distArray[index] = distArray[index] + tempPerson.getAge();  
                numberArray[index] ++;  
            }  
              
            //已将每个元素分到k个质心中,重新计算每个簇的均值,得到新的质心  
            for(int index = 0; index<k;index++){  
            	newCenterArray[index] = new Person("",distArray[index]/numberArray[index]);
            }  
            return newCenterArray;  
        }  
        
        /**
         * 质心调整的退出标准 
         * @param centerArray当前的质心
         * @param oldCenterArray 调整前的质心
         * @param personSet 数据集合
         * @return 数据集合的簇在质心调整前后无变化,则返回true
         */
        public static boolean exit(final Person[] centerArray,final Person[] oldCenterArray,final Set<Person> personSet){
        	boolean isExit = false;
        	int changedPointNum = 0;
            
            //遍历person集合,看质心调整后,person的所属簇是否改变  
            for(Person tempPerson:personSet){  
                int newIndex = nearestIndex(centerArray,tempPerson);  
                int oldIndex = nearestIndex(oldCenterArray,tempPerson);  
                if(oldIndex!=newIndex){  
                    changedPointNum++;  
                }
            } 
            if(changedPointNum==0){
            	isExit = true;
            }
        	return isExit;
        }  
        public static void initArray(int array[]){  
            for(int i = 0;i<array.length;i++){  
                array[i] = 0;  
            }  
        }  
          
        public static int nearestIndex(final Person[] centerArray,final Person tempPerson){  
            int minIndex = 0;//与该点距离最近的质心的index  
            int minValue = 100;//与最近的质心的距离  
              
            for(int i = 0;i<centerArray.length;i++){  
                int centerAge = centerArray[i].getAge();  
                int temp = Math.abs(tempPerson.getAge()-centerAge);//数据点的年龄与质心距离  
                if(temp<=minValue){  
                    minValue = temp;//  
                    minIndex = i;  
                }  
            }  
            return minIndex;  
        }  
          
      
          
        /** 
         * 初始化集合中的数据 
         * @param count:需要生成的集合的元素个数 
         * @return 
         */  
        public static Set<Person> initSet(int count){  
            Set<Person> set = new HashSet();  
    //      System.out.print("初始集合:");  
            for(int i=0;i<count;i++){  
                String name = "person"+i;  
                //随即生成0--99的人的年龄  
                int age = (int)(Math.random()*100);  
    //          System.out.print(age+" ");  
                set.add(new Person(name,age));  
            }  
            System.out.println();  
            return set;  
        }  
          
        /** 
         * 初始化集合中的数据,指定一些出错的数据,便于调试 
         * @return 
         */  
        public static Set<Person> initSet(){  
            Set<Person> set = new HashSet();  
            set.add(new Person("", 69));
            set.add(new Person("", 30));
            set.add(new Person("", 75));
            set.add(new Person("", 12));
            set.add(new Person("", 14));
            return set;  
        } 
    }  
 

运行一下main方法,随机生成5个人,按年龄分为3类,打印结果如下:


集合元素:75 69 14 12 30
质心:75 69 14
质心包含的元素:
75 :75

69 :69

14 :14 12 30

 

二天空了再来解释算法,敬请期待~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值