2017-10-13 62 views
1

有沒有辦法讓平行座標一直走到邊緣,然後等距離地將這些「尺寸」隔開?我認爲x = d3.scaleBand().rangeRound([0, width]).padding(1)是這裏的罪魁禍首,但似乎還有其他東西在起作用。如果有人幫我在這裏,我會很感激。如何使用帶標將圖表推向邊緣

enter image description here

var margin = {top: 20, right: 10, bottom: 10, left: 20}, 
        width = d3.select(ele[0])._groups[0][0].offsetWidth - margin.left - margin.right, 
        height = 400 - margin.top - margin.bottom; 

       // set the height based on the calculations above 
       svg.attr('height', height + margin.top + margin.bottom); 

       var x = d3.scaleBand().rangeRound([0, width]).padding(1), 
        y = {}, 
        dragging = {}, 
        selected; 

       var line = d3.line(), 
        background, 
        foreground, 
        extents; 

       x.domain(dimensions = d3.keys(data[0]).filter(function(d) { 
        if(d === "name") { 
         return false; 
        } 
        return y[d] = d3.scaleLinear() 
         .domain(d3.extent(data, function(p) { 
          return +p[d]; })) 
         .range([height, 0]); 
       })); 

       extents = dimensions.map(function(p) { return [0,0]; }); 

       // Add grey background lines for context. 
       background = svg.append("g") 
        .attr("class", "background") 
        .attr("transform", "translate(0," + margin.top + ")") 
        .selectAll("path") 
        .data(data) 
        .enter().append("path") 
        .attr("d", path); 

       // Add blue foreground lines for focus. 
       foreground = svg.append("g") 
        .attr("class", "foreground") 
        .attr("transform", "translate(0," + margin.top + ")") 
        .selectAll("path") 
        .data(data) 
        .enter().append("path") 
        .attr("d", path); 

       // Add a group element for each dimension. 
       var g = svg.selectAll(".dimension") 
        .data(dimensions) 
        .enter().append("g") 
        .attr("class", "dimension") 
        .attr("transform", function(d) { return "translate(" + x(d) + ")"; }) 
        .call(d3.drag() 
         .subject(function(d) { return {x: x(d)}; }) 
         .on("start", function(d) { 
          dragging[d] = x(d); 
          background.attr("visibility", "hidden"); 
         }) 
         .on("drag", function(d) { 
          dragging[d] = Math.min(width, Math.max(0, d3.event.x)); 
          foreground.attr("d", path); 
          dimensions.sort(function(a, b) { return position(a) - position(b); }); 
          x.domain(dimensions); 
          g.attr("transform", function(d) { return "translate(" + position(d) + ")"; }) 
         }) 
         .on("end", function(d) { 
          delete dragging[d]; 
          transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")"); 
          transition(foreground).attr("d", path); 
          background 
           .attr("d", path) 
           .transition() 
           .delay(500) 
           .duration(0) 
           .attr("visibility", null); 
         })); 

       // Add an axis and title. 
       g.append("g") 
        .attr("class", "y axis") 
        .each(function(d) { d3.select(this).call(d3.axisLeft(y[d]));}) 
        .attr("transform", "translate(0," + margin.top + ")") 
        .append("text") 
        .style("text-anchor", "middle") 
        .attr("y", -9) 
        .text(function(d) { return d; }); 

       // Add and store a brush for each axis. 
       g.append("g") 
        .attr("class", "brush") 
        .attr("transform", "translate(0," + margin.top + ")") 
        .each(function(d) { 
         d3.select(this) 
          .call(y[d].brush = d3.brushY().extent([[-8, 0], [8,height]]) 
           .on("brush start", brushstart) 
           .on("brush", brush_parallel_chart)); 
        }) 
        .selectAll("rect") 
        .attr("x", -8) 
        .attr("width", 16); 

       function position(d) { 
        var v = dragging[d]; 
        return v === null || v === undefined ? x(d) : v; 
       } 

       function transition(g) { 
        return g.transition().duration(500); 
       } 

       // Returns the path for a given data point. 
       function path(d) { 
        return line(dimensions.map(function(p) { 
         return [position(p), y[p](d[p])]; 
        })); 
       } 

       function brushstart() { 
        d3.event.sourceEvent.stopPropagation(); 
       } 

       function within(d, extent){ 
        return extent[1] <= d && d <= extent[0]; 
       } 

       // Handles a brush event, toggling the display of foreground lines. 
       function brush_parallel_chart() { 
        for(var i = 0; i < dimensions.length; ++i){ 
         if(d3.event.target === y[dimensions[i]].brush) { 
          extents[i] = d3.event.selection.map(y[dimensions[i]].invert,y[dimensions[i]]); 
         } 
        } 

        foreground.style("display", function(d) { 
         return dimensions.every(function(p, i) { 
          if(extents[i][0]===0 && extents[i][1]===0) { 
           return true; 
          } 
          return within(d[p], extents[i]) 
         }) ? null : "none"; 
        }); 

        selected = data.filter(function(item){ 
         if(dimensions.every(function(dim, index){ 
           if(extents[index][0]===0 && extents[index][1]===0) { 
            return true; 
           } 
           return within(item[dim], extents[index]); 
          })){ 
          return true; 
         } 
        }); 

        scope.onBrush({item: selected}); 
       } 

