Chapitre3 Preparation des données SOL
3.1 IMPORTATION DES DONNEES SOL SOUS R
Ce fichier présente pas à pas la préparation des données Sols sous R. Chaque étape est détaillée et commentée. Il est possible de faire des codes plus optimisés mais étant moins pédagogiques, nous avons choisis de vous présenter des codes assez détaillés pour une meilleure compréhension des étapes. Des options vous seront également proposées si vous souhaitez aller plus loin.
3.1.1 Les données Sol
Les données sols sont issues des fichiers d’export en format CSV de l’interface DoneSol-web https://dw4.gissol.fr/login. Sur le menu d’accueil de l’interface DoneSol-web, il suffit de choisir “Exporter” et de sélectionner la ou les étude(s) à exporter pour obtenir chaque table de DoneSol en format CSV contenant les données des études choisies. Avant d’exporter, il faut choisir le type de séparateur pour l’export en CSV : “,” ou “;”. Le mieux est de choisir “;”.
Pour en savoir plus sur l’export des données depuis l’interface DoneSol-web, vous pouvez consulter la vidéo “17- Les outils d’export des données” sur https://www.gissol.fr/outils/formations-en-ligne-videos-sur-la-saisie-des-donnees-dans-donesol-web-3638.
Pour en savoir plus sur les tables de DoneSol, vous pouvez consulter le dictionnaire de données de DoneSol téléchargeable à partir du menu d’accueil de l’interface DoneSol-web.
Nous importons ici sous R les tables des données ponctuelles de DoneSols. Les fichiers doivent avoir les mêmes noms que dans le dictionnaire DoneSol. L’ensemble des fichiers .csv doivent être stockés dans un même dossier que l’on nommera “donesol”. Pour cette importation, nous utilisons le package dplyr :
ⓘ NOTE
Un package est un ensemble de fonctions, de données et de documentation développé par la communauté R pour répondre à des besoins spécifiques et qui améliorent ou étendent les fonctionnalités de base de R.
Par la suite, il faut bien préciser le type de séparation des champs tel que nous l’avons choisi lors de l’export sous DoneSol-web.
Les données sont donc en format DoneSol et sont réparties dans différentes tables reliées entre elles. Les données utilisées sont des données ponctuelles issues de la description de profils et de sondages sur le terrain. Le schéma suivant présente la structure DoneSol pour les données ponctuelles :

