Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ava/insight): optimize insight marks #758

Merged
merged 17 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/ava/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@antv/ava",
"version": "3.2.0",
"version": "3.3.0",
"description": "A framework for automated visual analytics.",
"author": {
"name": "AntV",
Expand Down
4 changes: 3 additions & 1 deletion packages/ava/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export type {
} from './data';

/* insight */
export { getInsights, generateInsightVisualizationSpec, insightPatternsExtractor } from './insight';
export { getInsights, generateInsightVisualizationSpec, insightPatternsExtractor, getSpecificInsight } from './insight';
export type {
Datum,
DomainType,
Expand Down Expand Up @@ -125,6 +125,8 @@ export type {
CorrelationInfo,
InsightsResult,
InsightExtractorProps,
SpecificInsightProps,
AugmentedMarks,
} from './insight';

/* NTV (Narrative Text Vis) */
Expand Down
26 changes: 0 additions & 26 deletions packages/ava/src/insight/chart/generator/insights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,8 @@ import {
correlationStrategy,
timeSeriesOutlierStrategy,
trendStrategy,
trendAugmentedMarksStrategy,
categoryOutlierAugmentedMarksStrategy,
changePointAugmentedMarksStrategy,
lowVarianceAugmentedMarkStrategy,
viewSpecStrategy,
} from '../strategy';
import { LineMarkConfig, PointMarkConfig, TextMarkConfig } from '../types';

// The generateInsightAugmentedMarks needs to be refactored, it's not exported now.
export function generateInsightAugmentedMarks(
insight: InsightInfo<PatternInfo>,
markStyleConfig?: TextMarkConfig | LineMarkConfig | PointMarkConfig
): G2Spec {
const { type: insightType } = insight?.patterns[0];

const insightType2Strategy: Record<
string,
(insight: InsightInfo<PatternInfo>, markStyleConfig?: TextMarkConfig | LineMarkConfig | PointMarkConfig) => Mark[]
> = {
trend: trendAugmentedMarksStrategy,
category_outlier: categoryOutlierAugmentedMarksStrategy,
change_point: changePointAugmentedMarksStrategy,
low_variance: lowVarianceAugmentedMarkStrategy,
};

const augmentedMarks = insightType2Strategy[insightType]?.(insight, markStyleConfig);
return augmentedMarks;
}

export function generateInsightChartSpec(insight: InsightInfo<PatternInfo>): G2Spec {
const { type: insightType } = insight.patterns[0];
Expand Down
1 change: 1 addition & 0 deletions packages/ava/src/insight/chart/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './generator/homogeneous';
export * from './generator/insights';
export * from './strategy/augmentedMarks';
export * from './types';
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
import { Mark } from '@antv/g2';
import { size } from 'lodash';

import { dataFormat } from '../../../../utils';
import { InsightInfo, CategoryOutlierInfo } from '../../../types';
import { BOLD_FONT_WEIGHT } from '../../constants';
import { insight2ChartStrategy } from '../chart';
import { textMarkStrategy, intervalMarkStrategy } from '../commonMarks';
import { CategoryOutlierMark } from '../../types';
import { augmentedMarks2Marks } from '../../utils';

export const categoryOutlierAugmentedMarksStrategy = (insight: InsightInfo<CategoryOutlierInfo>): Mark[] => {
export const categoryOutlierAugmentedMarksStrategy = (
insight: InsightInfo<CategoryOutlierInfo>
): CategoryOutlierMark[] => {
const { patterns } = insight;
const rectMark = intervalMarkStrategy(patterns);
const textMark = textMarkStrategy(patterns, {
style: { fontWeight: BOLD_FONT_WEIGHT, dy: -8 },
formatter: dataFormat,

if (!size(patterns)) return [];

const categoryOutlierMarks: CategoryOutlierMark[] = [];
patterns.forEach((pattern) => {
const rectMark = intervalMarkStrategy([pattern]);
const textMark = textMarkStrategy([pattern], {
style: { fontWeight: BOLD_FONT_WEIGHT, dy: -8 },
formatter: dataFormat,
});
categoryOutlierMarks.push({
categoryOutlier: [rectMark, textMark],
});
});
return [rectMark, textMark];

return categoryOutlierMarks;
};

export const categoryOutlierStrategy = (insight: InsightInfo<CategoryOutlierInfo>): Mark[] => {
const chart = insight2ChartStrategy(insight);
const augmentedMarks = categoryOutlierAugmentedMarksStrategy(insight);
return [chart, ...augmentedMarks];
const categoryOutlierMarks = categoryOutlierAugmentedMarksStrategy(insight);
const marks = augmentedMarks2Marks(categoryOutlierMarks);
return [chart, ...marks];
};
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
import { Mark } from '@antv/g2';
import { Mark, PointMark, TextMark } from '@antv/g2';
import { size } from 'lodash';

import { ChangePointInfo, InsightInfo } from '../../../types';
import { INSIGHT_COLOR_PLATTE } from '../../constants';
import { dataFormat } from '../../../../utils';
import { pointMarkStrategy } from '../commonMarks/pointMark';
import { textMarkStrategy } from '../commonMarks/textMark';
import { insight2ChartStrategy } from '../chart';
import { ChangePointMark } from '../../types';
import { augmentedMarks2Marks } from '../../utils';

export const changePointAugmentedMarksStrategy = (insight: InsightInfo<ChangePointInfo>): Mark[] => {
export const changePointAugmentedMarksStrategy = (insight: InsightInfo<ChangePointInfo>): ChangePointMark[] => {
const { patterns } = insight;
const color = INSIGHT_COLOR_PLATTE.highlight;
const { measure } = patterns?.[0];
const pointMark = pointMarkStrategy(patterns, { style: { fill: color } });
const textMark = textMarkStrategy(patterns, {
formatter: dataFormat,
label: (pattern) => `${pattern.x}, ${measure}: ${pattern.y}`,
style: {
dy: -20,
background: true,
backgroundRadius: 2,
connector: true,
startMarker: true,
startMarkerFill: '#2C3542',
startMarkerFillOpacity: 0.65,
},

if (!size(patterns)) return [];

const { measure } = patterns[0];
const changePointMarks: ChangePointMark[] = [];
patterns.forEach((pattern) => {
const pointMark = pointMarkStrategy([pattern], { style: { fill: color } }) as PointMark;
const textMark = textMarkStrategy([pattern], {
formatter: dataFormat,
label: (pt) => `${pt.x}, ${measure}: ${pt.y}`,
style: {
dy: -20,
background: true,
backgroundRadius: 2,
connector: true,
startMarker: true,
startMarkerFill: '#2C3542',
startMarkerFillOpacity: 0.65,
},
}) as TextMark;
changePointMarks.push({
changePoint: [pointMark, textMark],
});
});
return [pointMark, textMark];

return changePointMarks;
};

export const changePointStrategy = (insight: InsightInfo<ChangePointInfo>): Mark[] => {
const chart = insight2ChartStrategy(insight);
const augmentedMarks = changePointAugmentedMarksStrategy(insight);
return [chart, ...augmentedMarks];
const changePointMarks = changePointAugmentedMarksStrategy(insight);
const marks = augmentedMarks2Marks(changePointMarks);
return [chart, ...marks];
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ export { changePointStrategy, changePointAugmentedMarksStrategy } from './change
export { majorityStrategy } from './majority';
export { correlationStrategy } from './correlation';
export { homogeneousStrategy } from './homogeneous';
export { timeSeriesOutlierStrategy } from './timeSeriesOutlier';
export { timeSeriesOutlierStrategy, timeSeriesOutlierStrategyAugmentedMarksStrategy } from './timeSeriesOutlier';
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import { Mark } from '@antv/g2';
import { LineMark, Mark } from '@antv/g2';

import { LowVarianceInfo, InsightInfo } from '../../../types';
import { lineMarkStrategy } from '../commonMarks';
import { insight2ChartStrategy } from '../chart';
import { LowVarianceMark } from '../../types';
import { augmentedMarks2Marks } from '../../utils';
import { dataFormat } from '../../../../utils';

export const lowVarianceAugmentedMarkStrategy = (insight: InsightInfo<LowVarianceInfo>): Mark[] => {
export const lowVarianceAugmentedMarkStrategy = (insight: InsightInfo<LowVarianceInfo>): LowVarianceMark[] => {
const { patterns } = insight;
const marks = [];
const marks: LowVarianceMark[] = [];
patterns.forEach((pattern) => {
const { mean } = pattern;
const meanLineMark = lineMarkStrategy({ y: mean }, { label: `mean: ${mean}` });
marks.push(meanLineMark);
const meanLineMark = lineMarkStrategy({ y: mean }, { label: `mean: ${dataFormat(mean)}` }) as LineMark;
marks.push({
meanLine: [meanLineMark],
});
});
return marks;
};

export const lowVarianceStrategy = (insight: InsightInfo<LowVarianceInfo>): Mark[] => {
const chartMark = insight2ChartStrategy(insight);
const augmentedMarks = lowVarianceAugmentedMarkStrategy(insight);
return [chartMark, ...augmentedMarks];
const lowVarianceMarks = lowVarianceAugmentedMarkStrategy(insight);
const marks = augmentedMarks2Marks(lowVarianceMarks);
return [chartMark, ...marks];
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Mark } from '@antv/g2';
import { AreaMark, LineMark, Mark, PointMark } from '@antv/g2';

import { TimeSeriesOutlierInfo, InsightInfo } from '../../../types';
import { AreaMarkData, LineMarkData } from '../../types';
import { AreaMarkData, LineMarkData, TimeSeriesOutlierMark } from '../../types';
import { insight2ChartStrategy } from '../chart';
import { areaMarkStrategy, lineMarkStrategy, pointMarkStrategy } from '../commonMarks';

Expand All @@ -11,7 +11,7 @@ const OUTLIER = 'outlier';

export const timeSeriesOutlierStrategyAugmentedMarksStrategy = (
insight: InsightInfo<TimeSeriesOutlierInfo>
): Mark[] => {
): TimeSeriesOutlierMark[] => {
const {
data: chartData,
dimensions: [{ fieldName: dimensionName }],
Expand Down Expand Up @@ -84,13 +84,22 @@ export const timeSeriesOutlierStrategyAugmentedMarksStrategy = (
},
});

return [intervalMark, baselineMark, outlierMark];
return [
{
trendLine: [baselineMark as LineMark],
anomalyArea: [intervalMark as AreaMark],
outliers: [outlierMark as PointMark],
},
];
};

export const timeSeriesOutlierStrategy = (insight: InsightInfo<TimeSeriesOutlierInfo>): Mark[] => {
// Should to support marks free combination
const chartMark = insight2ChartStrategy(insight);
const augmentedMarks = timeSeriesOutlierStrategyAugmentedMarksStrategy(insight)?.slice();
augmentedMarks.splice(2, 0, chartMark);
return augmentedMarks;
const {
trendLine = [],
anomalyArea = [],
outliers = [],
} = timeSeriesOutlierStrategyAugmentedMarksStrategy(insight)[0] || {};
return [...anomalyArea, ...trendLine, chartMark, ...outliers];
};
21 changes: 15 additions & 6 deletions packages/ava/src/insight/chart/strategy/augmentedMarks/trend.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Mark } from '@antv/g2';
import { LineMark, Mark } from '@antv/g2';

import { lineMarkStrategy } from '../commonMarks';
import { insight2ChartStrategy } from '../chart';
import { InsightInfo, TrendInfo } from '../../../types';
import { TrendMark } from '../../types';
import { dataFormat } from '../../../../utils';

export const trendAugmentedMarksStrategy = (insight: InsightInfo<TrendInfo>): Mark[] => {
export const trendAugmentedMarksStrategy = (insight: InsightInfo<TrendInfo>): TrendMark[] => {
const {
data: chartData,
dimensions: [{ fieldName: dimensionName }],
Expand All @@ -27,13 +29,20 @@ export const trendAugmentedMarksStrategy = (insight: InsightInfo<TrendInfo>): Ma

const lineData = points.map((point) => ({ x: point[0], y: point[1] }));

const regressionLineMark = lineMarkStrategy({ points: lineData }, { label: `y=${m.toFixed(2)}x+${c.toFixed(2)}` });
const regressionLineMark = lineMarkStrategy(
{ points: lineData },
{ label: `y=${dataFormat(m)}x${c < 0 ? '' : '+'}${dataFormat(c)}` }
);

return [regressionLineMark];
return [
{
trendLine: [regressionLineMark as LineMark],
},
];
};

export const trendStrategy = (insight: InsightInfo<TrendInfo>): Mark[] => {
const chart = insight2ChartStrategy(insight);
const augmentedMarks = trendAugmentedMarksStrategy(insight);
return [chart, ...augmentedMarks];
const trendMarks = trendAugmentedMarksStrategy(insight);
return [chart, ...(trendMarks[0]?.trendLine ?? [])];
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Mark } from '@antv/g2';
import { isNil } from 'lodash';

import { PointPatternInfo } from '../../../types';
import { PointMarkConfig } from '../../types';

/** get mark for point patterns, the patterns should have same dimension and measure */
export const pointMarkStrategy = (patterns: PointPatternInfo[], config: PointMarkConfig): Mark => {
const data = patterns.map(({ x, y }) => ({ x, y }));
const data = [];
patterns.forEach(({ x, y }) => {
if (isNil(x) || isNil(y)) return;
data.push({ x, y });
});

const pointMark: Mark = {
type: 'point',
Expand Down
31 changes: 30 additions & 1 deletion packages/ava/src/insight/chart/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Mark } from '@antv/g2';
import type { AreaMark, IntervalMark, LineMark, Mark, PointMark, TextMark } from '@antv/g2';
import type { Datum } from '../types';

type LabelType = string | ((d: Datum) => string);
Expand Down Expand Up @@ -42,3 +42,32 @@ export type AreaMarkConfig = {
export type IntervalMarkConfig = {
style?: Mark['style'];
};

export type ChangePointMark = {
changePoint: (PointMark | TextMark)[];
};

export type TrendMark = {
trendLine: LineMark[];
};

export type TimeSeriesOutlierMark = {
trendLine: LineMark[];
anomalyArea: AreaMark[];
outliers?: PointMark[];
};

export type CategoryOutlierMark = {
categoryOutlier: (IntervalMark | TextMark)[];
};

export type LowVarianceMark = {
meanLine: LineMark[];
};

export type AugmentedMarks =
| ChangePointMark[]
| TrendMark[]
| TimeSeriesOutlierMark[]
| CategoryOutlierMark[]
| LowVarianceMark[];
8 changes: 8 additions & 0 deletions packages/ava/src/insight/chart/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Mark } from '@antv/g2';
import { flattenDeep, map } from 'lodash';

import { AugmentedMarks } from './types';

export const augmentedMarks2Marks = (augmentedMarks: AugmentedMarks): Mark[] => {
return flattenDeep(map(augmentedMarks, (augmentedMark) => Object.values(augmentedMark)));
};
1 change: 1 addition & 0 deletions packages/ava/src/insight/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export { insightPatternsExtractor } from './insights';
export { generateInsightVisualizationSpec } from './pipeline/visualize';
export * from './types';
export { getSpecificInsight } from './pipeline/specificInsight';
export type { AugmentedMarks } from './chart';
Loading