You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2020/11/21 07:14:25 UTC

[incubator-echarts-examples] 01/03: example: aggregate

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

sushuang pushed a commit to branch next
in repository https://gitbox.apache.org/repos/asf/incubator-echarts-examples.git

commit e2cf1068c84faf72dfd828a9c35672a3cc1d4feb
Author: 100pah <su...@gmail.com>
AuthorDate: Thu Nov 19 19:48:46 2020 +0800

    example: aggregate
---
 public/data/asset/js/myTransform.js     | 697 +++++++++++++++++++++-----------
 public/data/data-transform-aggregate.js |  80 ++++
 2 files changed, 543 insertions(+), 234 deletions(-)

diff --git a/public/data/asset/js/myTransform.js b/public/data/asset/js/myTransform.js
index 1d72b52..a2f97b5 100644
--- a/public/data/asset/js/myTransform.js
+++ b/public/data/asset/js/myTransform.js
@@ -1,247 +1,476 @@
-/*
-* 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.
-*/
-
-(function (exports) {
-
-    /**
-     * @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: 'my:aggregate',
-     *         config: {
-     *             resultDimensions: [
-     *                 // 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: 'tag' }
-     *             ],
-     *             groupBy: 'tag'
-     *         }
-     *     }
-     *     // Then the result data will be:
-     *     // [
-     *     //     ['aa', 'bb', 'cc', 'tag'],
-     *     //     [12, 0.33, 5200, 'AA'],
-     *     //     [21, 0.65, 8100, 'BB'],
-     *     //     ...
-     *     // ]
-     * }]
-     * ```
-     */
-    var transform = {
-
-        type: 'myTransform:aggregate',
-
-        /**
-         * @param params
-         * @param params.config.resultDimensions Mandatory.
-         *        {
-         *            // 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: DimensionIndex | DimensionName;
-         *            // Optional. Aggregate method. Currently only these method supported.
-         *            // If not provided, use `'first'`.
-         *            method: 'sum' | 'count' | 'first';
-         *        }[]
-         * @param params.config.groupBy DimensionIndex | DimensionName Optional.
-         */
-        transform: function (params) {
-            var upstream = params.upstream;
-            var config = params.config;
-            var resultDimensionsConfig = config.resultDimensions;
-
-            var resultDimInfoList = [];
-            var resultDimensions = [];
-            for (var i = 0; i < resultDimensionsConfig.length; i++) {
-                var resultDimInfoConfig = resultDimensionsConfig[i];
-                var resultDimInfo = upstream.getDimensionInfo(resultDimInfoConfig.from);
-                assert(resultDimInfo, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
-                resultDimInfo.method = resultDimInfoConfig.method;
-                resultDimInfoList.push(resultDimInfo);
-                if (resultDimInfoConfig.name != null) {
-                    resultDimInfo.name = resultDimInfoConfig.name;
-                }
-                resultDimensions.push(resultDimInfo.name);
-            }
-
-            var resultData = [];
-
-            var groupBy = config.groupBy;
-            var groupByDimInfo;
-            if (groupBy != null) {
-                var groupMap = {};
-                groupByDimInfo = upstream.getDimensionInfo(groupBy);
-                assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupBy);
-
-                for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
-                    var groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index);
-
-                    if (!groupMap.hasOwnProperty(groupByVal)) {
-                        var newLine = createLine(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal);
-                        resultData.push(newLine);
-                        groupMap[groupByVal] = newLine;
-                    }
-                    else {
-                        var targetLine = groupMap[groupByVal];
-                        aggregateLine(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo);
-                    }
-                }
-            }
-            else {
-                var targetLine = createLine(upstream, 0, resultDimInfoList);
-                resultData.push(targetLine);
-                for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
-                    aggregateLine(upstream, dataIndex, targetLine, resultDimInfoList);
-                }
-            }
-
-            return {
-                dimensions: resultDimensions,
-                data: resultData
-            };
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.myTransform = {}));
+})(this, function (exports) {
+  'use strict';
+
+  var transform = {
+    type: 'myTransform: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
+      };
+    }
+  };
+  var arrayProto = Array.prototype;
+  var nativeForEach = arrayProto.forEach;
+  var nativeSlice = arrayProto.slice;
+  var nativeMap = arrayProto.map;
+
+  var ctorFunction = function () {}.constructor;
+
+  var protoFunction = ctorFunction ? ctorFunction.prototype : null;
+
+  function each(arr, cb, context) {
+    if (!(arr && cb)) {
+      return;
+    }
+
+    if (arr.forEach && arr.forEach === nativeForEach) {
+      arr.forEach(cb, context);
+    } else if (arr.length === +arr.length) {
+      for (var i = 0, len = arr.length; i < len; i++) {
+        cb.call(context, arr[i], i, arr);
+      }
+    } else {
+      for (var key in arr) {
+        if (arr.hasOwnProperty(key)) {
+          cb.call(context, arr[key], key, arr);
         }
+      }
+    }
+  }
+
+  function map(arr, cb, context) {
+    if (!arr) {
+      return [];
+    }
+
+    if (!cb) {
+      return slice(arr);
+    }
+
+    if (arr.map && arr.map === nativeMap) {
+      return arr.map(cb, context);
+    } else {
+      var result = [];
+
+      for (var i = 0, len = arr.length; i < len; i++) {
+        result.push(cb.call(context, arr[i], i, arr));
+      }
+
+      return result;
+    }
+  }
+
+  function bindPolyfill(func, context) {
+    var args = [];
+
+    for (var _i = 2; _i < arguments.length; _i++) {
+      args[_i - 2] = arguments[_i];
+    }
+
+    return function () {
+      return func.apply(context, args.concat(nativeSlice.call(arguments)));
     };
+  }
 
