io.github.msdk.features.joinaligner.JoinAlignerMethod.java Source code

Java tutorial

Introduction

Here is the source code for io.github.msdk.features.joinaligner.JoinAlignerMethod.java

Source

/* 
 * (C) Copyright 2015-2016 by MSDK Development Team
 *
 * This software is dual-licensed under either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation.
 */

package io.github.msdk.features.joinaligner;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.google.common.collect.Range;

import io.github.msdk.MSDKException;
import io.github.msdk.MSDKMethod;
import io.github.msdk.datamodel.datastore.DataPointStore;
import io.github.msdk.datamodel.featuretables.ColumnName;
import io.github.msdk.datamodel.featuretables.FeatureTable;
import io.github.msdk.datamodel.featuretables.FeatureTableColumn;
import io.github.msdk.datamodel.featuretables.FeatureTableRow;
import io.github.msdk.datamodel.featuretables.Sample;
import io.github.msdk.datamodel.impl.MSDKObjectBuilder;
import io.github.msdk.datamodel.ionannotations.IonAnnotation;
import io.github.msdk.datamodel.rawdata.ChromatographyInfo;
import io.github.msdk.util.FeatureTableUtil;
import io.github.msdk.util.MZTolerance;
import io.github.msdk.util.RTTolerance;

/**
 * This class aligns feature tables based on a match score. The score is
 * calculated based on the mass and retention time of each peak using a set of
 * tolerances.
 */
public class JoinAlignerMethod implements MSDKMethod<FeatureTable> {

    // Variables
    private final @Nonnull MZTolerance mzTolerance;
    private final @Nonnull RTTolerance rtTolerance;
    private final int mzWeight;
    private final int rtWeight;
    private final boolean requireSameCharge;
    private final boolean requireSameAnnotation;
    private final @Nonnull String featureTableName;
    private final @Nonnull DataPointStore dataStore;
    private final @Nonnull List<FeatureTable> featureTables;
    private final @Nonnull FeatureTable result;
    private boolean canceled = false;
    private int processedFeatures = 0, totalFeatures = 0;

    // ID counter for the new feature table
    private int newRowID = 1;

    /**
     * <p>
     * Constructor for MatchAlignerMethod.
     * </p>
     *
     * @param featureTables
     *            a {@link java.util.List} object.
     * @param dataStore
     *            a {@link io.github.msdk.datamodel.datastore.DataPointStore}
     *            object.
     * @param mzTolerance
     *            a {@link io.github.msdk.util.MZTolerance} object.
     * @param mzWeight
     *            a {@link java.lang.Integer} object.
     * @param rtWeight
     *            a {@link java.lang.Integer} object.
     * @param requireSameCharge
     *            a {@link java.lang.Boolean} object.
     * @param requireSameAnnotation
     *            a {@link java.lang.Boolean} object.
     * @param featureTableName
     *            a {@link java.lang.String} object.
     * @param rtTolerance
     *            a {@link io.github.msdk.util.RTTolerance} object.
     */
    public JoinAlignerMethod(@Nonnull List<FeatureTable> featureTables, @Nonnull DataPointStore dataStore,
            @Nonnull MZTolerance mzTolerance, @Nonnull RTTolerance rtTolerance, int mzWeight, int rtWeight,
            boolean requireSameCharge, boolean requireSameAnnotation, @Nonnull String featureTableName) {
        this.featureTables = featureTables;
        this.dataStore = dataStore;
        this.mzTolerance = mzTolerance;
        this.rtTolerance = rtTolerance;
        this.mzWeight = mzWeight;
        this.rtWeight = rtWeight;
        this.requireSameCharge = requireSameCharge;
        this.requireSameAnnotation = requireSameAnnotation;
        this.featureTableName = featureTableName;

        // Make a new feature table
        result = MSDKObjectBuilder.getFeatureTable(featureTableName, dataStore);
    }

