Documentation
  1. Data integration [FR]
  2. Consolidation de la nomenclature des analytes
  • Introduction
    • Purpose of this documentation
    • Contribute to this documentation
  • Database
    • Database structure
    • Lab results importation example
    • Database query
  • Data integration [FR]
    • Recensement des projets
    • Recensement des sites
    • Consolidation de la nomenclature des analytes
    • Description des contaminants
    • Déterminer la taille des échantillons par site
    • Injections des données

On this page

  • Analytes avec un numéro CAS fournis par le laboratoire
  • Obtention du numéro de CAS par le service CTS
  • Obtention du numéro de CAS par le service ChemSpider (csid)
  • Ajout de l’identifiant PubChem (pubcid)
  • Consolidation de la table de reference des analytes
    • Ajout des unités à la table de référence
  1. Data integration [FR]
  2. Consolidation de la nomenclature des analytes

Consolidation de la nomenclature des analytes

Author

Steve Vissault

Published

March 28, 2024

Nous désirons consolider la nomenclature des analytes. On peut observer plusieurs divergences dans la facon de nommer les analytes, exemple PCB-17 ou PCB 17. Cette section documente la démarche pour tendre vers une réconciliation des termes utilisés.

On charge les données intégrées depuis le package toxbox. Les données ne sont pas entreposées dans le package, la function itgr_measurements() permet de lire les fichiers Excels entreposées sur le disque réseau Z: et les fusionne dans un seul data.frame. Il faut pour cela que le VPN soit ouvert, si vous n’êtes pas directement sur le réseau d’environnement canada.

source("src/itgr_measurements.R")
measurements <- itgr_measurements()

Analytes avec un numéro CAS fournis par le laboratoire

La nomenclature des composées chimiques / analytes peut diverger d’un laboratoire à un autre. Chaque pays dispose également de sa propre nomenclature (ex. États-Unis, Allemagne, Royaume-Unis). Afin de reconcilier la nomenclature, nous souhaitons utiliser un identifiant unique international tel que l’International Chemical Identifier (InChl).

Dans un premier temps, on charge les données sur les analytes (incluant le CAS) fournies par les laboratoires. Le fichier analytes_consolidation_27032024 est la fusion des onglets Analyte Information des différentes base de données. On fusionne les données sur les analytes avec les mesures.

analytes <- readxl::read_excel("Z:/07-Données BD/intermediate_files/analytes_consolidation_27032024.xlsx") |>
    dplyr::select(Analyte, CASNumber) |>
    dplyr::filter(!is.na(CASNumber)) |>
    dplyr::mutate(
        key = janitor::make_clean_names(Analyte, allow_dupes = TRUE, case = "none") |> tolower()
    ) |>
    dplyr::distinct()

measurements <- measurements |> 
    dplyr::mutate(
        key = stringr::str_replace(variable, "PCB-|PCB ", "PCB") |>
            janitor::make_clean_names(allow_dupes = TRUE, case = "none") |> tolower()
    )

measurements <-  measurements |>
    dplyr::left_join(analytes, by = "key")

On isole le nom des contaminants pour lesquels nous n’avons pas de CAS associés. Voici la liste de ces contaminants:

contaminants_orphelins <- measurements |> 
    dplyr::filter(is.na(CASNumber)) |>
    dplyr::select(key, variable, CASNumber) |>
    dplyr::distinct()

reactable::reactable(contaminants_orphelins, searchable = TRUE)

Obtention du numéro de CAS par le service CTS

La fonction webchem::cts_convert() permet d’interroger plusieurs base de données de référence sur des composées chimiques et d’extraire des identifiants tels que le CAS à partir du nom d’un composé chimique. La fonction webchem::cts_convert() s’interface sur le service https://cts.fiehnlab.ucdavis.edu/.

cas_from_cts <- webchem::cts_convert(contaminants_orphelins$variable, from = "Chemical Name", to = "CAS", match = "first")

cas <- purrr::map2_df(names(cas_from_cts), cas_from_cts, \(n, c){
    return(data.frame(variable = n, CASNumber = c))
})

reactable::reactable(cas, searchable = TRUE)

En cherchant le numéro CAS avec le nom du composé, on s’apercoit que plusieurs PCB (ex. PCB 60 et 64) tombent sous le même CAS, ce qui est incorrect. On change donc d’approche puisque le service CTS ne semblent pas être un bon service de résolution par le nom.

Obtention du numéro de CAS par le service ChemSpider (csid)

