/*
* LineageCountThroughTime.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.evolution.tree;
import dr.stats.Variate;
import jebl.evolution.graphs.Node;
import jebl.evolution.io.ImportException;
import jebl.evolution.io.NewickImporter;
import jebl.evolution.io.NexusImporter;
import jebl.evolution.io.TreeImporter;
import jebl.evolution.trees.RootedTree;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
*/
public class LineageCountThroughTime {
public static Variate[] getLTT(String treeFile,
double minTime,
double maxTime,
int binCount,
int skip // number of trees to skip
) throws IOException, ImportException {
double delta = (maxTime - minTime) / (binCount - 1);
BufferedReader reader = new BufferedReader(new FileReader(treeFile));
String line = reader.readLine();
TreeImporter importer;
if (line.toUpperCase().startsWith("#NEXUS")) {
importer = new NexusImporter(reader);
} else {
importer = new NewickImporter(reader, false);
}
int state = 0;
while (importer.hasTree() && state < skip) {
importer.importNextTree();
state += 1;
}
// the age of the end of this group
List<double[]> branchingTimes = new ArrayList<double[]>();
state = 0;
while (importer.hasTree()) {
RootedTree tree = (RootedTree) importer.importNextTree();
double[] bt = new double[tree.getInternalNodes().size()];
int i = 0;
for (Node node : tree.getInternalNodes()) {
bt[i] = tree.getHeight(node);
i++;
}
Arrays.sort(bt);
branchingTimes.add(bt);
state += 1;
}
Variate[] bins = new Variate[binCount];
double height = 0;
double n = branchingTimes.get(0).length;
for (int k = 0; k < binCount; k++) {
bins[k] = new Variate.D();
if (height >= 0.0 && height <= maxTime) {
for (state = 0; state < branchingTimes.size(); state++) {
int index = 0;
while (index < branchingTimes.get(state).length && branchingTimes.get(state)[index] < height) {
index += 1;
}
double lineageCount = 1;
if (index < branchingTimes.get(state).length) {
lineageCount = n - index + 1;
}
bins[k].add(lineageCount);
}
}
height += delta;
}
Variate xData = new Variate.D();
Variate yDataMean = new Variate.D();
Variate yDataMedian = new Variate.D();
Variate yDataUpper = new Variate.D();
Variate yDataLower = new Variate.D();
double t = minTime;
for (Variate bin : bins) {
xData.add(t);
if (bin.getCount() > 0) {
yDataMean.add(bin.getMean());
yDataMedian.add(bin.getQuantile(0.5));
yDataLower.add(bin.getQuantile(0.025));
yDataUpper.add(bin.getQuantile(0.975));
} else {
yDataMean.add(Double.NaN);
yDataMedian.add(Double.NaN);
yDataLower.add(Double.NaN);
yDataUpper.add(Double.NaN);
}
t += delta;
}
return new Variate[]{xData, yDataMean};
}
}