2013-09-16 40 views
20

Tôi đang xây dựng biểu đồ thanh D3 với thang thời gian trên trục x. Phạm vi của trục x có thể thay đổi.D3.js: tính chiều rộng của các thanh trong thang thời gian với phạm vi thay đổi?

Làm cách nào để chỉ định chiều rộng chính xác cho các thanh trên biểu đồ thanh? Tôi đã thấy mọi người sử dụng rangeBands cho thang đo thứ tự, nhưng tôi không chắc chắn cách thực hiện điều này với thang thời gian.

Đây là mã của tôi:

var x = d3.time.scale().range([0, width]); 
var xAxis = d3.svg.axis().scale(x).orient("bottom"); 
[...] 

// After new data has been fetched, update the domain of x... 
x.domain(d3.extent(data, function(d) { return d.date; })); 
d3.select(".x.axis").call(xAxis); 

// ... and draw bars for chart 
var bars = svg.selectAll(".air_used") 
    .data(data, function(d) { return d.date; }); 

bars.attr("y", function(d) { return y(d.air_used); }) 
.attr("height", function(d) { return height - y(d.air_used); }) 
.attr("x", function(d) { return x(d.date); }) 
.attr("width", 20) // ?? 

tôi nên đặt gì trên dòng cuối cùng, ở vị trí của 20?

UPDATE: JSFiddle đây: http://jsfiddle.net/aWJtJ/2/

Trả lời

28

Không có chức năng để có được chiều rộng, nhưng bạn có thể tính toán nó khá dễ dàng:

.attr("width", width/data.length); 

Bạn có thể muốn trừ một lượng nhỏ từ rằng nếu bạn don không muốn các thanh chạm vào. Bạn cũng cần phải điều chỉnh vị trí x tương ứng, ví dụ:

.attr("x", function(d) { return x(d.date) - (width/data.length)/2; }) 
.attr("width", width/data.length); 

Để có được ve để sắp xếp đúng cách, bạn cũng sẽ cần phải điều chỉnh phạm vi của quy mô trục x vì đánh dấu đầu tiên sẽ được đặt ở giá trị đầu tiên:

var x = d3.time.scale().range([width/data.length/2, width-width/data.length/2]); 

Hoàn jsfiddle here.

+1

Cảm ơn! Đề xuất đầu tiên gần như ở đó, nhưng hàm 'x' dường như chèn một số phần đệm giữa các thanh: http://jsfiddle.net/aWJtJ/1/ Nếu tôi thử sử dụng hàm thứ hai, một lần nữa, nó gần như đúng nhưng nó vẫn chèn đệm giữa các thanh: http://jsfiddle.net/aWJtJ/2/ - Tôi không hiểu tại sao hàm 'x' có thể cho biết mã của tôi ở đâu vị trí' x' của các thanh nên, nhưng không thể cho biết chiều rộng nên ở đâu, vì nó rõ ràng không có ý tưởng vững chắc về chiều rộng nên là gì. – Richard

+0

Ok, bạn cần thực sự nhiều hơn một chút - đặc biệt, bạn cần điều chỉnh phạm vi vì dấu tick đầu tiên sẽ có giá trị đầu tiên (xem câu trả lời đã chỉnh sửa). Đối với lý do tại sao D3 không cho bạn biết chiều rộng chính nó, tôi không có ý tưởng - bạn có thể mở một yêu cầu tính năng. –

+0

Tại sao tôi không thể upvote câu trả lời này hai lần? – AperioOculus

2

Vì quy mô thời gian liên tục, có thể có nhiều ý tưởng về độ rộng thanh "chính xác" là gì. Nếu các điểm dữ liệu của bạn phân bố rất chi tiết và không đều, bạn có thể muốn sử dụng một thanh mỏng có chiều rộng cố định để giảm thiểu các chồng chéo. Nếu bạn biết điều gì đó về giá trị dữ liệu dự kiến ​​của mình trước thời hạn và chúng là chi tiết thống nhất, bạn có thể làm điều gì đó giống như những gì @LarsKotthoff sắp xếp chúng ra một cách đồng đều.

Một điều cần xem xét là liệu những gì bạn thực sự muốn có quy mô thời gian hay không. Các thanh thường được sử dụng để biểu diễn các giá trị phân loại, không phải các điểm trên thang đo liên tục. Có thể một quy mô thứ tự với tên miền bắt nguồn từ phạm vi ngày thực sự muốn bạn muốn. Trong trường hợp đó, bạn có thể sử dụng rangeBands theo bài đăng gốc của mình.

Không nói việc làm những gì bạn đang làm là sai. Chỉ là thức ăn cho sự suy nghĩ.

+0

Cảm ơn! Tôi cần sử dụng thang thời gian vì tôi muốn sử dụng định dạng thời gian cho các dấu tích: https://github.com/mbostock/d3/wiki/Quantitative-Scales#wiki-linear_tickFormat – Richard

+2

