Implementierung des HANTS-Zeitreihenglättungsalgorithmus (Python)

Python implementiert die harmonische Analyse von Zeitreihen (HANTS)

1. Einleitung

Harmonische Analyse von NDVI-Zeitreihen (Harmonische Analyse von NDVI-Zeitreihen) (kurz Hants) glättet Zeitreihendaten. Bei dieser Methode handelt es sich um eine neue phänologische Analysemethode, mit der dynamische Veränderungen in der Vegetation quantitativ erfasst werden können.

Sein Kernalgorithmus ist die Fourier-Transformation und die Anpassung der kleinsten Quadrate, die die Zeitspektrumsdaten in viele Sinus- und Kosinuskurven mit unterschiedlichen Phasen, Frequenzen und Amplituden zerlegt und mehrere Kurven auswählt, die die Eigenschaften der Zeitreihe zur Überlagerung widerspiegeln können. Um das zu erreichen Zweck der Rekonstruktion von Zeitreihendaten.

Die NDVI-Methode zur harmonischen Zeitreihenanalyse stellt eine Verbesserung gegenüber der schnellen Fourier-Transformation dar. Sie kann nicht nur Wolkenkontaminationspunkte (Hochfrequenzrauschen) entfernen, sondern stellt auch weniger strenge Anforderungen an Zeitreihenbilder als die schnelle Fourier-Transformation (FFT). Es kann von Zeitrafferbildern abweichen.

Der spezifische Implementierungsprozess dieses Algorithmus ist wie folgt:
Zuerst wird eine Fourier-Transformation an der Zeitreihe jedes Pixelpunkts durchgeführt;

Wählen Sie dann mehrere Niederfrequenzkomponenten aus, um eine inverse Fourier-Transformation durchzuführen und eine neue Sequenz zu erhalten (der Beitragskoeffizient jeder Sinus- oder Cosinusfunktion im rekonstruierten Originaldatensatz wird durch Kurvenanpassung nach der Methode der kleinsten Quadrate ermittelt).

Berechnen Sie die Differenz zwischen der ursprünglichen Zeitreihe und der neuen Zeitreihe. Wenn die Differenz größer als der festgelegte Schwellenwert ist, wird der Punkt als kontaminiert betrachtet und aus der ursprünglichen Sequenz entfernt und mit dem entsprechenden Wert in der neuen Sequenz gefüllt;

Wiederholen Sie den obigen Vorgang für die geänderte Originalsequenz, bis Punkte ohne Wolkenkontamination gefunden werden oder die festgelegte Endbedingung der Iteration erreicht ist.
Das Ergebnis dieses Prozesses ist eine glatte Kurve, wie in der folgenden Abbildung dargestellt:

**Bild**

Wenn der Hants-Algorithmus NDVI-Zeitreihen verarbeitet, muss er Parameter wie die Anzahl der Frequenzen, den Fehlerschwellenwert, die maximale Anzahl gelöschter Punkte und den gültigen Datenbereich festlegen. Es gibt keinen objektiven Standard für die Einstellung dieser Parameter und kann nur basierend darauf bestimmt werden auf Erfahrung oder mehreren Experimenten.

Neben der Glättung von Daten und der Entfernung von Ausreißern kann Hants auch zum Füllen von Lücken oder zum Erhalten fehlender Daten sowie zur Durchführung einer Datenkomprimierung verwendet werden.

Modis Datenglättung und -füllung:

Bild

2 Python-Implementierung

Schritte:
Konstruieren Sie zunächst die Zeitreihenoberflächendaten entsprechend dem Zeitbereich. Die räumlichen Daten mit fehlender Zeit werden durch einen angegebenen Wert dargestellt, z. B. -9999,0

Extrahieren Sie dann Pixel für Pixel die Streupunktsequenz

Wenden Sie dann den Algorithmus Hants auf die Streusequenz an

Abschließend wird es Pixel für Pixel gespeichert, um eine raumzeitliche Sequenz zu bilden
. Hinweis: Die ausgefüllten -9999 werden im Algorithmus herausgefiltert und nehmen nicht an der Berechnung teil.

  1. Hants-Implementierung
# ref:https://github.com/gespinoza/hants/tree/master

import pandas as pd
import math
from copy import deepcopy
import numpy as np

