详解Java实现的k-means聚类算法

需求

对MySQL数据库中某个表的某个字段执行k-means算法,将处理后的数据写入新表中。

源码及驱动

kmeans_jb51.rar

源码

import java.sql.*;
import java.util.*;

/**
 * @author tianshl
 * @version 2018/1/13 上午11:13
 */
public class Kmeans {
  // 源数据
  private List<Integer> origins = new ArrayList<>();

  // 分组数据
  private Map<Double, List<Integer>> grouped;

  // 初始质心列表
  private List<Double> cores;

  // 数据源
  private String tableName;
  private String colName;

  /**
   * 构造方法
   *
   * @param tableName 源数据表名称
   * @param colName  源数据列名称
   * @param cores   质心列表
   */
  private Kmeans(String tableName, String colName,List<Double> cores){
    this.cores = cores;
    this.tableName = tableName;
    this.colName = colName;
  }

  /**
   * 重新计算质心
   *
   * @return 新的质心列表
   */
  private List<Double> newCores(){
    List<Double> newCores = new ArrayList<>();

    for(List<Integer> v: grouped.values()){
      newCores.add(v.stream().reduce(0, (sum, num) -> sum + num) / (v.size() + 0.0));
    }

    Collections.sort(newCores);
    return newCores;
  }

  /**
   * 判断是否结束
   *
   * @return bool
   */
  private Boolean isOver(){
    List<Double> _cores = newCores();
    for(int i=0, len=cores.size(); i<len; i++){
      if(!cores.get(i).toString().equals(_cores.get(i).toString())){
        // 使用新质心
        cores = _cores;
        return false;
      }
    }
    return true;
  }

  /**
   * 数据分组
   */
  private void setGrouped(){
    grouped = new HashMap<>();

    Double core;
    for (Integer origin: origins) {
      core = getCore(origin);

      if (!grouped.containsKey(core)) {
        grouped.put(core, new ArrayList<>());
      }

      grouped.get(core).add(origin);
    }
  }

  /**
   * 选择质心
   *
   * @param num  要分组的数据
   * @return   质心
   */
  private Double getCore(Integer num){

    // 差 列表
    List<Double> diffs = new ArrayList<>();

    // 计算差
    for(Double core: cores){
      diffs.add(Math.abs(num - core));
    }

    // 最小差 -> 索引 -> 对应的质心
    return cores.get(diffs.indexOf(Collections.min(diffs)));
  }

  /**
   * 建立数据库连接
   * @return connection
   */
  private Connection getConn(){
    try {
      // URL指向要访问的数据库名mydata
      String url = "jdbc:mysql://localhost:3306/data_analysis_dev";
      // MySQL配置时的用户名
      String user = "root";
      // MySQL配置时的密码
      String password = "root";

      // 加载驱动
      Class.forName("com.mysql.jdbc.Driver");

      //声明Connection对象
      Connection conn = DriverManager.getConnection(url, user, password);

      if(conn.isClosed()){
        System.out.println("连接数据库失败!");
        return null;
      }
      System.out.println("连接数据库成功!");

      return conn;

    } catch (Exception e) {
      System.out.println("连接数据库失败!");
      e.printStackTrace();
    }

    return null;
  }

  /**
   * 关闭数据库连接
   *
   * @param conn 连接
   */
  private void close(Connection conn){
    try {
      if(conn != null && !conn.isClosed()) conn.close();
    } catch (Exception e){
      e.printStackTrace();
    }
  }

  /**
   * 获取源数据
   */
  private void getOrigins(){

    Connection conn = null;
    try {
      conn = getConn();
      if(conn == null) return;

      Statement statement = conn.createStatement();

      ResultSet rs = statement.executeQuery(String.format("select %s from %s", colName, tableName));

      while(rs.next()){
        origins.add(rs.getInt(1));
      }
      conn.close();
    } catch (Exception e){
      e.printStackTrace();
    } finally {
     close(conn);
    }
  }