Pour relier deux tables, nous utilisons les champs identiques dans chacune des tables. Par exemple, pour relier la table POI contenant les XY des points et la table PROFIL, nous utilisons le champ ID_PROFIL. Il est donc important de conserver ces champs tout au long de notre travail.
3.1.2 Importation de la table PROFIL
La table PROFIL contient la description synthétique du profil de sol (fosse, sondage à la tarrière…). La table PROFIL décrit la localisation géographique du profil, son environnement, sa situation géomorphologique, son organisation géologique, les différentes origines de sa différenciation en horizons, ses caractéristiques hydriques et sa classification.
Pour importer la table PROFIL, nous devons spécifier le chemin du fichier .csv correspondant. Pour cela, le plus simple est d’avoir l’ensemble de vos fichiers dans le même dossier que votre projet R.
De même, il est important de conserver les champs constituant les clefs des tables. Cette information est disponible dans le dictionnaire de données DoneSol et peut concerner 1 ou plusieurs champs selon les tables. Par exemple, pour la table PROFIL il s’agit du champ ID_PROFIL ; pour la table HORIZON il s’agit des champs ID_PROFIL et NO_HORIZON. Ces champs vous permettrons de faire le lien entre les tables.
profil <- read.csv2('donnees/jour1/donesol/profil.csv', sep=";")Nous sélectionnons les champs que nous souhaitons garder pour la suite. Vous pouvez modifier ce code pour sélectionner les champs de la table PROFIL qui vous intéressent. Il est conseillé de conserver le nom des champs identique à celui du dictionnaire de données pour éviter des confusion par la suite. Il est important aussi de ne pas oublier un champ qui nous sera utile par la suite. Cette étape est importante et devra s’appuyer sur le dictionnaire de données DoneSol.
profil <- data.frame("id_profil"=profil$id_profil, # identifiant du profil
"altitude"=profil$altitude, # altitude du profil
"date_p"=profil$date_p, # date de description du profil
"occup_codee"=profil$occup_codee, # occupation du sol
"drai_nat_p"=profil$drai_nat_p, # drainage naturel
"arret"=profil$arret, # cause d'arrêt de la description
"prof_arret"=profil$prof_arret, # profondeur maximale observée
"rp_95_ger"=profil$rp_95_ger, # GER selon le RP 95
"rp_2008_ger"=profil$rp_2008_ger, # GER selon le RP 2008
"cpcs_nom"=profil$cpcs_nom) # nom du sol dans la classification CPCS
str(profil) # visualiser la liste des champs de la table PROFIL avec les valeurs des premiers enregistrements## 'data.frame': 2894 obs. of 10 variables:
## $ id_profil : int 110185 132400 96907 84414 131978 90327 137960 110218 90246 96908 ...
## $ altitude : chr "" "" "105.0" "151.0" ...
## $ date_p : chr "01/10/1975" "15/04/1990" "14/08/1986" "17/09/1991" ...
## $ occup_codee: int 600 500 500 500 500 130 100 600 500 5010 ...
## $ drai_nat_p : int NA NA NA NA 7 NA 6 NA NA NA ...
## $ arret : int 1 2 1 1 1 1 1 1 1 1 ...
## $ prof_arret : int NA NA NA NA NA NA NA NA NA NA ...
## $ rp_95_ger : int NA NA 16 NA NA NA NA NA NA 16 ...
## $ rp_2008_ger: logi NA NA NA NA NA NA ...
## $ cpcs_nom : int 7114 7110 7110 2430 7114 7110 7114 7126 7114 7110 ...
ⓘ NOTE
Vous pouvez aussi obtenir le même résultat avec un code plus simple en indiquant le numéro de la colonne des variables que vous souhaitez conserver. Bien sûr, cette sélection doit être faite sur le fichier PROFIL brut importé au départ.
Le code suivant donne le numéro de la colonne du champ ID_PROFIL dans la table PROFIL :
which(colnames(profil) == “id_profil”)
Pour visualiser les numéros de toutes les colonnes de la table en une seule fois : names(PROFIL)
Une fois les numéros de colonnes identifiés, on peut les sélectionner avec :
profil <- profil[,c(1,5,6,33,82,86,87,92,96,91)]
3.1.2.1 Option 1 : Simplification de l’occupation du sol
Si nous souhaitons simplifier les occurences de l’occupation du sol, nous pouvons les regrouper en 8 classes : Forêts, Prairies permanentes, Cultures, Vergers, Maraîchage, Autres, Vignes, Environnements naturels. Nous y rajoutons une 9ème classe pour les données manquantes que nous nommerons ND (comme No Data - Pas de données).
Les classes souhaitées sont :
1- Forêts
2- Prairies permanentes
3- Cultures
4- Vergers
5- Maraîchage
6- Autres
7- Vignes
8- Environnements naturels
9- ND
# création et recodage du champ landuse contenant l'occupation du sol
profil <- profil %>%
mutate(
occup_codee = as.numeric(occup_codee),
landuse = case_when(
# Classe 1
between(occup_codee, 2000, 4200) ~ 1,
# Classe 2
between(occup_codee, 600, 603) ~ 2,
# Classe 3 (plusieurs intervalles)
between(occup_codee, 100, 509) ~ 3,
between(occup_codee, 700, 905) ~ 3,
between(occup_codee, 1400, 1600) ~ 3,
between(occup_codee, 6000, 6080) ~ 3,
# Classe 4
between(occup_codee, 1100, 1164) ~ 4,
# Classe 5
between(occup_codee, 1000, 1050) ~ 5,
# Classe 6
between(occup_codee, 1200, 1250) ~ 6,
between(occup_codee, 5200, 5250) ~ 6,
# Classe 7
between(occup_codee, 1300, 1303) ~ 7,
# Classe 8
between(occup_codee, 5000, 5151) ~ 8,
between(occup_codee, 7000, 13100) ~ 8,
# Sinon NA
TRUE ~ NA_real_
),
landuse = factor(landuse)
)
head(profil$landuse) # permet de connaître les classes présentes dans le jeu de données## [1] 2 3 3 3 3 3
## Levels: 1 2 3 4 5 8
summary(profil$landuse) # et de connaitre le nombre de profils pour chaque occupation des sols## 1 2 3 4 5 8 NA's
## 8 332 2166 14 14 84 276
# La classe "NA's" correspond à des profils sans occupation du sol.
# Ces informations sont ensuite utilisée pour affecter les labels des classes
profil$landuse<-factor(profil$landuse,
labels=c("Forets",
"Prairies_permanentes",
"Cultures",
"Vergers",
"Maraichage",
"Environnements_naturels")
)
# Nous mettons les données manquantes (NA's) dans la classe ND.
profil$landuse<-factor(ifelse(is.na(profil$landuse),
"ND",
paste(profil$landuse)),
levels = c(levels(profil$landuse),
"ND"))
summary(profil$landuse) # Affiche le nombre de profils par occupation du sol## Forets Prairies_permanentes Cultures
## 8 332 2166
## Vergers Maraichage Environnements_naturels
## 14 14 84
## ND
## 276
# Présenter l'occupation des sols sous forme de graphique
ggplot(profil, aes(x=factor(landuse)))+
geom_bar(stat="count", width=0.7,fill= "darkolivegreen4")+
ylab("Nombre de profils")+
xlab("Occupation du sol") +
scale_x_discrete(labels = c("Forets" = "Forêts",
"Prairies_permanentes" = "Prairies\npermanentes",
"Cultures" = "Cultures",
"Vergers" = "Vergers",
"Maraichage"="Maraîchage",
"Environnements_naturels"="Environnements\nnaturels",
"ND"="Non connue")) +
# ajout du nombre de profils par classe
geom_text(aes(label = after_stat(count)),
stat = "count",
vjust=0) +
theme_minimal()
3.1.2.2 Option 2 : Simplification des types de sols (GER)
Dans DoneSol, il y a 3 classifications couramment utilisées en France : CPCS, RP95 et RP2008. Le choix de la classification dépend de la date de description. Les données les plus ancienens sont décrites en CPCS et les plus récentes en RP2008. Nous souhaitons ici utiliser ces trois classifications pour créer un champ contenant le type de sol (GER = Grands Ensembles de Références). Nous allons partir de la classification la plus récente (RP2008) puis nous complétons les valeurs manquantes avec le RP95 puis avec la CPCS recodée en RP.
Dans DoneSol, le RP2008 et le RP95 sont des champs codés. Nous allons commencer par remplacer le code par le nom du GER. Nous allons utiliser le fichier “dico_profil_horizon.xlsx” contenant la correspondance entre les code et leur signification.
# chargement du fichier contenant la signification des codes
library(readxl)
dico <- read_xlsx('donnees/jour1/dico_profil_horizon.xlsx')
dico<-data.frame(dico)
# sous-ensembles du dico par classification de sol puis mise en facteurs des codes pour chaque classification
dico_ger2008<-subset(dico, variable=="rp_2008_ger")
dico_ger2008<-data.frame("rp_2008_ger"=dico_ger2008$code, "label_RP2008"=dico_ger2008$label)
dico_ger2008$rp_2008_ger<-as.factor(dico_ger2008$rp_2008_ger)
dico_ger95<-subset(dico, variable=="rp_95_ger")
dico_ger95<-data.frame("rp_95_ger"=dico_ger95$code, "label_RP95"=dico_ger95$label)
dico_ger95$rp_95_ger<-as.factor(dico_ger95$rp_95_ger)
dico_cpcs<-subset(dico, variable=="cpcs_nom")
dico_cpcs<-data.frame("cpcs_nom"=dico_cpcs$code, "label_cpcs"=dico_cpcs$label)
dico_cpcs$cpcs_nom<-as.integer(dico_cpcs$cpcs_nom)
# Mise en facteur des champs RP de la table PROFIL
profil$rp_2008_ger<-as.factor(profil$rp_2008_ger)
profil$rp_95_ger<-as.factor(profil$rp_95_ger)
# Nous rajoutons la signification des codes (labels) pour les 3 classifications dans la table PROFIL.
# Pour cela nous utilisons la jointure à gauche (voir note sur les jointures).
library(dplyr)
a<-dplyr::left_join(profil, dico_ger2008, by="rp_2008_ger")
b<-dplyr::left_join(a, dico_ger95, by="rp_95_ger")
profil<-dplyr::left_join(b, dico_cpcs, by="cpcs_nom")
# Nous supprimons les fichiers a et b devenus inutiles
rm(a)
rm(b)ⓘ NOTE SUR LES JOINTURES
Une tâche courante dans l’analyse de données consiste à rassembler différents ensembles de données afin de pouvoir combiner les colonnes de deux (ou plusieurs) tableaux. Pour ce faire, on peut utiliser la famille de fonctions “join” du package “dplyr”. Il existe différents types de jointures entre une table X et une table Y :
La partie des tables conservée par la jointure est indiquée en violet sur le schéma. Afin de pouvoir effectuer ces jointures, les tables X et Y doivent avoir un champ commun. Généralement ce champ commun constitue la clef des tables à joindre. Par exemple : id_profil pour la table PROFIL. Pour DoneSol, les clefs des tables sont indiquées dans le dictionnaire de données.
Nous rajoutons un champ GER que nous remplissons avec les valeurs des champs cpcs, rp_95_ger et rp_2008_ger. Pour cela, nous allons utiliser la fonction “ifelse”.
ⓘ NOTE SUR LA FONCTION ifelse
ifelse(condition, valeur_si_vrai, valeur_si_faux)
avec :
- condition : une expression logique qui est évaluée pour chaque élément.
- valeur_si_vrai : la valeur à retourner si la condition est vraie.
- valeur_si_faux : la valeur à retourner si la condition est fausse.
profil$GER<-"NA" # création du champ GER vide (valeurs manquantes notées NA).
profil$GER<-ifelse(is.na(profil$GER), profil$GER, profil$label_RP2008) # remplace les NA par les valeurs RP2008
profil$GER<-ifelse(is.na(profil$GER), profil$label_RP95, profil$GER) # remplace les NA par les valeurs RP95
# Nous transformons la CPCS en RP dans un nouveau champ GER_cpcs:
profil$GER_cpcs<-profil$cpcs_nom
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("1111","2412", "1210"), "LITHOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% "9224", "FERSIALSOL_ELUVIQUE",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("9225","9222","9221", "9220","9215", "9214", "9213", "9212", "9211", "9210", "9200", "9000"), "FERSIALSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("1112", "1140", "1141", "1142", "2440", "2441", "2442", "2443", "2410", "2411", "1100", "1000", "1150", "1151", "1152", "2450", "2451", "2452", "2453", "2400", "1110", "1162"), "REGOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("1120", "1121", "1122", "2420", "2421","2422", "2423"), "FLUVIOSOL",profil$cpcs_nom)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("10000", "10100", "10110", "10111", "10112", "10113", "10114", "10115", "10120", "10121", "10122", "10123", "10124", "10130", "10131", "10132", "10133", "10134", "10140", "10141", "10142", "10143", "10200", "10210", "10211", "10212", "10213", "10214", "10215", "10216", "10217", "10220", "10221", "10222", "10223", "10230", "10231", "10232", "10233", "10234", "10235", "10240", "10241", "10242", "10243", "10244", "10245", "10246", "10250", "10251", "10252", "10253", "10300", "10310", "10311", "10312", "10313", "10314", "10315", "10316", "10317", "10320", "10321", "10322", "10330", "10331", "10332", "10333", "10334", "10335", "10340", "10341", "10342", "10343", "10344", "10350", "10351", "10352", "10353", "10360", "10361", "10362", "10363", "10364"), "FERRALLITISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("11000", "11320", "11322", "11340","11341", "11342", "11350", "11360", "11361", "11362", "11300"), "REDOXISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("11100", "11110", "11111", "11112", "11120", "11121", "11122", "11130", "11131", "11132"), "HISTOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("2210", "2211", "2212", "2213", "2000", "2200", "2210","2211","2212","2213", "2220","2230", "2320"), "RANKOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("1130", "1131", "1132", "2430", "2431", "2432","2433"), "COLLUVIOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("3000", "3100", "3110", "3111", "3112", "3113", "3114", "3120", "3121", "3122", "3123", "3124", "3125", "3200", "3210", "3211", "3212", "3213", "3214", "3220", "3221", "3222", "3223", "3224", "3225"), "VERTISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("4000", "4100", "4110", "4200", "4210", "4211", "4212", "4213", "4220", "4221", "4222", "4223"), "ANDOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5110", "5111", "5112", "5113", "5114", "5115"), "RENDOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5120", "5121", "5122", "5123", "5124", "5220", "5221"), "CALCOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5130"), "DOLOMITOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5210", "5211", "5212", "5213", "5200"), "CALCISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5230", "5231", "5232"), "LEPTISMECTISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5300", "5310", "5320", "5321", "5322"), "GYPSOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("7110", "7111", "7113", "7114", "2140", "6231", "6232", "6233", "6234", "6235", "6410", "6411", "7000", "710", "7100", "7115", "7200", "7300", "7400", "7410", "7411", "7412","7413", "7414"), "BRUNISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("7112"), "ALOCRISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("7121"), "NEOLUVISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("7120", "7122", "7123", "7124", "7125", "7126", "7127"), "LUVISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("8000", "8100", "8110", "8111", "8112", "8113", "8114", "8115", "8120", "8121", "8122", "8123", "8124", "8125", "8130", "8131", "8132", "8140", "8141", "8142", "8200", "8210", "8220", "8300", "8310", "8311", "8312", "8320", "8330"), "PODZOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("11200", "11210", "11211", "11212", "11213", "11214", "11220", "11221", "11310", "11311","11312", "11313", "11314", "11330"), "REDUCTISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("11321"), "PELOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("13000", "13100", "13200"), "PLANOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("12000", "12200", "12210", "12211", "12212", "12220", "12221", "12222", "12230", "12231", "12232"), "SODISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("12100", "12110", "12111", "12112", "12113", "12114"), "SALISOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("2130", "2120"), "CRYOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("2460", "2461", "2462", "2463"), "ANTHROPOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("5000","5100", "1721", "5215", "6315", "6320","6321", "6322", "6323", "6324", "6325", "7116", "7170", "7425", "7710"), "ND",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("1111","2412", "1210"), "LITHOSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% "9224", "FERSIALSOL_ELUVIQUE",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("9225","9222","9221", "9220","9215", "9214", "9213", "9212", "9211", "9210", "9200", "9000"), "FERSIALSOL",profil$GER_cpcs)
profil$GER_cpcs<-ifelse(profil$GER_cpcs %in% c("1112", "1140", "1141", "1142", "2440", "2441", "2442", "2443", "2410", "2411", "1100", "1000", "1150", "1151", "1152", "2450", "2451", "2452", "2453", "2400", "1110", "1162"), "REGOSOL",profil$GER_cpcs)
# Nous remplaçons les valeurs manquantes (NA) du champ GER par la valeur du champ GER_cpcs
profil$GER<-ifelse(is.na(profil$GER), profil$GER_cpcs, profil$GER) # remplace les NA par les valeurs CPCS transformées en RP
str(profil$GER)## chr [1:2894] "BRUNISOL" "BRUNISOL" "BRUNISOL" "COLLUVIOSOL" "BRUNISOL" ...
profil$GER<-as.factor(profil$GER) # Mise du champ GER en facteur
# Nous pouvons utiliser cette information pour refaire des regroupements si besoin.
table(profil$GER) # nombre de profils par GER##
## ALOCRISOL ALOCRISOL-REDOXISOL ANTHROPOSOL ARTIFICIEL
## 15 1 1
## BRUNISOL BRUNISOL MESOSATURE BRUNISOL OLIGO-SATURE
## 1815 18 3
## BRUNISOL SATURE BRUNISOL-REDOXISOL CALCISOL
## 9 44 226
## CALCISOL-REDOXISOL CALCOSOL COLLUVIOSOL
## 1 11 192
## COLLUVIOSOL-REDOXISOL CRYOSOL FLUVIOSOL
## 8 2 83
## FLUVIOSOL-REDOXISOL HISTOSOL MESIQUE LUVISOL
## 1 1 98
## LUVISOL DEGRADE LUVISOL TRONQUE LUVISOL-REDOXISOL
## 1 1 41
## ND NEOLUVISOL NEOLUVISOL-REDOXISOL
## 5 189 20
## PELOSOL PLANOSOL TYPIQUE PODZOSOL
## 9 1 4
## PODZOSOL MEUBLE RANKOSOL REDOXISOL
## 1 30 12
## REDUCTISOL REDUCTISOL STAGNIQUE REGOSOL
## 4 1 3
## RENDOSOL
## 2
# remplace les valeurs manquantes (NA) par ND pour le champ GER de la table profil
profil$GER[is.na(profil$GER)]<-"ND"
# Supression des champs inutiles dans la table profil
profil <- subset(profil, select = -c(cpcs_nom, rp_95_ger, rp_2008_ger, label_RP2008, label_RP95, label_cpcs, GER_cpcs, occup_codee))
# visualisation des champs de la table PROFIL
str(profil)## 'data.frame': 2894 obs. of 8 variables:
## $ id_profil : int 110185 132400 96907 84414 131978 90327 137960 110218 90246 96908 ...
## $ altitude : chr "" "" "105.0" "151.0" ...
## $ date_p : chr "01/10/1975" "15/04/1990" "14/08/1986" "17/09/1991" ...
## $ drai_nat_p: int NA NA NA NA 7 NA 6 NA NA NA ...
## $ arret : int 1 2 1 1 1 1 1 1 1 1 ...
## $ prof_arret: int NA NA NA NA NA NA NA NA NA NA ...
## $ landuse : Factor w/ 7 levels "Forets","Prairies_permanentes",..: 2 3 3 3 3 3 3 2 3 6 ...
## $ GER : Factor w/ 34 levels "ALOCRISOL","ALOCRISOL-REDOXISOL",..: 4 4 4 12 4 4 4 18 4 4 ...
#supression des fichiers inutiles pour la suite
rm(dico_cpcs)
rm(dico_ger2008)
rm(dico_ger95)
rm(dico)
# Visualisation des données
library(ggplot2)
library(dplyr)
profil %>%
# compter le nombre de profils par GER
count(GER, sort = TRUE) %>%
# Réorganisez les GER en fonction du nombre de profils
mutate(GER = forcats::fct_reorder(.f = GER, .x = n, .desc = TRUE)) %>%
# Faire le graphique
ggplot(aes(x = n, y = GER)) +
geom_col() +
xlab("Nombre de profils") +
geom_text(aes(label = n), size = 3, fontface = "bold", col="red", hjust=0) + # rajout des labels
coord_cartesian(clip = "off") + # pour ne pas que les labels soient coupés
theme_minimal()
On peut le coder en dplyr
recodes <- list(
LITHOSOL = c("1111","2412","1210"),
FERSIALSOL_ELUVIQUE = c("9224"),
FERSIALSOL = c(
"9225","9222","9221","9220","9215","9214","9213","9212","9211",
"9210","9200","9000"
),
REGOSOL = c(
"1112","1140","1141","1142","2440","2441","2442","2443","2410",
"2411","1100","1000","1150","1151","1152","2450","2451","2452",
"2453","2400","1110","1162"
),
FLUVIOSOL = c("1120","1121","1122","2420","2421","2422","2423"),
FERRALLITISOL = c(
"10000","10100","10110","10111","10112","10113","10114","10115",
"10120","10121","10122","10123","10124","10130","10131","10132",
"10133","10134","10140","10141","10142","10143","10200","10210",
"10211","10212","10213","10214","10215","10216","10217","10220",
"10221","10222","10223","10230","10231","10232","10233","10234",
"10235","10240","10241","10242","10243","10244","10245","10246",
"10250","10251","10252","10253","10300","10310","10311","10312",
"10313","10314","10315","10316","10317","10320","10321","10322",
"10330","10331","10332","10333","10334","10335","10340","10341",
"10342","10343","10344","10350","10351","10352","10353","10360",
"10361","10362","10363","10364"
),
REDOXISOL = c(
"11000","11320","11322","11340","11341","11342","11350","11360",
"11361","11362","11300"
),
HISTOSOL = c(
"11100","11110","11111","11112","11120","11121","11122",
"11130","11131","11132"
),
RANKOSOL = c(
"2210","2211","2212","2213","2000","2200","2220","2230","2320"
),
COLLUVIOSOL = c(
"1130","1131","1132","2430","2431","2432","2433"
),
VERTISOL = c(
"3000","3100","3110","3111","3112","3113","3114","3120","3121",
"3122","3123","3124","3125","3200","3210","3211","3212","3213",
"3214","3220","3221","3222","3223","3224","3225"
),
ANDOSOL = c(
"4000","4100","4110","4200","4210","4211","4212","4213",
"4220","4221","4222","4223"
),
RENDOSOL = c("5110","5111","5112","5113","5114","5115"),
CALCOSOL = c("5120","5121","5122","5123","5124","5220","5221"),
DOLOMITOSOL = c("5130"),
CALCISOL = c("5210","5211","5212","5213","5200"),
LEPTISMECTISOL = c("5230","5231","5232"),
GYPSOSOL = c("5300","5310","5320","5321","5322"),
BRUNISOL = c(
"7110","7111","7113","7114","2140","6231","6232","6233","6234",
"6235","6410","6411","7000","710","7100","7115","7200","7300",
"7400","7410","7411","7412","7413","7414","7116","7170","7425",
"7710"
),
ALOCRISOL = c("7112"),
NEOLUVISOL = c("7121"),
LUVISOL = c("7120","7122","7123","7124","7125","7126","7127"),
PODZOSOL = c(
"8000","8100","8110","8111","8112","8113","8114","8115","8120",
"8121","8122","8123","8124","8125","8130","8131","8132","8140",
"8141","8142","8200","8210","8220","8300","8310","8311","8312",
"8320","8330"
),
REDUCTISOL = c(
"11200","11210","11211","11212","11213","11214","11220","11221",
"11310","11311","11312","11313","11314","11330"
),
PELOSOL = c("11321"),
PLANOSOL = c("13000","13100","13200"),
SODISOL = c(
"12000","12200","12210","12211","12212","12220","12221",
"12222","12230","12231","12232"
),
SALISOL = c("12100","12110","12111","12112","12113","12114"),
CRYOSOL = c("2130","2120"),
ANTHROPOSOL = c("2460","2461","2462","2463"),
ND = c(
"5000","5100","1721","5215","6315","6320","6321","6322","6323",
"6324","6325","7116","7170","7425","7710"
)
)
library(dplyr)
library(purrr)
profil <- profil %>%
mutate(GER_cpcs =
map_chr(cpcs_nom, ~ {
match <- names(recodes)[map_lgl(recodes, ~ .x %in% .y)]
ifelse(length(match) == 0, cpcs_nom, match)
})
)Sur la base de ce graphique, nous pouvons décider de regrouper les classes avec peu de profils dans une classe “autre”.
3.1.3 Importation de la table POI
3.1.3.1 Importation de POI
La table POI contient les coordonnées X,Y des points dans différentes projections. Nous allons utiliser par la suite la projection en lambert 93 en mètres.
poi <- read.csv2('donnees/jour1/donesol/poi.csv', sep=";")
poi <- poi[,c(3,7,8)] # sélection des coordonnées en lambert 93
# Mettre les coordonnées en numérique
poi$x_l93<-as.numeric(poi$x_l93)
poi$y_l93<-as.numeric(poi$y_l93)
str(poi) # visualiser la liste des champs de la table POI## 'data.frame': 2892 obs. of 3 variables:
## $ id_profil: int 382 388 397 413 426 433 438 446 454 458 ...
## $ x_l93 : num 440655 440742 440515 440648 440986 ...
## $ y_l93 : num 6782309 6782233 6782611 6782483 6782778 ...
3.1.3.2 Option : Cartogramme des points
Nous allons projeter les points XY sur le département de la Mayenne pour visualiser leur répartition. Nous commençons par importer les contours des Pays de la Loire qui est la région où se trouve notre zone d’étude.
Nous allons ici utiliser les données provenant du site de l’IGN pour obtenir un fond de carte. Nous pouvons les télécharger sur : https://github.com/ThinkR-open/iframe.illustrations/blob/master/data_article_tmap/DEPARTEMENT.zip
Il faudra décompresser le fichier DEPARTEMENT.zip, dans un dossier departement/.
Pour ce TD, le dossier “departement” est déjà présent dans les documents fournis.
#Nous chargeons les packages nécessaires
library(ggplot2)
library(sf)
library("ggspatial")
# Nous importons la couche departement en lambert 93
departements_l93 <- read_sf("donnees/jour1/departement/DEPARTEMENT.shp")
# Visualisation de la carte des départements
ggplot(data = departements_l93) +
geom_sf() +
scale_y_continuous(labels = ~ paste0(., '\u00B0', "N"))+ # permet l'affichage du symbole°
scale_x_continuous(labels = ~ paste0(., '\u00B0', "N"))
# Nous sélectionnons le département de la Mayenne
mayenne <- departements_l93 %>%
dplyr::filter(NOM_DEPT == "MAYENNE")
# Visualisation du département de la Mayenne
ggplot() +
geom_sf(data = mayenne)+
scale_y_continuous(labels = ~ paste0(., '\u00B0', "N"))+ # permet l'affichage du symbole°
scale_x_continuous(labels = ~ paste0(., '\u00B0', "N"))
# Nous vérifions le système de projection. Il doit être en lambert 93.
sf::st_crs(mayenne)## Coordinate Reference System:
## User input: RGF93 Lambert 93
## wkt:
## PROJCRS["RGF93 Lambert 93",
## BASEGEOGCRS["RGF93 geographiques (dms)",
## DATUM["Reseau Geodesique Francais 1993 v1",
## ELLIPSOID["GRS 1980",6378137,298.257222101,
## LENGTHUNIT["metre",1]]],
## PRIMEM["Greenwich",0,
## ANGLEUNIT["degree",0.0174532925199433]],
## ID["IGNF","RGF93G"]],
## CONVERSION["LAMBERT-93",
## METHOD["Lambert Conic Conformal (2SP)",
## ID["EPSG",9802]],
## PARAMETER["Latitude of false origin",46.5,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8821]],
## PARAMETER["Longitude of false origin",3,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8822]],
## PARAMETER["Latitude of 1st standard parallel",44,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8823]],
## PARAMETER["Latitude of 2nd standard parallel",49,
## ANGLEUNIT["degree",0.0174532925199433],
## ID["EPSG",8824]],
## PARAMETER["Easting at false origin",700000,
## LENGTHUNIT["metre",1],
## ID["EPSG",8826]],
## PARAMETER["Northing at false origin",6600000,
## LENGTHUNIT["metre",1],
## ID["EPSG",8827]]],
## CS[Cartesian,2],
## AXIS["easting (X)",east,
## ORDER[1],
## LENGTHUNIT["metre",1]],
## AXIS["northing (Y)",north,
## ORDER[2],
## LENGTHUNIT["metre",1]],
## USAGE[
## SCOPE["NATIONALE A CARACTERE LEGAL"],
## AREA["FRANCE METROPOLITAINE (CORSE COMPRISE)"],
## BBOX[41,-5.5,52,10]],
## ID["IGNF","LAMB93"]]
# Nous convertissons les données XY en fichier spatial
poi2 <- st_as_sf(poi, coords = c("x_l93", "y_l93"), crs = st_crs(mayenne))
# Nous projetons les points sur le fond de carte
ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = poi2)+
scale_y_continuous(labels = ~ paste0(., '\u00B0', "N"))+ # permet l'affichage du symbole°
scale_x_continuous(labels = ~ paste0(., '\u00B0', "N"))
# Pour ne conserver que les points de la zone d'étude
poi_mayenne <- st_join(poi2, mayenne, left = FALSE)
# carte de répartition des points en Mayenne
ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = poi_mayenne)+
scale_y_continuous(labels = ~ paste0(., '\u00B0', "N"))+ # permet l'affichage du symbole°
scale_x_continuous(labels = ~ paste0(., '\u00B0', "N"))+
# ajout d'une échelle
annotation_scale(location = "bl", # position de la barre d'échelle
line_width = 0.5, # largeur de ligne pour la barre d'échelle
style="ticks", # style de la barre d'échelle
width_hint = 0.1, # dimension de l'échelle (ici 10 km)
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # Distance entre la barre d'échelle et le bord du graphique
# ajout d'une flèche Nord
annotation_north_arrow(location = "tl", # position de la flèche
which_north = "true",
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical( # choix du style de la flèche
fill = c("grey40", "white"), # choix de la couleur de la flèche
line_col = "grey20",
text_family = "ArcherPro Book"))
3.1.4 I.4. Importation de la table HORIZON
La table HORIZON décrit les horizons du profil. Toutes les observations faites sur un horizon sont reportées dans cette table.
horizon <- read.csv2('donnees/jour1/donesol/horizon.csv', sep=";")Nous sélectionnons les champs que nous souhaitons garder pour la suite. Vous pouvez modifier ce code pour sélectionner les champs de la table HORIZON qui vous intéressent. Il est conseillé de conserver le nom des champs identique à celui du dictionnaire de données pour éviter des confusion par la suite.
horizon <- data.frame("id_profil"=horizon$id_profil, # identifiant du profil auquel appartient l'horizon
"no_horizon"=horizon$no_horizon, # numéro de l'horizon dans le profil
"cpcs_nom"=horizon$cpcs_nom, # lettre code de l'horizon selon la CPCS
"rp_95_nom"=horizon$rp_95_nom, # lettre code de l'horizon selon le RP95
"rp_2008_nom"=horizon$rp_2008_nom, # lettre code de l'horizon selon le RP2008
"prof_sup_min"=horizon$prof_sup_min, # profondeur minimum d'apparition de l'horizon
"prof_sup_moy"=horizon$prof_sup_moy, # profondeur moyenne d'apparition de l'horizon
"prof_sup_max"=horizon$prof_sup_max, # profondeur maximum d'apparition de l'horizon
"prof_inf_min"=horizon$prof_inf_min,# profondeur minimum de disparition de l'horizon
"prof_inf_moy"=horizon$prof_inf_moy,# profondeur moyenne de disparition de l'horizon
"prof_inf_max"=horizon$prof_inf_max,# profondeur maximum de disparition de l'horizon
"textur"=horizon$textur, # classe de texture selon le triangle de la table PROFIL
"abondance_eg"=horizon$abondance_eg, # abondance totale des éléments grossiers (EG)
"abondance_eg_prin"=horizon$abondance_eg_prin, # abondance des EG principaux
"abondance_eg_sec"=horizon$abondance_eg_sec) # abondance des EG secondaires
str(horizon) # visualiser la liste des champs de la table HORIZON## 'data.frame': 10208 obs. of 15 variables:
## $ id_profil : int 86878 3838 132232 125922 4099 132384 85427 90159 85646 124318 ...
## $ no_horizon : int 4 3 4 1 4 1 4 1 2 3 ...
## $ cpcs_nom : chr "C2G" "" "" "Apg" ...
## $ rp_95_nom : chr "" "S2" "Cg" "" ...
## $ rp_2008_nom : chr "" "" "" "" ...
## $ prof_sup_min : chr "" "" "" "" ...
## $ prof_sup_moy : chr "120.0" "40.0" "57.0" "0.0" ...
## $ prof_sup_max : chr "" "" "" "" ...
## $ prof_inf_min : chr "" "" "" "" ...
## $ prof_inf_moy : chr "" "75.0" "70.0" "35.0" ...
## $ prof_inf_max : chr "" "" "" "" ...
## $ textur : chr "LA" "LMS" "LA" "LSA" ...
## $ abondance_eg : chr "20.0" "0.0" "" "" ...
## $ abondance_eg_prin: chr "" "" "" "" ...
## $ abondance_eg_sec : chr "" "" "" "" ...
Il est important de prendre tous les champs concernant les profondeurs des horizons. Ces champs nous seront utiles dans la partie V.
3.1.5 I.5. Importation de la table PRELEVEMENT
La table PRELEVEMENT permet de décrire un prélèvement sur le terrain. Cette table pourrait nous être utile pour compléter les profondeurs des horizons lorsque celles-ci sont manquantes.
prelevement <- read.csv2('donnees/jour1/donesol/prelevement.csv', sep=";")
# Nous choisissons les champs à conserver :
prelevement <- data.frame("id_prelevement"=prelevement$id_prelevement, # identifiant du prélèvement
"id_profil"=prelevement$id_profil, # identifiant du profil auquel appartient le prélèvement
"no_horizon"=prelevement$no_horizon, # numéro de l'horizon auquel appartient le prélèvement
"prof_base"=prelevement$prof_base, # profondeur de la base du prélèvement
"prof_sommet"=prelevement$prof_sommet) # profondeur du sommet du prélèvement
str(prelevement) # visualiser la liste des champs de la table ANALYSE## 'data.frame': 8726 obs. of 5 variables:
## $ id_prelevement: int 114984 219707 159411 47442 205657 149534 219172 108741 216756 205721 ...
## $ id_profil : int 82301 139302 123946 4179 124318 90316 124167 10546 90129 124328 ...
## $ no_horizon : int 1 4 1 2 1 2 3 2 2 4 ...
## $ prof_base : chr "40.0" "100.0" "31.0" "40.0" ...
## $ prof_sommet : chr "0.0" "75.0" "0.0" "30.0" ...
3.1.6 I.6. Importation de la table ANALYSE
La table ANALYSE contient des informations sur le laboratoire d’analyse et des généralités sur les résultats des analyses réalisées sur les horizons. Nous allons nous en servir pour faire le lien avec la table PRELEVEMENT importée précédemment.
analyse <- read.csv2('donnees/jour1/donesol/analyse.csv', sep=";")
#Nous choisissons les champs à conserver :
analyse <- data.frame("id_profil"=analyse$id_profil, # identifiant du profil auquel appartient l'analyse
"no_horizon"=analyse$no_horizon, # numéro de l'horizon auquel appartient l'analyse
"id_analyse"=analyse$id_analyse, # identifiant de l'analyse
"id_prelevement"=analyse$id_prelevement) # identifiant du prélèvement
str(analyse) # visualiser la liste des champs de la table ANALYSE## 'data.frame': 9335 obs. of 4 variables:
## $ id_profil : int 382 382 382 388 388 388 388 397 397 397 ...
## $ no_horizon : int 1 2 3 1 2 3 4 1 2 3 ...
## $ id_analyse : int 130340 130341 130342 130346 130347 130348 130349 130308 130309 130310 ...
## $ id_prelevement: int 117005 117006 117007 117011 117012 117013 117014 116975 116976 116977 ...
Nous fusionnons ensuite les tables ANALYSE et PRELEVEMENT de façon à avoir les profondeurs de prélèvement dans la table ANALYSE.
analyse<-full_join(analyse, prelevement, by=c("id_prelevement", "id_profil", "no_horizon")) # jointure entre ANALYSE et PRELEVEMENT
# Nous mettons les champs de profondeurs en numérique
analyse$prof_base<-as.numeric(analyse$prof_base)
analyse$prof_sommet<-as.numeric(analyse$prof_sommet)
# Nous affichons le nouveau contenu de la table ANALYSE :
summary(analyse)## id_profil no_horizon id_analyse id_prelevement
## Min. : 382 Min. :1.000 Min. : 2925 Min. : 2922
## 1st Qu.: 84582 1st Qu.:1.000 1st Qu.:122710 1st Qu.:111392
## Median : 90456 Median :2.000 Median :136106 Median :149608
## Mean : 93683 Mean :2.274 Mean :138399 Mean :140336
## 3rd Qu.:126114 3rd Qu.:3.000 3rd Qu.:173284 3rd Qu.:204393
## Max. :139441 Max. :6.000 Max. :249765 Max. :272719
## NA's :1 NA's :600
## prof_base prof_sommet
## Min. : 0.00 Min. : -1.00
## 1st Qu.: 30.00 1st Qu.: 0.00
## Median : 50.00 Median : 30.00
## Mean : 62.22 Mean : 34.08
## 3rd Qu.: 85.00 3rd Qu.: 55.00
## Max. :760.00 Max. :200.00
## NA's :1267 NA's :600
# Nous supprimons la table PRELEVEMENT devenue inutile :
rm(prelevement)Cette table ANALYSE sera utilisée dans la partie V lorsque nous travaillerons sur les profondeurs des horizons.
3.1.7 I.7. Importation de la table RESULTAT_ANALYSE
La table RESULTAT_ANALYSE contient l’ensemble des résultats des analyses réalisées sur les échantillons de sols.
resultat_analyse <- read.csv2('donnees/jour1/donesol/resultat_analyse.csv', sep=";")
str(resultat_analyse) # visualiser la liste des champs de la table RESULTAT_ANALYSE## 'data.frame': 80470 obs. of 8 variables:
## $ id_resultat : int 1539548 2093510 1211236 1528944 1204905 1369230 1213579 1535124 1745592 2088927 ...
## $ determination : chr "p_ass" "c_n" "ca_ech" "na_ech" ...
## $ valeur : chr "0.081" "9.3" "1.61" "0.049" ...
## $ unite : chr "g/kg" "" "cmol+/kg" "cmol+/kg" ...
## $ precision_r : logi NA NA NA NA NA NA ...
## $ id_analyse : int 127756 54764 166289 127075 165494 171704 166561 166621 188240 54624 ...
## $ id_methode : int 198 1 106 106 20 20 106 106 71 14 ...
## $ id_methode_physique: int NA NA NA NA NA NA NA NA NA NA ...
3.1.8 I.8. Importation des données de granulométrie
Pour la granulométrie, les données sont stockées dans 3 tables :
La table RESULTAT_ANALYSE_GRANULO contient l’ensemble des résultats des analyses granulométriques réalisées sur les échantillons de sols.
La table ENS_RESULTAT_ANALYSE_GRANULO décrit les différentes méthodes utilisées lors de l’analyse granulométrique. Cette table est en lien avec la table de métadonnées METHODE_ANALYSE_GRANULO.
La table PREPARATION_GRANULO décrit les différentes préparations de l’échantillon avant l’analyse granulométrique. Par exemple, cette table nous est utile pour savoir si l’échantillon a été décarbonaté ou non avant l’analyse granulométrique.
resultat_analyse_granulo <- read.csv2('donnees/jour1/donesol/resultat_analyse_granulo.csv', sep=";")
ens_resultat_analyse_granulo <- read.csv2('donnees/jour1/donesol/ens_resultat_analyse_granulo.csv', sep=";")
preparation_granulo <- read.csv2('donnees/jour1/donesol/preparation_granulo.csv', sep=";")3.1.9 I.9. Importation de la table RESULTAT_EG
La table RESULTAT_EG contient l’ensemble des résultats de mesure de la quantité d’éléments grossiers réalisée sur les échantillons de sol. On appelle éléments grossiers toute fraction granulométrique supérieure à 2 mm.
resultat_eg <- read.csv2('donnees/jour1/donesol/resultat_eg.csv', sep=";")
# on sélectionne les champs à conserver
resultat_eg <- data.frame("id_resultat"=resultat_eg$id_resultat, # identifiant du résultat d'analyse
"id_analyse"=resultat_eg$id_analyse, # identifiant de l'analyse
"prc_mass_tot"=resultat_eg$prc_mass_tot, # % massique total des EG
"valeur"=resultat_eg$valeur) # % des EG
# Mise en numérique des champs "prc_mass_tot" et "valeur"
resultat_eg$prc_mass_tot <- as.numeric(resultat_eg$prc_mass_tot)
resultat_eg$valeur <- as.numeric(resultat_eg$valeur)
summary(resultat_eg)## id_resultat id_analyse prc_mass_tot valeur
## Min. : 298 Min. : 2925 Min. : 0.00 Min. : 0.000
## 1st Qu.: 64944 1st Qu.:126592 1st Qu.: 4.00 1st Qu.: 0.000
## Median : 65259 Median :127261 Median : 5.00 Median : 0.000
## Mean : 63066 Mean : 98198 Mean :16.77 Mean : 1.831
## 3rd Qu.:110076 3rd Qu.:127601 3rd Qu.:20.00 3rd Qu.: 0.000
## Max. :110237 Max. :128005 Max. :95.00 Max. :60.000
## NA's :356 NA's :249
3.1.10 Option : abondance des éléments grossiers
Pour l’abondance des éléments grossiers, nous allons utiliser le champ “abondance_eg” de la table HORIZON. Lorsque ce champ est vide, nous allons le compléter avec la somme des champs “abondange_eg_prin” + “abondance_eg_sec” de la table HORIZON.
# On met les champs d'abondance des EG en numérique
horizon$abondance_eg <- as.numeric(horizon$abondance_eg)
horizon$abondance_eg_prin <- as.numeric(horizon$abondance_eg_prin)
horizon$abondance_eg_sec <- as.numeric(horizon$abondance_eg_sec)
# On sort les statistiques du champ abondance_eg
summary(horizon$abondance_eg)## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 5.00 10.00 20.01 25.00 99.00 3625
# Nous rajoutons un champ "SumEG" contenant la somme des champs abondance_eg_prin + abondance_eg_sec
horizon$SumEG <- ifelse(is.na(horizon$abondance_eg_prin) & is.na(horizon$abondance_eg_sec),
horizon$SumEG <- NA,
ifelse(is.na(horizon$abondance_eg_prin),
horizon$SumEG <- 0 + horizon$abondance_eg_sec,
horizon$abondance_eg_prin + horizon$abondance_eg_sec))
# Nous remplaçons les NA du champ "abondance_eg" par "SumEG" si celui-ci n'est pas vide
horizon$abondance_eg <-ifelse(is.na(horizon$abondance_eg),
horizon$SumEG,
horizon$abondance_eg)
summary(horizon$abondance_eg)## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 5.00 10.00 20.08 25.00 100.00 3586
Il reste encore beaucoup de données manquantes dans le champ “abondance_eg”. Nous allons essayer de le compléter avec les données de la table RESULTAT_EG. Pour récupérer les identifants de l’horizon sur la table RESULTAT_EG, nous faisons une jointure avec la table ANALYSE.
# jointure entre ANALYSE et RESULTAT_EG
resultat_eg<-full_join(analyse,
resultat_eg,
by="id_analyse")
summary(resultat_eg)## id_profil no_horizon id_analyse id_prelevement
## Min. : 382 Min. :1.000 Min. : 2925 Min. : 2922
## 1st Qu.: 84468 1st Qu.:1.000 1st Qu.:123074 1st Qu.:112902
## Median : 90397 Median :2.000 Median :130304 Median :149162
## Mean : 93123 Mean :2.268 Mean :137994 Mean :139324
## 3rd Qu.:126085 3rd Qu.:3.000 3rd Qu.:172930 3rd Qu.:197141
## Max. :139441 Max. :6.000 Max. :249765 Max. :272719
## NA's :1 NA's :600
## prof_base prof_sommet id_resultat prc_mass_tot
## Min. : 0.0 Min. : -1.00 Min. : 298 Min. : 0.00
## 1st Qu.: 30.0 1st Qu.: 0.00 1st Qu.: 64944 1st Qu.: 4.00
## Median : 50.0 Median : 30.00 Median : 65259 Median : 5.00
## Mean : 62.1 Mean : 33.87 Mean : 63066 Mean :16.77
## 3rd Qu.: 83.0 3rd Qu.: 53.00 3rd Qu.:110076 3rd Qu.:20.00
## Max. :760.0 Max. :200.00 Max. :110237 Max. :95.00
## NA's :1267 NA's :600 NA's :9087 NA's :9443
## valeur
## Min. : 0.000
## 1st Qu.: 0.000
## Median : 0.000
## Mean : 1.831
## 3rd Qu.: 0.000
## Max. :60.000
## NA's :9336
Nous allons compléter le champ “prc_mass_tot” avec le champ “valeur” lorsque le champ “prc_mass_tot” est vide.
resultat_eg$prc_mass_tot<-ifelse(is.na(resultat_eg$prc_mass_tot),
resultat_eg$valeur,
resultat_eg$prc_mass_tot)
summary(resultat_eg$prc_mass_tot)## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.000 0.000 0.000 7.979 5.000 95.000 9087
Nous allons compléter le champ “abondance_eg” de la table HORIZON lorsque celui-ci est vide avec le champ “prc_mass_tot” de la table RESULTAT_EG.
resultat_eg <- data.frame("id_profil"=resultat_eg$id_profil,
"no_horizon"=resultat_eg$no_horizon,
"prc_mass_tot"=resultat_eg$prc_mass_tot) # % massique total des EG
# jointure entre HORIZON et RESULTAT_EG
horizon<-left_join(horizon, resultat_eg, by=c('id_profil','no_horizon'))
# suppression des lignes en doubles
horizon<-horizon[!duplicated(horizon), ]
# Nous remplaçons les valeurs vides du champ "abondance_eg" par
# les données du champ "prc_mass_tot" quand celui-ci n'est pas vide
horizon$abondance_eg<-ifelse(is.na(horizon$abondance_eg),
horizon$prc_mass_tot,
horizon$abondance_eg)
summary(horizon$abondance_eg)## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 0.00 5.00 10.00 19.93 25.00 100.00 3582
Nous relectionnons les champs de la table HORIZON que nous souhaitons conserver.
horizon <- data.frame("id_profil"=horizon$id_profil,
"no_horizon"=horizon$no_horizon,
"cpcs_nom"=horizon$cpcs_nom,
"rp_95_nom"=horizon$rp_95_nom,
"rp_2008_nom"=horizon$rp_2008_nom,
"prof_sup_min"=horizon$prof_sup_min,
"prof_sup_moy"=horizon$prof_sup_moy,
"prof_sup_max"=horizon$prof_sup_max,
"prof_inf_min"=horizon$prof_inf_min,
"prof_inf_moy"=horizon$prof_inf_moy,
"prof_inf_max"=horizon$prof_inf_max,
"textur"=horizon$textur,
"abondance_eg"=horizon$abondance_eg)
str(horizon) # visualiser la liste des champs de la table HORIZON## 'data.frame': 10282 obs. of 13 variables:
## $ id_profil : int 86878 3838 132232 125922 4099 132384 85427 90159 85646 124318 ...
## $ no_horizon : int 4 3 4 1 4 1 4 1 2 3 ...
## $ cpcs_nom : chr "C2G" "" "" "Apg" ...
## $ rp_95_nom : chr "" "S2" "Cg" "" ...
## $ rp_2008_nom : chr "" "" "" "" ...
## $ prof_sup_min: chr "" "" "" "" ...
## $ prof_sup_moy: chr "120.0" "40.0" "57.0" "0.0" ...
## $ prof_sup_max: chr "" "" "" "" ...
## $ prof_inf_min: chr "" "" "" "" ...
## $ prof_inf_moy: chr "" "75.0" "70.0" "35.0" ...
## $ prof_inf_max: chr "" "" "" "" ...
## $ textur : chr "LA" "LMS" "LA" "LSA" ...
## $ abondance_eg: num 20 0 NA NA 30 20 NA 5 5 70 ...
Nous pouvons décider que les valeurs manquantes du champ “abondance_eg” correspondent à des valeurs nulles (pas d’éléments grossiers). C’est une hypothèse forte.
Pour remplacer les NA par la valeur 0, il faut faire :
horizon$abondance_eg <- ifelse(is.na(horizon$abondance_eg), horizon$abondance_eg <-0, horizon$abondance_eg)
3.2 PREPARATION DES DONNEES D’ANALYSE GRANULOMETRIQUE SANS DECARBONATATION
3.2.1 II.1. Les seuils des fractions
Nous imposons que les seuils des fractions granulométriques et que la valeur soient bien en format numérique.
resultat_analyse_granulo$seuil_inferieur<-as.numeric(resultat_analyse_granulo$seuil_inferieur)
resultat_analyse_granulo$seuil_superieur<-as.numeric(resultat_analyse_granulo$seuil_superieur)
resultat_analyse_granulo$valeur<-as.numeric(resultat_analyse_granulo$valeur)Nous regardons les bornes des fractions présentes. Nous sortons pour cela un tableau du nombre d’occurences par seuil.
table(resultat_analyse_granulo$seuil_inferieur)##
## 0 2 20 50 100 200
## 9310 9310 9304 9310 16 9299
table(resultat_analyse_granulo$seuil_superieur)##
## 2 20 50 100 200 2000
## 9310 9304 9310 16 9310 9299
Nous allons sommer les valeurs pour avoir le nombre de fractions souhaitées.
3.2.2 II.2. Regroupement en 3 fractions
Les 3 fractions sont :
- Argile 0-2 µm
- Limon 2-50 µm
- Sable 50-2000 µm
ⓘ NOTE
Vous pouvez aussi décider d’avoir 5 fractions au lieu de 3 en prenant 2 fractions pour le limon et 2 fractions pour le sable. Ici, nous avons décidé de prendre 3 fractions. Toutefois, une note dans les parties limon et sable, vous indique comment faire pour avoir 5 fractions.
3.2.2.1 II.2.1. Pour l’argile (0-2 µm)
Nous sélectionnons les données dont les seuils sont compris entre 0 et 2 µm.
library(magrittr)
A<- resultat_analyse_granulo %>%
filter(seuil_inferieur < 2, seuil_superieur < 2.1)
table(A$seuil_superieur)##
## 2
## 9310
table(A$seuil_inferieur)##
## 0
## 9310
colnames(A) # donne le nom des champs du fichier A## [1] "id_resultat" "id_ens_resultat_analyse_granulo"
## [3] "seuil_inferieur" "seuil_superieur"
## [5] "unite_seuil" "valeur"
## id_resultat id_ens_resultat_analyse_granulo seuil_inferieur
## Min. : 12489 Min. : 2888 Min. :0
## 1st Qu.: 635854 1st Qu.:123450 1st Qu.:0
## Median : 790028 Median :153812 Median :0
## Mean : 715066 Mean :139277 Mean :0
## 3rd Qu.: 817768 3rd Qu.:159228 3rd Qu.:0
## Max. :1395493 Max. :271290 Max. :0
## seuil_superieur unite_seuil argile
## Min. :2 Length:9310 Min. : 5.3
## 1st Qu.:2 Class :character 1st Qu.:143.0
## Median :2 Mode :character Median :182.0
## Mean :2 Mean :204.0
## 3rd Qu.:2 3rd Qu.:238.0
## Max. :2 Max. :859.0
3.2.2.2 II.2.2. Pour le Limon (2-50 µm)
Pour le limon, nous avons plusieurs fractions dans DoneSol. Nous recherchons toutes les fractions comprises entre 2 et 50 µm et nous en faisons la somme par identifiant d’analyse granulo (champ id_ens_resultat_analyse_granulo).
L<- resultat_analyse_granulo %>%
filter(seuil_inferieur > 1.9, seuil_superieur < 50.1)
table(L$seuil_superieur)##
## 20 50
## 9304 9310
table(L$seuil_inferieur)##
## 2 20
## 9310 9304
# Somme par identifiant (champ id_ens_resultat_analyse_granulo)
L <- aggregate( valeur ~ id_ens_resultat_analyse_granulo, L, sum)
names(L)[names(L) == "valeur"] <- "limon" # renomme le champ valeur
summary(L)## id_ens_resultat_analyse_granulo limon
## Min. : 2888 Min. : 9
## 1st Qu.:123450 1st Qu.: 385
## Median :153812 Median : 508
## Mean :139277 Mean : 493
## 3rd Qu.:159228 3rd Qu.: 618
## Max. :271290 Max. :1033
ⓘ NOTE
Pour obtenir 2 fractions pour les limons, vous devez prendre 2-20 µm pour les limons fins (Lf) et 20-50 µm pour les limons grossiers (Lg).
Le code R à réaliser est :
Lf<- resultat_analyse_granulo %>% filter(seuil_inferieur > 1.9, seuil_superieur < 20.1)
Lg<- resultat_analyse_granulo %>% filter(seuil_inferieur > 19.1, seuil_superieur < 50.1)
En fonction des données d’entrée, voir s’il est utile de faire une agrégation pour Lf et pour Lg.
3.2.2.3 II.2.3. Pour le sable (50-2000 µm)
Pour le sable, nous avons plusieurs fractions dans DoneSol. Nous recherchons toutes les fractions comprises entre 50 et 2000 µm et nous en faisons la somme par identifiant d’analyse granulo (champ id_ens_resultat_analyse_granulo).
S<- resultat_analyse_granulo %>%
filter(seuil_inferieur > 49.9, seuil_superieur < 2000.1)
table(S$seuil_superieur)##
## 100 200 2000
## 16 9310 9299
table(S$seuil_inferieur)##
## 50 100 200
## 9310 16 9299
# Somme par identifiant (champ id_ens_resultat_analyse_granulo)
S <- aggregate(valeur ~ id_ens_resultat_analyse_granulo, S, sum)
# library(plyr)
names(S)[names(S) == "valeur"] <- "sable" # renomme le champ valeur
summary(S)## id_ens_resultat_analyse_granulo sable
## Min. : 2888 Min. : 12.0
## 1st Qu.:123450 1st Qu.:172.0
## Median :153812 Median :266.0
## Mean :139277 Mean :301.2
## 3rd Qu.:159228 3rd Qu.:393.0
## Max. :271290 Max. :964.0
ⓘ NOTE
Pour obtenir 2 fractions pour les sables, vous devez prendre 50-200 µm pour les sables fins (Sf) et 200-2000 µm pour les sables grossiers (Sg).
Le code R à réaliser pour le sable fin :
Sf<- resultat_analyse_granulo %>% filter(seuil_inferieur > 49.9, seuil_superieur < 200.1)
Sf <- aggregate(valeur ~ id_ens_resultat_analyse_granulo, Sf, sum)
Sf <- rename(Sf, c(“id_ens_resultat_analyse_granulo” = “id_ens_resultat_analyse_granulo”, “sable_fin” = “valeur”))
Le code R à réaliser pour le sable grossier :
Sg<- resultat_analyse_granulo %>% filter(seuil_inferieur > 199.9, seuil_superieur < 2000.1)
Sg <- aggregate(valeur ~ id_ens_resultat_analyse_granulo, Sg, sum)
Sg <- rename(Sg, c(“id_ens_resultat_analyse_granulo” = “id_ens_resultat_analyse_granulo”, >“sable_grossier” = “valeur”))
Nous compilons ensuite les 3 sommes argile + limon + sable dans un seul fichier (que nous avons appelé “granu”).
a<-full_join(A, L, by="id_ens_resultat_analyse_granulo")
granu<-full_join(a, S, by="id_ens_resultat_analyse_granulo")
rm(a) # supprime le dataframe intermédiaire
granu$somme <- (granu$argile + granu$limon + granu$sable) # rajoute un champ somme donnant la somme des 3 fractions
summary(granu)## id_resultat id_ens_resultat_analyse_granulo seuil_inferieur
## Min. : 12489 Min. : 2888 Min. :0
## 1st Qu.: 635854 1st Qu.:123450 1st Qu.:0
## Median : 790028 Median :153812 Median :0
## Mean : 715066 Mean :139277 Mean :0
## 3rd Qu.: 817768 3rd Qu.:159228 3rd Qu.:0
## Max. :1395493 Max. :271290 Max. :0
## seuil_superieur unite_seuil argile limon
## Min. :2 Length:9310 Min. : 5.3 Min. : 9
## 1st Qu.:2 Class :character 1st Qu.:143.0 1st Qu.: 385
## Median :2 Mode :character Median :182.0 Median : 508
## Mean :2 Mean :204.0 Mean : 493
## 3rd Qu.:2 3rd Qu.:238.0 3rd Qu.: 618
## Max. :2 Max. :859.0 Max. :1033
## sable somme
## Min. : 12.0 Min. : 100.0
## 1st Qu.:172.0 1st Qu.:1000.0
## Median :266.0 Median :1000.0
## Mean :301.2 Mean : 998.1
## 3rd Qu.:393.0 3rd Qu.:1000.0
## Max. :964.0 Max. :1400.0
3.2.3 II.3. Sélection de la granulométrie sans décarbonatation
Nous séparons les données sans décarbonatation de celles avec decarbonatation. Pour cela, nous utilisons la table PREPARATION_GRANULO qui donne le prétraitement de l’échantillon.
granulo<- full_join(granu, preparation_granulo, by="id_ens_resultat_analyse_granulo")
summary(granulo)## id_resultat id_ens_resultat_analyse_granulo seuil_inferieur
## Min. : 12489 Min. : 2888 Min. :0
## 1st Qu.: 635854 1st Qu.:123451 1st Qu.:0
## Median : 790028 Median :153812 Median :0
## Mean : 715066 Mean :139280 Mean :0
## 3rd Qu.: 817768 3rd Qu.:159227 3rd Qu.:0
## Max. :1395493 Max. :271290 Max. :0
## NA's :2 NA's :2
## seuil_superieur unite_seuil argile limon
## Min. :2 Length:9312 Min. : 5.3 Min. : 9
## 1st Qu.:2 Class :character 1st Qu.:143.0 1st Qu.: 385
## Median :2 Mode :character Median :182.0 Median : 508
## Mean :2 Mean :204.0 Mean : 493
## 3rd Qu.:2 3rd Qu.:238.0 3rd Qu.: 618
## Max. :2 Max. :859.0 Max. :1033
## NA's :2 NA's :2 NA's :2
## sable somme id_preparation_granulo pretrait_d2
## Min. : 12.0 Min. : 100.0 Min. : 22516 Min. :2.000
## 1st Qu.:172.0 1st Qu.:1000.0 1st Qu.: 69229 1st Qu.:9.000
## Median :266.0 Median :1000.0 Median : 72577 Median :9.000
## Mean :301.2 Mean : 998.1 Mean : 74120 Mean :8.142
## 3rd Qu.:393.0 3rd Qu.:1000.0 3rd Qu.: 78765 3rd Qu.:9.000
## Max. :964.0 Max. :1400.0 Max. :185249 Max. :9.000
## NA's :2 NA's :2 NA's :3384 NA's :9058
## pretrait_d3 peptis
## Min. :9 Mode:logical
## 1st Qu.:9 NA's:9312
## Median :9
## Mean :9
## 3rd Qu.:9
## Max. :9
## NA's :5679
granulo$traitement <- ifelse(granulo$pretrait_d2 ==1 | granulo$pretrait_d2 ==3 | granulo$pretrait_d2 ==6 |
granulo$pretrait_d2 ==8 | granulo$pretrait_d3 ==1 |
granulo$pretrait_d3 ==3 | granulo$pretrait_d3 ==6 |
granulo$pretrait_d3 ==8, "decarb", "no_decarb") # indique le prétraitement si connu
granu_decarb <- subset(granulo, traitement == "decarb") # analyses granulo après décarbonatation
granu_no_decarb <- setdiff(granulo, granu_decarb) # analyses granulo sans décarbonatationAu final, notre fichier “granu_decarb” contient les granulométries décarbonatées en 3 fractions et notre fichier “granu_no_decarb” contient les granulométries non décarbonatées en 3 fractions. Nos teneurs en argile, limon et sable sont en g/kg. Par la suite, nous utiliserons les granulométries non décarbonatées.
Nous rapatrions les identifiants des tables PROFIL et HORIZON.
id <- full_join(analyse, ens_resultat_analyse_granulo, by="id_analyse")
id <- data.frame("id_analyse"=id$id_analyse, "id_prelevement"=id$id_prelevement,
"id_profil"=id$id_profil, "no_horizon"=id$no_horizon,
"id_ens_resultat_analyse_granulo"=id$id_ens_resultat_analyse_granulo)
granulo_no_decarb <- left_join(granu_no_decarb, id, by="id_ens_resultat_analyse_granulo")
granulo_decarb <- left_join(granu_decarb, id, by="id_ens_resultat_analyse_granulo")
summary(granulo_no_decarb)## id_resultat id_ens_resultat_analyse_granulo seuil_inferieur
## Min. : 12489 Min. : 2888 Min. :0
## 1st Qu.: 635853 1st Qu.:123450 1st Qu.:0
## Median : 790018 Median :153810 Median :0
## Mean : 715056 Mean :139278 Mean :0
## 3rd Qu.: 817769 3rd Qu.:159228 3rd Qu.:0
## Max. :1395493 Max. :271290 Max. :0
## NA's :2 NA's :2
## seuil_superieur unite_seuil argile limon
## Min. :2 Length:9311 Min. : 5.3 Min. : 9
## 1st Qu.:2 Class :character 1st Qu.:143.0 1st Qu.: 385
## Median :2 Mode :character Median :182.0 Median : 508
## Mean :2 Mean :204.0 Mean : 493
## 3rd Qu.:2 3rd Qu.:238.0 3rd Qu.: 618
## Max. :2 Max. :859.0 Max. :1033
## NA's :2 NA's :2 NA's :2
## sable somme id_preparation_granulo pretrait_d2
## Min. : 12.0 Min. : 100.0 Min. : 22516 Min. :2.000
## 1st Qu.:172.0 1st Qu.:1000.0 1st Qu.: 69228 1st Qu.:9.000
## Median :266.0 Median :1000.0 Median : 72576 Median :9.000
## Mean :301.2 Mean : 998.1 Mean : 74120 Mean :8.142
## 3rd Qu.:393.0 3rd Qu.:1000.0 3rd Qu.: 78766 3rd Qu.:9.000
## Max. :964.0 Max. :1400.0 Max. :185249 Max. :9.000
## NA's :2 NA's :2 NA's :3384 NA's :9058
## pretrait_d3 peptis traitement id_analyse
## Min. :9 Mode:logical Length:9311 Min. : 2925
## 1st Qu.:9 NA's:9311 Class :character 1st Qu.:122774
## Median :9 Mode :character Median :136138
## Mean :9 Mean :138666
## 3rd Qu.:9 3rd Qu.:173300
## Max. :9 Max. :249765
## NA's :5678
## id_prelevement id_profil no_horizon
## Min. : 2922 Min. : 382 Min. :1.000
## 1st Qu.:111499 1st Qu.: 84580 1st Qu.:1.000
## Median :149622 Median : 90455 Median :2.000
## Mean :140618 Mean : 93677 Mean :2.271
## 3rd Qu.:204398 3rd Qu.:126116 3rd Qu.:3.000
## Max. :272719 Max. :139441 Max. :6.000
## NA's :600
Nous vérifions que la somme des 3 fractions = 1000 pour la granulométrie sans décarbonatation (fichier “granulo_no_decarb”). Nous supprimons les valeurs inférieures à 800 g/kg et supérieures à 1200 g/kg. A l’intérieur de cet intervale, nous considérons qu’il s’agit de l’erreur d’analyse car les anciennes données n’étaient pas fournies par le laboratoire avec une somme exactement égale à 1000 g/kg (ou 100%).
granulo_no_decarb<-granulo_no_decarb %>% filter(!is.na(id_profil)) # suppression des enregistrements sans id_profil
granulo_no_decarb<-granulo_no_decarb %>% filter(!is.na(somme)) # suppression des enregistrements sans granulo
granulo_no_decarb<-filter(granulo_no_decarb, somme > 800, somme < 1200) # filtre sur la somme de la granulo
# si la somme des granulo est différente de 1000, nous faisons une règle de 3 pour que la somme fasse 1000
# rajout de champs A, L et S corrigés
granulo_no_decarb$argile_cor<-((granulo_no_decarb$argile/granulo_no_decarb$somme)*1000)
granulo_no_decarb$limon_cor<-((granulo_no_decarb$limon/granulo_no_decarb$somme)*1000)
granulo_no_decarb$sable_cor<-((granulo_no_decarb$sable/granulo_no_decarb$somme)*1000)
granulo_no_decarb$somme_cor <- (granulo_no_decarb$argile_cor + granulo_no_decarb$limon_cor + granulo_no_decarb$sable_cor)
summary(granulo_no_decarb$somme_cor)## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1000 1000 1000 1000 1000 1000
# Choix des variables à conserver dans le fichier GRANULO_NO_DECARB
granulo_no_decarb<-data.frame("id_ens_resultat_analyse_granulo"=granulo_no_decarb$id_ens_resultat_analyse_granulo,
"id_analyse"=granulo_no_decarb$id_analyse,
"id_prelevement"=granulo_no_decarb$id_prelevement,
"id_profil"=granulo_no_decarb$id_profil,
"no_horizon"=granulo_no_decarb$no_horizon,
"argile"=granulo_no_decarb$argile_cor,
"limon"=granulo_no_decarb$limon_cor,
"sable"=granulo_no_decarb$sable_cor)
summary(granulo_no_decarb)## id_ens_resultat_analyse_granulo id_analyse id_prelevement
## Min. : 2888 Min. : 2925 Min. : 2922
## 1st Qu.:123451 1st Qu.:122765 1st Qu.:111499
## Median :153808 Median :136138 Median :149628
## Mean :139274 Mean :138673 Mean :140680
## 3rd Qu.:159224 3rd Qu.:173306 3rd Qu.:204407
## Max. :271290 Max. :249765 Max. :272719
## NA's :599
## id_profil no_horizon argile limon
## Min. : 382 Min. :1.000 Min. : 9.793 Min. : 9.0
## 1st Qu.: 84574 1st Qu.:1.000 1st Qu.:144.000 1st Qu.:387.0
## Median : 90455 Median :2.000 Median :182.182 Median :509.5
## Mean : 93685 Mean :2.271 Mean :204.316 Mean :493.9
## 3rd Qu.:126116 3rd Qu.:3.000 3rd Qu.:238.940 3rd Qu.:619.0
## Max. :139441 Max. :6.000 Max. :858.142 Max. :871.0
##
## sable
## Min. : 11.99
## 1st Qu.:172.00
## Median :266.40
## Mean :301.77
## 3rd Qu.:393.00
## Max. :964.96
##
Par la suite, nous utiliserons les champs corrigés : argile_cor, limon_cor et sable_cor en g/kg de notre nouvelle table granulo_no_decarb.
Nous pouvons donc supprimer les tables devenues inutiles :
rm(granu) # suppression de la table GRANU
rm(resultat_analyse_granulo)# suppression de la table RESULTAT_ANALYSE_GRANULO
rm(ens_resultat_analyse_granulo)# suppression de la table ENS_RESULTAT_ANALYSE_GRANULO
rm(preparation_granulo)# suppression de la table PREPARATION_GRANULO
rm(granulo_decarb) # suppression de la table GRANULO_DECARB
rm(A) # suppression de la table A
rm(L) # suppression de la table L
rm(S) # suppression de la table S
rm(id) # suppression de la table id
rm(granu_decarb) # supression de la table GRANU_DECARB
rm(granu_no_decarb) # supression de la table GRANU_NO_DECARB
rm(granulo) # supression de la table GRANULO3.2.4 II.4. Option : projection des valeurs de granulométrie dans un triangle de texture
Pour visualiser les valeurs de granulométrie dans un triangle de texture, nous utiliserons le package SoilTexture (https://cran.r-project.org/web/packages/soiltexture/vignettes/soiltexture_vignette.pdf). Pour projeter les valeurs de granulométrie, celles-ci doivent être en % et non en g/kg. Nous devons aussi renommer les variables en anglais avec CLAY pour l’argile, SILT pour le limon et SAND pour le sable. La somme des 3 fractions doit absolument boucler à 100%.
# Nous mettons les fractions en % et les renommons en CLAY, SILT, SAND
texture <- data.frame("CLAY"=granulo_no_decarb$argile/10, "SILT"=granulo_no_decarb$limon/10, "SAND" = granulo_no_decarb$sable/10)
# Projection dans le triangle de texture de l'Aisne
TT.plot(
class.sys = "FR.AISNE.TT", # choix du triangle de texture
tri.data = texture, # localisation des données
grid.show = FALSE, # effacer la grille de fond du triangle
new.mar = c(2,0,4,0), # marges du graphique
lwd.axis = 2, cex.axis=0.7, # épaisseur des lignes et taille du texte des axes
col = "blue", pch=20, cex = 0.0001, # couleur, forme et taille des points
cex.lab = 0.8, css.lab = c( "argile [%]", "limon [%]", "sable [%]" ), # label des cotés du triangle
main = "Triangle de l'Aisne" # titre de la figure
)
rm(texture) # suppression du fichier TEXTURE qui ne sera plus utile par la suiteIl s’agit ici de toutes les granulométries sans décarbonatation tout horizons confondus.
3.3 PREPARATION DES DONNEES D’ANALYSES CHIMIQUES
Pour ces données, nous devons faire particulièrement attention aux méthodes d’analyse et aux unités. Les données d’analyses chimiques sont stockées dans la table RESULTAT_ANALYSE de DoneSol. Attention : dans le dictionnaire de données, ce sont les numéros de méthode (no_methode) qui sont indiquées, alors que dans les tables nous trouvons les identifiants des méthodes (id_methode). Le fichier “id_methode.xlsx” contient la correspondance.
Attention à ne pas confondre le champ NO_METHODE qui correspond au code indiqué dans les tableaux de la table RESULTAT_ANALYSE du dictionnaire de données et le champ ID_METHODE qui est l’identifiant de la méthode d’analyse dans DoneSol. Il existe bien sûr une correspondance entre les deux (fichier “id_methode.xlsx”).
# importation des méthodes
methods <- read_xlsx("donnees/jour1/id_methode.xlsx") %>%
mutate(id_method = as.character(id_method) )3.3.1 III.1. Carbone organique total (g/kg)
Nous chargeons les données d’analyse du carbone avec l’identifiant de la méthode. Pour le carbone, la méthode d’analyse est importante et il est nécessaire de choisir les méthodes retenues.
carbone<- resultat_analyse %>%
filter(determination == 'carbone') %>%
select(id_resultat, id_analyse,id_methode,valeur)%>%
rename(id_methode_carbone = id_methode,
carbone = valeur)Il peut arriver que le carbone ne soit pas renseigné dans DoneSol mais que la détermination MAT_ORG (matière organique) le soit. Il est alors possible de compléter la détermination CARBONE à partir de la détermination MAT_ORG en appliquant CARBONE = MAT_ORG / 1.724.
mat_org<-subset(resultat_analyse, determination == 'mat_org')
mat_org<-data.frame("id_resultat"=mat_org$id_resultat,
"id_analyse"=mat_org$id_analyse,
"id_methode_mat_org"=mat_org$id_methode,
"mat_org"=mat_org$valeur)
## On complete carbone avec mat_org dans une nouvelle table CARBONE2
# jointure entière entre carbone et mat_org
carbone2<- full_join(carbone, mat_org, by="id_analyse")
# supression des doublons
carbone2<-distinct(carbone2, .keep_all = FALSE)
# on rajoute un champ carb_calc contenant une valeur de carbone calculée à partir de mat_org
carbone2$mat_org<-as.numeric(carbone2$mat_org) # mise en numérique du champ mat_org
carbone2$carbone<-as.numeric(carbone2$carbone) # mise en numérique du champ valeur
carbone2$carb_calc <- (carbone2$mat_org / 1.724)
# on complète carbone avec cette valeur calculée
carbone2$carbone <- ifelse(!is.na(carbone2$carbone),
carbone2$carbone,
carbone2$carb_calc)Pour le carbone, nous allons séparer les données selon les méthodes d’analyse. Nous allons créer un fichier contenant le carbone analysé selon la méthode Anne et un fichier contenant le carbone analysé par combustion sèche (CS).
carbone2$id_methode_carbone<-as.character(carbone2$id_methode_carbone)
carbone2<- left_join(carbone2,
methods,
by=c("id_methode_carbone"="id_method"))
# choix des méthodes de DoneSol correspondant à la méthode Anne
carbone_anne<-subset(carbone2, no_methode %in% c("1","17", "17.1", "17.2", "17.3", "17.4", "17.5",
"17.6", "17.7", "17.8", "17.9","17.10",
"17.10.1","17.10.2", "17.10.3","17.11", "120"))
colnames(carbone_anne)[4] <- c("carbone_anne") # renommer la colonne
carbone_anne$id_analyse <- as.numeric (carbone_anne$id_analyse) # Mettre le résultat en numérique. Il est toujours en g/kg.
carbone_anne<-data.frame("id_analyse"=carbone_anne$id_analyse,
"carbone_anne"=carbone_anne$carbone_anne)
summary(carbone_anne)## id_analyse carbone_anne
## Min. : 2925 Min. : 0.70
## 1st Qu.:122255 1st Qu.: 12.90
## Median :162857 Median : 17.00
## Mean :138284 Mean : 19.09
## 3rd Qu.:173582 3rd Qu.: 22.10
## Max. :188403 Max. :443.00
# choix des méthodes de DoneSol correspondant à la combustion sèche
carbone_cs<-subset(carbone2, no_methode %in% c("16", "16.1", "16.2", "16.2.1", "16.2.2", "16.3",
"16.4", "16.5", "16.5.1", "16.5.2"))
colnames(carbone_cs)[4] <- c("carbone_cs") # renommer la colonne
carbone_cs$id_analyse <- as.numeric (carbone_cs$id_analyse) # Mettre le résultat en numérique. Il est toujours en g/kg.
carbone_cs<-data.frame("id_analyse"=carbone_cs$id_analyse,
"carbone_cs"=carbone_cs$carbone_cs)
summary(carbone_cs)## id_analyse carbone_cs
## Min. :122961 Min. :12.00
## 1st Qu.:127254 1st Qu.:14.40
## Median :127327 Median :19.00
## Mean :135856 Mean :18.66
## 3rd Qu.:127598 3rd Qu.:21.80
## Max. :174139 Max. :26.10
Nous avons ainsi des résultats d’analyse propre pour le carbone. Nous pouvons les visualiser avec un histogramme avec la fonction hist() Sa syntaxe de base est : hist(x, breaks = “…”, col = “la couleur”, main = “le titre”, xlab = “Valeurs”, ylab = “Fréquence”…) Voici un exemple pour le carbone méthode Anne :
hist(carbone_anne$carbone_anne,
breaks= 100,
col="darkgoldenrod1",
main="Histogramme du carbone Anne",
xlab = "carbone (g/kg)")
Nous supprimons les fichiers devenus inutiles
3.3.2 III.2. pH eau (unité pH)
# Import pH eau
ph_eau<-subset(resultat_analyse, determination == 'ph_eau')
ph_eau<-data.frame("id_resultat"=ph_eau$id_resultat,
"id_analyse"=ph_eau$id_analyse,
"id_methode_ph_eau"=ph_eau$id_methode,
"ph_eau"=ph_eau$valeur)
ph_eau$ph_eau<- as.numeric(ph_eau$ph_eau)Nous réalisons l’histogramme du pH eau :
hist(ph_eau$ph_eau, breaks= 25, xlim=c(3,10), col="tomato", main="Histogramme du pHeau", xlab = "pH eau")
3.3.3 III.3. CEC (mé/100g)
# Import cec
cec<-subset(resultat_analyse, determination == 'cec')
cec<-data.frame("id_resultat"=cec$id_resultat,
"id_analyse"=cec$id_analyse,
"id_methode_cec"=cec$id_methode,
"cec"=cec$valeur)Pour la CEC, la méthode d’analyse est importante. Nous allons séparer la méthode Metson et la méthode à la cobaltihexammine.
## Pour la CEC
cec$id_methode_cec <- as.character(cec$id_methode_cec)
cec<- dplyr::left_join(cec, methods, by=c("id_methode_cec"="id_method"))
# Sélection de la méthode Metson
cec_metson<-subset(cec, no_methode %in% c("41", "41.4", "41.5", "41.5.1", "41.5.2", "41.6", "41.6.1", "41.7", "41.8", "41.9"))
colnames(cec_metson)[4] <- c("cec_metson")
cec_metson$cec_metson<-as.numeric(cec_metson$cec_metson)
summary(cec_metson$cec_metson) # synthèse de la cec metson## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.500 6.100 7.800 8.344 9.700 150.000
# Sélection de la méthode à la cobaltihexammine
cec_cobalti<-subset(cec, no_methode %in% c("40", "40.1", "40.1.1", "40.2", "40.2.2", "40.5"))
colnames(cec_cobalti)[4] <- c("cec_cobalti")
cec_cobalti$cec_cobalti<-as.numeric(cec_cobalti$cec_cobalti)
# Supression du fichier cec devenu inutile
rm(cec)
# Exploration des données
summary(cec_cobalti$cec_cobalti)## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.900 5.700 7.500 7.926 9.400 30.500
3.3.4 III.4. Calcaire total (calc_tot CaCO3 en g/kg)
# Importation de la détermination calc_tot
calc_tot<-subset(resultat_analyse, determination == 'calc_tot')
calc_tot<-data.frame("id_resultat"=calc_tot$id_resultat,
"id_analyse"=calc_tot$id_analyse,
"id_methode_calc_tot"=calc_tot$id_methode,
"calc_tot"=calc_tot$valeur)
calc_tot$calc_tot <- as.numeric(calc_tot$calc_tot)
summary(calc_tot)## id_resultat id_analyse id_methode_calc_tot calc_tot
## Min. : 413703 Min. :52975 Min. :1.000 Min. : 0.000
## 1st Qu.: 415262 1st Qu.:53076 1st Qu.:1.000 1st Qu.: 0.100
## Median : 415321 Median :53175 Median :1.000 Median : 0.400
## Mean :1068654 Mean :53826 Mean :1.526 Mean : 1.995
## 3rd Qu.:1924489 3rd Qu.:54512 3rd Qu.:1.000 3rd Qu.: 1.650
## Max. :2096270 Max. :55846 Max. :6.000 Max. :10.500
ⓘ NOTE
Vous pouvez sur le même modèle charger d’autres déterminations de la table RESULTAT_ANALYSE.
3.4 FUSION DES DONNEES DANS UN SEUL FICHIER
Nous allons regrouper l’ensemble de nos informations extraites et préparées précédemment dans un seul fichier. Cela sera ensuite plus facile à utiliser. Nous allons appeler ce fichier un datamart car il regroupe des informations de différentes tables de DoneSol.
Pour cela, nous utilisons des jointures entre les tables.
dm_sol<-full_join(profil,poi, by="id_profil") # jointure entre PROFIL et POI
dm_sol<-left_join(dm_sol,horizon, by="id_profil") # jointure entre DM_SOL et HORIZON
dm_sol<-left_join(dm_sol, granulo_no_decarb, by=c("id_profil", "no_horizon")) # jointure entre DM_SOL et GRANULO_NO_DECARB## Warning in left_join(dm_sol, granulo_no_decarb, by = c("id_profil", "no_horizon")): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 241 of `x` matches multiple rows in `y`.
## ℹ Row 1686 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
dm_sol<-left_join(dm_sol, carbone_anne, by= "id_analyse") # jointure entre DM_SOL et CARBONE_ANNE## Warning in left_join(dm_sol, carbone_anne, by = "id_analyse"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 5626 of `x` matches multiple rows in `y`.
## ℹ Row 938 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
## "many-to-many"` to silence this warning.
dm_sol<-left_join(dm_sol, calc_tot, by= "id_analyse") # jointure entre DM_SOL et CALC_TOT
dm_sol<-left_join(dm_sol, cec_cobalti, by= "id_analyse") # jointure entre DM_SOL et CEC_COBALTI
dm_sol<-left_join(dm_sol, cec_metson, by= "id_analyse") # jointure entre DM_SOL et CEC_METSON
dm_sol<-left_join(dm_sol, ph_eau, by= "id_analyse") # jointure entre DM_SOL et PH_EAU
dm_sol<-left_join(dm_sol, analyse, by= "id_analyse", copy=FALSE) # jointure entre DM_SOL et ANALYSENous allons ensuite simplifier ce fichier pour ne conserver que les champs utiles pour la suite.
# Nous supprimons les champs inutiles car redondants
names(dm_sol) # Nous donne le numéro de colonne de tous les champs de la table dm_sol. ## [1] "id_profil.x" "altitude"
## [3] "date_p" "drai_nat_p"
## [5] "arret" "prof_arret"
## [7] "landuse" "GER"
## [9] "x_l93" "y_l93"
## [11] "no_horizon.x" "cpcs_nom"
## [13] "rp_95_nom" "rp_2008_nom"
## [15] "prof_sup_min" "prof_sup_moy"
## [17] "prof_sup_max" "prof_inf_min"
## [19] "prof_inf_moy" "prof_inf_max"
## [21] "textur" "abondance_eg"
## [23] "id_ens_resultat_analyse_granulo" "id_analyse"
## [25] "id_prelevement.x" "argile"
## [27] "limon" "sable"
## [29] "carbone_anne" "id_resultat.x"
## [31] "id_methode_calc_tot" "calc_tot"
## [33] "id_resultat.y" "id_methode_cec.x"
## [35] "cec_cobalti" "no_methode.x"
## [37] "name_method.x" "id_resultat.x.x"
## [39] "id_methode_cec.y" "cec_metson"
## [41] "no_methode.y" "name_method.y"
## [43] "id_resultat.y.y" "id_methode_ph_eau"
## [45] "ph_eau" "id_profil.y"
## [47] "no_horizon.y" "id_prelevement.y"
## [49] "prof_base" "prof_sommet"
# Cela nous permet de sélectionner les champs à supprimer.
# Nous supprimons les champs que nous ne souhaitons pas conserver.
dm_sol <- dm_sol[,-c(33, 34,38, 39, 41,42, 43, 44, 46, 47, 48)]
# Renommer les champs
names(dm_sol)[names(dm_sol) == "id_profil.x"] <- "id_profil"
names(dm_sol)[names(dm_sol) == "no_horizon.x"] <- "no_horizon"
names(dm_sol)[names(dm_sol) == "id_prelevement.x"] <- "id_prelevement"
names(dm_sol)[names(dm_sol) == "id_resultat.x"] <- "id_resultat"
names(dm_sol)[names(dm_sol) == "no_methode.x"] <- "no_methode"
names(dm_sol)[names(dm_sol) == "name_method.x"] <- "name_method"
# On remet les champs qui doivent y être en numérique
dm_sol$cec_cobalti<-as.numeric(dm_sol$cec_cobalti)
dm_sol$cec_metson<-as.numeric(dm_sol$cec_metson)
dm_sol$prof_sup_min<-as.numeric(dm_sol$prof_sup_min)
dm_sol$prof_sup_moy<-as.numeric(dm_sol$prof_sup_moy)
dm_sol$prof_sup_max<-as.numeric(dm_sol$prof_sup_max)
dm_sol$prof_inf_min<-as.numeric(dm_sol$prof_inf_min)
dm_sol$prof_inf_moy<-as.numeric(dm_sol$prof_inf_moy)
dm_sol$prof_inf_max<-as.numeric(dm_sol$prof_inf_max)
str(dm_sol)## 'data.frame': 10334 obs. of 39 variables:
## $ id_profil : int 110185 110185 110185 132400 132400 132400 96907 96907 96907 84414 ...
## $ altitude : chr "" "" "" "" ...
## $ date_p : chr "01/10/1975" "01/10/1975" "01/10/1975" "15/04/1990" ...
## $ drai_nat_p : int NA NA NA NA NA NA NA NA NA NA ...
## $ arret : int 1 1 1 2 2 2 1 1 1 1 ...
## $ prof_arret : int NA NA NA NA NA NA NA NA NA NA ...
## $ landuse : Factor w/ 7 levels "Forets","Prairies_permanentes",..: 2 2 2 3 3 3 3 3 3 3 ...
## $ GER : Factor w/ 34 levels "ALOCRISOL","ALOCRISOL-REDOXISOL",..: 4 4 4 4 4 4 4 4 4 12 ...
## $ x_l93 : num 397918 397918 397918 472986 472986 ...
## $ y_l93 : num 6751603 6751603 6751603 6820284 6820284 ...
## $ no_horizon : int 1 3 2 2 1 3 2 1 3 1 ...
## $ cpcs_nom : chr "Ap" "C" "B" "" ...
## $ rp_95_nom : chr "" "" "" "Sg" ...
## $ rp_2008_nom : chr "" "" "" "" ...
## $ prof_sup_min : num NA NA NA NA NA NA NA NA NA NA ...
## $ prof_sup_moy : num 0 50 25 25 0 40 20 0 80 0 ...
## $ prof_sup_max : num NA NA NA NA NA NA NA NA NA NA ...
## $ prof_inf_min : num NA NA NA NA NA NA NA NA NA NA ...
## $ prof_inf_moy : num 25 60 50 40 25 130 80 20 120 20 ...
## $ prof_inf_max : num NA NA NA NA NA NA NA NA NA NA ...
## $ textur : chr "LSa" "ND" "LAS" "SL" ...
## $ abondance_eg : num 20 NA 70 60 30 60 70 20 65 10 ...
## $ id_ens_resultat_analyse_granulo: int 2936 NA 2937 124783 124782 124784 102543 102542 102544 101847 ...
## $ id_analyse : num 2981 NA 2982 165346 165345 ...
## $ id_prelevement : int 2978 NA 2979 151194 151193 151195 115273 115272 115274 114602 ...
## $ argile : num 188 NA 225 173 170 ...
## $ limon : num 542 NA 463 461 384 ...
## $ sable : num 270 NA 312 366 446 ...
## $ carbone_anne : num 18.4 NA NA NA 28.5 NA NA 33 NA 27 ...
## $ id_resultat : int NA NA NA NA NA NA NA NA NA NA ...
## $ id_methode_calc_tot : int NA NA NA NA NA NA NA NA NA NA ...
## $ calc_tot : num NA NA NA NA NA NA NA NA NA NA ...
## $ cec_cobalti : num NA NA NA NA NA NA NA NA NA NA ...
## $ no_methode : chr NA NA NA NA ...
## $ name_method : chr NA NA NA NA ...
## $ cec_metson : num NA NA NA 7.4 8 3.4 14 13 10.7 13.1 ...
## $ ph_eau : num 5.6 NA 6.3 6 5.8 5.6 7.5 6.6 5.8 4.8 ...
## $ prof_base : num 25 45 50 40 25 130 80 20 100 20 ...
## $ prof_sommet : num 0 30 25 25 0 40 25 0 80 0 ...
Nous vérifions que nos données n’ont pas changées et que nous retrouvons bien dans dm_sol nos données d’origine. Pour cela, nous pouvons par exemple refaire l’histogramme du pH à partir de dm_sol.
par(mfrow = c(1, 2)) # séparation de la page en 2 colonnes
# Histogramme à partir de la table ph_eau
hist(ph_eau$ph_eau,
breaks= 25,
xlim=c(3,10),
col="tomato",
main="Histogramme du pHeau (ph_eau)",
xlab = "pH eau")
# histogramme à partir de la table dm_sol
hist(dm_sol$ph_eau,
breaks= 25,
xlim=c(3,10),
col="tomato",
main="Histogramme du pHeau (dm_sol)",
xlab = "pH eau")
Les 2 histogrammes doivent être identiques.
Il est possible de sauvegarder le fichier final en CSV :
# write.csv2(dm_sol, file = "dm_sol.csv")
# Nous supprimons alors l'ensemble des tables et ne conservons que le fichier dm_sol.
rm(analyse)
rm(calc_tot)
rm(carbone_anne)
rm(carbone_cs)
rm(cec_cobalti)
rm(cec_metson)
rm(granulo_no_decarb)
rm(horizon)
rm(ph_eau)
rm(profil)
rm(resultat_analyse)
rm(resultat_eg)3.5 SELECTION DE COUCHES DE PROFONDEURS AVEC DES SPLINES
Ici nous allons faire une modélisation de la profondeur en utilisant un algorithme d’ajustement quadratique fondé sur des surfaces égales [souvent résumée par le mot « spline » (Bishop et al., 1999 ; Malone et al., 2009)]. Cette fonction est fondée sur des ajustements polynômiaux et lisse les données sur l’ensemble de la profondeur du sol. Elle impose une contrainte qui est que la courbe ajustée sur les horizons d’origine et passant par leur profondeur moyenne respecte pour chaque horizon des surfaces égales pour les aires comprises entre cette courbe et une droite verticale passant par les valeurs moyennes affectées aux horizons.

Schématisation du principe d’ajustement d’une fonction polynomiale quadratique (« spline ») à partir d’observations supposées représenter une valeur moyenne d’une propriété mesurée sur des horizons parallèles (rectangles verticaux en A) en respectant, par horizon, des surfaces de valeurs inférieures ou supérieures égales à la moyenne de cet horizon (surfaces hachurées en B), pour obtenir un « spline » (en C), puis utiliser le « spline » pour inférer des valeurs par couches GlobalSoilMap standardisées (en D). Exemple sur le carbone, extrait de (Arrouays et al., 2014), et lui-même adapté de Bishop et al. (1999) et Malone et al. (2009).
Liste des références
Arrouays, D., Grundy, M. G., Hartemink, A., Hempel, J., Heuvelink, G., Hong, S., Lagacherie, P., Lelyk, G., McBratney, A., McKenzie, N., Mendonca-Santos, M., Minasny, B., Montanarella, L., Odeh, I., Sanchez, P., Thompson, J. et Zhang, G. (2014a). GlobalSoilMap: Toward a Fine-Resolution Global Grid of Soil Properties. Dans D. Sparks (dir.), Advances in Agronomy (vol. 125, p. 93). https://doi.org/10.1016/B978-0-12-800137-0.00003-0
Bishop, T. F. A., McBratney, A. B. et Laslett, G. M. (1999). Modelling soil attribute depth functions with equal-area quadratic smoothing splines. Geoderma, 91(1‑2), 27‑45. https://doi.org/10.1016/S0016-7061(99)00003-8
Malone, B. P., McBratney, A. B., Minasny, B. et Laslett, G. M. (2009). Mapping continuous depth functions of soil carbon storage and available water capacity. Geoderma, 154(1‑2), 138‑152. https://doi.org/10.1016/j.geoderma.2009.10.007
La variable cible doit être un vecteur numérique mesuré sur au moins deux horizons pour que la spline puisse être ajustée. Les profils à un seul horizon sont acceptés et traités conformément aux exigences de sortie, mais aucune spline n’est ajustée en tant que telle.
Seuls les nombres positifs pour les profondeurs supérieures et inférieures peuvent être acceptés. Il est donc important d’enlever les horizons d’humus du jeu de données.
L’ordre des variables dans le fichier d’entrée est important et doit être :
“champ.id” : identifiant du profil. Pour nous il s’agit de id_profil
“champ.prof.sup” : profondeur d’apparition de l’horizon
“champ.prof.inf” : profondeur de disparition de l’horizon
“champ.variable” : valeur de la détermination d’intérêt
ⓘ NOTE
Il faut refaire les splines pour chacune des variables d’intérêt.
Pour les 3 fractions de granulométrie, il faut bien vérifier que la somme boucle à 1000 g/kg dans les splines.
Pour l’exemple, nous ferons les splines sur 3 variables : le pH eau, la teneur en argile et la teneur en carbone (méthode Anne).
3.5.1 Exemples
3.5.1.1 V.1. pH eau
Nous commençons par préparer les données.
# Préparation des données d'entrée des splines
ph <- data.frame("champ.id"=dm_sol$id_profil,
"champ.prof.sup"=dm_sol$prof_sup_moy,
"champ.prof.inf"=dm_sol$prof_inf_moy,
"champ.variable"=dm_sol$ph_eau)
# Nous supprimons les lignes où notre variable d'intérêt est absente
ph <- ph %>% filter(!is.na(champ.variable))
# Nous supprimons les lignes dont les profondeurs sont absentes
ph <- ph %>% filter(!is.na(champ.prof.sup))
# Nous complétons la profondeur de disparition quand celle-ci est absente
# (profondeur de disparition = profondeur d'apparition + 10 cm)
ph$champ.prof.inf<-ifelse(is.na(ph$champ.prof.inf), ph$champ.prof.sup+10, ph$champ.prof.inf)
# Nous mettons les profondeurs ("champ.prof.sup") par ordre croissant
ph <- arrange(ph, champ.id, champ.prof.sup)
# supression des profondeurs négatives
ph <- droplevels(ph[-which(ph$champ.prof.sup<0),])
summary(ph)## champ.id champ.prof.sup champ.prof.inf champ.variable
## Min. : 382 Min. : 0.00 Min. : 3.00 Min. :3.200
## 1st Qu.: 84550 1st Qu.: 0.00 1st Qu.: 30.00 1st Qu.:6.000
## Median : 90446 Median : 30.00 Median : 55.00 Median :6.600
## Mean : 93597 Mean : 34.85 Mean : 64.73 Mean :6.594
## 3rd Qu.:126114 3rd Qu.: 55.00 3rd Qu.: 90.00 3rd Qu.:7.200
## Max. :139441 Max. :200.00 Max. :280.00 Max. :8.900
Pour réaliser les splines, nous allons utiliser la fonction “ea_spline” du package ithir. La fonction ea_spline ajuste une spline continue, préservant la masse, depuis la surface du sol jusqu’à la profondeur maximale choisie. Elle interpole de manière fluide à la fois au sein des horizons observés et à travers les lacunes où aucune mesure n’existe. Pour ceux qui souhaite voir la fonction en détail : https://rdrr.io/rforge/ithir/src/R/ea_spline.R
Les couches de profondeurs obtenues sont choisies dans la fonction. Ici, nous choisissons les profondeurs du programme mondial GlobalSoilMap : 0-5, 5-15, 15-30, 30-60, 60-100 et 100-200 cm. Il est bien sûr possible de personnaliser ces profondeurs.
ⓘ Remarque sur les performances :
La fonction ea_spline utilise des calculs internes efficaces et un minimum de dépendances externes, ce qui vous permet de traiter des dizaines de milliers de profils en quelques minutes. Le principal facteur déterminant la durée d’exécution est simplement le nombre total de profils et les intervalles de profondeur.
#Chargement du package ithir
# install.packages("ithir", repos="http://R-Forge.R-project.org") # à faire si le package n'est pas installé
# Réalisation des splines
ph_spline <- ithir::ea_spline(ph, var.name="champ.variable",
d= t(c(0,5,15,30,60,100,200)), # Définit les intervalles de profondeur cibles pour les sorties harmonisées
lam = 0.1, # Paramètre de contrôle de la fluidité des splines
vlow=0, # la plus petite valeur de la variable cible (les valeurs plus petites seront remplacées)
show.progress=FALSE)
# Visualiser le résultat
str(ph_spline)## List of 4
## $ harmonised :'data.frame': 2862 obs. of 8 variables:
## ..$ id : num [1:2862] 382 388 397 413 426 433 438 446 454 458 ...
## ..$ 0-5 cm : num [1:2862] 6.65 5.71 5.38 6.05 5.53 ...
## ..$ 5-15 cm : num [1:2862] 6.67 5.78 5.46 6.27 5.48 ...
## ..$ 15-30 cm : num [1:2862] 6.75 6.1 5.58 7.14 5.27 ...
## ..$ 30-60 cm : num [1:2862] 7 6.64 5.23 6.83 5 ...
## ..$ 60-100 cm : num [1:2862] 7.31 5.7 NA NA NA ...
## ..$ 100-200 cm: num [1:2862] NA 5.25 NA NA NA ...
## ..$ soil depth: num [1:2862] 90 120 55 60 40 70 45 60 60 50 ...
## $ obs.preds :'data.frame': 9295 obs. of 6 variables:
## ..$ champ.id : num [1:9295] 382 382 382 388 388 388 388 397 397 397 ...
## ..$ champ.prof.sup: num [1:9295] 0 30 80 0 30 70 110 0 12 45 ...
## ..$ champ.prof.inf: num [1:9295] 30 80 90 30 70 110 120 12 45 55 ...
## ..$ champ.variable: num [1:9295] 6.7 7.1 7.4 5.9 6.6 5.4 5.3 5.4 5.5 5 ...
## ..$ predicted : num [1:9295] 6.7 7.1 7.4 5.92 6.56 ...
## ..$ FID : num [1:9295] 1 1 1 2 2 2 2 3 3 3 ...
## $ splineFitError:'data.frame': 2862 obs. of 2 variables:
## ..$ rmse : num [1:2862] 0.00269 0.02643 0.01303 0.09193 0.00583 ...
## ..$ rmseiqr: num [1:2862] 0.00767 0.03776 0.05213 0.06566 0.02913 ...
## $ var.1cm : num [1:200, 1:2862] 6.65 6.65 6.65 6.65 6.65 ...
Les résultats contiennent 4 objets :
- ph_spline$harmonised : valeurs harmonisées par profil et profondeur
- ph_spline$obs.preds : valeurs observées vs prédites par horizon
- ph_spline$splineFitError : RMSE et nRMSE (RMSE_IQR) pour chaque profil
- ph_spline$var.1cm : courbes splines haute résolution
Avec plusieurs profils, le résultat devient plus intuitif. En particulier, le cadre de données harmonisé “ph_spline$harmonised” répertorie une ligne par profil, chaque colonne représentant la valeur prévue à une profondeur spécifiée par l’utilisateur. La colonne “soil.depth” indique la profondeur maximale de chaque profil, ce qui vous aide à vérifier que les estimations harmonisées couvrent toute la plage observée.
Nous pouvons faire un graphique avec les valeurs prédites et mesurées à partir de “obs.preds”.
# Réalisation du graphique prédit / mesuré
plot(ph_spline$obs.preds$predicted,
ph_spline$obs.preds$champ.variable,
xlab="pH prédit par les splines",
ylab = "pH mesuré",
ylim = c(2,10),
xlim = c(2,10))
A partir de “splineFitError”, nous pouvons analyser les erreurs issues des splines. Pour cela, nous avons 2 métriques disponibles :
- La RMSE est la racine de l’erreur quadratique moyenne correspondant à l’écart-type des résidus (erreurs de prédiction).
\[ RMSE = \sqrt {\sum_{i = 1}^{n}\frac{(\hat{y_i} - y_i)^2}{n}} \] où \(\hat{y_i}\) sont les valeurs prédites, \({y_i}\) les valeurs mesurées et n le nombre de données.
- La RMSEIQR est la RMSE divisée par l’écart interquartile. Cette valeur normalisée devient moins sensible aux valeurs extrêmes de la variable cible.
\[ RMSE_{IQR} = \frac{RMSE}{Q3-Q1} \] où Q1 est le quantile 0,25 et Q3 le quantile 0,75.
L’unité de ces 2 métriques est la même que la variable (ici, unité pH).
boxplot(ph_spline$splineFitError$rmse,
ph_spline$splineFitError$rmseiqr,
names=c("RMSE","RMSEIQR"), # nom des boxplots
horizontal=T, # mettre le boxplot horizontal
xlab="unité pH", # label de l'axe X
col=c("dodgerblue2","darkorchid"), # couleurs des boxplots
ylim = c(0, 0.1), # fourchette de valeurs de l'axe Y (ici horizontal)
pch = 20) # format des points## Warning in bplt(at[i], wid = width[i], stats = z$stats[, i], out =
## z$out[z$group == : Une valeur extrême (Inf) dans la boite de dispersion 2 n'est
## pas représentée

Nous pouvons analyser les résultats, en regardant par exemple l’histogramme de la couche 0-5 cm :
summary(ph_spline$harmonised)## id 0-5 cm 5-15 cm 15-30 cm
## Min. : 382 Min. :2.974 Min. :0.4027 Min. :-9999.000
## 1st Qu.: 84575 1st Qu.:5.850 1st Qu.:5.9000 1st Qu.: 6.038
## Median : 90644 Median :6.219 Median :6.2722 Median : 6.417
## Mean : 94133 Mean :6.300 Mean :6.3429 Mean : -21.465
## 3rd Qu.:127120 3rd Qu.:6.655 3rd Qu.:6.6907 3rd Qu.: 6.902
## Max. :139441 Max. :9.512 Max. :8.5625 Max. : 30.207
## NA's :2
## 30-60 cm 60-100 cm 100-200 cm soil depth
## Min. :-9999.000 Min. :-9999.000 Min. :-9999.000 Min. : 9.00
## 1st Qu.: 6.170 1st Qu.: 5.796 1st Qu.: 5.183 1st Qu.: 65.00
## Median : 6.691 Median : 6.688 Median : 6.332 Median : 91.00
## Mean : -394.867 Mean : -560.547 Mean :-1035.637 Mean : 95.77
## 3rd Qu.: 7.300 3rd Qu.: 7.344 3rd Qu.: 7.163 3rd Qu.:120.00
## Max. : 19.909 Max. : 31.923 Max. : 9.116 Max. :280.00
## NA's :22 NA's :516 NA's :1585
hist(ph_spline$harmonised$`0-5 cm`, breaks= 100, col="darkcyan", main="Histogramme couche 0-5 cm",
xlab = "pH eau")
Nous allons faire le cartogramme du pH de la profondeur 0-5 cm. Pour cela, nous allons utiliser le fond de carte de la partie I.2.2.
# Nous rapatrions les coordonnées XY sur les résultats des splines
ph <- ph_spline$harmonised
ph <- left_join(ph, poi, by=c("id"="id_profil"))
# supression des points sans coordonnées
ph <-ph %>% filter(!is.na(x_l93))
ph <-ph %>% filter(!is.na(y_l93))
# Nous convertissons les données XY en fichier spatial
ph <- st_as_sf(ph, coords = c("x_l93", "y_l93"), crs = st_crs(mayenne))
# Pour ne conserver que les points de la zone d'étude
ph <- st_join(ph, mayenne, left = FALSE)
library("ggspatial")
ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `0-5 cm`))+
scale_color_viridis_c(option = "C") + # lettre indiquant l'option de couleurs
theme(legend.position = "right") + # position de la légende
labs(title = "pH couche 0-5 cm", fill = "pH eau") + # titre du graphique
labs(color="pH") + # titre de la légende
theme(legend.title = element_text(color="black", size=10, face = "bold"))+ # formet du texte de la légende
theme_void() +
# ajout d'une échelle
annotation_scale(location = "bl",
line_width = 0.5,
style="ticks",
width_hint = 0.1,
text_face=1,
pad_x = unit(0, "in"),
pad_y = unit(0, "in"),) +
# ajout d'une flèche Nord
annotation_north_arrow(location = "tl",
which_north = "true",
pad_x = unit(-0.09, "in"),
pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))## Ignoring unknown labels:
## • fill : "pH eau"

