}).map(function(quarter, ix, all) {
    var bgValues = quarter.records.map(function(record) {
      return record.sgv;
    });
    quarter.standardDeviation = ss.standard_deviation(bgValues);
    quarter.average = bgValues.length > 0? (sum(bgValues) / bgValues.length): 'N/A';
    quarter.lowerQuartile = ss.quantile(bgValues, 0.25); 
    quarter.upperQuartile = ss.quantile(bgValues, 0.75);
    quarter.numberLow = bgValues.filter(function(bg) {
      return bg < low;
    }).length;
    quarter.numberHigh = bgValues.filter(function(bg) {
      return bg >= high;
    }).length;
    quarter.numberInRange = bgValues.length - (quarter.numberHigh + quarter.numberLow);

    quarter.percentLow = (quarter.numberLow / bgValues.length) * 100;
    quarter.percentInRange = (quarter.numberInRange / bgValues.length) * 100;
    quarter.percentHigh = (quarter.numberHigh / bgValues.length) * 100;

    averages.percentLow += quarter.percentLow / all.length;
    averages.percentInRange += quarter.percentInRange / all.length;
    averages.percentHigh += quarter.percentHigh / all.length;
    averages.lowerQuartile += quarter.lowerQuartile / all.length;
    averages.upperQuartile += quarter.upperQuartile / all.length;
    averages.average += quarter.average / all.length;
    averages.standardDeviation += quarter.standardDeviation / all.length;
    return quarter;
  });
function toStats (sample) {
    return {
        "84th": ss.quantile(sample, 0.84),
        "98th": ss.quantile(sample, 0.98),
        "mean": ss.mean(sample),
        "standardDeviation": ss.standardDeviation(sample),
        "max": ss.max(sample),
        "min": ss.min(sample)
    };
}
示例#3
0
module.exports = function(fc, z, classification, numBreaks, colors){

  // JENKS
  if(classification === 'jenks'){
    var vals = _.chain(fc.features)
      .pluck('properties')
      .pluck(z)
      .value()
    var breaks = ss.jenks(vals, numBreaks)
    var normals = normalize(breaks,1)
    fc = colorize(fc, z, colors, breaks, normals)
    return fc
  }

  // QUANTILE
  else if(classification === 'quantile'){
    var vals = _.chain(fc.features)
      .pluck('properties')
      .pluck(z)
      .value()
    var min = ss.min(vals)
    var max = ss.max(vals)
    var interval = 1 / numBreaks
    var quants = [0]
    var currentBreak = 0
    for(var i=0;i<numBreaks;i++){
      currentBreak += interval
      quants.push(currentBreak)
    }
    var breaks = ss.quantile(vals, quants)
    var normals = normalize(breaks,1)
    fc = colorize(fc, z, colors, breaks, normals)
    return fc
  }

  // EQUAL INTERVAL
  else if(classification === 'interval'){
    var vals = _.chain(fc.features)
      .pluck('properties')
      .pluck(z)
      .value()
    var min = ss.min(vals)
    var max = ss.max(vals)
    var interval = (max - min) / numBreaks
    var breaks = [0]
    var currentBreak = 0
    for(var i=0;i<=numBreaks;i++){
      currentBreak += interval
      breaks.push(currentBreak)
    }
    var normals = normalize(breaks,1)
    fc = colorize(fc, z, colors, breaks, normals)
    return fc
  }

  // UNKOWN
  else{
    return new Error('unsupported classification: ' + z)
  }
}
 _.each(stat, function(s, field) {
   var stepRange;
   
   if (!s.values) {
     console.log('Issue with stat for group: ' + statGroup);
   }
   
   stats[statGroup].count = s.values.length;
   
   //stats[statGroup][field].sum = ss.sum(s.values);
   //stats[statGroup][field].min = ss.min(s.values);
   //stats[statGroup][field].max = ss.max(s.values);
   stats[statGroup][field].mean = ss.mean(s.values);
   stats[statGroup][field].median = ss.median(s.values);
   //stats[statGroup][field].mode = ss.mode(s.values);
   //stats[statGroup][field].variance = ss.variance(s.values);
   //stats[statGroup][field].standard_deviation = ss.standard_deviation(s.values);
   stats[statGroup][field].q25 = ss.quantile(s.values, 0.25);
   stats[statGroup][field].q75 = ss.quantile(s.values, 0.75);
   
   stepRange = outlierRange(s.values, stats[statGroup][field].q25, stats[statGroup][field].q75, stats[statGroup][field].median);
   stats[statGroup][field].stepL = stepRange[0];
   stats[statGroup][field].stepU = stepRange[1];
 });
