19、数据可视化测试驱动开发与交互式分析库介绍

数据可视化测试驱动开发与交互式分析库介绍

1. 测试驱动可视化 - 图表创建

在测试环境准备就绪后,我们可以开始以测试驱动的方式开发一个简单的柱状图。虽然这和之前创建柱状图的操作类似,但此次重点在于展示如何在每一步构建测试用例,并自动验证柱状图的实现是否符合预期。

1.1 测试用例设置

首先要确保柱状图的实现存在并能接收数据。以下是测试用例的代码:

describe('BarChart', function () {
    var div,
        chart,
        data = [
            {x: 0, y: 0},
            {x: 1, y: 3},
            {x: 2, y: 6}
        ];
    beforeEach(function () {
        div = d3.select('body').append('div');
        chart = BarChart(div);
    });
    afterEach(function () {
        div.remove();
    });
    describe('.data', function () {
        it('should allow setting and retrieve chart data',  
        function () {
            expect(chart.data(data).data()).toBe(data);
        });
    });
});

在这个测试用例中,使用了几个Jasmine的构造函数:
- describe :定义一组测试用例,可以嵌套子组并定义测试用例。
- it :定义一个测试用例。
- beforeEach :定义一个预执行钩子,在每个测试用例执行前执行给定的函数。
- afterEach :定义一个后执行钩子,在每个测试用例执行后执行给定的函数。
- expect :定义测试用例中的期望,可以与匹配器(如 toBe toBeEmpty )链式调用以进行断言。

1.2 实现柱状图对象

运行上述测试用例时,会提示没有 BarChart 对象,因此需要创建该对象和函数:

function BarChart(p) {
    var that = {};
    var _parent = p, _data;
    that.data = function (d) {
        if (!arguments.length) return _data;
        _data = d;
        return that;
    };
    return that;
}

此时再次运行 SpecRunner.html ,测试用例将通过。

2. 测试驱动可视化 - SVG渲染

在创建了柱状图对象的基本框架后,接下来尝试生成 svg:svg 元素。

2.1 测试用例设置

渲染 svg:svg 元素不仅要将其添加到HTML主体中,还要将图表对象的宽度和高度设置转换为适当的SVG属性。以下是测试用例:

describe('.render', function () {
    describe('svg', function () {
        it('should generate svg', function () {
            chart.render();
            expect(svg()).not.toBeEmpty();
        });
        it('should set default svg height and width', 
          function () {
            chart.render();
            expect(svg().attr('width')).toBe('500');
            expect(svg().attr('height')).toBe('350');
        });
        it('should allow changing svg height and width', 
          function () {
            chart.width(200).height(150).render();
            expect(svg().attr('width')).toBe('200');
            expect(svg().attr('height')).toBe('150');
        });
    });
});
function svg() {
    return div.select('svg');
}
2.2 实现渲染方法

由于此时还没有 render 函数,上述测试用例都会失败。以下是实现 render 方法的代码:

var _parent = p, _width = 500, _height = 350, _data;
that.render = function () {
    var svg = _parent
        .append("svg")
        .attr("height", _height)
        .attr("width", _width);
};
that.width = function (w) {
    if (!arguments.length) return _width;
    _width = w;
    return that;
};
that.height = function (h) {
    if (!arguments.length) return _height;
    _height = h;
    return that;
};

此时再次运行 SpecRunner.html ,测试用例将通过,但此时只是生成了一个空的SVG元素,还未使用数据。

3. 测试驱动可视化 - 像素完美的条形渲染

在这一阶段,我们将使用已有数据生成条形图,并确保所有条形图不仅被渲染,而且具有像素级的精度。

3.1 测试用例设置

以下是测试用例的代码:

describe('chart body', function () {
    it('should create body g', function () {
        chart.render();
        expect(chartBody()).not.toBeEmpty();
    });
    it('should translate to (left, top)', function () {
        chart.render();
        expect(chartBody().attr('transform')). 
        toBe('translate(30,10)')
    });
});
describe('bars', function () {
    beforeEach(function () {
        chart.data(data).width(100).height(100)
            .x(d3.scale.linear().domain([0, 3]))
            .y(d3.scale.linear().domain([0, 6]))
            .render();
    });
    it('should create 3 svg:rect elements', function () {
        expect(bars().size()).toBe(3);
    });
    it('should calculate bar width automatically', 
      function () {
        bars().each(function () { 
          expect(d3.select(this).attr('width')). 
          toBe('18');
        });
    });
    it('should map bar x using x-scale', function () { 
        expect(d3.select(bars()[0][0]). 
        attr('x')).toBe('0'); 
        expect(d3.select(bars()[0][1]). 
        attr('x')).toBe('20'); 
        expect(d3.select(bars()[0][2]). 
        attr('x')).toBe('40');
    });
    it('should map bar y using y-scale', function () { 
        expect(d3.select(bars()[0][0]). 
        attr('y')).toBe('60'); 
        expect(d3.select(bars()[0][1]). 
        attr('y')).toBe('30'); 
        expect(d3.select(bars()[0][2]). 
        attr('y')).toBe('0');
    });
    it('should calculate bar height based on y', 
      function () { 
        expect(d3.select(bars()[0][0]).
        attr('height')).toBe('10'); 
        expect(d3.select(bars()[0][1]). 
        attr('height')).toBe('40'); 
        expect(d3.select(bars()[0][2]). 
        attr('height')).toBe('70');
    });
});

function chartBody() {
    return svg().select('g.body');
}
function bars() {
    return chartBody().selectAll('rect.bar');
}
3.2 实现渲染方法

以下是实现渲染方法的代码:

var _parent = p, _width = 500, _height = 350,
    _margins = {top: 10, left: 30, right: 10, bottom: 30},
    _data,
    _x = d3.scale.linear(),
    _y = d3.scale.linear();
that.render = function () {
    var svg = _parent
        .append("svg")
        .attr("height", _height)
        .attr("width", _width);
    var body = svg.append("g")
        .attr("class", 'body')
        .attr("transform", "translate(" + _margins.left + ","  
         + _margins.top + ")")
    if (_data) {
        _x.range([0, quadrantWidth()]);
        _y.range([quadrantHeight(), 0]);
        body.selectAll('rect.bar')
            .data(_data).enter()
            .append('rect')
            .attr("class", 'bar')
            .attr("width", function () {
                return quadrantWidth() / _data.length -  
                BAR_PADDING;
            })
            .attr("x", function (d) {return _x(d.x); })
            .attr("y", function (d) {return _y(d.y); })
            .attr("height", function (d) {
                return _height - _margins.bottom - _y(d.y);
            });
    }
};
4. 交互式分析库介绍

在实际开发中,有时需要快速构建交互式分析或概念验证,而不是花费数天或数周时间。接下来介绍两个JavaScript库,它们可以帮助我们在几分钟内实现快速的浏览器内交互式多维数据分析。

4.1 crossfilter.js库

crossfilter.js 是由D3的作者Mike Bostock创建的库,最初用于为Square Register提供分析功能。它是一个用于在浏览器中探索大型多元数据集的JavaScript库,支持极快(<30ms)的协调视图交互,即使数据集包含数百万条记录。

数据维度可以看作是一种数据分组或分类方式,每个维度数据元素是一个分类变量。例如,对于一个描述酒吧支付交易的JSON数据集:

[
  {"date": "2011-11-14T01:17:54Z", "quantity": 2, "total": 190,  
  "tip": 100, "type": "tab"},
  {"date": "2011-11-14T02:20:19Z", "quantity": 2, "total": 190,  
  "tip": 100, "type": "tab"},
  {"date": "2011-11-14T02:28:54Z", "quantity": 1, "total": 300,  
  "tip": 200, "type": "visa"},
  ...
]

这个数据集可以有多个维度,如 date type quantity 。这些维度允许我们从不同角度查看数据,并提出一些有趣的问题,如:
- 用账单支付的客户是否更有可能购买更多数量的商品?
- 客户是否更有可能在周五晚上购买更多数量的商品?
- 使用账单支付与现金支付相比,客户是否更有可能给小费?

