Search code examples
javascriptarraysnormalizationlogarithm

Managing logarithmic Array data for use on a HTML canvas


I have a data set in an array ranging from 0.00164737 to 0.0021966

What I am trying to do is get the shape fixed by logarithmic scale, then to normalize it ready for drawing on the canvas. Say normalized to 0,100 or -100,0,100 so I can work in pixels

To simply normalize (without doing a log function) between 0-100 this works lovely in a for loop:

normalized[i] = ( ( close[i] - min ) / dif ) * 100;

dif is the close array's max minus it's min toFixed(7)

Normalized

I am no mathematician but I think I am 'sort of close' with the log part, though I only see a visual difference when I apply a log function during normalization. This messes up the normalization obviously and It also produces Infinity or -Infinity and very unnecessarily large numbers

Out of view

Notice that the chart is now no longer manageable in pixels as it is not sitting on the canvas properly, so to see what I have done I add 150 to all the y points to bring it into view

Pushed into view

So When I do this I am disrupting/changeing things on the same line by applying a logarithmic function (I know this part is wrong)

function logWithBase(n,b){return Math.log(n)/Math.log(b);}

changing the line like so:

normalized[i] = logWithBase( ( close[i] - min ) / dif , 10 ) * 100;

Here is a small part of the data before and after I attempt to make it logarithmic:

Before:

0: "0.00168927"
1: "0.00168742"
2: "0.0016818"
3: "0.00168124"
4: "0.00168067"
5: "0.0016805"
6: "0.00168657"
7: "0.00168362"
8: "0.00166886"
9: "0.00165052"    // < why does 0.00165 cause -Infinity
10: "0.0016647"
11: "0.00166251"
12: "0.00166664"
13: "0.00167196"
14: "0.00167084"
15: "0.00167667"

Same set after:

0: 38.93017025063121
1: 36.805636182304255
2: 29.629674005179268
3: 28.845120702345767
4: 28.03173121401521
5: 27.786162417624155
6: 35.79352647194307
7: 32.08579894397015
8: 6.4429326997987175
9: -Infinity             // < WOW!!
10: -4.729377348896662
11: -12.015082123716525
12: 0.8395033133056131
13: 13.225477668471285
14: 10.8953699275865
15: 21.850168886727374

Maybe a related clue here

If I get the difference between min and max / 2 (I'll probably get a bad mark for this) and use it as my base (before I do the simple normalization) I see the chart upside down...

var logclose = [];

var base = ( Math.max.apply(this,close) - Math.min.apply(this,close) ) / 2;

for( var i = close.length - 1; i >= 0; i-- ){

    logclose[i] = logWithBase( close[i] , base );

    }

close=logclose;

Upside down

Conclusion; I think the last example says that the base should be somehow related to the arrays number range as it does have an effect.

how do I tame the logarithm to not be ridiculously large numbers then normalize it to fit the canvas area?

Please do not recommend I use a library (I prefer vanilla JavaScript where it can be used, plus, I would very much like to grasp this proccess)

Edit, here is the code:

var close=[data]; // for example data see this question (line: 19) as the full data will not fit here (over 10000 entries)

//###

var max=Math.max.apply(this,close);
var min=Math.min.apply(this,close);
var dif=(max-min).toFixed(7);

var normalclose=[];
var logAttemptClose=[];

function logWithBase(n,b){return Math.log(n)/Math.log(b);}

for(var i=close.length-1;i>=0;i--){

    normalclose[i]=((close[i]-min)/dif)*100;

    logAttemptClose[i]=logWithBase( ( close[i] - min ) / dif , 10 ) * 100; // <-- here bad code
    }

//drawing to canvas
var top=100;
var x=1000-100;
var pen=[];

pen[0]=document.getElementById("chart");
pen[0]=pen[0].getContext("2d");
pen[0].clearRect(0,0,1000,300);
pen[0].beginPath();
pen[0].moveTo(x,normalclose[normalclose.length-1]+top);

for(var i=close.length-1;i>=0;i--){
    x=x-(2);
    pen[0].lineTo(x,normalclose[i]+top);
    }

pen[0].strokeStyle='#000000';
pen[0].stroke();

Solution

  • Got Ya!!

    Ok, this is not the best fix but it works! If anyone can do this better please post..

    Add this to line 2 of the above code (where the comment '###' is):

    var Lmax=Math.max.apply(this,close);
    var Lmin=Math.min.apply(this,close);
    var Ldif=(Lmax-Lmin);
    var Logar=[];
    var infinity=[];
    for(var i=close.length-1;i>=0;i--){
        Logar[i]=Math.log((close[i]-Lmin)/Ldif);
        if(Logar[i]===-Infinity){infinity.push(i);}
        }
    close=Logar;
    

    Now we have the array at logarithmic scale but there is always ALWAYS -Infinity in there somewhere so we remove it. On the next line write...

    for(var i=0;i<=infinity.length-1;i++){close.splice(infinity[i],1);
    

    Then we can continue with the code moving on to simple normalization and we have a nice new log chart:

    Hacked