使用时,需要修改K值,args值

运行流程:

先初始化中心点->map中和距离最近的中心点生成一对传入reduce->reduce中把相同key值的存到一起->更新中心点,计算和上一次的结果偏差是否达到要求,没有的话继续迭代

因为一般来说都是读取文件操作,所以使用String操作更方便

package kmeans;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class KMeans {
/**************
* agrs[0]:hdfs://localhost:9000/KMeans/cluster
* args[1]:hdfs://localhost:9000/KMeans/center
* args[2]:hdfs://localhost:9000/KMeans/output
* hdfs://localhost:9000/KMeans/center/center为质心点存放文件,每轮都会更新
**********/
public static void main(String[] args) throws Exception {
CenterInitial centerInitial = new CenterInitial();
centerInitial.run(args);
int times = 0;
double s = 0, shold = 0.001,temp = Integer.MAX_VALUE;;
do {
Configuration conf = new Configuration();
conf.set("fs.default.name", "hdfs://localhost:9000");
@SuppressWarnings("deprecation")
Job job = new Job(conf, "KMeans");
job.setJarByClass(KMeans.class); // 设置job所在的类在哪个jar包 job.setMapperClass(KMapper.class); // 指定job所用的map类 reducer类
job.setReducerClass(KReducer.class); job.setMapOutputKeyClass(Text.class); // map阶段的输出的key
job.setMapOutputValueClass(Text.class); job.setOutputKeyClass(Text.class);// reduce阶段的输出的key
job.setOutputValueClass(Text.class); FileSystem fs = FileSystem.get(conf);
fs.delete(new Path(args[2]), true);//输出路径下不能存在要输出的文件 否则报错,每次迭代要删除这个文件 FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[2])); if (job.waitForCompletion(true)) {
NewCenter newCenter = new NewCenter();
s = newCenter.run(args);
if(Math.abs(s - temp) < shold)
break;
else
temp = s;
times++;
}
} while (s > shold); // shold为阀值,当迭代后基本上收敛就可以停止了
System.out.println("Iterator: " + times); // 打印迭代次数
}
}

入口类

package kmeans;

//**************************    初始化质心(随机生成k个质心)   *************************

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils; public class CenterInitial { public void run(String[] args) throws IOException {
String[] clist;
int k = 5; // ---------随机生成5个质心点-----------
String string = ""; // string 存放k个顶点,形式如a,b c,d e,f ......,中间以空格分开 String inpath = args[0] + "/testData";
String outpath = args[1] + "/center"; Configuration conf = new Configuration(); // 读取hadoop文件系统的配置
// conf1.set("hadoop.job.ugi", "hadoop,hadoop");
FileSystem fs = FileSystem.get(URI.create(inpath), conf); // FileSystem是用户操作HDFS的核心类,它获得URI对应的HDFS文件系统
FSDataInputStream in = null; ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
in = fs.open(new Path(inpath));
IOUtils.copyBytes(in, out, 50, false); // 用Hadoop的IOUtils工具方法来让这个文件的指定字节复制到标准输出流上 clist = out.toString().split(" "); // clist是字符串数组,每一个元素如(a,b)这样的形式
/*
* ByteArrayOutputStream此类实现了一个输出流,其中的数据被写入一个 byte 数组。 缓冲区会随着数据的不断写入而自动增长。可使用
* toByteArray() 和 toString() 获取数据
*/
} finally {
IOUtils.closeStream(in); // out是ByteArrayOutputStream,关闭无效。这里我们手动关闭in
} FileSystem filesystem = FileSystem.get(URI.create(outpath), conf); for (int i = 0; i < k; i++) {
int j = (int) (Math.random() * 100) % clist.length;
if (string.contains(clist[j])) // k个初始 顶点不能重复,若重复此次循环作废(continue),增加一次循环(k++)以保证
{ // 有k个顶点
k++;
continue;
}
string = string + clist[j].replace(" ", "") + " ";
}
/*
* 如果没有重复的,处理每一个点——去掉空格,使其符合输入格式。 for example,( a, b )处理完后把所有空格都去除掉,变成(a,b)
* 7个质心点中间以空格分开,最后string格式为(a,b) (c,d) (e,f)...
*/
OutputStream out2 = filesystem.create(new Path(outpath));
IOUtils.copyBytes(new ByteArrayInputStream(string.getBytes()), out2, 4096, true); // write string
/*
* string写到center文件中,这里out2是个FSDataOutputStream,指向/KMeans/center/center
* 用字节数组流从字节数组中读取string,IOUtils把这个流复制给FSDataOutputStream
*/
System.out.println(string); // 在屏幕显示出随机生成的这k个点
} }