4.2 创建维度和分组

以下是使用 crossfilter.js 创建维度和分组的代码:

var timeFormat = d3.time.format.iso;
var data = crossfilter(json); // <-A
var hours = data.dimension(function(d){
  return d3.time.hour(timeFormat.parse(d.date)); // <-B
});
var totalByHour = hours.group().reduceSum(function(d){
  return d.total;
});
var types = data.dimension(function(d){return d.type;});
var transactionByType = types.group().reduceCount();

var quantities = data.dimension(function(d){return d.quantity;});
var salesByQuantity = quantities.group().reduceCount();

创建维度和分组的步骤如下:
1. 通过调用 crossfilter 函数将JSON数据集传入 crossfilter (代码中的A行)。
2. 调用 dimension 函数并传入一个访问器函数来创建维度。
3. 使用 dimension.group 方法进行分类或分组。

以下是使用的函数说明:
| 函数名 | 描述 |
| ---- | ---- |
| crossfilter | 如果指定了记录,则创建一个新的 crossfilter ,记录可以是任何对象或基本类型的数组。 |
| dimension | 使用给定的值访问器函数创建一个新的维度,函数必须返回自然排序的值。 |
| dimension.group | 为给定的维度创建一个新的分组。 |
| group.all | 按键的升序自然顺序返回所有组。 |
| group.reduceCount | 一个快捷函数,用于计算记录数,返回该组。 |
| group.reduceSum | 一个快捷函数,使用指定的值访问器函数对记录求和。 |

5. 维度图表 - dc.js

dc.js 是为了可视化 crossfilter 的维度和分组而创建的JavaScript库,它基于D3构建,API与D3类似。

5.1 创建图表

在一个示例中,我们将创建三个图表:
- 一个折线图,用于可视化时间序列上的交易总金额。
- 一个饼图,用于可视化按支付类型划分的交易数量。
- 一个柱状图,用于显示按购买数量划分的销售数量。

以下是创建图表的代码:

<div id="area-chart"></div>
<div id="donut-chart"></div>
<div id="bar-chart"></div>
dc.lineChart("#area-chart")
    .width(500)
    .height(250)
    .dimension(hours)
    .group(totalByHour)
    .x(d3.time.scale().domain([
     timeFormat.parse("2011-11-14T01:17:54Z"), 
      timeFormat.parse("2011-11-14T18:09:52Z")
]))
    .elasticY(true)
    .xUnits(d3.time.hours)
    .renderArea(true)
    .xAxis().ticks(5);
dc.pieChart("#donut-chart")
    .width(250)
    .height(250)
    .radius(125)
    .innerRadius(50)
    .dimension(types)
    .group(transactionByType);

dc.barChart("#bar-chart")
    .width(500)
    .height(250)
    .dimension(quantities)
    .group(salesByQuantity)
    .x(d3.scale.linear().domain([0, 7]))
    .y(d3.scale.linear().domain([0, 12]))
    .centerBar(true);
dc.renderAll();
5.2 图表创建步骤

图表通常按以下步骤创建:
1. 调用图表创建函数之一并传入一个D3选择器作为其锚点元素,例如:

dc.lineChart("#area-chart")
  1. 设置每个图表的宽度、高度、维度和分组,对于在笛卡尔平面上渲染的坐标图表,还需要设置x和y比例:
chart.width(500)
     .height(250)
     .dimension(hours)
     .group(totalByHour)
chart.x(d3.time.scale().domain([
  timeFormat.parse("2011-11-14T01:17:54Z"), 
  timeFormat.parse("2011-11-14T18:09:52Z")
])).elasticY(true)
5.3 支持的图表类型

在撰写本文时, dc.js 支持以下图表类型:
- 柱状图(可堆叠)
- 折线图(可堆叠)
- 面积图(可堆叠)
- 饼图
- 气泡图
- 复合图
- 等值区域图
- 气泡覆盖图

