I have Statewise, sectorwise(Agriculture, Manufacturing, Mining etc.) & yearwise GDP data of India. I have created a dashboard that can be found at India GDP. Now in the barchart I want to draw a line indicating growth rate at each year. I think it can be done by composite chart but I don't know how to calculate dynamic growth rates each time a filter is applied. Can anybody provide a guidance.
You are right that there should be a standard way to do this. The problem is that it doesn't fit into dc.js's way of dealing with data through crossfilter, and there are any number of graphical objects you might want to overlay on a chart.
So, for now we have renderlets.
The idea of using a renderlet is to write a function that drops into lower-level d3 code once the chart has rendered. You might search around for the exact d3 code you want, but here is something which adds a single red line on top of the bars:
.on('renderlet', function(chart) {
var extra_data = [{x: chart.x().range()[0], y: chart.y()(10)},
{x: chart.x().range()[1], y: chart.y()(70)}];
var line = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate('linear');
var path = chart.select('g.chart-body').selectAll('path.extra').data([extra_data]);
path.enter().append('path').attr('class', 'extra').attr('stroke', 'red');
path.attr('d', line);
});
This draws the line from the left side to the right side, from Y value 10 to Y value 70, using the x and y scales to obtain the SVG coordinates.
I created a bar with extra line example.
Hopefully this will get you started!
Related
I'm just starting out with D3 and am quickly understanding that it's a pretty low level tool.
I'm using D3 to produce a Marimekko chart using this great example by Mike Bostock in
b.locks, which is in all honestly a way too advanced place to start for me, but I started using D3 because I need a Marimekko chart, so here I am.
The x-axis here has ticks, 0 to 100% with 10% intervals. If my understanding of these code excerpts is correct...
Set the x axis to a linear scale
var x = d3.scale.linear().range([0, width - 3 * margin]);
Give the x-axis 10 ticks
var xtick = svg.selectAll(".x").data(x.ticks(10))
In my usage case , I'd like to have the x-axis ticks at the irregular intervals inherent to a Marimekko chart, and the axis labels to be the category, rather than a percentage.
The desired behaviour, as far as x-axis labelling, can be illustrated by this b.locks example by 'cool Blue'
I've got as far as understanding that I need a ordinal axis rather than a linear one (as in this excerpt of cool Blue's code)
var padding = 0.1, outerPadding = 0.3,
x = d3.scale.ordinal().rangeRoundBands([0, width], padding, outerPadding);
How can I modify Mike Bostock's code to give me an example where the x-axis ticks label the column (ideally centrally), as opposed to providing a %age of the width?
I wouldn't say that D3 is that low level, since it has a lot of abstractions. However, D3 is not a charting tool (and, in that sense, it is low level...), which makes things hard for a beginner.
However, you're lucky: the changes needed here are minimal. First, you'll pass the correct data to the selection that generates the axis...
var xtick = svg.selectAll(".x")
.data(segments)
//etc..
... and then use the same math for the translate, but adding half the sum:
.attr("transform", function(d, i) {
return "translate(" + x((d.offset + d.sum / 2) / sum) + "," + y(1) + ")";
});
Of course, you'll print the key, not a percentage:
xtick.text(function(d) {
return d.key
});
Here is the updated bl.ocks: https://bl.ocks.org/anonymous/09a8881e5bab2b12e7fd46c90a63b3fd/fd7b1a7b20f8436666f1544b6774778e748934ba
I am developing a few graphs by using D3.js and I am facing a few issues as I am not really familiar with the framework yet.
My main requirement is to plot a graph of price X time and my data points could be over a million data points in one day. So my idea was to use a similar approach to what google maps does. Every time you make a zoom in, maps takes more data from the server and display it. I would do the same by taking a few points to draw the chart and as you go zooming in I would take more points and making the chart sharper. How can I archive this solution?
You could interlink the number of data points plotted with the zoom factor by using the zoom as a modulus. For example, let's say your zoom has 10 levels with 10 being the most zoomed out (earth in Google maps) and 1 the most detailed view (a single house) and assuming this function:
var scatter = d3.select("#svg").selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cy", function(d) {
return y(d.y);
})
.attr("cx", function(d) {
return x(d.x);
})
.attr("r", function(d, i) {
return i % zoom == 0 ? 3 : 0; // <== This is _the_ line.
});
The marked line goes through the data points and only if i % zoom == 0 the drawn circle will have a radius of 3, all other circles get 0 and thus are not visible.
Of course, this still goes through all of the data points available but I think there was something like .defined() in d3 to avoid getting a data point drawn. The same function as above could be used there.
I am making a scatterplot using D3.js and I am unable to apply a proper time scale to the X axis. All my data is within the same year (2016), in a 3 months span.
I usually do this in a different way, and I want to learn what I am doing wrong in this one, which has a different code structure as I usually use in D3.
Here is the code in JsBin
This is X axis code:
var xValue = function(d) {
return d.datadois;
},
xScale = d3.time.scale(),
xMap = function(d) {
return xScale(xValue(d));
},
xAxis = d3.svg.axis().scale(xScale).orient("bottom").ticks(10, ".f");
Wonder how can I apply the time scale properly, to render the months in my data.
I'm a complete noop to D3 and partly SVG, so I got a few basic questions.
First off, my code in question can be viewed at http://dotnetcarpenter.github.io/d3-test/ and I've used Simple Pie Chart example with D3.js and Pie Chart Update, II as examples to get a running start.
As you can see, the animation gets skewed in the end when the low path values switch to the higher values. This is obviously not what I want. I think I'm getting the order of calculations wrong but I'm not sure what to do. I'm using the code from the last example:
function change() {
//...
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// where arcTween is
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
Another issue is placing labels on the sectors. I've put the update stuff in the change function and is able to read out and only render them if the value is between 0 and 100. I can't however place them in any way. Looking at the first example, I figure that I could do something like this:
text.data(data)
.text(setText)
.attr("transform", function (d) {
// we have to make sure to set these before calling arc.centroid
d.innerRadius = 0;
d.outerRadius = radius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
Where text is a d3 selection and arc is: d3.svg.arc().outerRadius(radius)
But I get "Unexpected value translate(NaN,NaN) parsing transform attribute." warning in Firefox and the labels are written on top of each other.
I appreciate any help and hints. Thanks!
I finally figured it out.
Maintain sector order throughout an animation.
You'd be forgiven for thinking that object contancy had something do with it. I did. But it turns out to be much simpler than that.
Every pie chart is by default sorted by value. If you don't want to sort by value but e.g. by data list order, you just have to disable sorting.
var pie = d3.layout.pie() // get a pie object structure
.value(function(d) { // define how to get your data value
return d.value; // (based on your data set)
})
.sort(null); // disable sort-by-value
Positioning labels according to your chart
Basically, you need to calculate your label positions depending on the type of chart or graph, your trying to connect them to. In my case, it's a pie chart. So if I want d3 to help with the calculations, I need to tell centroid the inner and outer radius and, most importantly to my issue, the start and end angles. The latter was missing from my code. Getting these values is as simple as, calling our pie layout above with our dataset and then do a transform.
Note that you don't have to call .data() again if you created the SVG with d3 and already supplied your data wrapped in .pie() structure. That is, that you didn't select any existing SVG from your page.
var svg = d3.select("svg")
// do stuff with your svg
var pie = d3.layout.pie()
// set stuff on your layout
var text = svg.selectAll("text")
.data(pie(dataset)) // where dataset contains your data
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
});
I have to give credit to Philip Pedruco for helping me along the way.
Bonus info
Use viewBox if you want to position your SVG cross browser, not transform/translate.
// move pie to center
d3.select("svg").attr("viewBox", -radius + ","+ -radius +"," + size + "," + size)
I've recently discovered dc.js and have been trying to implement a simple bar chart using the bar chart example provided on d3.js's website: http://bl.ocks.org/mbostock/3885304.
However, as part of dc.js implementation, a crossfilter dimension and group is required.
So using a simple TSV file with "letters" and "frequencies", I've changed the code a bit to look as follows:
d3.tsv('testdata.tsv', function(error, data) {
var cf = crossfilter(data);
var dimension = cf.dimension(function(d){
return d.letter;
}
var group = dimension.group().reduce(
function(p,v){
p.frequency += v.frequency
},
function(p,v){
p.frequency -= v.frequency
},
function(){
return { frequency: 0 };
});
I'm a little confused about what I should be setting the valueAccessor to (on my bar chart), because when I set the valueAccessor to return "frequency", and I set my xAxis scale to ordinal with a domain of all "letters" in the data set, I get a rendered bar graph with almost all x-axis values ("A - Y") at the ZERO point and one x-axis value (i.e. "Z") at the end of the x-axis line.
I've tried setting the ranges, but it doesn't seem to do the trick. Any ideas on what I'm misunderstanding would be greatly appreciated!
Turns out I had to set the .xUnits property to dc.units.ordinal() in order to get the x-axis spaced out correctly!