Title: | Extract Remote Sensing Vegetation Phenology |
---|---|
Description: | The merits of 'TIMESAT' and 'phenopix' are adopted. Besides, a simple and growing season dividing method and a practical snow elimination method based on Whittaker were proposed. 7 curve fitting methods and 4 phenology extraction methods were provided. Parameters boundary are considered for every curve fitting methods according to their ecological meaning. And 'optimx' is used to select best optimization method for different curve fitting methods. Reference: Kong, D., (2020). R package: A state-of-the-art Vegetation Phenology extraction package, phenofit version 0.3.1, <doi:10.5281/zenodo.5150204>; Kong, D., Zhang, Y., Wang, D., Chen, J., & Gu, X. (2020). Photoperiod Explains the Asynchronization Between Vegetation Carbon Phenology and Vegetation Greenness Phenology. Journal of Geophysical Research: Biogeosciences, 125(8), e2020JG005636. <doi:10.1029/2020JG005636>; Kong, D., Zhang, Y., Gu, X., & Wang, D. (2019). A robust method for reconstructing global MODIS EVI time series on the Google Earth Engine. ISPRS Journal of Photogrammetry and Remote Sensing, 155, 13–24; Zhang, Q., Kong, D., Shi, P., Singh, V.P., Sun, P., 2018. Vegetation phenology on the Qinghai-Tibetan Plateau and its response to climate change (1982–2013). Agric. For. Meteorol. 248, 408–417. <doi:10.1016/j.agrformet.2017.10.026>. |
Authors: | Dongdong Kong [aut, cre], Mingzhong Xiao [aut], Yongqiang Zhang [aut], Xihui Gu [aut], Jianjian Cui [aut] |
Maintainer: | Dongdong Kong <[email protected]> |
License: | GPL-2 | file LICENSE |
Version: | 0.3.9 |
Built: | 2024-10-31 22:19:03 UTC |
Source: | CRAN |
Variables in CA-NS6
:
site
: site name
y
: EVI
date
: date of image
t
: date of compositing image
w
: weights of data point
QC_flag
: QC flag of y, in the range of c("snow", "cloud", "shadow", "aerosol", "marginal", "good")
data('CA_NS6')
data('CA_NS6')
An object of class data.table
(inherits from data.frame
) with 161 rows and 6 columns.
Check input data, interpolate NA values in y, remove spike values, and set weights for NA in y and w.
check_input( t, y, w, QC_flag, nptperyear, south = FALSE, wmin = 0.2, wsnow = 0.8, ymin, missval, maxgap, alpha = 0.02, alpha_high = NULL, date_start = NULL, date_end = NULL, mask_spike = TRUE, na.rm = FALSE, ... )
check_input( t, y, w, QC_flag, nptperyear, south = FALSE, wmin = 0.2, wsnow = 0.8, ymin, missval, maxgap, alpha = 0.02, alpha_high = NULL, date_start = NULL, date_end = NULL, mask_spike = TRUE, na.rm = FALSE, ... )
t |
Numeric vector, |
y |
Numeric vector, vegetation index time-series |
w |
(optional) Numeric vector, weights of |
QC_flag |
Factor (optional) returned by |
nptperyear |
Integer, number of images per year. |
south |
Boolean. In south hemisphere, growing year is 1 July to the following year 31 June; In north hemisphere, growing year is 1 Jan to 31 Dec. |
wmin |
Double, minimum weight of bad points, which could be smaller the weight of snow, ice and cloud. |
wsnow |
Doulbe. Reset the weight of snow points, after get |
ymin |
If specified, |
missval |
Double, which is used to replace NA values in y. If missing,
the default vlaue is |
maxgap |
Integer, nptperyear/4 will be a suitable value. If continuous
missing value numbers less than maxgap, then interpolate those NA values by
zoo::na.approx; If false, then replace those NA values with a constant value
|
alpha |
Double, in |
alpha_high |
Double, |
date_start , date_end
|
starting and ending date of the original vegetation
time-sereis (before |
mask_spike |
Boolean. Whether to remove spike values? |
na.rm |
Boolean. If |
... |
Others will be ignored. |
A list object returned:
t
: Numeric vector
y0
: Numeric vector, original vegetation time-series.
y
: Numeric vector, checked vegetation time-series, NA
values are interpolated.
w
: Numeric vector
Tn
: Numeric vector
ylu
: = [ymin, ymax]
. w_critical
is used to filter not too bad values.
If the percentage good values (w=1) is greater than 30\
The else, if the percentage of w >= 0.5 points is greater than 10\
w_critical
=0.5. In boreal regions, even if the percentage of w >= 0.5
points is only 10\
We can't rely on points with the wmin weights. Then, y_good = y[w >= w_critical]
, ymin = pmax( quantile(y_good, alpha/2), 0)
ymax = max(y_good)
.
data("CA_NS6") d = CA_NS6 # head(d) nptperyear = 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) plot_input(INPUT)
data("CA_NS6") d = CA_NS6 # head(d) nptperyear = 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) plot_input(INPUT)
Curve fitting values are constrained in the range of ylu
.
Only constrain trough value for a stable background value. But not for peak
value.
check_ylu(yfit, ylu)
check_ylu(yfit, ylu)
yfit |
Numeric vector, curve fitting result |
ylu |
limits of y value, |
yfit, the numeric vector in the range of ylu
.
check_ylu(1:10, c(2, 8))
check_ylu(1:10, c(2, 8))
Curve fit vegetation index (VI) time-series of every growing season using fine curve fitting methods.
curvefit( y, t = index(y), tout = t, methods = c("AG", "Beck", "Elmore", "Gu", "Klos", "Zhang"), w = NULL, ..., type = 1L, use.cpp = FALSE )
curvefit( y, t = index(y), tout = t, methods = c("AG", "Beck", "Elmore", "Gu", "Klos", "Zhang"), w = NULL, ..., type = 1L, use.cpp = FALSE )
y |
Vegetation time-series index, numeric vector |
t |
The corresponding doy of x |
tout |
The output interpolated time. |
methods |
Fine curve fitting methods, can be one or more of |
w |
(optional) Numeric vector, weights of |
... |
other parameters passed to curve fitting function. |
type |
integer,
|
use.cpp |
(unstable, not used) boolean, whether to use c++ defined fine
fitting function? If |
fFITs S3 object, see fFITs()
for details.
'Klos' have too many parameters. It will be slow and not stable.
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c(mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout = tout, methods)
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c(mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout = tout, methods)
Fine Curve fitting for INPUT time-series.
curvefits(INPUT, brks, options = list(), ...)
curvefits(INPUT, brks, options = list(), ...)
INPUT |
A list object with the elements of 't', 'y', 'w', 'Tn' (optional)
and 'ylu', returned by |
brks |
A list object with the elements of 'fit' and 'dt', returned by
|
options |
see section: options for fitting for details. |
... |
other parameters to |
List of phenofit fitting object.
methods
(default c('AG', 'Beck', 'Elmore', 'Zhang')``): Fine curve fitting methods, can be one or more of
c('AG', 'Beck', 'Elmore', 'Zhang',
'Gu', 'Klos')‘. Note that ’Gu' and 'Klos' are very slow.
iters
(default 2): max iterations of fine fitting.
wFUN
(default wTSM
): Character or function, weights updating function
of fine fitting function.
wmin
(default 0.1): min weights in the weights updating procedure.
use.rough
(default FALSE): Whether to use rough fitting smoothed
time-series as input? If false
, smoothed VI by rough fitting will be used
for Phenological metrics extraction; If true
, original input y
will be
used (rough fitting is used to divide growing seasons and update weights.
use.y0
(default TRUE): boolean. whether to use original y0
as the input
of plot_input
, note that not for curve fitting. y0
is the original
value before the process of check_input
.
nextend
(default 2): Extend curve fitting window, until nextend
good or
marginal points are found in the previous and subsequent growing season.
maxExtendMonth
(default 1): Search good or marginal good values in
previous and subsequent maxExtendMonth
period.
minExtendMonth
(default 0.5): Extend period defined by nextend
and
maxExtendMonth
, should be no shorter than minExtendMonth
. When all
points of the input time-series are good value, then the extending period
will be too short. In that situation, we can't make sure the connection
between different growing seasons is smoothing.
minPercValid
: (default 0, not use). If the percentage of good- and
marginal- quality points is less than minPercValid
, curve fiting result is
set to NA
.
minT
: (not use). If Tn
not provided in INPUT
, minT
will
not be used. minT
use night temperature Tn to define backgroud value
(days with Tn < minT
treated as ungrowing season).
data("CA_NS6") d = CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) # plot_input(INPUT) # Rough fitting and growing season dividing wFUN <- "wTSM" brks2 <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = wFUN, r_min = 0.05, ypeak_min = 0.05, lambda = 10, verbose = FALSE )) # plot_season(INPUT, brks2, d) # Fine fitting fits <- curvefits( INPUT, brks2, options = list( methods = c("AG", "Beck", "Elmore", "Zhang"), #,"klos", "Gu" wFUN = wFUN, nextend = 2, maxExtendMonth = 2, minExtendMonth = 1, minPercValid = 0.2 ) ) r_param = get_param(fits) r_pheno = get_pheno(fits) r_gof = get_GOF(fits) d_fit = get_fitting(fits) g <- plot_curvefits(d_fit, brks2) grid::grid.newpage(); grid::grid.draw(g)
data("CA_NS6") d = CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) # plot_input(INPUT) # Rough fitting and growing season dividing wFUN <- "wTSM" brks2 <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = wFUN, r_min = 0.05, ypeak_min = 0.05, lambda = 10, verbose = FALSE )) # plot_season(INPUT, brks2, d) # Fine fitting fits <- curvefits( INPUT, brks2, options = list( methods = c("AG", "Beck", "Elmore", "Zhang"), #,"klos", "Gu" wFUN = wFUN, nextend = 2, maxExtendMonth = 2, minExtendMonth = 1, minPercValid = 0.2 ) ) r_param = get_param(fits) r_pheno = get_pheno(fits) r_gof = get_GOF(fits) d_fit = get_fitting(fits) g <- plot_curvefits(d_fit, brks2) grid::grid.newpage(); grid::grid.draw(g)
Local model functions f_L(t)
, f_C(t)
and f_R(t)
describe the VI variation in intervals around the left minima, the central
maxima and the right minima.
Local model function are merged into global model function via merge_LocalModels()
and Per J\"onsson et al. (2004; their Eq. 12),
where cut-off function sharply drop from 1 to 0 in small intervals around
(t_L + t_C)/2
and (t_C + t_R)/2
.
curvefits_LocalModel(INPUT, brks, options = list(), ...) merge_LocalModels(fits)
curvefits_LocalModel(INPUT, brks, options = list(), ...) merge_LocalModels(fits)
INPUT |
A list object with the elements of 't', 'y', 'w', 'Tn' (optional)
and 'ylu', returned by |
brks |
A list object with the elements of 'fit' and 'dt', returned by
|
options |
see section: options for fitting for details. |
... |
other parameters to |
fits |
List objects returned by |
methods
(default c('AG', 'Beck', 'Elmore', 'Zhang')``): Fine curve fitting methods, can be one or more of
c('AG', 'Beck', 'Elmore', 'Zhang',
'Gu', 'Klos')‘. Note that ’Gu' and 'Klos' are very slow.
iters
(default 2): max iterations of fine fitting.
wFUN
(default wTSM
): Character or function, weights updating function
of fine fitting function.
wmin
(default 0.1): min weights in the weights updating procedure.
use.rough
(default FALSE): Whether to use rough fitting smoothed
time-series as input? If false
, smoothed VI by rough fitting will be used
for Phenological metrics extraction; If true
, original input y
will be
used (rough fitting is used to divide growing seasons and update weights.
use.y0
(default TRUE): boolean. whether to use original y0
as the input
of plot_input
, note that not for curve fitting. y0
is the original
value before the process of check_input
.
nextend
(default 2): Extend curve fitting window, until nextend
good or
marginal points are found in the previous and subsequent growing season.
maxExtendMonth
(default 1): Search good or marginal good values in
previous and subsequent maxExtendMonth
period.
minExtendMonth
(default 0.5): Extend period defined by nextend
and
maxExtendMonth
, should be no shorter than minExtendMonth
. When all
points of the input time-series are good value, then the extending period
will be too short. In that situation, we can't make sure the connection
between different growing seasons is smoothing.
minPercValid
: (default 0, not use). If the percentage of good- and
marginal- quality points is less than minPercValid
, curve fiting result is
set to NA
.
minT
: (not use). If Tn
not provided in INPUT
, minT
will
not be used. minT
use night temperature Tn to define backgroud value
(days with Tn < minT
treated as ungrowing season).
Per J\"onsson, P., Eklundh, L., 2004. TIMESAT - A program for analyzing time-series of satellite sensor data. Comput. Geosci. 30, 833-845. doi:10.1016/j.cageo.2004.05.006.
## Not run: library(phenofit) data("CA_NS6") d = CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) # plot_input(INPUT) # Rough fitting and growing season dividing wFUN <- "wTSM" brks2 <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = wFUN, r_min = 0.05, ypeak_min = 0.05, lambda = 10, verbose = FALSE )) # plot_season(INPUT, brks2, d) # Fine fitting fits <- curvefits_LocalModel( INPUT, brks2, options = list( methods = c("AG", "Beck", "Elmore", "Zhang", "Gu"), #,"klos", "Gu" wFUN = wFUN, nextend = 2, maxExtendMonth = 2, minExtendMonth = 1, minPercValid = 0.2 ), constrain = TRUE ) # merge local model function into global model function fits_merged = merge_LocalModels(fits) ## Visualization --------------------------------------------------------------- l_fitting = map(fits %>% guess_names, get_fitting) #%>% melt_list("period") d_merged = get_fitting(fits_merged[[2]]) %>% cbind(type = "Merged") d_raw = l_fitting[2:4] %>% set_names(c("Left", "Central", "Right")) %>% melt_list("type") d_obs = d_raw[, .(t, y, QC_flag)] %>% unique() d_fit = rbind(d_merged, d_raw)[meth == "Zhang"] levs = c("Left", "Central", "Right", "Merged") levs_new = glue("({letters[1:4]}) {levs}") %>% as.character() d_fit$type %<>% factor(levs, levs_new) p = ggplot(d_obs, aes(t, y)) + geom_point() + geom_line(data = d_fit, aes(t, ziter2, color = type)) + facet_wrap(~type) + labs(x = "Date", y = "EVI") + scale_x_date(date_labels = "%b %Y", expand = c(1, 1)*0.08) + theme_bw(base_size = 13) + theme(legend.position = "none", strip.text = element_text(size = 14)) p ## End(Not run)
## Not run: library(phenofit) data("CA_NS6") d = CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) # plot_input(INPUT) # Rough fitting and growing season dividing wFUN <- "wTSM" brks2 <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = wFUN, r_min = 0.05, ypeak_min = 0.05, lambda = 10, verbose = FALSE )) # plot_season(INPUT, brks2, d) # Fine fitting fits <- curvefits_LocalModel( INPUT, brks2, options = list( methods = c("AG", "Beck", "Elmore", "Zhang", "Gu"), #,"klos", "Gu" wFUN = wFUN, nextend = 2, maxExtendMonth = 2, minExtendMonth = 1, minPercValid = 0.2 ), constrain = TRUE ) # merge local model function into global model function fits_merged = merge_LocalModels(fits) ## Visualization --------------------------------------------------------------- l_fitting = map(fits %>% guess_names, get_fitting) #%>% melt_list("period") d_merged = get_fitting(fits_merged[[2]]) %>% cbind(type = "Merged") d_raw = l_fitting[2:4] %>% set_names(c("Left", "Central", "Right")) %>% melt_list("type") d_obs = d_raw[, .(t, y, QC_flag)] %>% unique() d_fit = rbind(d_merged, d_raw)[meth == "Zhang"] levs = c("Left", "Central", "Right", "Merged") levs_new = glue("({letters[1:4]}) {levs}") %>% as.character() d_fit$type %<>% factor(levs, levs_new) p = ggplot(d_obs, aes(t, y)) + geom_point() + geom_line(data = d_fit, aes(t, ziter2, color = type)) + facet_wrap(~type) + labs(x = "Date", y = "EVI") + scale_x_date(date_labels = "%b %Y", expand = c(1, 1)*0.08) + theme_bw(base_size = 13) + theme(legend.position = "none", strip.text = element_text(size = 14)) p ## End(Not run)
Goal function of fine curve fitting methods
f_goal(par, fun, y, t, pred, w, ylu, ...)
f_goal(par, fun, y, t, pred, w, ylu, ...)
par |
A vector of parameters |
fun |
A curve fitting function, can be one of |
y |
Numeric vector, vegetation index time-series |
t |
Numeric vector, |
pred |
Numeric Vector, predicted values |
w |
(optional) Numeric vector, weights of |
ylu |
|
... |
others will be ignored. |
RMSE Root Mean Square Error of curve fitting values.
library(phenofit) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) par0 = c( mn = 0.15, mx = 0.65, sos = 100, rsp = 0.12, eos = 200, rau = 0.12) # simulate vegetation time-series fFUN = doubleLog_Beck t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- fFUN(par, t) f_goal(par0, fFUN, y, t)
library(phenofit) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) par0 = c( mn = 0.15, mx = 0.65, sos = 100, rsp = 0.12, eos = 200, rau = 0.12) # simulate vegetation time-series fFUN = doubleLog_Beck t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- fFUN(par, t) f_goal(par0, fFUN, y, t)
Find peaks (maxima) in a time series. This function is modified from
pracma::findpeaks
.
findpeaks( x, nups = 1, ndowns = nups, zero = "0", peakpat = NULL, minpeakheight = -Inf, minpeakdistance = 1, h_min = 0, h_max = 0, npeaks = 0, sortstr = FALSE, include_gregexpr = FALSE, IsPlot = F )
findpeaks( x, nups = 1, ndowns = nups, zero = "0", peakpat = NULL, minpeakheight = -Inf, minpeakdistance = 1, h_min = 0, h_max = 0, npeaks = 0, sortstr = FALSE, include_gregexpr = FALSE, IsPlot = F )
x |
Numeric vector. |
nups |
minimum number of increasing steps before a peak is reached |
ndowns |
minimum number of decreasing steps after the peak |
zero |
can be |
peakpat |
define a peak as a regular pattern, such as the default
pattern |
minpeakheight |
The minimum (absolute) height a peak has to have to be recognized as such |
minpeakdistance |
The minimum distance (in indices) peaks have to have
to be counted. If the distance of two maximum extreme value less than
|
h_min |
|
h_max |
Similar as |
npeaks |
the number of peaks to return. If |
sortstr |
Boolean, Should the peaks be returned sorted in decreasing oreder of their maximum value? |
include_gregexpr |
Boolean (default |
IsPlot |
Boolean, whether to plot? |
In versions before v0.3.4, findpeaks(c(1, 2, 3, 4, 4, 3, 1))
failed to detect
peaks when a flat pattern exit in the middle.
From version v0.3.4, the peak pattern was changed from [+]{%d,}[-]{%d,}
to
[+]{%d,}[0]{0,}[-]{%d,}
. The latter can escape the flat part successfully.
x <- seq(0, 1, len = 1024) pos <- c(0.1, 0.13, 0.15, 0.23, 0.25, 0.40, 0.44, 0.65, 0.76, 0.78, 0.81) hgt <- c(4, 5, 3, 4, 5, 4.2, 2.1, 4.3, 3.1, 5.1, 4.2) wdt <- c(0.005, 0.005, 0.006, 0.01, 0.01, 0.03, 0.01, 0.01, 0.005, 0.008, 0.005) pSignal <- numeric(length(x)) for (i in seq(along=pos)) { pSignal <- pSignal + hgt[i]/(1 + abs((x - pos[i])/wdt[i]))^4 } plot(pSignal, type="l", col="navy"); grid() x <- findpeaks(pSignal, npeaks=3, h_min=4, sortstr=TRUE) points(val~pos, x$X, pch=20, col="maroon")
x <- seq(0, 1, len = 1024) pos <- c(0.1, 0.13, 0.15, 0.23, 0.25, 0.40, 0.44, 0.65, 0.76, 0.78, 0.81) hgt <- c(4, 5, 3, 4, 5, 4.2, 2.1, 4.3, 3.1, 5.1, 4.2) wdt <- c(0.005, 0.005, 0.006, 0.01, 0.01, 0.03, 0.01, 0.01, 0.005, 0.008, 0.005) pSignal <- numeric(length(x)) for (i in seq(along=pos)) { pSignal <- pSignal + hgt[i]/(1 + abs((x - pos[i])/wdt[i]))^4 } plot(pSignal, type="l", col="navy"); grid() x <- findpeaks(pSignal, npeaks=3, h_min=4, sortstr=TRUE) points(val~pos, x$X, pch=20, col="maroon")
Fine curve fitting function is used to fit vegetation time-series in every growing season.
FitDL.Zhang(y, t = index(y), tout = t, method = "nlm", w, type = 1L, ...) FitDL.AG(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.AG2(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Beck(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Elmore(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Gu(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Klos(y, t = index(y), tout = t, method = "BFGS", w, type = 1L, ...)
FitDL.Zhang(y, t = index(y), tout = t, method = "nlm", w, type = 1L, ...) FitDL.AG(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.AG2(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Beck(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Elmore(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Gu(y, t = index(y), tout = t, method = "nlminb", w, type = 1L, ...) FitDL.Klos(y, t = index(y), tout = t, method = "BFGS", w, type = 1L, ...)
y |
input vegetation index time-series. |
t |
the corresponding doy(day of year) of y. |
tout |
the time of output curve fitting time-series. |
method |
method passed to |
w |
weights |
type |
integer,
|
... |
other paraters passed to |
tout
: The time of output curve fitting time-series.
zs
: Smoothed vegetation time-series of every iteration.
ws
: Weights of every iteration.
par
: Final optimized parameter of fine fitting.
fun
: The name of fine fitting.
Beck, P.S.A., Atzberger, C., Hogda, K.A., Johansen, B., Skidmore, A.K., 2006. Improved monitoring of vegetation dynamics at very high latitudes: A new method using MODIS NDVI. Remote Sens. Environ. https://doi.org/10.1016/j.rse.2005.10.021.
Elmore, A.J., Guinn, S.M., Minsley, B.J., Richardson, A.D., 2012.
Landscape controls on the timing of spring, autumn, and growing season
length in mid-Atlantic forests. Glob. Chang. Biol. 18, 656-674.
https://doi.org/10.1111/j.1365-2486.2011.02521.x.
Gu, L., Post, W.M., Baldocchi, D.D., Black, TRUE.A., Suyker, A.E., Verma,
S.B., Vesala, TRUE., Wofsy, S.C., 2009. Characterizing the Seasonal Dynamics
of Plant Community Photosynthesis Across a Range of Vegetation Types,
in: Noormets, A. (Ed.), Phenology of Ecosystem Processes: Applications
in Global Change Research. Springer New York, New York, NY, pp. 35-58.
https://doi.org/10.1007/978-1-4419-0026-5_2.
https://github.com/cran/phenopix/blob/master/R/FitDoubleLogGu.R
# simulate vegetation time-series t <- seq(1, 365, 8) par <- c(mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) data <- data.frame(t, y) # methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") tout <- seq(1, 365, 1) r <- FitDL.Elmore(y, t, tout) plot(r, data) get_GOF(r, data) get_param(r)
# simulate vegetation time-series t <- seq(1, 365, 8) par <- c(mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) data <- data.frame(t, y) # methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") tout <- seq(1, 365, 1) r <- FitDL.Elmore(y, t, tout) plot(r, data) get_GOF(r, data) get_param(r)
Get curve fitting data.frame
get_fitting(x) ## S3 method for class 'list' get_fitting(x) ## S3 method for class 'fFITs' get_fitting(x)
get_fitting(x) ## S3 method for class 'list' get_fitting(x) ## S3 method for class 'fFITs' get_fitting(x)
x |
|
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
Goodness-of-fitting (GOF) of fine curve fitting results.
get_GOF(x, ...) ## S3 method for class 'list' get_GOF(x, ...) ## S3 method for class 'fFITs' get_GOF(x, ...) ## S3 method for class 'fFIT' get_GOF(x, data, ...)
get_GOF(x, ...) ## S3 method for class 'list' get_GOF(x, ...) ## S3 method for class 'fFITs' get_GOF(x, ...) ## S3 method for class 'fFIT' get_GOF(x, data, ...)
x |
|
... |
ignored. |
data |
A data.frame with the columns of |
meth
: The name of fine curve fitting method
RMSE
: Root Mean Square Error
NSE
: Nash-Sutcliffe model efficiency coefficient
R
: Pearson-Correlation
R2
: determined coefficient
pvalue
: pvalue of R
n
: The number of observations
https://en.wikipedia.org/wiki/Nash-Sutcliffe_model_efficiency_coefficient
https://en.wikipedia.org/wiki/Pearson_correlation_coefficient
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
Get parameters from curve fitting result
get_param(x) ## S3 method for class 'list' get_param(x) ## S3 method for class 'fFITs' get_param(x) ## S3 method for class 'fFIT' get_param(x)
get_param(x) ## S3 method for class 'list' get_param(x) ## S3 method for class 'fFITs' get_param(x) ## S3 method for class 'fFIT' get_param(x)
x |
|
A list of tibble
with the length being equal to the number of methods.
Each line of tibble
cotains the corresponding parameters of each growing
season.
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
Get yearly vegetation phenological metrics of a curve fitting method
get_pheno(x, ...) ## S3 method for class 'rfit' get_pheno(x, TRS = c(0.2, 0.5), asymmetric = TRUE, ...) ## S3 method for class 'list' get_pheno( x, method, TRS = c(0.2, 0.5, 0.6), analytical = FALSE, smoothed.spline = FALSE, IsPlot = FALSE, show.title = TRUE, ... ) ## S3 method for class 'fFITs' get_pheno( x, method, TRS = c(0.2, 0.5), analytical = FALSE, smoothed.spline = FALSE, IsPlot = FALSE, title.left = "", show.PhenoName = TRUE, ... )
get_pheno(x, ...) ## S3 method for class 'rfit' get_pheno(x, TRS = c(0.2, 0.5), asymmetric = TRUE, ...) ## S3 method for class 'list' get_pheno( x, method, TRS = c(0.2, 0.5, 0.6), analytical = FALSE, smoothed.spline = FALSE, IsPlot = FALSE, show.title = TRUE, ... ) ## S3 method for class 'fFITs' get_pheno( x, method, TRS = c(0.2, 0.5), analytical = FALSE, smoothed.spline = FALSE, IsPlot = FALSE, title.left = "", show.PhenoName = TRUE, ... )
x |
One of:
|
... |
ignored. |
TRS |
Threshold for |
asymmetric |
If true, background value in spring season and autumn season is regarded as different. |
method |
Which fine curve fitting method to be extracted? |
analytical |
If true, |
smoothed.spline |
Whether apply |
IsPlot |
Boolean. Whether to plot figure? |
show.title |
Whether to show the name of fine curve fitting method in top title? |
title.left |
String of growing season flag. |
show.PhenoName |
Whether to show phenological methods names in the top panel? |
fFITs |
|
List of every year phenology metrics
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
library(phenofit) # simulate vegetation time-series FUN = doubleLog.Beck par = c( mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- FUN(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) # `fFITs` (fine-fitting) object fits <- list(`2001` = fit, `2002` = fit) # multiple years l_param <- get_param(fits) d_GOF <- get_GOF(fits) d_fitting <- get_fitting(fits) l_pheno <- get_pheno(fits, "AG", IsPlot=TRUE)
Good of fitting
GOF(Y_obs, Y_sim, w, include.r = TRUE, include.cv = FALSE)
GOF(Y_obs, Y_sim, w, include.r = TRUE, include.cv = FALSE)
Y_obs |
Numeric vector, observations |
Y_sim |
Numeric vector, corresponding simulated values |
w |
Numeric vector, weights of every points. If w included, when calculating mean, Bias, MAE, RMSE and NSE, w will be taken into considered. |
include.r |
If true, r and R2 will be included. |
include.cv |
If true, cv will be included. |
RMSE
root mean square error
NSE
NASH coefficient
MAE
mean absolute error
AI
Agreement index (only good points (w == 1)) participate to
calculate. See details in Zhang et al., (2015).
Bias
bias
Bias_perc
bias percentage
n_sim
number of valid obs
cv
Coefficient of variation
R2
correlation of determination
R
pearson correlation
pvalue
pvalue of R
Zhang Xiaoyang (2015), http://dx.doi.org/10.1016/j.rse.2014.10.012
Y_obs = rnorm(100) Y_sim = Y_obs + rnorm(100)/4 GOF(Y_obs, Y_sim)
Y_obs = rnorm(100) Y_sim = Y_obs + rnorm(100)/4 GOF(Y_obs, Y_sim)
Variables in input_single
:
t
: date of compositing image
y
: EVI
w
: weights of data point
ylu
: lower and upper boundary
nptperyear
: points per year
south
: boolean, whether in south Hemisphere?
data('input_single')
data('input_single')
An object of class list
of length 6.
double logistics, piecewise logistics and many other functions to curve fit VI time-series.
Logistic(par, t) doubleLog.Zhang(par, t) doubleLog.AG(par, t) doubleLog.AG2(par, t) doubleLog.Beck(par, t) doubleLog.Elmore(par, t) doubleLog.Gu(par, t) doubleLog.Klos(par, t)
Logistic(par, t) doubleLog.Zhang(par, t) doubleLog.AG(par, t) doubleLog.AG2(par, t) doubleLog.Beck(par, t) doubleLog.Elmore(par, t) doubleLog.Gu(par, t) doubleLog.Klos(par, t)
par |
A vector of parameters |
t |
A |
Logistic
The traditional simplest logistic function. It can
be only used in half growing season, i.e. vegetation green-up or senescence
period.
doubleLog.Zhang
Piecewise logistics, (Zhang Xiaoyang, RSE, 2003).
doubleAG
Asymmetric Gaussian.
doubleLog.Beck
Beck logistics.
doubleLog.Gu
Gu logistics.
doubleLog.Elmore
Elmore logistics.
doubleLog.Klos
Klos logistics.
All of those function have par
and formula
attributes for the
convenience for analytical D1 and D2
Beck, P.S.A., Atzberger, C., Hogda, K.A., Johansen, B., Skidmore, A.K., 2006. Improved monitoring of vegetation dynamics at very high latitudes: A new method using MODIS NDVI. Remote Sens. Environ. https://doi.org/10.1016/j.rse.2005.10.021.
Elmore, A.J., Guinn, S.M., Minsley, B.J., Richardson, A.D., 2012.
Landscape controls on the timing of spring, autumn, and growing season
length in mid-Atlantic forests. Glob. Chang. Biol. 18, 656-674.
https://doi.org/10.1111/j.1365-2486.2011.02521.x.
Gu, L., Post, W.M., Baldocchi, D.D., Black, TRUE.A., Suyker, A.E., Verma,
S.B., Vesala, TRUE., Wofsy, S.C., 2009. Characterizing the Seasonal Dynamics
of Plant Community Photosynthesis Across a Range of Vegetation Types,
in: Noormets, A. (Ed.), Phenology of Ecosystem Processes: Applications
in Global Change Research. Springer New York, New York, NY, pp. 35-58.
https://doi.org/10.1007/978-1-4419-0026-5_2.
Peter M. Atkinson, et al., 2012, RSE, 123:400-417
https://github.com/cran/phenopix/blob/master/R/FitDoubleLogGu.R
# simulate vegetation time-series t <- seq(1, 365, 8) par <- c(mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) data <- data.frame(t, y) # methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") tout <- seq(1, 365, 1) r <- FitDL.Elmore(y, t, tout) plot(r, data) get_GOF(r, data) get_param(r)
# simulate vegetation time-series t <- seq(1, 365, 8) par <- c(mn = 0.1, mx = 0.7, sos = 50, rsp = 0.1, eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) data <- data.frame(t, y) # methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") tout <- seq(1, 365, 1) r <- FitDL.Elmore(y, t, tout) plot(r, data) get_GOF(r, data) get_param(r)
A data.table dataset, raw data of MOD13A1 data, clipped in 10 representative points ('DE-Obe', 'IT-Col', 'CN-Cha', 'AT-Neu', 'ZA-Kru', 'AU-How', 'CA-NS6', 'US-KS2', 'CH-Oe2', 'CZ-wet').
data('MOD13A1')
data('MOD13A1')
An object of class list
of length 2.
Variables in MOD13A1:
dt
: vegetation index data
system:index
: image index
DayOfYear
: Numeric, Julian day of year
DayOfYear
: corresponding doy of compositing NDVI and EVI
DetailedQA
: VI quality indicators
SummaryQA
: Quality reliability of VI pixel
EVI
: Enhanced Vegetation Index
NDVI
: Normalized Difference Vegetation Index
date
: Date, corresponding date
site
: String, site name
sur_refl_b01
: Red surface reflectance
sur_refl_b02
: NIR surface reflectance
sur_refl_b03
: Blue surface reflectance
sur_refl_b07
: MIR surface reflectance
.geo
: geometry
st
: station info
ID
: site ID
site
: site name
lat
: latitude
lon
: longitude
IGBPname
: IGBP land cover type
https://code.earthengine.google.com/dataset/MODIS/006/MOD13A1
NA and Inf values in the yy will be ignored automatically.
movmean(y, halfwin = 1L, SG_style = FALSE, w = NULL)
movmean(y, halfwin = 1L, SG_style = FALSE, w = NULL)
y |
A numeric vector. |
halfwin |
Integer, half of moving window size |
SG_style |
If true, head and tail values will be in the style of SG (more weights on the center point), else traditional moving mean style. |
w |
Corresponding weights of yy, same long as yy. |
x <- 1:100 x[50] <- NA; x[80] <- Inf s1 <- movmean(x, 2, SG_style = TRUE) s2 <- movmean(x, 2, SG_style = FALSE)
x <- 1:100 x[50] <- NA; x[80] <- Inf s1 <- movmean(x, 2, SG_style = TRUE) s2 <- movmean(x, 2, SG_style = FALSE)
I_optimx
is rich of functionality, but with a low computing
performance. Some basic optimization functions are unified here, with some
input and output format.
opt_ncminf
General-Purpose Unconstrained Non-Linear Optimization,
see ucminf::ucminf()
.
opt_nlminb
Optimization using PORT routines, see stats::nlminb()
.
opt_nlm
Non-Linear Minimization, stats::nlm()
.
opt_optim
General-purpose Optimization, see stats::optim()
.
opt_ucminf(par0, objective, ...) opt_nlm(par0, objective, ...) opt_optim(par0, objective, method = "BFGS", ...) opt_nlminb(par0, objective, ...)
opt_ucminf(par0, objective, ...) opt_nlm(par0, objective, ...) opt_optim(par0, objective, method = "BFGS", ...) opt_nlminb(par0, objective, ...)
par0 |
Initial values for the parameters to be optimized over. |
objective |
A function to be minimized (or maximized), with first argument the vector of parameters over which minimization is to take place. It should return a scalar result. |
... |
other parameters passed to |
method |
optimization method to be used in |
convcode
: An integer code. 0 indicates successful convergence.
Various methods may or may not return sufficient information to allow all
the codes to be specified. An incomplete list of codes includes
1
: indicates that the iteration limit maxit
had been reached.
20
: indicates that the initial set of parameters is inadmissible,
that is, that the function cannot be computed or returns an infinite,
NULL, or NA value.
21
: indicates that an intermediate set of parameters is inadmissible.
10
: indicates degeneracy of the Nelder–Mead simplex.
51
: indicates a warning from the "L-BFGS-B"
method; see component
message
for further details.
52
: indicates an error from the "L-BFGS-B"
method; see component
message
for further details.
9999
: error
value
: The value of fn corresponding to par
par
: The best parameter found
nitns
: the number of iterations
fevals
: The number of calls to objective
.
library(phenofit) library(ggplot2) library(magrittr) library(purrr) library(data.table) # simulate vegetation time-series fFUN = doubleLog_Beck par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) par0 = c( mn = 0.15, mx = 0.65, sos = 100, rsp = 0.12, eos = 200, rau = 0.12) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- fFUN(par, t) optFUNs <- c("opt_ucminf", "opt_nlminb", "opt_nlm", "opt_optim") %>% set_names(., .) opts <- lapply(optFUNs, function(optFUN){ optFUN <- get(optFUN) opt <- optFUN(par0, f_goal, y = y, t = t, fun = fFUN) opt$ysim <- fFUN(opt$par, t) opt }) # visualization df <- map(opts, "ysim") %>% as.data.table() %>% cbind(t, y, .) pdat <- data.table::melt(df, c("t", "y"), variable.name = "optFUN") ggplot(pdat) + geom_point(data = data.frame(t, y), aes(t, y), size = 2) + geom_line(aes(t, value, color = optFUN), linewidth = 0.9)
library(phenofit) library(ggplot2) library(magrittr) library(purrr) library(data.table) # simulate vegetation time-series fFUN = doubleLog_Beck par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) par0 = c( mn = 0.15, mx = 0.65, sos = 100, rsp = 0.12, eos = 200, rau = 0.12) t <- seq(1, 365, 8) tout <- seq(1, 365, 1) y <- fFUN(par, t) optFUNs <- c("opt_ucminf", "opt_nlminb", "opt_nlm", "opt_optim") %>% set_names(., .) opts <- lapply(optFUNs, function(optFUN){ optFUN <- get(optFUN) opt <- optFUN(par0, f_goal, y = y, t = t, fun = fFUN) opt$ysim <- fFUN(opt$par, t) opt }) # visualization df <- map(opts, "ysim") %>% as.data.table() %>% cbind(t, y, .) pdat <- data.table::melt(df, c("t", "y"), variable.name = "optFUN") ggplot(pdat) + geom_point(data = data.frame(t, y), aes(t, y), size = 2) + geom_line(aes(t, value, color = optFUN), linewidth = 0.9)
Interface of optimization functions for double logistics and other parametric curve fitting functions.
optim_pheno( prior, sFUN, y, t, tout, method, w, nptperyear, ylu, iters = 2, wFUN = wTSM, lower = -Inf, upper = Inf, constrain = TRUE, verbose = FALSE, ..., use.cpp = FALSE )
optim_pheno( prior, sFUN, y, t, tout, method, w, nptperyear, ylu, iters = 2, wFUN = wTSM, lower = -Inf, upper = Inf, constrain = TRUE, verbose = FALSE, ..., use.cpp = FALSE )
prior |
A vector of initial values for the parameters for which optimal
values are to be found. |
sFUN |
The name of fine curve fitting functions, can be one of |
y |
Numeric vector, vegetation index time-series |
t |
Numeric vector, |
tout |
Corresponding doy of prediction. |
method |
The name of optimization method to solve fine fitting, one of
|
w |
(optional) Numeric vector, weights of |
nptperyear |
Integer, number of images per year, passed to |
ylu |
|
iters |
How many times curve fitting is implemented. |
wFUN |
weights updating function, can be one of 'wTSM', 'wChen' and 'wBisquare'. |
lower , upper
|
vectors of lower and upper bounds, replicated to be as long as
|
constrain |
boolean, whether to use parameter constrain |
verbose |
Whether to display intermediate variables? |
... |
other parameters passed to |
use.cpp |
(unstable, not used) boolean, whether to use c++ defined fine
fitting function? If |
A fFIT()
object, with the element of:
tout
: The time of output curve fitting time-series.
zs
: Smoothed vegetation time-series of every iteration.
ws
: Weights of every iteration.
par
: Final optimized parameter of fine fitting.
fun
: The name of fine fitting.
# library(magrittr) # library(purrr) # simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) FUN = doubleLog_Beck par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) par0 = c( mn = 0.15, mx = 0.65, sos = 100, rsp = 0.12, eos = 200, rau = 0.12) y <- FUN(par, t) methods = c("BFGS", "ucminf", "nlm", "nlminb") opt1 <- I_optim(par0, doubleLog_Beck, y, t, methods) # "BFGS", "ucminf", "nlm", # opt2 <- I_optimx(prior, fFUN, y, t, tout, ) sFUN = "doubleLog.Beck" # doubleLog.Beck r <- optim_pheno(par0, sFUN, y, t, tout, method = methods[4], nptperyear = 46, iters = 2, wFUN = wTSM, verbose = FALSE, use.julia = FALSE)
# library(magrittr) # library(purrr) # simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) FUN = doubleLog_Beck par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) par0 = c( mn = 0.15, mx = 0.65, sos = 100, rsp = 0.12, eos = 200, rau = 0.12) y <- FUN(par, t) methods = c("BFGS", "ucminf", "nlm", "nlminb") opt1 <- I_optim(par0, doubleLog_Beck, y, t, methods) # "BFGS", "ucminf", "nlm", # opt2 <- I_optimx(prior, fFUN, y, t, tout, ) sFUN = "doubleLog.Beck" # doubleLog.Beck r <- optim_pheno(par0, sFUN, y, t, tout, method = methods[4], nptperyear = 46, iters = 2, wFUN = wTSM, verbose = FALSE, use.julia = FALSE)
Phenology extraction in Derivative method (DER)
PhenoDeriv(x, t, ...) ## S3 method for class 'fFIT' PhenoDeriv(x, t = NULL, analytical = FALSE, smoothed.spline = FALSE, ...) ## Default S3 method: PhenoDeriv(x, t, der1, IsPlot = TRUE, show.legend = TRUE, ...)
PhenoDeriv(x, t, ...) ## S3 method for class 'fFIT' PhenoDeriv(x, t = NULL, analytical = FALSE, smoothed.spline = FALSE, ...) ## Default S3 method: PhenoDeriv(x, t, der1, IsPlot = TRUE, show.legend = TRUE, ...)
x |
numeric vector, or |
t |
|
... |
Other parameters will be ignored. |
analytical |
If true, |
smoothed.spline |
Whether apply |
der1 |
the first order difference |
IsPlot |
whether to plot? |
show.legend |
whether show figure lelend? |
Filippa, G., Cremonese, E., Migliavacca, M., Galvagno, M., Forkel, M., Wingate, L., … Richardson, A. D. (2016). Phenopix: A R package for image-based vegetation phenology. Agricultural and Forest Meteorology, 220, 141–150. doi:10.1016/j.agrformet.2016.01.006
PhenoTrs()
, PhenoGu()
, PhenoKl()
Phenology extraction in GU method (GU)
PhenoGu(x, t, ...) ## S3 method for class 'fFIT' PhenoGu(x, t = NULL, analytical = FALSE, smoothed.spline = FALSE, ...) ## Default S3 method: PhenoGu(x, t, der1, IsPlot = TRUE, ...)
PhenoGu(x, t, ...) ## S3 method for class 'fFIT' PhenoGu(x, t = NULL, analytical = FALSE, smoothed.spline = FALSE, ...) ## Default S3 method: PhenoGu(x, t, der1, IsPlot = TRUE, ...)
x |
numeric vector, or |
t |
|
... |
other parameters to |
analytical |
If true, |
smoothed.spline |
Whether apply |
der1 |
the first order difference |
IsPlot |
whether to plot? |
A numeric vector, with the elements of:
UD
: upturn date
SD
: stabilisation date
DD
: downturn date
RD
: recession date
Gu, L., Post, W. M., Baldocchi, D. D., Black, T. A., Suyker, A. E., Verma, S. B., … Wofsy, S. C. (2009). Characterizing the Seasonal Dynamics of Plant Community Photosynthesis Across a Range of Vegetation Types. In A. Noormets (Ed.), Phenology of Ecosystem Processes: Applications in Global Change Research (pp. 35–58). New York, NY: Springer New York. doi:10.1007/978-1-4419-0026-5_2
Filippa, G., Cremonese, E., Migliavacca, M., Galvagno, M., Forkel, M., Wingate, L., … Richardson, A. D. (2016). Phenopix: A R package for image-based vegetation phenology. Agricultural and Forest Meteorology, 220, 141–150. doi:10.1016/j.agrformet.2016.01.006
# `doubleLog.Beck` simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) x <- fit$model$AG # one model par(mfrow = c(2, 2)) PhenoTrs(x) PhenoDeriv(x) PhenoGu(x) PhenoKl(x)
# `doubleLog.Beck` simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) x <- fit$model$AG # one model par(mfrow = c(2, 2)) PhenoTrs(x) PhenoDeriv(x) PhenoGu(x) PhenoKl(x)
Phenology extraction in Inflection method (Zhang)
PhenoKl( fFIT, t = NULL, analytical = FALSE, smoothed.spline = FALSE, IsPlot = TRUE, show.legend = TRUE, ... )
PhenoKl( fFIT, t = NULL, analytical = FALSE, smoothed.spline = FALSE, IsPlot = TRUE, show.legend = TRUE, ... )
fFIT |
object return by |
t |
|
analytical |
If true, |
smoothed.spline |
Whether apply |
IsPlot |
whether to plot? |
show.legend |
whether show figure lelend? |
... |
Other parameters will be ignored. |
A numeric vector, with the elements of:
Greenup
, Maturity
, Senescence
, Dormancy
.
Zhang, X., Friedl, M. A., Schaaf, C. B., Strahler, A. H., Hodges, J. C. F. F., Gao, F., … Huete, A. (2003). Monitoring vegetation phenology using MODIS. Remote Sensing of Environment, 84(3), 471–475. doi:10.1016/S0034-4257(02)00135-9
# `doubleLog.Beck` simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) x <- fit$model$AG # one model par(mfrow = c(2, 2)) PhenoTrs(x) PhenoDeriv(x) PhenoGu(x) PhenoKl(x)
# `doubleLog.Beck` simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) x <- fit$model$AG # one model par(mfrow = c(2, 2)) PhenoTrs(x) PhenoDeriv(x) PhenoGu(x) PhenoKl(x)
Phenology extraction in Threshold method (TRS)
PhenoTrs( x, t = NULL, approach = c("White", "Trs"), trs = 0.5, asymmetric = TRUE, IsPlot = TRUE, ... ) ## S3 method for class 'fFIT' PhenoTrs(x, t = NULL, ...) ## Default S3 method: PhenoTrs( x, t = NULL, approach = c("White", "Trs"), trs = 0.5, asymmetric = TRUE, IsPlot = TRUE, ... )
PhenoTrs( x, t = NULL, approach = c("White", "Trs"), trs = 0.5, asymmetric = TRUE, IsPlot = TRUE, ... ) ## S3 method for class 'fFIT' PhenoTrs(x, t = NULL, ...) ## Default S3 method: PhenoTrs( x, t = NULL, approach = c("White", "Trs"), trs = 0.5, asymmetric = TRUE, IsPlot = TRUE, ... )
x |
numeric vector, or |
t |
|
approach |
to be used to calculate phenology metrics. 'White' (White et al. 1997) or 'Trs' for simple threshold. |
trs |
threshold to be used for approach "Trs", in (0, 1). |
asymmetric |
If true, background value in spring season and autumn season is regarded as different. |
IsPlot |
whether to plot? |
... |
other parameters to PhenoPlot |
PhenoDeriv()
, PhenoGu()
, PhenoKl()
# `doubleLog.Beck` simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) x <- fit$model$AG # one model par(mfrow = c(2, 2)) PhenoTrs(x) PhenoDeriv(x) PhenoGu(x) PhenoKl(x)
# `doubleLog.Beck` simulate vegetation time-series t <- seq(1, 365, 8) tout <- seq(1, 365, 1) par = c( mn = 0.1 , mx = 0.7 , sos = 50 , rsp = 0.1 , eos = 250, rau = 0.1) y <- doubleLog.Beck(par, t) methods <- c("AG", "Beck", "Elmore", "Gu", "Zhang") # "Klos" too slow fit <- curvefit(y, t, tout, methods) x <- fit$model$AG # one model par(mfrow = c(2, 2)) PhenoTrs(x) PhenoDeriv(x) PhenoGu(x) PhenoKl(x)
plot_curvefits
plot_curvefits( d_fit, seasons, d_obs = NULL, title = NULL, xlab = "Time", ylab = "Vegetation Index", yticks = NULL, font.size = 14, theme = NULL, cex = 2, shape = "point", angle = 30, show.legend = TRUE, layer_extra = NULL, ... )
plot_curvefits( d_fit, seasons, d_obs = NULL, title = NULL, xlab = "Time", ylab = "Vegetation Index", yticks = NULL, font.size = 14, theme = NULL, cex = 2, shape = "point", angle = 30, show.legend = TRUE, layer_extra = NULL, ... )
d_fit |
data.frame of curve fittings returned by |
seasons |
growing season division object returned by |
d_obs |
data.frame of original vegetation time series, with the columns
of |
title |
String, title of figure. |
xlab , ylab
|
String, title of |
yticks |
ticks of y axis |
font.size |
Font size of axis.text |
theme |
ggplot theme |
cex |
point size for VI observation. |
shape |
the shape of input VI observation? |
angle |
|
show.legend |
Boolean |
layer_extra |
(not used) extra ggplot layers |
... |
ignored |
data("CA_NS6") d = CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) # plot_input(INPUT) # Rough fitting and growing season dividing wFUN <- "wTSM" brks2 <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = wFUN, r_min = 0.05, ypeak_min = 0.05, lambda = 10, verbose = FALSE )) # plot_season(INPUT, brks2, d) # Fine fitting fits <- curvefits( INPUT, brks2, options = list( methods = c("AG", "Beck", "Elmore", "Zhang"), #,"klos", "Gu" wFUN = wFUN, nextend = 2, maxExtendMonth = 2, minExtendMonth = 1, minPercValid = 0.2 ) ) r_param = get_param(fits) r_pheno = get_pheno(fits) r_gof = get_GOF(fits) d_fit = get_fitting(fits) g <- plot_curvefits(d_fit, brks2) grid::grid.newpage(); grid::grid.draw(g)
data("CA_NS6") d = CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) # plot_input(INPUT) # Rough fitting and growing season dividing wFUN <- "wTSM" brks2 <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = wFUN, r_min = 0.05, ypeak_min = 0.05, lambda = 10, verbose = FALSE )) # plot_season(INPUT, brks2, d) # Fine fitting fits <- curvefits( INPUT, brks2, options = list( methods = c("AG", "Beck", "Elmore", "Zhang"), #,"klos", "Gu" wFUN = wFUN, nextend = 2, maxExtendMonth = 2, minExtendMonth = 1, minPercValid = 0.2 ) ) r_param = get_param(fits) r_pheno = get_pheno(fits) r_gof = get_GOF(fits) d_fit = get_fitting(fits) g <- plot_curvefits(d_fit, brks2) grid::grid.newpage(); grid::grid.draw(g)
Plot INPUT returned by check_input
plot_input(INPUT, wmin = 0.2, show.y0 = TRUE, ylab = "VI", ...)
plot_input(INPUT, wmin = 0.2, show.y0 = TRUE, ylab = "VI", ...)
INPUT |
A list object with the elements of |
wmin |
double, minimum weigth (i.e. weight of snow, ice and cloud). |
show.y0 |
boolean. Whether to show original time-series |
ylab |
y axis title |
... |
other parameter will be ignored. |
library(phenofit) data("CA_NS6"); d = CA_NS6 # global parameter IsPlot = TRUE nptperyear = 23 ypeak_min = 0.05 INPUT <- check_input(d$t, d$y, d$w, d$QC_flag, nptperyear, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) plot_input(INPUT)
library(phenofit) data("CA_NS6"); d = CA_NS6 # global parameter IsPlot = TRUE nptperyear = 23 ypeak_min = 0.05 INPUT <- check_input(d$t, d$y, d$w, d$QC_flag, nptperyear, maxgap = nptperyear/4, alpha = 0.02, wmin = 0.2) plot_input(INPUT)
Plot growing season divding result.
plot_season( INPUT, brks, plotdat, IsPlot.OnlyBad = FALSE, show.legend = TRUE, ylab = "VI", title = NULL, show.shade = TRUE, margin = 0.35 )
plot_season( INPUT, brks, plotdat, IsPlot.OnlyBad = FALSE, show.legend = TRUE, ylab = "VI", title = NULL, show.shade = TRUE, margin = 0.35 )
INPUT |
A list object with the elements of |
brks |
A list object returned by |
plotdat |
(optional) A list or data.table, with |
IsPlot.OnlyBad |
If true, only plot partial figures whose NSE < 0.3. |
show.legend |
Whether to show legend? |
ylab |
y axis title |
title |
The main title (on top) |
show.shade |
Boolean, period inside growing cycle colored as shade? |
margin |
|
SCL Value | Description | Quality | weight |
1 | Saturated or defective | Bad | |
2 | Dark Area Pixels | Bad | |
3 | Cloud Shadows | Bad | |
4 | Vegetation | Good | |
5 | Bare Soils | Good | |
6 | Water | Good | |
7 | Clouds Low Probability / Unclassified | Good | |
8 | Clouds Medium Probability | Marginal | |
9 | Clouds High Probability | Bad | |
10 | Cirrus | Good | |
11 | Snow / Ice | Bad | |
qc_sentinel2(SCL, wmin = 0.2, wmid = 0.5, wmax = 1)
qc_sentinel2(SCL, wmin = 0.2, wmid = 0.5, wmax = 1)
SCL |
quality control variable for sentinel2 |
wmin |
Double, minimum weigth (i.e. weight of snow, ice and cloud). |
wmid |
Dougle, middle weight, i.e. marginal |
wmax |
Double, maximum weight, i.e. good |
https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2_SR
qc_sentinel2(1:11)
qc_sentinel2(1:11)
getBits
: Extract bitcoded QA information from bin value
qc_summary
: Initial weigths based on Quality reliability of VI pixel,
suit for MOD13A1, MOD13A2 and MOD13Q1 (SummaryQA band).
qc_5l
: Initial weights based on Quality control of five-level
confidence score, suit for MCD15A3H(LAI, FparLai_QC), MOD17A2H(GPP, Psn_QC)
and MOD16A2(ET, ET_QC).
qc_StateQA
: Initial weights based on StateQA
, suit for MOD09A1, MYD09A1.
qc_FparLai
: For MODIS LAI
qc_NDVI3g
: For AVHRR NDVI3g
qc_NDVIv4
: For AVHRR NDVIv4
getBits(x, start, end = start) qc_summary(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_StateQA(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_FparLai(QA, FparLai_QC = NULL, wmin = 0.2, wmid = 0.5, wmax = 1) qc_5l(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_NDVIv4(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_NDVI3g(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_SPOT(QA, wmin = 0.2, wmid = 0.5, wmax = 1)
getBits(x, start, end = start) qc_summary(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_StateQA(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_FparLai(QA, FparLai_QC = NULL, wmin = 0.2, wmid = 0.5, wmax = 1) qc_5l(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_NDVIv4(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_NDVI3g(QA, wmin = 0.2, wmid = 0.5, wmax = 1) qc_SPOT(QA, wmin = 0.2, wmid = 0.5, wmax = 1)
x |
Binary value |
start |
Bit starting position, count from zero |
end |
Bit ending position |
QA |
quality control variable |
wmin |
Double, minimum weigth (i.e. weight of snow, ice and cloud). |
wmid |
Dougle, middle weight, i.e. marginal |
wmax |
Double, maximum weight, i.e. good |
FparLai_QC |
Another QC flag of |
If FparLai_QC
specified, I_margin = SCF_QC >= 2 & SCF_QC <= 3
.
A list object with
weigths
: Double vector, initial weights.
QC_flag
: Factor vector, with the level of
c("snow", "cloud", "shadow", "aerosol", "marginal", "good")
qc_5l
and qc_NDVIv4
only returns weight
, without QC_flag
.
https://developers.google.com/earth-engine/datasets/catalog/MODIS_006_MOD13A1
https://developers.google.com/earth-engine/datasets/catalog/MODIS_006_MCD15A3H
Erwin Wolters, Else Swinnen, Carolien Toté, Sindy Sterckx. SPOT-VGT COLLECTION 3 PRODUCTS USER MANUAL V1.2, 2018, P47
set.seed(100) QA <- as.integer(runif(100, 0, 2^7)) r1 <- qc_summary(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r2 <- qc_StateQA(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r_5l <- qc_5l(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r_NDVI3g <- qc_NDVI3g(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r_NDVIv4 <- qc_NDVIv4(QA, wmin = 0.2, wmid = 0.5, wmax = 1)
set.seed(100) QA <- as.integer(runif(100, 0, 2^7)) r1 <- qc_summary(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r2 <- qc_StateQA(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r_5l <- qc_5l(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r_NDVI3g <- qc_NDVI3g(QA, wmin = 0.2, wmid = 0.5, wmax = 1) r_NDVIv4 <- qc_NDVIv4(QA, wmin = 0.2, wmid = 0.5, wmax = 1)
NA and Inf values in the yy has been ignored automatically.
rcpp_wSG(y, halfwin = 1L, d = 1L, w = NULL) rcpp_SG(y, halfwin = 1L, d = 1L)
rcpp_wSG(y, halfwin = 1L, d = 1L, w = NULL) rcpp_SG(y, halfwin = 1L, d = 1L)
y |
colvec |
halfwin |
halfwin of Savitzky-Golay |
d |
polynomial of degree. When d = 1, it becomes moving average. |
w |
colvec of weight |
y <- 1:15 w <- seq_along(y)/length(y) frame = 5 d = 2 s1 <- rcpp_wSG(y, frame, d, w) s2 <- rcpp_SG(y, frame, d)
y <- 1:15 w <- seq_along(y)/length(y) frame = 5 d = 2 s1 <- rcpp_wSG(y, frame, d, w) s2 <- rcpp_SG(y, frame, d)
Moving growing season division
season_mov(INPUT, options = list(), ..., years.run = NULL)
season_mov(INPUT, options = list(), ..., years.run = NULL)
INPUT |
A list object with the elements of |
options |
see the following section |
... |
others parameter to |
years.run |
Numeric vector. Which years to run? If not specified, it is all years. |
rFUN
: character (default smooth_wWHIT
), the name of rough
curve fitting function, can be one of c("smooth_wSG", "smooth_wWHIT", "smooth_wHANTS")
, which are corresponding to smooth_wSG()
,
smooth_wWHIT()
and smooth_wHANTS()
.
wFUN
: character (default wTSM
), the name of weights
updating functions, can be one of c("wTSM", "wChen", "wBisquare",
"wSELF"). See wTSM()
, wChen()
, wBisquare()
and wSELF()
for
details.
iters
: integer (default 2), the number of rough fitting
iterations.
wmin
: double, the minimum weight of bad points (i.e. snow,
ice and cloud).
verbose
: logical (default FALSE
). If TRUE
,
options$season
will be printed on the console.
lambda
: double (default NULL), the smoothing parameter of
smooth_wWHIT()
.
If lambda = NULL
, V-curve theory will be employed to find the
optimal lambda
. See lambda_vcurve()
for details.
frame
: integer (default NULL), the parameter of
smooth_wSG()
, moving window size.
If frame = NULL
, frame
will be reset as floor(nptperyear/5)*2 + 1
(refered by TIMESAT).
nf
: integer (default 4), the number of frequencies in
smooth_wHANTS()
.
maxExtendMonth
: integer (default 12), previous and subsequent
maxExtendMonth
(in month) data were added to the current year for rough
fitting.
nextend
: integer (default NULL), same as maxExtendMonth
, but
in points.
If nextend
provided, maxExtendMonth
will be ignored.
If nextend = NULL
, nextend
will be reset as
ceiling(maxExtendMonth/12*nptperyear)
minpeakdistance
: double (default NULL), the minimum distance of two
peaks (in points). If the distance of two maximum extreme value less than
minpeakdistance
, only the maximum one will be kept.
If minpeakdistance = NULL
, it will be reset as nptperyear/6
.
r_max
: double (default 0.2; in (0, 1)). r_max
and r_min
are used to eliminate fake peaks and troughs.
The real peaks should satisfy:
where
are height difference from the peak to the left- and
right-hand troughs.
The troughs should satisfy:
where
are height difference from the trough
to the left- and right-hand peaks.
r_min
: double (default 0.05; in (0, 1)), see above r_max
for details. r_min
< r_max
.
rtrough_max
: double (default 0.6, in (0, 1)), .
ypeak_min
: double 0.1 (in VI unit), .
.check_season
: logical (default TRUE
). check the growing season
length according to len_min
and len_max
. If FALSE
, len_min
and
len_max
will lose their effect.
len_min
: integer (default 45), the minimum length (in days) of
growing season
len_max
: integer (default 650), the minimum length (in days)
of growing season
adj.param
: logical. If TRUE
(default), if there are too many
or too less peaks and troughs, phenofit
will automatically adjust rough
curve fitting function parameters. See MaxPeaksPerYear
and
MaxTroughsPerYear
for details.
MaxPeaksPerYear
(optional) : integer (default 2), the max number of
peaks per year. If PeaksPerYear
> MaxPeaksPerYear
, then lambda = lambda*2
.
MaxTroughsPerYear
(optional) : integer (default 3), the max number of
troughs per year. If TroughsPerYear
> MaxTroughsPerYear
, then lambda = lambda*2
.
calendarYear
: logical (default FALSE
). If TRUE
, the start and
end of a calendar year will be regarded as growing season division (North
Hemisphere is from 01 Jan to 31 Dec; South Hemisphere is from 01 Jul to 30
Jun).
rm.closed
: logical (default TRUE
). If TRUE
, closed peaks (or troughs)
will be further tidied. Only the maximum
is.continuous
(not used): logical (default TRUE
). This parameter is for
fluxnet2015
fluxsite data, where the input might be not continuous.
Kong, D., Zhang, Y., Wang, D., Chen, J., & Gu, X. (2020). Photoperiod Explains the Asynchronization Between Vegetation Carbon Phenology and Vegetation Greenness Phenology. Journal of Geophysical Research: Biogeosciences, 125(8), e2020JG005636. https://doi.org/10.1029/2020JG005636
Kong, D., Zhang, Y., Gu, X., & Wang, D. (2019). A robust method for reconstructing global MODIS EVI time series on the Google Earth Engine. ISPRS Journal of Photogrammetry and Remote Sensing, 155, 13-24.
data("CA_NS6") d <- CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear / 4, alpha = 0.02, wmin = 0.2 ) # curve fitting by year brks_mov <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = "wTSM", lambda = 10, r_min = 0.05, ypeak_min = 0.05, verbose = TRUE ) ) plot_season(INPUT, brks_mov) rfit <- brks2rfit(brks_mov) # Phenological Metrics from rough fitting r <- get_pheno(rfit)
data("CA_NS6") d <- CA_NS6 nptperyear <- 23 INPUT <- check_input(d$t, d$y, d$w, QC_flag = d$QC_flag, nptperyear = nptperyear, south = FALSE, maxgap = nptperyear / 4, alpha = 0.02, wmin = 0.2 ) # curve fitting by year brks_mov <- season_mov(INPUT, options = list( rFUN = "smooth_wWHIT", wFUN = "wTSM", lambda = 10, r_min = 0.05, ypeak_min = 0.05, verbose = TRUE ) ) plot_season(INPUT, brks_mov) rfit <- brks2rfit(brks_mov) # Phenological Metrics from rough fitting r <- get_pheno(rfit)
set and get phenofit option
set_options(..., options = NULL) get_options(names = NULL)
set_options(..., options = NULL) get_options(names = NULL)
... |
list of phenofit options FUN_season: character, |
options |
If not NULL,
|
names |
vector of character, names of options |
rFUN
: character (default smooth_wWHIT
), the name of rough
curve fitting function, can be one of c("smooth_wSG", "smooth_wWHIT", "smooth_wHANTS")
, which are corresponding to smooth_wSG()
,
smooth_wWHIT()
and smooth_wHANTS()
.
wFUN
: character (default wTSM
), the name of weights
updating functions, can be one of c("wTSM", "wChen", "wBisquare",
"wSELF"). See wTSM()
, wChen()
, wBisquare()
and wSELF()
for
details.
iters
: integer (default 2), the number of rough fitting
iterations.
wmin
: double, the minimum weight of bad points (i.e. snow,
ice and cloud).
verbose
: logical (default FALSE
). If TRUE
,
options$season
will be printed on the console.
lambda
: double (default NULL), the smoothing parameter of
smooth_wWHIT()
.
If lambda = NULL
, V-curve theory will be employed to find the
optimal lambda
. See lambda_vcurve()
for details.
frame
: integer (default NULL), the parameter of
smooth_wSG()
, moving window size.
If frame = NULL
, frame
will be reset as floor(nptperyear/5)*2 + 1
(refered by TIMESAT).
nf
: integer (default 4), the number of frequencies in
smooth_wHANTS()
.
maxExtendMonth
: integer (default 12), previous and subsequent
maxExtendMonth
(in month) data were added to the current year for rough
fitting.
nextend
: integer (default NULL), same as maxExtendMonth
, but
in points.
If nextend
provided, maxExtendMonth
will be ignored.
If nextend = NULL
, nextend
will be reset as
ceiling(maxExtendMonth/12*nptperyear)
minpeakdistance
: double (default NULL), the minimum distance of two
peaks (in points). If the distance of two maximum extreme value less than
minpeakdistance
, only the maximum one will be kept.
If minpeakdistance = NULL
, it will be reset as nptperyear/6
.
r_max
: double (default 0.2; in (0, 1)). r_max
and r_min
are used to eliminate fake peaks and troughs.
The real peaks should satisfy:
where
are height difference from the peak to the left- and
right-hand troughs.
The troughs should satisfy:
where
are height difference from the trough
to the left- and right-hand peaks.
r_min
: double (default 0.05; in (0, 1)), see above r_max
for details. r_min
< r_max
.
rtrough_max
: double (default 0.6, in (0, 1)), .
ypeak_min
: double 0.1 (in VI unit), .
.check_season
: logical (default TRUE
). check the growing season
length according to len_min
and len_max
. If FALSE
, len_min
and
len_max
will lose their effect.
len_min
: integer (default 45), the minimum length (in days) of
growing season
len_max
: integer (default 650), the minimum length (in days)
of growing season
adj.param
: logical. If TRUE
(default), if there are too many
or too less peaks and troughs, phenofit
will automatically adjust rough
curve fitting function parameters. See MaxPeaksPerYear
and
MaxTroughsPerYear
for details.
MaxPeaksPerYear
(optional) : integer (default 2), the max number of
peaks per year. If PeaksPerYear
> MaxPeaksPerYear
, then lambda = lambda*2
.
MaxTroughsPerYear
(optional) : integer (default 3), the max number of
troughs per year. If TroughsPerYear
> MaxTroughsPerYear
, then lambda = lambda*2
.
calendarYear
: logical (default FALSE
). If TRUE
, the start and
end of a calendar year will be regarded as growing season division (North
Hemisphere is from 01 Jan to 31 Dec; South Hemisphere is from 01 Jul to 30
Jun).
rm.closed
: logical (default TRUE
). If TRUE
, closed peaks (or troughs)
will be further tidied. Only the maximum
is.continuous
(not used): logical (default TRUE
). This parameter is for
fluxnet2015
fluxsite data, where the input might be not continuous.
methods
(default c('AG', 'Beck', 'Elmore', 'Zhang')``): Fine curve fitting methods, can be one or more of
c('AG', 'Beck', 'Elmore', 'Zhang',
'Gu', 'Klos')‘. Note that ’Gu' and 'Klos' are very slow.
iters
(default 2): max iterations of fine fitting.
wFUN
(default wTSM
): Character or function, weights updating function
of fine fitting function.
wmin
(default 0.1): min weights in the weights updating procedure.
use.rough
(default FALSE): Whether to use rough fitting smoothed
time-series as input? If false
, smoothed VI by rough fitting will be used
for Phenological metrics extraction; If true
, original input y
will be
used (rough fitting is used to divide growing seasons and update weights.
use.y0
(default TRUE): boolean. whether to use original y0
as the input
of plot_input
, note that not for curve fitting. y0
is the original
value before the process of check_input
.
nextend
(default 2): Extend curve fitting window, until nextend
good or
marginal points are found in the previous and subsequent growing season.
maxExtendMonth
(default 1): Search good or marginal good values in
previous and subsequent maxExtendMonth
period.
minExtendMonth
(default 0.5): Extend period defined by nextend
and
maxExtendMonth
, should be no shorter than minExtendMonth
. When all
points of the input time-series are good value, then the extending period
will be too short. In that situation, we can't make sure the connection
between different growing seasons is smoothing.
minPercValid
: (default 0, not use). If the percentage of good- and
marginal- quality points is less than minPercValid
, curve fiting result is
set to NA
.
minT
: (not use). If Tn
not provided in INPUT
, minT
will
not be used. minT
use night temperature Tn to define backgroud value
(days with Tn < minT
treated as ungrowing season).
set_options(verbose = FALSE) get_options("season") %>% str()
set_options(verbose = FALSE) get_options("season") %>% str()
Weighted HANTS smoother
smooth_wHANTS( y, t, w, nf = 3, ylu, periodlen = 365, nptperyear, wFUN = wTSM, iters = 2, wmin = 0.1, ... )
smooth_wHANTS( y, t, w, nf = 3, ylu, periodlen = 365, nptperyear, wFUN = wTSM, iters = 2, wmin = 0.1, ... )
y |
Numeric vector, vegetation index time-series |
t |
Numeric vector, |
w |
(optional) Numeric vector, weights of |
nf |
number of frequencies to be considered above the zero frequency |
ylu |
|
periodlen |
length of the base period, measured in virtual samples (days, dekads, months, etc.). nptperyear in timesat. |
nptperyear |
Integer, number of images per year. |
wFUN |
weights updating function, can be one of 'wTSM', 'wChen' and 'wBisquare'. |
iters |
How many times curve fitting is implemented. |
wmin |
Double, minimum weigth (i.e. weight of snow, ice and cloud). |
... |
Additional parameters are passed to |
ws
: weights of every iteration
zs
: curve fittings of every iteration
Wout Verhoef, NLR, Remote Sensing Dept. June 1998 Mohammad Abouali (2011), Converted to MATLAB Dongdong Kong (2018), introduced to R and modified into weighted model.
library(phenofit) data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) d <- dt[site == "AT-Neu", ] l <- check_input(d$t, d$y, d$w, nptperyear=23) r_wHANTS <- smooth_wHANTS(l$y, l$t, l$w, ylu = l$ylu, nptperyear = 23, iters = 2)
library(phenofit) data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) d <- dt[site == "AT-Neu", ] l <- check_input(d$t, d$y, d$w, nptperyear=23) r_wHANTS <- smooth_wHANTS(l$y, l$t, l$w, ylu = l$ylu, nptperyear = 23, iters = 2)
Weighted Savitzky-Golay
smooth_wSG( y, w, nptperyear, ylu, wFUN = wTSM, iters = 2, frame = floor(nptperyear/5) * 2 + 1, d = 2, ... )
smooth_wSG( y, w, nptperyear, ylu, wFUN = wTSM, iters = 2, frame = floor(nptperyear/5) * 2 + 1, d = 2, ... )
y |
Numeric vector, vegetation index time-series |
w |
(optional) Numeric vector, weights of |
nptperyear |
Integer, number of images per year. |
ylu |
(optional) |
wFUN |
weights updating function, can be one of 'wTSM', 'wChen' and 'wBisquare'. |
iters |
How many times curve fitting is implemented. |
frame |
Savitzky-Golay windows size |
d |
polynomial of degree. When d = 1, it becomes moving average. |
... |
Additional parameters are passed to |
ws
: weights of every iteration
zs
: curve fittings of every iteration
Chen, J., J\"onsson, P., Tamura, M., Gu, Z., Matsushita, B., Eklundh, L.,
2004. A simple method for reconstructing a high-quality NDVI time-series
data set based on the Savitzky-Golay filter. Remote Sens. Environ. 91,
332-344. https://doi.org/10.1016/j.rse.2004.03.014.
https://en.wikipedia.org/wiki/Savitzky%E2%80%93Golay_filter
library(phenofit) data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) d <- dt[site == "AT-Neu", ] l <- check_input(d$t, d$y, d$w, nptperyear=23) r_wSG <- smooth_wSG(l$y, l$w, l$ylu, nptperyear = 23, iters = 2)
library(phenofit) data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) d <- dt[site == "AT-Neu", ] l <- check_input(d$t, d$y, d$w, nptperyear=23) r_wSG <- smooth_wSG(l$y, l$w, l$ylu, nptperyear = 23, iters = 2)
Weigthed Whittaker Smoother
smooth_wWHIT( y, w, ylu, nptperyear, wFUN = wTSM, iters = 1, lambda = 15, second = FALSE, ... )
smooth_wWHIT( y, w, ylu, nptperyear, wFUN = wTSM, iters = 1, lambda = 15, second = FALSE, ... )
y |
Numeric vector, vegetation index time-series |
w |
(optional) Numeric vector, weights of |
ylu |
|
nptperyear |
Integer, number of images per year. |
wFUN |
weights updating function, can be one of 'wTSM', 'wChen' and 'wBisquare'. |
iters |
How many times curve fitting is implemented. |
lambda |
scaler or numeric vector, whittaker parameter.
|
second |
If true, in every iteration, Whittaker will be implemented twice to make sure curve fitting is smooth. If curve has been smoothed enough, it will not care about the second smooth. If no, the second one is just prepared for this situation. If lambda value has been optimized, second smoothing is unnecessary. |
... |
Additional parameters are passed to |
ws
: weights of every iteration
zs
: curve fittings of every iteration
Whittaker smoother of the second order difference is used!
Eilers, P.H.C., 2003. A perfect smoother. Anal. Chem. doi:10.1021/ac034173t
Frasso, G., Eilers, P.H.C., 2015. L- and V-curves for optimal smoothing. Stat. Modelling 15, 91-111. doi:10.1177/1471082X14549288.
data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) d <- dt[site == "AT-Neu", ] l <- check_input(d$t, d$y, d$w, nptperyear=23) r_wWHIT <- smooth_wWHIT(l$y, l$w, l$ylu, nptperyear = 23, iters = 2) ## Optimize `lambda` by V-curve theory # (a) optimize manually lambda_vcurve(l$y, l$w, plot = TRUE) # (b) optimize automatically by setting `lambda = NULL` in smooth_wWHIT r_wWHIT2 <- smooth_wWHIT(l$y, l$w, l$ylu, nptperyear = 23, iters = 2, lambda = NULL) #
data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) d <- dt[site == "AT-Neu", ] l <- check_input(d$t, d$y, d$w, nptperyear=23) r_wWHIT <- smooth_wWHIT(l$y, l$w, l$ylu, nptperyear = 23, iters = 2) ## Optimize `lambda` by V-curve theory # (a) optimize manually lambda_vcurve(l$y, l$w, plot = TRUE) # (b) optimize automatically by setting `lambda = NULL` in smooth_wWHIT r_wWHIT2 <- smooth_wWHIT(l$y, l$w, l$ylu, nptperyear = 23, iters = 2, lambda = NULL) #
This function smoothes signals with a finite difference penalty of order 2.
This function is modified from ptw
package.
whit2(y, lambda, w = rep(1, ny))
whit2(y, lambda, w = rep(1, ny))
y |
signal to be smoothed: a vector |
lambda |
smoothing parameter: larger values lead to more smoothing |
w |
weights: a vector of same length as y. Default weights are equal to one |
A numeric vector, smoothed signal.
Paul Eilers, Jan Gerretzen
Eilers, P.H.C. (2004) "Parametric Time Warping", Analytical Chemistry,
76 (2), 404 – 411.
Eilers, P.H.C. (2003) "A perfect smoother", Analytical Chemistry, 75, 3631 – 3636.
library(phenofit) data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) y <- dt[site == "AT-Neu", ][1:120, y] plot(y, type = "b") lines(whit2(y, lambda = 2), col = 2) lines(whit2(y, lambda = 10), col = 3) lines(whit2(y, lambda = 100), col = 4) legend("bottomleft", paste("lambda = ", c(2, 10, 15)), col = 2:4, lty = rep(1, 3))
library(phenofit) data("MOD13A1") dt <- tidy_MOD13(MOD13A1$dt) y <- dt[site == "AT-Neu", ][1:120, y] plot(y, type = "b") lines(whit2(y, lambda = 2), col = 2) lines(whit2(y, lambda = 10), col = 3) lines(whit2(y, lambda = 100), col = 4) legend("bottomleft", paste("lambda = ", c(2, 10, 15)), col = 2:4, lty = rep(1, 3))
wSELF
weigth are not changed and return the original.
wTSM
weight updating method in TIMESAT.
wBisquare
Bisquare weight update method. wBisquare has been
modified to emphasis on upper envelope.
wBisquare0
Traditional Bisquare weight update method.
wChen
Chen et al., (2004) weight updating method.
wBeck
Beck et al., (2006) weigth updating method. wBeck need
sos and eos input. The function parameter is different from others. It is
still not finished.
wSELF(y, yfit, w, ...) wTSM(y, yfit, w, iter = 2, nptperyear, wfact = 0.5, ...) wBisquare0(y, yfit, w, ..., wmin = 0.2) wBisquare(y, yfit, w, ..., wmin = 0.2, .toUpper = TRUE) wChen(y, yfit, w, ..., wmin = 0.2) wKong(y, yfit, w, ..., wmin = 0.2)
wSELF(y, yfit, w, ...) wTSM(y, yfit, w, iter = 2, nptperyear, wfact = 0.5, ...) wBisquare0(y, yfit, w, ..., wmin = 0.2) wBisquare(y, yfit, w, ..., wmin = 0.2, .toUpper = TRUE) wChen(y, yfit, w, ..., wmin = 0.2) wKong(y, yfit, w, ..., wmin = 0.2)
y |
Numeric vector, vegetation index time-series |
yfit |
Numeric vector curve fitting values. |
w |
(optional) Numeric vector, weights of |
... |
other parameters are ignored. |
iter |
iteration of curve fitting. |
nptperyear |
Integer, number of images per year. |
wfact |
weight adaptation factor (0-1), equal to the reciprocal of 'Adaptation strength' in TIMESAT. |
wmin |
Double, minimum weight of bad points, which could be smaller the weight of snow, ice and cloud. |
.toUpper |
Boolean. Whether to approach the upper envelope? |
wnew Numeric Vector, adjusted weights.
wTSM is implemented by Per J\"onsson, Malm\"o University, Sweden [email protected] and Lars Eklundh, Lund University, Sweden [email protected]. And Translated into Rcpp by Dongdong Kong, 01 May 2018.
Per J\"onsson, P., Eklundh, L., 2004. TIMESAT - A program for analyzing
time-series of satellite sensor data. Comput. Geosci. 30, 833-845.
https://doi.org/10.1016/j.cageo.2004.05.006.
https://au.mathworks.com/help/curvefit/smoothing-data.html#bq_6ys3-3
Garcia, D., 2010. Robust smoothing of gridded data in one and higher
dimensions with missing values. Computational statistics & data analysis,
54(4), pp.1167-1178.
Chen, J., J\"onsson, P., Tamura, M., Gu, Z., Matsushita, B., Eklundh, L.,
2004. A simple method for reconstructing a high-quality NDVI time-series
data set based on the Savitzky-Golay filter. Remote Sens. Environ. 91,
332-344. https://doi.org/10.1016/j.rse.2004.03.014.
Beck, P.S.A., Atzberger, C., Hogda, K.A., Johansen, B., Skidmore, A.K.,
2006. Improved monitoring of vegetation dynamics at very high latitudes:
A new method using MODIS NDVI. Remote Sens. Environ.
https://doi.org/10.1016/j.rse.2005.10.021
https://github.com/kongdd/phenopix/blob/master/R/FitDoubleLogBeck.R