Skip to content

Commit

Permalink
refactor(ava/advisor): separate visual encodingfrom spec generator mo…
Browse files Browse the repository at this point in the history
…dule
  • Loading branch information
chenluli committed Jun 17, 2024
1 parent 716ab18 commit 6b28516
Show file tree
Hide file tree
Showing 21 changed files with 724 additions and 395 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { hasSubset, intersects } from '../../../../../utils';
import { splitAreaXYSeries } from '../../visual-encoder/split-fields';
import { find } from 'lodash';

import { getLineSize } from '../../visual-encoder/utils';
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
import { areaEncodeRequirement } from '../../../../../../ckb/encode';

import type { Data, Datum } from '../../../../../../common/types';
import type { Advice, BasicDataPropertyForAdvice } from '../../../../../types';
import type { Datum } from '../../../../../../common/types';
import type { Advice } from '../../../../../types';
import type { GenerateChartSpecParams } from '../types';

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

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

const spec: Advice['spec'] = {
type: 'area',
data,
encode: {
x: field4X.name,
y: field4Y.name,
size: (datum: Datum) => getLineSize(datum, data, { field4X }),
x: field4X,
y: field4Y,
size: (datum: Datum) => getLineSize(datum, data, { field4X: find(dataProps, ['name', field4X]) }),
},
legend: {
size: false,
Expand All @@ -27,42 +32,44 @@ export function areaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]):
return spec;
}

export function stackedAreaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, field4Series] = splitAreaXYSeries(dataProps);
if (!field4X || !field4Y || !field4Series) return null;
export function stackedAreaChart({ data, dataProps, encode: customEncode }: GenerateChartSpecParams): Advice['spec'] {
const encode =
customEncode ?? mapFieldsToVisualEncode({ fields: dataProps, encodeRequirements: areaEncodeRequirement });
const [field4X, field4Y, field4Series] = [encode.x?.[0], encode.y?.[0], encode.color?.[0]];
if (!field4X || !field4Y) return null;

const spec: Advice['spec'] = {
type: 'area',
data,
encode: {
x: field4X.name,
y: field4Y.name,
color: field4Series.name,
size: (datum: Datum) => getLineSize(datum, data, { field4Split: field4Series, field4X }),
x: field4X,
y: field4Y,
size: (datum: Datum) =>
getLineSize(datum, data, {
field4Split: find(dataProps, ['name', field4Series]),
field4X: find(dataProps, ['name', field4X]),
}),
},
legend: {
size: false,
},
transform: [{ type: 'stackY' }],
};

if (field4Series) {
spec.encode.color = field4Series;
spec.transform = [{ type: 'stackY' }];
}

return spec;
}

export function percentStackedAreaChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, field4Series] = splitAreaXYSeries(dataProps);
if (!field4X || !field4Y || !field4Series) return null;

const spec: Advice['spec'] = {
type: 'area',
data,
encode: {
x: field4X.name,
y: field4Y.name,
color: field4Series.name,
},
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
};
export function percentStackedAreaChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
const spec = stackedAreaChart({ data, dataProps, encode });
if (spec?.transform) {
spec.transform.push({ type: 'normalizeY' });
} else {
spec.transform = [{ type: 'normalizeY' }];
}

return spec;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { splitBarXYSeries } from '../../visual-encoder/split-fields';
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
import { barEncodeRequirement } from '../../../../../../ckb/encode';

import type { Data } from '../../../../../../common/types';
import type { Advice, BasicDataPropertyForAdvice } from '../../../../../types';
import type { Advice } from '../../../../../types';
import type { GenerateChartSpecParams } from '../types';

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

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

Expand All @@ -14,81 +18,43 @@ export function barChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): A
// 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.
// 由于g2的实现是通过transpose来转换 column chart(竖着的bar)和bar chart(横着的bar),所以x和y的字段需要做交换
encode: {
x: field4Y.name,
y: field4X.name,
x: field4Y,
y: field4X,
},
coordinate: {
transform: [{ type: 'transpose' }],
},
};

if (field4Color) {
spec.encode.color = field4Color.name;
spec.encode.color = field4Color;
spec.transform = [{ type: 'stackY' }];
}

return spec;
}

export function groupedBarChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, field4Series] = splitBarXYSeries(dataProps);
if (!field4X || !field4Y || !field4Series) return null;

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4Y.name,
y: field4X.name,
color: field4Series.name,
},
transform: [{ type: 'dodgeX' }],
coordinate: {
transform: [{ type: 'transpose' }],
},
};
export function groupedBarChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
const spec = barChart({ data, dataProps, encode });

