【问题标题】:How to interpolate missing dates and data points in javascript arrays?如何在 javascript 数组中插入缺失的日期和数据点?
【发布时间】:2013-03-07 22:40:47
【问题描述】:

假设我有以下内容:

var sales = [5, 4, 2];
var months = ["Jan 2011", "Apr 2011", "Feb 2012"];

如果我有一个给定的范围说:

var range = ["Jan 2011", "Mar 2012"];

我想在月份之间“插值”,以便得到以下结果:

var sales = [5, 4, 4, 4, ..., 2, 2];
var months = ["Jan 2011", "Feb 2011", "Mar 2011", ...., "Feb 2012", "Mar 2012"];

是否有必要创建一个“按顺序排列的所有可能日期的范围”数组,其中包含所有希望包含的“月 + 年”?或者是否有可能使用 javascript 不必这样做? 如何做到这一点?

注意:假设如果月份数组中不存在日期,则将使用之前任何可用月份的数据填充它。

【问题讨论】:

  • 是否假设如果months 数组中不存在日期,则它的销售额与上个月的值相同?
  • 是的,假设如果月份数组中不存在日期,则将使用之前任何可用月份的数据填充它。
  • 您在 months 数组中使用了实际的 Date 对象吗?如果是这样(或者如果可以使用日期),那么您应该能够遍历月份数组,跟踪索引,并将日期增加 1 个月,直到达到下一个索引处的值,然后填充“结果”销售数组,具有前一个索引处的“输入”销售值。请参阅 stackoverflow.com/questions/5645058/… 在 javascript 中为日期添加月份
  • 它们可能是实际的日期对象,用于生成存储在这些数组中的字符串。
  • 如果可能的话,我建议使用实际的 Date 对象而不是字符串,因为 Date 对象公开了进行日期算术的方法(我认为这基本上是你需要的);在弦上做同样的事情显然更难。如果最终结果必须是字符串表示形式,那么我建议您在完成插值之后(即在表示层)进行转换为字符串

标签: javascript


【解决方案1】:

如果您不需要严格使用之前已知的销售数据,您可以对销售数据使用一种很好的样条插值:

/* Fritsch-Carlson monotone cubic spline interpolation
   Usage example:
    var f = createInterpolant([0, 1, 2, 3], [0, 1, 4, 9]);
    var message = '';
    for (var x = 0; x <= 3; x += 0.5) {
        var xSquared = f(x);
        message += x + ' squared is about ' + xSquared + '\n';
    }
    alert(message);
*/
var createInterpolant = function(xs, ys) {
    var i, length = xs.length;

    // Deal with length issues
    if (length != ys.length) { throw 'Need an equal count of xs and ys.'; }
    if (length === 0) { return function(x) { return 0; }; }
    if (length === 1) {
        // Impl: Precomputing the result prevents problems if ys is mutated later and allows garbage collection of ys
        // Impl: Unary plus properly converts values to numbers
        var result = +ys[0];
        return function(x) { return result; };
    }

    // Rearrange xs and ys so that xs is sorted
    var indexes = [];
    for (i = 0; i < length; i++) { indexes.push(i); }
    indexes.sort(function(a, b) { return xs[a] < xs[b] ? -1 : 1; });
    var oldXs = xs, oldYs = ys;
    // Impl: Creating new arrays also prevents problems if the input arrays are mutated later
    xs = []; ys = [];
    // Impl: Unary plus properly converts values to numbers
    for (i = 0; i < length; i++) { xs.push(+oldXs[indexes[i]]); ys.push(+oldYs[indexes[i]]); }

    // Get consecutive differences and slopes
    var dys = [], dxs = [], ms = [];
    for (i = 0; i < length - 1; i++) {
        var dx = xs[i + 1] - xs[i], dy = ys[i + 1] - ys[i];
        dxs.push(dx); dys.push(dy); ms.push(dy/dx);
    }

    // Get degree-1 coefficients
    var c1s = [ms[0]];
    for (i = 0; i < dxs.length - 1; i++) {
        var m = ms[i], mNext = ms[i + 1];
        if (m*mNext <= 0) {
            c1s.push(0);
        } else {
            var dx = dxs[i], dxNext = dxs[i + 1], common = dx + dxNext;
            c1s.push(3*common/((common + dxNext)/m + (common + dx)/mNext));
        }
    }
    c1s.push(ms[ms.length - 1]);

    // Get degree-2 and degree-3 coefficients
    var c2s = [], c3s = [];
    for (i = 0; i < c1s.length - 1; i++) {
        var c1 = c1s[i], m = ms[i], invDx = 1/dxs[i], common = c1 + c1s[i + 1] - m - m;
        c2s.push((m - c1 - common)*invDx); c3s.push(common*invDx*invDx);
    }

    // Return interpolant function
    return function(x) {
        // The rightmost point in the dataset should give an exact result
        var i = xs.length - 1;
        if (x == xs[i]) { return ys[i]; }

        // Search for the interval x is in, returning the corresponding y if x is one of the original xs
        var low = 0, mid, high = c3s.length - 1;
        while (low <= high) {
            mid = Math.floor(0.5*(low + high));
            var xHere = xs[mid];
            if (xHere < x) { low = mid + 1; }
            else if (xHere > x) { high = mid - 1; }
            else { return ys[mid]; }
        }
        i = Math.max(0, high);

        // Interpolate
        var diff = x - xs[i], diffSq = diff*diff;
        return ys[i] + c1s[i]*diff + c2s[i]*diffSq + c3s[i]*diff*diffSq;
    };
};

这可以与以下代码一起使用来做你想做的事:

var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var fromMonthNumber = function(monthNumber) {
    return monthNames[monthNumber % 12] + ' ' + ((monthNumber / 12) | 0);
};

var toMonthNumber = function(monthName) {
    var date = new Date(Date.parse(monthName));
    return 12*date.getFullYear() + date.getMonth();
};

var interpolateSales = function(sales, months, range) {
    var f = createInterpolant(months.map(toMonthNumber), sales);

    var resultSales = [], resultMonths = [];
    var firstMonth = toMonthNumber(range[0]), lastMonth = toMonthNumber(range[1]);
    for (var x = firstMonth; x <= lastMonth; x++) {
        resultSales.push(Math.round(f(x)));
        resultMonths.push(fromMonthNumber(x));
    }

    return { sales: resultSales, months: resultMonths };
};

这样,interpolateSales([5, 4, 2], ["Jan 2011", "Apr 2011", "Feb 2012"], ["Jan 2011", "Mar 2012"]) 就是:

{
    sales: [5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2],
    months: ["Jan 2011", "Feb 2011", "Mar 2011", "Apr 2011", "May 2011", "Jun 2011", "Jul 2011", "Aug 2011", "Sep 2011", "Oct 2011", "Nov 2011", "Dec 2011", "Jan 2012", "Feb 2012", "Mar 2012"]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多