zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C#,核心基础算法——简单易用、稳定可靠的统计学常用算法之原理与源代码

c#算法基础原理 简单 常用 核心 源代码
2023-09-11 14:15:48 时间

1、统计学常用算法

统计分析科学
在“政治算术”阶段出现的统计与数学的结合趋势逐渐发展形成了“统计分析科学”。
十九世纪末,欧洲大学开设的“国情纪要”或“政治算数”等课程名称逐渐消失,代之而起的是“统计分析科学”课程。当时的“统计分析科学”(Science of statistical analysis)课程的内容仍然是分析研究社会经济问题。
“统计分析科学”课程的出现是现代统计发展阶段的开端。1908年,“学生”氏(William Sleey Gosset的笔名Student)发表了关于t分布的论文。这是一篇在统计学发展史上划时代的文章,它创立了小样本代替大样本的方法,开创了统计学的新纪元。
正态分布的钟型曲线
正态分布的钟型曲线
现代统计学的代表人物首推比利时统计学家奎特莱(Adolphe Quelet),他将统计分析科学广泛应用于社会科学,自然科学和工程技术科学领域,因为他深信统计学是可以用于研究任何科学的一般研究方法.
现代统计学的理论基础概率论始于研究赌博的机遇问题,大约开始于1477年。数学家为了解释支配机遇的一般法则进行了长期的研究,逐渐形成了概率论理论框架。在概率论进一步发展的基础上,到十九世纪初,数学家们逐渐建立了观察误差理论,正态分布理论和最小平方法则。于是,现代统计方法便有了比较坚实的理论基础。
 

1.1 最大值

数组中的最大者。

1.2 最小值

数组中的最小者。

1.3 和值 Sum

数组所有数值的总和。

1.4 均值,平均值 Mean

平均数,统计学术语,是表示一组数据集中趋势的量数,是指在一组数据中所有数据之和再除以这组数据的个数。它是反映数据集中趋势的一项指标。解答平均数应用题的关键在于确定“总数量”以及和总数量对应的总份数。
在统计工作中,平均数(均值)和标准差是描述数据资料集中趋势和离散程度的两个最重要的测度值。
平均数是统计学中最常用的统计量,用来表明资料中各观测值相对集中较多的中心位置。在畜牧业、水产业生产实践和科学研究中,平均数被广泛用来描述或比较各种技术措施的效果、畜禽某些数量性状的指标等等。
统计平均数是用于反映现象总体的一般水平,或分布的集中趋势。数值平均数是总体标志总量对比总体单位数而计算的。
平均数是统计中的一个重要概念。小学数学里所讲的平均数一般是指算术平均数,也就是一组数据的和除以这组数据的个数所得的商。在统计中算术平均数常用于表示统计对象的一般水平,它是描述数据集中位置的一个统计量。既可以用它来反映一组数据的一般情况、和平均水平,也可以用它进行不同组数据的比较,以看出组与组之间的差别。
用平均数表示一组数据的情况,有直观、简明的特点,所以在日常生活中经常用到,如平均速度、平均身高、平均产量、平均成绩等等。

1.5 众数 Mode

对于给定的含有N个元素的多重集合,每个元素在S中出现次数最多的成为该元素的重数,多重集合S重的重数最大的元素成为【众数】。
简单地说,就是重复出现次数最多的数值。
众数(Mode)是指在统计分布上具有明显集中趋势点的数值,代表数据的一般水平。 也是一组数据中出现次数最多的数值,有时众数在一组数中有好几个。用M表示。
众数是样本观测值在频数分布表中频数最多的那一组的组中值,主要应用于大面积普查研究之中。
众数是在一组数据中,出现次数最多的数据,是一组数据中的原数据,而不是相应的次数。
一组数据中的众数不止一个,如数据2、3、-1、2、1、3中,2、3都出现了两次,它们都是这组数据中的众数。
一般来说,一组数据中,出现次数最多的数就叫这组数据的众数。
例如:1,2,3,3,4的众数是3。
但是,如果有两个或两个以上个数出现次数都是最多的,那么这几个数都是这组数据的众数。
例如:1,2,2,3,3,4的众数是2和3。
还有,如果所有数据出现的次数都一样,那么这组数据没有众数。
例如:1,2,3,4,5没有众数。
在高斯分布中,众数位于峰值。

