在 dc.js 中跟踪和过滤上个月的值
Track and filter last month value in dc.js
我遇到了与 DC.JS get last month value as filter 中描述的问题类似的问题。
也就是说,我想在条形图中显示上个月的股票价值(按公司和产品类型),同时我想在折线图中显示股票随时间的演变。
为此我需要跟踪通过刷折线图选择的最后一个月。在这里查看我的 jsFiddle:https://jsfiddle.net/BernG/wo60z64j/12/
var data = [{date:"201501",company:"A", product: "Prod1", stock:575}
, {date:"201502",company:"A", product: "Prod1", stock:325}
, {date:"201503",company:"A", product: "Prod1", stock:200}
, {date:"201504",company:"A", product: "Prod1", stock:450}
, {date:"201501",company:"A", product: "Prod2", stock:279}
, {date:"201502",company:"A", product: "Prod2", stock:93}
, {date:"201503",company:"A", product: "Prod2", stock:0}
, {date:"201504",company:"A", product: "Prod2", stock:372}
, {date:"201501",company:"A", product: "Prod3", stock:510}
, {date:"201502",company:"A", product: "Prod3", stock:340}
, {date:"201503",company:"A", product: "Prod3", stock:680}
, {date:"201504",company:"A", product: "Prod3", stock:170}
, {date:"201501",company:"B",product: "Prod1", stock:1000}
, {date:"201502",company:"B",product: "Prod1", stock:1100}
, {date:"201503",company:"B",product: "Prod1", stock:900}
, {date:"201504",company:"B",product: "Prod1", stock:1200}
, {date:"201501",company:"B",product: "Prod2", stock:1000}
, {date:"201502",company:"B",product: "Prod2", stock:1200}
, {date:"201503",company:"B",product: "Prod2", stock:900}
, {date:"201504",company:"B",product: "Prod2", stock:1200}
, {date:"201501",company:"B",product: "Prod3", stock:1000}
, {date:"201502",company:"B",product: "Prod3", stock:1100}
, {date:"201503",company:"B",product: "Prod3", stock:900}
, {date:"201504",company:"B",product: "Prod3", stock:600}];
// Reading and formatting values
var dateFormat = d3.time.format('%Y%m');
data.forEach(function (d) {
d.dd = dateFormat.parse(d.date);
d.year = d3.time.year(d.dd);
d.month = d3.time.month(d.dd);
});
// Definition of crossfilter and dimensions
var ndx = crossfilter(data)
, dimMonth = ndx.dimension(function(d){return d.month})
, dimProduct = ndx.dimension(function(d){return d.product})
, dimCompany = ndx.dimension(function(d){return d.company;});
var lastStaticDate = dimMonth.top(1)[0].month; // identify last date in full time domain
var firstStaticDate = dimMonth.bottom(1)[0].month; // identify first date in full time domain
// Definition of a function to keep track of the last date value in the brush attached to the chartMonth.
// If chartMonth object does not exist or its corresponding brush is empty (has not been brushed), then it returns the lastStaticDate
// otherwise, it returns the last date in the brush selection
var getLastDate = function(){
if (typeof chartMonth === "undefined"){ // test if chartMonth is already in the DOM, if not return lastStaticDate
return lastStaticDate;
}
else {
if (chartMonth.brush().empty()) { // if chartMonth has been created but brush does not have have a selection
return lastStaticDate;
}
else {
return chartMonth.brush().extent()[1];
};
}
};
var lastDate = d3.time.month.ceil(getLastDate()); // lastDate is snapped to return a date where we have data in the x-domain
dateBal.innerHTML = lastDate;
var grpMonth = dimMonth.group().reduceSum(function(d){return d.stock;}); // the line chart displays all values in the x-axis to show stock evolution
// Definition of custom reduce functions
function reduceAdd(p,v) {
if (p.month===lastDate){
p.stock += v.stock;
return p;
}
else {return p;}
};
function reduceRemove(p,v) {
if (p.month!== lastDate){
p.stock -= v.stock;
return p;}
else {return p;}
};
function reduceInitial() {
return {stock: 0}
};
// Application of reduce functions
var grpCompany = dimCompany
.group()
.reduce(reduceAdd, reduceRemove, reduceInitial);
var grpProduct = dimProduct
.group()
.reduce(reduceAdd, reduceRemove, reduceInitial);
var chartCompany = dc.barChart('#chartCompany');
var chartProduct = dc.barChart("#chartProduct");
var chartMonth = dc.lineChart('#chartMonth');
chartCompany
.width(400)
.height(400)
.margins({top: 50, right: 50, bottom: 50, left: 50})
.dimension(dimCompany)
.group(grpCompany)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.elasticY(true);
chartProduct
.width(400)
.height(400)
.margins({top: 50, right: 50, bottom: 50, left: 50})
.dimension(dimProduct)
.group(grpProduct)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.elasticY(true);
chartMonth
.width(400)
.height(400)
.margins({top: 50, right: 50, bottom: 50, left: 50})
.renderlet(function (chart) {
// rotate x-axis labels
chart.selectAll('g.x text')
.attr('transform', 'translate(-10,10) rotate(315)');
})
.dimension(dimMonth)
.group(grpMonth)
.x(d3.time.scale().domain([firstStaticDate, lastStaticDate]))
.xUnits(d3.time.months)
.elasticX(true);
dc.renderAll();
我尝试的解决方案是基于对上述问题的唯一答案(顺便说一下,它没有被标记为已接受)并遵循以下逻辑:
1) 首先,我尝试确定交叉过滤数据集中的最后日期。初始化后,它将是画笔在折线图中移动之前我时间维度的最后日期。否则,它 returns 画笔范围中第二个元素的日期。到目前为止,我应用上限函数以确保它 returns 的日期作为我的时间维度中的确切日期存在。
2) 我应用了自定义 reduce 函数,排除了与当前选择不同月份的数据。
我的具体问题是:
如何使变量 (lastDate) 具有反应性?以下内容在控制台中运行良好:d3.time.month.ceil(getLastDate())。但是,它不会对交互式刷牙事件做出反应。
我的自定义 reduce 函数需要做哪些更改才能仅累积与 lastDate 对应的值并排除所有其他值?由于某些原因,当前定义的客户减少功能无法正确累积库存值。例如,在初始化时,如果我检查 grpCompany 所在的对象,它会将股票的价值显示为 0。
grpCompany.all() in console
最后,在您投票将此问题标记为重复之前,请考虑以下事项:
- 原文 post 似乎没有公认的答案。
- 如果 OP 提供了一个有效的 jsFiddle,有人提议提供帮助,但提供了 none。
- 我想在原 post 中发表后续评论,要求澄清,但不可能这样做,因为我还没有 post 发表评论所需的声誉.
好吧,这真的很复杂,这就是为什么我在上一个问题的评论开始时说 Crossfilter 不擅长这个。我的回答很复杂,您没有按照我的建议去做,但这不是您的错。 Universe 库支持您提出的一些建议,但总的来说,如果它变得如此复杂,我建议您重新考虑您的方法。但是,您所说的场景也与上一个问题中的场景略有不同,并且有一个更简单的答案:
在您的情况下,您实际上应该做的是使用自定义 dc.js filterFunction 使股票余额图表仅过滤到刷子中的最后日期:
chartMonth
...
.filterHandler(function(d,f) {
if(f.length > 0) {
d.filterExact(d3.time.month.ceil(f[0][1]));
dateBal.innerHTML = d3.time.month.ceil(f[0][1]);
} else {
d.filterAll();
dateBal.innerHTML = "No month selected"
}
return f;
})
这是一个基于您的 JSFiddle 的工作版本:https://jsfiddle.net/esjewett/utohm7mq/2/
所以,这就是简单的答案。它实际上所做的只是将整个 Crossfilter 过滤到画笔中选择的最后一个月(或实际上是下一个月)。如果您也 想将其他一些图表过滤到画笔选择的实际日期,那么您就是在谈论有多个相互冲突的过滤器,而 Crossfilter 目前并不能很好地支持这一点。我们有兴趣添加支持多个过滤器组的功能,但还没有开始解决这个问题。在这种情况下,目前最好的方法可能是维护 2 个独立的交叉过滤器,并从不同的交叉过滤器中驱动不同的图表组。
我遇到了与 DC.JS get last month value as filter 中描述的问题类似的问题。
也就是说,我想在条形图中显示上个月的股票价值(按公司和产品类型),同时我想在折线图中显示股票随时间的演变。
为此我需要跟踪通过刷折线图选择的最后一个月。在这里查看我的 jsFiddle:https://jsfiddle.net/BernG/wo60z64j/12/
var data = [{date:"201501",company:"A", product: "Prod1", stock:575}
, {date:"201502",company:"A", product: "Prod1", stock:325}
, {date:"201503",company:"A", product: "Prod1", stock:200}
, {date:"201504",company:"A", product: "Prod1", stock:450}
, {date:"201501",company:"A", product: "Prod2", stock:279}
, {date:"201502",company:"A", product: "Prod2", stock:93}
, {date:"201503",company:"A", product: "Prod2", stock:0}
, {date:"201504",company:"A", product: "Prod2", stock:372}
, {date:"201501",company:"A", product: "Prod3", stock:510}
, {date:"201502",company:"A", product: "Prod3", stock:340}
, {date:"201503",company:"A", product: "Prod3", stock:680}
, {date:"201504",company:"A", product: "Prod3", stock:170}
, {date:"201501",company:"B",product: "Prod1", stock:1000}
, {date:"201502",company:"B",product: "Prod1", stock:1100}
, {date:"201503",company:"B",product: "Prod1", stock:900}
, {date:"201504",company:"B",product: "Prod1", stock:1200}
, {date:"201501",company:"B",product: "Prod2", stock:1000}
, {date:"201502",company:"B",product: "Prod2", stock:1200}
, {date:"201503",company:"B",product: "Prod2", stock:900}
, {date:"201504",company:"B",product: "Prod2", stock:1200}
, {date:"201501",company:"B",product: "Prod3", stock:1000}
, {date:"201502",company:"B",product: "Prod3", stock:1100}
, {date:"201503",company:"B",product: "Prod3", stock:900}
, {date:"201504",company:"B",product: "Prod3", stock:600}];
// Reading and formatting values
var dateFormat = d3.time.format('%Y%m');
data.forEach(function (d) {
d.dd = dateFormat.parse(d.date);
d.year = d3.time.year(d.dd);
d.month = d3.time.month(d.dd);
});
// Definition of crossfilter and dimensions
var ndx = crossfilter(data)
, dimMonth = ndx.dimension(function(d){return d.month})
, dimProduct = ndx.dimension(function(d){return d.product})
, dimCompany = ndx.dimension(function(d){return d.company;});
var lastStaticDate = dimMonth.top(1)[0].month; // identify last date in full time domain
var firstStaticDate = dimMonth.bottom(1)[0].month; // identify first date in full time domain
// Definition of a function to keep track of the last date value in the brush attached to the chartMonth.
// If chartMonth object does not exist or its corresponding brush is empty (has not been brushed), then it returns the lastStaticDate
// otherwise, it returns the last date in the brush selection
var getLastDate = function(){
if (typeof chartMonth === "undefined"){ // test if chartMonth is already in the DOM, if not return lastStaticDate
return lastStaticDate;
}
else {
if (chartMonth.brush().empty()) { // if chartMonth has been created but brush does not have have a selection
return lastStaticDate;
}
else {
return chartMonth.brush().extent()[1];
};
}
};
var lastDate = d3.time.month.ceil(getLastDate()); // lastDate is snapped to return a date where we have data in the x-domain
dateBal.innerHTML = lastDate;
var grpMonth = dimMonth.group().reduceSum(function(d){return d.stock;}); // the line chart displays all values in the x-axis to show stock evolution
// Definition of custom reduce functions
function reduceAdd(p,v) {
if (p.month===lastDate){
p.stock += v.stock;
return p;
}
else {return p;}
};
function reduceRemove(p,v) {
if (p.month!== lastDate){
p.stock -= v.stock;
return p;}
else {return p;}
};
function reduceInitial() {
return {stock: 0}
};
// Application of reduce functions
var grpCompany = dimCompany
.group()
.reduce(reduceAdd, reduceRemove, reduceInitial);
var grpProduct = dimProduct
.group()
.reduce(reduceAdd, reduceRemove, reduceInitial);
var chartCompany = dc.barChart('#chartCompany');
var chartProduct = dc.barChart("#chartProduct");
var chartMonth = dc.lineChart('#chartMonth');
chartCompany
.width(400)
.height(400)
.margins({top: 50, right: 50, bottom: 50, left: 50})
.dimension(dimCompany)
.group(grpCompany)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.elasticY(true);
chartProduct
.width(400)
.height(400)
.margins({top: 50, right: 50, bottom: 50, left: 50})
.dimension(dimProduct)
.group(grpProduct)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
.elasticY(true);
chartMonth
.width(400)
.height(400)
.margins({top: 50, right: 50, bottom: 50, left: 50})
.renderlet(function (chart) {
// rotate x-axis labels
chart.selectAll('g.x text')
.attr('transform', 'translate(-10,10) rotate(315)');
})
.dimension(dimMonth)
.group(grpMonth)
.x(d3.time.scale().domain([firstStaticDate, lastStaticDate]))
.xUnits(d3.time.months)
.elasticX(true);
dc.renderAll();
我尝试的解决方案是基于对上述问题的唯一答案(顺便说一下,它没有被标记为已接受)并遵循以下逻辑:
1) 首先,我尝试确定交叉过滤数据集中的最后日期。初始化后,它将是画笔在折线图中移动之前我时间维度的最后日期。否则,它 returns 画笔范围中第二个元素的日期。到目前为止,我应用上限函数以确保它 returns 的日期作为我的时间维度中的确切日期存在。
2) 我应用了自定义 reduce 函数,排除了与当前选择不同月份的数据。
我的具体问题是:
如何使变量 (lastDate) 具有反应性?以下内容在控制台中运行良好:d3.time.month.ceil(getLastDate())。但是,它不会对交互式刷牙事件做出反应。
我的自定义 reduce 函数需要做哪些更改才能仅累积与 lastDate 对应的值并排除所有其他值?由于某些原因,当前定义的客户减少功能无法正确累积库存值。例如,在初始化时,如果我检查 grpCompany 所在的对象,它会将股票的价值显示为 0。 grpCompany.all() in console
最后,在您投票将此问题标记为重复之前,请考虑以下事项:
- 原文 post 似乎没有公认的答案。
- 如果 OP 提供了一个有效的 jsFiddle,有人提议提供帮助,但提供了 none。
- 我想在原 post 中发表后续评论,要求澄清,但不可能这样做,因为我还没有 post 发表评论所需的声誉.
好吧,这真的很复杂,这就是为什么我在上一个问题的评论开始时说 Crossfilter 不擅长这个。我的回答很复杂,您没有按照我的建议去做,但这不是您的错。 Universe 库支持您提出的一些建议,但总的来说,如果它变得如此复杂,我建议您重新考虑您的方法。但是,您所说的场景也与上一个问题中的场景略有不同,并且有一个更简单的答案:
在您的情况下,您实际上应该做的是使用自定义 dc.js filterFunction 使股票余额图表仅过滤到刷子中的最后日期:
chartMonth
...
.filterHandler(function(d,f) {
if(f.length > 0) {
d.filterExact(d3.time.month.ceil(f[0][1]));
dateBal.innerHTML = d3.time.month.ceil(f[0][1]);
} else {
d.filterAll();
dateBal.innerHTML = "No month selected"
}
return f;
})
这是一个基于您的 JSFiddle 的工作版本:https://jsfiddle.net/esjewett/utohm7mq/2/
所以,这就是简单的答案。它实际上所做的只是将整个 Crossfilter 过滤到画笔中选择的最后一个月(或实际上是下一个月)。如果您也 想将其他一些图表过滤到画笔选择的实际日期,那么您就是在谈论有多个相互冲突的过滤器,而 Crossfilter 目前并不能很好地支持这一点。我们有兴趣添加支持多个过滤器组的功能,但还没有开始解决这个问题。在这种情况下,目前最好的方法可能是维护 2 个独立的交叉过滤器,并从不同的交叉过滤器中驱动不同的图表组。