/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.core.operation.binList; import java.util.ArrayList; import java.util.Collection; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import edu.yu.einstein.genplay.core.operation.Operation; import edu.yu.einstein.genplay.core.operationPool.OperationPool; import edu.yu.einstein.genplay.dataStructure.list.genomeWideList.SCWList.binList.BinList; import edu.yu.einstein.genplay.dataStructure.list.listView.ListView; import edu.yu.einstein.genplay.dataStructure.scoredChromosomeWindow.ScoredChromosomeWindow; import edu.yu.einstein.genplay.exception.exceptions.BinListDifferentWindowSizeException; /** * Computes the correlation coefficients between two {@link BinList} for every chromosome as well as genome wide * @author Julien Lajugie */ public class BLOCorrelate implements Operation<Double[]> { private final BinList binList1; // input BinList private final BinList binList2; // input BinList private final int[] counters; // counters for the none-null value private int counter = 0; // counter for the none-null value private final double[] means1; // chromosome averages of binList1 private double mean1 = 0; // average of binList1 private final double[] means2; // chromosome averages of binList2 private double mean2 = 0; // average of binList2 private final double[] stdevs1; // chromosome standard deviations of binList1 based on the chromosome average private double stdev1 = 0; // standard deviation of binList1 private final double[] stddevtotals1; // chromosome standard deviations of binList1 based on the total average private final double[] stdevs2; // chromosome standard deviations of binList2 based on the chromosome average private double stdev2 = 0; // standard deviation of binList2 private final double[] stddevtotals2; // chromosome standard deviations of binList2 based on the total average private final double[] correlations; // chromosome correlation coefficients private double correlation = 0;// correlation coefficient private boolean stopped = false;// true if the operation must be stopped /** * Computes the correlation coefficients between two {@link BinList} for every chromosome as well as genome wide * @param binList1 1st input {@link BinList} * @param binList2 2nd input {@link BinList} */ public BLOCorrelate(BinList binList1, BinList binList2) { this.binList1 = binList1; this.binList2 = binList2; counters = new int[binList1.size()]; means1 = new double[binList1.size()]; means2 = new double[binList2.size()]; stdevs1 = new double[binList1.size()]; stdevs2 = new double[binList2.size()]; stddevtotals1 = new double[binList1.size()]; stddevtotals2 = new double[binList2.size()]; correlations = new double[binList1.size()]; } @Override public Double[] compute() throws InterruptedException, ExecutionException, BinListDifferentWindowSizeException { try { computeMeans(); } catch (InterruptedException e) { // it the computation of the averages had been interrupted we return null return null; } // if there is no none-null value we return null if (counter == 0) { return null; } final OperationPool op = OperationPool.getInstance(); final Collection<Callable<Void>> threadList = new ArrayList<Callable<Void>>(); for (int i = 0; i < binList1.size(); i++) { final ListView<ScoredChromosomeWindow> currentList1 = binList1.get(i); final ListView<ScoredChromosomeWindow> currentList2 = binList2.get(i); final int currentIndex = i; Callable<Void> currentThread = new Callable<Void>() { @Override public Void call() throws Exception { if ((currentList1 != null) && (currentList2 != null)) { int j = 0; while ((j < currentList1.size()) && (j < currentList2.size())) { double scoreList1 = currentList1.get(j).getScore(); double scoreList2 = currentList2.get(j).getScore(); if ((scoreList1 != 0) && (scoreList2 != 0)) { stdevs1[currentIndex] += Math.pow(scoreList1 - means1[currentIndex], 2); stdevs2[currentIndex] += Math.pow(scoreList2 - means2[currentIndex], 2); stddevtotals1[currentIndex] += Math.pow(scoreList1 - mean1, 2); stddevtotals2[currentIndex] += Math.pow(scoreList2 - mean2, 2); correlations[currentIndex] += scoreList1 * scoreList2; } j++; } } // tell the operation pool that a chromosome is done op.notifyDone(); return null; } }; threadList.add(currentThread); } if (op.startPool(threadList) == null) { return null; } // we sum the chromosome results to have a genome wide result for (int i = 0; i < correlations.length; i++) { correlation += correlations[i]; stdev1 += stddevtotals1[i]; stdev2 += stddevtotals2[i]; if (counters[i] != 0) { stdevs1[i] = Math.sqrt(stdevs1[i] / counters[i]); stdevs2[i] = Math.sqrt(stdevs2[i] / counters[i]); } } // compute the standard deviation stdev1 = Math.sqrt(stdev1 / counter); stdev2 = Math.sqrt(stdev2 / counter); // we compute the correlation Double[] result = new Double[correlations.length + 1]; for (int i = 0; i < correlations.length; i++) { if (counters[i] != 0) { result[i] = (correlations[i] - (counters[i] * means1[i] * means2[i])) / ((counters[i] - 1) * stdevs1[i] * stdevs2[i]); } else { result[i] = null; } } correlation = (correlation - (counter * mean1 * mean2)) / ((counter - 1) * stdev1 * stdev2); result[result.length - 1] = correlation; return result; } /** * Computes the means of the two BinLists for every chromosome as well as genome wide * @throws InterruptedException * @throws ExecutionException * @throws BinListDifferentWindowSizeException */ private void computeMeans() throws InterruptedException, ExecutionException, BinListDifferentWindowSizeException { if (binList1.getBinSize() != binList2.getBinSize()) { throw new BinListDifferentWindowSizeException(); } final OperationPool op = OperationPool.getInstance(); final Collection<Callable<Void>> threadList = new ArrayList<Callable<Void>>(); for (int i = 0; i < binList1.size(); i++) { final ListView<ScoredChromosomeWindow> currentList1 = binList1.get(i); final ListView<ScoredChromosomeWindow> currentList2 = binList2.get(i); final int currentIndex = i; Callable<Void> currentThread = new Callable<Void>() { @Override public synchronized Void call() throws Exception { if ((currentList1 != null) && (currentList2 != null)) { int j = 0; // compute the average only when the two scores are not null while ((j < currentList1.size()) && (j < currentList2.size()) && !stopped) { double scoreList1 = currentList1.get(j).getScore(); double scoreList2 = currentList2.get(j).getScore(); if ((scoreList1 != 0) && (scoreList2 != 0)) { means1[currentIndex] += scoreList1; means2[currentIndex] += scoreList2; counters[currentIndex]++; } j++; } } // tell the operation pool that a chromosome is done op.notifyDone(); return null; } }; threadList.add(currentThread); } if (op.startPool(threadList) == null) { throw new InterruptedException(); } // we sum the chromosome results to have a genome wide result for (int i = 0; (i < counters.length) && !stopped; i++) { counter += counters[i]; mean1 += means1[i]; mean2 += means2[i]; if (counters[i] != 0) { means1[i] /= counters[i]; means2[i] /= counters[i]; } } // if there is no none-null value we return 0 if (counter != 0) { mean1 /= counter; mean2 /= counter; } } @Override public String getDescription() { return "Operation: Correlation Coefficient"; } @Override public String getProcessingDescription() { return "Computing Correlation"; } @Override public int getStepCount() { return 2; } @Override public void stop() { stopped = true; } }