  /**
   * 向新表中写数据
   */
  private void write(){

    Connection conn = null;
    try {
      conn = getConn();
      if(conn == null) return;

      // 创建表
      Statement statement = conn.createStatement();

      // 删除旧数据表
      statement.execute("DROP TABLE IF EXISTS k_means; ");
      // 创建新表
      statement.execute("CREATE TABLE IF NOT EXISTS k_means(`core` DECIMAL(11, 7), `col` INTEGER(11));");

      // 禁止自动提交
      conn.setAutoCommit(false);

      PreparedStatement ps = conn.prepareStatement("INSERT INTO k_means VALUES (?, ?)");

      for(Map.Entry<Double, List<Integer>> entry: grouped.entrySet()){
        Double core = entry.getKey();
        for(Integer value: entry.getValue()){
          ps.setDouble(1, core);
          ps.setInt(2, value);
          ps.addBatch();
        }
      }

      // 批量执行
      ps.executeBatch();

      // 提交事务
      conn.commit();

      // 关闭连接
      conn.close();
    } catch (Exception e){
      e.printStackTrace();
    } finally {
      close(conn);
    }
  }

  /**
   * 处理数据
   */
  private void run(){
    System.out.println("获取源数据");
    // 获取源数据
    getOrigins();

    // 停止分组
    Boolean isOver = false;

    System.out.println("数据分组处理");
    while(!isOver) {
      // 数据分组
      setGrouped();
      // 判断是否停止分组
      isOver = isOver();
    }

    System.out.println("将处理好的数据写入数据库");
    // 将分组数据写入新表
    write();

    System.out.println("写数据完毕");
  }

  public static void main(String[] args){
    List<Double> cores = new ArrayList<>();
    cores.add(260.0);
    cores.add(600.0);
    // 表名, 列名, 质心列表
    new Kmeans("attributes", "attr_length", cores).run();
  }
}

源文件

Kmeans.java

编译

javac Kmeans.java 

运行

# 指定依赖库
java -Djava.ext.dirs=./lib Kmeans

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • K-means聚类算法介绍与利用python实现的代码示例
  • python中实现k-means聚类算法详解
(0)

