void FeatureGroupingAlgorithm::transferSubelements(const vector<ConsensusMap>& maps, ConsensusMap& out) const { // accumulate file descriptions from the input maps: // cout << "Updating file descriptions..." << endl; out.getFileDescriptions().clear(); // mapping: (map index, original id) -> new id map<pair<Size, UInt64>, Size> mapid_table; for (Size i = 0; i < maps.size(); ++i) { const ConsensusMap& consensus = maps[i]; for (ConsensusMap::FileDescriptions::const_iterator desc_it = consensus.getFileDescriptions().begin(); desc_it != consensus.getFileDescriptions().end(); ++desc_it) { Size counter = mapid_table.size(); mapid_table[make_pair(i, desc_it->first)] = counter; out.getFileDescriptions()[counter] = desc_it->second; } } // look-up table: input map -> unique ID -> consensus feature // cout << "Creating look-up table..." << endl; vector<map<UInt64, ConsensusMap::ConstIterator> > feat_lookup(maps.size()); for (Size i = 0; i < maps.size(); ++i) { const ConsensusMap& consensus = maps[i]; for (ConsensusMap::ConstIterator feat_it = consensus.begin(); feat_it != consensus.end(); ++feat_it) { // do NOT use "id_lookup[i][feat_it->getUniqueId()] = feat_it;" here as // you will get "attempt to copy-construct an iterator from a singular // iterator" in STL debug mode: feat_lookup[i].insert(make_pair(feat_it->getUniqueId(), feat_it)); } } // adjust the consensus features: // cout << "Adjusting consensus features..." << endl; for (ConsensusMap::iterator cons_it = out.begin(); cons_it != out.end(); ++cons_it) { ConsensusFeature adjusted = ConsensusFeature( static_cast<BaseFeature>(*cons_it)); // remove sub-features for (ConsensusFeature::HandleSetType::const_iterator sub_it = cons_it->getFeatures().begin(); sub_it != cons_it->getFeatures().end(); ++sub_it) { UInt64 id = sub_it->getUniqueId(); Size map_index = sub_it->getMapIndex(); ConsensusMap::ConstIterator origin = feat_lookup[map_index][id]; for (ConsensusFeature::HandleSetType::const_iterator handle_it = origin->getFeatures().begin(); handle_it != origin->getFeatures().end(); ++handle_it) { FeatureHandle handle = *handle_it; Size new_id = mapid_table[make_pair(map_index, handle.getMapIndex())]; handle.setMapIndex(new_id); adjusted.insert(handle); } } *cons_it = adjusted; } }
void StablePairFinder::run(const std::vector<ConsensusMap>& input_maps, ConsensusMap& result_map) { // empty output destination: result_map.clear(false); // sanity checks: if (input_maps.size() != 2) { throw Exception::IllegalArgument(__FILE__, __LINE__, __PRETTY_FUNCTION__, "exactly two input maps required"); } checkIds_(input_maps); // set up the distance functor: double max_intensity = max(input_maps[0].getMaxInt(), input_maps[1].getMaxInt()); Param distance_params = param_.copy(""); distance_params.remove("use_identifications"); distance_params.remove("second_nearest_gap"); FeatureDistance feature_distance(max_intensity, false); feature_distance.setParameters(distance_params); // keep track of pairing: std::vector<bool> is_singleton[2]; is_singleton[0].resize(input_maps[0].size(), true); is_singleton[1].resize(input_maps[1].size(), true); typedef pair<double, double> DoublePair; DoublePair init = make_pair(FeatureDistance::infinity, FeatureDistance::infinity); // for every element in map 0: // - index of nearest neighbor in map 1: vector<UInt> nn_index_0(input_maps[0].size(), UInt(-1)); // - distances to nearest and second-nearest neighbors in map 1: vector<DoublePair> nn_distance_0(input_maps[0].size(), init); // for every element in map 1: // - index of nearest neighbor in map 0: vector<UInt> nn_index_1(input_maps[1].size(), UInt(-1)); // - distances to nearest and second-nearest neighbors in map 0: vector<DoublePair> nn_distance_1(input_maps[1].size(), init); // iterate over all feature pairs, find nearest neighbors: // TODO: iterate over SENSIBLE RT (and m/z) window -- sort the maps beforehand // to save a lot of processing time... // Once done, remove the warning in the description of the 'use_identifications' parameter for (UInt fi0 = 0; fi0 < input_maps[0].size(); ++fi0) { const ConsensusFeature& feat0 = input_maps[0][fi0]; for (UInt fi1 = 0; fi1 < input_maps[1].size(); ++fi1) { const ConsensusFeature& feat1 = input_maps[1][fi1]; if (use_IDs_ && !compatibleIDs_(feat0, feat1)) // check peptide IDs { continue; // mismatch } pair<bool, double> result = feature_distance(feat0, feat1); double distance = result.second; // we only care if distance constraints are satisfied for "best // matches", not for second-best; this means that second-best distances // can become smaller than best distances // (e.g. the RT is larger than allowed (->invalid pair), but m/z is perfect and has the most weight --> better score!) bool valid = result.first; // update entries for map 0: if (distance < nn_distance_0[fi0].second) { if (valid && (distance < nn_distance_0[fi0].first)) { nn_distance_0[fi0].second = nn_distance_0[fi0].first; nn_distance_0[fi0].first = distance; nn_index_0[fi0] = fi1; } else { nn_distance_0[fi0].second = distance; } } // update entries for map 1: if (distance < nn_distance_1[fi1].second) { if (valid && (distance < nn_distance_1[fi1].first)) { nn_distance_1[fi1].second = nn_distance_1[fi1].first; nn_distance_1[fi1].first = distance; nn_index_1[fi1] = fi0; } else { nn_distance_1[fi1].second = distance; } } } } // if features from the two maps are nearest neighbors of each other, they // can become a pair: for (UInt fi0 = 0; fi0 < input_maps[0].size(); ++fi0) { UInt fi1 = nn_index_0[fi0]; // nearest neighbor of "fi0" in map 1 // cout << "index: " << fi0 << ", RT: " << input_maps[0][fi0].getRT() // << ", MZ: " << input_maps[0][fi0].getMZ() << endl // << "neighbor: " << fi1 << ", RT: " << input_maps[1][fi1].getRT() // << ", MZ: " << input_maps[1][fi1].getMZ() << endl // << "d(i,j): " << nn_distance_0[fi0].first << endl // << "d2(i): " << nn_distance_0[fi0].second << endl // << "d2(j): " << nn_distance_1[fi1].second << endl; // criteria set by the parameters must be fulfilled: if ((nn_distance_0[fi0].first < FeatureDistance::infinity) && (nn_distance_0[fi0].first * second_nearest_gap_ <= nn_distance_0[fi0].second)) { // "fi0" satisfies constraints... if ((nn_index_1[fi1] == fi0) && (nn_distance_1[fi1].first * second_nearest_gap_ <= nn_distance_1[fi1].second)) { // ...nearest neighbor of "fi0" also satisfies constraints (yay!) // cout << "match!" << endl; result_map.push_back(ConsensusFeature()); ConsensusFeature& f = result_map.back(); f.insert(input_maps[0][fi0]); f.getPeptideIdentifications().insert(f.getPeptideIdentifications().end(), input_maps[0][fi0].getPeptideIdentifications().begin(), input_maps[0][fi0].getPeptideIdentifications().end()); f.insert(input_maps[1][fi1]); f.getPeptideIdentifications().insert(f.getPeptideIdentifications().end(), input_maps[1][fi1].getPeptideIdentifications().begin(), input_maps[1][fi1].getPeptideIdentifications().end()); f.computeConsensus(); double quality = 1.0 - nn_distance_0[fi0].first; double quality0 = 1.0 - nn_distance_0[fi0].first * second_nearest_gap_ / nn_distance_0[fi0].second; double quality1 = 1.0 - nn_distance_1[fi1].first * second_nearest_gap_ / nn_distance_1[fi1].second; quality = quality * quality0 * quality1; // TODO other formula? // incorporate existing quality values: Size size0 = max(input_maps[0][fi0].size(), size_t(1)); Size size1 = max(input_maps[1][fi1].size(), size_t(1)); // quality contribution from first map: quality0 = input_maps[0][fi0].getQuality() * (size0 - 1); // quality contribution from second map: quality1 = input_maps[1][fi1].getQuality() * (size1 - 1); f.setQuality((quality + quality0 + quality1) / (size0 + size1 - 1)); is_singleton[0][fi0] = false; is_singleton[1][fi1] = false; } } } // write out unmatched consensus features for (UInt input = 0; input <= 1; ++input) { for (UInt index = 0; index < input_maps[input].size(); ++index) { if (is_singleton[input][index]) { result_map.push_back(input_maps[input][index]); if (result_map.back().size() < 2) // singleton consensus feature { result_map.back().setQuality(0.0); } } } } // canonical ordering for checking the results, and the ids have no real meaning anyway result_map.sortByMZ(); // protein IDs and unassigned peptide IDs are added to the result by the // FeatureGroupingAlgorithm! }