2012-07-26 37 views
12

Tất cả các hướng dẫn d3 tôi đã tìm thấy dữ liệu được sắp xếp trong mảng các đối tượng mà từ đó chúng vẽ đồ thị một điểm cho mỗi đối tượng trong mảng. Dữ liệu đã cho trong cấu trúc sau:Có thể d3.js vẽ hai ô phân tán trên cùng một biểu đồ bằng cách sử dụng dữ liệu từ cùng một nguồn không?

data = [ 
    {id: 1, x: 4, y: 10, type: 1}, 
    {id: 2, x: 5, y: 20, type: 2} 
    ... 
] 

Giá trị x và y được sử dụng để tạo phân tán. Tham số kiểu được sử dụng để thay đổi màu của mỗi điểm. Xem jsfiddle này để biết ví dụ: http://jsfiddle.net/uxbHv/

Không may, tôi có cấu trúc dữ liệu khác và tôi không thể tìm ra cách tạo cùng một biểu đồ bằng cách vẽ hai điểm dữ liệu cho từng đối tượng. Dưới đây là một số ví dụ dữ liệu:

dataSet = [ 
    {xVar: 5, yVar1: 90, yVar2: 22}, 
    {xVar: 25, yVar1: 30, yVar2: 25}, 
    {xVar: 45, yVar1: 50, yVar2: 80}, 
    {xVar: 65, yVar1: 55, yVar2: 9}, 
    {xVar: 85, yVar1: 25, yVar2: 95} 
] 

tôi có thể vẽ đồ thị xVar cá nhân chống lại yVar1 hoặc yVar2, nhưng tôi không thể tìm ra cách để có được cả hai trên cùng một đồ thị: http://jsfiddle.net/634QG/

Trả lời

30

Quy tắc chung khi sử dụng data-join là bạn muốn ánh xạ một-một từ dữ liệu đến các thành phần. Vì vậy, nếu bạn có hai chuỗi trong phân tán của mình, bạn sẽ muốn có hai phần tử vùng chứa (chẳng hạn như G elements) để đại diện cho chuỗi. Vì hiện tại bạn chỉ có một mảng data, bạn cũng sẽ muốn sử dụng array.map để chuyển đổi đại diện dữ liệu thành hai mảng song song có cùng biểu diễn. Bằng cách này, bạn không phải lặp lại mã cho mỗi chuỗi.

Giả sử dữ liệu của bạn đã được đại diện trong một tập tin CSV với một cột cho x -values, và nhiều cột khác cho y -values ​​của mỗi series:

x,y1,y2 
5,90,22 
25,30,25 
45,50,80 
65,55,9 
85,25,95 

Nếu bạn muốn mã hoàn toàn chung chung, trước tiên bạn cần phải tính toán tên của chuỗi, chẳng hạn như ["y1", "y2"]. (Nếu bạn đã thêm cột thứ ba vào tệp CSV, có thể là ["y1", "y2", "y3"].) Bạn có thể tính toán tên bằng cách sử dụng d3.keys, trích xuất các thuộc tính được đặt tên từ một đối tượng. Ví dụ: d3.keys({foo: 1, bar: 2}) trả lại ["foo", "bar"].

// Compute the series names ("y1", "y2", etc.) from the loaded CSV. 
var seriesNames = d3.keys(data[0]) 
    .filter(function(d) { return d !== "x"; }) 
    .sort(); 

Bây giờ bạn có tên chuỗi, bạn có thể tạo một mảng các mảng điểm. Mảng bên ngoài đại diện cho chuỗi (trong đó có hai) và mảng bên trong lưu trữ các điểm dữ liệu. Bạn có thể đồng thời chuyển đổi các điểm thành một biểu diễn nhất quán (các đối tượng có các thuộc tính xy), cho phép bạn sử dụng lại mã qua hàng loạt.

// Map the data to an array of arrays of {x, y} tuples. 
var series = seriesNames.map(function(series) { 
    return data.map(function(d) { 
    return {x: +d.x, y: +d[series]}; 
    }); 
}); 

