克里金插值法的java实现

项目中要用到克里金插值法,大致了解了一下,今天做个笔记总结一下(有错误请评论指正)

关于克里金插值法,在我看来就是加强版的反距离加权,只不过他的权重系数的确定,复杂一点,是带着你自己的空间模型的分布特性,比如说你要用在气象领域,则权重系数和地质的就是完全不相同的。

我对于克里金方法的理解,认为他的算法可以分成五步(前提是你的模型已确定),第一步是求出每两个已知点之间的距离A,然后带入模型算法中求出对应值,组成已知点的距离矩阵,第二步是求出预测点与已知点的距离,代入模型算法,得到矩阵B,第三步就是通过A的逆阵*B,求出权重系数矩阵,第四步就是通过模型计算每个点的z,第五步就是通过z值和权重系数进行相乘相加,得到最终的预测值。

模型算法的求解,就是根据拟合的量度,克里金算法总共有六个,还是七个模型,可以以距离的半方差当作x,相应的value对应为y,然后查看与模型的拟合程度,确定模型的参数。

我这里是PM2.5的分布预测,在网上找了个关于gri3d的包,但是封装较为严重,想看明白里面的参数配置有点难,就自己写了一个,但是没有考虑一些克里金方面的极值情况,比如h=0等,只是实现了大致的逻辑部分,模型我选择的是幂指数模型,在我看来,选模型较为困难,我自己用python画图求拟合,表示根本拟合不了(可能是样本的原因吧),方程中的参数是算法同事给我的。废话不多说,程序如下:

public double[][] gridData(double[] x, double[] y, double[] z,
                           double[][] X, double[][] Y) {
    int xLength = x.length;
    int xSize = X.length;
    int ySize = X[0].length;
    double[][] data = new double[xLength + 1][xLength + 1];
    double[][] data2 = new double[xSize][ySize];

    for (int i = 0; i <= xLength; i++) {
        for (int j = 0; j <= xLength; j++) {
            if ((i < xLength) && (j < xLength)) {

                if (i != j) {
                    data[i][j] = Math.sqrt((x[i] - x[j]) * (x[i] - x[j])
                            + (y[i] - y[j]) * (y[i] - y[j]));  //算出每两点之间的距离
                } else {
                    data[i][j] = 0.0001;
                }
            } else if ((i == xLength) || (j == xLength)) {
                data[i][j] = 1;
            }
            if (i == xLength && j == xLength) {
                data[i][j] = 0;
            }
        }
    }
    //矩阵的初始化
    Matrix m = new Matrix(data);
    //矩阵求逆  逆矩阵矩阵a*a-1 = 单位矩阵
    Matrix mMatrix = m.inverse();

    for (int i = 0; i < xSize; i++) {
        for (int j = 0; j < ySize; j++) {
            double[] rData = new double[xLength + 1];

            for (int k = 0; k <= xLength; k++) {
                if (k < xLength) {
                    rData[k] = Math.sqrt((X[i][j] - x[k])
                            * (X[i][j] - x[k]) + (Y[i][j] - y[k])
                            * (Y[i][j] - y[k])); //算出预测值的距离
                } else {
                    rData[k] = 1;  //预测值的距离
                }
            }

            double[] rDa = new double[xLength + 1];
            for (int k = 0; k < (xLength + 1); k++) {
                for (int k2 = 0; k2 < (xLength + 1); k2++) {
                    rDa[k] += mMatrix.get(k, k2) * rData[k2];  //返回逆矩阵*rData距离 求权重
                }
            }
            for (int k = 0; k < xLength; k++) {
                data2[i][j] += z[k] * rDa[k];
            }
        }
    }

    return data2;
}

// 变差函数,克里金算法特有的函数
public double function(double h) {
    double r = 0;
    if (h == 0) {
        r = 0;
    } else if (h > 0 && h <= 11) { //此处是他的基程
        Kriging kriging = new Kriging();
        //kriging.variogramModel()
        //r = 0.0 + 1.154 * ((3 * h * h * h * h) / (4 * 8.535 * 8.535 * 8.535 * 8.535));
        r = 0.0 + 0.06 * (1.0D - Math.exp(- h / 11));

    } else {
        r = 3.202;
    }
    return r;

}

幂指数模型也有是-3h/a,感觉就是凭个人意愿吧,无非是a*3就行了。

有问题欢迎评论区指正,谢谢!!

上面的这个基本上就是完整的代码了,在测试时发现了一个问题,就是插值只能一个一个插,如果插太多值的话,插值结果会有问题。

附我的测试代码:

public static void main(String[] args) {
    double[] Lon = {116.376259, 116.376259, 116.376259, 116.376259, 116.376259, 116.376259, 116.376259, 116.375824, 116.375809, 116.375839, 116.375916, 116.375786, 116.375748, 116.375786, 116.375801, 116.375885, 116.37632, 116.37632, 116.37632, 116.374489, 116.374489, 116.375938, 116.376144, 116.375999, 116.376152
            };
    double[] Lat = {39.971764, 39.971764, 39.971764, 39.971764, 39.971764, 39.971764, 39.971764, 39.970943, 39.971077, 39.971268, 39.970734, 39.970695, 39.970432, 39.970318, 39.970619, 39.971119, 39.971336, 39.971336, 39.971336, 39.968445, 39.968445, 39.971176, 39.971252, 39.971279, 39.971516
    };
    double[] PM = {57.576897869, 57.576897869, 57.576897869, 57.576897869, 57.576897869, 57.576897869, 57.576897869, 60.347131145, 61.732247783, 63.80992274, 61.039689464, 63.117364421, 58.962014507, 62.424806102, 61.732247783, 61.039689464, 62.424806102, 60.347131145, 57.576897869, 60.347131145, 60.347131145, 63.117364421, 58.269456188, 61.039689464, 56.191781231//, 54.806664593

    };
    double[] lon = {116.376183};
    double[] lat =  {39.97163};
    double[] m = gridData(Lon, Lat, PM, lon, lat);
    System.out.println(Arrays.toString(m));
}

54是真实值,57是插的值,感觉还行吧,因为设备的值应该是发生了突变

 

 

评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值