Skip to content
This repository was archived by the owner on Oct 20, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
^hooks$
^playground$
^revdep$
^.*\.Rproj$
^\.Rproj\.user$
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
*.so
*.o
*.rds
.Rproj.user
5 changes: 3 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
Package: MALDIquant
Version: 1.19
Version: 1.19.50
Date: 2018-11-26
Title: Quantitative Analysis of Mass Spectrometry Data
Authors@R: c(person("Sebastian", "Gibb", role=c("aut", "cre"),
email="mail@sebastiangibb.de",
comment=c(ORCID="0000-0001-7406-4443")), person("Korbinian",
"Strimmer", role="ths"))
biocViews:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary to trick R to install dependencies from Bioconductor, right? (I am not sure that this works/will be accepted on CRAN)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your reply/review Sebastian.

I just tested this thing; it does not work, although I found a lot of people suggesting this trick! I see your point on removing the dependency on matter. As you showed in your onDiskVec example, we just need to write and read one vector at a time. The onDiskVec should have path, offset, size and length slots to account for imzML datatype.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, if that don't work I don't really want to "Depend" or "Import" a Bioconductor package if it is not really necessary. If it would be possible to move it to "Suggests" it would be fine (but I am not sure that this is really possible).

I add a new branch: https://github.com/sgibb/MALDIquant/tree/OnDiskVector

If you like you could use and play with the new OnDiskVector class. It should work for both non-imzML and imzML data (it supports single files and offsets):

library("MALDIquant")

odv <- OnDiskVector(1:10)
odv@path
# [1] "/tmp/Rtmpaz3SQG/file13ce3a31c136"
odv[1:5]
# [1] 1 2 3 4 5

f <- system.file(
    file.path("exampledata", "tiny_continuous.ibd"),
    package="MALDIquantForeign"
)
odv2 <- OnDiskVector(path=f, offset=56, n=5)
odv2@path
# [1] "/home/sebastian/opt/R/lib/R/library/MALDIquantForeign/exampledata/tiny_continuous.ibd"
odv2[1:5]
# [1]  6  7  8  9 10

Implementation could be found in https://github.com/sgibb/MALDIquant/blob/OnDiskVector/R/OnDiskVector-class.R

(Currently I have no time to really finish this thing. There is still the problem with odv2 <- odv and a subsequently different change of both vectors on the same file.)

Copy link
Owner

@sgibb sgibb Mar 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use a similar approach as we discussed for MSnbase in lgatto/MSnbase#429. TLDR; we store a modification counter in the object and in an additional file (in MSnbase we use the same file but that won't work for existing idb files). The modification counter is incremented if [<- is called, e.g.:

odv <- OnDiskVector(1:3)
odv2 <- odv
odv2[] <- 4:6
odv[]
# Error in .isModified.OnDiskVector(x) :
#   /tmp/RtmpKh7TLm/file54ec578fcbd3 was modified by a different object.
odv2[]
# [1] 4 5 6

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your reply Sebastian & sorry for the delayed reply.

So the way to go now, is to replicate whatever I did with the matter package but using the onDiskVector Class instead on the newly opened branch, right?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you satisfied with the OnDiskVector class that would be the way to go.

Depends: R (>= 3.2.0), methods
Imports: parallel
Imports: parallel, matter (>= 1.8.0)
Suggests: knitr, testthat (>= 0.8)
Description: A complete analysis pipeline for matrix-assisted laser
desorption/ionization-time-of-flight (MALDI-TOF) and other
Expand Down
11 changes: 10 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,24 @@ importFrom("utils",
"relist",
"tail")

importFrom("matter",
"matter_vec",
"matter_fc")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any code that uses matter_fc?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I remove, when building the package a warning is always thrown:

Warning: subclass "matter_fc" of class "matter_vec" is not local and cannot be updated for new inheritance information; consider setClassUnion()


importClassesFrom("matter",
"matter_vec",
"matter_fc")

exportClasses("MassPeaks",
"MassSpectrum")
"MassSpectrum",
"MassSpectrumOnDisk")