Ce service est fournis par la Société Royale de Chimie. C’est un des services qui semble être robuste pour obtenir un identifiant unique à partir du nom du composé. Une fois, l’identifiant ChemSpider obtenue il est possible d’obtenir le numéro CAS en utilisant le service https://cts.fiehnlab.ucdavis.edu/. On utilise ce service pour effectuer la conversion d’identifiants entre nomenclature (ex. csid vers CAS). Attention, ce service requière l’enregistrement d’un compte et le nombre de requête est limités à 1000 appels par mois.

csid <- webchem::get_csid(contaminants_orphelins$variable, from = "name") 
csid <- csid |> dplyr::rename(variable = query) |> 
    dplyr::left_join(
        dplyr::select(contaminants_orphelins, key, variable)
    ) |> 
    dplyr::filter(!is.na(csid)) |>
    dplyr::mutate(csid = as.character(csid))
saveRDS(csid, "data/csid.rds")
if(!exists("csid")) csid <- readRDS("data/csid.rds")

cas_from_csid <- webchem::cts_convert(csid$csid, from = "ChemSpider", to = "CAS", match = "first")

cas2 <- purrr::map2_df(names(cas_from_csid), cas_from_csid, \(n, c){
        return(data.frame(csid = n, CASNumber = c))
    }) |> dplyr::left_join(csid, by = "csid")

reactable::reactable(cas2, searchable = TRUE)

Effectuer la résolution de la nomenclature des composées par l’utilisation du service ChemSpider semble plus prometteur. Après une vérification visuelle, on voit que les composées ne présentent plus d’identifiant unique identique.

Ajout de l’identifiant PubChem (pubcid)

Afin de permettre le retrait d’informations supplémentaires sur les composées chimiques, une des base de données d’intérêt est PubChem. À partir de cette base de données, on peut effectuer le retrait de certaines propriétés chimiques du composée telles que la masse moléculaire par exemple.

pubcid <- webchem::cts_convert(cas2$csid, from = "ChemSpider", to = "PubChem CID", match = "first")

cas3 <- purrr::map2_df(names(pubcid), pubcid, \(n, c){
        return(data.frame(csid = n, pubcid = c))
    }) |> dplyr::distinct() |> 
        dplyr::left_join(cas2, by = "csid") |> 
        dplyr::distinct() |>
        # On consolide le tableau final
        dplyr::rename(casid = CASNumber) |>
        dplyr::mutate(variable = tolower(variable)) |>
        dplyr::distinct()

Consolidation de la table de reference des analytes

On a ajouté 3 identifiants pour chaque composée chimique de la base de données. Parmis, ces identifiants nous retrouvons: le CAS (casid, identifiant fournis généralement par le laboratoire d’analyse, nomenclature américaine), le ChemSpider ID (csid, nomenclature britanique) et enfin le PubChem ID (pubcid, nomenclature américaine).

On a enfin toutes les informations pour construire la table de référence des composées chimiques. On récupère les composées chimiques pour lesquelles le CAS est renseigné par le laboratoire.

contaminants_with_cas <- measurements |> 
    dplyr::filter(!is.na(CASNumber)) |>
    dplyr::select(key, variable, CASNumber) |>
    dplyr::rename(casid = CASNumber) |>
    dplyr::mutate(variable = tolower(variable)) |>
    dplyr::distinct()

On va chercher le PubChem ID et et le spiderChem ID pour les contaminants qui avaient déjà un CAS (fournit par le laboratoire).

csid <- webchem::cts_convert(contaminants_with_cas$casid, from = "CAS", to = "ChemSpider", match = "first")
csid <- purrr::map2_df(names(csid), csid, \(n, c){
        return(data.frame(casid = n, csid = c))
    })

pubcid <- webchem::cts_convert(contaminants_with_cas$casid, from = "CAS", to = "PubChem CID", match = "first")
pubcid <- purrr::map2_df(names(pubcid), pubcid, \(n, c){
        return(data.frame(casid = n, pubcid = c))
    })

contaminants <- contaminants_with_cas |> 
    dplyr::left_join(csid, by = "casid") |>
    dplyr::left_join(pubcid, by = "casid") |>
    dplyr::distinct() |>
    dplyr::bind_rows(cas3) |>
    dplyr::mutate(
        url_casid = ifelse(!is.na(casid), paste0("https://commonchemistry.cas.org/detail?cas_rn=", casid), NA),
        url_pubcid = ifelse(!is.na(pubcid), paste0("https://pubchem.ncbi.nlm.nih.gov/compound/", pubcid), NA),
        url_csid = ifelse(!is.na(csid), paste0("https://www.chemspider.com/Chemical-Structure.", csid, ".html"), NA)
    ) |>
    dplyr::select(-variable) |>
    dplyr::distinct()