1.6 中位数 Median

中位数(Median)又称中值,统计学中的专有名词,是按顺序排列的一组数据中居于中间位置的数,代表一个样本、种群或概率分布中的一个数值,其可将数值集合划分为相等的上下两部分。对于有限的数集,可以通过把所有观察值高低排序后找出正中间的一个作为中位数。如果观察值有偶数个,通常取最中间的两个数值的平均数作为中位数。

1.7 中列数,中值

中列数是指样本中极大值与极小值的平均。有时作为对称分布的均值的粗略估计。

1.8 四分位数 Quartile

四分位数(Quartile)也称四分位点,是指在统计学中把所有数值由小到大排列并分成四等份,处于三个分割点位置的数值。多应用于统计学中的箱线图绘制。它是一组数据排序后处于25%和75%位置上的值。四分位数是通过3个点将全部数据等分为4部分,其中每部分包含25%的数据。很显然,中间的四分位数就是中位数,因此通常所说的四分位数是指处在25%位置上的数值(称为下四分位数)和处在75%位置上的数值(称为上四分位数)。与中位数的计算方法类似,根据未分组数据计算四分位数时,首先对数据进行排序,然后确定四分位数所在的位置,该位置上的数值就是四分位数。与中位数不同的是,四分位数位置的确定方法有几种,每种方法得到的结果会有一定差异,但差异不会很大。

1.9 极差 Range

极差又称范围误差或全距(Range),以R表示,是用来表示统计资料中的变异量数(measures of variation),其最大值与最小值之间的差距,即最大值减最小值后所得之数据。
它是标志值变动的最大范围,它是测定标志变动的最简单的指标。移动极差(Moving Range)是其中的一种。极差不能用作比较,单位不同 ,方差能用作比较, 因为都是个比率。

移动极差(Moving Range),是指两个或多个连续样本值中最大值与最小值之差,这种差是按这样方式计算的:每当得到一个额外的数据点时,就在样本中加上这个新的点,同时删除其中时间上“最老的”点,然后计算与这点有关的极差,因此每个极差的计算至少与前一个极差的计算共用一个点的值。一般说来,移动极差用于单值控制图,并且通常用两点(连续的点)来计算移动极差。

在统计中常用极差来刻画一组数据的离散程度,以及反映的是变量分布的变异范围和离散幅度,在总体中任何两个单位的标准值之差都不能超过极差。同时,它能体现一组数据波动的范围。极差越大,离散程度越大,反之,离散程度越小。
极差只指明了测定值的最大离散范围,而未能利用全部测量值的信息,不能细致地反映测量值彼此相符合的程度,极差是总体标准偏差的有偏估计值,当乘以校正系数之后,可以作为总体标准偏差的无偏估计值,它的优点是计算简单,含义直观,运用方便,故在数据统计处理中仍有着相当广泛的应用。 但是,它仅仅取决于两个极端值的水平,不能反映其间的变量分布情况,同时易受极端值的影响。

1.10 四分位差 Quartile Deviation

四分位差(quartile deviation),它是上四分位数(Q3,即位于75%)与下四分位数(Q1,即位于25%)的差。
计算公式为:Q =Q3-Q1
四分位差反映了中间50%数据的离散程度,其数值越小,说明中间的数据越集中;其数值越大,说明中间的数据越分散。四分位差不受极值的影响。此外,由于中位数处于数据的中间位置,因此,四分位差的大小在一定程度上也说明了中位数对一组数据的代表程度。四分位差主要用于测度顺序数据的离散程度。对于数值型数据也可以计算四分位差,但不适合分类数据。
四分位数是将一组数据由小到大(或由大到小)排序后,用3个点将全部数据分为4等份,与这3个点位置上相对应的数值称为四分位数,分别记为Q1(第一四分位数),说明数据中有25%的数据小于或等于Q1,Q2(第二四分位数,即中位数)说明数据中有50%的数据小于或等于Q2、Q3(第三四分位数)说明数据中有75%的数据小于或等于Q3。其中,Q3到Q1之间的距离的差的一半又称为分半四分位差,记为(Q3-Q1)/2。

