Is it possible to change the x scale of a rowchart to sqrt? - d3.js

I want to change the scale of the x axis of a row chart because some values are close to 0 but others are over 100000. I have tried this:
chart.x([d3.scaleSqrt().domain([0, 75000]).clamp(true)])
but it doesn't work.
However, this same method does work with a barchart. Any suggestions of how I can solve this?
EDIT: What I meant by 'it doesn't work' is that it shows the rowchart but the x axis is what comes by default, so there is no rescaling of the axis.

First off, you've got an extra set of brackets - that should be
chart.x(d3.scaleSqrt().domain([0, 75000]).clamp(true))
Since you don't say what didn't work, I will guess that you got a "squished" chart.
Here is what I got when I first tried this:
Unlike other dc.js charts, the row chart will not set the range of the X scale when the scale is passed in by the user.
For other charts, dc.js will initialize the range based on the "effective width" - the width of the chart minus the left and right margins.
We can do the same:
chart.x(d3.scaleSqrt()
.domain([0, 75000])
.range([0,chart.effectiveWidth()])
.clamp(true)])
My example now looks like

Related

Format tick values in D3

I want to display values with floating point in my d3 chart, with two decimal places.
Data values set in floating point format as 12.385, 12.384, 13.627...
I create axis as yAxisR = d3.axisRight(y3).tickFormat(d3.format(".2f")) but in axis tick still integer values (12, 13).
If change format for some another value, for example ".0s", axis ticks changes but still not on the that need
https://jsfiddle.net/ochp2dmu/ red right axis
This works just fine for me in your fiddle.
yAxisR = d3.axisRight(y3).tickFormat(d3.format(".2f"))
Make sure that you add more of a margin on the right side of your chart in order to see the entire tick label on the right y-axis. It was overflowing off of the window for me, so I changed the right margin to be 40px.

D3.js: Mouse over between grid lines

I am creating a line chart with grid lines. Here's the similar example: http://bl.ocks.org/hunzy/11110940
I need to change the background of vertical space between X(2) to X(4), X(8) to X(10) and X(12) to X(14) on mouse hover. But I am not able to understand how to use D3 to reference those spaces between the grid lines.
There is nothing there to click on/hover over. The normal axis/grid creates lines, not rectangles. You would need to change the default behave of the axis objects to create "invisible, but clickable" rectangles in order to be able to attach a mouse event to those spaces.
I don't know if this is the recommended approach but it seems like it could work. After the axis has been created:
something
.attr('class','xaxis')
.call(xAxis)
You could select the ticks with something like this:
d3.select(svg).select('.xaxis g.tick').each(function(){
// this.transform will be "translate(X,Y)"
})
In the function you can query the existing properties of the g elements and extract the transform attribute which will contain the X and Y offset for the "tick". This can be used to determine one dimension of your rectangle objects. The other dimension is determined by the size of the other axis or something like that.

Birt: Formatting chart tooltip and adding aggregation to a chart tooltip?

I have three questions regarding Birt Tooltip
1) How do I format the number in the default tooltip (If I am not adding anything to the tooltip text). I tried to change the format of tooltip of Y Value Series but I realized that only works if I have put an expression in the tooltip of the Y Value series. I have not edited the tooltip of Y Value series, the chart will show the default data label instead and I am not sure how to format the default tooltip
2) I have plotted sum(revenue) on Y axis and months on x axis. I wanted a custom tooltip so I edited the tooltip expression of the Y Value series in chart. What I found was that the default tooltip was showing correct value but If I put the same expression row["revenue"] in the tooltip, the tooltip shows a data value that is much less than sum(revenue) that is plotted in the bar. I believe that this is because the tootltip is not aggregating the revenue? If Yes, then how do I put the sum(revenue) in the expression?
3) I wanted to add another information (count of units sold) so I created an aggregation count(unitssold). I called this cnt_unit_sold and added this to the tooltip of chart. The problem is that if I put my mouse over a month bar, the tooltip shows aggregated count for all months rather than that specific month. How can I add grouping to the aggregation so that it shows the aggregated count for each month rather than for all months
Regards
Arif
OK, so in case if someone has not found an answer to this question, here is the answer. In order to add aggregation to the tooltip, you need to create aggregate outside the chart's contains (for example create aggregation inside a grid). Then you can use that aggregation in the tooltip
Arif
I someone is still trying to achieve this, now you can use "valueData" instead.
row["dim1"]+" for "+row["period"]+" : "+valueData
in my example (the code goes in the tooltip expression) I asm using a stacked barchart, dim1 is the series used to stack values and period is the X series. Valuedata is the Y value (aggregated)

NVD3.js multiChart x-axis labels is aligned to multiple lines, but not multiple bars