Validation: Est-ce que tous les contaminants dans la table de référence sont présent dans la table des mesures?

keys <- measurements$key |>
    unique()

contaminants_without_ids <- data.frame(key = keys[!keys %in% contaminants$key], pubcid = NA, csid = NA, casid = NA)

contaminants <- dplyr::bind_rows(contaminants, contaminants_without_ids) |>
    dplyr::distinct()

sources <- dplyr::select(measurements, key, source, conpound_family) |> 
    dplyr::distinct()

On écrit le fichier pour pouvoir repasser manuellement dessus.

writexl::write_xlsx(list(contaminants, sources), path = "data/tbl_contaminants_ref.xlsx")

Ajout des unités à la table de référence

On utilise les données contenues dans le fichier Z:/07-Données BD/intermediate_files/analytes_consolidation_27032024.xlsx et qui contient les unités pour un certains nombres de composées. Ce tableau est une fusion des informations documentés dans l’onglet “Analytes informations” des bases de données COEI, HERG et GBHE.

tbl_analytes <- readxl::read_excel("data/tbl_contaminants_ref_manual.xlsx") |>
    tidyr::separate_rows(original_ids, sep = ";")

analytes_units <- readxl::read_excel("Z:/07-Données BD/intermediate_files/analytes_consolidation_27032024.xlsx") |>
    dplyr::mutate(
        key = janitor::make_clean_names(Analyte, allow_dupes = TRUE, case = "none") |> tolower()
    ) |>
    dplyr::select(key, Units) |>
    dplyr::distinct()

tbl_analytes <- tbl_analytes |> dplyr::left_join(analytes_info, by = c("original_ids" = "key")) |> 
    dplyr::group_by_at(dplyr::vars(-original_ids, -Units)) |>
    dplyr::summarize(original_ids = paste(original_ids, collapse = ";"), Units = paste(Units, collapse = ";"))

writexl::write_xlsx(tbl_analytes, path = "data/tbl_contaminants_ref.xlsx")
Source Code
---
title: "Consolidation de la nomenclature des analytes"
author: "Steve Vissault"
date: "2024-03-28"
execute:
    eval: FALSE
    cache: true
---

Nous désirons consolider la nomenclature des analytes. On peut observer plusieurs divergences dans la facon de nommer les analytes, exemple PCB-17 ou PCB 17. Cette section documente la démarche pour tendre vers une réconciliation des termes utilisés.

On charge les données intégrées depuis le package `toxbox`. Les données ne sont pas entreposées dans le package, la function `itgr_measurements()` permet de lire les fichiers Excels entreposées sur le disque réseau `Z:` et les fusionne dans un seul `data.frame`. Il faut pour cela que le VPN soit ouvert, si vous n'êtes pas directement sur le réseau d'environnement canada.

```{r}
source("src/itgr_measurements.R")
```

```{r}
measurements <- itgr_measurements()
```

## Analytes avec un numéro CAS fournis par le laboratoire