Lưu ý mã này sử dụng toán tử + để ép buộc giá trị CSV thành số. (Tệp CSV không được nhập, vì vậy ban đầu chúng là chuỗi.)

Bây giờ bạn đã ánh xạ dữ liệu của mình theo định dạng thông thường, bạn có thể tạo các phần tử G cho mỗi chuỗi và sau đó khoanh tròn các phần tử bên trong cho từng điểm. Kết quả là cấu trúc SVG sẽ trông như thế này:

<g class="series"> 
    <circle class="point" r="4.5" cx="1" cy="2"/> 
    <circle class="point" r="4.5" cx="3" cy="2"/> 
    … 
</g> 
<g class="series"> 
    <circle class="point" r="4.5" cx="5" cy="4"/> 
    <circle class="point" r="4.5" cx="7" cy="6"/> 
    … 
</g> 

Và mã D3 tương ứng:

// Add the points! 
svg.selectAll(".series") 
    .data(series) 
    .enter().append("g") 
    .attr("class", "series") 
    .style("fill", function(d, i) { return z(i); }) 
    .selectAll(".point") 
    .data(function(d) { return d; }) 
    .enter().append("circle") 
    .attr("class", "point") 
    .attr("r", 4.5) 
    .attr("cx", function(d) { return x(d.x); }) 
    .attr("cy", function(d) { return y(d.y); }); 

tôi tôi cũng đã thêm một chút mã để gán từng loạt một màu độc đáo bằng cách thêm một phong cách điền với phần tử G chứa. Có rất nhiều cách khác nhau để làm điều này, tất nhiên. (Ví dụ: bạn có thể muốn cụ thể hơn về màu sắc cho mỗi chuỗi). Tôi cũng đã để lại mã tính toán các miền của các quy mô xy của bạn (cũng như hiển thị các trục), nhưng nếu bạn muốn xem toàn bộ ví dụ làm việc:

+0

gần như một hướng dẫn ... điều này và các câu hỏi tương tự khác đã được trả lời thông qua câu trả lời dài như vậy bởi Mike Bostock nên được đưa vào Câu hỏi thường gặp – paxRoman

4

Đặt hai vòng tròn cho mỗi dữ liệu chỉ vào một thành phần svg:g. Điều này tạo ra một ánh xạ một-một cho dữ liệu cho các phần tử nhưng vẫn cho phép bạn hiển thị hai điểm khác nhau.

var nodeEnter = vis1.selectAll("circle") 
     .data(dataSet) 
     .enter() 
     .insert("svg:g"); 

nodeEnter.insert("svg:circle") 
      .attr("cx", function (d) { return 100 - d.xVar}) 
      .attr("cy", function (d) { return 100 - d.yVar1}) 
      .attr("r", 2) 
      .style("fill", "green"); 

nodeEnter.insert("svg:circle") 
      .attr("cx", function (d) { return 100 - d.xVar}) 
      .attr("cy", function (d) { return 100 - d.yVar2}) 
      .attr("r", 2) 
      .style("fill", "blue"); 

Làm việc JSFiddle.

+0

Cảm ơn các giải pháp! Tôi thấy cách làm việc này với dữ liệu tĩnh, nhưng bây giờ tôi tự hỏi nếu nó sẽ khó khăn để thêm chuyển tiếp và thoát .... trở lại jsfiddle ... –

+0

Điều này có thể làm việc (bây giờ), nhưng tôi sẽ mạnh mẽ ngăn cản mô hình này— API không được thiết kế để hoạt động theo cách này. Khi sử dụng kết nối dữ liệu, bạn nên luôn có một ánh xạ một-một giữa dữ liệu và các phần tử. Chèn hoặc thêm vào lựa chọn nhập hai lần là lạm dụng API. Giải pháp đúng trong trường hợp này là tạo hai phần tử 'vis' và ánh xạ dữ liệu thành hai mảng song song với một biểu diễn nhất quán. – mbostock

+0

@mbostock Đây có phải là cách tiếp cận tốt hơn không? http://jsfiddle.net/634QG/3/ –

Các vấn đề liên quan