Breaking change with contour-density thresholds starting from v3.0.2
HackerNeoAnderson opened this issue · comments
Hi guys,
We are updating our dependency on d3-contour from 3.0.1 to 4.0.2.
But, it looks like it's not backward compatible starting from 3.0.2.
The breaking change was introduced in PR#57 and published as a patch version (3.0.2)
In our application, we provide thresholds as a callback function which returns an array:
contourDensity().thresholds((densityValues) => someArrayOfThresholds)
Could you suggest, how to make it compatible with the latest version (4.0.2) of d3-contour?
Maybe we need to use some built-in function like thresholdFreedmanDiaconis
, thresholdScott
, thresholdSturges
to make the density chart drawn correctly again?
This notebook: https://observablehq.com/@fil/density-thresholds-70
Suppose you wanted to pass the following thresholds in d3-contour 3.0.1:
const contours = d3301
.contourDensity()
.thresholds([0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01])
(data)
this would return contours with values:
contours.map((d) => d.value) // [0, 0.0000625, 0.000125, 0.0001875, 0.00025, 0.0003125, 0.000375, 0.0004375, 0.0005, 0.0005625, 0.000625]
This was inconsistent (you ask for 1, you get 1/16). #57 fixes the inconsistency, so starting with 3.0.2, you would pass this instead:
const thresholds = [0, 0.0000625, 0.000125, 0.0001875, 0.00025, 0.0003125, 0.000375, 0.0004375, 0.0005, 0.0005625, 0.000625];
const contours = d3_301
.contourDensity()
.thresholds(thresholds)
(data)
and get the same contours (with the same values).
The relationship between the two is this ratio 2**k with k the cellSize which defaults to 4; in other words you should find the same contours as before if you did:
const thresholds = [0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01]; // old style
const contours = d3_302
.contourDensity()
.thresholds(thresholds.map(d => d/16)) // convert to new style
(data)
Hi, @Fil !
Thank you for the response. Faced the same issue.
After diving deep into d3-contour source code, I found that k depends on cellSize provided value:
Line 130 in 5e9ee8a
So, to get a correct results, seems like we need to use the following formula when using cellSize method:
k = Math.floor(Math.log(cellSizeValue) / Math.LN2)
const thresholds = [0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01]; // old style
const contours = d3_302
.contourDensity()
.cellSize(cellSizeValue)
.thresholds(thresholds.map(d => d/ Math.pow(2, 2 * k))) // convert to new style
(data)