La nomenclature des composées chimiques / analytes peut diverger d'un laboratoire à un autre. Chaque pays dispose également de sa propre nomenclature (ex. États-Unis, Allemagne, Royaume-Unis). Afin de reconcilier la nomenclature, nous souhaitons utiliser un identifiant unique international tel que l'[International Chemical Identifier](https://fr.wikipedia.org/wiki/International_Chemical_Identifier) (InChl). 

Dans un premier temps, on charge les données sur les analytes (incluant le CAS) fournies par les laboratoires. Le fichier `analytes_consolidation_27032024` est la fusion des onglets _Analyte Information_ des différentes base de données. 
On fusionne les données sur les analytes avec les mesures.

```{r}
analytes <- readxl::read_excel("Z:/07-Données BD/intermediate_files/analytes_consolidation_27032024.xlsx") |>
    dplyr::select(Analyte, CASNumber) |>
    dplyr::filter(!is.na(CASNumber)) |>
    dplyr::mutate(
        key = janitor::make_clean_names(Analyte, allow_dupes = TRUE, case = "none") |> tolower()
    ) |>
    dplyr::distinct()

measurements <- measurements |> 
    dplyr::mutate(
        key = stringr::str_replace(variable, "PCB-|PCB ", "PCB") |>
            janitor::make_clean_names(allow_dupes = TRUE, case = "none") |> tolower()
    )

measurements <-  measurements |>
    dplyr::left_join(analytes, by = "key")
```

On isole le nom des contaminants pour lesquels nous n'avons pas de CAS associés. Voici la liste de ces contaminants:

```{R, results="asis"}
contaminants_orphelins <- measurements |> 
    dplyr::filter(is.na(CASNumber)) |>
    dplyr::select(key, variable, CASNumber) |>
    dplyr::distinct()

reactable::reactable(contaminants_orphelins, searchable = TRUE)
```

## Obtention du numéro de CAS par le service CTS

La fonction `webchem::cts_convert()` permet d'interroger plusieurs base de données de référence sur des composées chimiques et d'extraire des identifiants tels que le CAS à partir du nom d'un composé chimique. La fonction `webchem::cts_convert()` s'interface sur le service https://cts.fiehnlab.ucdavis.edu/.

```{r}
cas_from_cts <- webchem::cts_convert(contaminants_orphelins$variable, from = "Chemical Name", to = "CAS", match = "first")

cas <- purrr::map2_df(names(cas_from_cts), cas_from_cts, \(n, c){
    return(data.frame(variable = n, CASNumber = c))
})

reactable::reactable(cas, searchable = TRUE)
```

En cherchant le numéro CAS avec le nom du composé, on s'apercoit que plusieurs PCB (ex. PCB 60 et 64) tombent sous le même CAS, ce qui est incorrect. On change donc d'approche puisque le service CTS ne semblent pas être un bon service de résolution par le nom.

## Obtention du numéro de CAS par le service ChemSpider (csid)

Ce service est fournis par la Société Royale de Chimie. C'est un des services qui semble être robuste pour obtenir un identifiant unique à partir du nom du composé. Une fois, l'identifiant ChemSpider obtenue il est possible d'obtenir le numéro CAS en utilisant le service https://cts.fiehnlab.ucdavis.edu/. On utilise ce service pour effectuer la conversion d'identifiants entre nomenclature (ex. csid vers CAS). Attention, ce service requière l'enregistrement d'un compte et le nombre de requête est limités à 1000 appels par mois.

```{R, eval = FALSE}
csid <- webchem::get_csid(contaminants_orphelins$variable, from = "name") 
csid <- csid |> dplyr::rename(variable = query) |> 
    dplyr::left_join(
        dplyr::select(contaminants_orphelins, key, variable)
    ) |> 
    dplyr::filter(!is.na(csid)) |>
    dplyr::mutate(csid = as.character(csid))
saveRDS(csid, "data/csid.rds")
```


```{r}
if(!exists("csid")) csid <- readRDS("data/csid.rds")

cas_from_csid <- webchem::cts_convert(csid$csid, from = "ChemSpider", to = "CAS", match = "first")

cas2 <- purrr::map2_df(names(cas_from_csid), cas_from_csid, \(n, c){
        return(data.frame(csid = n, CASNumber = c))
    }) |> dplyr::left_join(csid, by = "csid")

reactable::reactable(cas2, searchable = TRUE)
```

Effectuer la résolution de la nomenclature des composées par l'utilisation du service ChemSpider semble plus prometteur. Après une vérification visuelle, on voit que les composées ne présentent plus d'identifiant unique identique.

## Ajout de l'identifiant PubChem (pubcid)

Afin de permettre le retrait d'informations supplémentaires sur les composées chimiques, une des base de données d'intérêt est PubChem. À partir de cette base de données, on peut effectuer le retrait de certaines propriétés chimiques du composée telles que la masse moléculaire par exemple.

```{r}
pubcid <- webchem::cts_convert(cas2$csid, from = "ChemSpider", to = "PubChem CID", match = "first")

cas3 <- purrr::map2_df(names(pubcid), pubcid, \(n, c){
        return(data.frame(csid = n, pubcid = c))
    }) |> dplyr::distinct() |> 
        dplyr::left_join(cas2, by = "csid") |> 
        dplyr::distinct() |>
        # On consolide le tableau final
        dplyr::rename(casid = CASNumber) |>
        dplyr::mutate(variable = tolower(variable)) |>
        dplyr::distinct()
```

## Consolidation de la table de reference des analytes

On a ajouté 3 identifiants pour chaque composée chimique de la base de données. Parmis, ces identifiants nous retrouvons: le [CAS](https://commonchemistry.cas.org/) (casid, identifiant fournis généralement par le laboratoire d'analyse, nomenclature américaine), le [ChemSpider ID](https://www.chemspider.com/) (csid, nomenclature britanique) et enfin le [PubChem ID](https://pubchem.ncbi.nlm.nih.gov/) (pubcid, nomenclature américaine).

On a enfin toutes les informations pour construire la table de référence des composées chimiques. On récupère les composées chimiques pour lesquelles le CAS est renseigné par le laboratoire.

```{r}
contaminants_with_cas <- measurements |> 
    dplyr::filter(!is.na(CASNumber)) |>
    dplyr::select(key, variable, CASNumber) |>
    dplyr::rename(casid = CASNumber) |>
    dplyr::mutate(variable = tolower(variable)) |>
    dplyr::distinct()
```

On va chercher le PubChem ID et et le spiderChem ID pour les contaminants qui avaient déjà un CAS (fournit par le laboratoire).

```{r}
csid <- webchem::cts_convert(contaminants_with_cas$casid, from = "CAS", to = "ChemSpider", match = "first")
csid <- purrr::map2_df(names(csid), csid, \(n, c){
        return(data.frame(casid = n, csid = c))
    })

pubcid <- webchem::cts_convert(contaminants_with_cas$casid, from = "CAS", to = "PubChem CID", match = "first")
pubcid <- purrr::map2_df(names(pubcid), pubcid, \(n, c){
        return(data.frame(casid = n, pubcid = c))
    })

contaminants <- contaminants_with_cas |> 
    dplyr::left_join(csid, by = "casid") |>
    dplyr::left_join(pubcid, by = "casid") |>
    dplyr::distinct() |>
    dplyr::bind_rows(cas3) |>
    dplyr::mutate(
        url_casid = ifelse(!is.na(casid), paste0("https://commonchemistry.cas.org/detail?cas_rn=", casid), NA),
        url_pubcid = ifelse(!is.na(pubcid), paste0("https://pubchem.ncbi.nlm.nih.gov/compound/", pubcid), NA),
        url_csid = ifelse(!is.na(csid), paste0("https://www.chemspider.com/Chemical-Structure.", csid, ".html"), NA)
    ) |>
    dplyr::select(-variable) |>
    dplyr::distinct()
```

Validation: Est-ce que tous les contaminants dans la table de référence sont présent dans la table des mesures?

```{r}
keys <- measurements$key |>
    unique()

contaminants_without_ids <- data.frame(key = keys[!keys %in% contaminants$key], pubcid = NA, csid = NA, casid = NA)

contaminants <- dplyr::bind_rows(contaminants, contaminants_without_ids) |>
    dplyr::distinct()

sources <- dplyr::select(measurements, key, source, conpound_family) |> 
    dplyr::distinct()
```

On écrit le fichier pour pouvoir repasser manuellement dessus.

```{r}
writexl::write_xlsx(list(contaminants, sources), path = "data/tbl_contaminants_ref.xlsx")
```

### Ajout des unités à la table de référence

On utilise les données contenues dans le fichier `Z:/07-Données BD/intermediate_files/analytes_consolidation_27032024.xlsx` et qui contient les unités pour un certains nombres de composées. Ce tableau est une fusion des informations documentés dans l'onglet "Analytes informations" des bases de données COEI, HERG et GBHE.

```{r}
tbl_analytes <- readxl::read_excel("data/tbl_contaminants_ref_manual.xlsx") |>
    tidyr::separate_rows(original_ids, sep = ";")

analytes_units <- readxl::read_excel("Z:/07-Données BD/intermediate_files/analytes_consolidation_27032024.xlsx") |>
    dplyr::mutate(
        key = janitor::make_clean_names(Analyte, allow_dupes = TRUE, case = "none") |> tolower()
    ) |>
    dplyr::select(key, Units) |>
    dplyr::distinct()

tbl_analytes <- tbl_analytes |> dplyr::left_join(analytes_info, by = c("original_ids" = "key")) |> 
    dplyr::group_by_at(dplyr::vars(-original_ids, -Units)) |>
    dplyr::summarize(original_ids = paste(original_ids, collapse = ";"), Units = paste(Units, collapse = ";"))

writexl::write_xlsx(tbl_analytes, path = "data/tbl_contaminants_ref.xlsx")
```