@@ -38,17 +38,13 @@ module.exports = function calc(gd, trace) {
3838 for ( var i = 0 ; i < cd . length ; i ++ ) {
3939 var cdi = cd [ i ] ;
4040 var vals = cdi . pts . map ( helpers . extractVal ) ;
41- var len = vals . length ;
4241
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 ) ;
4743 var span = cdi . span = calcSpan ( trace , cdi , valAxis , bandwidth ) ;
4844
4945 // step that well covers the bandwidth and is multiple of span distance
5046 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 ) ) ;
5248 var step = dist / n ;
5349
5450 if ( ! isFinite ( step ) || ! isFinite ( n ) ) {
@@ -75,13 +71,36 @@ module.exports = function calc(gd, trace) {
7571 return cd ;
7672} ;
7773
78- // Default to Silveman's rule of thumb:
74+ // Default to Silveman's rule of thumb
7975// - https://stats.stackexchange.com/a/6671
8076// - https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator
8177// - https://github.com/statsmodels/statsmodels/blob/master/statsmodels/nonparametric/bandwidths.py
82- function ruleOfThumbBandwidth ( vals , ssd , iqr ) {
78+ function silvermanRule ( len , ssd , iqr ) {
8379 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+ }
85104}
86105
87106function calcSpan ( trace , cdi , valAxis , bandwidth ) {
0 commit comments