UNPKG

@visactor/vmind

Version:

<div align="center"> <a href="https://github.com/VisActor#gh-light-mode-only" target="_blank"> <img alt="VisActor Logo" width="200" src="https://github.com/VisActor/.github/blob/main/profile/logo_500_200_light.svg"/> </a> <a href="https://githu

61 lines (50 loc) 3.05 kB
import { isArray } from "@visactor/vutils"; import { InsightType } from "../../type"; import { ChartType } from "../../../../types"; import { getIntersection, isValidData } from "../../../../utils/common"; import { getMeanAndstdDev } from "../statistics"; import { isPercenSeries } from "../../utils"; export function getAbnormalByZScores(data, threshold = 3) { const {mean: mean, stdDev: stdDev} = getMeanAndstdDev(data.map((v => v.value))); return data.filter((v => Math.abs((v.value - mean) / stdDev) >= threshold)).map((v => v.index)); } function calculateQuantile(sortedData, quantile) { const pos = (sortedData.length - 1) * quantile, base = Math.floor(pos), rest = pos - base; return void 0 !== sortedData[base + 1] ? sortedData[base] + rest * (sortedData[base + 1] - sortedData[base]) : sortedData[base]; } export function getAbnormalByIQR(data) { const sortedData = data.slice().sort(((a, b) => a.value - b.value)), dataList = sortedData.map((v => v.value)), q1 = calculateQuantile(dataList, .25), q3 = calculateQuantile(dataList, .75), iqr = q3 - q1, lowerBound = q1 - 1.5 * iqr, upperBound = q3 + 1.5 * iqr; return sortedData.filter((dataPoint => dataPoint.value < lowerBound || dataPoint.value > upperBound)).map((v => v.index)); } const zscoreIQRAlgoFunc = (context, options) => { const result = [], {threshold: threshold = 3} = options || {}, {seriesDataMap: seriesDataMap, cell: cell, spec: spec} = context, {y: celly} = cell, yField = isArray(celly) ? celly.flat() : [ celly ]; return Object.keys(seriesDataMap).forEach((group => { const dataset = seriesDataMap[group]; yField.forEach((field => { if (isPercenSeries(spec, field)) return; const dataList = dataset.map(((d, index) => ({ index: index, value: Number(d.dataItem[field]) }))).filter((v => isValidData(v.value) && !isNaN(v.value))), zScoreResult = dataList.length >= 30 ? getAbnormalByZScores(dataList, threshold) : null, iqrResult = dataList.length >= 10 ? getAbnormalByIQR(dataList) : null; ((zScoreResult ? getIntersection(zScoreResult, iqrResult) : iqrResult) || []).forEach((index => { const insightDataItem = dataset[index], lofInsight = { type: InsightType.Outlier, data: [ insightDataItem ], fieldId: field, value: insightDataItem.dataItem[field], significant: 1, seriesName: group }; result.push(lofInsight); })); })); })), result; }; export const StatisticsAlo = { name: "statistics", forceChartType: [ ChartType.DualAxisChart, ChartType.LineChart, ChartType.BarChart, ChartType.AreaChart, ChartType.RadarChart, ChartType.PieChart, ChartType.RoseChart, ChartType.WaterFallChart ], insightType: InsightType.Outlier, algorithmFunction: zscoreIQRAlgoFunc, supportPercent: !1 }; //# sourceMappingURL=statistics.js.map