if (spec?.encode?.color) {
spec.transform = [{ type: 'dodgeX' }];
}
return spec;
}

export function stackedBarChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, field4Series] = splitBarXYSeries(dataProps);
if (!field4X || !field4Y || !field4Series) return null;

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4Y.name,
y: field4X.name,
color: field4Series.name,
},
transform: [{ type: 'stackY' }],
coordinate: {
transform: [{ type: 'transpose' }],
},
};

return spec;
export function stackedBarChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
return barChart({ data, dataProps, encode });
}

export function percentStackedBarChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
export function percentStackedBarChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
const [field4X, field4Y, field4Series] = splitBarXYSeries(dataProps);
if (!field4X || !field4Y || !field4Series) return null;

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4Y.name,
y: field4X.name,
color: field4Series.name,
},
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
coordinate: {
transform: [{ type: 'transpose' }],
},
};

const spec = barChart({ data, dataProps, encode });
if (spec?.transform) {
spec.transform.push({ type: 'normalizeY' });
} else {
spec.transform = [{ type: 'normalizeY' }];
}
return spec;
}
Original file line number Diff line number Diff line change
@@ -1,84 +1,50 @@
import { Data } from '../../../../../../common/types';
import { Advice, BasicDataPropertyForAdvice } from '../../../../../types';
import { compare, hasSubset } from '../../../../../utils';
import { splitColumnXYSeries } from '../../visual-encoder/split-fields';
import { columnEncodeRequirement } from '../../../../../../ckb/encode';
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';

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

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

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4X.name,
y: field4Y.name,
x: field4X,
y: field4Y,
},
};

if (field4Color) {
spec.encode.color = field4Color.name;
spec.encode.color = field4Color;
spec.transform = [{ type: 'stackY' }];
}

return spec;
}

export function groupedColumnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, field4Series] = splitColumnXYSeries(dataProps);
if (!field4X || !field4Y || !field4Series) return null;

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4X.name,
y: field4Y.name,
color: field4Series.name,
},
transform: [{ type: 'dodgeX' }],
};

export function groupedColumnChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
const spec = columnChart({ data, dataProps, encode });
if (spec?.encode?.color) {
spec.transform = [{ type: 'dodgeX' }];
}
return spec;
}

export function stackedColumnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, Field4Series] = splitColumnXYSeries(dataProps);
if (!field4X || !field4Y || !Field4Series) return null;

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4X.name,
y: field4Y.name,
color: Field4Series.name,
},
transform: [{ type: 'stackY' }],
};

return spec;
export function stackedColumnChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
return columnChart({ data, dataProps, encode });
}

export function percentStackedColumnChart(data: Data, dataProps: BasicDataPropertyForAdvice[]): Advice['spec'] {
const [field4X, field4Y, Field4Series] = splitColumnXYSeries(dataProps);
if (!field4X || !field4Y || !Field4Series) return null;

const spec: Advice['spec'] = {
type: 'interval',
data,
encode: {
x: field4X.name,
y: field4Y.name,
color: Field4Series.name,
},
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
};

export function percentStackedColumnChart({ data, dataProps, encode }: GenerateChartSpecParams): Advice['spec'] {
const spec = columnChart({ data, dataProps, encode });
if (spec?.transform) {
spec.transform.push({ type: 'normalizeY' });
} else {
spec.transform = [{ type: 'normalizeY' }];
}
return spec;
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import { intersects, compare, hasSubset } from '../../../../../utils';
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
import { heatmapEncodeRequirement } from '../../../../../../ckb/encode';

import type { Data } from '../../../../../../common/types';
import type { BasicDataPropertyForAdvice, Advice } from '../../../../../types';
import type { Advice } from '../../../../../types';
import type { GenerateChartSpecParams } from '../types';

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

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

const spec: Advice['spec'] = {
type: 'cell',
data,
encode: {
x: field4X.name,
y: field4Y.name,
color: field4Color.name,
x: field4X,
y: field4Y,
color: field4Color,
},
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { hasSubset } from '../../../../../utils';
import { mapFieldsToVisualEncode } from '../../visual-encoder/encode-mapping';
import { histogramEncodeRequirement } from '../../../../../../ckb/encode';

import type { Data } from '../../../../../../common/types';
import type { BasicDataPropertyForAdvice, Advice } from '../../../../../types';
import type { Advice } from '../../../../../types';
import type { GenerateChartSpecParams } from '../types';

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

const spec: Advice['spec'] = {
type: 'rect',
data,
encode: {
x: field.name,
x: field,
},
transform: [{ type: 'binX', y: 'count' }],
};
Expand Down
Loading

0 comments on commit 6b28516

Please sign in to comment.