Skip to content

Commit 6b28516

Browse files
committed
refactor(ava/advisor): separate visual encodingfrom spec generator module
1 parent 716ab18 commit 6b28516

File tree

21 files changed

+724
-395
lines changed

21 files changed

+724
-395
lines changed
Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
import { hasSubset, intersects } from '../../../../../utils';
2-
import { splitAreaXYSeries } from '../../visual-encoder/split-fields';
1+
import { find } from 'lodash';
2+
33
import { getLineSize } from '../../visual-encoder/utils';
4+
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
5+
import { areaEncodeRequirement } from '../../../../../../ckb/encode';
46

5-
import type { Data, Datum } from '../../../../../../common/types';
6-
import type { Advice, BasicDataPropertyForAdvice } from '../../../../../types';
7+
import type { Datum } from '../../../../../../common/types';
8+
import type { Advice } from '../../../../../types';
9+
import type { GenerateChartSpecParams } from '../types';
710

8-
export function areaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
9-
const field4X = dataProps.find((field) => intersects(field.levelOfMeasurements, ['Time', 'Ordinal']));
10-
const field4Y = dataProps.find((field) => hasSubset(field.levelOfMeasurements, ['Interval']));
11+
export function areaChart({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
12+
const encode =
13+
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: areaEncodeRequirement });
14+
const field4X = encode.x?.[0];
15+
const field4Y = encode.y?.[0];
1116

1217
if (!field4X || !field4Y) return null;
1318

1419
const spec: Advice['spec'] = {
1520
type: 'area',
1621
data,
1722
encode: {
18-
x: field4X.name,
19-
y: field4Y.name,
20-
size: (datum: Datum) => getLineSize(datum, data, { field4X }),
23+
x: field4X,
24+
y: field4Y,
25+
size: (datum: Datum) => getLineSize(datum, data, { field4X: find(dataProps, ['name', field4X]) }),
2126
},
2227
legend: {
2328
size: false,
@@ -27,42 +32,44 @@ export function areaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]):
2732
return spec;
2833
}
2934

30-
export function stackedAreaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
31-
const [field4X, field4Y, field4Series] = splitAreaXYSeries(dataProps);
32-
if (!field4X || !field4Y || !field4Series) return null;
35+
export function stackedAreaChart({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
36+
const encode =
37+
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: areaEncodeRequirement });
38+
const [field4X, field4Y, field4Series] = [encode.x?.[0], encode.y?.[0], encode.color?.[0]];
39+
if (!field4X || !field4Y) return null;
3340

3441
const spec: Advice['spec'] = {
3542
type: 'area',
3643
data,
3744
encode: {
38-
x: field4X.name,
39-
y: field4Y.name,
40-
color: field4Series.name,
41-
size: (datum: Datum) => getLineSize(datum, data, { field4Split: field4Series, field4X }),
45+
x: field4X,
46+
y: field4Y,
47+
size: (datum: Datum) =>
48+
getLineSize(datum, data, {
49+
field4Split: find(dataProps, ['name', field4Series]),
50+
field4X: find(dataProps, ['name', field4X]),
51+
}),
4252
},
4353
legend: {
4454
size: false,
4555
},
46-
transform: [{ type: 'stackY' }],
4756
};
4857

58+
if (field4Series) {
59+
spec.encode.color = field4Series;
60+
spec.transform = [{ type: 'stackY' }];
61+
}
62+
4963
return spec;
5064
}
5165

52-
export function percentStackedAreaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
53-
const [field4X, field4Y, field4Series] = splitAreaXYSeries(dataProps);
54-
if (!field4X || !field4Y || !field4Series) return null;
55-
56-
const spec: Advice['spec'] = {
57-
type: 'area',
58-
data,
59-
encode: {
60-
x: field4X.name,
61-
y: field4Y.name,
62-
color: field4Series.name,
63-
},
64-
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
65-
};
66+
export function percentStackedAreaChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
67+
const spec = stackedAreaChart({ data, dataProps, encode });
68+
if (spec?.transform) {
69+
spec.transform.push({ type: 'normalizeY' });
70+
} else {
71+
spec.transform = [{ type: 'normalizeY' }];
72+
}
6673

6774
return spec;
6875
}
Lines changed: 25 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import { splitBarXYSeries } from '../../visual-encoder/split-fields';
2+
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
3+
import { barEncodeRequirement } from '../../../../../../ckb/encode';
24

3-
import type { Data } from '../../../../../../common/types';
4-
import type { Advice, BasicDataPropertyForAdvice } from '../../../../../types';
5+
import type { Advice } from '../../../../../types';
6+
import type { GenerateChartSpecParams } from '../types';
57

6-
export function barChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
7-
const [field4X, field4Y, field4Color] = splitBarXYSeries(dataProps);
8+
export function barChart({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
9+
const encode =
10+
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: barEncodeRequirement });
11+
const [field4X, field4Y, field4Color] = [encode.x?.[0], encode.y?.[0], encode.color?.[0]];
812