This question relates to NVD3.js multiChart x-axis labels is aligned to lines, but not bars
I am using NVD3.js multiChart to show multiple lines and multiple bars in the chart. All is working fine, but the x-axis labels is aligned only to the line points, not bars. I want to correctly align labels directly below the bars as it should. But I get this:
As you can see - x-axis (example, 2014-Feb) is not aligned to Bars.
1) How to align x-axis labels to bars and lines at the same time?
2) I need this solution for NVD3.js or how to properly integrate.
I made jsFiddle: http://jsfiddle.net/n2hfN/28/
Thanks!
The problem here is that nv.models.multiChart uses a linear scale for its x-axis, and then when it draws the bars it calls nv.models.multiBar, which uses an ordinal scale with .rangeBands().
You can follow this mess through the source code:
First lets look at multiChart.js
HERE is where it sets the x-scale to be a linear scale.
HERE it calls the nv.models.multiBar model to create the bars.
If we jump over to have a look at multiBar.js
HERE it creates an ordinal scale, and HERE it sets the range of the scale using .rangeBands()
The result is that the ordinal scale used for placing the bars, and the linear scale used for the chart's axis do not align. Here's what the two scales look like on their own if plotted on an axis:
The solution would be to force the chart to render the line graphs and the x-axis in terms of the ordinal scale used by the bars. This would work in your case because the bars and the lines all use the same data for the x-axis. This is very simple to do if you are making your own chart and not relying on nvd3, as I showed in my answer to your previous question HERE. This is extraordinarily complicated to do if you're trying to work within nvd3, and many others have tried and failed to switch out the default scales used by nvd3 charts. Have a look at this issue on the nvd3 github page that has been open since January, 2013 for example.
I've tried a number of approaches myself to reuse the bars' ordinal scale, but with little success. If you want to poke around and try to brute-force it yourself, I can tell you that from my experiments I came closest when using chart.bars1.xScale().copy() to make a copy of the bars' scale, and set its domain and rangeBands. Unfortunately, since the chart's width is computed at render time, and I can't seem to create a hook into the chart.update function, it is impossible to set the rangeBands' extent to the correct values.
In short, if you can't live with the labels being offset, you're probably going to need to code up your own chart without nvd3, or else find a different type of layout for your visualization.
After playing around with the NVD3 v1.7.1 source code with the immensely helpful guidance offered by jshanley's answer, I think I've managed to come up with an answer (perhaps more of a kludge than a good solution).
What I did was to have the x-axis labels align with the bars, and have the line data points align with the bars.
1.1. To align the x-axis label, I shifted the x-axis to the right so that the first label appears underneath the middle of the first bar. I then shifted the last label to the left, so that it appears underneath the middle of the last bar. See code here. The amount to shift by is computed at drawing time using .rangeBand() and saved in a rbcOffset variable (I had to modify multiBar.js for this to work).
1.2. To align the line data points with the bars, a similar shift is also required. Luckily, this part is easy because scatter.js (which is used by line chart) comes with a padData boolean variable that does what we want already. So basically, I just set padData to true and the lines shift to align with the bars, see here.
In order to properly integrate with NVD3 and make everything look good, some additional changes are required. I've forked NVD3 on GitHub so you can see the complete solution there. Of course, contributions are welcome.
I use last solution and it runs. So, you can specify
lines1.padData(true)
in order to align lines too.
Same here, I used the last solution,it worked for me as well. Find the following line in multiChart.js
if(dataLines1.length){
lines1.scatter.padData(true); // add this code to make the line in sync with the bar
d3.transition(lines1Wrap).call(lines1);
}
I encountered the same problem and fixed it with below code:
at lines 7832 and 7878 replace
.attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
with :
var w = (x.rangeBand() / (stacked && !data[j].nonStackable ? 1 : data.length));
var sectionWidth = availableWidth/(bars.enter()[0].length - 1);
if(bars.enter().length == 2)
return 'translate(' + ((i-1)*w + i*w + (i*(sectionWidth - 2*w))) + ',0)';
else
return 'translate(' + ((i-0.5)*w + i*(sectionWidth - w)) + ',0)';
The first case handles multiple bars case while the second one handles single bar case.
lawry's solution works. Also if using interactive guidelines, you need to shift the interactive line to match the new scale. Modify:
if(useInteractiveGuideline){
interactiveLayer
.width(availableWidth)
.height(availableHeight)
.margin({left:margin.left, top:margin.top})
.svgContainer(container)
.xScale(x);
wrap.select(".nv-interactive").call(interactiveLayer);
//ADD THIS LINE
wrap.select(".nv-interactiveGuideLine")
.attr('transform', 'translate(' + rbcOffset +', ' + 0 + ')' +
'scale(' + ((availableWidth - rbcOffset*2)/availableWidth) + ', 1)');
}
in multiChart.js.

dc.js - is it possible to show the user grab handles upon page load

I'm building a basic dashboard for internal use. I've little experience with D3 / SVG etc so please excuse me if this is obvious:
Aim
I have a time series of values, one value per date for a full year. Inspired by the example on the dc.js wesbite, I'm displaying a 'mini' bar plot of the entire series, and allowing the user to select a range which in turn sets the limits of the axis scale range of a 'large' line plot.
By default (on page load / refreshAll()) I've set the limits of the large line plot to be the most recent 60 days.
The user is encouraged to set their desired range using the mini plot at the bottom, and when they do, the 'grab handles' appear and the chosen range is highlighted on the mini plot, for example:
My issue
On page load / refreshAll(), the grab handles and range highlighting aren't shown on the mini plot, for example:
Question
Is there a way I can set the grab handles to appear upon page load?
Here is how i do it by using crossfilter and dc.js. You can get the last x days by doing something like:
xrange = d3.extent(data, function(d) { return d.date; });
last60 = [d3.time.day.offset(xrange[1], -60), d3.time.day.offset(xrange[1], 1)]
Assuming you have a date in your data. Do this after reading the data with d3, but before feeding it into crossfilter.
Then for the bottom chart (mini plot) simply set the filter property to:
.filter(last60)
And also set the xrange for the top chart to the same range:
.x(d3.time.scale().domain(last60))
Something like this might also work for setting the xrange, but i havent had any success with that:
topchart.focus(bottomchart.filter())

Resources