@@ -38,17 +38,13 @@ module.exports = function calc(gd, trace) {
38
38
for ( var i = 0 ; i < cd . length ; i ++ ) {
39
39
var cdi = cd [ i ] ;
40
40
var vals = cdi . pts . map ( helpers . extractVal ) ;
41
- var len = vals . length ;
42
41
43
- // sample standard deviation
44
- var ssd = Lib . stdev ( vals , len - 1 , cdi . mean ) ;
45
- var bandwidthDflt = ruleOfThumbBandwidth ( vals , ssd , cdi . q3 - cdi . q1 ) ;
46
- var bandwidth = cdi . bandwidth = trace . bandwidth || bandwidthDflt ;
42
+ var bandwidth = cdi . bandwidth = calcBandwidth ( trace , cdi , vals ) ;
47
43
var span = cdi . span = calcSpan ( trace , cdi , valAxis , bandwidth ) ;
48
44
49
45
// step that well covers the bandwidth and is multiple of span distance
50
46
var dist = span [ 1 ] - span [ 0 ] ;
51
- var n = Math . ceil ( dist / ( Math . min ( bandwidthDflt , bandwidth ) / 3 ) ) ;
47
+ var n = Math . ceil ( dist / ( bandwidth / 3 ) ) ;
52
48
var step = dist / n ;
53
49
54
50
if ( ! isFinite ( step ) || ! isFinite ( n ) ) {
@@ -75,13 +71,36 @@ module.exports = function calc(gd, trace) {
75
71
return cd ;
76
72
} ;
77
73
78
- // Default to Silveman's rule of thumb:
74
+ // Default to Silveman's rule of thumb
79
75
// - https://stats.stackexchange.com/a/6671
80
76
// - https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator
81
77
// - https://github.com/statsmodels/statsmodels/blob/master/statsmodels/nonparametric/bandwidths.py
82
- function ruleOfThumbBandwidth ( vals , ssd , iqr ) {
78
+ function silvermanRule ( len , ssd , iqr ) {
83
79
var a = Math . min ( ssd , iqr / 1.349 ) ;
84
- return 1.059 * a * Math . pow ( vals . length , - 0.2 ) ;
80
+ return 1.059 * a * Math . pow ( len , - 0.2 ) ;
81
+ }
82
+
83
+ function calcBandwidth ( trace , cdi , vals ) {
84
+ var span = cdi . max - cdi . min ;
85
+
86
+ // Limit how small the bandwidth can be.
87
+ //
88
+ // Silverman's rule of thumb can be "very" small
89
+ // when IQR does a poor job at describing the spread
90
+ // of the distribution.
91
+ // We also want to limit custom bandwidths
92
+ // to not blow up kde computations.
93
+
94
+ if ( trace . bandwidth ) {
95
+ return Math . max ( trace . bandwidth , span / 1e4 ) ;
96
+ } else {
97
+ var len = vals . length ;
98
+ var ssd = Lib . stdev ( vals , len - 1 , cdi . mean ) ;
99
+ return Math . max (
100
+ silvermanRule ( len , ssd , cdi . q3 - cdi . q1 ) ,
101
+ span / 100
102
+ ) ;
103
+ }
85
104
}
86
105
87
106
function calcSpan ( trace , cdi , valAxis , bandwidth ) {
0 commit comments