Logarithmic bar chart in d3 with only one order of magnitude - javascript

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/

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"]);

How to set a minimum range on the y-axis for a stacked area chart in d3

I'm creating a stacked chart using nvd3, similar to the example on the nvd3 website.
However, my data set is a lot more uniform than their example; I'm actually visualizing a funnel with multiple steps over time, each step describing the % that made it from the last step to this one, so it ranges from 100% to about 80%. So it ends up just looking like 4 straight rectangles (see below).
I'd like to know if there is some way of setting a minimum value to display on the y-axis so that each step only takes into account the data that is actually likely to fluctuate, leading to a clearer indication of peaks and troughs.
I've tried using .range([100, 80]) and .domain([80, 100]) but neither seems to make any difference.
For example:
var y = d3.scale.linear().range([100, 80]);
chart.yAxis
.scale(y)
.tickFormat(d3.format(',.2f'));
Full code for my example is available here.
Any ideas how to achieve this?

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

Creating a histogram with distribution curve, where the curve series is larger than the bin series

I want to create a histogram in Highcharts. The bin series has about 8 elements. The series for the the distribution curve has about 200 elements. Since Highcharts infers the xAxis from the number of elements in the series, the xAxis stretches out to 200. How do I get the curve series to fit to the bin series on the xAxis?
I would suggest using 2 x axes for this. It is far easier than trying to make the points match on a single axis.
See my example here:
http://jsfiddle.net/FnhRV/19/
Well, first I advice to get familiar with Highcharts docs/tutorials. Like this one.
In general, you can manage distance between points, it's called pointInterval, for example: http://jsfiddle.net/Dd9Py/1/
When you have 8 columns, on xAxis you should have scale - according to pair [x,y] of values.
Another solution is to use two different xAxis, one for column and one for spline. Example: http://jsfiddle.net/Dd9Py/2/
Looking at this jsFiddle. Things I notice is that your xAxis is linear - meaning that each point is plotted consecutively. So, since your bar series only has 8 points they are plotted int he first 8 positions. Your curve contains 200 points and are also plotted first come first served. You need to link up your xAxis so that each series is linked. What are your xAxis increments/categories?

Plotting logarithmic graph (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

Categories

Resources