Nous pouvons aussi sortir les cartogrammes de toutes les profondeurs sur une seule figure. Pour cela, nous créons un objet par graphique et nous les regroupons tous à la fin.
plot1 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `0-5 cm`))+
scale_color_viridis_c(option = "H", limits=c(3, 9), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "pH 0-5 cm", fill = "pH eau") +
labs(color="pH") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot2 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `5-15 cm`))+
scale_color_viridis_c(option = "H", limits=c(3, 9), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "pH 5-15 cm", fill = "pH eau") +
labs(color="pH") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot3 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `15-30 cm`))+
scale_color_viridis_c(option = "H", limits=c(3, 9), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "pH 15-30 cm", fill = "pH eau") +
labs(color="pH") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot4 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `30-60 cm`))+
scale_color_viridis_c(option = "H", limits=c(3, 9), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "pH 30-60 cm", fill = "pH eau") +
labs(color="pH") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot5 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `60-100 cm`))+
scale_color_viridis_c(option = "H", limits=c(3, 9), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "pH 60-100 cm", fill = "pH eau") +
labs(color="pH") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot6 <-ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = ph, aes(color = `100-200 cm`))+
scale_color_viridis_c(option = "H", limits=c(3, 9), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "pH 100-200 cm", fill = "pH eau") +
labs(color="pH") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot_grid(plot1, plot2, plot3, plot4, plot5, plot6)## Ignoring unknown labels:
## • fill : "pH eau"
## Ignoring unknown labels:
## • fill : "pH eau"
## Ignoring unknown labels:
## • fill : "pH eau"
## Ignoring unknown labels:
## • fill : "pH eau"
## Ignoring unknown labels:
## • fill : "pH eau"
## Ignoring unknown labels:
## • fill : "pH eau"

