/// <summary> /// 混合处理,降采样和插值,不保留边界节点 /// </summary> /// <param name="data">原始数据</param> /// <param name="size">桶大小。如60/3600/86400</param> /// <param name="offset">偏移量。时间不是对齐零点时使用</param> /// <returns></returns> public TimePoint[] Process(TimePoint[] data, Int32 size, Int32 offset = 0) { if (data == null || data.Length < 2) { return(data); } if (size <= 1) { return(data); } var xs = new Int64[data.Length]; for (var i = 0; i < data.Length; i++) { xs[i] = data[i].Time; } var buckets = SamplingHelper.SplitByFixedSize(xs, size, offset); // 每个桶选择一个点作为代表 var sampled = new TimePoint[buckets.Length]; var last = 0; for (var i = 0; i < buckets.Length; i++) { // 断层,插值 var item = buckets[i]; if (item.Start < 0) { // 取last用于插值起点,如果不存在,可以取0点 // 此时End指向下一个有效点,即使下一个桶也是断层 sampled[i].Time = i * size; sampled[i].Value = Interpolation.Process(data, last, item.End, i); continue; } TimePoint point = default; var vs = 0.0; for (var j = item.Start; j < item.End; j++) { vs += data[j].Value; } last = item.End - 1; point.Value = vs / (item.End - item.Start); // 对齐 point.Time = AlignMode switch { AlignModes.Right => (i + 1) * size - 1, AlignModes.Center => data[(Int32)Math.Round((i + 0.5) * size)].Time, _ => i * size, }; sampled[i] = point; } return(sampled); }
/// <summary> /// 降采样处理 /// </summary> /// <param name="data">原始数据</param> /// <param name="threshold">阈值,采样数</param> /// <returns></returns> public TimePoint[] Down(TimePoint[] data, Int32 threshold) { if (data == null || data.Length < 2) { return(data); } if (threshold < 2 || threshold >= data.Length) { return(data); } var buckets = SamplingHelper.SplitByAverage(data.Length, threshold, true); // 每个桶选择一个点作为代表 var sampled = new TimePoint[buckets.Length]; for (var i = 0; i < buckets.Length; i++) { var item = buckets[i]; TimePoint point = default; var min_value = Double.MaxValue; for (var j = item.Start; j < item.End; j++) { if (data[j].Value < min_value) { min_value = data[j].Value; point = data[j]; } } // 对齐 switch (AlignMode) { case AlignModes.Left: point.Time = data[item.Start].Time; break; case AlignModes.Right: point.Time = data[item.End - 1].Time; break; case AlignModes.Center: point.Time = data[(Int32)Math.Round((item.Start + item.End) / 2.0)].Time; break; } sampled[i++] = point; } // 第一个点和最后一个点 if (AlignMode == AlignModes.None) { sampled[0] = data[0]; sampled[threshold - 1] = data[data.Length - 1]; } return(sampled); }
/// <summary> /// 降采样处理 /// </summary> /// <param name="data">原始数据</param> /// <param name="threshold">阈值,采样数</param> /// <returns></returns> public TimePoint[] Down(TimePoint[] data, Int32 threshold) { if (data == null || data.Length < 2) { return(data); } if (threshold < 2 || threshold >= data.Length) { return(data); } var buckets = SamplingHelper.SplitByAverage(data.Length, threshold, true); // 每个桶选择一个点作为代表 var sampled = new TimePoint[buckets.Length]; for (var i = 0; i < buckets.Length; i++) { var item = buckets[i]; TimePoint point = default; var vs = 0.0; for (var j = item.Start; j < item.End; j++) { vs += data[j].Value; } point.Value = vs; // 对齐 switch (AlignMode) { case AlignModes.Left: default: point.Time = data[item.Start].Time; break; case AlignModes.Right: point.Time = data[item.End - 1].Time; break; case AlignModes.Center: point.Time = data[(Int32)Math.Round((item.Start + item.End) / 2.0)].Time; break; } sampled[i] = point; } return(sampled); }
/// <summary> /// 降采样处理 /// </summary> /// <param name="data">原始数据</param> /// <param name="threshold">阈值,采样数</param> /// <returns></returns> public TimePoint[] Down(TimePoint[] data, Int32 threshold) { if (data == null || data.Length < 2) { return(data); } if (threshold < 2 || threshold >= data.Length) { return(data); } var buckets = SamplingHelper.SplitByAverage(data.Length, threshold, AlignMode == AlignModes.None); // 三角形选择当前同相邻三个ABC点,选择B,使得三角形有效面积最大 var sampled = new TimePoint[buckets.Length]; for (var i = 0; i < buckets.Length; i++) { var item = buckets[i]; // 计算每个点的有效区域,并选取有效区域最大的点作为桶的代表点 TimePoint point = default; var max_area = -1.0; for (var j = item.Start + 1; j < item.End - 1; j++) { // 选择一个点B,计算ABC三角形面积 var pointA = data[j - 1]; var pointB = data[j]; var pointC = data[j + 1]; var area = Math.Abs( (pointA.Time - pointC.Time) * (pointB.Value - pointA.Value) - (pointA.Time - pointB.Time) * (pointC.Value - pointA.Value) ) / 2; if (area > max_area) { max_area = area; point = pointB; } } // 对齐 switch (AlignMode) { case AlignModes.Left: point.Time = data[item.Start].Time; break; case AlignModes.Right: point.Time = data[item.End - 1].Time; break; case AlignModes.Center: point.Time = data[(Int32)Math.Round((item.Start + item.End) / 2.0)].Time; break; } sampled[i++] = point; } // 第一个点和最后一个点 if (AlignMode == AlignModes.None) { sampled[0] = data[0]; sampled[threshold - 1] = data[data.Length - 1]; } return(sampled); }
/// <summary> /// 混合处理,降采样和插值 /// </summary> /// <param name="data">原始数据</param> /// <param name="size">桶大小。如60/3600/86400</param> /// <param name="offset">偏移量。时间不是对齐零点时使用</param> /// <returns></returns> public TimePoint[] Process(TimePoint[] data, Int32 size, Int32 offset = 0) { if (data == null || data.Length < 2) { return(data); } if (size <= 1) { return(data); } var xs = new Int64[data.Length]; for (var i = 0; i < data.Length; i++) { xs[i] = data[i].Time; } var buckets = SamplingHelper.SplitByFixedSize(xs, size, offset); // 每个桶选择一个点作为代表 var sampled = new TimePoint[buckets.Length]; var last = 0; for (var i = 0; i < buckets.Length; i++) { // 断层,插值 var b = buckets[i]; if (b.Start < 0) { sampled[i].Time = i * size; sampled[i].Value = Interpolation.Process(data, last, b.End, i); continue; } TimePoint point = default; var vs = 0.0; for (var j = b.Start; j < b.End; j++) { vs += data[j].Value; } last = b.End - 1; point.Value = vs / (b.End - b.Start); // 对齐 switch (AlignMode) { case AlignModes.Left: default: point.Time = i * size; break; case AlignModes.Right: point.Time = (i + 1) * size - 1; break; case AlignModes.Center: point.Time = data[(Int32)Math.Round((i + 0.5) * size)].Time; break; } sampled[i] = point; } return(sampled); }
/// <summary> /// 降采样处理 /// </summary> /// <param name="data">原始数据</param> /// <param name="threshold">阈值,采样数</param> /// <returns></returns> public TimePoint[] Down(TimePoint[] data, Int32 threshold) { if (data == null || data.Length < 2) { return(data); } if (threshold < 2 || threshold >= data.Length) { return(data); } var buckets = SamplingHelper.SplitByAverage(data.Length, threshold, AlignMode == AlignModes.None); // 三角形选择相邻三个桶的ABC点,A是前一个桶选择点,C是后一个桶平均点,当前桶选择B,使得三角形有效面积最大 TimePoint pointA = default; if (AlignMode == AlignModes.None) { pointA = data[0]; } var sampled = new TimePoint[buckets.Length]; for (var i = 0; i < buckets.Length; i++) { var item = buckets[i]; // 计算下一个桶的平均点作为C TimePoint pointC = default; { var start = buckets[i + 1].Start; var end = buckets[i + 1].End; end = end < data.Length ? end : data.Length; var length = end - start; for (; start < end; start++) { pointC.Time += data[start].Time; pointC.Value += data[start].Value; } if (length > 0) { pointC.Time /= length; pointC.Value /= length; } else { pointC = data[end - 1]; } } // 计算每个点的有效区域,并选取有效区域最大的点作为桶的代表点 TimePoint point = default; { var max_area = -1.0; for (var j = item.Start; j < item.End; j++) { // 选择一个点B,计算ABC三角形面积 var pointB = data[j]; var area = Math.Abs( (pointA.Time - pointC.Time) * (pointB.Value - pointA.Value) - (pointA.Time - pointB.Time) * (pointC.Value - pointA.Value) ) / 2; if (area > max_area) { max_area = area; point = pointB; } } } pointA = point; // 对齐 switch (AlignMode) { case AlignModes.Left: point.Time = data[item.Start].Time; break; case AlignModes.Right: point.Time = data[item.End - 1].Time; break; case AlignModes.Center: point.Time = data[(Int32)Math.Round((item.Start + item.End) / 2.0)].Time; break; } sampled[i++] = point; } // 第一个点和最后一个点 if (AlignMode == AlignModes.None) { sampled[0] = data[0]; sampled[threshold - 1] = data[data.Length - 1]; } return(sampled); }