dc.js 复合图 - 为每个人绘制新线

dc.js Composite Graph - Plot New Line for Each Person

大家晚上好,

我正在尝试从充满小时报告(姓名、时间戳、工作时间等)的数据库中获取数据,并使用 dc.js 创建一个图表来可视化数据。我希望时间戳在 x-axis 上,特定时间戳的总小时数在 y-axis 上,每个唯一名称的新条形图都在同一张图表上。

根据我的目标看来,使用 crossfilter.js 时间戳应该是我的 'dimension',然后总小时数应该是我的 'group'。

问题1,我如何使用维度和分组根据人名进一步拆分数据,然后创建一个条形图以添加到我的复合图中?我希望 crossfilter.js 功能保持不变,这样如果我添加日期范围工具或其他一些用户可控制的过滤器,所有内容都会相应更新。

问题 2,我的时间戳采用 MySQL 日期时间格式:YYYY-mm-dd HH:MM:SS 那么我将如何降低精度?例如,如果我想将同一天的所有条目合并为一个条目(日精度)或将一个月的所有条目合并为一个条目(月精度)。

提前致谢!

---- 添加于 2017/01/28 16:06

为了进一步说明,我将 Crossfilter 和 DC API 与 DC NASDAQ and Composite 示例一起引用。 Composite 示例向我展示了如何在单个图形上放置多个 line/bar 图表。在我创建的复合图表上,我根据 data-set 中的时间戳添加了每个条形图的维度。现在我想弄清楚如何为每个人定义组。我希望每个条形图代表每个时间戳的总工作时间。

例如,我的数据库中有五个人,所以我希望单个复合图表中有五个条形图。今天所有五个提交的报告都说他们工作了 8 小时,所以现在所有五个条形图都应该在 x-axis 上显示 01/28/2017 标记,在 y-axis.

上显示 8 小时标记
    var parseDate = d3.time.format('%Y-%m-%d %H:%M:%S').parse;
    data.forEach(function(d) {
      d.timestamp = parseDate(d.timestamp);
    });
    var ndx              = crossfilter(data);
    var writtenDimension = ndx.dimension(function(d) {
      return d.timestamp;
    });
    var hoursSumGroup    = writtenDimension.group().reduceSum(function(d) {
      return d.time_total;
    });
    var minDate = parseDate('2017-01-01 00:00:00');
    var maxDate = parseDate('2017-01-31 23:59:59');
    var mybarChart = dc.compositeChart("#my_chart");
    mybarChart
      .width(window.innerWidth)
      .height(480)
      .x(d3.time.scale().domain([minDate,maxDate]))
      .brushOn(false)
      .clipPadding(10)
      .yAxisLabel("This is the Y Axis!")
      .compose([
        dc.barChart(mybarChart)
            .dimension(writtenDimension)
            .colors('red')
            .group(hoursSumGroup, "Top Line")
      ]);

所以根据我现在所拥有的和我提供的示例,在撰写部分我应该有 5 个图表,因为有 5 个人(显然这最终需要是动态的)并且每个人图表应仅显示该人的时间戳:total_time 数据。

此时我不知道如何进一步分解基于每个人的组 hoursSumGroup,这就是我的问题 #1 的来源,我需要帮助弄清楚。

上面的问题 #2 是我想确保代码是动态的(无需更改代码即可处理更多人),当 minDate 和 maxDate 稍后绑定到用户输入字段时,图表会自动更新(我假设通过以某种方式调整维度变量),如果我添加一个名称过滤器,如果我取消选择图表将通过删除该人的数据来更新的名称。

我现在意识到我想弄清楚的问题 #3 是如何让这个人的名字连同时间戳和 total_time 值一起显示在指针工具提示(标题)中.

有很多方法可以解决这个问题,但我认为最简单的方法是创建自定义缩减,将每个人缩减为 sub-bin。

首先,解决问题 #2,您需要根据您感兴趣的时间间隔设置维度。例如,如果您要查看天数:

var writtenDimension = ndx.dimension(function(d) {
    return d3.time.hour(d.timestamp);
});
chart.xUnits(d3.time.hours);

这将使每个时间戳向下舍入到最接近的小时,并告诉图表相应地计算条形宽度。

接下来,这是一个自定义缩减 (from the FAQ),它将为每个缩减的值创建一个 object,每个人的名字都有值:

var hoursSumGroup = writtenDimension.group().reduce(
    function(p, v) { // add
        p[v.name] = (p[v.name] || 0) + d.time_total;
        return p;
    },
    function(p, v) { // remove
        p[v.name] -= d.time_total;
        return p;
    },
    function() { // init
        return {};
    });    

我没有使用我在评论中提到的系列示例,因为我认为复合键可能很难处理。这是另一种选择,如果有必要,我会扩展我的答案。

接下来,我们可以为复合折线图提供值访问器,这些访问器可以按名称获取值。

假设我们有一个数组 names.

compositeChart.shareTitle(false);
compositeChart.compose(
    names.map(function(name) {
        return dc.lineChart(compositeChart)
            .dimension(writtenDimension)
            .colors('red')
            .group(hoursSumGroup)
            .valueAccessor(function(kv) {
                return kv.value[name];
            })
            .title(function(kv) {
                return name + ' ' + kv.key + ': ' + kv.value;
            });
    }));

同样,在这里使用条形图没有意义,因为它们会相互混淆。

如果您在别处过滤姓名,将导致该姓名所在的行降为零。让这条线完全消失可能不会那么简单。

以上shareTitle(false)确保child图表将绘制自己的标题; title 函数只是将当前名称添加到这些标题(通常只是 key:value)。