/** * Performs an exponential moving average of the specified range, * handling missing data in a way that does not distort the average. * * If the spcified window size is less than the range size, an array * is returned, so there is no need to call this function serveral * times in a row. * * @param alpha The geometric progression ratio. * @param windowSize The size of the moving average window. * @param values The range on which we will calculate the average. * @return The exponential moving average of the specified range. * @customfunction */ function ExponentialMovingAverage(alpha, windowSize, values) { var weights = []; for (var i = 0; i < windowSize; i++) { var w = Math.pow(alpha, windowSize - 1 - i); weights[weights.length] = w; } var average = WeightedMovingAverage(weights, values); return average; } /** * Performs a weighted moving average of the specified range, * handling missing data in a way that does not distort the average. * * @param weights An array with the weights to be applied. * @param values The range on which we will calculate the average. * @return The weighted moving average of the specified range. * @customfunction */ function WeightedMovingAverage(weights, values) { // If values is a scalar or weights is a scalar or // weights has length 1, return values. if (!values.map || !weights.map || weights.length === 1) { return values; } var windowSize = weights.length; var rows = values.length; if (windowSize > rows) { weights.splice(0, windowSize - rows); windowSize = weights.length; Logger.log("WARNING: windowSize greater than number of rows" + " in the range, truncating the weitghts to " + "the number of rows and using the last weights."); } /* If this function is called from the spreadsheet with a range, * weights will be array of columns, lets remove this extra * indirection. */ if (weights[0].map) { for (var i = 0; i < weights.length; i++) { weights[i] = weights[i][0]; } } var average = []; for (var i = 0; i < rows - windowSize + 1; i++) { var sumValues = 0; var sumWeigths = 0; for (var j = 0; j < windowSize; j++) { var x = values[i + j][0]; if (typeof x === "number") { var w = weights[j]; if (typeof w === "number") { sumWeigths += w; sumValues += w * x; } } } var avg = sumValues / sumWeigths; average[average.length] = avg; } return average; }