-
Notifications
You must be signed in to change notification settings - Fork 0
/
Time_series_decomposition.py
103 lines (79 loc) · 3.13 KB
/
Time_series_decomposition.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# coding: utf-8
# Python script to convert raw data in a .netcdf file into residuals
# via a linear decomposition approach: first the trend is calculated and subtracted, then a cyclical component.
# Usage in command line:
# python Time_series_decomposition.py resolution infile.nc outfile.nc
#
# resolution should be one of monthly - weekly - daily
# Import libraries
import os
import glob
import sys
import pandas as pd
import xarray as xr
import numpy as np
import scipy.stats # for calculation of trend
from tqdm import tqdm # progressbar
#-----COMMAND LINE ARGUMENTS----#
resolution = sys.argv[1]
infile = sys.argv[2]
outfile = sys.argv[3]
#-------------------------------#
assert resolution in ['monthly', 'weekly', 'daily'], "Resolution should be one of ('monthly', 'weekly', 'daily'"
# Define function to calculate the trend
def calc_trend(ndarray, t):
"""Calculate linear trend
Inputs
------
x: numpy array with original data
t: np array with timestamps
Returns
--------
T: estimate of the linear trend
"""
slope = scipy.stats.linregress(t, ndarray).slope
intercept = scipy.stats.linregress(t, ndarray).intercept
T = t*slope + intercept
return(T)
# Read in data
LAI = xr.open_dataset(infile)
# Extract to numpy
print('Extracting data to numpy...')
LAI_npdata = LAI['LAI'].values
npshape = LAI_npdata.shape
print('Done!')
# Numpy array to store trend component
LAI_trend_np = np.ndarray(shape=npshape)
print('Starting loop...')
# Loop over pixels, store the trend component
for lat in tqdm(range(0,len(LAI.lat))):
for lon in range(0,len(LAI.lon)):
if not np.all(np.isnan(LAI_npdata[:,lat,lon])): # skip if all data is NaN for this pixel
LAI_trend_np[:,lat,lon] = calc_trend(LAI_npdata[:,lat,lon], np.arange(0,npshape[0]))
else:
LAI_trend_np[:,lat,lon]=np.nan
print('Detrending done...')
# Store trend component in xarray dataArray
LAI_trend = xr.DataArray(LAI_trend_np, dims=['time', 'lat', 'lon'],
coords=LAI.coords)
# Calculate detrended data
LAI_detrended = LAI - LAI_trend
# Estimate cycle component
# Conform the cycle component onto the same index as the detrended data, and extract to numpy array so we
# can extract it element-wise
if resolution == 'monthly':
y_w_mean = LAI_detrended.groupby('time.month').mean(dim='time') # monthly resolution
y_w_mean_expanded = y_w_mean.reindex({'month': LAI_detrended.time.to_series().index.month})['LAI'].values
elif resolution == 'weekly':
y_w_mean = LAI_detrended.groupby('time.weekofyear').mean(dim='time') # weekly resolution
y_w_mean_expanded = y_w_mean.reindex({'weekofyear': LAI_detrended.time.to_series().index.weekofyear})['LAI'].values
elif resolution == 'daily':
y_w_mean = LAI_detrended.groupby('time.dayofyear').mean(dim='time') # daily resolution
y_w_mean_expanded = y_w_mean.reindex({'dayofyear': LAI_detrended.time.to_series().index.dayofyear})['LAI'].values
else:
print('Provide adequate resolution')
# Finally, obtain residuals
LAI_resid = LAI_detrended-y_w_mean_expanded
print('Done!')
# dump
LAI_resid.to_netcdf(outfile)