您现在的位置是:网站首页 > 异常检测可视化文章详情
异常检测可视化
陈川
【
ECharts
】
52089人已围观
9502字
异常检测可视化的核心需求
异常检测可视化需要直观展示数据中的离群点、异常模式或突发变化。传统表格形式难以快速识别异常,而可视化能将数据分布、趋势和异常值转化为图形元素,利用颜色、形状、大小等视觉通道突出异常。ECharts 提供了丰富的图表类型和交互功能,特别适合构建动态、多维度的异常检测可视化方案。
ECharts 基础图表选择
折线图与面积图
折线图能清晰展示时间序列数据的波动情况,通过标记点或分段着色突出异常值:
option = {
xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] },
yAxis: { type: 'value' },
series: [{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
markPoint: {
data: [
{ type: 'max', name: 'Max' },
{ type: 'min', name: 'Min' }
]
},
markArea: {
data: [[
{ xAxis: 'Tue' },
{ xAxis: 'Thu' }
]]
}
}]
};
散点图与气泡图
散点图通过坐标位置和点大小展示多维数据异常:
option = {
xAxis: { scale: true },
yAxis: { scale: true },
series: [{
type: 'scatter',
symbolSize: function (data) {
return Math.sqrt(data[2]) * 5;
},
data: [
[10.0, 8.04, 10], [8.0, 6.95, 20],
[13.0, 7.58, 50], [9.0, 8.81, 10],
[11.0, 8.33, 30], [14.0, 9.96, 10],
[6.0, 7.24, 25], [4.0, 4.26, 100], // 异常点
[12.0, 10.84, 9], [7.0, 4.82, 15],
[5.0, 5.68, 12]
],
itemStyle: {
color: function(params) {
return params.data[2] > 50 ? '#ff0000' : '#5470c6';
}
}
}]
};
高级异常检测可视化技术
箱线图与离群点
箱线图自动计算并显示四分位数和离群值:
option = {
dataset: [{
source: [
[850, 740, 900, 1070, 930, 850, 950],
[960, 940, 960, 940, 880, 800, 850],
[880, 800, 850, 880, 840, 790, 810],
[890, 840, 780, 810, 760, 810, 1000] // 包含异常值
]
}, {
transform: {
type: 'boxplot',
config: { itemNameFormatter: 'expr {value}' }
}
}],
xAxis: { type: 'category' },
yAxis: { type: 'value' },
series: [{
name: 'boxplot',
type: 'boxplot',
datasetIndex: 1
}]
};
热力图与矩阵图
热力图通过颜色梯度展示数据密度和异常区域:
option = {
tooltip: { position: 'top' },
grid: { height: '50%', top: '10%' },
xAxis: { type: 'category', data: ['12a', '1a', '2a', '3a', '4a', '5a'] },
yAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'] },
visualMap: {
min: 0,
max: 10,
calculable: true,
orient: 'horizontal',
left: 'center',
bottom: '0%'
},
series: [{
name: '异常频率',
type: 'heatmap',
data: [
[0, 0, 5], [1, 0, 7], [2, 0, 3],
[0, 1, 1], [1, 1, 2], [2, 1, 4],
[0, 2, 10], [1, 2, 1], [2, 2, 0], // 异常点
[0, 3, 1], [1, 3, 1], [2, 3, 3],
[0, 4, 2], [1, 4, 6], [2, 4, 2]
],
itemStyle: {
borderColor: '#333'
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
动态异常检测方案
实时数据流监控
结合 WebSocket 实现实时异常检测:
// 模拟实时数据
function randomData() {
now = new Date(+now + oneDay);
value = Math.random() * 10;
if(Math.random() > 0.95) value += 15; // 5%概率生成异常值
return { name: now.toString(), value: [[now.getFullYear(), now.getMonth()+1, now.getDate()].join('/'), value] };
}
let data = [];
let now = new Date(2023, 0, 1);
let oneDay = 24 * 3600 * 1000;
for(let i = 0; i < 100; i++) data.push(randomData());
option = {
title: { text: '动态异常检测' },
tooltip: { trigger: 'axis' },
xAxis: { type: 'time' },
yAxis: { type: 'value', boundaryGap: [0, '100%'] },
series: [{
name: '模拟数据',
type: 'line',
showSymbol: false,
data: data,
markPoint: {
data: [{
type: 'max', name: '最大值'
}, {
type: 'min', name: '最小值'
}],
symbolSize: 60,
label: {
formatter: function(params) {
return params.value > 15 ? '异常!' : params.name;
},
color: function(params) {
return params.value > 15 ? '#ff0000' : '#333';
}
}
}
}]
};
// 模拟实时更新
setInterval(function() {
for(let i = 0; i < 5; i++) data.shift();
for(let i = 0; i < 5; i++) data.push(randomData());
myChart.setOption({ series: [{ data: data }] });
}, 1000);
多维度联动分析
实现多视图联动的异常检测面板:
option = {
tooltip: { trigger: 'axis' },
legend: { data: ['CPU', '内存', '磁盘'] },
grid: [
{ left: '10%', top: '10%', width: '80%', height: '25%' },
{ left: '10%', top: '40%', width: '35%', height: '25%' },
{ left: '55%', top: '40%', width: '35%', height: '25%' }
],
xAxis: [
{ gridIndex: 0, type: 'category', data: ['1月','2月','3月','4月','5月','6月'] },
{ gridIndex: 1, type: 'category', data: ['1月','2月','3月','4月','5月','6月'] },
{ gridIndex: 2, type: 'category', data: ['1月','2月','3月','4月','5月','6月'] }
],
yAxis: [
{ gridIndex: 0 },
{ gridIndex: 1 },
{ gridIndex: 2 }
],
series: [
{
name: 'CPU',
type: 'line',
xAxisIndex: 0,
yAxisIndex: 0,
data: [65, 72, 83, 79, 95, 90],
markArea: {
silent: true,
data: [[
{ xAxis: '4月' },
{ xAxis: '5月' }
]]
}
},
{
name: '内存',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
data: [45, 52, 38, 60, 48, 33]
},
{
name: '磁盘',
type: 'scatter',
xAxisIndex: 2,
yAxisIndex: 2,
symbolSize: function(data) {
return data[1] * 2;
},
data: [
['1月', 25], ['2月', 40], ['3月', 10],
['4月', 35], ['5月', 70], ['6月', 20] // 5月为异常点
],
itemStyle: {
color: function(params) {
return params.data[1] > 60 ? '#ff0000' : '#5470c6';
}
}
}
]
};
自定义视觉编码策略
阈值区间着色
在折线图中根据阈值范围着色不同区域:
option = {
xAxis: { type: 'category', boundaryGap: false },
yAxis: { type: 'value' },
series: [{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'line',
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 0, 0, 0.3)' }, // 高于阈值
{ offset: 0.5, color: 'rgba(255, 255, 0, 0.3)' }, // 警告阈值
{ offset: 1, color: 'rgba(0, 255, 0, 0.3)' } // 正常范围
])
},
markLine: {
silent: true,
data: [{
yAxis: 150, // 警告阈值
lineStyle: { color: '#ff0' }
}, {
yAxis: 100, // 正常阈值
lineStyle: { color: '#0f0' }
}]
}
}]
};
异常点脉冲动画
为异常数据点添加特殊动画效果:
option = {
xAxis: { data: ['A', 'B', 'C', 'D', 'E', 'F'] },
yAxis: {},
series: [{
name: '数据',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
itemStyle: {
color: function(params) {
return params.data > 30 ? {
type: 'radial',
x: 0.5,
y: 0.5,
r: 0.5,
colorStops: [
{ offset: 0, color: '#ff0000' },
{ offset: 0.7, color: '#ff0000' },
{ offset: 1, color: 'transparent' }
]
} : '#5470c6';
}
}
}],
graphic: [{
type: 'circle',
shape: { r: 0 },
keyframeAnimation: {
duration: 2000,
loop: true,
keyframes: [{
percent: 0,
shape: { r: 0 },
style: { opacity: 0.8 }
}, {
percent: 1,
shape: { r: 20 },
style: { opacity: 0 }
}]
},
position: [540, 100] // 异常点位置
}]
};
交互式异常分析
数据下钻与筛选
实现从概览到细节的异常分析流程:
// 第一级视图 - 概览
const overviewOption = {
dataset: {
dimensions: ['product', '2015', '2016', '2017', '2018', '2019', '2020'],
source: [
{ product: '产品A', '2015': 43, '2016': 85, '2017': 93, '2018': 90, '2019': 106, '2020': 120 },
{ product: '产品B', '2015': 83, '2016': 73, '2017': 55, '2018': 89, '2019': 104, '2020': 30 }, // 异常
{ product: '产品C', '2015': 86, '2016': 65, '2017': 82, '2018': 96, '2019': 107, '2020': 101 }
]
},
tooltip: { trigger: 'axis' },
legend: {},
xAxis: { type: 'category' },
yAxis: {},
series: [
{ type: 'bar' },
{ type: 'bar' },
{ type: 'bar' }
]
};
// 第二级视图 - 详情
function getDetailOption(productName) {
return {
dataset: {
dimensions: ['month', 'sales'],
source: [
{ month: '1月', sales: Math.floor(Math.random() * 100) },
{ month: '2月', sales: Math.floor(Math.random() * 100) },
// ...其他月份数据
{ month: '12月', sales: productName === '产品B' ? 5 : Math.floor(Math.random() * 100) } // 异常
]
},
title: { text: `${productName}月度销售详情` },
xAxis: { type: 'category' },
yAxis: {},
series: [{
type: 'line',
markPoint: {
data: [
{ type: 'max', name: '最大值' },
{ type: 'min', name: '最小值' }
]
}
}]
};
}
// 点击事件处理
myChart.on('click', function(params) {
if(params.componentType === 'series') {
myChart.setOption(getDetailOption(params.seriesName));
}
});
异常模式标注
在图表中直接标注异常模式:
option = {
xAxis: { data: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'] },
yAxis: {},
series: [{
name: '销售额',
type: 'line',
data: [120, 132, 101, 134, 290, 230, 210, 132, 101, 134, 90, 230],
markPoint: {
symbol: 'pin',
symbolSize: 50,
data: [
{
coord: ['5月', 290],
label: {
formatter: '突增异常\n+150%',
color: '#fff',
fontSize: 14
},
itemStyle: { color: '#f00' }
},
{
coord: ['11月', 90],
label: {
formatter: '骤降异常\n-60%',
color: '#fff',
fontSize: 14
},
itemStyle: { color: '#00f' }
}
]
}
}]
};