    /** {@inheritDoc} */
    @Override
    public FeatureTable execute() throws MSDKException {

        // Calculate number of feature to process. Each feature will be
        // processed twice: first for score calculation and then for actual
        // alignment.
        for (FeatureTable featureTable : featureTables) {
            totalFeatures += featureTable.getRows().size() * 2;
        }

        // Iterate through all feature tables
        Boolean firstFeatureTable = true;
        for (FeatureTable featureTable : featureTables) {

            // Add columns from the original feature table to the result table
            for (FeatureTableColumn<?> column : featureTable.getColumns()) {
                if (firstFeatureTable)
                    result.addColumn(column);
                else if (column.getSample() != null)
                    result.addColumn(column);
            }
            firstFeatureTable = false;

            // Create a sorted array of matching scores between two rows
            List<RowVsRowScore> scoreSet = new ArrayList<RowVsRowScore>();

            // Calculate scores for all possible alignments of this row
            for (FeatureTableRow row : featureTable.getRows()) {

                final Double mz = row.getMz();
                if (mz == null)
                    continue;

                // Calculate the m/z range limit for the current row
                Range<Double> mzRange = mzTolerance.getToleranceRange(mz);

                // Continue if no chromatography info is available
                ChromatographyInfo chromatographyInfo = row.getChromatographyInfo();
                if (chromatographyInfo == null)
                    continue;

                // Calculate the RT range limit for the current row
                Range<Double> rtRange = rtTolerance.getToleranceRange(chromatographyInfo.getRetentionTime());

                // Get all rows of the aligned feature table within the m/z and
                // RT limits
                List<FeatureTableRow> candidateRows = result.getRowsInsideRange(rtRange, mzRange);

                // Calculate scores and store them
                for (FeatureTableRow candidateRow : candidateRows) {

                    // Check charge
                    if (requireSameCharge) {
                        FeatureTableColumn<Integer> chargeColumn1 = featureTable.getColumn(ColumnName.CHARGE, null);
                        FeatureTableColumn<Integer> chargeColumn2 = result.getColumn(ColumnName.CHARGE, null);
                        Integer charge1 = row.getData(chargeColumn1);
                        Integer charge2 = candidateRow.getData(chargeColumn2);
                        if (!charge1.equals(charge2))
                            continue;
                    }

                    // Check ion annotation
                    if (requireSameAnnotation) {
                        FeatureTableColumn<List<IonAnnotation>> ionAnnotationColumn1 = featureTable
                                .getColumn(ColumnName.IONANNOTATION, null);
                        FeatureTableColumn<List<IonAnnotation>> ionAnnotationColumn2 = result
                                .getColumn(ColumnName.IONANNOTATION, null);
                        List<IonAnnotation> ionAnnotations1 = row.getData(ionAnnotationColumn1);
                        List<IonAnnotation> ionAnnotations2 = candidateRow.getData(ionAnnotationColumn2);

                        // Check that all ion annotations in first row are in
                        // the candidate row
                        boolean equalIons = false;
                        if (ionAnnotations1 != null && ionAnnotations2 != null) {
                            for (IonAnnotation ionAnnotation : ionAnnotations1) {
                                for (IonAnnotation targetIonAnnotation : ionAnnotations2) {
                                    if (targetIonAnnotation.compareTo(ionAnnotation) == 0)
                                        equalIons = true;
                                }
                            }
                        }
                        if (!equalIons)
                            continue;

                    }

                    // Calculate score
                    double mzLength = mzRange.upperEndpoint() - mzRange.lowerEndpoint();
                    double rtLength = rtRange.upperEndpoint() - rtRange.lowerEndpoint();
                    RowVsRowScore score = new RowVsRowScore(row, candidateRow, mzLength / 2.0, mzWeight,
                            rtLength / 2.0, rtWeight);

                    // Add the score to the array
                    scoreSet.add(score);

                }

                processedFeatures++;

                if (canceled)
                    return null;
            }

            // Create a table of mappings for best scores
            Hashtable<FeatureTableRow, FeatureTableRow> alignmentMapping = new Hashtable<FeatureTableRow, FeatureTableRow>();

            // Iterate scores by descending order
            Iterator<RowVsRowScore> scoreIterator = scoreSet.iterator();
            while (scoreIterator.hasNext()) {
                RowVsRowScore score = scoreIterator.next();

                // Check if the row is already mapped
                if (alignmentMapping.containsKey(score.getFeatureTableRow()))
                    continue;

                // Check if the aligned row is already filled
                if (alignmentMapping.containsValue(score.getAlignedRow()))
                    continue;

                alignmentMapping.put(score.getFeatureTableRow(), score.getAlignedRow());
            }

            // Align all rows using the mapping
            for (FeatureTableRow sourceRow : featureTable.getRows()) {
                FeatureTableRow targetRow = alignmentMapping.get(sourceRow);

                // If we have no mapping for this row, add a new one
                if (targetRow == null) {
                    targetRow = MSDKObjectBuilder.getFeatureTableRow(result, newRowID);
                    result.addRow(targetRow);
                    FeatureTableColumn<Integer> column = result.getColumn(ColumnName.ID, null);
                    targetRow.setData(column, newRowID);
                    newRowID++;
                }

                // Add all features from the original row to the aligned row
                for (Sample sample : sourceRow.getFeatureTable().getSamples()) {
                    FeatureTableUtil.copyFeatureValues(sourceRow, targetRow, sample);
                }

                // Combine common values from the original row with the aligned
                // row
                FeatureTableUtil.copyCommonValues(sourceRow, targetRow, true);

                processedFeatures++;
            }

            // Re-calculate average row averages
            FeatureTableUtil.recalculateAverages(result);

            if (canceled)
                return null;

        }

        // Return the new feature table
        return result;
    }

    /** {@inheritDoc} */
    @Override
    @Nullable
    public Float getFinishedPercentage() {
        return totalFeatures == 0 ? null : (float) processedFeatures / totalFeatures;
    }

    /** {@inheritDoc} */
    @Override
    @Nullable
    public FeatureTable getResult() {
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public void cancel() {
        canceled = true;
    }

}