Commit 72bd9a1c authored by Delaigue Olivier's avatar Delaigue Olivier
Browse files

Merge branch 'newSeriesAggreg' into 'dev'

New series aggreg

Closes #41

See merge request !12
parents 8f92b190 6a69f2fe
Pipeline #18856 passed with stages
in 13 minutes and 6 seconds
......@@ -5,3 +5,6 @@
^tests/tmp/
^\.gitlab-ci.yml$
^\.regressionignore$
^\.gitlab-ci\.yml$
^\.vscode$
^Rplots\.pdf$
......@@ -3,9 +3,28 @@
# The format of this file is: 5 lines of comments followed by one line by
# ignored variable : [Topic]<SPACE>[Variable].
# Example for ignoring OutputsModel variable produced by example("RunModel_GR2M"): RunModel_GR2M OutputsModel
RunModel_GR1A BasinObs
RunModel_GR1A ConvertFun
RunModel_GR1A NewTabSeries
RunModel_GR1A NewTimeFormat
RunModel_GR1A OutputsModel
RunModel_GR1A TabSeries
RunModel_GR1A TimeFormat
RunModel_GR1A YearFirstMonth
RunModel_GR2M BasinObs
RunModel_GR2M ConvertFun
RunModel_GR2M NewTabSeries
RunModel_GR2M NewTimeFormat
RunModel_GR2M OutputsModel
RunModel_GR2M RunOptions
RunModel_GR1A OutputsModel
Calibration_Michel CalibOptions
Calibration CalibOptions
CreateCalibOptions CalibOptions
# New version of the SeriesAggreg function
RunModel_GR2M TabSeries
RunModel_GR2M TimeFormat
SeriesAggreg BasinInfo
SeriesAggreg BasinObs
SeriesAggreg NewTabSeries
Package: airGR
Type: Package
Title: Suite of GR Hydrological Models for Precipitation-Runoff Modelling
Version: 1.6.3.78
Version: 1.6.8.35
Date: 2021-01-05
Authors@R: c(
person("Laurent", "Coron", role = c("aut", "trl"), comment = c(ORCID = "0000-0002-1503-6204")),
......@@ -20,7 +20,7 @@ Authors@R: c(
person("Raji", "Pushpalatha", role = c("ctb")),
person("Audrey", "Valéry", role = c("ctb"))
)
Depends: R (>= 3.0.1)
Depends: R (>= 3.1.0)
Imports:
graphics,
grDevices,
......
......@@ -8,7 +8,11 @@ useDynLib(airGR, .registration = TRUE)
#####################################
## S3 methods ##
#####################################
S3method("plot", "OutputsModel")
S3method(plot, OutputsModel)
S3method(SeriesAggreg, data.frame)
S3method(SeriesAggreg, list)
S3method(SeriesAggreg, InputsModel)
S3method(SeriesAggreg, OutputsModel)
......@@ -58,11 +62,9 @@ export(TransfoParam_GR4J)
export(TransfoParam_GR5J)
export(TransfoParam_GR6J)
export(TransfoParam_Lag)
export(plot.OutputsModel)
export(.ErrorCrit)
#####################################
## Import ##
#####################################
......
......@@ -2,6 +2,19 @@
### 1.6.8.34 Release Notes (2021-01-05)
#### New features
- Added <code>SeriesAggreg</code> S3 method with functions for `InputsModel`, `OutputsModel`, `list`, `data.frame` class objects. This new version of the <code>SeriesAggreg()</code> function also allows to compute regimes. ([#25](https://gitlab.irstea.fr/HYCAR-Hydro/airgr/-/issues/25))
- Added <code>.GetAggregConvertFun()</code> private function in order to choose automatically the <code>ConvertFun</code> to apply on each element of objects used in <code>SeriesAggreg.InputsModel</code> and <code>SeriesAggreg.OutputsModel</code>.
- Added <code>.AggregConvertFunTable</code> data.frame that allows the user to see what names of list items or data.frame column names are guessed and eventually customise this correspondence table.
#### Bug fixes
- <code>TimeLag</code> of the <code>SeriesAggreg()</code> function now runs when <code>TimeLag >= 3600</code>. ([#41](https://gitlab.irstea.fr/HYCAR-Hydro/airgr/-/issues/41))
____________________________________________________________________________________
### 1.6.3.78 Release Notes (2021-01-05)
......@@ -28,9 +41,7 @@
#### Major user-visible changes
- Added output to <code>RunModel_GR2M()</code> function (Ps). ([#51](https://gitlab.irstea.fr/HYCAR-Hydro/airgr/-/issues/51))
- <code>PE_Oudin()</code> can now run for several locations (i.e. several latitudes) in the Fortran mode (<code>RunFortran = TRUE</code>). In this case <code>Lat</code> must be of the same length as <code>Temp</code>. ([#62](https://gitlab.irstea.fr/HYCAR-Hydro/airgr/-/issues/62))
- <code>RunModel()</code> now allows to run semi-distributed GR models. ([#34](https://gitlab.irstea.fr/HYCAR-Hydro/airgr/-/issues/34))
......@@ -779,5 +790,3 @@ ________________________________________________________________________________
- <code>CalibrationAlgo_nlminb_stats</code> argument was wrongly defined in <code>DefineFunctions_CalibrationAlgo()</code> (<code>optim</code> instead of <code>nlminb</code>).
- Format checking for <code>RunOptions</code> was incorrectly made in <code>CheckArg()</code> function.
......@@ -32,7 +32,7 @@ Imax <- function(InputsModel,
TabSeries <- data.frame(DatesR = InputsModel$DatesR[IndPeriod_Run],
Precip = InputsModel$Precip[IndPeriod_Run],
PotEvap = InputsModel$PotEvap[IndPeriod_Run])
daily_data <- SeriesAggreg(TabSeries, "hourly", "daily",
daily_data <- SeriesAggreg(TabSeries, Format = "%Y%m%d",
ConvertFun = c("sum", "sum"))
##calculate total interception of daily GR models on the period
......
SeriesAggreg.InputsModel <- function(x, ...) {
SeriesAggreg.list(x,
ConvertFun = .GetAggregConvertFun(names(x)),
except = c("ZLayers", "LengthHydro", "BasinAreas"),
...)
}
SeriesAggreg.OutputsModel <- function(x, ...) {
SeriesAggreg.list(x,
ConvertFun = .GetAggregConvertFun(names(x)),
except = "StateEnd",
...)
}
SeriesAggreg <- function(TabSeries,
TimeFormat, NewTimeFormat,
ConvertFun,
YearFirstMonth = 1, TimeLag = 0,
verbose = TRUE) {
##_Arguments_check
##check_TabSeries
if (!is.data.frame(TabSeries)) {
stop("'TabSeries' must be a data.frame containing the dates and data to be aggregated")
}
if (ncol(TabSeries) < 2) {
stop("'TabSeries' must contain at least two columns (including the column of dates)")
}
##check_TimeFormat
if (!any(class(TabSeries[, 1]) %in% "POSIXt")) {
stop("'TabSeries' first column must be a vector of class 'POSIXlt' or 'POSIXct'")
}
if (any(class(TabSeries[, 1]) %in% "POSIXlt")) {
TabSeries[, 1] <- as.POSIXct(TabSeries[, 1])
}
for (iCol in 2:ncol(TabSeries)) {
if (!is.numeric(TabSeries[,iCol])) {
stop("'TabSeries' columns (other than the first one) be of numeric class")
}
}
##check TimeFormat and NewTimeFormat
TimeStep <- c("hourly", "daily", "monthly", "yearly")
TimeFormat <- match.arg(TimeFormat , choices = TimeStep)
NewTimeFormat <- match.arg(NewTimeFormat, choices = TimeStep)
##check_ConvertFun
if (!is.vector(ConvertFun)) {
stop("'ConvertFun' must be a vector of character")
}
if (!is.character(ConvertFun)) {
stop("'ConvertFun' must be a vector of character")
}
if (length(ConvertFun) != (ncol(TabSeries) - 1)) {
stop(
paste("'ConvertFun' must be of length", ncol(TabSeries) - 1, "(length=ncol(TabSeries)-1)")
)
}
if (sum(ConvertFun %in% c("sum", "mean") == FALSE) != 0) {
stop("'ConvertFun' elements must be one of 'sum' or 'mean'")
}
##check_YearFirstMonth
if (!is.vector(YearFirstMonth)) {
stop("'YearFirstMonth' must be an integer between 1 and 12")
}
if (!is.numeric(YearFirstMonth)) {
stop("'YearFirstMonth' must be an integer between 1 and 12")
}
YearFirstMonth <- as.integer(YearFirstMonth)
if (length(YearFirstMonth) != 1) {
stop("'YearFirstMonth' must be only one integer between 1 and 12")
}
if (YearFirstMonth %in% (1:12) == FALSE) {
stop("'YearFirstMonth' must be only one integer between 1 and 12")
}
##check_DatesR_integrity
by <- switch(TimeFormat,
'hourly' = "hours",
'daily' = "days",
'monthly' = "months",
'yearly' = "years")
TmpDatesR <- seq(from = TabSeries[1, 1], to = tail(TabSeries[, 1], 1), by = by)
if (!identical(TabSeries[, 1], TmpDatesR)) {
stop("some dates might not be ordered or are missing in 'TabSeries'")
}
##check_conversion_direction
if ((TimeFormat == "daily" & NewTimeFormat %in% c("hourly") ) |
(TimeFormat == "monthly" & NewTimeFormat %in% c("hourly","daily") ) |
(TimeFormat == "yearly" & NewTimeFormat %in% c("hourly","daily","monthly"))) {
stop("only time aggregation can be performed")
}
##check_if_conversion_not_needed
if ((TimeFormat == "hourly" & NewTimeFormat == "hourly" ) |
(TimeFormat == "daily" & NewTimeFormat == "daily" ) |
(TimeFormat == "monthly" & NewTimeFormat == "monthly") |
(TimeFormat == "yearly" & NewTimeFormat == "yearly" )) {
if (verbose) {
warning("the old and new format are identical \n\t -> no time-step conversion was performed")
return(TabSeries)
}
}
##_Time_step_conversion
##_Handle_conventional_difference_between_hourly_series_and_others
TmpDatesR <- TabSeries[, 1]
#if (TimeFormat=="hourly") { TmpDatesR <- TmpDatesR - 60*60; }
TmpDatesR <- TmpDatesR + TimeLag
Hmax <- "00"
if (TimeFormat == "hourly") {
Hmax <- "23"
}
##_Identify_the_part_of_the_series_to_be_aggregated
NDaysInMonth <- list("31", c("28", "29"), "31", "30", "31", "30", "31", "31", "30", "31", "30", "31")
NDaysAndMonth <- sprintf("%02i%s", c(1:2, 2:12), unlist(NDaysInMonth))
YearLastMonth <- YearFirstMonth + 11
if (YearLastMonth > 12) {
YearLastMonth <- YearLastMonth - 12
}
YearFirstMonthTxt <- formatC(YearFirstMonth, format = "d", width = 2, flag = "0")
YearLastMonthTxt <- formatC(YearLastMonth , format = "d", width = 2, flag = "0")
if (NewTimeFormat == "daily") {
Ind1 <- which(format(TmpDatesR, "%H") == "00")
Ind2 <- which(format(TmpDatesR, "%H") == Hmax)
if (Ind2[1] < Ind1[1]) {
Ind2 <- Ind2[2:length(Ind2)]
}
if (tail(Ind1, 1) > tail(Ind2, 1)) {
Ind1 <- Ind1[1:(length(Ind1) - 1)]
}
### Aggr <- NULL; iii <- 0; for(kkk in 1:length(Ind1)) {
### iii <- iii+1; Aggr <- c(Aggr,rep(iii,length(Ind1[kkk]:Ind2[kkk]))); }
Aggr <- as.numeric(format(TmpDatesR[min(Ind1):max(Ind2)], "%Y%m%d"))
### more efficient
NewDatesR <- data.frame(seq(from = TmpDatesR[min(Ind1)], to = TmpDatesR[max(Ind2)], by = "days"))
}
if (NewTimeFormat=="monthly") {
Ind1 <- which(format(TmpDatesR, "%d%H") == "0100")
Ind2 <- which(format(TmpDatesR,"%m%d%H") %in% paste0(NDaysAndMonth, Hmax))
Ind2[1:(length(Ind2) - 1)][diff(Ind2) == 1] <- NA
Ind2 <- Ind2[!is.na(Ind2)] ### to keep only feb 29 if both feb 28 and feb 29 exists
if (Ind2[1] < Ind1[1]) {
Ind2 <- Ind2[2:length(Ind2)]
}
if (tail(Ind1, 1) > tail(Ind2, 1)) {
Ind1 <- Ind1[1:(length(Ind1) - 1)]
}
### Aggr <- NULL; iii <- 0; for(kkk in 1:length(Ind1)) {
### iii <- iii+1; Aggr <- c(Aggr,rep(iii,length(Ind1[kkk]:Ind2[kkk]))); }
Aggr <- as.numeric(format(TmpDatesR[min(Ind1):max(Ind2)],"%Y%m")); ### more efficient
NewDatesR <- data.frame(seq(from=TmpDatesR[min(Ind1)],to=TmpDatesR[max(Ind2)],by="months"))
}
if (NewTimeFormat == "yearly") {
Ind1 <- which(format(TmpDatesR, "%m%d%H") %in% paste0(YearFirstMonthTxt, "0100"))
Ind2 <- which(format(TmpDatesR, "%m%d%H") %in% paste0(YearLastMonthTxt, NDaysInMonth[[YearLastMonth]], Hmax))
Ind2[1:(length(Ind2) - 1)][diff(Ind2) == 1] <- NA
Ind2 <- Ind2[!is.na(Ind2)]
### to keep only feb 29 if both feb 28 and feb 29 exists
if (Ind2[1] < Ind1[1]) {
Ind2 <- Ind2[2:length(Ind2)]
}
if (tail(Ind1, 1) > tail(Ind2, 1)) {
Ind1 <- Ind1[1:(length(Ind1) - 1)]
}
Aggr <- NULL
iii <- 0
for (kkk in 1:length(Ind1)) {
iii <- iii + 1
Aggr <- c(Aggr, rep(iii, length(Ind1[kkk]:Ind2[kkk])))
}
### Aggr <- as.numeric(format(TmpDatesR[min(Ind1):max(Ind2)],"%Y")); ### not working if YearFirstMonth != 01
NewDatesR <- data.frame(seq(from = TmpDatesR[min(Ind1)], to = TmpDatesR[max(Ind2)], by = "years"))
}
##_Aggreation_and_export
NewTabSeries <- data.frame(NewDatesR)
for (iCol in 2:ncol(TabSeries)) {
AggregData <- aggregate(TabSeries[min(Ind1):max(Ind2), iCol],
by = list(Aggr),
FUN = ConvertFun[iCol - 1], na.rm = FALSE)[, 2]
NewTabSeries <- data.frame(NewTabSeries, AggregData)
}
names(NewTabSeries) <- names(TabSeries)
return(NewTabSeries)
}
\ No newline at end of file
SeriesAggreg <- function(x, Format, ...) {
UseMethod("SeriesAggreg")
}
SeriesAggreg.data.frame <- function(x,
Format,
ConvertFun,
TimeFormat = NULL,
NewTimeFormat = NULL,
YearFirstMonth = 1,
TimeLag = 0,
...) {
## Arguments checks
if (!is.null(TimeFormat)) {
warning("deprecated 'TimeFormat' argument", call. = FALSE)
}
if (missing(Format)) {
Format <- .GetSeriesAggregFormat(NewTimeFormat)
} else if (!is.null(NewTimeFormat)) {
warning("deprecated 'NewTimeFormat' argument: 'Format' argument is used instead",
call. = FALSE)
}
if (is.null(Format)) {
stop("argument 'Format' is missing")
}
## check x
if (!is.data.frame(x)) {
stop("'x' must be a data.frame containing the dates and data to be aggregated")
}
if (ncol(x) < 2) {
stop("'x' must contain at least two columns (including the column of dates)")
}
## check x date column
if (!inherits(x[[1L]], "POSIXt")) {
stop("'x' first column must be a vector of class 'POSIXlt' or 'POSIXct'")
}
if (inherits(x[[1L]], "POSIXlt")) {
x[[1L]] <- as.POSIXct(x[[1L]])
}
## check x other columns (boolean converted to numeric)
apply(x[, -1L, drop = FALSE],
MARGIN = 2,
FUN = function(iCol) {
if (!is.numeric(iCol)) {
stop("'x' columns (other than the first one) must be of numeric class")
}
})
## check Format
listFormat <- c("%Y%m%d", "%Y%m", "%Y", "%m", "%d")
Format <- gsub(pattern = "[[:punct:]]+", replacement = "%", Format)
Format <- match.arg(Format, choices = listFormat)
## check ConvertFun
listConvertFun <- list(sum = sum, mean = mean)
ConvertFun <- names(listConvertFun)[match(ConvertFun, names(listConvertFun))]
if (anyNA(ConvertFun)) {
stop("'ConvertFun' should be a one of 'sum' or 'mean'")
}
if (length(ConvertFun) != (ncol(x) - 1)) {
stop(sprintf("'ConvertFun' must be of length %i (ncol(x)-1)", ncol(x) - 1))
}
## check YearFirstMonth
msgYearFirstMonth <- "'YearFirstMonth' should be a single vector of numeric value between 1 and 12"
YearFirstMonth <- match(YearFirstMonth, 1:12)
if (anyNA(YearFirstMonth)) {
stop(msgYearFirstMonth)
}
if (length(YearFirstMonth) != 1) {
stop(msgYearFirstMonth)
}
if (YearFirstMonth != 1 & Format != "%Y") {
warning("'YearFirstMonth' is ignored because Format != '%Y'")
}
## check TimeLag
msgTimeLag <- "'TimeLag' should be a single vector of a positive numeric value"
if (!is.vector(TimeLag)) {
stop(msgTimeLag)
}
if (!is.numeric(TimeLag)) {
stop(msgTimeLag)
}
if (length(TimeLag) != 1 | !any(TimeLag >= 0)) {
stop(msgTimeLag)
}
TabSeries0 <- x
colnames(TabSeries0)[1L] <- "DatesR"
TabSeries0$DatesR <- TabSeries0$DatesR + TimeLag
TabSeries2 <- TabSeries0
if (!Format %in% c("%d", "%m")) {
start <- sprintf("%i-01-01 00:00:00",
as.numeric(format(TabSeries2$DatesR[1L], format = "%Y")) - 1)
stop <- sprintf("%i-12-31 00:00:00",
as.numeric(format(TabSeries2$DatesR[nrow(TabSeries2)], format = "%Y")) + 1)
unitTs <- format(diff(x[1:2, 1]))
if (gsub("[0-9]+ ", "", unitTs) == "hours") {
byTs <- "hours"
} else {
if (gsub(" days$", "", unitTs) == "1") {
byTs <- "days"
} else {
byTs <- "months"
}
}
fakeTs <- data.frame(DatesR = seq(from = as.POSIXct(start, tz = "UTC"),
to = as.POSIXct(stop , tz = "UTC"),
by = byTs) + TimeLag)
TabSeries2 <- merge(fakeTs, TabSeries2, by = "DatesR", all.x = TRUE)
}
TabSeries2$DatesRini <- TabSeries2$DatesR %in% TabSeries0$DatesR
TabSeries2$Selec2 <- format(TabSeries2$DatesR, Format)
if (nchar(Format) > 2 | Format == "%Y") {
# Compute aggregation
TabSeries2$Selec <- !duplicated(TabSeries2$Selec2)
if (all(TabSeries2$Selec)) {
warning("the requested time 'Format' is the same as the one in 'x'. No time-step conversion was performed")
return(x)
}
if (Format == "%Y") {
yfm <- sprintf("%02.f", YearFirstMonth)
spF1 <- "%m"
spF2 <- "%Y-%m"
TabSeries2$Selec1 <- format(TabSeries2$DatesR, spF1)
TabSeries2$Selec2 <- format(TabSeries2$DatesR, spF2)
TabSeries2$Selec <- !duplicated(TabSeries2$Selec2) & TabSeries2$Selec1 == yfm
}
TabSeries2$Fac2 <- cumsum(TabSeries2$Selec)
} else {
# Compute regime
if (Format == "%d") {
spF2 <- "%m-%d"
TabSeries2$Selec2 <- format(TabSeries2$DatesR, spF2)
}
TabSeries2$Fac2 <- TabSeries2$Selec2
TabSeries2$Selec <- !duplicated(TabSeries2$Selec2)
ConvertFun <- rep("mean", ncol(x) - 1)
}
listTsAggreg <- lapply(names(listConvertFun), function(y) {
if (any(ConvertFun == y)) {
colTsAggreg <- c("Fac2", colnames(x)[-1L][ConvertFun == y])
aggregate(. ~ Fac2,
data = TabSeries2[, colTsAggreg],
FUN = listConvertFun[[y]],
na.action = na.pass)
} else {
NULL
}
})
listTsAggreg <- listTsAggreg[!sapply(listTsAggreg, is.null)]
tsAggreg <- do.call(cbind, listTsAggreg)
tsAggreg <- tsAggreg[, !duplicated(colnames(tsAggreg))]
tsAggreg <- merge(tsAggreg,
TabSeries2[, c("Fac2", "DatesR", "DatesRini", "Selec")],
by = "Fac2",
all.x = TRUE,
all.y = FALSE)
tsAggreg <- tsAggreg[tsAggreg$Selec & tsAggreg$DatesRini, ]
tsAggreg <- tsAggreg[, colnames(TabSeries0)]
return(tsAggreg)
}
SeriesAggreg.list <- function(x,
Format,
ConvertFun,
NewTimeFormat = NULL,
simplify = FALSE,
except = NULL,
recursive = TRUE,
...) {
if (missing(Format)) {
Format <- .GetSeriesAggregFormat(NewTimeFormat)
} else if (!is.null(NewTimeFormat)) {
warning("deprecated 'NewTimeFormat' argument: 'Format' argument is used instead",
call. = FALSE)
}
# Determination of DatesR
if (!is.null(x$DatesR)) {
if (!inherits(x$DatesR, "POSIXt")) {
stop("'x$DatesR' should be of class 'POSIXt'")
}
DatesR <- x$DatesR
} else {
# Auto-detection of POSIXt item in Tabseries
itemPOSIXt <- which(sapply(x, function(x) {
inherits(x, "POSIXt")
}, simplify = TRUE))[1]
if (is.na(itemPOSIXt)) {
stop("At least one item of argument 'x' should be of class 'POSIXt'")
}
warning("Item 'DatesR' not found in 'x' argument: the item ",
names(x)[itemPOSIXt],
" has been automatically chosen")
DatesR <- x[[names(x)[itemPOSIXt]]]
}
# Selection of numeric items for aggregation
numericCols <- names(which(sapply(x, inherits, "numeric")))
arrayCols <- names(which(sapply(x, inherits, "array")))
numericCols <- setdiff(numericCols, arrayCols)
if (!is.null(except)) {
if (!inherits(except, "character")) {
stop("Argument 'except' should be a 'character'")
}
numericCols <- setdiff(numericCols, except)
}
cols <- x[numericCols]
lengthCols <- sapply(cols, length, simplify = TRUE)
if (any(lengthCols != length(DatesR))) {
sErr <- paste0(names(lengthCols)[lengthCols != length(DatesR)],
" (", lengthCols[lengthCols != length(DatesR)], ")",
collapse = ", ")
warning("The length of the following `numeric` items in 'x' ",
"is different than the length of 'DatesR (",
length(DatesR),
")': they will be ignored in the aggregation: ",
sErr)
cols <- cols[lengthCols == length(DatesR)]
}
dfOut <- NULL
if (length(cols)) {
ConvertFun2 <- .GetAggregConvertFun(names(cols))
if (is.null(recursive)) {
if (missing(ConvertFun)) {
stop("'ConvertFun' argument should provided if 'recursive = NULL'")
} else if (!is.na(ConvertFun)) {
ConvertFun2 <- rep(ConvertFun, length(cols))
}
}
dfOut <- SeriesAggreg(cbind(DatesR, as.data.frame(cols)),
Format,
...,
ConvertFun = ConvertFun2)
}
if (simplify) {
# Returns data.frame of numeric found in the first level of the list
return(dfOut)
} else {
res <- list()
# Convert aggregated data.frame into list
if (!is.null(dfOut)) {
res <- as.list(dfOut)
## To be consistent with InputsModel class and because plot.OutputsModel use the POSIXlt class
res$DatesR <- as.POSIXlt(res$DatesR)
}
# Exploration of embedded lists and data.frames
if (is.null(recursive) || recursive) {
listCols <- x[sapply(x, inherits, "list")]
dfCols <- x[sapply(x, inherits, "data.frame")]
dfCols <- c(dfCols, x[sapply(x, inherits, "matrix")])
listCols <- listCols[setdiff(names(listCols), names(dfCols))]
if (length(listCols) > 0) {
# Check for predefined ConvertFun for all sub-elements
ConvertFun <- .GetAggregConvertFun(names(listCols))
# Run SeriesAggreg for each embedded list
listRes <- lapply(names(listCols), function(x) {
listCols[[x]]$DatesR <- DatesR
SeriesAggreg(listCols[[x]],
Format = Format,
except = except,