Related
I am new to D3.js and got one issue.
I tried to create bar using some sample data.
var data = [{"date":"Chennai","value":"53"},
{"date":"Banglore","value":"165"},
{"date":"Pune","value":"269"},
{"date":"Ban","value":"344"},
{"date":"Hyderabad","value":"376"},
{"date":"HYd","value":"410"},
{"date":"Gurugram","value":"421"},
{"date":"Che","value":"376"}];
Able to display Strings(Ex: "Bangalore" on X-axis and values on Y-axis but I need to display Strings on Y-axis and values on X-axis.
var margin = {top: 20, right: 20, bottom: 70, left: 60},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var y = d3.scale.ordinal().rangeRoundBands([height,0]);
var x = d3.scale.linear().range([0, width]);
var data = [{"date":"Chennai","value":"53"},
{"date":"Banglore","value":"165"},
{"date":"Pune","value":"269"},
{"date":"Ban","value":"344"},
{"date":"Hyderabad","value":"376"},
{"date":"HYd","value":"410"},
{"date":"Gurugram","value":"421"},
{"date":"Kadapa","value":"405"},
{"date":"Che","value":"376"}]
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(12);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10);
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 + ")");
data.forEach(function(d) {
d.value = +d.value;
});
y.domain(data.map(function(d) { return d.date; }));
x.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.value); })
.attr("width", function(d) { return width - x(d.value); })
.attr("y", function(d) { return y(d.date); })
.attr("height", y.rangeBand())
.attr("title",function(d) { return y(d.value); });
svg.selectAll("bar")
.data(data)
.enter()
.append("text")
.attr("class","label")
.attr("y", (function(d) { return y(d.date) + y.rangeBand()/2 ; } ))
.attr("x", function(d) { return x(d.value) + 1; })
.attr("dx", ".75em")
.text(function(d) { return d.date; });
When I tried with the above code bars are coming as Horizontal but need as vertical.
Please help me on this.
Thanks in Advance.
You should be more accurate with your "height/x/y" manipulations :)
D3 uses coordinate space where x=0 and y=0 coordinates fall on the bottom left.
I've changed some of your code:
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x",function(d) { return x(d.value); })
.attr("width", y.rangeBand())
.attr("y", function(d) { return height - y(d.date); })
.attr("height", function(d) { return y(d.date); })
.attr("title",function(d) { return y(d.value); });
and I've got the result
Bars are in the right places.
I believe you can figure out with titles by your own, if no - you know what to do
ps: http://jsfiddle.net/om42ts61/
Since the values are dynamically appended it is better to use y-axis for the values.
I'm following a long Mike Bostocks' Let's make a Bar Chart tutorial and I'm stuck after I decided, that my bars need some text.
As far as I understood it, rect cannot contain text elements, so I need to create a grouping and wrap both, text and rect in it. The problem is, that my code refuses to render the groupings (and the bars for that matter).
In fact, it does not even execute the section to append a <g> element. If I throw in a console.log or alert into the callback it does never get called.
Full code for reference below. The part in question is:
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x(d.letter) + "," + y(d.value) + ")";
});
Full code:
var margin = { top: 20, right: 30, bottom: 30, left: 40 }, width = 960, height = 500;
var x = d3.scaleBand()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.ticks(10, "%").tickPadding(11);
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 + ")");
d3.tsv("../data.tsv", type, function (err, data) {
x.domain(data.map(function (d) {
return d.letter;
}));
//set domain of scale, it is now known
y.domain([0, d3.max(data, function (d) {
return d.value;
})]);
//append the xAis
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height) + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
var bar = chart.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + x(d.letter) + "," + y(d.value) + ")";
});
bar.append("rect")
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.bandwidth());
});
function type(d) {
//coerce property to be number ->
//find out what the difference between + and Number() is.
d.value = +d.frequency;
return d;
}
So, where did I go wrong and why?
You have two problems, the first one being a simple selection order and the second one being conceptual.
The first problem is this: when you write
var bar = chart.selectAll("g")
for creating your groups, you're selecting groups that already exist in your SVG, which are the axes and an initial group added to the SVG.
So, select something else, something that doesn't exist:
var bar = chart.selectAll(".foo")
Your second problem is conceptual: although you're correct about being impossible to append a text to a rect, you don't need g elements to achieve what you want. Just create a rect selection and a text selection, and append both to the SVG.
But if you want to add the groups, this is what you have to do.
First, in the data binding, select something that doesn't exist:
var groups = chart.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.letter) + ",0)";
});
Translate only the x position of the groups, the y position of their elements will be set individually.
Then, create the bars:
var bar = groups.append("rect")
.attr("y", function(d) {
return y(d.value);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
});
And finally your texts:
var text = groups.append("text")
.attr("y", function(d) {
return y(d.value) - 6;
})
.attr("x", x.bandwidth() / 2)
.attr("text-anchor", "middle")
.text(function(d) {
return d.value
});
This is a demo using your code and fake data:
var data = d3.csvParse(d3.select("#csv").text());
data.forEach(function(d) {
d.value = +d.value
});
var margin = {
top: 20,
right: 30,
bottom: 30,
left: 40
},
width = 960,
height = 500;
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.3);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(x),
yAxis = d3.axisLeft(y)
.ticks(10, "%").tickPadding(11);
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 + ")");
x.domain(data.map(function(d) {
return d.letter;
}));
//set domain of scale, it is now known
y.domain([0, d3.max(data, function(d) {
return d.value;
})]);
var groups = chart.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.letter) + ",0)";
})
var bar = groups.append("rect").attr("y", function(d) {
return y(d.value);
})
.attr("width", x.bandwidth())
.attr("height", function(d) {
return height - y(d.value);
});
var text = groups.append("text")
.attr("y", function(d) {
return y(d.value) - 6;
})
.attr("x", x.bandwidth() / 2)
.attr("text-anchor", "middle")
.text(function(d) {
return d.value
});
//append the xAis
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height) + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
pre {
display: none;
}
rect {
fill: teal;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg class="chart"></svg>
<pre id="csv">letter,value
A,.08167
B,.01492
C,.02782
D,.04253
E,.12702
F,.02288
G,.02015</pre>
I need to refactor a bar chart to look more like the designs.
https://jsfiddle.net/yu2qzxsn/
Do I need to also provide the chart here with metadata like if the xaxis will be a date or a number field? I think the designer would be expecting thinner bars.
var yLabel = "";
var margin = methods.getMargin();
var minLimit = 0;
var maxLimit = d3.max(data, function(d) { return d.value;} );
methods.setDimensions(w, h, margin);
methods.setX();
methods.setY();
var svg = d3.select(methods.el["selector"]).append("svg")
.attr("class", "barchart")
.attr("width", methods.width + margin.left + margin.right)
.attr("height",methods.height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
methods.x.domain(data.map(function(d) { return d.label; }));
methods.y.domain([minLimit, maxLimit]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + methods.height + ")")
.call(methods.xAxis);
svg.append("g")
.attr("class", "y axis")
.call(methods.yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text(yLabel);
this.barrects = svg.append("g")
.attr("class", "barrects")
.attr("transform", "translate(0,0)")
methods.animateBars(data);
function type(d) {
d.value = +d.value;
return d;
}
I have a csv, and i want a separate chart for every brand + date.
date,Apple,Google,Amazon,Microsoft,IBM,Facebook
2015-08-11,113.489998,690.299988,527.460022,46.41,155.509995,93.620003
2015-08-10,119.720001,663.140015,524,47.330002,156.75,94.150002
2015-08-07,115.519997,664.390015,522.619995,46.740002,155.119995,94.300003
2015-08-06,115.129997,670.150024,529.460022,46.619999,156.320007,95.120003
2015-08-05,115.400002,673.289978,537.01001,47.580002,157.899994,96.440002
For now I can create this code for every brand, and i get 6 separate charts. But I think there must be a simple solution for this.
// Adds the svg canvas
var chart1 = 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 + ")");
// Get the data
d3.csv("data1.php", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.m_data = +d.mrr;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([d3.min(data, function(d) { return d.mrr; }), d3.max(data, function(d) { return d.mrr; })]);
// Add the valueline path.
chart1.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
chart1.append("g")
.attr("class", "y axis")
.call(yAxis);
chart1.append("text")
.attr("x", width / 2 )
.attr("y", 0)
.style("text-anchor", "middle")
.text("mrr");
});
For now put that in function
add_chart("chart1",'mrr');
add_chart("chart2",'arr');
function add_chart(id,field_name){
console.log(id+', ' + field_name );
var my_object = {};
// Adds the svg canvas
my_object[id] = 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 + ")");
// Get the data
d3.csv("data1.php", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.m_data = +d[field_name];
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([d3.min(data, function(d) { return d[field_name]; }), d3.max(data, function(d) { return d[field_name]; })]);
// Add the valueline path.
my_object[id].append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
my_object[id].append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
my_object[id].append("g")
.attr("class", "y axis")
.call(yAxis);
my_object[id].append("text")
.attr("x", width / 2 )
.attr("y", 0)
.style("text-anchor", "middle")
.text(field_name);
});
}
I am using this multiline graph but so far have failed to generate data value labels on every tick (for every day).
<script>
var margin = {top: 30, right: 40, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%m-%y").parse;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(7);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(7);
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.sev3); });
var valueline2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.sev4); });
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 + ")");
// Get the data
d3.csv("data.tsv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.sev3 = +d.sev3;
d.sev4 = +d.sev4;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return Math.max(d.sev3, d.sev4); })]);
svg.append("path") // Add the valueline path.
.attr("class", "line")
.attr("d", valueline(data));
svg.append("path") // Add the valueline2 path.
.attr("class", "line")
.style("stroke", "red")
.attr("d", valueline2(data));
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[0].sev4) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Sev4");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[0].sev3) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "steelblue")
.text("Sev3");
});
</script>
Data.tsv
date,sev3,sev4
20-02-15,0,0
19-02-15,0,0
18-02-15,0,0
17-02-15,481,200
16-02-15,691,200
15-02-15,296,200
14-02-15,307,200
The code above gives this:
And THIS is what i am trying to accomplish
I understand that i must use .append("text") and position the text at about the same x,y coords as the data point and pull the value from the data to feed into the "text" but i am having difficulties in integrating that concept.
I suspect that the selection would occur with valueline.append ? I have looked at a HEAP of examples, i dont thing a linegraph with data value labels exists, if it does please point me to it :)
Any thoughts ?
Your text will not be visible, as it is located outside the boundaries of your svg: you added a group that is translated of margin.left, and the put your x at width+3, which means located at width+3+margin.left of the left border of your svg.
Try replacing your append text with something like:
svg.append("text")
.attr("transform", "translate(" + (width/2) + "," + y(data[0].sev4) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Sev4");
svg.append("text")
.attr("transform", "translate(" + (width/2) + "," + y(data[0].sev3) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "steelblue")
.text("Sev3");
I did not test it, so I cannot guarantee, but your code seems fine, that's the only thing I see.
Result of this add:
EDIT
After your clarifications, here is a plunk: http://plnkr.co/edit/lDlseqUQQXgoFwTK5Aop?p=preview
The part you will be interested in is:
svg.append('g')
.classed('labels-group', true)
.selectAll('text')
.data(data)
.enter()
.append('text')
.classed('label', true)
.attr({
'x': function(d, i) {
return x(d.date);
},
'y': function(d, i) {
return y(d.sev3);
}
})
.text(function(d, i) {
return d.sev3;
});
This will draw your labels. Is it the result you try to achieve?