度量距离的基本理论和计算方法见链接
欧式距离
//n维欧式距离
double EuclideanDistance(vector<double> &data1,vector<double> &data2){
double sum;
for (int i=0;i<data1.size();i++){
sum += pow((data1[i]-data2[i]),2);
}
return sqrt(sum);
}
规范化欧式距离
即在计算欧式距离之前先将数据规范化处理,普遍使用的方法有三种,最大最小规范化、零均值规范化和L2范数规范化。
//标准化欧式距离——数据标准化
void NormalizationMethod(vector<vector<double>> &dataset,vector<vector<double>> &Normalization_dataset,int normalizationFlag){
//标准化数据范围
vector<double> maxfeature; //最大值
vector<double> minfeature; //最小值
vector<double> meanfeature; //均值
vector<double> stdevfeature; //标准差
vector<double> l2feature; //L2范数
for (int j=0;j<dataset[0].size();j++){
double middle_max=0;
double middle_min=0x7FFFFFFF;
double sum=0;
for (int i=0;i<dataset.size();i++){
if (dataset[i][j] >= middle_max){
middle_max = dataset[i][j];
}
if (dataset[i][j] <= middle_min){
middle_min = dataset[i][j];
}
sum+=dataset[i][j];
}
meanfeature.push_back(sum / dataset[0].size());//计算均值
maxfeature.push_back(middle_max);
minfeature.push_back(middle_min);
}
//计算标准差和L2范数
for (int j=0;j<dataset[0].size();j++){
double stdev=0;
double l2=0;
for (int i=0;i<dataset.size();i++){
stdev+=(dataset[i][j]-meanfeature[j])*(dataset[i][j]-meanfeature[j]); //计算方差
l2+=dataset[i][j]*dataset[i][j];
}
stdev = stdev/(dataset[0].size()-1);
stdev = sqrt(stdev);//计算标准差
l2=sqrt(l2);//计算L2范数
l2feature.push_back(l2);
stdevfeature.push_back(stdev);
}
//数据转换到01区间
for (int i =0;i<dataset.size();i++){
vector<double> middleset;
for (int j =0;j<dataset[0].size();j++){
double middle;
if (normalizationFlag == MIN_MAX){
middle=(dataset[i][j]-minfeature[j])/(maxfeature[j]-minfeature[j]);
}
else if(normalizationFlag == Z_SCORE){
middle = (dataset[i][j]-meanfeature[j]) / stdevfeature[j];
}else if(normalizationFlag == L2Normalization){
middle=dataset[i][j] / l2feature[j];
}
middleset.push_back(middle);
}
Normalization_dataset.push_back(middleset);
middleset.clear();
}
}
马氏距离
即在规范化欧式距离之前,先将旋转数据的坐标使其在各个维度上线性无关(即令不同维度间变量的协方差为0,转换协方差矩阵为对角阵)。为便捷计算用了Eigen
库来实现。
马氏距离的缺点:
- 计算复杂
- 限制较大,必须总体样本数大于样本维数,不然无法求得协方差矩阵的逆矩阵(矩阵的秩<n,非满秩矩阵),小样本学习/迁移学习中可能难以使用。
- 扩大了变化微小变量的作用
int main(){
MatrixXd dataset(4,2);
dataset<<7,4,3,2,1,2,3,5;
//转置transpose()
MatrixXd datasetT=dataset.transpose();
//确定协方差矩阵的大小
MatrixXd CovarianceMatrix(datasetT.rows(),datasetT.rows());
//计算协方差矩阵值
for (int i=0;i<CovarianceMatrix.rows();i++){
for (int j=0;j<CovarianceMatrix.cols();j++){
double cov = 0;
for(int k=0;k<datasetT.cols();k++){
cov += (datasetT(i,k)-datasetT.row(i).mean())*(datasetT(j,k)-datasetT.row(j).mean());
}
CovarianceMatrix(i,j)=cov/(datasetT.cols()-1);
}
}
//协方差矩阵的逆矩阵 CovarianceMatrix.inverse()
//计算两点(i,j)间的马氏距离
//transpose()转置
int i = 0;
int j = 1;
VectorXd middle = dataset.row(i)-dataset.row(j);
double temp = middle.transpose()*CovarianceMatrix.inverse()*middle;
std::cout<<sqrt(temp)<<std::endl;
return 0;
}