export("alignSpectra",
"averageMassSpectra",
"binPeaks",
"createMassPeaks",
"createMassSpectrum",
"createMassSpectrumOnDisk",
"determineWarpingFunctions",
"filterPeaks",
"findEmptyMassObjects",
Expand Down
18 changes: 16 additions & 2 deletions R/AllClasses.R
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
## basic class for all spectra based information

## Set a class union to extend slots
# type to matter Objects
setClassUnion("NumericOrOnDisk", c("numeric", "matter_vec"))


setClass("AbstractMassObject",
slots=list(mass="numeric", intensity="numeric",
slots=list(mass="NumericOrOnDisk",
intensity="NumericOrOnDisk",
metaData="list"),
prototype=list(mass=numeric(), intensity=numeric(),
prototype=list(mass=numeric(),
intensity=numeric(),
metaData=list()),
contains="VIRTUAL")

## represent a spectrum
setClass("MassSpectrum",
contains="AbstractMassObject")

## represent an On-disk spectrum
setClass("MassSpectrumOnDisk",
slots = list(path = "character"),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why this slot is needed? Isn't the file path part of the matter_vec object and easily accessible by matter::paths(matter_vec_object)?

Copy link
Author

@dsammour dsammour Mar 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but I added it for convenience; not to call additional functions. If we opt to removing matter altogether, then it will have to stay.

prototype = list(path = character()),
contains="AbstractMassObject")

## represent a peak list from a single spectrum
setClass("MassPeaks",
slots=list(snr="numeric"),
Expand Down
54 changes: 54 additions & 0 deletions R/AllGenerics.R
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,60 @@ if (is.null(getGeneric("totalIonCurrent"))) {
}
## end of MassSpectrum

## MassSpectrumOnDisk
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these generics are already defined. No need to add any of them again.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought every class has its own Generics. I see your point now, I'll remove them.

if (is.null(getGeneric("approxfun"))) {
setGeneric("approxfun",
function(x, y=NULL, method="linear", yleft, yright, rule=1, f=0,
ties=mean)
standardGeneric("approxfun"))
}
if (is.null(getGeneric("calibrateIntensity"))) {
setGeneric("calibrateIntensity",
function(object, ...) standardGeneric("calibrateIntensity"))
}
if (is.null(getGeneric("detectPeaks"))) {
setGeneric("detectPeaks",
function(object, ...) standardGeneric("detectPeaks"))
}
if (is.null(getGeneric("estimateBaseline"))) {
setGeneric("estimateBaseline",
function(object, method=c("SNIP", "ConvexHull", "Median"), ...)
standardGeneric("estimateBaseline"))
}
if (is.null(getGeneric("estimateNoise"))) {
setGeneric("estimateNoise",
function(object, ...) standardGeneric("estimateNoise"))
}
if (is.null(getGeneric(".findLocalMaxima"))) {
setGeneric(".findLocalMaxima",
function(object, halfWindowSize=20L)
standardGeneric(".findLocalMaxima"))
}
if (is.null(getGeneric(".findLocalMaximaLogical"))) {
setGeneric(".findLocalMaximaLogical",
function(object, halfWindowSize=20L)
standardGeneric(".findLocalMaximaLogical"))
}
if (is.null(getGeneric("isRegular"))) {
setGeneric("isRegular",
function(object, ...) standardGeneric("isRegular"))
}
if (is.null(getGeneric("removeBaseline"))) {
setGeneric("removeBaseline",
function(object, ...) standardGeneric("removeBaseline"))
}
if (is.null(getGeneric("smoothIntensity"))) {
setGeneric("smoothIntensity",
function(object, ...)
standardGeneric("smoothIntensity"))
}
if (is.null(getGeneric("totalIonCurrent"))) {
setGeneric("totalIonCurrent",
function(object) standardGeneric("totalIonCurrent"))
}
## end of MassSpectrumOnDisk


## MassPeaks
if (is.null(getGeneric("labelPeaks"))) {
setGeneric("labelPeaks", function(object, ...) standardGeneric("labelPeaks"))
Expand Down
15 changes: 14 additions & 1 deletion R/approxfun-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,20 @@ setMethod(f="approxfun",
if (isEmpty(x)) {
function(x)rep.int(NA, length(x))
} else {
approxfun(x=x@mass, y=x@intensity, method=method,
approxfun(x=mass(x), y=intensity(x), method=method,
yleft=yleft, yright=yright, rule=rule, f=f, ties=ties)
}
})

## MassSpectrumOnDisk
setMethod(f="approxfun",
signature=signature(x="MassSpectrumOnDisk"),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After changing x@mass and x@intensity to mass(x) and intensity(x) in approxfun,MassSpectrum above there should be no need for an approxfun,MassSpectrumOnDisk method.
There are two possible solutions:

  1. Introduce a new VIRTUAL class AbstractMassSpectrum that directly inherit from AbstractMassObject and is the common parent of MassSpectrum and MassSpectrumOnDisk.
  2. Make MassSpectrumOnDisk a child of MassSpectrum.

I tend to the first solution (then the signature has to be changed into AbstractMassSpectrumObject).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean that instead of defining a method for every MassSpectrumOnDisk, simply use option #1 and define all methods for AbstractMassSpectrumObject?

definition=function(x, y=NULL, method="linear", yleft, yright,
rule=1L, f=0L, ties=mean) {
if (isEmpty(x)) {
function(x)rep.int(NA, length(x))
} else {
approxfun(x=mass(x), y=intensity(x), method=method,
yleft=yleft, yright=yright, rule=rule, f=f, ties=ties)
}
})
6 changes: 3 additions & 3 deletions R/as-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
## a MassSpectrum object

setAs(from="MassPeaks", to="MassSpectrum",
function (from)createMassSpectrum(mass=from@mass,
intensity=from@intensity,
metaData=from@metaData))
function (from)createMassSpectrum(mass=mass(from),
intensity=intensity(from),
metaData=metaData(from)))
4 changes: 2 additions & 2 deletions R/as.matrix-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
.as.matrix.MassObjectList <- function(l) {
.stopIfNotIsMassObjectList(l)

mass <- .unlist(lapply(l, function(x)x@mass))
intensity <- .unlist(lapply(l, function(x)x@intensity))
mass <- .unlist(lapply(l, function(x)mass(x)))
intensity <- .unlist(lapply(l, function(x)intensity(x)))
uniqueMass <- sort.int(unique(mass))
n <- lengths(l)
r <- rep.int(seq_along(l), n)
Expand Down
2 changes: 1 addition & 1 deletion R/as.matrix-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ setMethod(f="as.matrix",
signature=signature(x="AbstractMassObject"),
definition=function(x, index) {

matrix(c(x@mass[index], x@intensity[index]), ncol=2L, byrow=FALSE,
matrix(c(mass(x)[index], intensity(x)[index]), ncol=2L, byrow=FALSE,
dimnames=list(NULL, c("mass", "intensity")))
})
4 changes: 2 additions & 2 deletions R/averageMassSpectra-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ averageMassSpectra <- function(l, labels, method=c("mean", "median", "sum"),

## merge metaData
if (mergeMetaData) {
metaData <- .mergeMetaData(lapply(l, function(x)x@metaData))
metaData <- .mergeMetaData(lapply(l, function(x)metaData(x)))
} else {
metaData <- list()
}

## use the first non empty spectrum as reference
i <- which(!vapply(l, isEmpty, logical(1L)))[1L]
if (!is.na(i)) {
mass <- l[[i]]@mass
mass <- mass(l[[i]])
} else {
mass <- NA_real_
}
Expand Down
4 changes: 2 additions & 2 deletions R/calibrateIntensity-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
totalIonCurrent(object)
},
"median" = {
median(object@intensity)
median(intensity(object))
}
)
}
Expand Down Expand Up @@ -85,7 +85,7 @@

lapply(l, function(x) {
## 3. quotient calculation
q <- approxfun(x)(reference@mass) / reference@intensity
q <- approxfun(x)(mass(reference)) / intensity(reference)
## 4. median
m <- median(q, na.rm=TRUE)
## 5. divide by median
Expand Down
24 changes: 24 additions & 0 deletions R/calibrateIntensity-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ setMethod(f="calibrateIntensity",
})
})

## MassSpectrumOnDisk
setMethod(f="calibrateIntensity",
signature=signature(object="MassSpectrumOnDisk"),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem as for approxfun.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted.

definition=function(object,
method=c("TIC", "PQN", "median"),
range, ...) {

method <- match.arg(method)

switch(method,
"TIC" = ,
"median" = {
.transformIntensity(object, fun=.calibrateIntensitySimple,
offset=0L,
scaling=.scalingFactor(object, method=method,
range=range))
},
"PQN" = {
stop(dQuote("PQN"),
" is not supported for a single MassSpectrum object.")
})
})


## list
setMethod(f="calibrateIntensity",
signature=signature(object="list"),
Expand Down
36 changes: 36 additions & 0 deletions R/constructor-functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,42 @@ createMassSpectrum <- function(mass, intensity, metaData=list()) {
.reorder(object)
}


## MassSpectrumOnDisk

## createMassSpectrumOnDisk
## default constructor: MassSpectrumOnDisk class
##
## params:
## mass: matter_vec, spectrum mass
## intensity: matter_vec, spectrum intensities
## metaData: list, metadata
##
## returns:
## a MassSpectrumOnDisk object
##
createMassSpectrumOnDisk <- function(mass, intensity, metaData=list()) {

onDiskMass <- matter::matter_vec(data=mass, datamode="double", filemode="rb+",
paths=tempfile("spectrum", fileext=".mass")
onDiskIntensity <- matter::matter_vec(data=intensity, datamode="double", filemode="rb+",
paths=tempfile("spectrum", fileext=".intensity")

object <- new(Class="MassSpectrumOnDisk",
mass=onDiskMass,
intensity=onDiskIntensity,
metaData=metaData,
path=setNames(
c(onDiskMass@paths, onDiskIntensity@paths),
c("mass", "intensity")
))


.reorder(object)

}


## end of MassSpectrum

## MassPeaks
Expand Down
48 changes: 43 additions & 5 deletions R/detectPeaks-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,51 @@ setMethod(f="detectPeaks",
metaData=object@metaData)
})

## list



## MassSpectrumOnDisk
setMethod(f="detectPeaks",
signature=signature(object="MassSpectrumOnDisk"),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem as for approxfun.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted

definition=function(object, halfWindowSize=20L,
method=c("MAD", "SuperSmoother"), SNR=2L, ...) {

tmpMass <- mass(object)
tmpIntensity <- intensity(object)

## empty spectrum?
if (.isEmptyWarning(object)) {
return(createMassPeaks(mass=tmpMass, intensity=tmpIntensity,
metaData=object@metaData))
}

## estimate noise
noise <- .estimateNoise(x=tmpMass, y=tmpIntensity, method=method, ...)

## find local maxima
isLocalMaxima <- .findLocalMaximaLogical(object,
halfWindowSize=halfWindowSize)

## include only local maxima which are above the noise
isAboveNoise <- tmpIntensity > (SNR * noise)

peakIdx <- which(isAboveNoise & isLocalMaxima)

createMassPeaks(mass=tmpMass[peakIdx],
intensity=tmpIntensity[peakIdx],
snr=tmpIntensity[peakIdx] / noise[peakIdx],
metaData=object@metaData)
})

## list
setMethod(f="detectPeaks",
signature=signature(object="list"),
definition=function(object, ...) {

## test arguments
.stopIfNotIsMassSpectrumList(object)

.mapply(detectPeaks, object, ...)
})

## test arguments
.stopIfNotIsMassSpectrumList(object)

.mapply(detectPeaks, object, ...)
})
15 changes: 15 additions & 0 deletions R/estimateBaseline-methods.R
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,18 @@ setMethod(f="estimateBaseline",
intensity=.estimateBaseline(x=object@mass, y=object@intensity,
method=method, ...))
})

## MassSpectrumOnDisk
setMethod(f="estimateBaseline",
signature=signature(object="MassSpectrumOnDisk"),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same problem as for approxfun.

definition=function(object, method=c("SNIP", "TopHat", "ConvexHull",
"median"),
...) {
if (.isEmptyWarning(object)) {
return(NA)
}

cbind(mass=mass(object),
intensity=.estimateBaseline(x=mass(object), y=intensity(object),
method=method, ...))
})
Loading