def HANTS(ni, nb, nf, y, ts, HiLo, low, high, fet, dod, delta, fill_val):
    
    '''
    输入:
    ni:样本个数,散点时间序列长度
    nb:拟合样本的周期长度
    nf:非0频率的频率个数
    y:散点时间序列,真实值
    ts:拟合样本的数量(相对于虚拟周期长度)
    HiLo:{'Hi','Lo'},用于指示拒绝低的异常值还是高的异常值
    low:有效范围的最小值
    high:有效范围的最大值,范围外的值被拒绝
    fet:拟合曲线的误差容错,偏离大于fet的点将从曲线中删除
    dod:过度确定程度(迭代停止,如果迭代次数达到曲线拟合所需的最小值)
    delta:小正数(如0.1)抑制高振幅
    fill_val:填充值

    输出:
    yr:重构后的时间序列
    outlier:异常点
    '''


    """定义正余弦序列"""
    # [
    #  [1,     1,     ..., 1    ],
    #  [cosx1, cosx2, ..., cosxn],
    #  [sinx1, sinx2, ..., sinxn],
    #  [cosx1, cosx2, ..., cosxn],
    #  ...
    #  ]
    mat = pd.np.zeros((min(2*nf+1, ni), ni))  # [nr,ni]
    # amp = np.zeros((nf + 1, 1))

    # phi = np.zeros((nf+1, 1))
    yr = pd.np.zeros((ni, 1)) # 重构曲线
    outliers = pd.np.zeros((1, len(y))) # 异常值,[1,ni]

    """Filter"""
    sHiLo = 0
    if HiLo == 'Hi': # 
        sHiLo = -1
    elif HiLo == 'Lo':
        sHiLo = 1

    nr = min(2*nf+1, ni) # number of 2*+1 frequencies, or number of input images
    noutmax = ni - nr - dod # 异常点的最大个数
    # dg = 180.0/math.pi
    mat[0, :] = 1.0 # 用于求偏最小二乘中的b


    """定义一个周期内的正余弦曲线"""
    ang = 2*math.pi*pd.np.arange(nb)/nb 
    cs = pd.np.cos(ang)
    sn = pd.np.sin(ang)

    """将时序样本点转成cos、sin"""
    i = pd.np.arange(1, nf+1)
    for j in pd.np.arange(ni):
        index = pd.np.mod(i*ts[j], nb) # 取余
        mat[2 * i-1, j] = cs.take(index)
        mat[2 * i, j] = sn.take(index)


    """根据数值有效范围将范围外的值设为0"""
    p = pd.np.ones_like(y) # (ni,)
    bool_out = (y < low) | (y > high)
    p[bool_out] = 0
    outliers[bool_out.reshape(1, y.shape[0])] = 1
    nout = pd.np.sum(p == 0) # 统计异常值的数量


    """异常值个数判断"""
    if nout > noutmax:
        if pd.np.isclose(y, fill_val).any():
            # 当填充值是序列中的一员时,直接填充整个序列,并将整个序列设为异常值
            ready = pd.np.array([True])
            yr = y
            outliers = pd.np.zeros((y.shape[0]), dtype=int)
            outliers[:] = fill_val
        else:
            # 数据点太少
            raise Exception('Not enough data points.')
    else:
        ready = pd.np.zeros((y.shape[0]), dtype=bool) # 设置迭代的初值


    """PLS,迭代优化"""
    nloop = 0
    nloopmax = ni

    while ((not ready.all()) & (nloop < nloopmax)):

        nloop += 1
        za = pd.np.matmul(mat, p*y) # [nr,ni]*[ni,1],转换的傅里叶序列(去除异常点)

        A = pd.np.matmul(pd.np.matmul(mat, pd.np.diag(p)), # 将正常的列过滤出来
                         pd.np.transpose(mat)) # [nr, ni]*[ni,ni]*[ni,nr]
        
        #  add delta to suppress high amplitudes but not for [0,0]
        A = A + pd.np.identity(nr)*delta  # [nr,nr]+[nr,nr]
        A[0, 0] = A[0, 0] - delta

        zr = pd.np.linalg.solve(A, za) # 对傅里叶序列进行PLS [nr,nr],[nr,1]->[nr,1]

        # solve linear matrix equation and define reconstructed timeseries
        yr = pd.np.matmul(pd.np.transpose(mat), zr) # [ni,nr]*[nr,1] -> [ni,1]

        # calculate error and sort err by index
        diffVec = sHiLo*(yr-y)
        err = p*diffVec

        err_ls = list(err)
        err_sort = deepcopy(err)
        err_sort.sort()

        rankVec = [err_ls.index(f) for f in err_sort]

        # select maximum error and compute new ready status
        maxerr = diffVec[rankVec[-1]]
        ready = (maxerr <= fet) | (nout == noutmax)

        # if ready is still false
        if (not ready):
            i = ni - 1
            j = rankVec[i]
            
            # 迭代的本质就是p在不停的变,从而导致其他相应值的改变
            while ((p[j]*diffVec[j] > 0.5*maxerr) & (nout < noutmax)):
                p[j] = 0
                outliers[0, j] = 1
                nout += 1
                i -= 1
                if i == 0:
                    j = 0
                else:
                    j = 1

    return [yr, outliers]
  1. ein Anruf
