zoukankan      html  css  js  c++  java
  • 数据挖掘之KMeans算法应用与简单理解

    一、背景

    煤矿地磅产生了一系列数据:

    我想从这些数据中,取出最能反映当前车辆重量的数据(有很多数据是车辆上磅过程中产生的数据)。我于是想到了聚类算法KMeans,该算法思想比较简单。

    二、算法步骤

    1、从样本中随机取出k个值,作为初始中心

    2、以k个中心划分这些数据,分为k个组

    3、重新计算出每个组的中心,作为新中心

    4、如果初始中心和新中心不相等,则把新中心作为初始中心,重复2,3。反之,结束

    注意:

    1、我没有用严格的算法定义,怕不好理解

    2、KMeans善于处理球形数据,因此随机取k个质心,每个质心吸引离它最近的数据

    3、由于质心的取值是不科学的,所以需要不断地计算调整,直到质心名副其实

    三、算法分析及特点

    1、从算法步骤当中可以看出有两个问题,需要解决:

       首先,如何计算每个组(簇)的质心?

       其次,如何把值划分到不同的组?

    2、解决上面两个问题,因场景和要求不同而有不同的小算法,由于我的数据是一维的,而不是点,所以可以简单处理:

       a、以每个组的平均值作为质心

       b、根据值离质心的距离(相减),选择距离最近的组加入

    3、此算法有两个缺点:

       1)某个组(簇)划分不充分,还可以再划分为更小的组。(容易陷入局部最优)

       2)需要用户指定k,聚类结果对初始质心的选择较为敏感(初始选择不同,聚类结果可能不同)

    4、优点:简单易理解和上手

    四、实现

        public class KMeans
        {
            /*
            * 聚类函数主体。
            * 针对一维 decimal 数组。指定聚类数目 k。
            * 将数据聚成 k 类。
            */
            public static decimal[][] cluster(decimal[] p, int k)
            {
                // 存放聚类旧的聚类中心
                decimal[] c = new decimal[k];
                // 存放新计算的聚类中心
                decimal[] nc = new decimal[k];
                // 存放放回结果
                decimal[][] g;
                // 初始化聚类中心
                // 经典方法是随机选取 k 个
                // 本例中采用前 k 个作为聚类中心
                // 聚类中心的选取不影响最终结果
                for (int i = 0; i < k; i++)
                    c[i] = p[i];
                // 循环聚类,更新聚类中心
                // 到聚类中心不变为止
                while (true)
                {
                    // 根据聚类中心将元素分类
                    g = group(p, c);
                    // 计算分类后的聚类中心
                    for (int i = 0; i < g.Length; i++)
                    {
                        nc[i] = center(g[i]);
                    }
                    // 如果聚类中心不同
                    if (!equal(nc, c))
                    {
                        c = nc;
                        nc = new decimal[k];
                    }
                    else
                        break;
                }
                return g;
            }
            /*
             * 聚类中心函数
             * 简单的一维聚类返回其算数平均值
             * 可扩展
             */
            public static decimal center(decimal[] p)
            {
                if (p.Length == 0) return 0;
                return sum(p) / p.Length;
            }
            /*
             * 给定 decimal 型数组 p 和聚类中心 c。
             * 根据 c 将 p 中元素聚类。返回二维数组。
             * 存放各组元素。
             */
            public static decimal[][] group(decimal[] p, decimal[] c)
            {
                // 中间变量,用来分组标记
                int[] gi = new int[p.Length];
                // 考察每一个元素 pi 同聚类中心 cj 的距离
                // pi 与 cj 的距离最小则归为 j 类
                for (int i = 0; i < p.Length; i++)
                {
                    // 存放距离
                    decimal[] d = new decimal[c.Length];
                    // 计算到每个聚类中心的距离
                    for (int j = 0; j < c.Length; j++)
                    {
                        d[j] = distance(p[i], c[j]);
                    }
                    // 找出最小距离
                    int ci = min(d);
                    // 标记属于哪一组
                    gi[i] = ci;
                }
                // 存放分组结果
                decimal[][] g = new decimal[c.Length][];
                // 遍历每个聚类中心,分组
                for (int i = 0; i < c.Length; i++)
                {
                    // 中间变量,记录聚类后每一组的大小
                    int s = 0;
                    // 计算每一组的长度
                    for (int j = 0; j < gi.Length; j++)
                        if (gi[j] == i)
                            s++;
                    // 存储每一组的成员
                    g[i] = new decimal[s];
                    s = 0;
                    // 根据分组标记将各元素归位
                    for (int j = 0; j < gi.Length; j++)
                        if (gi[j] == i)
                        {
                            g[i][s] = p[j];
                            s++;
                        }
                }
                // 返回分组结果
                return g;
            }
    
            /*
             * 计算两个点之间的距离, 这里采用最简单得一维欧氏距离, 可扩展。
             */
            public static decimal distance(decimal x, decimal y)
            {
                return Math.Abs(x - y);
            }
    
            /*
             * 返回给定 decimal 数组各元素之和。
             */
            public static decimal sum(decimal[] p)
            {
                decimal sum = 0.0M;
                for (int i = 0; i < p.Length; i++)
                    sum += p[i];
                return sum;
            }
    
            /*
             * 给定 decimal 类型数组,返回最小值得下标。
             */
            public static int min(decimal[] p)
            {
                int i = 0;
                decimal m = p[0];
                for (int j = 1; j < p.Length; j++)
                {
                    if (p[j] < m)
                    {
                        i = j;
                        m = p[j];
                    }
                }
                return i;
            }
    
            /*
             * 判断两个 decimal 数组是否相等。 长度一样且对应位置值相同返回真。
             */
            public static bool equal(decimal[] a, decimal[] b)
            {
                if (a.Length != b.Length)
                    return false;
                else
                {
                    for (int i = 0; i < a.Length; i++)
                    {
                        if (a[i] != b[i])
                            return false;
                    }
                }
                return true;
            }
        }

    客户端调用:

     1        static void Main(string[] args)
     2         {
     3             var path = string.Empty;
     4             int k = 0;
     5             try
     6             {
     7                 path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "blanceTest.txt");//数据文件路径
     8                 k = 4;
     9             }
    10             catch (Exception)
    11             {
    12                 Console.Write("参数错误");
    13                 return;
    14             }
    15 
    16             decimal[] p = { 1, 2, 3, 5, 6, 7, 9, 10, 11, 20, 21, 22, 23, 27, 40, 41, 42, 43, 61, 62, 63, 100, 150, 200, 1000 };
    17 
    18             List<decimal> pList = new List<decimal>();
    19 
    20             var lines = File.ReadAllLines(path);
    21 
    22             foreach (var line in lines)
    23             {
    24                 var data = System.Text.RegularExpressions.Regex.Replace(line, @" +", " ");
    25                 var datas = data.Split(' ');
    26 
    27                 pList.AddRange(datas.Where(d => d != "").Select(d => Convert.ToDecimal(d)));
    28             }
    29 
    30             p = pList.ToArray();
    31 
    32             k = 5;
    33             decimal[][] g;
    34             g = KMeans.cluster(p, k);
    35             for (int i = 0; i < g.Length; i++)
    36             {
    37                 for (int j = 0; j < g[i].Length; j++)
    38                 {
    39                     Console.WriteLine(g[i][j]);
    40                 }
    41                 Console.WriteLine("----------------------");
    42             }
    43             Console.ReadKey();
    44 
    45         }

    注意:

    1、如果数据文件为空或不存在,则用初始化的p数组,作为测试数据

    2、文件中的数据,见开篇截图

    参考文章:

    一维数组的 K-Means 聚类算法理解

    深入理解K-Means聚类算法

     

       

  • 相关阅读:
    Reflector 插件
    Tips for ILMerge
    WaitAll for multiple handles on a STA thread is not supported 解决方案
    MSI: UAC return 0x800704C7
    SET与SETX的区别
    年在Copyright中的含义
    gacutil : 添加.NET 4.0 assembly 到GAC失败
    LicenseContext.GetSavedLicenseKey 需要 FileIOPermission
    Linq学习之linq基础知识
    SQL Server 2008如何导出带数据的脚本文件
  • 原文地址:https://www.cnblogs.com/wangqiang3311/p/11230975.html
Copyright © 2011-2022 走看看