Plotting logarithmic graph (Javascript) - javascript

This is a little tricky,
I'd like to generate some graph lines for a frequency spectrum.
e.g.
See how the x-axis graph lines change logarithmically in this way...
What i need is the maths to do this above. And then a way to plot x coordinates accurately upon it.
I want to be plotting frequencies between 20Hz to 16000Hz across the x-axis in this way.
(I'm not too worried about the drawing part I can use canvas, i'm just stuck on the maths)
I think i would then need a function to convert say 1525Hz into px (or%) to be plotted on it.
Many thanks

i'd use something like this (live example on jsFiddle):
var min_f = Math.log(20) / Math.log(10),
max_f = Math.log(16000) / Math.log(10),
range = max_f - min_f,
position_px = (Math.log(frequency) / Math.log(10) - min_f) / range * width_px

Related

D3 Color Scale confusion in mapping continuous input to predetermined discrete bins?

So I am trying to create a color scale with d3, but I'm just confused on what scale to use exactly. I'm coloring things based off pearson correlation and would like the following bins:
[-1,-0.5,-0.3,0,0.3,0.5,1]
Pearson correlation is continuous from -1 to 1, but the different values can mean higher correlations. For example:
high correlation: [-1,-.5] & [.5,1]
moderate correlation: [-.5,-.3] & [.3,.5]
low correlation: [-.3,0] & [0,.3]
I'm trying to make a diverging color scale with the following colors:
colors = ["#a6611a", "#dfc27d", "#f5f5f5", "#80cdc1", "#018571"]
Basically, for each level of correlation, I want it to be mapped to one of those exact colors. So, high correlations would be mapped to #a6611a and #018571, moderate ones would get mapped to #dfc27d and #80cdc1, and so on. I pretty much want:
continuous input => discrete output
But I'm confused on how to do this with d3 color scales.... I know there are the quantize and quantile scales as described here but I dont know if that's what I'm looking fo.
My understanding of those is that they take in a continuous input and split the domain into either uniform segments (quantize) or by domain quantiles... however thats not what I want. I want to put in a range from [-1,1] and always get #a6611a and #018571 for input in [-1,-.5] & [.5,1] and so on. Is this possible to do with either of those scales?
I tried:
var colorScale = d3.scale.quantile()
.domain([-1,-0.5,-0.3,0,0.3,0.5,1])
.range(colors);
But if I do that, I end up with colorScale(.29) = #80cdc1 when I want colorScale(.29) = "#f5f5f5".
I also tried a linear scale:
var colorScaleLinear = d3.scale.linear()
.domain([-1,-0.5,-0.3,0,0.3,0.5,1])
.range(colors);
But in that case, I will get my data mapped to colors that don't necessarily have to be one of my 5 colors. Example: colorScaleLinear(.29) = #058774
I'm just confused if there is a way to do this in d3, and I'm new to color scales so after spending hours on this I'm just not sure what to try... what color scale should I be using? Is there a way to set the output to be a discrete set of colors?
Any help would be greatly appreciated, thanks!!
EDIT Would threshold scales be the way to go?
You want to use d3.scale.threshold().
var q=d3.scale.threshold()
.domain([-1,-0.499999,-0.2999999,0.3,0.5,1])
.range( ["#a6611a","#a6611a", "#dfc27d", "#f5f5f5", "#80cdc1", "#018571","#018571"]);
a=[-0.5,-0.50001,-0.4999,-0.3,-0.2999,0,0.299,0.3001,0.5001,1];
a.forEach(function(i){
console.log(i, q(i));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
var q=d3.scale.threshold()
.domain([-1,-0.499999,-0.2999999,0.3,0.5,1])
.range( ["#a6611a","#a6611a", "#dfc27d", "#f5f5f5", "#80cdc1", "#018571","#018571"]);

Dealing with the inverted Y axis while graphing in Javascript?

I am using Javascripts built in canvas feature to draw a graph showing home loan payments, loan balance, and equity based on user input. I am not able to use any other form of graphing package, as the code is part of an assessment.
My graph is drawn by converting data to X and Y coordinates. When a loan price is input, some home loan payment equations calculate the total amount payed, which is divided by the canvas width to get a spacing variable. This spacing variable is used to convert dollar amounts into pixels on the canvas. A similar setup is used to get the years and months spacing pixels.
The problem I am having is that the Y axis on Javascript's canvas is inverted, with 0 being the top of the canvas and 280, my canvas height, being at the bottom. So far, I have been able to work around this, simply by swapping "+" and "-" operators, however, I am currently creating the code that draws the Loan Balance line on the graph, and the inversion is causing issues that I can't seem to solve. It may be something simple that I'm just not seeing, or it may be a more complex problem that needs to be solved, but either way, I can't figure it out.
X = 0; // same as before, iterators both set back to 0 for the new line.
iterator = 0;
c.beginPath // this next line is for loan balance, it starts at 300000 and goes down with each payment made, then back up with each bit of interest accrued.
// due to the fact that the y axis begins at the top, this means that the pixels for payments is added to the pixel count, and the interest accrued is taken away.
c.moveTo(0, loanLocation) // set starting point to x=0 y= loanLocation
while (X <= 510)// loan balance loop
{
X = X + 0.001; // iterates X by .001 each time, allowing an accurate subpixel resolution loop, see above for why this is needed.
iterator = iterator + 0.001;
if (iterator >= monthSpacing)
{
loanBalance = loanBalance - monthlyPayment + (monthlyInterest * loanBalance);
//alert(loanBalance);
//interestY =
//alert(interestY);
//alert(X + " " + monthSpacing);
loanY = loanY + paymentY - (loanY * monthlyInterest);
//alert(loanY);
//loanY = loanBalance * paySpacing;
c.lineTo(X, loanY);
iterator = 0;
}
}
c.strokeStyle = "black"
c.stroke(); // there is no fill for this line, so it is just left as a stroke.
This is the set of code which draws the line, above it are a few variables which are being used here:
var X = 0;
var iterator = 0;
var monthSpacing = yearSpacing / 12;
//alert(yearSpacing);
//alert(monthSpacing);
var monthlyInterest = interest/1200; // this gives the montly interest rate, the monthly interest pixel amount is below
//alert(monthlyInterest);//debugging, comment out.
var paymentY = monthlyPayment * paySpacing;
var interestY = monthlyInterest * paySpacing; // this is inaccurate, the interestY needs to be gotten by multiplying the remaining loan balance by the
//monthly interest each month.
//var interestY; // will be used further down, must be calculated monthly so cannot be set outside of the line drawing loops.
var totalY = 280;
var equityY = 280;
var loanBalance = loan;
var loanY = loanLocation;
When run I get a strange inversion of the desired outcome, I want the loan balance line to curve down towards zero, but instead, the curve is happening in the opposite direction, I have tried two different ways to get the coordinates, the loanBalance way, which involved working with dollar values and converting that to pixels, and the loanY way, which involved working with pixel values directly.
loanBalance provided a line which was the exact inverse of the desired line, it began at the loan value, and curved upwards in the exact opposite direction to what I want, I am confident that the math I'm using for the loanBalance method is accurate, I simply cannot think of a way to convert that dollar value into pixels due to the inverted nature of the Y axis.
loanY provides a line which is headed "down", but is curving downwards at an increasingly shortened rate, this leads me to believe that while the subtraction (addition due to the inversion) of monthly repayments is accurately being calculated, the addition (subtraction) of monthly interest is being calculated incorrectly. Multiplication cannot be simply replaced with division like addition and subtraction can, so converting this value to pixels is proving difficult. The line drawn by the loanY way is definitely being affected by the inversion, but is not a perfect inverse of the desired line, the math being used for that way is clearly very wrong.
Ideally, I'd like to find a way to use the loanY way, it is consistent with the rest of the program, and can be used when not working with such obvious values as dollars. If I have to though, I will use the loanBalance way.
If you aren't entirely certain what I'm asking, or what the code being used is, I can post the program in it's entirety if that would help. I've not done that yet as I don't want to clutter the question more than I already have.
You can change to a Cartesian coordinate system like this:
// get a reference to your canvas element (eg it might have id='myCanvas')
var canvas=document.getElementById('myCanvas');
// get the context for the canvas
var context=canvas.getContext('2d');
// vertically flip the canvas so its Y origin is at the bottom
context.setTransform(1,0,0,-1,0,canvas.height);
This makes y==0 at the bottom of the canvas and increases upward.
If you're using other transformations, then put this transformation before the others.

Fit the cumulative percentage line to the sorted histogram output with d3 for a pareto chart histogram

This is what I have so far: https://gist.github.com/daluu/fc1cbcab68852ed3c5fa and http://bl.ocks.org/daluu/fc1cbcab68852ed3c5fa. I'm trying to replicate Excel functionality.
The line fits the default histogram just fine as in the base/original http://bl.ocks.org/daluu/f58884c24ff893186416. And I'm able to sort the histogram in descending frequency, although in doing so, I switched x scales (from linear to ordinal). I can't seem to map the line to the sorted histogram correctly at this point. It should look like the following examples in terms of visual representation:
the Excel screenshot in a comment in my gist referenced above
the pareto chart sorted histogram in this SO post
the pareto chart (similar to but not exactly a sorted histogram) made with d3 here
What's the best design approach to get the remaining part working? Should I have started with a single x scale and not need to switch from linear to ordinal? If so, I'm not sure how to apply the histogram layout correctly using an ordinal scale or how not to use a linear x scale as a source of input to the histogram layout and still get the desired output.
Using the same ordinal scale with the code I have so far, the line looks ok but it's not the curve I am expecting to see.
Any help appreciated.
The main issue with the line is that the cumulative distribution needs to be recalculated after the bar is sorted, or if you're gunning for a static pareto chart, the cumulative distribution needs to be calculated in the target sort order. For this purpose i've created a small function to do this calculation:
function calcCDF(data){
data.forEach(function(d,i){
if(i === 0){
d.cum = d.y/dataset.length
}else{
d.cum = (d.y/dataset.length) + data[i-1].cum
}
})
return data
}
In my case, i'm toggling the pareto sort on/off and recalculating the d.cum property each time. One could theoretically create two cumulative dist properties to start with; i.e. d.cum for a regular ordered distribution and say d.ParetoCum for the sorted cumulative, but i'm using d.cum on a tooltip and decided against that.
Per the axis, i'm using a single ordinal scale which i think is cleaner, but required some work on getting the labels to be meaningful for number ranges since tick-marks and labels no longer delineate the bins as one would get with a linear scale. My solution here was to just use the number range as the tick mark e.g. "1 - 1.99" and add a function to alternate tickmarks (got that solution a while ago from Alternating tick padding in d3.js).
For the bar sorting, i'm using this d3 example as a reference in case you need to understand in the context of a simpler/smaller example.
See this fiddle that incorporates all of the above. If you want to use it, i would suggest adding a check to avoid the user being able to toggle off both bars and line (left a note in the code...should be trivial)
Instead of sorting the y.
data.sort(function(a,b){ return b.y - a.y;});
you should be sorting the x
data.sort(function(a,b){ return a.x - b.x;});
Working code here

What is the logic behind d3.js nice() ticks

I have generated some charts in d3.js. I use the following code to calculate the values to put in my y axis which works like a charm.
var s = d3.scale.linear().domain([minValue, maxValue]);
var ticks = s.nice().ticks(numberOfPoints);
However I now have to write python code using pycairo which generates clones of the d3.js charts on the server side.
My question is does anybody know the logic used in the above code, or something that can give similar results, so that I can replicate it in python to get nice values to use in my y axis?
D3 is open source, so you can look up the implementation. It basically boils down to rounding the extreme values:
domain[i0] = nice.floor(x0);
domain[i1] = nice.ceil(x1);

Logarithmic bar chart in d3 with only one order of magnitude

I'm creating bar chart with extreme variance in the quantities, and I have a button to switch from linear to a logarithmic scale.
What base/domain/range should be used so that each tick doesn't clump into specific orders of magnitude. I'd like a nice one smooth gradation as it compresses the upper values.
Right now my data is domain([1, 39000000]) and the range is the height of the graph. I could fake it and use [1,10] for the domain with base(10), but then the ticks don't match up with the quantities.
var log = d3.scale.log()
.domain([1, 39000000])
.range([500, 0])
.base(10);
Using a higher .base(10) value really slows the graph down. How do other people handle these types of charts? I'm thinking similar to how Yahoo has log scales for their charts with one nice smooth order of magnitude.
I made a JSFiddle as an example.
Turns out, I just needed to use a power scale, which is what they are designed for.
var log = d3.scale.pow()
.domain([1, 39000000])
.range([500, 0])
.exponent(.2);
And if it's helpful, here is a really nice site for calculating the exponent. http://edenhalperin.com/d3-scale-generator/

Categories

Resources