Skip to content

WIP: refactor(ava/advisor): separate visual encodingfrom spec generator module #785

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

Draft
wants to merge 1 commit into
base: dev-refactor-pipeline1
Choose a base branch
from
Draft
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
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