初始化中心点

package kmeans;

/*
* map的工作主要是把测试样例所有的点划分到k个类中,中间键值对的格式
* 为(key:center ,value:r若干属于center的点)
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper; public class KMapper extends Mapper<LongWritable, Text, Text, Text> { private String[] center; // 存放k个质点 protected void setup(Context context) throws IOException, InterruptedException
{
String centerlist = "hdfs://localhost:9000/KMeans/center/center"; // center文件
Configuration conf1 = new Configuration();
// conf1.set("hadoop.job.ugi", "hadoop-user,hadoop-user");
FileSystem fs = FileSystem.get(URI.create(centerlist), conf1); FSDataInputStream in = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
/*
* 下面的工作主要是从hdfs://localhost:9000/KMeans/center/center这个文件
* 中读出k个质点,并且放到center这个字符串数组里边
*/
try {
in = fs.open(new Path(centerlist));
IOUtils.copyBytes(in, out, 100, false);
center = out.toString().split(" ");
} finally {
IOUtils.closeStream(in); // 手动关闭FSDataInputStream
}
} public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
StringTokenizer itr = new StringTokenizer(value.toString());
// *map函数每次读取文件的一行,key为当前行在文件的字节偏移位置,value 就是该行的内容
while (itr.hasMoreTokens()) // 一次取一个点,outValue对应一个点,(x,y)
{
String outValue = new String(itr.nextToken());
String[] list = outValue.split(","); // a,b转换成string类型的a和b
float min = Integer.MAX_VALUE;
int pos = 0;
/*
* 下面这段for循环是对于一个确定的点outValue,计算其与k个质心点的欧式距离
* 记录距离最小的质心点,并且更新最短距离为outValue和该质心点的距离
*/
for (int i = 0; i < center.length; i++) {
String[] centerStrings = center[i].split(",");
float distance = 0;
for (int j = 0; j < list.length; j++)
distance += (float) Math.pow((Float.parseFloat(list[j]) - Float.parseFloat(centerStrings[j])), 2);
if (min > distance) {
min = distance;
pos = i;
}
}
context.write(new Text(center[pos]), new Text(outValue));// 中间键值对 -> key:质心点,value:当前处理完的点,相同的质心点会送到同一个reducer
}
}
}

重写map类

package kmeans;

/*
* reduce的主要工作是求出每个类别新的质点
*/
import java.io.IOException; import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer; public class KReducer extends Reducer<Text, Text, Text, Text> { public void reduce(Text key, Iterable<Text> value, Context context) throws IOException, InterruptedException {
StringBuffer outVal = new StringBuffer("");
int count = 0;
String center = ""; // 用于存放用平均值更新后的质点
int length = key.toString().split(",").length;
float[] ave = new float[length];
for (int i = 0; i < length; i++)
ave[i] = 0; for (Text val : value) {
outVal.append(val.toString() + " "); // outVal存放了属于key质心点这个类的所有点
String[] tmp = val.toString().split(",");
for (int i = 0; i < tmp.length; i++)
ave[i] += Float.parseFloat(tmp[i]); // 所有点每个维度都各自相加,然后用于求平均值,更新质点
count++; // 统计属于key质点类的所有点个数
} for (int i = 0; i < length; i++) {
ave[i] = ave[i] / count; // 对于每个维度,求其平均值
if (i == 0)
center += ave[i] + ",";
else {
if (i == length - 1) // 如果是最后一个维度
center += ave[i];
else { // 如果是介于第一个维度和最后一个维度
center += ave[i] + ",";
}
}
}
context.write(new Text(center), new Text(outVal.toString()));
/*
* reduce函数输出键值对格式为 key:初始质点,value:属于初始质点类的所有点+(a,b)
* 其中,(a,b)是更新后的质点;属于初始质点类的所有点之间都有空格分开
*/
} }

