normalizeY
概述
对 y 相关通道(如 y、y1 等)根据指定的 basis 进行归一化处理。归一化会将数据转换为 0 到 1 之间的比例值,使得不同尺度的数据可以在同一个坐标系中进行比较。这个转换会保持数据的相对关系不变,同时使得所有数据都按照相同的标准进行缩放。
下面这个示例展示了如何创建一个百分比堆积柱状图,展示不同年龄段的性别比例。
import { Chart } from '@antv/g2';
const chart = new Chart({
container: 'container',
});
chart.options({
type: 'interval',
coordinate: { transform: [{ type: 'transpose' }] },
data: {
type: 'fetch',
value:
'https://gw.alipayobjects.com/os/bmw-prod/87b2ff47-2a33-4509-869c-dae4cdd81163.csv',
transform: [
{
type: 'filter',
callback: (d) => d.year === 2000,
},
],
},
transform: [{ type: 'stackY' }, { type: 'normalizeY' }],
encode: {
x: 'age',
y: 'people',
color: 'sex',
},
scale: {
color: { type: 'ordinal', range: ['#ca8861', '#675193'] },
},
axis: {
y: { labelFormatter: '.0%' },
},
labels: [
{
text: 'people',
position: 'inside',
style: { fill: 'white' },
},
],
});
chart.render();
使用场景
- 百分比堆叠图表:当需要展示各部分占整体的百分比时,常与 stackY 转换一起使用。
- 数据标准化:当不同组的数据量级差异较大,但您更关注其分布或比例时。
- 相对比较:当需要比较不同类别之间的相对大小而不是绝对值时。
配置项
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| groupBy | string | string[] |
'x' |
用于指定如何对数据进行分组的字段。每个分组将独立进行归一化计算。 |
| basis | 'deviation' | 'first' | 'last' | 'max' | 'mean' | 'median' | 'min' | 'sum' | ((I, Y) => number) |
'max' |
指定归一化计算的基准值。 |
groupBy 说明
用于指定如何对数据进行分组的字段。每个分组将独立进行归一化计算。这里的字段名是指 encode 配置中的视觉通道名称,比如 ‘x’、‘y’、‘color’ 等。例如,当设置 groupBy: 'color' 时,会按照 encode.color 对应的数据字段进行分组。
- 类型:
string | string[] - 默认值:
'x'
basis 说明
-
'max':使用组内最大值作为基准 -
'min':使用组内最小值作为基准 -
'mean':使用组内平均值作为基准 -
'median':使用组内中位数作为基准 -
'sum':使用组内总和作为基准 -
'first':使用组内第一个值作为基准 -
'last':使用组内最后一个值作为基准 -
'deviation':使用组内标准差作为基准 -
自定义函数:可传入函数来自定义基准值的计算方式。该函数接收两个参数:
import { max, min } from '@antv/vendor/d3-array'; // I: 索引数组,Y: 数据值数组 // 使用值域的范围作为基准 (I, Y) => max(I, (i) => Y[+i]) - min(I, (i) => Y[+i]);
示例
使用自定义 basis 计算基准值
下面的例子展示了如何使用不同的 basis 进行归一化:
import { Chart } from '@antv/g2';
const chart = new Chart({
container: 'container',
});
chart.options({
type: 'interval',
data: [
{ category: 'A', value: 10, group: '1' },
{ category: 'B', value: 20, group: '1' },
{ category: 'C', value: 30, group: '1' },
{ category: 'A', value: 40, group: '2' },
{ category: 'B', value: 50, group: '2' },
{ category: 'C', value: 60, group: '2' },
],
encode: {
x: 'category',
y: 'value',
color: 'group',
},
transform: [
{
type: 'normalizeY',
basis: 'mean', // 使用平均值作为基准
},
],
});
chart.render();
交互式对比不同 basis 效果
import * as G2 from '@antv/g2';
const { Chart } = G2;
const chart = new Chart({
container: 'container',
});
const container = chart.getContainer();
const valueList = [
'first',
'deviation',
'last',
'max',
'mean',
'median',
'min',
'sum',
];
const valueMap = valueList.map((p) => {
return {
label: p,
value: p,
};
});
chart.options({
type: 'line',
width: 900,
height: 600,
insetRight: 20,
data: {
type: 'fetch',
value: 'https://assets.antv.antgroup.com/g2/indices.json',
},
encode: { x: (d) => new Date(d.Date), y: 'Close', color: 'Symbol' },
transform: [{ type: 'normalizeY', basis: 'first', groupBy: 'color' }],
scale: { y: { type: 'log' } },
axis: { y: { title: '↑ Change in price (%)' } },
labels: [{ text: 'Symbol', selector: 'last', fontSize: 10 }],
tooltip: { items: [{ channel: 'y', valueFormatter: '.1f' }] },
});
const handleSetValue = (value) => {
chart.options({
transform: [{ type: 'normalizeY', basis: value, groupBy: 'color' }],
});
chart.render(); // 重新渲染图表
};
// 插入Value 选择器
const selectorContainer = document.createElement('div');
selectorContainer.textContent = '选择 basis ';
const selector = document.createElement('select');
selector.innerHTML = valueMap.map(
(value, index) =>
`<option value="${value.value}" ${index === 0 ? 'selected' : ''}>${
value.label
}</option>`,
);
selector.onchange = (e) => {
handleSetValue(e.target.value);
};
selectorContainer.appendChild(selector);
container.insertBefore(selectorContainer, container.childNodes[0]);
chart.render();
自定义分组
可以通过 groupBy 选项指定如何对数据进行分组:
import { Chart } from '@antv/g2';
const chart = new Chart({
container: 'container',
});
chart.options({
type: 'interval',
data: [
{ category: 'A', year: '2020', value: 100 },
{ category: 'B', year: '2020', value: 200 },
{ category: 'A', year: '2021', value: 150 },
{ category: 'B', year: '2021', value: 300 },
{ category: 'A', year: '2022', value: 180 },
{ category: 'B', year: '2022', value: 360 },
],
encode: {
x: 'year',
y: 'value',
color: 'category',
},
transform: [
{
type: 'normalizeY',
groupBy: 'color', // 按照 color 通道进行归一化(对应数据字段 category)
},
],
axis: {
y: { labelFormatter: '.0%' },
},
});
chart.render();