Skip to content

Commit b21450f

Browse files
committed
feat: use fold to visualize more than 2 fields
1 parent 29e8810 commit b21450f

File tree

6 files changed

+96
-40
lines changed

6 files changed

+96
-40
lines changed

packages/vmind/src/common/vizDataToSpec/utils.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Cell, DataType, ROLE, SimpleFieldInfo } from 'src/typings';
1+
import { Cell, DataItem, DataType, ROLE, SimpleFieldInfo } from 'src/typings';
22
import { VIDEO_LENGTH_BY_CHART_TYPE, DEFAULT_VIDEO_LENGTH } from './constants';
3+
import { FOLD_NAME, FOLD_VALUE, fold } from '@visactor/chart-advisor';
34

45
export const detectAxesType = (values: any[], field: string) => {
56
const isNumber = values.every(d => !d[field] || !isNaN(Number(d[field])));
@@ -58,3 +59,9 @@ export const getFieldByRole = (fields: SimpleFieldInfo[], role: ROLE) => {
5859
export const getFieldByDataType = (fields: SimpleFieldInfo[], dataTypeList: DataType[]) => {
5960
return fields.find(f => dataTypeList.includes(f.type));
6061
};
62+
63+
export const foldDatasetByYField = (dataset: DataItem[], yFieldList: string[], fieldInfo: SimpleFieldInfo[]) => {
64+
const aliasMap = Object.fromEntries(fieldInfo.map(d => [d.fieldName, d.fieldName]));
65+
66+
return fold(dataset as any, yFieldList, FOLD_NAME, FOLD_VALUE, aliasMap, false);
67+
};

packages/vmind/src/gpt/chart-generation/NLToChart.ts

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const generateChartWithGPT = async (
5858
if (checkChartTypeAndCell(patchResult.chartTypeNew, patchResult.cellNew, fieldInfo)) {
5959
chartType = patchResult.chartTypeNew;
6060
cell = patchResult.cellNew;
61+
dataset = patchResult.datasetNew;
6162
}
6263
} catch (err) {
6364
console.warn(err);

packages/vmind/src/gpt/chart-generation/patch.ts

+21-18
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import { isNil } from 'lodash';
1+
import { isArray, isNil } from 'lodash';
22
import {
33
CARTESIAN_CHART_LIST,
44
detectAxesType,
5+
foldDatasetByYField,
56
getFieldByDataType,
67
getFieldByRole,
78
getRemainedFields
89
} from '../../common/vizDataToSpec/utils';
910
import { Cell, DataItem, DataType, PatchContext, PatchPipeline, ROLE, SimpleFieldInfo } from '../../typings';
1011
import { execPipeline } from '../../common/utils';
12+
import { FOLD_NAME, FOLD_VALUE } from '@visactor/chart-advisor';
1113

1214
export const patchUserInput = (userInput: string) => {
1315
const FULL_WIDTH_SYMBOLS = [',', '。'];
@@ -38,7 +40,6 @@ export const patchUserInput = (userInput: string) => {
3840
};
3941

4042
const patchAxisField: PatchPipeline = (context: PatchContext, _originalContext: PatchContext) => {
41-
// Todo: When multiple Y fields, use FOLD
4243
const { cell } = context;
4344

4445
const cellNew: any = { ...cell };
@@ -84,42 +85,44 @@ const patchLabelField: PatchPipeline = (context: PatchContext, _originalContext:
8485
};
8586

8687
const patchYField: PatchPipeline = (context: PatchContext, _originalContext: PatchContext) => {
87-
const { chartType, cell } = context;
88+
const { chartType, cell, dataset, fieldInfo } = context;
8889
let cellNew = { ...cell };
8990
const { x, y } = cellNew;
9091
let chartTypeNew = chartType;
92+
let datasetNew = dataset;
9193

9294
// y轴字段有多个时,处理方式:
9395
// 1. 图表类型为: 箱型图, 图表类型不做矫正
9496
// 2. 图表类型为: 柱状图 或 折线图, 图表类型矫正为双轴图
9597
// 3. 其他情况, 图表类型矫正为散点图
96-
if (y && typeof y !== 'string' && y.length > 1) {
97-
if (chartTypeNew === 'BOX PLOT') {
98+
if (y && isArray(y) && y.length > 1) {
99+
if (chartTypeNew === 'BOX PLOT' || (chartTypeNew === 'DUAL AXIS CHART' && y.length === 2)) {
98100
return {
99101
...context
100102
};
101103
}
104+
102105
if (chartTypeNew === 'BAR CHART' || chartTypeNew === 'LINE CHART' || chartTypeNew === 'DUAL AXIS CHART') {
103-
if (y.length === 2) {
104-
chartTypeNew = 'DUAL AXIS CHART';
105-
} else {
106-
//TODO: use fold to visualize more than 2 y fields
107-
}
106+
//use fold to visualize more than 2 y fields
107+
datasetNew = foldDatasetByYField(datasetNew, y, fieldInfo);
108+
cellNew.y = FOLD_VALUE.toString();
109+
cellNew.color = FOLD_NAME.toString();
108110
} else {
109-
(chartTypeNew = 'SCATTER PLOT'),
110-
(cellNew = {
111-
...cell,
112-
x: y[0],
113-
y: y[1],
114-
color: typeof x === 'string' ? x : x[0]
115-
});
111+
chartTypeNew = 'SCATTER PLOT';
112+
cellNew = {
113+
...cell,
114+
x: y[0],
115+
y: y[1],
116+
color: typeof x === 'string' ? x : x[0]
117+
};
116118
}
117119
}
118120

119121
return {
120122
...context,
121123
chartType: chartTypeNew,
122-
cell: cellNew
124+
cell: cellNew,
125+
dataset: datasetNew
123126
};
124127
};
125128

packages/vmind/src/skylark/chart-generation/NLToChart.ts

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const generateChartWithSkylark = async (
5858
if (checkChartTypeAndCell(patchResult.chartTypeNew, patchResult.cellNew, patchResult.fieldInfoNew)) {
5959
chartType = patchResult.chartTypeNew;
6060
cell = patchResult.cellNew;
61+
dataset = patchResult.datasetNew;
6162
}
6263
} catch (err) {
6364
console.warn(err);

packages/vmind/src/skylark/chart-generation/constants.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const ChartFieldInfo: ChannelInfo = {
3434
'LINE CHART': {
3535
visualChannels: {
3636
x: "x-axis of line chart. Can't be empty. Only string fields",
37-
y: "y-axis of line chart. Can't be empty. Only number fields",
37+
y: "y-axis of line chart. Can't be empty. Only number fields. Use array if there are more than one number fields need to show.",
3838
color:
3939
'color channel of line chart. Used to distinguish different lines. Only string fields. Can be empty if no suitable field.'
4040
},
@@ -43,7 +43,10 @@ export const ChartFieldInfo: ChannelInfo = {
4343
y: 'field assigned to y channel',
4444
color: 'field assigned to color channel. Can be empty if no suitable field.'
4545
},
46-
knowledge: ['Only string fields can be used in color channel.']
46+
knowledge: [
47+
'Only string fields can be used in color channel.',
48+
'Use an array in y-axis if you want to assign more than one fields in y-axis.'
49+
]
4750
},
4851
'SCATTER PLOT': {
4952
visualChannels: {

packages/vmind/src/skylark/chart-generation/patch.ts

+60-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { isArray } from 'lodash';
2-
import { Cell, DataItem, DataType, PatchContext, PatchPipeline, SimpleFieldInfo } from '../../typings';
3-
import { detectAxesType } from '../../common/vizDataToSpec/utils';
1+
import { isArray, isString } from 'lodash';
2+
import { Cell, DataItem, DataType, PatchContext, PatchPipeline, ROLE, SimpleFieldInfo } from '../../typings';
43
import { execPipeline } from '../../common/utils';
4+
import { foldDatasetByYField } from '../../common/vizDataToSpec/utils';
5+
import { FOLD_NAME, FOLD_VALUE } from '@visactor/chart-advisor';
56

67
const matchFieldWithoutPunctuation = (field: string, fieldList: string[]): string | undefined => {
78
//try to match the field without punctuation
@@ -47,6 +48,49 @@ const patchNullField: PatchPipeline = (context: PatchContext, _originalContext:
4748
};
4849
};
4950

51+
const patchField: PatchPipeline = (context: PatchContext, _originalContext: PatchContext) => {
52+
const { fieldInfo, cell } = context;
53+
const fieldNames = fieldInfo.map(field => field.fieldName);
54+
const cellNew = { ...cell };
55+
Object.keys(cellNew).forEach(key => {
56+
const value = cellNew[key];
57+
if (isString(value) && (value ?? '').includes(',')) {
58+
const newValue = (value as string).split(',').map(f => f.trim());
59+
if (newValue.every(f => fieldNames.includes(f))) {
60+
cellNew[key] = newValue;
61+
}
62+
}
63+
});
64+
return {
65+
...context,
66+
cell: cellNew
67+
};
68+
};
69+
70+
const patchColorField: PatchPipeline = (context: PatchContext, _originalContext: PatchContext) => {
71+
const { chartType, fieldInfo, cell } = context;
72+
const cellNew = { ...cell };
73+
const { color } = cellNew;
74+
let chartTypeNew = chartType;
75+
if (color) {
76+
const colorField = fieldInfo.find(f => f.fieldName === color);
77+
if (colorField && colorField.role === ROLE.MEASURE) {
78+
cellNew.color = undefined;
79+
if (['BAR CHART', 'LINE CHART', 'DUAL AXIS CHART'].includes(chartTypeNew)) {
80+
cellNew.y = [cellNew.y, color].flat();
81+
if (chartTypeNew === 'DUAL AXIS CHART' && cellNew.y.length > 2) {
82+
chartTypeNew = 'BAR CHART';
83+
}
84+
}
85+
}
86+
}
87+
88+
return {
89+
...context,
90+
cell: cellNew
91+
};
92+
};
93+
5094
const patchRadarChart: PatchPipeline = (context: PatchContext, _originalContext: PatchContext) => {
5195
const { chartType, cell } = context;
5296

@@ -83,27 +127,22 @@ const patchBoxPlot: PatchPipeline = (context: PatchContext, _originalContext: Pa
83127
};
84128

85129
const patchBarChart: PatchPipeline = (context: PatchContext, _originalContext: PatchContext) => {
86-
const { chartType, cell } = context;
87-
let chartTypeNew = chartType;
88-
let cellNew = { ...cell };
89-
if (chartTypeNew === 'BAR CHART') {
90-
if (isArray(cell.y) && cell.y.length === 2) {
91-
chartTypeNew = 'DUAL AXIS CHART';
92-
} else if ((cell.y ?? '').includes(',')) {
93-
const yNew = (cell.y as string).split(',');
94-
if (yNew.length === 2) {
95-
chartTypeNew = 'DUAL AXIS CHART';
96-
cellNew = {
97-
...cell,
98-
y: yNew
99-
};
100-
}
130+
const { chartType, cell, fieldInfo, dataset } = context;
131+
const chartTypeNew = chartType;
132+
const cellNew = { ...cell };
133+
let datasetNew = dataset;
134+
if (chartTypeNew === 'BAR CHART' || chartTypeNew === 'LINE CHART') {
135+
if (isArray(cellNew.y) && cellNew.y.length > 1) {
136+
datasetNew = foldDatasetByYField(datasetNew, cellNew.y, fieldInfo);
137+
cellNew.y = FOLD_VALUE.toString();
138+
cellNew.color = FOLD_NAME.toString();
101139
}
102140
}
103141
return {
104142
...context,
105143
chartType: chartTypeNew,
106-
cell: cellNew
144+
cell: cellNew,
145+
dataset: datasetNew
107146
};
108147
};
109148

@@ -162,6 +201,8 @@ const patchArrayField: PatchPipeline = (context: PatchContext, _originalContext:
162201

163202
const patchPipelines: PatchPipeline[] = [
164203
patchNullField,
204+
patchField,
205+
patchColorField,
165206
patchRadarChart,
166207
patchBoxPlot,
167208
patchBarChart,

0 commit comments

Comments
 (0)