A basic column chart, along with automatic data updates.
Credit owed to Mike Bostock's guides:
| height: 450 | |
A basic column chart, along with automatic data updates.
Credit owed to Mike Bostock's guides:
| letter | frequency | |
|---|---|---|
| A | .12102 | |
| B | .01492 | |
| C | .02782 | |
| D | .04253 | |
| E | .12702 | |
| F | .02288 | |
| G | .02015 | |
| H | .06094 | |
| I | .06966 | |
| J | .00153 | |
| K | .00772 | |
| L | .04025 | |
| M | .02406 | |
| N | .06749 | |
| O | .07507 | |
| P | .01929 | |
| Q | .00095 | |
| R | .05987 | |
| S | .06327 | |
| T | .09056 | |
| U | .02758 | |
| V | .00978 | |
| W | .02360 | |
| X | .00150 | |
| Y | .01974 | |
| Z | .00074 |
| <!DOCTYPE html> | |
| <head> | |
| <title>Updating column chart</title> | |
| <meta charset="utf-8"> | |
| <style> | |
| body { | |
| padding: 0; | |
| margin: 0; | |
| font-family: helvetica, arial, sans-serif; | |
| background-color: white; | |
| } | |
| .bar { fill: cornflowerblue; } | |
| .exit { fill: indianred; } | |
| .enter { fill: seagreen; } | |
| .axis { font: 10px sans-serif; } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| shape-rendering: crispEdges; | |
| } | |
| .axis path {stroke: black;} | |
| .axis line { | |
| stroke: gray; | |
| stroke-opacity: 0.2; | |
| stroke-width: 1px; | |
| } | |
| .x.axis path, | |
| .y.axis path { | |
| display: none; | |
| } | |
| .tick text { | |
| font-size: 12px; | |
| fill: black; | |
| text-shadow: 1px 1px 0 rgba(255, 255, 255, 0.3), | |
| 1px -1px 0 rgba(255, 255, 255, 0.3), | |
| -1px 1px 0 rgba(255, 255, 255, 0.3), | |
| -1px -1px 0 rgba(255, 255, 255, 0.3); | |
| } | |
| .attribution { | |
| background-color: lightgray; | |
| font-size: 12px; | |
| color: #333; | |
| padding: 5px 10px; | |
| } | |
| .source { | |
| float: right; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <svg class="chart"></svg> | |
| <div class="attribution"><span>The Lens/Thomas Thoren</span><span class="source">Source: Source</span></div> | |
| <script src="//d3js.org/d3.v3.min.js"></script> | |
| <script src="//d3js.org/topojson.v1.min.js"></script> | |
| <script> | |
| var margin = {top: 20, right: 30, bottom: 30, left: 30}, | |
| width = 960 - margin.left - margin.right, | |
| height = 400 - margin.top - margin.bottom; | |
| var formatNumber = d3.format("%"); | |
| var chart = d3.select(".chart") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom) | |
| .append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| var attribution = d3.select(".attribution") | |
| .style("width", width + margin.left + 'px'); | |
| // Used to assign names to column placement values along the x-axis. | |
| var x = d3.scale.ordinal() | |
| .rangeRoundBands([0, width], 0.1) | |
| var y = d3.scale.linear() | |
| .range([height, 0]); // Map reverse order to return from top down. Could avoid this and rework how to calculate height and y below. | |
| // Define axes | |
| var xAxis = d3.svg.axis() | |
| .scale(x) // Use the defined x ordinal scale. | |
| .orient("bottom"); | |
| var yAxis = d3.svg.axis() | |
| .scale(y) | |
| .orient("left") | |
| .tickFormat(function(d) { | |
| var s = Math.floor(d * 100); | |
| return d === Math.floor(y.domain()[1] * 100) / 100 | |
| ? s + "% of occurrences" | |
| : s; | |
| }) | |
| .tickSize(-width) | |
| .tickPadding(10); | |
| var xAxisLabels = chart.append("g") | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(0," + height + ")"); | |
| var yAxisLabels = chart.append("g") | |
| .attr("class", "y axis"); | |
| d3.csv("data.csv", type, function(error, data) { | |
| if (error) throw error; | |
| x.domain(data.map(function(d) { return d.letter; })) // data.map creates array with names | |
| y.domain([0, d3.max(data, function(d) { return d.frequency; })]) // Accessor function to map object values to an array. | |
| // Draw axes | |
| xAxisLabels.call(xAxis); | |
| yAxisLabels.call(yAxis); | |
| yAxisLabels.selectAll('text') | |
| .style('text-anchor', 'start'); | |
| yAxisLabels.selectAll('line') | |
| .attr("x1", function(d, i) { | |
| return d === Math.floor(y.domain()[1] * 100) / 100 // If last | |
| ? 100 | |
| : 4; | |
| }); | |
| // Draw columns | |
| chart.append('g').attr('id', 'columns').selectAll('.bar') // Group rect and text together | |
| .data(data, function(d) { return d.letter; }) | |
| .enter().append('rect') | |
| .attr("class", "bar") | |
| .attr("x", function(d) { return x(d.letter); }) | |
| .attr("y", function(d) { return y(d.frequency); }) | |
| .attr("width", x.rangeBand()) | |
| .attr("height", function(d) { return height - y(d.frequency) + "px"; }) | |
| .style("fill-opacity", 1); | |
| // Move y-axis in front of columns so the labels aren't covered. | |
| yAxisLabels.each(function() { | |
| this.parentNode.appendChild(this); | |
| }); | |
| }); | |
| function type(d) { | |
| d.frequency = +d.frequency; // Coerce to number. By default, CSV values are strings. | |
| return d; | |
| } | |
| function update(data) { | |
| // Update y scale input domain values. | |
| x.domain(data.map(function(d) { return d.letter; })) // data.map creates array with names | |
| y.domain([0, d3.max(data, function(d) { return d.frequency; })]) // Accessor function to map object values to an array. | |
| xAxis.scale(x); // Use the updated scales | |
| yAxis.scale(y); | |
| xAxisLabels.transition() // Transition to updated axes | |
| .duration(750) | |
| .call(xAxis); | |
| yAxisLabels.transition() | |
| .duration(750) | |
| .call(yAxis); | |
| yAxisLabels.selectAll('text') | |
| .style('text-anchor', 'start'); | |
| yAxisLabels.selectAll('line') | |
| .attr("x1", function(d, i) { | |
| return d === Math.floor(y.domain()[1] * 100) / 100 // If last | |
| ? 100 | |
| : 4; | |
| }); | |
| // DATA JOIN | |
| // Join new data with old elements, if any. | |
| var bar = chart.selectAll('rect') // Group rect and text together | |
| .data(data, function(d) { return d.letter; }) | |
| // UPDATE | |
| // Update old elements as needed. | |
| bar.attr("class", "bar") | |
| .transition() | |
| .duration(750) | |
| .attr("x", function(d) { return x(d.letter); }) | |
| .attr("y", function(d) { return y(d.frequency); }) | |
| .attr("width", x.rangeBand()) | |
| .attr("height", function(d) { return height - y(d.frequency) + "px"; }); | |
| // ENTER | |
| // Create new elements as needed. | |
| bar.enter().append("rect") | |
| .attr("class", "enter") | |
| .attr("x", function(d) { return x(d.letter); }) | |
| .attr("y", "0") | |
| .attr("width", x.rangeBand()) | |
| .attr("height", function(d) { return height - y(d.frequency) + "px"; }) | |
| .style("fill-opacity", 0) | |
| .transition() | |
| .duration(750) | |
| .attr("y", function(d) { return y(d.frequency); }) | |
| .style("fill-opacity", 1); | |
| // EXIT | |
| // Remove old elements as needed. | |
| bar.exit() | |
| .attr("class", "exit") | |
| .transition() | |
| .duration(750) | |
| .attr("y", 0) | |
| .style("fill-opacity", 0) | |
| .remove(); | |
| // Move y-axis in front of columns so the labels aren't covered. | |
| yAxisLabels.each(function() { | |
| this.parentNode.appendChild(this); | |
| }); | |
| } | |
| // Automatically update with random data every 1.5 seconds. | |
| setInterval(function() { | |
| var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""); | |
| var i; | |
| var new_data = []; | |
| for (i = 0; i < alphabet.length; i++) { | |
| var datum = {}; | |
| datum.letter = alphabet[i]; | |
| datum.frequency = Math.random() / 9; | |
| new_data.push(datum) | |
| } | |
| var number_to_keep = Math.floor(Math.random() * new_data.length); | |
| if (number_to_keep < 3) { | |
| number_to_keep = new_data.length; | |
| } | |
| new_data = d3.shuffle(new_data) | |
| .slice(0, number_to_keep) | |
| .sort(function(x, y) { return d3.ascending(x.letter, y.letter); }); | |
| update(new_data); | |
| }, 1500); | |
| // Allows iframe on bl.ocks.org. | |
| // d3.select(self.frameElement).style("height", height + "px"); | |
| </script> | |
| </body> | |
| </html> |