diff --git a/.Rbuildignore b/.Rbuildignore index 3a03a57..0591f37 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -10,3 +10,4 @@ _pkgdown.yml .BBSoptions ^\.vscode devnotes.md +temp_doc/* diff --git a/.github/workflows/check-bioc.yml b/.github/workflows/check-bioc.yml index a660cdb..702da21 100644 --- a/.github/workflows/check-bioc.yml +++ b/.github/workflows/check-bioc.yml @@ -52,9 +52,9 @@ jobs: fail-fast: false matrix: config: - - { os: ubuntu-latest, r: '4.4', bioc: '3.20', cont: "bioconductor/bioconductor_docker:RELEASE_3_20", rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest" } - - { os: macOS-latest, r: '4.4', bioc: '3.20'} - - { os: windows-latest, r: '4.4', bioc: '3.20'} + - { os: ubuntu-latest, r: 'devel', bioc: '3.21', cont: "bioconductor/bioconductor_docker:devel", rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest" } + - { os: macOS-latest, r: 'devel', bioc: '3.21'} + - { os: windows-latest, r: 'devel', bioc: '3.21'} env: R_REMOTES_NO_ERRORS_FROM_WARNINGS: true RSPM: ${{ matrix.config.rspm }} @@ -77,7 +77,7 @@ jobs: ## https://github.com/r-lib/actions/blob/master/examples/check-standard.yaml ## If they update their steps, we will also need to update ours. - name: Checkout Repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 ## R is already included in the Bioconductor docker images - name: Setup R from r-lib @@ -86,17 +86,35 @@ jobs: with: r-version: ${{ matrix.config.r }} + ## Install R dependencies using R-lib + - name: Setup R dependencies from r-lib + if: runner.os != 'Linux' + uses: r-lib/actions/setup-r-dependencies@v2 + ## pandoc is already included in the Bioconductor docker images - name: Setup pandoc from r-lib if: runner.os != 'Linux' uses: r-lib/actions/setup-pandoc@v2 + - name: Install quarto + if: runner.os != 'Linux' + uses: quarto-dev/quarto-actions/setup@v2 + - name: Query dependencies run: | install.packages('remotes') saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) shell: Rscript {0} + - name: Install Miniconda + run: | + Rscript -e "install.packages('reticulate')" + Rscript -e "reticulate::install_miniconda()" + + - name: Fix conda for macOS + if: runner.os == 'macOS' + run: echo "options(reticulate.conda_binary = reticulate:::miniconda_conda())" >> .Rprofile + - name: Cache R packages if: "!contains(github.event.head_commit.message, '/nocache') && runner.os != 'Linux'" uses: actions/cache@v2 @@ -137,9 +155,6 @@ jobs: ## For installing usethis's dependency gert brew install libgit2 - ## Python - brew install python@3.10 - - name: Install Windows system dependencies if: runner.os == 'Windows' run: | @@ -179,8 +194,9 @@ jobs: remotes::install_local(dependencies = TRUE, repos = BiocManager::repositories(), build_vignettes = FALSE, upgrade = TRUE) message(paste('****', Sys.time(), 'force installation of selected packages ****')) - BiocManager::install(c("BiocStyle", "rmarkdown", "magick")) + BiocManager::install(c("BiocStyle")) + BiocManager::install("mzR") BiocManager::install("Spectra") ## For running the checks message(paste('****', Sys.time(), 'installing rcmdcheck and BiocCheck ****')) @@ -219,7 +235,7 @@ jobs: _R_CHECK_CRAN_INCOMING_: false run: | rcmdcheck::rcmdcheck( - args = c("--no-build-vignettes", "--no-manual", "--timings"), + args = c("--no-manual", "--timings"), build_args = c("--no-manual", "--no-resave-data"), error_on = "warning", check_dir = "check" @@ -276,4 +292,4 @@ jobs: uses: actions/upload-artifact@master with: name: ${{ runner.os }}-biocversion-devel-r-devel-results - path: check \ No newline at end of file + path: check diff --git a/DESCRIPTION b/DESCRIPTION index bef713c..6156d2b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,10 +1,10 @@ Package: SpectriPy Title: Integrating Spectra with Python's matchms -Version: 0.2.1 +Version: 0.3.1 Description: The SpectriPy package allows integration of Python-based MS analysis code with the Spectra package. Spectra objects can be converted into Python's matchms Spectrum objects. In addition, SpectriPy integrates - and wraps the similarity scoring and processing/filtering functions + and wraps the similarity scoring and processing/filtering functions from the matchms package into R. Authors@R: c(person(given = "Michael", family = "Witting", role = c("aut"), @@ -20,47 +20,54 @@ Authors@R: c(person(given = "Michael", family = "Witting", comment = c(ORCID = "0000-0002-9355-8948")), person(given = "Helge", family = "Hecht", email = "helge.hecht@recetox.muni.cz", - role = c("aut"), + role = c("ctb"), comment = c(ORCID = "0000-0001-6744-996X")), person(given = "Marilyn", family = "De Graeve", email = "marilyn.degraeve@eurac.edu", - role = c("ctb"), + role = c("aut"), comment = c(ORCID = "0000-0001-6916-401X")), - person(given = "Thomas", family = "Naake", + person(given = "Wout", family = "Bittremieux", + email = "wout.bittremieux@uantwerpen.be", + role = c("aut"), + comment = c(ORCID = "0000-0002-3105-1359")), + person(given = "Thomas", family = "Naake", email = "thomasnaake@googlemail.com", - role = c("ctb"), + role = c("aut"), comment = c(ORCID = "0000-0001-7917-5580")), person(given = "Victor", family = "Chrone", email = "vchrone@bmb.sdu.dk", role = c("ctb"), comment = c(ORCID = "0009-0007-2121-4066")), - person(given = "Matthias", family = "Anagho-Mattanovich", + person(given = "Matthias", family = "Anagho-Mattanovich", email = "matthias.mattanovich@sund.ku.dk", role = c("ctb"), - comment = c(ORCID = "0000-0001-7561-7898"))) + comment = c(ORCID = "0000-0001-7561-7898")) + ) Depends: - R (>= 4.1.0) + R (>= 4.1.0), + reticulate Imports: Spectra (>= 1.5.8), - basilisk, - reticulate, IRanges, S4Vectors, - BiocParallel, - ProtGenerics, + MsCoreUtils, methods Suggests: testthat, + quarto, + MsBackendMgf, + msdata, + mzR, knitr, - BiocStyle, - rmarkdown + BiocStyle License: Artistic-2.0 -VignetteBuilder: knitr BugReports: https://github.com/RforMassSpectrometry/SpectriPy/issues URL: https://github.com/RforMassSpectrometry/SpectriPy biocViews: Infrastructure, Metabolomics, MassSpectrometry Encoding: UTF-8 -StagedInstall: no -SystemRequirements: python (>= 3.9) +SystemRequirements: + python (>= 3.10), + pandoc +VignetteBuilder: quarto Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 diff --git a/NAMESPACE b/NAMESPACE index f970629..85b44b7 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,10 +1,12 @@ # Generated by roxygen2: do not edit by hand -export(CosineGreedyParam) -export(CosineHungarianParam) -export(ModifiedCosineParam) -export(NeutralLossesCosineParam) +S3method(r_to_py,Spectra) +export(CosineGreedy) +export(CosineHungarian) +export(ModifiedCosine) +export(NeutralLossesCosine) export(compareSpectriPy) +export(defaultSpectraVariableMapping) export(filterSpectriPy) export(normalize_intensities) export(pyspec_to_rspec) @@ -12,27 +14,35 @@ export(remove_peaks_around_precursor_mz) export(rspec_to_pyspec) export(select_by_intensity) export(select_by_mz) +export(setSpectraVariableMapping) exportMethods(compareSpectriPy) exportMethods(filterSpectriPy) exportMethods(spectraVariableMapping) -importClassesFrom(ProtGenerics,Param) -importFrom(BiocParallel,SerialParam) -importFrom(BiocParallel,bplapply) importFrom(IRanges,NumericList) +importFrom(MsCoreUtils,rbindFill) importFrom(S4Vectors,DataFrame) -importFrom(Spectra,concatenateSpectra) -importFrom(basilisk,BasiliskEnvironment) -importFrom(basilisk,basiliskRun) -importFrom(basilisk,basiliskStart) -importFrom(basilisk,basiliskStop) +importFrom(Spectra,MsBackendMemory) +importFrom(Spectra,spectrapply) importFrom(methods,is) importFrom(methods,new) +importFrom(reticulate,conda_create) +importFrom(reticulate,conda_list) importFrom(reticulate,import) +importFrom(reticulate,iterate) importFrom(reticulate,np_array) importFrom(reticulate,py) +importFrom(reticulate,py_available) +importFrom(reticulate,py_dict) +importFrom(reticulate,py_install) +importFrom(reticulate,py_module_available) importFrom(reticulate,py_run_string) importFrom(reticulate,py_to_r) importFrom(reticulate,r_to_py) +importFrom(reticulate,use_condaenv) +importFrom(reticulate,use_virtualenv) +importFrom(reticulate,virtualenv_create) +importFrom(reticulate,virtualenv_exists) +importFrom(reticulate,virtualenv_remove) importMethodsFrom(Spectra,Spectra) importMethodsFrom(Spectra,intensity) importMethodsFrom(Spectra,mz) diff --git a/NEWS.md b/NEWS.md index 73b3c2a..4c79768 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,18 @@ +# SpectriPy 0.3 + +## Changes in 0.3.1 + +- Add `filterSpectriPy()` function for spectra filtering using *matchms*. +- Add a new example MGF file to the package. +- Add a new quarto vignette. + +## Changes in 0.3.0 + +- Add changes and results introduced a the EuBIC 2024 r-python hackathon in + Neustift, Italy. +- Remove dependency from *basilisk* and base the package entirely on + *reticulate*. + # SpectriPy 0.2 ## Changes in 0.2.1 @@ -29,4 +44,4 @@ - Add `basilisk` environment. - Add `spectraVariableMapping`. - Refactor functions to convert between R and python spectrum objects and add - unit tests. \ No newline at end of file + unit tests. diff --git a/R/basilisk.R b/R/basilisk.R deleted file mode 100644 index 3eda427..0000000 --- a/R/basilisk.R +++ /dev/null @@ -1,6 +0,0 @@ -#' @importFrom basilisk BasiliskEnvironment -matchms_env <- basilisk::BasiliskEnvironment( - envname = "matchms_env", pkgname = "SpectriPy", - packages = c("matchms==0.28.2"), - channels = c("bioconda", "conda-forge") - ) diff --git a/R/compareSpectriPy.R b/R/compareSpectriPy.R index 27932bc..92e2b0f 100644 --- a/R/compareSpectriPy.R +++ b/R/compareSpectriPy.R @@ -1,81 +1,94 @@ -#' @title Spectra similarity calculations using matchms +#' @title Spectra similarity calculations using Python's matchms library #' #' @name compareSpectriPy #' #' @description #' #' The `compareSpectriPy()` function allows to calculate spectral similarity -#' scores using the `calculate_scores module` of the python -#' [matchms.similarity package](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html) -#' package. +#' scores using the `calculate_scores()` function of the Python +#' [matchms.similarity](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html). +#' module. #' #' Selection and configuration of the algorithm can be performed with one of the -#' parameter objects: -#' -#' - `CosineGreedyParam`: calculate the *cosine similarity score* between -#' spectra. The score is calculated by finding best possible matches between -#' peaks of two spectra. Two peaks are considered a potential match if their -#' m/z ratios lie within the given `tolerance`. The underlying peak assignment -#' problem is here solved in a *greedy* way. This can perform notably faster, -#' but does occasionally deviate slightly from a fully correct solution (as -#' with the `CosineHungarianParam` algorithm). In practice this will rarely -#' affect similarity scores notably, in particular for smaller tolerances. The -#' algorithm can be configured with parameters `tolerance`, `mzPower` and -#' `intensityPower` (see parameter description for more details). -#' -#' - `CosineHungarianParam`: calculate the *cosine similarity score* as with -#' `CosineGreedyParam`, but using the Hungarian algorithm to find the best +#' *parameter* objects/functions: +#' +#' - `CosineGreedy()`: calculate the *cosine similarity score* between +#' spectra. The score is calculated by finding the best possible matches +#' between peaks of two spectra. Two peaks are considered a potential match if +#' their m/z ratios lie within the given `tolerance`. The underlying peak +#' assignment problem is here solved in a *greedy* way. This can perform +#' notably faster, but does occasionally deviate slightly from a fully correct +#' solution (as with the `CosineHungarian` algorithm). In practice this +#' will rarely affect similarity scores notably, in particular for smaller +#' tolerances. The algorithm can be configured with parameters `tolerance`, +#' `mz_power` and `intensity_power` (see parameter description for more +#' details). See also +#' [matchms CosineGreedy](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.CosineGreedy.html) for more information. +#' +#' - `CosineHungarian()`: calculate the *cosine similarity score* as with +#' `CosineGreedy`, but using the Hungarian algorithm to find the best #' matching peaks between the compared spectra. The algorithm can be -#' configured with parameters `tolerance`, `mzPower` and `intensityPower` -#' (see parameter description for more details). +#' configured with parameters `tolerance`, `mz_power` and `intensity_power` +#' (see parameter description for more details). See also +#' [matchms CosingHungarian](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.CosineHungarian.html) for more information. #' -#' - `ModifiedCosineParam`: The modified cosine score aims at quantifying the +#' - `ModifiedCosine()`: The modified cosine score aims at quantifying the #' similarity between two mass spectra. The score is calculated by finding -#' best possible matches between peaks of two spectra. Two peaks are +#' the best possible matches between peaks of two spectra. Two peaks are #' considered a potential match if their m/z ratios lie within the given #' `tolerance`, or if their m/z ratios lie within the tolerance once a -#' mass-shift is applied. The mass shift is simply the difference in +#' mass shift is applied. The mass shift is simply the difference in #' precursor-m/z between the two spectra. +#' See also [matchms ModifiedCosine](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.ModifiedCosine.html) for more information. #' -#' - `NeutralLossesCosineParam`: The neutral losses cosine score aims at +#' - `NeutralLossesCosine()`: The neutral losses cosine score aims at #' quantifying the similarity between two mass spectra. The score is -#' calculated by finding best possible matches between peaks of two spectra. -#' Two peaks are considered a potential match if their m/z ratios lie within -#' the given `tolerance` once a mass-shift is applied. The mass shift is the -#' difference in precursor-m/z between the two spectra. +#' calculated by finding the best possible matches between peaks of two +#' spectra. Two peaks are considered a potential match if their m/z ratios lie +#' within the given `tolerance` once a mass shift is applied. The mass shift +#' is the difference in precursor-m/z between the two spectra. See also +#' [matchms NeutralLossesCosine](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.NeutralLossesCosine.html) for more information. #' -#' - `FingerprintSimilarityParam`: Calculate similarity between molecules based -#' on their fingerprints. For this similarity measure to work, fingerprints -#' are expected to be derived by running *add_fingerprint()*. +#' - `FingerprintSimilarity()`: Calculate similarity between molecules based +#' on their fingerprints. For this similarity measure to work, fingerprints +#' are expected to be derived by running *add_fingerprint()*. See also +#' [matchms FingerprintSimilarity](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.FingerprintSimilarity.html) for more information. +#' +#' +#' @note +#' +#' Parameters and algorithms are named as originally defined in the *matchms* +#' library (i.e. all parameters in *snake_case* while *CamelCase* is used +#' for the algorithms. #' #' @param x A [Spectra::Spectra()] object. #' #' @param y A [Spectra::Spectra()] object to compare against. If missing, #' spectra similarities are calculated between all spectra in `x`. #' -#' @param param one of parameter classes listed above (such as -#' `CosineGreedyParam`) defining the similarity scoring function in python +#' @param param One of the parameter classes listed above (such as +#' `CosineGreedy`) defining the similarity scoring function in Python #' and its parameters. #' -#' @param tolerance `numeric(1)`: tolerated differences in peaks' m/z. Peaks +#' @param tolerance `numeric(1)`: tolerated differences in the peaks' m/z. Peaks #' with m/z differences `<= tolerance` are considered matching. #' -#' @param mzPower `numeric(1)`: the power to raise m/z to in the cosine +#' @param mz_power `numeric(1)`: the power to raise m/z to in the cosine #' function. The default is 0, in which case the peak intensity products will #' not depend on the m/z ratios. #' -#' @param ignorePeaksAbovePrecursor For `NeutralLossesCosineParam()`: +#' @param ignore_peaks_above_precursor For `NeutralLossesCosine()`: #' `logical(1)`: if `TRUE` (the default), peaks with m/z values larger than #' the precursor m/z are ignored. #' -#' @param intensityPower `numeric(1)`: the power to raise intensity to in the +#' @param intensity_power `numeric(1)`: the power to raise intensity to in the #' cosine function. The default is 1. #' #' @param ... ignored. #' -#' @return `compareSpectriPy()` returns a `numeric` matrix with the scores, -#' number of rows being equal to `length(x)` and number of columns equal to -#' `length(y)`. +#' @return `compareSpectriPy()` Returns a `numeric` matrix with the scores, +#' with the number of rows equal to `length(x)` and the number of columns +#' equal to `length(y)`. #' #' @author Carolin Huber, Michael Witting, Johannes Rainer, Helge Hecht, #' Marilyn De Graeve @@ -85,8 +98,6 @@ #' #' @export #' -#' @importFrom reticulate py_run_string -#' #' @examples #' #' library(Spectra) @@ -99,204 +110,190 @@ #' DF$intensity <- list( #' c(340.0, 416, 2580, 412), #' c(388.0, 3270, 85, 54, 10111), -#' c(3.407, 47.494, 3.094, 100.0, 13.240)) +#' c(3.407, 47.494, 3.094, 100.0, 13.240) +#' ) #' DF$mz <- list( #' c(135.0432, 138.0632, 163.0375, 195.0880), #' c(110.0710, 138.0655, 138.1057, 138.1742, 195.0864), -#' c(109.2, 124.2, 124.5, 170.16, 170.52)) +#' c(109.2, 124.2, 124.5, 170.16, 170.52) +#' ) #' sps <- Spectra(DF) #' #' ## Calculate pairwise similarity beween all spectra within sps with #' ## matchms' CosineGreedy algorithm #' ## Note: the first compareSpectriPy will take longer because the Python #' ## environment needs to be set up. -#' res <- compareSpectriPy(sps, param = CosineGreedyParam()) +#' res <- compareSpectriPy(sps, param = CosineGreedy()) #' res #' #' ## Next we calculate similarities for all spectra against the first one -#' res <- compareSpectriPy(sps, sps[1], param = CosineGreedyParam()) +#' res <- compareSpectriPy(sps, sps[1], param = CosineGreedy()) #' #' ## Calculate pairwise similarity of all spectra in sps with matchms' #' ## ModifiedCosine algorithm -#' res <- compareSpectriPy(sps, param = ModifiedCosineParam()) +#' res <- compareSpectriPy(sps, param = ModifiedCosine()) #' res #' #' ## Note that the ModifiedCosine method requires the precursor m/z to be #' ## known for all input spectra. Thus, it is advisable to remove spectra #' ## without precursor m/z before using this algorithm. #' sps <- sps[!is.na(precursorMz(sps))] -#' compareSpectriPy(sps, param = ModifiedCosineParam()) +#' compareSpectriPy(sps, param = ModifiedCosine()) NULL -setGeneric("compareSpectriPy", function(x, y, param, ...) - standardGeneric("compareSpectriPy")) +setGeneric("compareSpectriPy", function(x, y, param, ...) { + standardGeneric("compareSpectriPy") +}) -#' @importClassesFrom ProtGenerics Param -#' -#' @noRd -setClass("CosineGreedyParam", - slots = c( - tolerance = "numeric", - mzPower = "numeric", - intensityPower = "numeric" - ), - prototype = prototype( - tolerance = 0.1, - mzPower = 0.0, - intensityPower = 1.0 - ), - validity = function(object) { - msg <- NULL - if (length(object@tolerance) != 1 || object@tolerance < 0) - msg <- c("'tolerance' has to be a positive number of length 1") - if (length(object@mzPower) != 1) - msg <- c(msg, "'mzPower' has to be a number of length 1") - if (length(object@intensityPower) != 1) - msg <- c(msg, - "'intensityPower' has to be a number of length 1") - msg - }) -setClass("CosineHungarianParam", - contains = "CosineGreedyParam") -setClass("ModifiedCosineParam", - contains = "CosineGreedyParam") -setClass("NeutralLossesCosineParam", - contains = "CosineGreedyParam", - slots = c(ignorePeaksAbovePrecursor = "logical"), - prototype = prototype(ignorePeaksAbovePrecursor = TRUE), - validity = function(object) { - msg <- NULL - if (length(object@ignorePeaksAbovePrecursor) != 1) - msg <- paste0("'ignorePeaksAbovePrecursor' has to be a ", - "positive number of length 1") - msg - }) +setClass("CosineGreedy", + slots = c( + tolerance = "numeric", mzPower = "numeric", intensityPower = "numeric" + ), + prototype = prototype(tolerance = 0.1, mzPower = 0.0, intensityPower = 1.0), + validity = function(object) { + msg <- NULL + if (length(object@tolerance) != 1 || object@tolerance < 0) { + msg <- c("'tolerance' has to be a positive number of length 1") + } + if (length(object@mzPower) != 1) { + msg <- c(msg, "'mz_power' has to be a number of length 1") + } + if (length(object@intensityPower) != 1) { + msg <- c(msg, "'intensity_power' has to be a number of length 1") + } + msg + } +) +setClass("CosineHungarian", contains = "CosineGreedy") +setClass("ModifiedCosine", contains = "CosineGreedy") +setClass("NeutralLossesCosine", + contains = "CosineGreedy", + slots = c(ignorePeaksAbovePrecursor = "logical"), + prototype = prototype(ignorePeaksAbovePrecursor = TRUE), + validity = function(object) { + msg <- NULL + if (length(object@ignorePeaksAbovePrecursor) != 1) { + msg <- paste0("'ignorePeaksAbovePrecursor' has to be a ", + "positive number of length 1") + } + msg + } +) +setClass("FingerprintSimilarity", contains = "CosineGreedy", + slots = c(similarityMeasure = "character"), + prototype = prototype(similarityMeasure = "jaccard")) #' @rdname compareSpectriPy #' #' @importFrom methods new #' #' @export -CosineGreedyParam <- function(tolerance = 0.1, mzPower = 0.0, - intensityPower = 1.0) { - new("CosineGreedyParam", tolerance = as.numeric(tolerance), - mzPower = as.numeric(mzPower), - intensityPower = as.numeric(intensityPower)) +CosineGreedy <- function(tolerance = 0.1, mz_power = 0.0, + intensity_power = 1.0) { + new("CosineGreedy", + tolerance = as.numeric(tolerance), + mzPower = as.numeric(mz_power), + intensityPower = as.numeric(intensity_power) + ) } #' @rdname compareSpectriPy #' #' @export -CosineHungarianParam <- function(tolerance = 0.1, mzPower = 0.0, - intensityPower = 1.0) { - new("CosineHungarianParam", tolerance = as.numeric(tolerance), - mzPower = as.numeric(mzPower), - intensityPower = as.numeric(intensityPower)) +CosineHungarian <- function(tolerance = 0.1, mz_power = 0.0, + intensity_power = 1.0) { + new("CosineHungarian", + tolerance = as.numeric(tolerance), + mzPower = as.numeric(mz_power), + intensityPower = as.numeric(intensity_power) + ) } #' @rdname compareSpectriPy #' #' @export -ModifiedCosineParam <- function(tolerance = 0.1, mzPower = 0.0, - intensityPower = 1.0) { - new("ModifiedCosineParam", tolerance = as.numeric(tolerance), - mzPower = as.numeric(mzPower), - intensityPower = as.numeric(intensityPower)) +ModifiedCosine <- function(tolerance = 0.1, mz_power = 0.0, + intensity_power = 1.0) { + new("ModifiedCosine", + tolerance = as.numeric(tolerance), + mzPower = as.numeric(mz_power), + intensityPower = as.numeric(intensity_power) + ) } #' @rdname compareSpectriPy #' #' @export -NeutralLossesCosineParam <- function(tolerance = 0.1, mzPower = 0.0, - intensityPower = 1.0, - ignorePeaksAbovePrecursor = TRUE) { - new("NeutralLossesCosineParam", tolerance = as.numeric(tolerance), - mzPower = as.numeric(mzPower), - intensityPower = as.numeric(intensityPower), - ignorePeaksAbovePrecursor = as.logical(ignorePeaksAbovePrecursor)) +NeutralLossesCosine <- function(tolerance = 0.1, mz_power = 0.0, + intensity_power = 1.0, + ignore_peaks_above_precursor = TRUE) { + new("NeutralLossesCosine", + tolerance = as.numeric(tolerance), + mzPower = as.numeric(mz_power), + intensityPower = as.numeric(intensity_power), + ignorePeaksAbovePrecursor = as.logical(ignore_peaks_above_precursor) + ) } -#' @rdname compareSpectriPy -#' -#' @export -FingerprintSimilarityParam <- function(tolerance = 0.1, mzPower = 0.0, - intensityPower = 1.0) { - new("FingerprintSimilarityParam", tolerance = as.numeric(tolerance), - mzPower = as.numeric(mzPower), - intensityPower = as.numeric(intensityPower)) -} +## #' @rdname compareSpectriPy +## #' +## #' @export +## FingerprintSimilarity <- function(similarity_measure = "jaccard") { +## new("FingerprintSimilarity", +## similarityMeasure = similarity_measure +## ) +## } #' @rdname compareSpectriPy #' #' @exportMethod compareSpectriPy setMethod( "compareSpectriPy", - signature = c(x = "Spectra", y = "Spectra", param = "CosineGreedyParam"), + signature = c(x = "Spectra", y = "Spectra", param = "CosineGreedy"), function(x, y, param, ...) { .compare_spectra_python(x, y, param) - }) + } +) #' @rdname compareSpectriPy setMethod( "compareSpectriPy", - signature = c(x = "Spectra", y = "missing", param = "CosineGreedyParam"), + signature = c(x = "Spectra", y = "missing", param = "CosineGreedy"), function(x, y, param, ...) { .compare_spectra_python(x, y = NULL, param) - }) + } +) -#' helper function to extract cosine parameter settings for similarity functions -#' based on cosine. -#' -#' @noRd -.cosine_param_string <- function(x) { - paste0("tolerance=", x@tolerance, ", mz_power=", x@mzPower, - ", intensity_power=", x@intensityPower) -} -#' Could also define a method, but I guess that's overkill in this case. +#' Method to construct a python function name for a param object. #' #' @noRd +setGeneric("py_fun", function(object, ...) { + standardGeneric("py_fun") +}) +setMethod("py_fun", "CosineGreedy", function(object) { + matchms_similarity$CosineGreedy(object@tolerance, object@mzPower, + object@intensityPower) +}) +setMethod("py_fun", "CosineHungarian", function(object) { + matchms_similarity$CosineHungarian(object@tolerance, object@mzPower, + object@intensityPower) +}) +setMethod("py_fun", "ModifiedCosine", function(object) { + matchms_similarity$ModifiedCosine(object@tolerance, object@mzPower, + object@intensityPower) +}) +setMethod("py_fun", "NeutralLossesCosine", function(object) { + matchms_similarity$NeutralLossesCosine(object@tolerance, object@mzPower, + object@intensityPower, + object@ignorePeaksAbovePrecursor) +}) + .fun_name <- function(x) { - sub("Param$", "", class(x)[1L]) + class(x)[1L] } -#' (internal) helper method to build the python command to perform the -#' comparison. Each parameter class could (if needed) it's own implementation -#' to create the string. This methods will be called in the -#' `compare_spectra_python` function. -#' -#' @noRd -setGeneric("python_command", function(object, ...) - standardGeneric("python_command")) -setMethod( - "python_command", - "CosineGreedyParam", - function(object, input_param = "py_x, py_y", is_symmetric = "False") { - FUN <- .fun_name(object) - paste0("import matchms\n", - "from matchms.similarity import ", FUN, "\n", - "res = matchms.calculate_scores(", input_param, - ", ", FUN, "(", .cosine_param_string(object), "), ", - "is_symmetric=", is_symmetric, ")\n", - "sim = res.scores.to_array()[\"", FUN, "_score\"]\n") - }) -setMethod( - "python_command", - "NeutralLossesCosineParam", - function(object, input_param = "py_x, py_y", is_symmetric = "False") { - FUN <- .fun_name(object) - paste0("import matchms\n", - "from matchms.similarity import ", FUN, "\n", - "res = matchms.calculate_scores(", input_param, - ", ", FUN, "(", .cosine_param_string(object), ", ", - "ignore_peaks_above_precursor=", - ifelse(object@ignorePeaksAbovePrecursor, - yes = "True", no = "False"), - "), is_symmetric=", is_symmetric, ")\n", - "sim = res.scores.to_array()[\"", FUN, "_score\"]\n") - }) - -#' internal function to calculate similarities with python's matchms. `Spectra` -#' will be converted to python. +#' Internal function to calculate similarities with Python's matchms. `Spectra` +#' will be converted to Python. #' #' @param x `Spectra` #' @@ -309,52 +306,28 @@ setMethod( #' #' @return a `numeric` `matrix` nrow being length of `x`, nrow length `y`. #' -#' @importFrom basilisk basiliskStart basiliskRun basiliskStop -#' -#' @importFrom reticulate py -#' #' @noRd #' -#' @author Carolin Huber, Johannes Rainer -.compare_spectra_python <- function(x, y = NULL, param, value = "score") { - ## handle empty input - if (!length(x) || (!length(y) & !is.null(y))) +#' @author Carolin Huber, Johannes Rainer, Wout Bittremieux +#' +#' @importFrom reticulate r_to_py py_to_r +.compare_spectra_python <- function(x, y = NULL, param, + mapping = spectraVariableMapping()) { + ## Handle empty input. + if (!length(x) || (!length(y) & !is.null(y))) { return(matrix(NA_real_, ncol = length(y), nrow = length(x))) + } - cl <- basiliskStart(matchms_env) - on.exit(basiliskStop(cl)) + ## Convert R spectra to Python. + py_x <- rspec_to_pyspec(x, mapping = mapping) + py_y <- if (!is.null(y)) rspec_to_pyspec(y) else py_x + is_symmetric <- is.null(y) - basiliskRun(cl, function(x, y, param) { - ref <- import("matchms") - vars <- c(precursorMz = "precursor_mz") - is_symmetric <- "False" - py$py_x <- rspec_to_pyspec(x, reference = ref, mapping = vars) - if (is.null(y)) { - py$py_y <- py$py_x - is_symmetric <- "True" - } else - py$py_y <- rspec_to_pyspec(y, reference = ref, mapping = vars) - com <- python_command(param, is_symmetric = is_symmetric) - ## Run the command. Result is in py$res - py_run_string(com) - ## Collect the results - return(py$sim) - ## py_run_string( - ## paste0("sim = []\n", - ## "for x,y,z in res:\n sim.append(z['", value, "'])")) - ## matrix(unlist(py$sim, use.names = FALSE), - ## nrow = length(x), byrow = TRUE) - }, x = x, y = y, param = param) + ## Compute the similarity scores with matchms. + scores <- matchms$calculate_scores( + py_x, py_y, py_fun(param), + is_symmetric = is_symmetric + ) + ## Collect results + py_to_r(scores$to_array()[paste0(.fun_name(param), "_score")]) } - -## from matchms import calculate_scores -## from matchms.importing import load_from_msp -## from ms2deepscore import MS2DeepScore -## from ms2deepscore.models import load_model - -## model = load_model("load model file") -## similarity_measure = MS2DeepScore(model) -## scores_mat = similarity_measure.matrix( -## matchms_spectrum1, -## matchms_spectrum2 -## ) diff --git a/R/compareSpectriPyRet.R b/R/compareSpectriPyRet.R deleted file mode 100644 index 3fec2b8..0000000 --- a/R/compareSpectriPyRet.R +++ /dev/null @@ -1,340 +0,0 @@ -#' @title Spectra similarity calculations using matchms -#' -#' @name compareSpectriPyRet -#' -#' @description -#' -#' The `compareSpectriPyRet()` function allows to calculate spectral similarity -#' scores using the `calculate_scores module` of the python -#' [matchms.similarity package](https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html) -#' package. -#' -#' Selection and configuration of the algorithm can be performed with one of the -#' parameter objects: -#' -#' - `CosineGreedyParam`: calculate the *cosine similarity score* between -#' spectra. The score is calculated by finding best possible matches between -#' peaks of two spectra. Two peaks are considered a potential match if their -#' m/z ratios lie within the given `tolerance`. The underlying peak assignment -#' problem is here solved in a *greedy* way. This can perform notably faster, -#' but does occasionally deviate slightly from a fully correct solution (as -#' with the `CosineHungarianParam` algorithm). In practice this will rarely -#' affect similarity scores notably, in particular for smaller tolerances. The -#' algorithm can be configured with parameters `tolerance`, `mzPower` and -#' `intensityPower` (see parameter description for more details). -#' -#' - `CosineHungarianParam`: calculate the *cosine similarity score* as with -#' `CosineGreedyParam`, but using the Hungarian algorithm to find the best -#' matching peaks between the compared spectra. The algorithm can be -#' configured with parameters `tolerance`, `mzPower` and `intensityPower` -#' (see parameter description for more details). -#' -#' - `ModifiedCosineParam`: The modified cosine score aims at quantifying the -#' similarity between two mass spectra. The score is calculated by finding -#' best possible matches between peaks of two spectra. Two peaks are -#' considered a potential match if their m/z ratios lie within the given -#' `tolerance`, or if their m/z ratios lie within the tolerance once a -#' mass-shift is applied. The mass shift is simply the difference in -#' precursor-m/z between the two spectra. -#' -#' - `NeutralLossesCosineParam`: The neutral losses cosine score aims at -#' quantifying the similarity between two mass spectra. The score is -#' calculated by finding best possible matches between peaks of two spectra. -#' Two peaks are considered a potential match if their m/z ratios lie within -#' the given `tolerance` once a mass-shift is applied. The mass shift is the -#' difference in precursor-m/z between the two spectra. -#' -#' - `FingerprintSimilarityParam`: Calculate similarity between molecules based -#' on their fingerprints. For this similarity measure to work, fingerprints -#' are expected to be derived by running *add_fingerprint()*. -#' -#' @param x A [Spectra::Spectra()] object. -#' -#' @param y A [Spectra::Spectra()] object to compare against. If missing, -#' spectra similarities are calculated between all spectra in `x`. -#' -#' @param param one of parameter classes listed above (such as -#' `CosineGreedyParam`) defining the similarity scoring function in python -#' and its parameters. -#' -#' @param use_existing_env provide path to conda env. If nothing is provided -#' a basilisk env is set up and used. -#' -#' @param tolerance `numeric(1)`: tolerated differences in peaks' m/z. Peaks -#' with m/z differences `<= tolerance` are considered matching. -#' -#' @param mzPower `numeric(1)`: the power to raise m/z to in the cosine -#' function. The default is 0, in which case the peak intensity products will -#' not depend on the m/z ratios. -#' -#' @param ignorePeaksAbovePrecursor For `NeutralLossesCosineParam()`: -#' `logical(1)`: if `TRUE` (the default), peaks with m/z values larger than -#' the precursor m/z are ignored. -#' -#' @param intensityPower `numeric(1)`: the power to raise intensity to in the -#' cosine function. The default is 1. -#' -#' @param ... ignored. -#' -#' @return `compareSpectriPyRet()` returns a `numeric` matrix with the scores, -#' number of rows being equal to `length(x)` and number of columns equal to -#' `length(y)`. -#' -#' @author Carolin Huber, Michael Witting, Johannes Rainer, Helge Hecht, -#' Marilyn De Graeve -#' -#' @seealso [Spectra::compareSpectra()] in the *Spectra* package for pure R -#' implementations of spectra similarity calculations. -#' -#' @export -#' -#' @importFrom reticulate py_run_string -#' -#' @examples -#' -#' library(Spectra) -#' ## Create some example Spectra. -#' DF <- DataFrame( -#' msLevel = c(2L, 2L, 2L), -#' name = c("Caffeine", "Caffeine", "1-Methylhistidine"), -#' precursorMz = c(195.0877, 195.0877, 170.0924) -#' ) -#' DF$intensity <- list( -#' c(340.0, 416, 2580, 412), -#' c(388.0, 3270, 85, 54, 10111), -#' c(3.407, 47.494, 3.094, 100.0, 13.240)) -#' DF$mz <- list( -#' c(135.0432, 138.0632, 163.0375, 195.0880), -#' c(110.0710, 138.0655, 138.1057, 138.1742, 195.0864), -#' c(109.2, 124.2, 124.5, 170.16, 170.52)) -#' sps <- Spectra(DF) -#' -#' ## Calculate pairwise similarity beween all spectra within sps with -#' ## matchms' CosineGreedy algorithm -#' ## Note: the first compareSpectriPy will take longer because the Python -#' ## environment needs to be set up. -#' res <- compareSpectriPyRet(sps, param = CosineGreedyParam()) -#' res -#' -#' ## Next we calculate similarities for all spectra against the first one -#' res <- compareSpectriPyRet(sps, sps[1], param = CosineGreedyParam()) -#' -#' ## Calculate pairwise similarity of all spectra in sps with matchms' -#' ## ModifiedCosine algorithm -#' res <- compareSpectriPyRet(sps, param = ModifiedCosineParam(), use_existing_env = "my_env") -#' res -#' -#' ## Note that the ModifiedCosine method requires the precursor m/z to be -#' ## known for all input spectra. Thus, it is advisable to remove spectra -#' ## without precursor m/z before using this algorithm. -#' sps <- sps[!is.na(precursorMz(sps))] -#' compareSpectriPyRet(sps, param = ModifiedCosineParam()) -NULL - -setGeneric("compareSpectriPyRet", function(x, y, param, use_existing_env, ...) - standardGeneric("compareSpectriPyRet")) - -#' @rdname compareSpectriPyRet -#' -#' @exportMethod compareSpectriPyRet -setMethod( - "compareSpectriPyRet", - signature = c(x = "Spectra", y = "Spectra", param = "CosineGreedyParam", use_existing_env = "character"), - function(x, y, param, use_existing_env, ...) { - .compare_spectra_reticulate(x, y, param, use_existing_env) - }) - -#' @rdname compareSpectriPyRet -setMethod( - "compareSpectriPyRet", - signature = c(x = "Spectra", y = "missing", param = "CosineGreedyParam", use_existing_env = "missing"), - function(x, y, param, use_existing_env, ...) { - .compare_spectra_reticulate(x, y = NULL, param, use_existing_env = NULL) - }) - -#' @rdname compareSpectriPyRet -setMethod( - "compareSpectriPyRet", - signature = c(x = "Spectra", y = "Spectra", param = "CosineGreedyParam", use_existing_env = "missing"), - function(x, y, param, use_existing_env, ...) { - .compare_spectra_reticulate(x, y, param, use_existing_env = NULL) - }) - -#' @rdname compareSpectriPyRet -setMethod( - "compareSpectriPyRet", - signature = c(x = "Spectra", y = "missing", param = "CosineGreedyParam", use_existing_env = "character"), - function(x, y, param, use_existing_env, ...) { - .compare_spectra_reticulate(x, y = NULL, param, use_existing_env) - }) - -#' internal helper function which sets up a basilisk conda env unless a local -#' conda env is provided -#' -#' @param use_existing_env `character` -#' -#' @importFrom basilisk basiliskStart basiliskRun basiliskStop -#' -#' @importFrom reticulate py import use_condaenv -#' -#' @noRd -#' -#' @author Victor Chrone -.setup_conda_env <- function( use_existing_env = NULL) { - # If the user provides an existing Conda environment, use reticulate - if (!is.null(use_existing_env)) { - message("Using existing Conda environment: ", use_existing_env) - - # Activate the provided Conda environment - reticulate::use_condaenv(use_existing_env, required = TRUE) - - # Import required Python packages - matchms <- reticulate::import("matchms", convert = TRUE) - numpy <- reticulate::import("numpy", convert = TRUE) - - return(list(matchms = matchms, numpy = numpy)) - } - - # Otherwise, use basilisk to create an isolated environment - message("No existing Conda environment provided. Using basilisk to set up an isolated Conda environment...") - - # Start the environment and install packages if needed - cl <- basiliskStart(matchms_env) - on.exit(basiliskStop(cl), add = TRUE) - - basiliskRun(cl, function() { - reticulate::import("matchms") - reticulate::import("numpy") - }) - - # Load the Python packages from the basilisk-managed environment - matchms <- basiliskRun(cl, function() reticulate::import("matchms", convert = TRUE)) - numpy <- basiliskRun(cl, function() reticulate::import("numpy", convert = TRUE)) - - return(list(matchms = matchms, numpy = numpy)) -} - -#' internal helper function to convert R Spectra object to a Python-compatible -#' matchms.Spectrum format object using reticulate. -#' -#' @param spectra `Spectra` -#' -#' @return A Python list of matchms Spectrum objects -#' -#' @importFrom reticulate py import -#' -#' @noRd -#' -#' @author Victor Chrone -# Convert R Spectra object to Python-compatible format -.convert_to_python <- function(spectra) { - - # Import matchms and numpy package - matchms <- reticulate::import("matchms") - similarity <- reticulate::import("matchms.similarity") - np <- reticulate::import("numpy") # Needed for array conversion - - py_list <- list() - - for (i in seq_along(spectra)) { - py_list[[i]] <- matchms$Spectrum( - mz = np$array(mz(spectra)[[i]], dtype = "float64"), # Convert to NumPy array - intensities = np$array(intensity(spectra)[[i]], dtype = "float64"), # Convert to NumPy array - metadata = list(precursor_mz = as.numeric(precursorMz(spectra)[i])) # Ensure metadata is numeric - ) - } - - return(reticulate::r_to_py(py_list)) -} - -#' internal function to calculate similarities with python's matchms. `Spectra` -#' using reticulate -#' -#' @param x `Spectra` -#' -#' @param y `Spectra` -#' -#' @param param Parameter object. -#' -#' @param value `character(1)` defining which value should be returned from the -#' Python call. -#' -#' @return a `numeric` `matrix` nrow being length of `x`, nrow length `y`. -#' -#' @importFrom basilisk basiliskStart basiliskRun basiliskStop -#' -#' @importFrom reticulate py import -#' -#' @noRd -#' -#' @author Victor Chrone -.compare_spectra_reticulate <- function(x, y = NULL, param, use_existing_env = NULL) { - - #Set up Conda environment using basilisk if no local Conda environment is provided - env_packages <- .setup_conda_env(use_existing_env = use_existing_env) - - # Import matchms and numpy package - matchms <- reticulate::import("matchms") - similarity <- reticulate::import("matchms.similarity") - np <- reticulate::import("numpy") # Needed for array conversion - - # Convert R Spectra objects to Python-compatible format - py_x <- .convert_to_python(x) - py_y <- if (is.null(y)) py_x else .convert_to_python(y) - - # Determine if the comparison is symmetric - is_symmetric <- is.null(y) || (length(x) == length(y)) - - # Determine which similarity function to use - similarity_function <- switch( - class(param)[1], - "CosineGreedyParam" = similarity$CosineGreedy, - "CosineHungarianParam" = similarity$CosineHungarian, - "ModifiedCosineParam" = similarity$ModifiedCosine, - "NeutralLossesCosineParam" = similarity$NeutralLossesCosine, - "FingerprintSimilarityParam" = similarity$FingerprintSimilarity, - stop("Unknown similarity function") - ) - - # Initialize similarity function - sim_func <- similarity_function( - tolerance = param@tolerance, - mz_power = param@mzPower, - intensity_power = param@intensityPower - ) - - # Call calculate_scores from matchms, including `is_symmetric` - scores_obj <- matchms$calculate_scores(py_x, py_y, similarity_function = sim_func, is_symmetric = is_symmetric) - - # Convert structured NumPy array to an R list - scores_np <- reticulate::py_to_r(scores_obj$scores$to_array()) - - # Determine correct matrix dimensions - ncol_scores <- length(scores_np[[1]]) - nrow_scores <- length(scores_np) / ncol_scores - - # Initialize similarity matrix - scores_matrix <- matrix(NA, nrow = nrow_scores, ncol = ncol_scores) - - # Extract only similarity scores (first value in tuple) - for (i in 0:(nrow_scores - 1)) { - for (j in 0:(ncol_scores - 1)) { - scores_matrix[(i + 1), (j + 1)] <- as.numeric(scores_np[[i]][j][0]) # Extract first element (CosineGreedy_score) - } - } - - return(scores_matrix) -} - -## from matchms import calculate_scores -## from matchms.importing import load_from_msp -## from ms2deepscore import MS2DeepScore -## from ms2deepscore.models import load_model - -## model = load_model("load model file") -## similarity_measure = MS2DeepScore(model) -## scores_mat = similarity_measure.matrix( -## matchms_spectrum1, -## matchms_spectrum2 -## ) - diff --git a/R/conversion.R b/R/conversion.R index 746be04..2faae63 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -1,81 +1,187 @@ -#' @title Low level functions to convert between Spectra and matchms Spectrum +#' @title Converting between R and Python MS data structures #' -#' @name rspec_to_pyspec +#' @name conversion #' #' @description #' #' The `rspec_to_pyspec()` and `pyspec_to_rspec()` functions allow to convert -#' R [Spectra::Spectra()] objects into +#' (translate) MS data structures between R and Python. At present the +#' R [Spectra::Spectra()] objects are translated into a list of #' [matchms](https://github.com/matchms/matchms) Python `matchms.Spectrum` -#' objects. These functions are designed for -#' **advanced users or developers** who want/need to integrate Python/matchms -#' functionality into R using *reticulate*. All other users should use the -#' dedicated R functions within this package that take care of running the -#' Python code in the correct Python environment. +#' objects. For better integration with the *reticulate* R package also a +#' `r_to_py.Spectra()` method is available. #' -#' Parameter `mapping` allows to define which spectra variables (metadata) -#' should be copied between the R and Python spectra. Only provided spectra -#' variables will be copied to R respectively Python. `mapping` also defines -#' the mapping between the `Spectra`'s spectra variables and the Spectrum -#' metadata. The names of the character vector `mapping` are the R spectra -#' variables and the values the corresponding names in the Python's Spectrum -#' metadata. See the output of the `spectraVariableMapping()` function for the -#' default variables and the mapping of the names. +#' The mapping of spectra variables (in R) to (Python) spectra metadata can +#' be configured and defined with the `setSpectraVariableMapping()` and +#' `spectraVariableMapping()`. These get and set the *global* (system wide) +#' setting and are thus also used by the `r_to_py()` method. #' -#' The `spectraVariableMapping()` function provides a default mapping of some -#' core `Spectra` variables based on this [definition in matchms](https://github.com/matchms/matchms/blob/master/matchms/data/known_key_conversions.csv). -#' The function returns a named vector that can be directly used as parameter -#' `mapping` in the `rspec_to_pyspec()` and `pyspec_to_rspec()` functions. +#' See the indivudual function's documentation for more details. #' -#' @param .check Optionally disable input parameter checking. Input parameter -#' checking should only disabled for very good reasons. #' -#' @param BPPARAM Optional parallel processing setup. +#' @section Translation of MS data objects: #' -#' @param mapping Named `character` providing the spectra variable names -#' (metadata) to convert. Names are expected to be the spectra variable -#' names and values the corresponding names of the Python Spectrum metadata -#' fields. See description above for more details. +#' MS data structures can be translated between R and Python using the +#' `rspec_to_pyspec()` and `pyspec_to_rspec()` functions, or with the +#' `r_to_py()` method. #' -#' @param object ignored. +#' - `rspec_to_pyspec()` translates an R [Spectra::Spectra()] object into a +#' list of `matchms.Spectrum` objects. Parameter `mapping` allows to specify +#' which spectra variables from the `Spectra` object `x` should be converted +#' in addition to the peaks data (m/z and intensity values). It defaults to +#' `mapping = spectraVariableMapping()` (See the respective help below for +#' more information on the variable mapping). While being fast, this function +#' first loads all peaks and spectra data into memory before translating to +#' Python data structures. A less memory intense operation could be to call +#' this function in a loop to only load parts of the data at a time into +#' memory. #' -#' @param reference Optional reference to Python environment `matchms`. +#' - `pyspec_to_rspec()` translates a single, or a list of `matchms.Spectrum` +#' objects to a [Spectra::Spectra()] object. Parameter `mapping` allows to +#' speficy the metadata variables that should be translated and mapped in +#' addition to the peaks data. #' -#' @param x For `rspec_to_pyspec()`: `Spectra` object. For `pyspec_to_rspec()`: -#' a Python list of matchms Spectrum objects. +#' - `r_to_py.Spectra()` is equivalent to `rspec_to_pyspec()`. The spectra +#' variables that should be converted can be configures with +#' `setSpectraVariableMapping()` (see documentation below). #' -#' @param ... ignored. #' -#' @return For `rspec_to_pyspec()`: Python array of Spectrum objects, same -#' length than `x`. For `pyspec_to_rspec()`: [Spectra::Spectra()] with the -#' converted spectra. For `spectraVariableMapping()`: named `character` -#' vector with names being `Spectra` variable names and values the -#' corresponding names in `matchms`. +#' @section Mapping of spectra variables (metadata): #' -#' @author Michael Witting, Johannes Rainer +#' Metadata for MS spectra are represented and stored as *spectra variables* +#' in the R [Spectra::Spectra()] objects. Also Python MS data structures +#' store such metadata along with the mass peak data. While spectra metadata +#' is thus supported by data structures in both programming languages, +#' different names and naming conventions are used. The +#' `spectraVariableMapping()` and `setSpectraVariableMapping()` functions allow +#' to define how the names of spectra metadata (spectra variables) should be +#' translated between R and Python. The `r_to_py()` and `py_to_r()` functions +#' will used these to name the spectra variables accordingly. Also, only +#' spectra metadata/variables in `spectraVariableMapping()` will be translated. +#' The initial mapping is based on this +#' [definition in matchms](https://github.com/matchms/matchms/blob/master/matchms/data/known_key_conversions.csv). #' -#' @export +#' - `defaultSpectraVariableMapping()`: returns the *default* mapping between +#' spectra variables and *matchms* metadata names. +#' +#' - `spectraVariableMapping()`: returns the currently defined spectra +#' variable mapping as a named character vector, with names representing the +#' names of the spectra variables in R and elements the respective names +#' of the spectra metadata in Python. Use [Spectra::spectraVariables()] on +#' the `Spectra` object that should be converted with `r_to_py()` to list +#' all available spectra variables. `r_to_py()` and `py_to_r()` for MS data +#' structures will use this mapping. +#' +#' - `setSpectraVariableMapping()`: sets/replaces the currently defined mapping +#' of spectra variable names to Python metadata names. Setting +#' `setSpectraVariableMapping(character())` will only convert the mass peaks +#' data (m/z and intensity values) but no spectra metadata. +#' +#' @param mapping named `character()` vector defining which spectra +#' variables/metadata should be translated between R and Python and how +#' they should be renamed. Defaults to `spectraVariableMapping()`. +#' +#' @param x For `rspec_to_pyspec` or `r_to_py.Spectra()`: the +#' [Spectra::Spectra()]` object that should be translated. For +#' `pyspec_to_rspec()`: a single `matchms.Spectrum` object or a Python +#' list of `matchms.Spectrum` objects. +#' +#' @param object For `spectraVariableMapping()`: not used. #' -#' @importFrom reticulate r_to_py import py_to_r +#' @param ... For `spectraVariableMapping()`: not used. #' -#' @importFrom BiocParallel SerialParam bplapply +#' @return For `r_to_py.Spectra()` and `rspec_to_pyspec()`: Python list of +#' `matchms.Spectrum` objects. For `pyspec_to_rspec()`: +#' [Spectra::Spectra()] with the MS data of all `matchms.Spectrum` objects +#' in the submitted `list`. +#' +#' @author Michael Witting, Johannes Rainer, Wout Bittremieux +#' +#' @importFrom reticulate r_to_py py_to_r #' #' @importMethodsFrom Spectra spectrapply #' #' @examples #' -#' ## List the default spectra variables and their mapping. -#' spectraVariableMapping() -NULL - -#' @importMethodsFrom Spectra spectraVariableMapping +#' ## Import a MGF file as a `Spectra` object +#' library(MsBackendMgf) +#' library(SpectriPy) +#' s <- Spectra( +#' system.file("extdata", "mgf", "spectra2.mgf", package = "SpectriPy"), +#' source = MsBackendMgf()) +#' s #' -#' @exportMethod spectraVariableMapping +#' ######################### +#' ## Conversion R to Python #' -#' @rdname rspec_to_pyspec -setMethod("spectraVariableMapping", "missing", function(object, ...) { - .SPECTRA_2_MATCHMS -}) +#' ## A `Spectra` can be translated to a `list` of `matchms.Spectrum` objects +#' ## using either the `r_to_py()` method or the `rspec_to_pyspec()` function: +#' s_py <- r_to_py(s) +#' s_py +#' +#' ## The `s_py` can now be used like any other Python variable within the R +#' ## *reticulate* framework. Below we extract the m/z values of the first +#' ## spectrum +#' s_py[0]$mz +#' +#' ## Extracting that information from the `Spectra` object in R +#' s[1]$mz +#' +#' ## The `spectraVariableMapping()` defines which spectra variables (metadata) +#' ## should be translated between R and Python: +#' spectraVariableMapping() +#' +#' ## The names of that character vector represent the names of the spectra +#' ## variables in R, the elements the name of the metadata variable in Python. +#' ## Below we list the available metadata information from the first +#' ## Spectrum in Python +#' s_py[0]$metadata +#' +#' ## `setSpectraVariableMapping()` allows to replace the default mapping +#' ## of variables. Below we e.g. add a new spectra variable to the `Spectra` +#' ## object. +#' s$new_col <- 1:4 +#' +#' ## To translate that variable to Python we need to include it to the +#' ## `spectraVariableMapping()`. Below we define to translate only the +#' ## precursor m/z and the new spectra variable to Python. Be aware that +#' ## `setSpectraVariableMapping()` **globally** sets the default for any +#' ## spectra variable mapping between R and Python. Thus, any subsequent +#' ## calls mapping calls will use the same mapping. It is suggested to +#' ## eventually *restore* the default mapping again after the call or +#' ## use the `rspec_to_pyspec()` function instead, that allows to configure +#' ## the mapping using a parameter `mapping`. +#' setSpectraVariableMapping( +#' c(precursorMz = "precursor_mz", new_col = "new_col")) +#' s_py <- r_to_py(s) +#' +#' s_py[0]$metadata +#' +#' ## Restoring the global spectra variable mapping configuration to +#' ## the default mapping: +#' setSpectraVariableMapping(defaultSpectraVariableMapping()) +#' +#' ## As an alternative to the `r_to_py()` we can use the `rspec_to_pyspec()` +#' ## function and provide a custom mapping using the `mapping` parameter: +#' s_py <- rspec_to_pyspec( +#' s, mapping = c(precursorMz = "precursor_mz", new_col = "new_col")) +#' +#' ######################### +#' ## Conversion Python to R +#' +#' ## A `list` of `matchms.Spectrum` objects in Python can be translated into +#' ## the corresponding MS data structure in R (i.e. a `Spectra`) object using +#' ## the `pyspec_to_rspec()` function: +#' res <- pyspec_to_rspec(s_py) +#' res +#' +#' ## All spectra from Python are thus converted into a single `Spectra` object. +#' +#' ## Or providing a custom variable mapping: +#' res <- pyspec_to_rspec( +#' s_py, mapping = c(precursorMz = "precursor_mz", new_col = "new_col")) +#' res$new_col +NULL .SPECTRA_2_MATCHMS <- c( precursorMz = "precursor_mz", @@ -88,37 +194,69 @@ setMethod("spectraVariableMapping", "missing", function(object, ...) { msLevel = "ms_level" ) -#' @rdname rspec_to_pyspec +#' @importMethodsFrom Spectra spectraVariableMapping #' -#' @importFrom methods is +#' @exportMethod spectraVariableMapping +#' +#' @rdname conversion #' #' @export -rspec_to_pyspec <- function(x, mapping = spectraVariableMapping(), - reference = import("matchms"), - BPPARAM = SerialParam(), .check = TRUE) { - if (.check && !is(x, "Spectra")) - stop("'x' should be a Spectra object.") - plist <- spectrapply(x, .single_rspec_to_pyspec, spectraVariables = mapping, - reference = reference, BPPARAM = BPPARAM) - r_to_py(unname(plist)) +setMethod("spectraVariableMapping", "missing", function(object, ...) { + getOption("spectripy.spectra_variable_mapping", .SPECTRA_2_MATCHMS) +}) + +#' @rdname conversion +#' +#' @export +setSpectraVariableMapping <- function(x) { + if (!is.character(x) | length(names(x)) != length(x)) + stop("'x' is expected to be a named character vector") + options(spectripy.spectra_variable_mapping = x) } -#' @rdname rspec_to_pyspec +#' @rdname conversion #' -#' @importFrom Spectra concatenateSpectra +#' @export +defaultSpectraVariableMapping <- function() { + .SPECTRA_2_MATCHMS +} + +## -------- R TO PY ----------------------------------------------------------## + +#' @rdname conversion +#' +#' @description +#' +#' Function to convert R Spectra objects into a Python list of matchms Spectrum +#' objects using the `reticulate` package. +#' +#' @param x `Spectra` object. +#' +#' @param convert Boolean; should Python objects be automatically converted to +#' their R equivalent? Defaults to `FALSE`. +#' +#' @importFrom reticulate r_to_py +#' +#' @importFrom Spectra spectrapply #' #' @export -pyspec_to_rspec <- function(x, mapping = spectraVariableMapping(), - BPPARAM = SerialParam(), .check = TRUE) { - if (!(is(x, "list") | is(x, "python.builtin.list"))) - stop("'x' is expected to be a Python list.") - x <- py_to_r(x) - if (.check && !all(vapply(x, function(z) - is(z, "matchms.Spectrum.Spectrum"), logical(1)))) - stop("'x' is expected to be a Python list of matchms Spectrum objects.") - spectra_list <- bplapply(x, .single_pyspec_to_rspec, - spectraVariables = mapping, BPPARAM = BPPARAM) - do.call(concatenateSpectra, spectra_list) +r_to_py.Spectra <- function(x, convert = FALSE) { + .rspec_to_pyspec(x, mapping = spectraVariableMapping()) +} + +#' @rdname conversion +#' +#' @importFrom methods is +#' +#' @export +rspec_to_pyspec <- function(x, mapping = spectraVariableMapping()) { + if (!is(x, "Spectra")) + stop("'x' should be a Spectra object.") + ## that could be more memory efficient, but slower. + ## r_to_py(spectrapply(x, .single_rspec_to_pyspec, + ## spectraVariables = mapping, + ## BPPARAM = BPPARAM)) + .rspec_to_pyspec(x, mapping = mapping) } #' @description @@ -128,18 +266,16 @@ pyspec_to_rspec <- function(x, mapping = spectraVariableMapping(), #' #' @param x `Spectra` object **of length 1!**. #' -#' @param spectraVariables named `character` vector defining the spectra -#' varibles that should be stored as metadata in `matchms`' metadata. Names +#' @param spectraVariables Named `character` vector defining the spectra +#' variables that should be stored as metadata in `matchms`' metadata. Names #' are expected to be the spectra variable names and values the #' corresponding metadata fields in `matchms`. Defaults to -#' [spectraVariableMapping()]. If `spectraVariables = character()` no +#' `.SPECTRA_2_MATCHMS`. If `spectraVariables = character()` no #' metadata will be stored. #' -#' @param reference Reference to Python environment matchms -#' -#' @return `Spectrum` Single Python Spectrum +#' @return `Spectrum` Single Python Spectrum. #' -#' @author Michael Witting, Johannes Rainer +#' @author Michael Witting, Johannes Rainer, Wout Bittremieux #' #' @importMethodsFrom Spectra spectraData #' @@ -152,21 +288,90 @@ pyspec_to_rspec <- function(x, mapping = spectraVariableMapping(), #' @importFrom reticulate np_array r_to_py #' #' @noRd -.single_rspec_to_pyspec <- function(x, - spectraVariables = spectraVariableMapping(), - reference = import("matchms")) { - pks <- unname(peaksData(x, c("mz", "intensity")))[[1L]] - if (length(spectraVariables)) { - slist <- as.list(spectraData(x, columns = names(spectraVariables))) - ## ## Seems matchms.Spectrum does not support NA retention times? - ## if (any(names(slist) == "rtime") && is.na(slist$rtime)) - ## slist$rtime <- 0 - names(slist) <- spectraVariables - reference$Spectrum(mz = np_array(pks[, 1L]), - intensities = np_array(pks[, 2L]), - metadata = r_to_py(slist)) - } else reference$Spectrum(mz = np_array(pks[, 1L]), - intensities = np_array(pks[, 2L])) +.single_rspec_to_pyspec <- + function(x, mapping = spectraVariableMapping()) { + pks <- unname(peaksData(x, c("mz", "intensity")))[[1L]] + if (length(mapping)) { + slist <- as.list(spectraData(x, columns = names(mapping))) + names(slist) <- mapping[names(slist)] + matchms$Spectrum( + mz = np_array(pks[, 1L]), + intensities = np_array(pks[, 2L]), + metadata = r_to_py(slist) + ) + } else { + matchms$Spectrum( + mz = np_array(pks[, 1L]), + intensities = np_array(pks[, 2L]) + ) + } + } + +#' Converts a `Spectra::Spectra` to a `list` of `matchms.Spectrum` by first +#' extracting the peaks and spectra data and iterating over these. This is +#' a faster, but also more memory heavy implementation as the full peaks and +#' spectra data are read into memory. With the `single_rspec_to_pyspec()` only +#' the data from one spectrum at a time are read. +#' +#' @noRd +.rspec_to_pyspec <- function(x, mapping = spectraVariableMapping()) { + pks <- peaksData(x, c("mz", "intensity")) + sv <- mapping[!mapping %in% c("mz", "intensity")] + if (length(sv)) { + spd <- as.data.frame(spectraData(x, columns = names(sv))) + colnames(spd) <- sv[colnames(spd)] + rm(x) + spd <- split(spd, seq_along(pks)) + r_to_py(mapply(function(y, z) { + matchms$Spectrum(mz = np_array(z[, 1L]), + intensities = np_array(z[, 2L]), + metadata = r_to_py(as.list(y))) + }, spd, pks, SIMPLIFY = FALSE, USE.NAMES = FALSE)) + } else { + rm(x) + r_to_py(lapply(pks, function(z) + matchms$Spectrum(mz = np_array(z[, 1L],), + intensities = np_array(z[, 2L])))) + } +} + +## -------- PY TO R ----------------------------------------------------------## + +#' Function to extract the metadata from a (**single**) `matchms.Spectrum` +#' object. This function can be used in a loop over a `list` of objects to +#' create a `Spectra` from it. +#' +#' @param x `matchms.Spectrum`. +#' +#' @param mapping the mapping of spectra variables to metadata names. +#' +#' @return `data.frame()` with the spectra data. +#' +#' @noRd +.py_matchms_spectrum_spectra_data <- + function(x, mapping = spectraVariableMapping(), ...) { + pl <- x$metadata + map <- mapping[mapping %in% names(pl)] + if (length(map)) { + ## would be nice to be able to call list(pl.items()) + res <- lapply(map, function(z) py_to_r(pl[z])) + base::as.data.frame(res[lengths(res) > 0]) + } else data.frame(msLevel = NA_integer_) + } + +#' Function to extract the peaks data as a two-column `matrix` from a +#' (**single!**) `matchms.Spectrum` object. This function can be used in a +#' loop over a `list` of objects to create a `Spectra` from it. +#' +#' @param x `matchms.Spectrum` object. +#' +#' @return `numeric` `matrix` with two columns `"mz"` and `"intensity"`. +#' +#' @noRd +.py_matchms_spectrum_peaks_data <- function(x, ...) { + m <- py_to_r(x$peaks$to_numpy) + colnames(m) <- c("mz", "intensity") + m } #' @description @@ -176,7 +381,7 @@ pyspec_to_rspec <- function(x, mapping = spectraVariableMapping(), #' #' @param x `Spectrum` Single Python Spectrum. #' -#' @param spectraVariables named `character` vector with the names of the +#' @param mapping named `character` vector with the names of the #' spectra variables that should be extracted. Names are expected to be #' the spectra variable names (in `Spectra`) and values the corresponding #' names of the variables within the Python Spectrum. @@ -193,38 +398,135 @@ pyspec_to_rspec <- function(x, mapping = spectraVariableMapping(), #' #' @importMethodsFrom Spectra Spectra #' +#' @importFrom Spectra MsBackendMemory +#' #' @noRd .single_pyspec_to_rspec <- - function(x, spectraVariables = spectraVariableMapping()) { - plist <- x$metadata - vars <- spectraVariables[spectraVariables %in% names(plist)] - if (length(vars)) { - rlist <- lapply(vars, function(z) plist[z]) - ## Drop NULL variables. - spd <- DataFrame(rlist[lengths(rlist) > 0]) - if (!nrow(spd)) - spd <- DataFrame(msLevel = NA_integer_) - } else - spd <- DataFrame(msLevel = NA_integer_) - spd$mz <- NumericList(as.numeric(x$peaks$mz), compress = FALSE) - spd$intensity <- NumericList(as.numeric(x$peaks$intensities), - compress = FALSE) - Spectra(spd) + function(x, mapping = spectraVariableMapping(), ...) { + be <- MsBackendMemory() + be@spectraData <- .py_matchms_spectrum_spectra_data( + x, mapping = mapping) + be@spectraData$dataStorage <- "" + be@peaksData <- list(.py_matchms_spectrum_peaks_data(x)) + Spectra(be) } -#' Extract all spectraData and all mz and intensity values, give them to -#' Python to create an array of Spectrum. Could be faster because loop is -#' performed in Python rather than in R. +## Note: disabling this now. Generally, it would be a nice thing to have, +## but it seems R/reticulate starts automatically converting whenever a +## variable from the py is accessed, either throug `py$var` or even with +## `py_get_attr(py, "var")` - the `iterate()` call below is then converting +## the data to a `Spectra` and passing that along to the R function. +## #' @importFrom reticulate py_to_r +## #' @export +## py_to_r.matchms.Spectrum.Spectrum <- function(x) { +## .single_pyspec_to_rspec(x) +## } + +#' @export +#' +#' @importFrom MsCoreUtils rbindFill +#' +#' @importFrom reticulate iterate +#' +#' @rdname conversion +pyspec_to_rspec <- function(x, mapping = spectraVariableMapping()) { + if (is(x, "matchms.Spectrum.Spectrum")) + return(.single_pyspec_to_rspec(x, mapping = mapping)) + be <- MsBackendMemory() + orig_mapping <- spectraVariableMapping() + setSpectraVariableMapping(mapping) + if (is.list(x)) + ITER <- lapply + else ITER <- iterate + be@peaksData <- ITER(x, .py_matchms_spectrum_peaks_data, simplify = FALSE) + ## Not very efficient and elegant... get the indivudal elements and stuff + ## into list. pandas.DataFrame can not be easily created unfortunately. + be@spectraData <- do.call( + rbindFill, ITER(x, .py_matchms_spectrum_spectra_data, simplify = FALSE)) + be@spectraData$dataStorage <- "" + setSpectraVariableMapping(orig_mapping) + Spectra(be) +} + +## below are functions that would use direct python calls that iterate in +## Python. + +#' function to create a Python command to extract the peaks data. +#' +#' @param x `character(1)` with the variable name containing the `list` of +#' `matchms.Spectrum` data. +#' +#' @return `character(1)` with the Python command. #' #' @noRd -.multi_rspec_to_pyspec <- function() { +.py_matchms_cmd_peaks_data <- function(x) { + paste0("_res_ = list()\n", + "for i in range(len(", x, ")):\n", + " _res_.append(", x, "[i].peaks.to_numpy)\n") } -#' Extract data in Python for all elements: python function should return a -#' list with the metadata (convert to DataFrame) and the m/z and intensity -#' values. Would avoid loop in R alltogether. +#' function to get the `list` of peaks matrices from a Python `list` of +#' `matchms.Spectrum` objects. This function calls a Python command. +#' +#' @note +#' +#' The returned peak matrices don't have column names. +#' +#' @param x `character(1)` with the name of the variables containing the data. +#' +#' @param local `logical(1)` passed to [reticulate::py_run_string()] +#' +#' @return a `list` of 2-column `matrix` with the m/z and intensity values. +#' Note that the matrices don't have column names. +#' +#' @importFrom reticulate py_run_string #' #' @noRd -.multi_pyspec_to_rspec <- function() { +.py_matchms_peaks_data <- function(x, local = TRUE) { + py_to_r( + py_run_string( + .py_matchms_cmd_peaks_data(x), local = local, + convert = FALSE)[["_res_"]]) } +#' function to create a Python command to loop through the variable which name +#' was provided with parameter `x` and extract and combine the metadata of all +#' `matchms.Spectrum` objects in `x` as a `pandas.DataFrame`. +#' +#' @param x `character(1)` with the name of the (Python) variable with the +#' `list` of `matchms.Spectrum` objects. +#' +#' @return `character(1)` defining the Python command. +#' +#' @noRd +.py_matchms_cmd_spectra_data <- function(x) { + paste0( + "import pandas as pd\n", + "_res_ = pd.DataFrame()\n", + "for i in range(len(", x, ")):\n", + " _res_ = pd.concat([_res_, pd.DataFrame(", x, "[i].metadata, index = [0])], ignore_index = True)\n") +} + +#' Retrieve the metadata of all `matchms.Spectrum` objects in a Python `list` +#' as a R `data.frame`. +#' +#' @param x `character(1)` with the name of the variable containing the MS data +#' in Python. +#' +#' @param local `logical(1)` passed to the [reticulate::py_run_string()] +#' function. +#' +#' @return `data.frame()` with the metadata of the MS data. +#' +#' @noRd +.py_matchms_spectra_data <- + function(x, local = TRUE, mapping = spectraVariableMapping(), ...) { + res <- py_to_r( + py_run_string( + .py_matchms_cmd_spectra_data(x), + local = local, convert = FALSE)[["_res_"]]) + mapping <- mapping[mapping %in% colnames(res)] + res <- res[, mapping] + colnames(res) <- names(mapping) + res + } diff --git a/R/filterSpectriPy.R b/R/filterSpectriPy.R index 7ebfab4..dfa0ba5 100644 --- a/R/filterSpectriPy.R +++ b/R/filterSpectriPy.R @@ -1,52 +1,101 @@ -#' @title Filter Spectra using matchms +#' @title Filter Spectra using Python's matchms library #' #' @name filterSpectriPy #' #' @description +#' #' The `filterSpectriPy()` function allows to filter/process a `Spectra` object -#' using the `select_by_intensity`, `select_by_mz`, -#' `remove_peaks_around_precursor_mz`, and `normalize_intensities` of the python +#' using the `select_by_intensity()`, `select_by_mz()`, +#' `remove_peaks_around_precursor_mz()`, and `normalize_intensities()` +#' functions of the Python #' [matchms.filtering](https://matchms.readthedocs.io/en/latest/api/matchms.filtering.html) #' module. #' -#' Selection and configuration of the algorithm can be performed with one of the -#' parameter objects (equivalent to `matchms`' function names): +#' Selection and configuration of the algorithm can be performed with one of +#' the parameter objects (equivalent to *matchms*' function names): +#' +#' - `select_by_intensity()`: Keeps only the peaks within defined intensity +#' range (keep if `intensity_from` >= intensity >= `intensity_to`). See also +#' the respective [documentation in *matchms*](https://matchms.readthedocs.io/en/latest/api/matchms.filtering.peak_processing.select_by_intensity.html). +#' +#' - `select_by_mz()`: Keeps only the peaks between `mz_from` and `mz_to` +#' (keep if `mz_from` >= m/z >= `mz_to`). See also the respective +#' [documentation in *matchms*](https://matchms.readthedocs.io/en/latest/api/matchms.filtering.peak_processing.select_by_mz.html). +#' +#' - `remove_peaks_around_precursor_mz()`: Removes the peaks that are within +#' `mz_tolerance` (in Da) of the precursor mz, excluding the precursor peak. #' -#' - `select_by_intensity`: Keeps only the peaks within defined intensity range -#' (keep if `intensity_from` >= intensity >= `intensity_to`). +#' - `normalize_intensities()`: Normalizes the intensities of peaks +#' (and losses) to unit height. #' -#' - `select_by_mz`: Keeps only the peaks between `mz_from` and `mz_to` -#' (keep if `mz_from` >= m/z >= `mz_to`). +#' @note #' -#' - `remove_peaks_around_precursor_mz`: Removes the peaks that are within -#' `mz_tolerance` (in Da) of the precursor mz, exlcuding the precursor peak. +#' The first call to the `filterSpectriPy()` function can take longer because +#' the Python environment needs to be first set up. #' -#' - `normalize_intensities`: Normalizes the intensities of peaks -#' (and losses) to unit height. +#' `filterSpectriPy()` first translates the `Spectra` to Python, applies the +#' filter functions from the *matchms* Python libraries and then translates +#' the filtered data back to a `Spectra` object. Thus, any spectra variables +#' other than those that are translated between R and Python will be lost +#' during the processing. Use [setSpectraVariableMapping()] to define which +#' spectra variables should be transferred/converted between R and Python. +#' See also examples below for more information. #' -#' @param sps A [Spectra::Spectra()] object. +#' The [Spectra::Spectra()] object returned by `filterSpectriPy()` will +#' **always** use an in-memory backend (i.e. the [Spectra::MsBackendMemory()]), +#' independently of the backend used by the backend used by the input +#' `Spectra`. +#' +#' @param object A [Spectra::Spectra()] object. #' #' @param param one of parameter classes listed above (such as -#' `select_by_intensity`) defining the filter/processing function in python -#' and its parameters. +#' `select_by_intensity()`) defining the filter/processing function in +#' Python and its parameters. +#' +#' @param mapping named `character()` defining which spectra variables/metadata +#' should be converted between R and Python and how they should be renamed. +#' Defaults to `spectraVariableMapping()`. See [setSpectraVariableMapping()] +#' for more information. +#' +#' @param intensity_from `numeric(1)`: Set lower threshold for peak intensity. +#' Default is 10. +#' +#' @param intensity_to `numeric(1)`: Set upper threshold for peak intensity. +#' Default is 200. +#' +#' @param mz_from `numeric(1)`: Set lower threshold for m/z peak positions. +#' Default is 0. +#' +#' @param mz_to `numeric(1)`: Set upper threshold for m/z peak positions. +#' Default is 1000. +#' +#' @param mz_tolerance `numeric(1)`: Tolerance of m/z values that are not +#' allowed to lie within the precursor mz. Default is 17 Da. #' #' @param ... ignored. #' -#' @return `filterSpectriPy()` returns a `Spectra` object on which the -#' filtering/processing function has been applied +#' @return `filterSpectriPy()` returns a `Spectra` object on which the +#' filtering/processing function has been applied #' #' @author Thomas Naake #' -#' @seealso [Spectra::filterIntensity()], [Spectra::filterMzRange()], -#' [Spectra::scalePeaks()] in the `Spectra` package for pure R -#' implementations of filtering/processing calculations. -#' +#' @seealso +#' +#' - [Spectra::filterIntensity()], [Spectra::filterMzRange()], +#' [Spectra::scalePeaks()] in the `Spectra` package for pure R +#' implementations of filtering/processing calculations. +#' +#' - [rspec_to_pyspec()] or [pyspec_to_rspec()] for the functions used to +#' translated the MS data between R and Python. +#' #' @export #' #' @importFrom reticulate py_run_string #' #' @examples +#' #' library(Spectra) +#' #' ## create some example Spectra #' DF <- DataFrame( #' msLevel = c(2L, 2L, 2L), @@ -63,30 +112,46 @@ #' c(109.2, 124.2, 124.5, 170.16, 170.52)) #' sps <- Spectra(DF) #' -#' ## process Spectra with matchms' select_by_intensity algorithm -#' ## note: the first filterSpectriPy will take longer because the Python -#' ## environment needs to be set up. -#' filterSpectriPy(sps, param = select_by_intensity(intensity_from=50, intensity_to=400)) -#' -#' ## Process Spectra with matchms' select_by_mz algorithm -#' filterSpectriPy(sps, param = select_by_mz(mz_from=150, mz_to=450)) -#' -#' ## Calculate pairwise similarity of all spectra in sps with matchms' -#' ## remove_peaks_around_precursor_mz algorithm -#' filterSpectriPy(sps, param = remove_peaks_around_precursor_mz(mz_tolerance=20)) -#' -#' ## Calculate pairwise similarity of all spectra in sps with matchms' -#' ## normalize_intensities algorithm -#' filterSpectriPy(sps, normalize_intensities()) +#' ## Filter: select_by_intensity +#' res <- filterSpectriPy( +#' sps, select_by_intensity(intensity_from = 15, intensity_to = 300)) +#' ## Only mass peaks with intensities between the specified limits are +#' ## retained +#' intensity(res) +#' ## Compared to the original intensities +#' intensity(sps) +#' +#' ## Note that the spectra variable `"name"` was lost during conversion of +#' ## the MS data between R and Python: +#' sps$name +#' any(spectraVariables(res) == "name") +#' +#' ## Only spectra variables defined by `spectraVariableMapping()` are +#' ## converted and thus retained: +#' spectraVariableMapping() +#' +#' ## We can also pass a custom *spectra variable mapping* with the `mapping` +#' ## parameter to the `filterSpectriPy()` function. Below we create such +#' ## a mapping by adding the translation of a spectra variable `"name"` to +#' ## a metadata name `"compound_name"` to the default spectra variable +#' ## mapping `defaultSpectraVariableMapping()`. +#' map <- c(defaultSpectraVariableMapping(), name = "compound_name") +#' map +#' +#' ## Repeat the filtering operation passing this mapping information: +#' res <- filterSpectriPy( +#' sps, select_by_intensity(intensity_from = 15, intensity_to = 300), +#' mapping = map) +#' res$name +#' NULL -setGeneric("filterSpectriPy", function(sps, param, ...) +setGeneric("filterSpectriPy", function(object, param, ...) standardGeneric("filterSpectriPy")) -#' @importClassesFrom ProtGenerics Param -#' -#' @noRd +setClass("filter_param") setClass("select_by_intensity", + contains = "filter_param", slots = c( intensity_from = "numeric", intensity_to = "numeric"), prototype = prototype( @@ -101,6 +166,7 @@ setClass("select_by_intensity", } ) setClass("select_by_mz", + contains = "filter_param", slots = c( mz_from = "numeric", mz_to = "numeric"), @@ -117,6 +183,7 @@ setClass("select_by_mz", } ) setClass("remove_peaks_around_precursor_mz", + contains = "filter_param", slots = c( mz_tolerance = "numeric"), prototype = prototype( @@ -129,6 +196,7 @@ setClass("remove_peaks_around_precursor_mz", } ) setClass("normalize_intensities", + contains = "filter_param", prototype = prototype(), validity = function(object) { msg <- NULL @@ -138,12 +206,6 @@ setClass("normalize_intensities", #' @rdname filterSpectriPy #' -#' @param intensity_from `numeric(1)`: Set lower threshold for peak intensity. -#' Default is 10. -#' -#' @param intensity_to `numeric(1)`: Set upper threshold for peak intensity. -#' Default is 200. -#' #' @importFrom methods new #' #' @export @@ -153,27 +215,18 @@ select_by_intensity <- function(intensity_from = 10, intensity_to = 200) { } #' @rdname filterSpectriPy -#' -#' @param mz_from `numeric(1)`: Set lower threshold for m/z peak positions. -#' Default is 0. -#' -#' @param mz_to `numeric(1)`: Set upper threshold for m/z peak positions. -#' Default is 1000. -#' +#' #' @export select_by_mz <- function(mz_from = 0, mz_to = 1000) { - new("select_by_mz", mz_from = as.numeric(mz_from), + new("select_by_mz", mz_from = as.numeric(mz_from), mz_to = as.numeric(mz_to)) } #' @rdname filterSpectriPy #' -#' @param mz_tolerance `numeric(1)`: Tolerance of m/z values that are not -#' allowed to lie within the precursor mz. Default is 17 Da. -#' #' @export remove_peaks_around_precursor_mz <- function(mz_tolerance = 17) { - new("remove_peaks_around_precursor_mz", + new("remove_peaks_around_precursor_mz", mz_tolerance = as.numeric(mz_tolerance)) } @@ -189,113 +242,46 @@ normalize_intensities <- function() { #' @exportMethod filterSpectriPy setMethod( "filterSpectriPy", - signature = c(sps = "Spectra", param = "select_by_intensity"), - function(sps, param, ...) { - .filter_spectra_python(sps, param) + signature = c(object = "Spectra", param = "filter_param"), + function(object, param, mapping = spectraVariableMapping(), ...) { + .filter_spectra_python(object, param, mapping = mapping) }) -#' @rdname filterSpectriPy +#' Helper method to return a SpectrumProcessor that can be applied to the +#' list of spectra in Python #' -#' @exportMethod filterSpectriPy -setMethod( - "filterSpectriPy", - signature = c(sps = "Spectra", param = "select_by_mz"), - function(sps, param, ...) { - .filter_spectra_python(sps, param) - }) - -#' @rdname filterSpectriPy -#' -#' @exportMethod filterSpectriPy -setMethod( - "filterSpectriPy", - signature = c(sps = "Spectra", param = "remove_peaks_around_precursor_mz"), - function(sps, param, ...) { - .filter_spectra_python(sps, param) - }) - -#' @rdname filterSpectriPy -#' -#' @exportMethod filterSpectriPy -setMethod( - "filterSpectriPy", - signature = c(sps = "Spectra", param = "normalize_intensities"), - function(sps, param, ...) { - .filter_spectra_python(sps, param) - }) - - -#' helper function to extract parameter settings for filtering/processing -#' functions. +#' @importFrom reticulate py_dict #' #' @noRd -.select_by_intensity_param_string <- function(x) { - paste0("intensity_from=", x@intensity_from, ", intensity_to=", x@intensity_to) -} -.select_by_mz_param_string <- function(x) { - paste0("mz_from=", x@mz_from, ", mz_to=", x@mz_to) -} -.remove_peaks_around_precursor_mz_param_string <- function(x) { - paste0("mz_tolerance=", x@mz_tolerance) -} -.normalize_intensities_param_string <- function(x) { - paste0() -} +setMethod("py_fun", "select_by_intensity", function(object) { + matchms_filtering$SpectrumProcessor$create_partial_function( + matchms_filtering$select_by_intensity, + py_dict(c("intensity_from", "intensity_to"), + c(object@intensity_from, object@intensity_to))) +}) -#' Could also define a method, but I guess that's overkill in this case. -#' -#' @noRd -.fun_name <- function(x) { - sub("Param$", "", class(x)[1L]) -} +setMethod("py_fun", "select_by_mz", function(object) { + matchms_filtering$SpectrumProcessor$create_partial_function( + matchms_filtering$select_by_mz, + py_dict(c("mz_from", "mz_to"), + c(object@mz_from, object@mz_to))) +}) -#' (internal) helper method to build the python command to perform the -#' filtering/processing. Each parameter class could (if needed) it's own implementation -#' to create the string. This methods will be called in the -#' `filter_spectra_python` function. -#' -#' Generic "python_command" defined in `compareSpectriPy.R` -#' @noRd -setMethod( - "python_command", - "select_by_intensity", - function(object, input_param = "py_spectrum_in") { - FUN <- .fun_name(object) - paste0("import matchms\n", - "from matchms.filtering import ", FUN, "\n", - "res = [", FUN, "(s, ", .select_by_intensity_param_string(object), ") for s in ", input_param, "]\n") - }) -setMethod( - "python_command", - "select_by_mz", - function(object, input_param = "py_spectrum_in") { - FUN <- .fun_name(object) - paste0("import matchms\n", - "from matchms.filtering import ", FUN, "\n", - "res = [", FUN, "(s, ", .select_by_mz_param_string(object), ") for s in ", input_param, "]\n") - }) -setMethod( - "python_command", - "remove_peaks_around_precursor_mz", - function(object, input_param = "py_spectrum_in") { - FUN <- .fun_name(object) - paste0("import matchms\n", - "from matchms.filtering import ", FUN, "\n", - "res = [", FUN, "(s, ", .remove_peaks_around_precursor_mz_param_string(object), ") for s in ", input_param, "]\n") - }) -setMethod( - "python_command", - "normalize_intensities", - function(object, input_param = "py_spectrum_in") { - FUN <- .fun_name(object) - paste0("import matchms\n", - "from matchms.filtering import ", FUN, "\n", - "res = [", FUN, "(s, ", .normalize_intensities_param_string(object), ") for s in ", input_param, "]\n") - }) +setMethod("py_fun", "remove_peaks_around_precursor_mz", function(object) { + matchms_filtering$SpectrumProcessor$create_partial_function( + matchms_filtering$remove_peaks_around_precursor_mz, + py_dict(c("mz_tolerance"), + c(object@mz_tolerance))) +}) + +setMethod("py_fun", "normalize_intensities", function(object) { + matchms_filtering$SpectrumProcessor$create_partial_function( + matchms_filtering$normalize_intensities) +}) #' internal function to filter/processing with python's matchms. `Spectra` -#' will be converted to python `Spectrum` class and matchms' processing -#' functions will be applied on the `Spectrum` objects. After processing, the +#' will be converted to python `Spectrum` class and matchms' processing +#' functions will be applied on the `Spectrum` objects. After processing, the #' matchms' `Spectrum` objects will be converted back to `Spectra` objects. #' #' @param sps `Spectra` object @@ -304,34 +290,20 @@ setMethod( #' #' @return a `Spectra` object #' -#' @importFrom basilisk basiliskStart basiliskRun basiliskStop -#' #' @importFrom reticulate py #' #' @noRd #' #' @author Thomas Naake, Johannes Rainer -.filter_spectra_python <- function(sps, param) { +.filter_spectra_python <- function(x, param, + mapping = spectraVariableMapping()) { ## handle empty input - if (!length(sps)) + if (!length(x)) return(Spectra()) - cl <- basiliskStart(matchms_env) - on.exit(basiliskStop(cl)) - - basiliskRun(cl, function(sps, param) { - ref <- import("matchms") - vars <- c(precursorMz = "precursor_mz") - py$py_spectrum_in <- rspec_to_pyspec(sps, - reference = ref, mapping = vars) - - ## run the command. Result is in py$res - com <- python_command(param) - py_run_string(com) - - ## convert from Python Spectrum to R Spectra and return - pyspec_to_rspec(py$res, mapping = vars) - - }, sps = sps, param = param) + proc <- matchms_filtering$SpectrumProcessor$SpectrumProcessor( + r_to_py(list(py_fun(param)))) + pyspec_to_rspec( + proc$process_spectra(rspec_to_pyspec(x, mapping = mapping))[0], + mapping = mapping) } - diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..79dfc62 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,70 @@ +## based on https://rstudio.github.io/reticulate/articles/package.html#delay-loading-python-modules +matchms <- NULL +matchms_similarity <- NULL +matchms_filtering <- NULL + +#' @importFrom reticulate import use_virtualenv use_condaenv py_available py_install virtualenv_exists virtualenv_create virtualenv_remove conda_list conda_create +.onLoad <- function(libname, pkgname) { + envname <- .spectripy_env() + use_conda <- .spectripy_use_conda() + use_system <- .spectripy_use_system() + if (use_conda) { + if (!(envname %in% conda_list()$name)) { + conda_create(envname) + } + use_condaenv(envname, required = TRUE) + } else if (!use_system) { + if (!virtualenv_exists(envname)) { + virtualenv_create(envname) + } + use_virtualenv(envname) + } + .install_python_packages( + envname = envname, use_conda = use_conda, use_system = use_system + ) + matchms <<- import("matchms", delay_load = TRUE, convert = FALSE) + matchms_similarity <<- import("matchms.similarity", delay_load = TRUE, + convert = FALSE) + matchms_filtering <<- import("matchms.filtering", delay_load = TRUE, + convert = FALSE) +} + +.spectripy_env <- function() { + getOption( + "spectripy.env", Sys.getenv("SPECTRIPY_ENV", unset = "r-spectripy")) +} + +.spectripy_use_conda <- function() { + getOption( + "spectripy.use_conda", + as.logical(Sys.getenv("SPECTRIPY_USE_CONDA", unset = "TRUE"))) +} + +.spectripy_use_system <- function() { + getOption( + "spectripy.use_system", + as.logical(Sys.getenv("SPECTRIPY_USE_SYSTEM", unset = "FALSE"))) +} + +#' @importFrom reticulate py_install py_module_available +.install_python_packages <- function(..., envname = .spectripy_env(), + use_conda = .spectripy_use_conda(), + use_system = .spectripy_use_system()) { + ## We don't want to modify the system Python, users are expected to manage + ## dependencies themselves. + if (use_system) { + return() + } else if (!py_module_available("matchms")) { + if (use_conda) { + py_install(c("matchms==0.28.2"), envname = envname, + method = "conda", pip = TRUE, + channels = c("conda-forge"), ...) + } else { + ## Somehow an old version of numpy gets installed and installation + ## fails in the end. + py_install(c("matchms==0.28.2"), + envname = envname, method = "virtualenv", + channels = c("conda-forge"), ...) + } + } +} diff --git a/configure b/configure deleted file mode 100755 index 9b03c32..0000000 --- a/configure +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -${R_HOME}/bin/Rscript -e "basilisk::configureBasiliskEnv()" diff --git a/configure.win b/configure.win deleted file mode 100755 index e9af497..0000000 --- a/configure.win +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe -e "basilisk::configureBasiliskEnv()" diff --git a/inst/extdata/spectra2.mgf b/inst/extdata/mgf/spectra2.mgf similarity index 100% rename from inst/extdata/spectra2.mgf rename to inst/extdata/mgf/spectra2.mgf diff --git a/inst/extdata/mgf/test.mgf b/inst/extdata/mgf/test.mgf new file mode 100644 index 0000000..3398bbe --- /dev/null +++ b/inst/extdata/mgf/test.mgf @@ -0,0 +1,4989 @@ +BEGIN IONS +TITLE=62 +msLevel=2 +PEPMASS=259.0595 +PEPMASSINT=259.0588 +CHARGE=1+ +SMILES=CC1=CC(=CC2=C1C3=CC(=CC(=C3C(=O)O2)O)O)O +INCHI=InChI=1S/C14H10O5/c1-6-2-7(15)5-11-12(6)9-3-8(16)4-10(17)13(9)14(18)19-11/h2-5,15-17H,1H3 +NAME=Alternariol +213.0546 754969.625 +241.0495 1058878.75 +259.0601 20211204 +END IONS + +BEGIN IONS +TITLE=3385 +msLevel=2 +PEPMASS=837.5318 +PEPMASSINT=837.5363 +CHARGE=1+ +SMILES=CC[C@@H]1[C@@]([C@@H]([C@H](/C(=N/OCOCCOC)/[C@@H](C[C@@]([C@@H]([C@H]([C@@H]([C@H](C(=O)O1)C)O[C@H]2C[C@@]([C@H]([C@@H](O2)C)O)(C)OC)C)O[C@H]3[C@@H]([C@H](C[C@H](O3)C)N(C)C)O)(C)O)C)C)O)(C)O +INCHI=InChI=1S/C41H76N2O15/c1-15-29-41(10,49)34(45)24(4)31(42-53-21-52-17-16-50-13)22(2)19-39(8,48)36(58-38-32(44)28(43(11)12)18-23(3)54-38)25(5)33(26(6)37(47)56-29)57-30-20-40(9,51-14)35(46)27(7)55-30/h22-30,32-36,38,44-46,48-49H,15-21H2,1-14H3/b42-31+/t22-,23-,24+,25+,26-,27+,28+,29-,30+,32-,33+,34-,35+,36-,38+,39-,40-,41-/m1/s1 +NAME=Roxithromycin +116.0712 15660 +116.1074 28164 +127.0759 8616 +142.1231 4516 +158.1184 855092 +159.1218 68248 +380.2469 6148 +398.2568 8996 +679.4443 10596 +680.4436 5176 +END IONS + +BEGIN IONS +TITLE=3646 +msLevel=2 +PEPMASS=250.1802 +PEPMASSINT=250.1799 +CHARGE=1+ +SMILES=CN(C)C[C@@H]1CCCC([C@H]1C2=CC(=CC=C2)O)O +INCHI=InChI=1S/C15H23NO2/c1-16(2)10-12-6-4-8-14(18)15(12)11-5-3-7-13(17)9-11/h3,5,7,9,12,14-15,17-18H,4,6,8,10H2,1-2H3/t12-,14?,15-/m0/s1 +NAME=O-Desmethyltramadol +232.1689 20176 +250.18 2073248 +251.1831 282228 +252.1852 21640 +END IONS + +BEGIN IONS +TITLE=3647 +msLevel=2 +PEPMASS=250.1802 +PEPMASSINT=250.18 +CHARGE=1+ +SMILES=CN(C)C[C@@H]1CCCC([C@H]1C2=CC(=CC=C2)O)O +INCHI=InChI=1S/C15H23NO2/c1-16(2)10-12-6-4-8-14(18)15(12)11-5-3-7-13(17)9-11/h3,5,7,9,12,14-15,17-18H,4,6,8,10H2,1-2H3/t12-,14?,15-/m0/s1 +NAME=O-Desmethyltramadol +145.0638 864 +187.1102 2548 +232.1687 10716 +233.1721 1756 +250.1795 72988 +251.1824 12436 +252.1862 1036 +END IONS + +BEGIN IONS +TITLE=3652 +msLevel=2 +PEPMASS=250.1802 +PEPMASSINT=250.1799 +CHARGE=1+ +SMILES=CN(C)C[C@H]1CCCC[C@@]1(C2=CC(=CC=C2)O)O +INCHI=InChI=1S/C15H23NO2/c1-16(2)11-13-6-3-4-9-15(13,18)12-7-5-8-14(17)10-12/h5,7-8,10,13,17-18H,3-4,6,9,11H2,1-2H3/t13-,15+/m1/s1 +NAME=4-Aminoantipyrine +232.1689 20176 +250.18 2073248 +251.1831 282228 +252.1852 21640 +END IONS + +BEGIN IONS +TITLE=3653 +msLevel=2 +PEPMASS=250.1802 +PEPMASSINT=250.18 +CHARGE=1+ +SMILES=CN(C)C[C@H]1CCCC[C@@]1(C2=CC(=CC=C2)O)O +INCHI=InChI=1S/C15H23NO2/c1-16(2)11-13-6-3-4-9-15(13,18)12-7-5-8-14(17)10-12/h5,7-8,10,13,17-18H,3-4,6,9,11H2,1-2H3/t13-,15+/m1/s1 +NAME=4-Aminoantipyrine +145.0638 864 +187.1102 2548 +232.1687 10716 +233.1721 1756 +250.1795 72988 +251.1824 12436 +252.1862 1036 +END IONS + +BEGIN IONS +TITLE=3784 +msLevel=2 +PEPMASS=250.1802 +PEPMASSINT=250.1799 +CHARGE=1+ +SMILES=O[C@]2(c1cc(O)ccc1)CCCC[C@@H]2CN(C)C +INCHI=InChI=1S/C15H23NO2/c1-16(2)11-13-6-3-4-9-15(13,18)12-7-5-8-14(17)10-12/h5,7-8,10,13,17-18H,3-4,6,9,11H2,1-2H3/t13-,15+/m1/s1 +NAME=N,O-Desmethyltramadol +232.1689 20176 +250.18 2073248 +251.1831 282228 +252.1852 21640 +END IONS + +BEGIN IONS +TITLE=3785 +msLevel=2 +PEPMASS=250.1802 +PEPMASSINT=250.18 +CHARGE=1+ +SMILES=O[C@]2(c1cc(O)ccc1)CCCC[C@@H]2CN(C)C +INCHI=InChI=1S/C15H23NO2/c1-16(2)11-13-6-3-4-9-15(13,18)12-7-5-8-14(17)10-12/h5,7-8,10,13,17-18H,3-4,6,9,11H2,1-2H3/t13-,15+/m1/s1 +NAME=N,O-Desmethyltramadol +145.0638 864 +187.1102 2548 +232.1687 10716 +233.1721 1756 +250.1795 72988 +251.1824 12436 +252.1862 1036 +END IONS + +BEGIN IONS +TITLE=5973 +msLevel=2 +PEPMASS=195.0877 +PEPMASSINT=102.1287 +CHARGE=1+ +SMILES=CN1C=NC2=C1C(=O)N(C)C(=O)N2C +INCHI=InChI=1S/C8H10N4O2/c1-10-4-9-6-5(10)7(13)12(3)8(14)11(6)2/h4H,1-3H3 +NAME=Caffeine +135.0432 340 +138.0632 416 +163.0375 2580 +195.088 412 +END IONS + +BEGIN IONS +TITLE=5995 +msLevel=2 +PEPMASS=100.0757 +PEPMASSINT=315.1486 +CHARGE=1+ +SMILES=CN1CCCC1=O +INCHI=InChI=1S/C5H9NO/c1-6-4-2-3-5(6)7/h2-4H2,1H3 +NAME=N-Methyl-2-pyrrolidone +55.0537 360 +56.0487 588 +58.0281 15672 +58.0644 1420 +67.0427 384 +69.0329 3996 +71.0493 368 +72.0792 544 +82.0652 440 +100.0763 13060 +END IONS + +BEGIN IONS +TITLE=7029 +msLevel=2 +PEPMASS=154.0777 +PEPMASSINT=154.0798 +CHARGE=1+ +SMILES=C1=CC=C(C=C1)C2=CC=CC=C2 +INCHI=InChI=1S/C12H10/c1-3-7-11(8-4-1)12-9-5-2-6-10-12/h1-10H +NAME=Biphenyl +152.0644 232 +153.0722 1288 +154.0795 42548 +END IONS + +BEGIN IONS +TITLE=7139 +msLevel=2 +PEPMASS=190.1352 +PEPMASSINT=190.134 +CHARGE=1+ +SMILES=CC(C)C1=CC=C(C=C1)CC(C)C=O +INCHI=InChI=1S/C13H18O/c1-10(2)13-6-4-12(5-7-13)8-11(3)9-14/h4-7,9-11H,8H2,1-3H3 +NAME=3-(4-Isopropylphenyl)isobutyraldehyde +91.0544 268 +92.0613 120 +105.0695 900 +115.0547 1400 +116.0619 140 +117.0698 3072 +118.0755 444 +119.0853 832 +128.0624 564 +129.0697 6500 +131.0854 1176 +132.0933 2188 +133.0646 772 +133.1007 14792 +142.0765 308 +145.0666 116 +145.1006 128 +146.0712 144 +147.0792 8652 +147.116 716 +149.0978 104 +157.1005 600 +175.1108 6356 +189.1263 152 +END IONS + +BEGIN IONS +TITLE=7584 +msLevel=2 +PEPMASS=175.119 +PEPMASSINT=65.0596 +CHARGE=1+ +SMILES=C(C[C@@H](C(=O)O)N)CNC(=N)N +INCHI=InChI=1S/C6H14N4O2/c7-4(5(11)12)2-1-3-10-6(8)9/h4H,1-3,7H2,(H,11,12)(H4,8,9,10)/t4-/m0/s1 +NAME=L-Arginine +68.0492 594 +70.0645 58084 +71.0484 1046 +97.0762 74 +98.0605 86 +END IONS + +BEGIN IONS +TITLE=9226 +msLevel=2 +PEPMASS=287.0574 +CHARGE=1+ +SMILES=C1(=C(C=C2C(=C1)OC(CC2=O)C3=CC=C(C(=C3)O)O)O)O +INCHI=InChI=1S/C15H12O6/c16-9-2-1-7(3-11(9)18)14-5-10(17)8-4-12(19)13(20)6-15(8)21-14/h1-4,6,14,16,18-20H,5H2 +NAME=6,7,3',4'-Tetrahydroxyflavanone +107.0142 44 +111.0093 126 +123.0093 86 +133.0299 32 +134.0363 24 +135.0455 998 +136.0499 80 +151.0039 999 +152.0079 47 +161.0253 50 +165.02 39 +169.0099 31 +199.0382 48 +215.0348 39 +223.0402 116 +224.0463 56 +225.0498 25 +241.0492 47 +245.043 27 +267.03 31 +269.0449 24 +287.0574 108 +END IONS + +BEGIN IONS +TITLE=9808 +msLevel=2 +PEPMASS=83.0604 +PEPMASSINT=83.0602 +CHARGE=1+ +SMILES=CC1=NNC=C1 +INCHI=InChI=1S/C4H6N2/c1-4-2-3-5-6-4/h2-3H,1H3,(H,5,6) +NAME=3-Methylpyrazole +56.0496 4953124 +66.0339 492171.1 +83.0603 408783104 +END IONS + +BEGIN IONS +TITLE=9809 +msLevel=2 +PEPMASS=83.0604 +PEPMASSINT=83.0602 +CHARGE=1+ +SMILES=CC1=NNC=C1 +INCHI=InChI=1S/C4H6N2/c1-4-2-3-5-6-4/h2-3H,1H3,(H,5,6) +NAME=3-Methylpyrazole +56.0496 4953124 +66.0339 492171.1 +83.0603 408783104 +END IONS + +BEGIN IONS +TITLE=10279 +msLevel=2 +PEPMASS=195.0877 +PEPMASSINT=195.0874 +CHARGE=1+ +SMILES=CN1c2ncn(C)c2C(=O)N(C)C1=O +INCHI=InChI=1S/C8H10N4O2/c1-10-4-9-6-5(10)7(13)12(3)8(14)11(6)2/h4H,1-3H3 +NAME=Caffeine +56.0496 57497.2 +69.0448 485496.1 +83.0603 316303.2 +109.0397 115988.1 +110.0712 1912954.4 +111.0553 106564.9 +123.0426 111493.1 +138.0661 14712591 +151.0977 44199.5 +156.0766 39020.4 +180.064 47539.9 +195.0876 37106960 +END IONS + +BEGIN IONS +TITLE=10423 +msLevel=2 +CHARGE=1+ +SMILES=CCCCCC(O1)C(CC=CCCCCCCCC(O)=O)1 +INCHI=InChI=1S/C18H32O3/c1-2-3-10-13-16-17(21-16)14-11-8-6-4-5-7-9-12-15-18(19)20/h8,11,16-17H,2-7,9-10,12-15H2,1H3,(H,19,20)/b11-8+ +NAME=12,13-EODE +99.12 15000 +111.04 7500 +113.07 22500 +127.12 12500 +167.04 5000 +169.04 5000 +169.2 7500 +175.12 12500 +176.96 15000 +177.14 47500 +179.12 67500 +179.28 30000 +181.2 17500 +183.116 437500 +195.118 1210000 +197.24 7500 +203.12 17500 +205.25 27500 +220.08 40000 +227.04 25000 +227.36 7500 +233.18 82500 +238.08 7500 +241.2 10000 +249.04 15000 +251.227 45000 +259.12 117500 +259.28 25000 +275.143 15000 +276.8 5000 +277.168 2345000 +277.84 5000 +293.194 82500 +295.155 1802500 +295.98 12500 +END IONS + +BEGIN IONS +TITLE=10671 +msLevel=2 +CHARGE=1+ +SMILES=CCC=CCC=CC=CC(OO)CCCCCCCC(O)=O +INCHI=InChI=1S/C18H30O4/c1-2-3-4-5-6-8-11-14-17(22-21)15-12-9-7-10-13-16-18(19)20/h3-4,6,8,11,14,17,21H,2,5,7,9-10,12-13,15-16H2,1H3,(H,19,20)/b4-3-,8-6?,14-11+ +NAME=9-HpOTrE +171.107 218750 +185.115 359375 +197.046 43750 +209.183 181250 +211.12 84375 +222.96 18750 +227.04 15625 +227.21 21875 +229.2 218750 +231.213 15625 +247.232 225000 +255.28 9375 +263.2 9375 +273.2 175000 +273.84 6250 +275.2 171875 +288.96 6250 +289.12 9375 +290.72 6250 +291.134 4103125 +309.122 1e+06 +END IONS + +BEGIN IONS +TITLE=10991 +msLevel=2 +PEPMASS=780.55 +CHARGE=1+ +SMILES=C(CCCCCC)=CCC=CCC=CCC=CCCC(=O)OC(COP(OCCN)(O)=O)COC(CCCCCCCCCCCCCCCCCC)=O +INCHI=InChI=1S/C44H80NO8P/c1-3-5-7-9-11-13-15-17-19-21-23-25-27-29-31-33-35-37-44(47)53-42(41-52-54(48,49)51-39-38-45)40-50-43(46)36-34-32-30-28-26-24-22-20-18-16-14-12-10-8-6-4-2/h13,15,19,21,25,27,31,33,42H,3-12,14,16-18,20,22-24,26,28-30,32,34-41,45H2,1-2H3,(H,48,49)/b15-13-,21-19-,27-25-,33-31- +NAME=Phosphatidylethanolamine 19:0-20:4 +259.17 530 +260.31 22 +261.24 7.5 +277.07 4 +282.73 12.6 +285.23 30.9 +285.94 6.5 +297.12 2764.7 +298.17 358.7 +303.05 4753 +304.11 440.8 +304.88 5 +317.08 9.2 +432.77 13.6 +475.92 154.4 +481.9 71.9 +494.18 1437.4 +495.26 214.6 +500.38 26 +501.17 32.3 +632.05 10.7 +698.23 40.4 +730.37 60.2 +END IONS + +BEGIN IONS +TITLE=10993 +msLevel=2 +PEPMASS=770.57 +CHARGE=1+ +SMILES=OP(=O)(OCCN)OCC(COC(=O)CCCCCCCCCCCCCCCCCCC)OC(=O)CCC=CCC=CCCCCCCCCCC +INCHI=InChI=1S/C43H82NO8P/c1-3-5-7-9-11-13-15-17-19-20-22-23-25-27-29-31-33-35-42(45)49-39-41(40-51-53(47,48)50-38-37-44)52-43(46)36-34-32-30-28-26-24-21-18-16-14-12-10-8-6-4-2/h24,26,30,32,41H,3-23,25,27-29,31,33-40,44H2,1-2H3,(H,47,48)/b26-24-,32-30- +NAME=Phosphatidylethanolamine 20:0-18:2 +243.48 9.2 +279.04 5077.7 +280.1 549.7 +311.16 2306.1 +312.28 377.3 +457.77 27.8 +458.38 22.4 +476.19 4.7 +477.08 10.9 +490.14 79.2 +508.01 891.5 +509.08 148.5 +567.67 22.5 +695.72 7.5 +END IONS + +BEGIN IONS +TITLE=15210 +msLevel=2 +PEPMASS=179.1179 +PEPMASSINT=179.1183 +CHARGE=1+ +SMILES=N(C(=O)N)c1ccc(cc1)C(C)C +INCHI=InChI=1S/C10H14N2O/c1-7(2)8-3-5-9(6-4-8)12-10(11)13/h3-7H,1-2H3,(H3,11,12,13) +NAME=Isoproturon-didemethyl +53.0384 21486.8 +61.0103 30328 +63.006 13758.8 +64.9853 41907.9 +65.0385 56963 +77.0386 638594.5 +79.001 89734.8 +79.0542 136121.8 +80.9966 26497 +83.0291 10741.8 +91.0542 571976.2 +95.0491 60386.6 +99.0072 17512.6 +103.0541 140353.4 +105.0447 161049.5 +105.0698 159942.4 +109.0447 691866.2 +115.0542 179219.7 +123.006 134008.9 +125.0216 73170.3 +127.0542 20473.4 +128.0619 35183 +139.0542 103092.3 +141.0166 65921.1 +141.0697 34029.9 +143.0117 8818.8 +143.0322 10710.9 +149.0216 43787.6 +151.0373 35205.3 +151.0542 104505.2 +152.062 597299.3 +153.0698 90605.6 +155.0603 65557.7 +157.0278 71871.7 +159.0268 17311.1 +159.0603 52549.7 +164.0619 190244.1 +165.0698 2328026.5 +166.0779 10320.3 +167.0321 108616.5 +168.0568 55242.9 +169.0477 56951.4 +169.0647 25129.5 +170.0525 167721.6 +175.0221 23507 +175.0385 12172.2 +178.0776 116437.7 +179.0602 47982.1 +179.0854 23250 +183.0603 540577.6 +184.0682 69458.3 +185.0227 27482.4 +185.076 88918.3 +196.0517 20459.5 +199.0373 27417.9 +217.0277 30805.5 +217.0477 44394.9 +219.0434 141673 +231.0429 11930.1 +235.0382 103297.7 +237.0537 20021.2 +247.0698 8451.4 +247.0812 12745.5 +END IONS + +BEGIN IONS +TITLE=15505 +msLevel=2 +PEPMASS=195.0877 +PEPMASSINT=195.088 +CHARGE=1+ +SMILES=c12c(n(c(=O)n(c1=O)C)C)ncn2C +INCHI=InChI=1S/C8H10N4O2/c1-10-4-9-6-5(10)7(13)12(3)8(14)11(6)2/h4H,1-3H3 +NAME=Caffeine +64.0066 2125.9 +65.0146 3548.5 +65.9986 19397.6 +107.0127 2796.4 +122.0362 4517.4 +139.0393 1735.5 +END IONS + +BEGIN IONS +TITLE=15506 +msLevel=2 +PEPMASS=195.0877 +PEPMASSINT=195.088 +CHARGE=1+ +SMILES=c12c(n(c(=O)n(c1=O)C)C)ncn2C +INCHI=InChI=1S/C8H10N4O2/c1-10-4-9-6-5(10)7(13)12(3)8(14)11(6)2/h4H,1-3H3 +NAME=Caffeine +122.0366 2195.4 +139.0387 2918 +146.0725 1599.3 +147.0566 3685.8 +154.0623 238054.4 +162.0676 3784 +164.083 16606.9 +179.0577 5885 +182.987 12039.3 +210.0343 30510.7 +211.0182 7247.9 +228.0448 840189.7 +254.024 700495.3 +302.1262 13130.6 +END IONS + +BEGIN IONS +TITLE=15507 +msLevel=2 +PEPMASS=195.0877 +PEPMASSINT=195.088 +CHARGE=1+ +SMILES=c12c(n(c(=O)n(c1=O)C)C)ncn2C +INCHI=InChI=1S/C8H10N4O2/c1-10-4-9-6-5(10)7(13)12(3)8(14)11(6)2/h4H,1-3H3 +NAME=Caffeine +182.0559 170834.9 +251.0165 9682.9 +325.0965 916022.1 +368.1009 13447.3 +END IONS + +BEGIN IONS +TITLE=16005 +msLevel=2 +PEPMASS=425.1871 +PEPMASSINT=425.1886 +CHARGE=1+ +SMILES=CCC[C@@H]1C[C@H](N(C1)C)C(=O)NC([C@@H]2[C@@H]([C@@H]([C@H]([C@H](O2)SC)O)O)O)C(C)Cl +INCHI=InChI=1S/C18H33ClN2O5S/c1-5-6-10-7-11(21(3)8-10)17(25)20-12(9(2)19)16-14(23)13(22)15(24)18(26-16)27-4/h9-16,18,22-24H,5-8H2,1-4H3,(H,20,25)/t9?,10-,11+,12?,13+,14-,15-,16-,18-/m1/s1 +NAME=Clindamycin +56.0494 89546.4 +72.0808 362645.4 +74.06 540296.6 +98.0964 594258.4 +116.107 1922862.3 +121.0648 415926.1 +133.0647 128834.7 +159.0804 465733.6 +165.0912 109409.6 +176.107 430206.4 +177.0911 225562.3 +191.1066 1247659 +194.1177 180271.4 +218.154 510138.4 +226.1438 796892.8 +250.18 460703.1 +268.191 34926887 +END IONS + +BEGIN IONS +TITLE=16037 +msLevel=2 +PEPMASS=285.1809 +PEPMASSINT=285.1822 +CHARGE=1+ +SMILES=CCC(CC)O[C@@H]1C=C(C[C@@H]([C@H]1NC(=O)C)N)C(=O)O +INCHI=InChI=1S/C14H24N2O4/c1-4-10(5-2)20-12-7-9(14(18)19)6-11(15)13(12)16-8(3)17/h7,10-13H,4-6,15H2,1-3H3,(H,16,17)(H,18,19)/t11-,12+,13+/m0/s1 +NAME=Oseltamivir carboxylate +65.0386 47966.7 +68.0495 5421.7 +80.0495 10807.7 +81.0447 1768 +82.0651 3573.3 +92.0495 77839.6 +93.0572 4009.8 +95.0603 8508.8 +96.0682 3146 +107.0604 9285.1 +108.0444 71829.1 +110.06 5922.5 +123.0793 2192 +124.0869 86438.6 +125.0709 9797.5 +149.0234 2578.8 +213.1136 1820.6 +END IONS + +BEGIN IONS +TITLE=16127 +msLevel=2 +PEPMASS=224.0728 +PEPMASSINT=152.0886 +CHARGE=1+ +SMILES=Fc1ccc(cc1)N(C(C)C)C(=O)C(O)=O +INCHI=InChI=1S/C11H12FNO3/c1-7(2)13(10(14)11(15)16)9-5-3-8(12)4-6-9/h3-7H,1-2H3,(H,15,16) +NAME=Flufenacet OXA +55.0544 3244 +58.065 5711.2 +59.0494 2973.1 +72.0807 86441 +83.049 151121.6 +84.0807 7047.5 +88.0756 22668.1 +98.0962 48067.4 +113.0592 3511.1 +116.0704 33570.2 +116.1068 64695.1 +158.1173 25430.8 +375.2708 3021.6 +END IONS + +BEGIN IONS +TITLE=16251 +msLevel=2 +PEPMASS=202.0854 +PEPMASSINT=202.0861 +CHARGE=1+ +SMILES=Nc1nc(NC(C)(C)C)nc(Cl)n1 +INCHI=InChI=1S/C7H12ClN5/c1-7(2,3)13-6-11-4(8)10-5(9)12-6/h1-3H3,(H3,9,10,11,12,13) +NAME=Terbutylazine-desethyl +214.0419 303083.1 +215.0498 728273.6 +250.0183 24240.9 +END IONS + +BEGIN IONS +TITLE=16293 +msLevel=2 +PEPMASS=161.022 +PEPMASSINT=161.0225 +CHARGE=1+ +SMILES=Oc1ccc(C(F)(F)F)cc1 +INCHI=InChI=1S/C7H5F3O/c8-7(9,10)5-1-3-6(11)4-2-5/h1-4,11H +NAME=4-Trifluoromethylphenol +105.07 3096.9 +149.0954 3848.5 +161.1324 108381.7 +END IONS + +BEGIN IONS +TITLE=16385 +msLevel=2 +PEPMASS=296.1412 +PEPMASSINT=296.1418 +CHARGE=1+ +SMILES=ClCC(=O)N(CCOCC)\C(=C(\C)C)c1ccccc1 +INCHI=InChI=1S/C16H22ClNO2/c1-4-20-11-10-18(15(19)12-17)16(13(2)3)14-8-6-5-7-9-14/h5-9H,4,10-12H2,1-3H3 +NAME=Pethoxamide +179.0729 7064.8 +180.0807 226833.3 +181.0885 110898.3 +182.0599 4528.5 +183.0677 24047.4 +194.0596 3024 +194.0965 24793.5 +195.0678 6575 +196.0755 16405 +196.1114 5366.4 +204.0803 4169.5 +206.0971 3592.8 +207.0681 9754.3 +208.0756 249576.6 +209.0835 1658629.6 +222.0912 66071.5 +223.0994 45110.7 +224.107 931846.4 +END IONS + +BEGIN IONS +TITLE=17270 +msLevel=2 +PEPMASS=239.1503 +PEPMASSINT=239.1506 +CHARGE=1+ +SMILES=n(c(c(C)c1OC(=O)N(C)C)C)c(n1)N(C)C +INCHI=InChI=1S/C11H18N4O2/c1-7-8(2)12-10(14(3)4)13-9(7)17-11(16)15(5)6/h1-6H3 +NAME=Pirimicarb +149.9752 13594.9 +185.9517 35734.9 +231.0094 5097.2 +END IONS + +BEGIN IONS +TITLE=18543 +msLevel=2 +PEPMASS=990.9768 +PEPMASSINT=413.2651 +CHARGE=1+ +SMILES=FC(F)(CCOP(=O)(OCCC(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)F)O)C(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)C(F)(F)F +INCHI=InChI=1S/C20H9F34O4P/c21-5(22,7(25,26)9(29,30)11(33,34)13(37,38)15(41,42)17(45,46)19(49,50)51)1-3-57-59(55,56)58-4-2-6(23,24)8(27,28)10(31,32)12(35,36)14(39,40)16(43,44)18(47,48)20(52,53)54/h1-4H2,(H,55,56) +NAME=Bisperfluorodecyl phosphate +58.0651 3035338.1 +67.0545 13900.5 +79.0543 53009.3 +81.0698 130950.7 +107.0492 1119197 +121.0646 65929.3 +133.0648 721451.9 +145.0649 155665.8 +159.0804 274671.3 +163.099 30443.4 +164.107 38266.9 +173.0957 16387.3 +201.1272 94726.3 +END IONS + +BEGIN IONS +TITLE=20114 +msLevel=2 +PEPMASS=239.0673 +PEPMASSINT=239.0675 +CHARGE=1+ +SMILES=[O-][N+](=O)c1cc(cc(c1O)C(C)(C)C)[N+]([O-])=O +INCHI=InChI=1S/C10H12N2O5/c1-10(2,3)7-4-6(11(14)15)5-8(9(7)13)12(16)17/h4-5,13H,1-3H3 +NAME=Dinoterb +121.0649 88851.7 +147.0804 422239.3 +159.0805 145717.4 +171.0796 65512.2 +199.1116 26405.7 +213.1273 985539.8 +215.1428 2372683.1 +241.159 80265.8 +272.2009 12086127.8 +END IONS + +BEGIN IONS +TITLE=24015 +msLevel=2 +PEPMASS=163.0314 +PEPMASSINT=163.0315 +CHARGE=1+ +SMILES=OC(=O)C1=CNN=C1C(F)F +INCHI=InChI=1S/C5H4F2N2O2/c6-4(7)3-2(5(10)11)1-8-9-3/h1,4H,(H,8,9)(H,10,11) +NAME=Fluxapyroxad (BAS 700 F)-TP CSCD465008 +55.0302 147998.8 +61.9707 113588.8 +64.0068 470637.7 +65.0146 3575229 +66.0099 11387463 +107.0365 127721.1 +126.9052 810873.4 +END IONS + +BEGIN IONS +TITLE=24898 +msLevel=2 +PEPMASS=303.1339 +PEPMASSINT=65.0597 +CHARGE=1+ +SMILES=COC1=CC2=CC=CN=C2C(=C1)N(C=O)C(C)CCC(O)=O +INCHI=InChI=1S/C16H18N2O4/c1-11(5-6-15(20)21)18(10-19)14-9-13(22-2)8-12-4-3-7-17-16(12)14/h3-4,7-11H,5-6H2,1-2H3,(H,20,21) +NAME=PRI_303.1340_15.8 +51.0228 1789.3 +64.9786 3363.9 +75.0263 8724.9 +77.0385 8570 +96.9506 24592 +104.0496 25238.5 +105.0336 40458.5 +109.9825 1876.3 +114.9614 506373.2 +120.0447 3602 +124.9821 52256 +126.9435 11230.6 +128.977 21685.3 +132.0445 1293833 +135.9976 3088.6 +136.022 15038.5 +137.0056 153490.3 +138.0138 30611.9 +142.9387 390585 +148.0217 76388.3 +150.0551 118066.6 +152.9829 12236.5 +153.0135 229938.6 +160.0507 795983.5 +164.0706 11196.8 +166.0323 173521.5 +170.9699 718810.4 +171.0241 450585.7 +197.009 4263 +199.0013 200993.4531 +216.9719 12062.2 +232.9493 1009545 +260.9807 1060373.2 +261.9759 99609.4 +289.0119 282399.2813 +END IONS + +BEGIN IONS +TITLE=26318 +CHARGE=1+ +SMILES=CCOC(=O)C(C=1)=C(C(COC)=2)C(=C(COC)C=CC2)C(C#N)1 +INCHI=InChI=1S/C18H19NO4/c1-4-23-18(20)15-8-14(9-19)16-12(10-21-2)6-5-7-13(11-22-3)17(15)16/h5-8H,4,10-11H2,1-3H3 +NAME=ETHYL 3-CYANO-4,8-BIS(METHOXYMETHYL)AZULENE-1-CARBOXYLATE +41 23 +42 9.5 +43 99.99 +44 0.95 +45 31 +52 6 +53 10.5 +54 0.4 +55 35 +56 6 +57 25 +58 0.65 +59 6 +67 4.5 +68 6 +69 1.7 +70 16 +71 74 +72 9 +73 1.9 +79 7 +81 7 +82 13 +83 0.5 +84 4 +85 8 +86 16.5 +87 2.35 +88 5.5 +91 4.5 +95 5 +96 0.9 +97 7 +98 6 +99 48 +100 0.9 +101 6 +105 5 +109 6 +110 0.7 +113 5.5 +114 4.5 +115 11 +117 1.2 +123 6 +124 7 +125 10 +126 0.85 +127 59 +128 7 +129 6 +136 0.5 +137 5 +138 5 +142 6 +143 0.4 +144 24 +145 11 +149 16.5 +150 0.55 +151 4.5 +152 5 +153 6 +154 1.1 +155 15 +161 8 +165 5 +166 0.6 +167 5 +169 4.5 +170 6 +171 1.9 +172 3.5 +173 43 +174 6 +187 0.6 +189 9.5 +195 4.5 +196 4.5 +198 0.45 +199 39 +200 15 +201 8 +208 0.4 +209 4.5 +210 7 +216 5.5 +223 0.4 +226 7 +227 20 +228 4 +240 0.35 +241 4 +245 17 +246 7 +247 0.4 +272 12 +273 22 +313 2.5 +END IONS + +BEGIN IONS +TITLE=28683 +CHARGE=1+ +SMILES=COc(c2)cc(O1)c(c2)C=C(OC)C(=O)1 +INCHI=InChI=1S/C11H10O4/c1-13-8-4-3-7-5-10(14-2)11(12)15-9(7)6-8/h3-6H,1-2H3 +NAME=3,7-DIMETHOXYCOUMARIN +92 9 +119 2 +120 2 +135 26 +149 6 +161 3 +163 85 +164 8 +177 20 +191 2 +192 3 +205 3 +206 99.99 +207 10 +END IONS + +BEGIN IONS +TITLE=29635 +CHARGE=1+ +SMILES=O=C(C=2)Oc(c1)c(C2)cc(O)c(O)1 +INCHI=InChI=1S/C9H6O4/c10-6-3-5-1-2-9(12)13-8(5)4-7(6)11/h1-4,10-11H +NAME=6,7-DIHYDROXYCOUMARIN +38 4.3 +39 6.7 +40 1.9 +42 4.3 +43 1.8 +47 2.8 +49 1.7 +50 1.08 +51 14.1 +52 6.1 +53 9.6 +54 0.16 +55 1.8 +61 2 +62 2.3 +63 0.29 +64 1.3 +65 5.1 +66 7.3 +69 1.7 +70 1.8 +74 2.1 +75 10.3 +76 1.04 +77 2.4 +79 7.5 +80 4.6 +81 0.31 +89 2.2 +93 1.2 +94 7.8 +104 0.58 +105 1.1 +108 1.4 +109 1.1 +121 0.61 +122 2.9 +132 3.1 +149 2.7 +150 7.24 +151 6.7 +177 3.2 +178 99.99 +179 1.03 +180 1.3 +END IONS + +BEGIN IONS +TITLE=30293 +CHARGE=1+ +SMILES=CC(C)(C)[Si](C)(C)O +INCHI=InChI=1S/C6H16OSi/c1-6(2,3)8(4,5)7/h7H,1-5H3 +NAME=TERT-BUTYLDIMETHYLSILANOL +41 3.08 +43 1.46 +45 4.39 +47 3.59 +56 1.97 +57 1.25 +60 1.04 +61 2.19 +74 3.79 +75 99.99 +76 7.88 +77 4.02 +132 4.73 +END IONS + +BEGIN IONS +TITLE=31207 +CHARGE=1+ +SMILES=O=C(C2)C(CCC2)c(c1)cccc1 +INCHI=InChI=1S/C12H14O/c13-12-9-5-4-8-11(12)10-6-2-1-3-7-10/h1-3,6-7,11H,4-5,8-9H2 +NAME=2-PHENYLCYCLOHEXANONE +27 4.36 +28 1.87 +29 2.26 +39 9.8 +40 1.86 +41 8.42 +42 2.29 +50 1.92 +51 6.44 +53 1.02 +55 11.08 +56 1.01 +62 1.03 +63 2.81 +65 4.83 +66 1.09 +67 4.21 +68 2.04 +70 4.52 +76 8.08 +77 8.99 +78 3.12 +83 3.82 +89 2.72 +90 1.7 +91 27.34 +92 4.18 +97 3.34 +98 34.28 +99 2.65 +102 1.09 +103 6.86 +104 28.45 +105 7.63 +115 15.64 +116 2.83 +117 46.31 +118 9.42 +127 1.61 +128 2.48 +129 11.5 +130 99.99 +131 14.27 +132 1.9 +133 1.02 +145 2.35 +146 1.24 +156 1.14 +174 32.68 +175 4.04 +176 1 +END IONS + +BEGIN IONS +TITLE=31954 +CHARGE=1+ +SMILES=CCCCCCCCOC(=O)CCC +INCHI=InChI=1S/C12H24O2/c1-3-5-6-7-8-9-11-14-12(13)10-4-2/h3-11H2,1-2H3 +NAME=OCTYL BUTYRATE +15 1.05 +26 1.1 +27 26.74 +28 7.76 +29 27.75 +31 1.67 +39 11.61 +40 2.52 +41 56.35 +42 24.34 +43 99.99 +44 3.77 +45 1.31 +53 1.9 +54 2.88 +55 32.18 +56 36.86 +57 29.37 +58 1.68 +60 5.82 +61 2.96 +67 2.28 +68 5.67 +69 22.75 +70 40.69 +71 86.44 +72 2.86 +73 3.1 +82 5.16 +83 24.8 +84 21.92 +85 1.48 +88 5.14 +89 63.64 +90 2.67 +97 2.35 +101 1.74 +112 19.35 +113 1.18 +129 1.05 +END IONS + +BEGIN IONS +TITLE=32523 +CHARGE=1+ +SMILES=CC(C)COC(=O)C(C)C +INCHI=InChI=1S/C8H16O2/c1-6(2)5-10-8(9)7(3)4/h6-7H,5H2,1-4H3 +NAME=ISOBUTYL ISOBUTYRATE +15 1.52 +26 1.35 +27 21.71 +28 3.98 +29 19.76 +31 1.57 +39 15.87 +40 2.42 +41 45.44 +42 8.92 +43 91.71 +44 3.24 +55 4.3 +56 45.68 +57 38.49 +58 1.41 +70 2.18 +71 99.99 +72 4.66 +73 4.51 +88 1.65 +89 24.48 +90 1.09 +101 6.42 +END IONS + +BEGIN IONS +TITLE=33054 +CHARGE=1+ +SMILES=CC(CC(=O)O[Si](C)(C)C)C(=O)O[Si](C)(C)C +INCHI=InChI=1S/C11H24O4Si2/c1-9(11(13)15-17(5,6)7)8-10(12)14-16(2,3)4/h9H,8H2,1-7H3 +NAME=METHYLSUCCINIC ACID BIS(TRIMETHYLSILYL) ESTER +41 1 +42 1.3 +43 3.7 +44 0.44 +45 16.4 +46 1 +47 7.1 +55 2.91 +56 1.3 +57 1.3 +58 2.9 +59 0.39 +61 3.9 +69 15.8 +70 6.8 +71 0.1 +72 5.2 +73 99.99 +74 12.1 +75 3.06 +76 2.3 +77 3.9 +99 1.3 +117 0.31 +123 5 +129 4.4 +130 1 +131 0.21 +133 4.2 +143 7.1 +145 1 +147 1.71 +148 25.9 +149 14.5 +150 1 +158 0.21 +186 6.8 +187 2.6 +190 1.3 +204 0.21 +217 16.4 +218 2.3 +219 1 +232 0.97 +233 1.5 +238 1.5 +261 34.1 +262 0.89 +263 2.9 +276 0.7 +END IONS + +BEGIN IONS +TITLE=33668 +CHARGE=1+ +SMILES=CCCCCCCCCCCCCCCCOC +INCHI=InChI=1S/C17H36O/c1-3-4-5-6-7-8-9-10-11-12-13-14-15-16-17-18-2/h3-17H2,1-2H3 +NAME=1-METHOXYHEXADECANE +33 1.94 +35 0.11 +36 0.09 +39 2.79 +41 25.13 +42 8.84 +43 50.3 +45 99.99 +46 2.15 +48 0.3 +53 2.16 +54 8.25 +55 55.22 +56 40.28 +57 60.22 +58 4.06 +67 9.37 +68 41.21 +69 71.79 +70 47.18 +71 28.91 +72 2.53 +81 5.77 +82 50.03 +83 57 +84 35.74 +95 3.23 +96 17.17 +97 68.31 +98 23.51 +99 5.28 +109 0.72 +110 8.48 +111 38.86 +112 19.59 +113 4.05 +118 0.38 +123 1.06 +124 4.59 +125 19.8 +126 11.87 +127 2.96 +137 2.05 +138 3.04 +139 7.64 +140 8.48 +141 2.34 +152 2.49 +153 4.11 +154 7.18 +167 2.43 +168 6.37 +196 13.05 +197 2.07 +207 0.69 +208 1.08 +224 17.09 +225 3.26 +END IONS + +BEGIN IONS +TITLE=34672 +CHARGE=1+ +SMILES=CCCCCCCCCCCCCCCCCCCCC +INCHI=InChI=1S/C21H44/c1-3-5-7-9-11-13-15-17-19-21-20-18-16-14-12-10-8-6-4-2/h3-21H2,1-2H3 +NAME=HENICOSANE +27 6.4 +29 18.42 +39 3.87 +41 36.32 +42 9.47 +43 87.01 +44 2.51 +53 1.25 +54 2.23 +55 26.74 +56 13.48 +57 99.99 +58 3.82 +67 2.23 +68 2 +69 12.38 +70 10.71 +71 58.26 +72 2.95 +82 1.33 +83 7.88 +84 6.01 +85 39 +86 1.95 +97 4.95 +98 3.75 +99 10.03 +111 1.85 +112 2.48 +113 5.12 +126 1.73 +127 3.27 +140 1.15 +141 2.6 +155 1.7 +169 1.6 +183 1 +296 1.11 +END IONS + +BEGIN IONS +TITLE=35736 +CHARGE=1+ +SMILES=C(N(C4)CC(C5=O)C4C(N(c(c6)ccc(C)c6)5)=O)N(C1)CC(C2=O)C1C(N(c(c3)ccc(C)c3)2)=O +INCHI=InChI=1S/C27H28N4O4/c1-16-3-7-18(8-4-16)30-24(32)20-11-28(12-21(20)25(30)33)15-29-13-22-23(14-29)27(35)31(26(22)34)19-9-5-17(2)6-10-19/h3-10,20-23H,11-15H2,1-2H3 +NAME=5,5'-METHYLENEDI(2-PARA-TOLYLPERHYDROPYRROLO(3,4-C)PYRROLE-1,3-DIONE) +39 7.83 +40 12.34 +41 25.49 +42 29.13 +43 41.52 +51 5.85 +53 10.56 +54 5.95 +55 36.74 +57 6.25 +65 7.18 +67 7.97 +68 88.03 +69 8.16 +78 6.23 +80 20.32 +81 42.8 +82 16.22 +91 11.94 +96 23.58 +97 6.27 +104 9.94 +107 6.33 +132 13.45 +133 15.56 +160 6.87 +187 16.13 +230 8.75 +242 10.35 +243 99.99 +244 18.04 +END IONS + +BEGIN IONS +TITLE=36653 +CHARGE=1+ +SMILES=COC(=O)C(C)c(c1)ccc(c1)CC(C)(C)O[Si](C)(C)C +INCHI=InChI=1S/C17H28O3Si/c1-13(16(18)19-4)15-10-8-14(9-11-15)12-17(2,3)20-21(5,6)7/h8-11,13H,12H2,1-7H3 +NAME=METHYL 2-(4'-(2-TRIMETHYLSILYLOXY-2-METHYLPROPYL)PHENYL)PROPIONATE +52 1.1 +59 1.23 +73 13.74 +74 1.11 +75 4.29 +79 4.9 +91 2.08 +116 3.12 +118 5.71 +119 3.6 +131 99.99 +132 12.31 +133 4.19 +147 2.14 +159 3.3 +161 1.49 +178 5 +250 12.21 +251 2.09 +293 2.81 +END IONS + +BEGIN IONS +TITLE=37854 +CHARGE=1+ +SMILES=CC(C)c(c1)cc(C(C)C)cc(C(C)C)1 +INCHI=InChI=1S/C15H24/c1-10(2)13-7-14(11(3)4)9-15(8-13)12(5)6/h7-12H,1-6H3 +NAME=1,3,5-TRIISOPROPYLBENZENE +121 6.72 +149 3.74 +161 2.37 +163 57.37 +164 7.27 +189 65 +190 9.88 +191 36.95 +192 5.83 +203 77.37 +204 29.62 +205 99.99 +206 16.42 +207 1.3 +END IONS + +BEGIN IONS +TITLE=38111 +msLevel=2 +PEPMASSINT=285.0408 +CHARGE=1+ +SMILES=O([C@H]5C)[C@H]([C@@H]([C@@H]([C@H]5O)O)O)O[C@H]([C@H]4O)[C@@H](O[C@@H]([C@H]4O)CO)Oc(c3)cc(O1)c(c3O)C(C(=C(c(c2)ccc(O)c2)1)O)=O +INCHI=InChI=1S/C27H30O15/c1-9-17(31)20(34)23(37)26(38-9)42-25-21(35)18(32)15(8-28)41-27(25)39-12-6-13(30)16-14(7-12)40-24(22(36)19(16)33)10-2-4-11(29)5-3-10/h2-7,9,15,17-18,20-21,23,25-32,34-37H,8H2,1H3/t9-,15+,17-,18+,20+,21-,23+,25+,26-,27+/m0/s1 +NAME=Kaempferol-7-neohesperidoside +284.0332 545 +285.0408 5908 +286.0445 1125 +287.0474 173 +429.0839 123 +593.1512 780 +594.1551 295 +END IONS + +BEGIN IONS +TITLE=39875 +msLevel=2 +PEPMASS=296.1467 +PEPMASSINT=296.1468 +CHARGE=1+ +SMILES=CN(C)CCC=C1C2=CC=CC=C2CSC3=CC=CC=C31 +INCHI=InChI=1S/C19H21NS/c1-20(2)13-7-11-17-16-9-4-3-8-15(16)14-21-19-12-6-5-10-18(17)19/h3-6,8-12H,7,13-14H2,1-2H3 +NAME=Dosulepin +70.0648 29739 +72.0806 29641.9 +84.0807 98412.8 +91.0542 1051365.8 +103.0542 30011.7 +115.0542 451324.7 +117.0699 2201274.2 +123.0263 928949 +128.062 26614 +129.0698 333098.1 +135.0263 182204.2 +141.0699 279472.8 +142.0777 175392.7 +147.0262 738526.3 +165.0699 646899.9 +173.042 241612.9 +178.0777 1076870.2 +179.0855 443866.8 +190.0784 41370.3 +191.0855 625351.6 +192.0933 870101.2 +197.0421 42873.6 +202.0777 611996.7 +203.0855 2824895.5 +204.0931 178953.8 +205.1007 54677.6 +208.0334 76702 +209.0419 585741.3 +210.0497 969600.1 +211.0566 48516.6 +215.0855 114530.7 +216.0936 116366.5 +217.1012 2090960.5 +218.109 2873573.8 +221.0419 1822852.5 +222.0497 1208738.8 +223.0575 3137837 +225.0732 434986.2 +234.0495 145869.2 +235.0575 491154.1 +236.0654 518138.3 +249.0739 62442.1 +251.0892 43124.7 +END IONS + +BEGIN IONS +TITLE=40782 +msLevel=2 +PEPMASS=205.1911 +CHARGE=1+ +SMILES=CC[C@@H](CO)NCCN[C@@H](CC)CO +INCHI=InChI=1S/C10H24N2O2/c1-3-9(7-13)11-5-6-12-10(4-2)8-14/h9-14H,3-8H2,1-2H3/t9-,10-/m0/s1 +NAME=Ethambutol +53.0395 965544.4 +55.0551 7054821.6 +56.0504 2762764 +62.0609 3669331 +69.0707 1620661.8 +70.0659 2028822.9 +72.0816 3124304.9 +74.0608 3522168.4 +79.0549 296759.3 +81.0706 1040061.9 +90.0921 1317349.4 +98.097 9364114.8 +116.1074 187229872 +END IONS + +BEGIN IONS +TITLE=43635 +msLevel=2 +PEPMASS=317.212188720703 +PEPMASSINT=317.212188720703 +CHARGE=1+ +SMILES=C(\CC)=C\C/C=C\C/C=C\C\C=C/C=C/C(CCCC(=O)O)O +INCHI=InChI=1S/C20H30O3/c1-2-3-4-5-6-7-8-9-10-11-12-13-14-16-19(21)17-15-18-20(22)23/h3-4,6-7,9-10,12-14,16,19,21H,2,5,8,11,15,17-18H2,1H3,(H,22,23)/b4-3-,7-6-,10-9-,13-12-,16-14+ +NAME=5-HEPE +59.0136828133452 4299.7585 +71.0134210483509 233.01755 +93.0711216809144 394.382 +115.040279884789 3911.37076 +121.102669669371 467.119313333333 +201.165600719695 297.6476 +255.211679121424 682.075145 +299.201519442928 243.036345 +END IONS + +BEGIN IONS +TITLE=44271 +msLevel=2 +PEPMASS=216 +CHARGE=1+ +SMILES=NCCCC[C@@H](C(O)=O)NC(=O)CCN +INCHI=InChI=1S/C9H19N3O3/c10-5-2-1-3-7(9(14)15)12-8(13)4-6-11/h7H,1-6,10-11H2,(H,12,13)(H,14,15)/t7-/m0/s1 +NAME=b-Ala-Lys +41.3 103960.5 +41.9 143564.5 +58.2 386139 +67.7 39604 +70.8 19802 +78.9 39604 +86.8 9901 +97 133663.5 +144.9 188119 +END IONS + +BEGIN IONS +TITLE=44411 +msLevel=2 +PEPMASS=207 +CHARGE=1+ +SMILES=OC(=O)CC(C(O)=O)Cc(c1)cccc1 +INCHI=InChI=1S/C11H12O4/c12-10(13)7-9(11(14)15)6-8-4-2-1-3-5-8/h1-5,9H,6-7H2,(H,12,13)(H,14,15)/t9-/m1/s1 +NAME=Benzyl succinate +41.3 29703 +59.1 2306933 +70.7 89109 +78.7 341584.5 +91.1 15797045.5 +92.3 118812 +98.1 3712875 +102.8 24752.5 +114.7 193069.5 +117.2 960397 +119.2 2371289.5 +125 74257.5 +127.2 64356.5 +129.7 198020 +142.9 321782.5 +145.2 3376241 +147 108911 +149.3 39604 +163.1 21311902.5 +189.1 529703.5 +192 24752.5 +205.1 39604 +207.3 816832.5 +END IONS + +BEGIN IONS +TITLE=45320 +msLevel=2 +PEPMASS=125 +CHARGE=1+ +SMILES=OCCS(O)(=O)=O +INCHI=InChI=1S/C2H6O4S/c3-1-2-7(4,5)6/h3H,1-2H2,(H,4,5,6) +NAME=Isethionate +64.7 39604 +80.1 6361392.5 +95.2 84158.5 +106.7 64356.5 +END IONS + +BEGIN IONS +TITLE=46959 +msLevel=2 +PEPMASS=164 +CHARGE=1+ +SMILES=CCSCC[C@H](N)C(O)=O +INCHI=InChI=1S/C6H13NO2S/c1-2-10-4-3-5(7)6(8)9/h5H,2-4,7H2,1H3,(H,8,9)/t5-/m0/s1 +NAME=Ethionine +47 64356.5 +55.3 54455.5 +56.2 262376.5 +73.3 14851.5 +74.9 262376.5 +89.7 14851.5 +91.8 44554.5 +END IONS + +BEGIN IONS +TITLE=47123 +msLevel=2 +PEPMASS=256 +CHARGE=1+ +SMILES=OCC(CO)OCn(c2)c(N=1)c(n2)C(=O)NC(N)1 +INCHI=InChI=1S/C9H13N5O4/c10-9-12-7-6(8(17)13-9)11-3-14(7)4-18-5(1-15)2-16/h3,5,15-16H,1-2,4H2,(H3,10,12,13,17) +NAME=Ganciclovir +44.2 24752.5 +57.1 94059.5 +70.1 49505 +72.9 64356.5 +74.6 9901 +81.2 29703 +86.8 29703 +90.9 559406.5 +93.2 54455.5 +94.1 49505 +96.9 24752.5 +98.4 14851.5 +104.9 89109 +107.6 54455.5 +109 89109 +110.3 381188.5 +117.9 24752.5 +119.1 123762.5 +120.6 29703 +125 193069.5 +126.3 19802 +133.9 34653.5 +135.2 945545.5 +142.3 321782.5 +148.8 29703 +152.2 1004951.5 +153.3 118812 +162.8 29703 +165.4 14851.5 +END IONS + +BEGIN IONS +TITLE=47328 +msLevel=2 +PEPMASS=138 +CHARGE=1+ +SMILES=NNC(=O)c(c1)ccnc1 +INCHI=InChI=1S/C6H7N3O/c7-9-6(10)5-1-3-8-4-2-5/h1-4H,7H2,(H,9,10) +NAME=Isonicotinate hydrazide +43.9 14851.5 +58.7 69307 +60.2 178218 +61.1 89109 +62.6 34653.5 +71.2 14851.5 +74.1 871288 +78 8450503.5 +79.2 39604 +79.8 29703 +83.1 84158.5 +84 84158.5 +85 59406 +86.6 247525 +88.1 202970.5 +89.1 306931 +92.8 64356.5 +102.9 212871.5 +105.2 94059.5 +106.1 10079218 +106.9 39604 +108.1 272277.5 +109.2 79208 +109.9 34653.5 +120.3 148515 +121.3 18123780.5 +129.9 19802 +137.1 158416 +138.2 98247623 +158.4 19802 +172.5 89109 +END IONS + +BEGIN IONS +TITLE=48131 +msLevel=2 +PEPMASS=256 +CHARGE=1+ +SMILES=Nc(c2)ccc(c2)S(=O)(=O)Nc(n1)scc1 +INCHI=InChI=1S/C9H9N3O2S2/c10-7-1-3-8(4-2-7)16(13,14)12-9-11-5-6-15-9/h1-6H,10H2,(H,11,12) +NAME=Sulfathiazole +57 34653.5 +58.8 24752.5 +64.9 797030.5 +69.1 44554.5 +73.1 64356.5 +80 227723 +85.4 29703 +90.9 252475.5 +92.1 10029713 +95 54455.5 +98.1 29703 +101.3 767327.5 +105.3 74257.5 +108.1 6099016 +109.3 34653.5 +110 163366.5 +119.3 163366.5 +136.8 19802 +139.9 19802 +142.4 247525 +145.9 9901 +156.1 391089.5 +178.5 14851.5 +190.5 29703 +END IONS + +BEGIN IONS +TITLE=48941 +msLevel=2 +PEPMASS=436.946 +PEPMASSINT=436.9459 +CHARGE=1+ +SMILES=Clc1cc(C(F)(F)F)cc(Cl)c1N2C(N)=C(S(=O)C(F)(F)F)C(C#N)=N2 +INCHI=InChI=1S/C12H4Cl2F6N4OS/c13-5-1-4(11(15,16)17)2-6(14)8(5)24-10(22)9(7(3-21)23-24)26(25)12(18,19)20/h1-2H,22H2 +NAME=FIPRONIL +319.9818 257.5 +351.9703 166.9 +367.9248 111958.6 +388.9334 198.2 +418.9423 176.7 +END IONS + +BEGIN IONS +TITLE=49203 +msLevel=2 +PEPMASS=866.61 +PEPMASSINT=866.4878 +CHARGE=1+ +SMILES=O(C(OCC(C)=CCC(C2)C(C)(C)C(C=CC(C)=CC=CC(C)=CC=CC=C(C=CC=C(C=CC(C3=C)C(C)(C)C(CC3)CC=C(C)CO)C)C)C(=C)C2)1)C(C(C(O)C1O)O)CO +INCHI=InChI=1S/C56H82O7/c1-38(19-15-21-40(3)25-33-48-44(7)27-31-46(55(48,9)10)29-23-42(5)35-57)17-13-14-18-39(2)20-16-22-41(4)26-34-49-45(8)28-32-47(56(49,11)12)30-24-43(6)37-62-54-53(61)52(60)51(59)50(36-58)63-54/h13-26,33-34,46-54,57-61H,7-8,27-32,35-37H2,1-6,9-12H3/b14-13+,19-15+,20-16+,33-25+,34-26+,38-17+,39-18+,40-21+,41-22+,42-23+,43-24+/t46-,47-,48+,49+,50+,51+,52-,53+,54+/m0/s1 +NAME=Sarcinaxanthin monoglucoside +3.6358 0.03 +11.834 0.01 +13.9642 0.01 +15.0079 0.03 +17.8477 0.01 +18.9925 0.1 +22.9629 0.73 +25.9847 0.14 +27.0006 0.36 +27.9794 0.07 +29.0058 0.81 +30.9982 0.41 +32.622 0.02 +36.9866 0.02 +37.9669 0.02 +38.9884 0.97 +39.979 0.14 +41.0121 3.01 +42.0512 0.01 +43.0041 6.3 +43.9951 0.07 +45.0166 1.45 +46.9887 0.12 +49.9585 0.09 +50.651 0.01 +50.9832 0.42 +52.0076 0.17 +53.0005 1.77 +54.0085 0.16 +55.0175 3.15 +55.2122 0.02 +56.0417 0.24 +56.6004 0.01 +57.0077 2.72 +58.0091 0.12 +58.9903 0.42 +60.0404 0.04 +60.9858 1.9 +61.8804 0.04 +61.9816 0.05 +62.9919 0.56 +63.962 0.12 +65.007 1 +65.9711 0.2 +67.0132 2.48 +67.5372 0.05 +68.0083 0.15 +69.0197 7.95 +69.958 0.09 +70.0608 0.05 +71.0034 0.9 +72.0088 0.03 +72.9869 3.88 +74.0006 0.42 +74.8553 0.13 +74.9776 0.19 +75.3155 0.09 +75.9963 0.14 +76.5604 0.04 +76.7809 0.04 +77.0008 1.79 +77.4138 0.16 +77.9901 0.28 +79.0183 3.09 +79.5573 0.06 +80.0011 0.63 +81.0268 5.43 +81.8524 0.01 +82.0935 0.14 +83.0108 1.68 +83.7492 0.03 +83.984 0.12 +84.2667 0.1 +84.9972 6.68 +86.0138 0.15 +86.9975 3.7 +88.0175 0.14 +88.9591 0.43 +89.1367 0.13 +89.3225 0.04 +89.6402 0.15 +89.8453 0.05 +90.0099 0.19 +90.1603 0.11 +90.7347 0.12 +91.0226 7.07 +92.0512 0.56 +92.6247 0.02 +93.0354 7.12 +93.478 0.1 +94.037 0.6 +94.4107 0.02 +95.0502 4.24 +95.9425 0.1 +97.0236 1.71 +97.2753 0.04 +97.9906 0.31 +99.014 0.68 +99.2637 0.04 +100.09 0.07 +100.7467 0.09 +101.0139 0.75 +101.5752 0.04 +102.0048 0.2 +102.5744 0.04 +103.0253 1.43 +103.1719 0.14 +103.3877 0.1 +103.6796 0.01 +103.8206 0.09 +104.0409 0.45 +104.3448 0.11 +104.7613 0.1 +105.0433 8.08 +105.4961 0.02 +106.0671 0.75 +106.5692 0.08 +107.0535 5.93 +107.2855 0.11 +107.7473 0.07 +108.0854 0.3 +109.0547 2.27 +110.0628 0.26 +110.7261 0.04 +111.0514 0.52 +111.8026 0.03 +112.0194 0.32 +112.6258 0.03 +112.8842 0.08 +112.9836 0.03 +113.4975 0.1 +113.9556 0.11 +114.3515 0.08 +114.5407 0.21 +115.0251 3.45 +115.2239 0.19 +115.5218 0.1 +116.0342 1.11 +116.4438 0.1 +116.6389 0.07 +117.0369 3.07 +118.0563 0.67 +118.3942 0.09 +118.6237 0.03 +119.0573 8.93 +119.3414 0.02 +120.0619 0.99 +120.4464 0.02 +120.6034 0.07 +121.0856 4.62 +121.3931 0.02 +122.0565 0.3 +123.0788 1.37 +123.752 0.03 +124.037 0.26 +124.9959 0.07 +125.1026 0.09 +125.9971 0.4 +126.6436 0.04 +127.0042 2.88 +127.6191 0.04 +128.0037 2.64 +128.2683 0.22 +129.0274 3.61 +129.4554 0.04 +130.0086 1.92 +130.532 0.03 +130.657 0.03 +131.0341 5.37 +131.3678 0.04 +131.6931 0.03 +132.0717 0.96 +133.0439 5.53 +134.029 0.18 +135.0629 1.89 +135.6054 0.01 +135.9969 0.18 +137.0445 0.29 +137.9681 0.15 +139.003 0.42 +139.9779 0.3 +140.215 0.12 +140.5192 0.09 +140.6683 0.06 +140.9947 2.27 +141.5333 0.2 +142.0016 3.08 +142.4499 0.09 +142.6409 0.14 +143.0108 5.96 +144.0117 1.74 +144.3584 0.06 +145.0176 8.47 +145.3106 0.05 +145.4847 0.15 +145.6934 0.03 +146.0489 0.93 +146.5949 0.1 +146.7987 0.08 +147.0612 1.61 +147.355 0.02 +148.0205 0.26 +148.3923 0.08 +149.0444 0.47 +149.665 0.09 +150.0299 0.1 +150.9284 0.24 +151.0403 0.11 +151.3314 0.02 +151.5689 0.09 +151.7526 0.09 +152.0144 0.74 +152.3023 0.09 +152.3965 0.02 +152.5975 0.11 +152.9834 0.89 +153.4657 0.03 +153.6122 0.09 +153.995 0.96 +154.4869 0.04 +155.0007 2.72 +155.495 0.1 +155.6301 0.11 +156.022 2.21 +156.59 0.1 +157.0293 7.4 +157.7036 0.14 +158.0242 1.17 +158.5583 0.08 +159.0511 2.72 +159.47 0.03 +160.0168 0.23 +160.4009 0.02 +160.5694 0.12 +160.7434 0.04 +161.0443 0.51 +161.8099 0.04 +162.2001 0.09 +163.0376 0.23 +163.7567 0.08 +164.0568 0.26 +164.2761 0.06 +164.4436 0.08 +164.9989 2.09 +165.9867 1.07 +166.4353 0.07 +166.9919 1.47 +167.5059 0.1 +167.7269 0.06 +168.0449 0.78 +168.3081 0.09 +168.5329 0.15 +169.0368 2.82 +169.336 0.11 +170.0445 1.21 +170.6444 0.12 +171.0651 3.33 +171.3273 0.11 +171.4373 0.1 +171.5428 0.09 +171.9473 0.21 +172.1465 0.34 +173.0857 1.21 +173.4404 0.06 +174.0691 0.19 +175.1262 0.77 +175.744 0.04 +176.0185 0.22 +176.1364 0.16 +177.0066 0.29 +177.1671 0.11 +177.4798 0.11 +177.7059 0.04 +178.0751 0.86 +178.6337 0.18 +179.0356 1.72 +180.0534 1.1 +180.2736 0.09 +180.6307 0.06 +181.0611 2.31 +181.3463 0.05 +181.5426 0.1 +182.0679 1.11 +182.676 0.1 +183.0933 3.62 +183.3568 0.08 +183.753 0.09 +184.102 1.39 +184.687 0.08 +185.0977 1.28 +185.4648 0.05 +185.5847 0.03 +186.124 0.26 +186.3854 0.1 +186.8684 0.11 +187.1104 0.71 +187.8442 0.02 +188.3105 0.17 +188.8306 0.1 +189.1001 0.32 +189.3537 0.11 +190.0779 0.19 +190.2714 0.1 +191.0771 0.83 +191.6068 0.05 +192.0706 0.86 +192.6725 0.05 +193.0641 2.06 +193.7336 0.03 +194.0555 1.06 +195.0674 2.28 +196.0866 0.86 +196.7466 0.15 +197.0946 2.91 +197.5988 0.05 +198.0743 0.44 +198.3654 0.09 +198.6951 0.05 +199.1241 0.96 +200.1279 0.41 +200.6475 0.01 +200.8163 0.05 +201.0753 0.46 +201.5211 0.05 +201.8506 0.2 +202.0806 0.42 +203.0463 0.45 +203.2757 0.09 +204.04 0.3 +204.6044 0.11 +205.0523 0.5 +205.4948 0.07 +205.8102 0.1 +206.0475 0.58 +206.4234 0.05 +207.0415 1.7 +208.042 1.1 +208.6307 0.15 +209.0743 2.1 +210.0856 0.63 +210.6251 0.1 +211.0967 0.97 +211.7416 0.09 +212.0859 0.4 +212.7823 0.1 +213.0776 0.53 +213.7461 0.06 +214.0605 0.39 +214.4679 0.06 +215.0773 0.29 +215.9639 0.09 +216.2873 0.11 +216.8016 0.1 +217.0617 0.25 +217.3769 0.07 +217.7244 0.1 +218.0872 0.24 +219.0345 0.54 +220.0689 0.57 +220.6996 0.07 +221.0803 1.14 +221.7952 0.09 +222.0408 0.37 +223.0821 1.21 +223.3956 0.06 +224.061 0.43 +224.4034 0.02 +224.5941 0.08 +225.0702 0.51 +225.1809 0.22 +225.5645 0.07 +225.7258 0.08 +226.1611 0.25 +227.0484 0.24 +227.9657 0.2 +228.0957 0.08 +229.0357 0.47 +229.2384 0.08 +229.7953 0.08 +230.0718 0.16 +230.831 0.19 +231.0828 0.45 +231.3864 0.03 +231.8079 0.03 +232.0426 0.62 +233.0615 0.9 +233.8344 0.06 +234.103 0.21 +235.052 0.58 +235.6463 0.04 +235.8624 0.07 +236.1495 0.26 +236.6807 0.09 +237.017 0.39 +237.2977 0.03 +238.0311 0.18 +238.1619 0.22 +239.0621 0.68 +239.4653 0.1 +239.7751 0.07 +240.0495 0.25 +240.1929 0.08 +240.6396 0.16 +241.0161 0.25 +242.046 0.34 +242.2946 0.08 +242.6657 0.04 +242.8267 0.07 +243.0889 0.7 +243.8141 0.1 +244.0678 0.27 +244.227 0.15 +245.0974 0.42 +245.4565 0.1 +245.6795 0.07 +246.0715 0.34 +246.538 0.19 +247.0906 0.62 +247.6704 0.08 +247.8579 0.16 +248.0725 0.19 +249.0898 0.39 +249.379 0.09 +249.5434 0.09 +250.1637 0.36 +250.9341 0.07 +251.1646 0.32 +251.518 0.11 +252.0071 0.18 +252.2144 0.17 +253.0506 0.35 +253.2037 0.12 +253.6806 0.09 +253.9986 0.13 +254.1965 0.11 +254.3429 0.01 +255.0965 0.44 +255.2624 0.22 +255.44 0.05 +256.0368 0.31 +256.1776 0.15 +256.4212 0.04 +256.7299 0.1 +257.1465 0.35 +258.0857 0.19 +258.2635 0.07 +258.7241 0.07 +259.1148 0.44 +259.9276 0.08 +260.1289 0.2 +260.2738 0.04 +261.1245 0.5 +261.3324 0.07 +262.1736 0.35 +262.5807 0.01 +263.1424 0.69 +264.2255 0.31 +264.3587 0.1 +264.4599 0.04 +265.1067 0.36 +266.1781 0.26 +266.5801 0.1 +266.9004 0.1 +267.0056 0.06 +267.2091 0.29 +267.5363 0.07 +267.7759 0.03 +268.0915 0.14 +269.0025 0.1 +269.1565 0.26 +270.0943 0.27 +271.1349 0.38 +271.807 0.18 +272.0988 0.25 +272.2647 0.1 +272.6298 0.11 +273.1276 0.38 +274.1569 0.1 +275.123 0.25 +276.0837 0.19 +276.3144 0.19 +277.2072 0.16 +277.3832 0.03 +277.6854 0.12 +278.1022 0.28 +278.2773 0.17 +278.974 0.28 +279.2794 0.19 +280.0616 0.11 +280.2995 0.02 +281.0331 0.07 +281.181 0.09 +282.1783 0.09 +283.185 0.22 +284.0978 0.05 +284.4882 0.11 +284.9021 0.15 +285.1199 0.26 +286.0855 0.07 +286.2121 0.09 +286.3849 0.03 +287.1467 0.19 +287.403 0.1 +288.1248 0.16 +289.0768 0.24 +289.9023 0.09 +290.1431 0.11 +290.267 0.1 +290.9424 0.1 +291.1961 0.1 +292.2639 0.06 +293.1826 0.29 +293.331 0.19 +294.024 0.15 +294.3081 0.09 +295.1803 0.12 +296.0823 0.11 +297.1096 0.23 +297.7513 0.09 +299.1101 0.22 +299.2559 0.14 +299.9474 0.12 +300.1956 0.16 +300.8785 0.08 +301.186 0.14 +302.1974 0.21 +303.1736 0.41 +304.1453 0.18 +304.9169 0.09 +305.0738 0.11 +305.2327 0.09 +306.1745 0.14 +306.4934 0.04 +307.1698 0.06 +307.3259 0.04 +309.0785 0.11 +309.2445 0.11 +309.6794 0.12 +310.2893 0.09 +310.5583 0.09 +310.9241 0.12 +311.2227 0.1 +311.3484 0.07 +312.1021 0.02 +312.2712 0.09 +312.6432 0.06 +312.8074 0.05 +313.1703 0.2 +313.7971 0.01 +314.0127 0.17 +314.2965 0.09 +315.0443 0.09 +315.2303 0.09 +315.8397 0.11 +315.9496 0.03 +316.1158 0.1 +317.1659 0.1 +317.351 0.09 +317.6015 0.03 +318.2242 0.08 +318.3906 0.12 +319.1443 0.18 +319.3156 0.17 +320.5392 0.05 +321.0981 0.09 +321.2944 0.13 +322.1042 0.11 +322.3015 0.04 +323.1543 0.11 +323.3179 0.11 +324.3257 0.04 +324.8913 0.01 +325.1641 0.11 +325.3745 0.21 +325.7705 0.01 +326.6728 0.08 +327.2347 0.18 +327.4373 0.06 +328.1225 0.13 +329.2112 0.28 +329.5202 0.01 +330.3768 0.07 +331.159 0.28 +332.2159 0.18 +333.1729 0.1 +333.3337 0.03 +334.1747 0.17 +335.1451 0.04 +335.2514 0.03 +336.1039 0.1 +337.2507 0.16 +339.1257 0.14 +339.3445 0.06 +339.8179 0.02 +340.9002 0.08 +341.1221 0.15 +341.549 0.14 +342.1331 0.21 +343.247 0.21 +343.6313 0.13 +344.895 0.1 +345.1719 0.13 +346.3883 0.1 +346.8994 0.07 +347.1187 0.08 +349.1532 0.17 +349.2848 0.06 +350.1674 0.08 +351.2127 0.11 +352.3781 0.1 +353.1376 0.13 +353.3562 0.1 +354.1288 0.02 +355.2581 0.08 +355.5747 0.11 +357.0141 0.1 +357.2029 0.12 +357.4237 0.11 +358.9357 0.1 +359.0276 0.04 +359.3165 0.05 +359.7594 0.05 +361.1502 0.1 +362.1644 0.03 +363.0657 0.1 +363.3529 0.04 +364.1674 0.14 +365.2204 0.11 +367.2058 0.14 +367.329 0.08 +368.0822 0.1 +368.4558 0.09 +369.1932 0.1 +369.3775 0.06 +369.8605 0.01 +370.112 0.04 +371.1534 0.13 +371.2767 0.1 +372.1669 0.02 +372.3193 0.1 +373.1094 0.12 +375.1075 0.13 +375.273 0.02 +376.3978 0.1 +376.9969 0.1 +377.2107 0.17 +379.0569 0.11 +379.2849 0.02 +381.0123 0.09 +381.2906 0.15 +383.1243 0.1 +385.1985 0.08 +386.1766 0.19 +386.4044 0.1 +387.2643 0.1 +387.9478 0.01 +388.1404 0.09 +388.287 0.06 +389.1212 0.1 +389.325 0.12 +390.091 0.12 +391.0641 0.08 +391.1986 0.07 +391.3513 0.17 +392.2191 0.09 +393.4116 0.04 +394.5361 0.07 +395.2326 0.09 +396.0066 0.12 +397.2492 0.18 +397.4865 0.09 +398.6783 0.11 +399.0504 0.11 +399.326 0.05 +400.0775 0.06 +401.1199 0.13 +401.3131 0.16 +402.095 0.1 +403.179 0.09 +404.0106 0.13 +404.217 0.17 +404.8433 0.01 +405.1545 0.11 +406.2321 0.11 +407.328 0.01 +408.1219 0.06 +409.2657 0.18 +409.3755 0.1 +410.1566 0.17 +410.5195 0.1 +411.1787 0.17 +411.3995 0.1 +411.9298 0.1 +412.8586 0.09 +413.1994 0.04 +414.2725 0.11 +415.0472 0.1 +415.2611 0.19 +415.3963 0.1 +415.5537 0.04 +416.0743 0.12 +416.262 0.13 +417.0885 0.07 +417.2136 0.1 +417.3689 0.1 +418.46 0.03 +419.0974 0.09 +419.2638 0.19 +419.4373 0.07 +419.5724 0.13 +420.1494 0.07 +421.1532 0.09 +423.2371 0.1 +424.1637 0.05 +424.3656 0.15 +425.2368 0.16 +425.5054 0.02 +426.2546 0.03 +426.5222 0.11 +427.3756 0.04 +428.1938 0.11 +429.3555 0.11 +430.1608 0.11 +431.2704 0.19 +432.176 0.15 +432.3822 0.11 +433.1474 0.2 +433.4345 0.11 +434.3172 0.15 +435.1323 0.15 +437.3787 0.11 +438.6638 0.14 +439.2469 0.16 +440.2545 0.18 +441.1429 0.12 +441.3087 0.09 +442.2064 0.04 +443.4076 0.26 +444.0912 0.08 +444.251 0.23 +445.1865 0.11 +445.3777 0.12 +446.0554 0.08 +446.2173 0.08 +447.3352 0.09 +447.7419 0.03 +448.2884 0.09 +450.1698 0.15 +451.2742 0.09 +453.3853 0.11 +454.1823 0.11 +456.3592 0.24 +458.2823 0.09 +459.239 0.07 +460.3438 0.11 +460.9531 0.19 +461.1153 0.09 +461.9629 0.08 +462.3644 0.15 +463.0803 0.11 +463.1989 0.08 +464.0802 0.18 +464.2273 0.23 +464.4575 0.09 +465.2223 0.09 +465.4454 0.1 +466.2732 0.1 +467.3639 0.1 +470.1111 0.11 +471.2713 0.09 +472.2257 0.1 +474.0017 0.09 +474.1375 0.09 +475.1669 0.09 +476.1925 0.11 +476.8462 0.09 +477.2881 0.09 +478.0909 0.09 +478.3585 0.11 +478.761 0.11 +479.2852 0.16 +480.0983 0.1 +480.726 0.1 +481.2634 0.1 +481.4521 0.1 +483.296 0.22 +483.5354 0.06 +483.79 0.07 +484.4271 0.11 +485.1849 0.18 +485.4508 0.12 +487.2925 0.1 +489.9032 0.09 +490.4738 0.15 +492.0476 0.1 +492.2497 0.1 +493.3901 0.07 +493.5565 0.05 +494.3607 0.23 +495.306 0.25 +496.0343 0.21 +496.2788 0.24 +496.4513 0.14 +497.3237 0.25 +497.6677 0.1 +498.2014 0.13 +498.452 0.16 +499.2283 0.1 +499.3917 0.1 +499.5013 0.08 +500.0695 0.09 +500.1751 0.04 +500.4562 0.1 +501.3159 0.11 +502.3757 0.08 +503.1035 0.22 +503.3442 0.1 +503.9797 0.1 +505.3211 0.09 +505.8888 0.08 +506.2747 0.12 +507.1004 0.12 +507.9648 0.08 +508.3102 0.09 +508.6525 0.1 +509.2427 0.03 +509.5424 0.12 +510.401 0.12 +511.2697 0.11 +511.4628 0.12 +512.3276 0.12 +513.0675 0.06 +513.2952 0.06 +514.0443 0.05 +516.1821 0.1 +518.053 0.09 +519.2388 0.15 +520.3332 0.05 +521.1063 0.01 +521.3942 0.13 +522.1622 0.09 +523.3963 0.17 +524.2245 0.03 +525.0712 0.06 +525.2748 0.08 +526.0299 0.18 +526.1734 0.12 +526.3994 0.03 +526.8523 0.1 +527.0681 0.09 +527.4091 0.06 +528.0676 0.02 +528.6387 0.11 +528.8953 0.09 +530.8053 0.1 +531.3378 0.06 +531.7289 0.06 +533.0436 0.13 +533.228 0.06 +534.3606 0.09 +535.3986 0.12 +537.3539 0.08 +539.3202 0.25 +539.5604 0.24 +542.2948 0.12 +543.3395 0.09 +544.2875 0.16 +545.3751 0.08 +546.0738 0.11 +546.5637 0.1 +547.347 0.11 +548.058 0.11 +549.4812 0.21 +549.9692 0.1 +550.2239 0.05 +550.55 0.12 +551.0676 0.09 +552.1386 0.1 +552.5901 0.1 +553.3565 0.09 +554.0975 0.09 +554.3044 0.09 +555.1841 0.07 +555.4073 0.12 +556.1902 0.18 +556.4471 0.15 +557.1077 0.09 +557.8469 0.09 +558.0774 0.09 +558.4618 0.12 +559.299 0.12 +561.1418 0.04 +562.178 0.11 +564.1468 0.12 +564.8191 0.11 +565.2425 0.15 +566.6026 0.11 +567.2495 0.17 +567.8629 0.01 +568.5547 0.09 +569.3978 0.09 +571.243 0.11 +572.2326 0.14 +572.7534 0.01 +573.211 0.1 +573.4882 0.11 +575.5931 0.02 +576.1507 0.11 +576.5045 0.1 +579.1635 0.14 +579.395 0.05 +580.1649 0.2 +580.4392 0.17 +580.9889 0.11 +581.5886 0.03 +582.1465 0.08 +582.3943 0.13 +582.5698 0.12 +582.9604 0.09 +583.196 0.12 +584.1213 0.11 +584.2274 0.03 +584.7446 0.11 +585.1608 0.14 +586.3382 0.12 +588.0452 0.05 +588.5197 0.14 +591.2048 0.09 +591.36 0.1 +591.5754 0.05 +592.2725 0.13 +593.3618 0.1 +593.5731 0.07 +594.0988 0.14 +594.2673 0.09 +595.3801 0.09 +597.4243 0.18 +598.3925 0.12 +599.8887 0.03 +600.2012 0.28 +600.718 0.04 +601.2349 0.05 +601.433 0.1 +601.6023 0.13 +602.2891 0.15 +602.4552 0.11 +602.6234 0.13 +603.3173 0.19 +603.5303 0.03 +603.6791 0.1 +604.3965 0.17 +604.6462 0.1 +605.2435 0.1 +605.585 0.21 +606.5033 0.11 +607.1822 0.06 +608.1455 0.24 +608.3392 0.09 +609.215 0.04 +609.3873 0.1 +609.5494 0.13 +610.0377 0.09 +610.3467 0.1 +611.0441 0.09 +612.5553 0.08 +613.0417 0.11 +613.2508 0.02 +613.467 0.06 +614.3828 0.1 +615.5313 0.14 +616.1759 0.13 +617.5092 0.07 +618.1335 0.11 +618.3998 0.45 +618.562 0.14 +619.0568 0.06 +619.2853 0.12 +619.5342 0.23 +620.2015 0.11 +620.3577 0.15 +621.1474 0.09 +621.2994 0.1 +622.1454 0.15 +622.3127 0.09 +623.0128 0.07 +623.3189 0.1 +623.9741 0.12 +624.1446 0.05 +624.3079 0.27 +624.4855 0.11 +625.1601 0.07 +625.3693 0.14 +628.1826 0.01 +628.4674 0.09 +629.4669 0.1 +629.7701 0.12 +630.5553 0.08 +631.4375 0.09 +631.8735 0.09 +632.0226 0.12 +632.236 0.05 +633.3848 0.18 +633.5952 0.09 +633.9731 0.12 +634.4183 0.1 +635.4131 0.1 +635.739 0.1 +637.0259 0.13 +637.2834 0.12 +637.4478 0.11 +638.0669 0.09 +638.404 0.22 +638.8402 0.04 +640.1734 0.14 +640.388 0.09 +641.5211 0.06 +642.8893 0.1 +643.2336 0.23 +644.0849 0.09 +646.4284 0.06 +646.64 0.11 +647.2113 0.13 +647.6243 0.16 +648.5105 0.13 +649.3253 0.1 +649.9938 0.11 +650.2791 0.13 +650.4222 0.06 +650.9221 0.05 +652.3372 0.13 +652.5069 0.03 +653.2563 0.1 +653.4435 0.12 +654.1102 0.11 +655.1462 0.09 +655.3055 0.11 +655.8108 0.04 +657.0337 0.08 +657.1736 0.15 +657.8903 0.06 +658.3047 0.27 +658.5477 0.2 +658.7815 0.04 +659.2634 0.1 +659.5167 0.17 +660.1905 0.06 +660.7501 0.12 +662.0693 0.13 +663.1937 0.13 +663.3326 0.08 +664.2456 0.08 +666.3517 0.1 +667.2383 0.11 +667.3823 0.1 +668.2935 0.1 +668.5346 0.09 +669.4111 0.11 +669.6481 0.04 +670.2764 0.12 +670.5226 0.06 +671.1857 0.1 +671.4218 0.16 +672.1462 0.1 +672.5018 0.38 +672.7889 0.25 +673.2865 0.11 +673.5604 0.2 +673.7218 0.14 +674.3186 0.09 +674.571 0.22 +675.5684 0.11 +676.1969 0.19 +677.3253 0.08 +677.5603 0.05 +677.733 0.08 +678.006 0.05 +678.4751 0.11 +679.2908 0.12 +679.4543 0.11 +680.2854 0.02 +680.5685 0.1 +682.2779 0.11 +682.4629 0.11 +683.5027 0.11 +683.7142 0.1 +684.2503 0.17 +684.9191 0.1 +685.3205 0.04 +685.6462 0.11 +686.308 0.31 +686.647 0.26 +687.0433 0.17 +687.4344 0.86 +687.8624 0.1 +688.0228 0.13 +688.1821 0.21 +688.4058 0.19 +688.6876 0.11 +689.2933 0.27 +689.5701 0.13 +689.705 0.09 +690.0502 0.13 +690.233 0.07 +691.2709 0.07 +691.6581 0.04 +692.3109 0.13 +693.3795 0.15 +694.869 0.09 +695.0386 0.15 +695.9255 0.07 +696.2157 0.13 +696.4558 0.1 +698.4055 0.05 +698.8511 0.1 +699.1689 0.09 +699.312 0.09 +701.2916 0.09 +701.4142 0.09 +701.637 0.18 +702.4926 0.73 +703.3338 0.58 +703.4482 0.24 +703.6138 0.12 +703.7999 0.11 +704.0063 0.12 +704.3989 0.26 +704.7004 0.28 +705.3956 0.16 +705.7902 0.12 +706.147 0.15 +708.251 0.11 +708.3921 0.13 +708.5741 0.09 +709.2233 0.08 +709.5107 0.09 +709.709 0.04 +710.0229 0.03 +710.1988 0.1 +710.3757 0.1 +710.8113 0.14 +711.2397 0.1 +712.3195 0.23 +712.6308 0.1 +713.2944 0.11 +713.8569 0.16 +714.3041 0.11 +714.5184 0.08 +715.6681 0.11 +716.344 0.04 +716.5482 0.09 +716.9097 0.06 +717.1731 0.18 +717.3467 0.05 +718.1779 0.09 +718.4117 0.12 +719.5033 0.13 +720.1957 0.17 +720.3172 0.05 +721.3773 0.1 +722.4006 0.1 +723.0615 0.11 +724.231 0.13 +724.522 0.09 +724.6283 0.04 +725.707 0.1 +726.6355 0.09 +728.0637 0.05 +728.3671 0.09 +728.6348 0.12 +729.0894 0.11 +730.1857 0.09 +730.6219 0.11 +730.812 0.1 +731.2104 0.17 +731.3382 0.13 +731.5242 0.13 +732.4158 2.05 +732.6618 0.79 +733.0997 0.09 +733.3058 0.13 +734.0816 0.11 +734.597 0.11 +735.4055 0.22 +737.8546 0.04 +738.2925 0.08 +738.4477 0.23 +738.5764 0.16 +739.8852 0.06 +740.1599 0.11 +740.3896 0.21 +740.4927 0.08 +740.749 0.08 +742.3531 0.12 +744.0319 0.08 +745.5117 0.1 +745.8762 0.11 +746.4083 0.51 +746.6014 0.25 +746.7719 0.19 +746.9731 0.17 +747.351 0.23 +747.5512 0.21 +748.0629 0.11 +748.3795 0.12 +748.6563 0.18 +748.9138 0.11 +749.1058 0.12 +749.5154 0.08 +749.7452 0.1 +750.1946 0.2 +750.4572 0.17 +750.6369 0.15 +750.8116 0.16 +751.2049 0.13 +751.4919 0.13 +751.7289 0.16 +752.0221 0.22 +752.2009 0.21 +752.3705 0.23 +752.5237 0.19 +752.9303 0.07 +753.3461 0.16 +753.5739 0.25 +753.7292 0.13 +754.0602 0.2 +754.3483 0.23 +754.996 0.13 +755.2331 0.03 +755.6509 0.15 +756.124 0.23 +756.6993 0.17 +756.925 0.05 +757.2336 0.09 +757.4973 0.23 +757.6281 0.09 +758.4282 0.16 +758.5876 0.04 +758.7499 0.01 +760.035 0.19 +760.3422 0.22 +760.5882 0.18 +760.7056 0.12 +760.9843 0.12 +761.5855 0.04 +761.8876 0.09 +762.3746 0.14 +762.6379 0.12 +763.5016 0.11 +763.9058 0.08 +764.3989 0.23 +764.5184 0.11 +764.692 0.08 +765.0799 0.12 +765.3341 0.2 +766.2254 0.28 +766.4357 0.23 +766.6093 0.12 +766.8819 0.09 +767.9989 0.13 +768.1418 0.1 +768.533 0.11 +768.6462 0.08 +771.3574 0.11 +771.7271 0.11 +772.3633 0.09 +772.6339 0.12 +773.4407 0.15 +773.8687 0.07 +774.4866 1.26 +774.9471 0.1 +775.2321 0.06 +775.6161 0.11 +775.7469 0.08 +775.9042 0.13 +776.2086 0.23 +776.4221 0.12 +776.6039 0.1 +777.8204 0.11 +778.0625 0.13 +778.3812 0.11 +778.6907 0.08 +779.4456 0.08 +779.6346 0.05 +780.3813 0.38 +780.6868 0.1 +780.8993 0.2 +781.168 0.09 +781.313 0.11 +781.4959 0.32 +781.7002 0.13 +781.9393 0.09 +782.916 0.11 +784.3044 0.08 +786.0159 0.04 +786.5503 0.01 +787.1736 0.24 +790.2853 0.12 +791.6773 0.11 +792.1372 0.12 +792.3048 0.28 +792.5215 0.1 +793.2042 0.06 +794.118 0.29 +794.3868 0.26 +794.5912 0.29 +794.7292 0.13 +794.8938 0.28 +795.4212 0.31 +796.5303 0.1 +798.3018 0.13 +798.6688 0.07 +799.3987 0.1 +801.1276 0.09 +804.1857 0.21 +804.7317 0.24 +805.7992 0.05 +806.15 0.09 +806.513 0.21 +806.6868 0.11 +806.876 0.35 +807.3934 0.05 +807.554 0.17 +808.0898 0.1 +808.4447 0.31 +808.7229 0.18 +808.861 0.15 +809.132 0.11 +809.4152 0.06 +810.8049 0.1 +811.9583 0.1 +813.5034 0.09 +814.2897 0.13 +814.7466 0.05 +815.7339 0.12 +815.993 0.13 +816.6152 0.12 +817.2323 0.07 +818.3217 0.18 +819.0806 0.1 +820.3139 0.19 +820.5251 0.28 +821.5738 0.12 +821.7819 0.09 +822.3114 0.25 +822.6399 0.29 +822.9949 0.11 +823.2928 0.16 +824.51 0.2 +825.1467 0.08 +828.9813 0.09 +829.4634 0.11 +829.6143 0.21 +829.8997 0.11 +830.7162 0.07 +831.6888 0.19 +831.907 0.11 +832.3677 0.25 +832.6114 0.16 +832.8652 0.19 +833.1569 0.13 +833.4382 0.2 +834.1468 0.41 +834.4608 0.32 +834.683 0.39 +834.9838 0.27 +835.2836 0.3 +835.6373 0.31 +836.1563 0.37 +836.558 0.56 +836.8099 0.11 +836.9333 0.12 +837.126 0.19 +837.5032 0.23 +837.8071 0.13 +837.9906 0.16 +838.2486 0.28 +838.5494 0.31 +838.7319 0.35 +839.0929 0.27 +839.4712 0.22 +839.9362 0.07 +840.1299 0.24 +840.2411 0.13 +840.4716 0.15 +840.6979 0.1 +840.9263 0.21 +841.1527 0.11 +841.5402 0.32 +842.2266 0.28 +842.5988 0.22 +842.9006 0.16 +843.0598 0.11 +843.4249 0.16 +843.7563 0.17 +844.2193 0.27 +844.3172 0.13 +844.7507 0.25 +845.0556 0.11 +845.3035 0.29 +845.5105 0.28 +845.7032 0.19 +846.3009 0.53 +846.6202 0.32 +847.1485 0.26 +847.4932 0.1 +847.6768 0.16 +848.5275 2.01 +848.8702 1.07 +849.2477 0.69 +849.5261 0.32 +849.7648 0.36 +849.9127 0.18 +850.2667 0.45 +850.4809 0.3 +850.7339 0.41 +851.5632 1.35 +851.7979 0.61 +851.955 0.41 +852.4242 0.37 +852.7078 0.26 +853.0546 0.11 +853.3821 0.29 +853.5698 0.13 +853.7473 0.06 +854.1166 0.08 +854.3339 0.2 +854.5706 0.31 +854.8705 0.27 +855.297 0.2 +855.5521 0.18 +855.7225 0.11 +856.3264 0.14 +856.6611 0.24 +856.9814 0.23 +857.1314 0.3 +857.3039 0.15 +857.6722 0.21 +857.8232 0.35 +857.9497 0.21 +858.2977 0.37 +858.4435 0.26 +858.6456 0.13 +858.8262 0.38 +859.2527 0.63 +859.4863 0.37 +859.7547 0.29 +860.2485 0.61 +861.3384 0.78 +861.7874 1.31 +862.1751 1.36 +866.4878 99.97 +867.3083 7.92 +868.1585 3.9 +869.2639 2.03 +869.9253 1.55 +870.6571 1.18 +871.4155 0.81 +871.7023 0.7 +872.0566 0.7 +872.4639 0.61 +872.6588 0.52 +872.7548 0.25 +873.059 0.25 +873.3141 0.32 +873.4877 0.16 +873.7266 0.19 +873.9164 0.14 +END IONS + +BEGIN IONS +TITLE=49394 +msLevel=2 +PEPMASS=254.2478 +PEPMASSINT=79.0211 +CHARGE=1+ +SMILES=CCCCCCCCCCCCN1CCCC1=O +INCHI=InChI=1S/C16H31NO/c1-2-3-4-5-6-7-8-9-10-11-14-17-15-12-13-16(17)18/h2-15H2,1H3 +NAME=1-Dodecyl-2-pyrrolidinone +53.0023 389544.5 +53.9975 43283.2 +54.01 8884.6 +55.0543 101153.5 +57.0699 1234735.6 +58.0652 52215.4 +68.0495 246268.6 +69.0335 1861984.5 +70.0651 100325.3 +71.0491 17505.4 +71.0855 82650.1 +84.0444 23241.3 +85.1012 12953.7 +86.06 7322894.5 +98.0601 71028.4 +99.0679 14121.7 +112.0757 12963.6 +114.0915 29004.4 +128.107 13283.8 +END IONS + +BEGIN IONS +TITLE=50488 +msLevel=2 +PEPMASS=335.0512 +PEPMASSINT=335.0513 +CHARGE=1+ +SMILES=OC1=CC=C(C=C1)C(C1=CC=C(O)C=C1)(C(F)(F)F)C(F)(F)F +INCHI=InChI=1S/C15H10F6O2/c16-14(17,18)13(15(19,20)21,9-1-5-11(22)6-2-9)10-3-7-12(23)8-4-10/h1-8,22-23H +NAME=Bisphenol AF +68.9958 5424287.5 +84.9906 20175.7 +108.0217 136039.4 +108.9906 25235.3 +111.0252 1322438 +141.0347 14511.7 +149.0397 160193.1 +153.0345 108129.2 +161.022 274892.7 +167.0502 95063.8 +169.0459 235484.2 +171.0451 29172.9 +177.0346 991843 +178.0425 14783.5 +185.0407 55847.2 +187.0565 258724.4 +195.0451 850064 +196.0531 61964.9 +197.0408 1481845.5 +199.0371 35832.9 +201.0358 35523.3 +205.0295 456701.3 +207.0252 90080.7 +213.0359 138275.5 +215.0515 961981.2 +216.0392 139525.4 +217.047 173635.1 +221.0238 14566.7 +223.0402 53190.1 +223.0595 60195 +224.0279 135255.8 +225.0358 1834166.9 +227.0316 227070.5 +235.0199 45901.3 +237.0536 17872.2 +241.0306 36519.1 +244.0341 1724825.6 +245.0419 2178394.5 +246.0496 42056.7 +247.0374 35980.2 +255.0265 151155.8 +265.0482 12176363 +275.0324 69969.9 +295.0385 61333.3 +END IONS + +BEGIN IONS +TITLE=51366 +msLevel=2 +PEPMASS=257.1285 +PEPMASSINT=79.0211 +CHARGE=1+ +SMILES=COC1=CC=C(C=C1)C(O)(C1CC1)C1=CN=CN=C1 +INCHI=InChI=1S/C15H16N2O2/c1-19-14-6-4-12(5-7-14)15(18,11-2-3-11)13-8-16-10-17-9-13/h4-11,18H,2-3H2,1H3 +NAME=Ancymidol +81.0447 139262.5 +95.0491 12805.8 +121.0648 7741.2 +121.076 8707.1 +122.0599 41628.6 +135.0439 119719.4 +149.0709 41391.2 +161.0961 8699 +165.0064 20038.6 +171.0803 10661.2 +177.0909 76111.6 +185.096 74656.8 +195.0802 28991.2 +202.122 16096.7 +212.1071 22995 +213.0912 43603.8 +215.0817 26259.6 +216.0891 8591.4 +225.01 34227.8 +230.1178 29876.5 +239.1178 107390.8 +242.1049 8778.6 +257.1283 7356261.5 +END IONS + +BEGIN IONS +TITLE=51380 +msLevel=2 +PEPMASS=239.0815 +PEPMASSINT=79.0211 +CHARGE=1+ +SMILES=NC1=CC=C(N)C2=C1C(=O)C1=CC=CC=C1C2=O +INCHI=InChI=1S/C14H10N2O2/c15-9-5-6-10(16)12-11(9)13(17)7-3-1-2-4-8(7)14(12)18/h1-6H,15-16H2 +NAME=1,4-Diaminoanthraquinone +72.0444 13315.3 +238.0741 2905 +239.0811 1992142.2 +END IONS + +BEGIN IONS +TITLE=51381 +msLevel=2 +PEPMASS=239.0815 +PEPMASSINT=79.0211 +CHARGE=1+ +SMILES=NC1=CC=C(N)C2=C1C(=O)C1=CC=CC=C1C2=O +INCHI=InChI=1S/C14H10N2O2/c15-9-5-6-10(16)12-11(9)13(17)7-3-1-2-4-8(7)14(12)18/h1-6H,15-16H2 +NAME=1,4-Diaminoanthraquinone +72.0443 20615.9 +105.0336 3229.3 +210.0557 2989.3 +221.0711 5689 +238.0742 7991.7 +239.0809 2977019.5 +END IONS + +BEGIN IONS +TITLE=51382 +msLevel=2 +PEPMASS=239.0815 +PEPMASSINT=79.0211 +CHARGE=1+ +SMILES=NC1=CC=C(N)C2=C1C(=O)C1=CC=CC=C1C2=O +INCHI=InChI=1S/C14H10N2O2/c15-9-5-6-10(16)12-11(9)13(17)7-3-1-2-4-8(7)14(12)18/h1-6H,15-16H2 +NAME=1,4-Diaminoanthraquinone +65.0385 2552.8 +72.0443 19175.4 +91.0541 2374.3 +92.0495 9400.7 +105.0335 52552 +110.0601 4234.3 +130.0287 7735.6 +161.0346 27918 +161.0596 5767.1 +166.0653 11568.3 +182.0605 5055.9 +183.0918 5409.9 +184.0759 4998 +193.0761 22722.8 +194.0603 18281 +195.0919 4269 +196.0757 2833.7 +198.0552 3280.1 +199.0627 6250.2 +210.0552 9511.1 +210.0786 7996 +211.0867 52353.7 +212.0707 31737 +221.0709 104547.4 +222.0551 26223.4 +223.0629 25557.8 +238.0738 88044.3 +239.081 1935558.2 +END IONS + +BEGIN IONS +TITLE=51796 +msLevel=2 +PEPMASS=301.1445 +PEPMASSINT=141.0113 +CHARGE=1+ +SMILES=CC(CC1=CC(O)=C(O)C=C1)C(C)CC1=CC=C(O)C(O)=C1 +INCHI=InChI=1S/C18H22O4/c1-11(7-13-3-5-15(19)17(21)9-13)12(2)8-14-4-6-16(20)18(22)10-14/h3-6,9-12,19-22H,7-8H2,1-2H3 +NAME=Nordihydroguaiaretic acid +122.0378 12105.9 +149.0614 9453.9 +301.1445 8294490.5 +END IONS + +BEGIN IONS +TITLE=51797 +msLevel=2 +PEPMASS=301.1445 +PEPMASSINT=141.0113 +CHARGE=1+ +SMILES=CC(CC1=CC(O)=C(O)C=C1)C(C)CC1=CC=C(O)C(O)=C1 +INCHI=InChI=1S/C18H22O4/c1-11(7-13-3-5-15(19)17(21)9-13)12(2)8-14-4-6-16(20)18(22)10-14/h3-6,9-12,19-22H,7-8H2,1-2H3 +NAME=Nordihydroguaiaretic acid +108.0218 13572.2 +109.0295 20678.7 +121.0294 2565.8 +122.0373 54305 +123.0452 21629.5 +124.0164 6106.9 +125.0244 5526.8 +137.0245 9197.4 +149.024 2062 +151.0036 5238.1 +154.0272 15110 +177.092 6771.4 +273.15 14772.2 +301.1445 1538593.4 +END IONS + +BEGIN IONS +TITLE=53349 +msLevel=2 +PEPMASS=302.3054 +PEPMASSINT=79.0211 +CHARGE=1+ +SMILES=CCCCCCCCCCCCCCN(CCO)CCO +INCHI=InChI=1S/C18H39NO2/c1-2-3-4-5-6-7-8-9-10-11-12-13-14-19(15-17-20)16-18-21/h20-21H,2-18H2,1H3 +NAME=2,2'-(Tetradecylimino)diethanol +57.0696 111460.1 +70.065 126115.5 +71.0854 88387.1 +85.1011 47629.3 +88.0756 527104.2 +102.0913 131510.3 +106.0862 813156 +258.279 85640.6 +284.2946 961214.1 +302.3051 31544706 +END IONS + +BEGIN IONS +TITLE=55341 +msLevel=2 +PEPMASS=175.11896 +CHARGE=1+ +SMILES=C(CC(C(=O)O)N)CN=C(N)N +INCHI=InChI=1S/C6H14N4O2/c7-4(5(11)12)2-1-3-10-6(8)9/h4H,1-3,7H2,(H,11,12)(H4,8,9,10) +NAME=Arginine +70.06517 390277.3125 +116.070511 72842.054688 +130.097519 76350.84375 +END IONS + +BEGIN IONS +TITLE=55347 +msLevel=2 +PEPMASS=175.11896 +CHARGE=1+ +SMILES=C(CC(C(=O)O)N)CN=C(N)N +INCHI=InChI=1S/C6H14N4O2/c7-4(5(11)12)2-1-3-10-6(8)9/h4H,1-3,7H2,(H,11,12)(H4,8,9,10) +NAME=Arginine +70.065178 279998.96875 +116.070435 28455.871094 +130.097427 31947.271484 +END IONS + +BEGIN IONS +TITLE=56089 +CHARGE=1+ +SMILES=CC(C)C(=C)CC[C@H]([C@H]1[C@@H](C[C@@]2([C@@]1(CC=C3C2=CC[C@H]([C@]3(C)CCC(=O)O)C(=C)C)C)C)O)C(=O)O +INCHI=InChI=1S/C31H46O5/c1-18(2)20(5)9-10-21(28(35)36)27-25(32)17-31(8)24-12-11-22(19(3)4)29(6,15-14-26(33)34)23(24)13-16-30(27,31)7/h12-13,18,21-22,25,27,32H,3,5,9-11,14-17H2,1-2,4,6-8H3,(H,33,34)(H,35,36)/t21-,22+,25-,27+,29+,30-,31+/m1/s1 +NAME=Poricoic acid A +481.3318 856.027 +482.3356 265.802 +499.3426 501.054 +500.3462 158.509 +521.3229 5779.976 +522.3276 1767.67 +523.3313 296.787 +543.3068 180.746 +1019.6617 1145.328 +1020.6651 730.867 +END IONS + +BEGIN IONS +TITLE=56098 +msLevel=2 +PEPMASS=497.32725 +CHARGE=1+ +SMILES=CC(C)C(=C)CC[C@H]([C@H]1[C@@H](C[C@@]2([C@@]1(CC=C3C2=CC[C@H]([C@]3(C)CCC(=O)O)C(=C)C)C)C)O)C(=O)O +INCHI=InChI=1S/C31H46O5/c1-18(2)20(5)9-10-21(28(35)36)27-25(32)17-31(8)24-12-11-22(19(3)4)29(6,15-14-26(33)34)23(24)13-16-30(27,31)7/h12-13,18,21-22,25,27,32H,3,5,9-11,14-17H2,1-2,4,6-8H3,(H,33,34)(H,35,36)/t21-,22+,25-,27+,29+,30-,31+/m1/s1 +NAME=Poricoic acid A +381.3164 21.33 +423.2888 250.532 +424.2971 115.123 +425.3052 106.457 +497.3239 468.646 +END IONS + +BEGIN IONS +TITLE=56567 +msLevel=2 +PEPMASS=175.11894 +CHARGE=1+ +SMILES=C(C[C@@H](C(=O)O)N)CN=C(N)N +INCHI=InChI=1S/C6H14N4O2/c7-4(5(11)12)2-1-3-10-6(8)9/h4H,1-3,7H2,(H,11,12)(H4,8,9,10)/t4-/m0/s1 +NAME=L-Arginine +30.0334 1.831 +43.0288 3.087 +60.0554 9.797 +70.0642 114.417 +END IONS + +BEGIN IONS +TITLE=56568 +msLevel=2 +PEPMASS=175.11894 +CHARGE=1+ +SMILES=C(C[C@@H](C(=O)O)N)CN=C(N)N +INCHI=InChI=1S/C6H14N4O2/c7-4(5(11)12)2-1-3-10-6(8)9/h4H,1-3,7H2,(H,11,12)(H4,8,9,10)/t4-/m0/s1 +NAME=L-Arginine +28.0178 6.75 +30.0334 4.1681 +41.0383 1.5731 +43.0287 8.022 +43.0539 4.1813 +60.0554 3.6626 +70.0644 75.3701 +END IONS + +BEGIN IONS +TITLE=56627 +msLevel=2 +PEPMASS=230.09576 +CHARGE=1+ +SMILES=C[N+](C)(C)[C@@H](CC1=CNC(=S)N1)C(=O)[O-] +INCHI=InChI=1S/C9H15N3O2S/c1-12(2,3)7(8(13)14)4-6-5-10-9(15)11-6/h5,7H,4H2,1-3H3,(H2-,10,11,13,14,15)/t7-/m0/s1 +NAME=L-(+)-Ergothioneine +39.0231 0.54734 +41.0387 0.99328 +42.034 0.45597 +43.0418 0.33981 +44.0496 0.33709 +45.0575 0.21168 +54.0342 0.39162 +56.0498 0.33774 +58.0655 0.35006 +59.9907 0.26779 +60.0811 0.50505 +67.0421 1.0317 +68.05 0.80284 +69.0578 0.74839 +END IONS + +BEGIN IONS +TITLE=58019 +msLevel=2 +PEPMASS=350.1598 +PEPMASSINT=350.1596 +CHARGE=1+ +SMILES=CC=C1CC2(C(O2)(C(=O)OCC3=CCN4C3C(CC4)OC1=O)C)CO +INCHI=InChI=1S/C18H23NO6/c1-3-11-8-18(10-20)17(2,25-18)16(22)23-9-12-4-6-19-7-5-13(14(12)19)24-15(11)21/h3-4,13-14,20H,5-10H2,1-2H3 +NAME=Erucifoline +67.0543 1731.4 +93.0698 1502.7 +94.0653 3436.8 +96.0809 2821 +108.0811 1687.8 +110.0961 1620.5 +120.0808 11316.5 +122.0964 1808.8 +138.0913 9561.4 +END IONS + +BEGIN IONS +TITLE=61621 +CHARGE=1+ +SMILES=C[C@H](CCC(=O)O)[C@H]1CC[C@@H]2[C@@]1(CC[C@H]3[C@H]2CC[C@H]4[C@@]3(CC[C@@H]([C@@H]4O)O)C)C +INCHI=InChI=1S/C24H40O4/c1-14(4-9-21(26)27)16-7-8-17-15-5-6-19-22(28)20(25)11-13-24(19,3)18(15)10-12-23(16,17)2/h14-20,22,25,28H,4-13H2,1-3H3,(H,26,27)/t14-,15+,16-,17+,18+,19-,20+,22-,23-,24-/m1/s1 +NAME=3b,4b-Dihydroxy-5b-cholan-24-oic acid +58.97921 15.1113 +59.38238 0.1312 +59.98315 0.295 +61.95464 22.8604 +62.48625 0.1959 +63.96198 0.1475 +68.96631 4.8351 +73.00308 0.5274 +74.98043 0.3703 +78.89127 100 +79.11557 0.3944 +79.46133 0.3814 +80.88978 99.8626 +81.14476 0.5 +81.48923 0.8733 +81.59805 0.076 +87.02062 1.4392 +89.00256 3.1946 +90.00849 0.0987 +92.9074 0.2842 +94.90839 0.1624 +98.93063 0.2172 +110.96172 0.3248 +112.97164 12.9284 +113.97817 0.2438 +115.06454 0.8274 +116.96209 0.1141 +119.9347 0.1467 +126.89443 1.1472 +128.02735 0.2107 +129.07317 0.1595 +136.8653 0.779 +138.86388 0.9887 +140.86283 0.3158 +143.09973 0.2549 +157.11851 0.5502 +160.84218 0.1344 +160.91826 0.2506 +163.89153 0.292 +165.89109 0.4639 +180.82699 0.9597 +182.82117 1.9681 +184.81911 0.8107 +212.07947 0.5867 +233.94345 0.113 +255.24058 0.3045 +297.16825 0.3559 +311.17136 1.0346 +312.17768 0.1028 +325.19476 1.0485 +326.19532 0.1581 +339.21571 0.8157 +340.21779 0.2235 +391.28882 5.9023 +392.06557 0.056 +392.11168 0.0529 +392.15585 0.0544 +392.28799 2.2005 +393.32045 0.3306 +423.31547 0.3856 +424.31366 0.1189 +427.2666 7.3066 +428.2727 2.1076 +429.27787 2.4943 +430.27909 0.5809 +430.68331 0.0752 +430.77043 0.061 +437.29832 0.6199 +438.32741 0.33 +449.25892 0.2025 +450.25191 0.0606 +451.31459 2.4542 +451.95357 0.0577 +452.31578 0.7935 +452.68038 0.1025 +452.79113 0.0745 +453.32691 0.0938 +453.41508 0.0799 +454.29921 2.3378 +455.30523 0.7978 +471.21469 15.7898 +472.22874 4.633 +473.21219 16.0578 +474.22071 4.0257 +476.25779 0.2737 +477.28338 0.0836 +479.40724 0.2641 +480.36544 0.1015 +481.33282 0.8168 +482.34062 0.1223 +482.51248 0.0575 +485.26718 0.0763 +487.24109 0.301 +493.22565 0.4922 +495.21629 0.4842 +505.30136 0.6536 +506.32122 0.2809 +507.37351 0.446 +549.4312 0.1481 +587.45063 0.5039 +587.94123 0.3743 +588.43957 0.1855 +605.42036 0.2307 +647.55085 0.1993 +675.57741 0.2201 +703.49391 0.339 +717.49893 0.2047 +717.70947 0.0548 +718.49555 0.089 +731.53367 0.3703 +732.53497 0.2044 +783.59932 5.8335 +784.60818 3.7428 +819.57282 4.4518 +820.58835 2.6082 +821.56651 2.502 +824.05583 0.1397 +824.22704 0.0772 +824.53579 0.1826 +841.53238 0.1324 +846.59062 0.6247 +847.57177 0.5052 +848.60519 0.2128 +863.52679 8.1481 +864.53943 4.7186 +865.51783 19.2718 +873.6107 0.1056 +897.59043 0.1913 +END IONS + +BEGIN IONS +TITLE=63288 +msLevel=2 +CHARGE=1+ +SMILES=COc(c(O)3)cc(cc3)C(O1)=C(O)C(=O)c(c(O)2)c(cc(O)c2)1 +INCHI=InChI=1S/C16H12O7/c1-22-11-4-7(2-3-9(11)18)16-15(21)14(20)13-10(19)5-8(17)6-12(13)23-16/h2-6,17-19,21H,1H3 +NAME=3,4',5,7-tetrahydroxy-3'-methoxyflavone +161.8376 5 +286.039 7 +286.05316 8 +286.98383 6 +287.01602 18 +287.05396 1000 +287.07257 141 +288.04486 35 +288.05344 106 +288.065 74 +289.07217 9 +289.82996 5 +311.52286 5 +363.86816 8 +448.75311 5 +449.04974 17 +449.09006 180 +449.10593 440 +449.1246 153 +END IONS + +BEGIN IONS +TITLE=63972 +msLevel=2 +PEPMASS=611.16118 +CHARGE=1+ +SMILES=C(C5O)(O)C(C)OC(C5O)OCC(O1)C(C(C(C1Oc(c3)c([o+1]c(c4)c(c(O)cc4O)3)c(c2)cc(c(c2O)O)O)O)O)O +INCHI=InChI=1S/C27H30O16/c1-8-18(32)21(35)23(37)26(40-8)39-7-17-20(34)22(36)24(38)27(43-17)42-16-6-11-12(29)4-10(28)5-15(11)41-25(16)9-2-13(30)19(33)14(31)3-9/h2-6,8,17-18,20-24,26-27,32,34-38H,7H2,1H3,(H4-,28,29,30,31,33)/p+1/t8-,17+,18-,20+,21+,22-,23+,24+,26+,27+/m0/s1 +NAME=Delphinidin-3-O-(6''-O-alpha-rhamnopyranosyl-beta-glucopyranoside) +53.03038 9 +55.05161 19 +55.0576 18 +58.79025 6 +67.0404 9 +67.05455 155 +68.05675 9 +69.07159 40 +77.03714 75 +77.0444 32 +77.06685 6 +79.04545 18 +79.05495 241 +79.11001 6 +79.85172 6 +80.04874 8 +80.0565 7 +81.03935 10 +81.04779 5 +81.07019 238 +82.07097 20 +82.07552 13 +83.04719 9 +83.05295 9 +83.08127 10 +83.0868 6 +83.93068 9 +85.01413 10 +89.67115 11 +91.02942 8 +91.05408 666 +92.04977 11 +92.0548 23 +92.05914 29 +92.15939 7 +92.3892 6 +93.05332 8 +93.0703 486 +93.58314 5 +94.04075 11 +94.07217 20 +94.55843 6 +94.79437 5 +95.00871 13 +95.04281 8 +95.04984 36 +95.05554 15 +95.08426 290 +96.0872 30 +96.09393 6 +97.02732 7 +97.03384 17 +97.06309 7 +97.49565 6 +103.05112 34 +103.05816 42 +105.05009 19 +105.06982 1000 +105.20243 6 +106.0395 6 +106.07396 71 +107.05107 102 +107.08467 390 +107.10461 9 +107.9571 6 +108.08822 42 +108.09644 11 +109.02725 53 +109.04842 6 +109.07082 13 +109.08782 18 +109.10066 92 +109.73253 6 +111.04237 9 +111.18037 8 +113.02244 9 +113.05888 6 +115.04301 14 +115.05631 83 +115.16881 7 +116.0596 67 +117.06977 359 +118.05664 6 +118.06728 15 +118.0768 33 +118.97888 5 +119.0491 35 +119.06369 6 +119.08462 601 +119.11095 7 +120.05705 7 +120.0705 7 +120.08472 29 +120.09289 22 +121.06506 64 +121.07348 23 +121.09069 13 +121.10189 148 +122.06594 14 +122.10601 7 +123.04105 15 +123.04936 46 +123.0816 19 +123.11182 13 +123.11853 33 +124.05013 14 +124.07432 5 +124.12009 9 +124.13123 6 +124.27411 8 +127.05548 27 +128.03499 6 +128.06082 168 +128.07291 24 +129.0668 125 +129.07349 118 +130.0759 156 +130.08585 19 +130.99266 5 +131.04527 33 +131.05212 14 +131.0844 505 +131.70306 7 +132.0495 16 +132.06142 12 +132.09421 40 +133.04317 9 +133.06532 45 +133.07957 9 +133.10139 523 +134.10364 34 +134.11154 30 +135.03569 6 +135.04976 32 +135.08134 28 +135.10847 7 +135.11694 64 +136.05127 6 +136.11742 6 +136.12567 6 +137.05449 13 +137.06235 15 +137.09517 8 +138.06831 12 +140.06535 11 +141.07083 206 +141.42934 8 +141.94521 7 +142.07724 154 +142.08737 13 +142.65857 6 +143.04987 11 +143.08463 321 +144.06995 5 +144.08986 87 +144.09924 35 +145.06487 88 +145.10034 347 +146.07329 85 +146.10103 27 +146.10724 38 +146.68013 7 +147.03983 11 +147.04715 28 +147.07518 57 +147.08206 67 +147.11754 225 +148.05159 5 +148.08424 13 +148.11057 6 +148.11908 13 +149.06276 12 +149.08907 7 +149.11647 6 +149.12393 7 +149.13461 23 +151.03491 6 +151.11266 7 +152.10826 6 +153.05626 12 +153.06984 126 +154.07239 54 +154.08286 28 +154.59076 5 +154.68895 9 +155.08122 48 +155.08832 117 +156.09175 76 +157.05852 14 +157.09734 80 +157.1048 54 +158.06708 8 +158.07521 6 +158.09898 17 +158.10995 62 +158.9722 6 +159.04549 8 +159.0641 15 +159.07986 38 +159.09599 7 +159.11801 145 +159.77995 9 +160.08421 33 +160.09309 9 +160.11708 37 +160.50819 5 +160.96831 7 +161.02707 6 +161.06731 19 +161.0818 13 +161.09749 81 +161.1288 61 +161.13631 68 +162.10648 12 +162.13625 12 +163.07108 6 +163.10539 13 +165.05605 6 +165.0677 46 +165.07521 33 +166.05397 8 +166.06316 6 +166.07712 62 +166.11459 5 +167.07735 35 +167.08803 86 +168.05446 14 +168.07103 9 +168.09077 75 +168.10431 15 +169.101 159 +170.07225 16 +170.08585 19 +170.105 11 +170.3295 6 +171.07799 59 +171.09921 7 +171.11816 97 +172.12163 20 +173.05763 14 +173.10011 34 +173.11578 7 +173.13098 102 +173.13663 46 +174.05769 6 +174.06711 6 +174.10501 7 +174.13457 15 +174.14873 14 +175.07838 5 +175.14857 7 +176.11551 6 +178.07619 28 +178.08548 5 +179.07869 27 +179.09128 17 +180.09477 24 +181.07326 11 +181.09343 46 +181.10225 84 +181.11607 7 +182.06792 7 +182.10295 54 +182.11208 32 +183.08353 21 +183.1127 25 +183.12064 93 +184.08342 18 +184.10774 6 +184.12271 19 +185.07466 7 +185.08531 7 +185.09412 18 +185.12102 17 +185.12956 40 +186.0952 10 +186.12915 13 +187.07289 10 +187.08156 12 +187.11351 13 +187.1492 24 +189.08379 10 +189.09062 26 +189.13332 7 +192.07196 6 +192.0847 18 +192.09319 45 +193.09265 7 +193.10254 21 +194.06699 14 +194.10994 22 +195.08113 6 +195.1178 66 +196.13307 14 +197.11346 7 +197.12744 12 +197.1333 28 +198.10806 11 +198.12888 7 +198.13986 7 +199.06982 14 +199.10965 9 +199.14958 59 +200.08754 6 +200.09747 6 +201.1055 7 +201.15868 6 +202.15819 6 +202.17055 6 +203.08461 14 +205.07146 10 +205.1001 34 +205.1192 5 +206.10185 8 +206.11455 15 +207.08215 15 +207.10902 21 +207.12332 25 +207.99229 6 +208.0957 5 +209.09462 6 +209.13071 14 +209.14169 31 +211.10042 6 +211.11455 10 +211.12228 8 +211.14609 29 +211.15628 6 +211.21368 8 +212.1463 17 +212.15855 18 +213.0963 7 +213.15767 9 +214.12508 7 +214.13797 11 +214.16391 10 +216.14792 6 +217.09235 6 +219.11507 14 +220.095 7 +221.13313 28 +222.14868 8 +223.1586 9 +227.10793 17 +229.16345 9 +231.16948 7 +232.14691 10 +233.13376 14 +234.14954 6 +234.81161 7 +235.15154 22 +237.13528 8 +237.16701 8 +239.14229 5 +239.17485 11 +239.18098 6 +239.89276 5 +240.17812 5 +240.26027 6 +242.12508 6 +242.72453 6 +246.13538 6 +248.01703 6 +249.11292 11 +249.15572 8 +251.17773 7 +256.21249 10 +259.35675 6 +261.15585 6 +262.14734 8 +265.18732 7 +274.17303 10 +277.19409 9 +281.14896 5 +285.16055 5 +289.16821 7 +295.19949 11 +307.20993 7 +324.68466 5 +369.23483 16 +END IONS + +BEGIN IONS +TITLE=64560 +msLevel=2 +PEPMASS=273.138625 +CHARGE=1+ +SMILES=C1CCC2=CC3=C4N=C5C=CC=CC5=C4C=CN3C=C2C1 +INCHI=InChI=1S/C19H16N2/c1-2-6-14-12-21-10-9-16-15-7-3-4-8-17(15)20-19(16)18(21)11-13(14)5-1/h3-4,7-12H,1-2,5-6H2 +NAME=Sempervirine +77.03973 6 +103.05298 22 +104.05452 9 +128.04712 7 +130.06487 1000 +131.06752 101 +132.0706 13 +END IONS + +BEGIN IONS +TITLE=64589 +msLevel=2 +PEPMASS=385.2121838 +CHARGE=1+ +SMILES=COC(=O)[C@@]12C[C@H]3C[C@H]([C@H](C)O)[C@@H]1N(C3)CCC1=C2NC2=C1C=C(OC)C=C2 +INCHI=InChI=1S/C22H28N2O4/c1-12(25)16-8-13-10-22(21(26)28-3)19-15(6-7-24(11-13)20(16)22)17-9-14(27-2)4-5-18(17)23-19/h4-5,9,12-13,16,20,23,25H,6-8,10-11H2,1-3H3/t12-,13+,16+,20-,22+/m0/s1 +NAME=Voacristine +107.04568 51 +119.04062 19 +119.05291 19 +123.03834 20 +129.08026 22 +135.04558 18 +141.06668 36 +144.06604 21 +147.06323 43 +147.0757 23 +157.05943 36 +165.06572 19 +181.0583 24 +183.08911 19 +193.05333 22 +194.07747 20 +210.06538 38 +211.05869 27 +211.07388 21 +212.08327 42 +227.06114 25 +227.0769 63 +228.05342 18 +228.06749 123 +228.08121 224 +229.08653 1000 +230.08801 129 +230.1035 26 +231.0887 23 +241.08556 39 +271.08914 21 +272.10556 21 +279.07678 19 +295.08411 30 +295.10843 20 +319.10004 19 +355.13501 19 +391.14594 38 +END IONS + +BEGIN IONS +TITLE=68246 +msLevel=2 +PEPMASS=449.1078379 +CHARGE=1+ +SMILES=OC[C@H]1O[C@@H](OC2=C(O)C(O)=C3C(=O)C=C(OC3=C2)C2=CC=C(O)C=C2)[C@H](O)[C@@H](O)[C@@H]1O +INCHI=InChI=1S/C21H20O11/c22-7-14-17(26)19(28)20(29)21(32-14)31-13-6-12-15(18(27)16(13)25)10(24)5-11(30-12)8-1-3-9(23)4-2-8/h1-6,14,17,19-23,25-29H,7H2/t14-,17-,19+,20-,21-/m1/s1 +NAME=Plantaginin +65.03515 44 +66.04109 24 +79.05058 18 +92.0276 20 +93.03357 62 +106.04105 1000 +107.04108 36 +107.04877 233 +107.06096 21 +119.05 91 +119.05803 21 +120.05089 44 +129.03491 17 +130.03537 37 +130.04298 63 +131.04224 19 +131.05116 104 +132.04922 18 +132.05827 44 +133.06299 241 +134.0674 18 +138.17578 19 +143.04726 93 +143.05423 53 +144.06491 18 +145.02484 19 +145.06499 44 +145.07278 46 +146.07253 45 +147.04181 40 +149.05798 26 +157.06595 19 +162.06635 19 +163.07422 48 +167.04828 26 +173.06512 19 +194.06644 18 +222.06496 19 +231.08104 16 +END IONS + +BEGIN IONS +TITLE=70261 +msLevel=2 +PEPMASS=447.09328504783 +CHARGE=1+ +SMILES=C1=CC(=C(C=C1C2=CC(=O)C3=C(C=C(C=C3O2)O)O)O)O[C@H]4[C@@H]([C@H]([C@@H]([C@H](O4)CO)O)O)O +INCHI=InChI=1S/C21H20O11/c22-7-16-18(27)19(28)20(29)21(32-16)31-13-2-1-8(3-10(13)24)14-6-12(26)17-11(25)4-9(23)5-15(17)30-14/h1-6,16,18-25,27-29H,7H2/t16-,18-,19+,20-,21-/m1/s1 +NAME=Luteolin-4'-O-glucoside +128.04684 40 +209.15263 20 +255.0491 33 +255.06816 298 +446.42679 18 +459.12598 49 +END IONS + +BEGIN IONS +TITLE=70921 +msLevel=2 +PEPMASS=591.17192924783 +CHARGE=1+ +SMILES=C[C@H]1[C@@H]([C@H]([C@H]([C@@H](O1)O[C@@H]2[C@H]([C@@H]([C@H](O[C@H]2OC3=CC(=C4C(=C3)OC(=CC4=O)C5=CC=C(C=C5)OC)O)CO)O)O)O)O)O +INCHI=InChI=1S/C28H32O14/c1-11-21(32)23(34)25(36)27(38-11)42-26-24(35)22(33)19(10-29)41-28(26)39-14-7-15(30)20-16(31)9-17(40-18(20)8-14)12-3-5-13(37-2)6-4-12/h3-9,11,19,21-30,32-36H,10H2,1-2H3/t11-,19+,21-,22+,23+,24-,25+,26+,27-,28+/m0/s1 +NAME=Fortunellin +78.95506 31 +78.95631 20 +78.95756 51 +78.95882 20 +78.96007 41 +78.96132 20 +78.96258 20 +78.96383 31 +78.96509 20 +78.96634 31 +96.97127 41 +123.1194 20 +152.9866 20 +152.9883 31 +152.9901 41 +152.9918 61 +152.9936 82 +152.9953 61 +152.9971 112 +152.9988 41 +153.0005 41 +153.0023 61 +153.004 31 +153.0093 20 +171.0916 20 +171.0934 20 +171.0953 51 +171.0971 71 +171.099 82 +171.1008 71 +171.1026 92 +171.1045 61 +171.1063 41 +171.1082 71 +171.11 41 +171.1119 20 +171.1156 20 +222.9853 20 +222.9874 20 +222.9895 31 +222.9916 61 +222.9937 41 +222.9958 41 +222.9979 82 +223 102 +223.0021 155 +223.0042 92 +223.0063 82 +223.0085 20 +223.0106 61 +223.0211 31 +236.2964 21 +240.1051 21 +240.2625 21 +240.2647 21 +240.9848 21 +240.987 21 +240.9914 31 +240.9936 21 +240.9958 52 +240.998 73 +241.0002 146 +241.0024 190 +241.0045 369 +241.0067 456 +241.0089 635 +241.0111 488 +241.0133 350 +241.0155 457 +241.0177 307 +241.0199 244 +241.0221 138 +241.0243 105 +241.0265 75 +241.0286 63 +241.0308 32 +241.033 11 +241.0374 11 +241.7016 11 +241.8618 11 +241.8662 11 +242.6524 11 +255.241 20 +259.0194 41 +259.0217 51 +259.024 41 +259.0262 61 +259.0285 41 +259.0308 20 +277.2018 31 +277.2041 41 +277.2065 71 +277.2088 122 +277.2112 204 +277.2135 102 +277.2159 102 +277.2182 235 +277.2206 102 +277.2229 92 +277.2253 61 +277.2276 82 +277.2299 20 +277.2346 52 +277.2393 52 +282.3657 21 +282.3846 21 +282.4107 21 +282.4155 21 +283.2412 20 +283.2436 42 +283.246 31 +283.2484 104 +283.2508 242 +283.2531 211 +283.2555 460 +283.2579 385 +283.2603 524 +283.2626 699 +283.265 634 +283.2674 504 +283.2697 513 +283.2721 268 +283.2745 333 +283.2769 139 +283.2792 129 +283.2816 129 +283.284 53 +283.2864 97 +283.2888 32 +283.2935 11 +283.2959 11 +284.2609 31 +286.695 11 +287.4911 21 +292.5323 21 +293.2205 11 +294.425 21 +294.8803 21 +294.9603 21 +295.2002 42 +295.2075 32 +295.2099 53 +295.2124 179 +295.2148 160 +295.2172 329 +295.2196 404 +295.222 414 +295.2245 555 +295.2269 711 +295.2293 552 +295.2318 469 +295.2342 404 +295.2366 289 +295.239 117 +295.2415 254 +295.2439 96 +295.2463 32 +295.2487 53 +295.2512 85 +295.256 11 +295.2875 11 +295.2924 11 +295.5422 11 +296.2339 21 +297.0115 21 +297.0261 31 +297.0286 20 +297.031 31 +297.0334 82 +297.0359 51 +297.0383 102 +297.0407 71 +297.0432 51 +297.0456 71 +297.048 31 +297.0529 20 +297.0553 31 +315.0283 20 +315.0383 41 +315.0433 41 +315.0458 51 +315.0483 31 +315.0508 20 +315.0533 20 +315.0558 20 +315.0583 31 +417.947 20 +417.9787 21 +419.2287 41 +419.2316 51 +419.2345 82 +419.2374 92 +419.2403 93 +419.2432 93 +419.246 134 +419.2489 247 +419.2518 237 +419.2547 349 +419.2576 217 +419.2605 144 +419.2634 187 +419.2663 144 +419.2692 144 +419.2721 122 +419.2749 112 +419.2778 20 +419.2807 61 +419.2836 31 +419.2952 20 +431.1984 31 +431.2013 41 +431.2043 41 +431.2072 61 +431.2101 41 +431.213 41 +431.216 82 +431.2189 61 +431.2218 122 +431.2248 82 +431.2277 143 +431.2306 102 +431.2336 92 +431.2365 51 +431.2394 20 +431.2423 31 +437.2494 20 +437.2552 20 +437.2582 31 +437.2611 20 +437.2641 20 +437.27 20 +437.2788 31 +437.2818 31 +437.2848 20 +482.1891 20 +521.3034 20 +571.2091 21 +579.4976 20 +581.2686 31 +581.272 72 +581.2755 41 +581.2789 31 +581.2822 52 +581.2856 72 +581.2891 157 +581.2924 136 +581.2958 144 +581.2993 238 +581.3027 209 +581.306 229 +581.3094 236 +581.3129 291 +581.3163 289 +581.3196 206 +581.3231 186 +581.3265 113 +581.3299 186 +581.3333 123 +581.3367 72 +581.3401 20 +581.3434 31 +581.3469 61 +581.3503 41 +581.3537 20 +581.3571 31 +581.3639 20 +590.8842 20 +593.2463 41 +593.2532 20 +593.2567 51 +593.2635 71 +593.267 82 +593.2704 61 +593.2739 133 +593.2773 102 +593.2807 41 +593.2842 102 +593.2876 41 +593.291 71 +593.2944 41 +593.2979 31 +593.3013 31 +593.3047 20 +593.3082 20 +593.3116 31 +593.3151 20 +593.3323 20 +599.2931 31 +599.2966 31 +599.3 20 +599.3035 20 +599.3069 51 +599.3104 31 +599.3138 61 +599.3173 71 +599.3207 51 +599.3242 102 +599.3276 31 +599.3311 102 +599.3345 20 +599.338 31 +599.3414 31 +599.3449 20 +599.3484 20 +599.3622 31 +600.316 20 +611.2762 20 +715.4631 31 +715.4706 31 +715.4744 20 +715.482 20 +715.4896 20 +715.5084 41 +715.5122 20 +715.5159 31 +874.739 20 +874.7431 20 +874.7473 20 +874.7515 20 +874.7556 31 +874.7598 20 +874.7806 20 +874.7849 61 +874.789 41 +874.7932 51 +874.7974 31 +874.8057 31 +874.8182 31 +874.8224 41 +874.8349 20 +877.2906 20 +877.3992 20 +877.4243 20 +877.4369 20 +877.4619 20 +877.487 145 +877.4912 174 +877.4954 395 +877.4996 554 +877.5037 774 +877.5079 1125 +877.5121 1249 +877.5163 1991 +877.5204 2783 +877.5247 3443 +877.5288 3905 +877.533 4540 +877.5372 4968 +877.5413 5051 +877.5455 5606 +877.5497 6233 +877.5539 7218 +877.558 6170 +877.5623 5781 +877.5664 5001 +877.5706 4699 +877.5748 3811 +877.579 3336 +877.5831 2225 +877.5873 1576 +877.5915 1218 +877.5957 951 +877.5999 543 +877.6041 389 +877.6082 288 +877.6124 185 +877.6166 153 +877.6208 140 +877.6249 119 +877.6292 98 +877.6333 27 +877.6375 76 +877.6417 44 +877.6459 34 +877.7211 18 +877.8047 20 +878.5114 51 +878.5155 41 +878.5281 61 +878.5322 51 +878.5364 92 +878.5406 61 +878.5448 61 +878.549 112 +878.5532 82 +878.5574 92 +878.5615 51 +878.5657 61 +878.5699 41 +878.5741 71 +878.5782 41 +878.5825 51 +878.5866 20 +878.5908 31 +878.595 31 +878.6033 41 +879.5613 20 +END IONS + +BEGIN IONS +TITLE=74186 +msLevel=2 +PEPMASS=339.2067 +CHARGE=1+ +SMILES=COC(=O)C1CC23CCCN4CCC5(C24)C2=CC=CC=C2NC15C3C +INCHI=InChI=1S/C21H26N2O2/c1-13-19-8-5-10-23-11-9-20(18(19)23)14-6-3-4-7-16(14)22-21(13,20)15(12-19)17(24)25-2/h3-4,6-7,13,15,18,22H,5,8-12H2,1-2H3 +NAME=Pseudocopsinine +80 4285 +81 50060 +136 27610 +137 121686 +216 11684 +217 22294 +END IONS + +BEGIN IONS +TITLE=75490 +msLevel=2 +PEPMASS=878.5001 +CHARGE=1+ +SMILES=CC\C=C/C\C=C/C\C=C/C\C=C/C\C=C/C\C=C/CCC(=O)OCC(COP(O)(=O)OCC(N)C(O)=O)OC(=O)CC\C=C/C\C=C/C\C=C/C\C=C/C\C=C/C\C=C/CC +INCHI=InChI=1S/C50H74NO10P/c1-3-5-7-9-11-13-15-17-19-21-23-25-27-29-31-33-35-37-39-41-48(52)58-43-46(44-59-62(56,57)60-45-47(51)50(54)55)61-49(53)42-40-38-36-34-32-30-28-26-24-22-20-18-16-14-12-10-8-6-4-2/h5-8,11-14,17-20,23-26,29-32,35-38,46-47H,3-4,9-10,15-16,21-22,27-28,33-34,39-45,51H2,1-2H3,(H,54,55)(H,56,57)/b7-5-,8-6-,13-11-,14-12-,19-17-,20-18-,25-23-,26-24-,31-29-,32-30-,37-35-,38-36- +NAME=PS 44:12 +119 1967 +122 11243 +123 29356 +124 1973 +138 20918 +139 61646 +146 4304 +147 7950 +161 4266 +165 2062 +207 2545 +END IONS + +BEGIN IONS +TITLE=76900 +msLevel=2 +CHARGE=1+ +SMILES=C[C@@H]1CCC2[C@](C)(CC[C@H](O)[C@@]2(C)CO)[C@@]12CCC1(COC(=O)C1)O2 +INCHI=InChI=1S/C20H32O5/c1-13-4-5-14-17(2,11-21)15(22)6-7-18(14,3)20(13)9-8-19(25-20)10-16(23)24-12-19/h13-15,21-22H,4-12H2,1-3H3/t13-,14?,15+,17+,18+,19?,20-/m1/s1 +NAME=Lagohirsin +376 322423 +377 889188 +378 77924 +END IONS + +BEGIN IONS +TITLE=78049 +msLevel=2 +PEPMASS=102.06 +CHARGE=1+ +SMILES=C1CC1(C(=O)O)N +INCHI=InChI=1S/C4H7NO2/c5-4(1-2-4)3(6)7/h1-2,5H2,(H,6,7) +NAME=1-Aminocyclopropanecarboxylic acid +227.0383 1312 +229.0544 673.1 +255.0336 2634 +257.0503 495.1 +284.0364 4404 +285.0443 5694 +593.1506 6804 +END IONS + +BEGIN IONS +TITLE=79526 +msLevel=2 +PEPMASS=465.41 +CHARGE=1+ +SMILES=CC1C(C(C(C(O1)OC2=C(OC3=CC(=CC(=C3C2=O)O)O)C4=CC(=C(C(=C4)O)O)O)O)O)O +INCHI=InChI=1S/C21H20O12/c1-6-14(26)17(29)18(30)21(31-6)33-20-16(28)13-9(23)4-8(22)5-12(13)32-19(20)7-2-10(24)15(27)11(25)3-7/h2-6,14,17-18,21-27,29-30H,1H3 +NAME=myricetin-3-O-rhamnoside +59.0152 355.5 +71.0148 581.4 +85.0298 91.98 +89.0249 516.9 +101.0245 592.9 +113.0245 261.1 +119.0348 212.6 +143.0354 82.99 +161.0457 107 +179.0561 385.5 +341.1084 920 +END IONS + +BEGIN IONS +TITLE=84123 +msLevel=2 +PEPMASS=459.27478 +CHARGE=1+ +SMILES=[H]C([H])(C(C([H])([H])[H])(C([H])([H])[H])C([H])([H])[H])C(OC([H])([H])C(=O)C(O[H])(C4([H])[H])C(C([H])([H])[H])(C3([H])[H])C(C([H])([H])4)([H])C([H])(C([H])(C3([H])O[H])1)C(C(C(=C([H])2)C1(C([H])=C(C2=O)[H])C([H])([H])[H])([H])[H])([H])[H])=O +INCHI=InChI=1S/C27H38O6/c1-24(2,3)14-22(31)33-15-21(30)27(32)11-9-19-18-7-6-16-12-17(28)8-10-25(16,4)23(18)20(29)13-26(19,27)5/h8,10,12,18-20,23,29,32H,6-7,9,11,13-15H2,1-5H3 +NAME=Prednisolone_Tebutate +99.0801 101.217 +279.1704 228.949 +289.1608 237.055 +307.1671 989.608 +325.1786 1965.855 +405.2417 101.722 +423.2524 916.568 +441.2622 7348.575 +459.2719 10000 +END IONS + +BEGIN IONS +TITLE=84355 +CHARGE=1+ +SMILES=OCc(c4)cc(c(c(O)4)2)C([H])(c(c3)c(c(O)cc3)C(=O)2)C([H])(O1)C(O)C(O)C(O)C(CO)1 +INCHI=InChI=1S/C21H22O9/c22-6-8-4-10-14(21-20(29)19(28)17(26)13(7-23)30-21)9-2-1-3-11(24)15(9)18(27)16(10)12(25)5-8/h1-5,13-14,17,19-26,28-29H,6-7H2/t13-,14+,17-,19+,20-,21+/m1/s1 +NAME=Barbaloin +401.1176 6194632 +419.1317 116700801 +420.1342 28560839 +854.2835 7201988 +END IONS + +BEGIN IONS +TITLE=84825 +msLevel=2 +PEPMASS=301.1445 +PEPMASSINT=301 +CHARGE=1+ +SMILES=CC(CC1=CC(=C(C=C1)O)O)C(C)CC2=CC(=C(C=C2)O)O +INCHI=InChI=1S/C18H22O4/c1-11(7-13-3-5-15(19)17(21)9-13)12(2)8-14-4-6-16(20)18(22)10-14/h3-6,9-12,19-22H,7-8H2,1-2H3 +NAME=Nordihydroguaiaretic acid +109.0292 370 +110.0336 179 +122.0366 341 +300.131 100 +301.1436 18336 +END IONS + +BEGIN IONS +TITLE=84829 +msLevel=2 +PEPMASS=301.1445 +PEPMASSINT=301 +CHARGE=1+ +SMILES=CC(CC1=CC(=C(C=C1)O)O)C(C)CC2=CC(=C(C=C2)O)O +INCHI=InChI=1S/C18H22O4/c1-11(7-13-3-5-15(19)17(21)9-13)12(2)8-14-4-6-16(20)18(22)10-14/h3-6,9-12,19-22H,7-8H2,1-2H3 +NAME=Nordihydroguaiaretic acid +108.0233 224 +109.0278 295 +122.0372 1801 +123.0449 324 +135.0476 70 +177.0895 119 +273.1472 207 +301.1437 12765 +END IONS + +BEGIN IONS +TITLE=84964 +msLevel=2 +PEPMASS=407.1347 +PEPMASSINT=351 +CHARGE=1+ +SMILES=CC1C(=O)OC2C1(C34C(=O)OC5C3(C2)C6(C(C5)C(C)(C)C)C(C(=O)OC6O4)O)O +INCHI=InChI=1S/C20H24O9/c1-7-12(22)26-10-6-17-9-5-8(16(2,3)4)18(17)11(21)13(23)28-15(18)29-20(17,14(24)27-9)19(7,10)25/h7-11,15,21,25H,5-6H2,1-4H3 +NAME=Ginkgolide A +277.0995 21 +289.1473 26 +291.1494 27 +301.1531 46 +307.1414 38 +317.1308 36 +319.1555 143 +333.1339 63 +333.1609 22 +335.1505 152 +351.1438 1487 +351.2007 83 +363.144 206 +379.1447 117 +407.1343 680 +END IONS + +BEGIN IONS +TITLE=84965 +msLevel=2 +PEPMASS=304.1918 +PEPMASSINT=168 +CHARGE=1+ +SMILES=CC(C)/C=C/CCCCC(=O)NCC1=CC(=C(C=C1)O)OC +INCHI=InChI=1S/C18H27NO3/c1-14(2)8-6-4-5-7-9-18(21)19-13-15-10-11-16(20)17(12-15)22-3/h6,8,10-12,14,20H,4-5,7,9,13H2,1-3H3,(H,19,21)/b8-6+ +NAME=Capsaicin +168.1387 24842 +168.1767 882 +168.2301 182 +168.2585 363 +304.1906 2143 +END IONS + +BEGIN IONS +TITLE=85235 +msLevel=2 +PEPMASS=361.202 +PEPMASSINT=331 +CHARGE=1+ +SMILES=C[C@]12CCC(=O)C=C1CC[C@@H]3[C@@H]2[C@H](C[C@]4([C@H]3CC[C@@]4(C(=O)CO)O)C)O +INCHI=InChI=1S/C21H30O5/c1-19-7-5-13(23)9-12(19)3-4-14-15-6-8-21(26,17(25)11-22)20(15,2)10-16(24)18(14)19/h9,14-16,18,22,24,26H,3-8,10-11H2,1-2H3/t14-,15-,16-,18+,19-,20-,21-/m0/s1 +NAME=(8S,9S,10R,11S,13S,14S,17R)-11,17-dihydroxy-17-(2-hydroxyacetyl)-10,13-dimethyl-2,6,7,8,9,11,12,14,15,16-decahydro-1H-cyclopenta[a]phenanthren-3-one +106.7645 39 +116.93 51 +125.0583 108 +135.4193 25 +189.0905 236 +196.6881 26 +243.1448 24 +282.1256 242 +287.1604 22 +290.6498 25 +297.15 645 +297.1844 45 +297.2038 27 +308.2317 23 +313.1869 38 +315.1567 247 +315.1806 24 +315.2091 24 +331.1893 1266 +331.2263 44 +331.2483 51 +331.2753 41 +331.3588 23 +332.1059 20 +354.5234 80 +END IONS + +BEGIN IONS +TITLE=89570 +CHARGE=1+ +SMILES=c(c3)ccc(c3)CC(=O)NC(C(=O)1)C([H])(S2)N1C(C(O)=O)C(C)(C)2 +INCHI=InChI=1S/C16H18N2O4S/c1-16(2)12(15(21)22)18-13(20)11(14(18)23-16)17-10(19)8-9-6-4-3-5-7-9/h3-7,11-12,14H,8H2,1-2H3,(H,17,19)(H,21,22)/t11-,12+,14-/m1/s1 +NAME=Penicillin G +100 47 +101 153 +122 39 +142 16 +159 71 +160 239 +161 20 +162 12 +176 43 +177 999 +178 98 +179 12 +194 82 +215 121 +216 20 +218 20 +307 16 +313 12 +329 24 +335 858 +336 145 +337 51 +345 27 +352 110 +353 24 +354 16 +357 12 +373 388 +374 63 +375 43 +376 12 +377 12 +END IONS + diff --git a/man/compareSpectriPy.Rd b/man/compareSpectriPy.Rd index f899409..0a0acb0 100644 --- a/man/compareSpectriPy.Rd +++ b/man/compareSpectriPy.Rd @@ -2,43 +2,43 @@ % Please edit documentation in R/compareSpectriPy.R \name{compareSpectriPy} \alias{compareSpectriPy} -\alias{CosineGreedyParam} -\alias{CosineHungarianParam} -\alias{ModifiedCosineParam} -\alias{NeutralLossesCosineParam} -\alias{compareSpectriPy,Spectra,Spectra,CosineGreedyParam-method} -\alias{compareSpectriPy,Spectra,missing,CosineGreedyParam-method} -\title{Spectra similarity calculations using matchms} +\alias{CosineGreedy} +\alias{CosineHungarian} +\alias{ModifiedCosine} +\alias{NeutralLossesCosine} +\alias{compareSpectriPy,Spectra,Spectra,CosineGreedy-method} +\alias{compareSpectriPy,Spectra,missing,CosineGreedy-method} +\title{Spectra similarity calculations using Python's matchms library} \usage{ -CosineGreedyParam(tolerance = 0.1, mzPower = 0, intensityPower = 1) +CosineGreedy(tolerance = 0.1, mz_power = 0, intensity_power = 1) -CosineHungarianParam(tolerance = 0.1, mzPower = 0, intensityPower = 1) +CosineHungarian(tolerance = 0.1, mz_power = 0, intensity_power = 1) -ModifiedCosineParam(tolerance = 0.1, mzPower = 0, intensityPower = 1) +ModifiedCosine(tolerance = 0.1, mz_power = 0, intensity_power = 1) -NeutralLossesCosineParam( +NeutralLossesCosine( tolerance = 0.1, - mzPower = 0, - intensityPower = 1, - ignorePeaksAbovePrecursor = TRUE + mz_power = 0, + intensity_power = 1, + ignore_peaks_above_precursor = TRUE ) -\S4method{compareSpectriPy}{Spectra,Spectra,CosineGreedyParam}(x, y, param, ...) +\S4method{compareSpectriPy}{Spectra,Spectra,CosineGreedy}(x, y, param, ...) -\S4method{compareSpectriPy}{Spectra,missing,CosineGreedyParam}(x, y, param, ...) +\S4method{compareSpectriPy}{Spectra,missing,CosineGreedy}(x, y, param, ...) } \arguments{ -\item{tolerance}{\code{numeric(1)}: tolerated differences in peaks' m/z. Peaks +\item{tolerance}{\code{numeric(1)}: tolerated differences in the peaks' m/z. Peaks with m/z differences \verb{<= tolerance} are considered matching.} -\item{mzPower}{\code{numeric(1)}: the power to raise m/z to in the cosine +\item{mz_power}{\code{numeric(1)}: the power to raise m/z to in the cosine function. The default is 0, in which case the peak intensity products will not depend on the m/z ratios.} -\item{intensityPower}{\code{numeric(1)}: the power to raise intensity to in the +\item{intensity_power}{\code{numeric(1)}: the power to raise intensity to in the cosine function. The default is 1.} -\item{ignorePeaksAbovePrecursor}{For \code{NeutralLossesCosineParam()}: +\item{ignore_peaks_above_precursor}{For \code{NeutralLossesCosine()}: \code{logical(1)}: if \code{TRUE} (the default), peaks with m/z values larger than the precursor m/z are ignored.} @@ -47,56 +47,70 @@ the precursor m/z are ignored.} \item{y}{A \code{\link[Spectra:Spectra]{Spectra::Spectra()}} object to compare against. If missing, spectra similarities are calculated between all spectra in \code{x}.} -\item{param}{one of parameter classes listed above (such as -\code{CosineGreedyParam}) defining the similarity scoring function in python +\item{param}{One of the parameter classes listed above (such as +\code{CosineGreedy}) defining the similarity scoring function in Python and its parameters.} \item{...}{ignored.} } \value{ -\code{compareSpectriPy()} returns a \code{numeric} matrix with the scores, -number of rows being equal to \code{length(x)} and number of columns equal to -\code{length(y)}. +\code{compareSpectriPy()} Returns a \code{numeric} matrix with the scores, +with the number of rows equal to \code{length(x)} and the number of columns +equal to \code{length(y)}. } \description{ The \code{compareSpectriPy()} function allows to calculate spectral similarity -scores using the \verb{calculate_scores module} of the python -\href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html}{matchms.similarity package} -package. +scores using the \code{calculate_scores()} function of the Python +\href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.html}{matchms.similarity}. +module. Selection and configuration of the algorithm can be performed with one of the -parameter objects: +\emph{parameter} objects/functions: \itemize{ -\item \code{CosineGreedyParam}: calculate the \emph{cosine similarity score} between -spectra. The score is calculated by finding best possible matches between -peaks of two spectra. Two peaks are considered a potential match if their -m/z ratios lie within the given \code{tolerance}. The underlying peak assignment -problem is here solved in a \emph{greedy} way. This can perform notably faster, -but does occasionally deviate slightly from a fully correct solution (as -with the \code{CosineHungarianParam} algorithm). In practice this will rarely -affect similarity scores notably, in particular for smaller tolerances. The -algorithm can be configured with parameters \code{tolerance}, \code{mzPower} and -\code{intensityPower} (see parameter description for more details). -\item \code{CosineHungarianParam}: calculate the \emph{cosine similarity score} as with -\code{CosineGreedyParam}, but using the Hungarian algorithm to find the best +\item \code{CosineGreedy()}: calculate the \emph{cosine similarity score} between +spectra. The score is calculated by finding the best possible matches +between peaks of two spectra. Two peaks are considered a potential match if +their m/z ratios lie within the given \code{tolerance}. The underlying peak +assignment problem is here solved in a \emph{greedy} way. This can perform +notably faster, but does occasionally deviate slightly from a fully correct +solution (as with the \code{CosineHungarian} algorithm). In practice this +will rarely affect similarity scores notably, in particular for smaller +tolerances. The algorithm can be configured with parameters \code{tolerance}, +\code{mz_power} and \code{intensity_power} (see parameter description for more +details). See also +\href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.CosineGreedy.html}{matchms CosineGreedy} for more information. +\item \code{CosineHungarian()}: calculate the \emph{cosine similarity score} as with +\code{CosineGreedy}, but using the Hungarian algorithm to find the best matching peaks between the compared spectra. The algorithm can be -configured with parameters \code{tolerance}, \code{mzPower} and \code{intensityPower} -(see parameter description for more details). -\item \code{ModifiedCosineParam}: The modified cosine score aims at quantifying the +configured with parameters \code{tolerance}, \code{mz_power} and \code{intensity_power} +(see parameter description for more details). See also +\href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.CosineHungarian.html}{matchms CosingHungarian} for more information. +\item \code{ModifiedCosine()}: The modified cosine score aims at quantifying the similarity between two mass spectra. The score is calculated by finding -best possible matches between peaks of two spectra. Two peaks are +the best possible matches between peaks of two spectra. Two peaks are considered a potential match if their m/z ratios lie within the given \code{tolerance}, or if their m/z ratios lie within the tolerance once a -mass-shift is applied. The mass shift is simply the difference in +mass shift is applied. The mass shift is simply the difference in precursor-m/z between the two spectra. -\item \code{NeutralLossesCosineParam}: The neutral losses cosine score aims at +See also \href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.ModifiedCosine.html}{matchms ModifiedCosine} for more information. +\item \code{NeutralLossesCosine()}: The neutral losses cosine score aims at quantifying the similarity between two mass spectra. The score is -calculated by finding best possible matches between peaks of two spectra. -Two peaks are considered a potential match if their m/z ratios lie within -the given \code{tolerance} once a mass-shift is applied. The mass shift is the -difference in precursor-m/z between the two spectra. +calculated by finding the best possible matches between peaks of two +spectra. Two peaks are considered a potential match if their m/z ratios lie +within the given \code{tolerance} once a mass shift is applied. The mass shift +is the difference in precursor-m/z between the two spectra. See also +\href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.NeutralLossesCosine.html}{matchms NeutralLossesCosine} for more information. +\item \code{FingerprintSimilarity()}: Calculate similarity between molecules based +on their fingerprints. For this similarity measure to work, fingerprints +are expected to be derived by running \emph{add_fingerprint()}. See also +\href{https://matchms.readthedocs.io/en/latest/api/matchms.similarity.FingerprintSimilarity.html}{matchms FingerprintSimilarity} for more information. } } +\note{ +Parameters and algorithms are named as originally defined in the \emph{matchms} +library (i.e. all parameters in \emph{snake_case} while \emph{CamelCase} is used +for the algorithms. +} \examples{ library(Spectra) @@ -109,38 +123,41 @@ DF <- DataFrame( DF$intensity <- list( c(340.0, 416, 2580, 412), c(388.0, 3270, 85, 54, 10111), - c(3.407, 47.494, 3.094, 100.0, 13.240)) + c(3.407, 47.494, 3.094, 100.0, 13.240) +) DF$mz <- list( c(135.0432, 138.0632, 163.0375, 195.0880), c(110.0710, 138.0655, 138.1057, 138.1742, 195.0864), - c(109.2, 124.2, 124.5, 170.16, 170.52)) + c(109.2, 124.2, 124.5, 170.16, 170.52) +) sps <- Spectra(DF) ## Calculate pairwise similarity beween all spectra within sps with ## matchms' CosineGreedy algorithm ## Note: the first compareSpectriPy will take longer because the Python ## environment needs to be set up. -res <- compareSpectriPy(sps, param = CosineGreedyParam()) +res <- compareSpectriPy(sps, param = CosineGreedy()) res ## Next we calculate similarities for all spectra against the first one -res <- compareSpectriPy(sps, sps[1], param = CosineGreedyParam()) +res <- compareSpectriPy(sps, sps[1], param = CosineGreedy()) ## Calculate pairwise similarity of all spectra in sps with matchms' ## ModifiedCosine algorithm -res <- compareSpectriPy(sps, param = ModifiedCosineParam()) +res <- compareSpectriPy(sps, param = ModifiedCosine()) res ## Note that the ModifiedCosine method requires the precursor m/z to be ## known for all input spectra. Thus, it is advisable to remove spectra ## without precursor m/z before using this algorithm. sps <- sps[!is.na(precursorMz(sps))] -compareSpectriPy(sps, param = ModifiedCosineParam()) +compareSpectriPy(sps, param = ModifiedCosine()) } \seealso{ -\code{\link[Spectra:compareSpectra]{Spectra::compareSpectra()}} in the \code{Spectra} package for pure R +\code{\link[Spectra:compareSpectra]{Spectra::compareSpectra()}} in the \emph{Spectra} package for pure R implementations of spectra similarity calculations. } \author{ -Carolin Huber, Michael Witting, Johannes Rainer, Helge Hecht +Carolin Huber, Michael Witting, Johannes Rainer, Helge Hecht, +Marilyn De Graeve } diff --git a/man/conversion.Rd b/man/conversion.Rd new file mode 100644 index 0000000..97c8b14 --- /dev/null +++ b/man/conversion.Rd @@ -0,0 +1,205 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/conversion.R +\name{conversion} +\alias{conversion} +\alias{spectraVariableMapping,missing-method} +\alias{setSpectraVariableMapping} +\alias{defaultSpectraVariableMapping} +\alias{r_to_py.Spectra} +\alias{rspec_to_pyspec} +\alias{pyspec_to_rspec} +\title{Converting between R and Python MS data structures} +\usage{ +\S4method{spectraVariableMapping}{missing}(object, ...) + +setSpectraVariableMapping(x) + +defaultSpectraVariableMapping() + +\method{r_to_py}{Spectra}(x, convert = FALSE) + +rspec_to_pyspec(x, mapping = spectraVariableMapping()) + +pyspec_to_rspec(x, mapping = spectraVariableMapping()) +} +\arguments{ +\item{object}{For \code{spectraVariableMapping()}: not used.} + +\item{...}{For \code{spectraVariableMapping()}: not used.} + +\item{x}{\code{Spectra} object.} + +\item{convert}{Boolean; should Python objects be automatically converted to +their R equivalent? Defaults to \code{FALSE}.} + +\item{mapping}{named \code{character()} vector defining which spectra +variables/metadata should be translated between R and Python and how +they should be renamed. Defaults to \code{spectraVariableMapping()}.} +} +\value{ +For \code{r_to_py.Spectra()} and \code{rspec_to_pyspec()}: Python list of +\code{matchms.Spectrum} objects. For \code{pyspec_to_rspec()}: +\code{\link[Spectra:Spectra]{Spectra::Spectra()}} with the MS data of all \code{matchms.Spectrum} objects +in the submitted \code{list}. +} +\description{ +The \code{rspec_to_pyspec()} and \code{pyspec_to_rspec()} functions allow to convert +(translate) MS data structures between R and Python. At present the +R \code{\link[Spectra:Spectra]{Spectra::Spectra()}} objects are translated into a list of +\href{https://github.com/matchms/matchms}{matchms} Python \code{matchms.Spectrum} +objects. For better integration with the \emph{reticulate} R package also a +\code{r_to_py.Spectra()} method is available. + +The mapping of spectra variables (in R) to (Python) spectra metadata can +be configured and defined with the \code{setSpectraVariableMapping()} and +\code{spectraVariableMapping()}. These get and set the \emph{global} (system wide) +setting and are thus also used by the \code{r_to_py()} method. + +See the indivudual function's documentation for more details. + +Function to convert R Spectra objects into a Python list of matchms Spectrum +objects using the \code{reticulate} package. +} +\section{Translation of MS data objects}{ + + +MS data structures can be translated between R and Python using the +\code{rspec_to_pyspec()} and \code{pyspec_to_rspec()} functions, or with the +\code{r_to_py()} method. +\itemize{ +\item \code{rspec_to_pyspec()} translates an R \code{\link[Spectra:Spectra]{Spectra::Spectra()}} object into a +list of \code{matchms.Spectrum} objects. Parameter \code{mapping} allows to specify +which spectra variables from the \code{Spectra} object \code{x} should be converted +in addition to the peaks data (m/z and intensity values). It defaults to +\code{mapping = spectraVariableMapping()} (See the respective help below for +more information on the variable mapping). While being fast, this function +first loads all peaks and spectra data into memory before translating to +Python data structures. A less memory intense operation could be to call +this function in a loop to only load parts of the data at a time into +memory. +\item \code{pyspec_to_rspec()} translates a single, or a list of \code{matchms.Spectrum} +objects to a \code{\link[Spectra:Spectra]{Spectra::Spectra()}} object. Parameter \code{mapping} allows to +speficy the metadata variables that should be translated and mapped in +addition to the peaks data. +\item \code{r_to_py.Spectra()} is equivalent to \code{rspec_to_pyspec()}. The spectra +variables that should be converted can be configures with +\code{setSpectraVariableMapping()} (see documentation below). +} +} + +\section{Mapping of spectra variables (metadata)}{ + + +Metadata for MS spectra are represented and stored as \emph{spectra variables} +in the R \code{\link[Spectra:Spectra]{Spectra::Spectra()}} objects. Also Python MS data structures +store such metadata along with the mass peak data. While spectra metadata +is thus supported by data structures in both programming languages, +different names and naming conventions are used. The +\code{spectraVariableMapping()} and \code{setSpectraVariableMapping()} functions allow +to define how the names of spectra metadata (spectra variables) should be +translated between R and Python. The \code{r_to_py()} and \code{py_to_r()} functions +will used these to name the spectra variables accordingly. Also, only +spectra metadata/variables in \code{spectraVariableMapping()} will be translated. +The initial mapping is based on this +\href{https://github.com/matchms/matchms/blob/master/matchms/data/known_key_conversions.csv}{definition in matchms}. +\itemize{ +\item \code{defaultSpectraVariableMapping()}: returns the \emph{default} mapping between +spectra variables and \emph{matchms} metadata names. +\item \code{spectraVariableMapping()}: returns the currently defined spectra +variable mapping as a named character vector, with names representing the +names of the spectra variables in R and elements the respective names +of the spectra metadata in Python. Use \code{\link[Spectra:spectraData]{Spectra::spectraVariables()}} on +the \code{Spectra} object that should be converted with \code{r_to_py()} to list +all available spectra variables. \code{r_to_py()} and \code{py_to_r()} for MS data +structures will use this mapping. +\item \code{setSpectraVariableMapping()}: sets/replaces the currently defined mapping +of spectra variable names to Python metadata names. Setting +\code{setSpectraVariableMapping(character())} will only convert the mass peaks +data (m/z and intensity values) but no spectra metadata. +} +} + +\examples{ + +## Import a MGF file as a `Spectra` object +library(MsBackendMgf) +library(SpectriPy) +s <- Spectra( + system.file("extdata", "mgf", "spectra2.mgf", package = "SpectriPy"), + source = MsBackendMgf()) +s + +######################### +## Conversion R to Python + +## A `Spectra` can be translated to a `list` of `matchms.Spectrum` objects +## using either the `r_to_py()` method or the `rspec_to_pyspec()` function: +s_py <- r_to_py(s) +s_py + +## The `s_py` can now be used like any other Python variable within the R +## *reticulate* framework. Below we extract the m/z values of the first +## spectrum +s_py[0]$mz + +## Extracting that information from the `Spectra` object in R +s[1]$mz + +## The `spectraVariableMapping()` defines which spectra variables (metadata) +## should be translated between R and Python: +spectraVariableMapping() + +## The names of that character vector represent the names of the spectra +## variables in R, the elements the name of the metadata variable in Python. +## Below we list the available metadata information from the first +## Spectrum in Python +s_py[0]$metadata + +## `setSpectraVariableMapping()` allows to replace the default mapping +## of variables. Below we e.g. add a new spectra variable to the `Spectra` +## object. +s$new_col <- 1:4 + +## To translate that variable to Python we need to include it to the +## `spectraVariableMapping()`. Below we define to translate only the +## precursor m/z and the new spectra variable to Python. Be aware that +## `setSpectraVariableMapping()` **globally** sets the default for any +## spectra variable mapping between R and Python. Thus, any subsequent +## calls mapping calls will use the same mapping. It is suggested to +## eventually *restore* the default mapping again after the call or +## use the `rspec_to_pyspec()` function instead, that allows to configure +## the mapping using a parameter `mapping`. +setSpectraVariableMapping( + c(precursorMz = "precursor_mz", new_col = "new_col")) +s_py <- r_to_py(s) + +s_py[0]$metadata + +## Restoring the global spectra variable mapping configuration to +## the default mapping: +setSpectraVariableMapping(defaultSpectraVariableMapping()) + +## As an alternative to the `r_to_py()` we can use the `rspec_to_pyspec()` +## function and provide a custom mapping using the `mapping` parameter: +s_py <- rspec_to_pyspec( + s, mapping = c(precursorMz = "precursor_mz", new_col = "new_col")) + +######################### +## Conversion Python to R + +## A `list` of `matchms.Spectrum` objects in Python can be translated into +## the corresponding MS data structure in R (i.e. a `Spectra`) object using +## the `pyspec_to_rspec()` function: +res <- pyspec_to_rspec(s_py) +res + +## All spectra from Python are thus converted into a single `Spectra` object. + +## Or providing a custom variable mapping: +res <- pyspec_to_rspec( + s_py, mapping = c(precursorMz = "precursor_mz", new_col = "new_col")) +res$new_col +} +\author{ +Michael Witting, Johannes Rainer, Wout Bittremieux +} diff --git a/man/filterSpectriPy.Rd b/man/filterSpectriPy.Rd index 4479245..0a9504c 100644 --- a/man/filterSpectriPy.Rd +++ b/man/filterSpectriPy.Rd @@ -6,11 +6,8 @@ \alias{select_by_mz} \alias{remove_peaks_around_precursor_mz} \alias{normalize_intensities} -\alias{filterSpectriPy,Spectra,select_by_intensity-method} -\alias{filterSpectriPy,Spectra,select_by_mz-method} -\alias{filterSpectriPy,Spectra,remove_peaks_around_precursor_mz-method} -\alias{filterSpectriPy,Spectra,normalize_intensities-method} -\title{Filter Spectra using matchms} +\alias{filterSpectriPy,Spectra,filter_param-method} +\title{Filter Spectra using Python's matchms library} \usage{ select_by_intensity(intensity_from = 10, intensity_to = 200) @@ -20,13 +17,7 @@ remove_peaks_around_precursor_mz(mz_tolerance = 17) normalize_intensities() -\S4method{filterSpectriPy}{Spectra,select_by_intensity}(sps, param, ...) - -\S4method{filterSpectriPy}{Spectra,select_by_mz}(sps, param, ...) - -\S4method{filterSpectriPy}{Spectra,remove_peaks_around_precursor_mz}(sps, param, ...) - -\S4method{filterSpectriPy}{Spectra,normalize_intensities}(sps, param, ...) +\S4method{filterSpectriPy}{Spectra,filter_param}(object, param, mapping = spectraVariableMapping(), ...) } \arguments{ \item{intensity_from}{\code{numeric(1)}: Set lower threshold for peak intensity. @@ -44,11 +35,16 @@ Default is 1000.} \item{mz_tolerance}{\code{numeric(1)}: Tolerance of m/z values that are not allowed to lie within the precursor mz. Default is 17 Da.} -\item{sps}{A \code{\link[Spectra:Spectra]{Spectra::Spectra()}} object.} +\item{object}{A \code{\link[Spectra:Spectra]{Spectra::Spectra()}} object.} \item{param}{one of parameter classes listed above (such as -\code{select_by_intensity}) defining the filter/processing function in python -and its parameters.} +\code{select_by_intensity()}) defining the filter/processing function in +Python and its parameters.} + +\item{mapping}{named \code{character()} defining which spectra variables/metadata +should be converted between R and Python and how they should be renamed. +Defaults to \code{spectraVariableMapping()}. See \code{\link[=setSpectraVariableMapping]{setSpectraVariableMapping()}} +for more information.} \item{...}{ignored.} } @@ -58,26 +54,48 @@ filtering/processing function has been applied } \description{ The \code{filterSpectriPy()} function allows to filter/process a \code{Spectra} object -using the \code{select_by_intensity}, \code{select_by_mz}, -\code{remove_peaks_around_precursor_mz}, and \code{normalize_intensities} of the python +using the \code{select_by_intensity()}, \code{select_by_mz()}, +\code{remove_peaks_around_precursor_mz()}, and \code{normalize_intensities()} +functions of the Python \href{https://matchms.readthedocs.io/en/latest/api/matchms.filtering.html}{matchms.filtering} module. -Selection and configuration of the algorithm can be performed with one of the -parameter objects: +Selection and configuration of the algorithm can be performed with one of +the parameter objects (equivalent to \emph{matchms}' function names): \itemize{ -\item \code{select_by_intensity}: Keeps only the peaks within defined intensity range -(keep if \code{intensity_from} >= intensity >= \code{intensity_to}). -\item \code{select_by_mz}: Keeps only the peaks between \code{mz_from} and \code{mz_to} -(keep if \code{mz_from} >= m/z >= \code{mz_to}). -\item \code{remove_peaks_around_precursor_mz}: Removes the peaks that are within -\code{mz_tolerance} (in Da) of the precursor mz, exlcuding the precursor peak. -\item \code{normalize_intensities}: Normalizes the intensities of peaks +\item \code{select_by_intensity()}: Keeps only the peaks within defined intensity +range (keep if \code{intensity_from} >= intensity >= \code{intensity_to}). See also +the respective \href{https://matchms.readthedocs.io/en/latest/api/matchms.filtering.peak_processing.select_by_intensity.html}{documentation in \emph{matchms}}. +\item \code{select_by_mz()}: Keeps only the peaks between \code{mz_from} and \code{mz_to} +(keep if \code{mz_from} >= m/z >= \code{mz_to}). See also the respective +\href{https://matchms.readthedocs.io/en/latest/api/matchms.filtering.peak_processing.select_by_mz.html}{documentation in \emph{matchms}}. +\item \code{remove_peaks_around_precursor_mz()}: Removes the peaks that are within +\code{mz_tolerance} (in Da) of the precursor mz, excluding the precursor peak. +\item \code{normalize_intensities()}: Normalizes the intensities of peaks (and losses) to unit height. } } +\note{ +The first call to the \code{filterSpectriPy()} function can take longer because +the Python environment needs to be first set up. + +\code{filterSpectriPy()} first translates the \code{Spectra} to Python, applies the +filter functions from the \emph{matchms} Python libraries and then translates +the filtered data back to a \code{Spectra} object. Thus, any spectra variables +other than those that are translated between R and Python will be lost +during the processing. Use \code{\link[=setSpectraVariableMapping]{setSpectraVariableMapping()}} to define which +spectra variables should be transferred/converted between R and Python. +See also examples below for more information. + +The \code{\link[Spectra:Spectra]{Spectra::Spectra()}} object returned by \code{filterSpectriPy()} will +\strong{always} use an in-memory backend (i.e. the \code{\link[Spectra:MsBackend]{Spectra::MsBackendMemory()}}), +independently of the backend used by the backend used by the input +\code{Spectra}. +} \examples{ + library(Spectra) + ## create some example Spectra DF <- DataFrame( msLevel = c(2L, 2L, 2L), @@ -94,26 +112,47 @@ DF$mz <- list( c(109.2, 124.2, 124.5, 170.16, 170.52)) sps <- Spectra(DF) -## process Spectra with matchms' select_by_intensity algorithm -## note: the first filterSpectriPy will take longer because the Python -## environment needs to be set up. -filterSpectriPy(sps, param = select_by_intensity(intensity_from=50, intensity_to=400)) - -## Process Spectra with matchms' select_by_mz algorithm -filterSpectriPy(sps, param = select_by_mz(mz_from=150, mz_to=450)) - -## Calculate pairwise similarity of all spectra in sps with matchms' -## remove_peaks_around_precursor_mz algorithm -filterSpectriPy(sps, param = remove_peaks_around_precursor_mz(mz_tolerance=20)) +## Filter: select_by_intensity +res <- filterSpectriPy( + sps, select_by_intensity(intensity_from = 15, intensity_to = 300)) +## Only mass peaks with intensities between the specified limits are +## retained +intensity(res) +## Compared to the original intensities +intensity(sps) + +## Note that the spectra variable `"name"` was lost during conversion of +## the MS data between R and Python: +sps$name +any(spectraVariables(res) == "name") + +## Only spectra variables defined by `spectraVariableMapping()` are +## converted and thus retained: +spectraVariableMapping() + +## We can also pass a custom *spectra variable mapping* with the `mapping` +## parameter to the `filterSpectriPy()` function. Below we create such +## a mapping by adding the translation of a spectra variable `"name"` to +## a metadata name `"compound_name"` to the default spectra variable +## mapping `defaultSpectraVariableMapping()`. +map <- c(defaultSpectraVariableMapping(), name = "compound_name") +map + +## Repeat the filtering operation passing this mapping information: +res <- filterSpectriPy( + sps, select_by_intensity(intensity_from = 15, intensity_to = 300), + mapping = map) +res$name -## Calculate pairwise similarity of all spectra in sps with matchms' -## normalize_intensities algorithm -filterSpectriPy(sps, normalize_intensities()) } \seealso{ -\code{\link[Spectra:filterMsLevel]{Spectra::filterIntensity()}}, \code{\link[Spectra:filterMsLevel]{Spectra::filterMzRange()}}, +\itemize{ +\item \code{\link[Spectra:filterMsLevel]{Spectra::filterIntensity()}}, \code{\link[Spectra:filterMsLevel]{Spectra::filterMzRange()}}, \code{\link[Spectra:addProcessing]{Spectra::scalePeaks()}} in the \code{Spectra} package for pure R implementations of filtering/processing calculations. +\item \code{\link[=rspec_to_pyspec]{rspec_to_pyspec()}} or \code{\link[=pyspec_to_rspec]{pyspec_to_rspec()}} for the functions used to +translated the MS data between R and Python. +} } \author{ Thomas Naake diff --git a/man/rspec_to_pyspec.Rd b/man/rspec_to_pyspec.Rd deleted file mode 100644 index 765dd1f..0000000 --- a/man/rspec_to_pyspec.Rd +++ /dev/null @@ -1,84 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/conversion.R -\name{rspec_to_pyspec} -\alias{rspec_to_pyspec} -\alias{spectraVariableMapping,missing-method} -\alias{pyspec_to_rspec} -\title{Low level functions to convert between Spectra and matchms Spectrum} -\usage{ -\S4method{spectraVariableMapping}{missing}(object, ...) - -rspec_to_pyspec( - x, - mapping = spectraVariableMapping(), - reference = import("matchms"), - BPPARAM = SerialParam(), - .check = TRUE -) - -pyspec_to_rspec( - x, - mapping = spectraVariableMapping(), - BPPARAM = SerialParam(), - .check = TRUE -) -} -\arguments{ -\item{object}{ignored.} - -\item{...}{ignored.} - -\item{x}{For \code{rspec_to_pyspec()}: \code{Spectra} object. For \code{pyspec_to_rspec()}: -a Python list of matchms Spectrum objects.} - -\item{mapping}{Named \code{character} providing the spectra variable names -(metadata) to convert. Names are expected to be the spectra variable -names and values the corresponding names of the Python Spectrum metadata -fields. See description above for more details.} - -\item{reference}{Optional reference to Python environment \code{matchms}.} - -\item{BPPARAM}{Optional parallel processing setup.} - -\item{.check}{Optionally disable input parameter checking. Input parameter -checking should only disabled for very good reasons.} -} -\value{ -For \code{rspec_to_pyspec()}: Python array of Spectrum objects, same -length than \code{x}. For \code{pyspec_to_rspec()}: \code{\link[Spectra:Spectra]{Spectra::Spectra()}} with the -converted spectra. For \code{spectraVariableMapping()}: named \code{character} -vector with names being \code{Spectra} variable names and values the -corresponding names in \code{matchms}. -} -\description{ -The \code{rspec_to_pyspec()} and \code{pyspec_to_rspec()} functions allow to convert -R \code{\link[Spectra:Spectra]{Spectra::Spectra()}} objects into -\href{https://github.com/matchms/matchms}{matchms} Python \code{matchms.Spectrum} -objects. These functions are designed for -\strong{advanced users or developers} who want/need to integrate Python/matchms -functionality into R using \emph{reticulate}. All other users should use the -dedicated R functions within this package that take care of running the -Python code in the correct Python environment. - -Parameter \code{mapping} allows to define which spectra variables (metadata) -should be copied between the R and Python spectra. Only provided spectra -variables will be copied to R respectively Python. \code{mapping} also defines -the mapping between the \code{Spectra}'s spectra variables and the Spectrum -metadata. The names of the character vector \code{mapping} are the R spectra -variables and the values the corresponding names in the Python's Spectrum -metadata. See the output of the \code{spectraVariableMapping()} function for the -default variables and the mapping of the names. - -The \code{spectraVariableMapping()} function provides a default mapping of some -core \code{Spectra} variables based on this \href{https://github.com/matchms/matchms/blob/master/matchms/data/known_key_conversions.csv}{definition in matchms}. -The function returns a named vector that can be directly used as parameter -\code{mapping} in the \code{rspec_to_pyspec()} and \code{pyspec_to_rspec()} functions. -} -\examples{ - -## List the default spectra variables and their mapping. -spectraVariableMapping() -} -\author{ -Michael Witting, Johannes Rainer -} diff --git a/inst/Installation_with_R_v1.qmd b/temp_doc/Installation_with_R_v1.qmd similarity index 100% rename from inst/Installation_with_R_v1.qmd rename to temp_doc/Installation_with_R_v1.qmd diff --git a/vignettes/SpectriPy.Rmd b/temp_doc/SpectriPy.Rmd similarity index 70% rename from vignettes/SpectriPy.Rmd rename to temp_doc/SpectriPy.Rmd index d0c88d5..7169157 100644 --- a/vignettes/SpectriPy.Rmd +++ b/temp_doc/SpectriPy.Rmd @@ -1,16 +1,16 @@ --- -title: "Integrating Spectra with Python's matchms package" +title: "Enabling integration of Python libraries and R packages for combined mass spectrometry data analysis" package: SpectriPy output: BiocStyle::html_document: toc_float: true vignette: > - %\VignetteIndexEntry{Integrating Spectra with Python's matchms package} + %\VignetteIndexEntry{Enabling integration of Python libraries and R packages for combined mass spectrometry data analysis} %\VignetteEngine{knitr::rmarkdown} %\VignetteKeywords{Mass Spectrometry, MS, MSMS, Metabolomics, Infrastructure, Quantitative} %\VignettePackage{SpectriPy} %\VignetteEncoding{UTF-8} - %\VignetteDepends{Spectra,BiocStyle,SpectriPy,basilisk,reticulate} + %\VignetteDepends{Spectra,BiocStyle,SpectriPy,reticulate} --- ```{r style, echo = FALSE, results = 'asis', message = FALSE} @@ -21,33 +21,116 @@ BiocStyle::markdown() # Introduction -The *SpectriPy* package allows integration of Python MS packages into a -*Spectra*-based MS analysis in *R*. Python functionality is wrapped into R -functions allowing a seamless integration of the functionality of Python's -*matchms* package into R. In addition, functions to convert between R's -`Spectra` objects and Python's *matchms* spectrum objects are available to the -advanced user or developer enabling to create custom functions or workflows on -`Spectra` objects in Python and executing them in R using the *reticulate* R -package. +Powerful software libraries for mass spectrometry (MS) data are available in +both Python and R, covering specific and sometimes distinct aspects in the +analysis of proteomics and metabolomics data. R's *reticulate* package converts +basic data types between Python and R and enables a seamless interoperability of +both programming languages. The *SpectriPy* package extends *reticulate* +allowing to translate MS data data structures between Python and R. In addition, +functionality from Python's *matchms* library is directly wrapped into R +functions allowing a seamless integration into R based workflows. *SpectriPy* +thus enables powerful proteomics or metabolomics analysis workflows combining +the strengths of Python and R MS libraries. # Installation -If the *BiocManager* package is not already available, -please install it with `install.packages("BiocManager")` - -To install the development version of *SpectriPy* from GitHub, please install the -*remotes* package with `install.packages("remotes"). +If the *BiocManager* package is not already available, please install it with +`install.packages("BiocManager")`. To install the development version of +*SpectriPy* from GitHub, please install in addition the *remotes* package with +`install.packages("remotes"). As a system dependency, the package requires a +Python environment to be available. -The package requires a python environment to be available and can be installed +LLLL Update installation instructions. +and can be installed with the *BiocManager* R package using the command `BiocManager::install("RforMassSpectrometry/SpectriPy")`. This will install the latest version of the package from GitHub. -All required python libraries are installed automatically on demand. -Note that first installation or first invocation of specific functions might +All required python libraries are installed automatically on demand. +Note that first installation or first invocation of specific functions might take long because of this installation process. +# Translating data structures between Python and R + +The [*reticulate*](https://rstudio.github.io/reticulate/) package enables a +seamless integration of Python with R by translating the core data structures +between the two programming languages and sharing a Python (respectively R) +runtime environment that can be accessed from the other programming languages +including shared variables. The *SpectriPy* package builds on that providing +functionality to translate data structures for mass spectrometry (MS) data +between the two languages. Specifically, the package translates between R's +`Spectra` objects (from the `r BiocStyle::Biocpkg("Spectra")` package) and +`matchms.Spectrum` objects from the +[*matchms*](https://github.com/matchms/matchms) Python library. + + +## R to Python + +In the first examples we translated MS data from R to Python. We first load data +from a mzML data file provided through the `r BiocStyle::Biocpkg("msdata")` +package. + +```{r} +#' Loading the data from a mzML file as a `Spectra` object. +library(Spectra) +fl <- system.file("TripleTOF-SWATH", "PestMix1_DDA.mzML", package = "msdata") +s_r <- Spectra(fl) +``` + +We next restrict the data to MS2 spectra and remove spectra with less than 3 +peaks (fragments). + +```{r} +#' Restrict to MS level 2 spectra +s_r <- filterMsLevel(s_r, 2) +s_r <- s_r[lengths(s_r) < 3] +s_r +``` + +```{r} +library(AnnotationHub) +ah <- AnnotationHub(localHub = TRUE) +query(ah, "MassBank") +mb <- ah[["AH107049"]] +mb <- Spectra(mb) +``` + +```{r} +register(SerialParam()) +library(MetaboAnnotation) +m <- matchSpectra(s_r, mb, CompareSpectraParam(ppm = 40)) +set.seed(123) +idx <- c(whichTarget(m), 5970, 5971, 5972, 5973, 10279, 15505, 15506, + sample(seq_along(mb), 51)) + +mb <- mb[sort(idx)] +tmp <- selectSpectraVariables( + mb, c("msLevel", "rtime", "acquisitionNum", "precursorMz", + "precursorIntensity", "precursorCharge", "inchi", "smiles", + "name")) + +library(MsBackendMgf) +sm <- c(spectraVariableMapping(MsBackendMgf()), inchi = "INCHI", + smiles = "SMILES", name = "NAME") +export(tmp, MsBackendMgf(), file = "/home/jo/tmp/test.mgf", mapping = sm) + + +library(AnnotationHub) +tmp <- system.file() +``` + + + + + +## Python to R + +We next import MS data from a file in MGF format using the *matchms* Python +library. + + + # Spectra similarity calculations using `matchms` The *SpectriPy* package provides the `compareSpectriPy()` function that allows @@ -67,7 +150,7 @@ added in future. We next create some simple example spectra and subsequently use the `compareSpectriPy()` function to calculate pairwise similarities between these. -```{r, message = FALSE} +```{r, message = FALSE, eval = FALSE} library(Spectra) library(SpectriPy) @@ -103,7 +186,7 @@ mhd <- Spectra(mhd) We first calculate pairwise similarities between all spectra defined above and those of caffeine using *Spectra*'s built-in `compareSpectra()` function. -```{r} +```{r, eval = FALSE} all <- c(caf, mhd) res_r <- compareSpectra(all, caf) res_r @@ -117,10 +200,10 @@ similarity function (from *matchms*) using a dedicated parameter object. Below we calculate the similarity using the *CosineGreedy* function changing the `tolerance` to a value of `0.05` (instead of the default `0.1`). -Executing the following step will automatically download and install conda mini +Executing the following step will automatically download and install conda mini forge to manage Python package dependencies. -```{r} +```{r, eval = FALSE} res <- compareSpectriPy(all, caf, param = CosineGreedyParam(tolerance = 0.05)) res ``` @@ -132,7 +215,7 @@ because the Python setup has to be initialized. Next we use the *ModifiedCosine* algorithm that considers also differences between the spectra's precursor *m/z* in the calculation. -```{r} +```{r, eval = FALSE} res <- compareSpectriPy(all, caf, param = ModifiedCosineParam()) res ``` @@ -143,7 +226,7 @@ from *matchms*. This algorithm calculates the *cosine similarity score* as with peaks between the compared spectra. The algorithm can be configured with parameters `tolerance`, `mzPower` and `intensityPower` (see parameter description for more details). -```{r} +```{r, eval = FALSE} res <- compareSpectriPy(all, caf, param = CosineHungarianParam()) res ``` @@ -156,7 +239,7 @@ potential match if their *m/z* ratios lie within the given `tolerance` once a mass-shift is applied. The mass shift is the difference in precursor-*m/z* between the two spectra. -```{r} +```{r, eval = FALSE} res <- compareSpectriPy(all, caf, param = NeutralLossesCosineParam()) res ``` @@ -168,7 +251,7 @@ similarity scoring with this similarity method. Below we remove the precursor *m/z* from one of our input spectra and then show how the `Spectra` object could be subsetted to *valid* spectra for this method. -```{r} +```{r, eval = FALSE} ## Remove precursor m/z from the 3rd spectrum all$precursorMz[3] <- NA @@ -191,7 +274,7 @@ the *matchms* Python package. To illustrate their use we initialize below the Python environment that is bundled using the `r BiocStyle::Biocpkg("basilisk")` package within *SpectriPy*. -```{r setup-basilisk} +```{r setup-basilisk, eval = FALSE} library(SpectriPy) library(basilisk) cl <- basiliskStart(SpectriPy:::matchms_env) @@ -200,7 +283,7 @@ cl <- basiliskStart(SpectriPy:::matchms_env) We next create a simple `Spectra` object representing fragment spectra for some small compounds. -```{r} +```{r, eval = FALSE} library(Spectra) # create example spectra @@ -225,7 +308,7 @@ sps <- Spectra(spd) We next convert the `Spectra` to a `matchms.Spectrum` object. -```{r} +```{r, eval = FALSE} pysps <- rspec_to_pyspec(sps) pysps ``` @@ -239,7 +322,7 @@ with their respective names in the Python Spectrum object can be defined too. Below we list the pre-selected spectra variables that are converted by default. -```{r} +```{r, eval = FALSE} spectraVariableMapping() ``` @@ -250,7 +333,7 @@ from the *matchms.filtering* library. We thus need to first import the functionality from that package and can then call the function directly on the (Python) objects. -```{r} +```{r, eval = FALSE} library(reticulate) filters <- import("matchms.filtering") @@ -264,7 +347,7 @@ res We can now convert the list of Python `matchms.Spectrum` objects back to R with `pyspec_to_rspec()`: -```{r} +```{r, eval = FALSE} sps_r <- pyspec_to_rspec(res) #' The normalized intensities intensity(sps_r) @@ -280,7 +363,7 @@ mixed R and Python code used in the example above. Below we define a simple python script that iterates over the spectra in python and performs the normalization. -```{r} +```{r, eval = FALSE} py_script <- paste0("from matchms.filtering import normalize_intensities\n", "for i in range(len(pysps)):\n", " pysps[i] = normalize_intensities(pysps[i])\n") @@ -293,7 +376,7 @@ intensity(tmp) At very last we need also to stop the Python environment enabled by *basilisk*. -```{r} +```{r, eval = FALSE} basiliskStop(cl) ``` diff --git a/inst/doc/cross-language-ms-analysis.qmd b/temp_doc/cross-language-ms-analysis.qmd similarity index 100% rename from inst/doc/cross-language-ms-analysis.qmd rename to temp_doc/cross-language-ms-analysis.qmd diff --git a/inst/doc/ms2deepscore.Rmd b/temp_doc/ms2deepscore.Rmd similarity index 100% rename from inst/doc/ms2deepscore.Rmd rename to temp_doc/ms2deepscore.Rmd diff --git a/tests/testthat.R b/tests/testthat.R index 7514bf0..08557b1 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,7 +1,5 @@ library(testthat) library(Spectra) library(SpectriPy) -library(reticulate) -library(basilisk) -test_check("SpectriPy") \ No newline at end of file +test_check("SpectriPy") diff --git a/tests/testthat/test_compareSpectriPy.R b/tests/testthat/test_compareSpectriPy.R index 3079e2d..199185c 100644 --- a/tests/testthat/test_compareSpectriPy.R +++ b/tests/testthat/test_compareSpectriPy.R @@ -26,141 +26,100 @@ mhd <- Spectra(mhd) test_that("param constructors work", { ## Errors - expect_error(CosineGreedyParam(tolerance = c(1.2, 1.5)), "length 1") - expect_error(CosineGreedyParam(tolerance = -1.2), "length 1") - expect_error(CosineGreedyParam(mzPower = c(1, 2)), "length 1") - expect_error(CosineGreedyParam(intensityPower = c(1, 2)), "length 1") - expect_error(NeutralLossesCosineParam( - ignorePeaksAbovePrecursor = c(TRUE, FALSE)), "length 1") + expect_error(CosineGreedy(tolerance = c(1.2, 1.5)), "length 1") + expect_error(CosineGreedy(tolerance = -1.2), "length 1") + expect_error(CosineGreedy(mz_power = c(1, 2)), "length 1") + expect_error(CosineGreedy(intensity_power = c(1, 2)), "length 1") + expect_error(NeutralLossesCosine( + ignore_peaks_above_precursor = c(TRUE, FALSE)), "length 1") }) -test_that("CosineGreedyParam constructor works", { - res <- CosineGreedyParam(tolerance = 5) - expect_s4_class(res, "CosineGreedyParam") +test_that("CosineGreedy constructor works", { + res <- CosineGreedy(tolerance = 5) + expect_s4_class(res, "CosineGreedy") expect_equal(res@tolerance, 5) }) -test_that("CosineHungarianParam constructor works", { - res <- CosineHungarianParam(intensityPower = 1.3) - expect_s4_class(res, "CosineGreedyParam") +test_that("CosineHungarian constructor works", { + res <- CosineHungarian(intensity_power = 1.3) + expect_s4_class(res, "CosineGreedy") expect_equal(res@intensityPower, 1.3) }) -test_that("ModifiedCosineParam constructor works", { - res <- ModifiedCosineParam(mzPower = 4.3) - expect_s4_class(res, "ModifiedCosineParam") +test_that("ModifiedCosine constructor works", { + res <- ModifiedCosine(mz_power = 4.3) + expect_s4_class(res, "ModifiedCosine") expect_equal(res@mzPower, 4.3) }) -test_that("NeutralLossesCosineParam constructor works", { - res <- NeutralLossesCosineParam(ignorePeaksAbovePrecursor = FALSE) - expect_s4_class(res, "NeutralLossesCosineParam") +test_that("NeutralLossesCosine constructor works", { + res <- NeutralLossesCosine(ignore_peaks_above_precursor = FALSE) + expect_s4_class(res, "NeutralLossesCosine") expect_false(res@ignorePeaksAbovePrecursor) }) -test_that(".fun name works parameterized", { - a <- CosineGreedyParam() - expect_equal(SpectriPy:::.fun_name(a), "CosineGreedy") - a <- CosineHungarianParam() - expect_equal(SpectriPy:::.fun_name(a), "CosineHungarian") - a <- ModifiedCosineParam() - expect_equal(SpectriPy:::.fun_name(a), "ModifiedCosine") - a <- NeutralLossesCosineParam() - expect_equal(SpectriPy:::.fun_name(a), "NeutralLossesCosine") +test_that(".fun_name works", { + a <- CosineGreedy() + expect_equal(.fun_name(a), "CosineGreedy") + a <- CosineHungarian() + expect_equal(.fun_name(a), "CosineHungarian") + a <- ModifiedCosine() + expect_equal(.fun_name(a), "ModifiedCosine") + a <- NeutralLossesCosine() + expect_equal(.fun_name(a), "NeutralLossesCosine") }) -test_that("python_command and .cosine_param_string work", { - a <- ModifiedCosineParam(tolerance = 0.9) - res <- .cosine_param_string(a) - expect_equal(res, "tolerance=0.9, mz_power=0, intensity_power=1") - a <- CosineHungarianParam(mzPower = 4, intensityPower = 10) - res <- .cosine_param_string(a) - expect_equal(res, "tolerance=0.1, mz_power=4, intensity_power=10") - res <- python_command(a) - expect_match(res, "CosineHungarian") - expect_match(res, "mz_power=4, intensity_power=10") - expect_match(res, "py_x, py_y") - expect_match(res, "is_symmetric=False") - res <- python_command(a, "py_x", is_symmetric = "True") - expect_match(res, "py_x, Co") - expect_match(res, "is_symmetric=True") - - a <- NeutralLossesCosineParam(ignorePeaksAbovePrecursor = FALSE, - tolerance = 0.9) - res <- .cosine_param_string(a) - expect_equal(res, "tolerance=0.9, mz_power=0, intensity_power=1") - res <- python_command(a) - expect_match(res, "ignore_peaks_above_precursor=False") - - a <- NeutralLossesCosineParam(ignorePeaksAbovePrecursor = TRUE, - tolerance = 0.9) - res <- .cosine_param_string(a) - expect_equal(res, "tolerance=0.9, mz_power=0, intensity_power=1") - res <- python_command(a) - expect_match(res, "ignore_peaks_above_precursor=True") -}) - -test_that("python commands evaluation", { - p <- CosineGreedyParam(tolerance = 0.05) - pstring <- SpectriPy:::python_command(p) - ## Invoke basilisk - cl <- basiliskStart(matchms_env) - py$py_x <- rspec_to_pyspec(caf, reference = import("matchms"), - mapping = c(precursorMz = "precursor_mz")) - py$py_y <- rspec_to_pyspec(caf, reference = import("matchms"), - mapping = c(precursorMz = "precursor_mz")) - py_run_string(pstring) - ## Convert to similarity array - py_run_string("sim = res.scores.to_array()") - py$sim["CosineGreedy_score"] - - p <- CosineHungarianParam() - pstring <- SpectriPy:::python_command(p) - py_run_string(pstring) - py_run_string("sim = res.scores.to_array()") - py$sim["CosineHungarian_score"] - - p <- ModifiedCosineParam() - pstring <- SpectriPy:::python_command(p) - py_run_string(pstring) - py_run_string("sim = res.scores.to_array()") - py$sim["ModifiedCosine_score"] - - p <- SpectriPy:::NeutralLossesCosineParam() - pstring <- SpectriPy:::python_command(p) - py_run_string(pstring) - py_run_string("sim = res.scores.to_array()[\"NeutralLossesCosine_score\"]") - - basiliskStop(cl) +test_that("py_fun works", { + res <- py_fun(CosineGreedy(tolerance = 0.5, mz_power = 0.3, + intensity_power = 0.2)) + expect_equal(class(res)[1L], + "matchms.similarity.CosineGreedy.CosineGreedy") + res <- py_fun(CosineHungarian(tolerance = 0.4, mz_power = 0.3, + intensity_power = 0.2)) + expect_equal( + class(res)[1L], "matchms.similarity.CosineHungarian.CosineHungarian") + res <- py_fun(ModifiedCosine(tolerance = 0.1, mz_power = 0.3, + intensity_power = 0.2)) + expect_equal( + class(res)[1L], "matchms.similarity.ModifiedCosine.ModifiedCosine") + res <- py_fun(NeutralLossesCosine(tolerance = 0.1, mz_power = 0.3, + intensity_power = 0.2, + ignore_peaks_above_precursor = FALSE)) + expect_equal( + class(res)[1L], + "matchms.similarity.NeutralLossesCosine.NeutralLossesCosine") }) test_that(".compare_spectra_python works", { all <- c(caf, mhd) - res <- SpectriPy:::.compare_spectra_python(all, caf, CosineGreedyParam()) + res <- .compare_spectra_python(all, caf, CosineGreedy()) expect_true(nrow(res) == 4) expect_true(ncol(res) == 2) + expect_equal(res[1, 1], 1) + expect_equal(res[2, 2], 1) + expect_true(all(res[3:4, ]== 0)) - res_sps <- compareSpectra(all, caf) + res_sps <- compareSpectra(all, caf, tolerance = 0.1, ppm = 0) diffs <- abs(as.numeric(res - res_sps)) expect_true(all(diffs < 0.01)) ## only one spectra - res <- SpectriPy:::.compare_spectra_python(all, param = CosineGreedyParam()) - res_2 <- SpectriPy:::.compare_spectra_python(all, all, param = CosineGreedyParam()) + res <- .compare_spectra_python(all, param = CosineGreedy()) + res_2 <- .compare_spectra_python(all, all, param = CosineGreedy()) expect_equal(res, res_2) ## try with empty Spectra - res <- SpectriPy:::.compare_spectra_python(all, all[integer()], CosineGreedyParam()) + res <- .compare_spectra_python(all, all[integer()], CosineGreedy()) expect_true(is.numeric(res)) expect_true(nrow(res) == length(all)) expect_true(ncol(res) == 0) - res <- SpectriPy:::.compare_spectra_python(all[integer()], param = CosineGreedyParam()) + res <- .compare_spectra_python(all[integer()], param = CosineGreedy()) expect_true(is.numeric(res)) expect_true(nrow(res) == 0) expect_true(ncol(res) == 0) - res <- SpectriPy:::.compare_spectra_python(all[integer()], all, CosineGreedyParam()) + res <- .compare_spectra_python(all[integer()], all, CosineGreedy()) expect_true(is.numeric(res)) expect_true(nrow(res) == 0) expect_true(ncol(res) == length(all)) @@ -168,61 +127,53 @@ test_that(".compare_spectra_python works", { test_that("compareSpectriPy works", { all <- c(caf, mhd) - res_all <- compareSpectriPy(all, param = CosineGreedyParam()) + res_all <- compareSpectriPy(all, param = CosineGreedy()) expect_true(is.numeric(res_all)) expect_true(nrow(res_all) == length(all)) expect_true(ncol(res_all) == length(all)) expect_equal(diag(res_all), c(1, 1, 1, 1)) - ## Test python call. LLLLLLL - ## cl <- basiliskStart(SpectriPy:::matchms_env) - ## p <- CosineGreedyParam() - ## cmd <- SpectriPy:::python_command(p) - ## py$py_x <- rspec_to_pyspec(caf, mapping = c(precursorMz = "precursor_mz")) - ## py$py_y <- rspec_to_pyspec(mhd, mapping = c(precursorMz = "precursor_mz")) - ## strng <- paste0("import matchms\n", - ## "from matchms.similarity import CosineGreedy\n", - ## "res = matchms.calculate_scores(py_x, py_y, CosineGreedy(), is_symmetric = False)\n") - ## py_run_string(strng) - ## basiliskStop(cl) - ## res <- compareSpectriPy(caf, mhd, param = CosineGreedyParam()) - ## WHY is this not working??? Seems to be some python issue, maybe a bug - ## in CosineGreedy? - ## - ## WORKS res <- compareSpectriPy(caf, caf, param = CosineGreedyParam()) - ## WORKS res <- compareSpectriPy(mhd, mhd, param = CosineGreedyParam()) - ## NOT res <- compareSpectriPy(mhd, caf, param = CosineGreedyParam()) - ## NOT res <- compareSpectriPy(caf, mhd, param = CosineGreedyParam()) - ## Maybe the issue is with spectra without any similarity (score = 0) and - ## having a result matrix with more than 1 column or row. - ## Seems to be the case: - res <- compareSpectriPy(caf, mhd, param = CosineGreedyParam(tolerance = 10)) - - res <- compareSpectriPy(caf[1L], mhd[1L], param = CosineGreedyParam()) + ## CosineGreedy + res <- compareSpectriPy(caf, mhd, param = CosineGreedy()) + expect_true(nrow(res) == 2) + expect_true(ncol(res) == 2) + expect_true(all(res == 0)) + res <- compareSpectriPy(caf, mhd, param = CosineGreedy(tolerance = 10)) + expect_true(any(res > 0)) + + res <- compareSpectriPy(caf[1L], mhd[1L], param = CosineGreedy()) expect_true(is.numeric(res)) expect_true(nrow(res) == 1) expect_true(ncol(res) == 1) expect_true(all(res == 0)) expect_equal(res[1, 1], res_all[1, 3]) - res <- compareSpectriPy(caf, c(mhd, caf[1]), param = CosineGreedyParam()) - expect_true(nrow(res) == 2L) - expect_true(ncol(res) == 3L) - expect_equal(res[1, 3], 1) - expect_equal(res[2, 3], res_all[1, 2]) - - ## Add tests after matchms > 0.14.0 - ## res <- compareSpectriPy(all, all, param = NeutralLossesCosineParam()) + ## CosineHungarian + res <- compareSpectriPy(caf, mhd, param = CosineHungarian()) + expect_true(nrow(res) == 2) + expect_true(ncol(res) == 2) + expect_true(all(res == 0)) + res <- compareSpectriPy(caf, mhd, param = CosineHungarian(tolerance = 10)) + expect_true(any(res > 0)) - ## ModifiedCosine with and without precursor m/z - res <- compareSpectriPy(all, all, param = ModifiedCosineParam()) - expect_true(nrow(res) == length(all)) - expect_true(ncol(res) == length(all)) + ## ModifiedCosine + res <- compareSpectriPy(caf, mhd, param = ModifiedCosine()) + expect_true(nrow(res) == 2) + expect_true(ncol(res) == 2) expect_true(all(res > 0)) - expect_equal(diag(res), c(1, 1, 1, 1)) + res <- compareSpectriPy(caf, mhd, param = ModifiedCosine(tolerance = 10)) + expect_true(all(res > 0.3)) all_mod <- all all_mod$precursorMz[3] <- NA_real_ - expect_error(compareSpectriPy(all_mod, all, param = ModifiedCosineParam()), + expect_error(compareSpectriPy(all_mod, all, param = ModifiedCosine()), "Expect precursor to be positive") -}) \ No newline at end of file + + ## NeutralLossesCosine + res <- compareSpectriPy(caf, mhd, param = NeutralLossesCosine()) + expect_true(nrow(res) == 2) + expect_true(ncol(res) == 2) + expect_true(all(res == 0)) + res <- compareSpectriPy(caf, mhd, param = NeutralLossesCosine(tolerance = 10)) + expect_true(all(res > 0)) +}) diff --git a/tests/testthat/test_conversion.R b/tests/testthat/test_conversion.R index 3f54bea..13201f7 100644 --- a/tests/testthat/test_conversion.R +++ b/tests/testthat/test_conversion.R @@ -11,159 +11,177 @@ df$intensity <- list( ) sps <- Spectra(df) -test_that("spectraVariableMapping works", { +test_that("spectraVariableMapping, spectraVariableMapping<- works", { res <- spectraVariableMapping() expect_true(is.character(res)) expect_true(length(res) > 0) + expect_true(length(names(res)) > 0) + expect_equal(res, .SPECTRA_2_MATCHMS) + + setSpectraVariableMapping(c(a = "b", d = "e")) + expect_equal(spectraVariableMapping(), c(a = "b", d = "e")) + setSpectraVariableMapping(.SPECTRA_2_MATCHMS) + expect_equal(spectraVariableMapping(), + .SPECTRA_2_MATCHMS) + expect_equal(defaultSpectraVariableMapping(), .SPECTRA_2_MATCHMS) +}) + +############# +## R to Py + +test_that(".single_rspec_to_pyspec works", { + res <- .single_rspec_to_pyspec(sps[1L]) + expect_true(is(res, "matchms.Spectrum.Spectrum")) + expect_true(all(.SPECTRA_2_MATCHMS %in% names(res$metadata))) + expect_equal(sps$mz[[1L]], as.vector(py_to_r(res$mz))) + expect_equal(sps$intensity[[1L]], as.vector(py_to_r(res$intensities))) + expect_equal(sps$rtime[1L], py_to_r(res$metadata["retention_time"])) + expect_equal(sps$msLevel[1L], py_to_r(res$metadata["ms_level"])) + expect_equal(sps$precursorMz[1L], py_to_r(res$metadata["precursor_mz"])) + + res <- .single_rspec_to_pyspec(sps[2L], character()) + expect_equal(sps$mz[[2L]], as.vector(py_to_r(res$mz))) + expect_equal(sps$intensity[[2L]], as.vector(py_to_r(res$intensities))) + expect_equal(names(res$metadata), character()) +}) + +test_that(".rspec_to_pyspec works", { + res <- .rspec_to_pyspec(sps) + expect_true(is(res, "python.builtin.list")) + expect_true(length(res) == length(sps)) + + expect_equal(sps$mz[[2L]], as.vector(py_to_r(res[[1]]$mz))) + expect_equal(sps$intensity[[2L]], as.vector(py_to_r(res[[1]]$intensities))) + expect_true(all(.SPECTRA_2_MATCHMS %in% names(res[[1]]$metadata))) + + res <- .rspec_to_pyspec(sps, character()) + expect_equal(sps$mz[[2L]], as.vector(py_to_r(res[[1]]$mz))) + expect_equal(sps$intensity[[2L]], as.vector(py_to_r(res[[1]]$intensities))) + expect_equal(names(res[[1]]$metadata), character()) }) test_that("rspec_to_pyspec works", { - cl <- basiliskStart(SpectriPy:::matchms_env) - basiliskRun(cl, function(x) { - res <- rspec_to_pyspec(x) - expect_true(is(res, "python.builtin.list")) - expect_equal(length(res), length(x)) - expect_equal(as.numeric(py_to_r(res[2]$peaks$mz)), mz(x)[[3]]) - expect_equal(rtime(x)[1], py_to_r(res[0]$metadata$retention_time)) - }, x = sps) - - basiliskRun(cl, function(x) { - x$new_col <- c(9, 3, 1) - res <- rspec_to_pyspec(x, mapping = c(rtime = "retention_time", - new_col = "new_col")) - expect_true(is(res, "python.builtin.list")) - expect_equal(length(res), length(x)) - expect_equal(as.numeric(py_to_r(res[2]$peaks$mz)), mz(x)[[3]]) - expect_equal(rtime(x)[1], py_to_r(res[0]$metadata$retention_time)) - expect_equal(x$new_col[1], py_to_r(res[0]$metadata$new_col)) - }, x = sps) - - basiliskRun(cl, function(x) { - x$rtime <- NULL - res <- rspec_to_pyspec(x) - expect_true(is(res, "python.builtin.list")) - expect_equal(length(res), length(x)) - expect_equal(as.numeric(py_to_r(res[2]$peaks$mz)), mz(x)[[3]]) - ## Seems NA for rtime is changed to NULL in Spectrum - expect_equal(py_to_r(res[0]$metadata$retention_time), NULL) - }, x = sps) - - basiliskStop(cl) + res <- r_to_py(sps) + expect_equal(res, rspec_to_pyspec(sps)) + expect_true(is(res, "python.builtin.list")) + expect_equal(length(res), length(sps)) + expect_equal(as.numeric(py_to_r(res[2]$peaks$mz)), mz(sps)[[3]]) + expect_equal(rtime(sps)[1], py_to_r(res[0]$metadata$retention_time)) + expect_equal(msLevel(sps)[1], py_to_r(res[0]$metadata$ms_level)) + expect_equal(msLevel(sps)[2], py_to_r(res[1]$metadata$ms_level)) + expect_equal(msLevel(sps)[3], py_to_r(res[2]$metadata$ms_level)) + expect_equal(precursorMz(sps)[1], py_to_r(res[0]$metadata$precursor_mz)) + + ## mapping only specific spectra variables. + sps$new_col <- c(9, 3, 1) + setSpectraVariableMapping(c(collisionEnergy = "collision_energy", + new_col = "new_col")) + res <- r_to_py(sps) + expect_true(is(res, "python.builtin.list")) + expect_equal(length(res), length(sps)) + expect_equal(sort(names(res[0]$metadata)), + sort(c("collision_energy", "new_col"))) + expect_equal(py_to_r(res[0]$metadata$new_col), 9) + + s <- sps + s$rtime <- NULL + setSpectraVariableMapping(c(precursorMz = "precursor_mz", + rtime = "retention_time")) + res <- r_to_py(s) + expect_equal(sort(names(res[0]$metadata)), + c("precursor_mz", "retention_time")) + expect_equal(py_to_r(res[0]$metadata$precursor_mz), NA_real_) + ## It's a bit odd - a retention time of NA is converted to a NULL + expect_equal(py_to_r(res[0]$metadata$retention_time), NULL) + + s$new_col <- NULL + setSpectraVariableMapping(c(precursorMz = "precursor_mz", + new_col = "new_col")) + expect_error(res <- r_to_py(s), "requested spectra variables") + + ## No mapping at all. + setSpectraVariableMapping(character()) + res <- rspec_to_pyspec(s) + expect_equal(names(res[0]$metadata), character()) + + setSpectraVariableMapping(.SPECTRA_2_MATCHMS) }) -test_that("pyspec_to_rspec works", { - cl <- basiliskStart(SpectriPy:::matchms_env) - basiliskRun(cl, function(x) { - p <- rspec_to_pyspec(x) - res <- pyspec_to_rspec(p, mapping = spectraVariableMapping()) - expect_true(is(res, "Spectra")) - expect_equal(spectraData(x), spectraData(res)) - }, x = sps) - - ## Map only selected values back. - basiliskRun(cl, function(x) { - p <- rspec_to_pyspec(x) - res <- pyspec_to_rspec(p, mapping = c(rtime = "retention_time", - what = "not_exists")) - expect_true(is(res, "Spectra")) - expect_equal(mz(x), mz(res)) - expect_equal(rtime(x), rtime(res)) - expect_equal(intensity(x), intensity(res)) - expect_true(all(is.na(msLevel(res)))) - }, x = sps) - - basiliskRun(cl, function(x) { - x$rtime <- NULL - p <- rspec_to_pyspec(x) - res <- pyspec_to_rspec(p) - expect_equal(mz(x), mz(res)) - expect_equal(rtime(x), rtime(res)) - expect_equal(intensity(x), intensity(res)) - }, x = sps) - ## errors - expect_error(pyspec_to_rspec(5), "Python list") - - basiliskStop(cl) +############# +## Py to R + +test_that(".py_matchms_spectrum_spectra_data works", { + p <- r_to_py(sps) + res <- .py_matchms_spectrum_spectra_data(p[[1]]) + expect_true(is.data.frame(res)) + expect_true(all(names(.SPECTRA_2_MATCHMS) %in% colnames(res))) + expect_equal(res$msLevel, sps$msLevel[2]) + expect_equal(res$rtime, sps$rtime[2]) + expect_equal(res$precursorCharge, sps$precursorCharge[2]) + expect_equal(res$precursorMz, sps$precursorMz[2]) + expect_equal(res$precursorIntensity, sps$precursorIntensity[2]) + expect_equal(res$collisionEnergy, sps$collisionEnergy[2]) + + res <- .py_matchms_spectrum_spectra_data( + p[[1]], mapping = c(msLevel = "ms_level", other_col = "other_col")) + expect_equal(colnames(res), "msLevel") + expect_equal(res$msLevel, sps$msLevel[2]) + + res <- .py_matchms_spectrum_spectra_data(p[[1]], mapping = character()) + expect_equal(colnames(res), "msLevel") + expect_equal(res$msLevel, NA_integer_) }) -test_that(".single_rspec_to_pyspec works", { - cl <- basiliskStart(SpectriPy:::matchms_env) - vars <- spectraVariableMapping() - basiliskRun(cl, function(x) { - res <- SpectriPy:::.single_rspec_to_pyspec(sps[1L], vars) - expect_equal(class(res)[1L], "matchms.Spectrum.Spectrum") - expect_equal(sort(names(res$metadata)), sort(unname(vars))) - expect_equal(as.numeric(res$peaks$intensities), - intensity(sps)[[1L]]) - }, x = sps) - ## No metadata - basiliskRun(cl, function(x) { - res <- SpectriPy:::.single_rspec_to_pyspec(sps[1L], character()) - expect_equal(class(res)[1L], "matchms.Spectrum.Spectrum") - expect_equal(sort(names(res$metadata)), character()) - expect_equal(as.numeric(res$peaks$intensities), - intensity(sps)[[1L]]) - }, x = sps) - ## Only msLevel - basiliskRun(cl, function(x) { - res <- SpectriPy:::.single_rspec_to_pyspec(sps[1L], c(msLevel = "msLevel")) - expect_equal(class(res)[1L], "matchms.Spectrum.Spectrum") - expect_equal(sort(names(res$metadata)), "ms_level") - }, x = sps) - basiliskStop(cl) +test_that(".py_matchms_spectrum_peaks_data works", { + p <- r_to_py(sps) + res <- .py_matchms_spectrum_peaks_data(p[[1]]) + expect_true(is.matrix(res)) + expect_equal(colnames(res), c("mz", "intensity")) + expect_equal(res, peaksData(sps)[[2L]]) }) -test_that(".single_pyspec_to_rspec works", { - cl <- basiliskStart(SpectriPy:::matchms_env) - vars <- spectraVariableMapping() - - p <- SpectriPy:::.single_rspec_to_pyspec(sps[1L]) - res <- SpectriPy:::.single_pyspec_to_rspec(p, vars) - expect_equal(mz(res), mz(sps[1L])) - expect_equal(intensity(res), intensity(sps[1L])) - expect_equal(rtime(res), rtime(sps[1L])) - expect_equal(msLevel(res), msLevel(sps[1L])) - - p <- SpectriPy:::.single_rspec_to_pyspec(sps[2L]) - res <- SpectriPy:::.single_pyspec_to_rspec(p, vars) - expect_equal(mz(res), mz(sps[2L])) - expect_equal(intensity(res), intensity(sps[2L])) - expect_equal(rtime(res), rtime(sps[2L])) - expect_equal(msLevel(res), msLevel(sps[2L])) - - p <- SpectriPy:::.single_rspec_to_pyspec(sps[3L]) - res <- SpectriPy:::.single_pyspec_to_rspec(p, vars) - expect_equal(mz(res), mz(sps[3L])) - expect_equal(intensity(res), intensity(sps[3L])) - expect_equal(rtime(res), rtime(sps[3L])) - expect_equal(msLevel(res), msLevel(sps[3L])) - - basiliskStop(cl) +test_that("pyspec_to_rspec and .single_pyspec_to_rspec work", { + p <- r_to_py(sps) + res <- pyspec_to_rspec(p[0]) + expect_s4_class(res, "Spectra") + expect_equal(peaksData(res), peaksData(sps[1L])) + expect_equal(res$msLevel, sps[1L]$msLevel) + expect_equal(res$rtime, sps[1L]$rtime) + + res <- .single_pyspec_to_rspec( + p[1L], mapping = c(rtime = "retention_time")) + expect_equal(res$rtime, sps[2L]$rtime) + expect_true(is.na(res$msLevel)) + + res <- .single_pyspec_to_rspec(p[1L], mapping = character()) + expect_true(is.na(res$rtime)) + expect_true(is.na(res$msLevel)) }) -test_that(".single_pyspec_to_rspec works", { - cl <- basiliskStart(SpectriPy:::matchms_env) - vars <- spectraVariableMapping() - - ## Request single spectra variable - vars <- c(rtime = "retention_time") - p <- SpectriPy:::.single_rspec_to_pyspec(sps[1L]) - res <- SpectriPy:::.single_pyspec_to_rspec(p, vars) - expect_equal(rtime(res), rtime(sps[1L])) - expect_true(is.na(msLevel(res))) - - ## Request spectra variables that don't exist. - vars <- c(rtime = "retention_time", other_col = "other_col", b = "b") - p <- SpectriPy:::.single_rspec_to_pyspec(sps[1L]) - res <- SpectriPy:::.single_pyspec_to_rspec(p, vars) - expect_equal(rtime(res), rtime(sps[1L])) - expect_true(is.na(msLevel(res))) - - ## Request spectra variables that don't exist. - vars <- c(other_col = "other_col", b = "b") - p <- SpectriPy:::.single_rspec_to_pyspec(sps[1L]) - res <- SpectriPy:::.single_pyspec_to_rspec(p, vars) - expect_true(is.na(rtime(res))) - expect_true(is.na(msLevel(res))) - basiliskStop(cl) -}) \ No newline at end of file +test_that("pyspec_to_rspec works", { + p <- r_to_py(sps) + res <- pyspec_to_rspec(p) + expect_s4_class(res, "Spectra") + expect_equal(peaksData(res), peaksData(sps)) + expect_equal(spectraData(res, names(spectraVariableMapping())), + spectraData(sps, names(spectraVariableMapping()))) + + res_1 <- pyspec_to_rspec(p[0]) + expect_equal(res_1, res[1]) + + ## custom mapping. + sps$new_col <- seq_along(sps) + maps <- c(rtime = "retention_time", new_col = "r_new_col") + p <- rspec_to_pyspec(sps, mapping = maps) + res <- pyspec_to_rspec(p, mapping = maps) + expect_equal(peaksData(res), peaksData(sps)) + expect_equal(res$rtime, sps$rtime) + expect_equal(res$new_col, sps$new_col) + expect_equal(spectraVariableMapping(), .SPECTRA_2_MATCHMS) + + p <- rspec_to_pyspec(sps, character()) + res <- pyspec_to_rspec(p) + expect_true(all(is.na(res$msLevel))) + expect_true(all(is.na(res$rtime))) + expect_equal(spectraVariableMapping(), .SPECTRA_2_MATCHMS) +}) diff --git a/tests/testthat/test_filterSpectriPy.R b/tests/testthat/test_filterSpectriPy.R index 52a8a2d..918f6e9 100644 --- a/tests/testthat/test_filterSpectriPy.R +++ b/tests/testthat/test_filterSpectriPy.R @@ -11,6 +11,20 @@ caf$mz <- list( c(110.0710, 138.0655, 138.1057, 138.1742, 195.0864)) caf <- Spectra(caf) +mhd <- DataFrame( + msLevel = c(2L, 2L), + precursorMz = c(170.0924, 170.0924), + id = c("HMDB0000001", "HMDB0000001"), + name = c("1-Methylhistidine", "1-Methylhistidine")) +mhd$mz <- list( + c(109.2, 124.2, 124.5, 170.16, 170.52), + c(83.1, 96.12, 97.14, 109.14, 124.08, 125.1, 170.16)) +mhd$intensity <- list( + c(3.407, 47.494, 3.094, 100.0, 13.240), + c(6.685, 4.381, 3.022, 16.708, 100.0, 4.565, 40.643)) +mhd <- Spectra(mhd) +s_all <- c(caf, mhd) + ## select_by_intensity test_that("select_by_intensity constructor works", { ## errors @@ -19,13 +33,33 @@ test_that("select_by_intensity constructor works", { expect_error(select_by_intensity(intensity_to = c(1, 2)), "length 1") expect_error(select_by_intensity(intensity_to = c(1, 2)), "length 1") expect_error(select_by_intensity(foo = 1), "unused argument") - + res <- select_by_intensity(intensity_from = 5, intensity_to = 20) expect_s4_class(res, "select_by_intensity") expect_equal(res@intensity_from, 5) expect_equal(res@intensity_to, 20) }) +test_that("py_fun,select_by_intensity works", { + p <- select_by_intensity(intensity_from = 3, intensity_to = 24) + res <- py_fun(p) + expect_true(is(res, "functools.partial")) + expect_equal(py_to_r(res$keywords["intensity_from"]), 3.0) + expect_equal(py_to_r(res$keywords["intensity_to"]), 24.0) +}) + +test_that("filterSpectriPy,Spectra,select_by_intensity works", { + p <- select_by_intensity(intensity_from = 17, intensity_to = 300) + res <- filterSpectriPy(s_all, p) + expect_s4_class(res, "Spectra") + expect_equal(intensity(res)[[1L]], numeric()) + expect_true(all(intensity(res)[[2L]] > 17 & intensity(res)[[2L]] < 300)) + expect_true(all(intensity(res)[[3L]] > 17 & intensity(res)[[3L]] < 300)) + expect_true(all(intensity(res)[[4L]] > 17 & intensity(res)[[4L]] < 300)) + expect_equal(msLevel(s_all), msLevel(res)) + expect_equal(rtime(s_all), rtime(res)) +}) + ## select_by_mz test_that("select_by_mz constructor works", { ## errors @@ -34,211 +68,92 @@ test_that("select_by_mz constructor works", { expect_error(select_by_mz(mz_to = c(1, 2)), "length 1") expect_error(select_by_mz(mz_to = c(1, 2)), "length 1") expect_error(select_by_mz(foo = 1), "unused argument") - + res <- select_by_mz(mz_from = 0, mz_to = 400) expect_s4_class(res, "select_by_mz") expect_equal(res@mz_from, 0) expect_equal(res@mz_to, 400) }) +test_that("py_fun,select_by_mz works", { + p <- select_by_mz(mz_from = 3, mz_to = 24) + res <- py_fun(p) + expect_true(is(res, "functools.partial")) + expect_equal(py_to_r(res$keywords["mz_from"]), 3.0) + expect_equal(py_to_r(res$keywords["mz_to"]), 24.0) +}) + +test_that("filterSpectriPy,Spectra,select_by_mz works", { + p <- select_by_mz(mz_from = 100, mz_to = 180) + res <- filterSpectriPy(s_all, p) + expect_s4_class(res, "Spectra") + expect_true(all(mz(res)[[1L]] > 100 & mz(res)[[1L]] < 180)) + expect_true(all(mz(res)[[2L]] > 100 & mz(res)[[2L]] < 180)) + expect_true(all(mz(res)[[3L]] > 100 & mz(res)[[3L]] < 180)) + expect_true(all(mz(res)[[4L]] > 100 & mz(res)[[4L]] < 180)) + expect_equal(msLevel(s_all), msLevel(res)) + expect_equal(rtime(s_all), rtime(res)) +}) + ## remove_peaks_around_precursor_mz test_that("remove_peaks_around_precursor_mz constructor works", { ## errors - expect_error(remove_peaks_around_precursor_mz(mz_tolerance = c(1.2, 1.5)), "length 1") - expect_error(remove_peaks_around_precursor_mz(mz_tolerance = -1.2), "length 1") + expect_error(remove_peaks_around_precursor_mz( + mz_tolerance = c(1.2, 1.5)), "length 1") + expect_error(remove_peaks_around_precursor_mz( + mz_tolerance = -1.2), "length 1") expect_error(remove_peaks_around_precursor_mz(foo = 1), "unused argument") - + res <- remove_peaks_around_precursor_mz(mz_tolerance = 17) expect_s4_class(res, "remove_peaks_around_precursor_mz") expect_equal(res@mz_tolerance, 17) }) +test_that("py_fun,remove_peaks_around_precursor_mz works", { + p <- remove_peaks_around_precursor_mz(mz_tolerance = 200) + res <- py_fun(p) + expect_true(is(res, "functools.partial")) + expect_equal(py_to_r(res$keywords["mz_tolerance"]), 200) +}) + +test_that("filterSpectriPy,Spectra,remove_peaks_around_precursor_mz works", { + p <- remove_peaks_around_precursor_mz(mz_tolerance = 200) + res <- filterSpectriPy(s_all, p) + expect_s4_class(res, "Spectra") + expect_true(all(lengths(res) == 0)) + expect_equal(msLevel(s_all), msLevel(res)) + expect_equal(rtime(s_all), rtime(res)) + + p <- remove_peaks_around_precursor_mz(mz_tolerance = 10) + res <- filterSpectriPy(s_all, p) + expect_true(all(lengths(res) < lengths(s_all))) +}) + +## normalize_intensities test_that("normalize_intensities constructor works", { ## errors expect_error(normalize_intensities(foo = 1), "unused argument") - + res <- normalize_intensities() expect_s4_class(res, "normalize_intensities") }) -test_that(".fun name works parameterized", { - a <- select_by_intensity() - expect_equal(SpectriPy:::.fun_name(a), "select_by_intensity") - a <- select_by_mz() - expect_equal(SpectriPy:::.fun_name(a), "select_by_mz") - a <- remove_peaks_around_precursor_mz() - expect_equal(SpectriPy:::.fun_name(a), "remove_peaks_around_precursor_mz") - a <- normalize_intensities() - expect_equal(SpectriPy:::.fun_name(a), "normalize_intensities") +test_that("py_fun,select_by_intensity works", { + p <- select_by_mz(mz_from = 3, mz_to = 24) + res <- py_fun(p) + expect_true(is(res, "functools.partial")) + expect_equal(py_to_r(res$keywords["mz_from"]), 3.0) + expect_equal(py_to_r(res$keywords["mz_to"]), 24.0) }) -test_that("python_command and ...._param_string work", { - ## select_by_intensity - a <- select_by_intensity(intensity_from = 0, intensity_to = 100) - res <- .select_by_intensity_param_string(a) - expect_equal(res, "intensity_from=0, intensity_to=100") - res <- python_command(a) - expect_match(res, "import matchms\nfrom matchms.filtering import ") - expect_match(res, "matchms.filtering import select_by_intensity\nres = ") - expect_match(res, "select_by_intensity") - expect_match(res, "s, intensity_from=0, intensity_to=100) for s in py_spectrum_in]\n") - res <- python_command(a, "py_x") - expect_match(res, "m=0, intensity_to=100) for s in py_x]\n") - - ## select_by_mz - a <- select_by_mz(mz_from = 0, mz_to = 400) - res <- .select_by_mz_param_string(a) - expect_equal(res, "mz_from=0, mz_to=400") - res <- python_command(a) - expect_match(res, "import matchms\nfrom matchms.filtering import ") - expect_match(res, "matchms.filtering import select_by_mz\nres = ") - expect_match(res, "select_by_mz") - expect_match(res, "s, mz_from=0, mz_to=400) for s in py_spectrum_in]\n") - res <- python_command(a, "py_x") - expect_match(res, "m=0, mz_to=400) for s in py_x]\n") - - ## remove_peaks_around_precursor_mz - a <- remove_peaks_around_precursor_mz(mz_tolerance = 17) - res <- .remove_peaks_around_precursor_mz_param_string(a) - expect_equal(res, "mz_tolerance=17") - res <- python_command(a) - expect_match(res, "import matchms\nfrom matchms.filtering import ") - expect_match(res, "matchms.filtering import remove_peaks_around_precursor_mz\nres = ") - expect_match(res, "remove_peaks_around_precursor_mz") - expect_match(res, "s, mz_tolerance=17) for s in py_spectrum_in]\n") - res <- python_command(a, "py_x") - expect_match(res, "mz_tolerance=17) for s in py_x]\n") - - ## normalize_intensities - a <- normalize_intensities() - res <- .normalize_intensities_param_string(a) - expect_equal(res, character()) - res <- python_command(a) - expect_match(res, "import matchms\nfrom matchms.filtering import ") - expect_match(res, "matchms.filtering import normalize_intensities\nres = ") - expect_match(res, "normalize_intensities") - expect_match(res, "s, ) for s in py_spectrum_in]\n") - res <- python_command(a, "py_x") - expect_match(res, ") for s in py_x]\n") +test_that("filterSpectriPy,Spectra,select_by_mz works", { + p <- select_by_mz(mz_from = 100, mz_to = 180) + res <- filterSpectriPy(s_all, p) + expect_s4_class(res, "Spectra") + expect_true(all(mz(res)[[1L]] > 100 & mz(res)[[1L]] < 180)) + expect_true(all(mz(res)[[2L]] > 100 & mz(res)[[2L]] < 180)) + expect_true(all(mz(res)[[3L]] > 100 & mz(res)[[3L]] < 180)) + expect_true(all(mz(res)[[4L]] > 100 & mz(res)[[4L]] < 180)) + expect_equal(msLevel(s_all), msLevel(res)) + expect_equal(rtime(s_all), rtime(res)) }) - -test_that("python commands evaluation", { - vars <- c(precursorMz = "precursor_mz") - - ## invoke basilisk - cl <- basiliskStart(matchms_env) - - ## select_by_intensity - p <- select_by_intensity(intensity_from = 1000, intensity_to = 20000) - pstring <- SpectriPy:::python_command(p) - py$py_spectrum_in <- rspec_to_pyspec(caf, reference = import("matchms"), - mapping = vars) - py_run_string(pstring) - expect_is(py$res, "list") - res <- pyspec_to_rspec(py$res, mapping = vars) - expect_equal(intensity(caf)[[1]], c(340, 416, 2580, 412), tolerance = 1e-04) - expect_equal(intensity(caf)[[2]], c(388, 3270, 85, 54, 10111), tolerance = 1e-04) - expect_equal(unname(intensity(res)[[1]]), 2580, tolerance = 1e-04) - expect_equal(intensity(res)[[2]], c(3270, 10111), tolerance = 1e-04) - - ## select_by_mz - p <- select_by_mz(mz_from = 0, mz_to = 150) - pstring <- SpectriPy:::python_command(p) - py$py_spectrum_in <- rspec_to_pyspec(caf, reference = import("matchms"), - mapping = vars) - py_run_string(pstring) - expect_is(py$res, "list") - res <- pyspec_to_rspec(py$res, mapping = vars) - expect_equal(mz(caf)[[1]], c(135.0432, 138.0632, 163.0375, 195.088), tolerance = 1e-04) - expect_equal(mz(caf)[[2]], c(110.071, 138.0655, 138.1057, 138.1742, 195.0864), tolerance = 1e-04) - expect_equal(mz(res)[[1]], c(135.0432, 138.0632), tolerance = 1e-04) - expect_equal(mz(res)[[2]], c(110.071, 138.0655, 138.1057, 138.1742), tolerance = 1e-04) - - ## remove_peaks_around_precursor_mz - p <- remove_peaks_around_precursor_mz(mz_tolerance = 20) - pstring <- SpectriPy:::python_command(p) - py$py_spectrum_in <- rspec_to_pyspec(caf, reference = import("matchms"), - mapping = vars) - py_run_string(pstring) - expect_is(py$res, "list") - res <- pyspec_to_rspec(py$res, mapping = vars) - expect_equal(mz(caf)[[1]], c(135.0432, 138.0632, 163.0375, 195.088), tolerance = 1e-04) - expect_equal(mz(caf)[[2]], c(110.071, 138.0655, 138.1057, 138.1742, 195.0864), tolerance = 1e-04) - expect_equal(mz(res)[[1]], c(135.0432, 138.0632, 163.0375), tolerance = 1e-04) - expect_equal(mz(res)[[2]], c(110.071, 138.0655, 138.1057, 138.1742), tolerance = 1e-04) - - ## normalize_intensities - p <- normalize_intensities() - pstring <- SpectriPy:::python_command(p) - py$py_spectrum_in <- rspec_to_pyspec(caf, reference = import("matchms"), - mapping = vars) - py_run_string(pstring) - expect_is(py$res, "list") - res <- pyspec_to_rspec(py$res, mapping = vars) - expect_equal(intensity(caf)[[1]], c(340, 416, 2580, 412), tolerance = 1e-04) - expect_equal(intensity(caf)[[2]], c( 388, 3270, 85, 54, 10111)) - expect_equal(intensity(res)[[1]], c(0.1317829, 0.1612403, 1.0, 0.1596899), tolerance = 1e-04) - expect_equal(intensity(res)[[2]], c(0.038374048, 0.323410147, 0.008406686, 0.005340718, 1.0), tolerance = 1e-04) - - basiliskStop(cl) -}) - -test_that(".filter_spectra_python works", { - res <- SpectriPy:::.filter_spectra_python(caf, select_by_intensity()) - expect_true(length(res) == 2) - expect_is(res, "Spectra") - - ## try with empty Spectra - res <- SpectriPy:::.filter_spectra_python(caf[integer()], select_by_intensity()) - expect_true(length(res) == 0) - expect_is(res, "Spectra") -}) - -test_that("filterSpectriPy works", { - - ## check object caf before filtering - expect_equal(mz(caf)[[1]], c(135.0432, 138.0632, 163.0375, 195.088), tolerance = 1e-04) - expect_equal(mz(caf)[[2]], c(110.071, 138.0655, 138.1057, 138.1742, 195.0864), tolerance = 1e-04) - expect_equal(intensity(caf)[[1]], c(340, 416, 2580, 412), tolerance = 1e-04) - expect_equal(intensity(caf)[[2]], c( 388, 3270, 85, 54, 10111), tolerance = 1e-04) - - ## select_by_intensity - res <- filterSpectriPy(caf, - param = select_by_intensity(intensity_from = 1000, intensity_to = 20000)) - expect_true(length(res) == 2) - expect_is(res, "Spectra") - expect_equal(unname(intensity(res)[[1]]), 2580, tolerance = 1e-04) - expect_equal(intensity(res)[[2]], c(3270, 10111), tolerance = 1e-04) - - ## select_by_mz - res <- filterSpectriPy(caf, - param = select_by_mz(mz_from = 0, mz_to = 150)) - expect_true(length(res) == 2) - expect_is(res, "Spectra") - expect_equal(mz(res)[[1]], c(135.0432, 138.0632), tolerance = 1e-04) - expect_equal(mz(res)[[2]], c(110.071, 138.0655, 138.1057, 138.1742), tolerance = 1e-04) - - ## remove_peaks_around_precursor_mz - res <- filterSpectriPy(caf, - param = remove_peaks_around_precursor_mz(mz_tolerance = 20)) - expect_true(length(res) == 2) - expect_is(res, "Spectra") - expect_equal(mz(res)[[1]], c(135.0432, 138.0632, 163.0375), tolerance = 1e-04) - expect_equal(mz(res)[[2]], c(110.071, 138.0655, 138.1057, 138.1742), tolerance = 1e-04) - - ## normalize_intensities - res <- filterSpectriPy(caf, - param = normalize_intensities()) - expect_true(length(res) == 2) - expect_is(res, "Spectra") - expect_equal(intensity(caf)[[1]], c(340, 416, 2580, 412), tolerance = 1e-04) - expect_equal(intensity(caf)[[2]], c(388, 3270, 85, 54, 10111), tolerance = 1e-04) - expect_equal(intensity(res)[[1]], c(0.1317829, 0.1612403, 1.0, 0.1596899), tolerance = 1e-04) - expect_equal(intensity(res)[[2]], c(0.038374048, 0.323410147, 0.008406686, 0.005340718, 1.0), tolerance = 1e-04) - - ##caf_mod <- caf - ##caf_mod$precursorMz[2] <- NA_real_ - ##expect_error(filterSpectriPy(caf_mod, - ## param = remove_peaks_around_precursor_mz()), - ## "Expect precursor to be positive") -}) \ No newline at end of file diff --git a/vignettes/SpectriPy.qmd b/vignettes/SpectriPy.qmd new file mode 100644 index 0000000..09218e5 --- /dev/null +++ b/vignettes/SpectriPy.qmd @@ -0,0 +1,450 @@ +--- +title: "Enabling integration of Python libraries and R packages for combined mass spectrometry data analysis" +package: SpectriPy +vignette: > + %\VignetteIndexEntry{Enabling integration of Python libraries and R packages for combined mass spectrometry data analysis} + %\VignetteKeywords{Mass Spectrometry, MS, MSMS, Metabolomics, Infrastructure, Quantitative} + %\VignettePackage{SpectriPy} + %\VignetteEncoding{UTF-8} + %\VignetteEngine{quarto::html} + %\VignetteDepends{Spectra,BiocStyle,SpectriPy,reticulate,MsBackendMgf,msdata,mzR} +--- + +**Compiled**: `r date()` + +# Introduction + +Powerful software libraries for mass spectrometry (MS) data are available in +both Python and R, covering specific and sometimes distinct aspects in the +analysis of proteomics and metabolomics data. R's *reticulate* package converts +basic data types between Python and R and enables a seamless interoperability of +both programming languages. The *SpectriPy* package extends *reticulate* +allowing to translate MS data data structures between Python and R. In addition, +functionality from Python's *matchms* library is directly wrapped into R +functions allowing a seamless integration into R based workflows. *SpectriPy* +thus enables powerful proteomics or metabolomics analysis workflows combining +the strengths of Python and R MS libraries. + + +# Installation + +If the *BiocManager* package is not already available, please install it with +`install.packages("BiocManager")`. To install the development version of +*SpectriPy* from GitHub, please install in addition the *remotes* package with +`install.packages("remotes"). As a system dependency, the package requires a +Python environment to be available. + +LLLL Update installation instructions. +and can be installed +with the *BiocManager* R package using the command +`BiocManager::install("RforMassSpectrometry/SpectriPy")`. This will install the +latest version of the package from GitHub. +All required python libraries are installed automatically on demand. +Note that first installation or first invocation of specific functions might +take long because of this installation process. + + +# Translating data structures between Python and R + +The [*reticulate*](https://rstudio.github.io/reticulate/) package enables a +seamless integration of Python with R by translating the core data structures +between the two programming languages and sharing a Python (respectively R) +runtime environment that can be accessed from the other programming languages +including shared variables. The *SpectriPy* package builds on that providing +functionality to translate data structures for mass spectrometry (MS) data +between the two languages. Specifically, the package translates between R's +`Spectra` objects (from the `r BiocStyle::Biocpkg("Spectra")` package) and +`matchms.Spectrum` objects from the +[*matchms*](https://github.com/matchms/matchms) Python library. + +```{r, include = FALSE} +library(reticulate) +``` + + +## Library loading and system setup + +Below we load all required packages. By loading *SpectriPy*, the package will +evaluate if the required Python libraries (i.e. *matchms* version 0.28.2) are +available. If they are not available, *SpectriPy* tries to install them using +functionality from the *reticulate* R package (see also here for more information on +Python and library +[versions](https://rstudio.github.io/reticulate/articles/versions.html)). By +default, the *SpectriPy* will install its dependencies into LLLL TODO continue +here once it's clear how we do that). + +The *reticulate* package will be loaded by *SpectriPy* hence, the Python/R +integration provided by that package will be available as well. To better +discriminate between R and Python code chunks we add and show the comment `#' R +session:` or `#' Python session:` to label the R and Python code chunks, +respectively. + +```{r} +#' R session: +library(Spectra) +library(SpectriPy) +``` + +TODO: add info on detailed/custom configuration (separate subsection). + +## R to Python + +In the first examples we translated MS data from R to Python. We first load data +from a mzML data file provided through the `r BiocStyle::Biocpkg("msdata")` +package. + +```{r} +#| message: false +#' R session: +#' Loading the data from a mzML file as a `Spectra` object. +fl <- system.file("TripleTOF-SWATH", "PestMix1_DDA.mzML", package = "msdata") +s_r <- Spectra(fl) +``` + +We next restrict the data to MS2 data and remove spectra with less than 3 +peaks (fragments). + +```{r} +#' R session: +#' Restrict to MS level 2 spectra +s_r <- filterMsLevel(s_r, 2) +s_r <- s_r[lengths(s_r) >= 3] +s_r +``` + +The data is thus available in R as a `Spectra` object. This MS data structure +can be translated to corresponding data structures in Python using the +`rspec_to_pyspec()` function: + +```{r} +#' R session: +#' Convert the R Spectra to a list of Python matchms.Spectrum objects +s_p <- rspec_to_pyspec(s_r) +``` + +The `s_p` variable is now a Python list of `matchms.Spectrum` objects: + +```{r} +#' R session: +#' Class of the converted variable +class(s_p) + +#' First element +class(s_p[1]) +``` + +While the variable is stored in the R session, it can also be accessed from the +current Python session managed through the *reticulate* package. This Python +session contains the special *variable* `r` that can be used to access any +variable defined in the R session using `r.`. In the Python code +block below we first load the required Python library *matchms* and access the +translated MS data. + +```{python} +#' Python session: +import matchms + +#' data type of the variable: +type(r.s_p) + +#' the length of the list: +len(r.s_p) + +#' data type of the first element: +type(r.s_p[0]) +``` + +We can then for example process the data in Python and convert the results back +to R. Below we load the *matchms.filtering* library and scale the intensity +values of each spectrum with the `normalize_intensities()` function such that +their sum is 1. + +```{python} +#' Python session: +import matchms.filtering as mms_filt + +#' Iterate over the Spectrum list and scale the intensities +for i in range(len(r.s_p)): + r.s_p[i] = mms_filt.normalize_intensities(r.s_p[i]) + +#' Intensities for the first spectrum +r.s_p[0].peaks.intensities +``` + +We can also access the changed data from R. Through the *reticulate* package it +is possible to call attributes and functions in Python directly from R. To +extract the intensities of the first spectrum we can use the same code as above, +just replacing `.` with `$`. Note also that, since the variable is a Python +object, we need to use index `0` to access the first element. + +```{r} +#' R session: +#' Access the intensities of the first spectrum +s_p[0]$peaks$intensities +``` + +As a result we got a Python array, that we can however translate to the +respective R data type (`numeric`) using the `py_to_r()` function. For easier +readability of the code we use the R `|>` pipe operator to sequentially combine +the functions. + +```{r} +#' R session: +#' Access the intensities and translate to R +s_p[0]$peaks$intensities |> + py_to_r() +``` + +A more efficient way to translate the results back to R data structure is +however the `pyspec_to_rspec()` function, that will translate the list of +`matchms.Spectrum` objects to a `Spectra` object. + +```{r} +#' R session: +#' Convert the data back to R data structures +s_r2 <- pyspec_to_rspec(s_p) +``` + +And the `s_r2` variable contains now the scaled intensities. + +```{r} +#' R session: +intensity(s_r2) +``` + +Note that any R data type for which a `r_to_py()` method is implemented, would +also be automatically be translated into the respective Python data type when +the variable in R is accessed from Python. The variable `fl` that we defined +above and which contains the name of the data file would for example be +automatically converted from the R `character` data type to a Python `str`: + +```{python} +#' Python session: +#' Access the `fl` variable from the R session: +r.fl + +type(r.fl) +``` + +*SpectriPy* provides also a `r_to_py()` method for `Spectra` objects, thus, we +would not need to convert the data *manually* but the data would be translated +*on-the-fly* when the `Spectra` object is accessed from Python: + +```{python} +#' Python session: +#' Access the `Spectra` object with the original data from R; the data +#' gets directly translated on-the-fly +r.s_r + +#' Access the intensities of the first spectrum +r.s_r[0].peaks.intensities +``` + +Note however that the `rspec_to_pyspec()` and `pyspec_to_rspec()` allow also to +configure the handling of the spectra variables (i.e. metadata) during the +conversion and should thus be preferred. + +Also, be however aware that the conversion between R and Python generally +**copies** the data, thus, any data processing performed in the other +programming language will not change the data variables from the former (unless +they are directly replaced as in our example above). + + +## Python to R + +To show conversion of MS data from Python to R we import in this section a file +in MGF format using the *matchms* Python library. The MGF file is provided +within the *SpectriPy* package and we define below its file name and path in R +and assign that to a variable in R. + +```{r} +#' R session: +f_mgf <- system.file("extdata", "mgf", "test.mgf", package = "SpectriPy") +``` + +We next load the required Python library and import the data in Python. We can +access the variable (defined in the R session) with the file name to import the +data from through the `r.`. + +```{python} +#| warning: false + +#' Python session +import matchms +from matchms.importing import load_from_mgf + +mgf_p = list(load_from_mgf(r.f_mgf)) +mgf_p +``` + +The MS data from the MGF is now loaded in Python as a `list` of +`matchms.Spectrum` objects. We can directly access this variable +also from the R session through `py$`. +Below we access the first spectrum in that list: + +```{r} +#' R session: +py$mgf_p[[1]] +``` + +The data is thus provided in a *matchms.Spectrum* object. We could extract the +*m/z* and intensity peak matrix from that spectrum using built-in functionality +from the *reticulate* package that allows to call Python functions from R or +translates between basic data types. As an example we below get the `peaks` +attribute from the first spectrum and convert that to a Python `numpy` +array. This array can then be translated to an R `matrix` with the `py_to_r()` +function (see also the [*Calling Python from +R*](https://rstudio.github.io/reticulate/articles/calling_python.html) vignette +from the *reticulate* package for examples on accessing data from Python): + +```{r} +#' R session: +py_to_r(py$mgf_p[[1]]$peaks$to_numpy) +``` + +While such functionality could thus be used to extract the MS data from Python, +it is (for R users) more convenient to transform the MS data to a `Spectra` +object using the `pyspec_to_rspec()` function and use this container to perform +MS data analyses in R. Note that below we use the `py_get_attr()` function to +get the Python variable with the MS data instead of `py$mgf_p`. The `py$mgf_p` +calls by default `py_to_r()` on the variable which will convert the Python list +to an R `list`. `pyspec_to_rspec()` would thus iterate over the R `list` (in R), +while with `py_get_attr()` the loop will be performed in Python, which has a +minimal performance advantage. + +```{r} +#' R session: +mgf_r <- pyspec_to_rspec(py_get_attr(py, "mgf_p")) +mgf_r +``` + +The full MS data is thus now available as a `Spectra` object. Note however that +with `pyspec_to_rspec()` the data gets copied, thus, we have now two (detached) +variables containing the MS data, one in R and one in Python. + + +## Conversion of spectra variables/metadata + +Conversion of the MS peaks data (i.e. *m/z* and intensity values) is always +performed by the `rspec_to_pyspec()` and `pyspec_to_rspec()` functions. But next +to the peaks data, also additional information are available for individual +spectra. In R/*Spectra* they are called *spectra variables* while in *matchms* +they are stored as a *metadata* attribute of a `matchms.Spectrum`. The +*SpectriPy* package defines a core set of spectra variables/metadata that are by +default converted (transferred) by the `rspec_to_pyspec()` and +`pyspec_to_rspec()` function. They are also renamed to match the respective (and +differing) definitions in *Spectra* and *matchms*. This set of default variables +can be accessed using the `defaultSpectraVariableMapping()` function: + +```{r} +#' R session: +defaultSpectraVariableMapping() +``` + +These variables, if present in the respective data object, are transferred to +the MS data structure of the other programming language. The names of this +character vector represent the name of the spectra variable in the `Spectra` +object, while the value (element) is the name of the metadata key in Python's +`matchms.Spectrum` class. The `precursorMz()` spectra variable in thus +transferred by default to Python and renamed to `"precursor_mz"`. Any additional +spectra variable not being defined will **not** be converted. Below we inspect +the available metadata in the `matchms.Spectrum` objects that were imported from +the MGF file. + +```{python} +#' Python session: +#' Available metadata for the first spectrum +mgf_p[0].metadata.keys() +``` + +Several additional metadata variables, such as `"smiles"`, `"inchi"` or +`"compound_name"`, not part of the default variables, would be available. To +also transfer these to our `Spectra` object we could add a mapping for them to +the default mapping and pass that to the `pyspec_to_rspec()` function. Both the +`rspec_to_pyspec()` and `pyspec_to_rspec()` functions allow to provide a custom +mapping between spectra variables/metadata with the `mapping` parameter. Below +we define such mapping and call the `pyspec_to_rspec()` to convert the full +data. + +```{r} +#' R session: +#' Add mapping for additional spectra variables to the default mapping +map <- c(defaultSpectraVariableMapping(), smiles = "smiles", + inchi = "inchi", name = "compound_name") +mgf_r <- pyspec_to_rspec(py_get_attr(py, "mgf_p"), mapping = map) +``` + +The respective metadata values have been added as new spectra variables to our +`Spectra` object: + +```{r} +#' R session: +mgf_r$name |> head() + +mgf_r$inchi |> head() +``` + +The `r_to_py()` methods don't support additional parameters, thus, in order to +use a similar mapping also with the `r_to_py()` method for `Spectra` the +*global* spectra variable mapping would have to be changed. See the help of the +`setSpectraVariableMapping()` function for more details. + + +## Combined MS data analysis + +With the functionality to translate between R and Python MS data structures, the +*SpectriPy* package enables thus a MS data analysis combining functionality +provided by both R and Python libraries. Some of the functionality of the +*matchms* Python library was also directly wrapped into R functions simplifying +their use and inclusion in R-based workflows (see help of `compareSpectraPy()` +and `filterSpectriPy()` functions). Using both R and Python code for data +analyses has however clear advantages, since it allows a more customized use of +the functionality and a better performance. As an example we below calculate +spectra similarities between the MS2 spectra imported from the mzML and the MGF +file. + +In R we can use the `compareSpectra()` function that calculates a pairwise +similarity between the compared spectra. With the default settings a normalized +dot product similarity is calculated. We calculate the similarity between the +`Spectra` object with the data imported from the mzML file and the `Spectra` +object that was translated from Python using the `pyspec_to_rspec()` function. + +```{r} +#' R session: +#' Calculate the pairwise similarity between all spectra +sim <- compareSpectra(s_r, mgf_r, tolerance = 0.1) +dim(sim) +``` + +For comparison, we calculate in Python a pairwise similarity using the *cosine +hungarian* method. Here we use the previously translated `Spectra` object with +the data from the mzML file and the MS data imported from the MGF file. + +```{python} +#' Python session: +import matchms.similarity as mms_similarity + +#' Calculate similarity scores +scores = matchms.calculate_scores( + r.s_p, mgf_p, mms_similarity.CosineHungarian(tolerance = 0.1)) +#' Extract similarity scores +sim = scores.to_array()["CosineHungarian_score"] +``` + +We can also directly compare the scores calculated using the two different +algorithms. + +```{r} +plot(sim, py$sim, pch = 21, col = "#000000ce", bg = "#00000060", + xlab = "Dot product", ylab = "Cosine Hungarian") +grid() +``` + + + + +# Session information + +```{r} +sessionInfo() +``` diff --git a/vignettes/detailed-installation-configuration.qmd b/vignettes/detailed-installation-configuration.qmd new file mode 100644 index 0000000..a57da08 --- /dev/null +++ b/vignettes/detailed-installation-configuration.qmd @@ -0,0 +1,269 @@ +--- +title: "Detailed information on installation and configuration" +package: SpectriPy +output: + BiocStyle::html_document: + toc_float: true +vignette: > + %\VignetteIndexEntry{Detailed information on installation and configuration} + %\VignetteKeywords{Mass Spectrometry, MS, MSMS, Metabolomics, Infrastructure, Quantitative} + %\VignettePackage{SpectriPy} + %\VignetteEncoding{UTF-8} + %\VignetteEngine{quarto::html} + %\VignetteDepends{BiocStyle,SpectriPy,reticulate} +--- + +```{r style, echo = FALSE, results = 'asis', message = FALSE} +BiocStyle::markdown() +``` + +# Introduction + +This document provides detailed installation and configuration instructions for +the [*SpectriPy*](https://github.com/RforMassSpectrometry/SpectriPy) +package. For first time R users see also section @sec-first in the appendix. + + +# Installation + +## System requirements + +*SpectriPy* relies on, and extends, the +[*reticulate*](https://rstudio.github.io/reticulate/index.html) package for +interoperability between Python and R. For installation of *SpectriPy*, Python +needs to be installed on the system. See also the [Install Python +Packages](https://rstudio.github.io/reticulate/articles/python_packages.html) +documentation from the *reticulate* package for information on installing +Python. Ideally *miniconda* should be installed on the system. The code below +shows how *miniconda* can be installed with the *reticulate* package (which +needs to be first installed with `install.package("reticulate")` if not already +available): + +```{r} +#| eval: false +install.packages("reticulate") +reticulate::install_miniconda() +``` + +# Configuration + +- which reticulate? + +# Appendix + +## Installation instructions for first-time R users{#sec-first} + +Instructions to install R and RStudio for the first time are described below, +from source +[https://rstudio-education.github.io/hopr/packages2.html](https://rstudio-education.github.io/hopr/packages2.html). + +## Installing R and RStudio + +To get started with R, you need to acquire your own copy. This appendix will +show you how to download R as well as RStudio, a software application that makes +R easier to use. You’ll go from downloading R to opening your first R session. + +Both R and RStudio are free and easy to download. + +### How to Download and Install R + +R is maintained by an international team of developers who make the language +available through the web page of The Comprehensive R Archive Network. The top +of the web page provides three links for downloading R. Follow the link that +describes your operating system: Windows, Mac, or Linux. + +#### Windows + +To install R on Windows, click the “Download R for Windows” link. Then click the +“base” link. Next, click the first link at the top of the new page. This link +should say something like “Download R 3.0.3 for Windows,” except the `3.0.3` +will be replaced by the most current version of R. The link downloads an +installer program, which installs the most up-to-date version of R for +Windows. Run this program and step through the installation wizard that +appears. The wizard will install R into your program files folders and place a +shortcut in your Start menu. Note that you’ll need to have all of the +appropriate administration privileges to install new software on your machine. + +#### Mac + +To install R on a Mac, click the “Download R for Mac” link. Next, click on the +`R-3.0.3` package link (or the package link for the most current release of +R). An installer will download to guide you through the installation process, +which is very easy. The installer lets you customize your installation, but the +defaults will be suitable for most users. I’ve never found a reason to change +them. If your computer requires a password before installing new progams, you’ll +need it here. + +> **Binaries Versus Source** +> +> R can be installed from precompiled binaries or built from source on any +> operating system. For Windows and Mac machines, installing R from binaries is +> extremely easy. The binary comes preloaded in its own installer. Although you +> can build R from source on these platforms, the process is much more +> complicated and won’t provide much benefit for most users. For Linux systems, +> the opposite is true. Precompiled binaries can be found for some systems, but +> it is much more common to build R from source files when installing on +> Linux. The download pages on [CRAN’s website](https://cran.r-project.org/) +> provide information about building R from source for the Windows, Mac, and +> Linux platforms. + +#### Linux + +R comes preinstalled on many Linux systems, but you’ll want the newest version +of R if yours is out of date. The [CRAN website](https://cran.r-project.org/) +provides files to build R from source on Debian, Redhat, SUSE, and Ubuntu +systems under the link “Download R for Linux.” Click the link and then follow +the directory trail to the version of Linux you wish to install on. The exact +installation procedure will vary depending on the Linux system you use. CRAN +guides the process by grouping each set of source files with documentation or +README files that explain how to install on your system. + +> **32-bit Versus 64-bit** +> +> R comes in both 32-bit and 64-bit versions. Which should you use? In most +> cases, it won’t matter. Both versions use 32-bit integers, which means they +> compute numbers to the same numerical precision. The difference occurs in the +> way each version manages memory. 64-bit R uses 64-bit memory pointers, and +> 32-bit R uses 32-bit memory pointers. This means 64-bit R has a larger memory +> space to use (and search through). As a rule of thumb, 32-bit builds of R are +> faster than 64-bit builds, though not always. On the other hand, 64-bit builds +> can handle larger files and data sets with fewer memory management +> problems. In either version, the maximum allowable vector size tops out at +> around 2 billion elements. If your operating system doesn’t support 64-bit +> programs, or your RAM is less than 4 GB, 32-bit R is for you. The Windows and +> Mac installers will automatically install both versions if your system +> supports 64-bit R. + +### Using R + +R isn’t a program that you can open and start using, like Microsoft Word or +Internet Explorer. Instead, R is a computer language, like C, C++, or UNIX. You +use R by writing commands in the R language and asking your computer to +interpret them. In the old days, people ran R code in a UNIX terminal window—as +if they were hackers in a movie from the 1980s. Now almost everyone uses R with +an application called RStudio, and I recommend that you do, too. + +> **R and UNIX** +> +> You can still run R in a UNIX or BASH window by typing the command: +> +> `R` +> +> which opens an R interpreter. You can then do your work and close the +> interpreter by running q() when you are finished. + +### RStudio + +RStudio is an application like Microsoft Word—except that instead of helping you +write in English, RStudio helps you write in R. I use RStudio throughout the +book because it makes using R much easier. Also, the RStudio interface looks the +same for Windows, Mac OS, and Linux. That will help me match the book to your +personal experience. + +You can download [RStudio](https://posit.co/products/open-source/rstudio/) for +free. Just click the “Download RStudio” button and follow the simple +instructions that follow. Once you’ve installed RStudio, you can open it like +any other program on your computer—usually by clicking an icon on your desktop. + +> **The R GUIs** +> +> Windows and Mac users usually do not program from a terminal window, so the +> Windows and Mac downloads for R come with a simple program that opens a +> terminal-like window for you to run R code in. This is what opens when you +> click the R icon on your Windows or Mac computer. These programs do a little +> more than the basic terminal window, but not much. You may hear people refer +> to them as the Windows or Mac R GUIs. + +When you open RStudio, a window appears with three panes in it, as in Figure 1. +The largest pane is a console window. This is where you’ll run your R code and +see results. The console window is exactly what you’d see if you ran R from a +UNIX console or the Windows or Mac GUIs. Everything else you see is unique to +RStudio. Hidden in the other panes are a text editor, a graphics window, a +debugger, a file manager, and much more. You’ll learn about these panes as they +become useful throughout the course of this book. The RStudio IDE for R. + +![The RStudio IDE for R](https://rstudio-education.github.io/hopr/images/hopr_aa01.png) + +Figure 1: The RStudio IDE for R. + +> **Do I still need to download R?** +> +> Even if you use RStudio, you’ll still need to download R to your +> computer. RStudio helps you use the version of R that lives on your computer, +> but it doesn’t come with a version of R on its own. + +### Opening R + +Now that you have both R and RStudio on your computer, you can begin using R by +opening the RStudio program. Open RStudio just as you would any program, by +clicking on its icon or by typing “RStudio” at the Windows Run prompt. + +## SpectriPy pre-requisites and installation instructions + +### Installing Bioconductor + +Bioconductor is required to install SpectriPy, as described below and from +source [https://bioconductor.org/install/](https://bioconductor.org/install/). + +The current release of Bioconductor is version 3.20; it works with R version +4.4.0. Users of older R and Bioconductor must update their installation to take +advantage of new features and to access packages that have been added to +Bioconductor since the last release. + +The development version of Bioconductor is version 3.21; it works with R version +4.5.0. More recent ‘devel’ versions of R (if available) will be supported during +the next Bioconductor release cycle. + +Once R has been installed, get the latest version of Bioconductor by starting R +and entering the following commands. + +It may be possible to change the Bioconductor version of an existing +installation; see the *Changing version* section of the BiocManager vignette. + +Details, including instructions to install additional packages and to update, +find, and troubleshoot are provided below. A devel version of Bioconductor is +available. There are good reasons for using `BiocManager::install()` for +managing Bioconductor resources. + +```{r} +#| eval: false +if (!require("BiocManager", quietly = TRUE)) + install.packages("BiocManager") +BiocManager::install(version = "3.20") +``` + +### Installing SpectriPy + +Since *SpectriPy* is currently still in development, you will need the *remotes* +package to install it from GitHub. If you do not have the *remotes* package yet, +you can install it using the following command: + +```{r} +#| eval: false +install.packages("remotes") +``` + +To install the *SpectriPi* package use + +```{r} +#| eval: false +BiocManager::install("RforMassSpectrometry/SpectriPy") +``` + +### Check installation completed + +The status of installation can be easily checked by starting R and entering the +following commands. + +This command loads the *SpectriPy* package, if correct installed. + +```{r} +library(SpectriPy) +``` + + +# Session information + +```{r} +sessionInfo() +```