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
相关产品推荐
相关产品推荐

