2012-07-26 9 views
12

उन सभी डी 3 ट्यूटोरियल जिन्हें मैंने ऑब्जेक्ट्स के सरणी में व्यवस्थित डेटा का उपयोग किया है, जिससे वे सरणी में प्रत्येक ऑब्जेक्ट के लिए एक बिंदु ग्राफ़ करते हैं। निम्नलिखित संरचना में देखते हुए डेटा:क्या d3.js एक ही स्रोत से डेटा का उपयोग कर एक ही ग्राफ पर दो स्कैटरप्लॉट खींच सकता है?

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

x और y मान एक scatterplot बनाने के लिए उपयोग किया जाता है। टाइप पैरामीटर का उपयोग प्रत्येक बिंदु के रंग को बदलने के लिए किया जाता है। उदाहरण के लिए यह jsfiddle देखें: http://jsfiddle.net/uxbHv/

असुविधाजनक रूप से, मेरे पास एक अलग डेटा संरचना है और मैं यह समझ नहीं सकता कि प्रत्येक ऑब्जेक्ट के लिए दो डेटा बिंदुओं को चित्रित करके एक ही ग्राफ को कैसे बनाया जाए। यहाँ कुछ उदाहरण डेटा है:

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} 
] 

मैं yVar1 या yVar2 के खिलाफ अलग-अलग xVar ग्राफ़ बना सकते हैं, लेकिन मैं समझ नहीं ही ग्राफ पर दोनों पाने के लिए: प्रत्येक डेटा के लिए http://jsfiddle.net/634QG/

उत्तर

30

data-join का उपयोग करते समय सामान्य नियम यह है कि आप डेटा से तत्वों के लिए एक-से-एक मैपिंग चाहते हैं। इसलिए, यदि आपके स्कैटरप्लॉट में दो श्रृंखलाएं हैं, तो आपको श्रृंखला का प्रतिनिधित्व करने के लिए दो कंटेनर तत्व (जैसे G elements) चाहिए। चूंकि आपके पास वर्तमान में केवल data सरणी है, इसलिए आप डेटा प्रतिनिधित्व को दो समानांतर सरणी में समान प्रतिनिधित्व के साथ परिवर्तित करने के लिए array.map का उपयोग भी करना चाहेंगे। इस तरह, आपको प्रत्येक श्रृंखला के लिए कोड डुप्लिकेट करने की आवश्यकता नहीं है।

मान लें कि आपका डेटा एक CSV में प्रस्तुत किया गया था के लिए एक्स -values ​​के लिए एक स्तंभ, और कई अन्य कॉलम के साथ फाइल y प्रत्येक श्रृंखला के -values:

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

यदि आप चाहते हैं कोड पूरी तरह से सामान्य होने के लिए, आपको पहले श्रृंखला के नामों की गणना करने की आवश्यकता है, जैसे कि ["y1", "y2"]। (यदि आपने CSV फ़ाइल में तीसरा कॉलम जोड़ा है, तो यह ["y1", "y2", "y3"] हो सकता है।) आप d3.keys का उपयोग करके नामों की गणना कर सकते हैं, जो ऑब्जेक्ट से नामित गुणों को निकालते हैं। उदाहरण के लिए, d3.keys({foo: 1, bar: 2})["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(); 

अब आपके पास श्रृंखला के नाम हैं, तो आप अंक के सरणी बना सकते हैं। बाहरी सरणी श्रृंखला का प्रतिनिधित्व करती है (जिसमें से दो हैं) और आंतरिक सरणी डेटा बिंदुओं को संग्रहित करती हैं। आप अंक को लगातार प्रतिनिधित्व (x और y गुणों के साथ ऑब्जेक्ट्स) में परिवर्तित कर सकते हैं, जिससे आप श्रृंखला में कोड का पुन: उपयोग कर सकते हैं।

// 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]}; 
    }); 
}); 

नोट इस कोड नंबरों के लिए सीएसवी मूल्यों मजबूर करने के लिए + ऑपरेटर का उपयोग करता है। (सीएसवी फाइलें अनियमित हैं, इसलिए वे शुरुआत में तार हैं।)

अब जब आपने अपने डेटा को नियमित प्रारूप में मैप किया है, तो आप प्रत्येक श्रृंखला के लिए जी तत्व बना सकते हैं, और फिर प्रत्येक बिंदु के लिए तत्वों को सर्कल कर सकते हैं। परिणामी SVG संरचना इस तरह दिखेगा:

<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> 

और इसी डी 3 कोड:

// 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); }); 

मैं भी एक भरने शैली जोड़कर प्रत्येक श्रृंखला के लिए एक अनूठा रंग आवंटित करने के लिए कोड का एक सा जोड़ दिया है युक्त जी तत्व के लिए। निश्चित रूप से ऐसा करने के कई तरीके हैं। (उदाहरण के लिए, आप प्रत्येक श्रृंखला के लिए रंग के बारे में और अधिक विशिष्ट होना चाहते हैं।) मैंने कोड को भी छोड़ दिया है जो आपके x और y स्केल (साथ ही अक्षों को प्रतिपादित करने) के डोमेन की गणना करता है, लेकिन आप पूरे काम कर उदाहरण देखना चाहते हैं:

+0

लगभग एक ट्यूटोरियल ... माइक Bostock द्वारा इस तरह के लंबे उत्तरों के माध्यम से उत्तर दिया गया था और अन्य समान प्रश्नों को FAQ में रखा जाना चाहिए – paxRoman

4

प्लेस दो हलकों एक svg:g तत्व में इंगित करें। यह तत्वों के लिए डेटा के लिए एक-से-एक मैपिंग उत्पन्न करता है लेकिन फिर भी आपको दो अलग-अलग बिंदु दिखाने की अनुमति देता है।

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"); 

कार्य JSFiddle

+0

समाधान के लिए धन्यवाद! मैं देखता हूं कि यह स्थिर डेटा के साथ कैसे काम करता है, लेकिन अब मैं सोच रहा हूं कि संक्रमण और निकास जोड़ना मुश्किल होगा .... वापस jsfiddle ... –

+0

यह काम कर सकता है (अब), लेकिन मैं दृढ़ता से इस पैटर्न को हतोत्साहित करता हूं- एपीआई इस तरह काम करने के लिए डिज़ाइन नहीं किया गया है। डेटा-इन-इन का उपयोग करते समय, आपके पास डेटा और तत्वों के बीच हमेशा एक-एक-एक मैपिंग होना चाहिए। प्रविष्टि चयन में दो बार सम्मिलित करना या जोड़ना एपीआई का दुरुपयोग है। इस मामले में सही समाधान दो 'vis' तत्वों को बनाना है, और लगातार प्रतिनिधित्व के साथ डेटा को दो समांतर सरणी में मैप करना है। – mbostock

+0

@mbostock क्या यह एक बेहतर तरीका है? http://jsfiddle.net/634QG/3/ –

संबंधित मुद्दे