Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid log zero in graph using d3.js

I am current working on library d3.js.I have line graph using Dynamic Line Graph Here we have option of drawing power linear and log. But my problem is that I might have some values zero in my data set and as log 0 is undefined so the code is unable to plot it. Here in my code the scale set like this

y = d3.scale.log().domain([0.1, max_y _value]).range([h, 0]).nice();

and this is how it is used

  lineFunction = d3.svg.line()
        .y(function(d) {
          return y(d);
        });

I know that its an odd question.But is there a way by which I can handle log 0 value so that if I have single zero value the rest of them are plotted correctly. Can I gave two domain and range in same statement (to handle 0 value) if yes than how will it bind to y axis?

Thanks any help will be appreciated.

like image 622
A_user Avatar asked Jul 04 '12 04:07

A_user


2 Answers

I solved my problem by using a clamp function. This is how I have changed the code:

y = d3.scale.log().clamp(true).domain([0.1, max_y _value]).range([h, 0]).nice();
like image 146
A_user Avatar answered Nov 05 '22 11:11

A_user


If you have a domain which has one of the domain boundaries as 0 log scale won't work for you. Also log scale provides you a tick() function with uncofigurable number of ticks.

My current assignment is to display data with arbitrary domains and ranges on a linear scaled scatter chart BUT optionally the user can change over to logarithmic scale. This includes the problematic [0,n] and [n,0] domains.

Here's a solution i came up with to handle these cases: The issue can be avoided if we are using a linear scale to project our domain to an arbitrary range with positive boundaries. I choose [1,10] but it can take any positive numbers. After that we can use a normal log scale.

d3.scale.genericLog = function() {
    return GenericLog();
};
function GenericLog() {
    var PROJECTION=[1,10];
    var linearScale, logScale;

    linearScale=d3.scale.linear();
    linearScale.range(PROJECTION);

    logScale=d3.scale.log();
    logScale.domain(PROJECTION);

    function scale(x) {
        return logScale(linearScale(x));
    }
    scale.domain = function(x) {
        if (!arguments.length) return linearScale.domain();
        linearScale.domain(x);
        return scale;
    };
    scale.range = function(x) {
        if (!arguments.length) return logScale.range();
        logScale.range(x);
        return scale;
    };
    scale.ticks = function(m) {
        return linearScale.ticks(m);
    };
    return scale;

}

Usage:

var scale1 = d3.scale.genericLog().domain([0,1]).range([500,1000]);
var scale2 = d3.scale.genericLog().domain([-10,0]).range([500,1000]);
scale1(0)   //500
scale2(-10) //500
scale2(0)   //100

I only needed the range(), scale(), ticks() functions so i only included these but it shouldn't take more than 5 minutes to implement all the others. Also: note that i was using the linear scale's ticks() values because I had to limit the number of ticks and that is easier with the linear scale.

EDIT: BE AWARE Depending on what you choose as PROJECTION it will distort your log scale. The wider interval you use will strecth your scale's lower part more.

like image 43
SandorRacz Avatar answered Nov 05 '22 11:11

SandorRacz