using MathNet.Numerics.Distributions;
namespace IStation.Test
{
public static class CorrelationHelper
{
///
/// 计算皮尔逊相关系数及显著性p值
///
public static (double r, double pValue) PearsonTest(double[] x, double[] y)
{
// 计算皮尔逊相关系数
double r = Pearson(x, y);
// 计算显著性p值(双尾t检验)
int n = x.Length;
double t = r * Math.Sqrt((n - 2) / (1 - r * r));
var tDist = new StudentT(location: 0, scale: 1, freedom: n - 2);
double pValue = 2 * (1 - tDist.CumulativeDistribution(Math.Abs(t)));
return (r, pValue);
}
///
/// 计算斯皮尔曼等级相关系数及显著性p值
///
public static (double rho, double pValue) SpearmanTest(double[] x, double[] y)
{
// 计算斯皮尔曼相关系数
double rho = Spearman(x, y);
// 大样本正态近似法计算p值
int n = x.Length;
double z = rho * Math.Sqrt(n - 1);
var normalDist = new Normal();
double pValue = 2 * (1 - normalDist.CumulativeDistribution(Math.Abs(z)));
return (rho, pValue);
}
///
/// 计算皮尔逊积差相关系数
///
/// 数据样本A
/// 数据样本B
/// 皮尔逊积差相关系数
public static double Pearson(IEnumerable dataA, IEnumerable dataB)
{
int n = 0;
double r = 0.0;
double meanA = 0;
double meanB = 0;
double varA = 0;
double varB = 0;
using (var ieA = dataA.GetEnumerator())
using (var ieB = dataB.GetEnumerator())
{
while (ieA.MoveNext())
{
if (!ieB.MoveNext())
{
throw new ArgumentOutOfRangeException("dataB", "数据长度不一致");
}
double currentA = ieA.Current;
double currentB = ieB.Current;
double deltaA = currentA - meanA;
double scaleDeltaA = deltaA / ++n;
double deltaB = currentB - meanB;
double scaleDeltaB = deltaB / n;
meanA += scaleDeltaA;
meanB += scaleDeltaB;
varA += scaleDeltaA * deltaA * (n - 1);
varB += scaleDeltaB * deltaB * (n - 1);
r += (deltaA * deltaB * (n - 1)) / n;
}
if (ieB.MoveNext())
{
throw new ArgumentOutOfRangeException("dataA", "数据长度不一致");
}
}
return r / Math.Sqrt(varA * varB);
}
///
/// 计算斯皮尔曼等级相关系数
///
public static double Spearman(IEnumerable dataA, IEnumerable dataB)
{
return Pearson(Rank(dataA), Rank(dataB));
}
private static IEnumerable Rank(IEnumerable sequence)
{
var sorted = new List(sequence);
sorted.Sort();
return (IEnumerable)sequence.Select(x => (double)sorted.IndexOf(x) + 1);
}
}
}