回答

1

對於這個dataviz,band scale是錯誤的工具。

問題是,一個頻段有相關的帶寬。所以,當你使用一個帶刻度,它會返回各自帶寬的左緣(或,換句話說,「臺階」的左邊空白處):

enter image description here

看看返回的值通過使用5個值和範圍會從0100一個帶刻度:

var data = "abcde".split("") 
 
var s = d3.scaleBand() 
 
    .domain(data) 
 
    .range([0, 100]); 
 

 
data.forEach(function(d) { 
 
    console.log(s(d)) 
 
})
<script src="https://d3js.org/d3.v4.min.js"></script>

如果設置填充爲1它將幫助減少帶寬,但並不多(更多的下面):

var data = "abcde".split("") 
 
var s = d3.scaleBand() 
 
    .domain(data) 
 
    .range([0, 100]) 
 
    .padding(1); 
 

 
data.forEach(function(d) { 
 
    console.log(s(d)) 
 
})
<script src="https://d3js.org/d3.v4.min.js"></script>

解決方案

使用一個點,而不是規模。點秤有沒有帶寬:

enter image description here

這裏是演示,看看值:

var data = "abcde".split("") 
 
var s = d3.scalePoint() 
 
    .domain(data) 
 
    .range([0, 100]); 
 

 
data.forEach(function(d) { 
 
    console.log(s(d)) 
 
})
<script src="https://d3js.org/d3.v4.min.js"></script>

關於填充,因爲我說上「更多低於「:你差不多了!問題是padding是...

...一種方便的方法,用於將內部和外部填充設置爲相同的填充值。

因此,它設置內部填充和外部填充。如果您使用paddingInner(1)paddingOuter(0)你會得到一個點規模相同的結果:

var data = "abcde".split("") 
 
var s = d3.scaleBand() 
 
    .domain(data) 
 
    .range([0, 100]) 
 
    .paddingInner(1) 
 
    .paddingOuter(0); 
 

 
data.forEach(function(d) { 
 
    console.log(s(d)) 
 
})
<script src="https://d3js.org/d3.v4.min.js"></script>

+1

這太棒了。這很有道理。我知道它必須是樂隊的東西。我最初的直覺是將'outerPadding()'設置爲0,但沒有設置'innerPadding()',它仍然不正確。謝謝你澄清! – konrad

-2

嘗試改變margin到:第一行

var margin = {top: 20, right: 0, bottom: 10, left: 0}, 

。餘量是圖表周圍有多少空間。這也應該更改width的值,該值在其聲明中使用了邊距。由於寬度將增加,x尺度應該改變,使得「尺寸」等間距但彼此進一步分開。

+0

正如你可以看到我的當前設置,右邊距爲10px的。這幾乎沒有接近正在應用的偏移量。 SVG對象的寬度設置爲100%,因此它是它所在的div框架的大小。「width」值設置爲div的寬度 - margin.left - margin.right,因此它只小了30px,但我的圖表並沒有佔用太多的空間。我猜問題是RangeRound()以及間隔如何設置,或者尺寸如何偏移。 – konrad