使用轮廓系数来确定K-means算法簇的数量

在使用K-means算法的时候,我们最纠结的两个点,第一个点是我们不知道分为几类,第二个点初始的聚类中心对聚类结果的影响,我们在这些使用轮廓系数评判聚类结果的好坏来确定初始的聚类中心和簇的数量。
首先随机生成多个聚类中心,然后分别计算每一个聚类中心的聚类结果的轮廓系数,然后我们我们找到最好的初始聚类中心。我们按照多个类进行聚类,分别计算轮廓系数,然后找到最优的聚类结果。

package com.winning.dm.pathway;

import java.util.ArrayList;

public class Kmeans {
	// 一维数据集合
	public ArrayList<Double> dataset;
	// 聚类的数量
	public int k;
	// 计算当前的聚类中心
	public double[] nowcenter;
	// 存储上一次的聚类中心
	public double[] center;
	// 聚类中心最好改变最小的阈值
	public double min;
	// 聚类结果每一个ArrayList是一个聚类结果
	public ArrayList<ArrayList<Double>> son;
	// 进行随机生成五次的聚类中心
	public ArrayList<double[]> mangtimecenter;
	// 记录每次每次聚类计算的s值。
	public ArrayList<Double> ssum;

	public ArrayList<Double> getDataset() {
		return dataset;
	}

	public void setDataset(ArrayList<Double> dataset) {
		this.dataset = dataset;
	}

	public int getK() {
		return k;
	}

	public void setK(int k) {
		this.k = k;
	}

	public double[] getCenter() {
		return center;
	}

	public void setCenter(double[] center) {
		this.center = center;
	}

	public double getMin() {
		return min;
	}

	public void setMin(double min) {
		this.min = min;
	}

	public ArrayList<ArrayList<Double>> getSon() {
		return son;
	}