from hants.wa_gdal import *  # from hants.wa_arcpy import *

# Data parameters
rasters_path = r'C:\example\data'
name_format = 'PROBAV_S1_TOC_{0}_100M_V001.tif'
start_date = '2015-08-01'
end_date = '2016-07-28'
latlim = [11.4505, 11.4753]
lonlim = [108.8605, 108.8902]
cellsize = 0.00099162627
nc_path = r'C:\example\ndvi_probav.nc'
rasters_path_out = r'C:\example\output_rasters'

# HANTS parameters
nb = 365
nf = 3
low = -1
high = 1
HiLo = 'Lo'
fet = 0.05
delta = 0.1
dod = 1

# Run
run_HANTS(rasters_path, name_format,
          start_date, end_date, latlim, lonlim, cellsize, nc_path,
          nb, nf, HiLo, low, high, fet, dod, delta,
          4326, -9999.0, rasters_path_out)

# Check fit
point = [108.87, 11.47]
ylim = [-1, 1]
plot_point(nc_path, point, ylim)
  1. Rufen Sie stapelweise an
from hants.wa_arcpy import *  # from hants.wa_gdal import *

# Create netcdf file
rasters_path = r'C:\example\data'
name_format = 'PROBAV_S1_TOC_{0}_100M_V001.tif'
start_date = '2015-08-01'
end_date = '2016-07-28'
latlim = [11.4505, 11.4753]
lonlim = [108.8605, 108.8902]
cellsize = 0.00099162627
nc_path = r'C:\example\ndvi_probav.nc'
create_netcdf(rasters_path, name_format, start_date, end_date,
              latlim, lonlim, cellsize, nc_path)

# Run HANTS for a single point
nb = 365
nf = 3
low = -1
high = 1
HiLo = 'Lo'
fet = 0.05
delta = 0.1
dod = 1

point = [108.87, 11.47]
df = HANTS_singlepoint(nc_path, point, nb, nf, HiLo, low, high, fet,
                       dod, delta)
print df

# Run HANTS
HANTS_netcdf(nc_path, nb, nf, HiLo, low, high, fet, dod, delta)

# Check fit
ylim = [-1, 1]
plot_point(nc_path, point, ylim)

# Export rasters
rasters_path_out = r'C:\example\output_rasters'
export_tiffs(rasters_path_out, nc_path, name_format)
  1. Ergebnisanzeige

Streuanpassungseffekt:

Bild

Oberflächenanpassungseffekt:


import matplotlib.pyplot as plt
%matplotlib inline
import xarray as xr

p = r'./example/ndvi_probav.nc'
ds = xr.open_dataset(p, engine='netcdf4')
plt.figure(figsize=(20,5))
ax  = plt.subplot(1,3,1)
ds['original_values'][:,:,0].plot(ax=ax,vmin=0,vmax=1)
ax2  = plt.subplot(1,3,2)
ds['hants_values'][:,:,0].plot(ax=ax2,vmin=0,vmax=1)
ax3  = plt.subplot(1,3,3)
ds['outliers'][:,:,0].plot(ax=ax3,vmin=0,vmax=1)

Fügen Sie hier eine Bildbeschreibung ein

plt.figure(figsize=(20,5))
ax  = plt.subplot(1,3,1)
ds['original_values'][:,:,1].plot(ax=ax,vmin=0,vmax=1)
ax2  = plt.subplot(1,3,2)
ds['hants_values'][:,:,1].plot(ax=ax2,vmin=0,vmax=1)
ax3  = plt.subplot(1,3,3)
ds['outliers'][:,:,1].plot(ax=ax3,vmin=0,vmax=1)

Fügen Sie hier eine Bildbeschreibung ein

Referenz:
https://mabouali.wordpress.com/projects/harmonic-analysis-of-time-series-hants/

http://zhihu.geoscene.cn/article/1997

Vergleich der Wolkenentfernungsalgorithmen für MODIS NDVI-Zeitreihendaten – Liang Shouzhen.

Guess you like

Origin blog.csdn.net/mengjizhiyou/article/details/131819486