How to limit d3.svg.axis to integer labels? - javascript

Is there any possibility to limit the number of d3.svg.axis integer labels displayed on the graph? Take for instance this graph. There are only 5 sizes here: [0, 1, 2, 3, 4]. However, the ticks are also displayed for .5, 1.5, 2.5 and 3.5.

You should be able to use d3.format instead of writing your own format function for this.
d3.svg.axis()
.tickFormat(d3.format("d"));
You can also use tickFormat on your scale which the axis will automatically use by default.

I've realised that it is enough to hide these values, rather than to completely exclude. This can be done using the tickFormat (https://github.com/mbostock/d3/wiki/Formatting#wiki-d3_format) as such:
d3.svg.axis()
.tickFormat(function(e){
if(Math.floor(e) != e)
{
return;
}
return e;
});

The use of d3.format("d") as tickFormat might not work in all scenarios (see this bug). On Windows Phone devices (and probably others) imprecise calculations may lead to situations where ticks are not correctly detected as integer numbers, i. e. a tick of "3" may be internally stored as 2.9999999999997 which for d3.format("d") is not an integer number.
As a result the axis is not displayed at all. This behaviour especially becomes apparent in tick ranges up to 10.
One possible solution is to tolerate the machine error by specifying an epsilon value. The values do not have to be exact integers this way, but may differ from the next integer up to the epsilon range (which should be way lower than your data error):
var epsilon = Math.pow(10,-7);
var axis = d3.svg.axis().tickFormat(function(d) {
if (((d - Math.floor(d)) > epsilon) && ((Math.ceil(d) -d) > epsilon))
return;
return d;
}
Another solution, which is also mentioned in other threads, is to specify a fixed tick count and then round the ticks (which now should be "almost" integer) to the nearest integer. You need to know the maximum value of your data for this to work:
var axis = d3.svg.axis().ticks(dataMax).tickFormat(d3.format(".0f"))

Related

Bubble chart c3js

Following the c3js documentation there is no option for Bubble chart. One workaround for that is to setup scatter plot and specify point radius, but all of the bubbles will be the same height.
point = {
r: function(d) {
var num = d.value;
return num
},
Adding the value of axis inside the r solve the problem, but now the problem is how to setup very high or very low values ? For e.g if there is 1 000 000 value the whole chart will be colored. Is there any simple workarounds for that ?
First of all, set r to return the square root of your chosen variable e.g. return sqrt(num), that way a circle representing a data point 100 times the size of another has 100, not 10,000, times the area (area=pi r2 and all that)
If the numbers are still too big use a linear scale to restrict them to a usable size:
rscale = d3.scale.linear().domain([1,1000]).range([0,10])
and then return rscale(sqrt(num))
If your problem is to represent large and small values on the same chart so small values don't disappear and large values don't exceed the chart size look at using a d3 log scale:
rscale = d3.scale.log().base(10).domain([1,1000]).range([0,10])
Of course on a log scale the areas aren't linearly proportionate any more so whether the sqrt step is necessary is debatable. If you don't just remember to adjust the domain to account for this - change it to domain([1,1000000])
if you don't know the size of your numbers beforehand it will be worthwhile looping through your dataset to pick out the min and max to plug into the domain value: domain([your_min, your_max]). my examples above all assume a max of one million.
Here's an example I forked on jsfiddle, numbers from a few hundred to over a hundred thousand are displayed using a log scale and all are visible but the differences are still obvious:
http://jsfiddle.net/m9gcno5n/

d3 js axis Percentage Values with decimals

I'm using d3 to create a graph - (still learning both of those). I'd like my Y-axis to display values in percentages. The min and max values on the Y-axis are allocated dynamically, so the axis scale could sometimes be 25% to 38% or sometimes even 13% to 16%.
When there is a relatively larger range such as 25% to 28%, I'm fine with the numbers appearing as they are, i.e. 25%, 28%, 31% ....
However, for a really small range, I notice it appears as 13%, 14%, 14%, 15%, 15%,... (numbers repeating, probably because behind the scenes they might be something like say, 14.2% and 14.8%).
In such cases, I'd like the numbers to appear with 1 decimal place specified, such as 13.5%.
I know I can specify
.tickFormat(d3.format(".1%"))
and this gives me what I need in that case. The problem is when I have a larger scale, it essentially gives me 25.0%, 28.0%, 31.0%,... and in this case, I'd like no decimal precision.
Is there a simple way I can handle each scenario? Any guidance would be most appreciated.
As mentioned in the comments, you could check if the range is big enough and add the decimal point format depending on that but that wouldn't be very clean. I suggest you pass your own function as the tickFormat that applies the format depending if the value has a decimal portion or not. Something like:
// defined your formatters somewhere above
var decimalFormatter = d3.format(".1");
myAxis.tickFormat(function(d) {
if (!Number.isInteger(d)) {
d = decimalFormatter(d); // add the decimal point for non-integers
}
return d + '%';
}
Sample code:
// defined your formatters somewhere above
var decimalFormatter = d3.format(".1");
var tickFormat = function(d) {
if (!Number.isInteger(d)) {
d = decimalFormatter(d); // add the decimal point for non-integers
}
return d + '%';
};
console.log(tickFormat(12.5)); // 12.5%
console.log(tickFormat(14)); // 14%
console.log(tickFormat(15)); // 15%
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Where you can adjust the function above in any specific way you might need. For example, if you are using strings instead of numbers, you will need to first convert to a number and then use Number.isInteger because it will always return false for a string.
You already have the solution for numbers of a small scale: d3.format(.1%). For numbers of a large scale, you can use d3.format(~%). This will automatically delete the trailing zero for, say, 25.0%, 28.0%, 31.0%,...
The documentation of d3-format mentions that:
The ~ option trims insignificant trailing zeros across all format types. This is most commonly used in conjunction with types r, e, s and %.

Angular-nvd3: same distance between data on x scale

By default scale of x axis is calculated from values. This gives uneven distance between two adjasted points. Like for example if I have an array of values like [1,2,5], there will be different distance on x axis for point, and also x axis labels will contain some other values, like 1,2,3,4,5. In case on dates displayed on x axis there may be cases when two equal dates are printed, looks ugly.
Take a look at this plunker. If you maximize your browser window, you could see that x axis labels have duplicates (like 02/09/2015 is visible 2 times on my screen). Also the distance between point is different.
How can I:
Make so that no duplicate x axis labes are present?
Distance between points is equaly distributed based in graph's width (not scaled based on values)?
You can explicitly set tickValues() and specify what ticks you want to show.
Alternatively, ticks() is more flexible, but gives you less control.
(Search for "D3 duplicate dates". This one may be helpful for example).
The reason the dates are repeating is that they are in fact different (equally spaced) timestamps that occur on the same day, since the spacing is < 24 hours. If you want to label with distinct dates, you could select a specific time on each day for the tick to fall on.

D3: What does the scale do when passed an axis?

In d3, if you want to create an axis you might do something like this:
var xAxis = d3.svg.axis()
.scale(x)
where x is a scale function. I understand that the domain of x defines the start and ending values for the ticks. I'm having trouble understanding how the range of x changes the resulting axis. What does the domain map to in the context of an axis.
Think about what one must do to create a visual representation of any data set. You must convert each data point (e.g. 1 million dollars) into a point on the screen. If your data has a minimum value of $0 and maximum value of $1000000, you have a domain of 0 to 1000000. Now to represent your data on a computer screen you must convert each data point (e.g. $25) into a number of pixels. You could try a simple 1 to 1 linear conversion ($25 converts to 25 pixels on the screen), in which case your range would be the same as your domain = 0 to 1000000. But this would require a bloody big screen. More likely we have an idea of how large we want the graphic to appear on the screen, so we set our range accordingly (e.g. 0 to 600).
The d3 scale function converts each data point in your dataset into a corresponding value within your range. That enables it to be presented on the screen. The previous example is a simple conversion so the d3.scale() function is not doing much for you, but spend some time converting data points into a visual representation and you will quickly discover some situations where the scale function is doing a lot of work for you.
In the particular case of an axis, the scale function is doing exactly the same thing. It is doing the conversion (to pixels) for each 'tick' and placing them on the screen.

How do we change the tick values generated by a linear scale in a d3.js line plot?

I only had 5 values[1,2,3,4,5] as my y - coordinates in the d3.js line plot. But, I end up getting more values [0.5,1,1.5,2,2.5,3,3.5,4,4.5,5] Is there a way to edit the d3.js file or the html file inorder to plot the values as per my requirement?
The tick marks created by a d3 axis can be controlled in two ways:
Using axis.tickValues(arrayOfValues) you can explicitly set the values that you want to show up on the axis. The ticks are positioned by passing each value to the associated scale, so the values should be within your scale's domain. This works for any type of scale, including ordinal scales, so long as the values you give are appropriate to that scale.
Alternately, using axis.ticks(parameters) you can modify the way the scale calculates tick marks. The types of parameters you can use depends on the type of scale you're using -- the values you specify will be passed directly to the scale's .ticks() method, so check the documentation for each scale type. (Parameters will be ignored for ordinal scales, which don't have a ticks() method.)
For linear scales, the scale.ticks() method accepts a number as a parameter; the scale then generates approximately that many ticks, evenly spaced within the domain with round number values. If you do not specify a tick count, the default is to create approximately 10 ticks, which is why you were getting ticks on 0.5 intervals when your domain was from 0 to 5.
So how do you get the behaviour you want (no decimal tick values)?
Using .tickValues(), you would create an array of unique Y-values to be your ticks:
var yValues = data.map(function(d){return d.y;});
//array of all y-values
yValues = d3.set(yValues).values();
//use a d3.set to eliminate duplicate values
yAxis.tickValues( yValues );
Be aware that this approach will use the specified y values even if they aren't evenly spaced. That can be useful (some data visualization books suggest using this approach as an easy way of annotating your graph), but some people may think your graph looks messy or broken.
Using .ticks(), you would figure out the extent of your Y domain, and set the number of ticks so that you do not have more tick marks then you have integers available on your domain:
var yDomain = yScale.domain();
yAxis.ticks( Math.min(10, (yDomain[1] - yDomain[0]) );
This will create the default (approximately 10) ticks for wide domains, but will create one tick per integer value when the difference between the max and min of your domain is less than 10. (Although the tick count is usually approximate, the scale will always prefer integer values if that matches the tick count specified.)
Yes you can also try
yAxis.ticks(5).tickFormat(D3.numberFormat(",d"));
It does the trick of eliminating the decimal numbers, does not effect number of ticks
Here is a good resource for the format of the numbers using D3.

Categories