From 2c05724bd6b196795706a9860925c8284bcdcc96 Mon Sep 17 00:00:00 2001
From: Le Roux Erwan <erwan.le-roux@irstea.fr>
Date: Thu, 1 Apr 2021 17:43:28 +0200
Subject: [PATCH] [projection snowfall] add evgam call for
 abstract_temporal_linear_margin_model.py. account for log_scale in the
 margin_function. rename get_coord_df to
 get_r_dataframe_from_python_dataframe. create evgam_fixed.R for debugging

---
 extreme_fit/distribution/gev/evgam_example.R  | 18 ++++
 extreme_fit/distribution/gev/evgam_fixed.R    | 42 +++++++++
 extreme_fit/distribution/gev/main_fevd_mle.R  | 12 +--
 extreme_fit/estimator/utils.py                |  7 +-
 .../abstract_margin_function.py               |  3 +-
 .../independent_margin_function.py            |  6 +-
 .../margin_function/linear_margin_function.py |  6 +-
 .../parametric_margin_function.py             | 10 +-
 .../polynomial_margin_function.py             |  8 +-
 .../abstract_temporal_linear_margin_model.py  | 28 +++++-
 extreme_fit/model/margin_model/utils.py       |  1 +
 .../quantile_regression_model.py              |  4 +-
 .../abstract_result_from_model_fit.py         |  5 +
 .../result_from_extremes/result_from_evgam.py | 42 +++++++++
 extreme_fit/model/utils.py                    | 17 ++--
 .../test_gev_temporal_evgam.py                | 93 +++++++++++++++++++
 16 files changed, 269 insertions(+), 33 deletions(-)
 create mode 100644 extreme_fit/distribution/gev/evgam_example.R
 create mode 100644 extreme_fit/distribution/gev/evgam_fixed.R
 create mode 100644 extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_evgam.py
 create mode 100644 test/test_extreme_fit/test_estimator/test_temporal_estimator/test_gev_temporal_evgam.py