1.11 百分位数

对于有序数据,考虑值集的百分位数(percentile) 更有意义。具体地说,给定-一个有序的或连续的属性x和0与100之间的数p,第p个百分位数x是一个x值,使得x的p%的观测值小于xp。例如,第50个百分位数是值x50%,使得x的所有值的50%小于x50%。如第五百分位,它表示在所有测量数据中,测量值的累计频次达5%。以身高为例,身高分布的第五百分位表示有5%的人的身高小于此测量值,95%的身高大于此测量值。

1.12 截断均值(Trimmed Mean)

为了克服传统均值定义的问题,有时使用截断均值(trimmed mean)概念。指定0和100之间的百分位数p,丢弃高端和低端(p/2)%的数据,然后用常规的方法计算均值,所得的结果即是截断均值。中位数是p = 100%时的截断均值,而标准均值是对应于p = 0%的截断均值。

1.13 方差(Variance)

方差是在概率论和统计方差衡量随机变量或一组数据时离散程度的度量。概率论中方差用来度量随机变量和其数学期望(即均值)之间的偏离程度。统计中的方差(样本方差)是每个样本值与全体样本值的平均数之差的平方值的平均数。在许多实际问题中,研究方差即偏离程度有着重要意义。方差是衡量源数据和期望值相差的度量值。

1.14 平均绝对误差(Absolute Average Deviation)

平均绝对误差是所有单个观测值与算术平均值的偏差的绝对值的平均。平均绝对误差可以避免误差相互抵消的问题,因而可以准确反映实际预测误差的大小。

1.15 中值绝对偏差(中位数绝对偏差),Median Absolute Deviation

一组来自人口的数字数据样本用于了解给定情况下的典型情况。统计数据是用来描述数据的数值。一些统计数据是中心的度量,如均值、中值和众数。其他统计数据是变异性的度量,如标准偏差、平均绝对偏差和中值绝对偏差。中位数是指当数据按从最小到最大的顺序排列时,位于数据列表中间的数据值。
数据集:5、8、9、2、0、5、7、9、3
有序集合:0、2、3、5、5、7、8、9、9
中位数是5,因为它是数据集中间的数字。
如果有偶数个数据值,那么中间将有两个数字,必须求平均值才能找到中间值。如果集合中存在极值,中值有时比平均值更能代表数据。例如,由于收入过高或过低,通常报告的是收入中值而不是平均收入,这会扭曲数据。

1.16 标准差(Standard Deviation) 

标准差(Standard Deviation) ,数学术语,是离均差平方的算术平均数(即:方差)的算术平方根,用σ表示。标准差也被称为标准偏差,或者实验标准差,在概率统计中最常使用作为统计分布程度上的测量依据。
标准差是方差的算术平方根。标准差能反映一个数据集的离散程度。平均数相同的两组数据,标准差未必相同。

标准差(Standard Deviation),在概率统计中最常使用作为统计分布程度(statistical dispersion)上的测量。标准差定义是总体各单位标准值与其平均数离差平方的算术平均数的平方根。它反映组内个体间的离散程度。测量到分布程度的结果,原则上具有两种性质:
为非负数值,与测量资料具有相同单位。一个总量的标准差或一个随机变量的标准差,及一个子集合样品数的标准差之间,有所差别。
简单来说,标准差是一组数据平均值分散程度的一种度量。一个较大的标准差,代表大部分数值和其平均值之间差异较大;一个较小的标准差,代表这些数值较接近平均值。

所有数减去其平均值的平方和,所得结果除以该组数之个数(或个数减一,即变异数),再把所得值开根号,所得之数就是这组数据的标准差。

1.17 数据规范化(归一化)

数据规范化(归一化)处理是数据挖掘的一项基础工作。不同评价指标往往具有不同的量纲,数值见的差别可能很大,不进行处理可能会影响到数据分析的结果。为了消除指标之间的量纲和取值范围差异的影响,需要进行标准化处理,将数据按照比例进行缩放,使之落入一个特定的区域,便于进行综合分析。如将工资收入属性值映射到[-1, 1]或者[0, 1]内。
数据规范化对于基于距离的挖掘算法尤为重要。

