Skip to content

Commit e9fd603

Browse files
committed
updating trend functions
1 parent 75fad44 commit e9fd603

File tree

1 file changed

+136
-29
lines changed

1 file changed

+136
-29
lines changed

R/trend_functions.R

Lines changed: 136 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ tsEvaTransformSeriesToStationaryTrendOnly_ciPercentile <- function(timeStamps,
215215
#' @param series A vector of the time series data.
216216
#' @param timeWindow The size of the time window used for detrending.
217217
#' @param TrendTh The threshold for fitting the trend on the means above a
218-
#' given quantile. Default is 0.5.
218+
#' given quantile. If "NA", default to 0.5.
219219
#'
220220
#' @return A list containing the following components:
221221
#' \describe{
@@ -263,53 +263,160 @@ tsEvaTransformSeriesToStationaryPeakTrend <- function(timeStamps, series, timeWi
263263
}
264264
message(paste0("trend threshold= ", TrendTh))
265265
qd <- quantile(series, TrendTh, na.rm = T)
266-
serieb <- series
267-
serieb[which(serieb < qd)] <- NA
266+
series_above_threshold <- series
267+
series_above_threshold[which(series_above_threshold < threshold)] <- NA
268268

269269

270-
rs <- tsEvaDetrendTimeSeries(timeStamps, serieb, timeWindow)
270+
detrend_result <- tsEvaDetrendTimeSeries(timeStamps, series_above_threshold, timeWindow)
271+
272+
# Detrend the series
273+
detrended_series <- series - detrend_result@trendSeries
274+
detrended_series_above_threshold <- series_above_threshold - detrend_result@trendSeries
275+
276+
# Calculate running variance and standard deviation
277+
n_run_mean <- detrend_result@nRunMn
278+
variance_series <- tsEvaNanRunningVariance(detrended_series_above_threshold, n_run_mean)
279+
variance_series <- tsEvaNanRunningMean(variance_series, ceiling(n_run_mean / 2))
280+
std_dev_series <- sqrt(variance_series)
281+
avg_std_dev <- mean(std_dev_series, na.rm = TRUE)
282+
S <- 2
283+
284+
# Calculate error on standard deviation
285+
N <- timeWindow * 4
286+
std_dev_error <- avg_std_dev * (2 * S^2 / N^3)^(1 / 4)
287+
288+
# Compute running statistics of the stationary series
289+
stationary_series <- detrended_series / std_dev_series
290+
stationary_series_no_na <- na.omit(stationary_series)
291+
292+
running_moments <- tsEvaNanRunningStatistics(stationary_series_no_na, n_run_mean)
293+
stat_ser_3_mom <- tsEvaNanRunningMean(running_moments$rn3mom, ceiling(n_run_mean))
294+
stat_ser_4_mom <- tsEvaNanRunningMean(running_moments$rn4mom, ceiling(n_run_mean))
295+
296+
# Calculate error on the trend
297+
trend_error <- avg_std_dev / sqrt(N)
271298

272-
# adding the threshold to avoid values on the zero
299+
# Store the results in a list
300+
transformed_data <- list(
301+
runningStatsMulteplicity = n_run_mean,
302+
stationarySeries = stationary_series,
303+
trendSeries = detrend_result@trendSeries,
304+
trendSeriesNonSeasonal = NULL,
305+
trendError = trend_error,
306+
stdDevSeries = std_dev_series,
307+
stdDevSeriesNonSeasonal = NULL,
308+
stdDevError = std_dev_error * rep(1, length(std_dev_series)),
309+
timeStamps = timeStamps,
310+
nonStatSeries = series,
311+
statSer3Mom = stat_ser_3_mom,
312+
statSer4Mom = stat_ser_4_mom
313+
)
314+
315+
return(transformed_data)
316+
}
317+
318+
#' tsEvaTransformSeriesToStationaryMMXTrend
319+
#'
320+
#' \code{tsEvaTransformSeriesToStationaryMMXTrend}
321+
#' transforms a time series to a stationary one by focusing on the monthly maximum values.
322+
#' The trend and slowly varying amplitude are computed on the monthly maximum values.
323+
#'
324+
#' @param timeStamps A vector of time stamps corresponding to the observations in the series.
325+
#' @param series A vector of the time series data.
326+
#' @param timeWindow The size of the time window used for detrending.
327+
#'
328+
#' @return A list containing the following components:
329+
#' \describe{
330+
#' \item{\code{runningStatsMulteplicity}}{The multiplicity of running statistics.}
331+
#' \item{\code{stationarySeries}}{The stationary series after removing the trend.}
332+
#' \item{\code{trendSeries}}{The trend component of the series.}
333+
#' \item{\code{trendSeriesNonSeasonal}}{NULL (not used).}
334+
#' \item{\code{trendError}}{The error on the trend component.}
335+
#' \item{\code{stdDevSeries}}{The standard deviation series.}
336+
#' \item{\code{stdDevSeriesNonSeasonal}}{NULL (not used).}
337+
#' \item{\code{stdDevError}}{The error on the standard deviation series.}
338+
#' \item{\code{timeStamps}}{The time stamps.}
339+
#' \item{\code{nonStatSeries}}{The original non-stationary series.}
340+
#' \item{\code{statSer3Mom}}{The running mean of the third moment of the stationary series.}
341+
#' \item{\code{statSer4Mom}}{The running mean of the fourth moment of the stationary series.}
342+
#' }
343+
#'
344+
#' @import stats
345+
#' @importFrom dplyr mutate group_by summarise
346+
#' @importFrom lubridate floor_date
347+
#' @examples
348+
#' timeAndSeries <- ArdecheStMartin
349+
#' timeStamps <- ArdecheStMartin[,1]
350+
#' series <- ArdecheStMartin[,2]
351+
#' # select only the 5 latest years
352+
#' yrs <- as.integer(format(timeStamps, "%Y"))
353+
#' tokeep <- which(yrs >= 2015)
354+
#' timeStamps <- timeStamps[tokeep]
355+
#' series <- series[tokeep]
356+
#' timeWindow <- 365 # 1 year
357+
#' result <- tsEvaTransformSeriesToStationaryMMXTrend(timeStamps, series, timeWindow)
358+
#' plot(result$trendSeries)
359+
#'
360+
#' @seealso [tsEvaDetrendTimeSeries()], [tsEvaNanRunningVariance()],
361+
#' [tsEvaNanRunningMean()], [tsEvaNanRunningStatistics()], [mutate()], [group_by()],
362+
#' [summarise()], [floor_date()]
363+
#' @export
364+
tsEvaTransformSeriesToStationaryMMXTrend <- function(timeStamps, series, timeWindow) {
365+
# Check if timeStamps and series are of the same length
366+
if (length(timeStamps) != length(series)) {
367+
stop("timeStamps and series must be of the same length.")
368+
}
369+
370+
# Check if timeWindow is a positive integer
371+
if (!is.numeric(timeWindow) || timeWindow <= 0) {
372+
stop("timeWindow must be a positive integer.")
373+
}
374+
375+
# Create a data frame with timeStamps and series
376+
tserie <- data.frame(timeStamps, series)
377+
378+
# Calculate monthly maximum values
379+
monthly_max <- tserie %>%
380+
mutate(month = floor_date(timeStamps, "month")) %>%
381+
group_by(month) %>%
382+
summarise(max_value = max(series, na.rm = TRUE))
383+
384+
# Create a series with only the monthly maximum values
385+
serieb <- series
386+
tm <- na.omit(match(as.Date(as.character(monthly_max$month)), as.Date(timeStamps)))
387+
serieb[-tm] <- NA
388+
389+
# Detrend the series based on monthly maximum values
390+
rs <- tsEvaDetrendTimeSeries(timeStamps, serieb, timeWindow)
273391
detrendSeries <- series - rs@trendSeries
274392
detrendSerie1 <- serieb - rs@trendSeries
275-
qd2 <- min(detrendSerie1, na.rm = T)
276-
nRunMn <- rs@nRunMn
277393

394+
# Calculate running variance and standard deviation
395+
nRunMn <- rs@nRunMn
278396
varianceSeries <- tsEvaNanRunningVariance(detrendSerie1, nRunMn)
279-
# further smoothing
280397
varianceSeries <- tsEvaNanRunningMean(varianceSeries, ceiling(nRunMn / 2))
281-
282-
stdDevSeries1 <- varianceSeries^.5
398+
stdDevSeries1 <- varianceSeries^0.5
283399
stdDevSeries <- stdDevSeries1
284-
avgStdDev <- mean(stdDevSeries)
400+
avgStdDev <- mean(stdDevSeries, na.rm = TRUE)
285401
S <- 2
286402

287-
# N is the size of each sample used to compute the average
288-
# Counts the real amount of timstep, need to specify original temporal resolution
403+
# Calculate error on standard deviation
289404
N <- timeWindow * 4
290-
# computation of the error on the standard deviation explained in
291-
# Mentaschi et al 2016
292405
stdDevError <- avgStdDev * (2 * S^2 / N^3)^(1 / 4)
293406

294407
# Compute running statistics of the stationary series
295408
statSeries <- detrendSeries / stdDevSeries
409+
statSeries_no_na <- na.omit(statSeries)
296410

297-
xtremS <- statSeries
298-
xtremS <- na.omit(xtremS)
299-
allS <- na.omit(serieb)
300-
301-
statSer3Mom <- tsEvaNanRunningStatistics(statSeries, nRunMn)$rn3mom
302-
statSer4Mom <- tsEvaNanRunningStatistics(statSeries, nRunMn)$rn4mom
303-
statSer3Mom <- tsEvaNanRunningMean(statSer3Mom, ceiling(nRunMn))
304-
statSer4Mom <- tsEvaNanRunningMean(statSer4Mom, ceiling(nRunMn))
305-
411+
running_moments <- tsEvaNanRunningStatistics(statSeries_no_na, nRunMn)
412+
statSer3Mom <- tsEvaNanRunningMean(running_moments$rn3mom, ceiling(nRunMn))
413+
statSer4Mom <- tsEvaNanRunningMean(running_moments$rn4mom, ceiling(nRunMn))
306414

307-
# the error on the trend is computed as the error on the average:
308-
# error(average) = stdDev/sqrt(N)
309-
trendError <- mean(stdDevSeries) / N^.5
415+
# Calculate error on the trend
416+
trendError <- avgStdDev / sqrt(N)
310417

311418
# Store the results in a list
312-
trasfData <- list(
419+
transformed_data <- list(
313420
runningStatsMulteplicity = nRunMn,
314421
stationarySeries = statSeries,
315422
trendSeries = rs@trendSeries,
@@ -324,7 +431,7 @@ tsEvaTransformSeriesToStationaryPeakTrend <- function(timeStamps, series, timeWi
324431
statSer4Mom = statSer4Mom
325432
)
326433

327-
return(trasfData)
434+
return(transformed_data)
328435
}
329436

330437

0 commit comments

Comments
 (0)