253 lines
9.8 KiB
HTML
Generated
253 lines
9.8 KiB
HTML
Generated
<!DOCTYPE html>
|
|
<!--
|
|
Licensed to the Apache Software Foundation (ASF) under one
|
|
or more contributor license agreements. See the NOTICE file
|
|
distributed with this work for additional information
|
|
regarding copyright ownership. The ASF licenses this file
|
|
to you under the Apache License, Version 2.0 (the
|
|
"License"); you may not use this file except in compliance
|
|
with the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing,
|
|
software distributed under the License is distributed on an
|
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
KIND, either express or implied. See the License for the
|
|
specific language governing permissions and limitations
|
|
under the License.
|
|
-->
|
|
|
|
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<script src="lib/simpleRequire.js"></script>
|
|
<script src="lib/config.js"></script>
|
|
<script src="../dist/echarts.js"></script>
|
|
<script src="lib/jquery.min.js"></script>
|
|
<script src="lib/facePrint.js"></script>
|
|
<script src="lib/testHelper.js"></script>
|
|
<link rel="stylesheet" href="lib/reset.css" />
|
|
</head>
|
|
<body>
|
|
<style>
|
|
</style>
|
|
|
|
|
|
|
|
<div id="main-cluster-step"></div>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
require(['echarts', 'ecStat'], function (echarts, ecStat) {
|
|
|
|
var originalData = [[3.275154, 2.957587], [-3.344465, 2.603513], [0.355083, -3.376585], [1.852435, 3.547351], [-2.078973, 2.552013], [-0.993756, -0.884433], [2.682252, 4.007573], [-3.087776, 2.878713], [-1.565978, -1.256985], [2.441611, 0.444826], [-0.659487, 3.111284], [-0.459601, -2.618005], [2.17768, 2.387793], [-2.920969, 2.917485], [-0.028814, -4.168078], [3.625746, 2.119041], [-3.912363, 1.325108], [-0.551694, -2.814223], [2.855808, 3.483301], [-3.594448, 2.856651], [0.421993, -2.372646], [1.650821, 3.407572], [-2.082902, 3.384412], [-0.718809, -2.492514], [4.513623, 3.841029], [-4.822011, 4.607049], [-0.656297, -1.449872], [1.919901, 4.439368], [-3.287749, 3.918836], [-1.576936, -2.977622], [3.598143, 1.97597], [-3.977329, 4.900932], [-1.79108, -2.184517], [3.914654, 3.559303], [-1.910108, 4.166946], [-1.226597, -3.317889], [1.148946, 3.345138], [-2.113864, 3.548172], [0.845762, -3.589788], [2.629062, 3.535831], [-1.640717, 2.990517], [-1.881012, -2.485405], [4.606999, 3.510312], [-4.366462, 4.023316], [0.765015, -3.00127], [3.121904, 2.173988], [-4.025139, 4.65231], [-0.559558, -3.840539], [4.376754, 4.863579], [-1.874308, 4.032237], [-0.089337, -3.026809], [3.997787, 2.518662], [-3.082978, 2.884822], [0.845235, -3.454465], [1.327224, 3.358778], [-2.889949, 3.596178], [-0.966018, -2.839827], [2.960769, 3.079555], [-3.275518, 1.577068], [0.639276, -3.41284]];
|
|
var DIM_CLUSTER_INDEX = 2;
|
|
var DATA_DIM_IDX = [0, 1];
|
|
var CENTER_DIM_IDX = [3, 4];
|
|
|
|
var step = ecStat.clustering.hierarchicalKMeans(originalData, {
|
|
clusterCount: 6,
|
|
outputType: 'single',
|
|
outputClusterIndexDimension: DIM_CLUSTER_INDEX,
|
|
outputCentroidDimensions: CENTER_DIM_IDX,
|
|
stepByStep: true
|
|
});
|
|
|
|
var colorAll = [
|
|
'#bbb', '#37A2DA', '#e06343', '#37a354', '#b55dba', '#b5bd48', '#8378EA', '#96BFFF'
|
|
];
|
|
var ANIMATION_DURATION_UPDATE = 1500;
|
|
|
|
function renderItemPoint(params, api) {
|
|
var coord = api.coord([api.value(0), api.value(1)]);
|
|
var clusterIdx = api.value(2);
|
|
if (clusterIdx == null || isNaN(clusterIdx)) {
|
|
clusterIdx = 0;
|
|
}
|
|
var isNewCluster = clusterIdx === api.value(3);
|
|
|
|
var extra = {
|
|
transition: []
|
|
};
|
|
|
|
var contentColor = colorAll[clusterIdx];
|
|
// addColorTransition(extra, contentColor, 'content');
|
|
|
|
return {
|
|
type: 'circle',
|
|
x: coord[0],
|
|
y: coord[1],
|
|
shape: {
|
|
cx: 0,
|
|
cy: 0,
|
|
r: 10
|
|
},
|
|
extra: extra,
|
|
style: {
|
|
fill: contentColor,
|
|
stroke: '#333',
|
|
lineWidth: 1,
|
|
shadowColor: contentColor,
|
|
shadowBlur: isNewCluster ? 12 : 0,
|
|
transition: ['shadowBlur', 'fill']
|
|
}
|
|
};
|
|
}
|
|
|
|
function renderBoundary(params, api) {
|
|
var xVal = api.value(0);
|
|
var yVal = api.value(1);
|
|
var maxDist = api.value(2);
|
|
var center = api.coord([xVal, yVal]);
|
|
var size = api.size([maxDist, maxDist]);
|
|
var renderProgressStart;
|
|
|
|
return {
|
|
type: 'ellipse',
|
|
shape: {
|
|
cx: isNaN(center[0]) ? 0 : center[0],
|
|
cy: isNaN(center[1]) ? 0 : center[1],
|
|
rx: isNaN(size[0]) ? 0 : size[0] + 15,
|
|
ry: isNaN(size[1]) ? 0 : size[1] + 15
|
|
},
|
|
extra: {
|
|
renderProgress: ++targetRenderProgress,
|
|
enterFrom: {
|
|
renderProgress: 0
|
|
},
|
|
transition: 'renderProgress'
|
|
},
|
|
style: {
|
|
fill: null,
|
|
stroke: 'rgba(0,0,0,0.2)',
|
|
lineDash: [4, 4],
|
|
lineWidth: 4
|
|
},
|
|
during: function (apiDuring) {
|
|
var currProgress = apiDuring.getExtra('renderProgress');
|
|
renderProgressStart == null && (renderProgressStart = currProgress);
|
|
var opacity = (currProgress - renderProgressStart)
|
|
/ (targetRenderProgress - renderProgressStart);
|
|
apiDuring.setStyle('opacity', opacity);
|
|
}
|
|
};
|
|
}
|
|
|
|
function makeStepOption(option, data, centroids) {
|
|
var newCluIdx = centroids ? centroids.length - 1 : -1;
|
|
var maxDist = 0;
|
|
for (var i = 0; i < data.length; i++) {
|
|
var line = data[i];
|
|
if (line[DIM_CLUSTER_INDEX] === newCluIdx) {
|
|
var dist0 = Math.pow(line[DATA_DIM_IDX[0]] - line[CENTER_DIM_IDX[0]], 2);
|
|
var dist1 = Math.pow(line[DATA_DIM_IDX[1]] - line[CENTER_DIM_IDX[1]], 2);
|
|
maxDist = Math.max(maxDist, dist0 + dist1);
|
|
}
|
|
}
|
|
var boundaryData = centroids
|
|
? [
|
|
[
|
|
centroids[newCluIdx][0],
|
|
centroids[newCluIdx][1],
|
|
Math.sqrt(maxDist)
|
|
]
|
|
]
|
|
: [];
|
|
|
|
option.options.push({
|
|
series: [{
|
|
type: 'custom',
|
|
encode: {
|
|
tooltip: [0, 1]
|
|
},
|
|
renderItem: renderItemPoint,
|
|
data: data
|
|
}, {
|
|
type: 'custom',
|
|
renderItem: renderBoundary,
|
|
animationDuration: 3000,
|
|
silent: true,
|
|
data: boundaryData
|
|
}]
|
|
});
|
|
}
|
|
|
|
var targetRenderProgress = 0;
|
|
|
|
var option = {
|
|
timeline: {
|
|
top: 'center',
|
|
right: 50,
|
|
height: 300,
|
|
width: 10,
|
|
inverse: true,
|
|
autoPlay: false,
|
|
playInterval: 2500,
|
|
symbol: 'none',
|
|
orient: 'vertical',
|
|
axisType: 'category',
|
|
label: {
|
|
formatter: 'step {value}',
|
|
position: 10
|
|
},
|
|
checkpointStyle: {
|
|
color: '#555',
|
|
borderWidth: 0,
|
|
animationDuration: ANIMATION_DURATION_UPDATE
|
|
},
|
|
data: []
|
|
},
|
|
baseOption: {
|
|
animationDurationUpdate: ANIMATION_DURATION_UPDATE,
|
|
tooltip: {
|
|
},
|
|
xAxis: {
|
|
type: 'value'
|
|
},
|
|
yAxis: {
|
|
type: 'value'
|
|
},
|
|
series: [{
|
|
type: 'scatter'
|
|
}]
|
|
},
|
|
options: []
|
|
};
|
|
|
|
makeStepOption(option, originalData);
|
|
option.timeline.data.push('0');
|
|
for (var i = 1, stepResult; !(stepResult = step.next()).isEnd; i++) {
|
|
makeStepOption(
|
|
option,
|
|
echarts.util.clone(stepResult.data),
|
|
echarts.util.clone(stepResult.centroids)
|
|
);
|
|
option.timeline.data.push(i + '');
|
|
}
|
|
|
|
var chart = testHelper.create(echarts, 'main-cluster-step', {
|
|
title: [
|
|
'Cluster algorithm visualization'
|
|
],
|
|
height: 600,
|
|
option: option
|
|
});
|
|
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
</body>
|
|
</html>
|
|
|