D3 creating X-axis - javascript

Hi I'm using the following code
var data = [4, 8, 15, 16, 23, 42];
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, 420]);
d3.select(".chart")
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function(d) { return x(d) + "px"; })
.text(function(d) { return d; });
From http://codepen.io/mbostock/pen/Jaemg and I'm trying to add x-axis with a logarithmic format.
What I'm trying to achieve is the below image but I keep hitting dead ends. Any clue or idea on how to achieve this.
Many thanks for your help.

My first comment would be to shift to a proper svg based drawing instead of using div to create your bars. When starting a new plot, I tend to start my work off this classic example. You, though are going to want to use a log scale on your x-axis:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = [4, 8, 15, 16, 23, 42];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 500 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.log()
.domain([0.01, 100])
.range([0, width]);
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], .1)
.domain(d3.range(data.length))
var xAxis = d3.svg.axis()
.scale(x)
.orient("top")
.ticks(0, "1g")
.tickFormat(function(d){
if (d === 0.01) return "0"
else if (d === 1 || d === 10 || d === 100) return d;
else return "";
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0,0)")
.call(xAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", 0)
.attr("width", function(d){
return x(d);
})
.attr("y", function(d, i) {
return y(i);
})
.attr("height", function(d,i) {
return y.rangeBand();
});
</script>

Related

Plot bar chart using D3 JS

I am plotting a bar chart in D3.js (Version 3). Which has two axis, one is receive_data and another one is responses. I have a JSON file where I stored the data. JSON format looks like,
[{"receive_date":"2013-11-04","responses":"2"}]
In my JSON, I have two responses values for the same date 2013-11-04 .
Like,
[{"receive_date":"2013-11-04","responses":"2"},{"receive_date":"2013-11-04","responses":"8668"}
This is the JSON Source :- https://api.myjson.com/bins/gdpu7
So, when I am plotting the graph, it is not taking the sum of the values for the same receive_date instead it is showing two times. I want it to show the sum of responses. responses should be (8668+2) for the receive_date 2013-11-04
I also found it that by using reduce we can do this. I tried to use d3.json.reduce . But it is showing error d3.json.reduce is not a function.
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
x.domain(data.map(function(d) {
return d.receive_date
}));
y.domain([0, d3.max(data, function(d) {
return d.responses
})]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="chart"></div>
JSfiddle :- https://jsfiddle.net/bL9940at/
The relevant part:
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// scale to ordinal because x axis is not numerical
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
//scale to numerical value by height
var y = d3.scale.linear().range([height, 0]);
var chart = d3.select("#chart")
.append("svg") //append svg element inside #chart
.attr("width", width + (2 * margin.left) + margin.right) //set width
.attr("height", height + margin.top + margin.bottom); //set height
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom"); //orient bottom because x-axis will appear below the bars
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
d3.json("https://api.myjson.com/bins/gdpu7", function(error, data) {
//create new arrays
var array1 = data; //input
var array2 = [];
var last_d;
array1.reduce(function (accumulator, currentValue, i) {
var r = Number(currentValue.responses),
d = currentValue.receive_date;
if (d == last_d) r += accumulator;
array2[i] = {
receive_date: d,
responses: r
};
last_d = d;
return accumulator + Number(currentValue.responses);
}, 0);
data = array2; //output
x.domain(data.map(function(d) {
return d.receive_date;
}));
y.domain([0, d3.max(data, function(d) {
return d.responses;
})*1.1]);
var bar = chart.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function(d, i) {
return "translate(" + x(d.receive_date) + ", 0)";
});
bar.append("rect")
.attr("y", function(d) {
return y(d.responses);
})
.attr("x", function(d, i) {
return x.rangeBand() + (margin.left / 2);
})
.attr("height", function(d) {
return height - y(d.responses);
})
.attr("width", x.rangeBand()); //set width base on range on ordinal data
bar.append("text")
.attr("x", x.rangeBand() + margin.left)
.attr("y", function(d) {
return y(d.responses) - 10;
})
.attr("dy", ".75em")
.text(function(d) {
return d.responses;
});
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + margin.left + "," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + margin.left + ",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("responses");
});
function type(d) {
d.receive_date = +d.receive_date; // coerce to number
return d;
}
#chart rect {
fill: #4aaeea;
}
#chart text {
fill: white;
font: 10px sans-serif;
text-anchor: end;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #fff;
shape-rendering: crispEdges;
}
body {
background: #1a1a1a;
color: #eaeaea;
padding: 10px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://code.jquery.com/jquery-git.js"></script>
<div id="chart"></div>
</body>
</html>

Draw Line chart with positive & negative values

I am trying to draw a Dovish & Hawkish line chart diagram.
Example:
However, it's throwing an error.
I took the example from a bar chart, that draws the bars with a positive and negative value. Then I have used the code only related to draw the xScale/yScale and the lines for xAxis/yAxis.
JSFiddle can be found here.
The problem here is that you are using an ordinal scale for the x axis:
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
Which has this domain:
[0, 1, 2, 3, 4, 5, 6, 7]
However, you're passing the datum to the line generator:
var line = d3.svg.line()
.x(function(d) { return x(d);})
//datum here -------------^
Instead of that, pass the index of the datum:
var line = d3.svg.line()
.x(function(d, i) { return x(i);})
//index here ----------------^
Here is your updated code:
var data = [-0.1, -0.5, -0.32, 0.2, 1, 0.5, -0.3, -1.0];
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 30
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height, 0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d);
});
var dataSum = d3.sum(data, function(d) {
return d;
});
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("line")
.attr("y1", y(0))
.attr("y2", y(0))
.attr("x1", 0)
.attr("x2", width);
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text{
font-size: 12px;
}
.tick line{
opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
PS: You don't need that awkward line in the middle of the chart. You can create a proper axis:
var data = [-0.1, -0.5, -0.32, 0.2, 1, 0.5, -0.3, -1.0];
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 30
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var y0 = Math.max(Math.abs(d3.min(data)), Math.abs(d3.max(data)));
var y = d3.scale.linear()
.domain([-y0, y0])
.range([height, 0])
.nice();
var x = d3.scale.ordinal()
.domain(d3.range(data.length))
.rangeRoundBands([0, width], .2);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line = d3.svg.line()
.x(function(d, i) {
return x(i);
})
.y(function(d) {
return y(d);
});
var dataSum = d3.sum(data, function(d) {
return d;
});
svg.append("g")
.attr("class", "x axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(xAxis)
svg.append("path")
.data([data])
.attr("class", "line")
.attr("d", line);
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: blue;
stroke-width: 2px;
}
.tick text {
font-size: 12px;
}
.tick line {
opacity: 0.2;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Error: <rect> attribute width: Expected length, "NaN". and <text> attribute dx: Expected length, "NaN"

I want to implement a bar chart in D3, but my values on the dx axis are of type Date, data type which the D3 library should accept, but it seems to give me an error like this: attribute width: Expected length, "NaN".
This is my code:
<html>
<head>
<meta charset="utf-8">
<title>a bar graph</title>
</head>
<style>
.axis path,
.axis line{
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.MyRect {
fill: steelblue;
}
.MyText {
fill: white;
text-anchor: middle;
}
</style>
<body>
<script src="http://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script>
var width=400;
var height=400;
var svg=d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var padding = {left:30, right:30, top:20, bottom:20};
var dataset=[10,20,30,40,33,24,12,5];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, width-padding.left-padding.right]);
var yScale = d3.scaleLinear()
.domain([0,d3.max(dataset)])
.range([height-padding.top-padding.bottom,0]);
var xAxis = d3.axisBottom()
.scale(xScale)
var yAxis = d3.axisLeft()
.scale(yScale)
var rectPadding=4;
var rects = svg.selectAll(".Myrect")
.data(dataset)
.enter()
.append("rect")
.attr("class","Myrect")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x",function(d,i){
return xScale(i) + rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("width",xScale.range()- rectPadding)
.attr("height",function(d){
return height - padding.top - padding.bottom - yScale(d);
});
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.range() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
</html>
And the another error: attribute dx: Expected length, "NaN". I think it arises from band scales, but after using the introduction of the official, it still can't work.
The introduction of the official:
var x = d3.scaleBand()
.domain(["a", "b", "c"])
.range([0, width]);
So when I want to call the code, I think it should be used like this in my pasted code:
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class","MyText")
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d,i){
return xScale(i) + rectPadding/2;
})
.attr("y",function(d){
return yScale(d);
})
.attr("dx",function(){
return (xScale.range() - rectPadding)/2;
})
.attr("dy",function(d){
return 20;
})
.text(function(d){
return d;
});
But it seems to give me two errors. I'm a beginner. Thank you very much!
Right now, for the width of the rectangles and the dx of the texts, you're using:
xScale.range() - rectPadding
But xScale.range() returns an array, and array - number will give you a NaN. And you're not getting anywhere with a NaN...
Instead of xScale.range(), which will return an array, you should use:
xScale.bandwidth();
Which not only returns a proper number, but it's also what you're looking for.
Here is your code with that change:
<style>
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.MyRect {
fill: steelblue;
}
.MyText {
fill: white;
text-anchor: middle;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script>
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var padding = {
left: 30,
right: 30,
top: 20,
bottom: 20
};
var dataset = [10, 20, 30, 40, 33, 24, 12, 5];
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, width - padding.left - padding.right]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([height - padding.top - padding.bottom, 0]);
var xAxis = d3.axisBottom()
.scale(xScale)
var yAxis = d3.axisLeft()
.scale(yScale)
var rectPadding = 4;
var rects = svg.selectAll(".Myrect")
.data(dataset)
.enter()
.append("rect")
.attr("class", "Myrect")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d, i) {
return xScale(i) + rectPadding / 2;
})
.attr("y", function(d) {
return yScale(d);
})
.attr("width", xScale.bandwidth() - rectPadding)
.attr("height", function(d) {
return height - padding.top - padding.bottom - yScale(d);
});
var texts = svg.selectAll(".MyText")
.data(dataset)
.enter()
.append("text")
.attr("class", "MyText")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.attr("x", function(d, i) {
return xScale(i) + rectPadding / 2;
})
.attr("y", function(d) {
return yScale(d);
})
.attr("dx", function() {
return (xScale.bandwidth() - rectPadding) / 2;
})
.attr("dy", function(d) {
return 20;
})
.text(function(d) {
return d;
});
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding.left + "," + padding.top + ")")
.call(yAxis);
</script>
</body>
PS: You don't need rectPadding. Just set the padding in the band scale:
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([0, width-padding.left-padding.right])
.padding(0.2);//some value here
I have got the same error when element height for chart is too small due to this code (20px per row)
document.getElementById('chart').style.height = (data.getNumberOfRows() * 20) + 'px';
It was fine for several data items but when I had only a few items I got error. So I fixed it by adding some min-height
<div id="chart" style="width: 100%; min-height: 100px"></div>