3.5.1.2 V.2. Teneur en argile
Pour notre second exemple, nous ferons la teneur en argile (g/kg). Si nous voulons faire les 3 fractions, il faudra s’assurer que la somme des 3 fractions en sortie des splines bouche bien à 1000 g/kg.
# Préparation des données d'entrée des splines
argile <- data.frame("champ.id"=dm_sol$id_profil,
"champ.prof.sup"=dm_sol$prof_sup_moy,
"champ.prof.inf"=dm_sol$prof_inf_moy,
"champ.variable"=dm_sol$argile)
# Nous supprimons les lignes où notre variable d'intérêt est absente
argile <- argile %>% filter(!is.na(champ.variable))
# Nous complétons la profondeur de disparition quand celle-ci est absente (profondeur de disparition = profondeur d'apparition + 10 cm)
argile$champ.prof.inf<-ifelse(is.na(argile$champ.prof.inf), argile$champ.prof.sup+10, argile$champ.prof.inf)
# Nous mettons les profondeurs ("champ.prof.sup") par ordre croissant
argile <- arrange(argile, champ.id, champ.prof.sup)
# Nous supprimons les lignes dont les profondeurs sont absentes
argile <- argile %>% filter(!is.na(champ.prof.sup))
summary(argile) # vérification des données## champ.id champ.prof.sup champ.prof.inf champ.variable
## Min. : 382 Min. : -7.00 Min. : -2.00 Min. : 9.793
## 1st Qu.: 84551 1st Qu.: 0.00 1st Qu.: 30.00 1st Qu.:143.143
## Median : 90447 Median : 30.00 Median : 55.00 Median :182.000
## Mean : 93606 Mean : 34.85 Mean : 64.73 Mean :204.134
## 3rd Qu.:126114 3rd Qu.: 55.00 3rd Qu.: 90.00 3rd Qu.:238.000
## Max. :139441 Max. :200.00 Max. :280.00 Max. :858.142
# supression des profondeurs négatives
argile <- droplevels(argile[-which(argile$champ.prof.sup<0),]) Pour réaliser les splines, nous allons utiliser la fonction “ea_spline” du package ithir. La fonction ea_spline ajuste une spline continue, préservant la masse, depuis la surface du sol jusqu’à la profondeur maximale choisie. Elle interpole de manière fluide à la fois au sein des horizons observés et à travers les lacunes où aucune mesure n’existe.
#Chargement du package ithir
# install.packages("ithir", repos="http://R-Forge.R-project.org")
# Réalisation des splines
argile_spline <- ithir::ea_spline(argile, var.name="champ.variable",
d= t(c(0,5,15,30,60,100,200)), # Définit les intervalles de profondeur cibles pour les sorties harmonisées
lam = 0.1, # Paramètre de contrôle de la fluidité des splines
vlow=0, # la plus petite valeur de la variable cible (les valeurs plus petites seront remplacées)
show.progress=FALSE)
# Visualiser le résultat
str(argile_spline)## List of 4
## $ harmonised :'data.frame': 2862 obs. of 8 variables:
## ..$ id : num [1:2862] 382 388 397 413 426 433 438 446 454 458 ...
## ..$ 0-5 cm : num [1:2862] 217.9 345.5 250.6 195.1 32.2 ...
## ..$ 5-15 cm : num [1:2862] 220.2 333.4 196.1 209.7 70.1 ...
## ..$ 15-30 cm : num [1:2862] 230 285 141 268 222 ...
## ..$ 30-60 cm : num [1:2862] 251 217 459 358 419 ...
## ..$ 60-100 cm : num [1:2862] 253 463 NA NA NA ...
## ..$ 100-200 cm: num [1:2862] NA 634 NA NA NA ...
## ..$ soil depth: num [1:2862] 90 120 55 60 40 70 45 60 60 50 ...
## $ obs.preds :'data.frame': 9305 obs. of 6 variables:
## ..$ champ.id : num [1:9305] 382 382 382 388 388 388 388 397 397 397 ...
## ..$ champ.prof.sup: num [1:9305] 0 30 80 0 30 70 110 0 12 45 ...
## ..$ champ.prof.inf: num [1:9305] 30 80 90 30 70 110 120 12 45 55 ...
## ..$ champ.variable: num [1:9305] 224 253 247 316 232 ...
## ..$ predicted : num [1:9305] 224 252 247 313 240 ...
## ..$ FID : num [1:9305] 1 1 1 2 2 2 2 3 3 3 ...
## $ splineFitError:'data.frame': 2862 obs. of 2 variables:
## ..$ rmse : num [1:2862] 0.452 4.702 10.195 2.522 4.238 ...
## ..$ rmseiqr: num [1:2862] 0.0309 0.0172 0.0462 0.0385 0.0291 ...
## $ var.1cm : num [1:200, 1:2862] 218 218 218 218 218 ...
Les résultats contiennent 4 objets :
- argile_spline$harmonised : valeurs harmonisées par profil et profondeur
- argile_spline$obs.preds : valeurs observées vs prédites par horizon
- argile_spline$splineFitError : RMSE et nRMSE pour chaque profil
- argile_spline$var.1cm : courbes splines haute résolution
Nous pouvons faire un graphique avec les valeurs prédites et mesurées à partir de “obs.preds”.
# Réalisation du graphique prédit / mesuré
plot(argile_spline$obs.preds$predicted,
argile_spline$obs.preds$champ.variable,
xlab="teneur en argile prédite par les splines (g/kg)",
ylab = "teneur en argile mesurée (g/kg)",
xlim=c(0, 1000),
ylim=c(0, 1000))
summary(argile_spline$harmonised)## id 0-5 cm 5-15 cm 15-30 cm
## Min. : 382 Min. : 0.0 Min. : 0.0 Min. :-9999.0
## 1st Qu.: 84575 1st Qu.:139.0 1st Qu.:139.2 1st Qu.: 136.7
## Median : 90644 Median :162.3 Median :162.6 Median : 164.7
## Mean : 94133 Mean :176.5 Mean :177.1 Mean : 150.8
## 3rd Qu.:127120 3rd Qu.:200.6 3rd Qu.:200.0 3rd Qu.: 204.7
## Max. :139441 Max. :650.9 Max. :871.3 Max. : 600.0
## NA's :2
## 30-60 cm 60-100 cm 100-200 cm soil depth
## Min. :-9999.0 Min. :-9999.0 Min. :-9999.0 Min. : 9.00
## 1st Qu.: 135.0 1st Qu.: 151.8 1st Qu.: 141.0 1st Qu.: 65.25
## Median : 176.8 Median : 206.4 Median : 204.3 Median : 92.00
## Mean : -211.5 Mean : -346.6 Mean : -818.8 Mean : 95.83
## 3rd Qu.: 229.0 3rd Qu.: 278.1 3rd Qu.: 293.2 3rd Qu.:120.00
## Max. : 834.5 Max. : 1000.0 Max. : 958.4 Max. :280.00
## NA's :22 NA's :515 NA's :1583
hist(argile_spline$harmonised$`0-5 cm`,
breaks= 100,
col="darkgoldenrod1",
main="Histogramme couche 0-5 cm",
xlab = "argile (g/kg)")
A partir de “splineFitError”, nous pouvons analyser les erreurs issues des splines.
L’unité de ces 2 métriques est la même que la variable (ici, g/kg).
boxplot(argile_spline$splineFitError$rmse,
argile_spline$splineFitError$rmseiqr,
names=c("RMSE","RMSEIQR"), # nom des boxplots
horizontal=T, # mettre le boxplot horizontal
xlab="g/kg", # label de l'axe X
col=c("dodgerblue2","darkorchid"), # couleurs des boxplots
ylim = c(0, 5), # fourchette de valeurs de l'axe Y (ici horizontal)
pch = 20) # format des points## Warning in bplt(at[i], wid = width[i], stats = z$stats[, i], out =
## z$out[z$group == : Une valeur extrême (Inf) dans la boite de dispersion 2 n'est
## pas représentée

