- * Extracts one series from the raw data (a 2D array) into an array of (date,
- * value) tuples.
- *
- * This is where undesirable points (i.e. negative values on log scales and
- * missing values through which we wish to connect lines) are dropped.
- * TODO(danvk): the "missing values" bit above doesn't seem right.
- *
- * @private
- * @param {Array.<Array.<(number|Array<Number>)>>} rawData Input data. Rectangular
- * grid of points, where rawData[row][0] is the X value for the row,
- * and rawData[row][i] is the Y data for series #i.
- * @param {number} i Series index, starting from 1.
- * @param {boolean} logScale True if using logarithmic Y scale.
- * @return {Array.<Array.<(?number|Array<?number>)>} Series array, where
- * series[row] = [x,y] or [x, [y, err]] or [x, [y, yplus, yminus]].
- */
-Dygraph.prototype.extractSeries_ = function(rawData, i, logScale) {
- // TODO(danvk): pre-allocate series here.
- var series = [];
- var errorBars = this.attr_("errorBars");
- var customBars = this.attr_("customBars");
- for (var j = 0; j < rawData.length; j++) {
- var x = rawData[j][0];
- var point = rawData[j][i];
- if (logScale) {
- // On the log scale, points less than zero do not exist.
- // This will create a gap in the chart.
- if (errorBars || customBars) {
- // point.length is either 2 (errorBars) or 3 (customBars)
- for (var k = 0; k < point.length; k++) {
- if (point[k] <= 0) {
- point = null;
- break;
- }
- }
- } else if (point <= 0) {
- point = null;
- }
- }
- // Fix null points to fit the display type standard.
- if (point !== null) {
- series.push([x, point]);
- } else {
- series.push([x, errorBars ? [null, null] : customBars ? [null, null, null] : point]);
- }
- }
- return series;
-};
-
-/**
- * @private
- * Calculates the rolling average of a data set.
- * If originalData is [label, val], rolls the average of those.
- * If originalData is [label, [, it's interpreted as [value, stddev]
- * and the roll is returned in the same form, with appropriately reduced
- * stddev for each value.
- * Note that this is where fractional input (i.e. '5/10') is converted into
- * decimal values.
- * @param {Array} originalData The data in the appropriate format (see above)
- * @param {Number} rollPeriod The number of points over which to average the
- * data
- */
-Dygraph.prototype.rollingAverage = function(originalData, rollPeriod) {
- rollPeriod = Math.min(rollPeriod, originalData.length);
- var rollingData = [];
- var sigma = this.attr_("sigma");
-
- var low, high, i, j, y, sum, num_ok, stddev;
- if (this.fractions_) {
- var num = 0;
- var den = 0; // numerator/denominator
- var mult = 100.0;
- for (i = 0; i < originalData.length; i++) {
- num += originalData[i][1][0];
- den += originalData[i][1][1];
- if (i - rollPeriod >= 0) {
- num -= originalData[i - rollPeriod][1][0];
- den -= originalData[i - rollPeriod][1][1];
- }
-
- var date = originalData[i][0];
- var value = den ? num / den : 0.0;
- if (this.attr_("errorBars")) {
- if (this.attr_("wilsonInterval")) {
- // For more details on this confidence interval, see:
- // http://en.wikipedia.org/wiki/Binomial_confidence_interval
- if (den) {
- var p = value < 0 ? 0 : value, n = den;
- var pm = sigma * Math.sqrt(p*(1-p)/n + sigma*sigma/(4*n*n));
- var denom = 1 + sigma * sigma / den;
- low = (p + sigma * sigma / (2 * den) - pm) / denom;
- high = (p + sigma * sigma / (2 * den) + pm) / denom;
- rollingData[i] = [date,
- [p * mult, (p - low) * mult, (high - p) * mult]];
- } else {
- rollingData[i] = [date, [0, 0, 0]];
- }
- } else {
- stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
- rollingData[i] = [date, [mult * value, mult * stddev, mult * stddev]];
- }
- } else {
- rollingData[i] = [date, mult * value];
- }
- }
- } else if (this.attr_("customBars")) {
- low = 0;
- var mid = 0;
- high = 0;
- var count = 0;
- for (i = 0; i < originalData.length; i++) {
- var data = originalData[i][1];
- y = data[1];
- rollingData[i] = [originalData[i][0], [y, y - data[0], data[2] - y]];
-
- if (y !== null && !isNaN(y)) {
- low += data[0];
- mid += y;
- high += data[2];
- count += 1;
- }
- if (i - rollPeriod >= 0) {
- var prev = originalData[i - rollPeriod];
- if (prev[1][1] !== null && !isNaN(prev[1][1])) {
- low -= prev[1][0];
- mid -= prev[1][1];
- high -= prev[1][2];
- count -= 1;
- }
- }
- if (count) {
- rollingData[i] = [originalData[i][0], [ 1.0 * mid / count,
- 1.0 * (mid - low) / count,
- 1.0 * (high - mid) / count ]];
- } else {
- rollingData[i] = [originalData[i][0], [null, null, null]];
- }
- }
- } else {
- // Calculate the rolling average for the first rollPeriod - 1 points where
- // there is not enough data to roll over the full number of points
- if (!this.attr_("errorBars")) {
- if (rollPeriod == 1) {
- return originalData;
- }
-
- for (i = 0; i < originalData.length; i++) {
- sum = 0;
- num_ok = 0;
- for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
- y = originalData[j][1];
- if (y === null || isNaN(y)) continue;
- num_ok++;
- sum += originalData[j][1];
- }
- if (num_ok) {
- rollingData[i] = [originalData[i][0], sum / num_ok];
- } else {
- rollingData[i] = [originalData[i][0], null];
- }
- }
-
- } else {
- for (i = 0; i < originalData.length; i++) {
- sum = 0;
- var variance = 0;
- num_ok = 0;
- for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
- y = originalData[j][1][0];
- if (y === null || isNaN(y)) continue;
- num_ok++;
- sum += originalData[j][1][0];
- variance += Math.pow(originalData[j][1][1], 2);
- }
- if (num_ok) {
- stddev = Math.sqrt(variance) / num_ok;
- rollingData[i] = [originalData[i][0],
- [sum / num_ok, sigma * stddev, sigma * stddev]];
- } else {
- // This explicitly preserves NaNs to aid with "independent series".
- // See testRollingAveragePreservesNaNs.
- var v = (rollPeriod == 1) ? originalData[i][1][0] : null;
- rollingData[i] = [originalData[i][0], [v, v, v]];
- }
- }
- }
- }
-
- return rollingData;
-};
-
-/**