Kmeans聚类java图形界面演示程序
程序员文章站
2024-03-22 23:14:16
...
效果图
Tuple.java
实体类
/*
* 数据对象
* 二维坐标
*/
public class Tuple {
float x;
float y;
public Tuple(){}
public Tuple(float a,float b){
this.x=a;
this.y=b;
}
public void setXY(float a,float b){
this.x=a;
this.y=b;
}
public void setX(float a){
this.x=a;
}
public void setY(float a){
this.y=a;
}
public float getX(){
return x;
}
public float getY(){
return y;
}
}
KmeansCalc.java
运算类
/*
* KmeansCalc
* 实现K-均值聚类算法
* 默认聚类中心为3个,
* 初始化数据对象100个
* 收敛条件为中心点不再变化
*/
import java.lang.Math;
import java.util.Random;
import java.util.Vector;
public class KmeansCalc {
static int k=3; //聚类中心个数
int i=0;
int lable = 0; //簇标签
static int flag = 0;
static StringBuilder data = new StringBuilder();
static Vector<Tuple> tuples = new Vector<Tuple>(); //元组坐标集
static Tuple means[]= new Tuple[k]; //中心点集合
static Vector<Tuple> clusters[]=new Vector[k]; //簇集合
Tuple meansLast[]= new Tuple[k];
Tuple meanFrist[] = new Tuple[k];
public KmeansCalc(){
for(int i=0;i<100;i++){
tuples.addElement(new Tuple(0,0));
}
for(i=0;i<k;i++){
means[i] = new Tuple(0,0);
meanFrist[i] = new Tuple(0,0);
clusters[i] = new Vector<Tuple>();
clusters[i].addElement(means[i]);
}
}
//REset Data,bt1Action
public void ReSet(){
data.delete(0, data.length());
tuples.clear();
for(i=0;i<k;i++){
means[i] = new Tuple(0,0);
clusters[i] = new Vector<Tuple>();
clusters[i].clear();
clusters[i].addElement(means[i]);
}
flag=0;
}
//计算欧氏距离
public double getDistXY(Tuple t1,Tuple t2){
return Math.sqrt(Math.pow((t1.x-t2.x)*(t1.x-t2.x), 2)+Math.pow((t1.y-t2.y)*(t1.y-t2.y),2));
}
//根据质心,决定当前元组属于哪个簇
public int clusterOfTuple(Tuple means[],Tuple tuple){
double dist=getDistXY(means[0],tuple);
double tmp;
int label=0;
for(int i=1;i<k;i++){
tmp=getDistXY(means[i],tuple);
if(tmp<dist) {dist=tmp;label=i;}
}
return label;
}
//获得当前簇的质心
public Tuple getMeans(Vector<Tuple> cluster){
float meansX=0,meansY=0;
Tuple t = new Tuple();
for (int i=0;i<cluster.size();i++)
{
meansX+=cluster.get(i).getX();
meansY+=cluster.get(i).getY();
}
t.setX(meansX/cluster.size());
t.setY(meansY/cluster.size());
return t;
}
//bt4 Action
public static String getData(){
return data.toString();
}
//init(),bt1Action
public void KMeansInit(){
Random ran = new Random();
tuples.addElement(new Tuple(ran.nextInt(19),ran.nextInt(17)));
data.append("本次数据集\n(仅作为查看复制使用,在此处修改值并不会影响程序):\n("+String.valueOf(tuples.get(0).getX())+","+
String.valueOf(tuples.get(0).getY())+")\n");
for(int i=1;i<100;i++){
Tuple tem = new Tuple(ran.nextInt(19),ran.nextInt(17));
if(!tuples.contains(tem)){
tuples.addElement(tem);
data.append("("+String.valueOf(tem.getX())+","+
String.valueOf(tem.getY())+")\n");
}
else{
i--;
}
}
meanFrist[0] = tuples.get(ran.nextInt(100));
for(i=1;i<k;i++){
meanFrist[i] = tuples.get(ran.nextInt(100));
for(int j=0;j<i;j++){
if(meanFrist[i]==meanFrist[i-1]){
i--;
}
}
}
for(i=0;i<k;i++){
meansLast[i]= new Tuple();
means[i].setX(meanFrist[i].getX());
meansLast[i].x=means[i].x;
means[i].y=meanFrist[i].getY();
meansLast[i].y=means[i].y;
}
//根据默认的质心给簇赋值
for(i=0;i!=tuples.size();++i){
lable=clusterOfTuple(means,tuples.get(i));
clusters[lable].addElement(tuples.get(i));
}
for(lable=0;lable<k;lable++){
System.out.print("第"+(lable+1)+"个簇:\n");
Vector<Tuple> t=clusters[lable];
for (i=0;i<t.size();i++)
{
System.out.print("("+t.get(i).getX()+","+t.get(i).getY()+")"+" ");
}
System.out.print("\n");
}
}
//Later handle,bt2&bt3 Action
public void KMeansHandle(){
for (i=0;i<k;i++) //赋新值同时与上一次比较
{
means[i]=getMeans(clusters[i]);
if(means[i].x==meansLast[i].x&&means[i].y==meansLast[i].y)flag++;
else { meansLast[i].x=means[i].x; meansLast[i].y=means[i].y;}
}
if(flag==k)return;
flag=0;
for (i=0;i<k;i++) //清空每个簇
{
clusters[i].clear();
}
//根据新的质心获得新的簇
for(i=0;i!=tuples.size();++i){
lable=clusterOfTuple(means,tuples.get(i));
clusters[lable].add(tuples.get(i));
}
for(lable=0;lable<k;lable++){
System.out.print("第"+(lable+1)+"个簇:\n");
System.out.print("中心点:("+means[lable].x+","+means[lable].y+") \n");
System.out.print("元组成员:\n");
Vector<Tuple> t=clusters[lable];
for (i=0;i<t.size();i++)
{
System.out.print("("+t.get(i).getX()+","+t.get(i).getY()+")"+" ");
}
System.out.print("\n");
}
System.out.print("\n");
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.print("Thread.sleep Exception in Kmeans.java\n");
System.exit(0);
}
}
}
KmeansPaint.java
界面,入口
/*
* KmeansPaint
* 实现图形界面部分
*/
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class KmeansPaint extends JFrame {
static int Flag = 0; //End judge
int stepCount = 0,countflag=0;
private final int FREAME_X = 20;
private final int FREAME_Y = 80;
private final int FREAME_WIDTH = 650;// 横
private final int FREAME_HEIGHT = 550;// 纵
// 原点坐标
private final int Origin_X = FREAME_X + 50;
private final int Origin_Y = FREAME_Y + FREAME_HEIGHT - 50;
// X,Y轴终点坐标
private final int XAxis_X = FREAME_X + FREAME_WIDTH - 30;
private final int XAxis_Y = Origin_Y;
private final int YAxis_X = Origin_X;
private final int YAxis_Y = FREAME_Y;
//分度值
private final int INTERVAL = 30;
JLabel cluster1 =new JLabel("No.1Cluster:");
JLabel cluster2 =new JLabel("No.2Cluster:");
JLabel cluster3 =new JLabel("No.3Cluster:");
JLabel countResult = new JLabel();
JLabel tip = new JLabel("注:每次随机生成100个坐标数据,3个初始中心点坐标,均为整数。");
JButton bt1 = new JButton("加载数据");
JButton bt2 = new JButton("自动演示");
JButton bt3 = new JButton("单步演示");
JButton bt4 = new JButton("查看数据");
JTextArea lab = new JTextArea();
JFrame newF = new JFrame("数据集");
KmeansCalc km=new KmeansCalc();
private MyCanva trendChartCanvas = new MyCanva();
public KmeansPaint(){
super("K-均值聚类算法演示系统");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setBounds(300, 200, 1000, 700);
bt1.setBounds(XAxis_X+60,Origin_Y-110, 100, 40);
bt4.setBounds(XAxis_X+200,Origin_Y-110, 100, 40);
bt2.setBounds(XAxis_X+60,Origin_Y-50, 100, 40);
bt3.setBounds(XAxis_X+200,Origin_Y-50, 100, 40);
cluster1.setBounds(XAxis_X+60, YAxis_Y, 200, 40);
cluster2.setBounds(XAxis_X+60, YAxis_Y+50, 200, 40);
cluster3.setBounds(XAxis_X+60, YAxis_Y+100, 200, 40);
cluster1.setFont(new java.awt.Font("Dialog", 1, 15));
cluster2.setFont(new java.awt.Font("Dialog", 1, 15));
cluster3.setFont(new java.awt.Font("Dialog", 1, 15));
countResult.setBounds(XAxis_X+60, YAxis_Y+150, 400, 40);
countResult.setFont(new java.awt.Font("Dialog", 1, 17));
tip.setBounds(Origin_X, Origin_Y+30, 400, 20);
this.add(bt1);
this.add(bt2);
this.add(bt3);
this.add(bt4);
this.add(cluster1);
this.add(cluster2);
this.add(cluster3);
this.add(countResult);
this.add(tip);
this.add(trendChartCanvas, BorderLayout.CENTER);
this.setVisible(true);
bt1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
stepCount=0;
countflag=1;
km.ReSet();
km.KMeansInit();
repaint();
}
});
bt2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
public void run() {
while (KmeansCalc.flag<KmeansCalc.k){
km.KMeansHandle();
stepCount++;
repaint();
}
}
}).start();
}
});
bt3.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
if (KmeansCalc.flag<KmeansCalc.k){
km.KMeansHandle();
stepCount++;
repaint();
}
}
});
bt4.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
if(countflag!=0){
lab.setText("");
lab.setFont(new java.awt.Font("Dialog", 0, 15));
lab.setLineWrap(true);
lab.setText(KmeansCalc.getData());
JScrollPane jscrollPane = new JScrollPane(lab);
newF.add(jscrollPane);
newF.setBounds(1300, 100, 350, 600);
newF.setVisible(true);
}
}
});
}
class MyCanva extends JPanel {
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
Color c = new Color(200, 70, 0);
g.setColor(c);
super.paintComponent(g);
// 画坐标轴
g2D.setStroke(new BasicStroke(Float.parseFloat("2.0F")));// 轴线粗度
// X轴以及方向箭头
g.drawLine(Origin_X, Origin_Y, XAxis_X, XAxis_Y);// x轴线的轴线
g.drawLine(XAxis_X, XAxis_Y, XAxis_X - 5, XAxis_Y - 5);// 上边箭头
g.drawLine(XAxis_X, XAxis_Y, XAxis_X - 5, XAxis_Y + 5);// 下边箭头
// Y轴以及方向箭头
g.drawLine(Origin_X, Origin_Y, YAxis_X, YAxis_Y);
g.drawLine(YAxis_X, YAxis_Y, YAxis_X - 5, YAxis_Y + 5);
g.drawLine(YAxis_X, YAxis_Y, YAxis_X + 5, YAxis_Y + 5);
// 画X轴上的时间刻度(从坐标轴原点起,每隔TIME_INTERVAL(时间分度)像素画一时间点,到X轴终点止)
g.setColor(Color.BLUE);
g2D.setStroke(new BasicStroke(Float.parseFloat("1.0f")));
// X轴刻度依次变化情况
int xyString=0;
for (int i = Origin_X; i < XAxis_X; i += INTERVAL) {
g.drawString(" " + xyString, i - 10, Origin_Y + 20);
xyString++;
}
g.drawString("X", XAxis_X + 5, XAxis_Y + 5);
xyString=0;
// 画Y轴上刻度
for (int i = Origin_Y; i > YAxis_Y; i -= INTERVAL) {
g.drawString(xyString + " ", Origin_X - 30, i + 3);
xyString++;
}
g.drawString("Y", YAxis_X - 5, YAxis_Y - 5);// 刻度小箭头值
DecimalFormat df = new DecimalFormat("######0.00"); //小数点保留两位
cluster1.setText("No.1Cluster: ("+df.format(KmeansCalc.means[0].getX())+","+df.format(KmeansCalc.means[0].getX())+")");
cluster2.setText("No.2Cluster: ("+df.format(KmeansCalc.means[1].getX())+","+df.format(KmeansCalc.means[1].getY())+")");
cluster3.setText("No.3Cluster: ("+df.format(KmeansCalc.means[2].getX())+","+df.format(KmeansCalc.means[2].getY())+")");
//标签文字
if(countflag==0){
countResult.setText("点击加载数据按钮载入数据");
}
else if(stepCount==0){
//绘制初始元组
for(int i=0;i<KmeansCalc.tuples.size();i++){
g.fillOval(Origin_X+Math.round((KmeansCalc.tuples.get(i).getX())*INTERVAL)-5,
Origin_Y-Math.round((KmeansCalc.tuples.get(i).getY())*INTERVAL)-5, 10, 10);
}
countResult.setText("请选择自动演示或单步演示按钮");
}
else{
//重绘元组颜色
for(int i=0;i<KmeansCalc.k;i++){
for(int j=0;j<KmeansCalc.clusters[i].size();j++){
if(i==0){
g.setColor(Color.gray);
}
else if(i==1){
g.setColor(Color.green);
}
else{
g.setColor(Color.magenta);
}
g.fillOval(Origin_X+Math.round((KmeansCalc.clusters[i].get(j).getX())*INTERVAL)-5,
Origin_Y-Math.round((KmeansCalc.clusters[i].get(j).getY())*INTERVAL)-5, 10, 10);
}
}
if(KmeansCalc.flag<KmeansCalc.k){
countResult.setText("正在运算中...");
}
else{
countResult.setText("计算结束,一共进行了 "+stepCount+" 次迭代。");
}
}
//绘制中心点及连线
for(int i=0;i<KmeansCalc.k;i++){
g.setColor(Color.red);
g.fillOval(Origin_X+Math.round((KmeansCalc.means[i].getX())*INTERVAL)-5,
Origin_Y-Math.round((KmeansCalc.means[i].getY())*INTERVAL)-5, 10, 10);
for(int j=0;j<KmeansCalc.clusters[i].size();j++){
g.drawLine(Origin_X+Math.round((KmeansCalc.means[i].getX())*INTERVAL),
Origin_Y-Math.round((KmeansCalc.means[i].getY())*INTERVAL)
,Origin_X+Math.round((KmeansCalc.clusters[i].get(j).getX())*INTERVAL),
Origin_Y-Math.round((KmeansCalc.clusters[i].get(j).getY())*INTERVAL));
}
}
}
}
public static void main(String[] args){
new KmeansPaint();
}
}
上一篇: Mac EOS 采坑记录
推荐阅读