2017-08-29 114 views
0

我剛開始dc.js,並期待在主網站在納斯達克例如:https://dc-js.github.io/dc.js/變化百分比/ crossfilter

我創建a Fiddle一些樣本僞數據,只是兩個相關的圖表這個問題。

與納斯達克例子類似,我想要有一個氣泡圖,Y軸是在不同圖表中由畫筆控制的時間範圍內的%變化值。對於NASDAQ示例的代碼執行以下操作:

var yearlyPerformanceGroup = yearlyDimension.group().reduce(
    /* callback for when data is added to the current filter results */ 
    function (p, v) { 
     ++p.count; 
     p.absGain += v.close - v.open; 
     p.fluctuation += Math.abs(v.close - v.open); 
     p.sumIndex += (v.open + v.close)/2; 
     p.avgIndex = p.sumIndex/p.count; 
     p.percentageGain = p.avgIndex ? (p.absGain/p.avgIndex) * 100 : 0; 
     p.fluctuationPercentage = p.avgIndex ? (p.fluctuation/p.avgIndex) * 100 : 0; 
     return p; 
    }, 
    /* callback for when data is removed from the current filter results */ 
    function (p, v) { 
     --p.count; 
     p.absGain -= v.close - v.open; 
     p.fluctuation -= Math.abs(v.close - v.open); 
     p.sumIndex -= (v.open + v.close)/2; 
     p.avgIndex = p.count ? p.sumIndex/p.count : 0; 
     p.percentageGain = p.avgIndex ? (p.absGain/p.avgIndex) * 100 : 0; 
     p.fluctuationPercentage = p.avgIndex ? (p.fluctuation/p.avgIndex) * 100 : 0; 
     return p; 
    }, 
    /* initialize p */ 
    function() { 
     return { 
      count: 0, 
      absGain: 0, 
      fluctuation: 0, 
      fluctuationPercentage: 0, 
      sumIndex: 0, 
      avgIndex: 0, 
      percentageGain: 0 
     }; 
    } 
); 

我目前解釋爲求和(關 - 開)對所有的數據和由平均每日平均指數的分裂。但這不是我熟悉的百分比變化公式。 (例如,(新老)/舊×100)

雖然它似乎爲納斯達克例如工作,我的數據會比較像下面這樣:

country_id,received_week,product_type,my_quantity,my_revenue,country_other_quantity 
3,2017-04-02,1,1,361,93881 
1,2017-04-02,4,45,140,93881 
2,2017-04-02,4,2,30,93881 
3,2017-04-02,3,1,462,93881 
2,2017-04-02,3,48,497,93881 

等。在許多月和product_types 。

假設我對計算特定國家的百分比變化感興趣。如何獲得給定國家的開始和結束數量,以便我可以將變化計算爲結束開始/開始* 100?

我在想的東西,如以下(假設我建立合適的尺寸和一切)

var country_dim = ndx.dimension(function (d) { return d['country_id']; }) 
var received_day_dim = ndx.dimension(function (d) { return d['received_day']; }) 
var date_min = received_day_dim.bottom(1)[0]['received_day'] 
var date_max = received_day_dim.top(1)[0]['received_day'] 

然後在我的自定義減少目前的例子(錯誤)的靜脈功能:

var statsByCountry = country_dim.group().reduce(
      function (p, v) { 
       ++p.count; 
       p.units += +v["my_units"]; 
       p.example_rate = +v['my_units']/(v['quantity_unpacked']*90) //place holder for total units per day per country 
       p.sumRate += p.opp_buy_rate; 
       p.avgRate = p.opp_buy_rate/p.count; 
       p.percentageGain = p.avgRate ? (p.opp_buy_rate/p.avgRate) * 100 : 0; 
       p.dollars += +v["quantity_unpacked"]/2; 
       // p.max_date = v['received_week'].max(); 
       // p.min_date 
       //dateDimension.top(Infinity)[dateDimension.top(Infinity).length - 1]['distance'] - dateDimension.top(Infinity)[0]['distance'] 


       return p; 
      }, 
      function (p, v) { 
       --p.count; 
       if (v.region_id > 2) { 
       p.test -= 100; 
       } 
       p.units -= +v["quantity_unpacked"]; 
       p.opp_buy_rate = +v['quantity_unpacked']/(v['quantity_unpacked']*90) //place holder for total units per day per country 
       p.sumRate -= p.opp_buy_rate; 
       p.avgRate = p.count ? p.opp_buy_rate/p.count : 0; 
       p.percentageGain = p.avgRate ? (p.opp_buy_rate/p.avgRate) * 100 : 0; 
       p.dollars -= +v["quantity_unpacked"]/2; 
       // p.max_date = v['received_week'].max(); 
       return p; 
      }, 
      function() { 
       return {quantity_unpacked: 0, 
         count: 0, 
         units: 0, 
         opp_buy_rate: 0, 
         sumRate: 0, 
         avgRate: 0, 
         percentageGain: 0, 
         dollars: 0, 
         test: 0 
       };//, dollars: 0} 
      } 
); 

和我的圖表:

country_bubble 
    .width(990) 
    .height(250) 
    .margins({top:10, right: 50, bottom: 30, left:80}) 
    .dimension(country_dim) 
    .group(statsByCountry) 
    .keyAccessor(function (p) { 
     return p.value.units; 
    }) 
    .valueAccessor(function (p) { //y alue 
     return p.value.percentageGain; 
    }) 
    .radiusValueAccessor(function (p) { //radius 
     return p.value.dollars/10000000; 
    }) 
    .maxBubbleRelativeSize(0.05) 
    .elasticX(true) 
    .elasticY(true)   
    .elasticRadius(true) 
    .x(d3.scale.linear()) 
    .y(d3.scale.linear()) 
    // .x(d3.scale.linear().domain([0, 1.2*bubble_xmax])) 
    // .y(d3.scale.linear().domain([0, 10000000])) 
    .r(d3.scale.linear().domain([0, 10])) 
    .yAxisPadding('25%') 
    .xAxisPadding('15%') 
    .renderHorizontalGridLines(true) 

    .renderVerticalGridLines(true)   

    .on('renderlet', function(chart, filter){ 
    chart.svg().select(".chart-body").attr("clip-path",null); 
}); 

