Home  · Classes  · Annotated Classes  · Modules  · Members  · Namespaces  · Related Pages

SpectraMerger.h

Go to the documentation of this file.
00001 // --------------------------------------------------------------------------
00002 //                   OpenMS -- Open-Source Mass Spectrometry
00003 // --------------------------------------------------------------------------
00004 // Copyright The OpenMS Team -- Eberhard Karls University Tuebingen,
00005 // ETH Zurich, and Freie Universitaet Berlin 2002-2012.
00006 //
00007 // This software is released under a three-clause BSD license:
00008 //  * Redistributions of source code must retain the above copyright
00009 //    notice, this list of conditions and the following disclaimer.
00010 //  * Redistributions in binary form must reproduce the above copyright
00011 //    notice, this list of conditions and the following disclaimer in the
00012 //    documentation and/or other materials provided with the distribution.
00013 //  * Neither the name of any author or any participating institution
00014 //    may be used to endorse or promote products derived from this software
00015 //    without specific prior written permission.
00016 // For a full list of authors, refer to the file AUTHORS.
00017 // --------------------------------------------------------------------------
00018 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00019 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021 // ARE DISCLAIMED. IN NO EVENT SHALL ANY OF THE AUTHORS OR THE CONTRIBUTING
00022 // INSTITUTIONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00023 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00024 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
00025 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00026 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00027 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
00028 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 //
00030 // --------------------------------------------------------------------------
00031 // $Maintainer: Chris Bielow $
00032 // $Authors: Chris Bielow, Andreas Bertsch $
00033 // --------------------------------------------------------------------------
00034 //
00035 #ifndef OPENMS_FILTERING_TRANSFORMERS_SPECTRAMERGER_H
00036 #define OPENMS_FILTERING_TRANSFORMERS_SPECTRAMERGER_H
00037 
00038 #include <OpenMS/DATASTRUCTURES/DefaultParamHandler.h>
00039 #include <OpenMS/COMPARISON/CLUSTERING/CompleteLinkage.h>
00040 #include <OpenMS/COMPARISON/CLUSTERING/SingleLinkage.h>
00041 #include <OpenMS/COMPARISON/CLUSTERING/ClusterAnalyzer.h>
00042 #include <OpenMS/COMPARISON/CLUSTERING/ClusterHierarchical.h>
00043 #include <OpenMS/COMPARISON/SPECTRA/SpectrumAlignment.h>
00044 #include <OpenMS/KERNEL/StandardTypes.h>
00045 #include <OpenMS/KERNEL/RangeUtils.h>
00046 #include <OpenMS/KERNEL/BaseFeature.h>
00047 #include <OpenMS/CONCEPT/LogStream.h>
00048 #include <vector>
00049 
00050 namespace OpenMS
00051 {
00052 
00061   class OPENMS_DLLAPI SpectraMerger :
00062     public DefaultParamHandler
00063   {
00064 
00065 protected:
00066 
00067     /* Determine distance between two spectra
00068 
00069       Distance is determined as
00070 
00071         (d_rt/rt_max_ + d_mz/mz_max_) / 2
00072 
00073     */
00074     class SpectraDistance_ :
00075       public DefaultParamHandler
00076     {
00077 public:
00078       SpectraDistance_() :
00079         DefaultParamHandler("SpectraDistance")
00080       {
00081         defaults_.setValue("rt_tolerance", 10.0, "Maximal RT distance (in [s]) for two spectra's precursors.");
00082         defaults_.setValue("mz_tolerance", 1.0, "Maximal m/z distance (in Da) for two spectra's precursors.");
00083         defaultsToParam_();
00084       }
00085 
00086       void updateMembers_()
00087       {
00088         rt_max_ = (DoubleReal) param_.getValue("rt_tolerance");
00089         mz_max_ = (DoubleReal) param_.getValue("mz_tolerance");
00090 
00091         return;
00092       }
00093 
00094       double getSimilarity(const DoubleReal d_rt, const DoubleReal d_mz) const
00095       {
00096         //     1 - distance
00097         return 1 - ((d_rt / rt_max_ + d_mz / mz_max_) / 2);
00098       }
00099 
00100       // measure of SIMILARITY (not distance, i.e. 1-distance)!!
00101       double operator()(const BaseFeature & first, const BaseFeature & second) const
00102       {
00103         // get RT distance:
00104         DoubleReal d_rt = fabs(first.getRT() - second.getRT());
00105         DoubleReal d_mz = fabs(first.getMZ() - second.getMZ());
00106 
00107         if (d_rt > rt_max_ || d_mz > mz_max_) {return 0; }
00108 
00109         // calculate similarity (0-1):
00110         DoubleReal sim = getSimilarity(d_rt, d_mz);
00111 
00112         return sim;
00113       }
00114 
00115 protected:
00116       DoubleReal rt_max_;
00117       DoubleReal mz_max_;
00118 
00119     }; // end of SpectraDistance
00120 
00121 public:
00122 
00124     typedef Map<Size, std::vector<Size> > MergeBlocks;
00125 
00126     // @name Constructors and Destructors
00127     // @{
00129     SpectraMerger();
00130 
00132     SpectraMerger(const SpectraMerger & source);
00133 
00135     virtual ~SpectraMerger();
00136     // @}
00137 
00138     // @name Operators
00139     // @{
00141     SpectraMerger & operator=(const SpectraMerger & source);
00142     // @}
00143 
00144     // @name Merging functions
00145     // @{
00147     template <typename MapType>
00148     void mergeSpectraBlockWise(MapType & exp)
00149     {
00150       IntList ms_levels = (IntList) (param_.getValue("block_method:ms_levels"));
00151       Int rt_block_size(param_.getValue("block_method:rt_block_size"));
00152       DoubleReal rt_max_length = (param_.getValue("block_method:rt_max_length"));
00153 
00154       if (rt_max_length == 0)  // no rt restriction set?
00155       {
00156         rt_max_length = (std::numeric_limits<DoubleReal>::max)(); // set max rt span to very large value
00157       }
00158 
00159       for (IntList::iterator it_mslevel = ms_levels.begin(); it_mslevel < ms_levels.end(); ++it_mslevel)
00160       {
00161         MergeBlocks spectra_to_merge;
00162         Size idx_block(0);
00163         SignedSize block_size_count(rt_block_size + 1);
00164         Size idx_spectrum(0);
00165         for (typename MapType::const_iterator it1 = exp.begin(); it1 != exp.end(); ++it1)
00166         {
00167           if (Int(it1->getMSLevel()) == *it_mslevel)
00168           {
00169             // block full if it contains a maximum number of scans or if maximum rt length spanned
00170             if (++block_size_count >= rt_block_size ||
00171                 exp[idx_spectrum].getRT() - exp[idx_block].getRT() > rt_max_length)
00172             {
00173               block_size_count = 0;
00174               idx_block = idx_spectrum;
00175             }
00176             else
00177             {
00178               spectra_to_merge[idx_block].push_back(idx_spectrum);
00179             }
00180           }
00181 
00182           ++idx_spectrum;
00183         }
00184         // check if last block had sacrifice spectra
00185         if (block_size_count == 0) //block just got initialized
00186         {
00187           spectra_to_merge[idx_block] = std::vector<Size>();
00188         }
00189 
00190         // merge spectra, remove all old MS spectra and add new consensus spectra
00191         mergeSpectra_(exp, spectra_to_merge, *it_mslevel);
00192       }
00193 
00194       exp.sortSpectra();
00195 
00196       return;
00197     }
00198 
00200     template <typename MapType>
00201     void mergeSpectraPrecursors(MapType & exp)
00202     {
00203 
00204       // convert spectra's precursors to clusterizable data
00205       Size data_size;
00206       std::vector<BinaryTreeNode> tree;
00207       Map<Size, Size> index_mapping;
00208       // local scope to save memory - we do not need the clustering stuff later
00209       {
00210         std::vector<BaseFeature> data;
00211 
00212         for (Size i = 0; i < exp.size(); ++i)
00213         {
00214           if (exp[i].getMSLevel() != 2) continue;
00215 
00216           // remember which index in distance data ==> experiment index
00217           index_mapping[data.size()] = i;
00218 
00219           // make cluster element
00220           BaseFeature bf;
00221           bf.setRT(exp[i].getRT());
00222           std::vector<Precursor> pcs = exp[i].getPrecursors();
00223           if (pcs.empty()) throw Exception::MissingInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__, String("Scan #") + String(i) + " does not contain any precursor information! Unable to cluster!");
00224           if (pcs.size() > 1) LOG_WARN << "More than one precursor found. Using first one!" << std::endl;
00225           bf.setMZ(pcs[0].getMZ());
00226           data.push_back(bf);
00227         }
00228         data_size = data.size();
00229 
00230         SpectraDistance_ llc;
00231         llc.setParameters(param_.copy("precursor_method:", true));
00232         SingleLinkage sl;
00233         DistanceMatrix<Real> dist;         // will be filled
00234         ClusterHierarchical ch;
00235 
00236         //ch.setThreshold(0.99);
00237         // clustering ; threshold is implicitly at 1.0, i.e. distances of 1.0 (== similiarity 0) will not be clustered
00238         ch.cluster<BaseFeature, SpectraDistance_>(data, llc, sl, tree, dist);
00239       }
00240 
00241       // extract the clusters
00242       ClusterAnalyzer ca;
00243       std::vector<std::vector<Size> > clusters;
00244       // count number of real tree nodes (not the -1 ones):
00245       Size node_count = 0;
00246       for (Size ii = 0; ii < tree.size(); ++ii)
00247       {
00248         if (tree[ii].distance >= 1) tree[ii].distance = -1;       // manually set to disconnect, as SingleLinkage does not support it
00249         if (tree[ii].distance != -1) ++node_count;
00250       }
00251       ca.cut(data_size - node_count, tree, clusters);
00252 
00253       //std::cerr << "Treesize: " << (tree.size()+1) << "   #clusters: " << clusters.size() << std::endl;
00254       //std::cerr << "tree:\n" << ca.newickTree(tree, true) << "\n";
00255 
00256       // convert to blocks
00257       MergeBlocks spectra_to_merge;
00258 
00259       for (Size i_outer = 0; i_outer < clusters.size(); ++i_outer)
00260       {
00261         if (clusters[i_outer].size() <= 1) continue;
00262         // init block with first cluster element
00263         Size cl_index0 = clusters[i_outer][0];
00264         spectra_to_merge[index_mapping[cl_index0]] = std::vector<Size>();
00265         // add all other elements
00266         for (Size i_inner = 1; i_inner < clusters[i_outer].size(); ++i_inner)
00267         {
00268           Size cl_index = clusters[i_outer][i_inner];
00269           spectra_to_merge[index_mapping[cl_index0]].push_back(index_mapping[cl_index]);
00270         }
00271       }
00272 
00273       // do it
00274       mergeSpectra_(exp, spectra_to_merge, 2);
00275 
00276       exp.sortSpectra();
00277 
00278       return;
00279     }
00280 
00281     // @}
00282 
00283 protected:
00284 
00295     template <typename MapType>
00296     void mergeSpectra_(MapType & exp, const MergeBlocks & spectra_to_merge, const UInt ms_level)
00297     {
00298       DoubleReal mz_binning_width(param_.getValue("mz_binning_width"));
00299       String mz_binning_unit(param_.getValue("mz_binning_width_unit"));
00300 
00301       // merge spectra
00302       MapType merged_spectra;
00303 
00304       Map<Size, Size> cluster_sizes;
00305       std::set<Size> merged_indices;
00306 
00307       // set up alignment
00308       SpectrumAlignment sas;
00309       Param p;
00310       p.setValue("tolerance", mz_binning_width);
00311       if (!(mz_binning_unit == "Da" || mz_binning_unit == "ppm")) throw Exception::IllegalSelfOperation(__FILE__, __LINE__, __PRETTY_FUNCTION__);  // sanity check
00312       p.setValue("is_relative_tolerance", mz_binning_unit == "Da" ? "false" : "true");
00313       sas.setParameters(p);
00314       std::vector<std::pair<Size, Size> > alignment;
00315 
00316       Size count_peaks_aligned(0);
00317       Size count_peaks_overall(0);
00318 
00319       // each BLOCK
00320       for (Map<Size, std::vector<Size> >::ConstIterator it = spectra_to_merge.begin(); it != spectra_to_merge.end(); ++it)
00321       {
00322 
00323         ++cluster_sizes[it->second.size() + 1];  // for stats
00324 
00325         typename MapType::SpectrumType consensus_spec = exp[it->first];
00326         consensus_spec.setMSLevel(ms_level);
00327 
00328         //consensus_spec.unify(exp[it->first]); // append meta info
00329         merged_indices.insert(it->first);
00330 
00331         //typename MapType::SpectrumType all_peaks = exp[it->first];
00332         DoubleReal rt_average = consensus_spec.getRT();
00333         DoubleReal precursor_mz_average = 0.0;
00334         Size precursor_count(0);
00335         if (!consensus_spec.getPrecursors().empty())
00336         {
00337           precursor_mz_average = consensus_spec.getPrecursors()[0].getMZ();
00338           ++precursor_count;
00339         }
00340 
00341         count_peaks_overall += consensus_spec.size();
00342 
00343         // block elements
00344         for (std::vector<Size>::const_iterator sit = it->second.begin(); sit != it->second.end(); ++sit)
00345         {
00346           consensus_spec.unify(exp[*sit]); // append meta info
00347           merged_indices.insert(*sit);
00348 
00349           rt_average += exp[*sit].getRT();
00350           if (ms_level >= 2 && exp[*sit].getPrecursors().size() > 0)
00351           {
00352             precursor_mz_average += exp[*sit].getPrecursors()[0].getMZ();
00353             ++precursor_count;
00354           }
00355 
00356           // merge data points
00357           sas.getSpectrumAlignment(alignment, consensus_spec, exp[*sit]);
00358           //std::cerr << "alignment of " << it->first << " with " << *sit << " yielded " << alignment.size() << " common peaks!\n";
00359           count_peaks_aligned += alignment.size();
00360           count_peaks_overall += exp[*sit].size();
00361 
00362           Size align_index(0);
00363           Size spec_b_index(0);
00364 
00365           // sanity check for number of peaks
00366           Size spec_a = consensus_spec.size(), spec_b = exp[*sit].size(), align_size = alignment.size();
00367           for (typename MapType::SpectrumType::ConstIterator pit = exp[*sit].begin(); pit != exp[*sit].end(); ++pit)
00368           {
00369             // either add aligned peak height to existing peak
00370             if (alignment.size() > 0 && alignment[align_index].second == spec_b_index)
00371             {
00372               consensus_spec[alignment[align_index].first].setIntensity(consensus_spec[alignment[align_index].first].getIntensity() +
00373                                                                         pit->getIntensity());
00374               ++align_index; // this aligned peak was explained, wait for next aligned peak ...
00375               if (align_index == alignment.size()) alignment.clear();  // end reached -> avoid going into this block again
00376             }
00377             else // ... or add unaligned peak
00378             {
00379               consensus_spec.push_back(*pit);
00380             }
00381             ++spec_b_index;
00382           }
00383           consensus_spec.sortByPosition(); // sort, otherwise next alignment will fail
00384           if (spec_a + spec_b - align_size != consensus_spec.size()) std::cerr << "\n\n ERRROR \n\n";
00385         }
00386         rt_average /= it->second.size() + 1;
00387         consensus_spec.setRT(rt_average);
00388 
00389         if (ms_level >= 2)
00390         {
00391           if (precursor_count) precursor_mz_average /= precursor_count;
00392           std::vector<Precursor> pcs = consensus_spec.getPrecursors();
00393           //if (pcs.size()>1) LOG_WARN << "Removing excessive precursors - leaving only one per MS2 spectrum.\n";
00394           pcs.resize(1);
00395           pcs[0].setMZ(precursor_mz_average);
00396           consensus_spec.setPrecursors(pcs);
00397         }
00398 
00399         if (consensus_spec.empty()) continue;
00400         else merged_spectra.push_back(consensus_spec);
00401       }
00402 
00403       LOG_INFO << "Cluster sizes:\n";
00404       for (Map<Size, Size>::const_iterator it = cluster_sizes.begin(); it != cluster_sizes.end(); ++it)
00405       {
00406         LOG_INFO << "  size " << it->first << ": " << it->second << "x\n";
00407       }
00408 
00409       char buffer[200];
00410       sprintf(buffer, "%d/%d (%.2f %%) of blocked spectra", (int)count_peaks_aligned,
00411               (int)count_peaks_overall, float(count_peaks_aligned) / float(count_peaks_overall) * 100.);
00412       LOG_INFO << "Number of merged peaks: " << String(buffer) << "\n";
00413 
00414       // remove all spectra that were within a cluster
00415       typename MapType::SpectrumType empty_spec;
00416       MapType exp_tmp;
00417       for (Size i = 0; i < exp.size(); ++i)
00418       {
00419         if (merged_indices.count(i) == 0) // save unclustered ones
00420         {
00421           exp_tmp.push_back(exp[i]);
00422           exp[i] = empty_spec;
00423         }
00424       }
00425       typedef std::vector<typename MapType::SpectrumType> Base;
00426       exp.Base::operator=(exp_tmp);
00427 
00428       // exp.erase(remove_if(exp.begin(), exp.end(), InMSLevelRange<typename MapType::SpectrumType>(IntList::create(String(ms_level)), false)), exp.end());
00429 
00430       // ... and add consensus spectra
00431       exp.insert(exp.end(), merged_spectra.begin(), merged_spectra.end());
00432 
00433     }
00434 
00435   };
00436 
00437 }
00438 #endif //OPENMS_FILTERING_TRANSFORMERS_SPECTRAMERGER_H

OpenMS / TOPP release 1.10.0 Documentation generated on Thu Mar 7 2013 09:42:44 using doxygen 1.7.1