You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#时序数据移动平均如何设置最小周期为1?

C#实现支持最小周期的移动平均计算

问题背景

现有如下时序数据:

var numNames = new SeriesBuilder<int, string>() {
  { 1, "one" }, { 2, "two" },{ 3, "three" }, { 4, "four" }, { 5, "five" }, { 6, "six" }, {7, "seven"}, {8, "eight"}, {9, "nine"}, {10, "ten"} }.Series;

需要计算窗口大小为n的移动平均,要求最小周期为1(即从第一个元素开始就计算对应窗口内的均值,无需等窗口填满)。但使用Stats.movingMean(5, numNames)时,前6个元素均为缺失值——因为该方法不支持设置最小周期,而Python中可通过numNames.rolling(5, min_periods=1).mean()轻松实现该逻辑,需要在C#中找到替代方案。

解决方案

假设你使用的是Deedle库(从SeriesBuilder判断),可以通过自定义扩展方法实现支持最小周期的移动平均。以下是完整实现:

1. 扩展方法实现(适配Deedle)

using Deedle;
using System;
using System.Collections.Generic;
using System.Linq;

public static class SeriesMovingAverageExtensions
{
    public static Series<K, double> MovingMeanWithMinPeriods<K>(this Series<K, double> numericSeries, int windowSize, int minPeriods)
        where K : IComparable<K>
    {
        // 参数合法性校验
        if (windowSize < 1)
            throw new ArgumentOutOfRangeException(nameof(windowSize), "窗口大小必须大于0");
        if (minPeriods < 1 || minPeriods > windowSize)
            throw new ArgumentOutOfRangeException(nameof(minPeriods), $"最小周期必须介于1和{windowSize}之间");

        var values = numericSeries.Values.ToList();
        var keys = numericSeries.Keys.ToList();
        var resultValues = new List<double>(values.Count);

        for (int i = 0; i < values.Count; i++)
        {
            // 计算当前窗口的起始索引
            int startIdx = Math.Max(0, i - windowSize + 1);
            int currentWindowCount = i - startIdx + 1;

            // 根据最小周期计算均值
            if (currentWindowCount < minPeriods)
            {
                // 若不满足最小周期,可返回NaN;若要匹配Python min_periods=1的行为,替换为下方注释代码
                resultValues.Add(double.NaN);
                // resultValues.Add(values.Take(i + 1).Average());
            }
            else
            {
                var window = values.GetRange(startIdx, currentWindowCount);
                resultValues.Add(window.Average());
            }
        }

        return Series(keys, resultValues);
    }
}

2. 使用示例

注意:你的numNames是字符串类型的序列,需要先转换为数值型序列才能计算均值,示例如下:

// 字符串转数值(根据实际映射规则调整)
var numberMapping = new Dictionary<string, int>
{
    {"one", 1}, {"two", 2}, {"three", 3}, {"four",4}, {"five",5},
    {"six",6}, {"seven",7}, {"eight",8}, {"nine",9}, {"ten",10}
};

var numericSeries = numNames.Select(kv => numberMapping[kv.Value])
                            .ToSeries(numNames.Keys);

// 计算窗口大小为5、最小周期为1的移动平均
var average_5 = numericSeries.MovingMeanWithMinPeriods(5, 1);

3. 通用列表实现(不依赖Deedle)

如果不使用时序库,直接处理数值列表,可使用以下方法:

using System;
using System.Collections.Generic;
using System.Linq;

public static class MovingAverageHelper
{
    public static List<double> CalculateMovingAverage(List<double> data, int windowSize, int minPeriods)
    {
        if (windowSize < 1 || minPeriods < 1 || minPeriods > windowSize)
            throw new ArgumentException("参数不合法:窗口大小需大于0,最小周期需介于1和窗口大小之间");

        var result = new List<double>();
        for (int i = 0; i < data.Count; i++)
        {
            int startIdx = Math.Max(0, i - windowSize + 1);
            int currentCount = i - startIdx + 1;

            if (currentCount < minPeriods)
            {
                result.Add(double.NaN);
                // 替换为下方代码可实现最小周期1时从第一个元素开始计算均值:
                // result.Add(data.Take(i + 1).Average());
            }
            else
            {
                var window = data.GetRange(startIdx, currentCount);
                result.Add(window.Average());
            }
        }
        return result;
    }
}

关键逻辑说明

核心逻辑是遍历每个元素时,动态计算当前窗口的起始位置,然后根据当前窗口内的元素数量是否满足最小周期要求,决定是否计算均值:

  • 当元素数量不足最小周期时,可返回缺失值(NaN),或直接计算现有元素的均值(对应Python中min_periods=1的行为)
  • 当元素数量满足要求时,计算窗口内所有元素的均值

内容的提问来源于stack exchange,提问作者mhern0749

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 08:09:55