This is a very old question so, unlikely that the answer will be closed. However, I wanted to add some additional options to the previous answer and modernize the spatial classes. I have several timeseries related functions, applicable to rasters, in the spatialEco library (imputing missing values, smoothing, Mann-Kendall statistic, ..).
We can use the previous simulated example data.
library(terra)
library(spatialEco)
library(forecast)
set.seed(42)
dates <- date_seq("2001/01/01", "2002/12/31", "week")
n.row <- 100
n.col <- 100
n.time <- length(dates)
sd.err <- outer(1:n.row, 1:n.col, function(x,y) 5 *
((1/2 - y/n.col)^2 + (1/2 - x/n.row)^2))
e <- array(rnorm(n.row * n.col * n.time, sd=sd.err),
dim=c(n.row, n.col, n.time))
beta.1 <- outer(1:n.row, 1:n.col, function(x,y) { sin((x/n.row)^2 -
(y/n.col)^3)*5} ) / n.time
beta.0 <- outer(1:n.row, 1:n.col, function(x,y) { atan2(y, n.col-x) } )
times <- 1:n.time
y <- rast(array(outer(as.vector(beta.1), times) + as.vector(beta.0),
dim=c(n.row, n.col, n.time)) + e)
We then want to fill in missing values (which is not relevant here but part of EDA of timeseries data) and smooth the timeseries to mitigate outlier effect while deriving the trend.
y.smooth <- app(y, impute.loess, smooth=TRUE)
Now we can start deriving trends (slope). First we calculate the nonparametric Mann-Kendall statistic (temporal correlation) with the monotonic Sen slope.
( k <- raster.kendall(y.smooth, tau = TRUE) )
Next, we apply a parametric method incorporating an Autoregressive AR(I) term. It is critical to address any periodicity in the data before applying these types of methods. This can be done by decomposing the data into the trend, seasonal and noise components and then extract the trend. We can write a function that can be passed to the terra app function that does both of these things (a step through is provided below).
# Detrend/slope function
ts.trend <- function(y, d=c(2001, 01), f = 52) {
y <- na.omit(y)
if(length(y) >= 12) {
detrend <- stats::decompose(stats::ts(y, start= c(2000, 01),
frequency = f))
trend <- forecast::tslm(detrend$trend ~ trend)
s <- stats::coefficients(trend)[2]
se <- summary(trend)$sigma
} else {
s <- NA
se <- NA
}
return(c(as.numeric(s),as.numeric(se)))
}
# pass function to terra app and plot
slp <- app(y.smooth, ts.trend)
names(slp) <- c("slope", "std.err")
Now, plot Kendall and detrended slopes. Note that there are +/- 95% confidence interval and significance (p-value) rasters available for the Kendall statistic and standard errors for the parametric autoregression trend, we just are not plotting them.
dev.new(height=11, width=8)
par(mfcol=c(2,1))
plot(slp[[1]], main="detrended slope")
plot(k[[1]], main="Kendall slope")
Here is a step through of what is going on at the pixel-level. We can extract the values of a single pixel and apply the above workflow.
Extract timeseries at cell number 1000.
x <- as.numeric(y[1000])
Smooth data with Loess regression and evaluate uncertainty using a Bootstrap. Some exploratory analysis helps hone in on an appropriate smoothing parameter. If you are simply filling in NA's (ie., smooth = FALSE) then you want a rather exact smoothing parameter such as s = 0.10. However, you do want to apply some smoothing so that outliers (eg., rare events) do not influence the underlying trend.
x.smooth <- impute.loess(x, s=0.02)
sboot <- suppressWarnings(loess.boot(1:length(x), x, nreps=99,
confidence=0.30, span=0.20))
dev.new(height=6, width=14)
plot(sboot, main = "Loess smoothing with raw timeseries")
lines(1:length(x), x, lty=3, col="blue")
points(1:length(x), x, pch=20, cex=0.75, col="blue")
legend("bottomleft", legend=c("smoothed (s=0.2)", "raw timeseires"),
lty=c(2,3), pch=c(NA,20), col=c("black", "blue") )
Here we derive the Kendall statistic
( k <- kendall(x.smooth) )
And, this breaks down the above function for decomposing and deriving an AR(I) trend
x.ts <- ts(x.smooth, start= c(2001, 01), frequency = 52)
detrend <- decompose(x.ts)
fit.dt <- forecast::tslm(detrend$trend ~ trend)
trend.dt <- forecast(fit.dt, h=length(x), level=0)
stats::coefficients(fit.dt)[2] # slope/trend
Note that the parametric slope is -0.0469 and the nonparametric slope on the smoothed data (not decomposed) is -0.04129 with a correlation (Tau) of -0.35348. So, there are times that the nonparametric statistic is more efficient. I will note that in certain cases the is something referred to as the long-run effect where, with very long timeseries (eg., n>500) can flatten out the slope making them very small in relation to the change. There are special statistics to help account for this (eg., long run mean and variance to help normalize the timeseries).