重写reduce类

package kmeans;

//***************************更新质心(写入到center文件中),输出两次质心距离变化的大小***********************

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils; public class NewCenter { int k = 5;
// -------------------------质心个数,要与CenterInitial的k相对应---------------------- float shold = Integer.MIN_VALUE;
/*
* shold先初始化为最小值,下面对于reduce传来的每行数据,都会计算这行数据的初始质心和更新后的质心之间的欧氏距离 这个距离存储
* 在局部变量temp里,shold会一直更新,当遇到temp比它大时,将temp的值赋值给shold 所以,shold中存放的是每个类更新后变化的最大值
*/
String[] line;
String newcenter = new String(""); // 类似CenterInitial中的string,存放k个新顶点,形式如(a,b) (c,d) (e,f) .....,中间以空格分开 public float run(String[] args) throws IOException, InterruptedException {
Configuration conf = new Configuration();
// conf.set("hadoop.job.ugi", "hadoop,hadoop");
FileSystem fs = FileSystem.get(URI.create(args[2] + "/part-r-00000"), conf);
FSDataInputStream in = null;
ByteArrayOutputStream out = new ByteArrayOutputStream();
try { in = fs.open(new Path(args[2] + "/part-r-00000"));
IOUtils.copyBytes(in, out, 50, false);
line = out.toString().split("\n");// 一行是一个reduce的输出,用"\n"分隔后,一个line的成员代表一个reduce的输出
} finally {
IOUtils.closeStream(in);
}
for (int i = 0; i < k; i++) // line按道理应该是有k个元素
{
/*
* 要注意,reduce函数输出的key,value之间是以/t分隔的,并不是空格! 所以要先替换掉 \t再以空格分隔
*/
String[] l = line[i].split("\t");
String[] clust = l[1].split(" "); // ----clust存储的是质点------------
float tmp = 0;
for (int j = 0; j < clust.length; j++) {
String center[] = l[0].split(",");
String point[] = clust[j].split(",");
float dis = 0;
for(int k=0; k<center.length; k++)
{
dis += Math.pow(Float.parseFloat(center[k]) - Float.parseFloat(point[k]), 2);
}
tmp += dis;
}
newcenter = newcenter + l[0] + " ";
if (shold <= tmp)
shold = tmp;
}
OutputStream out2 = fs.create(new Path(args[1] + "/center"));
/*********
* args[1]:hdfs://localhost:9000/KMeans/center,每轮会更新
************/
IOUtils.copyBytes(new ByteArrayInputStream(newcenter.getBytes()), out2, 4096, true);
System.out.println("newcenter" +newcenter);
return shold;
}
}

更新中点

随机生成5个二维点的簇

package hello;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random; public class NewData { public static void main(String[] args) {
Random rd = new Random();
try {
FileOutputStream out = new FileOutputStream(new File("testData"));
BufferedOutputStream Buff = new BufferedOutputStream(out);
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 100; j++) {
int x = rd.nextInt(500) + 500 * i;
int y = rd.nextInt(500) + 500 * i;
String str = "";
str = x + "," + y + " ";
Buff.write(str.getBytes());
}
Buff.write("\n".getBytes());
}
Buff.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("ok~");
}
}

自动生成测试数据