相关推荐

  • K-means聚类算法介绍与利用python实现的代码示例

    聚类 今天说K-means聚类算法,但是必须要先理解聚类和分类的区别,很多业务人员在日常分析时候不是很严谨,混为一谈,其实二者有本质的区别. 分类其实是从特定的数据中挖掘模式,作出判断的过程.比如Gmail邮箱里有垃圾邮件分类器,一开始的时候可能什么都不过滤,在日常使用过程中,我人工对于每一封邮件点选"垃圾"或"不是垃圾",过一段时间,Gmail就体现出一定的智能,能够自动过滤掉一些垃圾邮件了.这是因为在点选的过程中,其实是给每一条邮件打了一个"标签&qu

  • python中实现k-means聚类算法详解

    算法优缺点: 优点:容易实现 缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢 使用数据类型:数值型数据 算法思想 k-means算法实际上就是通过计算不同样本间的距离来判断他们的相近关系的,相近的就会放到同一个类别中去. 1.首先我们需要选择一个k值,也就是我们希望把数据分成多少类,这里k值的选择对结果的影响很大,Ng的课说的选择方法有两种一种是elbow method,简单的说就是根据聚类的结果和k的函数关系判断k为多少的时候效果最好.另一种则是根据具体的需求确定,比如说进行衬衫尺寸的聚

  • 详解Java分布式系统中一致性哈希算法

    业务场景 近年来B2C.O2O等商业概念的提出和移动端的发展,使得分布式系统流行了起来.分布式系统相对于单系统,解决了流量大.系统高可用和高容错等问题.功能强大也意味着实现起来需要更多技术的支持.例如系统访问层的负载均衡,缓存层的多实例主从复制备份,数据层的分库分表等. 我们以负载均衡为例,常见的负载均衡方法有很多,但是它们的优缺点也都很明显: 随机访问策略.系统随机访问,缺点:可能造成服务器负载压力不均衡,俗话讲就是撑的撑死,饿的饿死. 轮询策略.请求均匀分配,如果服务器有性能差异,则无法实现

  • 详解Java如何实现数值校验的算法

    给定一个字符串如何判断它是否为数值类型?例如:字符串+100.5e2.-123.3.1416以及-1E-16都表示数值,为数值类型,但12e.1a3.14.1.2.3.+-5以及12e+5.4都不是. 本文将带着大家实现这个判断算法,欢迎各位感兴趣的开发者阅读本文. 实现思路 我们先来看一下数值的定义规则:表示数值的字符串遵循模式A[.[B]][e|EC]或者.B[e|EC],其中: A为数值的整数部分 B紧跟着小数点为数值的小数部分 C紧跟着e或者E为数值的指数部分 在小数里可能没有数值的整数

  • 详解Java中Dijkstra(迪杰斯特拉)算法的图解与实现

    目录 简介 工作过程 总体思路 实现 小根堆 Dijsktra 测试 简介 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等.注意该算法要求图中不存在负权边.对应问题:在无向图G=(V,E)中,假设每条边E(i)的长度W(i),求由顶点V0到各节点的最短路径. 工作过

  • 详解Java中使用泛型实现快速排序算法的方法

    快速排序算法概念 快速排序一般基于递归实现.其思路是这样的: 1.选定一个合适的值(理想情况中值最好,但实现中一般使用数组第一个值),称为"枢轴"(pivot). 2.基于这个值,将数组分为两部分,较小的分在左边,较大的分在右边. 3.可以肯定,如此一轮下来,这个枢轴的位置一定在最终位置上. 4.对两个子数组分别重复上述过程,直到每个数组只有一个元素. 5.排序完成. 基本实现方式: public static void quickSort(int[] arr){ qsort(arr,

  • K均值聚类算法的Java版实现代码示例

    1.简介 K均值聚类算法是先随机选取K个对象作为初始的聚类中心.然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心.聚类中心以及分配给它们的对象就代表一个聚类.一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算.这个过程将不断重复直到满足某个终止条件.终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小. 2.什么是聚类 聚类是一个将数据集中在某些方面相似的数据成员进行分类组织

  • 详解Java中的 枚举与泛型

    详解Java中的 枚举与泛型 一:首先从枚举开始说起 枚举类型是JDK5.0的新特征.Sun引进了一个全新的关键字enum来定义一个枚举类.下面就是一个典型枚举类型的定义: public enum Color{ RED,BLUE,BLACK,YELLOW,GREEN } 显然,enum很像特殊的class,实际上enum声明定义的类型就是一个类. 而这些类都是类库中Enum类的子类(Java.lang.Enum).它们继承了这个Enum中的许多有用的方法.我们对代码编译之后发现,编译器将 enu

  • 详解java面试题中的i++和++i

    代码如下所示: public class TestPlusPlus{ public static void main(String[] args){ int k = addAfterReturn(10); System.out.println(k); //输出 10 int k1 = addbeforeReturn(10); System.out.println(k1); //输出11 } public static int addbeforeReturn(int i){ return ++i;

  • 详解Java中AbstractMap抽象类

    jdk1.8.0_144 下载地址:http://www.jb51.net/softs/551512.html AbstractMap抽象类实现了一些简单且通用的方法,本身并不难.但在这个抽象类中有两个方法非常值得关注,keySet和values方法源码的实现可以说是教科书式的典范. 抽象类通常作为一种骨架实现,为各自子类实现公共的方法.上一篇我们讲解了Map接口,此篇对AbstractMap抽象类进行剖析研究. Java中Map类型的数据结构有相当多,AbstractMap作为它们的骨架实现实

  • 详解Java分布式IP限流和防止恶意IP攻击方案

    前言 限流是分布式系统设计中经常提到的概念,在某些要求不严格的场景下,使用Guava RateLimiter就可以满足.但是Guava RateLimiter只能应用于单进程,多进程间协同控制便无能为力.本文介绍一种简单的处理方式,用于分布式环境下接口调用频次管控. 如何防止恶意IP攻击某些暴露的接口呢(比如某些场景下短信验证码服务)?本文介绍一种本地缓存和分布式缓存集成方式判断远程IP是否为恶意调用接口的IP. 分布式IP限流 思路是使用redis incr命令,完成一段时间内接口请求次数的统

  • 详解Java的初始化与清理

    大家都知道,Java是站在巨人的肩上成功的,它是在C&C++的基础上进一步的开发,投入面向对象开发的怀抱.Java吸取了很多以前的教训,加入自己很多独创的方式.在程序语言发展初期,许多C程序员经常忘记初始化变量,在程序结束后也经常忘记对创建的数据类型进行释放内存,造成内存泄漏.这些"不安全"的编程方式当然需要程序员有良好的编程习惯,但如果编程语言能够加入自动清理与初始化的工作,这回大大降低开发成本.随着技术的发展,C++语言引入了构造器(constructor),即在创建对象自

随机推荐