1.18 最小-最大规范化

最小-最大规范化也称为离散标准化,是对原始数据的线性变换,将数据值映射到[0, 1]之间。


1.19 零-均值规范化(z-score标准化)

零-均值规范化也称标准差标准化,经过处理的数据的均值为0,标准差为1。

1.20 小数定标规范化(Decimals_Normalize)

通过移动属性值的小数位数,将属性值映射到[-1, 1]之间,移动的小数位数取决于属性值绝对值的最大值。

2、统计学常用算法的C#源代码

using System;
using System.Collections;
using System.Collections.Generic;

namespace Legalsoft.Truffer.Statistics
{
	public static class StatisticsUtility
	{
		/// <summary>
		/// 最小值
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Min(double[] data)
		{
#if __ORIGINAL__
			double min = double.MaxValue;
            foreach (double x in data)
            {
                if (x < min) min = x;
            }
            return min;
#else
			Array.Sort(data);
			return data[0];
#endif
		}

		/// <summary>
		/// 最大值
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Max(double[] data)
		{
#if __ORIGINAL__
            double max = double.MinValue;
            foreach (double x in data)
            {
                if (x > max) max = x;
            }
            return max;
#else
			Array.Sort(data);
			return data[data.Length - 1];
#endif
		}

		/// <summary>
		/// 和值
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Sum(double[] data)
		{
			double sum = 0.0;
			foreach (double x in data)
			{
				sum += x;
			}
			return sum;
		}

		/// <summary>
		/// 均值
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Mean(double[] data)
		{
			return (data.Length == 0) ? 0.0 : (Sum(data) / (double)data.Length);
		}

		/// <summary>
		/// 众数
		/// 对于给定的含有N个元素的多重集合,每个元素在S中出现次数最多的成为该元素的重数,
		/// 多重集合S重的重数最大的元素成为【众数】。
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Mode(double[] data)
		{
#if __ORIGINAL__
			IDictionary<double, int> map = new Dictionary<double, int>();
			for (int i = 0; i < data.Length; i++)
			{
				if (map.ContainsKey(data[i]))
				{
					map[data[i]] = map[data[i]] + 1;
				}
				else
				{
					map[data[i]] = 1;
				}
			}
			int maxCount = 0;
			double mode = -1.0;
			IEnumerator<double> iter = map.Keys.GetEnumerator();
			while (iter.MoveNext())
			{
				double num = iter.Current;
				int count = map[num];
				if (count > maxCount)
				{
					maxCount = count;
					mode = num;
				}
			}
			return mode;
#else
			int count = -1;
			double mode = data[0];
			Hashtable hash = new Hashtable();
			foreach (double x in data)
			{
				if (!hash.ContainsKey(x))
				{
					hash.Add(x, 1);
					if (count < 1)
					{
						count = 1;
						mode = x;
					}
				}
				else
				{
					int n = (int)hash[x] + 1;
					hash[x] = n;
					if (n > count)
					{
						count = n;
						mode = x;
					}
				}
			}
			return mode;
#endif
		}

		/// <summary>
		/// 中位数
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Median(double[] data)
		{
			// 创建缓存,避免修改data的数据
			//double[] dump = Arrays.CopyOf(data, data.Length);
			double[] dump = new double[data.Length];
			Array.Copy(data, dump, data.Length);

			Array.Sort(dump);

			if ((dump.Length % 2) == 0)
			{
				return (dump[dump.Length >> 1] + dump[(dump.Length >> 1) - 1]) / 2;
			}
			else
			{
				return dump[(dump.Length >> 1)];
			}
		}

		/// <summary>
		/// 中列数
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Middle_Range(double[] data)
		{
#if __ORIGINAL__
			double max = data[0];
			double min = data[0];
			for (int i = 0; i < data.Length; i++)
			{
				if (data[i] > max)
				{
					max = data[i];
				}
				if (data[i] < min)
				{
					min = data[i];
				}
			}
			return (min + max) / 2.0;
#else
			return (Max(data) + Min(data)) * 0.5;
#endif
		}