此外,还有一些其他基于D3的可重用图表库,如NVD3和Rickshaw,它们虽然不是为了与 crossfilter 原生配合使用,但在处理一般可视化挑战时往往更丰富和灵活。

综上所述,通过测试驱动开发可以确保数据可视化的准确性和稳定性,而 crossfilter.js dc.js 库则可以帮助我们快速构建交互式数据分析。希望这些内容能对你在数据可视化和分析方面的工作有所帮助。

数据可视化测试驱动开发与交互式分析库介绍

6. 测试驱动开发的优势与流程总结

测试驱动开发(TDD)在数据可视化中有着显著的优势。它能够确保每一步的实现都符合预期,避免在开发后期出现难以调试的问题,提高代码的可维护性和稳定性。以下是测试驱动开发的基本流程:

graph LR
    A[编写测试用例] --> B[运行测试,测试失败]
    B --> C[编写实现代码]
    C --> D[运行测试,测试通过]
    D --> E{是否还有功能待实现}
    E -- 是 --> A
    E -- 否 --> F[完成开发]

在前面的柱状图开发过程中,我们严格遵循了这个流程。首先编写了关于数据设置和获取的测试用例,运行时测试失败,然后编写了 BarChart 对象和 data 方法,再次运行测试,测试通过。接着针对SVG渲染和条形渲染分别编写测试用例,重复上述流程,逐步完善柱状图的功能。

7. crossfilter.js 与 dc.js 的协同工作

crossfilter.js dc.js 可以很好地协同工作,实现快速的交互式多维数据分析可视化。以下是它们协同工作的步骤:
1. 数据准备 :使用 crossfilter.js 加载和处理数据,创建维度和分组。例如:

var timeFormat = d3.time.format.iso;
var data = crossfilter(json);
var hours = data.dimension(function(d){
  return d3.time.hour(timeFormat.parse(d.date));
});
var totalByHour = hours.group().reduceSum(function(d){
  return d.total;
});
var types = data.dimension(function(d){return d.type;});
var transactionByType = types.group().reduceCount();
var quantities = data.dimension(function(d){return d.quantity;});
var salesByQuantity = quantities.group().reduceCount();
  1. 图表创建 :使用 dc.js 基于 crossfilter.js 创建的维度和分组来创建图表。例如:
dc.lineChart("#area-chart")
    .width(500)
    .height(250)
    .dimension(hours)
    .group(totalByHour)
    .x(d3.time.scale().domain([
     timeFormat.parse("2011-11-14T01:17:54Z"), 
      timeFormat.parse("2011-11-14T18:09:52Z")
]))
    .elasticY(true)
    .xUnits(d3.time.hours)
    .renderArea(true)
    .xAxis().ticks(5);
dc.pieChart("#donut-chart")
    .width(250)
    .height(250)
    .radius(125)
    .innerRadius(50)
    .dimension(types)
    .group(transactionByType);
dc.barChart("#bar-chart")
    .width(500)
    .height(250)
    .dimension(quantities)
    .group(salesByQuantity)
    .x(d3.scale.linear().domain([0, 7]))
    .y(d3.scale.linear().domain([0, 12]))
    .centerBar(true);
dc.renderAll();
  1. 交互实现 :用户与 dc.js 创建的图表进行交互时, crossfilter.js 会相应地过滤数据,所有图表都会实时更新。例如,当用户点击或拖动折线图时,其他饼图和柱状图也会根据过滤后的数据进行显示。
8. 实际应用场景分析

crossfilter.js dc.js 在很多实际场景中都有广泛的应用,以下是一些常见的场景:
| 应用场景 | 描述 |
| ---- | ---- |
| 销售数据分析 | 可以对销售数据进行多维度分析,如按时间、产品类型、销售地区等维度进行分组和可视化,帮助企业了解销售趋势和市场需求。 |
| 金融数据分析 | 对金融交易数据进行分析,如按交易时间、交易类型、客户类型等维度进行过滤和可视化,帮助金融机构监控风险和发现潜在机会。 |
| 医疗数据分析 | 对医疗数据进行分析,如按疾病类型、治疗时间、患者年龄等维度进行分组和可视化,帮助医疗机构提高医疗质量和效率。 |

