最近一年多来,一直比较忙,最近一段时间终于空闲了,把以前没写的都补上.....
这边随笔主要是计算一系列数据的间隔数据。从一堆数据中查询出每个区间的起始数据,结束数据以及数据个数,同时可以设置相应精度(小数位数)。
区间数据数据结构
1、区间数据主要包括当前区间的起始数据,结束数据以及数据个数。结构如下:
class="brush:csharp;gutter:false;"> public struct IntervalData<TKey, TValue> { private TKey _startValue; private TKey _endValue; private TValue _count; public IntervalData(TKey startValue, TKey endValue, TValue count) { this._startValue = startValue; this._endValue = endValue; this._count = count; } public TKey StartValue { get { return this._startValue; } set { this._startValue = value; } } public TKey EndValue { get { return this._endValue; } set { this._endValue = value; } } public TValue Count { get { return this._count; } set { this._count = value; } } }
区间数据计算算法
首先需要注意的几点如下:
1、区间应该大于等于1,精度必须小于等于15(double精度最大值)。
2、区间宽度需要微调,相应需要增加相对应的精度值。
3、最大值和最小值需要微调,相应需要增加或者减少相对应的精度值。
public class DataCalculator { public int IntervalCount { get; set; } public double IntervalWidth { get; private set; } public double MaxValue { get; set; } public double MinValue { get; private set; } public const int MAX_DIGIT_SCALE = 15; public DataCalculator() { } public DataCalculator(int intervalCount) { if (intervalCount <= 0) { this.IntervalCount = 1; } else { this.IntervalCount = intervalCount; } } /// <summary> /// 计算间隔数据起始点,结束点以及数量的列表。 /// </summary> /// <param name="values">需要计算的数值列表。</param> /// <param name="digits">小数点位数。用于精确到指定位数的小数点。 /// 大于等于0,小于等于15。小于0时设置为0,大于15时设置为15。</param> /// <returns>返回间隔数据列表。</returns> public IList<IntervalData<double, int>> Calculate(IList<double> values, int digits = 0) { if (values == null || values.Count == 0) { return new List<IntervalData<double, int>>(); } CheckDoubleScale(ref digits); AdjustMinAndMaxValue(values, digits); AdjustIntervalWidth(digits); return CalculateResult(values, digits); } private IList<IntervalData<double, int>> CalculateResult(IEnumerable<double> values, int digits) { var dataResult = new List<IntervalData<double, int>>(); double startValue = this.MinValue; for (int index = 0; index < this.IntervalCount; index++) { int count = 0; double endValue = Math.Round(startValue + this.IntervalWidth, digits); foreach (double currValue in values) { if (currValue >= startValue && currValue < endValue) { ++count; } } if (index == this.IntervalCount - 1 && this.MaxValue < endValue) { this.MaxValue = endValue; } dataResult.Add(new IntervalData<double, int>(startValue, endValue, count)); startValue = endValue; } return dataResult; } private void AdjustIntervalWidth(int digits) { double intervalWidth = (this.MaxValue - this.MinValue) / this.IntervalCount; double currentIntervalWidth = Math.Round(intervalWidth, digits); if (currentIntervalWidth < intervalWidth) { currentIntervalWidth += 1 / Math.Pow(10, digits); } if (currentIntervalWidth == 0) { currentIntervalWidth = 1; } this.IntervalWidth = currentIntervalWidth; } private void AdjustMinAndMaxValue(IEnumerable<double> values, int digits) { double minValue = values.Min(); double maxValue = values.Max(); // 计算最小值,将最小值减少相应的精度值,避免最小值未进入计算 double currentMinValue = Math.Round(minValue, digits); if (currentMinValue > minValue) { currentMinValue -= 1 / Math.Pow(10, digits); } // 计算最大值,将最大值增加相应的精度值,避免最大值未进入计算 double currentMaxValue = Math.Round(maxValue, digits); if (currentMaxValue <= maxValue) { currentMaxValue += 1 / Math.Pow(10, digits); } this.MinValue = currentMinValue; this.MaxValue = currentMaxValue; } private static void CheckDoubleScale(ref int digits) { if (digits < 0) { digits = 0; } if (digits > MAX_DIGIT_SCALE) { digits = MAX_DIGIT_SCALE; } } }
具体应用
应用比较简单,示例如下:
IList<double> dataPoints = new List<double>() { -4, 5, 6, 99.54, 0, 65 }; var calculator = new DataCalculator(5); IList<IntervalData<double, int>> datas = calculator.Calculate(dataPoints, 2); StringBuilder builder = new StringBuilder(); foreach (var data in datas) { builder.AppendLine(string.Format("StartValue:{0} EndValue:{1} Count:{2}", data.StartValue, data.EndValue, data.Count)); } string result = builder.ToString(); Console.Write(result);
输出结果为:
StartValue:-4 EndValue:16.71 Count:4
StartValue:16.71 EndValue:37.42 Count:0
StartValue:37.42 EndValue:58.13 Count:0
StartValue:58.13 EndValue:78.84 Count:1
StartValue:78.84 EndValue:99.55 Count:1
可以将该返回数据用于相关图形进行绑定以及显示。