证券投资书中对K线分了12种,对于输入的股票开盘,收盘,最高,最低好像不太适合完全套用,毕竟不是机器说了算,也是人为分的,总觉得不靠谱(一个屌丝程序员中的毒^_^)。所以还是想要让机器自己判断。
之前一直用scikit-learn直接实现,最近一个前端的朋友也想研究,就用javascript帮忙写了一下,算是记录一下心得吧。
首先介绍一下K均值聚类算法的原理吧。摘要一下百度百科:K均值聚类算法是先随机选取K个对象作为初始的聚类中心。然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是没有(或最小数目)对象被重新分配给不同的聚类,没有(或最小数目)聚类中心再发生变化,误差平方和局部最小。
按照百度百科的描述,很容易整理出流程:
创建K个点作为初始中心;
判断是否满足分类结果
遍历数据集中每个数据点
{
计算数据点到每个中心的距离;
分配数据点到最近中心所定义的簇
}
计算每个簇中所有点的均值并使其作为该簇新的中心
满足分类结果输出结果
最重要的是计算两个点的距离,这里直接使用欧式距离作为距离函数,实现代码:
function distEclud(vecA,vecB){
var sum = 0;
for(var i = 0;i < vecA.length;i++){
var deta = vecA[i] - vecB[i];
sum = sum + deta * deta;
}
return Math.sqrt(sum);
}
其次就是随机产生K个初始中心:
function randCent(dataSet,k){
//var n = dataSet[0].length;
//var minJ = $M.min(dataSet,1);
//var rangeJ = $V.sub($M.max(dataSet,1),minJ);
var centroids = []
for(var i = 0;i < k;i++){
//centroids.push($V.add(minJ,$V.mul(rangeJ,$V.rand(n))));
centroids.push(dataSet[i]);
}
return centroids;
}
上面非注释代码是将数据集的K个数据点作为中心数据,也可以采用注释的代码,注释代码产生K个随机数据点,$M.min函数计算矩阵(二维数组)列最小值,$M.max计算最大值,$V.add,$V.mul分别计算数组的加与乘,$V.rand生成n维随机数组。
接下来就是对数据集进行聚类,实现代码如下:
function kMeans(dataSet,k){
var m = dataSet.length;
var clusterAssment0 = [];
var clusterAssment1 = [];
for(var i = 0;i < m;i++){
clusterAssment0.push(0);
clusterAssment1.push(1);
}
var centroids = randCent(dataSet,k);
var clusterChanged = true;
while(clusterChanged){
clusterChanged = false;
for(var i = 0;i < m;i++){
var minDist = 10000000;minIndex = -1;
for(var j = 0;j < k;j++){
var distJI = distEclud(centroids[j],dataSet[i]);
if(distJI < minDist){
minDist = distJI;
minIndex = j;
}
}
if(clusterAssment0[i] != minIndex){
clusterChanged = true;
}
clusterAssment0[i] = minIndex;
clusterAssment1[i] = minDist * minDist;
}
for(var i = 0;i < k;i++){
var ptsInClust = $M.subm(dataSet,$V.where(clusterAssment0,"==",i),0);
if(ptsInClust.length == 0){
continue;
}
centroids[i] = $M.mean(ptsInClust,1);
}
}
return {
centroids:centroids,
cluster:clusterAssment0
};
}
结果返回聚类中心与聚类结果。
以上K均值聚类的代码已经实现。
接下来就是使用K均值聚类算法应用到股票数据,股票数据我们使用腾讯数据,我们采用2017年浦发银行的日K作为数据集合,代码:<script src = "http://data.gtimg.cn/flashdata/hushen/daily/17/sh600000.js"></script>
对数据的解析代码如下:
var ev_data = daily_data_17.split("\n");
var open = [];
var high = [], low = [],close = [],volume = [],date = [];bar = [];
for(var i = 1;i < ev_data.length - 1;i++){
var es = ev_data[i].split(" ");
date.push(es[0]);
open.push(es[1]);
close.push(es[2]);
high.push(es[3]);
low.push(es[4]);
volume.push(es[5]);
bar.push([es[1],es[2],es[4],es[3],es[5]]);
}
为了计算K线的形态,股价的大小不能作为特征值的大小,开盘、收盘等之间存在关联,所以我们要对各个数据进行整理,下面是我对数据处理的方式:
var data = [];
for(var i = 1;i < close.length;i++){
var mean = (parseFloat(close[i]) + parseFloat(open[i]) + parseFloat(high[i]) + parseFloat(low[i]))/4;
var tmp = [((parseFloat(high[i]) - mean) == 0)?1:(parseFloat(close[i]) - mean)/(parseFloat(high[i]) - mean),
((parseFloat(high[i]) - mean) == 0)?1:(parseFloat(low[i]) - mean)/(parseFloat(high[i]) - mean),
((parseFloat(high[i]) - mean) == 0)?1:(parseFloat(open[i]) - mean)/(parseFloat(high[i]) - mean),
(volume[i] - volume[i - 1])/volume[i - 1]
]
data.push(tmp);
}
var max = $M.max(data,1);
var min = $M.min(data,1);
var tz = $M.div_vector($M.sub_vector(data,min,1),$V.sub(max,min),1);
将tz作为数据集输入K均值聚类模型,并画出K线图和分类图:
var result = kMeans(tz,12);
聚类结果如下:
0:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
转载于:https://blog.51cto.com/janwool/2058124