		/// <summary>
		/// 四分位数
		/// </summary>
		/// <param name="data"> </param>
		/// <returns>存放三个四分位数的数组</returns>
		public static double[] Quartiles(double[] data)
		{
			// 创建缓存,避免修改data的数据
			//double[] dump = Arrays.CopyOf(data, data.Length);
			double[] dump = new double[data.Length];
			Array.Copy(data, dump, data.Length);

			Array.Sort(dump);

			double[] quartiles = new double[3];
			// 第二四分位数(中位数)
			quartiles[1] = Median(dump);
			// 求另外两个四分位数
			if ((dump.Length % 2) == 0)
			{
				//quartiles[0] = getMedian(Arrays.CopyOfRange(dump, 0, dump.Length / 2));
				double[] t0 = new double[dump.Length / 2];
				Array.ConstrainedCopy(dump, 0, t0, 0, dump.Length / 2);
				quartiles[0] = Median(t0);

				//quartiles[2] = getMedian(Array.Copy(dump, dump.Length / 2, dump.Length));
				double[] t2 = new double[dump.Length / 2];
				Array.ConstrainedCopy(dump, dump.Length / 2, t2, 0, dump.Length / 2);
				quartiles[2] = Median(t2);
			}
			else
			{
				//quartiles[0] = getMedian(Arrays.CopyOfRange(dump, 0, dump.Length / 2));
				double[] t0 = new double[dump.Length / 2];
				Array.ConstrainedCopy(dump, 0, t0, 0, dump.Length / 2);
				quartiles[0] = Median(t0);

				//quartiles[2] = getMedian(Arrays.CopyOfRange(dump, dump.Length / 2 + 1, dump.Length));
				double[] t2 = new double[dump.Length / 2 - 1];
				Array.ConstrainedCopy(dump, dump.Length / 2 + 1, t2, 0, dump.Length / 2 - 1);
				quartiles[2] = Median(t2);
			}
			return quartiles;
		}

		/// <summary>
		/// 极差
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Range(double[] data)
		{
#if __ORIGINAL__
			double max = data[0];
			double min = data[0];
			for (int i = 0; i < data.Length; i++)
			{
				if (data[i] > max)
				{
					max = data[i];
				}
				if (data[i] < min)
				{
					min = data[i];
				}
			}
			return (max - min);
#else
			return (Max(data) - Min(data));
#endif
		}

		/// <summary>
		/// 四分位数差
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Quartiles_Range(double[] data)
		{
			return Range(Quartiles(data));
		}

		/// <summary>
		/// 截断均值
		/// </summary>
		/// <param name="data"> 求值数组 </param>
		/// <param name="p">截断量p,例如p的值为20,则截断20%(高10%,低10%)</param>
		/// <returns></returns>
		public static double Trimmed_Mean(double[] data, int p)
		{
			int tmp = (data.Length * p) / 100;
			//double[] dump = Arrays.CopyOfRange(data, tmp, data.Length + 1 - tmp);
			double[] dump = new double[data.Length + 1 - tmp];
			Array.ConstrainedCopy(dump, tmp, dump, 0, dump.Length);

			return Mean(dump);
		}

		/// <summary>
		/// 方差
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Variance(double[] data)
		{
			if (data.Length == 0) return 0.0;
			//double variance = 0.0;
			double sum1 = 0.0;
			double sum2 = 0.0;
			for (int i = 0; i < data.Length; i++)
			{
				sum1 += data[i];
				sum2 += data[i] * data[i];
			}
			sum1 /= (double)data.Length;
			sum2 /= (double)data.Length;
			return sum2 - (sum1 * sum1);
			//double variance = sum2 / data.Length - (sum / data.Length) * (sum / data.Length);
			//return variance;
		}

        /// <summary>
        /// 绝对平均偏差(AAD)
        /// 平均绝对误差
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static double Absolute_Average_Deviation(double[] data)
		{
			double sum = 0.0;
			double mean = Mean(data);
			for (int i = 0; i < data.Length; i++)
			{
				sum += Math.Abs(data[i] - mean);
			}
			return (data.Length == 0) ? 0.0 : (sum / (double)data.Length);
		}