diff --git a/extreme_fit/distribution/gev/evgam_example.R b/extreme_fit/distribution/gev/evgam_example.R
new file mode 100644
index 00000000..0aadfd88
--- /dev/null
+++ b/extreme_fit/distribution/gev/evgam_example.R
@@ -0,0 +1,18 @@
+# Title     : TODO
+# Objective : TODO
+# Created by: erwan
+# Created on: 30/03/2021
+source('evgam_fixed.R')
+library(evgam)
+data(COprcp)
+COprcp$year <- format(COprcp$date, "%Y")
+COprcp_gev <- aggregate(prcp ~ year + meta_row, COprcp, max)
+COprcp_gev <- cbind(COprcp_gev, COprcp_meta[COprcp_gev$meta_row,])
+# print(COprcp_gev)
+print('before call')
+fmla_gev2 <- list(prcp ~ s(elev, bs="cr"), ~ s(lon, fx=FALSE, k=20), ~ 1)
+m_gev2 <- evgam_fixed(fmla_gev2, data=COprcp_gev, family="gev")
+# m_gev2 <- evgam(fmla_gev2, data=COprcp_gev, family="gev")
+# summary(m_gev2)
+# print(attributes(m_gev2))
+print('good finish')
diff --git a/extreme_fit/distribution/gev/evgam_fixed.R b/extreme_fit/distribution/gev/evgam_fixed.R
new file mode 100644
index 00000000..d978021a
--- /dev/null
+++ b/extreme_fit/distribution/gev/evgam_fixed.R
@@ -0,0 +1,42 @@
+# Title     : TODO
+# Objective : TODO
+# Created by: erwan
+# Created on: 01/04/2021
+library(evgam)
+attach(loadNamespace("evgam"), name = "evgam_all")
+evgam_fixed <- function(formula, data, family = "gev", correctV = TRUE, rho0 = 0, inits = NULL, outer = "bfgs", control = NULL, removeData = FALSE, trace = 0, knots = NULL, maxdata = 1e+20, maxspline = 1e+20, compact = FALSE, ald.args = list(), exi.args = list(), pp.args = list(), sandwich.args = list()) {
+  family.info <- .setup.family(family, pp.args)
+  if (is.null(family.info$lik.fns$d340)) outer <- "fd"
+  formula <- .setup.formulae(formula, family.info$npar, family.info$npar2, data, trace)
+  response.name <- attr(formula, "response.name")
+  temp.data <- .setup.data(data, response.name, formula, family, family.info$nms, removeData, exi.args, ald.args, pp.args, knots, maxdata, maxspline, compact, sandwich.args, tolower(outer), trace)
+  data <- temp.data$data
+  beta <- .setup.inner.inits(inits, temp.data$lik.data, family.info$lik.fns, family.info$npar, family)
+  lik.data <- .sandwich(temp.data$lik.data, beta)
+  if (trace > 0 & lik.data$adjust > 0) cat(paste("\n Sandwich correct lambda =", signif(lik.data$k, 3), "\n"))
+  smooths <- length(temp.data$gotsmooth) > 0
+  if (smooths) {
+    S.data <- .joinSmooth(temp.data$gams)
+    nsp <- length(attr(S.data, "Sl"))
+    if (is.null(rho0)) {
+      diagSl <- sapply(attr(S.data, "Sl"), diag)
+      rho0 <- apply(diagSl, 2, function(y) uniroot(.guess, c(-100, 100), d = attr(beta, "diagH"), s = y)$root)
+    } else {
+      if (length(rho0) == 1) rho0 <- rep(rho0, nsp)
+    }
+    lik.data$S <- .makeS(S.data, exp(rho0))
+    fit.reml <- .outer(rho0, beta, family.info$lik.fns, lik.data, S.data, control, correctV, lik.data$outer, trace)
+    sp <- exp(fit.reml$par)
+    lik.data$S <- .makeS(S.data, sp)
+  } else {
+    S.data <- NULL
+    fit.reml <- .outer.nosmooth(beta, family.info$lik.fns, lik.data, control, trace)
+  }
+  VpVc <- .VpVc(fit.reml, family.info$lik.fns, lik.data, S.data, correctV = correctV, sandwich = temp.data$sandwich, smooths = smooths, trace = trace)
+  edf <- .edf(fit.reml$beta, family.info$lik.fns, lik.data, VpVc, temp.data$sandwich)
+  names(temp.data$gams) <- family.info$nms
+  gams <- .swap(fit.reml, temp.data$gams, lik.data, VpVc, temp.data$gotsmooth, edf, smooths)
+  gams <- .finalise(gams, data, family.info$lik.fns, lik.data, S.data, fit.reml, VpVc, family, temp.data$gotsmooth, formula, response.name, removeData, edf)
+  return(gams)
+}
+
diff --git a/extreme_fit/distribution/gev/main_fevd_mle.R b/extreme_fit/distribution/gev/main_fevd_mle.R
index 972c9a64..1a97d9ad 100644
--- a/extreme_fit/distribution/gev/main_fevd_mle.R
+++ b/extreme_fit/distribution/gev/main_fevd_mle.R
@@ -17,16 +17,16 @@ x_gev <- rgev(N, loc = loc, scale = scale, shape = shape)
 # N <- 50
 # loc = 0; scale = 1; shape <- 0.1
 # x_gev <- rgev(N, loc = loc, scale = scale, shape = shape)
-coord <- matrix(ncol=3, nrow = N)
+coord <- matrix(ncol=1, nrow = N)
 coord[,1]=seq(0,N-1,1)
-coord[,2]=c(rep(0, N/2), rep(1, N/2))
-coord[,3]=c(rep(1, N/2), rep(0, N/2))
-colnames(coord) = c("T", "G1", "G2")
+# coord[,2]=c(rep(0, N/2), rep(1, N/2))
+# coord[,3]=c(rep(1, N/2), rep(0, N/2))
+colnames(coord) = c("T")
 print(coord)
 coord = data.frame(coord, stringsAsFactors = TRUE)
 # res = fevd_fixed(x_gev, data=coord, method='MLE', verbose=TRUE, use.phi=FALSE)
-# res = fevd_fixed(x_gev, data=coord, location.fun= ~T, scale.fun= ~T, method='MLE', type="GEV", verbose=FALSE, use.phi=FALSE)
-res = fevd_fixed(x_gev, data=coord, shape.fun= ~1 + (G1-G2) + I(G2), method='MLE', type="GEV", verbose=FALSE, use.phi=FALSE)
+res = fevd_fixed(x_gev, data=coord, location.fun= ~T, scale.fun= ~T, method='MLE', type="GEV", verbose=FALSE, use.phi=FALSE)
+# res = fevd_fixed(x_gev, data=coord, shape.fun= ~1 + (G1-G2) + I(G2), method='MLE', type="GEV", verbose=FALSE, use.phi=FALSE)
 print(res)
 
 # Some display for the results
