TIFF 图像变化趋势分析

一、Theil-Sen Median 变化趋势分析

Theil-Sen Median 方法能反映变化趋势,公式如下:

β v = M e d i a n ( v j ‾ − v i ‾ j − i ) \beta_{v}=Median\left(\frac{\overline{v_j}-\overline{v_i}}{j-i}\right) βv=Median(j−ivj−vi)

其中 v v v 表示变化的指标, v j ‾ \overline{v_j} vj 和 v i ‾ \overline{v_i} vi 表示第 j j j 年和第 i i i 年指标 f f f 的平均值。当 β v > 0 \beta_v>0 βv>0 时,表明指标变化呈改善趋势,反之降低。

二、Mann-Kendall 检验

Mann-Kendall 方法能检验变化趋势显著性,公式如下:

S = ∑ i = 1 n − 1 ∑ j = i + 1 n f ( v ‾ j − v ‾ i ) S=\sum_{i=1}^{n-1}{\sum_{j=i+1}^{n}{f\left(\overline v_j-\overline v_i\right)}} S=i=1∑n−1j=i+1∑nf(vj−vi)
f ( v ‾ j − v ‾ i ) = { 1 , v ‾ j − v ‾ i > 0 0 , v ‾ j − v ‾ i = 0 − 1 , v ‾ j − v ‾ i < 0 f\left(\overline v_j-\overline v_i\right)=\left\{\begin{matrix} 1, & \overline v_j-\overline v_i>0 \\ 0, & \overline v_j-\overline v_i=0 \\ -1, & \overline v_j-\overline v_i<0 \end{matrix}\right. f(vj−vi)=⎩ ⎨ ⎧1,0,−1,vj−vi>0vj−vi=0vj−vi<0

方差: V ( S ) = n ( n − 1 ) ( 2 n + 5 ) 18 V(S)=\frac{n(n-1)(2n+5)}{18} V(S)=18n(n−1)(2n+5) ,统计量:

Z = { S − 1 V ( S ) , S > 0 0 , S = 0 S + 1 V ( S ) , S < 0 Z=\left\{\begin{matrix} \frac{S-1}{\sqrt{V(S)}}, & S>0 \\ 0, & S=0 \\ \frac{S+1}{\sqrt{V(S)}}, & S<0 \end{matrix}\right. Z=⎩ ⎨ ⎧V(S) S−1,0,V(S) S+1,S>0S=0S<0

其中 n n n 表示指标变化的时间长度, f f f 为符号函数,统计量 Z Z Z 的取值范围为 ( − ∞ , + ∞ ) \left(-\infty,+\infty\right) (−∞,+∞) ,表示在给定显著水平 α \alpha α 下,当 ∣ Z ∣ > μ 1 − α 2 \left|Z\right|>\mu_{1-\frac{\alpha}{2}} ∣Z∣>μ1−2α 时,趋势显著。

三、Hurst 指数分析

给定指标 v v v 的时间序列 v t , t = 1 , 2 , ⋯   , n v_t,t=1,2,\cdots,n vt,t=1,2,⋯,n ,对于任意正整数 u u u ,定义均值序列、累计差、极差和标准差如下:

v u ‾ = 1 u ∑ t = 1 u v t , u = 1 , 2 , ⋯   , n \overline{v_u}=\frac{1}{u}\sum_{t=1}^u{v_t},u=1,2,\cdots,n vu=u1t=1∑uvt,u=1,2,⋯,n
X ( t , u ) = ∑ t = 1 u v t − v u , 1 ≤ t ≤ u X(t,u)=\sum_{t=1}^u{v_t-v_u},1\le t\le u X(t,u)=t=1∑uvt−vu,1≤t≤u
R u = max ⁡ 1 ≤ t ≤ u X ( t , u ) , u = 1 , 2 , ⋯   , n R_u=\max_{1\le t\le u}X(t,u),u=1,2,\cdots,n Ru=1≤t≤umaxX(t,u),u=1,2,⋯,n
S u = 1 u ∑ t = 1 u ( v t − v u ‾ ) 2 S_u=\sqrt{\frac{1}{u}\sum_{t=1}^{u}\left(v_t-\overline{v_u}\right)^2} Su=u1t=1∑u(vt−vu)2

若存在 R S ∝ u H \frac{R}{S}\propto u^H SR∝uH ,表示该时间序列的指标存在 Hurst 现象,大小用 H H H 表示,若 H = 0.5 H=0.5 H=0.5 则该指标自相关性为 0 ,即时间序列前后变化是完全随机的;当 0.5 < H < 1 0.5<H<1 0.5<H<1 ,表示该指标自相关性大于 0 ,即时间序列前后变化一致,且越接近 1 持续性能力越强;当 0 < H < 0.5 0<H<0.5 0<H<0.5 ,表示该指标时间序列自相关性小于 0 ,越接近 0 反持续性能力越强。

四、代码

python 复制代码
import os

import numpy as np
from osgeo import gdal


def read_img(filename):
    # 打开文件
    dataset = gdal.Open(filename)

    # 获取栅格数据的元信息
    im_width = dataset.RasterXSize  # 栅格矩阵的列数
    im_height = dataset.RasterYSize  # 栅格矩阵的行数
    im_bands = dataset.RasterCount  # 波段数
    im_geotrans = dataset.GetGeoTransform()  # 仿射矩阵,左上角像素的大地坐标和像素分辨率
    im_proj = dataset.GetProjection()  # 地图投影信息,字符串表示

    # 读取栅格数据到数组
    im_data = dataset.ReadAsArray(0, 0, im_width, im_height)

    # 返回元信息、投影、仿射矩阵和数据数组
    return im_proj, im_geotrans, im_data


def write_img(filename, im_proj, im_geotrans, im_data, driverName="GTiff"):
    # 判断栅格数据的数据类型
    if "int8" in im_data.dtype.name:
        datatype = gdal.GDT_Byte
    elif "int16" in im_data.dtype.name:
        datatype = gdal.GDT_UInt16
    else:
        datatype = gdal.GDT_Float32

    # 判读数组维数
    if len(im_data.shape) == 3:
        im_bands, im_height, im_width = im_data.shape
    else:
        im_bands, (im_height, im_width) = 1, im_data.shape

    # 创建文件
    driver = gdal.GetDriverByName(driverName)
    dataset = driver.Create(filename, im_width, im_height, im_bands, datatype)

    # 写入仿射变换参数
    dataset.SetGeoTransform(im_geotrans)

    # 写入投影
    dataset.SetProjection(im_proj)

    # 写入数组数据
    if im_bands == 1:
        dataset.GetRasterBand(1).WriteArray(im_data)
    else:
        for i in range(im_bands):
            dataset.GetRasterBand(i + 1).WriteArray(im_data[i])


def theil_sen_slope(data):
    n_timepoints, height, width = data.shape
    i, j = np.triu_indices(n_timepoints, k=1)  # 获取数据索引的上三角部分
    index_diff = i - j
    index_diff = index_diff[:, np.newaxis]
    # 将数据的时间轴展平,便于进行矩阵操作
    flat_data = data.reshape(n_timepoints, -1)
    # 计算斜率
    slope_values = (flat_data[j] - flat_data[i]) / index_diff
    # 根据每列计算中位数
    slopes = np.median(slope_values, axis=0)
    # 将结果重新形状成与原始数据的高度和宽度相匹配的形状
    slopes = slopes.reshape(height, width)
    return slopes


def mann_kendall_test(data):
    height, width = data.shape[1], data.shape[2]
    Z_values = np.zeros((height, width))
    for h in range(height):
        for w in range(width):
            data_values = data[:, h, w]
            n = len(data_values)
            i, j = np.triu_indices(n, k=1)  # 获取数据索引的上三角部分
            slope_values = data_values[j] - data_values[i]
            # 计算 sgn(slope_j - slope_i)
            sgn_values = np.sign(slope_values)
            # 计算 S
            S = np.sum(sgn_values)
            # 计算 var(S)
            var_S = (n * (n - 1) * (2 * n + 5)) / 18
            # 计算 Z
            if S > 0:
                Z = (S - 1) / np.sqrt(var_S)
            elif S < 0:
                Z = (S + 1) / np.sqrt(var_S)
            else:
                Z = 0
            Z_values[h, w] = Z
    return Z_values


def Hurst(data):
    from sklearn.linear_model import LinearRegression
    n_timepoints, height, width = data.shape
    cumsum_data = np.cumsum(data, axis=0)
    divisor = np.arange(1, n_timepoints + 1)[:, np.newaxis, np.newaxis]
    means = cumsum_data / divisor
    i, j = np.triu_indices(n_timepoints, k=0)
    point = list(zip(i, j))
    X_t_tau = [[] for _ in range(n_timepoints)]
    for x, y in point:
        X_t_tau[y].append(cumsum_data[x] - means[y] * (x + 1))
    R_tau = []
    for row in X_t_tau:
        tmp = np.zeros((height, width))
        row = np.array(row)
        for h in range(height):
            for w in range(width):
                tmp[h, w] = np.max(row[:, h, w]) - np.min(row[:, h, w])
        R_tau.append(tmp)
    R_tau = np.stack(R_tau)
    cumsum_data = np.cumsum((data - means)**2, axis=0)
    S_tau = (cumsum_data / divisor)**0.5
    log_tau = np.log(np.arange(1, n_timepoints + 1))
    log_R_S_ratio = np.log(R_tau / S_tau)
    model = LinearRegression()
    model.fit(log_tau.reshape(-1, 1), log_R_S_ratio.reshape(n_timepoints, -1))
    fit_result = model.coef_.reshape(height, width)
    # for h in range(height):
    #     for w in range(width):
    #         X_current_position = mean_log_R_S_ratio[:, h, w]
    #         model.fit(X_current_position, log_tau)
    #         fit_result[h, w] = model.coef_[0]
    return fit_result


def run():
    data_path = "E:/by/dataset/生态系统质量/"
    out_path = "E:/by/output/生态系统质量/"
    if not os.path.exists(out_path):
        os.makedirs(out_path)
    files = [file for file in os.listdir(data_path) if file.endswith(".tif")]
    data_list = []
    for file in files:
        proj, geotrans, band = read_img(data_path + file)
        data = np.ma.masked_where(
            np.logical_or(band == np.finfo(np.float32).min, np.isnan(band)), band
        )
        data_list.append(data)
    # 将列表转换为数组
    data = np.stack(data_list, axis=0)
    # theil_sen_slope
    # tss = theil_sen_slope(data)
    # write_img(out_path + "tss.tif", proj, geotrans, tss)
    # print('theil_sen_slope finished!')

    # mann_kendall_test
    # mk = mann_kendall_test(data)
    # write_img(out_path + "mk.tif", proj, geotrans, mk)
    # print('mann_kendall_test finished!')

    # Hurst
    hurst = Hurst(data)
    write_img(out_path + "hurst.tif", proj, geotrans, hurst)
    print('hurst finished!')


if __name__ == "__main__":
    run()
相关推荐
西岸行者11 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意11 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码11 天前
嵌入式学习路线
学习
毛小茛11 天前
计算机系统概论——校验码
学习
babe小鑫11 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms11 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下11 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。11 天前
2026.2.25监控学习
学习
im_AMBER11 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J11 天前
从“Hello World“ 开始 C++
c语言·c++·学习