913
if (!field4X || !field4Y) return null;
1014

@@ -14,81 +18,43 @@ export function barChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): A
1418
// G2's implementation converts column chart (vertical bar) and bar chart (horizontal bar) by transpose, so the x and y fields need to be swapped.
1519
// 由于g2的实现是通过transpose来转换 column chart(竖着的bar)和bar chart(横着的bar),所以x和y的字段需要做交换
1620
encode: {
17-
x: field4Y.name,
18-
y: field4X.name,
21+
x: field4Y,
22+
y: field4X,
1923
},
2024
coordinate: {
2125
transform: [{ type: 'transpose' }],
2226
},
2327
};
2428

2529
if (field4Color) {
26-
spec.encode.color = field4Color.name;
30+
spec.encode.color = field4Color;
2731
spec.transform = [{ type: 'stackY' }];
2832
}
2933

3034
return spec;
3135
}
3236

33-
export function groupedBarChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
34-
const [field4X, field4Y, field4Series] = splitBarXYSeries(dataProps);
35-
if (!field4X || !field4Y || !field4Series) return null;
36-
37-
const spec: Advice['spec'] = {
38-
type: 'interval',
39-
data,
40-
encode: {
41-
x: field4Y.name,
42-
y: field4X.name,
43-
color: field4Series.name,
44-
},
45-
transform: [{ type: 'dodgeX' }],
46-
coordinate: {
47-
transform: [{ type: 'transpose' }],
48-
},
49-
};
37+
export function groupedBarChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
38+
const spec = barChart({ data, dataProps, encode });
5039

40+
if (spec?.encode?.color) {
41+
spec.transform = [{ type: 'dodgeX' }];
42+
}
5143
return spec;
5244
}
5345

54-
export function stackedBarChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
55-
const [field4X, field4Y, field4Series] = splitBarXYSeries(dataProps);
56-
if (!field4X || !field4Y || !field4Series) return null;
57-
58-
const spec: Advice['spec'] = {
59-
type: 'interval',
60-
data,
61-
encode: {
62-
x: field4Y.name,
63-
y: field4X.name,
64-
color: field4Series.name,
65-
},
66-
transform: [{ type: 'stackY' }],
67-
coordinate: {
68-
transform: [{ type: 'transpose' }],
69-
},
70-
};
71-
72-
return spec;
46+
export function stackedBarChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
47+
return barChart({ data, dataProps, encode });
7348
}
7449

75-
export function percentStackedBarChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
50+
export function percentStackedBarChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
7651
const [field4X, field4Y, field4Series] = splitBarXYSeries(dataProps);
7752
if (!field4X || !field4Y || !field4Series) return null;
78-
79-
const spec: Advice['spec'] = {
80-
type: 'interval',
81-
data,
82-
encode: {
83-
x: field4Y.name,
84-
y: field4X.name,
85-
color: field4Series.name,
86-
},
87-
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
88-
coordinate: {
89-
transform: [{ type: 'transpose' }],
90-
},
91-
};
92-
53+
const spec = barChart({ data, dataProps, encode });
54+
if (spec?.transform) {
55+
spec.transform.push({ type: 'normalizeY' });
56+
} else {
57+
spec.transform = [{ type: 'normalizeY' }];
58+
}
9359
return spec;
9460
}
Lines changed: 25 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,50 @@
1-
import { Data } from '../../../../../../common/types';
2-
import { Advice, BasicDataPropertyForAdvice } from '../../../../../types';
3-
import { compare, hasSubset } from '../../../../../utils';
4-
import { splitColumnXYSeries } from '../../visual-encoder/split-fields';
1+
import { columnEncodeRequirement } from '../../../../../../ckb/encode';
2+
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
53

6-
export function columnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
7-
const nominalFields = dataProps.filter((field) => hasSubset(field.levelOfMeasurements, ['Nominal']));
8-
const sortedNominalFields = nominalFields.sort(compare);
9-
const field4X = sortedNominalFields[0];
10-
const field4Color = sortedNominalFields[1];
11-
const field4Y = dataProps.find((field) => hasSubset(field.levelOfMeasurements, ['Interval']));
4+
import type { Advice } from '../../../../../types';
5+
import type { GenerateChartSpecParams } from '../types';
126

7+
export function columnChart({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
8+
const encode =
9+
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: columnEncodeRequirement });
10+
const [field4X, field4Y, field4Color] = [encode.x?.[0], encode.y?.[0], encode.color?.[0]];
1311
if (!field4X || !field4Y) return null;
1412

1513
const spec: Advice['spec'] = {
1614
type: 'interval',
1715
data,
1816
encode: {
19-
x: field4X.name,
20-
y: field4Y.name,
17+
x: field4X,
18+
y: field4Y,
2119
},
2220
};
2321

2422
if (field4Color) {
25-
spec.encode.color = field4Color.name;
23+
spec.encode.color = field4Color;
2624
spec.transform = [{ type: 'stackY' }];
2725
}
2826