Nous allons faire les cartogrammes des teneurs en argile par couche de profondeurs. Pour cela, nous allons utiliser le fond de carte de la partie I.2.2.
# Nous rapatrions les coordonnées XY sur les résultats des splines
A <- argile_spline$harmonised
A <- left_join(A, poi, by=c("id"="id_profil"))
# supression des points sans coordonnées
A <-A %>% filter(!is.na(x_l93))
A <-A %>% filter(!is.na(y_l93))
# Nous convertissons les données XY en fichier spatial
A <- st_as_sf(A, coords = c("x_l93", "y_l93"), crs = st_crs(mayenne))
# Pour ne conserver que les points de la zone d'étude
A <- st_join(A, mayenne, left = FALSE)
plot7 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = A, aes(color = `0-5 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 1000), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "argile 0-5 cm", fill = "argile") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot8 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = A, aes(color = `5-15 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 1000), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "argile 5-15 cm", fill = "argile eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot9 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = A, aes(color = `15-30 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 1000), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "argile 15-30 cm", fill = "argile eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot10 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = A, aes(color = `30-60 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 1000), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "argile 30-60 cm", fill = "argile eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot11 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = A, aes(color = `60-100 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 1000), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "argile 60-100 cm", fill = "argile eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot12 <-ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = A, aes(color = `100-200 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 1000), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "argile 100-200 cm", fill = "argile eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot_grid(plot7, plot8, plot9, plot10, plot11, plot12)## Ignoring unknown labels:
## • fill : "argile"
## Ignoring unknown labels:
## • fill : "argile eau"
## Ignoring unknown labels:
## • fill : "argile eau"
## Ignoring unknown labels:
## • fill : "argile eau"
## Ignoring unknown labels:
## • fill : "argile eau"
## Ignoring unknown labels:
## • fill : "argile eau"

