区间数据计算

区间数据计算

最近一年多来,一直比较忙,最近一段时间终于空闲了,把以前没写的都补上.....

这边随笔主要是计算一系列数据的间隔数据。从一堆数据中查询出每个区间的起始数据,结束数据以及数据个数,同时可以设置相应精度(小数位数)。

区间数据数据结构

 1、区间数据主要包括当前区间的起始数据,结束数据以及数据个数。结构如下:

?
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

可以将该返回数据用于相关图形进行绑定以及显示。

作者: JasenKin
出处: http://www.cnblogs.com/jasenkin/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
标签:  C#Interval Data

你可能感兴趣的:(C#,Data,interval)