2013-02-05 71 views
22

我正在使用d3.stack創建堆積區域圖表,但如果我沒有相同數量的項目每層。我開始與數據陣列是這樣的:如何在d3.layout.stack()中處理丟失數據點的圖層()

[ 
    {key:'Group1',value,date}, 
    {key:'Group1',value,date}, 
    {key:'Group1',value,date}, 
    {key:'Group2',value,date}, 
    {key:'Group2',value,date} 
] 

和後我運行它通過巢()和堆()我結束了這種格式,如所預期:

[ 
    {key: 'Group1', 
    values: [ {key,value,date}, {key,value,date}, {key,value,date} ] }, 
    {key: 'Group2', 
    values: [ {key,value,date}, {key,value,date} ] } 
] 

我已經稍微修改了堆疊區域示例以演示此jsFiddle中的問題:http://jsfiddle.net/brentkeller/rTC3c/2/

如果您刪除sourceData數組中的任何一個數據點,您將看到錯誤消息「Can not read property'1 of undefined」在控制檯。

有沒有辦法讓d3.stack只是假設零值爲缺失的數據點?如果沒有,是否有一個優雅的解決方案來填補缺失的值?

回答

17

這不是d3特有的,而是填補鍵控數據陣列中的空白的一般解決方案。我修改你的jsfiddle here具有以下功能:

function assignDefaultValues(dataset) 
{ 
    var defaultValue = 0; 
    var keys = [ 'Group1' , 'Group2', 'Group3' ]; 
    var hadData = [ true, true, true]; 
    var newData = []; 
    var previousdate = new Date(); 
    var sortByDate = function(a,b){ return a.date > b.date ? 1 : -1; }; 

    dataset.sort(sortByDate); 
    dataset.forEach(function(row){ 
     if(row.date.valueOf() !== previousdate.valueOf()){ 
      for(var i = 0 ; i < keys.length ; ++i){ 
       if(hadData[i] === false){ 
        newData.push({ key: keys[i], 
            value: defaultValue, 
            date: previousdate }); 
       } 
       hadData[i] = false; 
      } 
      previousdate = row.date; 
     } 
     hadData[keys.indexOf(row.key)] = true; 
    }); 
    for(i = 0 ; i < keys.length ; ++i){ 
     if(hadData[i] === false){ 
      newData.push({ key: keys[i], value: defaultValue, 
          date: previousdate }); 
     } 
    } 
    return dataset.concat(newData).sort(sortByDate); 
} 

它遍歷給出的數據集和,每當遇到一個新的date值,指定一個默認值到尚未看到任何keys

+0

默認值可能無濟於事。這將使堆棧圖具有任意驟降(降至默認值,可能爲0)。 –

+0

除原始請求外:「有沒有辦法讓d3.stack只是假設零值用於缺失的數據點?」 –

+0

謝謝@ChrisG。我決定採用這種方法。我稍微編輯了你的答案,以使鍵和hadData數組更加靈活。感謝您的幫助! –

5

堆棧確實如此,堆疊圖形,因此您作爲用戶負責以正確的格式提供數據。如果你仔細想一想,這是有道理的,因爲堆棧基本上是數據格式不可知的。它提供了很大的靈活性,唯一的限制是每層可以訪問相同數量的點。它將如何確定哪些點缺失?鑑於第一層有五個點,第二層有十個點,第一層是否缺少五個點?或者都是圖層缺失點,因爲第三個圖層包含更多點。然後,如果缺少點,哪些?一開始,最後,在中間的某個地方?再次,沒有明智的方法來實現堆棧實現(除非它會強制非常嚴格的數據結構)。

所以,但你有什麼可以做的嗎?我想你可以。我不能給你一個完整的實現,但可以給你一些正確方向的指針。我們從這裏開始:

var stack = d3.layout.stack() 
    .offset("zero") 
    .values(function(d) { return d.values; }) 

在這裏你可以返回值,這在你的榜樣將是鳥巢運營的結果。所以在這一點上,你有能力「修復」這些值。

您需要做的第一件事就是確定最大觀察次數。

var nested = nest.entries(data); 
var max = nested.reduce(function(prev, cur) { 
    return Math.max(prev, cur.values.length); 
}, 0); 

現在棘手的部分。一旦知道了元素的最大數量,您需要調整傳遞給值的函數 。在這裏你必須對數據做出假設。 從你的問題我明白,對於一些團體的價值觀缺失。所以有兩個 的可能性。您或者假定具有最大元素數的組包含該範圍內的所有項目,或者您假定某個範圍並檢查所有組,如果他們的 包含您範圍內每個「打勾」的值。因此,如果您的範圍是一個日期範圍(例如您的 示例),並且您期望每天(或該事件的間隔時間)進行一次測量,那麼您必須在羣組中行走項目並自行填補空白。我會盡力給出一個(未經測試),例如,對於數值範圍:

// define some calculated values that can be reused in correctedValues 
var range = [0, 1]; 
var step = 0.1; 

function correctedValues(d) { 
    var values = d.values; 
    var result = []; 
    var expected = 0; 
    for (var i = 0; i < values.length; ++i) { 
    var value = values[i]; 
    // Add null-entries 
    while (value.x > expected) { 
     result.push({x: expected, otherproperties_you_need... }); 
     expected += step; 
    } 
    result.push(value); // Now add the real data point. 
    expected = value.x; 
    } 

    // Fill up the end of of the array if needed 
    while(expected < range[1]) { 
    result.push({x: expected, otherproperties_you_need... }); 
    expected += step; 
    } 
    return result; 
} 

// Now use our costom function for the stack 
var stack = d3.layout.stack() 
.offset("zero") 
.values(correctedValues) 
... 

至於說,這部分是未經測試並不能直接解決您的問題(因爲我使用一個數值範圍),但我認爲這應該給你一個關於如何解決你的問題的想法(以及你的問題的實際來源是什麼)。

1

正如其他人已經解釋的那樣,疊加圖表猜測每個數據點的缺失值是不合理的,因爲插值的方法有很多,並且沒有明顯的選擇。

但是,d3.svg.line()似乎爲您提供了一種合理的方法來選擇自己的插值方法並填寫缺失值。雖然它是爲生成SVG路徑而設計的,但您也可以將其用於一般定義線條。插值方法在這裏建議:

https://github.com/mbostock/d3/wiki/SVG-Shapes#wiki-line_interpolate

這是不幸的是,類,現在,擁有所有這些美好的插值方法(沒有其他地方出現在D3),但僅限於生成SVG路徑數據,而不是的任意中間值。也許如果@mbostock看到這一點,他會考慮推廣功能。

但是,現在你可能只是想使D3叉子和之前被寫入SVG path串,在做插值,低於source的部分採取的line(data)中間結果:

function line(data) { 
    var segments = [], 
     points = [], 
     i = -1, 
     n = data.length, 
     d, 
     fx = d3_functor(x), 
     fy = d3_functor(y); 

    function segment() { 
     segments.push("M", interpolate(projection(points), tension)); 
    } 

    while (++i < n) { 
     if (defined.call(this, d = data[i], i)) { 
     points.push([+fx.call(this, d, i), +fy.call(this, d, i)]); 
     } else if (points.length) { 
     segment(); 
     points = []; 
     } 
    } 

    if (points.length) segment(); 

    return segments.length ? segments.join("") : null; 
    } 
+0

+1用於查找D3件的非標準用途。我一直在使用比例尺.. –