9. 总结与展望

通过本文的介绍,我们了解了测试驱动开发在数据可视化中的应用,以及 crossfilter.js dc.js 这两个强大的JavaScript库在快速构建交互式多维数据分析可视化中的作用。测试驱动开发能够保证代码的质量和稳定性,而 crossfilter.js dc.js 的结合则可以让我们在短时间内实现复杂的数据分析和可视化功能。

在未来的发展中,随着数据量的不断增加和数据分析需求的不断提高,这些技术和工具将会发挥更加重要的作用。同时,我们也可以期待更多的优秀开源库和工具的出现,为数据可视化和分析带来更多的可能性。

希望本文能够帮助你更好地理解和应用这些技术,在实际项目中取得更好的效果。如果你在使用过程中遇到任何问题,欢迎在评论区留言讨论。

计及源荷不确定性的综合能源生产单元运行调度容量配置优化研究(Matlab代码实现)内容概要:本文围绕“计及源荷不确定性的综合能源生产单元运行调度容量配置优化”展开研究,利用Matlab代码实现相关模型的构建仿真。研究重点在于综合能源系统中多能耦合特性以及风、光等可再生能源出力和负荷需求的不确定性,通过鲁棒优化、场景生成(如Copula方法)、两阶段优化等手段,实现对能源生产单元的运行调度容量配置的协同优化,旨在提高系统经济性、可靠性和可再生能源消纳能力。文中提及多种优化算法(如BFO、CPO、PSO等)在调度预测中的应用,并强调了模型在实际能源系统规划运行中的参考价值。; 适合人群:具备一定电力系统、能源系统或优化理论基础的研究生、科研人员及工程技术人员,熟悉Matlab编程和基本优化工具(如Yalmip)。; 使用场景及目标:①用于学习和复现综合能源系统中考虑不确定性的优化调度容量配置方法;②为含高比例可再生能源的微电网、区域能源系统规划设计提供模型参考和技术支持;③开展学术研究,如撰写论文、课题申报时的技术方案借鉴。; 阅读建议:建议结合文中提到的Matlab代码和网盘资料,先理解基础模型(如功率平衡、设备模型),再逐步深入不确定性建模优化求解过程,注意区分鲁棒优化、随机优化分布鲁棒优化的适用场景,并尝试复现关键案例以加深理解。
内容概要:本文系统分析了DesignData(设计数据)的存储结构,围绕其形态多元化、版本关联性强、读写特性差异化等核心特性,提出了灵活性、版本化、高效性、一致性和可扩展性五大设计原则。文章深入剖析了三类主流存储方案:关系型数据适用于结构化元信息存储,具备强一致性高效查询能力;文档型数据适配半结构化数据,支持动态字段扩展嵌套结构;对象存储结合元数据索引则有效应对非结构化大文件的存储需求,具备高扩展性低成本优势。同时,文章从版本管理、性能优化和数据安全三个关键维度提出设计要点,建议采用全量增量结合的版本策略、索引缓存优化性能、并通过权限控制、MD5校验和备份机制保障数据安全。最后提出按数据形态分层存储的核心结论,并针对不同规模团队给出实践建议。; 适合人群:从事工业设计、UI/UX设计、工程设计等领域数字化系统开发的技术人员,以及负责设计数据管理系统架构设计的中高级工程师和系统架构师。; 使用场景及目标:①为设计数据管理系统选型提供依据,合理选择或组合使用关系型数据、文档型数据对象存储;②构建支持版本追溯、高性能访问、安全可控的DesignData存储体系;③解决多用户协作、大文件存储、历史版本管理等实际业务挑战。; 阅读建议:此资源以实际应用场景为导向,结合具体数据类型和表结构设计进行讲解,建议读者结合自身业务数据特征,对比分析不同存储方案的适用边界,并在系统设计中综合考虑成本、性能可维护性之间的平衡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值