精华内容
下载资源
问答
  • Style Intelligence™敏捷商业智能软件-多维图表可以在一个图表里,通过图形的颜色,形状,图形的填充方式来多维度的展现信息,让使用者能够在一个图表中得到更多相关信息,从而使数据展现和数据分析更加的科学,...
  • React-dc-js是一个围绕构建的库,用于提供React图表绑定。 我们的目标是提供简单的组件来呈现构建与本地合作无尽的三维制图可能性使用呈现 。 :waving_hand: 注意:用于React的Dc.js尚未完全完成,并且仍在大量...
  • 三维图表内置到工作本身使用呈现 。 在dc.js中,每个图表都通过其元素的位置,大小和颜色显示了一些属性的汇总,并且还提供了可以过滤的维度。 当过滤器或画笔更改时,所有其他图表都将使用动画过渡来动态更新。 ...
  • 然而有了多维动态数据分析,分析图表不再只有一个分析视角,也不再仅仅服务于具体某个人,而是服务于所有浏览者。所有打开这张图表的浏览者,都能够从自身分析视角出发去做更具个性化、更符合自身分析需求的自助式...

    大家都听过“数据可视化”,也知道要用直观的图表让受众理解复杂多变的数据。但不同日期维度不同地域维度的数值对比,往往一张数据图表并不能直观地展示效果,又不想直接用表格呈现一“坨”数据。结果很多人花了大力气做可视化图表,却没达到想要的效果。

    然而有了多维动态数据分析,分析图表不再只有一个分析视角,也不再仅仅服务于具体某个人,而是服务于所有浏览者。所有打开这张图表的浏览者,都能够从自身分析视角出发去做更具个性化、更符合自身分析需求的自助式数据分析。并且一开始的创建者也可以根据浏览者级别不同,给浏览者不同的权限。

    听起来很神奇吧?带你一起看看Smartbi是如何操作的。

    Smartbi操作入口

    1、进入Smartbi Demo“分析展现”界面。

    入口1:在系统主界面的系统快捷菜单选择 分析展现 > 更多分析:

    在这里插入图片描述

    入口2:在系统导航栏中选择 分析展现 :

    在这里插入图片描述

    2、在“分析展现”界面主菜单新建多维分析:

    入口1:在“分析展现”资源目录区的“分析图表”、“公共空间”、“我的空间”或其它文件夹的更多操作中,选择 新建分析 > 多维分析,弹出“选择多维数据源”窗口,确定多维数据集后,进入“新建多维分析”界面。

    入口2:在“分析展现”界面主菜单,选择系统快捷菜单 更多分析 > 多维分析,弹出“选择多维数据源”窗口,确定多维数据集后,进入“新建多维分析”界面。

    在这里插入图片描述

    注:当只有一个多维数据源时,新建多维分析时不会弹出“选择多维数据源”窗口,会直接新建多维分析;只有在有两个及以上的多维数据源时才会弹出“选择多维数据源”窗口。

    界面介绍

    在这里插入图片描述

    “多维分析”界面主要分为以下几个区域:

    资源选择区:该区用于显示定制多维分析允许使用的各类资源:维、度量值、 自定义成员 和 自定义命名集。可以通过双击、拖拽成员或在成员/级别上右键选择函数的方式来添加资源。

    查询面板:该区根据定制多维分析需要划分出三个小区:列区、行区和切块区。用于为在可选资源区选中的资源确定其在多维分析中所处的位置。该面板中橙色的区表示当前选中的区域,在左边资源选择区通过双击,或右键选择函数添加新维度资源时会自动加载到该区域。查询浮动面板各区中的维允许通过右键菜单进行编辑和删除的操作。

    在这里插入图片描述

    工作区:该区主要用于显示已设计好的多维分析的基本内容,主要包含:参数、维度过滤器、告警、已选自定义成员和已选层次结构。

    属性面板区:该区通常与"工作区"结合使用,以实现对定制好的多维分析进行深入设计,主要可以实现:参数属性、维度过滤器设置、告警设置、已选自定义成员优先级设置、已选层次结构管理。

    图表展示区:该区用于展现多维分析,并允许对表格或图形中的数据进行相关的分析操作,如:上钻、下钻、层钻、等。

    Smartbi 制作多维分析,无需编写任何的代码,用户只需要通过语义层的定义就可以轻松搭建自己的多维数据模型。同时具有灵活的分析功能、直观的数据操作和分析结果可视化表示等突出优点,从而使用户对基于大量复杂数据的分析变得轻松而高效,以利于迅速做出正确判断。

    展开全文
  • Android多维报表.zip,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
  • 本文针对多维离散数据和多维连续数据,利用Echarts...由于离散和连续图表展示的效果差别大,故多维图表的可视化,分为两大部分进行设计和实现。总体设计思想:主要是采用类似树形图结合柱状图、线图、散点图、面积图。

    说完了内部扩展和新增接口,接着就是外部的接口了。
    外部的接口主要对原始数据进行一系列的计算、变换、包装,然后放入option各个参数中。
    createChart接口

    /**
       * 通过Echarts的setOption接口建立echarts多维图表
    */
    createChart: function () {
        //optionData由外部接口计算出的配置项数据
        var optionData = this.optionData;
        var option = {
            color:this.colors,
            grid:optionData.gridData,
            tooltip: {
                trigger: 'item'
            },
            //图例默认为垂直放置
            legend: {
                show: true,
                orient: 'vertical',
                data: optionData.legendData,
                right: 0,
                top: 60,
                formatter: function (name) {
                    return echarts.format.truncateText(name, 80, '14px Microsoft Yahei', '…');
                },
                tooltip:{
                    show: true
                }
            },
            calculable: true,
            singleAxis: optionData.singleAxisData,
            xAxis:optionData.xAxisData,
            yAxis: optionData.yAxisData,
            series: optionData.seriesData
        };
        if(optionData.formatter){
            option.tooltip.formatter = optionData.formatter;
        }               
        this.chart.setOption(option);           
    },

    从上面代码中可以看到,多维图表的构造和普通图表并没有本质上的区别,只是传入的参数不同而已。
    不知大家有木有发现,在上面代码中optionData是一个关键变量,各种赋值都跟它密切相关。

    getChartOption接口: 而 optionData是从何处来的呢?在我的设计中getChartOption函数经过一系列的处理和计算,最终得到optionData所需的数据。getChartOption函数代码较多,其实还可以继续划分为几个小函数,这样代码看起来更优雅一些。

    /**
     * 获取图表的option配置项 
    */
    getChartOption: function () {
        var that = this;
        var state = that.properties.state;
        var measure = that.properties.measure;
        var dimension = that.properties.dimension;
        var dLen = dimension.length;
        var mLen = measure.length;
        var visualMLen = mLen === 0 ? 1 : mLen;
        var chart = this.chart;
        var color = this.colors;
        var finalData = that.finalData;
        // 求出图例的数据
        var legendData = mLen > 0 && measure.map(function(item) {
                return item.alias;
            });
        // 获取维度中连续的字段
        var continuousKey = that.getContinueKey(dimension, mLen);
        // 该展示连续和离散图表
        var isContinue = continuousKey.length > 0;
        // 将原始数据进行标准化,得到按维度分类的数据
        var categoryData = that.standarHigherData(finalData, measure, dimension);
        // 计算出各级维度label显示需要的各种属性信息,如:label坐标值、所占区域宽度、分隔线坐标值
        var percentData = that.calLabelProperty(categoryData.chartLabelData, continuousKey.length > 0);
        var chartData = categoryData.chartData;
        var peakValue = categoryData.peakValue;
        //图表到左边的距离
        var LEFT_DISTANCE = mLen === 0 ? 10 : 60;
        var sum = categoryData.chartLabelData[0] && categoryData.chartLabelData[0].sum;
        var showClassNum = categoryData.chartLabelData.length;
        //图表grid到图表最上面的距离
        var TOP_DISTANCE = 28;
        //图表grid的x轴label所占的高度
        var AXIS_LABEL_HEIGHT = 50;
        //图表图形显示区域到图表最右边的距离
        var RIGHT_DISTANCE = 100;
        //图表图形实际显示宽度
        var realChartWidth = this.getWidth() - LEFT_DISTANCE - RIGHT_DISTANCE;
        //图表图形实际显示高度
        var realChartHeight = this.getHeight() - (showClassNum == 0 ? 14 : TOP_DISTANCE * showClassNum) - (visualMLen - 1 ) * 10 - AXIS_LABEL_HEIGHT;
        //图表每一个grid所占的宽度
        var perChartWidth = realChartWidth / (isContinue ? chartData.length : sum);
        var xAxisData = [], yAxisData = [], gridData = [], seriesData = [], singleAxisData = [];
        var xAxisObj, gridObj, yAxisObj, seriesObj, singleAxisObj;
        var perGridHeight = realChartHeight / visualMLen, gridWidth = 0;
        var gridIndex = 0, left = LEFT_DISTANCE;
        /*
         * 为了避免label数量众多,而图表所占宽度不够时导致地标签重叠的情况发生,
         * 故需要对chartData和percentData的数据,结合realChartWidth和continuousKey进行二次计算
         * 以便图表得到最佳的展示结果,此时会人为剔除一些label标签,不会展示所有的label
        */
        var category = that.preprocessCategory(percentData, realChartWidth, continuousKey, chartData);
        var categoryIndex = category.categoryIndex;
        var disContinuousKey = that.getDisGroupItem(dLen, continuousKey);
        // 处理悬浮框的显示格式
        var formatter = that.curry(that.processTooltipFormatter, dimension, measure, disContinuousKey);     
        //分别对每一个度量进行遍历
        for (var j = 0; j < visualMLen; j++) {
            left = LEFT_DISTANCE;
            //对chartData图表数据进行遍历
            for (var i = 0, dataItem ; i < chartData.length; i++) {
                 dataItem = chartData[i];
                 //求出每一个分组的宽度大小
                 gridWidth = isContinue ? perChartWidth : dataItem.count * perChartWidth;
                 if (i >= 1) {
                     if (isContinue) {
                         left += perChartWidth;
                     } else {
                         left += chartData[i - 1].count * perChartWidth;
                     }
                 }
                 //grid对象的配置信息
                 gridObj = {
                     width: gridWidth,
                     height: perGridHeight,
                     left: left,
                     top: (showClassNum  == 0 ? 14 : TOP_DISTANCE * showClassNum) + (j * perGridHeight + ((j > 0) &&  j * 10))
                 };
                 //x轴对象的配置信息
                 xAxisObj = that.getXAxisOption({
                     type: dataItem.type,
                     mLen: mLen,
                     measureIndex: j,
                     axisPeak: dataItem.xAxisPeak,
                     realTimeFormat: dataItem.realTimeFormat,
                     timeFormat: dataItem.timeFormat,
                     axisLabel: dataItem.axisLabel,
                     categoryIndex: categoryIndex,
                     gridIndex: gridIndex,
                     gridWidth: gridWidth,
                     continueIndex: dataItem.continueIndex,                 
                     minInterval: dataItem.minInterval,
                     labelData: dataItem.axisLabelData,
                     index: i,
                     dimension:dimension
                 });
                 //y轴对象的配置信息
                 yAxisObj = that.getYAxisOption({
                     index: i,
                     mLen: mLen,
                     context:  that,
                     measure: measure,                  
                     peakValue: peakValue,
                     gridIndex: gridIndex,
                     measureIndex: j
                 });
                 //series对象的配置信息
                 seriesObj = that.getSeriesOption({
                     gridIndex: gridIndex,
                     measureIndex: j,
                     context:  that,
                     measure: measure,
                     xAxisType: dataItem.type,
                     xAxisKeyName: dataItem.xAxisKeyName,
                     xAxisKey: dataItem.xAxisKey,
                     state: state,                      
                     realTimeFormat: dataItem.realTimeFormat,
                     dateFormat: dataItem.dateFormat,
                     perChartWidth: perChartWidth,
                     barWidth: '20%',
                     color: color,
                     data: dataItem.data[j]
                 });
                 xAxisData.push(xAxisObj);
                 yAxisData.push(yAxisObj);
                 gridData.push(gridObj);
                 seriesData.push(seriesObj);
                 gridIndex++;
            }
        }
        var labelFormatWidth = [];
        //处理非坐标轴上的label的配置信息
        for (i = 0; i < percentData.length; i++) {
            labelFormatWidth[i] = realChartWidth / percentData[i].label.length - 10;
            labelFormatWidth[i] = labelFormatWidth[i] <= 26 ? 26 : labelFormatWidth[i];
            //获取singleAxisObj轴对象的配置信息
            singleAxisObj = that.getSingleAxisOption({
                index: i,
                mLen: mLen,
                realChartWidth: realChartWidth,
                realChartHeight: realChartHeight,
                distance: {
                    topDistance: TOP_DISTANCE,
                    rightDistance: RIGHT_DISTANCE,
                    axisLabelHeight: AXIS_LABEL_HEIGHT,
                    leftDistance: mLen === 0 ? 10 : LEFT_DISTANCE
                },
                labelFormatWidth: labelFormatWidth,
                percentData: percentData,
                labelData: category.category
            });
            singleAxisData.push(singleAxisObj);
            seriesObj = that.getSingSeriesOption({index: i});
            seriesData.push(seriesObj);
        }
        that.optionData = {
            xAxisData: xAxisData,
            yAxisData: yAxisData,
            gridData: gridData,
            formatter: formatter,
            legendData: legendData,
            singleAxisData: singleAxisData,
            seriesData: seriesData
        };
    },  

    getChartOption函数作为数据处理的总函数,是整个多维数据图表外部接口的核心,对于其中一些重要的子函数,本文会一一进行讲解。首先来看standarHigherData接口:
    standarHigherData接口:该函数主要将原始的多维数据进行分解、变换、包装。如图1所示,为多维原始数据。如图2所示,为处理后的数据。
    这里写图片描述

    图1 多维原始数据

    这里写图片描述
    图2 经过standarHigherData接口处理后的数据

    从上面两图中,可以看出处理后的数据和原始数据之间差异性还是很大的,做了很多变换、分解、包装。
    该接口的具体源码如下所示:

    /**
     * 用于规整高维数据,处理连续和离散情况下数据的内容和格式
     * @param {Array.<Array>} data: 需要分组的源数据
     * @param {Array.<Object>} measure: 包含度量信息的数组
     * @param {Array.<Object>} dimension: 包含维度信息的数组
    */
    standarHigherData: function (data, measure, dimension) {
        var that = this, axisIndex;
        var continuousIndexs = [], groupedCategoryData = {};
        var dLen = dimension.length;
        var mLen = measure.length;
        var yAxisPeak = [], chartData = [], initChartData ;
        //获取连续的序号
        continuousIndexs = this.getContinueKey(dimension, mLen);
        //是否有连续型数据
        var isContinue = continuousIndexs.length > 0;
        // 当存在两个和以上的维度连续的情况下,需要对原始数据进行分组合计
        var groupData = this.groupByData(data, dLen, mLen, continuousIndexs);
        //只有一个维度连续或者没有维度连续的情况
        if (continuousIndexs.length <= 1) {
            groupedCategoryData[dLen - 1] = {};
            groupedCategoryData[dLen - 1].data = that.getDimensionCategory(groupData, dLen, mLen, isContinue);
            (continuousIndexs.length && mLen) && (groupedCategoryData[dLen - 1].xAxisPeak = getDimensionPeak(groupedCategoryData[dLen - 1].data.chartData, dimension, dLen - 1, that));
        //多个维度连续
        } else {
            for (var i = 0; i < continuousIndexs.length; i++) {
                var continuousItem = continuousIndexs[i];
                groupedCategoryData[continuousItem] = {};
                groupedCategoryData[continuousItem].data = that.getDimensionCategory(groupData[continuousItem], dLen - continuousIndexs.length + 1, mLen, isContinue);
                groupedCategoryData[continuousItem].xAxisPeak = getDimensionPeak(groupedCategoryData[continuousItem].data.chartData, dimension, continuousItem, that);
                var curYAxisPeakData = groupedCategoryData[continuousItem].data.peakValue;
                if (i == 0) {
                    yAxisPeak = curYAxisPeakData;
                } else {
                    for (var j = 0; j < mLen; j++) {
                        if (curYAxisPeakData[j].min < yAxisPeak[j].min) {
                            yAxisPeak[j].min = curYAxisPeakData[j].min;
                        }
                        if (curYAxisPeakData[j].max > yAxisPeak[j].max) {
                            yAxisPeak[j].max = curYAxisPeakData[j].max;
                        }
                    }
                }
            }
        }
        //一个连续维度或者无连续维度
        if (continuousIndexs.length <= 1) {
            initChartData = groupedCategoryData[dLen - 1].data.chartData;
            for (var i = 0; i < initChartData.length; i++) {
                addAxisDataItem(groupedCategoryData, dimension, chartData, dLen - 1, i, that);
            }
            groupedCategoryData[dLen - 1].data.chartData = chartData;
            axisIndex = dLen - 1;
        //多个维度连续
        } else {
            initChartData = groupedCategoryData[continuousIndexs[0]].data.chartData;
            for (var i = 0, index; i < initChartData.length; i++) {
                for (var j = 0; j < continuousIndexs.length; j++) {
                    index = continuousIndexs[j];
                    addAxisDataItem(groupedCategoryData, dimension, chartData, index, i, that);
                }
            }
            groupedCategoryData[continuousIndexs[0]].data.chartData = chartData;
            groupedCategoryData[continuousIndexs[0]].data.peakValue = yAxisPeak;
            axisIndex = continuousIndexs[0];
        }
        return groupedCategoryData[axisIndex].data;
        /**
         * 每个数据项添加轴数据
         * @param groupedCategoryData
         * @param dimension
         * @param chartData
         */
        function addAxisDataItem(groupedCategoryData, dimension, chartData, index, dataIndex, context) {
            var gridData = {};
            var xAxisData = {};
            var key = dimension[index];
            var mLen = context.properties.measure.length;
            context.updateAxis('xAxis', xAxisData, key, null, 50);
            gridData = groupedCategoryData[index].data.chartData[dataIndex];
            echarts.util.merge(gridData, xAxisData);
            // ?! 量词对其后没有紧接着":"的"mm"字符串进行搜索
            gridData.realTimeFormat = gridData.realTimeFormat && gridData.realTimeFormat.replace(/m{2}(?!:)/g, 'MM');
            mLen == 0 && (gridData.type = 'category');
            gridData.xAxisKeyName = key.alias;
            gridData.xAxisKey = key.key;
            gridData.continueIndex = index;
            gridData.xAxisPeak = groupedCategoryData[index].xAxisPeak || null;
            chartData.push(gridData);
        }
        /**
           * 当轴连续的情况下,求出维度的最大值和最小值
           * @param {Array} chartData:图表x轴维度数据
           * @return {Object} 包含各个维度最大值和最小值的数组
        */
        function getDimensionPeak(chartData, dimension, index, context) {
            var totalData = [];
            var xAxisData = {};
            var key = dimension[index];
            context.updateAxis('xAxis', xAxisData, key);
            dateFormat = key.timeFormat || key.colType;
            if (xAxisData.type == 'category') {
                return;
            } else if (xAxisData.type == 'time') {
                for (var i = 0; i < chartData.length; i++) {
                    totalData = totalData.concat(chartData[i].axisLabelData.map(function (item) {
                        item = (item === null || item == '' || item == 'null') ? '1970-01-01 00:00:00' : context.processTimeItem(dateFormat, item);
                        return new Date(item).getTime();
                    }));
                }
            } else {
                for (var i = 0; i < chartData.length; i++) {
                    totalData = totalData.concat(chartData[i].axisLabelData.map(function(item) {
                        return ((item === null || item == '' || item == 'null') ? 0 : item);
                    }));
                }
            }
            var min = Math.min.apply(Array, totalData);
            var max = Math.max.apply(Array, totalData);
            return {
                min: min,
                max: max
            };
        }
    },

    在standarHigherData接口,也有几个重要的子函数,比如groupByData,主要是当连续的维度在两个和以上的时候,对数据进行分组合计,在这个过程有新的数据生成。
    groupByData接口源码如下:

    /**
      将数据进行分组,以'\&%#@'分割数据
        @param {Array} data: 需要分组的源数据
        @param {Number} dLen: 维度的个数
        @param {Number} mLen: 度量的个数
        @param {Array} groupDimenIndex: 需要分组的维度序号:[0, 1, 2]
        @return {Array} groupData: 分组后的数据
    */
    groupByData: function(data, dLen, mLen, groupDimenIndex) {
        var inGroupIndex = getDisGroupItem(dLen, groupDimenIndex);
        var SPLIT_STR = '\&%#@';
        var key;
        var map = {};
        var dataItem;
        var groupDataMap = {};
    
        if (groupDimenIndex.length < 2) {
            return data;
        }
        for (var i = 0; i < groupDimenIndex.length; i++) {
            for (var j = 0 ; j < data.length; j++) {
                dataItem = data[j];
                key = getItemKey(dataItem, dLen, groupDimenIndex[i], inGroupIndex, SPLIT_STR);
                if (map[key]) {
                    map[key] = sumMeasureItem(map[key], dataItem.slice(dLen, dLen + mLen));
                } else {
                    map[key] = dataItem.slice(dLen, dLen + mLen);
                }
            }
            groupDataMap[groupDimenIndex[i]] = map;
            map = {};
        }
        return setMapToArray(groupDataMap);
        /**
           map的数据转为数据
           @param {Object} map
           @return 返回处理后的map
        */
        function setMapToArray(map) {
            var dimension = [];
            for (var prop in map) {
                if (map.hasOwnProperty(prop)) {
                    for (var name in map[prop]) {
                        if (map[prop].hasOwnProperty(name)) {
                            dimension.push(name.split(SPLIT_STR).concat(map[prop][name]));
                        }
                    }
                    map[prop] = dimension;
                    dimension = [];
                }
            }
            return map;
        }
        /**
         用于累加各个度量值
         @param {Array} curSum: 当前累加值
         @param {Array} addMeasure: 需要累加的度量值
            @return {Array} sum: 累加后的值
        */
        function sumMeasureItem(curSum, addMeasure) {
           var sum = [];
           for (var i = 0; i < curSum.length; i++) {
               sum.push(Number(curSum[i]) + Number(addMeasure[i]));
           }
           return sum;
        }
        /**
             获取分组的key值
             @param {Array} item:分组的单条数据
             @param {Number} groupDimenValue:分组字段的序号
             @param {Array} inGroupIndex:无需分组的序号
             @return {String} key: 分组的key值
        */
        function getItemKey(item, dLen, groupDimenValue, inGroupIndex, SPLIT_STR) {
            var key ;
            var keyIndex = inGroupIndex.concat([groupDimenValue]);
            for (var i = 0; i < dLen; i++) {
                if (keyIndex.indexOf(i) > -1) {
                   key = key == null ? item[i] : (key + SPLIT_STR + item[i]);
                }
            }
            return key;
        }
        /**
           获取无需分组的字段序号
           @param {Number} dLen: 维度的个数
           @param {Array} groupDimenIndex: 需要分组的维度序号:[0, 1, 2]
           @return {Array} disGroupIndex: 无需分组的序号
        */
        function getDisGroupItem(dLen, groupDimenIndex) {
            var disGroupIndex = [];
            for (var i = 0; i < dLen; i++) {
                if (groupDimenIndex.indexOf(i) == -1) {
                    disGroupIndex.push(i);
                }
            }
            return disGroupIndex;
        }
    },

    getDimensionPeak子函数,主要是当图表处于连续的情况下,求出各个连续维度的维度值的最大值和最小值,这样在各个分组中,才能对比连续性。

    接着我们再来看preprocessCategory接口,
    preprocessCategory接口:该接口主要对standarHigherData和calLabelProperty处理后的数据进行加工,筛选出在图表上不会互相重叠的标签label,最终经过这样筛选出的数据,才能显示在图表上。

    /**
       * 根据图表宽度筛选各个维度上可以容纳的标签和标签显示所需要的属性信息
       * @param {Array} data: 标签数据
       * @param {Number} realChartWidth: 图表宽度
       * @param {Array} continuousKey:连续维度信息数组
       * @return {Object} 包含各个维度要显示的分组信息和坐标轴上显示的label内容
    */
    preprocessCategory: function(data, realChartWidth, continuousKey, chartData) {
        var percentData = echarts.util.clone(data);
        var category = [];
        var width = 0;
        var percent, label, categoryItem, area;
        var lastLabelIndex;
        var interval = 1, classNum = percentData.length;
        var categoryIndex = [];
        var index = classNum - 1;
        var length = continuousKey.length;
        for(var i = 0; i < percentData.length; i++) {
            category[i] = {};
            percent = category[i].percent = [];
            label = category[i].label = [];
            area = category[i].area = [];
            categoryItem = percentData[i];
            for(var j = 0; j < categoryItem.percent.length; j++) {
                //前后两个标签是否重叠
                if (j == 0 || calLabelIndex(realChartWidth, categoryItem.percent, j, lastLabelIndex)) {
                    percent.push(categoryItem.percent[j]);
                    label.push(categoryItem.label[j]);
                    area.push(categoryItem.area[j]);
                    lastLabelIndex = j;
                    //与坐标轴分类一致的类别才放入数组
                    if (i == index) {
                       categoryIndex.push(j);
                    }
                }
            }
        }
        //如果坐标轴上连续的维度key个数大于1
        if (length >= 2) {
            categoryIndex = calContinueAxisLabelIndex(chartData, length, realChartWidth);
        }
        //category是最终筛选出的分类数据,categoryIndex则是最靠近坐标轴的维度数据序号值
        return {category: category, categoryIndex: categoryIndex};
        /**
           计算前后两个label是否重叠
        */
        function calLabelIndex(realChartWidth, data, j, lastLabelIndex) {
            if (Math.round(realChartWidth * (data[j] - data[lastLabelIndex])) >= 26) {
              return true;
            }
            return false;
        }
        /**
         * 计算1个以上连续维度时,坐标轴的label显示情况
         * @param {Array} data :与坐标轴上最接近的维度标签数据
         * @param {Number} length: 连续维度的个数
             */
        function calContinueAxisLabelIndex(data, length, realChartWidth) {
            var categoryIndex = [];
            var chartBit = [];
                  //计算出每一个坐标系的比率
            for (var i = 0, len = data.length; i < len; i++) {
                chartBit[i] = (i + 1) / len;
            }
            for(var j = 0; j < data.length; j++) {
                //前后两个标签是否重叠
                if (j == 0 || calLabelIndex(realChartWidth, chartBit, j, lastLabelIndex)) {
                    lastLabelIndex = j;
                    categoryIndex.push(j);
                }
            }
            return categoryIndex;
        }
    },

    processTooltipFormatter接口:该接口主要设置悬浮框的显示格式和内容,代码如下所示

            /**
               * 处理悬浮框显示的格式
               * @param {Array.<Object>} dimension: 包含维度信息的数组
               * @param {Array.<Object>} measure: 包含度量信息的数组
               * @param {Array} disContinuousKey:离散字段数组
               * @param {Object} params: echarts内部参数
            */
            processTooltipFormatter: function(dimension, measure, disContinuousKey, params) {
                var data = params.data;
                var len = data.length;
                var color = params.color;
                var formatter = '';
                var continueLen = dimension.length - disContinuousKey.length;
                var filterDimension = [];
                var timeFormat = params.timeFormat;
                var aliasIndex = 0;
                if (continueLen == 0) {
                    //无连续字段
                    filterDimension = dimension.filter(function (item, index) {
                        return index != (dimension.length -1)
                    });
                } else {
                    //有连续字段,并从dimension中删除,并得到新数组
                    filterDimension = dimension.slice(0, disContinuousKey.length);
                }
                while (len - 2) {
                    formatter += getFormatter(color, filterDimension[aliasIndex].alias, data[len - 1]);
                    len -- ;
                    aliasIndex ++ ;
                }
                formatter += getFormatter(color, params.xAxisKeyName, timeFormat ? new Date(data[0]).Format(timeFormat) : data[0]);
                measure.length > 0 && (formatter += getFormatter(color, measure[params.measureIndex].alias, data[1]));            
                return formatter;
                /**
                   获取数据的html字符串
                */
                function getFormatter(color, name, value) {
                     return  ('<span style="display:inline-block;margin-right:5px;border-radius:10px;width:9px;height:9px;background-color:'
                               + color + '"></span>'+ name +':' + value + '</br>');
                }
            },

    以上就是实现过程中的主要接口的介绍和阐释,还有一些小的功能和接口,由于比较简单,所以就没有介绍。还有本文将连续性图表和离散性图表分别进行了设计,但是在图表的实现过程中,由于两者有太多的相同处理步骤和方法,只是在一些细微处有一些差别。故为了提高代码的可复用性,在实现过程中,多维离散和连续图表的实现是放在一起的。

    本人已将整个实现源码进行了封装,只要按文中的要求传入data、dimension、measure三个参数即可构造出图表。

    demo源码下载地址:http://download.csdn.net/download/mulumeng981/9985030 (基于Echarts最新版3.7.1)

    如果你注意到一个不准确或似乎不太正确的地方,请让我知道。谢谢!

    展开全文
  • motionchart地址: https://github.com/larrylgq/motionchart/wiki   as3-xmpp协议栈 https://github.com/larrylgq/as3-xmpp
    展开全文
  • 本文针对多维离散数据和多维连续数据,利用Echarts...由于离散和连续图表展示的效果差别大,故多维图表的可视化,分为两大部分进行设计和实现。总体设计思想:主要是采用类似树形图结合柱状图、线图、散点图、面积图。

    前言:本文针对多维离散数据和多维连续数据,分别构建多维可视化图表。由于离散和连续图表展示的效果差别大,故多维图表的可视化,分为两大部分进行设计和实现。

    总体设计思想:主要是采用类似树形图结合柱状图、线图、散点图、面积图。树形图主要展示的是维度的值,和各个维度值之间的关系。如图(1)所示。第一条数据:[“企业”, “个人政府征订”,”奉化站”,”2013-07”, 6500],在图表中与这条数据相关的内容,除了第一根蓝色方形,还包括红线相连的名称(企业,个人政府征订,奉化站)。如果把图中所有的名称相互连接起来,像图中红线一样,是不是感觉像一棵树?特别要注意的是(企业-个人政府征订-奉化站) 可以看成一个分组,在这个分组里可能有不止一个值,它分布着多条数据。

    这里写图片描述

    图1:多维数据展示

    总体实现方式:要实现如图1所示的效果,可以采用Echarts插件的各种组件经过一系列的计算和整合,当然其中肯定涉及插件若干接口的改造和新增。最终生成的图表才能展示出我们需要的效果。

    多维离散型数据的设计思路

    多维离散型数据的定义:所有的维度都是离散的,不存在任何连续型维度。对于多维离散型数据,由于在每一条数据相互之间在轴上并没直接关系,关系同属于一个分组。对于同一个度量,各个分组展示出来的图表类型应该是一致的。(特殊情况,对于三个维度的数据,类目气泡图也是可以展示的)

    基于Echarts插件的多维离散型数据可视化实现
    一、问题阐释
    1. 基于Echarts插件,要实现多维数据可视化,是否有官方接口,如果有官方接口不就轻松搞定?
    通过查看Echarts的文档,发现确实有一个图表类型,可以展示多维数据,如图2所示,但是这种展示方式,貌似并不能适合所有多维数据,而且可视化的效果有点差强人意。
    这里写图片描述

    图2 Echarts的多维数据展示

    2. 如果要实现我们设计的树形加普通图表的展示方式,如何实现树形?如何进行分组?每一个分组高和宽占用整个图表高和宽多少?每一个分组由于是同一个度量,是不是应该共享同一个度量轴?
    对于这些问题,暂时不做回答。接下来,我会手把手教大家如何解决这些问题,最终实现整个多维图表。

    二 多维数据的结构
    要实现整个设计,一共需要三个数据结果,我把它们取名:data,measure, dimension。具体的数据例子,如下所示,data:是一个二级数组[[d1,..,dn,m1,..,mn],…,[d1,..,dn,m1,..,mn]] (dn:表示第n个维度的信息,而mn表示n个度量的信息),它存储着图表要显示的多维数据详情,每一条数据时一个数组,数组的前几位是维度的值,后几位是度量的值。measure:是一个数组,它存储着度量字段的相关信息,数组内每条数据时一个js对象,对象的key属性,是度量的关键字,用于区分其他度量;alias属性时度量的别名,用于显示度量的中文名称;state属性表示度量所对应的图表类型。dimension :也是一个数组,构造跟measure差不多,公共的属性表示的含义和measure是一样的,continuity字段表示的是维度的连续性,’11’表示是连续的。colType:表示维度的数据类型。如果是离散的,维度的数据类型一般都是字符串型的。

    var data = [
        [
            "企业",
            "余姚站",
            "2013-05",
            0.015
        ],  
        ...
        [
            "普通客户",
            "仓基站",
            "2013-01",
            0.019
        ]
    ];
    var dimension = [
        {
            "key":"cust_type",       
            "alias":"cust_type",
            "continuity":"00",
            "colType":"string"
        },
        ....
        {
            "key":"Month",     
            "alias":"Month", 
            "continuity":"11",
            "colType":"date"
        }
    ];
    var measure =      
    [
        {
            "key":"huan_bi",     
            "alias":"总和_huan_bi",            
            "state":"bar"
        },
        ...
         {
            "key":"tong_bi",
            "alias":"总和_tong_bi",                  
            "state":"bar"
        }
    ];
    

    三、Echarts接口的扩展和新增

    为了解决(1)提出的问题,在Echarts的接口一共需要扩展一个接口,新增2个接口。
    扩展的接口主要是
    Axis.prototype.dataToCoord;该接口主要负责计算名称或者叫标签的坐标值;在Echarts中标签值叫label,而字符串的label一般用category离散的轴表示,每一个label之间的距离是等距的。这个与我们要求是有出入的,因为在多维图表中,每一个label之间并不一定是等距的,所以我们要定距给其一个值,使之满足我们的要求。而这个值的计算是在Echarts插件外部进行的,由函数calLabelProperty完成。

    calLabelProperty函数的功能,主要是计算跟label定位和显示相关的三个属性值,line:表示两个label之间的分割线;area表示每条label信息在图表中所占的宽度;curBit表示每条label在图表上的位置。代码中normalizeData变量保存着每一个label在一条轴上的比率值,介于0和1之间。

     dataToCoord: function (data, clamp) {
                    var extent = this._extent;
                    var scale = this.scale;
                    var normalizeData = this.model.get('normalizeData');
                    //沈才良 @face
                    if (normalizeData) {
                        data = normalizeData[data];
                    } else {
                        data = scale.normalize(data);
                    }
    
                    if (this.onBand && scale.type === 'ordinal') {
                        extent = extent.slice();
                        fixExtentWithBands(extent, scale.count());
                    }
    
                    return linearMap(data, normalizedExtent, extent, clamp);
                },
    calLabelProperty: function(chartLabelData, isContinue) {
        var categoryLabel = [];
        var sum = 0, labelArray, percentArray, areaArray;
        var curSum = 0, curBit = 0;
        var curBitArray = [];
        var classNum = chartLabelData.length;
    
        for (var i = 0; i < classNum; i++) {                
            //如果图表是离散的,则是label的总和
            sum = isContinue ? chartLabelData[classNum - 1].labelData.length : chartLabelData[i].sum;
            //当前的统计和
            curSum = 0;
            categoryLabel[i] = {};
            /*
               labelArray: 保存每一个维度上的label
               lineArray: 保存各个label之间的分隔线的位置
               curBitArray: 保存各个label的位置
               areaArray: 保存各个label在图表上所占的区域宽
            */
            labelArray = categoryLabel[i].label = [];
            lineArray = categoryLabel[i].line = [];
            curBitArray = categoryLabel[i].percent = [];
            areaArray = categoryLabel[i].area = [];
            //求出每一个类别子项的显示位置的值
            for(var j = 0, dataItem; j < chartLabelData[i].labelData.length; j++) {
                dataItem = chartLabelData[i].labelData[j];  
                //连续的时候,离散的最后一个维度计算curSum等于 j + 1               
                if (isContinue && i == (classNum - 1)) {
                    curSum = j + 1;
                } else {
                    curSum += dataItem.count;
                }   
                 //删除label里面的换行符号
                labelArray.push((dataItem.labelName + '').replace(/\r\n/g, ' '));
                lineArray.push(curSum / sum);
            }
            //每一个维度的第一个分组信息计算,跟其他分组不一样
            curBitArray[0] = lineArray[0] / 2;
            areaArray[0] = lineArray[0];
            for ( j = 1; j < lineArray.length; j++) {
                areaArray.push(lineArray[j] - lineArray[j-1]);
                curBitArray.push(lineArray[j - 1] + (lineArray[j] - lineArray[j - 1]) / 2);
            }
        }
        return categoryLabel;
    },

    新增的接口主要是:label之间的分割线、悬浮label所占区域时候区域背景色改变的变化接口。
    新增接口的定义在AxisBuilder构造函数所属区域中builders.axisLabel函数中。

       //添加标签的hover事件
        buildHoverRect({
            group: this.group, 
            pos: pos, 
            textEl: textEl, 
            axisModel: axisModel, 
            contentBit: areaData[index]
        });
        /**
          * 当用户悬浮在label上面
          * 则显示整个label分组所占的空间
          * 由于zrender默认的hide和show函数
          * 当数据超过100多时,性能差,故采用
          * 设置opacity达到显示和隐藏的效果
        */
        textEl.on('mouseover', function (event) {
            this.hoverRect.setStyle('opacity', 0.24);
        });
        /**
           * opacity = 0表示隐藏该label有颜色背景
           * textEl:表示每一个label在图表上的实体
        */
        textEl.on('mouseout', function (event) {
            this.hoverRect.setStyle('opacity', 0);
        });
        /**
            * 生成hover时候出现的矩形和背景
            * @param {Object} params: userful params to build hover rect
            * @param {Number} contentBit: 每一个label所在的区域长度,[0,1]之间
            * @param {Array.<Number>} pos: label的坐标值,二维的
        */
        function buildHoverRect(params) {   
            var axisModel = params.axisModel;
            var coordinateRect = axisModel.coordinateSystem && axisModel.coordinateSystem._rect;
            var extent = axisModel.axis.getExtent();     
            var axisWidth = extent[1] - extent[0];  
            //得到显示区域的宽度大小       
            var contentWidth = Math.ceil(axisWidth * params.contentBit);
            var rect = new graphic.Rect({
                shape: {
                    x: params.pos[0] + coordinateRect.x - contentWidth / 2,
                    y: params.pos[1] + coordinateRect.y - 7, 
                    height: 28,
                    width: contentWidth
                },
                z: 2,
                style: {
                     fill:'green',
                     opacity: 0,
                     lineWidth: 1
                },
                silent: false
            });
            //添加到group中,以便echarts统一绘制
            params.group.add(rect);
            //方便隐藏和显示
            params.textEl.hoverRect = rect;
        }

    第二个重要的接口新增是buildSplitLine函数,主要负责构建label之间的分隔线。具体代码和注释如下所示:

    /**
      * 生成label之间的分割线,方便用户识别不同的label分组
       * {Array} group:分组组件,生成的line只有放入这个组件中,才能统一绘制
       * {Object} axisModel: 坐标轴的model模型,主要保存与坐标轴相关的信息
    */
    function buildSplitLine(group, axisModel) {
        var axisType = axisModel.mainType;
        var show = axisModel.get('classSplitLine.show');
        if (axisType != "singleAxis" || show !== true) {
            return;
        }
        //获取分割线的所需的数据,由外部接口calLabelProperty计算出
        var data = axisModel.get('classSplitLine.data');
        var coordinateRect = axisModel.coordinateSystem && axisModel.coordinateSystem._rect;
        var extent = axisModel.axis.getExtent();     
        var axisWidth = extent[1] - extent[0];  
        var point1 = [];
        var point2 = [];
        for (var i = 0 ; i < data.length - 1; i++) {
            //求出分隔线两个端点的坐标值
            point1[0] = data[i] * axisWidth + coordinateRect.x;
            point1[1] = coordinateRect.y;
            point2[0] = data[i] * axisWidth + coordinateRect.x;
            point2[1] = coordinateRect.y + 26;  
            var line = new graphic.Line(graphic.subPixelOptimizeLine({
                shape: {
                    x1: point1[0],
                    y1: point1[1],
                    x2: point2[0],
                    y2: point2[1]
                },
                style: {
                     stroke: "#d3d3d3",
                     color: '#666',
                    // lineDash: [4, 4],
                     opacity: 0.5,
                     lineWidth: 1
                },
                z2: 3,
                silent: true
            }));
            group.add(line);
        }
    }

    这几个接口具体定义的位置,请参考我详细的源码。
    源码下载地址:http://download.csdn.net/download/mulumeng981/9985030 (基于Echarts最新版3.7.1)

    如果你注意到一个不准确或似乎不太正确的地方,请让我知道。谢谢!

    展开全文
  • 本文针对多维离散数据和多维连续数据,利用Echarts...由于离散和连续图表展示的效果差别大,故多维图表的可视化,分为两大部分进行设计和实现。总体设计思想:主要是采用类似树形图结合柱状图、线图、散点图、面积图。
  • 处理三维数据做图表,比如返回的数据就是一个个list,list里面某几个数据同属于一个维度,项目中的实例效果如下:  上面的khfx会有多个,比如db1、db2、db3等,下面的那些数据也会变化,目前需求就是做的下面的...
  • 运用BIRT2.3新功能:图表展示多维数据

    千次阅读 2008-06-12 17:53:00
    BIRT在2.2的版本中提供了交叉表(cross tab)的支持,它可以用来显示多维数据,使得用户可以从各个角度,比如时间,产品类型,来分析数据. 请看得到的交叉表结果数据反应了各个时间段的销售业绩,但繁杂的数据却往往让人必须...
  • 多维数据库

    2016-12-28 15:31:19
    多维数据库(Multi Dimensional Database,MDD)可以简单地理解为:将数据存放在一个n维数组中,而不是像关系数据库那样以记录的形式存放。因此它存在大量稀疏矩阵,人们可以通过多维视图来观察数据。多维数据库增加...
  • 数据可视化本身就是一种数据分析方法,把数据用可视化的方法展示出来,本身就说明了一个故事,表达了一个观点,形成一种定性的判断。...07雷达图、扇形图——多维图表,描述事物多个维度的特征 08...
  • 图表

    2013-03-07 10:36:00
    在很多项目中都会有在前端展现数据图表的需求,而在开发过程中,开发者往往会使用一些JavaScript库,从而更有效地达到想要的目标。最近,TechSlide上的一篇文章总结了50种用于展现图表的JavaScript库,并对每种库做...
  • qwquiver A website for exploring and analyzing exam results. Features 多平台支持 (Win, macOS, Linux) Material Design 风格 多条件查询 支持正则表达式 数据模糊查询 根据学校班级查询 单科成绩 ...
  • 文章目录一.Excel组成元素二.Excel图表类型与用途三.Excel可视化大全-应用1.饼状图2.折线图3.条形图4.对称图表比较两公司5.散点图6.气泡图7.雷达图8.组合图表9.各种对比10.其他对比-子弹图11.瀑布图12.漏斗图13.增维...
  • FineReport html5图表简介

    千次阅读 2015-03-19 20:37:28
    国内报表及商业智能产品的图表功能通常靠第三方插件实现,但这种做法有天然的缺陷:基础功能薄弱,需要高级图表时需要支付额外费用;技术支持能力薄弱,后续升级缺乏保障等。 ...多维坐标轴 坐标
  • ECharts 图表插件使用整理 说明 ECharts 是一个 JavaScript 实现的开源可视化库,兼容当前绝大部分浏览器 IE 8 以上,是一个可高度个性化定制的数据可视化图表 ECharts 版本 –&gt; 4.0.2 这篇文章...
  • 在当前的数据分析领域中,多维数据分析有着先进的数据分析理念,作为一项新兴技术一直以来都备受企业的青睐。 多维数据分析是指按照多个维度(即多个角度)对数据进行观察和分析,多维的分析操作是指通过对多维形式...
  • 原文链接:'''''''一、背景数据可视化技术的基本思想,是将数据库中每一个数据项作为单个图元元素表示,大量的数据集构成数据图像,同时将数据的各个属性值以多维数据的形式表示,可以从不同的维度观察数据,从而对...
  • echart-3.2.3图表的使用

    2016-11-12 13:51:03
    ECharts 提供了常规的折线图,柱状图,散点图,饼图,K线图,用于统计的盒形图,用于地理数据可视化的地图,热力图,线图,用于关系数据可视化的关系图,treemap,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,...
  • 图表在网页中应用十分广泛,尤其是后台的统计功能,更能体现出图表的强大之处。本文将介绍一些比较实用的HTML5图表动画,有些结合jQuery,有些是直接基于Canvas,一起来看看吧。1、超酷HTML5 Canvas图表应用Chart.js...
  • Style Intelligence™敏捷商业智能软件不仅可以连接各种OLAP数据库,还提供了多维数据建模工具。使用多维数据建模工具进行多维数据建模不但简单易用而且无需编写任何的代码。用户只需要通过语义层的定义就可以轻松...
  • ECharts图表

    千次阅读 2020-07-12 18:35:41
    这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的...,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入...
  • 图表

    2017-04-21 23:54:00
    百度的图表库 链接:http://echarts.baidu.com 一、ECharts 特性介绍 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari...
  • 统计学原理 合理使用图表

    千次阅读 2020-05-10 15:37:57
    这里写目录标题鉴别图表优劣的准则1. 一张好的图表应包括以下基本特征2. 5种鉴别图表优劣的准则统计表的设计 鉴别图表优劣的准则 1. 一张好的图表应包括以下基本特征 显示数据 让读者把注意力集中在...是多维的 表

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,372
精华内容 3,748
关键字:

多维图表