Peak & Peak Lists
Introduction
In the CCPN data model there are a few classes related to peaks found in spectra.
PeakList - A list of Peaks, all belonging to a specific spectrum (DataSource). Each spectrum can have more than one PeakList. So one PeakList could be for peaks found using one peak-picking algorithm, a second with peaks found using a second peak-picking algorithm, a third with peaks created synthetically, etc.
Peak - This represents a crosspeak at a specific position in a specific spectrum.
PeakDim - This contains the information for a given dimension of a peak, for example the position.
PeakContrib - These describe a Peak as a sum of different contributions (e.g. ovelapped peaks), whose relative values are determined by their relative weights. They also serve simply as a way of describing mutually exclusive sets of assignment possibilities, without any implication about the peak arising from multiple contributions. The two aspects are almost identical in practice.
PeakDimContrib - This is a PeakDim assignment contribution, one of the multiple assignments of a PeakDim. This is a connection record, connecting a PeakDim and a PeakContrib to a Resonance or a set of Resonances. Note that two different AbstractPeakDimContribs may have both the same PeakDim and the same Resonance.
Setup
The following examples assume that you have loaded the demonstration project, so that we can look at pre-existing peaks etc. If you have not already done so, make sure that you load such a CCPN project. This can be done as follows, remembering to change the location of the project directory to that it is appropriate to your system:
from memops.general.Io import loadProject
rootProject = loadProject('/home/user/myProjDirName')
Next we will find an HSQC experiment and an associated spectrum to work with:
# There may be a pause on issuing this command
# while the NMR data is loaded from disk
experiment = rootProject.currentNmrProject.findFirstExperiment(name='HSQC')
spectrum = experiment.findFirstDataSource()
PeakList
Given a spectrum, creating a PeakList is simple:
peakList = spectrum.newPeakList()
print peakList.peaks
This is of course an empty peak list because we have not made any Peak objects for it. And if you wanted to delete it you just do:
peakList.delete()
That deletes not only the peak list but all the peaks (and other contained objects like peakDims, peakDimContribs, etc.) contained inside the PeakList object.
Sometimes one wants to copy a PeakList from one spectrum to another or duplicate a PeakList inside one spectrum (so one can then start working on the PeakList while preserving the original version). This is not a totally trivial exercise (because there is a whole slew of objects that need copying over) but there is a utility function that makes the job easy:
print spectrum.peakLists
peakList = spectrum.findFirstPeakList()
from ccpnmr.analysis.core.PeakBasic import copyPeakListNew
copyPeakListNew(peakList, spectrum)
print spectrum.peakLists
which creates a copy of PeakList in the DataSource object (spectrum).
To loop through peaks in a peakList one does one of the following. Note that peakList.peaks is an unordered set and so will give you the peaks in random order. peakList.sortedPeaks gives a list of the peaks in order of the key (here the serial attribute)
for peak in peakList.peaks:
print peak.serial
for peak in peakList.sortedPeaks():
print peak.serial
Peak
To create a new peak in a peakList one does:
peak = peakList.newPeak()
This automatically creates the peak dimension objects (PeakDims) of the peak, however the new peak dimensions will carry no positional information:
print "number of peakDims = %d" % len(peak.peakDims)
for peakDim in peak.peakDims:
print peakDim.dim, peakDim.position
To delete a peak one does the following, which automatically deletes all the PeakDims and PeakContribs (etc.) associated with the peak:
peak.delete()
To create a peak at a specific location, you would have to set the position of the PeakDims and specify which of the DataDimRefs (the dimension referencing values from the spectrum) to work with; so that a point position can be converted into PPM. This is sligntly complicated, so in most instances you would use the utility function pickPeak() to place a peak:
from ccpnmr.analysis.core.PeakBasic import pickPeak
position = (7.321, 132.87)
# doFit must be False unless we are working within Analysis
peak = pickPeak(peakList, position, unit='ppm', doFit=False)
print [peakDim.value for peakDim in peak.peakDims]
The list of all peakDims for a given peak in dimension order is given by using the "sorted" API call. Note how the peak dimension objects contain a record of their position and assignment label:
peak = peakList.findFirstPeak()
peakDims = peak.sortedPeakDims()
for peakDim in peakDims:
print peakDim.annotation, peakDim.value
If we were to issue peak.peakDims, rather than the "sorted" function we would not necessarily get the in dimensions back in order.
A peak can have PeakIntensities; each of these is a height or a volume. They are a bit of a pain to construct because a PeakIntensity object must have a Method object which specifies what algorithm was used to determine the intensity value, and a Method belongs to a MethodStore. So one yucky example of manually constructing such a PeakIntensity is:
methodStore = rootProject.findFirstMethodStore()
if not methodStore:
methodStore = rootProject.newMethodStore(name="myMethodStore")
method = methodStore.findFirstMethod(task="Peak intensity", name="myPeakIntensityAlgorithm")
if not method:
method = methodStore.newMethod(task="Peak intensity", name="myPeakIntensityAlgorithm")
peakIntensity = peak.newPeakIntensity(intensityType="height", value=1234.56, method=method)
print peakIntensity.value
Thankfully, there are some utility functions in Analysis which allow the height and volume to be calculated using the standard Analysis routines. There is also an additional utility function which allows the intensity to be set as desired (so "manually", although it could come from some other algorithm).
from ccpnmr.analysis.core.PeakBasic import setupPeakHeight, setupPeakVolume, setManualPeakIntensity
# These calculate intensities from the real spectrum data
setupPeakHeight(peak)
setupPeakVolume(peak)
height = peak.findFirstPeakIntensity(intensityType='height')
print height.value
# This sets the intensity manually
setManualPeakIntensity(peak, value=1234.56, intensityType="height")
height = peak.findFirstPeakIntensity(intensityType='height')
print height.value
PeakDim
The position of a peak in given dimension is specified in the corresponding PeakDim. By default it is not set. The position can be given either in points or in the unit of the corresponding ExpDimRef object (so normally "ppm").
peakDim = peak.findFirstPeakDim(dim=1)
# Get position
print peakDim.position
print peakDim.value
# Set position
peakDim.position = 784.3 # points
peakDim.value = 8.72 # ppm
Now the peakDim position must be between 1.0 and 1.0 + peakDim.dataDim.numPoints. If the peak is aliased and its true position is not in the "fundamental" region then a numAliasing value can be specified.
peakDim.numAliasing = 1
In this case the actual position is at peakDim.position + peakDim.numAliasing*peakDim.dataDim.numPointsOrig
If you have the positions (possibly not all inside the fundamental region) for all peakDims of a peak specified in a list in dimension order then there is a utility function which lets you set the position all in one go (and so in particular will calculate the numAliasing value):
from ccpnmr.analysis.core.PeakBasic import movePeak
position = (701.17, 66.358)
movePeak(peak, position)
Each PeakDim has an optional annotation which is a string that is supposed to contain a description of the assignment status. There is a utility function which will set the annotation based on the existing assignment (if any), this annotation is stored as an attribute on the PeakDim object for display purposes. However, this text annotation should not be relied upon when working with assignmemnts; instead the assignment objects (PeakDimContrib below) should be queried.
from ccpnmr.analysis.core.AssignmentBasic import makePeakDimAnnotation
makePeakDimAnnotation(peakDim)
print peakDim.annotation
If we clear the assignment to the peak dimension (assuming it had one) then the annotation attribute will not change:
from ccpnmr.analysis.core.AssignmentBasic import clearPeakDim
clearPeakDim(peakDim)
print peakDim.annotation
The annotation must be refreshed, according to the new (blank) assignment.
makePeakDimAnnotation(peakDim)
print peakDim.annotation
Special note: If you are writing scripts that are executed within the Analysis program, then the peakDim.annotation will be automatically updated if you change a peak assignment. This is because Analysis uses a system of notification calls to detect when something has changed and automatically attempts to keep the annotations consistent.
Assignment Contributions
Peaks are linked to Resonance objects in order to assign them. These resonances in turn may be linked to atoms or other intermediate assignment information. A PeakDimContrib is a contribution to the assignment found on a given peak dimension (PeakDim); and there may be more than one contribution to indicate an ambiguous assignment. As far as the data model objects are concerned, the Resonance assignment contributions (PeakDimContribs) and the groupings of these (PeakContribs) are not difficult to create with the Python API. For example PeakContribs objects are made via a Peak and have a weight (default value 0.0), which determines their relative contribution:
peakContrib = peak.newPeakContrib(weight=1.0)
However, there are many checks and further API calls that usually need to be made when assigning Resonances to peaks. These include:
Whether a Resonance's isotope type matches the isotopes of the spectrum dimension.
Whether a known chemical shift for the Resonance is to far away to be assigned to the peak.
Whether the assignment would indicate that the peak was to be unaliased.
Updating the chemical shift values for the Resonance to take account of the peak's location.
Updating the textual annotation of the peak given a new assignment.
For these reasons and more it is usually best to leave setting of peak assignments to the high-level Analysis functions. This is covered more fully in the section on assignment.