/* * RateStatistic.java * * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut * * 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 beast.evolution.branchratemodel; import java.io.PrintStream; import beast.core.BEASTObject; import beast.core.Description; import beast.core.Function; import beast.core.Input; import beast.core.Input.Validate; import beast.core.Loggable; import beast.evolution.likelihood.GenericTreeLikelihood; import beast.evolution.tree.Node; import beast.evolution.tree.Tree; import beast.math.statistic.DiscreteStatistics; @Description("A statistic that tracks the mean, variance and coefficent of variation of rates. " + "It has three dimensions, one for each statistic.") public class RateStatistic extends BEASTObject implements Loggable, Function { final public Input<GenericTreeLikelihood> likelihoodInput = new Input<>("treeLikelihood", "TreeLikelihood containing branch rate model that provides rates for a tree"); final public Input<BranchRateModel> branchRateModelInput = new Input<>("branchratemodel", "model that provides rates for a tree", Validate.XOR, likelihoodInput); final public Input<Tree> treeInput = new Input<>("tree", "tree for which the rates apply"); final public Input<Boolean> internalInput = new Input<>("internal", "consider internal nodes, default true", true); final public Input<Boolean> externalInput = new Input<>("external", "consider external nodes, default true", true); private Tree tree = null; private BranchRateModel branchRateModel = null; private boolean internal = true; private boolean external = true; final static int MEAN = 0; final static int VARIANCE = 1; final static int COEFFICIENT_OF_VARIATION = 2; @Override public void initAndValidate() { tree = treeInput.get(); branchRateModel = branchRateModelInput.get(); if (branchRateModel == null) { branchRateModel = likelihoodInput.get().branchRateModelInput.get(); } this.internal = internalInput.get(); this.external = externalInput.get(); } /** * calculate the three statistics from scratch * */ public double[] calcValues() { int length = 0; int offset = 0; final int nrOfLeafs = tree.getLeafNodeCount(); if (external) { length += nrOfLeafs; } if (internal) { length += tree.getInternalNodeCount() - 1; } final double[] rates = new double[length]; // need those only for mean final double[] branchLengths = new double[length]; final Node[] nodes = tree.getNodesAsArray(); /** handle leaf nodes **/ if (external) { for (int i = 0; i < nrOfLeafs; i++) { final Node child = nodes[i]; final Node parent = child.getParent(); branchLengths[i] = parent.getHeight() - child.getHeight(); rates[i] = branchRateModel.getRateForBranch(child); } offset = nrOfLeafs; } /** handle internal nodes **/ if (internal) { final int n = tree.getNodeCount(); int k = offset; for (int i = nrOfLeafs; i < n; i++) { final Node child = nodes[i]; if (!child.isRoot()) { final Node parent = child.getParent(); branchLengths[k] = parent.getHeight() - child.getHeight(); rates[k] = branchRateModel.getRateForBranch(child); k++; } } } final double[] values = new double[3]; double totalWeightedRate = 0.0; double totalTreeLength = 0.0; for (int i = 0; i < rates.length; i++) { totalWeightedRate += rates[i] * branchLengths[i]; totalTreeLength += branchLengths[i]; } values[MEAN] = totalWeightedRate / totalTreeLength; // Q2R why not? // final double mean = DiscreteStatistics.mean(rates); // values[VARIANCE] = DiscreteStatistics.variance(rates, mean); // values[COEFFICIENT_OF_VARIATION] = Math.sqrt(D values[VARIANCE]) / mean; values[VARIANCE] = DiscreteStatistics.variance(rates); final double mean = DiscreteStatistics.mean(rates); values[COEFFICIENT_OF_VARIATION] = Math.sqrt(DiscreteStatistics.variance(rates, mean)) / mean; return values; } /** * Valuable implementation * */ @Override public int getDimension() { return 3; } @Override public double getArrayValue() { return calcValues()[0]; } @Override public double getArrayValue(final int dim) { if (dim > 3) { throw new IllegalArgumentException(); } return calcValues()[dim]; } /** * Loggable implementation * */ @Override public void init(final PrintStream out) { String id = getID(); if (id == null) { id = ""; } out.print(id + ".mean\t" + id + ".variance\t" + id + ".coefficientOfVariation\t"); } @Override public void log(final int sample, final PrintStream out) { final double[] values = calcValues(); out.print(values[0] + "\t" + values[1] + "\t" + values[2] + "\t"); } @Override public void close(final PrintStream out) { // nothing to do } }