public static TimeSeriesValues Differentiate(TimeSeriesValues seq) { using (seq.Lock.GetReadLock()) { TimeSeriesValues ret = new TimeSeriesValues(seq.ColumnNames.Select(n => "diff " + n).ToArray()); for (int i = 0; i < seq.SequenceLength - 1; i++) { decimal preTime, postTime; if (!seq.TryGetTimeFromIndex(i, out preTime) || !seq.TryGetTimeFromIndex(i + 1, out postTime)) { continue; } decimal elapse = postTime - preTime; if (elapse > 0) { decimal?[] pre = seq[i]; decimal?[] post = seq[i + 1]; decimal?[] diff = new decimal?[seq.ColumnCount]; for (int j = 0; j < seq.ColumnCount; j++) { if (pre[j].HasValue && post[j].HasValue) { diff[j] = (post[j].Value - pre[j].Value) / elapse; } } ret[preTime] = diff; } } return(ret); } }
public static TimeSeriesValues FrameMean(TimeSeriesValues seq, int meanLength) { using (seq.Lock.GetReadLock()) { if (meanLength <= 0) { throw new ArgumentOutOfRangeException("meanLength", "meanLength > 0"); } TimeSeriesValues ret = new TimeSeriesValues(seq.ColumnNames.Select(n => "mean " + n).ToArray()); for (int i = 0; i < seq.SequenceLength - meanLength; i++) { decimal?[] mean = new decimal?[seq.ColumnCount]; for (int j = 0; j < seq.ColumnCount; j++) { mean[j] = 0; } for (int j = 0; j < seq.ColumnCount; j++) { mean[j] = 0; for (int k = 0; k < meanLength; k++) { decimal?value = seq[i + k][j]; if (value.HasValue) { mean[j] += value.Value; } else { mean[j] = null; break; } } if (mean[j].HasValue) { mean[j] /= meanLength; } } decimal time; if (seq.TryGetTimeFromIndex(i, out time)) { ret.SetValue(time, mean); } } return(ret); } }
/// <summary> /// シーケンスの移動平均を求めます /// </summary> /// <param name="seq">シーケンス</param> /// <param name="smoothSpan">移動平均を取る時間範囲</param> /// <param name="timeOffset">出力の時間のオフセット.0で時間範囲の終了点の時間,smoothSpanの値で時間範囲の開始点の時間のところに出力される</param> /// <param name="limitRatioToContainEmptyValue">データの欠損があっても値を出力する限界率(0~1)</param> /// <returns></returns> public static TimeSeriesValues TimeSmooth(TimeSeriesValues seq, decimal smoothSpan, decimal timeOffset, decimal limitRatioToContainEmptyValue) { using (seq.Lock.GetReadLock()) { if (smoothSpan < 0) { throw new ArgumentOutOfRangeException("time", "'time' cannot be negative"); } TimeSeriesValues ret = new TimeSeriesValues(seq.ColumnNames); int[] nullCount = new int[seq.ColumnCount]; decimal[] sum = new decimal[seq.ColumnCount]; int count = 0; int beginIndex = 0; for (int endIndex = 0; endIndex < seq.SequenceLength; endIndex++) { decimal endTime; if (!seq.TryGetTimeFromIndex(endIndex, out endTime)) { continue; } for (int j = 0; j < seq.ColumnCount; j++) { if (seq[endIndex][j].HasValue) { sum[j] += seq[endIndex][j].Value; } else { nullCount[j]++; } } count++; decimal beginTime; while (beginIndex < endIndex && seq.TryGetTimeFromIndex(beginIndex, out beginTime)) { if (beginTime > endTime - smoothSpan) { break; } for (int j = 0; j < seq.ColumnCount; j++) { if (seq[beginIndex][j].HasValue) { sum[j] -= seq[beginIndex][j].Value; } else { nullCount[j]--; } } count--; beginIndex++; } Debug.Assert(nullCount.Max() <= count); Debug.Assert(count > 0); decimal?[] values = new decimal?[seq.ColumnCount]; for (int j = 0; j < seq.ColumnCount; j++) { decimal rate = (decimal)nullCount[j] / count; int validCount = count - nullCount[j]; if (rate <= limitRatioToContainEmptyValue && validCount > 0) { values[j] = sum[j] / validCount; } else { values[j] = null; } } ret[endTime + timeOffset] = values; } return(ret); } }