MapReduce Kmeans算法含测试数据的更多相关文章

  1. MapReduce Kmeans聚类算法

    最近在网上查看用MapReduce实现的Kmeans算法,例子是不错,http://blog.csdn.net/jshayzf/article/details/22739063 但注释太少了,而且参数 ...

  2. hadoop在实现kmeans算法——一个mapreduce实施

    写mapreduce程序实现kmeans算法.我们的想法可能是 1. 次迭代后的质心 2. map里.计算每一个质心与样本之间的距离,得到与样本距离最短的质心,以这个质心作为key,样本作为value ...

  3. mahout运行测试与kmeans算法解析

    在使用mahout之前要安装并启动hadoop集群 将mahout的包上传至linux中并解压即可 mahout下载地址: 点击打开链接 mahout中的算法大致可以分为三大类: 聚类,协同过滤和分类 ...

  4. mahout运行测试与数据挖掘算法之聚类分析(一)kmeans算法解析

    在使用mahout之前要安装并启动hadoop集群 将mahout的包上传至linux中并解压即可 mahout下载地址: 点击打开链接 mahout中的算法大致可以分为三大类: 聚类,协同过滤和分类 ...

  5. mahout中KMeans算法

    本博文主要内容有   1.kmeans算法简介 2.kmeans执行过程  3.关于查看mahout中聚类结果的一些注意事项 4.kmeans算法图解      5.mahout的kmeans算法实现 ...

  6. kmeans算法并行化的mpi程序

    用c语言写了kmeans算法的串行程序,再用mpi来写并行版的,貌似参照着串行版来写并行版,效果不是很赏心悦目~ 并行化思路: 使用主从模式.由一个节点充当主节点负责数据的划分与分配,其他节点完成本地 ...

  7. 【原创】数据挖掘案例——ReliefF和K-means算法的医学应用

    数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘 (DataMiriing),指的是从大型数据库或数据仓库中提取人们感兴趣的知识,这些知识是隐含的.事先未知 ...

  8. K-means算法和矢量量化

    语音信号的数字处理课程作业——矢量量化.这里采用了K-means算法,即假设量化种类是已知的,当然也可以采用LBG算法等,不过K-means比较简单.矢量是二维的,可以在平面上清楚的表示出来. 1. ...

  9. 基于ReliefF和K-means算法的医学应用实例

    基于ReliefF和K-means算法的医学应用实例 数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘 (DataMiriing),指的是从大型数据库或数据 ...

随机推荐

  1. SpringMVC的环境搭建

    MyBatis框架-->持久层框架-->Object[对象]Relation[关系型数据库]Mapping[在MyBatis的体现是哪个映射文件中国的<resultMap>标签 ...

  2. 机器学习:SVM(SVM 思想解决回归问题)

    一.SVM 思想在解决回归问题上的体现 回归问题的本质:找到一条直线或者曲线,最大程度的拟合数据点: 怎么定义拟合,是不同回归算法的关键差异: 线性回归定义拟合方式:让所有数据点到直线的 MSE 的值 ...

  3. C语言-数组

    数组是具有同一属性的若干个数据组织成一个整体,互相关联 数组是有序数据的集合.数组中的每一个元素都属于同一个数据类型,用一个统一的数组名和下标来唯一地确定数组中的元素 一维数组 一维数组的定义 在定义 ...

  4. pa15-三省吾身

    序号 项 1 凡事提前10分钟    凡事提前10分钟,会让你有充裕的时间应对可能的突发事件,更加从容.    试着把起床闹钟提前10分钟,你就会发现你出门不必急匆匆,早饭也可慢慢享用,一整天的状态也 ...

  5. Solaris11修改主机名

    在Solaris10中,主机名的修改是通过修改相关的配置文件实现的.在Solaris11中,主机名的配置信息已经转移到SMF配置库中,因此修改主机名的方式与Solaris10完全不同.以下是修改Sol ...

  6. [原]实例-简单设计&精简代码&复用代码

    引言 本文以实际项目为例谈一谈我个人对于软件开发的理解,偏细节   软件项目B 基于.net平台,使用WPF框架,c#语言,MVVM模式开发的桌面软件 该软件支持可视化的设计器功能,允许所见即所得的方 ...

  7. XSS的各种用途

    0x01 最常见之窃取用户cookie 当cookie没有设置HttpOnly属性时,可以通过javascript代码创建img,script,iframe等标签,并把src属性设置为自己部署的xss ...

  8. JS中substring()方法(用于提取字符串中介于两个指定下标之间的字符)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. 【263】Linux 添加环境变量 & 全局 shell 脚本

    Linux电脑添加环境变量 方法一:通过修改 profile 文件添加环境变量 1. 打开终端,输入[vi /etc/profile],如下所示,点击回车 [ocean@ygs-jhyang-w1 L ...

  10. [解决问题] pandas读取csv文件报错OSError解决方案

    python用padans.csv_read函数出现OSError: Initializing from file failed 问题:文件路径中存在中文 解决办法:修改文件路径名为全英文包括文件名