示例#5
0
  quantiles: function(fc, z, numBreaks, colors, style){
    var vals = _.chain(fc.features)
      .pluck('properties')
      .pluck(z)
      .value()

    var min = ss.min(vals)
    var max = ss.max(vals)
    var interval = 1 / numBreaks
    var quants = [0]
    var currentBreak = 0
    for(var i=0;i<numBreaks;i++){
      currentBreak += interval
      quants.push(currentBreak)
    }
    var breaks = ss.quantile(vals, quants)
    var normals = normalize(breaks.length)
    fc = colorize(fc, z, colors, breaks, normals)
    fc = applyStyle(fc, style)
    return fc
  },
示例#6
0
/**
 * Builds and returns an array describing the legend colors.
 * Each element is an object with keys "color" and "upperBound", eg.
 * [ { color: [r, g, b, a], upperBound: 20 } , { color: [r, g, b, a]: upperBound: 80 } ]
 * @private
 * @param {LegendHelper} legendHelper The legend helper.
 * @param {Integer|Number[]} colorBins The number of color bins to use, or the boundaries to use.
 * @return {Array} Array of objects with keys "color" and "upperBound".
 */
function buildBinColors(legendHelper, colorBins) {
    var tableColumn = legendHelper.tableColumn;
    var tableColumnStyle = legendHelper.tableColumnStyle;
    var colorGradient = legendHelper._colorGradient;

    // If colorBins is an array, just return it in the right format.
    var extremes = getExtremes(tableColumn, tableColumnStyle);
    if (Array.isArray(colorBins) && defined(extremes.minimum) && defined(extremes.maximum)) {
        // If the max value is beyond the range, add it to the end.
        // Do this to be symmetric with min and max.
        if (colorBins[colorBins.length - 1] < extremes.maximum) {
            colorBins = colorBins.concat(extremes.maximum);
        }
        var numberOfColorBins = colorBins.length;
        var filteredBins = colorBins.filter(function(bound, i) {
            // By cutting off all bins equal to or lower than the min value,
            // the min value will be added as a titleBelow instead of titleAbove.
            // Since any bins wholy below the min are removed, do the same with max.
            return (bound > extremes.minimum) && (i === 0 || colorBins[i - 1] < extremes.maximum);
        });
        // Offset to make sure that the correct color is used when the legend is truncated
        var binOffset = colorBins.indexOf(filteredBins[0]);
        return filteredBins.map(function(bound, i) {
            return {
                // Just use the provided bound, but cap it at the max value.
                upperBound: Math.min(bound, extremes.maximum),
                colorArray: getColorArrayFromColorGradient(colorGradient, (binOffset + i) / (numberOfColorBins - 1))
            };
        });
    }

    if (colorBins <= 0 || tableColumnStyle.colorBinMethod.match(/none/i)) {
        return undefined;
    }
    var binColors = [];
    var i;
    var numericalValues = tableColumn.numericalValues;

    if (numericalValues.length === 0) {
        return [];
    }

    // Must ask for fewer clusters than the number of items.
    var binCount = Math.min(colorBins, numericalValues.length);

    var method = tableColumnStyle.colorBinMethod.toLowerCase();
    if (method === 'auto') {
        if (numericalValues.length > 1000) {
            // The quantile method is simpler and less accurate, but faster for large datasets.
            method = 'quantile';
        } else {
            method = 'ckmeans';
        }
    }

   if (method === 'quantile') {
        // One issue is we don't check to see if any values actually lie within a given quantile, so it's bad for small datasets.
        for (i = 0; i < binCount; i++) {
            binColors.push({
                upperBound: simplestats.quantile(numericalValues, (i + 1) / binCount),
                colorArray: getColorArrayFromColorGradient(colorGradient, i / (binCount - 1))
            });
        }
    } else if (method === 'ckmeans') {
        var clusters = simplestats.ckmeans(numericalValues, binCount);
        // Convert the ckmeans format [ [5, 20], [65, 80] ] into our format.
        for (i = 0; i < clusters.length; i++) {
            if (i > 0 && clusters[i].length === 1 && clusters[i][0] === clusters[i - 1][clusters[i - 1].length - 1]) {
                // When there are few unique values, we can end up with clusters like [1], [2],[2],[2],[3]. Let's avoid that.
                continue;
            }
            binColors.push({
                upperBound: clusters[i][clusters[i].length - 1],
            });
        }
        if (binColors.length > 1) {
            for (i = 0; i < binColors.length; i++) {
                binColors[i].colorArray = getColorArrayFromColorGradient(colorGradient, i / (binColors.length - 1));
            }
        } else {
            // only one binColor, pick the middle of the color gradient.
            binColors[0].colorArray = getColorArrayFromColorGradient(colorGradient, 0.5);
        }
    }
    return binColors;
}
glucosedistribution.report = function report_glucosedistribution(datastorage, sorteddaystoshow, options) {
    var Nightscout = window.Nightscout;
    var client = Nightscout.client;
    var translate = client.translate;

    var ss = require('simple-statistics');

    var colors = ['#f88', '#8f8', '#ff8'];
    var tablecolors = {
        Low: '#f88',
        Normal: '#8f8',
        High: '#ff8'
    };

    var enabledHours = [true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true];

    var report = $('#glucosedistribution-report');
    report.empty();

    var stability = $('#glucosedistribution-stability');
    stability.empty();

    var stats = [];
    var table = $('<table class="centeraligned">');
    var thead = $('<tr/>');
    $('<th>' + translate('Range') + '</th>').appendTo(thead);
    $('<th>' + translate('% of Readings') + '</th>').appendTo(thead);
    $('<th>' + translate('# of Readings') + '</th>').appendTo(thead);
    $('<th>' + translate('Average') + '</th>').appendTo(thead);
    $('<th>' + translate('Median') + '</th>').appendTo(thead);
    $('<th>' + translate('Standard Deviation') + '</th>').appendTo(thead);
    $('<th>' + translate('A1c estimation*') + '</th>').appendTo(thead);
    thead.appendTo(table);

    var data = datastorage.allstatsrecords;
    var days = datastorage.alldays;

    $('#glucosedistribution-days').text(days + ' ' + translate('days total'));

    for (var i = 0; i < 23; i++) {
        $('#glucosedistribution-' + i).unbind('click').click(onClick);
        enabledHours[i] = $('#glucosedistribution-' + i).is(':checked');
    }

    //console.log(enabledHours);

    var result = {};

	// Filter data for noise
	
    var glucose_data = [data[0]];

    // data cleaning pass 0 - remove duplicates and sort
    
    var seen = {};
    data = data.filter(function(item) {
        return seen.hasOwnProperty(item.displayTime) ? false : (seen[item.displayTime] = true);
        });

    data.sort(function(a,b){
        return a.displayTime.getTime()-b.displayTime.getTime();
    });

    // data cleaning pass 1 - add interpolated missing points

    for (var i = 0; i < data.length - 2; i++) {

        var entry = data[i];
        var nextEntry = data[i + 1];

        var timeDelta = nextEntry.displayTime.getTime() - entry.displayTime.getTime();

        if (timeDelta < 9 * 60 * 1000 ||  timeDelta > 25 * 60 * 1000) {
            glucose_data.push(entry);
            continue;
        }

        var missingRecords = Math.floor(timeDelta / (5 * 60 * 990)) -1;

        var timePatch = Math.floor(timeDelta / (missingRecords + 1));
        var bgDelta = (nextEntry.bgValue - entry.bgValue) / (missingRecords + 1);

        glucose_data.push(entry);

        for (var j = 1; j <= missingRecords; j++) {
        
            var bg = Math.floor(entry.bgValue + bgDelta * j);
            var t = new Date(entry.displayTime.getTime() + j * timePatch);
            var newEntry = {
                    bgValue: bg,
                    displayTime: t
                };
            glucose_data.push(newEntry);
        }

    }

    // data cleaning pass 2 - replace single jumpy measures with interpolated values
    
    var glucose_data2 = [glucose_data[0]];
    
    var prevEntry = glucose_data[0];
    
	for (var i = 1; i < glucose_data.length-2; i++) {
   
//     var prevEntry = glucose_data[i-1];
     var entry = glucose_data[i];
     var nextEntry = glucose_data[i+1];
     
     var timeDelta = nextEntry.displayTime.getTime() - entry.displayTime.getTime();
     var timeDelta2 = entry.displayTime.getTime() - nextEntry.displayTime.getTime();
     
     var maxGap = (5 * 60 * 1000) + 10000;
     
     if (timeDelta > maxGap || timeDelta2 > maxGap ) {
     	glucose_data2.push(entry);
     	prevEntry = entry;
     	continue;
     }
     
	 var delta1 = entry.bgValue - prevEntry.bgValue;
	 var delta2 = nextEntry.bgValue - entry.bgValue;
	 
	 if (delta1 <= 8 && delta2 <= 8) {
	    glucose_data2.push(entry);
     	prevEntry = entry;
     	continue;
	 }
	 
	 
	 if ((delta1 > 0 && delta2 <0) || (delta1 < 0 && delta2 > 0)) {
	 	var d = (nextEntry.bgValue - prevEntry.bgValue) / 2;
		var newEntry = {
                    bgValue: prevEntry.bgValue + d,
                    displayTime: entry.displayTime
                };
            glucose_data2.push(newEntry);
			prevEntry = newEntry;
        	continue;
	 
	 }
	 
	 glucose_data2.push(entry);
	 prevEntry = entry;
   }
   
   glucose_data = data = glucose_data2.filter(function(r) {
        return enabledHours[new Date(r.displayTime).getHours()]
    });

    ['Low', 'Normal', 'High'].forEach(function(range) {
        result[range] = {};
        var r = result[range];
        r.rangeRecords = glucose_data.filter(function(r) {
            if (range === 'Low') {
                return r.sgv > 0 && r.sgv < options.targetLow;
            } else if (range === 'Normal') {
                return r.sgv >= options.targetLow && r.sgv < options.targetHigh;
            } else {
                return r.sgv >= options.targetHigh;
            }
        });
        stats.push(r.rangeRecords.length);
        r.rangeRecords.sort(function(a, b) {
            return a.sgv - b.sgv;
        });
        r.localBgs = r.rangeRecords.map(function(r) {
            return r.sgv;
        }).filter(function(bg) {
            return !!bg;
        });
        r.midpoint = Math.floor(r.rangeRecords.length / 2);
        r.readingspct = (100 * r.rangeRecords.length / data.length).toFixed(1);
        if (r.rangeRecords.length > 0) {
            r.mean = Math.floor(10 * ss.mean(r.localBgs)) / 10;
            r.median = r.rangeRecords[r.midpoint].sgv;
            r.stddev = Math.floor(ss.standard_deviation(r.localBgs) * 10) / 10;
        }
    });

    // make sure we have total 100%
    result.Normal.readingspct = (100 - result.Low.readingspct - result.High.readingspct).toFixed(1);

    ['Low', 'Normal', 'High'].forEach(function(range) {
        var tr = $('<tr>');
        var r = result[range];

        var rangeExp = '';

        if (range == 'Low') {
            rangeExp = ' (<' + options.targetLow + ')';
        }
        if (range == 'High') {
            rangeExp = ' (>=' + options.targetHigh + ')';
        }

        $('<td class="tdborder" style="background-color:' + tablecolors[range] + '"><strong>' + translate(range) + rangeExp + ': </strong></td>').appendTo(tr);
        $('<td class="tdborder">' + r.readingspct + '%</td>').appendTo(tr);
        $('<td class="tdborder">' + r.rangeRecords.length + '</td>').appendTo(tr);
        if (r.rangeRecords.length > 0) {
            $('<td class="tdborder">' + r.mean.toFixed(1) + '</td>').appendTo(tr);
            $('<td class="tdborder">' + r.median.toFixed(1) + '</td>').appendTo(tr);
            $('<td class="tdborder">' + r.stddev.toFixed(1) + '</td>').appendTo(tr);
            $('<td> </td>').appendTo(tr);
        } else {
            $('<td class="tdborder">N/A</td>').appendTo(tr);
            $('<td class="tdborder">N/A</td>').appendTo(tr);
            $('<td class="tdborder">N/A</td>').appendTo(tr);
            $('<td class="tdborder"> </td>').appendTo(tr);
        }

        table.append(tr);
    });

    var tr = $('<tr>');
    $('<td class="tdborder"><strong>' + translate('Overall') + ': </strong></td>').appendTo(tr);
    $('<td> </td>').appendTo(tr);
    $('<td class="tdborder">' + glucose_data.length + '</td>').appendTo(tr);
    if (glucose_data.length > 0) {
        var localBgs = glucose_data.map(function(r) {
            return r.sgv;
        }).filter(function(bg) {
            return !!bg;
        });
        var mgDlBgs = glucose_data.map(function(r) {
            return r.bgValue;
        }).filter(function(bg) {
            return !!bg;
        });
        $('<td class="tdborder">' + (Math.round(10 * ss.mean(localBgs)) / 10).toFixed(1) + '</td>').appendTo(tr);
        $('<td class="tdborder">' + (Math.round(10 * ss.quantile(localBgs, 0.5)) / 10).toFixed(1) + '</td>').appendTo(tr);
        $('<td class="tdborder">' + (Math.round(ss.standard_deviation(localBgs) * 10) / 10).toFixed(1) + '</td>').appendTo(tr);
        $('<td class="tdborder"><center>' + (Math.round(10 * (ss.mean(mgDlBgs) + 46.7) / 28.7) / 10).toFixed(1) + '%<sub>DCCT</sub> | ' + Math.round(((ss.mean(mgDlBgs) + 46.7) / 28.7 - 2.15) * 10.929) + '<sub>IFCC</sub></center></td>').appendTo(tr);
    } else {
        $('<td class="tdborder">N/A</td>').appendTo(tr);
        $('<td class="tdborder">N/A</td>').appendTo(tr);
        $('<td class="tdborder">N/A</td>').appendTo(tr);
        $('<td class="tdborder">N/A</td>').appendTo(tr);
    }
    table.append(tr);
    report.append(table);


    // Stability

    var t1 = 6;
    var t2 = 11;
    var t1count = 0;
    var t2count = 0;

    var total = 0;
    var events = 0;

	var GVITotal = 0;
	var GVIIdeal = 0;

    var RMSTotal = 0;

	var usedRecords = 0;
	var glucoseTotal = 0;
	var deltaTotal = 0;

    for (var i = 0; i < glucose_data.length - 2; i++) {

        var entry = glucose_data[i];
        var nextEntry = glucose_data[i + 1];

        var timeDelta = nextEntry.displayTime.getTime() - entry.displayTime.getTime();

        if (timeDelta > 6 * 60 * 1000) {
//            console.log("Record skipped");
            continue;
        }
        
        usedRecords += 1;

        var delta = Math.abs(nextEntry.bgValue - entry.bgValue);
        
        deltaTotal += delta;

        total += delta;
        events += 1;

        if (delta >= t1) {
            t1count += 1;
        }
        
        if (delta >= t2) {
            t2count += 1;
        }
        
       GVITotal += Math.sqrt(25 + Math.pow(delta, 2));  
       glucoseTotal +=  entry.bgValue;

       if (entry.bgValue < options.targetLow) {
           RMSTotal += Math.pow(options.targetLow - entry.bgValue, 2);
       }
       if (entry.bgValue > options.targetHigh) {
           RMSTotal += Math.pow(entry.bgValue - options.targetHigh, 2);
       }

    }
    
	var GVIDelta = Math.floor(glucose_data[0].bgValue,glucose_data[glucose_data.length-1].bgValue);
	
    GVIIdeal = Math.sqrt(Math.pow(usedRecords*5,2) + Math.pow(GVIDelta,2));
    
    var GVI = Math.round(GVITotal / GVIIdeal * 100) / 100;
    
    console.log('GVI',GVI,'GVIIdeal',GVIIdeal,'GVITotal',GVITotal);
    
    var glucoseMean = Math.floor(glucoseTotal / usedRecords);

    var tirMultiplier = result.Normal.readingspct / 100.0;

    var PGS = Math.round(GVI * glucoseMean * (1-tirMultiplier) * 100) / 100;

    console.log('glucoseMean', glucoseMean,'tirMultiplier',tirMultiplier, 'PGS',PGS);

	var days = (glucose_data[glucose_data.length-1].displayTime.getTime() - glucose_data[0].displayTime.getTime()) / (24*60*60*1000.0);

	var TDC = deltaTotal / days;
	var TDCHourly = TDC / 24.0;

    var RMS = Math.sqrt(RMSTotal / events);

//	console.log('TADC',TDC,'days',days);

    var timeInT1 = Math.round(100 * t1count / events).toFixed(1);
    var timeInT2 = Math.round(100 * t2count / events).toFixed(1);
    var mac = (total / events).toFixed(1);

    var unitString = ' mg/dl';

    if (client.settings.units == 'mmol') {
        mac = (total / events / 18.0).toFixed(2);
        TDC = TDC / 18.0;
        TDCHourly = TDCHourly / 18.0;
        unitString = ' mmol/L';
        
        RMS = Math.sqrt(RMSTotal / events) / 18;

    }
    
    TDC = Math.round(TDC * 100) / 100;
    TDCHourly = Math.round(TDCHourly * 100) / 100;

    var stabilitytable = $('<table style="width: 100%;">');

    var t1exp = '>5 mg/dl/5m';
    var t2exp = '>10 mg/dl/5m';

    if (client.settings.units == 'mmol') {
        t1exp = '>0.27 mmol/l/5m';
        t2exp = '>0.55 mmol/l/5m';
    }

    $('<tr><th>' + translate('Mean Total Daily Change') + '</th><th>' + translate('Time in fluctuation') + '<br>(' + t1exp + ')</th><th>' + translate('Time in rapid fluctuation') + '<br>(' + t2exp + ')</th></tr>').appendTo(stabilitytable);
    $('<tr><td class="tdborder">' + TDC + unitString + '</td><td class="tdborder">' + timeInT1 + '%</td><td class="tdborder">' + timeInT2 + '%</td></tr>').appendTo(stabilitytable);

    $('<tr><th>' + translate('Mean Hourly Change') + '</th><th>GVI</th><th>PGS</th></tr>').appendTo(stabilitytable);
    $('<tr><td class="tdborder">' + TDCHourly + unitString + '</td><td class="tdborder">' + GVI + '</td><td class="tdborder">' + PGS + '</td></tr>').appendTo(stabilitytable);

//    $('<tr><th>Out of Range RMS</th></tr>').appendTo(stabilitytable);
//    $('<tr><td class="tdborder">' + Math.round(RMS * 100) / 100 + unitString + '</td></tr>').appendTo(stabilitytable);

    stabilitytable.appendTo(stability);
    
    setTimeout(function() {
        $.plot(
            '#glucosedistribution-overviewchart',
            stats, {
                series: {
                    pie: {
                        show: true
                    }
                },
                colors: colors
            }
        );
    });

    function onClick() {
        report_glucosedistribution(datastorage, sorteddaystoshow, options);
    }

};
示例#8
0
文件: site.js 项目: tmcw/unicode-map
var UnicodeMap = require('./');
var ss = require('simple-statistics');