原本以爲具有在statsbycountry類似於以下內容:

  if (v.received_day == date_min) { 
      p.start_value += v.my_quantity; 
      } 
      if (v.received_day == date_max) { 
      p.end_value += v.my_quantity; 
      } 

這似乎有點笨拙?但是如果我這樣做,我不認爲這會隨着其他過濾器的變化(比如時間或產品)而不斷更新? Ethan建議我使用假組,但我有點失落。

+0

Crossfilter不會是在這個計算中多好,所以它不是真的值得嘗試它楔進Crossfilter範例。進行過濾,然後用'dimension.top(Infinity)'提取當前過濾器中的數據,然後使用該數據計算一個時間範圍內的變化。如果你需要在dc.js圖表​​中顯示,那麼使用假組(https://github.com/dc-js/dc.js/wiki/FAQ#fake-groups) –

+0

@Ethan,這樣做你是否建議我不使用你對dimension.top(infinity)的建議?有什麼方法可以進行計算,以便在更改時間範圍時進行更新?對不起,我有點困惑和新奇。 – user3878014

+0

我說的是使用一個維度,但不是一個組。改用假組。對不起,我在這幾個星期沒有在線,所以希望其他人可以幫忙 –

回答

0

隨着工作小提琴,我們可以演示一種方法來做到這一點。我並不認爲這是最好的方法,但它是Crossfilter的方式。

首先,您需要使用您的自定義功能,減少維護所有數據組中的有序排列組的一部分:

var statsByCountry = country_dim.group().reduce(
    function(p, v) { 
    ++p.count; 
    p.units += +v["my_quantity"]; 
    p.country_rate = p.units/(1.0 * v['country_other_quantity']) //hopefully total sum of my_quantity divided by the fixed country_other_quantity for that week 
    p.percent_change = 50 //placeholder for now, ideally this would be the change in units over the timespan brush on the bottom chart 
    p.dollars += +v["my_revenue"]; 

    i = bisect(p.data, v, 0, p.data.length); 
    p.data.splice(i, 0, v); 
    return p; 
    }, 
    function(p, v) { 
    --p.count; 
    p.units -= +v["my_quantity"]; 
    p.country_rate = p.units/(1.0 * v['country_other_quantity']) //hopefully total sum of my_quantity divided by the fixed country_other_quantity for that week 
    p.percent_change = 50 //placeholder for now, ideally this would be the change in units over the timespan brush on the bottom chart 
    p.dollars -= +v["my_revenue"]; 

    i = bisect(p.data, v, 0, p.data.length); 
    p.data.splice(i, 1) 
    return p; 
    }, 
    function() { 
    return { 
     data: [], 
     count: 0, 
     units: 0, 
     country_rate: 0, 
     dollars: 0, 
     percent_change: 0 
    }; //, dollars: 0} 
    } 
); 

以上,我已經更新了你的減少功能來保持這種有序陣列(按received_week排序),屬於.data財產。它使用Crossfilter的bisect函數有效地維護訂單。

然後在你的valueAccessor您想根據這個數據值實際計算你的變化:

.valueAccessor(function(p) { //y alue 
    // Calculate change in units/day from first day to last day. 
    var firstDay = p.value.data[p.value.data.length-1].received_week.toString(); 
    var lastDay = p.value.data[0].received_week.toString(); 
    var firstDayUnits = d3.sum(p.value.data, function(d) { return d.received_week.toString() === firstDay ? d.my_quantity : 0 }) 
    var lastDayUnits = d3.sum(p.value.data, function(d) { return d.received_week.toString() === lastDay ? d.my_quantity : 0 }) 
    return lastDayUnits - firstDayUnits; 
    }) 

您在值存取做到這一點,因爲它只能每更換過濾器運行一次,而減少功能運行每個記錄添加或刪除一次,每個過濾器可能會有數千次。

如果你想計算%變化,你也可以在這裏做到這一點,但%計算的關鍵問題始終是「%是什麼?從你的問題來看,這個問題的答案並不清楚。

值得注意的是,採用這種方法,您的團隊結構將會變得非常大,因爲您將整個數據集存儲在組中。如果您在過濾時遇到性能問題,我仍然建議您不要採用這種方法,而應該採用基於假組的方法。

工作更新小提琴:https://jsfiddle.net/vysbxd1h/1/

+0

超級有用的Ethan!謝謝! 正如你所預料的那樣,我注意到使用這種方法的性能下降了(儘管作爲第一步仍然很高興)。將其轉化爲假組的建議?你也有任何建議調試valueAccessor和所有在控制檯,而不是從圖表中讀取它?更容易仔細檢查確切的數字。再次感謝! – user3878014

+0

它出現在我的全面儀表板上,數據數組排序不正確。嗯 – user3878014

+0

讓一切工作!再次感謝Ethan!在我的真實儀表板中,我必須在將參數傳遞給二等分函數時放置v.received_week。 快速的問題,雖然我很好奇,如果你能闡明一些。如果我在valueAccessor中放置了一個console.log(或者執行一步),它會在國家組中多次重複計算,然後纔會最終停止。例如console.log(p.key + firstDay)給出了類似於1firstDay,2firstDay,3firstDay 8次或更少的內容。這與我總共有多少周組有關係嗎? – user3878014