3.5.1.3 V.3. Carbone Anne
Pour notre troisième exemple, nous ferons la teneur en carbone (g/kg) méthode Anne.
# Préparation des données d'entrée des splines
c_anne <- data.frame("champ.id"=dm_sol$id_profil,
"champ.prof.sup"=dm_sol$prof_sup_moy,
"champ.prof.inf"=dm_sol$prof_inf_moy,
"champ.variable"=dm_sol$carbone_anne)
# Nous supprimons les lignes où notre variable d'intérêt est absente
c_anne <- c_anne %>% filter(!is.na(champ.variable))
# Nous complétons la profondeur de disparition quand celle-ci est absente (profondeur de disparition = profondeur d'apparition + 10 cm)
c_anne$champ.prof.inf<-ifelse(is.na(c_anne$champ.prof.inf),
c_anne$champ.prof.sup+10,
c_anne$champ.prof.inf)
# Nous mettons les profondeurs ("champ.prof.sup") par ordre croissant
c_anne <- arrange(c_anne, champ.id, champ.prof.sup)
# Nous supprimons les lignes dont les profondeurs sont absentes
c_anne <- c_anne %>% filter(!is.na(champ.prof.sup))
summary(c_anne) # vérification des données## champ.id champ.prof.sup champ.prof.inf champ.variable
## Min. : 382 Min. : -7.000 Min. : -2.00 Min. : 0.70
## 1st Qu.: 84565 1st Qu.: 0.000 1st Qu.: 25.00 1st Qu.: 12.80
## Median : 90868 Median : 0.000 Median : 30.00 Median : 17.00
## Mean : 93965 Mean : 3.173 Mean : 31.04 Mean : 18.71
## 3rd Qu.:127441 3rd Qu.: 0.000 3rd Qu.: 32.00 3rd Qu.: 22.10
## Max. :139441 Max. :130.000 Max. :200.00 Max. :248.00
# supression des profondeurs négatives
c_anne <- droplevels(c_anne[-which(c_anne$champ.prof.sup<0),]) Pour réaliser les splines, nous allons utiliser la fonction “ea_spline” du package ithir. La fonction ea_spline ajuste une spline continue, préservant la masse, depuis la surface du sol jusqu’à la profondeur maximale choisie. Elle interpole de manière fluide à la fois au sein des horizons observés et à travers les lacunes où aucune mesure n’existe.
#Chargement du package ithir
# install.packages("ithir", repos="http://R-Forge.R-project.org")
# Réalisation des splines
c_anne_spline <- ithir::ea_spline(c_anne, var.name="champ.variable",
d= t(c(0,5,15,30,60,100,200)), # Définit les intervalles de profondeur cibles pour les sorties harmonisées
lam = 0.1, # Paramètre de contrôle de la fluidité des splines
vlow=0, # la plus petite valeur de la variable cible (les valeurs plus petites seront remplacées)
show.progress=FALSE)
# Visualiser le résultat
str(c_anne_spline)## List of 4
## $ harmonised :'data.frame': 2788 obs. of 8 variables:
## ..$ id : num [1:2788] 382 388 397 413 426 433 438 446 454 458 ...
## ..$ 0-5 cm : num [1:2788] 18 23.8 45 15.8 21.8 13.8 16.9 18.5 14.7 28.8 ...
## ..$ 5-15 cm : num [1:2788] 18 23.8 45 15.8 21.8 13.8 16.9 18.5 14.7 28.8 ...
## ..$ 15-30 cm : num [1:2788] 18 23.8 -9999 15.8 21.8 ...
## ..$ 30-60 cm : num [1:2788] -9999 -9999 -9999 -9999 -9999 ...
## ..$ 60-100 cm : num [1:2788] -9999 -9999 -9999 -9999 -9999 ...
## ..$ 100-200 cm: num [1:2788] -9999 -9999 -9999 -9999 -9999 ...
## ..$ soil depth: num [1:2788] 30 30 12 30 30 30 35 25 35 25 ...
## $ obs.preds :'data.frame': 3138 obs. of 6 variables:
## ..$ champ.id : num [1:3138] 382 388 397 413 426 433 438 446 454 458 ...
## ..$ champ.prof.sup: num [1:3138] 0 0 0 0 0 0 0 0 0 0 ...
## ..$ champ.prof.inf: num [1:3138] 30 30 12 30 30 30 35 25 35 25 ...
## ..$ champ.variable: num [1:3138] 18 23.8 45 15.8 21.8 13.8 16.9 18.5 14.7 28.8 ...
## ..$ predicted : num [1:3138] 18 23.8 45 15.8 21.8 13.8 16.9 18.5 14.7 28.8 ...
## ..$ FID : num [1:3138] 1 2 3 4 5 6 7 8 9 10 ...
## $ splineFitError:'data.frame': 2788 obs. of 2 variables:
## ..$ rmse : num [1:2788] 0 0 0 0 0 0 0 0 0 0 ...
## ..$ rmseiqr: num [1:2788] 0 0 0 0 0 0 0 0 0 0 ...
## $ var.1cm : num [1:200, 1:2788] 18 18 18 18 18 18 18 18 18 18 ...
Les résultats contiennent 4 objets :
- c_anne_spline$harmonised : valeurs harmonisées par profil et profondeur
- c_anne_spline$obs.preds : valeurs observées vs prédites par horizon
- c_anne_spline$splineFitError : RMSE et nRMSE pour chaque profil
- c_anne_spline$var.1cm : courbes splines haute résolution
Nous pouvons faire un graphique avec les valeurs prédites et mesurées à partir de “obs.preds”.
# Réalisation du graphique prédit / mesuré
plot(c_anne_spline$obs.preds$predicted,
c_anne_spline$obs.preds$champ.variable,
xlab="carbone Anne prédit par les splines (g/kg)",
ylab = "Carbone Anne mesuré (g/kg)",
xlim=c(0, 400),
ylim=c(0, 400))
summary(c_anne_spline$harmonised)## id 0-5 cm 5-15 cm 15-30 cm
## Min. : 382 Min. : 0.00 Min. :-9999.000 Min. :-9999.00
## 1st Qu.: 84562 1st Qu.: 14.00 1st Qu.: 13.929 1st Qu.: 13.10
## Median : 90644 Median : 17.90 Median : 17.800 Median : 16.97
## Mean : 93927 Mean : 19.95 Mean : 5.269 Mean : -333.76
## 3rd Qu.:126131 3rd Qu.: 22.90 3rd Qu.: 22.800 3rd Qu.: 21.60
## Max. :139441 Max. :246.00 Max. : 246.000 Max. : 246.00
## NA's :3
## 30-60 cm 60-100 cm 100-200 cm soil depth
## Min. :-9999.000 Min. :-9999.0 Min. :-9999.00 Min. : 3.00
## 1st Qu.:-9999.000 1st Qu.:-9999.0 1st Qu.:-9999.00 1st Qu.: 25.00
## Median :-9999.000 Median :-9999.0 Median :-9999.00 Median : 30.00
## Mean :-7287.836 Mean :-9603.8 Mean :-9927.02 Mean : 31.07
## 3rd Qu.: 4.092 3rd Qu.:-9999.0 3rd Qu.:-9999.00 3rd Qu.: 32.00
## Max. : 191.323 Max. : 44.3 Max. : 19.32 Max. :200.00
## NA's :40 NA's :206 NA's :286
hist(c_anne_spline$harmonised$`0-5 cm`,
breaks= 100,
col="darkgoldenrod1",
main="Histogramme couche 0-5 cm",
xlab = "c_anne (g/kg)")
A partir de “splineFitError”, nous pouvons analyser les erreurs issues des splines.
L’unité de ces 2 métriques est la même que la variable (ici, g/kg).
boxplot(c_anne_spline$splineFitError$rmse,
c_anne_spline$splineFitError$rmseiqr,
names=c("RMSE","RMSEIQR"), # nom des boxplots
horizontal=T, # mettre le boxplot horizontal
xlab="g/kg", # label de l'axe X
col=c("dodgerblue2","darkorchid"), # couleurs des boxplots
ylim = c(0, 0.5), # fourchette de valeurs de l'axe Y (ici horizontal)
pch = 20) # format des points## Warning in bplt(at[i], wid = width[i], stats = z$stats[, i], out =
## z$out[z$group == : Une valeur extrême (Inf) dans la boite de dispersion 2 n'est
## pas représentée