var map = new UnicodeMap(document.getElementById('map'));

var data = [];

var scale = [
  '#21313E',
  '#324D60',
  '#436C83',
  '#538DA8',
  '#62AFCE',
  '#70D3F4'].reverse();

var values = [];
for (var k in all) {
    data.push([+k, all[k]]);
    values.push(all[k]);
}

var quantiles = ss.quantile(values, [0, 0.2, 0.4, 0.6, 0.8, 1]);

for (var i = 0; i < data.length; i++) {
    for (var j = 0; j < scale.length; j++) {
        if (data[i][1] <= quantiles[j]) data[i][1] = scale[j];
    }
}

map.draw(data);
示例#9
0
 _.each(percentiles, function(percentile){
   quantiles.push(ss.quantile(vals, percentile * .01))
 })
  sorteddaystoshow.forEach(function (day) {
    var tr = $('<tr>');

    var daysRecords = datastorage[day].statsrecords;
    
    if (daysRecords.length === 0) {
      $('<td/>').appendTo(tr);
      $('<td class=\"tdborder\" style=\"width:160px\">' +  report_plugins.utils.localeDate(day) + '</td>').appendTo(tr);
      $('<td  class=\"tdborder\"colspan="10">'+translate('No data available')+'</td>').appendTo(tr);
      table.append(tr);
      return;;
    }

    minForDay = daysRecords[0].sgv;
    maxForDay = daysRecords[0].sgv;
    sum = 0;

    var stats = daysRecords.reduce(function(out, record) {
      record.sgv = parseFloat(record.sgv);
      if (record.sgv < options.targetLow) {
        out.lows++;
      } else if (record.sgv < options.targetHigh) {
        out.normal++;
      } else {
        out.highs++;
      }
      if (minForDay > record.sgv) {
        minForDay = record.sgv;
      }
      if (maxForDay < record.sgv) {
        maxForDay = record.sgv;
      }
      sum += record.sgv;
      return out;
    }, {
      lows: 0,
      normal: 0,
      highs: 0
    });
    var average = sum / daysRecords.length;

    var bgValues = daysRecords.map(function(r) { return r.sgv; });
    $('<td><div id=\"dailystat-chart-' + day.toString() + '\" class=\"inlinepiechart\"></div></td>').appendTo(tr);

    $('<td class=\"tdborder\" style=\"width:160px\">' +  report_plugins.utils.localeDate(day) + '</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + Math.round((100 * stats.lows) / daysRecords.length) + '%</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + Math.round((100 * stats.normal) / daysRecords.length) + '%</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + Math.round((100 * stats.highs) / daysRecords.length) + '%</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + daysRecords.length +'</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + minForDay +'</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + maxForDay +'</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + average.toFixed(1) +'</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + ss.standard_deviation(bgValues).toFixed(1) + '</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + ss.quantile(bgValues, 0.25).toFixed(1) + '</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + ss.quantile(bgValues, 0.5).toFixed(1) + '</td>').appendTo(tr);
    $('<td class=\"tdborder\">' + ss.quantile(bgValues, 0.75).toFixed(1) + '</td>').appendTo(tr);

    table.append(tr);
    var inrange = [
      {
        label: translate('Low'),
        data: Math.round(stats.lows * 1000 / daysRecords.length) / 10
      },
      {
        label: translate('In Range'),
        data: Math.round(stats.normal * 1000 / daysRecords.length) / 10
      },
      {
        label: translate('High'),
        data: Math.round(stats.highs * 1000 / daysRecords.length) / 10
      }
    ];
    $.plot(
      '#dailystat-chart-' + day.toString(),
      inrange,
      {
        series: {
          pie: {
            show: true
          }
        },
        colors: ['#f88', '#8f8', '#ff8']
      }
    );
  });
