I have chart with next properties:
barChart
.width(width)
.height(height)
.dimension(dim)
.group(group)
.x(d3.scale.ordinal())
.xUnits(dc.units.ordinal)
My axis tick looks like "ZZxBob", "PxBob"
How to use split function by 'x' to each tick?
The axes are generated entirely by D3.
You can use chart.xAxis() to retrieve the x axis object, and then axis.tickFormat() to change the way it's displayed.
So, e.g. to display the part before x:
barChart.xAxis()
.tickFormat(function(l) { return l.split('x')[0]; })
Related
I want to display percentages on the y-axis of a dc.js barchart that can dynamically change when filtering the chart itself or some other charts.
Here is my case:
var ndx = crossfilter(dataCsvInitial);
var all = ndx.groupAll();
var accCredLimDim = ndx.dimension(function(d) { return d.acct_curr_crlimit;});
Then, I group by bins:
var value_range_credlim = maxCredLim - minCredLim; // defined earlier...
var nb_of_bins_credlim = 50,
bin_width_credlim = value_range_credlim/nb_of_bins_credlim;
var accCredLimGrp = accCredLimDim.group(function(d) {return Math.floor(d/bin_width_credlim)*bin_width_credlim;});
And draw my bar chart:
var creditBar = dc.barChart("#creditDistrib");
creditBar
.width(600)
.height(250)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.dimension(accCredLimDim)
.group(accCredLimGrp)
.transitionDuration(500)
.x(d3.scaleLinear().domain([minCredLim, maxCredLim]))
.xUnits(function(){return nb_of_bins_credlim;})
.elasticY(true)
.brushOn(true)
.xAxisLabel("Credit Limit")
I succeeded to do what I want initially just by customizing the yAxis().tickFormat() attribute of the bar chart, by dividing the tick value by the total number of rows being filtered at the moment:
creditBarChart.yAxis().tickFormat(function (d) {
return 100*d/all.value() + '%';
});
And I recompute these ticks every time a transition is being made, because my y-axis is elastic:
creditBar
.on("pretransition", function(){
creditBar.yAxis().tickFormat(function (d) {
if (!creditBar.hasFilter()){
return Math.trunc(100*d/all.value()) + '%';
}
});
});
As you can see, I don't update the ticks when the bar chart is being filtered. Indeed, when it is filtered I want the ticks to remain unchanged, as the y-axis should not change. However, because I am dividing the tick value by all.value() this rule cannot work when filtering the bar chart itself. The displayed percentages are obviously wrong.
This question is quite close to solving my problem: link but it is applicable only for categorical bar chart...
How can I display percentages on the y-axis ticks, that can change of values when filtering other charts and also when filtering the chart itself?
Is there a sort of all.value() that would be computed excluding the effect of filtering a specified chart?
Thanks!
Since you want the groupAll not to observe the filter on this chart, you should use the chart dimension's groupAll not the one on the crossfilter object. From the docs:
Note: a grouping intersects the crossfilter's current filters, except
for the associated dimension's filter. Thus, group methods consider
only records that satisfy every filter except this dimension's filter.
So, if the crossfilter of payments is filtered by type and total, then
groupAll by total only observes the filter by type.
That's kind of a mouthful, but I hope the intention is clear.
var accCredLimDim = ndx.dimension(function(d) { return d.acct_curr_crlimit;});
var all = accCredLimDim.groupAll();
Once you do that, you don't have to put an if statement in your tickFormat definition:
creditBar
.on("pretransition", function(){
creditBar.yAxis().tickFormat(function (d) {
return Math.trunc(100*d/all.value()) + '%';
});
});
The if statement was incorrect for a couple of reasons. First, there could be a filter on this chart and also filters on the other charts. Second, any accessor you call, like tickFormat, needs to return a value every time it is called. But this would return undefined if there was any filter on this chart, because that is the default return value in JS.
I would like to create a bar chart based on dates in x-axis. Labels should be displayed as month (i.e. Jan, Jan'17 - preferred). Within my data I have always first date of following months, i.e. 01Jan, 01Feb, 01Mar. I have created a chart but I am not able to make it aligned.
var chart = dc.barChart("#" + el.id);
var chCategory = ndx.dimension(function(d) {return d[chCategoryName];});
chValues = chCategory.group().reduceSum(
return parseFloat(d[chValueName]);});
//set range for x-axis
var minDate = chCategory.bottom(1)[0][chCategoryName];
var maxDate = chCategory.top(1)[0][chCategoryName];
chart
.width(800)
.height(200)
.x(d3.time.scale().domain([minDate,maxDate]))
.xUnits(d3.time.months)
.dimension(chCategory)
.group(chValues)
.renderHorizontalGridLines(true)
// .centerBar(true) //does not look better
.controlsUseVisibility(true)
.ordinalColors(arrColors)
.transitionDuration(1000)
.margins({top: 10, left: 80, right: 5, bottom: 20})
I have already read post: dc.js x-axis will not display ticks as months, shows decimals instead
but I am not able to implement it in a way that will keep correct sorting for different years.
dc.js takes the domain pretty literally - the x axis stretches exactly from the beginning to the end, disregarding the width of the bars or their placement. It's a design bug.
Here are two workarounds.
keep bars centered and add padding
If you're using elasticX you can manually correct it like this:
chart.centerBar(true)
.xAxisPadding(15).xAxisPaddingUnit('day')
If you're just setting the domain manually, that's
minDate = d3.time.day.offset(minDate, -15);
maxDate = d3.time.day.offset(maxDate, 15);
align the ticks to the left of bars and correct the right side of the domain
You don't say what problem you run into when you don't center the bars. But I know the right bar can get clipped.
If you want the elasticX effect, you can implement it manually like this, offsetting the right side by a month (example):
function calc_domain(chart) {
var min = d3.min(chart.group().all(), function(kv) { return kv.key; }),
max = d3.max(chart.group().all(), function(kv) { return kv.key; });
max = d3.time.month.offset(max, 1);
chart.x().domain([min, max]);
}
chart.on('preRender', calc_domain);
chart.on('preRedraw', calc_domain);
Or without elasticX that's just:
maxDate = d3.time.month.offset(maxDate, 1);
I am working on dc.js, i have draw pieChart
pieChart.width(300)
.height(200)
.transitionDuration(1500)
.dimension(startValue)
.group(startValueGroup, "StartValue")
.radius(100)
.minAngleForLabel(0.5)
.legend(dc.legend().x(230).y(0))
.title(function(d) {
return d.key + ": " + d.value;
})
.renderTitle(true)
.on("filtered", function (chart) {
dc.events.trigger(function () {
//console.log(total_payment);
});
});
Now, I want to set specific x axis value. Currently, pieChart taking center position of define width and height. That mean it's taking position of (150,100). I want to change this position to (100, 100).
How can i change position of x axis as per above code?
You can't set this directly in the dc options, but you can modify these values after the chart has been rendered. Assuming that "pie" is the ID of the DOM element that you rendered the pie chart into, you can do
d3.select("#pie > svg > g").attr("transform", "translate(100,100)");
I know it is an old post and that the answer works perfectly but when the chart is part of a bigger system (several charts) I found easier to add the above command directly to the chart by doing this:
pieChart.on('renderlet', function (chart) {
chart.select("svg > g").attr("transform", "translate("100,100)");
});
Edit:
Actually I just found out you can do:
.cx(100)
.cy(100)
which will set the centre of the pie chart to 100, 100 before render.
Is it possible, using dc.js, to construct a bar chart where the stacks are not placed on top of each other? Currently, my code produces charts that look like this:
Instead I'd like each of the stack bars to start from y axis and not from the value where the previous stack value ends. This may/will lead to the bars overlapping, so perhaps adding transparency will help here. A simple css rule will probably work here:
.dc-chart rect.bar { opacity: 0.75; }
You likely want to build a composite chart that includes a bar chart for each category.
You can build a group for each color in your chart like this:
usaGroup = categoryDimension.group().reduceSum(dc.pluck('usa')),
russiaGroup = categoryDimension.group().reduceSum(dc.pluck('russia'));
Here is a jsFiddle to demsonstrate this: http://jsfiddle.net/djmartin_umich/nK6K7/
composite
.width(400)
.height(300)
.x(d3.scale.ordinal().domain([1,2,3]))
.xUnits(dc.units.ordinal)
.yAxisLabel("User Count")
.renderHorizontalGridLines(true)
.compose([
dc.barChart(composite)
.centerBar(true)
.gap(100)
.colors('red')
.dimension(categoryDimension)
.group(russiaGroup),
dc.barChart(composite)
.centerBar(true)
.gap(100)
.colors('blue')
.dimension(categoryDimension)
.group(usaGroup)])
.brushOn(false)
.render();
You'll need to play around with the styling... the blue and red combine into a purple color when the opacity is lowered.
I have the following dataset:
var data = [
{
"air_used": 0.660985,
"datestr": "2012-12-01 00:00:00",
"energy_used": 0.106402
},
{
"air_used": 0.824746,
"datestr": "2013-01-01 00:00:00",
"energy_used": 0.250462
} ...
]
And I want to draw a bar graph (for air_used) and line graph (for energy_used) that look like this:
My problem is that at the moment, with the x-scale I'm using, the graph looks like this - basically the bars are in the wrong position, and the last bar is falling off the chart:
Here is a JSFiddle with full code and working graph: http://jsfiddle.net/aWJtJ/4/
To achieve what I want, I think I need to amend the x-scale so that there is extra width before the first data point and after the last data point, and so that the bars are all shifted to the left by half the width of each bar.
Can anyone help me figure out what I need to do with the x-scale?
I've tried adding an extra month to the domain - that stops the last bar falling off the end of the graph, but it also adds an extra tick that I don't want, and it doesn't fix the position of the line graph and ticks.
If possible I want to continue to a time scale for the x-axis, rather than an ordinal scale, because I want to use D3's clever time-based tick formatters and date parsers, e.g. xAxis.ticks(d3.time.weeks, 2).
Expand your domain to be +1 and -1 month from the actual extent of your data. That will pad the graph with the extra months on either side and then update the bar width to add 2 to the count of data elements.
var barRawWidth = width / (data.length + 2);
See this fiddle: http://jsfiddle.net/reblace/aWJtJ/6/
If you want to hide the lower and upper boundary months, you can hack it like this: http://jsfiddle.net/reblace/aWJtJ/7/ by just adding and subtracting 20 days instead of a whole month, but there are probably more elegant ways to do it.
var xExtent = d3.extent(data, function(d) { return d.date; });
var nxExtent = [d3.time.day.offset(xExtent[0], -20), d3.time.day.offset(xExtent[1], 20)];
x.domain(nxExtent);
As pointed out in the comments, I think the best approach is to use d3.scale.ordinal. Note that using it doesn't prevent you from using d3.time parsers, but you need to take into account the bar width to align the line with the bars.
An example solution is here:
http://jsfiddle.net/jcollado/N8tuR/
Relevant code from the solution above is as follows:
// Map data set to dates to provide the whole domain information
var x = d3.scale.ordinal()
.domain(data.map(function(d) {
return d.date;
}))
.rangeRoundBands([0, width], 0.1);
...
// Use x.rangeBand() to align line with bars
var line = d3.svg.line()
.x(function(d) { return x(d.date) + x.rangeBand() / 2; })
.y(function(d) { return y(d.energy_used); });
...
// Use x.rangeBand() to set bar width
bars.enter().append("rect")
.attr("class", "air_used")
.attr("width", x.rangeBand())
...
Note that date parsing code has been moved up to have d.date available when creating the x scale. Aside from that, d3.time statements have not been modified at all.