blob: 4e2a8ef15fa011118af95e10b735f982373e2797 [file] [log] [blame]
/*
title: Clustering Process
category: scatter
titleCN: 聚合过程可视化
difficulty: 1
*/
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];
// See https://github.com/ecomfe/echarts-stat
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];
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]);
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
}
};
}
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;
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 + '');
}