library(sp)
library(sf)
library(terra)
data(meuse)
data(meuse.grid)
library(tidyverse)
library(gstat)
library(GeoModels)
Comparaison de l’efficatité de R et Julia en Geostatistiques
L’objectif est de comparer des packages R, et Julia pour du krigeage.
On va utiliser :
- R : gstat
- R : GeoModels
- Julia : GeoStats
On compare les points suivant :
- simplicité des données d’entrée
- concision du code
- vitesse d’exécution
- type d’estimation
Pour chaque package, on calculera pour les mêmes données le variogramme empirique, puis on ajustera un variogramme théorique et on fera un krigeage.
Données
On utilise les données meuse du package sp. Celles-ci contiennent des données de concentration en métaux près de la meuse, avec des coordonnées cartésiennes en m.
Pour travailler sur ces concentrations, on passera tout au log afin de les normaliser.
Pour la visualisation en R, on utilise le package terra.
0 - Le package terra
dernière version : 1.7-39 du 22/06/2023
Travail des données spatiales sous 2 formes : raster et vector.
raster = grilles rectangulaires, adapté aux données continues -> Classe SpatRaster
vector = points, lignes, polygones = données discrètes. -> classe SpatVector
Début de la doc = récap des méthodes importantes (il faut bien, la doc fait 300 pages)
Sert à manipuler, visualiser des objets spatiaux, notamment après les calculs pour simplifier le travail sur les résultats.
Méthode rast = créer un objet de classe SpatRaster. Le type de la 1ere entrée peut varier.
= rast(meuse.grid,type="xyz",crs = "EPSG:2154")
meuseRast = vect(meuse,geom=(c('x','y'))) meuseVect
= rast(select(meuse,x,y,zinc),type="xyz")
meuseRastCd log(meuseRastCd)
class : SpatRaster
dimensions : 3898, 2786, 1 (nrow, ncol, nlyr)
resolution : 1, 1 (x, y)
extent : 178604.5, 181390.5, 329713.5, 333611.5 (xmin, xmax, ymin, ymax)
coord. ref. :
source(s) : memory
name : zinc
min value : 4.727388
max value : 7.516977
= rast(select(meuse,x,y,copper),type="xyz")
meuseRastCu = c(meuseRastCd,meuseRastCd) #methode c() = combinaison de layers meuseRastCuCd
1 - Package R gstat
gstat est disponible sur le CRAN : https://CRAN.R-project.org/package=gstat
On peut y trouver également un tutoriel sur le dataset “meuse” utilisé ici, qui présentent le type de données spatiales utilisées par le package (spatial dataframe, données grillées) et les méthodes permettant de travailler avec (méthodes de visualisation du package sp, ajustement de variogrammes, directionnels ou non, krigeage, simulation conditionnelle).
La version la plus ancienne sur les archives du CRAN date de 2003.
Au départ, les données spatiales supportées étaient celles du package sp, mais les données de type sf le sont également.
INSERER AU BON ENDROIT LES CELLULES
Transformation au format sf
= select(meuse,x,y,cadmium,zinc)
df = select(meuse.grid,x,y)
df_grid <- df %>%
df_sf ::st_as_sf( coords = c('x','y'))
sf
<- sf::st_make_valid(df_sf)
df_sf = df_grid%>% sf::st_as_sf(coords = c('x','y'))%>%sf::st_make_valid()
df_grid_sf ggplot(df, aes(y=log(zinc))) +
geom_boxplot(notch=FALSE) +
labs(y = "Log(Zn concentration)") +
theme_minimal() +
theme(
axis.text.x = element_blank(),
axis.ticks = element_blank(),
axis.line = element_blank()
)
Variogramme empirique
Pour simplifier la comparaison avec le package Julia, on va se contenter d’une moyenne constante sans introduire de prédicteurs : on demande donc log(zinc)~1. Si on avait voulu, comme dans le tutoriel du package, expliquer la moyenne par la distance à la rivière, il faudrait modifier cette formule en log(zinc)~distance après avoir rajouté une colonne distance au sf dataframe.
Appeler plot sur un objet issu de variogram permet d’en obtenir un tracé.
<- Sys.time()
start_time <- variogram(log(zinc)~1 , df_sf,cutoff = 2000)
test <- Sys.time()
end_time - start_time end_time
Time difference of 0.02656889 secs
plot(test)
Le type vgm
Plusieurs modèles de variogrammes spatiaux sont disponibles dans la librairie.
show.vgms()
vgm()) (
short long
1 Nug Nug (nugget)
2 Exp Exp (exponential)
3 Sph Sph (spherical)
4 Gau Gau (gaussian)
5 Exc Exclass (Exponential class/stable)
6 Mat Mat (Matern)
7 Ste Mat (Matern, M. Stein's parameterization)
8 Cir Cir (circular)
9 Lin Lin (linear)
10 Bes Bes (bessel)
11 Pen Pen (pentaspherical)
12 Per Per (periodic)
13 Wav Wav (wave)
14 Hol Hol (hole)
15 Log Log (logarithmic)
16 Pow Pow (power)
17 Spl Spl (spline)
18 Leg Leg (Legendre)
19 Err Err (Measurement error)
20 Int Int (Intercept)
Un variogramme théorique est un objet de type vgm, qui doit être initialisé avec des paramètres avant de l’insérer dans la fonction d’estimation.
Un inconvénient dans la création d’un objet vgm est l’absence de nom pour certains paramètres, il faut se référer à la documentation pour savoir à quel place il faut rentrer quel argument lorsque’on appelle vgm(). Ne pas renseigner un argument initialise celui-ci à NA. Utiliser fit.variogram sur un objet avec des NA fait appel à une étape supplémentaire qui initialise avec des paramètres issus du variogramme empirique. Cela empêche de contrôler les valeurs initiales de l’estimation.
Estimation du variogramme théorique
Les paramètres du variogramme théorique sont ajustés par moindres carrés sur le variogramme empirique.
vgmModelwav <- fit.variogram(test,vgm(‘Wav’))
<- fit.variogram(test, vgm(2, "Exp", 1000))
vgmModelexp vgmModelexp
model psill range
1 Exp 0.6849652 421.2255
<- fit.variogram(test, vgm(10, "Mat", 300, 4.5, kappa = 0.7))
vgmModelmat vgmModelmat
model psill range kappa
1 Nug 0.05112814 0.0000 0.0
2 Mat 0.62395526 340.0403 0.7
<- fit.variogram(test,vgm('Mat'))
vgmModelNA vgmModelNA
model psill range kappa
1 Nug 0.001864289 0.0000 0.0
2 Mat 0.684275121 424.4234 0.5
<- Sys.time()
start_time <- fit.variogram(test,vgm('Wav'))
vgmModelwav <- Sys.time()
end_time - start_time end_time
Time difference of 0.004230022 secs
vgmModelwav
model psill range
1 Nug 0.1556503 0.0000
2 Wav 0.4407740 643.9395
La fonction plot() appelée sur un modèle estimé trace en continu le variogramme théorique et en points le variogramme empirique.
plot(test, vgmModelmat)
plot(test, vgmModelwav)
Krigeage
<- Sys.time()
start_time <- krige(log(zinc)~1 , df_sf, df_grid_sf, model = vgmModelmat) krig
[using ordinary kriging]
<- Sys.time()
end_time - start_time end_time
Time difference of 0.5332577 secs
= data.frame(x=meuse.grid$x,y=meuse.grid$y,pred=krig$var1.pred)
Kres_pred <- terra::rast(Kres_pred, type="xyz", crs="EPSG:2154")
krig_res
= data.frame(x=meuse.grid$x,y=meuse.grid$y,pred=krig$var1.var)
Kres_var <- terra::rast(Kres_var, type="xyz", crs="EPSG:2154")
krig_var
plot((krig_res), main = 'Krigeage de log(zinc) - gstat')
plot(krig_var,main = 'Erreur de krigeage pour log(zinc) - gstat')
2 - Package R GeoModels
GeoModels est un package relativement récent (2022), il prend la suite de CompRandFld suite à une mise à jour majeure de R.
Il est disponible sur le CRAN :
https://CRAN.R-project.org/package=GeoModels
Le package propose des fonctions pour de la simulation, de l’inférence et de la prédiction de champs spatiaux et spatio-temporels, univarié ou multivarié. Il contient des méthode spour des champs Gaussiens et non Gaussiens.
On peut travailler en coordonnées euclidiennes et sphériques.
GeoModels propose de faire des modèles spatiaux et spatiotemporels.
Les estimations se font par maximisation de vraisemblance composite.
Du fait de la variété de modèles et de données possibles en entrée (spatial, spatiotemporel, 1 ou plusieurs variables …), chaque fonction est définie généralement pour prendre en entrée des données (notamment data et coordonnées) sous plusieurs types. Dans tous les cas, on ne dépend pas de structures “évoluées”, on n’a besoin que de vecteurs et de matrices. En contrepartie, on est probablement moins libres sur les systèmes de coordonnées.
Les données en entrée doivent être, si on fait du spatial uniquement :
- data : un vecteur de taille \(d\)
- coordx : une matrice \(d \times 2\) si on met directement les coordonnées, ou un vecteur de taille \(d\) si on sépare les coordonnées
- coordy : NULL si on a déjà mis les 2 coordonnées dans coordx, vecteur de taille \(d\) sinon.
Il n’y a donc pas besoin de gérer des types de données spatiales comme dans gstat.
=log(df$zinc)
data
= df$x
coordx
= df$y coordy
Variogramme empirique
# Variogramme empirique :
<- Sys.time()
start_time = GeoVariogram(data, coordx, coordy=coordy,numbins = 16,maxdist = 2000)
emp_vario
<- Sys.time()
end_time - start_time end_time
Time difference of 0.009788275 secs
plot(emp_vario,main = "Variogramme empirique Zinc - GeoModels")
Modèles supportés
Voir documentation, fonction “GeoCovMatrix”
CorrParam, et NuisParam permettent d’obtenir la listes des paramètres requis pour les modèles.
NuisParam('Gaussian')
[1] "mean" "nugget" "sill"
CorrParam("Exp")
[1] "scale"
CorrParam('Matern')
[1] "scale" "smooth"
Estimation du variogramme théorique
L’estimation nécessite de donner en entrée deux listes, fixed et start, correspondant aux paramètres fixés et libres dans l’ajustement.
ATTENTION : si on ne fixe rien, il ne faut pas une liste vide mais un NULL, ou alors ne pas renseigner l’argument (NULL est la valeur par défaut)
L’algorithme vérifiera automatiquement que tous les paramètres nécessaires sont remplis. Il n’y a pas comme dans gstat de méthode qui rempli automatiquement des oublis, il faut vérifier avec les fonctions précédentes qu’on a bien tout initialisé.
= NULL
fixed
= list( mean = 0 , sill=0.5,smooth=0.7,scale = 300,nugget = 0.044)
start
= GeoFit(data, coordx=coordx, coordy=coordy, fixed = fixed, start=start,
modelemat
corrmodel="Matern",distance="Eucl",maxdist = 2000)
modelemat
##################################################################
Maximum Composite-Likelihood Fitting of Gaussian Random Fields
Setting: Marginal Composite-Likelihood
Model: Gaussian
Type of the likelihood objects: Pairwise
Covariance model: Matern
Optimizer: Nelder-Mead
Number of spatial coordinates: 155
Number of dependent temporal realisations: 1
Type of the random field: univariate
Number of estimated parameters: 5
Type of convergence: Successful
Maximum log-Composite-Likelihood value: -37443.52
Estimated parameters:
mean nugget scale sill smooth
5.8692 0.9994 266.5697 0.5482 68.5193
##################################################################
# Vario fit : il faut mettre en entrée des valeurs de départ et des valeurs qu'on fixe pour les paramètres
= list(nugget = 0.044) #fixe la valeur obtenue avec gstat
fixed
= list( mean = 0 , sill=0.5,smooth=0.7,scale = 300)
start
= GeoFit(data, coordx=coordx, coordy=coordy, fixed = fixed, start=start,
modelemat2
corrmodel="Matern",distance="Eucl",maxdist = 2000)
modelemat2
##################################################################
Maximum Composite-Likelihood Fitting of Gaussian Random Fields
Setting: Marginal Composite-Likelihood
Model: Gaussian
Type of the likelihood objects: Pairwise
Covariance model: Matern
Optimizer: Nelder-Mead
Number of spatial coordinates: 155
Number of dependent temporal realisations: 1
Type of the random field: univariate
Number of estimated parameters: 4
Type of convergence: Successful
Maximum log-Composite-Likelihood value: -37218.17
Estimated parameters:
mean scale sill smooth
5.8673 114.4160 0.5495 1.5971
##################################################################
= NULL
fixed
= list( mean = 0 , sill=0.5,scale = 300,nugget = 0.044)
start
<- Sys.time()
start_time = GeoFit(data, coordx=coordx, coordy=coordy, fixed = fixed, start=start,
modelewav
corrmodel="Wave",distance="Eucl",maxdist = 2000)
<- Sys.time()
end_time - start_time end_time
Time difference of 0.755408 secs
modelewav
##################################################################
Maximum Composite-Likelihood Fitting of Gaussian Random Fields
Setting: Marginal Composite-Likelihood
Model: Gaussian
Type of the likelihood objects: Pairwise
Covariance model: Wave
Optimizer: Nelder-Mead
Number of spatial coordinates: 155
Number of dependent temporal realisations: 1
Type of the random field: univariate
Number of estimated parameters: 4
Type of convergence: Successful
Maximum log-Composite-Likelihood value: -37191.31
Estimated parameters:
mean nugget scale sill
5.8669 0.3839 200.4165 0.5509
##################################################################
On peut comparer les modèles que l’on vient de faire avec le variogramme empirique :
# Comparaison avec l'empirique matern :
GeoCovariogram(modelemat, distance="Eucl",show.vario= TRUE,vario = emp_vario)
GeoCovariogram(modelemat2, distance="Eucl",show.vario= TRUE,vario = emp_vario)
# Comparaison avec l'empirique Wave :
GeoCovariogram(modelewav, distance="Eucl",show.vario= TRUE,vario = emp_vario)
Krigeage
= data.frame (x =meuse.grid$x,y= meuse.grid$y)
locskrig
<- Sys.time()
start_time = GeoKrig(loc= locskrig,coordx=coordx, coordy=coordy,corrmodel = 'Wave',param = c(modelemat$fixed,modelewav$param),mse =TRUE,data=data)
krig_geomodels
<- Sys.time()
end_time - start_time end_time
Time difference of 0.7958126 secs
= cbind(coordx,coordy)
coords
= data.frame(x=meuse.grid$x,y=meuse.grid$y,pred=krig_geomodels$pred)
Kres_pred_geo
<- terra::rast(Kres_pred_geo, type="xyz", crs="EPSG:2154")
krig_res_geo
= data.frame(x=meuse.grid$x,y=meuse.grid$y,pred=krig_geomodels$mse)
Kres_var_geo
<- terra::rast(Kres_var_geo, type="xyz", crs="EPSG:2154")
krig_var_geo
plot((krig_res_geo), main = 'Krigeage de log(zinc) - GeoModels')
plot(krig_var_geo,main = 'Erreur de krigeage pour log(zinc) - GeoModels')
3 - Package Julia GeoStats
Julia est un langage de programmation de haut niveau, performant et dynamique pour le calcul scientifique, avec une syntaxe familière aux utilisateurs d’autres environnements de développement similaires (Matlab, R, Scilab, Python, etc.). Il fournit un compilateur sophistiqué, un système de types dynamiques avec polymorphisme paramétré, une exécution parallèle distribuée, des appels directs de fonctions C, Fortran et Python. (source Wikipedia)
Un avantage de Julia est la possibilité d’écrire en LaTeX dans les lignes de code.
GeoStats est un package récent (avril 2015), sa dernière mise à jour date de 3 mois, c’est le package majeur en statistiques spatiales de Julia.
Il est disponible sur le :
https://juliaearth.github.io/GeoStats.jl/stable/
Pour l’installer faire la commande suivante dans le terminal de votre IDE : ]add GeoStats
Le package propose des fonctions pour de la simulation, de l’inférence et de la prédiction de champs spatiaux
On peut travailler en coordonnées euclidiennes et sphériques.
GeoStats propose de faire des modèles spatiaux. Sa particularité va être de tester tous les variogrammes disponibles (dans une même exécution) dans le package pour obtenir le plus optimisé avec les paramètres qui fonctionnent le mieux.
Bibliothèques Julia
# Import package
=GeoDataFrames; import WGLMakie as Mke using GeoDataFrames, Distributed, StatsAPI, Plots, DelimitedFiles, DataFrames, CSV, GeoStats, Rasters, Shapefile,GeoTables, CairoMakie; const GDF
Importer les données
# Get the current working directory
= pwd()
current_directory
#import data
= CSV.read(joinpath(current_directory,"meuse.txt"),DataFrame;delim="," )
meuse = CSV.read(joinpath(current_directory, "meuse.grid.txt"),DataFrame; delim=",")
meuse_grid = georef(CSV.File(joinpath(current_directory,"meuse.txt")),(:x,:y,:cadmium))
meuse_geom
#describe data
describe(meuse)
describe(meuse_grid)
Transformation en données spatiale
#add log zinc to the data
= log.(meuse.zinc)
logmeuse = logmeuse
meuse.logzinc
#create coord of data meuse
= [(x, y) for (x, y) in zip(meuse.x, meuse.y)]
coord #create spatial object of meuse
= georef(meuse[:,["x","y","logzinc"]],coord)
meuselog
# list of properties with coordinates
= (logzinc=meuse.logzinc,)
props = georef(props, coord) 𝒟
Variogramme
Le fit pour tester les différents variogramme va nous donner le SineHoleVariogram avec tous les paramètres optimisé. On décide à la base d’utiliser ce dernier mais il s’avère qu’il ne donne pas de bon résultat pour le Krigeage. On utilisera donc un variogramme de Matérn. Si vous voulez observer ce dernier dans le krigeage il suffira de mettre dans la fonction kriging, le variogramme désiré.
# empirical variogram
@time empvario = EmpiricalVariogram(meuselog, :logzinc, maxlag = 2000.)
## plot of empirical variogram
# Mke.plot(empvario) plot interactive
# CairoMakie.plot(empvario) #plot sans interaction
## plot theorical variogram
# CairoMakie.plot(MaternVariogram(range=691, nugget=0.04))
# CairoMakie.plot(SineHoleVariogram(range=691, nugget=0.25))
#fit theorical and empirical variogramand
@time fitvario = fit(Variogram, empvario)
# CairoMakie.plot(empvario)
# CairoMakie.plot!(fitvario, maxlag = 2000.)
# CairoMakie.current_figure()
Création de la grille
# grid creation
# Convert the x and y coordinates to numeric arrays
= Float64.(meuse.x)
x_coords = Float64.(meuse.y)# Create the Cartesian grid dimensions, origin, and spacing
y_coords = (100, 100) # Number of grid points in x and y directions
dims = (minimum(x_coords), minimum(y_coords)) # Lower left corner of the grid
origin = ((maximum(x_coords) - minimum(x_coords)) / (dims[1] - 1),
spacing maximum(y_coords) - minimum(y_coords)) / (dims[2] - 1))
(# Cell spacing in x and y directions# Create the CartesianGrid
= CartesianGrid(dims, origin, spacing) 𝒢
Krigeage
= EstimationProblem(𝒟, 𝒢, :logzinc)
𝒫
@time 𝒮 = Kriging(:logzinc => (variogram=MaternVariogram(nugget = 0.21,sill = 0.59,range=706.78),))
# perform estimation
@time Ω = solve(𝒫, 𝒮)
# CairoMakie.plot(Ω.geometry, color = Ω.logzinc_variance)
Affichage du Krigeage
#Export Kriging
GeoTables.save(joinpath(current_directory,"data_geostats_julia/Kriging2.shp"), Ω,force=true )
#read kriging
= Shapefile.Handle(joinpath(current_directory,"data_geostats_julia/Kriging2.shp")).shapes
shape # read grid
= Shapefile.Handle(joinpath(current_directory,"data_geostats_julia/boundary1.shp")).shapes
grid_shp
# Define a reducer function (e.g., to sum values within each cell)
reducer(cell_values) = sum(cell_values)
= rasterize(shape, fill=Ω.logzinc, reducer = reducer, size=(100, 100))
rasterized_krig Plots.plot(rasterized_krig)
Burning each geometry to a BitArray slice... 0%| | ETA: 0:48:28
Burning each geometry to a BitArray slice... 100%|██████████████████████████████████████████████████| Time: 0:00:00
Reducing... 4%|██ | ETA: 0:00:02
Reducing... 47%|███████████████████████▌ | ETA: 0:00:00
Reducing... 89%|████████████████████████████████████████████▌ | ETA: 0:00:00
Reducing... 100%|██████████████████████████████████████████████████| Time: 0:00:00
#Crop
= mask(rasterized_krig,with=grid_shp)
masked Plots.plot(masked, title = "Krigeage logzinc")
4 - Comparaison
Package | R : gstat | R : GeoModels | Julia : GeoStats |
---|---|---|---|
Type de données d’entrée | sp, sf | matrices, vecteurs |
dataframes Coordonées de type CartesianGrid Inconvénient : peu de documentation - difficile à prendre en main |
Documentation | CRAN |
CRAN https://vmoprojs.github.io/GeoModels-page/ |
https://github.com/JuliaEarth/GeoStats.jl https://juliapackages.com/p/geostats |
Estimation de paramètres du variogramme |
Moindres carrés sur variogramme empirique 1 modèle à la fois au choix |
Vraisemblance composite sur les données 1 modèle à la fois Paramètres fixes ou variables au choix |
Moindres carrés sur le variogramme empirique Tous les modèles disponibles à la fois |
Vitesse d’exécution - estimation du variogramme empirique | 0.05s | 0.07s | 0.012s |
Vitesse d’exécution - estimation du variogramme théorique | 0.04s | 1.15s | 0.05s (TOUS LES MODELES) |
Vitesse d’exécution - krigeage | 0.42s | 0.87s |
0.08s (Kriging) 10.6s (solve) |