diff --git a/extreme_fit/estimator/utils.py b/extreme_fit/estimator/utils.py
index b42a1b64..2d8699c5 100644
--- a/extreme_fit/estimator/utils.py
+++ b/extreme_fit/estimator/utils.py
@@ -5,14 +5,17 @@ from extreme_fit.model.margin_model.linear_margin_model.linear_margin_model impo
 from extreme_fit.function.margin_function.linear_margin_function import LinearMarginFunction
 
 
-def load_margin_function(estimator: AbstractEstimator, margin_model: LinearMarginModel, coef_dict=None):
+def load_margin_function(estimator: AbstractEstimator, margin_model: LinearMarginModel, coef_dict=None, log_scale=None):
     if coef_dict is None:
         coef_dict = estimator.result_from_model_fit.margin_coef_ordered_dict
+    if log_scale is None:
+        log_scale = estimator.result_from_model_fit.log_scale
     margin_function_class = type(margin_model.margin_function)
     return margin_function_class.from_coef_dict(coordinates=estimator.dataset.coordinates,
                                                 param_name_to_dims=margin_model.param_name_to_list_for_result,
                                                 coef_dict=coef_dict,
-                                                starting_point=margin_model.starting_point)
+                                                starting_point=margin_model.starting_point,
+                                                log_scale=log_scale)
 
 
 
diff --git a/extreme_fit/function/margin_function/abstract_margin_function.py b/extreme_fit/function/margin_function/abstract_margin_function.py
index a271da29..8f3a7b14 100644
--- a/extreme_fit/function/margin_function/abstract_margin_function.py
+++ b/extreme_fit/function/margin_function/abstract_margin_function.py
@@ -20,8 +20,9 @@ class AbstractMarginFunction(AbstractFunction):
     VISUALIZATION_RESOLUTION = 100
     VISUALIZATION_TEMPORAL_STEPS = 2
 
-    def __init__(self, coordinates: AbstractCoordinates, params_class: type = GevParams):
+    def __init__(self, coordinates: AbstractCoordinates, params_class: type = GevParams, log_scale=None):
         super().__init__(coordinates)
+        self.log_scale = log_scale
         self.params_class = params_class
         self.mask_2D = None
 
