證券投資書中對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]]);
}
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);