You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2022/03/18 04:15:33 UTC

[echarts] 02/02: feat(transform): introduce aggregate and id transform

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch aggregate-transform
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit f6a7c3aee0fa7ed717a06da8e94492048eae957c
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Mar 18 12:15:00 2022 +0800

    feat(transform): introduce aggregate and id transform
---
 src/component/transform/aggregateTransform.ts | 595 ++++++++++++++++++++++++++
 src/component/transform/idTransform.ts        |  95 ++++
 src/component/transform/install.ts            |   4 +
 test/custom-shape-morphing2.html              |  13 +-
 test/custom-shape-morphing3.html              |  13 +-
 test/data-transform-aggregate.html            |  26 +-
 test/lib/config.js                            |   1 -
 test/lib/ecSimpleTransform.js                 | 362 ----------------
 test/universalTransition2.html                |  10 +-
 9 files changed, 718 insertions(+), 401 deletions(-)

diff --git a/src/component/transform/aggregateTransform.ts b/src/component/transform/aggregateTransform.ts
new file mode 100644
index 0000000..5bbf131
--- /dev/null
+++ b/src/component/transform/aggregateTransform.ts
@@ -0,0 +1,595 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import { assert, hasOwn } from 'zrender/src/core/util';
+import {
+    DataTransformOption,
+    ExternalDataTransform,
+    ExternalDimensionDefinition,
+    ExternalSource
+} from '../../data/helper/transform';
+import { asc, quantile } from '../../util/number';
+import { DimensionLoose, DimensionName, OptionDataValue } from '../../util/types';
+
+/**
+ * @usage
+ *
+ * ```js
+ * dataset: [{
+ *     source: [
+ *         ['aa', 'bb', 'cc', 'tag'],
+ *         [12, 0.33, 5200, 'AA'],
+ *         [21, 0.65, 7100, 'AA'],
+ *         [51, 0.15, 1100, 'BB'],
+ *         [71, 0.75, 9100, 'BB'],
+ *         ...
+ *     ]
+ * }, {
+ *     transform: {
+ *         type: 'ecSimpleTransform:aggregate',
+ *         config: {
+ *             output: [
+ *                 // by default, use the same name with `from`.
+ *                 { from: 'aa', method: 'sum' },
+ *                 { from: 'bb', method: 'count' },
+ *                 { from: 'cc' }, // method by default: use the first value.
+ *                 { from: 'dd', method: 'Q1' },
+ *                 { from: 'tag' }
+ *             ],
+ *             groupBy: 'tag'
+ *         }
+ *     }
+ *     // Then the result data will be:
+ *     // [
+ *     //     ['aa', 'bb', 'cc', 'tag'],
+ *     //     [12, 0.33, 5200, 'AA'],
+ *     //     [21, 0.65, 8100, 'BB'],
+ *     //     ...
+ *     // ]
+ * }]
+ * ```
+ */
+
+export interface AggregateTransformOption extends DataTransformOption {
+    type: 'echarts:aggregate';
+    config: {
+        // Mandatory
+        output: {
+            // Optional. The name of the result dimensions.
+            // If not provided, inherit the name from `from`.
+            name: DimensionName;
+            // Mandatory. `from` is used to reference dimension from `source`.
+            from: DimensionLoose;
+            // Optional. Aggregate method. Currently only these method supported.
+            // If not provided, use `'first'`.
+            method: AggregateMethodLoose;
+        }[];
+        // Optional
+        groupBy: DimensionLoose;
+    };
+}
+
+const METHOD_INTERNAL = {
+    'SUM': true,
+    'COUNT': true,
+    'FIRST': true,
+    'AVERAGE': true,
+    'Q1': true,
+    'Q2': true,
+    'Q3': true,
+    'MIN': true,
+    'MAX': true
+} as const;
+const METHOD_NEEDS_COLLECT = {
+    AVERAGE: ['COUNT']
+} as const;
+const METHOD_NEEDS_GATHER_VALUES = {
+    Q1: true,
+    Q2: true,
+    Q3: true
+} as const;
+const METHOD_ALIAS = {
+    MEDIAN: 'Q2'
+} as const;
+
+type AggregateMethodLoose =
+    AggregateMethodInternal
+    | 'sum' | 'count' | 'first' | 'average' | 'Q1' | 'Q2' | 'Q3' | 'median' | 'min' | 'max';
+type AggregateMethodInternal = keyof typeof METHOD_INTERNAL;
+
+
+class ResultDimInfoInternal {
+
+    readonly method: AggregateMethodInternal;
+    readonly name: DimensionName;
+    readonly index: number;
+    readonly indexInUpstream: number;
+
+    readonly collectionInfoList = [] as {
+        method: AggregateMethodInternal;
+        indexInLine: number;
+    }[];
+
+    // FIXME: refactor
+    readonly gatheredValuesByGroup: { [groupVal: string]: number[] } = {};
+    readonly gatheredValuesNoGroup = [] as number[];
+    readonly needGatherValues: boolean = false;
+
+    __collectionResult: TravelResult<CollectionResultLine>;
+
+    private _collectionInfoMap = {} as {
+        // number is the index of `list`
+        [method in AggregateMethodInternal]: number
+    };
+
+    constructor(
+        index: number,
+        indexInUpstream: number,
+        method: AggregateMethodInternal,
+        name: DimensionName,
+        needGatherValues: boolean
+    ) {
+        this.method = method;
+        this.name = name;
+        this.index = index;
+        this.indexInUpstream = indexInUpstream;
+        this.needGatherValues = needGatherValues;
+    }
+
+    addCollectionInfo(item: ResultDimInfoInternal['collectionInfoList'][number]) {
+        this._collectionInfoMap[item.method] = this.collectionInfoList.length;
+        this.collectionInfoList.push(item);
+    }
+
+    getCollectionInfo(method: AggregateMethodInternal) {
+        return this.collectionInfoList[this._collectionInfoMap[method]];
+    }
+
+    // FIXME: temp implementation. Need refactor.
+    gatherValue(groupByDimInfo: ExternalDimensionDefinition, groupVal: OptionDataValue, value: OptionDataValue) {
+        // FIXME: convert to number compulsorily temporarily.
+        value = +value;
+        if (groupByDimInfo) {
+            if (groupVal != null) {
+                const groupValStr = groupVal + '';
+                const values = this.gatheredValuesByGroup[groupValStr]
+                    || (this.gatheredValuesByGroup[groupValStr] = []);
+                values.push(value);
+            }
+        }
+        else {
+            this.gatheredValuesNoGroup.push(value);
+        }
+    }
+}
+
+type CreateInTravel<LINE> = (
+    upstream: ExternalSource,
+    dataIndex: number,
+    dimInfoList: ResultDimInfoInternal[],
+    groupByDimInfo?: ExternalDimensionDefinition,
+    groupByVal?: OptionDataValue
+) => LINE;
+type UpdateInTravel<LINE> = (
+    upstream: ExternalSource,
+    dataIndex: number,
+    targetLine: LINE,
+    dimInfoList: ResultDimInfoInternal[],
+    groupByDimInfo?: ExternalDimensionDefinition,
+    groupByVal?: OptionDataValue
+) => void;
+
+export const aggregateTransform: ExternalDataTransform<AggregateTransformOption> = {
+
+    type: 'echarts:aggregate',
+
+    transform: function (params) {
+        const upstream = params.upstream;
+        const config = params.config;
+
+        const groupByDimInfo = prepareGroupByDimInfo(config, upstream);
+        const { finalResultDimInfoList, collectionDimInfoList } = prepareDimensions(
+            config, upstream, groupByDimInfo
+        );
+
+        // Collect
+        let collectionResult: TravelResult<CollectionResultLine>;
+        if (collectionDimInfoList.length) {
+            collectionResult = travel(
+                groupByDimInfo,
+                upstream,
+                collectionDimInfoList,
+                createCollectionResultLine,
+                updateCollectionResultLine
+            );
+        }
+
+        for (let i = 0; i < collectionDimInfoList.length; i++) {
+            const dimInfo = collectionDimInfoList[i];
+            dimInfo.__collectionResult = collectionResult;
+            // FIXME: just for Q1, Q2, Q3: need asc.
+            asc(dimInfo.gatheredValuesNoGroup);
+
+            const gatheredValuesByGroup = dimInfo.gatheredValuesByGroup;
+            for (const key in gatheredValuesByGroup) {
+                if (hasOwn(gatheredValuesByGroup, key)) {
+                    asc(gatheredValuesByGroup[key]);
+                }
+            }
+        }
+
+        // Calculate
+        const finalResult = travel(
+            groupByDimInfo,
+            upstream,
+            finalResultDimInfoList,
+            createFinalResultLine,
+            updateFinalResultLine
+        );
+
+        const dimensions = [];
+        for (let i = 0; i < finalResultDimInfoList.length; i++) {
+            dimensions.push(finalResultDimInfoList[i].name);
+        }
+
+        return {
+            dimensions: dimensions,
+            data: finalResult.outList
+        };
+    }
+};
+
+function prepareDimensions(
+    config: AggregateTransformOption['config'],
+    upstream: ExternalSource,
+    groupByDimInfo: ExternalDimensionDefinition
+): {
+    finalResultDimInfoList: ResultDimInfoInternal[];
+    collectionDimInfoList: ResultDimInfoInternal[];
+} {
+    const outputConfig = config.output;
+    const finalResultDimInfoList: ResultDimInfoInternal[] = [];
+    const collectionDimInfoList: ResultDimInfoInternal[] = [];
+    let gIndexInLine = 0;
+
+    for (let i = 0; i < outputConfig.length; i++) {
+        const resultDimInfoConfig = outputConfig[i];
+
+        const dimInfoInUpstream = upstream.getDimensionInfo(resultDimInfoConfig.from);
+        if (__DEV__) {
+            assert(dimInfoInUpstream, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
+        }
+
+        const rawMethod = resultDimInfoConfig.method;
+
+        if (__DEV__) {
+            assert(
+                groupByDimInfo.index !== dimInfoInUpstream.index || rawMethod == null,
+                `Dimension ${dimInfoInUpstream.name} is the "groupBy" dimension, must not have any "method".`
+            );
+        }
+
+        const method = normalizeMethod(rawMethod);
+
+        if (__DEV__) {
+            assert(method, 'method is required');
+        }
+
+        const name = resultDimInfoConfig.name != null ? resultDimInfoConfig.name : dimInfoInUpstream.name;
+
+        const finalResultDimInfo = new ResultDimInfoInternal(
+            finalResultDimInfoList.length,
+            dimInfoInUpstream.index,
+            method,
+            name,
+            hasOwn(METHOD_NEEDS_GATHER_VALUES, method)
+        );
+        finalResultDimInfoList.push(finalResultDimInfo);
+
+        // For collection.
+        let needCollect = false;
+        if (hasOwn(METHOD_NEEDS_COLLECT, method)) {
+            needCollect = true;
+            const collectionTargetMethods = METHOD_NEEDS_COLLECT[method as keyof typeof METHOD_NEEDS_COLLECT];
+            for (let j = 0; j < collectionTargetMethods.length; j++) {
+                finalResultDimInfo.addCollectionInfo({
+                    method: collectionTargetMethods[j],
+                    indexInLine: gIndexInLine++
+                });
+            }
+        }
+        if (hasOwn(METHOD_NEEDS_GATHER_VALUES, method)) {
+            needCollect = true;
+        }
+        if (needCollect) {
+            collectionDimInfoList.push(finalResultDimInfo);
+        }
+    }
+
+    return { collectionDimInfoList, finalResultDimInfoList };
+}
+
+function prepareGroupByDimInfo(
+    config: AggregateTransformOption['config'],
+    upstream: ExternalSource
+): ExternalDimensionDefinition {
+    const groupByConfig = config.groupBy;
+    let groupByDimInfo;
+    if (groupByConfig != null) {
+        groupByDimInfo = upstream.getDimensionInfo(groupByConfig);
+        assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupByConfig);
+    }
+    return groupByDimInfo;
+}
+
+interface TravelResult<LINE> {
+    mapByGroup: { [groupVal: string]: LINE };
+    outList: LINE[];
+}
+
+function travel<LINE>(
+    groupByDimInfo: ExternalDimensionDefinition,
+    upstream: ExternalSource,
+    resultDimInfoList: ResultDimInfoInternal[],
+    doCreate: CreateInTravel<LINE>,
+    doUpdate: UpdateInTravel<LINE>
+): TravelResult<LINE> {
+    const outList: TravelResult<LINE>['outList'] = [];
+    let mapByGroup: TravelResult<LINE>['mapByGroup'];
+
+    if (groupByDimInfo) {
+        mapByGroup = {};
+
+        for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
+            const groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index);
+
+            // PENDING: when value is null/undefined
+            if (groupByVal == null) {
+                continue;
+            }
+
+            const groupByValStr = groupByVal + '';
+
+            if (!hasOwn(mapByGroup, groupByValStr)) {
+                const newLine = doCreate(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal);
+                outList.push(newLine);
+                mapByGroup[groupByValStr] = newLine;
+            }
+            else {
+                const targetLine = mapByGroup[groupByValStr];
+                doUpdate(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo, groupByVal);
+            }
+        }
+    }
+    else {
+        const targetLine = doCreate(upstream, 0, resultDimInfoList);
+        outList.push(targetLine);
+        for (let dataIndex = 1, len = upstream.count(); dataIndex < len; dataIndex++) {
+            doUpdate(upstream, dataIndex, targetLine, resultDimInfoList);
+        }
+    }
+
+    return { mapByGroup, outList };
+}
+
+function normalizeMethod(method: AggregateMethodLoose): AggregateMethodInternal {
+    if (method == null) {
+        return 'FIRST';
+    }
+    let methodInternal = method.toUpperCase() as AggregateMethodInternal;
+    methodInternal = hasOwn(METHOD_ALIAS, methodInternal)
+        ? METHOD_ALIAS[methodInternal as keyof typeof METHOD_ALIAS]
+        : methodInternal;
+    assert(hasOwn(METHOD_INTERNAL, methodInternal), `Illegal method ${method}.`);
+    return methodInternal;
+}
+
+
+
+type CollectionResultLine = number[];
+
+const createCollectionResultLine: CreateInTravel<CollectionResultLine> = (
+    upstream, dataIndex, collectionDimInfoList, groupByDimInfo, groupByVal
+) => {
+    const newLine = [] as number[];
+    for (let i = 0; i < collectionDimInfoList.length; i++) {
+        const dimInfo = collectionDimInfoList[i];
+        const collectionInfoList = dimInfo.collectionInfoList;
+        for (let j = 0; j < collectionInfoList.length; j++) {
+            const collectionInfo = collectionInfoList[j];
+            // FIXME: convert to number compulsorily temporarily.
+            newLine[collectionInfo.indexInLine] = +lineCreator[collectionInfo.method](
+                upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal
+            );
+        }
+        // FIXME: refactor
+        if (dimInfo.needGatherValues) {
+            const val = upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
+            dimInfo.gatherValue(groupByDimInfo, groupByVal, val);
+        }
+    }
+    return newLine;
+};
+
+const updateCollectionResultLine: UpdateInTravel<CollectionResultLine> = (
+    upstream, dataIndex, targetLine: number[], collectionDimInfoList, groupByDimInfo, groupByVal
+) => {
+    for (let i = 0; i < collectionDimInfoList.length; i++) {
+        const dimInfo = collectionDimInfoList[i];
+        const collectionInfoList = dimInfo.collectionInfoList;
+        for (let j = 0; j < collectionInfoList.length; j++) {
+            const collectionInfo = collectionInfoList[j];
+            const indexInLine = collectionInfo.indexInLine;
+            // FIXME: convert to number compulsorily temporarily.
+            targetLine[indexInLine] = +lineUpdater[collectionInfo.method](
+                targetLine[indexInLine], upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal
+            );
+        }
+        // FIXME: refactor
+        if (dimInfo.needGatherValues) {
+            const val = upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
+            dimInfo.gatherValue(groupByDimInfo, groupByVal, val);
+        }
+    }
+};
+
+
+
+type FinalResultLine = OptionDataValue[];
+
+const createFinalResultLine: CreateInTravel<FinalResultLine> = (
+    upstream, dataIndex, finalResultDimInfoList, groupByDimInfo, groupByVal
+) => {
+    const newLine = [];
+    for (let i = 0; i < finalResultDimInfoList.length; i++) {
+        const dimInfo = finalResultDimInfoList[i];
+        const method = dimInfo.method;
+        newLine[i] = isGroupByDimension(groupByDimInfo, dimInfo)
+            ? groupByVal
+            : lineCreator[method](
+                upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal
+            );
+    }
+    return newLine;
+};
+
+const updateFinalResultLine: UpdateInTravel<FinalResultLine> = (
+    upstream, dataIndex, targetLine, finalResultDimInfoList, groupByDimInfo, groupByVal
+) => {
+    for (let i = 0; i < finalResultDimInfoList.length; i++) {
+        const dimInfo = finalResultDimInfoList[i];
+        if (isGroupByDimension(groupByDimInfo, dimInfo)) {
+            continue;
+        }
+        const method = dimInfo.method;
+        targetLine[i] = lineUpdater[method](
+            targetLine[i], upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal
+        );
+    }
+};
+
+function isGroupByDimension(
+    groupByDimInfo: ExternalDimensionDefinition,
+    targetDimInfo: ResultDimInfoInternal
+): boolean {
+    return groupByDimInfo && targetDimInfo.indexInUpstream === groupByDimInfo.index;
+}
+
+const lineCreator: {
+    [key in AggregateMethodInternal]: (
+        upstream: ExternalSource,
+        dataIndex: number,
+        dimInfo: ResultDimInfoInternal,
+        groupByDimInfo: ExternalDimensionDefinition,
+        groupByVal: OptionDataValue
+    ) => OptionDataValue
+} = {
+    'SUM'(upstream, dataIndex, dimInfo) {
+        return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
+    },
+    'COUNT'() {
+        return 1;
+    },
+    'FIRST'(upstream, dataIndex, dimInfo) {
+        return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
+    },
+    'MIN'(upstream, dataIndex, dimInfo) {
+        return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
+    },
+    'MAX'(upstream, dataIndex, dimInfo) {
+        return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
+    },
+    'AVERAGE'(upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
+        const collectionResult = dimInfo.__collectionResult;
+        // FIXME: refactor, bad implementation.
+        const collectLine = groupByDimInfo
+            ? collectionResult.mapByGroup[groupByVal + '']
+            : collectionResult.outList[0];
+        return (upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) as number)
+            / collectLine[dimInfo.getCollectionInfo('COUNT').indexInLine];
+    },
+    // FIXME: refactor
+    'Q1'(upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
+        return lineCreatorForQ(0.25, dimInfo, groupByDimInfo, groupByVal);
+    },
+    'Q2'(upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
+        return lineCreatorForQ(0.5, dimInfo, groupByDimInfo, groupByVal);
+    },
+    'Q3'(upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
+        return lineCreatorForQ(0.75, dimInfo, groupByDimInfo, groupByVal);
+    }
+};
+
+const lineUpdater: {
+    [key in AggregateMethodInternal]: (
+        val: OptionDataValue,
+        upstream: ExternalSource,
+        dataIndex: number,
+        dimInfo: ResultDimInfoInternal,
+        groupByDimInfo: ExternalDimensionDefinition,
+        groupByVal: OptionDataValue
+    ) => OptionDataValue
+} = {
+    'SUM'(val, upstream, dataIndex, dimInfo) {
+        // FIXME: handle other types
+        return (val as number) + (upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) as number);
+    },
+    'COUNT'(val) {
+        return (val as number) + 1;
+    },
+    'FIRST'(val) {
+        return val;
+    },
+    'MIN'(val, upstream, dataIndex, dimInfo) {
+        return Math.min(val as number, upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) as number);
+    },
+    'MAX'(val, upstream, dataIndex, dimInfo) {
+        return Math.max(val as number, upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) as number);
+    },
+    'AVERAGE'(val, upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
+        // FIXME: refactor, bad implementation.
+        const collectLine = groupByDimInfo
+            ? dimInfo.__collectionResult.mapByGroup[groupByVal + '']
+            : dimInfo.__collectionResult.outList[0];
+        return (val as number)
+            + (upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream) as number)
+            / collectLine[dimInfo.getCollectionInfo('COUNT').indexInLine];
+    },
+    'Q1'(val, upstream, dataIndex, dimInfo) {
+        return val;
+    },
+    'Q2'(val, upstream, dataIndex, dimInfo) {
+        return val;
+    },
+    'Q3'(val, upstream, dataIndex, dimInfo) {
+        return val;
+    }
+};
+
+function lineCreatorForQ(
+    percent: number,
+    dimInfo: ResultDimInfoInternal,
+    groupByDimInfo: ExternalDimensionDefinition,
+    groupByVal: OptionDataValue
+) {
+    const gatheredValues = groupByDimInfo
+        ? dimInfo.gatheredValuesByGroup[groupByVal + '']
+        : dimInfo.gatheredValuesNoGroup;
+    return quantile(gatheredValues, percent);
+}
\ No newline at end of file
diff --git a/src/component/transform/idTransform.ts b/src/component/transform/idTransform.ts
new file mode 100644
index 0000000..8c01628
--- /dev/null
+++ b/src/component/transform/idTransform.ts
@@ -0,0 +1,95 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import { DataTransformOption, ExternalDataTransform } from '../../data/helper/transform';
+import {
+    DimensionDefinitionLoose,
+    DimensionIndex,
+    DimensionName,
+    OptionSourceDataArrayRows
+} from '../../util/types';
+
+
+/**
+ * @usage
+ *
+ * ```js
+ * dataset: [{
+ *     source: [
+ *         ['aa', 'bb', 'cc', 'tag'],
+ *         [12, 0.33, 5200, 'AA'],
+ *         [21, 0.65, 8100, 'AA'],
+ *         ...
+ *     ]
+ * }, {
+ *     transform: {
+ *         type: 'ecSimpleTransform:id',
+ *         config: {
+ *             dimensionIndex: 4,
+ *             dimensionName: 'ID'
+ *         }
+ *     }
+ *     // Then the result data will be:
+ *     // [
+ *     //     ['aa', 'bb', 'cc', 'tag', 'ID'],
+ *     //     [12, 0.33, 5200, 'AA', 0],
+ *     //     [21, 0.65, 8100, 'BB', 1],
+ *     //     ...
+ *     // ]
+ * }]
+ * ```
+ */
+
+export interface IdTransformOption extends DataTransformOption {
+    type: 'echarts:id';
+    config: {
+        // Mandatory. Specify where to put the new id dimension.
+        dimensionIndex: DimensionIndex;
+        // Optional. If not provided, left the dimension name not defined.
+        dimensionName: DimensionName;
+    };
+}
+
+export const idTransform: ExternalDataTransform<IdTransformOption> = {
+
+    type: 'echarts:id',
+
+    transform: function (params) {
+        const upstream = params.upstream;
+        const config = params.config;
+        const dimensionIndex = config.dimensionIndex;
+        const dimensionName = config.dimensionName;
+
+        const dimsDef = upstream.cloneAllDimensionInfo() as DimensionDefinitionLoose[];
+        dimsDef[dimensionIndex] = dimensionName;
+
+        const data = upstream.cloneRawData() as OptionSourceDataArrayRows;
+
+        // TODO: support objectRows
+        for (let i = 0, len = data.length; i < len; i++) {
+            const line = data[i];
+            line[dimensionIndex] = i;
+        }
+
+        return {
+            dimensions: dimsDef,
+            data: data
+        };
+    }
+};
\ No newline at end of file
diff --git a/src/component/transform/install.ts b/src/component/transform/install.ts
index 208f6e0..01c1074 100644
--- a/src/component/transform/install.ts
+++ b/src/component/transform/install.ts
@@ -20,8 +20,12 @@
 import { EChartsExtensionInstallRegisters } from '../../extension';
 import {filterTransform} from './filterTransform';
 import {sortTransform} from './sortTransform';