Nếu định dạng là lý do duy nhất và bạn có số lượng các giá trị rời rạc đã biết bạn có thể định dạng ngày tháng thành chuỗi trước khi bạn chuyển chúng vào thang đo thứ tự. Tất nhiên, bạn cần phải cẩn thận để định dạng tất cả các dữ liệu ngày thành chuỗi theo cách giống hệt nhau trước khi mở rộng quy mô, có thể với một hàm getter được chia sẻ hoặc một cái gì đó. Hoặc chuyển đổi dữ liệu thành chuỗi sớm. Nó thực sự phụ thuộc vào việc bạn cần ngữ nghĩa của kiểu dữ liệu thời gian hay không. –

+0

BTW, bạn có thể sử dụng trình phân tích cú pháp và định dạng thời gian của D3 độc lập với mọi quy mô: https://github.com/mbostock/d3/wiki/Time-Formatting –

3

Mặc dù tôi đồng ý với Scott rằng biểu đồ thanh có nghĩa là được sử dụng với dữ liệu thứ tự hoặc phân loại trên trục x, tôi đoán câu hỏi là nhiều hơn về sự tiện lợi khi vẽ trục thời gian. Khi d3.time.scale thực hiện công việc thực sự tốt với sự giúp đỡ của d3.time.format.multi của trục thời gian vẽ có thời lượng khác nhau (giờ, ngày, tháng, v.v.) và các dấu tích của nó, có thể là một ý tưởng tốt để kết hợp d3.time.scale cho trục và d3.scale.ordinal cho một băng tần tính toán chiều rộng.

Đoạn mã dưới đây được lấy cảm hứng từ the discussion trong Nhóm Google D3 về chủ đề. Các đơn vị cho quy mô thứ tự là một ngày.

function prepare(data) 
 
{ 
 
    var dateParse = d3.time.format('%Y-%m-%d'); 
 
    data.forEach(function(v) 
 
    { 
 
    v.date = dateParse.parse(v.date); 
 
    }); 
 

 
    var dateValueMap = data.reduce(function(r, v) 
 
    { 
 
    r[v.date.toISOString()] = v.value; 
 
    return r; 
 
    }, {}); 
 

 
    var dateExtent = d3.extent(data.map(function(v) 
 
    { 
 
    return v.date; 
 
    })); 
 

 
    // make data have each date within the extent 
 
    var fullDayRange = d3.time.day.range(
 
    dateExtent[0], 
 
    d3.time.day.offset(dateExtent[1], 1) 
 
); 
 
    fullDayRange.forEach(function(date) 
 
    { 
 
    if(!(date.toISOString() in dateValueMap)) 
 
    { 
 
     data.push({ 
 
     'date' : date, 
 
     'value' : 0 
 
     }); 
 
    } 
 
    }); 
 

 
    data = data.sort(function(a, b) 
 
    { 
 
    return a.date - b.date; 
 
    }); 
 

 
    return data; 
 
} 
 

 
function draw(data) 
 
{ 
 
    var margin = { 
 
    'top' : 10, 
 
    'right' : 20, 
 
    'bottom' : 20, 
 
    'left' : 60 
 
    }; 
 
    var size = { 
 
    'width' : 600 - margin.left - margin.right, 
 
    'height' : 180 - margin.top - margin.bottom 
 
    }; 
 

 
    var svg = d3.select('#chart').append('svg') 
 
    .attr('width', '100%') 
 
    .attr('height', '100%') 
 
    .append('g') 
 
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); 
 

 
    var dates = data.map(function(v) 
 
    { 
 
    return v.date; 
 
    }); 
 
    var x = d3.time.scale() 
 
    .range([0, size.width]) 
 
    .domain(d3.extent(dates)); 
 

 
    var y = d3.scale.linear() 
 
    .range([size.height, 0]) 
 
    .domain([0, d3.max(data.map(function(v) 
 
    { 
 
     return v.value; 
 
    }))]); 
 

 
    var xAxis = d3.svg.axis() 
 
    .scale(x) 
 
    .orient('bottom'); 
 

 
    var yAxis = d3.svg.axis() 
 
    .scale(y) 
 
    .orient('left'); 
 

 
    var barWidth = d3.scale.ordinal() 
 
    .domain(dates) 
 
    .rangeRoundBands(x.range(), 0.1) 
 
    .rangeBand(); 
 

 
    svg.append('g') 
 
    .attr('class', 'x axis') 
 
    .attr('transform', 'translate(' + barWidth/2 + ',' + size.height + ')') 
 
    .call(xAxis); 
 

 
    svg.append('g') 
 
    .attr('class', 'y axis') 
 
    .call(yAxis) 
 
    .append('text') 
 
    .attr('transform', 'rotate(-90)') 
 
    .attr('y', 6) 
 
    .attr('dy', '.71em') 
 
    .style('text-anchor', 'end') 
 
    .text('Amount'); 
 

 
    svg.selectAll('.bar') 
 
    .data(data) 
 
    .enter() 
 
    .append('rect') 
 
    .attr('class', 'bar') 
 
    .attr('x', function(d) 
 
    { 
 
     return x(d.date); 
 
    }) 
 
    .attr('width', barWidth) 
 
    .attr('y', function(d) 
 
    { 
 
     return y(d.value); 
 
    }) 
 
    .attr('height', function(d) 
 
    { 
 
     return size.height - y(d.value); 
 
    }); 
 
} 
 

 
function getData() 
 
