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

匿名 (未验证) 提交于 2019-12-02 23:51:01

一、背景

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

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

二、算法步骤

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

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

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

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

注意:

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

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

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

三、算法分析及特点

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

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

3、此算法有两个缺点:

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聚类算法

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!