		/// <summary>
		/// 中位数绝对偏差(MAD)
		/// </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Median_Absolute_Deviation(double[] data)
		{
			// 创建缓存,避免修改data的数据
			double[] dump = new double[data.Length];
			double median = Median(data);
			for (int i = 0; i < data.Length; i++)
			{
				dump[i] = Math.Abs(data[i] - median);
			}
			return Median(dump);
		}

		/// <summary>
		/// 标准差 </summary>
		/// <param name="data"></param>
		/// <returns></returns>
		public static double Standard_Devition(double[] data)
		{
			double sum = 0.0;
			double mean = Mean(data);
			for (int i = 0; i < data.Length; i++)
			{
				if (Math.Abs(data[i] - mean) > float.Epsilon)
				{
					sum += Math.Sqrt((data[i] - mean) * (data[i] - mean));
				}
			}
			return (data.Length == 0) ? 0.0 : (sum / (double)(data.Length - 1));
		}

		/// <summary>
		/// 最小-最大规范化
		/// </summary>
		/// <param name="data"> </param>
		/// <returns>规范化后的数组</returns>
		public static double[] Minmax_Normalize(double[] data)
		{
			// 拷贝数组
			//double[] dump = Arrays.CopyOf(data, data.Length);
			double[] dump = new double[data.Length];
			Array.Copy(data, dump, data.Length);

#if __ORIGINAL__
			// 找到最大值和最小值
			double max = dump[0];
			double min = dump[0];
			for (int i = 0; i < dump.Length; i++)
			{
				if (dump[i] > max)
				{
					max = dump[i];
				}
				if (dump[i] < min)
				{
					min = dump[i];
				}
			}
#else
			double max = Max(dump);
			double min = Min(dump);
#endif

			if (Math.Abs(max - min) < float.Epsilon)
			{
				return data;
			}

			// 规范化
			for (int i = 0; i < dump.Length; i++)
			{
				dump[i] = (dump[i] - min) / (max - min);
			}
			return dump;
		}


		/// <summary>
		/// Z-score规范化 
		/// </summary>
		/// <param name="data"> </param>
		/// <returns>规范化后的数组</returns>
		public static double[] ZScore_Normalize(double[] data)
		{
			if (data.Length <= 1) return data;

			// 创建缓存,避免修改data的数据
			//double[] dump = Arrays.CopyOf(data, data.Length);
			double[] dump = new double[data.Length];
			Array.Copy(data, dump, data.Length);

			// 求均值
			double mean = Mean(data);
#if __ORIGINAL__
			double sum = 0.0;
			for (int i = 0; i < dump.Length; i++)
			{
				if (Math.Abs(dump[i] - mean) > float.Epsilon)
				{
					sum += Math.Sqrt((dump[i] - mean) * (dump[i] - mean));
				}
			}
			double sd = sum / (dump.Length - 1);
#else
			double sd = Standard_Devition(dump);
#endif
			// 标准化
			for (int i = 0; i < dump.Length; i++)
			{
				dump[i] = (dump[i] - mean) / sd;
			}
			return dump;
		}

		/// <summary>
		/// 小数定标规范化
		/// </summary>
		/// <param name="data"> </param>
		/// <returns>规范化后的数组</returns>
		public static double[] Decimals_Normalize(double[] data)
		{
			// 创建缓存,避免修改data的数据
			//double[] dump = Arrays.CopyOf(data, data.Length);
			double[] dump = new double[data.Length];
			Array.Copy(data, dump, data.Length);

			// 找到最大值
			double max = Max(dump);

			// 确定j的值(j为使max(|v'|)<1的最小整数)
			int j = 0;
			while (Math.Abs(max / Math.Pow(10, j)) >= 1)
			{
				j++;
			}

			// 规范化
			for (int i = 0; i < dump.Length; i++)
			{
				dump[i] = dump[i] / Math.Pow(10, j);
			}
			return dump;
		}
	}
}