2927
return spec;
3028
}
3129

32-
export function groupedColumnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
33-
const [field4X, field4Y, field4Series] = splitColumnXYSeries(dataProps);
34-
if (!field4X || !field4Y || !field4Series) return null;
35-
36-
const spec: Advice['spec'] = {
37-
type: 'interval',
38-
data,
39-
encode: {
40-
x: field4X.name,
41-
y: field4Y.name,
42-
color: field4Series.name,
43-
},
44-
transform: [{ type: 'dodgeX' }],
45-
};
46-
30+
export function groupedColumnChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
31+
const spec = columnChart({ data, dataProps, encode });
32+
if (spec?.encode?.color) {
33+
spec.transform = [{ type: 'dodgeX' }];
34+
}
4735
return spec;
4836
}
4937

50-
export function stackedColumnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
51-
const [field4X, field4Y, Field4Series] = splitColumnXYSeries(dataProps);
52-
if (!field4X || !field4Y || !Field4Series) return null;
53-
54-
const spec: Advice['spec'] = {
55-
type: 'interval',
56-
data,
57-
encode: {
58-
x: field4X.name,
59-
y: field4Y.name,
60-
color: Field4Series.name,
61-
},
62-
transform: [{ type: 'stackY' }],
63-
};
64-
65-
return spec;
38+
export function stackedColumnChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
39+
return columnChart({ data, dataProps, encode });
6640
}
6741

68-
export function percentStackedColumnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
69-
const [field4X, field4Y, Field4Series] = splitColumnXYSeries(dataProps);
70-
if (!field4X || !field4Y || !Field4Series) return null;
71-
72-
const spec: Advice['spec'] = {
73-
type: 'interval',
74-
data,
75-
encode: {
76-
x: field4X.name,
77-
y: field4Y.name,
78-
color: Field4Series.name,
79-
},
80-
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
81-
};
82-
42+
export function percentStackedColumnChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
43+
const spec = columnChart({ data, dataProps, encode });
44+
if (spec?.transform) {
45+
spec.transform.push({ type: 'normalizeY' });
46+
} else {
47+
spec.transform = [{ type: 'normalizeY' }];
48+
}
8349
return spec;
8450
}

packages/ava/src/advisor/advise-pipeline/plugin/presets/spec-generator/charts/heatmap.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
import { intersects, compare, hasSubset } from '../../../../../utils';
1+
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
2+
import { heatmapEncodeRequirement } from '../../../../../../ckb/encode';
23

3-
import type { Data } from '../../../../../../common/types';
4-
import type { BasicDataPropertyForAdvice, Advice } from '../../../../../types';
4+
import type { Advice } from '../../../../../types';
5+
import type { GenerateChartSpecParams } from '../types';
56

6-
export function heatmap(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
7-
const axisFields = dataProps.filter((field) => intersects(field.levelOfMeasurements, ['Nominal', 'Ordinal']));
8-
const sortedFields = axisFields.sort(compare);
9-
const field4X = sortedFields[0];
10-
const field4Y = sortedFields[1];
11-
const field4Color = dataProps.find((field) => hasSubset(field.levelOfMeasurements, ['Interval']));
7+
export function heatmap({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
8+
const encode =
9+
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: heatmapEncodeRequirement });
10+
const field4X = encode?.x?.[0];
11+
const field4Y = encode?.y?.[0];
12+
const field4Color = encode?.color?.[0];
1213

1314
if (!field4X || !field4Y || !field4Color) return null;
1415

1516
const spec: Advice['spec'] = {
1617
type: 'cell',
1718
data,
1819
encode: {
19-
x: field4X.name,
20-
y: field4Y.name,
21-
color: field4Color.name,
20+
x: field4X,
21+
y: field4Y,
22+
color: field4Color,
2223
},
2324
};
2425

packages/ava/src/advisor/advise-pipeline/plugin/presets/spec-generator/charts/histogram.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
import { hasSubset } from '../../../../../utils';
1+
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
2+
import { histogramEncodeRequirement } from '../../../../../../ckb/encode';
23

3-
import type { Data } from '../../../../../../common/types';
4-
import type { BasicDataPropertyForAdvice, Advice } from '../../../../../types';
4+
import type { Advice } from '../../../../../types';
5+
import type { GenerateChartSpecParams } from '../types';
56

6-
export function histogram(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
7-
const field = dataProps.find((field) => hasSubset(field.levelOfMeasurements, ['Interval']));
7+
export function histogram({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
8+
const encode =
9+
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: histogramEncodeRequirement });
10+
const field = encode.x?.[0];
811
if (!field) return null;
912

1013
const spec: Advice['spec'] = {
1114
type: 'rect',
1215
data,
1316
encode: {
14-
x: field.name,
17+
x: field,
1518
},
1619
transform: [{ type: 'binX', y: 'count' }],
1720
};

0 commit comments

Comments
 (0)