K-S检验法(Kolmogorov-Smirnov test,柯尔莫哥罗夫-斯米尔诺夫检验)是一种非参数检验方法,用于检验一个样本是否来自特定的概率分布(one-sample K-S test),或者检验两个样本是否来自同一概率分布(two-sample K-S test)。
此外,还有一种校验法,夏皮洛-威尔克检验(Shapiro—Wilk test),简称S-W检验。
国标 GB/T 4882-2001《数据的统计处理和解释正态性检验》:SW检验适用于样本数8≤n≤50,小样本(n<8)对偏离正态分布的检验不太有效。
因此:
分析小于50行的小样本数据时,我们倾向于看 S-W 检验得到的正态性检验结果;
分析大于50行的大样本数据时,我们倾向于看 K-S 检验得到的正态性检验结果;
只就检验一个样本是否来自正态分布的判别进行介绍,这也是工程中比较常用的。
一组数据 x i {x_i} xi,个数有 n n n个。此时的判断临界值为 α \alpha α。
注:单样本K-S检验统计量的临界值参照表
北亚利桑那大学网站——参照表
下面是使用 C++ 实现 K-S 检验法检验一组数据的分布类型的步骤:
//均值
double average(double X[2000], int num)
{
double ave = 0;
double sum = 0;
for (int i = 0; i < num; i++) sum = sum + X[i];
ave = sum / num;
return ave;
}
//标准差
double calbzc(double X[2000] , int num, int free)
{
double sumvi2 = 0;
double bzc; //标准差
for (int i = 0; i < num; i++)
{
sumvi2 = sumvi2 + (X[i] - average(X,num)) * (X[i] - average(X, num));
}
bzc = sqrt(sumvi2 / (num - free));
return bzc;
}
标准差求解需要考虑数据自由度 (即程序中参数 f r e e free free )。
double cdfA(double x, double ave, double bzc)
{
return 0.5 * (1 + erf((x - ave) / bzc / sqrt(2)));
}
其中,erf (x) 为误差函数(error function),可使用cmath库中的erf函数计算,表示标准正态分布中,小于 x 的分布占比,例如erf(1.5)=0.966105
参数data是待检验的数据数组,n是数据数组的长度,cdf是累积分布函数
double kstest(double data[], int n)
{
// 均值
double ave=0;
ave = average(data, n);
cout << "均值:" << ave << endl;
// 标准差
double bzc = 0;
int free=1;
bzc = calbzc(data, n, free);
cout <<"标准差:" << bzc << endl;
// 排序数据数组 升序
sort(data, data + n);
double D = 0;
for (int i = 0; i < n; i++)
{
// 计算经验分布函数的值
double F = (double)(i + 1) / n;
// 计算CDF函数的值
double S = cdfA(data[i],ave,bzc);
// 计算D值
D = max(D, fabs(S - F)); //fabs浮点数绝对值
}
// 计算临界值
double alpha = 1.36 / sqrt(n);
cout <<"D:"<< D << endl;
// 判断是否拒绝原假设
if (D > alpha)
return 0; // 拒绝原假设
else
return 1; // 未拒绝原假设
}
上述程序中,fabs函数表示求取浮点数的绝对值。
int main()
{
double data[2000];
string txtname;
cout << "文件路径:" << endl;
cin >> txtname;
// 计数
int count = 0;
ifstream inFile(txtname); // 打开文本文件
double zhongzhuan;
while (inFile >> zhongzhuan)
{ // 逐个读取文件中的数字
count++; // 每读取一个数字,计数器加1
}
inFile.close(); // 关闭文件
ifstream ifs;
ifs.open(txtname, ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return 0;
}
for (int i = 0; i < count; i++) //
{
ifs >> data[i];
//cout << data[i] << "\t";
}
cout << endl;
// 进行K-S检验
double p = kstest(data, count);
if (p == 0)
cout << "数据不服从正态分布。" << endl;
else
cout << "数据服从正态分布。" << endl;
return 0;
}
#include
#include
#include
#include
using namespace std;
//均值
double average(double X[2000], int num)
{
double ave = 0;
double sum = 0;
for (int i = 0; i < num; i++) sum = sum + X[i];
ave = sum / num;
return ave;
}
//标准差
double calbzc(double X[2000] , int num, int free)
{
double sumvi2 = 0;
double bzc; //标准差
for (int i = 0; i < num; i++)
{
sumvi2 = sumvi2 + (X[i] - average(X,num)) * (X[i] - average(X, num));
}
bzc = sqrt(sumvi2 / (num - free));
return bzc;
}
//cdf函数
double cdfA(double x, double ave, double bzc)
{
return 0.5 * (1 + erf((x - ave) / bzc / sqrt(2)));
}
//kstest函数
double kstest(double data[], int n)
{
// 均值
double ave=0;
ave = average(data, n);
cout << "均值:" << ave << endl;
// 标准差
double bzc = 0;
int free=1;
bzc = calbzc(data, n, free);
cout <<"标准差:" << bzc << endl;
// 排序数据数组 升序
sort(data, data + n);
double D = 0;
for (int i = 0; i < n; i++)
{
// 计算经验分布函数的值
double F = (double)(i + 1) / n;
// 计算CDF函数的值
double S = cdfA(data[i],ave,bzc);
// 计算D值
D = max(D, fabs(S - F)); //fabs浮点数绝对值
}
// 计算临界值
double alpha = 1.36 / sqrt(n);
cout <<"D:"<< D << endl;
// 判断是否拒绝原假设
if (D > alpha)
return 0; // 拒绝原假设
else
return 1; // 未拒绝原假设
}
int main()
{
double data[2000];
string txtname;
cout << "文件路径:" << endl;
cin >> txtname;
// 计数
int count = 0;
ifstream inFile(txtname); // 打开文本文件
double zhongzhuan;
while (inFile >> zhongzhuan)
{ // 逐个读取文件中的数字
count++; // 每读取一个数字,计数器加1
}
inFile.close(); // 关闭文件
ifstream ifs;
ifs.open(txtname, ios::in);
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return 0;
}
for (int i = 0; i < count; i++) //
{
ifs >> data[i];
//cout << data[i] << "\t";
}
cout << endl;
// 进行K-S检验
double p = kstest(data, count);
if (p == 0)
cout << "数据不服从正态分布。" << endl;
else
cout << "数据服从正态分布。" << endl;
return 0;
}
参考目录:
https://zhuanlan.zhihu.com/p/292678346