-    function createLine(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal) {
-        var newLine = [];
-        for (var j = 0; j < resultDimInfoList.length; j++) {
-            var resultDimInfo = resultDimInfoList[j];
-            var method = resultDimInfo.method;
-            newLine[j] = (groupByDimInfo && resultDimInfo.index === groupByDimInfo.index)
-                ? groupByVal
-                : (method === 'sum' || method === 'count')
-                ? 0
-                // By default, method: 'first'
-                : upstream.retrieveValue(dataIndex, resultDimInfo.index);
-        }
-        return newLine;
-    }
-
-    function aggregateLine(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo) {
-        for (var j = 0; j < resultDimInfoList.length; j++) {
-            var resultDimInfo = resultDimInfoList[j];
-            var method = resultDimInfo.method;
-            if (!groupByDimInfo || resultDimInfo.index !== groupByDimInfo.index) {
-                if (method === 'sum') {
-                    targetLine[j] += upstream.retrieveValue(dataIndex, resultDimInfo.index);
-                }
-                else if (method === 'count') {
-                    targetLine[j] += 1;
-                }
-            }
+  var bind = protoFunction && isFunction(protoFunction.bind) ? protoFunction.call.bind(protoFunction.bind) : bindPolyfill;
+
+  function isFunction(value) {
+    return typeof value === 'function';
+  }
+
+  function slice(arr) {
+    var args = [];
+
+    for (var _i = 1; _i < arguments.length; _i++) {
+      args[_i - 1] = arguments[_i];
+    }
+
+    return nativeSlice.apply(arr, args);
+  }
+
+  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: 'myTransform: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);
+      }
+
+      each(collectionDimInfoList, function (dimInfo) {
+        dimInfo.__collectionResult = collectionResult;
+        asc(dimInfo.gatheredValuesNoGroup);
+        each(dimInfo.gatheredValuesByGroup, function (values) {
+          asc(values);
+        });
+      });
+      var finalResult = travel(groupByDimInfo, upstream, finalResultDimInfoList, createFinalResultLine, updateFinalResultLine);
+      return {
+        dimensions: map(finalResultDimInfoList, function (item) {
+          return item.name;
+        }),
+        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;
 
-    function assert(cond, msg) {
-        if (!cond) {
-            throw new Error(msg || 'transition player error');
+      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);
+      }
     }
 
-    var myTransform = exports.myTransform = exports.myTransform || {};
-    myTransform.aggregate = transform;
-
-})(this);
-
-
-
-
-
-(function (exports) {
-
-    /**
-     * @usage
-     *
-     * ```js
-     * dataset: [{
-     *     source: [
-     *         ['aa', 'bb', 'cc', 'tag'],
-     *         [12, 0.33, 5200, 'AA'],
-     *         [21, 0.65, 8100, 'AA'],
-     *         ...
-     *     ]
-     * }, {
-     *     transform: {
-     *         type: 'my: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],
-     *     //     ...
-     *     // ]
-     * }]
-     * ```
-     */
-    var transform = {
-
-        type: 'myTransform:id',
-
-        /**
-         * @param params.config.dimensionIndex DimensionIndex
-         *        Mandatory. Specify where to put the new id dimension.
-         * @param params.config.dimensionName DimensionName
-         *        Optional. If not provided, left the dimension name not defined.
-         */
-        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: upstream.data
-            };
+    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;
+    }
+  };
 
