Heatmap color range based on max & min value - d3.js

I am trying to leverage this example for my visualization
https://bl.ocks.org/Bl3f/cdb5ad854b376765fa99
It pretty much works except this part
var colorScale = d3.scale.threshold()
.domain([0.85, 1])
.range(["#2980B9", "#E67E22", "#27AE60", "#27AE60"]);
I basically want a standard vibgyor visualization based on min & max value of the values in csv data?
Any suggestions, how I can modify the above example.
Thanks

If you want a Violet–Indigo–Blue–Green–Yellow–Orange–Red scale, you can use d3.interpolateRainbow with a sequential scale.
Then, set your domain using the min and max in your values, like any other continuous scale.
Here is a basic example. Suppose this data:
const data = [12, 43, 76, 54, 87, 91, 17, 42, 36];
We set the scale like this:
const scale = d3.scaleSequential()
.domain(d3.extent(data))
.interpolator(d3.interpolateRainbow);
Running demo:
const data = [12, 43, 76, 54, 87, 91, 17, 42, 36];
const scale = d3.scaleSequential()
.domain(d3.extent(data))
.interpolator(d3.interpolateRainbow);
d3.select("body").selectAll(null)
.data(data)
.enter()
.append("div")
.style("background-color", d => scale(d))
div {
display: inline-block;
width: 20px;
height: 20px;
margin-left: 4px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Have in mind that this scale won't work for the specific example you linked, since that uses the old D3 v3.

Related

d3.scaleLog ticks with base 2

I trying to produce ticks for scaleLog().base(2).
Seems to be, it does not work correctly.
For instance, for the call:
d3.scaleLog().base(2).domain([50,500]).ticks(10)
I got:
[ 50, 100, 150, 200, 250, 300, 350, 400, 450, 500 ]
Which just linear placed ticks. For base(10) it works properly.
d3.scaleLog().base(10).domain([50,500]).ticks(10)
[ 50, 60, 70, 80, 90, 100, 200, 300, 400, 500 ]
I using d3.js version 6.1.1.
I am missing something?
You're not missing anything, but there is this line, inside the source code:
if (z.length * 2 < n) z = ticks(u, v, n);
Here, z is the generated array (in this case [64, 128, 256]), n is the required number of ticks (10), and u and v are the domain (50 and 500).
Because the number of generated ticks is too low, d3 defaults to a linear scale. Try one of the following instead:
console.log(d3.scaleLog().base(2).domain([50, 500]).ticks(6));
console.log(d3.scaleLog().base(2).domain([32, 512]).ticks(10));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>
If all parameters are variable, you could calculate the maximum possible number of ticks and use that as an upper bound:
const domain = [50, 500];
const ticks = 100;
console.log(d3.scaleLog().base(2).domain(domain).ticks(ticks));
function getNTicks(domain, ticks) {
const maxPossibleTicks = Math.floor(Math.log2(domain[1]) - Math.log2(domain[0]));
return Math.min(ticks, maxPossibleTicks);
}
console.log(d3.scaleLog().base(2).domain(domain).ticks(getNTicks(domain, ticks)));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.1/d3.min.js"></script>

Margins on composite Chart

I've recently tried to add margins to composite charts and the charts are plotting to the left of the Y axis.
The charts are built dynamically. I tried more combinations of values for margins, but it didn't work. I want to add margin-left because some charts have big values on y axis.
Here is the esential code:
hist_margins = {top: 10, right: 50, bottom: 30, left: 40}
charts[i]
.compose([
dc.barChart(charts[i])
.dimension(dim)
.group(static_group)
.colors('#ccc')
.barPadding(0.1)
.controlsUseVisibility(true),
dc.barChart(charts[i])
.dimension(dim)
.colors('rgb(85, 160, 185)')
.group(group)
.barPadding(0.1)
.controlsUseVisibility(true)
.brushOn(false),
])
var min = dim.bottom(1)[0][it.variable],
max = dim.top(1)[0][it.variable];
charts[i]
.width(w_hist)
.height(h_hist)
.margins(hist_margins)
.dimension(dim)
.group(group)
.x(d3.scaleLinear().domain([min, max]))
.xAxisLabel(it.variable, 20)
.xUnits(dc.units.fp.precision(it.precision*1.5))
.brushOn(true)
.transitionDuration(0)
.renderTitle(true)
.title(function (d) {
return it.variable + ': ' + d.value
})
.controlsUseVisibility(true);
Any ideas?
As of dc.js 3.1.4, you would have to call .margins() before calling .compose() in order for the values to be reflected in the child charts.
However, thanks to a PR by Keith Dahlby, this limitation has been eliminated.
So, you can either upgrade to dc.js 3.1.5 (just released), or you can call .margins() first.

d3.js. Stream chart shifts with different data sets. Why?

Code available here http://jsfiddle.net/zeleniy/ea1uL7w9/
data = [47, 13, 61, 46, 26, 32, 6, 85, 1, 14, 86, 77, 13, 66, 0, 20, 11, 87, 5, 15];
data = [52, 33, 53, 45, 59, 45, 42, 50, 53, 50, 37, 45, 52, 50, 46, 48, 52, 56, 58, 59];
width = 300;
height = 100;
xScale = d3.scale.linear()
.domain([0, data.length])
.range([0, width]);
yScale = d3.scale.linear()
.domain([d3.min(data), d3.max(data)])
.range([0, height])
area = d3.svg.area()
.interpolate("basis")
.x(function(d, i) { return xScale(i); })
.y0(function(d) { return yScale(-d / 2); })
.y1(function(d) { return yScale(d / 2); });
svg = d3.select("#stream")
.append("svg")
.attr("width", width)
.attr("height", height);
svg.selectAll("path")
.data([this.data])
.enter()
.append("path")
.attr("transform", "translate(0," + (height / 2) + ")")
.style("fill", "red")
.attr("d", area);
With first data set chart drawn in the center of svg element, as i expect. But with second data set stream shifts to the top of svg element. And i can't understand why. So why?
The first array contains values close to 0 and it's opening up your range. This line, then, is a fudge to shift the path into that open window:
.attr("transform", "translate(0," + (height / 2) + ")")
That said, you are setting up your scales in a confusing way (to me at least). I think about my domain as the min/max of my (plotted) dataset, in your case -max(d/2) and max(d/2). Further, I also think about my y-scale going from bottom to top as it would in a normal plot. With these changes, you don't need to artificially move anything:
var dataMax = d3.max(data);
yScale = d3.scale.linear()
.domain([ -dataMax/2, dataMax/2 ]) // real min/max of plotted data
.range([height, 0]) //<- bottom to top, although it still works without this change...
In this example, I left an axis overlayed for illustration.

unable to enter data in Input Control of a GUI in autoit

The below one is my code. I am unable to click and edit in the second Input control.
#include <GUIConstants.au3>
$gui = GuiCreate("Hello World", 700, 600)
$Label_HelloWorld = GuiCtrlCreateLabel("Path / Directory", 40, 20, 300, 18)
$file = GUICtrlCreateInput("", 140, 20, 300, 20)
$Label_boot = GuiCtrlCreateLabel("path of boot.c", 40, 60, 300, 18)
$file2 = GUICtrlCreateInput("", 140, 60, 300, 20)
$Button_OK = GuiCtrlCreateButton("CHECK", 400, 90, 50, 20)
GuiSetState(#SW_SHOW, $gui)
Sleep(10000)
Your labels are overlapping the controls (again), this time horizontally. A width of 300px when the inputs are 100px to the right means the first 200px is overlapping. If you try to click in the last 100px of the input then it will work.
This is very easy to check for, just use the autoit window info tool and look at the outlines of the controls.

d3.js ticks function giving more elements than needed

I have this simple linear scale:
var x = d3.scale.linear().domain([0, 250]);
x.ticks(6), as expected, returns:
[0, 50, 100, 150, 200, 250]
However, x.ticks(11) returns:
[0, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240]
When what I want is:
[0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250]
How do I fix this?
I had a similar issue with ordinal scales, I simply wrote some code to pick evenly spaced intervals in my data. Since I wanted it to always choose the first and last data element on the axis, I calculate the middle part only. Since some things do not divide evenly, rather than having the residual in one or two bins, I spread it out across the bins as I go; until there is no more residual.
There is probably a simpler way to accomplish this but here's what I did:
function getTickValues(data, numValues, accessor)
{
var interval, residual, tickIndices, last, i;
if (numValues <= 0)
{
tickIndices = [];
}
else if (numValues == 1)
{
tickIndices = [ Math.floor(numValues/2) ];
}
else
{
// We have at least 2 ticks to display.
// Calculate the rough interval between ticks.
interval = Math.floor(data.length / (numValues-1));
// If it's not perfect, record it in the residual.
residual = Math.floor(data.length % (numValues-1));
// Always label our first datapoint.
tickIndices = [0];
// Set stop point on the interior ticks.
last = data.length-interval;
// Figure out the interior ticks, gently drift to accommodate
// the residual.
for (i=interval; i<last; i+=interval)
{
if (residual > 0)
{
i += 1;
residual -= 1;
}
tickIndices.push(i);
}
// Always graph the last tick.
tickIndices.push(data.length-1);
}
if (accessor)
{
return tickIndices.map(function(d) { return accessor(d); });
}
return tickIndices.map(function(i) { return data[i]; });
}
You call the function via:
getTickvalues(yourData, numValues, [optionalAccessor]);
Where yourData is your array of data, numvalues is the number of ticks you want. If your array contains a complex datastructure then the optional accessor comes in handy.
Lastly, you then feed this into your axis. Instead of ticks(numTicks) which is only a hint to d3 apparently, you call tickValues() instead.
I learned the hard way that your tickValues have to match your data exactly for ordinal scales. This may or may not be as helpful for linear scales, but I thought I'd share it anyways.
Hope this helps.
Pat
You can fix this by replacing the x.ticks(11) with your desired array.
So if your code looks like this and x is your linear scale:
chart.selectAll("line")
.data(x.ticks(11))
.enter()
.append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", 0)
.attr("y2",120)
.style("stroke", "#CCC");
You can replace x.ticks(11) with your array:
var desiredArray = [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250]
chart.selectAll("line")
.data(desiredArray)
.enter()
.append("line")
.attr("x1", x)
.attr("x2", x)
.attr("y1", 0)
.attr("y2",120)
.style("stroke", "#CCC");
The linear scale will automatically place your desired axes based on your input. The reason why the ticks() isn't giving you your desired separation is because d3 just treats ticks() as a suggestion.
axis.tickvalues((function(last, values) {
var myArray = [0];
for(var i = 1; i < values; i++) {
myArray.push(last*i/(values-1))
}
return myArray;
})(250, 11));
This should give you an evenly spaced out array for specifying the number of tick values you want in a particular range.

Resources