diff --git a/extreme_fit/function/margin_function/independent_margin_function.py b/extreme_fit/function/margin_function/independent_margin_function.py
index 79db8d38..055c2848 100644
--- a/extreme_fit/function/margin_function/independent_margin_function.py
+++ b/extreme_fit/function/margin_function/independent_margin_function.py
@@ -14,9 +14,9 @@ class IndependentMarginFunction(AbstractMarginFunction):
         IndependentMarginFunction: each parameter of the GEV are modeled independently
     """
 
-    def __init__(self, coordinates: AbstractCoordinates, params_class: type = GevParams):
+    def __init__(self, coordinates: AbstractCoordinates, params_class: type = GevParams, log_scale=None):
         """Attribute 'param_name_to_param_function' maps each GEV parameter to its corresponding function"""
-        super().__init__(coordinates, params_class)
+        super().__init__(coordinates, params_class, log_scale)
         self.param_name_to_param_function = None  # type: Union[None, Dict[str, AbstractParamFunction]]
 
     def get_params(self, coordinate: np.ndarray, is_transformed: bool = True) -> GevParams:
@@ -28,6 +28,8 @@ class IndependentMarginFunction(AbstractMarginFunction):
         transformed_coordinate = coordinate if is_transformed else self.transform(coordinate)
         params = {param_name: param_function.get_param_value(transformed_coordinate)
                   for param_name, param_function in self.param_name_to_param_function.items()}
+        if self.log_scale:
+            params[GevParams.SCALE] = np.exp(params[GevParams.SCALE])
         return self.params_class.from_dict(params)
 
     def get_first_derivative_param(self, coordinate: np.ndarray, is_transformed: bool, dim: int = 0):
diff --git a/extreme_fit/function/margin_function/linear_margin_function.py b/extreme_fit/function/margin_function/linear_margin_function.py
index b989c1c6..d387341f 100644
--- a/extreme_fit/function/margin_function/linear_margin_function.py
+++ b/extreme_fit/function/margin_function/linear_margin_function.py
@@ -28,11 +28,9 @@ class LinearMarginFunction(ParametricMarginFunction):
 
     COEF_CLASS = LinearCoef
 
-    def __init__(self, coordinates: AbstractCoordinates, param_name_to_dims: Dict[str, List[int]],
-                 param_name_to_coef: Dict[str, AbstractCoef], starting_point: Union[None, int] = None,
-                 params_class: type = GevParams):
+    def __init__(self, *args, **kwargs):
         self.param_name_to_coef = None  # type: Union[None, Dict[str, LinearCoef]]
-        super().__init__(coordinates, param_name_to_dims, param_name_to_coef, starting_point, params_class)
+        super().__init__(*args, **kwargs)
 
     def load_specific_param_function(self, param_name) -> AbstractParamFunction:
         return LinearParamFunction(dims=self.param_name_to_dims[param_name],
diff --git a/extreme_fit/function/margin_function/parametric_margin_function.py b/extreme_fit/function/margin_function/parametric_margin_function.py
index d08609d8..41d8f882 100644
--- a/extreme_fit/function/margin_function/parametric_margin_function.py
+++ b/extreme_fit/function/margin_function/parametric_margin_function.py
@@ -32,10 +32,11 @@ class ParametricMarginFunction(IndependentMarginFunction):
 
     def __init__(self, coordinates: AbstractCoordinates, param_name_to_dims: Dict[str, List[int]],
                  param_name_to_coef: Dict[str, AbstractCoef], starting_point: Union[None, int] = None,
-                 params_class: type = GevParams):
+                 params_class: type = GevParams,
+                 log_scale=None):
         # Starting point for the trend is the same for all the parameters
         self.starting_point = starting_point
-        super().__init__(coordinates, params_class)
+        super().__init__(coordinates, params_class, log_scale=log_scale)
         self.param_name_to_dims = param_name_to_dims  # type: Dict[str, List[int]]
 
         # Check the dimension are well-defined with respect to the coordinates
@@ -89,7 +90,8 @@ class ParametricMarginFunction(IndependentMarginFunction):
 
     @classmethod
     def from_coef_dict(cls, coordinates: AbstractCoordinates, param_name_to_dims: Dict[str, List[int]],
-                       coef_dict: Dict[str, float], starting_point: Union[None, int] = None):
+                       coef_dict: Dict[str, float], starting_point: Union[None, int] = None,
+                       log_scale=None):
         assert cls.COEF_CLASS is not None, 'a COEF_CLASS class attributes needs to be defined'
         param_name_to_coef = {}
         for param_name in GevParams.PARAM_NAMES:
@@ -97,7 +99,7 @@ class ParametricMarginFunction(IndependentMarginFunction):
             coef = cls.COEF_CLASS.from_coef_dict(coef_dict=coef_dict, param_name=param_name, dims=dims,
                                                  coordinates=coordinates)
             param_name_to_coef[param_name] = coef
-        return cls(coordinates, param_name_to_dims, param_name_to_coef, starting_point)
+        return cls(coordinates, param_name_to_dims, param_name_to_coef, starting_point=starting_point, log_scale=log_scale)
 
     @property
     def form_dict(self) -> Dict[str, str]:
diff --git a/extreme_fit/function/margin_function/polynomial_margin_function.py b/extreme_fit/function/margin_function/polynomial_margin_function.py
index 06bbeac5..e7814ed7 100644
--- a/extreme_fit/function/margin_function/polynomial_margin_function.py
+++ b/extreme_fit/function/margin_function/polynomial_margin_function.py
@@ -14,13 +14,13 @@ class PolynomialMarginFunction(LinearMarginFunction):
 
     def __init__(self, coordinates: AbstractCoordinates, param_name_to_dim_and_max_degree: Dict[str, List[Tuple[int, int]]],
                  param_name_to_coef: Dict[str, PolynomialAllCoef], starting_point: Union[None, int] = None,
-                 params_class: type = GevParams):
+                 params_class: type = GevParams, log_scale=None):
         param_name_to_dims = {}
         for param_name in param_name_to_dim_and_max_degree.keys():
             dims = [c[0] for c in param_name_to_dim_and_max_degree[param_name]]
             param_name_to_dims[param_name] = dims
         self.param_name_to_dim_and_max_degree = param_name_to_dim_and_max_degree
-        super().__init__(coordinates, param_name_to_dims, param_name_to_coef, starting_point, params_class)
+        super().__init__(coordinates, param_name_to_dims, param_name_to_coef, starting_point, params_class, log_scale)
 
     COEF_CLASS = PolynomialAllCoef
 
@@ -33,7 +33,7 @@ class PolynomialMarginFunction(LinearMarginFunction):
 
     @classmethod
     def from_coef_dict(cls, coordinates: AbstractCoordinates, param_name_to_dims: Dict[str, List[Tuple[int, int]]],
-                       coef_dict: Dict[str, float], starting_point: Union[None, int] = None):
+                       coef_dict: Dict[str, float], starting_point: Union[None, int] = None, log_scale=None):
         param_name_to_dim_and_max_degree = param_name_to_dims
         assert cls.COEF_CLASS is not None, 'a COEF_CLASS class attributes needs to be defined'
         param_name_to_coef = {}
@@ -43,5 +43,5 @@ class PolynomialMarginFunction(LinearMarginFunction):
                                                  dims=dims,
                                                  coordinates=coordinates)
             param_name_to_coef[param_name] = coef
-        return cls(coordinates, param_name_to_dim_and_max_degree, param_name_to_coef, starting_point)
+        return cls(coordinates, param_name_to_dim_and_max_degree, param_name_to_coef, starting_point, log_scale=log_scale)
 
diff --git a/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py b/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py
index 090cfb25..22a74298 100644
--- a/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py
+++ b/extreme_fit/model/margin_model/linear_margin_model/abstract_temporal_linear_margin_model.py
@@ -1,5 +1,6 @@
 import numpy as np
 import pandas as pd
+from rpy2 import robjects
 
 from extreme_fit.distribution.exp_params import ExpParams
 from extreme_fit.distribution.gev.gev_params import GevParams
@@ -9,9 +10,10 @@ from extreme_fit.model.margin_model.utils import MarginFitMethod, fitmethod_to_s
 from extreme_fit.model.result_from_model_fit.abstract_result_from_model_fit import AbstractResultFromModelFit
 from extreme_fit.model.result_from_model_fit.result_from_extremes.result_from_bayesian_extremes import \
     AbstractResultFromExtremes, ResultFromBayesianExtremes
+from extreme_fit.model.result_from_model_fit.result_from_extremes.result_from_evgam import ResultFromEvgam
 from extreme_fit.model.result_from_model_fit.result_from_extremes.result_from_mle_extremes import ResultFromMleExtremes
 from extreme_fit.model.result_from_model_fit.result_from_ismev import ResultFromIsmev
-from extreme_fit.model.utils import r, ro, get_null, get_margin_formula_extremes, get_coord_df
+from extreme_fit.model.utils import r, ro, get_null, get_margin_formula_extremes, get_r_dataframe_from_python_dataframe
 from extreme_fit.model.utils import safe_run_r_estimator
 from spatio_temporal_dataset.coordinates.abstract_coordinates import AbstractCoordinates
 
@@ -46,6 +48,8 @@ class AbstractTemporalLinearMarginModel(LinearMarginModel):
                 return self.ismev_gev_fit(x, df_coordinates_temp)
             elif self.fit_method in FEVD_MARGIN_FIT_METHODS:
                 return self.extremes_fevd_fit(x, df_coordinates_temp, df_coordinates_spat)
+            elif self.fit_method is MarginFitMethod.evgam:
+                return self.extremes_evgam_fit(x, df_coordinates_temp, df_coordinates_spat)
             else:
                 raise NotImplementedError
         elif self.params_class is ExpParams:
@@ -62,6 +66,26 @@ class AbstractTemporalLinearMarginModel(LinearMarginModel):
                                    sigl=self.sigl, shl=self.shl)
         return ResultFromIsmev(res, self.param_name_to_list_for_result)
 
+    # Gev fit with evgam
+
+    def extremes_evgam_fit(self, x, df_coordinates_temp, df_coordinates_spat) -> AbstractResultFromExtremes:
+        margin_formula = get_margin_formula_extremes(self.margin_function.form_dict, transformed_as_formula=False)
+        maxima_column_name = 'Maxima'
+        formula_list = [maxima_column_name + " " + v if i == 0 else v for i, v in enumerate(margin_formula.values())]
+        formula = r.list(*[robjects.Formula(f) for f in formula_list])
+        df = pd.DataFrame({maxima_column_name: np.array(x)})
+        df = pd.concat([df, df_coordinates_spat, df_coordinates_temp], axis=1)
+        data = get_r_dataframe_from_python_dataframe(df)
+        res = safe_run_r_estimator(function=r('evgam'),
+                                   formula=formula,
+                                   data=data,
+                                   family=self.type_for_mle.lower(),
+                                   maxdata=1e10,
+                                   )
+        return ResultFromEvgam(res, self.param_name_to_list_for_result,
+                                     self.coordinates.dim_to_coordinate,
+                                     type_for_mle=self.type_for_mle)
+
     # Gev fit with extRemes package
 
     def extremes_fevd_fit(self, x, df_coordinates_temp, df_coordinates_spat) -> AbstractResultFromExtremes:
@@ -118,7 +142,7 @@ class AbstractTemporalLinearMarginModel(LinearMarginModel):
             df = df_coordinates_temp
         else:
             df = pd.concat([df_coordinates_spat, df_coordinates_temp], axis=1)
-        y = get_coord_df(df)
+        y = get_r_dataframe_from_python_dataframe(df)
 
         # Disable the use of log sigma parametrization
         r_type_argument_kwargs = {'use.phi': False,
diff --git a/extreme_fit/model/margin_model/utils.py b/extreme_fit/model/margin_model/utils.py
index e713e18c..943ebbc0 100644
--- a/extreme_fit/model/margin_model/utils.py
+++ b/extreme_fit/model/margin_model/utils.py
@@ -8,6 +8,7 @@ class MarginFitMethod(Enum):
     extremes_fevd_gmle = 3
     extremes_fevd_l_moments = 4
     spatial_extremes_mle = 5
+    evgam = 6
 
 
 FEVD_MARGIN_FIT_METHOD_TO_ARGUMENT_STR = {
diff --git a/extreme_fit/model/quantile_model/quantile_regression_model.py b/extreme_fit/model/quantile_model/quantile_regression_model.py
index 7bf51b02..3a84f1de 100644
--- a/extreme_fit/model/quantile_model/quantile_regression_model.py
+++ b/extreme_fit/model/quantile_model/quantile_regression_model.py
@@ -3,7 +3,7 @@ from rpy2 import robjects
 
 from extreme_fit.model.abstract_model import AbstractModel
 from extreme_fit.model.result_from_model_fit.result_from_quantilreg import ResultFromQuantreg
-from extreme_fit.model.utils import r, safe_run_r_estimator, get_coord_df
+from extreme_fit.model.utils import r, safe_run_r_estimator, get_r_dataframe_from_python_dataframe
 from spatio_temporal_dataset.coordinates.abstract_coordinates import AbstractCoordinates
 from spatio_temporal_dataset.dataset.abstract_dataset import AbstractDataset
 
@@ -16,7 +16,7 @@ class AbstractQuantileRegressionModel(AbstractModel):
 
     @property
     def data(self):
-        return get_coord_df(self.dataset.df_dataset)
+        return get_r_dataframe_from_python_dataframe(self.dataset.df_dataset)
 
     @property
     def first_column_of_observation(self):
diff --git a/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py b/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py
index 85dfbea3..9f5bd468 100644
--- a/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py
+++ b/extreme_fit/model/result_from_model_fit/abstract_result_from_model_fit.py
@@ -32,6 +32,11 @@ class AbstractResultFromModelFit(object):
     def all_parameters(self):
         raise NotImplementedError
 
+    @property
+    def log_scale(self):
+        # todo: refactor, put raise NotImplementError, and fix the subfunctions for the other Result objects
+        return None
+
     @property
     def margin_coef_ordered_dict(self):
         raise NotImplementedError
diff --git a/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_evgam.py b/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_evgam.py
new file mode 100644
index 00000000..8e674a52
--- /dev/null
+++ b/extreme_fit/model/result_from_model_fit/result_from_extremes/result_from_evgam.py
@@ -0,0 +1,42 @@
+import numpy as np
+from rpy2 import robjects
+
+from extreme_fit.model.result_from_model_fit.result_from_extremes.abstract_result_from_extremes import \
+    AbstractResultFromExtremes
+from extreme_fit.model.result_from_model_fit.result_from_extremes.confidence_interval_method import \
+    ci_method_to_method_name
+from extreme_fit.model.result_from_model_fit.utils import get_margin_coef_ordered_dict
+from extreme_fit.model.utils import r
+
+
+class ResultFromEvgam(AbstractResultFromExtremes):
+
+    def __init__(self, result_from_fit: robjects.ListVector, param_name_to_dim=None,
+                 dim_to_coordinate=None,
+                 type_for_mle="GEV") -> None:
+        super().__init__(result_from_fit, param_name_to_dim, dim_to_coordinate)
+        self.type_for_mle = type_for_mle
+
+    @property
+    def aic(self):
+        """Compute the aic from the list of parameters in the results,
+         find a way to comptue it directly from the result to compare"""
+        location = self.name_to_value['location']
+        a = np.array(location)
+        print(a)
+        print(len(a))
+        print('here2')
+        # 'location', 'logscale', 'shape'
+        raise NotImplementedError
+
+    @property
+    def log_scale(self):
+        return True
+
+    @property
+    def margin_coef_ordered_dict(self):
+        # print(self.name_to_value.keys())
+        # raise NotImplementedError
+        values = np.array(self.name_to_value['coefficients'])
+        return get_margin_coef_ordered_dict(self.param_name_to_dim, values, self.type_for_mle,
+                                            dim_to_coordinate_name=self.dim_to_coordinate)
diff --git a/extreme_fit/model/utils.py b/extreme_fit/model/utils.py
index 89a11766..30147834 100644
--- a/extreme_fit/model/utils.py
+++ b/extreme_fit/model/utils.py
@@ -4,6 +4,7 @@ import io
 import os.path as op
 import random
 import warnings
+from collections import OrderedDict
 from contextlib import redirect_stdout
 from typing import Dict
 
@@ -25,6 +26,7 @@ pandas2ri.activate()
 r.library('SpatialExtremes')
 r.library('data.table')
 r.library('quantreg')
+r.library('evgam')
 # Desactivate temporarily warnings
 default_filters = warnings.filters.copy()
 warnings.filterwarnings("ignore")
@@ -151,10 +153,9 @@ def get_coord(df_coordinates: pd.DataFrame):
     return coord
 
 
-def get_coord_df(df_coordinates: pd.DataFrame):
-    coord = pandas2ri.py2ri_pandasdataframe(df_coordinates)
-    # coord = r.transpose(coord)
-    colname = df_coordinates.columns
+def get_r_dataframe_from_python_dataframe(df: pd.DataFrame):
+    coord = pandas2ri.py2ri_pandasdataframe(df)
+    colname = df.columns
     coord.colnames = r.c(colname)
     coord = r('data.frame')(coord, stringsAsFactors=True)
     return coord
@@ -179,14 +180,18 @@ def new_coef_name_to_old_coef_names():
     }
 
 
-def get_margin_formula_extremes(fit_marge_form_dict) -> Dict:
+def get_margin_formula_extremes(fit_marge_form_dict, transformed_as_formula=True) -> Dict:
     v_to_str = lambda v: ' '.join(v.split()[2:]) if v != 'NULL' else ' 1'
     form_dict = {
         k: '~ ' + ' + '.join(
             [v_to_str(fit_marge_form_dict[e]) for e in l if e in fit_marge_form_dict])
         for k, l in new_coef_name_to_old_coef_names().items()
     }
-    return {k: robjects.Formula(v) for k, v in form_dict.items()}
+    res = OrderedDict()
+    ordered_keys = ['location.fun', 'scale.fun', 'shape.fun']
+    for k in ordered_keys:
+        res[k] = robjects.Formula(form_dict[k]) if transformed_as_formula else form_dict[k]
+    return res
 
 # def conversion_to_FloatVector(data):
 #     """Convert DataFrame or numpy array into FloatVector for r"""
diff --git a/test/test_extreme_fit/test_estimator/test_temporal_estimator/test_gev_temporal_evgam.py b/test/test_extreme_fit/test_estimator/test_temporal_estimator/test_gev_temporal_evgam.py
new file mode 100644
index 00000000..71a69072
--- /dev/null
+++ b/test/test_extreme_fit/test_estimator/test_temporal_estimator/test_gev_temporal_evgam.py
@@ -0,0 +1,93 @@
+import unittest
+
+import numpy as np
+import pandas as pd
+
+from extreme_fit.estimator.margin_estimator.utils import fitted_linear_margin_estimator
+from extreme_fit.estimator.margin_estimator.abstract_margin_estimator import LinearMarginEstimator
+from extreme_fit.model.margin_model.utils import \
+    MarginFitMethod
+from extreme_fit.model.margin_model.linear_margin_model.temporal_linear_margin_models import StationaryTemporalModel, \
+    NonStationaryLocationTemporalModel
+from extreme_fit.model.utils import r, set_seed_r
+from spatio_temporal_dataset.coordinates.abstract_coordinates import AbstractCoordinates
+from spatio_temporal_dataset.coordinates.temporal_coordinates.abstract_temporal_coordinates import \
+    AbstractTemporalCoordinates
+from spatio_temporal_dataset.dataset.abstract_dataset import AbstractDataset
+from spatio_temporal_dataset.spatio_temporal_observations.abstract_spatio_temporal_observations import \
+    AbstractSpatioTemporalObservations
+from test.test_utils import load_non_stationary_temporal_margin_models
+
+
+class TestGevTemporalEvGam(unittest.TestCase):
+
+    def setUp(self) -> None:
+        set_seed_r()
+        r("""
+        N <- 50
+        loc = 0; scale = 2; shape <- 1
+        x_gev <- rgev(N, loc = loc, scale = scale, shape = shape)
+        start_loc = 0; start_scale = 1; start_shape = 1
+        """)
+        # Compute the stationary temporal margin with isMev
+        self.start_year = 0
+        df = pd.DataFrame({AbstractCoordinates.COORDINATE_T: range(self.start_year, self.start_year + 50)})
+        self.coordinates = AbstractTemporalCoordinates.from_df(df)
+        df2 = pd.DataFrame(data=np.array(r['x_gev']), index=df.index)
+        observations = AbstractSpatioTemporalObservations(df_maxima_gev=df2)
+        self.dataset = AbstractDataset(observations=observations, coordinates=self.coordinates)
+        self.fit_method = MarginFitMethod.evgam
+
+    def test_gev_temporal_margin_fit_stationary(self):
+        # Create estimator
+        estimator = fitted_linear_margin_estimator(StationaryTemporalModel, self.coordinates, self.dataset,
+                                                   starting_year=0,
+                                                   fit_method=self.fit_method)
+        ref = {'loc': 0.043852626609682636, 'scale': 2.0696383929640807, 'shape': 0.8290699088428524}
+        for year in range(1, 3):
+            mle_params_estimated = estimator.function_from_fit.get_params(np.array([year])).to_dict()
+            for key in ref.keys():
+                self.assertAlmostEqual(ref[key], mle_params_estimated[key], places=3)
+
+    # def test_gev_temporal_margin_fit_nonstationary(self):
+    #     # Create estimator
+    #     margin_models = load_non_stationary_temporal_margin_models(self.coordinates)
+    #     for margin_model in margin_models:
+    #         estimator = LinearMarginEstimator(self.dataset, margin_model)
+    #         estimator.fit()
+    #         # Checks that parameters returned are indeed different
+    #         mle_params_estimated_year1 = estimator.function_from_fit.get_params(np.array([1])).to_dict()
+    #         mle_params_estimated_year3 = estimator.function_from_fit.get_params(np.array([3])).to_dict()
+    #         self.assertNotEqual(mle_params_estimated_year1, mle_params_estimated_year3)
+    #
+    # def test_gev_temporal_margin_fit_nonstationary_with_start_point(self):
+    #     # Create estimator
+    #     estimator = self.fit_non_stationary_estimator(starting_point=3)
+    #     self.assertNotEqual(estimator.function_from_fit.mu1_temporal_trend, 0.0)
+    #     # Checks starting point parameter are well passed
+    #     self.assertEqual(3, estimator.function_from_fit.starting_point)
+    #     # Checks that parameters returned are indeed different
+    #     mle_params_estimated_year1 = estimator.function_from_fit.get_params(np.array([1])).to_dict()
+    #     mle_params_estimated_year3 = estimator.function_from_fit.get_params(np.array([3])).to_dict()
+    #     self.assertEqual(mle_params_estimated_year1, mle_params_estimated_year3)
+    #     mle_params_estimated_year5 = estimator.function_from_fit.get_params(np.array([5])).to_dict()
+    #     self.assertNotEqual(mle_params_estimated_year5, mle_params_estimated_year3)
+    #
+    # def fit_non_stationary_estimator(self, starting_point):
+    #     margin_model = NonStationaryLocationTemporalModel(self.coordinates,
+    #                                                       starting_point=starting_point + self.start_year)
+    #     estimator = LinearMarginEstimator(self.dataset, margin_model)
+    #     estimator.fit()
+    #     return estimator
+    #
+    # def test_two_different_starting_points(self):
+    #     # Create two different estimators
+    #     estimator1 = self.fit_non_stationary_estimator(starting_point=3)
+    #     estimator2 = self.fit_non_stationary_estimator(starting_point=28)
+    #     mu1_estimator1 = estimator1.function_from_fit.mu1_temporal_trend
+    #     mu1_estimator2 = estimator2.function_from_fit.mu1_temporal_trend
+    #     self.assertNotEqual(mu1_estimator1, mu1_estimator2)
+
+
+if __name__ == '__main__':
+    unittest.main()
-- 
GitLab