示例#11
0
 percentiles.forEach(function(percentile) {
   quantiles.push(ss.quantile(vals, percentile * 0.01));
 });
示例#12
0
/**
 * Builds and returns an array describing the legend colors.
 * Each element is an object with keys "color" and "upperBound", eg.
 * [ { color: [r, g, b, a], upperBound: 20 } , { color: [r, g, b, a]: upperBound: 80 } ]
 *
 * @param  {LegendHelper} legendHelper The legend helper.
 * @param {Integer} [colorBins] The number of color bins to use; defaults to legendHelper.tableColumnStyle.colorBins.
 * @return {Array} Array of objects with keys "color" and "upperBound".
 */
function buildBinColors(legendHelper, colorBins) {
    var tableColumn = legendHelper.tableColumn;
    var tableColumnStyle = legendHelper.tableColumnStyle;
    var colorGradient = legendHelper._colorGradient;
    var regionProvider = legendHelper._regionProvider;
    if (!defined(colorBins)) {
        colorBins = tableColumnStyle.colorBins;
    }

    if (colorBins <= 0 || tableColumnStyle.colorBinMethod.match(/none/i)) {
        return undefined;
    }
    var binColors = [];
    var i;
    var numericalValues;
    var usesIndicesIntoUniqueValues;
    if (!defined(tableColumn) || !defined(tableColumn.values)) {
        usesIndicesIntoUniqueValues = false;
        // There is no tableColumn.
        // Number by the index into regions instead, if it's region mapped; otherwise return undefined.
        if (regionProvider) {
            numericalValues = regionProvider.regions.map(function(region, index) { return index; });
        } else {
            return undefined;
        }
    } else {
        usesIndicesIntoUniqueValues = tableColumn.usesIndicesIntoUniqueValues;
        numericalValues = tableColumn.indicesOrNumericalValues;
    }
    // Must ask for fewer clusters than the number of items.
    var binCount = Math.min(colorBins, numericalValues.length);

    // Convert the output formats of two binning methods into our format.
    if (tableColumnStyle.colorBinMethod === 'quantile' ||
        tableColumnStyle.colorBinMethod === 'auto' && numericalValues.length > 1000 && (!usesIndicesIntoUniqueValues)) {
        // the quantile method is simpler, less accurate, but faster for large datasets. One issue is we don't check to see if any
        // values actually lie within a given quantile, so it's bad for small datasets.
        for (i = 0; i < binCount; i++) {
            binColors.push({
                upperBound: simplestats.quantile(numericalValues, (i + 1) / binCount),
                colorArray: getColorArrayFromColorGradient(colorGradient, i / (binCount - 1))
            });
        }
    } else {
        var clusters = simplestats.ckmeans(numericalValues, binCount);
        // Convert the ckmeans format [ [5, 20], [65, 80] ] into our format.
        for (i = 0; i < clusters.length; i++) {
            if (i > 0 && clusters[i].length === 1 && clusters[i][0] === clusters[i - 1][clusters[i - 1].length - 1]) {
                // When there are few unique values, we can end up with clusters like [1], [2],[2],[2],[3]. Let's avoid that.
                continue;
            }
            binColors.push({
                upperBound: clusters[i][clusters[i].length - 1],
            });
        }
        if (binColors.length > 1) {
            for (i = 0; i < binColors.length; i++) {
                binColors[i].colorArray = getColorArrayFromColorGradient(colorGradient, i / (binColors.length - 1));
            }
        } else {
            // only one binColor, pick the middle of the color gradient.
            binColors[0].colorArray = getColorArrayFromColorGradient(colorGradient, 0.5);
        }
    }
    return binColors;
}
 var dat90 = bins.map(function(bin) {
   return [bin[0], ss.quantile(bin[1], 0.9)];
 });