d3 - multiple overlaying charts with custom DOM element

I'm new to d3, but pretty familiar with the HighCharts api.
I've seen lots of examples of multiple d3 charts on the same page; but can't seem to find examples of one chart overlaying/sitting directly on top of another chart. Is this possible?
With HighCharts, you can define multiple chart types in the plotOptions config object. Is there something similar with d3? Or, how could you do this with d3?
I would effectively like to have a line graph on top of a bar chart. There will be different 'stages' according to the data, so some of the bar's could be inactive/empty.
Additionally, I need to display an indicator to show where the 'stage' is currently; and ensure that this is all responsive.
Example (rough mockup):
After researching d3 and looking for similar examples, I am thinking that maybe d3 isn't the best choice for this; maybe a custom CSS/JS/HTML solution (inside an angular app) would be better.
Any recommendations or pointers would be very appreciated.
Here's a quick mock-up started from this excellent bar chart example:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.point rect {
fill: steelblue;
}
.point circle {
fill: orange;
}
.point rect:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: orange
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 75, right: 20, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = "abcdefghijklmnopqrstuvwxyz".split("").map(function(d){
return {
letter: d,
bar: Math.random() * 10,
line: Math.random() * 10
};
})
x.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d3.max([d.bar, d.line]); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
var points = svg.selectAll(".point")
.data(data)
.enter().append("g")
.attr("class", "point");
points.append('rect')
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.bar); })
.attr("height", function(d) { return height - y(d.bar); });
points.append('circle')
.attr("r", 5)
.attr("cx", function(d){ return x(d.letter) + x.rangeBand() / 2; })
.attr("cy", function(d){ return y(d.line)});
var line = d3.svg.line()
.x(function(d) { return x(d.letter) + x.rangeBand() / 2; })
.y(function(d) { return y(d.line); });
svg.append("path")
.attr("class", "line")
.datum(data)
.attr("d", line);
var indicator = svg.append("g")
.attr("r", 5)
.attr("transform", "translate(" + (x("q") + x.rangeBand() / 2) + "," + -20 + ")");
indicator.append("circle")
.attr("r", 40)
.style("fill", "red");
indicator.append("text")
.text("!")
.style("fill", "white")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", 70);
indicator.append("line")
.attr("y1", 20)
.attr("y2", height + 20)
.attr("x1", 0)
.attr("x2", 0)
.style("stroke", "red")
.style("stroke-width", "4px");
</script>
New Solution Based on Comments
Given your input data, here's a new example. I went a bit overboard here, so please ask question on any confusing bits. I tried to comment it out:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect {
fill: steelblue;
}
circle {
fill: orange;
}
rect:hover {
fill: brown;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: orange
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 75,
right: 20,
bottom: 30,
left: 40
},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// here's your data
var data =
{
'point1': [{
'value': 50
}, {
'value': 100
}, {
'value': 100
}, {
'value': 150
}],
'point2': [{
'value': 25
}, {
'value': 40
}, {
'value': 60
}],
'point3': [{
'value': 25
}]
};
// d3ify your data
// d3 likes arrays of objects, you have an object of objects
// so first make it an array
var barData = d3.entries(data);
// set x domain
x.domain(barData.map(function(d){ return d.key }));
// create lineData
var lineData = [];
barData.forEach(function(d0, i){
d0.mean = d3.mean(d0.value, function(d1){ return d1.value });
d0.max = d3.max(d0.value, function(d1){ return d1.value});
var N = d0.value.length,
// this is an inner scale
// that represents each bar
s = d3.scale.linear().range([
x(d0.key) + (x.rangeBand() / N) / 2,
x(d0.key) + x.rangeBand()
]).domain([
0, N
])
d0.value.forEach(function(d1, j){
lineData.push({
x: s(j), // this is the pixel position of x, it's jittered on the bar
y: d1.value // this is the user position of y
})
});
});
// set y domain
y.domain([0, d3.max(barData, function(d) {
return d.max;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text");
// draw bars
var bars = svg.selectAll(".bar")
.data(barData)
.enter()
.append('rect')
.attr('class', 'bar')
.attr("x", function(d) {
return x(d.key);
})
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.mean);
})
.attr("height", function(d) {
return height - y(d.mean);
});
// add points
var points = svg.selectAll('point')
.data(lineData)
.enter()
.append('circle')
.attr('class', 'point')
.attr("r", 5)
.attr("cx", function(d) {
return d.x; // already pixel position
})
.attr("cy", function(d) {
return y(d.y)
});
var line = d3.svg.line()
.x(function(d) {
return d.x; // already pixel position
})
.y(function(d) {
return y(d.y);
});
svg.append("path")
.attr("class", "line")
.datum(lineData)
.attr("d", line);
var indicator = svg.append("g")
.attr("transform", "translate(" + (x("point2") + x.rangeBand() / 2) + "," + -20 + ")");
indicator.append("circle")
.attr("r", 40)
.style("fill", "red");
indicator.append("text")
.text("!")
.style("fill", "white")
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", 70);
indicator.append("line")
.attr("y1", 20)
.attr("y2", height + 20)
.attr("x1", 0)
.attr("x2", 0)
.style("stroke", "red")
.style("stroke-width", "4px");
</script>
Happy d3ing!

How can I create hierarchical bar chart in d3.js

I wanted to use the Sortable Bar Chart of Mike Bostock without it's transition property and sort the bars according to their lenght but I couldn't. I want the bars just like this. without interactivity.
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<style>
body {
font: 10px sans-serif;
}
.bar rect {
fill: steelblue;
}
.bar text {
fill: white;
}
.axis path, .axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
</style>
<script src="http://mbostock.github.com/d3/d3.js"></script>
<script>
var margin = {top: 0, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var index = d3.range(24),
data = index.map(d3.random.normal(100, 10));
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, width]);
var y = d3.scale.ordinal()
.domain(index)
.rangeRoundBands([0, height], .1);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d, i) { return "translate(0," + y(i) + ")"; });
bar.append("rect")
.attr("height", y.rangeBand())
.attr("width", x);
bar.append("text")
.attr("text-anchor", "end")
.attr("x", function(d) { return x(d) - 6; })
.attr("y", y.rangeBand() / 2)
.attr("dy", ".35em")
.text(function(d, i) { return i; });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom"));
//var sort = false;
//setInterval(function() {
// if (sort = !sort) {
// index.sort(function(a, b) { return data[a] - data[b]; });
// } else {
// index = d3.range(24);
// }
// y.domain(index);
// bar.transition()
// .duration(750)
// .delay(function(d, i) { return i * 50; })
// .attr("transform", function(d, i) { return "translate(0," + y(i) + ")"; });
//}, 5000);
var hierarchy = d3.layout.partition()
.data(function(d) { return d.data; });
</script>
I removed the the code from var sort = false; part and added
var hierarchy =.. part but It's still not working. How can I make it?
Any help will be appreciated.
Thanks.
You should add the following line to sort the values:
index.sort(function(a, b) { return data[b] - data[a]; });
Check out the jsfiddle: http://jsfiddle.net/k9rcyeyo/

Categories

Resources