{ 
 
    return [ 
 
    {'date': '2014-01-31', 'value': 5261.38}, 
 
    {'date': '2014-02-02', 'value': 7460.23}, 
 
    {'date': '2014-02-03', 'value': 8553.39}, 
 
    {'date': '2014-02-04', 'value': 3897.18}, 
 
    {'date': '2014-02-05', 'value': 2822.22}, 
 
    {'date': '2014-02-06', 'value': 6762.49}, 
 
    {'date': '2014-02-07', 'value': 8624.56}, 
 
    {'date': '2014-02-08', 'value': 7870.35}, 
 
    {'date': '2014-02-09', 'value': 7991.43}, 
 
    {'date': '2014-02-10', 'value': 9947.14}, 
 
    {'date': '2014-02-11', 'value': 6539.75}, 
 
    {'date': '2014-02-12', 'value': 2487.3}, 
 
    {'date': '2014-02-15', 'value': 3517.38}, 
 
    {'date': '2014-02-16', 'value': 1919.08}, 
 
    {'date': '2014-02-19', 'value': 1764.8}, 
 
    {'date': '2014-02-20', 'value': 5607.57}, 
 
    {'date': '2014-02-21', 'value': 7148.87}, 
 
    {'date': '2014-02-22', 'value': 5496.45}, 
 
    {'date': '2014-02-23', 'value': 296.89}, 
 
    {'date': '2014-02-24', 'value': 1578.59}, 
 
    {'date': '2014-02-26', 'value': 1763.16}, 
 
    {'date': '2014-02-27', 'value': 8622.26}, 
 
    {'date': '2014-02-28', 'value': 7298.99}, 
 
    {'date': '2014-03-01', 'value': 3014.06}, 
 
    {'date': '2014-03-05', 'value': 6971.12}, 
 
    {'date': '2014-03-06', 'value': 2949.03}, 
 
    {'date': '2014-03-07', 'value': 8512.96}, 
 
    {'date': '2014-03-09', 'value': 7734.72}, 
 
    {'date': '2014-03-10', 'value': 6703.21}, 
 
    {'date': '2014-03-11', 'value': 9798.07}, 
 
    {'date': '2014-03-12', 'value': 6541.8}, 
 
    {'date': '2014-03-13', 'value': 915.44}, 
 
    {'date': '2014-03-14', 'value': 9570.82}, 
 
    {'date': '2014-03-16', 'value': 6459.17}, 
 
    {'date': '2014-03-17', 'value': 9389.62}, 
 
    {'date': '2014-03-18', 'value': 6216.9}, 
 
    {'date': '2014-03-19', 'value': 4433.5}, 
 
    {'date': '2014-03-20', 'value': 9017.23}, 
 
    {'date': '2014-03-23', 'value': 2828.45}, 
 
    {'date': '2014-03-24', 'value': 63.29}, 
 
    {'date': '2014-03-25', 'value': 3855.02}, 
 
    {'date': '2014-03-26', 'value': 4203.06}, 
 
    {'date': '2014-03-27', 'value': 3132.32} 
 
    ]; 
 
} 
 

 
draw(prepare(getData()));
#chart { 
 
    width : 600px; 
 
    height : 180px; 
 
} 
 
.bar { 
 
    fill : steelblue; 
 
} 
 

 
.axis { 
 
    font : 10px sans-serif; 
 
} 
 

 
.axis path, 
 
.axis line { 
 
    fill   : none; 
 
    stroke   : #000; 
 
    shape-rendering : crispEdges; 
 
} 
 

 
.x.axis path { 
 
    display : none; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<div id='chart'></div>

2

Tôi chạy vào vấn đề này khi thay đổi mức thu phóng trên một biểu đồ thanh với một chuỗi thời gian dựa trục x và tìm thấy hai giải pháp..

1) Tạo một người bạn đồng quy mô thứ để giúp tính toán độ rộng (một nỗi đau)

2) Các chuỗi thời gian trục x là bạn của bạn - sử dụng nó để tính toán chiều rộng.

Nếu bạn muốn thanh của mình luôn là "tháng" rộng, bất kể mức thu phóng trong bạn có thể làm điều gì đó như thế này. Im giả sử d.date có sẵn trong dữ liệu.

svg.selectAll(".air_used").attr("width", function(d.date) { 
    var next = d3.time.month.offset(d.date, 1); 
    return (x(next)- x(d)); 
    }); 

này hoạt động tốt vì nó hoạt động cho mọi quy mô zoom, và bạn chỉ cần gọi chức năng này ở phần cuối của bạn trên "zoom" xử lý tức là .Trên ("zoom", thu nhỏ); Trục x thường sẽ được điều chỉnh tại thời điểm này.