Datasets, dimensions and transforms

Drive series from dataset.source, name your dimensions, use encode to map them, and filter or aggregate data with transforms instead of in JavaScript.

dataset.source shapes

// 1. Array of arrays, with dimensions declared separately
const optionA = {
  dataset: {
    dimensions: ['month', 'revenue', 'cost'],
    source: [
      ['Jan', 820, 410],
      ['Feb', 932, 480],
      ['Mar', 901, 455]
    ]
  },
  xAxis: { type: 'category' },
  yAxis: { type: 'value' },
  series: [
    { type: 'bar', encode: { x: 'month', y: 'revenue' } },
    { type: 'bar', encode: { x: 'month', y: 'cost' } }
  ]
};

// 2. Array of objects: the keys become the dimension names, order does not matter
const optionB = {
  dataset: {
    source: [
      { month: 'Jan', revenue: 820, cost: 410 },
      { month: 'Feb', revenue: 932, cost: 480 }
    ]
  },
  // ...series identical: encode still refers to dimension names
};

// 3. An external dataset with a source header row
const optionC = {
  dataset: { source: rawCsvRows, sourceHeader: true }
};

// 4. Several datasets, referred to by index or id
const optionD = {
  dataset: [
    { id: 'raw', source: rows },
    { id: 'monthly', fromDatasetId: 'raw', transform: { type: 'filter', config: { dimension: 'month', value: 'Jan' } } }
  ],
  series: [{ type: 'line', datasetId: 'monthly' }]
};
Source shapeDimensions come fromWhen to use
[['Jan', 820], ...]The dimensions arrayCompact data, hand-written examples
[{ month: 'Jan', ... }]The object keysData straight from a JSON API
CSV-style with a header rowsourceHeader: trueParsed files and pasted spreadsheet data
Typed arraysThe dimensions arrayVery large numeric series
A typed-array datasetdimensions plus sourceHeaderPerformance-critical rendering

The dimensions array does more than label columns: it sets the order, so a dimension can be referenced by name rather than by index in encode. That matters because reordering the source columns then requires no change at all in the series.

encode and multiple series over one dataset

const option = {
  dataset: {
    dimensions: ['month', 'revenue', 'cost', 'margin'],
    source: [
      ['Jan', 820, 410, 0.5],
      ['Feb', 932, 480, 0.485],
      ['Mar', 901, 455, 0.495],
      ['Apr', 1290, 610, 0.527]
    ]
  },
  tooltip: { trigger: 'axis' },
  legend: { data: ['Revenue', 'Cost', 'Margin'] },
  grid: { left: 56, right: 64, top: 40, bottom: 40 },
  xAxis: { type: 'category', axisLabel: { rotate: 0 } },
  yAxis: [
    { type: 'value', name: 'Currency' },
    { type: 'value', name: 'Ratio', min: 0, max: 1, position: 'right',
      axisLabel: { formatter: (value) => (value * 100).toFixed(0) + '%' } }
  ],
  series: [
    { name: 'Revenue', type: 'bar', encode: { x: 'month', y: 'revenue', tooltip: ['revenue'] } },
    { name: 'Cost',    type: 'bar', encode: { x: 'month', y: 'cost' } },
    { name: 'Margin',  type: 'line', yAxisIndex: 1, smooth: true,
      encode: { x: 'month', y: 'margin' } }
  ]
};

// A different view of the same data: no new arrays were built for any of this.
const switched = {
  ...option,
  series: [
    { name: 'Revenue', type: 'line', encode: { x: 'month', y: 'revenue' } },
    { name: 'Cost',    type: 'line', encode: { x: 'month', y: 'cost' } }
  ]
};
  • encode maps dimensions to the axes and to other slots: x, y, itemName, value, tooltip, label, seriesName.
  • encode.tooltip is how you include a dimension in the tooltip that is not on any axis.
  • For a pie chart the mapping is itemName and value, not x and y. Using the axis names on a pie silently renders nothing.
  • Switching chart type is a change to type only, because the data lives in the dataset. That is the practical benefit of this model.
💡
Datasets are per-instance, not shared. Two charts showing the same data each hold their own copy of it. If a very large dataset feeds several charts, keep one source array in your application state and pass the same reference to each chart's dataset.source — ECharts reads it, it does not clone it.

Filter, sort and aggregation transforms

const option = {
  dataset: [
    // the raw source
    {
      id: 'sales',
      dimensions: ['region', 'month', 'amount'],
      source: [
        ['North', 'Jan', 120], ['North', 'Feb', 140], ['North', 'Mar', 130],
        ['South', 'Jan', 90],  ['South', 'Feb', 110], ['South', 'Mar', 150],
        ['East',  'Jan', 75],  ['East',  'Feb', 80],  ['East',  'Mar', 95]
      ]
    },

    // filter: only March
    {
      id: 'march',
      fromDatasetId: 'sales',
      transform: { type: 'filter', config: { dimension: 'month', '=': 'Mar' } }
    },

    // sort: by amount, descending
    {
      id: 'topMarch',
      fromDatasetId: 'march',
      transform: { type: 'sort', config: { dimension: 'amount', order: 'desc' } }
    },

    // aggregate: sum of amount grouped by region
    {
      id: 'byRegion',
      fromDatasetId: 'sales',
      transform: {
        type: 'ecSimpleTransform:aggregate',
        config: { resultDimensions: [{ name: 'total', from: 'amount', method: 'sum' }], groupBy: 'region' }
      }
    }
  ],

  tooltip: { trigger: 'axis' },
  xAxis: { type: 'category', name: 'Region' },
  yAxis: { type: 'value' },
  series: [{ type: 'bar', datasetId: 'byRegion', encode: { x: 'region', y: 'total' } }]
};
TransformConfig keysTypical use
filterdimension, =, >, <, and, or, parserSlice by a category or range
sortdimension, orderRank before rendering a bar chart
ecSimpleTransform:aggregategroupBy, resultDimensions with methodSum, average, min, max, count
boxplotconfig with layoutDerive quartiles
ecSimpleTransform:regressionmethod, dimensionsTrend line from raw points
Custom (registerTransform)Your own functionDomain-specific calculation
// A custom transform: registered once, then usable by name anywhere.
import { registerTransform } from 'echarts/core';

registerTransform({
  type: 'transform',
  transform(params) {
    const { upstream, upstreamData } = params;
    const values = upstreamData.map((row) => [row[0], row[1] * 1.2]);
    const dims = upstream[0].dimensions.slice();
    return { dimensions: dims, data: values };
  }
});

// Parsing strings to numbers and dates inside a transform
const withOption = {
  dataset: [{
    id: 'raw',
    source: [{ when: '2026-01-15', amount: '820' }]
  }, {
    fromDatasetId: 'raw',
    transform: {
      type: 'filter',
      config: {
        dimension: 'when',
        '>': '2026-02-01',
        parser: 'time'                  // parse the dimension as a date before comparing
      }
    }
  }]
};

Transforms run inside ECharts, in the same pass that prepares the series data. That means the derived dataset is available to several series without extra JavaScript, and the logic is declarative — a real advantage when the chart option is itself generated from configuration.

FAQ

Should I use dataset or series.data?
Use dataset when more than one series reads the same rows, when the data comes from a table-shaped API, or when you want transforms. Use series.data for a single series with hand-built values where a dataset would be ceremony.
Why does my pie chart render nothing from a dataset?
The mapping names differ. A pie needs encode: { itemName: 'name', value: 'amount' }; the axis-oriented x and y keys mean nothing without a Cartesian coordinate system.

Option configuration Coordinate systems, axes and scales

Last refreshed 2026-09-18.