	public void setSon(ArrayList<ArrayList<Double>> son) {
		this.son = son;
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:14:49
	 * @描述: 初始化数据k
	 * @param k
	 *            簇的个数
	 * @param dataset
	 *            数据集
	 * @param min
	 *            中心停止迭代的阈值
	 * @备注:
	 */
	public Kmeans(int k, ArrayList<Double> dataset, double min) {
		this.k = k;
		this.dataset = dataset;
		this.min = min;
		this.son = new ArrayList<ArrayList<Double>>();
		for (int i = 0; i < this.k; i++) {
			ArrayList<Double> al = new ArrayList<Double>();
			this.son.add(al);
		}
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:16:01
	 * @描述: 随机生成聚类中心
	 * @备注:
	 */
	public void createCenter() {
		double[] hs = new double[this.k];
		for (int i = 0; i < this.k; i++) {
			double a = this.dataset.get((int) Math.random()
					* (this.dataset.size()));
			for (int j = 0; j < i; j++) {
				if (a == hs[j]) {
					i--;
				} else {
					hs[i] = a;
				}
			}
		}
		// System.out.println(hs[0] + "-----" + hs[1]);
		this.setCenter(hs);
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:16:29
	 * @描述: 根据距离聚类中心的距离对数据进行分组
	 * @备注:
	 */
	public void groupby() {
		for (Double db : dataset) {
			double[] dl = new double[this.k];
			for (int i = 0; i < this.k; i++) {
				dl[i] = this.distance(db, this.center[i]);
			}
			int m = this.findMinDistance(dl);
			// 对数据进行分组
			this.son.get(m).add(db);
		}
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:17:48
	 * @描述:计算两个数据之间的距离
	 * @param a
	 * @param b
	 * @return
	 * @备注:
	 */
	public double distance(double a, double b) {
		return Math.abs(a - b);
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:18:55
	 * @描述: 找到聚类中心
	 * @param db
	 * @return
	 * @备注:
	 */
	public int findMinDistance(double[] db) {
		int result = 0;
		double min = db[0];
		for (int i = 0; i < this.k; i++) {
			if (db[i] < min) {
				db[i] = min;
				result = i;
			}
		}
		return result;
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:20:57
	 * @描述: 更新聚类中心位置
	 * @return
	 * @备注:
	 */
	public double[] iteration() {
		double[] result = new double[this.son.size()];
		for (int i = 0; i < this.son.size(); i++) {
			result[i] = this.mean(this.son.get(i));
		}
		return result;
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:21:56
	 * @描述: 计算每一个簇的中心位置
	 * @param s
	 * @return
	 * @备注:
	 */
	public double mean(ArrayList<Double> s) {
		double result = 0;
		for (Double d : s) {
			result += d;
		}
		return result / s.size();
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:22:22
	 * @描述: 使用迭代找到聚类的中心
	 * @return
	 * @备注:
	 */
	public ArrayList<ArrayList<Double>> cluster() {
		// 根据中心进行分组以便于迭代出下一个组中心
		this.groupby();
		// 根据分组的数据求出新的聚类中心
		this.nowcenter = this.iteration();
		// 进行判断分析迭代是否终止,只有所有的中心的差都满足条件的时候才终止迭代
		for (int i = 0; i < this.center.length; i++) {
			if (Math.abs(this.center[i] - this.nowcenter[i]) > this.min) {
				this.center = this.nowcenter;
				// 每次迭代重新赋值一下分类好的集合
				// this.son = new ArrayList<ArrayList<Double>>();
				// 对重新分好类的集合进行重新赋值
				// for (int m = 0; m < this.k; m++) {
				// ArrayList<Double> al = new ArrayList<Double>();
				// this.son.add(al);
				// }
				for (int m = 0; m < this.k; m++) {
					this.son.get(m).clear();
				}
				// 重新进行迭代
				this.cluster();
			}
		}

		return this.son;
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午5:50:53
	 * @描述: 进行多次随机生成中心,然后选取最好的
	 * @return
	 * @备注:
	 */
	public ArrayList<ArrayList<Double>> mangTimeGroup() {
		this.ssum = new ArrayList<Double>();
		this.mangtimecenter = new ArrayList<double[]>();
		for (int i = 0; i < 5; i++) {
			// 创造一个随机的中心
			this.createCenter();
			// 对中心进行迭代
			Online ol = new Online(this.cluster());
			// 计算每一次分组的轮廓系数
			this.ssum.add(ol.counts());
			this.mangtimecenter.add(this.center);
		}
		double max = this.ssum.get(0);
		int upp = 0;
		for (int i = 1; i < this.ssum.size(); i++) {
			if (this.ssum.get(i) > max) {
				max = this.ssum.get(i);
				upp = i;
			}
		}
		this.setCenter(this.mangtimecenter.get(upp));
		return this.cluster();
	}
}

以上是K-means聚类算法。
下面是轮廓系数的计算过程。

package com.winning.dm.pathway;

import java.util.ArrayList;

public class Online {
	public ArrayList<ArrayList<Double>> data;
	public ArrayList<ArrayList<Double>> sal;
	public ArrayList<ArrayList<Double>> aal;
	public ArrayList<ArrayList<Double>> bal;

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午3:54:00
	 * @描述: 把分类好的数据data导入类中
	 * @param data
	 *            分好类的数据
	 * @备注:
	 */
	public Online(ArrayList<ArrayList<Double>> data) {
		this.data = data;
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午3:55:12
	 * @描述: 计算簇间平均距离
	 * @param i
	 *            表示数据在哪一个簇中
	 * @param j
	 *            表示数据在簇中的位置
	 * @param al
	 *            数据所在的簇
	 * @备注:
	 */
	public void counta(int i, int j, ArrayList<Double> al) {
		this.aal.get(i)
				.set(j,
						this.countDistance(this.data.get(i).get(j), al)
								/ al.size() - 1);
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午3:55:59
	 * @描述: 计算簇外平均距离
	 * @param i
	 *            数据所在的簇
	 * @param j
	 *            数据在簇中的位置
	 * @备注:
	 */
	public void countb(int i, int j) {
		double[] d = new double[this.data.size()];
		for (int m = 0; m < this.data.size(); m++) {
			if (m != i) {
				d[m] = this.countDistance(this.data.get(m).get(j),
						this.data.get(m))
						/ this.data.get(m).size();
			}
		}
		Sort.Sort(d);
		this.bal.get(i).set(j, d[1]);
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:07:30
	 * @描述: 计算数据跟簇之间的距离和
	 * @param d
	 *            数据
	 * @param al
	 *            簇
	 * @return
	 * @备注:
	 */
	public double countDistance(double d, ArrayList<Double> al) {
		double sum = 0;
		for (Double db : al) {
			sum += Math.abs(db - d);
		}
		return sum;
	}

	/**
	 * 
	 * @作者: liusen
	 * @时间: 2016-10-21 下午4:08:12
	 * @描述: 计算出数据的轮廓系数
	 * @return
	 * @备注:
	 */
	public double counts() {
		//初始化aal数据
		this.aal=new ArrayList<ArrayList<Double>>();
		for(int i=0;i<this.data.size();i++){
			ArrayList<Double> al=new ArrayList<Double>();
			this.aal.add(al);
		}
		//初始化bal
		this.bal=new ArrayList<ArrayList<Double>>();
		for(int i=0;i<this.data.size();i++){
			ArrayList<Double> al=new ArrayList<Double>();
			this.bal.add(al);
		}
		//分别计算每一个簇的簇内距离
		for (int i = 0; i < this.data.size(); i++) {
			for (int j = 0; j < this.data.get(i).size(); j++) {
				this.counta(i, j, this.data.get(i));
				this.countb(i, j);
				this.sal.get(i).add(
						(this.bal.get(i).get(j) - this.aal.get(i).get(j))
								/ Math.max(this.bal.get(i).get(j), this.aal
										.get(i).get(j)));
			}
		}
		double sum = 0;
		double result = 0;
		for (int i = 0; i < this.sal.size(); i++) {
			for (int j = 0; j < this.sal.get(i).size(); j++) {
				sum += this.sal.get(i).get(j);
				result++;
			}
		}
		return sum / result;
	}
}

程序员有偿接单和程序指导加QQ:734564390

相关推荐
关系型数据库和NoSQL数据库 什么是NoSQL 大家有没有听说过“NoSQL”呢?近年,这个词极受关注。看到“NoSQL”这个词,大家可能会误以为是“No!SQL”的缩写,并深感愤怒:“SQL怎么会没有必要了呢?”但实际上,它是“Not Only SQL”的缩写。它的意义是:适用关系型数据库的时候就使用关系型数据库,不适用的时候也没有必要非使用关系型数据库不可,可以考虑使用更加合适的数据存储。 为弥补关系型数据库的不足,各种各样的NoSQL数据库应运而生。 为了更好地了解本书所介绍的NoSQL数据库,对关系型数据库的理解是必不可少的。那么,就让我们先来看一看关系型数据库的历史、分类和特征吧。 关系型数据库简史 1969年,埃德加•弗兰克•科德(Edgar Frank Codd)发表了划时代的论文,首次提出了关系数据模型的概念。但可惜的是,刊登论文的《IBM Research Report》只是IBM公司的内部刊物,因此论文反响平平。1970年,他再次在刊物《Communication of the ACM》上发表了题为“A Relational Model of Data for Large Shared Data banks”(大型共享数据库的关系模型)的论文,终于引起了大家的关注。 科德所提出的关系数据模型的概念成为了现今关系型数据库的基础。当时的关系型数据库由于硬件性能低劣、处理速度过慢而迟迟没有得到实际应用。但之后随着硬件性能的提升,加之使用简单、性能优越等优点,关系型数据库得到了广泛的应用。 通用性及高性能 虽然本书是讲解NoSQL数据库的,但有一个重要的大前提,请大家一定不要误解。这个大前提就是“关系型数据库的性能绝对不低,它具有非常好的通用性和非常高的性能”。毫无疑问,对于绝大多数的应用来说它都是最有效的解决方案。 突出的优势 关系型数据库作为应用广泛的通用型数据库,它的突出优势主要有以下几点: 保持数据的一致性(事务处理) 由于以标准化为前提,数据更新的开销很小(相同的字段基本上都只有一处) 可以进行JOIN等复杂查询 存在很多实际成果和专业技术信息(成熟的技术) 这其中,能够保持数据的一致性是关系型数据库的最大优势。在需要严格保证数据一致性和处理完整性的情况下,用关系型数据库是肯定没有错的。但是有些情况不需要JOIN,对上述关系型数据库的优点也没有什么特别需要,这时似乎也就没有必要拘泥于关系型数据库了。 关系型数据库的不足 不擅长的处理 就像之前提到的那样,关系型数据库的性能非常高。但是它毕竟是一个通用型的数据库,并不能完全适应所有的用途。具体来说它并不擅长以下处理: 大量数据的写入处理 为有数据更新的表做索引或表结构(schema)变更 字段不固定时应用 对简单查询需要快速返回结果的处理 。。。。。。 NoSQL数据库 为了弥补关系型数据库的不足(特别是最近几年),NoSQL数据库出现了。关系型数据库应用广泛,能进行事务处理和JOIN等复杂处理。相对地,NoSQL数据库只应用在特定领域,基本上不进行复杂的处理,但它恰恰弥补了之前所列举的关系型数据库的不足之处。 易于数据的分散 如前所述,关系型数据库并不擅长大量数据的写入处理。原本关系型数据库就是以JOIN为前提的,就是说,各个数据之间存在关联是关系型数据库得名的主要原因。为了进行JOIN处理,关系型数据库不得不把数据存储在同一个服务器内,这不利于数据的分散。相反,NoSQL数据库原本就不支持JOIN处理,各个数据都是独立设计的,很容易把数据分散到多个服务器上。由于数据被分散到了多个服务器上,减少了每个服务器上的数据量,即使要进行大量数据的写入操作,处理起来也更加容易。同理,数据的读入操作当然也同样容易。 提升性能和增大规模 下面说一点题外话,如果想要使服务器能够轻松地处理更大量的数据,那么只有两个选择:一是提升性能,二是增大规模。下面我们来整理一下这两者的不同。 首先,提升性能指的就是通过提升现行服务器自身的性能来提高处理能力。这是非常简单的方法,程序方面也不需要进行变更,但需要一些费用。若要购买性能翻倍的服务器,需要花费的资金往往不只是原来的2倍,可能需要多达5到10倍。这种方法虽然简单,但是成本较高。 另一方面,增大规模指的是使用多台廉价的服务器来提高处理能力。它需要对程序进行变更,但由于使用廉价的服务器,可以控制成本。另外,以后只要依葫芦画瓢增加廉价服务器的数量就可以了。 不对大量数据进行处理的话就没有使用的必要吗? NoSQL数据库基本上来说为了“使大量数据的写入处理更加容易(让增加服务器数量更容易)”而设计的。但如果不是对大量数据进行操作的话,NoSQ
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页