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)。
大家晚上好,
我正在尝试从充满小时报告(姓名、时间戳、工作时间等)的数据库中获取数据,并使用 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)。