+import {aggregateTransform} from './aggregateTransform';
+import { idTransform } from './idTransform';
 
 export function install(registers: EChartsExtensionInstallRegisters) {
     registers.registerTransform(filterTransform);
     registers.registerTransform(sortTransform);
+    registers.registerTransform(aggregateTransform);
+    registers.registerTransform(idTransform);
 }
\ No newline at end of file
diff --git a/test/custom-shape-morphing2.html b/test/custom-shape-morphing2.html
index 8b5d2bc..91e8890 100644
--- a/test/custom-shape-morphing2.html
+++ b/test/custom-shape-morphing2.html
@@ -75,10 +75,9 @@ under the License.
         <script>
 
             require([
-                'echarts', 'ecStat', 'ecSimpleTransform', 'ecSimpleOptionPlayer'
-            ], function (echarts, ecStat, ecSimpleTransform, ecSimpleOptionPlayer) {
+                'echarts', 'ecStat', 'ecSimpleOptionPlayer'
+            ], function (echarts, ecStat, ecSimpleOptionPlayer) {
 
-                echarts.registerTransform(ecSimpleTransform.aggregate);
                 echarts.registerTransform(ecStat.transform.clustering);
 
 
@@ -146,9 +145,9 @@ under the License.
                         id: 'mTagSum',
                         fromDatasetId: 'raw',
                         transform: {
-                            type: 'ecSimpleTransform:aggregate',
+                            type: 'aggregate',
                             config: {
-                                resultDimensions: [
+                                output: [
                                     { from: 'ATA', method: 'sum' },
                                     { from: 'STE', method: 'sum' },
                                     { from: 'CTZ', method: 'sum' },
@@ -183,10 +182,10 @@ under the License.
                         id: 'rawClusterCenters',
                         fromDatasetId: 'rawClusters',
                         transform: {
-                            type: 'ecSimpleTransform:aggregate',
+                            type: 'aggregate',
                             print: true,
                             config: {
-                                resultDimensions: [
+                                output: [
                                     { name: 'COUNT', from: 'ATA', method: 'count' },
                                     { from: 'CLUSTER_CENTER_ATA' },
                                     { from: 'CLUSTER_CENTER_STE' },
diff --git a/test/custom-shape-morphing3.html b/test/custom-shape-morphing3.html
index a5765f0..ad0f908 100644
--- a/test/custom-shape-morphing3.html
+++ b/test/custom-shape-morphing3.html
@@ -40,12 +40,9 @@ under the License.
         <script>
 
         require([
-            'echarts', 'ecStat', 'ecSimpleTransform', 'ecSimpleOptionPlayer',
+            'echarts', 'ecStat', 'ecSimpleOptionPlayer',
             'data/life-expectancy-table.json'
-        ], function (echarts, ecStat, ecSimpleTransform, ecSimpleOptionPlayer, rawData) {
-
-            echarts.registerTransform(ecSimpleTransform.aggregate);
-            echarts.registerTransform(ecSimpleTransform.id);
+        ], function (echarts, ecStat, ecSimpleOptionPlayer, rawData) {
 
             const COLORS = [
                 '#37A2DA', '#e06343', '#37a354', '#b55dba', '#b5bd48', '#8378EA', '#96BFFF'
@@ -93,7 +90,7 @@ under the License.
                             dimension: 'Year', gte: 1950
                         }
                     }, {
-                        type: 'ecSimpleTransform:id',
+                        type: 'id',
                         config: {
                             dimensionIndex: ID_RAW_DATA_DIMENSIONS.indexOf('Id'),
                             dimensionName: 'Id'
@@ -116,9 +113,9 @@ under the License.
                     id: 'CountryABSumIncome',
                     fromDatasetId: 'CountryABData',
                     transform: {
-                        type: 'ecSimpleTransform:aggregate',
+                        type: 'aggregate',
                         config: {
-                            resultDimensions: [
+                            output: [
                                 { from: 'Income', method: 'sum' },
                                 { from: 'Country' }
                             ],
diff --git a/test/data-transform-aggregate.html b/test/data-transform-aggregate.html
index f282099..97be6ed 100644
--- a/test/data-transform-aggregate.html
+++ b/test/data-transform-aggregate.html
@@ -47,10 +47,8 @@ under the License.
         <script>
         require([
             'echarts',
-            'ecSimpleTransform',
             'data/life-expectancy-table.json'
-        ], function (echarts, ecSimpleTransform, _rawData) {
-            echarts.registerTransform(ecSimpleTransform.aggregate);
+        ], function (echarts, _rawData) {
 
             option = {
                 dataset: [{
@@ -65,9 +63,9 @@ under the License.
                             dimension: 'Year', gte: 1950
                         }
                     }, {
-                        type: 'ecSimpleTransform:aggregate',
+                        type: 'aggregate',
                         config: {
-                            resultDimensions: [
+                            output: [
                                 { from: 'Income', method: 'sum' },
                                 { from: 'Country' }
                             ],
@@ -123,11 +121,9 @@ under the License.
         <script>
         require([
             'echarts',
-            'ecSimpleTransform',
             'data/life-expectancy-table.json'
-        ], function (echarts, ecSimpleTransform, _rawData) {
+        ], function (echarts, _rawData) {
 
-            echarts.registerTransform(ecSimpleTransform.aggregate);
 
             option = {
                 dataset: [{
@@ -146,9 +142,9 @@ under the License.
                             }]
                         }
                     }, {
-                        type: 'ecSimpleTransform:aggregate',
+                        type: 'aggregate',
                         config: {
-                            resultDimensions: [
+                            output: [
                                 { name: 'min', from: 'Income', method: 'min' },
                                 { name: 'Q1', from: 'Income', method: 'Q1' },
                                 { name: 'Q2', from: 'Income', method: 'Q2' },
@@ -212,11 +208,9 @@ under the License.
         <script>
         require([
             'echarts',
-            'ecSimpleTransform',
             'data/life-expectancy-table.json'
-        ], function (echarts, ecSimpleTransform, _rawData) {
+        ], function (echarts, _rawData) {
 
-            echarts.registerTransform(ecSimpleTransform.aggregate);
 
             option = {
                 dataset: [{
@@ -231,14 +225,12 @@ under the License.
                         config: {
                             and: [{
                                 dimension: 'Year', gte: 1950
-                            // }, {
-                            //     dimension: 'Country', eq: 'Japan'
                             }]
                         }
                     }, {
-                        type: 'ecSimpleTransform:aggregate',
+                        type: 'aggregate',
                         config: {
-                            resultDimensions: [
+                            output: [
                                 { from: 'Income', method: 'average' },
                                 { from: 'Country' }
                             ],
diff --git a/test/lib/config.js b/test/lib/config.js
index 1c99b35..2b3eeb8 100644
--- a/test/lib/config.js
+++ b/test/lib/config.js
@@ -58,7 +58,6 @@
                 'echarts': ecDistPath,
                 'zrender': 'node_modules/zrender/dist/zrender',
                 'ecStat': 'lib/ecStat.min',
-                'ecSimpleTransform': 'lib/ecSimpleTransform',
                 'ecSimpleOptionPlayer': 'lib/ecSimpleOptionPlayer',
                 // 'ecStat': 'http://localhost:8001/echarts/echarts-stat/dist/ecStat',
                 'geoJson': '../geoData/geoJson',
diff --git a/test/lib/ecSimpleTransform.js b/test/lib/ecSimpleTransform.js
deleted file mode 100644
index d780fc4..0000000
--- a/test/lib/ecSimpleTransform.js
+++ /dev/null
@@ -1,362 +0,0 @@
-(function (global, factory) {
-    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
-    typeof define === 'function' && define.amd ? define(['exports'], factory) :
-    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ecSimpleTransform = {}));
-}(this, (function (exports) { 'use strict';
-
-    var transform = {
-        type: 'ecSimpleTransform:id',
-        transform: function (params) {
-            var upstream = params.upstream;
-            var config = params.config;
-            var dimensionIndex = config.dimensionIndex;
-            var dimensionName = config.dimensionName;
-            var dimsDef = upstream.cloneAllDimensionInfo();
-            dimsDef[dimensionIndex] = dimensionName;
-            var data = upstream.cloneRawData();
-            for (var i = 0, len = data.length; i < len; i++) {
-                var line = data[i];
-                line[dimensionIndex] = i;
-            }
-            return {
-                dimensions: dimsDef,
-                data: data
-            };
-        }
-    };
-
-    function assert(condition, message) {
-        if (!condition) {
-            throw new Error(message);
-        }
-    }
-    function hasOwn(own, prop) {
-        return own.hasOwnProperty(prop);
-    }
-    function quantile(ascArr, p) {
-        var H = (ascArr.length - 1) * p + 1;
-        var h = Math.floor(H);
-        var v = +ascArr[h - 1];
-        var e = H - h;
-        return e ? v + e * (ascArr[h] - v) : v;
-    }
-
-    var METHOD_INTERNAL = {
-        'SUM': true,
-        'COUNT': true,
-        'FIRST': true,
-        'AVERAGE': true,
-        'Q1': true,
-        'Q2': true,
-        'Q3': true,
-        'MIN': true,
-        'MAX': true
-    };
-    var METHOD_NEEDS_COLLECT = {
-        AVERAGE: ['COUNT']
-    };
-    var METHOD_NEEDS_GATHER_VALUES = {
-        Q1: true,
-        Q2: true,
-        Q3: true
-    };
-    var METHOD_ALIAS = {
-        MEDIAN: 'Q2'
-    };
-    var ResultDimInfoInternal = (function () {
-        function ResultDimInfoInternal(index, indexInUpstream, method, name, needGatherValues) {
-            this.collectionInfoList = [];
-            this.gatheredValuesByGroup = {};
-            this.gatheredValuesNoGroup = [];
-            this.needGatherValues = false;
-            this._collectionInfoMap = {};
-            this.method = method;
-            this.name = name;
-            this.index = index;
-            this.indexInUpstream = indexInUpstream;
-            this.needGatherValues = needGatherValues;
-        }
-        ResultDimInfoInternal.prototype.addCollectionInfo = function (item) {
-            this._collectionInfoMap[item.method] = this.collectionInfoList.length;
-            this.collectionInfoList.push(item);
-        };
-        ResultDimInfoInternal.prototype.getCollectionInfo = function (method) {
-            return this.collectionInfoList[this._collectionInfoMap[method]];
-        };
-        ResultDimInfoInternal.prototype.gatherValue = function (groupByDimInfo, groupVal, value) {
-            value = +value;
-            if (groupByDimInfo) {
-                if (groupVal != null) {
-                    var groupValStr = groupVal + '';
-                    var values = this.gatheredValuesByGroup[groupValStr]
-                        || (this.gatheredValuesByGroup[groupValStr] = []);
-                    values.push(value);
-                }
-            }
-            else {
-                this.gatheredValuesNoGroup.push(value);
-            }
-        };
-        return ResultDimInfoInternal;
-    }());
-    var transform$1 = {
-        type: 'ecSimpleTransform:aggregate',
-        transform: function (params) {
-            var upstream = params.upstream;
-            var config = params.config;
-            var groupByDimInfo = prepareGroupByDimInfo(config, upstream);
-            var _a = prepareDimensions(config, upstream, groupByDimInfo), finalResultDimInfoList = _a.finalResultDimInfoList, collectionDimInfoList = _a.collectionDimInfoList;
-            var collectionResult;
-            if (collectionDimInfoList.length) {
-                collectionResult = travel(groupByDimInfo, upstream, collectionDimInfoList, createCollectionResultLine, updateCollectionResultLine);
-            }
-            for (var i = 0; i < collectionDimInfoList.length; i++) {
-                var dimInfo = collectionDimInfoList[i];
-                dimInfo.__collectionResult = collectionResult;
-                asc(dimInfo.gatheredValuesNoGroup);
-                var gatheredValuesByGroup = dimInfo.gatheredValuesByGroup;
-                for (var key in gatheredValuesByGroup) {
-                    if (hasOwn(gatheredValuesByGroup, key)) {
-                        asc(gatheredValuesByGroup[key]);
-                    }
-                }
-            }
-            var finalResult = travel(groupByDimInfo, upstream, finalResultDimInfoList, createFinalResultLine, updateFinalResultLine);
-            var dimensions = [];
-            for (var i = 0; i < finalResultDimInfoList.length; i++) {
-                dimensions.push(finalResultDimInfoList[i].name);
-            }
-            return {
-                dimensions: dimensions,
-                data: finalResult.outList
-            };
-        }
-    };
-    function prepareDimensions(config, upstream, groupByDimInfo) {
-        var resultDimensionsConfig = config.resultDimensions;
-        var finalResultDimInfoList = [];
-        var collectionDimInfoList = [];
-        var gIndexInLine = 0;
-        for (var i = 0; i < resultDimensionsConfig.length; i++) {
-            var resultDimInfoConfig = resultDimensionsConfig[i];
-            var dimInfoInUpstream = upstream.getDimensionInfo(resultDimInfoConfig.from);
-            assert(dimInfoInUpstream, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
-            var rawMethod = resultDimInfoConfig.method;
-            assert(groupByDimInfo.index !== dimInfoInUpstream.index || rawMethod == null, "Dimension " + dimInfoInUpstream.name + " is the \"groupBy\" dimension, must not have any \"method\".");
-            var method = normalizeMethod(rawMethod);
-            assert(method, 'method is required');
-            var name_1 = resultDimInfoConfig.name != null ? resultDimInfoConfig.name : dimInfoInUpstream.name;
-            var finalResultDimInfo = new ResultDimInfoInternal(finalResultDimInfoList.length, dimInfoInUpstream.index, method, name_1, hasOwn(METHOD_NEEDS_GATHER_VALUES, method));
-            finalResultDimInfoList.push(finalResultDimInfo);
-            var needCollect = false;
-            if (hasOwn(METHOD_NEEDS_COLLECT, method)) {
-                needCollect = true;
-                var collectionTargetMethods = METHOD_NEEDS_COLLECT[method];
-                for (var j = 0; j < collectionTargetMethods.length; j++) {
-                    finalResultDimInfo.addCollectionInfo({
-                        method: collectionTargetMethods[j],
-                        indexInLine: gIndexInLine++
-                    });
-                }
-            }
-            if (hasOwn(METHOD_NEEDS_GATHER_VALUES, method)) {
-                needCollect = true;
-            }
-            if (needCollect) {
-                collectionDimInfoList.push(finalResultDimInfo);
-            }
-        }
-        return { collectionDimInfoList: collectionDimInfoList, finalResultDimInfoList: finalResultDimInfoList };
-    }
-    function prepareGroupByDimInfo(config, upstream) {
-        var groupByConfig = config.groupBy;
-        var groupByDimInfo;
-        if (groupByConfig != null) {
-            groupByDimInfo = upstream.getDimensionInfo(groupByConfig);
-            assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupByConfig);
-        }
-        return groupByDimInfo;
-    }
-    function travel(groupByDimInfo, upstream, resultDimInfoList, doCreate, doUpdate) {
-        var outList = [];
-        var mapByGroup;
-        if (groupByDimInfo) {
-            mapByGroup = {};
-            for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
-                var groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index);
-                if (groupByVal == null) {
-                    continue;
-                }
-                var groupByValStr = groupByVal + '';
-                if (!hasOwn(mapByGroup, groupByValStr)) {
-                    var newLine = doCreate(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal);
-                    outList.push(newLine);
-                    mapByGroup[groupByValStr] = newLine;
-                }
-                else {
-                    var targetLine = mapByGroup[groupByValStr];
-                    doUpdate(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo, groupByVal);
-                }
-            }
-        }
-        else {
-            var targetLine = doCreate(upstream, 0, resultDimInfoList);
-            outList.push(targetLine);
-            for (var dataIndex = 1, len = upstream.count(); dataIndex < len; dataIndex++) {
-                doUpdate(upstream, dataIndex, targetLine, resultDimInfoList);
-            }
-        }
-        return { mapByGroup: mapByGroup, outList: outList };
-    }
-    function normalizeMethod(method) {
-        if (method == null) {
-            return 'FIRST';
-        }
-        var methodInternal = method.toUpperCase();
-        methodInternal = hasOwn(METHOD_ALIAS, methodInternal)
-            ? METHOD_ALIAS[methodInternal]
-            : methodInternal;
-        assert(hasOwn(METHOD_INTERNAL, methodInternal), "Illegal method " + method + ".");
-        return methodInternal;
-    }
-    var createCollectionResultLine = function (upstream, dataIndex, collectionDimInfoList, groupByDimInfo, groupByVal) {
-        var newLine = [];
-        for (var i = 0; i < collectionDimInfoList.length; i++) {
-            var dimInfo = collectionDimInfoList[i];
-            var collectionInfoList = dimInfo.collectionInfoList;
-            for (var j = 0; j < collectionInfoList.length; j++) {
-                var collectionInfo = collectionInfoList[j];
-                newLine[collectionInfo.indexInLine] = +lineCreator[collectionInfo.method](upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal);
-            }
-            if (dimInfo.needGatherValues) {
-                var val = upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
-                dimInfo.gatherValue(groupByDimInfo, groupByVal, val);
-            }
-        }
-        return newLine;
-    };
-    var updateCollectionResultLine = function (upstream, dataIndex, targetLine, collectionDimInfoList, groupByDimInfo, groupByVal) {
-        for (var i = 0; i < collectionDimInfoList.length; i++) {
-            var dimInfo = collectionDimInfoList[i];
-            var collectionInfoList = dimInfo.collectionInfoList;
-            for (var j = 0; j < collectionInfoList.length; j++) {
-                var collectionInfo = collectionInfoList[j];
-                var indexInLine = collectionInfo.indexInLine;
-                targetLine[indexInLine] = +lineUpdater[collectionInfo.method](targetLine[indexInLine], upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal);
-            }
-            if (dimInfo.needGatherValues) {
-                var val = upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
-                dimInfo.gatherValue(groupByDimInfo, groupByVal, val);
-            }
-        }
-    };
-    var createFinalResultLine = function (upstream, dataIndex, finalResultDimInfoList, groupByDimInfo, groupByVal) {
-        var newLine = [];
-        for (var i = 0; i < finalResultDimInfoList.length; i++) {
-            var dimInfo = finalResultDimInfoList[i];
-            var method = dimInfo.method;
-            newLine[i] = isGroupByDimension(groupByDimInfo, dimInfo)
-                ? groupByVal
-                : lineCreator[method](upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal);
-        }
-        return newLine;
-    };
-    var updateFinalResultLine = function (upstream, dataIndex, targetLine, finalResultDimInfoList, groupByDimInfo, groupByVal) {
-        for (var i = 0; i < finalResultDimInfoList.length; i++) {
-            var dimInfo = finalResultDimInfoList[i];
-            if (isGroupByDimension(groupByDimInfo, dimInfo)) {
-                continue;
-            }
-            var method = dimInfo.method;
-            targetLine[i] = lineUpdater[method](targetLine[i], upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal);
-        }
-    };
-    function isGroupByDimension(groupByDimInfo, targetDimInfo) {
-        return groupByDimInfo && targetDimInfo.indexInUpstream === groupByDimInfo.index;
-    }
-    function asc(list) {
-        list.sort(function (a, b) {
-            return a - b;
-        });
-    }
-    var lineCreator = {
-        'SUM': function () {
-            return 0;
-        },
-        'COUNT': function () {
-            return 1;
-        },
-        'FIRST': function (upstream, dataIndex, dimInfo) {
-            return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
-        },
-        'MIN': function (upstream, dataIndex, dimInfo) {
-            return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
-        },
-        'MAX': function (upstream, dataIndex, dimInfo) {
-            return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
-        },
-        'AVERAGE': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
-            var collectLine = groupByDimInfo
-                ? dimInfo.__collectionResult.mapByGroup[groupByVal + '']
-                : dimInfo.__collectionResult.outList[0];
-            return upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream)
-                / collectLine[dimInfo.getCollectionInfo('COUNT').indexInLine];
-        },
-        'Q1': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
-            return lineCreatorForQ(0.25, dimInfo, groupByDimInfo, groupByVal);
-        },
-        'Q2': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
-            return lineCreatorForQ(0.5, dimInfo, groupByDimInfo, groupByVal);
-        },
-        'Q3': function (upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
-            return lineCreatorForQ(0.75, dimInfo, groupByDimInfo, groupByVal);
-        }
-    };
-    var lineUpdater = {
-        'SUM': function (val, upstream, dataIndex, dimInfo) {
-            return val + upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream);
-        },
-        'COUNT': function (val) {
-            return val + 1;
-        },
-        'FIRST': function (val) {
-            return val;
-        },
-        'MIN': function (val, upstream, dataIndex, dimInfo) {
-            return Math.min(val, upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream));
-        },
-        'MAX': function (val, upstream, dataIndex, dimInfo) {
-            return Math.max(val, upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream));
-        },
-        'AVERAGE': function (val, upstream, dataIndex, dimInfo, groupByDimInfo, groupByVal) {
-            var collectLine = groupByDimInfo
-                ? dimInfo.__collectionResult.mapByGroup[groupByVal + '']
-                : dimInfo.__collectionResult.outList[0];
-            return val
-                + upstream.retrieveValue(dataIndex, dimInfo.indexInUpstream)
-                    / collectLine[dimInfo.getCollectionInfo('COUNT').indexInLine];
-        },
-        'Q1': function (val, upstream, dataIndex, dimInfo) {
-            return val;
-        },
-        'Q2': function (val, upstream, dataIndex, dimInfo) {
-            return val;
-        },
-        'Q3': function (val, upstream, dataIndex, dimInfo) {
-            return val;
-        }
-    };
-    function lineCreatorForQ(percent, dimInfo, groupByDimInfo, groupByVal) {
-        var gatheredValues = groupByDimInfo
-            ? dimInfo.gatheredValuesByGroup[groupByVal + '']
-            : dimInfo.gatheredValuesNoGroup;
-        return quantile(gatheredValues, percent);
-    }
-
-    exports.aggregate = transform$1;
-    exports.id = transform;
-
-    Object.defineProperty(exports, '__esModule', { value: true });
-
-})));
-//# sourceMappingURL=index.js.map
diff --git a/test/universalTransition2.html b/test/universalTransition2.html
index ef3a998..ff8c21e 100644
--- a/test/universalTransition2.html
+++ b/test/universalTransition2.html
@@ -165,12 +165,10 @@ under the License.
         <script>
 
             require([
-                'echarts', 'ecStat', 'ecSimpleTransform'
-            ], function (echarts, ecStat, ecSimpleTransform) {
+                'echarts', 'ecStat'
+            ], function (echarts, ecStat) {
 
-                echarts.registerTransform(ecSimpleTransform.aggregate);
                 echarts.registerTransform(ecStat.transform.clustering);
-                echarts.registerTransform(ecSimpleTransform.id);
 
                 const RAW_DATA = [[1425139200000,34,0.13,2,"MD","ZD","P0"],[1425225600000,28,0.71,1.5,"MB","ZD","P1"],[1425312000000,23,0.9,2.8,"MA","ZC","P2"],[1425398400000,21,0.58,6,"MB","ZC","P3"],[1425484800000,14,0.1,1.6,"MC","ZA","P4"],[1425571200000,21,0.6,7.7,"MC","ZA","P5"],[1425657600000,23,0.31,2.6,"MC","ZC","P6"],[1425744000000,34,0.74,2.4,"MD","ZE","P7"],[1425830400000,14,0.59,2.3,"MB","ZD","P8"],[1425916800000,18,0.85,5.1,"MB","ZB","P9"],[1426003200000,36,0.96,1.2,"MC","ZC [...]
                 const RAW_DATA_DIMENSIONS = ['DATE', 'COUNT', 'PRICE', 'WEIGHT', 'M_TAG', 'Z_TAG', 'ID'];
@@ -205,9 +203,9 @@ under the License.
                     id: 'raw_cluster_centers_dataset',
                     fromDatasetId: 'raw_clustered_dataset',
                     transform: {
-                        type: 'ecSimpleTransform:aggregate',
+                        type: 'aggregate',
                         config: {
-                            resultDimensions: [
+                            output: [
                                 { from: 'COUNT', method: 'count' },
                                 { from: 'CLUSTER_CENTER_COUNT', method: 'first' },
                                 { from: 'CLUSTER_CENTER_PRICE', method: 'first' },

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org