Nous allons faire les cartogrammes des teneurs en c_anne par couche de profondeurs. Pour cela, nous allons utiliser le fond de carte de la partie I.2.2.
# Nous rapatrions les coordonnées XY sur les résultats des splines
C <- c_anne_spline$harmonised
C <- left_join(C, poi, by=c("id"="id_profil"))
# supression des points sans coordonnées
C <-C %>% filter(!is.na(x_l93))
C <-C %>% filter(!is.na(y_l93))
# Nous convertissons les données XY en fichier spatial
C <- st_as_sf(C, coords = c("x_l93", "y_l93"), crs = st_crs(mayenne))
# Pour ne conserver que les points de la zone d'étude
C <- st_join(C, mayenne, left = FALSE)
plot13 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = C, aes(color = `0-5 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 100), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "C Anne 0-5 cm", fill = "c_anne") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot14 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = C, aes(color = `5-15 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 100), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "C Anne 5-15 cm", fill = "c_anne eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot15 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = C, aes(color = `15-30 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 100), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "C Anne 15-30 cm", fill = "c_anne eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot16 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = C, aes(color = `30-60 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 100), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "C Anne 30-60 cm", fill = "c_anne eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot17 <- ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = C, aes(color = `60-100 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 100), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "C Anne 60-100 cm", fill = "c_anne eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot18 <-ggplot() +
geom_sf(data = mayenne) +
geom_sf(data = C, aes(color = `100-200 cm`))+
scale_color_viridis_c(option = "H", limits=c(0, 100), na.value = "transparent") +
theme(legend.position = "right") +
labs(title = "C Anne 100-200 cm", fill = "c_anne eau") +
labs(color="g/kg") +
theme(legend.title = element_text(color="black", size=10, face = "bold"))+
theme_void()+
annotation_scale(location = "bl", line_width = 0.5, style="ticks", width_hint = 0.1, text_face=1,
pad_x = unit(0, "in"), pad_y = unit(0, "in"),) + # ajout d'une échelle
annotation_north_arrow(location = "br", which_north = "true", # ajout d'une flèche Nord
pad_x = unit(-0.09, "in"), pad_y = unit(0.1, "in"),
style = ggspatial::north_arrow_nautical(
fill = c("grey40", "white"),
line_col = "grey20",
text_family = "ArcherPro Book"))
plot_grid(plot13, plot14, plot15, plot16, plot17, plot18)## Ignoring unknown labels:
## • fill : "c_anne"
## Ignoring unknown labels:
## • fill : "c_anne eau"
## Ignoring unknown labels:
## • fill : "c_anne eau"
## Ignoring unknown labels:
## • fill : "c_anne eau"
## Ignoring unknown labels:
## • fill : "c_anne eau"
## Ignoring unknown labels:
## • fill : "c_anne eau"

