【问题标题】:Track and filter last month value in dc.js在 dc.js 中跟踪和过滤上个月的值
【发布时间】:2016-04-27 04:20:14
【问题描述】:

我遇到了与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) 首先,我尝试确定交叉过滤数据集中的最后日期。初始化后,它将是画笔在折线图中移动之前我的时间维度的最后一个日期。否则,它从画笔范围中的第二个元素返回日期。到目前为止,我应用了天花板函数来确保它返回的日期在我的时间维度中作为准确的日期存在。

2) 我应用了自定义 reduce 函数,不包括与当前选择不同月份的数据。

我的具体问题是:

  • 如何使变量 (lastDate) 具有响应性?以下在控制台中运行良好:d3.time.month.ceil(getLastDate())。但是,它不会对交互式刷牙事件做出反应。

  • 需要在我的自定义 reduce 函数中进行哪些更改以仅累积与 lastDate 对应的值并排除所有其他值?由于某种原因,当前定义的客户减少功能无法正确累积库存值。例如,在初始化时,如果我检查 grpCompany 所在的对象,它会将股票的值显示为 0。 grpCompany.all() in console

    最后,在您投票将此问题标记为重复之前,请考虑以下事项:

  • 原始帖子似乎没有被接受的答案。
  • 如果 OP 提供了一个可以工作的 jsFiddle,就会提供帮助,但没有提供。
  • 我想在原始帖子中发表后续评论,要求澄清,但无法这样做,因为我还没有发表评论所需的声誉。

【问题讨论】:

    标签: d3.js dc.js crossfilter stock


    【解决方案1】:

    好的,这真的很复杂,这就是为什么我开始对上一个问题的评论说 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 个独立的交叉过滤器,并将不同的图表组从不同的交叉过滤器中分离出来。

    【讨论】:

    • 感谢您的及时答复。提供的解决方案当然非常简单,而且效果很好。我只是在 filterHandler 中做了一点改动,以包含 f.length == 0 的条件,因此我可以使用 lastStaticDate 应用过滤器。这允许条形图显示在已根据折线图中的最后日期过滤的页面加载上。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-14
    • 1970-01-01
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多