-    var myTransform = exports.myTransform = exports.myTransform || {};
-    myTransform.id = transform;
+  function lineCreatorForQ(percent, dimInfo, groupByDimInfo, groupByVal) {
+    var gatheredValues = groupByDimInfo ? dimInfo.gatheredValuesByGroup[groupByVal + ''] : dimInfo.gatheredValuesNoGroup;
+    return quantile(gatheredValues, percent);
+  }
 
-})(this);
+  exports.aggregate = transform$1;
+  exports.id = transform;
+  Object.defineProperty(exports, '__esModule', {
+    value: true
+  });
+});
\ No newline at end of file
diff --git a/public/data/data-transform-aggregate.js b/public/data/data-transform-aggregate.js
new file mode 100644
index 0000000..4a8c467
--- /dev/null
+++ b/public/data/data-transform-aggregate.js
@@ -0,0 +1,80 @@
+/*
+title: Data Transform Simple Aggregate
+category: bar
+titleCN: 简单的数据聚合
+difficulty: 3
+*/
+
+$.when(
+    $.get(ROOT_PATH + '/data/asset/data/life-expectancy-table.json'),
+    $.getScript(ROOT_PATH + '/data/asset/js/myTransform.js')
+).done(function (res) {
+    run(res[0]);
+});
+
+function run(_rawData) {
+
+    echarts.registerTransform(window.myTransform.aggregate);
+
+    option = {
+        dataset: [{
+            id: 'raw',
+            source: _rawData
+        }, {
+            id: 'income_aggregate',
+            fromDatasetId: 'raw',
+            transform: [{
+                type: 'filter',
+                config: {
+                    dimension: 'Year', gte: 1950
+                }
+            }, {
+                type: 'myTransform:aggregate',
+                config: {
+                    resultDimensions: [
+                        { name: 'min', from: 'Income', method: 'min' },
+                        { name: 'Q1', from: 'Income', method: 'Q1' },
+                        { name: 'median', from: 'Income', method: 'median' },
+                        { name: 'Q3', from: 'Income', method: 'Q3' },
+                        { name: 'max', from: 'Income', method: 'max' },
+                        { name: 'Country', from: 'Country' }
+                    ],
+                    groupBy: 'Country'
+                }
+            }, {
+                type: 'sort',
+                config: {
+                    dimension: 'max',
+                    order: 'asc'
+                }
+            }]
+        }],
+        title: {
+            text: 'Income since 1950'
+        },
+        tooltip: {
+            trigger: 'axis'
+        },
+        xAxis: {
+            name: 'Income',
+            nameLocation: 'middle',
+            nameGap: 30
+        },
+        yAxis: {
+            type: 'category'
+        },
+        series: {
+            type: 'boxplot',
+            datasetId: 'income_aggregate',
+            encode: {
+                x: ['min', 'Q1', 'median', 'Q3', 'max'],
+                y: 'Country',
+                itemName: ['Country'],
+                tooltip: ['min', 'Q1', 'median', 'Q3', 'max']
+            }
+        }
+    };
+
+    myChart.setOption(option);
+
+}


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