D3 Drawing Circles Proportional to Data
Basic D3 Graphs
Scales, Axes, and Scatterplots
In this section, nosotros'll take what we've learned to create a scatterplot that displays iv variables. Along the way nosotros'll talk over .scales() and .centrality()
<!DOCTYPE html> <html> <caput> <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script> <style type="text/css"> .axis path, .axis line { fill: none; stroke: black; shape-rendering: crispEdges; } .axis text { font-family: sans-serif; font-size: 11px; } </way> </head> <body> <div id="scatterplot"> <h2 style = "text-align:center">G/F Life Expectancy & Median Income By Country</h2> </div> <script type="text/javascript"> var xMaleLE = [55, 70, 65, sixty, 70, 67, 70, 80]; var yFemaleLE = [57, 58, 55, 57, 62, 75, 83, 85]; var rMedianIncome =[4800, 4900, 5200, 10000, 15000, 20000, 25000, 27000 ]; var tCountry = ["North Korea", "Ethiopia", "Vietnam", "South Africa", "Italy", "French republic","Great britain", "The states"]; var cCountry = ["rgb(127, 201, 127)","rgb(190, 174, 212)","rgb(253, 192, 134)", "rgb(255, 255, 153)", "rgb(56, 108, 176)", "rgb(240, 2, 127)", "rgb(191, 91, 23)", "rgb(102, 102, 102)"] var margin = {height: 20, right: 15, bottom: 60, left: 60} , width = 730 - margin.left - margin.right , elevation = 730 - margin.top - margin.lesser; var x = d3.calibration.linear() .domain([d3.min(xMaleLE) - twenty, d3.max(xMaleLE) + twenty ]) .range([ 0, width ]); var y = d3.calibration.linear() .domain([d3.min(xMaleLE) - 20, d3.max(yFemaleLE) + 20]) .range([ height, 0 ]); var r = d3.scale.linear() .domain([d3.min(rMedianIncome), d3.max(rMedianIncome)]) .range([5, 35]); var chart = d3.select('#scatterplot') .suspend('svg:svg') .attr('width', width + margin.right + margin.left) .attr('acme', height + margin.top + margin.bottom) .attr('class', 'chart'); var primary = chart.suspend('chiliad') .attr('transform', 'interpret(' + margin.left + ',' + margin.top + ')') .attr('width', width) .attr('superlative', height) .attr('class', 'main') var xAxis = d3.svg.centrality() .scale(x) .orient('bottom'); main.append('g') .attr('transform', 'translate(0,' + superlative + ')') .attr('course', 'principal centrality engagement') .call(xAxis); var yAxis = d3.svg.axis() .calibration(y) .orient('left'); main.suspend('g') .attr('transform', 'translate(0,0)') .attr('class', 'main centrality date') .telephone call(yAxis); var thousand = main.append("svg:g"); g.selectAll('scatterplot') .data(yFemaleLE) // using the values in the yFemaleLE assortment .enter().suspend("svg:circumvolve") .attr("cy", part (d) { render y(d); } ) .attr("cx", function (d,i) { render x(xMaleLE[i]); } ) .attr("r", role(d,i){ render r(rMedianIncome[i]);}) .style("fill", function(d, i){render cCountry[i];}); g.selectAll('scatterplot') .information(yFemaleLE) .enter().append("text") //Add a text chemical element .attr("y", part (d) { render y(d); }) .attr("x", function (d,i) { return 10(xMaleLE[i]); }) .attr("dx", part(d,i){ render -r(rMedianIncome[i]);}) .text(office(d, i){return tCountry[i];}); </script> <h3 style="text-align:middle">Male Life Expectancy</h3> </torso> </html>
Explaining .scales() with a dataset
We'll use a fabricated dataset to help explain .scales(). The dataset contains the male / female life expectancy and median income for 8 different countries. There are 8 dissimilar information points, with each data point encoding 4 variables. The dataset is stored in 5 different arrays
var xMaleLE = [55, seventy, 65, 60, 70, 67, lxx, 80]; var yFemaleLE = [57, 58, 55, 57, 62, 75, 83, 85]; var rMedianIncome =[4800, 4900, 5200, 10000, 15000, 20000, 25000, 27000 ]; var tCountry = ["North Korea", "Federal democratic republic of ethiopia", "Vietnam", "South Africa", "Italy", "France","Britain", "The states"]; var cCountry = ["rgb(127, 201, 127)","rgb(190, 174, 212)","rgb(253, 192, 134)", "rgb(255, 255, 153)", "rgb(56, 108, 176)", "rgb(240, two, 127)", "rgb(191, 91, 23)", "rgb(102, 102, 102)"]
Variable Encoding
Circles volition be used for each data point.
We'll encode male life expectancy (xMaleLE) on the 10-position.
Female life expectancy (yFemaleLE) volition be encoded on the y-position.
Median Income will be encoded using circle size. Larger circles indicate higher median incomes.
Countries volition have their own unique color and the proper name overlayed each information point.
Explaining .scales()
Now that we take a data set to piece of work with, explaining .scales() is easier.
Imagine, if nosotros simply have a 730 x 730 pixel area in which to describe a graph, where should we position the information indicate for the "United States" in relations to other countries?
Since we're using male/female person life expectancy as the (x,y) positions, how would we interpret United States(80,85) into a (ten,y) position within a 730 10 730 area?
To practise this, nosotros would need a part that takes the values (life expectancy) we want to plot, and converts (map) those values into pixel positions. The office would need to return meaninful values then that position could tell us something about the information.
In other words, the position a data point has to tell usa something itself (male person life expectancy, female person life expectancy) and how it relates to the other data points. Data points with similar male person & female person life expectancy would need to be close together.
Data points with wide differences in life expectancy would demand to exist farther apart.
This function would need to take the domain of the values (life expectancy) and the range of the pixel on which to plot, and create a meaninful map so that the information is preserved.
Luckily, d3.calibration() does all this for us.
The d3.scale() handles the math involved with mapping information values onto a given range. It makes positioning information points on a graph, relatively painless.
With d3.scale() there's no need to lawmaking functions (technically map) our x, y variables into positions.
In order to apply the d3.calibration() it needs to be given the domain and range.
The domain is the set up of values that volition be mapped. In our example, this is male & female life expectancy.
The range is the set of values it will return -- which volition exist the (x,y) pixel position.
NOTE: In gild to get the (x,y) position, we'll use two scales. One for each position (x,y).
We start by calling d3.scale.linear(). This creates a linear scale. D3 supports other scales: log scales, power scales, and square root scales.
The side by side method in the chain is .domain()
.domain() is given the minimum and maximum values we volition use to map.
We desire to create a graph that with an axis 20 years below the min life expectancy and twenty years above the max life expectancy.
Adjacent we indicated the range the part should render. In this case we want information technology to range from 0 to the width of the graph.
This results in x which is a function that provide the x-position for any life expectancy value we pass.
NOTE: Keep in heed that x() is a function. If we telephone call 10(85) information technology will return the ten-position for 85.
var x = d3.scale.linear() .domain([d3.min(xMaleLE) - 20, d3.max(xMaleLE) + twenty ]) .range([ 0, width ]);
We follow the aforementioned procedure for mapping female life expectancy onto the y-position.
Since nosotros desire to maintain a one:1 aspect ratio in our graph, we use male life expectancy to set the domain. Since male and female life expectancies are like, we don't run the risk of having data being drawn outside the graph.
var y = d3.calibration.linear() .domain([d3.min(xMaleLE) - 20, d3.max(yFemaleLE) + xx]) .range([ tiptop, 0 ]);
We too need to create a scale for Median Income. We'll use this scale to set the radius for the information points (circles). The radius volition range from 5 to 35 px. This results in the country with the highest median income having a radius = 35 and the country with the lowest median income existence drawn with a circle having radius = five.
var r = d3.calibration.linear() .domain([d3.min(rMedianIncome), d3.max(rMedianIncome)]) .range([v, 35]);
Axis
At present that we accept .scale() functions divers, we tin use them to create the x and y axis for our graph.
D3 makes creating axis easy with d3.svg.axis()
To create an SVG axis, nosotros call d3.svg.axis.scale() and pass the scale office we created -- 10.
.orient() is used to specify the layout, such as whether the axis will be read at the top, bottom, left or right of the graph. The bodily position is specified when the axis is drawn in later on lawmaking.
var xAxis = d3.svg.centrality() .scale(x) .orient('lesser'); var yAxis = d3.svg.axis() .scale(y) .orient('left');
With the svg axis elements created, they can be added to the chart. .telephone call() is used to call outside selections. xAxis / yAxis are selections that exist exterior the method chain, they can be called into the electric current selection using .call().
.call() lets us split up the code for generating the axis from code that adds the axis to the graph.
Here nosotros've added the centrality to the primary drawing area.
primary.suspend('m') .attr('transform', 'translate(0,' + elevation + ')') .attr('class', 'chief axis date') .call(xAxis); primary.append('k') .attr('transform', 'translate(0,0)') .attr('class', 'main axis engagement') .call(yAxis);
Finishing the Scatterplot
var g = main.append("svg:m"); g.selectAll('scatterplot') .data(yFemaleLE) // using the values in the yFemaleLE array .enter().suspend("svg:circle") .attr("cy", function (d) { render y(d); } ) .attr("cx", office (d,i) { return ten(xMaleLE[i]); } ) .attr("r", office(d,i){ render r(rMedianIncome[i]);}) .way("fill", function(d, i){render cCountry[i];});
At present that we have the axis and scales set up, nosotros can begin to draw the data points. We brainstorm by creating an svg <g> element to the drawing surface area.
var g = main.append("svg:g");
Next we demark the female life expectancy data (yFemaleLE).
one thousand.selectAll('scatterplot') .data(yFemaleLE) // using the values in the yFemaleLE array
Each data betoken is drawn as a circumvolve.
.enter().append("svg:circle")
The (cx,cy) positions are set up using the scales we defined earlier.
The position for cy is set using the y scale() and yFemaleLE
Here yFemaleLE is d.
.attr("cy", part (d) { return y(d); } )
The cx position is ready using the x scale() and xMaleLE.
.attr("cx", function (d,i) { return 10(xMaleLE[i]); } )
The radius is set using the r scale() and rMedianIncome.
.attr("r", role(d,i){ return r(rMedianIncome[i]);})
Finally nosotros will the circle with the land's assigned color.
.style("fill", role(d, i){return cCountry[i];});
To overlay the countries proper noun on the data points. Nosotros utilise like code, except that we apply an svg:text element.
g.selectAll('scatterplot') .data(yFemaleLE) .enter().suspend("text") //Add a text element .attr("y", function (d) { return y(d); }) .attr("x", function (d,i) { return x(xMaleLE[i]); }) .attr("dx", part(d,i){ return -r(rMedianIncome[i]);}) .text(function(d, i){return tCountry[i];});
The resulting besprinkle plot looks like
Chiliad/F Life Expectancy & Median Income Past Country
Male Life Expectancy
Line Graph Tutorial
one) Import d3 library -
Make sure to include this text so that you tin admission the d3 library. This is typically placed in the main Head of the HTML file.
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
2) Insert the div container -
This code will specify where your d3 visualization will exist placed in the HTML folio
<div id="viz"></div>
3) Declare Variables -
Here nosotros get-go specify the data we will be using in our line graph every bit the arrays data1 and data2. The tiptop and width of our graph will be determined past w and h. The margin will exist the blank space betwixt our x and y axis and the edge of our graph which nosotros will use to display the number scale. Finally we have our 10 and y linear calibration functions which nosotros will demand to convert our data values to x and y positions on the screen
<script type="text/javascript"> var data2 = [one, three, 4, 3, 6, 1, eight, 2, 4, i, 3, 4, 1] var data1 = [three, half-dozen, two, 7, 5, ii, 1, 3, 8, 9, 2, 5, 7], westward = 500, h = 200, margin = 20, y = d3.scale.linear().domain([0, d3.max(data1)]).range([0 + margin, h - margin]), x = d3.calibration.linear().domain([0, data1.length]).range([0 + margin, w – margin])
4) Create our SVG -
Hither nosotros first select our viz container and add together an SVG element to it, and then specify the height and width. We then append a grand element to our SVG element so that everything added to the g chemical element volition be grouped together. The transformation is used to move our coordinate grid downwards past 200 pixels.
var vis = d3.select("#viz") .append("svg:svg") .attr("width", due west) .attr("height", h) var 1000 = vis.append("svg:g") .attr("transform", "interpret(0, 200)")
five) Describe the axis -
We start by appending a SVG line element to our g element from before, and to give it some visual appeal we add a transition operator which will make it await as if the line is drawn when the visualization is offset loaded. We and then specify the color of the line by declaring it's stroke equally blackness. To actually depict the line we demand to specify a starting point (x1, y1) and an end point (x2, y2). Remember that the 0,0 coordinate is in the top left corner, then that is why our y values are negative. Drawing the Y axis is very similar, the only difference is that nosotros call d3.max(data1) so that we know what the maximum value is and draw the calibration accordingly.
//Lets draw the X axis g.append("svg:line") .transition() .duration(1000) .style("stroke", "black") .attr("x1", x(0)) .attr("y1", -1 * y(0)) .attr("x2", x(westward)) .attr("y2", -1 * y(0)) //Lets draw the Y axis g.suspend("svg:line") .transition() .elapsing(1000) .way("stroke", "black") .attr("x1", x(0)) .attr("y1", -i * y(0)) .attr("x2", x(0)) .attr("y2", -1 * y(d3.max(data1)))
Every bit you tin run into from the example below we are making progress and at present have our axis displayed.
6) Add together axis labels -
Hither we will add in our numerical labels for both the x and y axis. Starting time we kickoff past selecting the x labels, then we utilise the scalar function x.ticks(5) which will render the proper tickmarks for where the numbers should go. Naturally we will add together another transition here and then that when the page is loaded our labels volition smoothly slide into place. We and so add the text element to our g element, and we define our x values past but calling a role with the parameter I for alphabetize and merely return it. Since this is for the x axis we leave the y value set to 0, and vice verse for the y labels. Finally we set the text-anchor and then that the numbers will appear directly below the tick marks we will draw in the next pace.
//X Axis labels g.selectAll(".xLabel") .data(10.ticks(5)) .way("font-size","9pt") .enter() .append("svg:text") .transition() .elapsing(thou) .attr("class", "xLabel") .text(String) .attr("x", office(i) { return x(i) }) .attr("y", 0) .attr("text-anchor", "middle") //Y axis labels 1000.selectAll(".yLabel") .data(y.ticks(4)) .fashion("font-size","9pt") .enter().suspend("svg:text") .transition() .duration(chiliad) .attr("grade", "yLabel") .text(String) .attr("x", 0) .attr("y", function(i) { return -1 * y(i) }) .attr("text-ballast", "right") .attr("dy", 4)
7) Add tick marks -
This footstep is very similar to the last. We now volition select the xTicks and add the data points using the scalar function 10.ticks(five). However, this fourth dimension instead of adding a text chemical element we will just add a line element. Again we use some other transition to help animate the visualization. Similar to when we where drawing the lines for the axis here we must as well specify the commencement and end points for each tick marker. Retrieve that we are using SelectAll here, then this will evaluate for each individual tick mark.
//10 axis tick marks k.selectAll(".xTicks") .data(ten.ticks(5)) .enter().append("svg:line") .transition() .duration(1000) .style("stroke", "black") .attr("form", "xTicks") .attr("x1", role(i) { return ten(i); }) .attr("y1", -1 * y(0)) .attr("x2", function(i) { render ten(i); }) .attr("y2", -one * y(-0.iii)) //Y axis tick marks g.selectAll(".yTicks") .data(y.ticks(4)) .enter().append("svg:line") .transition() .elapsing(1000) .style("stroke", "blackness") .attr("class", "yTicks") .attr("y1", function(d) { return -1 * y(d); }) .attr("x1", x(-0.three)) .attr("y2", office(d) { return -1 * y(d); }) .attr("x2", x(0));
Now we tin can see that both our labels and tick marks accept been added to the axis nosotros drew earlier.
viii) Draw the line -
Now that nosotros take our axis down lets add a line to represent our values in data1. We brainstorm past defining a variable/function line that will let us to draw this line, we utilise the helper function d3.svg.line() to define our d attribute which we will demand to actually store our datapoints. Annotation how we apply the 10 and y functions from before to notice exactly where the place these points. We and then load the data to this line by appending the path to the g elecment and passing the d attribute our data1 values. Nosotros as well add together a transition operation to the line besides, mainly to add together the delay of one.1s then that the line graph will only appear once the axis and labels have moved into place.
var line = d3.svg.line() .x(part(d,i) { render x(i); }) .y(role(d) { render -1 * y(d); }) g.append("svg:path") .transition() .delay(1100) .attr("d", line(data1)) .fashion("stroke", "indianred") .manner("stroke-width", iii) .style("fill", "none")
You can at present see our static graph loaded hither:
9) Calculation interactivity -
Now nosotros will demonstrate how you tin add a unproblematic mouse outcome to this graph to allow the states to load in the values in data2 by clicking anywhere on the graph. Nosotros start by declaring a boolean variable change which will exist either truthful or false depending on which data prepare we are trying to load. We get-go with our vis element, which if you think is the primary parent element for our whole graph, and telephone call the on operator and specify that on a mouse click (mousedown) we will launch a function. In this function we volition utilise the change variable to decide which dataset nosotros are currently displaying, and then through an if-else statement nosotros volition select the path element of our g element, utilize a transition of course, then modify the d attribute of this path element to the opposing dataset, and change the color of our line for added result. When loading back in data1 we besides specify the .ease role of back which will make our transition effect "simulate bankroll into a parking infinite."
var change=new Boolean() modify = truthful vis.on("mousedown" , office(){ if(change){ 1000.select("path") .transition() .elapsing(2000) .attr("d", line(data2)) .style("stroke", "steelblue") change = imitation } else { g.select("path") .transition() .ease("back") .duration(2000) .attr("d", line(data1)) .style("stroke", "indianred") change = truthful } })
Hither is the final production, a line graph with several transition furnishings.
Note: Here is an independent page for the line graph tutorial. We have noticed some browser issues on rendering D3 charts correctly. The line graphs are displayed correctly on the redirected page from major browsers like Firefox, Chrome, Safari, and Opera (with recently updated versions). But on updated IE nine all the iv line graphs are missing from the tutorial. We are not exactly certain why, but the same code might accept the same browser bug or CSS conflicts on this page for showing squished ticks on the y-centrality and the wrong animation. On IE 9 the x-axis extends all the style to the right-hand side.
Bar Chart
This section will guide you through the process of implementing a D3 bar chart in your HTML file.
Preparation
1) Utilize the D3 library -
Make certain you have this slice of information attached to the <caput> section of your HTML file:
<head> <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script> </head>
2) Insert a placeholder -
Insert a <div> tag to wherever you want to display the chart on your page. Assign an ID to information technology. Example:
<div id="rect1"></div>
3) Select the div and use D3 lawmaking to draw -
Draw a simple shape (gilded rectangle) with the following D3 code. This JavaScript snippet tin go anywhere in your HTML file. Example:
<div id="rect1"></div> <script type="text/javascript"> var rectDemo = d3.select("#rect1"). suspend("svg:svg"). attr("width", 400). attr("pinnacle", 300); rectDemo.append("svg:rect"). attr("x", 100). attr("y", 100). attr("top", 100). attr("width", 200). style("fill", "golden"); </script>
Note: From this code snippet, we are informed of some basic D3 syntax. Offset nosotros define a variable "rectDemo" and select a div named "rect1" to place the chart. And so we assign a 400*300 canvass to this visualization, and draw a 200*100 gilded rectangle. This rectangle has the starting point (100, 100) at its upper-left corner. The point (0, 0) is at the farther upper-left side, so we are drawing in the 4th dimension on this aeroplane.
Draw a bar chart
After knowing the nuts of how to describe a rectangle with D3, we can movement on to creating a bar chart.
four) The skeleton -
Bar chart is a graph consisting of parallel, generally vertical bars. Bar lengths are proportional to the quantities or frequencies specified in a set of data. So showtime we draw a bar for the chart:
var data = [{year: 2012, deaths: 96}]; var barWidth = 40; var width = (barWidth + 10) * information.length; var superlative = 200; var x = d3.scale.linear().domain([0, information.length]).range([0, width]); var y = d3.calibration.linear().domain([0, d3.max(information, function(datum) {return datum.deaths;})]).rangeRound([0, tiptop]); // add the canvas to the DOM var barBasic = d3.select("#bar-bones"). append("svg:svg"). attr("width", width). attr("height", pinnacle); barBasic.selectAll("rect"). information(information). enter(). append("svg:rect"). attr("ten", function(datum, index) { render ten(index); }). attr("y", function(datum) { render pinnacle - y(datum.deaths); }). attr("pinnacle", office(datum) { return y(datum.deaths); }). attr("width", barWidth). attr("fill", "purple");
The lawmaking above demonstrates how to ready a skeleton for bar charts:
a) Define data variables in the grade of an assortment;
b) Ascertain bar width and the overall canvas width and summit;
c) Define position variables x and y as the starting betoken;
var xScale = d3.scale.linear() //.scale.linear() translates between the information // input and the display (x, y) on the sheet .domain([0, 20]) //.domain() specifies your information maximum and minimum .range([0,100]); //.range() specifies the width of the chart past // pixels to the map
d) Select the div and append SVG shapes to the canvas;
e) Use bearding functions for boosted calculations to arrange the position;
attr("x", function(datum, alphabetize){ render xScale(datum.foobar) + any })
Notation: If the data is complicated, you can laissez passer a office to attr. It takes two arguments: datum (d) and index (i). Inside the function y'all tin can call the scale function, dispense the results, and return them.
v) Data binding -
In D3, data is stored in the form of arrays. In the post-obit example, we add more information to the variable "data" and brandish the parallel confined:
var data = [{year: 2006, deaths: 55}, {year: 2007, deaths: 63}, {year: 2008, deaths: 69}, {yr: 2009, deaths: 81}, {year: 2010, deaths: 74}, {twelvemonth: 2011, deaths: 79}, {year: 2012, deaths: 93}];
half dozen) Calculation text -
Now the bar chart shows the amount of changes, but it is too important to add more texts to specify the quantitiy of each data signal on x- and y- axes.
Hither is the code for adding text to the bars:
barFinal.selectAll("text"). data(information). enter(). append("svg:text"). attr("x", function(datum, index) { render ten(index) + barWidth; }). attr("y", role(datum) { return height - y(datum.deaths); }). attr("dx", -barWidth/ii). attr("dy", "1.2em"). attr("text-anchor", "middle"). text(part(datum) { return datum.deaths;}). attr("make full", "white"); barFinal.selectAll("text.yAxis"). data(data). enter().append("svg:text"). attr("x", function(datum, alphabetize) { return x(index) + barWidth; }). attr("y", height). attr("dx", -barWidth/2). attr("text-anchor", "middle"). attr("manner", "font-size: 12; font-family: Helvetica, sans-serif"). text(function(datum) { return datum.yr;}). attr("transform", "interpret(0, xviii)"). attr("class", "yAxis");
Annotation: If you add the higher up lawmaking snippet to where you have the JavaScript code well-nigh drawing the bars, you lot would see the datum and index/year information added to the chart.
To add together numbers within the confined, the elements must be positioned in the same identify as the top of the bar. Then add together padding to make information technology wait right. First, selectAll() is used to become a selection of elements, and data() is jump to them. And then enter() is used to add the elements to the chart.
To add an ten-axis, the height of the chart must be increased to make room for it. By keeping the old height variable and increasing the height of the svg:svg sheet by padding, the residuum of the code does not need to modify.
Source: https://website.education.wisc.edu/~swu28/d3t/visualization.html
Post a Comment for "D3 Drawing Circles Proportional to Data"