# HG changeset patch # User sagun98 # Date 1622744012 0 # Node ID 0de566f214482a232e6742eee78ca715fc28bfa6 v2 diff -r 000000000000 -r 0de566f21448 Archive.zip Binary file Archive.zip has changed diff -r 000000000000 -r 0de566f21448 HMPStool10PCoA.png Binary file HMPStool10PCoA.png has changed diff -r 000000000000 -r 0de566f21448 MicroPITA.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MicroPITA.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1145 @@ +#!/usr/bin/env python +""" +Author: Timothy Tickle +Description: Class to Run analysis for the microPITA paper +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import sys +import argparse +from src.breadcrumbs.src.AbundanceTable import AbundanceTable +from src.breadcrumbs.src.ConstantsBreadCrumbs import ConstantsBreadCrumbs +from src.breadcrumbs.src.Metric import Metric +from src.breadcrumbs.src.KMedoids import Kmedoids +from src.breadcrumbs.src.MLPYDistanceAdaptor import MLPYDistanceAdaptor +from src.breadcrumbs.src.SVM import SVM +from src.breadcrumbs.src.UtilityMath import UtilityMath + +from src.ConstantsMicropita import ConstantsMicropita +import csv +import logging +import math +import mlpy +import numpy as np +import operator +import os +import random +import scipy.cluster.hierarchy as hcluster +import scipy.spatial.distance +from types import * + +class MicroPITA: + """ + Selects samples from a first tier of a multi-tiered study to be used in a second tier. + Different methods can be used for selection. + The expected input is an abundance table (and potentially a text file of targeted features, + if using the targeted features option). Output is a list of samples exhibiting the + characteristics of interest. + """ + + #Constants + #Diversity metrics Alpha + c_strInverseSimpsonDiversity = Metric.c_strInvSimpsonDiversity + c_strChao1Diversity = Metric.c_strChao1Diversity + + #Diversity metrics Beta + c_strBrayCurtisDissimilarity = Metric.c_strBrayCurtisDissimilarity + + #Additive inverses of diversity metrics beta + c_strInvBrayCurtisDissimilarity = Metric.c_strInvBrayCurtisDissimilarity + + #Technique Names + ConstantsMicropita.c_strDiversity2 = ConstantsMicropita.c_strDiversity+"_C" + + #Targeted feature settings + c_strTargetedRanked = ConstantsMicropita.c_strTargetedRanked + c_strTargetedAbundance = ConstantsMicropita.c_strTargetedAbundance + + #Technique groupings +# c_lsDiversityMethods = [ConstantsMicropita.c_strDiversity,ConstantsMicropita.c_strDiversity2] + + #Converts ecology metrics into standardized method selection names + dictConvertAMetricDiversity = {c_strInverseSimpsonDiversity:ConstantsMicropita.c_strDiversity, c_strChao1Diversity:ConstantsMicropita.c_strDiversity2} +# dictConvertMicroPITAToAMetric = {ConstantsMicropita.c_strDiversity:c_strInverseSimpsonDiversity, ConstantsMicropita.c_strDiversity2:c_strChao1Diversity} + dictConvertBMetricToMethod = {c_strBrayCurtisDissimilarity:ConstantsMicropita.c_strRepresentative} + dictConvertInvBMetricToMethod = {c_strBrayCurtisDissimilarity:ConstantsMicropita.c_strExtreme} + + #Linkage used in the Hierarchical clustering + c_strHierarchicalClusterMethod = 'average' + +####Group 1## Diversity + #Testing: Happy path Testing (8) + def funcGetTopRankedSamples(self, lldMatrix = None, lsSampleNames = None, iTopAmount = None): + """ + Given a list of lists of measurements, for each list the indices of the highest values are returned. If lsSamplesNames is given + it is treated as a list of string names that is in the order of the measurements in each list. Indices are returned or the sample + names associated with the indices. + + :param lldMatrix: List of lists [[value,value,value,value],[value,value,value,value]]. + :type: List of lists List of measurements. Each list is a different measurement. Each measurement in positionally related to a sample. + :param lsSampleNames: List of sample names positionally related (the same) to each list (Optional). + :type: List of strings List of strings. + :param iTopAmount: The amount of top measured samples (assumes the higher measurements are better). + :type: integer Integer amount of sample names/ indices to return. + :return List: List of samples to be selected. + """ + topRankListRet = [] + for rowMetrics in lldMatrix: + #Create 2 d array to hold value and index and sort + liIndexX = [rowMetrics,range(len(rowMetrics))] + liIndexX[1].sort(key = liIndexX[0].__getitem__,reverse = True) + + if lsSampleNames: + topRankListRet.append([lsSampleNames[iIndex] for iIndex in liIndexX[1][:iTopAmount]]) + else: + topRankListRet.append(liIndexX[1][:iTopAmount]) + + return topRankListRet + + ####Group 2## Representative Dissimilarity + #Testing: Happy path tested 1 + def funcGetCentralSamplesByKMedoids(self, npaMatrix=None, sMetric=None, lsSampleNames=None, iNumberSamplesReturned=0, istmBetaMatrix=None, istrmTree=None, istrmEnvr=None): + """ + Gets centroid samples by k-medoids clustering of a given matrix. + + :param npaMatrix: Numpy array where row=features and columns=samples + :type: Numpy array Abundance Data. + :param sMetric: String name of beta metric used as the distance metric. + :type: String String name of beta metric. + :param lsSampleNames: The names of the sample + :type: List List of strings + :param iNumberSamplesReturned: Number of samples to return, each will be a centroid of a sample. + :type: Integer Number of samples to return + :return List: List of selected samples. + :param istmBetaMatrix: File with beta-diversity matrix + :type: File stream or file path string + """ + + #Count of how many rows + sampleCount = npaMatrix.shape[0] + if iNumberSamplesReturned > sampleCount: + logging.error("MicroPITA.funcGetCentralSamplesByKMedoids:: There are not enough samples to return the amount of samples specified. Return sample count = "+str(iNumberSamplesReturned)+". Sample number = "+str(sampleCount)+".") + return False + + #If the cluster count is equal to the sample count return all samples + if sampleCount == iNumberSamplesReturned: + return list(lsSampleNames) + + #Get distance matrix + distanceMatrix=scipy.spatial.distance.squareform(Metric.funcReadMatrixFile(istmMatrixFile=istmBetaMatrix,lsSampleOrder=lsSampleNames)[0]) if istmBetaMatrix else Metric.funcGetBetaMetric(npadAbundancies=npaMatrix, sMetric=sMetric, istrmTree=istrmTree, istrmEnvr=istrmEnvr, lsSampleOrder=lsSampleNames) + if type(distanceMatrix) is BooleanType: + logging.error("MicroPITA.funcGetCentralSamplesByKMedoids:: Could not read in the supplied distance matrix, returning false.") + return False + + # Handle unifrac output + if sMetric in [Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]: + distanceMatrix = distanceMatrix[0] + + #Log distance matrix + logging.debug("MicroPITA.funcGetCentralSamplesByKMedoids:: Distance matrix for representative selection using metric="+str(sMetric)) + + distance = MLPYDistanceAdaptor(npaDistanceMatrix=distanceMatrix, fIsCondensedMatrix=True) + + #Create object to determine clusters/medoids + medoidsMaker = Kmedoids(k=iNumberSamplesReturned, dist=distance) + #medoidsData includes(1d numpy array, medoids indexes; + # 1d numpy array, non-medoids indexes; + # 1d numpy array, cluster membership for non-medoids; + # double, cost of configuration) + #npaMatrix is samples x rows + #Build a matrix of lists of indicies to pass to the distance matrix + lliIndicesMatrix = [[iIndexPosition] for iIndexPosition in xrange(0,len(npaMatrix))] + medoidsData = medoidsMaker.compute(np.array(lliIndicesMatrix)) + logging.debug("MicroPITA.funcGetCentralSamplesByKMedoids:: Results from the kmedoid method in representative selection:") + logging.debug(str(medoidsData)) + + #If returning the same amount of clusters and samples + #Return centroids + selectedIndexes = medoidsData[0] + return [lsSampleNames[selectedIndexes[index]] for index in xrange(0,iNumberSamplesReturned)] + + ####Group 3## Highest Dissimilarity + #Testing: Happy path tested + def funcSelectExtremeSamplesFromHClust(self, strBetaMetric, npaAbundanceMatrix, lsSampleNames, iSelectSampleCount, istmBetaMatrix=None, istrmTree=None, istrmEnvr=None): + """ + Select extreme samples from HClustering. + + :param strBetaMetric: The beta metric to use for distance matrix generation. + :type: String The name of the beta metric to use. + :param npaAbundanceMatrix: Numpy array where row=samples and columns=features. + :type: Numpy Array Abundance data. + :param lsSampleNames: The names of the sample. + :type: List List of strings. + :param iSelectSampleCount: Number of samples to select (return). + :type: Integer Integer number of samples returned. + :return Samples: List of samples. + :param istmBetaMatrix: File with beta-diversity matrix + :type: File stream or file path string + """ + + #If they want all the sample count, return all sample names + iSampleCount=len(npaAbundanceMatrix[:,0]) + if iSelectSampleCount==iSampleCount: + return lsSampleNames + + #Holds the samples to be returned + lsReturnSamplesRet = [] + + #Generate beta matrix + #Returns condensed matrix + tempDistanceMatrix = scipy.spatial.distance.squareform(Metric.funcReadMatrixFile(istmMatrixFile=istmBetaMatrix,lsSampleOrder=lsSampleNames)[0]) if istmBetaMatrix else Metric.funcGetBetaMetric(npadAbundancies=npaAbundanceMatrix, sMetric=strBetaMetric, istrmTree=istrmTree, istrmEnvr=istrmEnvr, lsSampleOrder=lsSampleNames, fAdditiveInverse = True) + + if strBetaMetric in [Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]: + tempDistanceMatrix = tempDistanceMatrix[0] + + if type(tempDistanceMatrix) is BooleanType: + logging.error("MicroPITA.funcSelectExtremeSamplesFromHClust:: Could not read in the supplied distance matrix, returning false.") + return False + + if istmBetaMatrix: + tempDistanceMatrix = 1-tempDistanceMatrix + + #Feed beta matrix to linkage to cluster + #Send condensed matrix + linkageMatrix = hcluster.linkage(tempDistanceMatrix, method=self.c_strHierarchicalClusterMethod) + + #Extract cluster information from dendrogram + #The linakge matrix is of the form + #[[int1 int2 doube int3],...] + #int1 and int1 are the paired samples indexed at 0 and up. + #each list is an entry for a branch that is number starting with the first + #list being sample count index + 1 + #each list is then named by an increment as they appear + #this means that if a number is in the list and is = sample count or greater it is not + #terminal and is instead a branch. + #This method just takes the lowest metric measurement (highest distance pairs/clusters) + #Works much better than the original technique + #get total number of samples + + iCurrentSelectCount = 0 + for row in linkageMatrix: + #Get nodes ofthe lowest pairing (so the furthest apart pair) + iNode1 = int(row[0]) + iNode2 = int(row[1]) + #Make sure the nodes are a terminal node (sample) and not a branch in the dendrogram + #The branching in the dendrogram will start at the number of samples and increment higher. + #Add each of the pair one at a time breaking when enough samples are selected. + if iNode1= sampleCount): + return lsSamples + + #Get the random indices for the sample (without replacement) + liRandomIndices = random.sample(range(sampleCount), iNumberOfSamplesToReturn) + + #Create a boolean array of if indexes are to be included in the reduced array + return [sSample for iIndex, sSample in enumerate(lsSamples) if iIndex in liRandomIndices] + + #Happy path tested (case 3) + def funcGetAveragePopulation(self, abndTable, lfCompress): + """ + Get the average row per column in the abndtable. + + :param abndTable: AbundanceTable of data to be averaged + :type: AbudanceTable + :param lfCompress: List of boolean flags (false means to remove sample before averaging + :type: List of floats + :return List of doubles: + """ + if sum(lfCompress) == 0: + return [] + + #Get the average populations + lAverageRet = [] + + for sFeature in abndTable.funcGetAbundanceCopy(): + sFeature = list(sFeature)[1:] + sFeature=np.compress(lfCompress,sFeature,axis=0) + lAverageRet.append(sum(sFeature)/float(len(sFeature))) + return lAverageRet + + #Happy path tested (2 cases) + def funcGetDistanceFromAverage(self, abndTable,ldAverage,lsSamples,lfSelected): + """ + Given an abundance table and an average sample, this returns the distance of each sample + (measured using brays-curtis dissimilarity) from the average. + The distances are reduced by needing to be in the lsSamples and being a true in the lfSelected + (which is associated with the samples in the order of the samples in the abundance table; + use abundancetable.funcGetSampleNames() to see the order if needed). + + :param abndTable: Abundance table holding the data to be analyzed. + :type: AbundanceTable + :param ldAverage: Average population (Average features of the abundance table of samples) + :type: List of doubles which represent the average population + :param lsSamples: These are the only samples used in the analysis + :type: List of strings (sample ids) + :param lfSelected: Samples to be included in the analysis + :type: List of boolean (true means include) + :return: List of distances (doubles) + """ + #Get the distance from label 1 of all samples in label0 splitting into selected and not selected lists + ldSelectedDistances = [] + + for sSampleName in [sSample for iindex, sSample in enumerate(lsSamples) if lfSelected[iindex]]: + #Get the sample measurements + ldSelectedDistances.append(Metric.funcGetBrayCurtisDissimilarity(np.array([abndTable.funcGetSample(sSampleName),ldAverage]))[0]) + return ldSelectedDistances + + #Happy path tested (1 case) + def funcMeasureDistanceFromLabelToAverageOtherLabel(self, abndTable, lfGroupOfInterest, lfGroupOther): + """ + Get the distance of samples from one label from the average sample of not the label. + Note: This assumes 2 classes. + + :param abndTable: Table of data to work out of. + :type: Abundace Table + :param lfGroupOfInterest: Boolean indicator of the sample being in the first group. + :type: List of floats, true indicating an individual in the group of interest. + :param lfGroupOther: Boolean indicator of the sample being in the other group. + :type: List of floats, true indicating an individual in the + :return List of List of doubles: [list of tuples (string sample name,double distance) for the selected population, list of tuples for the not selected population] + """ + #Get all sample names + lsAllSamples = abndTable.funcGetSampleNames() + + #Get average populations + lAverageOther = self.funcGetAveragePopulation(abndTable=abndTable, lfCompress=lfGroupOther) + + #Get the distance from the average of the other label (label 1) + ldSelectedDistances = self.funcGetDistanceFromAverage(abndTable=abndTable, ldAverage=lAverageOther, + lsSamples=lsAllSamples, lfSelected=lfGroupOfInterest) + + return zip([lsAllSamples[iindex] for iindex, fGroup in enumerate(lfGroupOfInterest) if fGroup],ldSelectedDistances) + + #Happy path tested (1 test case) + def funcPerformDistanceSelection(self, abndTable, iSelectionCount, sLabel, sValueOfInterest): + """ + Given metadata, metadata of one value (sValueOfInterest) is measured from the average (centroid) value of another label group. + An iSelectionCount of samples is selected from the group of interest closest to and furthest from the centroid of the other group. + + :params abndTable: Abundance of measurements + :type: AbundanceTable + :params iSelectionCount: The number of samples selected per sample. + :type: Integer Integer greater than 0 + :params sLabel: ID of the metadata which is the supervised label + :type: String + :params sValueOfInterest: Metadata value in the sLabel metadta row of the abundance table which defines the group of interest. + :type: String found in the abundance table metadata row indicated by sLabel. + :return list list of tuples (samplename, distance) [[iSelectionCount of tuples closest to the other centroid], [iSelectionCount of tuples farthest from the other centroid], [all tuples of samples not selected]] + """ + + lsMetadata = abndTable.funcGetMetadata(sLabel) + #Other metadata values + lsUniqueOtherValues = list(set(lsMetadata)-set(sValueOfInterest)) + + #Get boolean indicator of values of interest + lfLabelsInterested = [sValueOfInterest == sValue for sValue in lsMetadata] + + #Get the distances of the items of interest from the other metadata values + dictDistanceAverages = {} + for sOtherLabel in lsUniqueOtherValues: + #Get boolean indicator of labels not of interest + lfLabelsOther = [sOtherLabel == sValue for sValue in lsMetadata] + + #Get the distances of data from two different groups to the average of the other + ldValueDistances = dict(self.funcMeasureDistanceFromLabelToAverageOtherLabel(abndTable, lfLabelsInterested, lfLabelsOther)) + + for sKey in ldValueDistances: + dictDistanceAverages[sKey] = ldValueDistances[sKey] + dictDistanceAverages[sKey] if sKey in dictDistanceAverages else ldValueDistances[sKey] + + #Finish average by dividing by length of lsUniqueOtherValues + ltpleAverageDistances = [(sKey, dictDistanceAverages[sKey]/float(len(lsUniqueOtherValues))) for sKey in dictDistanceAverages] + + #Sort to extract extremes + ltpleAverageDistances = sorted(ltpleAverageDistances,key=operator.itemgetter(1)) + + #Get the closest and farthest distances + ltupleDiscriminantSamples = ltpleAverageDistances[:iSelectionCount] + ltupleDistinctSamples = ltpleAverageDistances[iSelectionCount*-1:] + + #Remove the selected samples from the larger population of distances (better visualization) + ldSelected = [tpleSelected[0] for tpleSelected in ltupleDiscriminantSamples+ltupleDistinctSamples] + + #Return discriminant tuples, distinct tuples, other tuples + return [ltupleDiscriminantSamples, ltupleDistinctSamples, + [tplData for tplData in ltpleAverageDistances if tplData[0] not in ldSelected]] + + #Run the supervised method surrounding distance from centroids + #Happy path tested (3 test cases) + def funcRunSupervisedDistancesFromCentroids(self, abundanceTable, fRunDistinct, fRunDiscriminant, + xOutputSupFile, xPredictSupFile, strSupervisedMetadata, + iSampleSupSelectionCount, lsOriginalSampleNames, lsOriginalLabels, fAppendFiles = False): + """ + Runs supervised methods based on measuring distances of one label from the centroid of another. NAs are evaluated as theirown group. + + :param abundanceTable: AbundanceTable + :type: AbudanceTable Data to analyze + :param fRunDistinct: Run distinct selection method + :type: Boolean boolean (true runs method) + :param fRunDiscriminant: Run discriminant method + :type: Boolean boolean (true runs method) + :param xOutputSupFile: File output from supervised methods detailing data going into the method. + :type: String or FileStream + :param xPredictSupFile: File output from supervised methods distance results from supervised methods. + :type: String or FileStream + :param strSupervisedMetadata: The metadata that will be used to group samples. + :type: String + :param iSampleSupSelectionCount: Number of samples to select + :type: Integer int sample selection count + :param lsOriginalSampleNames: List of the sample names, order is important and should be preserved from the abundanceTable. + :type: List of samples + :param fAppendFiles: Indicates that output files already exist and appending is occuring. + :type: Boolean + :return Selected Samples: A dictionary of selected samples by selection ID + Dictionary {"Selection Method":["SampleID","SampleID"...]} + """ + #Get labels and run one label against many + lstrMetadata = abundanceTable.funcGetMetadata(strSupervisedMetadata) + dictlltpleDistanceMeasurements = {} + for sMetadataValue in set(lstrMetadata): + + #For now perform the selection here for the label of interest against the other labels + dictlltpleDistanceMeasurements.setdefault(sMetadataValue,[]).extend(self.funcPerformDistanceSelection(abndTable=abundanceTable, + iSelectionCount=iSampleSupSelectionCount, sLabel=strSupervisedMetadata, sValueOfInterest=sMetadataValue)) + + #Make expected output files for supervised methods + #1. Output file which is similar to an input file for SVMs + #2. Output file that is similar to the probabilitic output of a SVM (LibSVM) + #Manly for making output of supervised methods (Distance from Centroid) similar + #MicropitaVis needs some of these files + if xOutputSupFile: + if fAppendFiles: + SVM.funcUpdateSVMFileWithAbundanceTable(abndAbundanceTable=abundanceTable, xOutputSVMFile=xOutputSupFile, + lsOriginalLabels=lsOriginalLabels, lsSampleOrdering=lsOriginalSampleNames) + else: + SVM.funcConvertAbundanceTableToSVMFile(abndAbundanceTable=abundanceTable, xOutputSVMFile=xOutputSupFile, + sMetadataLabel=strSupervisedMetadata, lsOriginalLabels=lsOriginalLabels, lsSampleOrdering=lsOriginalSampleNames) + + #Will contain the samples selected to return + #One or more of the methods may be active so this is why I am extending instead of just returning the result of each method type + dictSelectedSamplesRet = dict() + for sKey, ltplDistances in dictlltpleDistanceMeasurements.items(): + if fRunDistinct: + dictSelectedSamplesRet.setdefault(ConstantsMicropita.c_strDistinct,[]).extend([ltple[0] for ltple in ltplDistances[1]]) + if fRunDiscriminant: + dictSelectedSamplesRet.setdefault(ConstantsMicropita.c_strDiscriminant,[]).extend([ltple[0] for ltple in ltplDistances[0]]) + + if xPredictSupFile: + dictFlattenedDistances = dict() + [dictFlattenedDistances.setdefault(sKey, []).append(tple) + for sKey, lltple in dictlltpleDistanceMeasurements.items() + for ltple in lltple for tple in ltple] + if fAppendFiles: + self._updatePredictFile(xPredictSupFile=xPredictSupFile, xInputLabelsFile=xOutputSupFile, + dictltpleDistanceMeasurements=dictFlattenedDistances, abundanceTable=abundanceTable, lsOriginalSampleNames=lsOriginalSampleNames) + else: + self._writeToPredictFile(xPredictSupFile=xPredictSupFile, xInputLabelsFile=xOutputSupFile, + dictltpleDistanceMeasurements=dictFlattenedDistances, abundanceTable=abundanceTable, lsOriginalSampleNames=lsOriginalSampleNames) + return dictSelectedSamplesRet + + #Two happy path test cases + def _updatePredictFile(self, xPredictSupFile, xInputLabelsFile, dictltpleDistanceMeasurements, abundanceTable, lsOriginalSampleNames): + """ + Manages updating the predict file. + + :param xPredictSupFile: File that has predictions (distances) from the supervised method. + :type: FileStream or String file path + :param xInputLabelsFile: File that as input to the supervised methods. + :type: FileStream or String file path + :param dictltpleDistanceMeasurements: + :type: Dictionary of lists of tuples {"labelgroup":[("SampleName",dDistance)...], "labelgroup":[("SampleName",dDistance)...]} + """ + + if not isinstance(xPredictSupFile, str): + xPredictSupFile.close() + xPredictSupFile = xPredictSupFile.name + csvr = open(xPredictSupFile,'r') + + f = csv.reader(csvr,delimiter=ConstantsBreadCrumbs.c_strBreadCrumbsSVMSpace) + lsHeader = f.next()[1:] + dictlltpleRead = dict([(sHeader,[]) for sHeader in lsHeader]) + + #Read data in + iSampleIndex = 0 + for sRow in f: + sLabel = sRow[0] + [dictlltpleRead[lsHeader[iDistanceIndex]].append((lsOriginalSampleNames[iSampleIndex],dDistance)) for iDistanceIndex, dDistance in enumerate(sRow[1:]) + if not dDistance == ConstantsMicropita.c_sEmptyPredictFileValue] + iSampleIndex += 1 + + #Combine dictltpleDistanceMeasurements with new data + #If they share a key then merge keeping parameter data + #If they do not share the key, keep the full data + dictNew = {} + for sKey in dictltpleDistanceMeasurements.keys(): + lsSamples = [tple[0] for tple in dictltpleDistanceMeasurements[sKey]] + dictNew[sKey] = dictltpleDistanceMeasurements[sKey]+[tple for tple in dictlltpleRead[sKey] if tple[0] not in lsSamples] if sKey in dictlltpleRead.keys() else dictltpleDistanceMeasurements[sKey] + for sKey in dictlltpleRead: + if sKey not in dictltpleDistanceMeasurements.keys(): + dictNew[sKey] = dictlltpleRead[sKey] + + #Call writer + self._writeToPredictFile(xPredictSupFile=xPredictSupFile, xInputLabelsFile=xInputLabelsFile, + dictltpleDistanceMeasurements=dictNew, abundanceTable=abundanceTable, + lsOriginalSampleNames=lsOriginalSampleNames, fFromUpdate=True) + + #2 happy path test cases + def _writeToPredictFile(self, xPredictSupFile, xInputLabelsFile, dictltpleDistanceMeasurements, abundanceTable, lsOriginalSampleNames, fFromUpdate=False): + """ + Write to the predict file. + + :param xPredictSupFile: File that has predictions (distances) from the supervised method. + :type: FileStream or String file path + :param xInputLabelsFile: File that as input to the supervised methods. + :type: FileStream or String file path + :param dictltpleDistanceMeasurements: + :type: Dictionary of lists of tuples {"labelgroup":[("SampleName",dDistance)...], "labelgroup":[("SampleName",dDistance)...]} + :param abundanceTable: An abundance table of the sample data. + :type: AbundanceTable + :param lsOriginalSampleNames: Used if the file is being updated as the sample names so that it may be passed in and consistent with other writing. + Otherwise will use the sample names from the abundance table. + :type: List of strings + :param fFromUpdate: Indicates if this is part of an update to the file or not. + :type: Boolean + """ + + xInputLabelsFileName = xInputLabelsFile + if not isinstance(xInputLabelsFile,str): + xInputLabelsFileName = xInputLabelsFile.name + f = csv.writer(open(xPredictSupFile,"w") if isinstance(xPredictSupFile, str) else xPredictSupFile,delimiter=ConstantsBreadCrumbs.c_strBreadCrumbsSVMSpace) + + lsAllSampleNames = abundanceTable.funcGetSampleNames() + lsLabels = SVM.funcReadLabelsFromFile(xSVMFile=xInputLabelsFileName, lsAllSampleNames= lsOriginalSampleNames if fFromUpdate else lsAllSampleNames, + isPredictFile=False) + dictLabels = dict([(sSample,sLabel) for sLabel in lsLabels.keys() for sSample in lsLabels[sLabel]]) + + #Dictionay keys will be used to order the predict file + lsMeasurementKeys = dictltpleDistanceMeasurements.keys() + #Make header + f.writerow(["labels"]+lsMeasurementKeys) + + #Reformat dictionary to make it easier to use + for sKey in dictltpleDistanceMeasurements: + dictltpleDistanceMeasurements[sKey] = dict([ltpl for ltpl in dictltpleDistanceMeasurements[sKey]]) + + for sSample in lsOriginalSampleNames: + #Make body of file + f.writerow([dictLabels.get(sSample,ConstantsMicropita.c_sEmptyPredictFileValue)]+ + [str(dictltpleDistanceMeasurements[sKey].get(sSample,ConstantsMicropita.c_sEmptyPredictFileValue)) + for sKey in lsMeasurementKeys]) + + def _funcRunNormalizeSensitiveMethods(self, abndData, iSampleSelectionCount, dictSelectedSamples, lsAlphaMetrics, lsBetaMetrics, lsInverseBetaMetrics, + fRunDiversity, fRunRepresentative, fRunExtreme, strAlphaMetadata=None, + istmBetaMatrix=None, istrmTree=None, istrmEnvr=None, fInvertDiversity=False): + """ + Manages running methods that are sensitive to normalization. This is called twice, once for the set of methods which should not be normalized and the other + for the set that should be normalized. + + :param abndData: Abundance table object holding the samples to be measured. + :type: AbundanceTable + :param iSampleSelectionCount The number of samples to select per method. + :type: Integer + :param dictSelectedSamples Will be added to as samples are selected {"Method:["strSelectedSampleID","strSelectedSampleID"...]}. + :type: Dictionary + :param lsAlphaMetrics: List of alpha metrics to use on alpha metric dependent assays (like highest diversity). + :type: List of strings + :param lsBetaMetrics: List of beta metrics to use on beta metric dependent assays (like most representative). + :type: List of strings + :param lsInverseBetaMetrics: List of inverse beta metrics to use on inverse beta metric dependent assays (like most dissimilar). + :type: List of strings + :param fRunDiversity: Run Diversity based methods (true indicates run). + :type: Boolean + :param fRunRepresentative: Run Representative based methods (true indicates run). + :type: Boolean + :param fRunExtreme: Run Extreme based methods (true indicates run). + :type: Boolean + :param istmBetaMatrix: File that has a precalculated beta matrix + :type: File stream or File path string + :return Selected Samples: Samples selected by methods. + Dictionary {"Selection Method":["SampleID","SampleID","SampleID",...]} + """ + + #Sample ids/names + lsSampleNames = abndData.funcGetSampleNames() + + #Generate alpha metrics and get most diverse + if fRunDiversity: + + #Get Alpha metrics matrix + internalAlphaMatrix = None + #Name of technique + strMethod = [strAlphaMetadata] if strAlphaMetadata else lsAlphaMetrics + + #If given an alpha-diversity metadata + if strAlphaMetadata: + internalAlphaMatrix = [[float(strNum) for strNum in abndData.funcGetMetadata(strAlphaMetadata)]] + else: + #Expects Observations (Taxa (row) x sample (column)) + #Returns [[metric1-sample1, metric1-sample2, metric1-sample3],[metric1-sample1, metric1-sample2, metric1-sample3]] + internalAlphaMatrix = Metric.funcBuildAlphaMetricsMatrix(npaSampleAbundance = abndData.funcGetAbundanceCopy() + if not abndData.funcIsSummed() + else abndData.funcGetFeatureAbundanceTable(abndData.funcGetTerminalNodes()).funcGetAbundanceCopy(), + lsSampleNames = lsSampleNames, lsDiversityMetricAlpha = lsAlphaMetrics) + + if internalAlphaMatrix: + #Invert measurments + if fInvertDiversity: + lldNewDiversity = [] + for lsLine in internalAlphaMatrix: + lldNewDiversity.append([1/max(dValue,ConstantsMicropita.c_smallNumber) for dValue in lsLine]) + internalAlphaMatrix = lldNewDiversity + #Get top ranked alpha diversity by most diverse + #Expects [[sample1,sample2,sample3...],[sample1,sample2,sample3..],...] + #Returns [[sampleName1, sampleName2, sampleNameN],[sampleName1, sampleName2, sampleNameN]] + mostDiverseAlphaSamplesIndexes = self.funcGetTopRankedSamples(lldMatrix=internalAlphaMatrix, lsSampleNames=lsSampleNames, iTopAmount=iSampleSelectionCount) + + #Add to results + for index in xrange(0,len(strMethod)): + strSelectionMethod = self.dictConvertAMetricDiversity.get(strMethod[index],ConstantsMicropita.c_strDiversity+"="+strMethod[index]) + dictSelectedSamples.setdefault(strSelectionMethod,[]).extend(mostDiverseAlphaSamplesIndexes[index]) + + logging.info("MicroPITA.funcRunNormalizeSensitiveMethods:: Selected Samples 1b") + logging.info(dictSelectedSamples) + + #Generate beta metrics and + if fRunRepresentative or fRunExtreme: + + #Abundance matrix transposed + npaTransposedAbundance = UtilityMath.funcTransposeDataMatrix(abndData.funcGetAbundanceCopy(), fRemoveAdornments=True) + + #Get center selection using clusters/tiling + #This will be for beta metrics in normalized space + if fRunRepresentative: + + if istmBetaMatrix: + #Get representative dissimilarity samples + medoidSamples=self.funcGetCentralSamplesByKMedoids(npaMatrix=npaTransposedAbundance, sMetric=ConstantsMicropita.c_custom, lsSampleNames=lsSampleNames, iNumberSamplesReturned=iSampleSelectionCount, istmBetaMatrix=istmBetaMatrix, istrmTree=istrmTree, istrmEnvr=istrmEnvr) + + if medoidSamples: + dictSelectedSamples.setdefault(ConstantsMicropita.c_strRepresentative+"="+ConstantsMicropita.c_custom,[]).extend(medoidSamples) + else: + logging.info("MicroPITA.funcRunNormalizeSensitiveMethods:: Performing representative selection on normalized data.") + for bMetric in lsBetaMetrics: + + #Get representative dissimilarity samples + medoidSamples=self.funcGetCentralSamplesByKMedoids(npaMatrix=npaTransposedAbundance, sMetric=bMetric, lsSampleNames=lsSampleNames, iNumberSamplesReturned=iSampleSelectionCount, istmBetaMatrix=istmBetaMatrix, istrmTree=istrmTree, istrmEnvr=istrmEnvr) + + if medoidSamples: + dictSelectedSamples.setdefault(self.dictConvertBMetricToMethod.get(bMetric,ConstantsMicropita.c_strRepresentative+"="+bMetric),[]).extend(medoidSamples) + + #Get extreme selection using clusters, tiling + if fRunExtreme: + logging.info("MicroPITA.funcRunNormalizeSensitiveMethods:: Performing extreme selection on normalized data.") + if istmBetaMatrix: + + #Samples for representative dissimilarity + #This involves inverting the distance metric, + #Taking the dendrogram level of where the number cluster == the number of samples to select + #Returning a repersentative sample from each cluster + extremeSamples = self.funcSelectExtremeSamplesFromHClust(strBetaMetric=ConstantsMicropita.c_custom, npaAbundanceMatrix=npaTransposedAbundance, lsSampleNames=lsSampleNames, iSelectSampleCount=iSampleSelectionCount, istmBetaMatrix=istmBetaMatrix, istrmTree=istrmTree, istrmEnvr=istrmEnvr) + + #Add selected samples + if extremeSamples: + dictSelectedSamples.setdefault(ConstantsMicropita.c_strExtreme+"="+ConstantsMicropita.c_custom,[]).extend(extremeSamples) + + else: + #Run KMedoids with inverse custom distance metric in normalized space + for bMetric in lsInverseBetaMetrics: + + #Samples for representative dissimilarity + #This involves inverting the distance metric, + #Taking the dendrogram level of where the number cluster == the number of samples to select + #Returning a repersentative sample from each cluster + extremeSamples = self.funcSelectExtremeSamplesFromHClust(strBetaMetric=bMetric, npaAbundanceMatrix=npaTransposedAbundance, lsSampleNames=lsSampleNames, iSelectSampleCount=iSampleSelectionCount, istmBetaMatrix=istmBetaMatrix, istrmTree=istrmTree, istrmEnvr=istrmEnvr) + + #Add selected samples + if extremeSamples: + dictSelectedSamples.setdefault(self.dictConvertInvBMetricToMethod.get(bMetric,ConstantsMicropita.c_strExtreme+"="+bMetric),[]).extend(extremeSamples) + + logging.info("MicroPITA.funcRunNormalizeSensitiveMethods:: Selected Samples 2,3b") + logging.info(dictSelectedSamples) + return dictSelectedSamples + + def funcRun(self, strIDName, strLastMetadataName, istmInput, + ostmInputPredictFile, ostmPredictFile, ostmCheckedFile, ostmOutput, + cDelimiter, cFeatureNameDelimiter, strFeatureSelection, + istmFeatures, iCount, lstrMethods, strLastRowMetadata = None, strLabel = None, strStratify = None, + strCustomAlpha = None, strCustomBeta = None, strAlphaMetadata = None, istmBetaMatrix = None, istrmTree = None, istrmEnvr = None, + iMinSeqs = ConstantsMicropita.c_liOccurenceFilter[0], iMinSamples = ConstantsMicropita.c_liOccurenceFilter[1], fInvertDiversity = False): + """ + Manages the selection of samples given different metrics. + + :param strIDName: Sample Id metadata row + :type: String + :param strLastMetadataName: The id of the metadata positioned last in the abundance table. + :type: String String metadata id. + :param istmInput: File to store input data to supervised methods. + :type: FileStream of String file path + :param ostmInputPredictFile: File to store distances from supervised methods. + :type: FileStream or String file path + :param ostmCheckedFile: File to store the AbundanceTable data after it is being checked. + :type: FileStream or String file path + :param ostmOutPut: File to store sample selection by methods of interest. + :type: FileStream or String file path + :param cDelimiter: Delimiter of abundance table. + :type: Character Char (default TAB). + :param cFeatureNameDelimiter: Delimiter of the name of features (for instance if they contain consensus lineages indicating clades). + :type: Character (default |). + :param stFeatureSelectionMethod: Which method to use to select features in a targeted manner (Using average ranked abundance or average abundance). + :type: String (specific values indicated in ConstantsMicropita.lsTargetedFeatureMethodValues). + :param istmFeatures: File which holds the features of interest if using targeted feature methodology. + :type: FileStream or String file path + :param iCount: Number of samples to select in each methods, supervised methods select this amount per label if possible. + :type: Integer integer. + :param lstrMethods: List of strings indicating selection techniques. + :type: List of string method names + :param strLabel: The metadata used for supervised labels. + :type: String + :param strStratify: The metadata used to stratify unsupervised data. + :type: String + :param strCustomAlpha: Custom alpha diversity metric + :type: String + :param strCustomBeta: Custom beta diversity metric + :type: String + :param strAlphaMetadata: Metadata id which is a diveristy metric to use in highest diversity sampling + :type: String + :param istmBetaMatrix: File containing precalculated beta-diversity matrix for representative sampling + :type: FileStream or String file path + :param istrmTree: File containing tree for phylogentic beta-diversity analysis + :type: FileStream or String file path + :param istrmEnvr: File containing environment for phylogentic beta-diversity analysis + :type: FileStream or String file path + :param iMinSeqs: Minimum sequence in the occurence filter which filters all features not with a minimum number of sequences in each of a minimum number of samples. + :type: Integer + :param iMinSamples: Minimum sample count for the occurence filter. + :type: Integer + :param fInvertDiversity: When true will invert diversity measurements before using. + :type: boolean + :return Selected Samples: Samples selected by methods. + Dictionary {"Selection Method":["SampleID","SampleID","SampleID",...]} + """ + + #Holds the top ranked samples from different metrics + #dict[metric name] = [samplename,samplename...] + selectedSamples = dict() + + #If a target feature file is given make sure that targeted feature is in the selection methods, if not add + if ConstantsMicropita.c_strFeature in lstrMethods: + if not istmFeatures: + logging.error("MicroPITA.funcRun:: Did not receive both the Targeted feature file and the feature selection method. MicroPITA did not run.") + return False + + #Diversity metrics to run + #Use custom metrics if specified + #Custom beta metrics set to normalized only, custom alpha metrics set to count only + diversityMetricsAlpha = [] if strCustomAlpha or strAlphaMetadata else [MicroPITA.c_strInverseSimpsonDiversity] + diversityMetricsBeta = [] if istmBetaMatrix else [strCustomBeta] if strCustomBeta else [MicroPITA.c_strBrayCurtisDissimilarity] +# inverseDiversityMetricsBeta = [MicroPITA.c_strInvBrayCurtisDissimilarity] + diversityMetricsAlphaNoNormalize = [strAlphaMetadata] if strAlphaMetadata else [strCustomAlpha] if strCustomAlpha else [] + diversityMetricsBetaNoNormalize = [] +# inverseDiversityMetricsBetaNoNormalize = [] + + #Targeted taxa + userDefinedTaxa = [] + + #Perform different flows flags + c_RUN_MAX_DIVERSITY_1 = ConstantsMicropita.c_strDiversity in lstrMethods + c_RUN_REPRESENTIVE_DISSIMILARITY_2 = ConstantsMicropita.c_strRepresentative in lstrMethods + c_RUN_MAX_DISSIMILARITY_3 = ConstantsMicropita.c_strExtreme in lstrMethods + c_RUN_RANK_AVERAGE_USER_4 = False + if ConstantsMicropita.c_strFeature in lstrMethods: + c_RUN_RANK_AVERAGE_USER_4 = True + if not istmFeatures: + logging.error("MicroPITA.funcRun:: No taxa file was given for taxa selection.") + return False + #Read in taxa list, break down to lines and filter out empty strings + userDefinedTaxa = filter(None,(s.strip( ) for s in istmFeatures.readlines())) + c_RUN_RANDOM_5 = ConstantsMicropita.c_strRandom in lstrMethods + c_RUN_DISTINCT = ConstantsMicropita.c_strDistinct in lstrMethods + c_RUN_DISCRIMINANT = ConstantsMicropita.c_strDiscriminant in lstrMethods + + #Read in abundance data + #Abundance is a structured array. Samples (column) by Taxa (rows) with the taxa id row included as the column index=0 + #Abundance table object to read in and manage data + totalAbundanceTable = AbundanceTable.funcMakeFromFile(xInputFile=istmInput, lOccurenceFilter = [iMinSeqs, iMinSamples], + cDelimiter=cDelimiter, sMetadataID=strIDName, sLastMetadataRow=strLastRowMetadata, + sLastMetadata=strLastMetadataName, cFeatureNameDelimiter=cFeatureNameDelimiter, xOutputFile=ostmCheckedFile) + if not totalAbundanceTable: + logging.error("MicroPITA.funcRun:: Could not read in the abundance table. Analysis was not performed."+ + " This often occurs when the Last Metadata is not specified correctly."+ + " Please check to make sure the Last Metadata selection is the row of the last metadata,"+ + " all values after this selection should be microbial measurements and should be numeric.") + return False + + lsOriginalLabels = SVM.funcMakeLabels(totalAbundanceTable.funcGetMetadata(strLabel)) if strLabel else strLabel + + dictTotalMetadata = totalAbundanceTable.funcGetMetadataCopy() + logging.debug("MicroPITA.funcRun:: Received metadata=" + str(dictTotalMetadata)) + #If there is only 1 unique value for the labels, do not run the Supervised methods + if strLabel and ( len(set(dictTotalMetadata.get(strLabel,[]))) < 2 ): + logging.error("The label " + strLabel + " did not have 2 or more values. Labels found=" + str(dictTotalMetadata.get(strLabel,[]))) + return False + + #Run unsupervised methods### + #Stratify the data if need be and drop the old data + lStratifiedAbundanceTables = totalAbundanceTable.funcStratifyByMetadata(strStratify) if strStratify else [totalAbundanceTable] + + #For each stratified abundance block or for the unstratfified abundance + #Run the unsupervised blocks + fAppendSupFiles = False + for stratAbundanceTable in lStratifiedAbundanceTables: + logging.info("MicroPITA.funcRun:: Running abundance block:"+stratAbundanceTable.funcGetName()) + + ###NOT SUMMED, NOT NORMALIZED + #Only perform if the data is not yet normalized + if not stratAbundanceTable.funcIsNormalized( ): + #Need to first work with unnormalized data + if c_RUN_MAX_DIVERSITY_1 or c_RUN_REPRESENTIVE_DISSIMILARITY_2 or c_RUN_MAX_DISSIMILARITY_3: + + self._funcRunNormalizeSensitiveMethods(abndData=stratAbundanceTable, iSampleSelectionCount=iCount, + dictSelectedSamples=selectedSamples, lsAlphaMetrics=diversityMetricsAlphaNoNormalize, + lsBetaMetrics=diversityMetricsBetaNoNormalize, + lsInverseBetaMetrics=diversityMetricsBetaNoNormalize, + fRunDiversity=c_RUN_MAX_DIVERSITY_1,fRunRepresentative=c_RUN_REPRESENTIVE_DISSIMILARITY_2, + fRunExtreme=c_RUN_MAX_DISSIMILARITY_3, strAlphaMetadata=strAlphaMetadata, + istrmTree=istrmTree, istrmEnvr=istrmEnvr, fInvertDiversity=fInvertDiversity) + + + #Generate selection by the rank average of user defined taxa + #Expects (Taxa (row) by Samples (column)) + #Expects a column 0 of taxa id that is skipped + #Returns [(sample name,average,rank)] + #SUMMED AND NORMALIZED + stratAbundanceTable.funcSumClades() + #Normalize data at this point + stratAbundanceTable.funcNormalize() + if c_RUN_RANK_AVERAGE_USER_4: + selectedSamples[ConstantsMicropita.c_strFeature] = self.funcSelectTargetedTaxaSamples(abndMatrix=stratAbundanceTable, + lsTargetedTaxa=userDefinedTaxa, iSampleSelectionCount=iCount, sMethod=strFeatureSelection) + logging.info("MicroPITA.funcRun:: Selected Samples Rank") + logging.info(selectedSamples) + + ###SUMMED AND NORMALIZED analysis block + #Diversity based metric will move reduce to terminal taxa as needed + if c_RUN_MAX_DIVERSITY_1 or c_RUN_REPRESENTIVE_DISSIMILARITY_2 or c_RUN_MAX_DISSIMILARITY_3: + + self._funcRunNormalizeSensitiveMethods(abndData=stratAbundanceTable, iSampleSelectionCount=iCount, + dictSelectedSamples=selectedSamples, lsAlphaMetrics=diversityMetricsAlpha, + lsBetaMetrics=diversityMetricsBeta, + lsInverseBetaMetrics=diversityMetricsBeta, + fRunDiversity=c_RUN_MAX_DIVERSITY_1,fRunRepresentative=c_RUN_REPRESENTIVE_DISSIMILARITY_2, + fRunExtreme=c_RUN_MAX_DISSIMILARITY_3, + istmBetaMatrix=istmBetaMatrix, istrmTree=istrmTree, istrmEnvr=istrmEnvr, fInvertDiversity=fInvertDiversity) + + #5::Select randomly + #Expects sampleNames = List of sample names [name, name, name...] + if(c_RUN_RANDOM_5): + #Select randomly from sample names + selectedSamples[ConstantsMicropita.c_strRandom] = self.funcGetRandomSamples(lsSamples=stratAbundanceTable.funcGetSampleNames(), iNumberOfSamplesToReturn=iCount) + logging.info("MicroPITA.funcRun:: Selected Samples Random") + logging.info(selectedSamples) + + #Perform supervised selection + if c_RUN_DISTINCT or c_RUN_DISCRIMINANT: + if strLabel: + dictSelectionRet = self.funcRunSupervisedDistancesFromCentroids(abundanceTable=stratAbundanceTable, + fRunDistinct=c_RUN_DISTINCT, fRunDiscriminant=c_RUN_DISCRIMINANT, + xOutputSupFile=ostmInputPredictFile,xPredictSupFile=ostmPredictFile, + strSupervisedMetadata=strLabel, iSampleSupSelectionCount=iCount, + lsOriginalSampleNames = totalAbundanceTable.funcGetSampleNames(), + lsOriginalLabels = lsOriginalLabels, + fAppendFiles=fAppendSupFiles) + + [selectedSamples.setdefault(sKey,[]).extend(lValue) for sKey,lValue in dictSelectionRet.items()] + + if not fAppendSupFiles: + fAppendSupFiles = True + logging.info("MicroPITA.funcRun:: Selected Samples Unsupervised") + logging.info(selectedSamples) + return selectedSamples + + #Testing: Happy path tested + @staticmethod + def funcWriteSelectionToFile(dictSelection,xOutputFilePath): + """ + Writes the selection of samples by method to an output file. + + :param dictSelection: The dictionary of selections by method to be written to a file. + :type: Dictionary The dictionary of selections by method {"method":["sample selected","sample selected"...]} + :param xOutputFilePath: FileStream or String path to file inwhich the dictionary is written. + :type: String FileStream or String path to file + """ + + if not dictSelection: + return + + #Open file + f = csv.writer(open(xOutputFilePath,"w") if isinstance(xOutputFilePath, str) else xOutputFilePath, delimiter=ConstantsMicropita.c_outputFileDelim ) + + #Create output content from dictionary + for sKey in dictSelection: + f.writerow([sKey]+dictSelection[sKey]) + logging.debug("MicroPITA.funcRun:: Selected samples output to file:"+str(dictSelection[sKey])) + + #Testing: Happy Path tested + @staticmethod + def funcReadSelectionFileToDictionary(xInputFile): + """ + Reads in an output selection file from micropita and formats it into a dictionary. + + :param xInputFile: String path to file or file stream to read and translate into a dictionary. + {"method":["sample selected","sample selected"...]} + :type: FileStream or String Path to file + :return Dictionary: Samples selected by methods. + Dictionary {"Selection Method":["SampleID","SampleID","SampleID",...]} + """ + + #Open file + istmReader = csv.reader(open(xInputFile,'r') if isinstance(xInputFile, str) else xInputFile, delimiter = ConstantsMicropita.c_outputFileDelim) + + #Dictionary to hold selection data + return dict([(lsLine[0], lsLine[1:]) for lsLine in istmReader]) + +#Set up arguments reader +argp = argparse.ArgumentParser( prog = "MicroPITA.py", + description = """Selects samples from abundance tables based on various selection schemes.""" ) + +args = argp.add_argument_group( "Common", "Commonly modified options" ) +args.add_argument(ConstantsMicropita.c_strCountArgument,"--num", dest="iCount", metavar = "samples", default = 10, type = int, help = ConstantsMicropita.c_strCountHelp) +args.add_argument("-m","--method", dest = "lstrMethods", metavar = "method", default = [], help = ConstantsMicropita.c_strSelectionTechniquesHelp, + choices = ConstantsMicropita.c_lsAllMethods, action = "append") + +args = argp.add_argument_group( "Custom", "Selecting and inputing custom metrics" ) +args.add_argument("-a","--alpha", dest = "strAlphaDiversity", metavar = "AlphaDiversity", default = None, help = ConstantsMicropita.c_strCustomAlphaDiversityHelp, choices = Metric.setAlphaDiversities) +args.add_argument("-b","--beta", dest = "strBetaDiversity", metavar = "BetaDiversity", default = None, help = ConstantsMicropita.c_strCustomBetaDiversityHelp, choices = list(Metric.setBetaDiversities)+[Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]) +args.add_argument("-q","--alphameta", dest = "strAlphaMetadata", metavar = "AlphaDiversityMetadata", default = None, help = ConstantsMicropita.c_strCustomAlphaDiversityMetadataHelp) +args.add_argument("-x","--betamatrix", dest = "istmBetaMatrix", metavar = "BetaDiversityMatrix", default = None, help = ConstantsMicropita.c_strCustomBetaDiversityMatrixHelp) +args.add_argument("-o","--tree", dest = "istrmTree", metavar = "PhylogeneticTree", default = None, help = ConstantsMicropita.c_strCustomPhylogeneticTreeHelp) +args.add_argument("-i","--envr", dest = "istrmEnvr", metavar = "EnvironmentFile", default = None, help = ConstantsMicropita.c_strCustomEnvironmentFileHelp) +args.add_argument("-f","--invertDiversity", dest = "fInvertDiversity", action="store_true", default = False, help = ConstantsMicropita.c_strInvertDiversityHelp) + +args = argp.add_argument_group( "Miscellaneous", "Row/column identifiers and feature targeting options" ) +args.add_argument("-d",ConstantsMicropita.c_strIDNameArgument, dest="strIDName", metavar="sample_id", help= ConstantsMicropita.c_strIDNameHelp) +args.add_argument("-l",ConstantsMicropita.c_strLastMetadataNameArgument, dest="strLastMetadataName", metavar = "metadata_id", default = None, + help= ConstantsMicropita.c_strLastMetadataNameHelp) +args.add_argument("-r",ConstantsMicropita.c_strTargetedFeatureMethodArgument, dest="strFeatureSelection", metavar="targeting_method", default=ConstantsMicropita.lsTargetedFeatureMethodValues[0], + choices=ConstantsMicropita.lsTargetedFeatureMethodValues, help= ConstantsMicropita.c_strTargetedFeatureMethodHelp) +args.add_argument("-t",ConstantsMicropita.c_strTargetedSelectionFileArgument, dest="istmFeatures", metavar="feature_file", type=argparse.FileType("rU"), help=ConstantsMicropita.c_strTargetedSelectionFileHelp) +args.add_argument("-w",ConstantsMicropita.c_strFeatureMetadataArgument, dest="strLastFeatureMetadata", metavar="Last_Feature_Metadata", default=None, help=ConstantsMicropita.c_strFeatureMetadataHelp) + +args = argp.add_argument_group( "Data labeling", "Metadata IDs for strata and supervised label values" ) +args.add_argument("-e",ConstantsMicropita.c_strSupervisedLabelArgument, dest="strLabel", metavar= "supervised_id", help=ConstantsMicropita.c_strSupervisedLabelHelp) +args.add_argument("-s",ConstantsMicropita.c_strUnsupervisedStratifyMetadataArgument, dest="strUnsupervisedStratify", metavar="stratify_id", + help= ConstantsMicropita.c_strUnsupervisedStratifyMetadataHelp) + +args = argp.add_argument_group( "File formatting", "Rarely modified file formatting options" ) +args.add_argument("-j",ConstantsMicropita.c_strFileDelimiterArgument, dest="cFileDelimiter", metavar="column_delimiter", default="\t", help=ConstantsMicropita.c_strFileDelimiterHelp) +args.add_argument("-k",ConstantsMicropita.c_strFeatureNameDelimiterArgument, dest="cFeatureNameDelimiter", metavar="taxonomy_delimiter", default="|", help=ConstantsMicropita.c_strFeatureNameDelimiterHelp) + +args = argp.add_argument_group( "Debugging", "Debugging options - modify at your own risk!" ) +args.add_argument("-v",ConstantsMicropita.c_strLoggingArgument, dest="strLogLevel", metavar = "log_level", default="WARNING", + choices=ConstantsMicropita.c_lsLoggingChoices, help= ConstantsMicropita.c_strLoggingHelp) +args.add_argument("-c",ConstantsMicropita.c_strCheckedAbundanceFileArgument, dest="ostmCheckedFile", metavar = "output_qc", type = argparse.FileType("w"), help = ConstantsMicropita.c_strCheckedAbundanceFileHelp) +args.add_argument("-g",ConstantsMicropita.c_strLoggingFileArgument, dest="ostmLoggingFile", metavar = "output_log", type = argparse.FileType("w"), help = ConstantsMicropita.c_strLoggingFileHelp) +args.add_argument("-u",ConstantsMicropita.c_strSupervisedInputFile, dest="ostmInputPredictFile", metavar = "output_scaled", type = argparse.FileType("w"), help = ConstantsMicropita.c_strSupervisedInputFileHelp) +args.add_argument("-p",ConstantsMicropita.c_strSupervisedPredictedFile, dest="ostmPredictFile", metavar = "output_labels", type = argparse.FileType("w"), help = ConstantsMicropita.c_strSupervisedPredictedFileHelp) + +argp.add_argument("istmInput", metavar = "input.pcl/biome", type = argparse.FileType("rU"), help = ConstantsMicropita.c_strAbundanceFileHelp, + default = sys.stdin) +argp.add_argument("ostmOutput", metavar = "output.txt", type = argparse.FileType("w"), help = ConstantsMicropita.c_strGenericOutputDataFileHelp, + default = sys.stdout) + +__doc__ = "::\n\n\t" + argp.format_help( ).replace( "\n", "\n\t" ) + __doc__ + +def _main( ): + args = argp.parse_args( ) + + #Set up logger + iLogLevel = getattr(logging, args.strLogLevel.upper(), None) + logging.basicConfig(stream = args.ostmLoggingFile if args.ostmLoggingFile else sys.stderr, filemode = 'w', level=iLogLevel) + + #Run micropita + logging.info("MicroPITA:: Start microPITA") + microPITA = MicroPITA() + + #Argparse will append to the default but will not remove the default so I do this here + if not len(args.lstrMethods): + args.lstrMethods = [ConstantsMicropita.c_strRepresentative] + + dictSelectedSamples = microPITA.funcRun( + strIDName = args.strIDName, + strLastMetadataName = args.strLastMetadataName, + istmInput = args.istmInput, + ostmInputPredictFile = args.ostmInputPredictFile, + ostmPredictFile = args.ostmPredictFile, + ostmCheckedFile = args.ostmCheckedFile, + ostmOutput = args.ostmOutput, + cDelimiter = args.cFileDelimiter, + cFeatureNameDelimiter = args.cFeatureNameDelimiter, + istmFeatures = args.istmFeatures, + strFeatureSelection = args.strFeatureSelection, + iCount = args.iCount, + strLastRowMetadata = args.strLastFeatureMetadata, + strLabel = args.strLabel, + strStratify = args.strUnsupervisedStratify, + strCustomAlpha = args.strAlphaDiversity, + strCustomBeta = args.strBetaDiversity, + strAlphaMetadata = args.strAlphaMetadata, + istmBetaMatrix = args.istmBetaMatrix, + istrmTree = args.istrmTree, + istrmEnvr = args.istrmEnvr, + lstrMethods = args.lstrMethods, + fInvertDiversity = args.fInvertDiversity + ) + + if not dictSelectedSamples: + logging.error("MicroPITA:: Error, did not get a result from analysis.") + return -1 + logging.info("End microPITA") + + #Log output for debugging + logging.debug("MicroPITA:: Returned the following samples:"+str(dictSelectedSamples)) + + #Write selection to file + microPITA.funcWriteSelectionToFile(dictSelection=dictSelectedSamples, xOutputFilePath=args.ostmOutput) + +if __name__ == "__main__": + _main( ) diff -r 000000000000 -r 0de566f21448 README.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.txt Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,185 @@ +# Using microPITA commandline # + +These common commands can be used on the default data set obtained when downloading microPITA, simply cut and paste them into a commandline in the downloaded microPITA directory. + + +## Expected input file. ## + +I. PCL file or BIOM file + +BIOM file definition: +For BIOM file definition please see http://biom-format.org/ + +PCL file definition: +Although some defaults can be changed, microPITA expects a PCL file as an input file. Several PCL files are supplied by default in the input directory. A PCL file is a TEXT delimited file similar to an excel spread sheet with the following characteristics. + +1. Rows represent metadata and features (bugs), columns represent samples. +2. The first row by default should be the sample ids. +3. Metadata rows should be next. +4. Lastly, rows containing features (bugs) measurements (like abundance) should be after metadata rows. +5. The first column should contain the ID describing the column. For metadata this may be, for example, "Age" for a row containing the age of the patients donating the samples. For measurements, this should be the feature name (bug name). +5. By default the file is expected to be TAB delimited. +6. If a consensus lineage or hierarchy of taxonomy is contained in the feature name, the default delimiter between clades is the pipe ("|"). + +II. Targeted feature file +If using the targeted feature methodology, you will need to provide a txt file listing the feature(s) of interest. Each feature should be on it's own line and should be written as found in the input PCL file. + + +## Basic unsupervised methods ## +Please note, all calls to microPITA should work interchangeably with PCL or BIOM files. BIOM files do not require the --lastmeta or --id arguments. + +There are four unsupervised methods which can be performed: +diverse (maximum diversity), extreme (most dissimilar), representative (representative dissimilarity) and features (targeted feature). + +The first three methods are performed as follows (selecting a default 10 samples): + +> python MicroPITA.py --lastmeta Label -m representative input/Test.pcl output.txt +> python MicroPITA.py -m representative input/Test.biom output.txt + +> python MicroPITA.py --lastmeta Label -m diverse input/Test.pcl output.txt +> python MicroPITA.py -m diverse input/Test.biom output.txt + +> python MicroPITA.py --lastmeta Label -m extreme input/Test.pcl output.txt +> python MicroPITA.py -m extreme input/Test.biom output.txt + +Each of the previous methods are made up of the following pieces: +1. python MicroPITA.py to call the microPITA script. +2. --lastmeta which indicates the keyword (first column value) of the last row that contains metadata (PCL input only). +3. -m which indicates the method to use in selection. +4. input/Test.pcl or input/Test.biom which is the first positional argument indicating an input file +5. output.txt which is the second positional argument indicating the location to write to the output file. + +Selecting specific features has additional arguments to consider --targets (required) and --feature_method (optional). + +> python MicroPITA.py --lastmeta Label -m features --targets input/TestFeatures.taxa input/Test.pcl output.txt +> python MicroPITA.py -m features --targets input/TestFeatures.taxa input/Test.biom output.txt + +> python MicroPITA.py --lastmeta Label -m features --feature_method abundance --targets input/TestFeatures.taxa input/Test.pcl output.txt +> python MicroPITA.py -m features --feature_method abundance --targets input/TestFeatures.taxa input/Test.biom output.txt + +These additional arguments are described as: +1. --targets The path to the file that has the features (bugs or clades) of interest. Make sure they are written as they appear in your input file! +2. --feature_method is the method of selection used and can be based on ranked abundance ("rank") or abundance ("abundance"). The default value is rank. +To differentiate the methods, rank tends to select samples in which the feature dominates the samples regardless of it's abundance. +Abundance tends to select samples in which the feature is most abundant without a guarantee that the feature is the most abundant feature in the sample. + + +## Basic supervised methods ## + +Two supervised methods are also available: +distinct and discriminant + +These methods require an additional argument --label which is the first column keyword of the row used to classify samples for the supervised methods. +These methods can be performed as follows: + +> python MicroPITA.py --lastmeta Label --label Label -m distinct input/Test.pcl output.txt +> python MicroPITA.py --label Label -m distinct input/Test.biom output.txt + +> python MicroPITA.py --lastmeta Label --label Label -m discriminant input/Test.pcl output.txt +> python MicroPITA.py --label Label -m discriminant input/Test.biom output.txt + + +## Custom alpha- and beta-diversities ## + +The default alpha diversity for the maximum diversity sampling method is inverse simpson; the default beta-diversity for representative and most dissimilar +selection is bray-curtis dissimilarity. There are several mechanisms that allow one to change this. You may: + +1. Choose from a selection of alpha-diveristy metrics. +Note when supplying an alpha diversity. This will affect the maximum diveristy sampling method only. Please make sure to use a diversity metric where the larger number indicates a higher diversity. If this is not the case make sure to use the -f or --invertDiversity flag to invert the metric. The inversion is multiplicative (1/alpha-metric). + +> python MicroPITA.py --lastmeta Label -m diverse -a simpson input/Test.pcl output.txt +> python MicroPITA.py -m diverse -a simpson input/Test.biom output.txt + +A case where inverting the metric is needed. + +> python MicroPITA.py --lastmeta Label -m diverse -a dominance -f input/Test.pcl output.txt +> python MicroPITA.py -m diverse -a dominance -f input/Test.biom output.txt + +2. Choose from a selection of beta-diversity metrics. +Note when supplying a beta-diversity. This will effect both the representative and most dissimilar sampling methods. The metric as given will be used for the representative method while 1-beta-metric is used for the most dissimilar. + +> python MicroPITA.py --lastmeta Label -m representative -b euclidean input/Test.pcl output.txt +> python MicroPITA.py -m representative -b euclidean input/Test.biom output.txt + +> python MicroPITA.py --lastmeta Label -m extreme -b euclidean input/Test.pcl output.txt +> python MicroPITA.py -m extreme -b euclidean input/Test.biom output.txt + +Note for using Unifrac. Both Weighted and Unweighted unifrac are available for use. Make sure to supply the associated tree (-o, --tree) and environment files +(-i,--envr) as well as indicate using Unifrac with (-b,--beta) + +> python MicroPITA.py --lastmeta Label -m extreme -b unifrac_weighted -o input/Test.tree -i input/Test-env.txt input/Test.pcl output.txt +> python MicroPITA.py -m extreme -b unifrac_weighted -o input/Test.tree -i input/Test-env.txt input/Test.biom output.txt +> python MicroPITA.py --lastmeta Label -m extreme -b unifrac_unweighted -o input/Test.tree -i input/Test-env.txt input/Test.pcl output.txt +> python MicroPITA.py -m extreme -b unifrac_unweighted -o input/Test.tree -i input/Test-env.txt input/Test.biom output.txt +> python MicroPITA.py --lastmeta Label -m representative -b unifrac_weighted -o input/Test.tree -i input/Test-env.txt input/Test.pcl output.txt +> python MicroPITA.py -m representative -b unifrac_weighted -o input/Test.tree -i input/Test-env.txt input/Test.biom output.txt +> python MicroPITA.py --lastmeta Label -m representative -b unifrac_unweighted -o input/Test.tree -i input/Test-env.txt input/Test.pcl output.txt +> python MicroPITA.py -m representative -b unifrac_unweighted -o input/Test.tree -i input/Test-env.txt input/Test.biom output.txt + +3. Supply your own custom alpha-diversity per sample as a metadata (row) in your pcl file. + +> python MicroPITA.py --lastmeta Label -m diverse -q alpha_custom input/Test.pcl output.txt +> python MicroPITA.py -m diverse -q alpha_custom input/Test2.biom output.txt + +4. Supply your own custom beta diversity as a matrix (provided in a seperate file). + +> python MicroPITA.py --lastmeta Label -m representative -x input/Test_Matrix.txt input/Test.pcl output.txt +> python MicroPITA.py -m representative -x input/Test_Matrix.txt input/Test.biom output.txt +> python MicroPITA.py --lastmeta Label -m extreme -x input/Test_Matrix.txt input/Test.pcl output.txt +> python MicroPITA.py -m extreme -x input/Test_Matrix.txt input/Test.biom output.txt + + +## Changing defaults ## + +Sample Selection: +To change the number of selected samples for any method use the -n argument. This example selects 6 representative samples instead of the default 10. + +> python MicroPITA.py --lastmeta Label -m representative -n 6 input/Test.pcl output.txt +> python MicroPITA.py -m representative -n 6 input/Test.biom output.txt + +When using a supervised method this indicates how many samples will be selected per class of sample. For example if you are performing supervised selection of 6 samples (-n 6) on a dataset with 2 classes (values) in it's label row, you will get 6 x 2 = 12 samples. If a class does not have 6 samples in it, you will get the max possible for that class. In a scenario where you are selecting 6 samples (-n 6) and have two classes but one class has only 3 samples then you will get 6 + 3 = 9 selected samples. + +Stratification: +To stratify any method use the --stratify argument which is the first column keyword of the metadata row used to stratify samples before selection occurs. (Selection will occur independently within each strata). This example stratifies diverse selection by the "Label". + +> python MicroPITA.py --lastmeta Label --stratify Label -m representative input/Test.pcl output.txt +> python MicroPITA.py --stratify Label -m representative input/Test.biom output.txt + +> python MicroPITA.py --lastmeta Label --label Label --stratify StratifyLabel -m distinct input/Test.pcl output.txt +> python MicroPITA.py --label Label --stratify StratifyLabel -m distinct input/Test2.biom output.txt + +Changing PCL file defaults: +Some PCL files have feature metadata. These are columns of data that comment on bug features (rows) in the file. An example of this could be a certain taxonomy clade for different bug features. If this type of data exists please use -w or --lastFeatureMetadata to indicate the last column of feature metadata before the first column which is a sample. For an example please look in docs for PCL-Description.txt. This only applys to PCL files. + +> python MicroPITA.py --lastmeta Label -m representative -w taxonomy_5 input/FeatureMetadata.pcl output.txt + +MicroPITA assumes the first row of the input file is the sample IDs, if it is not you may use --id to indicate the row. +--id expects the entry in the first column of your input file that matches the row used as Sample Ids. See the input file and the following command as an example. +This only applys to PCL files. + +> python MicroPITA.py --id Sample --lastmeta Label -m representative input/Test.pcl output.txt + +MicroPITA assumes the input file is TAB delimited, we strongly recommend you use this convention. If not, you can use --delim to change the delimiter used to read in the file. +Here is an example of reading the comma delimited file micropita/input/CommaDelim.pcl +This only applys to PCL files. + +> python MicroPITA.py --delim , --lastmeta Label -m representative input/CommaDelim.pcl output.txt + +MicroPITA assumes the input file has feature names in which, if the name contains the consensus lineage or full taxonomic hierarchy, it is delimited with a pipe "|". We strongly recommend you use this default. The delimiter of the feature name can be changed using --featdelim. Here is an example of reading in a file with periods as the delimiter. +This only applys to PCL files. + +> python MicroPITA.py --featdelim . --lastmeta Label -m representative input/PeriodDelim.pcl output.txt + + +## Dependencies ## +Please note the following dependencies need to be installed for micropita to run. +1. Python 2.x http://www.python.org/download/ +2. blist http://pypi.python.org/pypi/blist/ +3. NumPy http://numpy.scipy.org/ +4. SciPy http://www.scipy.org/ +5. PyCogent http://pycogent.sourceforge.net/install.html +6. mlpy http://mlpy.sourceforge.net/ +7. mpi4py http://mpi4py.scipy.org/ +8. biome support http://biom-format.org/ + +This covers how to use microPITA. Thank you for using this software and good luck with all your endeavors! diff -r 000000000000 -r 0de566f21448 __init__.py diff -r 000000000000 -r 0de566f21448 datatypes_conf.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/datatypes_conf.xml Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,6 @@ + + + + + + diff -r 000000000000 -r 0de566f21448 input/FeatureMetadata.pcl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/input/FeatureMetadata.pcl Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,8 @@ +ID taxonomy_0 taxonomy_1 taxonomy_2 taxonomy_3 taxonomy_4 taxonomy_5 70009986 70009894 70008980 70009988 70003470 70007472 70003744 70007476 70003274 70476 +TID NA NA NA NA NA NA CohortA CohortA CohortB CohortB CohortB CohortA CohortA CohortB CohortA CohortB +Label NA NA NA NA NA NA L_Antecubital_fossa R_Retroauricular_crease Subgingival_plaque R_Antecubital_fossa L_Retroauricular_crease R_Retroauricular_crease L_Antecubital_fossa R_Antecubital_fossa L_Antecubital_fossa R_Antecubital_fossa +72 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 1.23 0 12 0 6 0 2 1 2 1 +4904 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 0 10 43 6 0 23 0 6 5 53 +1361 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 3 0 29 0 45 0 1 1 1 3 +3417 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 0 45 34 3 0 0 0 4 0 3 +1368 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 5 0 2 0 6 0 1 1 3 1 diff -r 000000000000 -r 0de566f21448 input/Test.biom --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/input/Test.biom Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +{"id": "None","format": "Biological Observation Matrix 1.0.0","format_url": "http://biom-format.org","type": "OTU table","generated_by": "BreadCrumbs","date": "2013-10-07T16:48:52.647220","matrix_type": "sparse","matrix_element_type": "float","shape": [224, 48],"data": [[0,1,0.13263911008834839],[0,2,51.672050476074219],[0,6,51.744468688964844],[0,12,51.208335876464844],[0,13,52.149501800537109],[0,15,54.280982971191406],[0,16,51.682929992675781],[0,18,0.31236764788627625],[0,24,0.21669530868530273],[0,26,0.2411828488111496],[0,33,0.31222966313362122],[0,47,0.17723028361797333],[1,0,54.099388122558594],[1,2,0.44196206331253052],[1,6,50.664783477783203],[1,7,53.447223663330078],[1,8,50.581748962402344],[1,10,53.541297912597656],[1,16,54.532711029052734],[1,28,0.30667436122894287],[1,30,0.4312838613986969],[1,32,0.37312093377113342],[1,45,0.12748651206493378],[2,0,51.461025238037109],[2,5,53.026569366455078],[2,10,54.047443389892578],[2,12,52.526435852050781],[2,16,54.940284729003906],[2,32,0.42233455181121826],[2,37,0.460744708776474],[2,39,0.34424543380737305],[3,16,53.750659942626953],[3,18,0.41481569409370422],[3,22,0.26857671141624451],[3,28,0.36630621552467346],[3,44,50.526988983154297],[3,45,51.854343414306641],[3,46,53.768180847167969],[3,47,52.634426116943359],[4,0,50.240570068359375],[4,1,51.777767181396484],[4,3,54.845878601074219],[4,11,53.880828857421875],[4,16,50.028224945068359],[4,20,0.098072387278079987],[4,22,0.38868135213851929],[4,28,0.4286598265171051],[4,32,0.47316429018974304],[4,37,0.15683920681476593],[5,5,51.951015472412109],[5,6,0.22961589694023132],[5,7,52.969863891601562],[5,11,52.197437286376953],[5,14,0.22526906430721283],[5,16,53.653339385986328],[5,25,0.39630568027496338],[5,28,0.06783304363489151],[6,5,54.567390441894531],[6,10,51.190956115722656],[6,11,0.25222322344779968],[6,13,52.400714874267578],[6,14,54.537200927734375],[6,16,51.323921203613281],[6,29,0.17403842508792877],[6,41,0.052579630166292191],[7,2,54.889991760253906],[7,3,50.592502593994141],[7,7,54.676334381103516],[7,8,0.11892234534025192],[7,9,50.23931884765625],[7,16,54.696922302246094],[7,19,0.26401954889297485],[7,25,0.093931779265403748],[7,29,0.0068426746875047684],[7,33,0.18845303356647491],[8,0,50.167209625244141],[8,2,52.330379486083984],[8,7,0.26085031032562256],[8,8,51.234970092773438],[8,9,53.837917327880859],[8,16,50.158138275146484],[8,17,0.00067086971830576658],[8,30,102.35082244873047],[8,37,0.25920611619949341],[8,38,0.27336984872817993],[9,2,54.295417785644531],[9,3,0.39004826545715332],[9,4,53.497718811035156],[9,6,52.853614807128906],[9,13,53.070518493652344],[9,14,53.997322082519531],[9,15,52.567996978759766],[9,16,52.433670043945312],[9,18,0.44207152724266052],[9,26,0.20625883340835571],[9,28,0.22174246609210968],[9,30,102.36073303222656],[10,1,52.030044555664062],[10,4,50.834751129150391],[10,6,54.687831878662109],[10,8,52.774848937988281],[10,10,0.17284990847110748],[10,13,0.34940233826637268],[10,15,53.292324066162109],[10,16,52.094409942626953],[10,30,103.40880584716797],[10,39,0.2798474133014679],[11,5,50.226642608642578],[11,10,54.800552368164062],[11,13,0.26145485043525696],[11,14,54.634014129638672],[11,16,53.305400848388672],[11,30,104.29749298095703],[11,31,0.41477712988853455],[11,43,0.2095019668340683],[11,44,0.2636646032333374],[11,46,0.14962244033813477],[12,1,51.245124816894531],[12,4,52.189498901367188],[12,8,0.14854021370410919],[12,11,51.457836151123047],[12,15,53.714405059814453],[12,16,52.488780975341797],[12,18,0.49128276109695435],[12,21,0.040838729590177536],[12,23,0.12988743185997009],[12,28,0.40605998039245605],[12,30,100.32480621337891],[12,45,0.11192221939563751],[13,0,0.30497664213180542],[13,5,0.25770536065101624],[13,7,0.033960685133934021],[13,8,54.769084930419922],[13,9,50.697971343994141],[13,12,53.864070892333984],[13,14,52.003578186035156],[13,16,53.642868041992188],[13,24,0.42540961503982544],[13,25,0.22494150698184967],[13,27,0.18784832954406738],[13,30,102.10746002197266],[14,1,52.225990295410156],[14,3,50.245712280273438],[14,7,50.186416625976562],[14,16,50.970901489257812],[14,17,0.24925577640533447],[14,30,101.39744567871094],[14,43,0.15557312965393066],[15,3,51.428195953369141],[15,4,52.69622802734375],[15,6,0.32013928890228271],[15,9,54.775680541992188],[15,11,50.501811981201172],[15,12,54.193744659423828],[15,13,53.206466674804688],[15,16,54.25494384765625],[15,30,102.28659820556641],[15,38,0.47058898210525513],[16,0,51.002975463867188],[16,4,51.060733795166016],[16,5,53.286212921142578],[16,9,51.913478851318359],[16,10,54.854099273681641],[16,14,54.063098907470703],[16,15,54.535636901855469],[16,17,50.582279205322266],[17,6,0.20488639175891876],[17,17,53.088150024414062],[17,35,0.035023260861635208],[17,39,0.35104057192802429],[18,1,51.990985870361328],[18,2,54.274215698242188],[18,3,53.165328979492188],[18,6,50.877193450927734],[18,7,50.90771484375],[18,8,54.972938537597656],[18,10,0.13535825908184052],[18,13,50.270320892333984],[18,17,53.454654693603516],[18,31,0.19759136438369751],[18,38,0.25250700116157532],[18,47,0.36680755019187927],[19,17,53.208427429199219],[19,18,0.031785517930984497],[19,23,0.073154278099536896],[19,25,0.27936533093452454],[19,29,0.32328686118125916],[19,44,51.327350616455078],[19,45,51.795059204101562],[19,46,51.514873504638672],[19,47,50.144603729248047],[20,3,54.290149688720703],[20,5,52.560630798339844],[20,6,52.373138427734375],[20,7,54.452667236328125],[20,14,52.098415374755859],[20,17,51.751327514648438],[20,21,0.36221933364868164],[20,30,0.16919450461864471],[21,0,50.282546997070312],[21,6,50.982170104980469],[21,11,51.380966186523438],[21,13,52.272113800048828],[21,17,50.870861053466797],[21,20,0.19776757061481476],[21,26,0.4552416205406189],[21,31,0.34358644485473633],[21,34,0.48803713917732239],[21,36,0.053676586598157883],[22,4,52.302909851074219],[22,5,52.598339080810547],[22,6,54.505264282226562],[22,9,54.188888549804688],[22,10,50.026618957519531],[22,11,50.222110748291016],[22,12,50.663322448730469],[22,13,51.357936859130859],[22,15,52.134590148925781],[22,17,53.373260498046875],[22,23,0.3609204888343811],[22,27,0.49932181835174561],[22,38,0.15913538634777069],[23,0,54.615615844726562],[23,1,0.48862737417221069],[23,7,52.510749816894531],[23,8,52.946086883544922],[23,10,0.39570984244346619],[23,17,52.653926849365234],[23,29,0.23033072054386139],[23,30,0.23117353022098541],[23,47,0.063020221889019012],[24,0,52.526584625244141],[24,7,51.618122100830078],[24,9,52.527744293212891],[24,10,52.562164306640625],[24,11,0.42123815417289734],[24,17,50.036830902099609],[24,31,102.75434875488281],[25,7,0.13728067278862],[25,11,52.016689300537109],[25,17,53.928443908691406],[25,21,0.21439807116985321],[25,30,0.40063384175300598],[25,31,100.17539978027344],[25,39,0.46024471521377563],[26,1,54.780891418457031],[26,7,0.2249763160943985],[26,9,0.48445135354995728],[26,14,50.698005676269531],[26,17,51.896354675292969],[26,31,104.48031616210938],[27,1,52.181842803955078],[27,3,0.0091068772599101067],[27,17,52.071174621582031],[27,18,0.34459295868873596],[27,31,101.49213409423828],[27,34,0.23583111166954041],[27,37,0.34782198071479797],[27,42,0.029288522899150848],[28,2,52.090179443359375],[28,3,52.273052215576172],[28,4,50.097866058349609],[28,12,51.104297637939453],[28,13,50.339679718017578],[28,15,51.857917785644531],[28,17,54.147850036621094],[28,23,0.19240042567253113],[28,31,103.86058807373047],[28,36,0.29582729935646057],[28,45,0.24325108528137207],[29,2,50.625392913818359],[29,8,51.463695526123047],[29,17,52.443458557128906],[29,26,0.02061726339161396],[29,28,0.4787711501121521],[29,31,103.66421508789062],[29,37,0.089619070291519165],[29,38,0.048293761909008026],[29,39,0.37758848071098328],[30,3,53.270271301269531],[30,5,50.953044891357422],[30,6,0.39060020446777344],[30,8,52.564964294433594],[30,12,50.534202575683594],[30,15,53.352542877197266],[30,16,0.39554506540298462],[30,17,52.137779235839844],[30,31,104.56910705566406],[30,37,0.07115115225315094],[30,41,0.29691353440284729],[30,46,0.082002714276313782],[31,1,53.416091918945312],[31,2,50.241264343261719],[31,4,52.750377655029297],[31,5,0.26600098609924316],[31,9,51.415676116943359],[31,10,52.349578857421875],[31,11,50.429630279541016],[31,12,53.074653625488281],[31,13,0.23127266764640808],[31,14,50.159145355224609],[31,17,52.674037933349609],[31,18,0.06841190904378891],[31,19,0.35549464821815491],[31,20,0.22106747329235077],[31,29,0.40446197986602783],[31,31,103.68357849121094],[31,35,0.10313024371862411],[32,1,50.575607299804688],[32,4,52.031375885009766],[32,5,53.711055755615234],[32,6,52.390720367431641],[32,7,53.884662628173828],[32,10,0.0046756584197282791],[32,12,52.474929809570312],[32,13,0.23978191614151001],[32,15,54.054447174072266],[32,18,54.835540771484375],[32,19,0.12978900969028473],[32,26,0.45939821004867554],[32,35,0.35066312551498413],[32,36,0.46294641494750977],[33,3,50.878131866455078],[33,4,0.49990585446357727],[33,9,51.351829528808594],[33,10,0.37612366676330566],[33,11,0.39440682530403137],[33,18,50.317455291748047],[33,21,0.15044829249382019],[33,23,0.12714003026485443],[33,33,0.25482487678527832],[34,0,53.320610046386719],[34,5,53.359424591064453],[34,6,54.755043029785156],[34,10,0.20374700427055359],[34,11,54.108608245849609],[34,18,54.111351013183594],[34,19,0.31384673714637756],[34,31,0.34905159473419189],[34,36,0.19817408919334412],[34,39,0.12082114070653915],[35,0,51.574825286865234],[35,1,51.855949401855469],[35,3,54.080478668212891],[35,10,54.739360809326172],[35,11,53.375148773193359],[35,12,53.000400543212891],[35,13,50.951164245605469],[35,17,0.42627128958702087],[35,18,51.452655792236328],[35,25,0.29797333478927612],[35,28,0.41117364168167114],[35,37,0.068900987505912781],[35,43,0.38085871934890747],[35,44,0.36821377277374268],[35,46,0.34612327814102173],[36,4,51.7955322265625],[36,5,50.7825927734375],[36,6,0.074825257062911987],[36,9,54.952423095703125],[36,11,0.35481184720993042],[36,12,51.870368957519531],[36,15,51.05682373046875],[36,18,53.387546539306641],[36,34,0.052977506071329117],[37,2,52.993377685546875],[37,6,54.690509796142578],[37,8,52.792209625244141],[37,14,50.304485321044922],[37,18,54.747543334960938],[37,25,0.045788772404193878],[37,26,0.38097414374351501],[37,41,0.22798745334148407],[38,2,51.42706298828125],[38,4,54.678600311279297],[38,6,54.820697784423828],[38,8,53.938587188720703],[38,13,51.412956237792969],[38,14,53.126502990722656],[38,18,54.007125854492188],[38,23,0.093990691006183624],[38,24,0.36732301115989685],[38,36,0.47896763682365417],[39,9,0.23879404366016388],[39,18,54.162742614746094],[39,25,0.27959668636322021],[40,3,50.021083831787109],[40,8,0.21509011089801788],[40,13,52.186756134033203],[40,14,53.62646484375],[40,18,52.949188232421875],[40,27,0.25075152516365051],[40,32,100.86255645751953],[40,46,0.21088157594203949],[41,0,51.497703552246094],[41,1,52.916496276855469],[41,2,0.35341593623161316],[41,5,0.11574866622686386],[41,8,51.701892852783203],[41,9,0.13458873331546783],[41,11,51.998493194580078],[41,12,0.30815255641937256],[41,14,0.06155196949839592],[41,18,53.300819396972656],[41,32,104.43079376220703],[41,39,0.28674045205116272],[42,7,50.100700378417969],[42,9,54.946121215820312],[42,18,53.037506103515625],[42,22,0.43193840980529785],[42,24,0.17162227630615234],[42,32,102.19640350341797],[43,18,51.044620513916016],[43,26,0.07927926629781723],[43,32,104.88037109375],[43,44,51.371574401855469],[43,45,52.904411315917969],[43,46,51.740909576416016],[43,47,50.500438690185547],[44,1,53.951423645019531],[44,2,53.797454833984375],[44,5,0.21723970770835876],[44,7,54.460433959960938],[44,10,54.686206817626953],[44,18,53.194450378417969],[44,20,0.17020769417285919],[44,28,0.10002410411834717],[44,31,0.43790563941001892],[44,32,102.22212982177734],[44,41,0.070241943001747131],[45,0,0.44143962860107422],[45,5,53.344242095947266],[45,8,52.043350219726562],[45,18,51.009937286376953],[45,32,102.92622375488281],[45,41,0.008315584622323513],[46,0,50.8759765625],[46,2,53.207527160644531],[46,3,50.674629211425781],[46,4,51.236316680908203],[46,5,0.052973974496126175],[46,9,51.711841583251953],[46,10,52.478534698486328],[46,14,52.866855621337891],[46,15,52.624881744384766],[46,18,53.253269195556641],[46,22,0.20683418214321136],[46,24,0.050955794751644135],[46,32,101.58198547363281],[46,35,0.39298108220100403],[46,40,0.40840697288513184],[47,7,51.7620849609375],[47,10,51.318531036376953],[47,11,50.13214111328125],[47,12,51.764583587646484],[47,13,53.272975921630859],[47,15,53.236888885498047],[47,18,54.633903503417969],[47,24,0.27216443419456482],[47,31,0.1529640406370163],[47,32,102.17061614990234],[47,40,0.48087078332901001],[47,44,0.025993958115577698],[48,0,52.426708221435547],[48,3,54.121410369873047],[48,4,50.092826843261719],[48,6,53.477237701416016],[48,7,54.786056518554688],[48,11,51.969760894775391],[48,16,0.29127556085586548],[48,19,52.4029541015625],[48,32,0.17309793829917908],[49,1,54.141971588134766],[49,5,0.31099069118499756],[49,7,0.2574068009853363],[49,12,51.832080841064453],[49,19,54.683048248291016],[49,20,0.27364835143089294],[49,23,0.15304872393608093],[49,35,0.48234385251998901],[50,1,0.41244181990623474],[50,2,52.669319152832031],[50,4,0.4591352641582489],[50,7,50.00762939453125],[50,16,0.089165627956390381],[50,19,52.060661315917969],[50,34,0.45611995458602905],[51,2,0.42902174592018127],[51,3,0.36514613032341003],[51,5,52.193714141845703],[51,11,54.882003784179688],[51,12,50.988983154296875],[51,19,53.729080200195312],[51,26,0.10773327946662903],[51,32,0.23969066143035889],[51,37,0.12089108675718307],[51,45,0.11706044524908066],[52,0,54.837665557861328],[52,2,50.680797576904297],[52,6,50.242336273193359],[52,9,53.12158203125],[52,12,53.079925537109375],[52,19,51.035022735595703],[52,27,0.21839159727096558],[52,29,0.037748463451862335],[53,0,0.40822666883468628],[53,1,53.888378143310547],[53,2,0.44155597686767578],[53,5,54.638629913330078],[53,8,51.269351959228516],[53,10,54.622165679931641],[53,16,0.30414998531341553],[53,18,0.37247315049171448],[53,19,54.412796020507812],[53,20,0.13671597838401794],[53,41,0.39803388714790344],[54,0,0.32731133699417114],[54,3,54.9263916015625],[54,9,0.30886456370353699],[54,13,0.060517732053995132],[54,15,50.044486999511719],[54,19,54.63714599609375],[54,26,0.47719183564186096],[54,27,0.27686047554016113],[54,37,0.033551629632711411],[55,2,50.065605163574219],[55,5,0.017944198101758957],[55,14,0.1389617919921875],[55,15,54.933082580566406],[55,19,52.576915740966797],[55,27,0.17821782827377319],[55,32,0.34076377749443054],[55,40,0.47821888327598572],[55,41,0.45426815748214722],[55,45,0.48750394582748413],[56,0,54.598953247070312],[56,4,53.242694854736328],[56,5,50.250740051269531],[56,9,53.954669952392578],[56,13,53.889816284179688],[56,14,52.02008056640625],[56,19,50.128131866455078],[56,23,0.32643041014671326],[56,24,0.24274888634681702],[56,32,0.29801353812217712],[56,33,102.34198760986328],[56,41,0.20575700700283051],[56,42,0.19268074631690979],[57,1,51.354415893554688],[57,5,50.455276489257812],[57,8,53.913833618164062],[57,9,53.496509552001953],[57,12,0.44028562307357788],[57,14,50.987522125244141],[57,17,0.28433093428611755],[57,19,52.833759307861328],[57,33,100.00974273681641],[57,45,0.48467403650283813],[58,0,0.43709355592727661],[58,7,53.569564819335938],[58,8,53.592277526855469],[58,10,52.4207763671875],[58,12,0.08282383531332016],[58,13,54.043624877929688],[58,15,54.143932342529297],[58,19,53.610965728759766],[58,22,0.24731944501399994],[58,28,0.12936684489250183],[58,33,102.81724548339844],[59,0,54.187778472900391],[59,1,54.873485565185547],[59,2,51.963199615478516],[59,4,53.789318084716797],[59,11,52.966205596923828],[59,12,0.38438034057617188],[59,19,50.593837738037109],[59,33,103.81158447265625],[59,41,0.16972598433494568],[59,44,0.43379676342010498],[60,3,51.646255493164062],[60,6,52.195060729980469],[60,7,52.214561462402344],[60,8,53.811183929443359],[60,9,54.627727508544922],[60,13,0.40979909896850586],[60,19,54.478790283203125],[60,33,100.14827728271484],[61,5,0.2040732353925705],[61,9,0.27831506729125977],[61,13,50.5350341796875],[61,14,51.030941009521484],[61,19,51.882293701171875],[61,31,0.062283892184495926],[61,32,0.36123722791671753],[61,33,102.43135070800781],[61,38,0.14927451312541962],[61,45,0.1621554046869278],[61,47,0.43460196256637573],[62,3,50.670333862304688],[62,6,54.485752105712891],[62,10,54.087116241455078],[62,11,50.599266052246094],[62,12,53.7767333984375],[62,15,51.389842987060547],[62,19,50.364398956298828],[62,29,0.48533239960670471],[62,31,0.44734451174736023],[62,33,102.42562103271484],[63,0,0.45294782519340515],[63,4,54.237972259521484],[63,10,53.752510070800781],[63,12,0.43608000874519348],[63,13,53.317527770996094],[63,14,54.264698028564453],[63,19,54.001842498779297],[63,33,103.87953948974609],[63,44,0.20028656721115112],[64,7,51.067226409912109],[64,8,52.728569030761719],[64,20,53.313385009765625],[64,25,0.12246885150671005],[64,29,0.21952486038208008],[64,45,0.45129898190498352],[65,5,54.28265380859375],[65,6,0.05467228963971138],[65,7,50.587398529052734],[65,12,50.740871429443359],[65,13,51.922332763671875],[65,15,51.465606689453125],[65,20,50.605960845947266],[65,31,0.031305234879255295],[65,33,0.024850502610206604],[65,38,0.18012678623199463],[66,4,50.169445037841797],[66,6,0.20981484651565552],[66,8,54.813095092773438],[66,9,52.181678771972656],[66,15,0.0087912138551473618],[66,20,52.455574035644531],[66,22,0.1946081817150116],[66,42,0.23115138709545135],[67,0,53.084690093994141],[67,1,54.412517547607422],[67,2,54.5238037109375],[67,9,54.595577239990234],[67,17,0.24150419235229492],[67,20,54.706981658935547],[67,21,0.29965484142303467],[67,39,0.042535740882158279],[67,43,0.46450555324554443],[68,0,51.399547576904297],[68,3,50.331794738769531],[68,4,51.044830322265625],[68,5,53.620193481445312],[68,8,53.409934997558594],[68,10,50.770175933837891],[68,11,54.268398284912109],[68,13,52.164340972900391],[68,14,54.044288635253906],[68,15,0.10323792695999146],[68,18,0.35222142934799194],[68,20,54.454067230224609],[68,22,0.11017544567584991],[68,33,0.43301385641098022],[68,38,0.18861587345600128],[69,0,50.964836120605469],[69,1,53.673618316650391],[69,2,51.590496063232422],[69,3,51.841209411621094],[69,5,54.298751831054688],[69,6,52.363498687744141],[69,10,51.151809692382812],[69,11,53.735935211181641],[69,14,54.791725158691406],[69,16,0.43252116441726685],[69,20,53.354568481445312],[69,27,0.069227688014507294],[69,33,0.38823777437210083],[69,38,0.29765713214874268],[69,42,0.026355072855949402],[70,4,50.721790313720703],[70,6,54.706615447998047],[70,20,50.631568908691406],[70,28,0.37249466776847839],[70,30,0.26735427975654602],[70,33,0.33283764123916626],[71,0,0.16204395890235901],[71,1,0.067454010248184204],[71,2,0.03590000793337822],[71,5,52.036930084228516],[71,10,53.277114868164062],[71,13,50.219799041748047],[71,15,0.19805477559566498],[71,20,52.680374145507812],[71,24,0.43417102098464966],[71,33,0.26464435458183289],[71,40,0.19235385954380035],[71,47,0.088784299790859222],[72,3,0.20847718417644501],[72,4,0.19112280011177063],[72,6,53.387794494628906],[72,11,54.860530853271484],[72,12,52.880657196044922],[72,13,0.46349427103996277],[72,15,53.785923004150391],[72,20,54.039096832275391],[72,24,0.20153769850730896],[72,34,104.81826782226562],[72,42,0.47012403607368469],[73,2,54.054355621337891],[73,12,52.507492065429688],[73,14,51.883331298828125],[73,20,50.780792236328125],[73,23,0.23294238746166229],[73,26,0.12204519659280777],[73,33,0.28289276361465454],[73,34,100.87276458740234],[73,43,0.45391014218330383],[73,44,0.44332057237625122],[73,46,0.11329615116119385],[73,47,0.31856563687324524],[74,2,54.380939483642578],[74,11,0.23045583069324493],[74,12,0.31302088499069214],[74,20,53.825958251953125],[74,23,0.057209685444831848],[74,32,0.016839217394590378],[74,34,103.90423583984375],[74,35,0.31137755513191223],[74,40,0.48662644624710083],[74,45,0.33841273188591003],[75,0,0.43075042963027954],[75,1,51.763355255126953],[75,4,51.421550750732422],[75,7,53.613151550292969],[75,8,0.030880527570843697],[75,12,52.351436614990234],[75,17,0.1696651428937912],[75,20,53.906696319580078],[75,23,0.12816387414932251],[75,34,102.95844268798828],[75,38,0.060918673872947693],[75,47,0.099397070705890656],[76,7,0.12065150588750839],[76,9,51.828189849853516],[76,10,0.1220819503068924],[76,15,50.829078674316406],[76,20,54.970966339111328],[76,33,0.42973843216896057],[76,34,104.08345031738281],[76,45,0.34263303875923157],[77,2,0.36012068390846252],[77,3,53.051681518554688],[77,10,52.427139282226562],[77,11,53.05377197265625],[77,14,51.980960845947266],[77,15,51.435840606689453],[77,20,53.81402587890625],[77,22,0.25189700722694397],[77,29,0.43795982003211975],[77,34,101.15478515625],[78,1,50.997764587402344],[78,2,0.20581555366516113],[78,3,50.301883697509766],[78,4,0.24851344525814056],[78,6,54.861274719238281],[78,9,54.741752624511719],[78,20,53.263320922851562],[78,31,0.11586814373731613],[78,34,103.56839752197266],[78,38,0.28377345204353333],[79,0,53.238864898681641],[79,7,54.499160766601562],[79,8,52.837394714355469],[79,13,50.343578338623047],[79,20,53.176166534423828],[79,21,0.17991693317890167],[79,26,0.32166054844856262],[79,27,0.090265832841396332],[79,34,102.05396270751953],[79,38,0.39411613345146179],[79,40,0.40245679020881653],[80,0,54.789260864257812],[80,1,54.531818389892578],[80,4,52.502426147460938],[80,6,0.28246057033538818],[80,7,54.152042388916016],[80,9,51.634788513183594],[80,10,54.289909362792969],[80,11,52.960609436035156],[80,12,51.967189788818359],[80,15,53.537857055664062],[80,19,0.16637007892131805],[80,21,50.401981353759766],[81,1,51.833786010742188],[81,2,54.404998779296875],[81,4,51.855743408203125],[81,5,52.522911071777344],[81,6,0.043628737330436707],[81,8,54.575859069824219],[81,11,0.19116869568824768],[81,12,0.023937981575727463],[81,13,51.948249816894531],[81,18,0.41399797797203064],[81,21,52.911853790283203],[81,29,0.36625263094902039],[81,42,0.39868706464767456],[82,6,50.398490905761719],[82,10,54.497608184814453],[82,14,51.010643005371094],[82,21,52.028373718261719],[82,30,0.13616576790809631],[82,43,0.075846493244171143],[83,3,50.379474639892578],[83,5,52.734336853027344],[83,6,53.019355773925781],[83,7,51.225788116455078],[83,8,0.00063199491705745459],[83,11,0.27203482389450073],[83,12,52.99078369140625],[83,17,0.22333556413650513],[83,20,0.10501588135957718],[83,21,54.945869445800781],[83,41,0.2172519713640213],[84,8,54.140850067138672],[84,9,52.83380126953125],[84,21,51.602252960205078],[85,1,52.323574066162109],[85,4,0.33209896087646484],[85,5,0.424825519323349],[85,8,51.746608734130859],[85,9,50.450359344482422],[85,11,54.591449737548828],[85,21,52.434597015380859],[85,33,0.29279392957687378],[85,45,0.084778569638729095],[86,7,50.890029907226562],[86,21,54.72119140625],[86,25,0.18605111539363861],[86,30,0.40991571545600891],[86,32,0.25392207503318787],[87,0,54.180183410644531],[87,1,0.27550530433654785],[87,2,54.492942810058594],[87,3,53.837730407714844],[87,7,52.404029846191406],[87,11,54.780128479003906],[87,12,54.040027618408203],[87,15,0.19667473435401917],[87,21,51.816287994384766],[87,32,0.46794068813323975],[87,36,0.29597935080528259],[88,3,53.939098358154297],[88,5,51.563343048095703],[88,6,51.295852661132812],[88,9,53.515281677246094],[88,14,54.401676177978516],[88,15,53.239032745361328],[88,16,0.41468554735183716],[88,21,54.413082122802734],[88,34,0.020496150478720665],[88,35,101.32682037353516],[88,45,0.10662335902452469],[88,46,0.036330036818981171],[89,0,53.054389953613281],[89,6,53.674266815185547],[89,11,50.771278381347656],[89,14,0.10191795974969864],[89,21,53.797538757324219],[89,23,0.097789809107780457],[89,29,0.099345296621322632],[89,35,100.99777221679688],[89,36,0.36196213960647583],[89,40,0.39049482345581055],[89,43,0.087204471230506897],[90,2,50.811542510986328],[90,4,50.754505157470703],[90,8,52.394111633300781],[90,12,51.824333190917969],[90,15,51.377239227294922],[90,21,52.467842102050781],[90,35,100.52031707763672],[90,38,0.21330057084560394],[90,39,0.12645332515239716],[90,41,0.17757473886013031],[91,1,0.14407473802566528],[91,3,52.242034912109375],[91,10,51.306304931640625],[91,13,54.502338409423828],[91,14,51.614372253417969],[91,21,53.261833190917969],[91,35,103.30667877197266],[91,46,0.25916048884391785],[92,1,54.621063232421875],[92,4,53.530540466308594],[92,10,54.356853485107422],[92,12,0.37603586912155151],[92,13,53.614501953125],[92,21,50.174091339111328],[92,26,0.18624916672706604],[92,35,102.86859893798828],[93,2,52.187129974365234],[93,7,0.4391171932220459],[93,11,0.37605097889900208],[93,19,0.20892411470413208],[93,20,0.17231622338294983],[93,21,51.927978515625],[93,35,103.48123168945312],[93,38,0.22005566954612732],[94,8,0.32566362619400024],[94,13,0.36496508121490479],[94,21,53.122978210449219],[94,25,0.15595875680446625],[94,26,0.40010342001914978],[94,30,0.32628187537193298],[94,33,0.20766395330429077],[94,35,101.02719116210938],[94,40,0.27072256803512573],[94,41,0.41707423329353333],[95,0,50.692886352539062],[95,5,53.830135345458984],[95,8,0.010356404818594456],[95,9,0.39863049983978271],[95,13,54.237724304199219],[95,14,52.369647979736328],[95,15,53.028778076171875],[95,19,0.0081133227795362473],[95,21,51.311847686767578],[95,35,100.43609619140625],[96,3,0.030608227476477623],[96,6,53.425811767578125],[96,9,53.459526062011719],[96,12,54.111068725585938],[96,22,51.115188598632812],[96,31,0.071831606328487396],[96,35,0.085618868470191956],[96,45,0.10887780785560608],[97,0,54.01318359375],[97,1,0.31625869870185852],[97,8,53.554191589355469],[97,10,50.853588104248047],[97,14,52.536506652832031],[97,16,0.19937156140804291],[97,22,51.267467498779297],[97,40,0.33015045523643494],[98,2,53.714900970458984],[98,3,0.41735139489173889],[98,4,51.107311248779297],[98,5,52.964694976806641],[98,6,50.296413421630859],[98,7,50.865592956542969],[98,10,50.670944213867188],[98,13,52.532917022705078],[98,18,0.12959572672843933],[98,22,50.697494506835938],[98,32,0.47315937280654907],[98,35,0.12483229488134384],[98,43,0.28223007917404175],[98,47,0.4313599169254303],[99,4,53.960617065429688],[99,8,53.230075836181641],[99,11,53.383071899414062],[99,19,0.24758689105510712],[99,22,52.893184661865234],[99,25,0.48672026395797729],[99,35,0.36889320611953735],[99,36,0.056371461600065231],[99,46,0.37127342820167542],[99,47,0.44187578558921814],[100,0,50.056869506835938],[100,1,53.882656097412109],[100,2,51.755844116210938],[100,8,52.124610900878906],[100,12,52.528915405273438],[100,15,0.1157214492559433],[100,22,54.124191284179688],[100,34,0.21082127094268799],[100,39,0.060194846242666245],[101,1,53.274940490722656],[101,2,0.34573149681091309],[101,5,54.422710418701172],[101,10,52.630062103271484],[101,12,54.415401458740234],[101,14,0.067188233137130737],[101,22,53.100719451904297],[101,34,0.090001866221427917],[101,36,0.17511479556560516],[101,37,0.47374668717384338],[102,0,52.695041656494141],[102,2,0.43547633290290833],[102,4,54.998683929443359],[102,5,0.053637061268091202],[102,6,0.39949232339859009],[102,11,51.415481567382812],[102,12,0.46643480658531189],[102,13,53.200283050537109],[102,14,51.817855834960938],[102,15,0.27121061086654663],[102,22,54.865547180175781],[102,24,0.37104740738868713],[102,33,0.38226845860481262],[102,36,0.0064234849996864796],[102,43,0.016287678852677345],[102,46,0.10179373621940613],[103,0,0.34259313344955444],[103,1,52.999362945556641],[103,11,52.309211730957031],[103,14,0.27679923176765442],[103,22,50.525413513183594],[103,32,0.015074462629854679],[103,40,0.39598149061203003],[103,43,0.19435745477676392],[104,3,53.471302032470703],[104,4,54.357948303222656],[104,5,52.695568084716797],[104,6,50.759651184082031],[104,8,0.17314149439334869],[104,9,52.144214630126953],[104,13,50.383598327636719],[104,19,0.40604761242866516],[104,22,50.733283996582031],[104,36,104.21462249755859],[105,1,54.780647277832031],[105,5,54.082668304443359],[105,8,0.067264735698699951],[105,9,52.167583465576172],[105,13,53.955451965332031],[105,15,52.430324554443359],[105,19,0.23343504965305328],[105,21,0.40988191962242126],[105,22,54.702846527099609],[105,24,0.2667853832244873],[105,36,101.79543304443359],[106,2,52.556545257568359],[106,9,53.661933898925781],[106,15,51.396152496337891],[106,17,0.21408958733081818],[106,20,0.29955717921257019],[106,21,0.22177949547767639],[106,22,52.570888519287109],[106,31,0.12338664382696152],[106,36,102.57060241699219],[106,42,0.41185289621353149],[106,44,0.37667000293731689],[107,0,54.530372619628906],[107,3,51.057956695556641],[107,10,52.939258575439453],[107,14,54.668304443359375],[107,15,0.33562737703323364],[107,17,0.4393523633480072],[107,22,53.854663848876953],[107,25,0.49056351184844971],[107,31,0.10358358174562454],[107,36,101.68701171875],[108,1,0.08312535285949707],[108,3,50.937732696533203],[108,7,53.572826385498047],[108,19,0.42503887414932251],[108,22,50.474151611328125],[108,36,101.55259704589844],[109,3,50.729171752929688],[109,4,0.14870145916938782],[109,6,50.319465637207031],[109,7,54.886299133300781],[109,8,51.069805145263672],[109,11,0.32244300842285156],[109,12,54.070755004882812],[109,15,52.748043060302734],[109,18,0.22737953066825867],[109,22,54.824081420898438],[109,36,104.10604095458984],[109,38,0.4804401695728302],[110,1,0.25515612959861755],[110,2,52.683059692382812],[110,8,0.31930544972419739],[110,9,0.21359248459339142],[110,11,52.243049621582031],[110,14,51.152660369873047],[110,15,50.841999053955078],[110,22,50.415149688720703],[110,23,0.13062088191509247],[110,26,0.21868458390235901],[110,29,0.47872477769851685],[110,36,102.88645172119141],[110,38,0.2203846275806427],[110,39,0.22828556597232819],[110,44,0.082453601062297821],[110,47,0.13505487143993378],[111,3,0.45335984230041504],[111,7,54.095252990722656],[111,9,0.24641448259353638],[111,15,0.27343785762786865],[111,22,54.349334716796875],[111,36,100.44292449951172],[112,0,52.879650115966797],[112,8,52.843166351318359],[112,9,52.543598175048828],[112,12,0.25953185558319092],[112,13,52.128128051757812],[112,15,50.343433380126953],[112,17,0.20097506046295166],[112,18,0.27121621370315552],[112,23,52.698966979980469],[112,24,0.32894200086593628],[112,39,0.17759707570075989],[112,44,0.29781097173690796],[113,4,53.857349395751953],[113,5,54.100589752197266],[113,12,0.46474730968475342],[113,13,0.07784513384103775],[113,23,50.883525848388672],[113,29,0.037749331444501877],[113,35,0.21985806524753571],[113,45,0.048796392977237701],[114,3,51.301090240478516],[114,4,50.623580932617188],[114,8,54.329437255859375],[114,10,52.491527557373047],[114,12,50.220493316650391],[114,14,54.672706604003906],[114,16,0.10250360518693924],[114,21,0.4953540563583374],[114,23,53.594875335693359],[114,41,0.23760607838630676],[115,1,50.384647369384766],[115,2,0.045362472534179688],[115,3,52.281082153320312],[115,10,0.29621094465255737],[115,11,52.356552124023438],[115,15,53.24737548828125],[115,23,50.4842529296875],[116,2,50.789379119873047],[116,9,50.304706573486328],[116,10,52.728107452392578],[116,14,53.117267608642578],[116,23,51.515193939208984],[116,30,0.46672102808952332],[116,37,0.090131945908069611],[116,46,0.49115315079689026],[117,0,54.421501159667969],[117,1,51.706626892089844],[117,3,52.735115051269531],[117,6,53.563518524169922],[117,7,50.630947113037109],[117,11,50.329998016357422],[117,23,50.367393493652344],[117,44,0.062279839068651199],[118,3,50.050071716308594],[118,4,51.205711364746094],[118,5,0.32215797901153564],[118,8,54.68536376953125],[118,11,53.180103302001953],[118,12,0.34838578104972839],[118,18,0.025149665772914886],[118,21,0.44567438960075378],[118,22,0.13225488364696503],[118,23,50.38104248046875],[118,24,0.3577083945274353],[118,27,0.1275077611207962],[118,35,0.06021595373749733],[119,0,51.922080993652344],[119,5,54.625232696533203],[119,6,51.184844970703125],[119,9,52.4525146484375],[119,10,0.005679671186953783],[119,11,52.674484252929688],[119,12,54.972072601318359],[119,15,54.142147064208984],[119,23,53.392631530761719],[119,37,0.002203882671892643],[119,43,0.17473334074020386],[120,1,0.053538531064987183],[120,6,53.988597869873047],[120,7,51.171550750732422],[120,9,50.735950469970703],[120,13,0.05746808648109436],[120,20,0.13002805411815643],[120,23,51.134681701660156],[120,24,0.070559576153755188],[120,34,0.12518115341663361],[120,37,101.99199676513672],[120,40,0.10033676773309708],[121,0,0.00081549858441576362],[121,2,0.32803145051002502],[121,4,0.18200397491455078],[121,8,0.45771273970603943],[121,10,54.109588623046875],[121,12,53.524177551269531],[121,23,50.207370758056641],[121,30,0.18400496244430542],[121,37,102.90958404541016],[121,40,0.18199367821216583],[122,0,50.868114471435547],[122,2,51.320735931396484],[122,3,0.0095748389139771461],[122,5,53.363616943359375],[122,12,52.18524169921875],[122,13,51.2623291015625],[122,17,0.062243327498435974],[122,23,53.704746246337891],[122,37,100.0345458984375],[123,8,0.013331643305718899],[123,10,50.808761596679688],[123,12,0.22454611957073212],[123,17,0.45568543672561646],[123,23,54.320903778076172],[123,25,0.3736911416053772],[123,37,104.07657623291016],[123,45,0.15636444091796875],[123,47,0.19383686780929565],[124,1,54.159458160400391],[124,8,52.486217498779297],[124,13,52.496543884277344],[124,15,54.905139923095703],[124,19,0.44909483194351196],[124,23,52.251819610595703],[124,29,0.038412421941757202],[124,30,0.051153946667909622],[124,37,101.02059173583984],[124,44,0.31091159582138062],[125,2,54.433586120605469],[125,5,0.49506545066833496],[125,6,54.747760772705078],[125,13,53.112277984619141],[125,14,54.023921966552734],[125,23,53.189060211181641],[125,24,0.44718253612518311],[125,37,101.14735412597656],[125,39,0.203592449426651],[125,47,0.19102291762828827],[126,1,52.137577056884766],[126,2,52.18658447265625],[126,5,52.137008666992188],[126,6,0.22218695282936096],[126,7,54.704837799072266],[126,11,0.26066681742668152],[126,14,52.935749053955078],[126,16,0.45621243119239807],[126,20,0.059009801596403122],[126,23,53.513607025146484],[126,29,0.25802215933799744],[126,37,101.60216522216797],[126,42,0.03847590833902359],[126,46,0.042616270482540131],[127,4,51.672172546386719],[127,7,50.045486450195312],[127,23,51.092029571533203],[127,37,104.95039367675781],[127,42,0.16674824059009552],[128,0,0.1331336498260498],[128,8,53.180416107177734],[128,24,50.410099029541016],[128,43,0.49212521314620972],[129,12,53.299465179443359],[129,14,50.859283447265625],[129,24,53.326473236083984],[129,25,0.013709104619920254],[129,41,0.45377001166343689],[129,47,0.25797092914581299],[130,1,50.257312774658203],[130,2,54.332328796386719],[130,6,53.318874359130859],[130,7,52.486927032470703],[130,9,52.196865081787109],[130,11,51.757404327392578],[130,13,52.986835479736328],[130,19,0.46736112236976624],[130,24,53.0377197265625],[130,28,0.016580682247877121],[130,36,0.033538851886987686],[130,43,0.19145593047142029],[131,0,52.053699493408203],[131,4,0.31075713038444519],[131,7,53.776603698730469],[131,8,51.115882873535156],[131,10,50.862159729003906],[131,15,54.472358703613281],[131,24,54.748001098632812],[131,43,0.44481962919235229],[132,5,0.17278917133808136],[132,7,50.880653381347656],[132,9,51.407012939453125],[132,12,51.505290985107422],[132,15,50.347358703613281],[132,24,53.432598114013672],[132,29,0.31053045392036438],[132,46,0.25831663608551025],[133,1,52.765411376953125],[133,3,50.063270568847656],[133,5,52.051422119140625],[133,6,50.602985382080078],[133,7,50.786102294921875],[133,9,52.409782409667969],[133,11,51.73419189453125],[133,13,52.062328338623047],[133,14,51.858837127685547],[133,15,53.942680358886719],[133,24,50.234031677246094],[133,32,0.29404768347740173],[133,37,0.27951601147651672],[133,42,0.43425586819648743],[134,0,54.276969909667969],[134,19,0.23784691095352173],[134,24,52.412876129150391],[134,27,0.27319535613059998],[134,39,0.048874702304601669],[134,42,0.076239615678787231],[135,2,52.735576629638672],[135,18,0.15757472813129425],[135,24,53.433120727539062],[135,35,0.46804985404014587],[135,44,0.29916101694107056],[136,0,50.166393280029297],[136,1,50.849384307861328],[136,3,52.907291412353516],[136,10,52.662441253662109],[136,12,52.909339904785156],[136,13,52.34637451171875],[136,24,52.931056976318359],[136,28,0.019280396401882172],[136,29,0.10480639338493347],[136,38,103.64399719238281],[136,39,0.34637901186943054],[136,47,0.4044988751411438],[137,2,50.327182769775391],[137,3,54.610786437988281],[137,4,54.705410003662109],[137,6,53.372650146484375],[137,7,0.43842431902885437],[137,24,54.835380554199219],[137,38,104.59642028808594],[138,4,50.350837707519531],[138,5,53.6026611328125],[138,6,0.27545422315597534],[138,11,50.117160797119141],[138,12,50.188751220703125],[138,13,0.33728539943695068],[138,19,0.11169642210006714],[138,24,53.128593444824219],[138,36,0.124480240046978],[138,38,102.39148712158203],[139,3,50.085166931152344],[139,4,53.060577392578125],[139,9,0.12909442186355591],[139,13,52.600631713867188],[139,24,54.355014801025391],[139,38,102.41065979003906],[139,42,0.17075303196907043],[140,0,54.95098876953125],[140,1,0.26438626646995544],[140,5,53.303680419921875],[140,7,0.47345653176307678],[140,9,51.940780639648438],[140,10,54.763111114501953],[140,14,53.214954376220703],[140,21,0.44816392660140991],[140,24,52.635643005371094],[140,30,0.40173816680908203],[140,37,0.34266853332519531],[140,38,101.12872314453125],[141,2,50.455741882324219],[141,11,53.772304534912109],[141,13,0.47157302498817444],[141,20,0.48297831416130066],[141,21,0.29707106947898865],[141,24,53.880500793457031],[141,38,101.64122009277344],[141,47,0.33726543188095093],[142,1,52.637054443359375],[142,4,51.220382690429688],[142,5,54.042507171630859],[142,6,51.780002593994141],[142,8,54.986316680908203],[142,10,0.044383767992258072],[142,16,0.17825473845005035],[142,20,0.097646474838256836],[142,24,50.588523864746094],[142,26,0.21880584955215454],[142,33,0.33337926864624023],[142,38,102.06330108642578],[143,2,0.29030194878578186],[143,3,0.35520610213279724],[143,4,0.38688176870346069],[143,8,52.350208282470703],[143,10,50.696144104003906],[143,12,0.3403821587562561],[143,14,52.338874816894531],[143,15,53.288547515869141],[143,24,54.037368774414062],[143,26,0.49111688137054443],[143,32,0.48745808005332947],[143,38,102.20440673828125],[144,2,51.850627899169922],[144,9,0.10377472639083862],[144,12,52.886978149414062],[144,14,52.226406097412109],[144,16,0.11893319338560104],[144,25,51.83380126953125],[144,45,0.39404311776161194],[145,1,52.782344818115234],[145,6,52.460922241210938],[145,10,50.098705291748047],[145,13,50.968242645263672],[145,15,50.926353454589844],[145,25,54.8807373046875],[145,36,0.41323855519294739],[146,4,53.753040313720703],[146,5,52.963123321533203],[146,8,53.566516876220703],[146,11,52.955905914306641],[146,25,50.576084136962891],[146,43,0.080164246261119843],[147,2,54.290760040283203],[147,7,54.286399841308594],[147,11,52.287242889404297],[147,15,52.078643798828125],[147,25,50.852863311767578],[148,4,0.10477688908576965],[148,11,0.29396480321884155],[148,13,52.455150604248047],[148,25,53.400215148925781],[148,32,0.19294361770153046],[148,36,0.1053827702999115],[148,46,0.40515834093093872],[149,0,54.277702331542969],[149,3,54.187393188476562],[149,9,53.443367004394531],[149,10,51.413013458251953],[149,19,0.41451776027679443],[149,22,0.10050428658723831],[149,25,53.206768035888672],[149,27,0.32073765993118286],[150,7,0.061268128454685211],[150,8,0.42440328001976013],[150,9,50.562667846679688],[150,11,0.011167231947183609],[150,12,53.973972320556641],[150,13,54.217571258544922],[150,20,0.37813559174537659],[150,25,51.471973419189453],[150,27,0.38516595959663391],[150,37,0.39796751737594604],[151,3,52.743160247802734],[151,7,50.2017822265625],[151,11,53.074806213378906],[151,12,50.522483825683594],[151,17,0.17542804777622223],[151,25,52.454639434814453],[151,26,0.3528670072555542],[151,40,0.3672558069229126],[152,0,53.067844390869141],[152,3,0.054698936641216278],[152,6,51.231521606445312],[152,11,50.411487579345703],[152,13,53.707023620605469],[152,14,52.769252777099609],[152,25,50.313407897949219],[152,32,0.11121632903814316],[152,39,102.15706634521484],[152,42,0.05461445078253746],[152,47,0.41112634539604187],[153,0,54.962947845458984],[153,1,50.557365417480469],[153,2,54.274642944335938],[153,3,51.623279571533203],[153,5,50.920490264892578],[153,6,52.165431976318359],[153,7,0.18840891122817993],[153,8,50.747920989990234],[153,9,0.022164536640048027],[153,10,53.107765197753906],[153,15,52.651214599609375],[153,17,0.29819890856742859],[153,21,0.080771535634994507],[153,25,50.137088775634766],[153,27,0.4542573094367981],[153,33,0.41928821802139282],[153,39,103.66280364990234],[153,41,0.39798888564109802],[154,3,50.163330078125],[154,4,54.322517395019531],[154,8,54.274196624755859],[154,9,0.39877820014953613],[154,11,0.39069300889968872],[154,25,50.116371154785156],[154,34,0.33650168776512146],[154,39,103.67110443115234],[155,1,54.185050964355469],[155,3,0.49079629778862],[155,4,54.982597351074219],[155,5,52.236049652099609],[155,7,0.21190515160560608],[155,8,52.558597564697266],[155,9,51.509361267089844],[155,14,53.357524871826172],[155,20,0.43588981032371521],[155,24,0.34942531585693359],[155,25,50.397495269775391],[155,30,0.12426097691059113],[155,35,0.46715068817138672],[155,38,0.18684974312782288],[155,39,104.22255706787109],[155,44,0.47122600674629211],[156,1,0.14882984757423401],[156,3,0.14266350865364075],[156,6,53.235622406005859],[156,7,52.332511901855469],[156,9,51.572513580322266],[156,12,50.050765991210938],[156,13,0.42156296968460083],[156,22,0.33571198582649231],[156,25,53.193996429443359],[156,35,0.36480662226676941],[156,39,103.45829010009766],[157,0,50.697872161865234],[157,2,50.673484802246094],[157,7,51.777725219726562],[157,14,53.729476928710938],[157,16,0.16446207463741302],[157,25,54.194187164306641],[157,31,0.34761896729469299],[157,34,0.36116567254066467],[157,39,102.50889587402344],[157,46,0.33942884206771851],[157,47,0.37074881792068481],[158,1,53.756160736083984],[158,3,0.17096838355064392],[158,4,51.986381530761719],[158,5,52.101390838623047],[158,15,52.537567138671875],[158,25,50.207359313964844],[158,28,0.10387121140956879],[158,39,104.7215576171875],[158,45,0.23709443211555481],[159,0,0.38743162155151367],[159,4,0.055291913449764252],[159,10,54.412464141845703],[159,14,0.22278584539890289],[159,18,0.38613465428352356],[159,25,51.020896911621094],[159,39,103.99785614013672],[159,40,0.29304406046867371],[159,41,0.2454792857170105],[160,2,52.941902160644531],[160,6,52.434963226318359],[160,7,53.101905822753906],[160,12,53.768020629882812],[160,13,50.937068939208984],[160,18,0.46170219779014587],[160,26,53.863956451416016],[161,6,50.946388244628906],[161,8,50.297325134277344],[161,9,52.358627319335938],[161,10,54.024665832519531],[161,11,53.579776763916016],[161,12,52.586288452148438],[161,14,54.661449432373047],[161,16,0.14509673416614532],[161,26,50.315567016601562],[161,29,0.42962554097175598],[161,35,0.46298211812973022],[161,40,0.1737198531627655],[161,41,0.26867172122001648],[161,43,0.097852014005184174],[162,0,53.148059844970703],[162,10,52.364547729492188],[162,13,53.907859802246094],[162,14,0.083070866763591766],[162,22,0.052251800894737244],[162,23,0.099839061498641968],[162,26,54.641803741455078],[162,27,0.10715925693511963],[163,8,0.10322535037994385],[163,14,0.13758742809295654],[163,15,51.620071411132812],[163,23,0.45113947987556458],[163,26,53.787357330322266],[163,31,0.29519319534301758],[163,34,0.18801470100879669],[164,0,0.4835112988948822],[164,3,50.371971130371094],[164,5,51.977123260498047],[164,11,50.354717254638672],[164,12,50.250221252441406],[164,14,53.845920562744141],[164,15,54.374130249023438],[164,16,0.024577204138040543],[164,26,50.328678131103516],[164,42,0.39257714152336121],[165,0,52.0968017578125],[165,1,0.17273826897144318],[165,4,52.006435394287109],[165,7,50.558387756347656],[165,8,53.60430908203125],[165,9,53.032394409179688],[165,10,0.015868494287133217],[165,11,0.22763176262378693],[165,19,0.4527866542339325],[165,22,0.30274209380149841],[165,24,0.38487371802330017],[165,26,53.927051544189453],[165,33,0.18093493580818176],[165,34,0.38205015659332275],[165,39,0.41453477740287781],[166,0,52.750591278076172],[166,1,53.954948425292969],[166,2,51.427131652832031],[166,3,52.63507080078125],[166,7,51.407241821289062],[166,8,0.13222008943557739],[166,23,0.32357150316238403],[166,26,51.753349304199219],[166,46,0.3218342661857605],[167,3,0.35586860775947571],[167,14,0.14325621724128723],[167,15,50.682334899902344],[167,22,0.2934262752532959],[167,26,50.024097442626953],[168,5,54.345142364501953],[168,7,51.349468231201172],[168,8,51.513168334960938],[168,9,52.573352813720703],[168,10,0.39453479647636414],[168,12,53.826644897460938],[168,25,0.062496297061443329],[168,26,50.270557403564453],[168,36,0.31685817241668701],[168,40,100.7926025390625],[168,44,0.18244071304798126],[169,1,52.308151245117188],[169,2,52.050464630126953],[169,8,0.20752283930778503],[169,11,50.188697814941406],[169,13,0.47287943959236145],[169,14,54.132400512695312],[169,16,0.43315348029136658],[169,20,0.064268946647644043],[169,22,0.12519651651382446],[169,26,53.944564819335938],[169,34,0.3991452157497406],[169,40,100.66136932373047],[170,1,53.780570983886719],[170,3,50.994438171386719],[170,4,51.986660003662109],[170,5,53.685997009277344],[170,10,51.98333740234375],[170,16,0.45721593499183655],[170,17,0.049216538667678833],[170,26,50.915458679199219],[170,40,102.19636535644531],[170,43,0.14190778136253357],[171,1,53.209480285644531],[171,2,0.14895810186862946],[171,4,52.263076782226562],[171,8,50.194828033447266],[171,15,54.2354736328125],[171,17,0.033363964408636093],[171,22,0.1990114152431488],[171,26,54.803371429443359],[171,27,0.45119577646255493],[171,31,0.23292656242847443],[171,40,102.98052215576172],[171,43,0.30167123675346375],[172,0,51.067188262939453],[172,5,0.051178131252527237],[172,10,0.21091768145561218],[172,11,50.473804473876953],[172,26,50.735858917236328],[172,36,0.43048781156539917],[172,40,100.32500457763672],[173,4,54.012866973876953],[173,6,54.360546112060547],[173,9,0.46847304701805115],[173,10,53.705303192138672],[173,26,52.480602264404297],[173,30,0.44401761889457703],[173,40,102.32750701904297],[173,41,0.12991304695606232],[174,2,54.51568603515625],[174,3,52.598686218261719],[174,5,51.407318115234375],[174,9,52.067398071289062],[174,11,0.20297315716743469],[174,13,54.711563110351562],[174,15,0.12491515278816223],[174,17,0.0041863336227834225],[174,26,53.309669494628906],[174,28,0.22520719468593597],[174,40,101.37160491943359],[175,6,50.353790283203125],[175,13,51.032501220703125],[175,14,51.510963439941406],[175,15,0.44443008303642273],[175,22,0.071385137736797333],[175,26,51.488491058349609],[175,27,0.16217443346977234],[175,28,0.065018944442272186],[175,34,0.29747790098190308],[175,36,0.48629856109619141],[175,40,103.47823333740234],[176,1,0.45555037260055542],[176,6,53.863693237304688],[176,8,52.670822143554688],[176,16,0.42372149229049683],[176,17,0.37679395079612732],[176,27,52.052330017089844],[176,38,0.22570949792861938],[177,0,50.475410461425781],[177,3,52.234676361083984],[177,6,0.34362953901290894],[177,8,53.724948883056641],[177,9,51.003192901611328],[177,10,53.950904846191406],[177,11,53.833396911621094],[177,21,0.35975226759910583],[177,27,50.109382629394531],[177,33,0.34785717725753784],[177,34,0.21790637075901031],[177,36,0.2819056510925293],[177,42,0.085490137338638306],[177,44,0.29402780532836914],[178,0,51.127159118652344],[178,1,54.886016845703125],[178,15,51.626811981201172],[178,27,51.38494873046875],[178,29,0.48586493730545044],[179,4,52.166290283203125],[179,7,53.765174865722656],[179,10,0.19704228639602661],[179,16,0.42650097608566284],[179,27,52.527976989746094],[179,34,0.3352695107460022],[179,37,0.33414372801780701],[179,41,0.3020387589931488],[179,44,0.24315281212329865],[180,4,0.32008197903633118],[180,11,50.583385467529297],[180,20,0.0013638078235089779],[180,27,53.434913635253906],[180,34,0.41860219836235046],[180,37,0.12179930508136749],[180,39,0.33722025156021118],[181,0,0.28192517161369324],[181,3,54.749313354492188],[181,4,54.24920654296875],[181,7,50.894584655761719],[181,12,0.42309224605560303],[181,13,52.455776214599609],[181,15,50.547042846679688],[181,27,50.262424468994141],[182,8,52.886062622070312],[182,20,0.21776948869228363],[182,23,0.042618211358785629],[182,27,51.743118286132812],[182,33,0.41201609373092651],[182,46,0.42024585604667664],[183,2,51.405788421630859],[183,4,0.34895902872085571],[183,5,51.813388824462891],[183,6,53.009456634521484],[183,10,54.142871856689453],[183,12,51.868728637695312],[183,15,52.256103515625],[183,21,0.25990775227546692],[183,27,54.708507537841797],[183,39,0.030025763437151909],[183,41,0.058526713401079178],[184,3,0.31371024250984192],[184,5,52.143074035644531],[184,11,51.319591522216797],[184,12,54.867115020751953],[184,14,54.471061706542969],[184,19,0.28158286213874817],[184,23,0.45891600847244263],[184,25,0.32259324193000793],[184,27,51.514183044433594],[184,41,103.05934143066406],[184,45,0.21815131604671478],[184,47,0.22146391868591309],[185,2,54.389312744140625],[185,9,54.726730346679688],[185,12,51.591567993164062],[185,14,50.219989776611328],[185,18,0.42467635869979858],[185,23,0.31087028980255127],[185,27,50.642326354980469],[185,31,0.30460977554321289],[185,37,0.18165159225463867],[185,41,104.65705108642578],[186,0,0.25788909196853638],[186,3,54.941764831542969],[186,6,52.525196075439453],[186,7,50.624126434326172],[186,9,54.336635589599609],[186,12,53.895957946777344],[186,13,50.606910705566406],[186,19,0.33931231498718262],[186,21,0.22821156680583954],[186,27,52.370635986328125],[186,30,0.36943098902702332],[186,38,0.02148253470659256],[186,41,100.21382141113281],[186,42,0.47981896996498108],[187,0,52.922557830810547],[187,1,50.955696105957031],[187,3,51.963218688964844],[187,4,53.041225433349609],[187,5,52.135051727294922],[187,9,0.010942969471216202],[187,11,52.607738494873047],[187,14,52.601673126220703],[187,24,0.11150573194026947],[187,27,53.664424896240234],[187,29,0.044489149004220963],[187,31,0.22644561529159546],[187,40,0.43517783284187317],[187,41,104.24137115478516],[187,45,0.30246543884277344],[187,47,0.0033932260703295469],[188,2,50.792934417724609],[188,7,0.023050190880894661],[188,9,53.674190521240234],[188,10,50.590877532958984],[188,13,54.378143310546875],[188,14,53.915081024169922],[188,15,51.944061279296875],[188,27,51.305023193359375],[188,28,0.30837661027908325],[188,41,101.22265625],[189,0,53.115684509277344],[189,2,50.200401306152344],[189,4,53.539379119873047],[189,7,0.47677996754646301],[189,18,0.25107145309448242],[189,24,0.03134748712182045],[189,27,54.358589172363281],[189,35,0.38156014680862427],[189,38,0.066175200045108795],[189,41,103.09375],[189,43,0.33280619978904724],[190,1,51.842105865478516],[190,5,53.342754364013672],[190,6,52.595291137695312],[190,8,52.213890075683594],[190,10,0.15846012532711029],[190,17,0.16063010692596436],[190,27,52.685287475585938],[190,41,104.90682983398438],[190,42,0.24240745604038239],[191,1,51.278114318847656],[191,4,0.24457579851150513],[191,7,52.265346527099609],[191,10,50.77734375],[191,12,0.096988648176193237],[191,13,52.950069427490234],[191,27,50.354633331298828],[191,28,0.36422383785247803],[191,31,0.25804945826530457],[191,39,0.4405137300491333],[191,41,102.64510345458984],[192,0,52.08270263671875],[192,3,53.359157562255859],[192,6,52.398464202880859],[192,12,53.136501312255859],[192,13,52.122447967529297],[192,14,0.24133208394050598],[192,17,0.42085009813308716],[192,28,54.159107208251953],[193,0,51.129478454589844],[193,1,54.000377655029297],[193,5,52.120113372802734],[193,7,51.47930908203125],[193,9,52.702804565429688],[193,11,0.25548422336578369],[193,13,51.027820587158203],[193,28,52.539890289306641],[193,32,0.29210367798805237],[193,35,0.012117686681449413],[193,42,0.17377012968063354],[194,1,53.317520141601562],[194,2,54.316276550292969],[194,4,50.122386932373047],[194,6,0.4184720516204834],[194,7,53.825492858886719],[194,9,53.736583709716797],[194,11,50.308506011962891],[194,13,0.04402715340256691],[194,14,54.294963836669922],[194,20,0.20283131301403046],[194,25,0.19449774920940399],[194,26,0.027815774083137512],[194,28,51.0262451171875],[194,44,0.27682694792747498],[194,46,0.13557696342468262],[195,9,54.217769622802734],[195,10,52.920818328857422],[195,12,51.335056304931641],[195,15,53.698760986328125],[195,28,50.450595855712891],[195,32,0.23660404980182648],[195,35,0.070698380470275879],[195,37,0.40480682253837585],[196,2,50.610984802246094],[196,5,51.065895080566406],[196,9,0.45768356323242188],[196,10,52.250141143798828],[196,12,53.243350982666016],[196,14,0.086671985685825348],[196,15,0.10447340458631516],[196,21,0.49636343121528625],[196,28,53.820209503173828],[196,36,0.47763049602508545],[197,2,51.631385803222656],[197,28,52.79638671875],[197,35,0.29739049077033997],[197,36,0.040816269814968109],[197,43,0.098230026662349701],[198,1,53.047206878662109],[198,5,53.23809814453125],[198,6,54.565174102783203],[198,11,50.075981140136719],[198,21,0.19116789102554321],[198,28,50.087863922119141],[198,30,0.11276478320360184],[198,34,0.31035763025283813],[198,44,0.3988252580165863],[199,4,52.071487426757812],[199,7,51.309669494628906],[199,10,50.479316711425781],[199,14,53.234268188476562],[199,21,0.32288277149200439],[199,25,0.051803793758153915],[199,28,53.230083465576172],[199,43,0.21337148547172546],[199,47,0.4607870876789093],[200,8,54.867694854736328],[200,12,54.846549987792969],[200,14,50.009967803955078],[200,15,50.756149291992188],[200,20,0.13072864711284637],[200,25,0.041507352143526077],[200,28,52.654560089111328],[200,35,0.18415877223014832],[200,42,104.02641296386719],[201,2,0.068427205085754395],[201,15,0.33333477377891541],[201,22,0.066154502332210541],[201,28,51.444358825683594],[201,31,0.07719796895980835],[201,41,0.27611050009727478],[201,42,102.27503204345703],[202,0,54.508865356445312],[202,4,0.032175637781620026],[202,8,52.072601318359375],[202,11,53.025325775146484],[202,26,0.2083645761013031],[202,27,0.10627796500921249],[202,28,54.298171997070312],[202,36,0.3699929416179657],[202,42,101.94718170166016],[203,3,53.089004516601562],[203,4,53.447830200195312],[203,7,52.080699920654297],[203,10,0.39325517416000366],[203,13,50.88592529296875],[203,22,0.48196598887443542],[203,28,50.446304321289062],[203,34,0.0022291217464953661],[203,38,0.026039259508252144],[203,42,103.24577331542969],[204,3,50.2568359375],[204,8,52.497600555419922],[204,16,0.39389380812644958],[204,24,0.17521539330482483],[204,28,50.825435638427734],[204,39,0.29116761684417725],[204,42,102.00128173828125],[205,2,52.716400146484375],[205,6,52.267696380615234],[205,10,53.595897674560547],[205,13,0.31199353933334351],[205,14,0.47649309039115906],[205,15,0.038221698254346848],[205,26,0.28074204921722412],[205,28,50.472057342529297],[205,31,0.04505518451333046],[205,32,0.39187705516815186],[205,42,102.4862060546875],[205,45,0.40592673420906067],[205,46,0.43562990427017212],[205,47,0.4526614248752594],[206,3,53.508731842041016],[206,5,53.032669067382812],[206,6,53.907768249511719],[206,13,53.893898010253906],[206,14,52.557449340820312],[206,15,53.809852600097656],[206,28,54.240188598632812],[206,29,0.22426807880401611],[206,30,0.11523848026990891],[206,35,0.4881061315536499],[206,42,102.18634796142578],[207,0,51.21832275390625],[207,1,51.156375885009766],[207,4,54.176292419433594],[207,8,51.270488739013672],[207,9,52.896858215332031],[207,11,52.288967132568359],[207,15,54.390640258789062],[207,20,0.0048042153939604759],[207,28,51.240825653076172],[207,42,103.110595703125],[208,3,0.054680503904819489],[208,5,0.15994095802307129],[208,6,50.047206878662109],[208,7,0.40997469425201416],[208,8,51.405136108398438],[208,13,53.288730621337891],[208,15,0.084893539547920227],[208,23,0.15559166669845581],[208,29,53.013538360595703],[208,30,0.14970351755619049],[208,32,0.06919938325881958],[208,34,0.33737727999687195],[208,44,0.25229451060295105],[209,1,50.103015899658203],[209,6,54.165019989013672],[209,7,52.223060607910156],[209,15,0.1143171638250351],[209,29,51.887195587158203],[209,30,0.29943034052848816],[209,37,0.47982215881347656],[210,6,50.554969787597656],[210,7,52.157680511474609],[210,15,52.399253845214844],[210,19,0.17067147791385651],[210,24,0.38117825984954834],[210,25,0.000851770571898669],[210,29,51.020755767822266],[210,40,0.01028074324131012],[210,45,0.008518158458173275],[211,0,50.536258697509766],[211,10,51.981346130371094],[211,12,51.591121673583984],[211,29,53.129642486572266],[211,46,0.40776640176773071],[212,4,0.46998137235641479],[212,9,54.553054809570312],[212,11,50.743118286132812],[212,12,51.315589904785156],[212,15,53.294181823730469],[212,26,0.034202255308628082],[212,29,54.103801727294922],[212,33,0.040960993617773056],[212,37,0.49479383230209351],[212,42,0.38620385527610779],[213,0,0.33994728326797485],[213,6,54.667179107666016],[213,7,0.050131801515817642],[213,8,50.299995422363281],[213,10,51.871322631835938],[213,14,53.294315338134766],[213,15,51.124359130859375],[213,29,50.610130310058594],[214,0,50.011520385742188],[214,5,50.837127685546875],[214,9,54.413986206054688],[214,19,0.30154353380203247],[214,27,0.087291926145553589],[214,28,0.27774399518966675],[214,29,50.317981719970703],[214,44,0.45576402544975281],[215,1,0.45612981915473938],[215,3,53.521640777587891],[215,4,54.88909912109375],[215,5,50.912956237792969],[215,14,53.488094329833984],[215,27,0.19404751062393188],[215,28,0.28034219145774841],[215,29,50.358501434326172],[215,33,0.34588330984115601],[215,34,0.35342556238174438],[215,40,0.20757989585399628],[215,42,0.44938763976097107],[216,1,52.664566040039062],[216,2,54.928630828857422],[216,5,0.35284602642059326],[216,9,53.827190399169922],[216,10,53.286415100097656],[216,13,53.891666412353516],[216,16,0.039039663970470428],[216,28,0.050140336155891418],[216,29,53.910659790039062],[216,43,102.50341796875],[216,46,0.23237238824367523],[217,3,53.818977355957031],[217,10,51.073921203613281],[217,11,51.932571411132812],[217,12,53.658687591552734],[217,14,50.884868621826172],[217,21,0.41347506642341614],[217,22,0.29842096567153931],[217,27,0.3984011709690094],[217,29,50.063747406005859],[217,30,0.17452739179134369],[217,42,0.097008273005485535],[217,43,102.13336944580078],[218,0,53.630901336669922],[218,3,54.199352264404297],[218,5,53.860916137695312],[218,18,0.42304453253746033],[218,25,0.34608793258666992],[218,29,54.032810211181641],[218,40,0.1875089555978775],[218,43,102.74257659912109],[219,2,50.695960998535156],[219,8,52.958332061767578],[219,9,51.206306457519531],[219,14,0.18390923738479614],[219,15,0.1624310314655304],[219,18,0.22174374759197235],[219,29,51.398952484130859],[219,30,0.1469196081161499],[219,33,0.45154839754104614],[219,40,0.36239805817604065],[219,43,103.42655181884766],[220,0,53.138858795166016],[220,1,54.26025390625],[220,2,50.112220764160156],[220,4,53.70526123046875],[220,6,0.47603422403335571],[220,8,51.938503265380859],[220,9,0.19128337502479553],[220,12,51.217540740966797],[220,14,0.25808006525039673],[220,15,50.117458343505859],[220,22,0.11588933318853378],[220,29,53.311115264892578],[220,43,104.43067932128906],[221,1,0.33845359086990356],[221,2,0.41068929433822632],[221,3,52.141147613525391],[221,4,50.524120330810547],[221,7,53.977413177490234],[221,11,52.108203887939453],[221,16,0.41722539067268372],[221,29,50.981712341308594],[221,39,0.42248430848121643],[221,43,102.376953125],[221,44,0.22609518468379974],[221,46,0.30678614974021912],[222,1,52.317245483398438],[222,2,52.647129058837891],[222,4,51.567115783691406],[222,7,53.672454833984375],[222,13,54.242855072021484],[222,14,51.648265838623047],[222,21,0.24258989095687866],[222,29,51.111881256103516],[222,40,0.2951844334602356],[222,43,102.04779052734375],[223,2,0.26003819704055786],[223,5,50.257778167724609],[223,6,0.49925112724304199],[223,11,51.37200927734375],[223,13,53.152656555175781],[223,14,0.38648059964179993],[223,17,0.051977165043354034],[223,23,0.38961988687515259],[223,27,0.44477462768554688],[223,29,51.736705780029297],[223,43,103.39791107177734]],"rows": [{"id": "Root|Taxa_0", "metadata": null},{"id": "Root|Taxa_1", "metadata": null},{"id": "Root|Taxa_2", "metadata": null},{"id": "Root|Taxa_3", "metadata": null},{"id": "Root|Taxa_4", "metadata": null},{"id": "Root|Taxa_5", "metadata": null},{"id": "Root|Taxa_6", "metadata": null},{"id": "Root|Taxa_7", "metadata": null},{"id": "Root|Taxa_8", "metadata": null},{"id": "Root|Taxa_9", "metadata": null},{"id": "Root|Taxa_10", "metadata": null},{"id": "Root|Taxa_11", "metadata": null},{"id": "Root|Taxa_12", "metadata": null},{"id": "Root|Taxa_13", "metadata": null},{"id": "Root|Taxa_14", "metadata": null},{"id": "Root|Taxa_15", "metadata": null},{"id": "Root|Taxa_16", "metadata": null},{"id": "Root|Taxa_17", "metadata": null},{"id": "Root|Taxa_18", "metadata": null},{"id": "Root|Taxa_19", "metadata": null},{"id": "Root|Taxa_20", "metadata": null},{"id": "Root|Taxa_21", "metadata": null},{"id": "Root|Taxa_22", "metadata": null},{"id": "Root|Taxa_23", "metadata": null},{"id": "Root|Taxa_24", "metadata": null},{"id": "Root|Taxa_25", "metadata": null},{"id": "Root|Taxa_26", "metadata": null},{"id": "Root|Taxa_27", "metadata": null},{"id": "Root|Taxa_28", "metadata": null},{"id": "Root|Taxa_29", "metadata": null},{"id": "Root|Taxa_30", "metadata": null},{"id": "Root|Taxa_31", "metadata": null},{"id": "Root|Taxa_32", "metadata": null},{"id": "Root|Taxa_33", "metadata": null},{"id": "Root|Taxa_34", "metadata": null},{"id": "Root|Taxa_35", "metadata": null},{"id": "Root|Taxa_36", "metadata": null},{"id": "Root|Taxa_37", "metadata": null},{"id": "Root|Taxa_38", "metadata": null},{"id": "Root|Taxa_39", "metadata": null},{"id": "Root|Taxa_40", "metadata": null},{"id": "Root|Taxa_41", "metadata": null},{"id": "Root|Taxa_42", "metadata": null},{"id": "Root|Taxa_43", "metadata": null},{"id": "Root|Taxa_44", "metadata": null},{"id": "Root|Taxa_45", "metadata": null},{"id": "Root|Taxa_46", "metadata": null},{"id": "Root|Taxa_47", "metadata": null},{"id": "Root|Taxa_48", "metadata": null},{"id": "Root|Taxa_49", "metadata": null},{"id": "Root|Taxa_50", "metadata": null},{"id": "Root|Taxa_51", "metadata": null},{"id": "Root|Taxa_52", "metadata": null},{"id": "Root|Taxa_53", "metadata": null},{"id": "Root|Taxa_54", "metadata": null},{"id": "Root|Taxa_55", "metadata": null},{"id": "Root|Taxa_56", "metadata": null},{"id": "Root|Taxa_57", "metadata": null},{"id": "Root|Taxa_58", "metadata": null},{"id": "Root|Taxa_59", "metadata": null},{"id": "Root|Taxa_60", "metadata": null},{"id": "Root|Taxa_61", "metadata": null},{"id": "Root|Taxa_62", "metadata": null},{"id": "Root|Taxa_63", "metadata": null},{"id": "Root|Taxa_64", "metadata": null},{"id": "Root|Taxa_65", "metadata": null},{"id": "Root|Taxa_66", "metadata": null},{"id": "Root|Taxa_67", "metadata": null},{"id": "Root|Taxa_68", "metadata": null},{"id": "Root|Taxa_69", "metadata": null},{"id": "Root|Taxa_70", "metadata": null},{"id": "Root|Taxa_71", "metadata": null},{"id": "Root|Taxa_72", "metadata": null},{"id": "Root|Taxa_73", "metadata": null},{"id": "Root|Taxa_74", "metadata": null},{"id": "Root|Taxa_75", "metadata": null},{"id": "Root|Taxa_76", "metadata": null},{"id": "Root|Taxa_77", "metadata": null},{"id": "Root|Taxa_78", "metadata": null},{"id": "Root|Taxa_79", "metadata": null},{"id": "Root|Taxa_80", "metadata": null},{"id": "Root|Taxa_81", "metadata": null},{"id": "Root|Taxa_82", "metadata": null},{"id": "Root|Taxa_83", "metadata": null},{"id": "Root|Taxa_84", "metadata": null},{"id": "Root|Taxa_85", "metadata": null},{"id": "Root|Taxa_86", "metadata": null},{"id": "Root|Taxa_87", "metadata": null},{"id": "Root|Taxa_88", "metadata": null},{"id": "Root|Taxa_89", "metadata": null},{"id": "Root|Taxa_90", "metadata": null},{"id": "Root|Taxa_91", "metadata": null},{"id": "Root|Taxa_92", "metadata": null},{"id": "Root|Taxa_93", "metadata": null},{"id": "Root|Taxa_94", "metadata": null},{"id": "Root|Taxa_95", "metadata": null},{"id": "Root|Taxa_96", "metadata": null},{"id": "Root|Taxa_97", "metadata": null},{"id": "Root|Taxa_98", "metadata": null},{"id": "Root|Taxa_99", "metadata": null},{"id": "Root|Taxa_100", "metadata": null},{"id": "Root|Taxa_101", "metadata": null},{"id": "Root|Taxa_102", "metadata": null},{"id": "Root|Taxa_103", "metadata": null},{"id": "Root|Taxa_104", "metadata": null},{"id": "Root|Taxa_105", "metadata": null},{"id": "Root|Taxa_106", "metadata": null},{"id": "Root|Taxa_107", "metadata": null},{"id": "Root|Taxa_108", "metadata": null},{"id": "Root|Taxa_109", "metadata": null},{"id": "Root|Taxa_110", "metadata": null},{"id": "Root|Taxa_111", "metadata": null},{"id": "Root|Taxa_112", "metadata": null},{"id": "Root|Taxa_113", "metadata": null},{"id": "Root|Taxa_114", "metadata": null},{"id": "Root|Taxa_115", "metadata": null},{"id": "Root|Taxa_116", "metadata": null},{"id": "Root|Taxa_117", "metadata": null},{"id": "Root|Taxa_118", "metadata": null},{"id": "Root|Taxa_119", "metadata": null},{"id": "Root|Taxa_120", "metadata": null},{"id": "Root|Taxa_121", "metadata": null},{"id": "Root|Taxa_122", "metadata": null},{"id": "Root|Taxa_123", "metadata": null},{"id": "Root|Taxa_124", "metadata": null},{"id": "Root|Taxa_125", "metadata": null},{"id": "Root|Taxa_126", "metadata": null},{"id": "Root|Taxa_127", "metadata": null},{"id": "Root|Taxa_128", "metadata": null},{"id": "Root|Taxa_129", "metadata": null},{"id": "Root|Taxa_130", "metadata": null},{"id": "Root|Taxa_131", "metadata": null},{"id": "Root|Taxa_132", "metadata": null},{"id": "Root|Taxa_133", "metadata": null},{"id": "Root|Taxa_134", "metadata": null},{"id": "Root|Taxa_135", "metadata": null},{"id": "Root|Taxa_136", "metadata": null},{"id": "Root|Taxa_137", "metadata": null},{"id": "Root|Taxa_138", "metadata": null},{"id": "Root|Taxa_139", "metadata": null},{"id": "Root|Taxa_140", "metadata": null},{"id": "Root|Taxa_141", "metadata": null},{"id": "Root|Taxa_142", "metadata": null},{"id": "Root|Taxa_143", "metadata": null},{"id": "Root|Taxa_144", "metadata": null},{"id": "Root|Taxa_145", "metadata": null},{"id": "Root|Taxa_146", "metadata": null},{"id": "Root|Taxa_147", "metadata": null},{"id": "Root|Taxa_148", "metadata": null},{"id": "Root|Taxa_149", "metadata": null},{"id": "Root|Taxa_150", "metadata": null},{"id": "Root|Taxa_151", "metadata": null},{"id": "Root|Taxa_152", "metadata": null},{"id": "Root|Taxa_153", "metadata": null},{"id": "Root|Taxa_154", "metadata": null},{"id": "Root|Taxa_155", "metadata": null},{"id": "Root|Taxa_156", "metadata": null},{"id": "Root|Taxa_157", "metadata": null},{"id": "Root|Taxa_158", "metadata": null},{"id": "Root|Taxa_159", "metadata": null},{"id": "Root|Taxa_160", "metadata": null},{"id": "Root|Taxa_161", "metadata": null},{"id": "Root|Taxa_162", "metadata": null},{"id": "Root|Taxa_163", "metadata": null},{"id": "Root|Taxa_164", "metadata": null},{"id": "Root|Taxa_165", "metadata": null},{"id": "Root|Taxa_166", "metadata": null},{"id": "Root|Taxa_167", "metadata": null},{"id": "Root|Taxa_168", "metadata": null},{"id": "Root|Taxa_169", "metadata": null},{"id": "Root|Taxa_170", "metadata": null},{"id": "Root|Taxa_171", "metadata": null},{"id": "Root|Taxa_172", "metadata": null},{"id": "Root|Taxa_173", "metadata": null},{"id": "Root|Taxa_174", "metadata": null},{"id": "Root|Taxa_175", "metadata": null},{"id": "Root|Taxa_176", "metadata": null},{"id": "Root|Taxa_177", "metadata": null},{"id": "Root|Taxa_178", "metadata": null},{"id": "Root|Taxa_179", "metadata": null},{"id": "Root|Taxa_180", "metadata": null},{"id": "Root|Taxa_181", "metadata": null},{"id": "Root|Taxa_182", "metadata": null},{"id": "Root|Taxa_183", "metadata": null},{"id": "Root|Taxa_184", "metadata": null},{"id": "Root|Taxa_185", "metadata": null},{"id": "Root|Taxa_186", "metadata": null},{"id": "Root|Taxa_187", "metadata": null},{"id": "Root|Taxa_188", "metadata": null},{"id": "Root|Taxa_189", "metadata": null},{"id": "Root|Taxa_190", "metadata": null},{"id": "Root|Taxa_191", "metadata": null},{"id": "Root|Taxa_192", "metadata": null},{"id": "Root|Taxa_193", "metadata": null},{"id": "Root|Taxa_194", "metadata": null},{"id": "Root|Taxa_195", "metadata": null},{"id": "Root|Taxa_196", "metadata": null},{"id": "Root|Taxa_197", "metadata": null},{"id": "Root|Taxa_198", "metadata": null},{"id": "Root|Taxa_199", "metadata": null},{"id": "Root|Taxa_200", "metadata": null},{"id": "Root|Taxa_201", "metadata": null},{"id": "Root|Taxa_202", "metadata": null},{"id": "Root|Taxa_203", "metadata": null},{"id": "Root|Taxa_204", "metadata": null},{"id": "Root|Taxa_205", "metadata": null},{"id": "Root|Taxa_206", "metadata": null},{"id": "Root|Taxa_207", "metadata": null},{"id": "Root|Taxa_208", "metadata": null},{"id": "Root|Taxa_209", "metadata": null},{"id": "Root|Taxa_210", "metadata": null},{"id": "Root|Taxa_211", "metadata": null},{"id": "Root|Taxa_212", "metadata": null},{"id": "Root|Taxa_213", "metadata": null},{"id": "Root|Taxa_214", "metadata": null},{"id": "Root|Taxa_215", "metadata": null},{"id": "Root|Taxa_216", "metadata": null},{"id": "Root|Taxa_217", "metadata": null},{"id": "Root|Taxa_218", "metadata": null},{"id": "Root|Taxa_219", "metadata": null},{"id": "Root|Taxa_220", "metadata": null},{"id": "Root|Taxa_221", "metadata": null},{"id": "Root|Taxa_222", "metadata": null},{"id": "Root|Taxa_223", "metadata": null}],"columns": [{"id": "Sample_0_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_0_D", "Label": "Class-One"}},{"id": "Sample_1_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_1_D", "Label": "Class-One"}},{"id": "Sample_2_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_2_D", "Label": "Class-One"}},{"id": "Sample_3_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_3_D", "Label": "Class-Two"}},{"id": "Sample_4_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_4_D", "Label": "Class-One"}},{"id": "Sample_5_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_5_D", "Label": "Class-One"}},{"id": "Sample_6_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_6_D", "Label": "Class-One"}},{"id": "Sample_7_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_7_D", "Label": "Class-Two"}},{"id": "Sample_8_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_8_D", "Label": "Class-Two"}},{"id": "Sample_9_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_9_D", "Label": "Class-One"}},{"id": "Sample_10_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_10_D", "Label": "Class-Two"}},{"id": "Sample_11_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_11_D", "Label": "Class-Two"}},{"id": "Sample_12_D", "metadata": {"Group": "Complex", "Old": "Class-One", "ID": "Sample_12_D", "Label": "Class-Two"}},{"id": "Sample_13_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_13_D", "Label": "Class-Two"}},{"id": "Sample_14_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_14_D", "Label": "Class-Two"}},{"id": "Sample_15_D", "metadata": {"Group": "Complex", "Old": "Class-Two", "ID": "Sample_15_D", "Label": "Class-One"}},{"id": "Sample_16_R", "metadata": {"Group": "Moderate_Dissimilarity_Feature", "Old": "Class-Two", "ID": "Sample_16_R", "Label": "Class-Two"}},{"id": "Sample_17_R", "metadata": {"Group": "Moderate_Dissimilarity_Feature", "Old": "Class-Two", "ID": "Sample_17_R", "Label": "Class-Two"}},{"id": "Sample_18_R", "metadata": {"Group": "Moderate_Dissimilarity_Feature", "Old": "Class-Two", "ID": "Sample_18_R", "Label": "Class-Two"}},{"id": "Sample_19_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-Two", "ID": "Sample_19_R", "Label": "Class-Two"}},{"id": "Sample_20_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_20_R", "Label": "Class-One"}},{"id": "Sample_21_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_21_R", "Label": "Class-One"}},{"id": "Sample_22_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_22_R", "Label": "Class-One"}},{"id": "Sample_23_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_23_R", "Label": "Class-One"}},{"id": "Sample_24_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_24_R", "Label": "Class-One"}},{"id": "Sample_25_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-Two", "ID": "Sample_25_R", "Label": "Class-Two"}},{"id": "Sample_26_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_26_R", "Label": "Class-One"}},{"id": "Sample_27_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-Two", "ID": "Sample_27_R", "Label": "Class-Two"}},{"id": "Sample_28_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-Two", "ID": "Sample_28_R", "Label": "Class-Two"}},{"id": "Sample_29_R", "metadata": {"Group": "Moderate_Dissimilarity", "Old": "Class-One", "ID": "Sample_29_R", "Label": "Class-One"}},{"id": "Sample_30_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-Two", "ID": "Sample_30_E", "Label": "Class-Two"}},{"id": "Sample_31_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-Two", "ID": "Sample_31_E", "Label": "Class-Two"}},{"id": "Sample_32_E", "metadata": {"Group": "High_Dissimilarity_Feature", "Old": "Class-Two", "ID": "Sample_32_E", "Label": "Class-Two"}},{"id": "Sample_33_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-Two", "ID": "Sample_33_E", "Label": "Class-Two"}},{"id": "Sample_34_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_34_E", "Label": "Class-One"}},{"id": "Sample_35_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_35_E", "Label": "Class-One"}},{"id": "Sample_36_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_36_E", "Label": "Class-One"}},{"id": "Sample_37_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_37_E", "Label": "Class-One"}},{"id": "Sample_38_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_38_E", "Label": "Class-One"}},{"id": "Sample_39_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-Two", "ID": "Sample_39_E", "Label": "Class-Two"}},{"id": "Sample_40_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_40_E", "Label": "Class-One"}},{"id": "Sample_41_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-Two", "ID": "Sample_41_E", "Label": "Class-Two"}},{"id": "Sample_42_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-Two", "ID": "Sample_42_E", "Label": "Class-Two"}},{"id": "Sample_43_E", "metadata": {"Group": "High_Dissimilarity", "Old": "Class-One", "ID": "Sample_43_E", "Label": "Class-One"}},{"id": "Sample_44_T", "metadata": {"Group": "Targeted_Feature", "Old": "Class-Two", "ID": "Sample_44_T", "Label": "Class-Two"}},{"id": "Sample_45_T", "metadata": {"Group": "Targeted_Feature", "Old": "Class-Two", "ID": "Sample_45_T", "Label": "Class-Two"}},{"id": "Sample_46_T", "metadata": {"Group": "Targeted_Feature", "Old": "Class-Two", "ID": "Sample_46_T", "Label": "Class-Two"}},{"id": "Sample_47_T", "metadata": {"Group": "Targeted_Feature", "Old": "Class-Two", "ID": "Sample_47_T", "Label": "Class-Two"}}]} \ No newline at end of file diff -r 000000000000 -r 0de566f21448 input/Test2.biom --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/input/Test2.biom Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +{"id": "None","format": "Biological Observation Matrix 1.0.0","format_url": "http://biom-format.org","type": "OTU table","generated_by": "BreadCrumbs","date": "2013-10-08T12:59:54.028525","matrix_type": "sparse","matrix_element_type": "float","shape": [224, 48],"data": [[0,1,0.13263911008834839],[0,2,51.672050476074219],[0,6,51.744468688964844],[0,12,51.208335876464844],[0,13,52.149501800537109],[0,15,54.280982971191406],[0,16,51.682929992675781],[0,18,0.31236764788627625],[0,24,0.21669530868530273],[0,26,0.2411828488111496],[0,33,0.31222966313362122],[0,47,0.17723028361797333],[1,0,54.099388122558594],[1,2,0.44196206331253052],[1,6,50.664783477783203],[1,7,53.447223663330078],[1,8,50.581748962402344],[1,10,53.541297912597656],[1,16,54.532711029052734],[1,28,0.30667436122894287],[1,30,0.4312838613986969],[1,32,0.37312093377113342],[1,45,0.12748651206493378],[2,0,51.461025238037109],[2,5,53.026569366455078],[2,10,54.047443389892578],[2,12,52.526435852050781],[2,16,54.940284729003906],[2,32,0.42233455181121826],[2,37,0.460744708776474],[2,39,0.34424543380737305],[3,16,53.750659942626953],[3,18,0.41481569409370422],[3,22,0.26857671141624451],[3,28,0.36630621552467346],[3,44,50.526988983154297],[3,45,51.854343414306641],[3,46,53.768180847167969],[3,47,52.634426116943359],[4,0,50.240570068359375],[4,1,51.777767181396484],[4,3,54.845878601074219],[4,11,53.880828857421875],[4,16,50.028224945068359],[4,20,0.098072387278079987],[4,22,0.38868135213851929],[4,28,0.4286598265171051],[4,32,0.47316429018974304],[4,37,0.15683920681476593],[5,5,51.951015472412109],[5,6,0.22961589694023132],[5,7,52.969863891601562],[5,11,52.197437286376953],[5,14,0.22526906430721283],[5,16,53.653339385986328],[5,25,0.39630568027496338],[5,28,0.06783304363489151],[6,5,54.567390441894531],[6,10,51.190956115722656],[6,11,0.25222322344779968],[6,13,52.400714874267578],[6,14,54.537200927734375],[6,16,51.323921203613281],[6,29,0.17403842508792877],[6,41,0.052579630166292191],[7,2,54.889991760253906],[7,3,50.592502593994141],[7,7,54.676334381103516],[7,8,0.11892234534025192],[7,9,50.23931884765625],[7,16,54.696922302246094],[7,19,0.26401954889297485],[7,25,0.093931779265403748],[7,29,0.0068426746875047684],[7,33,0.18845303356647491],[8,0,50.167209625244141],[8,2,52.330379486083984],[8,7,0.26085031032562256],[8,8,51.234970092773438],[8,9,53.837917327880859],[8,16,50.158138275146484],[8,17,0.00067086971830576658],[8,30,102.35082244873047],[8,37,0.25920611619949341],[8,38,0.27336984872817993],[9,2,54.295417785644531],[9,3,0.39004826545715332],[9,4,53.497718811035156],[9,6,52.853614807128906],[9,13,53.070518493652344],[9,14,53.997322082519531],[9,15,52.567996978759766],[9,16,52.433670043945312],[9,18,0.44207152724266052],[9,26,0.20625883340835571],[9,28,0.22174246609210968],[9,30,102.36073303222656],[10,1,52.030044555664062],[10,4,50.834751129150391],[10,6,54.687831878662109],[10,8,52.774848937988281],[10,10,0.17284990847110748],[10,13,0.34940233826637268],[10,15,53.292324066162109],[10,16,52.094409942626953],[10,30,103.40880584716797],[10,39,0.2798474133014679],[11,5,50.226642608642578],[11,10,54.800552368164062],[11,13,0.26145485043525696],[11,14,54.634014129638672],[11,16,53.305400848388672],[11,30,104.29749298095703],[11,31,0.41477712988853455],[11,43,0.2095019668340683],[11,44,0.2636646032333374],[11,46,0.14962244033813477],[12,1,51.245124816894531],[12,4,52.189498901367188],[12,8,0.14854021370410919],[12,11,51.457836151123047],[12,15,53.714405059814453],[12,16,52.488780975341797],[12,18,0.49128276109695435],[12,21,0.040838729590177536],[12,23,0.12988743185997009],[12,28,0.40605998039245605],[12,30,100.32480621337891],[12,45,0.11192221939563751],[13,0,0.30497664213180542],[13,5,0.25770536065101624],[13,7,0.033960685133934021],[13,8,54.769084930419922],[13,9,50.697971343994141],[13,12,53.864070892333984],[13,14,52.003578186035156],[13,16,53.642868041992188],[13,24,0.42540961503982544],[13,25,0.22494150698184967],[13,27,0.18784832954406738],[13,30,102.10746002197266],[14,1,52.225990295410156],[14,3,50.245712280273438],[14,7,50.186416625976562],[14,16,50.970901489257812],[14,17,0.24925577640533447],[14,30,101.39744567871094],[14,43,0.15557312965393066],[15,3,51.428195953369141],[15,4,52.69622802734375],[15,6,0.32013928890228271],[15,9,54.775680541992188],[15,11,50.501811981201172],[15,12,54.193744659423828],[15,13,53.206466674804688],[15,16,54.25494384765625],[15,30,102.28659820556641],[15,38,0.47058898210525513],[16,0,51.002975463867188],[16,4,51.060733795166016],[16,5,53.286212921142578],[16,9,51.913478851318359],[16,10,54.854099273681641],[16,14,54.063098907470703],[16,15,54.535636901855469],[16,17,50.582279205322266],[17,6,0.20488639175891876],[17,17,53.088150024414062],[17,35,0.035023260861635208],[17,39,0.35104057192802429],[18,1,51.990985870361328],[18,2,54.274215698242188],[18,3,53.165328979492188],[18,6,50.877193450927734],[18,7,50.90771484375],[18,8,54.972938537597656],[18,10,0.13535825908184052],[18,13,50.270320892333984],[18,17,53.454654693603516],[18,31,0.19759136438369751],[18,38,0.25250700116157532],[18,47,0.36680755019187927],[19,17,53.208427429199219],[19,18,0.031785517930984497],[19,23,0.073154278099536896],[19,25,0.27936533093452454],[19,29,0.32328686118125916],[19,44,51.327350616455078],[19,45,51.795059204101562],[19,46,51.514873504638672],[19,47,50.144603729248047],[20,3,54.290149688720703],[20,5,52.560630798339844],[20,6,52.373138427734375],[20,7,54.452667236328125],[20,14,52.098415374755859],[20,17,51.751327514648438],[20,21,0.36221933364868164],[20,30,0.16919450461864471],[21,0,50.282546997070312],[21,6,50.982170104980469],[21,11,51.380966186523438],[21,13,52.272113800048828],[21,17,50.870861053466797],[21,20,0.19776757061481476],[21,26,0.4552416205406189],[21,31,0.34358644485473633],[21,34,0.48803713917732239],[21,36,0.053676586598157883],[22,4,52.302909851074219],[22,5,52.598339080810547],[22,6,54.505264282226562],[22,9,54.188888549804688],[22,10,50.026618957519531],[22,11,50.222110748291016],[22,12,50.663322448730469],[22,13,51.357936859130859],[22,15,52.134590148925781],[22,17,53.373260498046875],[22,23,0.3609204888343811],[22,27,0.49932181835174561],[22,38,0.15913538634777069],[23,0,54.615615844726562],[23,1,0.48862737417221069],[23,7,52.510749816894531],[23,8,52.946086883544922],[23,10,0.39570984244346619],[23,17,52.653926849365234],[23,29,0.23033072054386139],[23,30,0.23117353022098541],[23,47,0.063020221889019012],[24,0,52.526584625244141],[24,7,51.618122100830078],[24,9,52.527744293212891],[24,10,52.562164306640625],[24,11,0.42123815417289734],[24,17,50.036830902099609],[24,31,102.75434875488281],[25,7,0.13728067278862],[25,11,52.016689300537109],[25,17,53.928443908691406],[25,21,0.21439807116985321],[25,30,0.40063384175300598],[25,31,100.17539978027344],[25,39,0.46024471521377563],[26,1,54.780891418457031],[26,7,0.2249763160943985],[26,9,0.48445135354995728],[26,14,50.698005676269531],[26,17,51.896354675292969],[26,31,104.48031616210938],[27,1,52.181842803955078],[27,3,0.0091068772599101067],[27,17,52.071174621582031],[27,18,0.34459295868873596],[27,31,101.49213409423828],[27,34,0.23583111166954041],[27,37,0.34782198071479797],[27,42,0.029288522899150848],[28,2,52.090179443359375],[28,3,52.273052215576172],[28,4,50.097866058349609],[28,12,51.104297637939453],[28,13,50.339679718017578],[28,15,51.857917785644531],[28,17,54.147850036621094],[28,23,0.19240042567253113],[28,31,103.86058807373047],[28,36,0.29582729935646057],[28,45,0.24325108528137207],[29,2,50.625392913818359],[29,8,51.463695526123047],[29,17,52.443458557128906],[29,26,0.02061726339161396],[29,28,0.4787711501121521],[29,31,103.66421508789062],[29,37,0.089619070291519165],[29,38,0.048293761909008026],[29,39,0.37758848071098328],[30,3,53.270271301269531],[30,5,50.953044891357422],[30,6,0.39060020446777344],[30,8,52.564964294433594],[30,12,50.534202575683594],[30,15,53.352542877197266],[30,16,0.39554506540298462],[30,17,52.137779235839844],[30,31,104.56910705566406],[30,37,0.07115115225315094],[30,41,0.29691353440284729],[30,46,0.082002714276313782],[31,1,53.416091918945312],[31,2,50.241264343261719],[31,4,52.750377655029297],[31,5,0.26600098609924316],[31,9,51.415676116943359],[31,10,52.349578857421875],[31,11,50.429630279541016],[31,12,53.074653625488281],[31,13,0.23127266764640808],[31,14,50.159145355224609],[31,17,52.674037933349609],[31,18,0.06841190904378891],[31,19,0.35549464821815491],[31,20,0.22106747329235077],[31,29,0.40446197986602783],[31,31,103.68357849121094],[31,35,0.10313024371862411],[32,1,50.575607299804688],[32,4,52.031375885009766],[32,5,53.711055755615234],[32,6,52.390720367431641],[32,7,53.884662628173828],[32,10,0.0046756584197282791],[32,12,52.474929809570312],[32,13,0.23978191614151001],[32,15,54.054447174072266],[32,18,54.835540771484375],[32,19,0.12978900969028473],[32,26,0.45939821004867554],[32,35,0.35066312551498413],[32,36,0.46294641494750977],[33,3,50.878131866455078],[33,4,0.49990585446357727],[33,9,51.351829528808594],[33,10,0.37612366676330566],[33,11,0.39440682530403137],[33,18,50.317455291748047],[33,21,0.15044829249382019],[33,23,0.12714003026485443],[33,33,0.25482487678527832],[34,0,53.320610046386719],[34,5,53.359424591064453],[34,6,54.755043029785156],[34,10,0.20374700427055359],[34,11,54.108608245849609],[34,18,54.111351013183594],[34,19,0.31384673714637756],[34,31,0.34905159473419189],[34,36,0.19817408919334412],[34,39,0.12082114070653915],[35,0,51.574825286865234],[35,1,51.855949401855469],[35,3,54.080478668212891],[35,10,54.739360809326172],[35,11,53.375148773193359],[35,12,53.000400543212891],[35,13,50.951164245605469],[35,17,0.42627128958702087],[35,18,51.452655792236328],[35,25,0.29797333478927612],[35,28,0.41117364168167114],[35,37,0.068900987505912781],[35,43,0.38085871934890747],[35,44,0.36821377277374268],[35,46,0.34612327814102173],[36,4,51.7955322265625],[36,5,50.7825927734375],[36,6,0.074825257062911987],[36,9,54.952423095703125],[36,11,0.35481184720993042],[36,12,51.870368957519531],[36,15,51.05682373046875],[36,18,53.387546539306641],[36,34,0.052977506071329117],[37,2,52.993377685546875],[37,6,54.690509796142578],[37,8,52.792209625244141],[37,14,50.304485321044922],[37,18,54.747543334960938],[37,25,0.045788772404193878],[37,26,0.38097414374351501],[37,41,0.22798745334148407],[38,2,51.42706298828125],[38,4,54.678600311279297],[38,6,54.820697784423828],[38,8,53.938587188720703],[38,13,51.412956237792969],[38,14,53.126502990722656],[38,18,54.007125854492188],[38,23,0.093990691006183624],[38,24,0.36732301115989685],[38,36,0.47896763682365417],[39,9,0.23879404366016388],[39,18,54.162742614746094],[39,25,0.27959668636322021],[40,3,50.021083831787109],[40,8,0.21509011089801788],[40,13,52.186756134033203],[40,14,53.62646484375],[40,18,52.949188232421875],[40,27,0.25075152516365051],[40,32,100.86255645751953],[40,46,0.21088157594203949],[41,0,51.497703552246094],[41,1,52.916496276855469],[41,2,0.35341593623161316],[41,5,0.11574866622686386],[41,8,51.701892852783203],[41,9,0.13458873331546783],[41,11,51.998493194580078],[41,12,0.30815255641937256],[41,14,0.06155196949839592],[41,18,53.300819396972656],[41,32,104.43079376220703],[41,39,0.28674045205116272],[42,7,50.100700378417969],[42,9,54.946121215820312],[42,18,53.037506103515625],[42,22,0.43193840980529785],[42,24,0.17162227630615234],[42,32,102.19640350341797],[43,18,51.044620513916016],[43,26,0.07927926629781723],[43,32,104.88037109375],[43,44,51.371574401855469],[43,45,52.904411315917969],[43,46,51.740909576416016],[43,47,50.500438690185547],[44,1,53.951423645019531],[44,2,53.797454833984375],[44,5,0.21723970770835876],[44,7,54.460433959960938],[44,10,54.686206817626953],[44,18,53.194450378417969],[44,20,0.17020769417285919],[44,28,0.10002410411834717],[44,31,0.43790563941001892],[44,32,102.22212982177734],[44,41,0.070241943001747131],[45,0,0.44143962860107422],[45,5,53.344242095947266],[45,8,52.043350219726562],[45,18,51.009937286376953],[45,32,102.92622375488281],[45,41,0.008315584622323513],[46,0,50.8759765625],[46,2,53.207527160644531],[46,3,50.674629211425781],[46,4,51.236316680908203],[46,5,0.052973974496126175],[46,9,51.711841583251953],[46,10,52.478534698486328],[46,14,52.866855621337891],[46,15,52.624881744384766],[46,18,53.253269195556641],[46,22,0.20683418214321136],[46,24,0.050955794751644135],[46,32,101.58198547363281],[46,35,0.39298108220100403],[46,40,0.40840697288513184],[47,7,51.7620849609375],[47,10,51.318531036376953],[47,11,50.13214111328125],[47,12,51.764583587646484],[47,13,53.272975921630859],[47,15,53.236888885498047],[47,18,54.633903503417969],[47,24,0.27216443419456482],[47,31,0.1529640406370163],[47,32,102.17061614990234],[47,40,0.48087078332901001],[47,44,0.025993958115577698],[48,0,52.426708221435547],[48,3,54.121410369873047],[48,4,50.092826843261719],[48,6,53.477237701416016],[48,7,54.786056518554688],[48,11,51.969760894775391],[48,16,0.29127556085586548],[48,19,52.4029541015625],[48,32,0.17309793829917908],[49,1,54.141971588134766],[49,5,0.31099069118499756],[49,7,0.2574068009853363],[49,12,51.832080841064453],[49,19,54.683048248291016],[49,20,0.27364835143089294],[49,23,0.15304872393608093],[49,35,0.48234385251998901],[50,1,0.41244181990623474],[50,2,52.669319152832031],[50,4,0.4591352641582489],[50,7,50.00762939453125],[50,16,0.089165627956390381],[50,19,52.060661315917969],[50,34,0.45611995458602905],[51,2,0.42902174592018127],[51,3,0.36514613032341003],[51,5,52.193714141845703],[51,11,54.882003784179688],[51,12,50.988983154296875],[51,19,53.729080200195312],[51,26,0.10773327946662903],[51,32,0.23969066143035889],[51,37,0.12089108675718307],[51,45,0.11706044524908066],[52,0,54.837665557861328],[52,2,50.680797576904297],[52,6,50.242336273193359],[52,9,53.12158203125],[52,12,53.079925537109375],[52,19,51.035022735595703],[52,27,0.21839159727096558],[52,29,0.037748463451862335],[53,0,0.40822666883468628],[53,1,53.888378143310547],[53,2,0.44155597686767578],[53,5,54.638629913330078],[53,8,51.269351959228516],[53,10,54.622165679931641],[53,16,0.30414998531341553],[53,18,0.37247315049171448],[53,19,54.412796020507812],[53,20,0.13671597838401794],[53,41,0.39803388714790344],[54,0,0.32731133699417114],[54,3,54.9263916015625],[54,9,0.30886456370353699],[54,13,0.060517732053995132],[54,15,50.044486999511719],[54,19,54.63714599609375],[54,26,0.47719183564186096],[54,27,0.27686047554016113],[54,37,0.033551629632711411],[55,2,50.065605163574219],[55,5,0.017944198101758957],[55,14,0.1389617919921875],[55,15,54.933082580566406],[55,19,52.576915740966797],[55,27,0.17821782827377319],[55,32,0.34076377749443054],[55,40,0.47821888327598572],[55,41,0.45426815748214722],[55,45,0.48750394582748413],[56,0,54.598953247070312],[56,4,53.242694854736328],[56,5,50.250740051269531],[56,9,53.954669952392578],[56,13,53.889816284179688],[56,14,52.02008056640625],[56,19,50.128131866455078],[56,23,0.32643041014671326],[56,24,0.24274888634681702],[56,32,0.29801353812217712],[56,33,102.34198760986328],[56,41,0.20575700700283051],[56,42,0.19268074631690979],[57,1,51.354415893554688],[57,5,50.455276489257812],[57,8,53.913833618164062],[57,9,53.496509552001953],[57,12,0.44028562307357788],[57,14,50.987522125244141],[57,17,0.28433093428611755],[57,19,52.833759307861328],[57,33,100.00974273681641],[57,45,0.48467403650283813],[58,0,0.43709355592727661],[58,7,53.569564819335938],[58,8,53.592277526855469],[58,10,52.4207763671875],[58,12,0.08282383531332016],[58,13,54.043624877929688],[58,15,54.143932342529297],[58,19,53.610965728759766],[58,22,0.24731944501399994],[58,28,0.12936684489250183],[58,33,102.81724548339844],[59,0,54.187778472900391],[59,1,54.873485565185547],[59,2,51.963199615478516],[59,4,53.789318084716797],[59,11,52.966205596923828],[59,12,0.38438034057617188],[59,19,50.593837738037109],[59,33,103.81158447265625],[59,41,0.16972598433494568],[59,44,0.43379676342010498],[60,3,51.646255493164062],[60,6,52.195060729980469],[60,7,52.214561462402344],[60,8,53.811183929443359],[60,9,54.627727508544922],[60,13,0.40979909896850586],[60,19,54.478790283203125],[60,33,100.14827728271484],[61,5,0.2040732353925705],[61,9,0.27831506729125977],[61,13,50.5350341796875],[61,14,51.030941009521484],[61,19,51.882293701171875],[61,31,0.062283892184495926],[61,32,0.36123722791671753],[61,33,102.43135070800781],[61,38,0.14927451312541962],[61,45,0.1621554046869278],[61,47,0.43460196256637573],[62,3,50.670333862304688],[62,6,54.485752105712891],[62,10,54.087116241455078],[62,11,50.599266052246094],[62,12,53.7767333984375],[62,15,51.389842987060547],[62,19,50.364398956298828],[62,29,0.48533239960670471],[62,31,0.44734451174736023],[62,33,102.42562103271484],[63,0,0.45294782519340515],[63,4,54.237972259521484],[63,10,53.752510070800781],[63,12,0.43608000874519348],[63,13,53.317527770996094],[63,14,54.264698028564453],[63,19,54.001842498779297],[63,33,103.87953948974609],[63,44,0.20028656721115112],[64,7,51.067226409912109],[64,8,52.728569030761719],[64,20,53.313385009765625],[64,25,0.12246885150671005],[64,29,0.21952486038208008],[64,45,0.45129898190498352],[65,5,54.28265380859375],[65,6,0.05467228963971138],[65,7,50.587398529052734],[65,12,50.740871429443359],[65,13,51.922332763671875],[65,15,51.465606689453125],[65,20,50.605960845947266],[65,31,0.031305234879255295],[65,33,0.024850502610206604],[65,38,0.18012678623199463],[66,4,50.169445037841797],[66,6,0.20981484651565552],[66,8,54.813095092773438],[66,9,52.181678771972656],[66,15,0.0087912138551473618],[66,20,52.455574035644531],[66,22,0.1946081817150116],[66,42,0.23115138709545135],[67,0,53.084690093994141],[67,1,54.412517547607422],[67,2,54.5238037109375],[67,9,54.595577239990234],[67,17,0.24150419235229492],[67,20,54.706981658935547],[67,21,0.29965484142303467],[67,39,0.042535740882158279],[67,43,0.46450555324554443],[68,0,51.399547576904297],[68,3,50.331794738769531],[68,4,51.044830322265625],[68,5,53.620193481445312],[68,8,53.409934997558594],[68,10,50.770175933837891],[68,11,54.268398284912109],[68,13,52.164340972900391],[68,14,54.044288635253906],[68,15,0.10323792695999146],[68,18,0.35222142934799194],[68,20,54.454067230224609],[68,22,0.11017544567584991],[68,33,0.43301385641098022],[68,38,0.18861587345600128],[69,0,50.964836120605469],[69,1,53.673618316650391],[69,2,51.590496063232422],[69,3,51.841209411621094],[69,5,54.298751831054688],[69,6,52.363498687744141],[69,10,51.151809692382812],[69,11,53.735935211181641],[69,14,54.791725158691406],[69,16,0.43252116441726685],[69,20,53.354568481445312],[69,27,0.069227688014507294],[69,33,0.38823777437210083],[69,38,0.29765713214874268],[69,42,0.026355072855949402],[70,4,50.721790313720703],[70,6,54.706615447998047],[70,20,50.631568908691406],[70,28,0.37249466776847839],[70,30,0.26735427975654602],[70,33,0.33283764123916626],[71,0,0.16204395890235901],[71,1,0.067454010248184204],[71,2,0.03590000793337822],[71,5,52.036930084228516],[71,10,53.277114868164062],[71,13,50.219799041748047],[71,15,0.19805477559566498],[71,20,52.680374145507812],[71,24,0.43417102098464966],[71,33,0.26464435458183289],[71,40,0.19235385954380035],[71,47,0.088784299790859222],[72,3,0.20847718417644501],[72,4,0.19112280011177063],[72,6,53.387794494628906],[72,11,54.860530853271484],[72,12,52.880657196044922],[72,13,0.46349427103996277],[72,15,53.785923004150391],[72,20,54.039096832275391],[72,24,0.20153769850730896],[72,34,104.81826782226562],[72,42,0.47012403607368469],[73,2,54.054355621337891],[73,12,52.507492065429688],[73,14,51.883331298828125],[73,20,50.780792236328125],[73,23,0.23294238746166229],[73,26,0.12204519659280777],[73,33,0.28289276361465454],[73,34,100.87276458740234],[73,43,0.45391014218330383],[73,44,0.44332057237625122],[73,46,0.11329615116119385],[73,47,0.31856563687324524],[74,2,54.380939483642578],[74,11,0.23045583069324493],[74,12,0.31302088499069214],[74,20,53.825958251953125],[74,23,0.057209685444831848],[74,32,0.016839217394590378],[74,34,103.90423583984375],[74,35,0.31137755513191223],[74,40,0.48662644624710083],[74,45,0.33841273188591003],[75,0,0.43075042963027954],[75,1,51.763355255126953],[75,4,51.421550750732422],[75,7,53.613151550292969],[75,8,0.030880527570843697],[75,12,52.351436614990234],[75,17,0.1696651428937912],[75,20,53.906696319580078],[75,23,0.12816387414932251],[75,34,102.95844268798828],[75,38,0.060918673872947693],[75,47,0.099397070705890656],[76,7,0.12065150588750839],[76,9,51.828189849853516],[76,10,0.1220819503068924],[76,15,50.829078674316406],[76,20,54.970966339111328],[76,33,0.42973843216896057],[76,34,104.08345031738281],[76,45,0.34263303875923157],[77,2,0.36012068390846252],[77,3,53.051681518554688],[77,10,52.427139282226562],[77,11,53.05377197265625],[77,14,51.980960845947266],[77,15,51.435840606689453],[77,20,53.81402587890625],[77,22,0.25189700722694397],[77,29,0.43795982003211975],[77,34,101.15478515625],[78,1,50.997764587402344],[78,2,0.20581555366516113],[78,3,50.301883697509766],[78,4,0.24851344525814056],[78,6,54.861274719238281],[78,9,54.741752624511719],[78,20,53.263320922851562],[78,31,0.11586814373731613],[78,34,103.56839752197266],[78,38,0.28377345204353333],[79,0,53.238864898681641],[79,7,54.499160766601562],[79,8,52.837394714355469],[79,13,50.343578338623047],[79,20,53.176166534423828],[79,21,0.17991693317890167],[79,26,0.32166054844856262],[79,27,0.090265832841396332],[79,34,102.05396270751953],[79,38,0.39411613345146179],[79,40,0.40245679020881653],[80,0,54.789260864257812],[80,1,54.531818389892578],[80,4,52.502426147460938],[80,6,0.28246057033538818],[80,7,54.152042388916016],[80,9,51.634788513183594],[80,10,54.289909362792969],[80,11,52.960609436035156],[80,12,51.967189788818359],[80,15,53.537857055664062],[80,19,0.16637007892131805],[80,21,50.401981353759766],[81,1,51.833786010742188],[81,2,54.404998779296875],[81,4,51.855743408203125],[81,5,52.522911071777344],[81,6,0.043628737330436707],[81,8,54.575859069824219],[81,11,0.19116869568824768],[81,12,0.023937981575727463],[81,13,51.948249816894531],[81,18,0.41399797797203064],[81,21,52.911853790283203],[81,29,0.36625263094902039],[81,42,0.39868706464767456],[82,6,50.398490905761719],[82,10,54.497608184814453],[82,14,51.010643005371094],[82,21,52.028373718261719],[82,30,0.13616576790809631],[82,43,0.075846493244171143],[83,3,50.379474639892578],[83,5,52.734336853027344],[83,6,53.019355773925781],[83,7,51.225788116455078],[83,8,0.00063199491705745459],[83,11,0.27203482389450073],[83,12,52.99078369140625],[83,17,0.22333556413650513],[83,20,0.10501588135957718],[83,21,54.945869445800781],[83,41,0.2172519713640213],[84,8,54.140850067138672],[84,9,52.83380126953125],[84,21,51.602252960205078],[85,1,52.323574066162109],[85,4,0.33209896087646484],[85,5,0.424825519323349],[85,8,51.746608734130859],[85,9,50.450359344482422],[85,11,54.591449737548828],[85,21,52.434597015380859],[85,33,0.29279392957687378],[85,45,0.084778569638729095],[86,7,50.890029907226562],[86,21,54.72119140625],[86,25,0.18605111539363861],[86,30,0.40991571545600891],[86,32,0.25392207503318787],[87,0,54.180183410644531],[87,1,0.27550530433654785],[87,2,54.492942810058594],[87,3,53.837730407714844],[87,7,52.404029846191406],[87,11,54.780128479003906],[87,12,54.040027618408203],[87,15,0.19667473435401917],[87,21,51.816287994384766],[87,32,0.46794068813323975],[87,36,0.29597935080528259],[88,3,53.939098358154297],[88,5,51.563343048095703],[88,6,51.295852661132812],[88,9,53.515281677246094],[88,14,54.401676177978516],[88,15,53.239032745361328],[88,16,0.41468554735183716],[88,21,54.413082122802734],[88,34,0.020496150478720665],[88,35,101.32682037353516],[88,45,0.10662335902452469],[88,46,0.036330036818981171],[89,0,53.054389953613281],[89,6,53.674266815185547],[89,11,50.771278381347656],[89,14,0.10191795974969864],[89,21,53.797538757324219],[89,23,0.097789809107780457],[89,29,0.099345296621322632],[89,35,100.99777221679688],[89,36,0.36196213960647583],[89,40,0.39049482345581055],[89,43,0.087204471230506897],[90,2,50.811542510986328],[90,4,50.754505157470703],[90,8,52.394111633300781],[90,12,51.824333190917969],[90,15,51.377239227294922],[90,21,52.467842102050781],[90,35,100.52031707763672],[90,38,0.21330057084560394],[90,39,0.12645332515239716],[90,41,0.17757473886013031],[91,1,0.14407473802566528],[91,3,52.242034912109375],[91,10,51.306304931640625],[91,13,54.502338409423828],[91,14,51.614372253417969],[91,21,53.261833190917969],[91,35,103.30667877197266],[91,46,0.25916048884391785],[92,1,54.621063232421875],[92,4,53.530540466308594],[92,10,54.356853485107422],[92,12,0.37603586912155151],[92,13,53.614501953125],[92,21,50.174091339111328],[92,26,0.18624916672706604],[92,35,102.86859893798828],[93,2,52.187129974365234],[93,7,0.4391171932220459],[93,11,0.37605097889900208],[93,19,0.20892411470413208],[93,20,0.17231622338294983],[93,21,51.927978515625],[93,35,103.48123168945312],[93,38,0.22005566954612732],[94,8,0.32566362619400024],[94,13,0.36496508121490479],[94,21,53.122978210449219],[94,25,0.15595875680446625],[94,26,0.40010342001914978],[94,30,0.32628187537193298],[94,33,0.20766395330429077],[94,35,101.02719116210938],[94,40,0.27072256803512573],[94,41,0.41707423329353333],[95,0,50.692886352539062],[95,5,53.830135345458984],[95,8,0.010356404818594456],[95,9,0.39863049983978271],[95,13,54.237724304199219],[95,14,52.369647979736328],[95,15,53.028778076171875],[95,19,0.0081133227795362473],[95,21,51.311847686767578],[95,35,100.43609619140625],[96,3,0.030608227476477623],[96,6,53.425811767578125],[96,9,53.459526062011719],[96,12,54.111068725585938],[96,22,51.115188598632812],[96,31,0.071831606328487396],[96,35,0.085618868470191956],[96,45,0.10887780785560608],[97,0,54.01318359375],[97,1,0.31625869870185852],[97,8,53.554191589355469],[97,10,50.853588104248047],[97,14,52.536506652832031],[97,16,0.19937156140804291],[97,22,51.267467498779297],[97,40,0.33015045523643494],[98,2,53.714900970458984],[98,3,0.41735139489173889],[98,4,51.107311248779297],[98,5,52.964694976806641],[98,6,50.296413421630859],[98,7,50.865592956542969],[98,10,50.670944213867188],[98,13,52.532917022705078],[98,18,0.12959572672843933],[98,22,50.697494506835938],[98,32,0.47315937280654907],[98,35,0.12483229488134384],[98,43,0.28223007917404175],[98,47,0.4313599169254303],[99,4,53.960617065429688],[99,8,53.230075836181641],[99,11,53.383071899414062],[99,19,0.24758689105510712],[99,22,52.893184661865234],[99,25,0.48672026395797729],[99,35,0.36889320611953735],[99,36,0.056371461600065231],[99,46,0.37127342820167542],[99,47,0.44187578558921814],[100,0,50.056869506835938],[100,1,53.882656097412109],[100,2,51.755844116210938],[100,8,52.124610900878906],[100,12,52.528915405273438],[100,15,0.1157214492559433],[100,22,54.124191284179688],[100,34,0.21082127094268799],[100,39,0.060194846242666245],[101,1,53.274940490722656],[101,2,0.34573149681091309],[101,5,54.422710418701172],[101,10,52.630062103271484],[101,12,54.415401458740234],[101,14,0.067188233137130737],[101,22,53.100719451904297],[101,34,0.090001866221427917],[101,36,0.17511479556560516],[101,37,0.47374668717384338],[102,0,52.695041656494141],[102,2,0.43547633290290833],[102,4,54.998683929443359],[102,5,0.053637061268091202],[102,6,0.39949232339859009],[102,11,51.415481567382812],[102,12,0.46643480658531189],[102,13,53.200283050537109],[102,14,51.817855834960938],[102,15,0.27121061086654663],[102,22,54.865547180175781],[102,24,0.37104740738868713],[102,33,0.38226845860481262],[102,36,0.0064234849996864796],[102,43,0.016287678852677345],[102,46,0.10179373621940613],[103,0,0.34259313344955444],[103,1,52.999362945556641],[103,11,52.309211730957031],[103,14,0.27679923176765442],[103,22,50.525413513183594],[103,32,0.015074462629854679],[103,40,0.39598149061203003],[103,43,0.19435745477676392],[104,3,53.471302032470703],[104,4,54.357948303222656],[104,5,52.695568084716797],[104,6,50.759651184082031],[104,8,0.17314149439334869],[104,9,52.144214630126953],[104,13,50.383598327636719],[104,19,0.40604761242866516],[104,22,50.733283996582031],[104,36,104.21462249755859],[105,1,54.780647277832031],[105,5,54.082668304443359],[105,8,0.067264735698699951],[105,9,52.167583465576172],[105,13,53.955451965332031],[105,15,52.430324554443359],[105,19,0.23343504965305328],[105,21,0.40988191962242126],[105,22,54.702846527099609],[105,24,0.2667853832244873],[105,36,101.79543304443359],[106,2,52.556545257568359],[106,9,53.661933898925781],[106,15,51.396152496337891],[106,17,0.21408958733081818],[106,20,0.29955717921257019],[106,21,0.22177949547767639],[106,22,52.570888519287109],[106,31,0.12338664382696152],[106,36,102.57060241699219],[106,42,0.41185289621353149],[106,44,0.37667000293731689],[107,0,54.530372619628906],[107,3,51.057956695556641],[107,10,52.939258575439453],[107,14,54.668304443359375],[107,15,0.33562737703323364],[107,17,0.4393523633480072],[107,22,53.854663848876953],[107,25,0.49056351184844971],[107,31,0.10358358174562454],[107,36,101.68701171875],[108,1,0.08312535285949707],[108,3,50.937732696533203],[108,7,53.572826385498047],[108,19,0.42503887414932251],[108,22,50.474151611328125],[108,36,101.55259704589844],[109,3,50.729171752929688],[109,4,0.14870145916938782],[109,6,50.319465637207031],[109,7,54.886299133300781],[109,8,51.069805145263672],[109,11,0.32244300842285156],[109,12,54.070755004882812],[109,15,52.748043060302734],[109,18,0.22737953066825867],[109,22,54.824081420898438],[109,36,104.10604095458984],[109,38,0.4804401695728302],[110,1,0.25515612959861755],[110,2,52.683059692382812],[110,8,0.31930544972419739],[110,9,0.21359248459339142],[110,11,52.243049621582031],[110,14,51.152660369873047],[110,15,50.841999053955078],[110,22,50.415149688720703],[110,23,0.13062088191509247],[110,26,0.21868458390235901],[110,29,0.47872477769851685],[110,36,102.88645172119141],[110,38,0.2203846275806427],[110,39,0.22828556597232819],[110,44,0.082453601062297821],[110,47,0.13505487143993378],[111,3,0.45335984230041504],[111,7,54.095252990722656],[111,9,0.24641448259353638],[111,15,0.27343785762786865],[111,22,54.349334716796875],[111,36,100.44292449951172],[112,0,52.879650115966797],[112,8,52.843166351318359],[112,9,52.543598175048828],[112,12,0.25953185558319092],[112,13,52.128128051757812],[112,15,50.343433380126953],[112,17,0.20097506046295166],[112,18,0.27121621370315552],[112,23,52.698966979980469],[112,24,0.32894200086593628],[112,39,0.17759707570075989],[112,44,0.29781097173690796],[113,4,53.857349395751953],[113,5,54.100589752197266],[113,12,0.46474730968475342],[113,13,0.07784513384103775],[113,23,50.883525848388672],[113,29,0.037749331444501877],[113,35,0.21985806524753571],[113,45,0.048796392977237701],[114,3,51.301090240478516],[114,4,50.623580932617188],[114,8,54.329437255859375],[114,10,52.491527557373047],[114,12,50.220493316650391],[114,14,54.672706604003906],[114,16,0.10250360518693924],[114,21,0.4953540563583374],[114,23,53.594875335693359],[114,41,0.23760607838630676],[115,1,50.384647369384766],[115,2,0.045362472534179688],[115,3,52.281082153320312],[115,10,0.29621094465255737],[115,11,52.356552124023438],[115,15,53.24737548828125],[115,23,50.4842529296875],[116,2,50.789379119873047],[116,9,50.304706573486328],[116,10,52.728107452392578],[116,14,53.117267608642578],[116,23,51.515193939208984],[116,30,0.46672102808952332],[116,37,0.090131945908069611],[116,46,0.49115315079689026],[117,0,54.421501159667969],[117,1,51.706626892089844],[117,3,52.735115051269531],[117,6,53.563518524169922],[117,7,50.630947113037109],[117,11,50.329998016357422],[117,23,50.367393493652344],[117,44,0.062279839068651199],[118,3,50.050071716308594],[118,4,51.205711364746094],[118,5,0.32215797901153564],[118,8,54.68536376953125],[118,11,53.180103302001953],[118,12,0.34838578104972839],[118,18,0.025149665772914886],[118,21,0.44567438960075378],[118,22,0.13225488364696503],[118,23,50.38104248046875],[118,24,0.3577083945274353],[118,27,0.1275077611207962],[118,35,0.06021595373749733],[119,0,51.922080993652344],[119,5,54.625232696533203],[119,6,51.184844970703125],[119,9,52.4525146484375],[119,10,0.005679671186953783],[119,11,52.674484252929688],[119,12,54.972072601318359],[119,15,54.142147064208984],[119,23,53.392631530761719],[119,37,0.002203882671892643],[119,43,0.17473334074020386],[120,1,0.053538531064987183],[120,6,53.988597869873047],[120,7,51.171550750732422],[120,9,50.735950469970703],[120,13,0.05746808648109436],[120,20,0.13002805411815643],[120,23,51.134681701660156],[120,24,0.070559576153755188],[120,34,0.12518115341663361],[120,37,101.99199676513672],[120,40,0.10033676773309708],[121,0,0.00081549858441576362],[121,2,0.32803145051002502],[121,4,0.18200397491455078],[121,8,0.45771273970603943],[121,10,54.109588623046875],[121,12,53.524177551269531],[121,23,50.207370758056641],[121,30,0.18400496244430542],[121,37,102.90958404541016],[121,40,0.18199367821216583],[122,0,50.868114471435547],[122,2,51.320735931396484],[122,3,0.0095748389139771461],[122,5,53.363616943359375],[122,12,52.18524169921875],[122,13,51.2623291015625],[122,17,0.062243327498435974],[122,23,53.704746246337891],[122,37,100.0345458984375],[123,8,0.013331643305718899],[123,10,50.808761596679688],[123,12,0.22454611957073212],[123,17,0.45568543672561646],[123,23,54.320903778076172],[123,25,0.3736911416053772],[123,37,104.07657623291016],[123,45,0.15636444091796875],[123,47,0.19383686780929565],[124,1,54.159458160400391],[124,8,52.486217498779297],[124,13,52.496543884277344],[124,15,54.905139923095703],[124,19,0.44909483194351196],[124,23,52.251819610595703],[124,29,0.038412421941757202],[124,30,0.051153946667909622],[124,37,101.02059173583984],[124,44,0.31091159582138062],[125,2,54.433586120605469],[125,5,0.49506545066833496],[125,6,54.747760772705078],[125,13,53.112277984619141],[125,14,54.023921966552734],[125,23,53.189060211181641],[125,24,0.44718253612518311],[125,37,101.14735412597656],[125,39,0.203592449426651],[125,47,0.19102291762828827],[126,1,52.137577056884766],[126,2,52.18658447265625],[126,5,52.137008666992188],[126,6,0.22218695282936096],[126,7,54.704837799072266],[126,11,0.26066681742668152],[126,14,52.935749053955078],[126,16,0.45621243119239807],[126,20,0.059009801596403122],[126,23,53.513607025146484],[126,29,0.25802215933799744],[126,37,101.60216522216797],[126,42,0.03847590833902359],[126,46,0.042616270482540131],[127,4,51.672172546386719],[127,7,50.045486450195312],[127,23,51.092029571533203],[127,37,104.95039367675781],[127,42,0.16674824059009552],[128,0,0.1331336498260498],[128,8,53.180416107177734],[128,24,50.410099029541016],[128,43,0.49212521314620972],[129,12,53.299465179443359],[129,14,50.859283447265625],[129,24,53.326473236083984],[129,25,0.013709104619920254],[129,41,0.45377001166343689],[129,47,0.25797092914581299],[130,1,50.257312774658203],[130,2,54.332328796386719],[130,6,53.318874359130859],[130,7,52.486927032470703],[130,9,52.196865081787109],[130,11,51.757404327392578],[130,13,52.986835479736328],[130,19,0.46736112236976624],[130,24,53.0377197265625],[130,28,0.016580682247877121],[130,36,0.033538851886987686],[130,43,0.19145593047142029],[131,0,52.053699493408203],[131,4,0.31075713038444519],[131,7,53.776603698730469],[131,8,51.115882873535156],[131,10,50.862159729003906],[131,15,54.472358703613281],[131,24,54.748001098632812],[131,43,0.44481962919235229],[132,5,0.17278917133808136],[132,7,50.880653381347656],[132,9,51.407012939453125],[132,12,51.505290985107422],[132,15,50.347358703613281],[132,24,53.432598114013672],[132,29,0.31053045392036438],[132,46,0.25831663608551025],[133,1,52.765411376953125],[133,3,50.063270568847656],[133,5,52.051422119140625],[133,6,50.602985382080078],[133,7,50.786102294921875],[133,9,52.409782409667969],[133,11,51.73419189453125],[133,13,52.062328338623047],[133,14,51.858837127685547],[133,15,53.942680358886719],[133,24,50.234031677246094],[133,32,0.29404768347740173],[133,37,0.27951601147651672],[133,42,0.43425586819648743],[134,0,54.276969909667969],[134,19,0.23784691095352173],[134,24,52.412876129150391],[134,27,0.27319535613059998],[134,39,0.048874702304601669],[134,42,0.076239615678787231],[135,2,52.735576629638672],[135,18,0.15757472813129425],[135,24,53.433120727539062],[135,35,0.46804985404014587],[135,44,0.29916101694107056],[136,0,50.166393280029297],[136,1,50.849384307861328],[136,3,52.907291412353516],[136,10,52.662441253662109],[136,12,52.909339904785156],[136,13,52.34637451171875],[136,24,52.931056976318359],[136,28,0.019280396401882172],[136,29,0.10480639338493347],[136,38,103.64399719238281],[136,39,0.34637901186943054],[136,47,0.4044988751411438],[137,2,50.327182769775391],[137,3,54.610786437988281],[137,4,54.705410003662109],[137,6,53.372650146484375],[137,7,0.43842431902885437],[137,24,54.835380554199219],[137,38,104.59642028808594],[138,4,50.350837707519531],[138,5,53.6026611328125],[138,6,0.27545422315597534],[138,11,50.117160797119141],[138,12,50.188751220703125],[138,13,0.33728539943695068],[138,19,0.11169642210006714],[138,24,53.128593444824219],[138,36,0.124480240046978],[138,38,102.39148712158203],[139,3,50.085166931152344],[139,4,53.060577392578125],[139,9,0.12909442186355591],[139,13,52.600631713867188],[139,24,54.355014801025391],[139,38,102.41065979003906],[139,42,0.17075303196907043],[140,0,54.95098876953125],[140,1,0.26438626646995544],[140,5,53.303680419921875],[140,7,0.47345653176307678],[140,9,51.940780639648438],[140,10,54.763111114501953],[140,14,53.214954376220703],[140,21,0.44816392660140991],[140,24,52.635643005371094],[140,30,0.40173816680908203],[140,37,0.34266853332519531],[140,38,101.12872314453125],[141,2,50.455741882324219],[141,11,53.772304534912109],[141,13,0.47157302498817444],[141,20,0.48297831416130066],[141,21,0.29707106947898865],[141,24,53.880500793457031],[141,38,101.64122009277344],[141,47,0.33726543188095093],[142,1,52.637054443359375],[142,4,51.220382690429688],[142,5,54.042507171630859],[142,6,51.780002593994141],[142,8,54.986316680908203],[142,10,0.044383767992258072],[142,16,0.17825473845005035],[142,20,0.097646474838256836],[142,24,50.588523864746094],[142,26,0.21880584955215454],[142,33,0.33337926864624023],[142,38,102.06330108642578],[143,2,0.29030194878578186],[143,3,0.35520610213279724],[143,4,0.38688176870346069],[143,8,52.350208282470703],[143,10,50.696144104003906],[143,12,0.3403821587562561],[143,14,52.338874816894531],[143,15,53.288547515869141],[143,24,54.037368774414062],[143,26,0.49111688137054443],[143,32,0.48745808005332947],[143,38,102.20440673828125],[144,2,51.850627899169922],[144,9,0.10377472639083862],[144,12,52.886978149414062],[144,14,52.226406097412109],[144,16,0.11893319338560104],[144,25,51.83380126953125],[144,45,0.39404311776161194],[145,1,52.782344818115234],[145,6,52.460922241210938],[145,10,50.098705291748047],[145,13,50.968242645263672],[145,15,50.926353454589844],[145,25,54.8807373046875],[145,36,0.41323855519294739],[146,4,53.753040313720703],[146,5,52.963123321533203],[146,8,53.566516876220703],[146,11,52.955905914306641],[146,25,50.576084136962891],[146,43,0.080164246261119843],[147,2,54.290760040283203],[147,7,54.286399841308594],[147,11,52.287242889404297],[147,15,52.078643798828125],[147,25,50.852863311767578],[148,4,0.10477688908576965],[148,11,0.29396480321884155],[148,13,52.455150604248047],[148,25,53.400215148925781],[148,32,0.19294361770153046],[148,36,0.1053827702999115],[148,46,0.40515834093093872],[149,0,54.277702331542969],[149,3,54.187393188476562],[149,9,53.443367004394531],[149,10,51.413013458251953],[149,19,0.41451776027679443],[149,22,0.10050428658723831],[149,25,53.206768035888672],[149,27,0.32073765993118286],[150,7,0.061268128454685211],[150,8,0.42440328001976013],[150,9,50.562667846679688],[150,11,0.011167231947183609],[150,12,53.973972320556641],[150,13,54.217571258544922],[150,20,0.37813559174537659],[150,25,51.471973419189453],[150,27,0.38516595959663391],[150,37,0.39796751737594604],[151,3,52.743160247802734],[151,7,50.2017822265625],[151,11,53.074806213378906],[151,12,50.522483825683594],[151,17,0.17542804777622223],[151,25,52.454639434814453],[151,26,0.3528670072555542],[151,40,0.3672558069229126],[152,0,53.067844390869141],[152,3,0.054698936641216278],[152,6,51.231521606445312],[152,11,50.411487579345703],[152,13,53.707023620605469],[152,14,52.769252777099609],[152,25,50.313407897949219],[152,32,0.11121632903814316],[152,39,102.15706634521484],[152,42,0.05461445078253746],[152,47,0.41112634539604187],[153,0,54.962947845458984],[153,1,50.557365417480469],[153,2,54.274642944335938],[153,3,51.623279571533203],[153,5,50.920490264892578],[153,6,52.165431976318359],[153,7,0.18840891122817993],[153,8,50.747920989990234],[153,9,0.022164536640048027],[153,10,53.107765197753906],[153,15,52.651214599609375],[153,17,0.29819890856742859],[153,21,0.080771535634994507],[153,25,50.137088775634766],[153,27,0.4542573094367981],[153,33,0.41928821802139282],[153,39,103.66280364990234],[153,41,0.39798888564109802],[154,3,50.163330078125],[154,4,54.322517395019531],[154,8,54.274196624755859],[154,9,0.39877820014953613],[154,11,0.39069300889968872],[154,25,50.116371154785156],[154,34,0.33650168776512146],[154,39,103.67110443115234],[155,1,54.185050964355469],[155,3,0.49079629778862],[155,4,54.982597351074219],[155,5,52.236049652099609],[155,7,0.21190515160560608],[155,8,52.558597564697266],[155,9,51.509361267089844],[155,14,53.357524871826172],[155,20,0.43588981032371521],[155,24,0.34942531585693359],[155,25,50.397495269775391],[155,30,0.12426097691059113],[155,35,0.46715068817138672],[155,38,0.18684974312782288],[155,39,104.22255706787109],[155,44,0.47122600674629211],[156,1,0.14882984757423401],[156,3,0.14266350865364075],[156,6,53.235622406005859],[156,7,52.332511901855469],[156,9,51.572513580322266],[156,12,50.050765991210938],[156,13,0.42156296968460083],[156,22,0.33571198582649231],[156,25,53.193996429443359],[156,35,0.36480662226676941],[156,39,103.45829010009766],[157,0,50.697872161865234],[157,2,50.673484802246094],[157,7,51.777725219726562],[157,14,53.729476928710938],[157,16,0.16446207463741302],[157,25,54.194187164306641],[157,31,0.34761896729469299],[157,34,0.36116567254066467],[157,39,102.50889587402344],[157,46,0.33942884206771851],[157,47,0.37074881792068481],[158,1,53.756160736083984],[158,3,0.17096838355064392],[158,4,51.986381530761719],[158,5,52.101390838623047],[158,15,52.537567138671875],[158,25,50.207359313964844],[158,28,0.10387121140956879],[158,39,104.7215576171875],[158,45,0.23709443211555481],[159,0,0.38743162155151367],[159,4,0.055291913449764252],[159,10,54.412464141845703],[159,14,0.22278584539890289],[159,18,0.38613465428352356],[159,25,51.020896911621094],[159,39,103.99785614013672],[159,40,0.29304406046867371],[159,41,0.2454792857170105],[160,2,52.941902160644531],[160,6,52.434963226318359],[160,7,53.101905822753906],[160,12,53.768020629882812],[160,13,50.937068939208984],[160,18,0.46170219779014587],[160,26,53.863956451416016],[161,6,50.946388244628906],[161,8,50.297325134277344],[161,9,52.358627319335938],[161,10,54.024665832519531],[161,11,53.579776763916016],[161,12,52.586288452148438],[161,14,54.661449432373047],[161,16,0.14509673416614532],[161,26,50.315567016601562],[161,29,0.42962554097175598],[161,35,0.46298211812973022],[161,40,0.1737198531627655],[161,41,0.26867172122001648],[161,43,0.097852014005184174],[162,0,53.148059844970703],[162,10,52.364547729492188],[162,13,53.907859802246094],[162,14,0.083070866763591766],[162,22,0.052251800894737244],[162,23,0.099839061498641968],[162,26,54.641803741455078],[162,27,0.10715925693511963],[163,8,0.10322535037994385],[163,14,0.13758742809295654],[163,15,51.620071411132812],[163,23,0.45113947987556458],[163,26,53.787357330322266],[163,31,0.29519319534301758],[163,34,0.18801470100879669],[164,0,0.4835112988948822],[164,3,50.371971130371094],[164,5,51.977123260498047],[164,11,50.354717254638672],[164,12,50.250221252441406],[164,14,53.845920562744141],[164,15,54.374130249023438],[164,16,0.024577204138040543],[164,26,50.328678131103516],[164,42,0.39257714152336121],[165,0,52.0968017578125],[165,1,0.17273826897144318],[165,4,52.006435394287109],[165,7,50.558387756347656],[165,8,53.60430908203125],[165,9,53.032394409179688],[165,10,0.015868494287133217],[165,11,0.22763176262378693],[165,19,0.4527866542339325],[165,22,0.30274209380149841],[165,24,0.38487371802330017],[165,26,53.927051544189453],[165,33,0.18093493580818176],[165,34,0.38205015659332275],[165,39,0.41453477740287781],[166,0,52.750591278076172],[166,1,53.954948425292969],[166,2,51.427131652832031],[166,3,52.63507080078125],[166,7,51.407241821289062],[166,8,0.13222008943557739],[166,23,0.32357150316238403],[166,26,51.753349304199219],[166,46,0.3218342661857605],[167,3,0.35586860775947571],[167,14,0.14325621724128723],[167,15,50.682334899902344],[167,22,0.2934262752532959],[167,26,50.024097442626953],[168,5,54.345142364501953],[168,7,51.349468231201172],[168,8,51.513168334960938],[168,9,52.573352813720703],[168,10,0.39453479647636414],[168,12,53.826644897460938],[168,25,0.062496297061443329],[168,26,50.270557403564453],[168,36,0.31685817241668701],[168,40,100.7926025390625],[168,44,0.18244071304798126],[169,1,52.308151245117188],[169,2,52.050464630126953],[169,8,0.20752283930778503],[169,11,50.188697814941406],[169,13,0.47287943959236145],[169,14,54.132400512695312],[169,16,0.43315348029136658],[169,20,0.064268946647644043],[169,22,0.12519651651382446],[169,26,53.944564819335938],[169,34,0.3991452157497406],[169,40,100.66136932373047],[170,1,53.780570983886719],[170,3,50.994438171386719],[170,4,51.986660003662109],[170,5,53.685997009277344],[170,10,51.98333740234375],[170,16,0.45721593499183655],[170,17,0.049216538667678833],[170,26,50.915458679199219],[170,40,102.19636535644531],[170,43,0.14190778136253357],[171,1,53.209480285644531],[171,2,0.14895810186862946],[171,4,52.263076782226562],[171,8,50.194828033447266],[171,15,54.2354736328125],[171,17,0.033363964408636093],[171,22,0.1990114152431488],[171,26,54.803371429443359],[171,27,0.45119577646255493],[171,31,0.23292656242847443],[171,40,102.98052215576172],[171,43,0.30167123675346375],[172,0,51.067188262939453],[172,5,0.051178131252527237],[172,10,0.21091768145561218],[172,11,50.473804473876953],[172,26,50.735858917236328],[172,36,0.43048781156539917],[172,40,100.32500457763672],[173,4,54.012866973876953],[173,6,54.360546112060547],[173,9,0.46847304701805115],[173,10,53.705303192138672],[173,26,52.480602264404297],[173,30,0.44401761889457703],[173,40,102.32750701904297],[173,41,0.12991304695606232],[174,2,54.51568603515625],[174,3,52.598686218261719],[174,5,51.407318115234375],[174,9,52.067398071289062],[174,11,0.20297315716743469],[174,13,54.711563110351562],[174,15,0.12491515278816223],[174,17,0.0041863336227834225],[174,26,53.309669494628906],[174,28,0.22520719468593597],[174,40,101.37160491943359],[175,6,50.353790283203125],[175,13,51.032501220703125],[175,14,51.510963439941406],[175,15,0.44443008303642273],[175,22,0.071385137736797333],[175,26,51.488491058349609],[175,27,0.16217443346977234],[175,28,0.065018944442272186],[175,34,0.29747790098190308],[175,36,0.48629856109619141],[175,40,103.47823333740234],[176,1,0.45555037260055542],[176,6,53.863693237304688],[176,8,52.670822143554688],[176,16,0.42372149229049683],[176,17,0.37679395079612732],[176,27,52.052330017089844],[176,38,0.22570949792861938],[177,0,50.475410461425781],[177,3,52.234676361083984],[177,6,0.34362953901290894],[177,8,53.724948883056641],[177,9,51.003192901611328],[177,10,53.950904846191406],[177,11,53.833396911621094],[177,21,0.35975226759910583],[177,27,50.109382629394531],[177,33,0.34785717725753784],[177,34,0.21790637075901031],[177,36,0.2819056510925293],[177,42,0.085490137338638306],[177,44,0.29402780532836914],[178,0,51.127159118652344],[178,1,54.886016845703125],[178,15,51.626811981201172],[178,27,51.38494873046875],[178,29,0.48586493730545044],[179,4,52.166290283203125],[179,7,53.765174865722656],[179,10,0.19704228639602661],[179,16,0.42650097608566284],[179,27,52.527976989746094],[179,34,0.3352695107460022],[179,37,0.33414372801780701],[179,41,0.3020387589931488],[179,44,0.24315281212329865],[180,4,0.32008197903633118],[180,11,50.583385467529297],[180,20,0.0013638078235089779],[180,27,53.434913635253906],[180,34,0.41860219836235046],[180,37,0.12179930508136749],[180,39,0.33722025156021118],[181,0,0.28192517161369324],[181,3,54.749313354492188],[181,4,54.24920654296875],[181,7,50.894584655761719],[181,12,0.42309224605560303],[181,13,52.455776214599609],[181,15,50.547042846679688],[181,27,50.262424468994141],[182,8,52.886062622070312],[182,20,0.21776948869228363],[182,23,0.042618211358785629],[182,27,51.743118286132812],[182,33,0.41201609373092651],[182,46,0.42024585604667664],[183,2,51.405788421630859],[183,4,0.34895902872085571],[183,5,51.813388824462891],[183,6,53.009456634521484],[183,10,54.142871856689453],[183,12,51.868728637695312],[183,15,52.256103515625],[183,21,0.25990775227546692],[183,27,54.708507537841797],[183,39,0.030025763437151909],[183,41,0.058526713401079178],[184,3,0.31371024250984192],[184,5,52.143074035644531],[184,11,51.319591522216797],[184,12,54.867115020751953],[184,14,54.471061706542969],[184,19,0.28158286213874817],[184,23,0.45891600847244263],[184,25,0.32259324193000793],[184,27,51.514183044433594],[184,41,103.05934143066406],[184,45,0.21815131604671478],[184,47,0.22146391868591309],[185,2,54.389312744140625],[185,9,54.726730346679688],[185,12,51.591567993164062],[185,14,50.219989776611328],[185,18,0.42467635869979858],[185,23,0.31087028980255127],[185,27,50.642326354980469],[185,31,0.30460977554321289],[185,37,0.18165159225463867],[185,41,104.65705108642578],[186,0,0.25788909196853638],[186,3,54.941764831542969],[186,6,52.525196075439453],[186,7,50.624126434326172],[186,9,54.336635589599609],[186,12,53.895957946777344],[186,13,50.606910705566406],[186,19,0.33931231498718262],[186,21,0.22821156680583954],[186,27,52.370635986328125],[186,30,0.36943098902702332],[186,38,0.02148253470659256],[186,41,100.21382141113281],[186,42,0.47981896996498108],[187,0,52.922557830810547],[187,1,50.955696105957031],[187,3,51.963218688964844],[187,4,53.041225433349609],[187,5,52.135051727294922],[187,9,0.010942969471216202],[187,11,52.607738494873047],[187,14,52.601673126220703],[187,24,0.11150573194026947],[187,27,53.664424896240234],[187,29,0.044489149004220963],[187,31,0.22644561529159546],[187,40,0.43517783284187317],[187,41,104.24137115478516],[187,45,0.30246543884277344],[187,47,0.0033932260703295469],[188,2,50.792934417724609],[188,7,0.023050190880894661],[188,9,53.674190521240234],[188,10,50.590877532958984],[188,13,54.378143310546875],[188,14,53.915081024169922],[188,15,51.944061279296875],[188,27,51.305023193359375],[188,28,0.30837661027908325],[188,41,101.22265625],[189,0,53.115684509277344],[189,2,50.200401306152344],[189,4,53.539379119873047],[189,7,0.47677996754646301],[189,18,0.25107145309448242],[189,24,0.03134748712182045],[189,27,54.358589172363281],[189,35,0.38156014680862427],[189,38,0.066175200045108795],[189,41,103.09375],[189,43,0.33280619978904724],[190,1,51.842105865478516],[190,5,53.342754364013672],[190,6,52.595291137695312],[190,8,52.213890075683594],[190,10,0.15846012532711029],[190,17,0.16063010692596436],[190,27,52.685287475585938],[190,41,104.90682983398438],[190,42,0.24240745604038239],[191,1,51.278114318847656],[191,4,0.24457579851150513],[191,7,52.265346527099609],[191,10,50.77734375],[191,12,0.096988648176193237],[191,13,52.950069427490234],[191,27,50.354633331298828],[191,28,0.36422383785247803],[191,31,0.25804945826530457],[191,39,0.4405137300491333],[191,41,102.64510345458984],[192,0,52.08270263671875],[192,3,53.359157562255859],[192,6,52.398464202880859],[192,12,53.136501312255859],[192,13,52.122447967529297],[192,14,0.24133208394050598],[192,17,0.42085009813308716],[192,28,54.159107208251953],[193,0,51.129478454589844],[193,1,54.000377655029297],[193,5,52.120113372802734],[193,7,51.47930908203125],[193,9,52.702804565429688],[193,11,0.25548422336578369],[193,13,51.027820587158203],[193,28,52.539890289306641],[193,32,0.29210367798805237],[193,35,0.012117686681449413],[193,42,0.17377012968063354],[194,1,53.317520141601562],[194,2,54.316276550292969],[194,4,50.122386932373047],[194,6,0.4184720516204834],[194,7,53.825492858886719],[194,9,53.736583709716797],[194,11,50.308506011962891],[194,13,0.04402715340256691],[194,14,54.294963836669922],[194,20,0.20283131301403046],[194,25,0.19449774920940399],[194,26,0.027815774083137512],[194,28,51.0262451171875],[194,44,0.27682694792747498],[194,46,0.13557696342468262],[195,9,54.217769622802734],[195,10,52.920818328857422],[195,12,51.335056304931641],[195,15,53.698760986328125],[195,28,50.450595855712891],[195,32,0.23660404980182648],[195,35,0.070698380470275879],[195,37,0.40480682253837585],[196,2,50.610984802246094],[196,5,51.065895080566406],[196,9,0.45768356323242188],[196,10,52.250141143798828],[196,12,53.243350982666016],[196,14,0.086671985685825348],[196,15,0.10447340458631516],[196,21,0.49636343121528625],[196,28,53.820209503173828],[196,36,0.47763049602508545],[197,2,51.631385803222656],[197,28,52.79638671875],[197,35,0.29739049077033997],[197,36,0.040816269814968109],[197,43,0.098230026662349701],[198,1,53.047206878662109],[198,5,53.23809814453125],[198,6,54.565174102783203],[198,11,50.075981140136719],[198,21,0.19116789102554321],[198,28,50.087863922119141],[198,30,0.11276478320360184],[198,34,0.31035763025283813],[198,44,0.3988252580165863],[199,4,52.071487426757812],[199,7,51.309669494628906],[199,10,50.479316711425781],[199,14,53.234268188476562],[199,21,0.32288277149200439],[199,25,0.051803793758153915],[199,28,53.230083465576172],[199,43,0.21337148547172546],[199,47,0.4607870876789093],[200,8,54.867694854736328],[200,12,54.846549987792969],[200,14,50.009967803955078],[200,15,50.756149291992188],[200,20,0.13072864711284637],[200,25,0.041507352143526077],[200,28,52.654560089111328],[200,35,0.18415877223014832],[200,42,104.02641296386719],[201,2,0.068427205085754395],[201,15,0.33333477377891541],[201,22,0.066154502332210541],[201,28,51.444358825683594],[201,31,0.07719796895980835],[201,41,0.27611050009727478],[201,42,102.27503204345703],[202,0,54.508865356445312],[202,4,0.032175637781620026],[202,8,52.072601318359375],[202,11,53.025325775146484],[202,26,0.2083645761013031],[202,27,0.10627796500921249],[202,28,54.298171997070312],[202,36,0.3699929416179657],[202,42,101.94718170166016],[203,3,53.089004516601562],[203,4,53.447830200195312],[203,7,52.080699920654297],[203,10,0.39325517416000366],[203,13,50.88592529296875],[203,22,0.48196598887443542],[203,28,50.446304321289062],[203,34,0.0022291217464953661],[203,38,0.026039259508252144],[203,42,103.24577331542969],[204,3,50.2568359375],[204,8,52.497600555419922],[204,16,0.39389380812644958],[204,24,0.17521539330482483],[204,28,50.825435638427734],[204,39,0.29116761684417725],[204,42,102.00128173828125],[205,2,52.716400146484375],[205,6,52.267696380615234],[205,10,53.595897674560547],[205,13,0.31199353933334351],[205,14,0.47649309039115906],[205,15,0.038221698254346848],[205,26,0.28074204921722412],[205,28,50.472057342529297],[205,31,0.04505518451333046],[205,32,0.39187705516815186],[205,42,102.4862060546875],[205,45,0.40592673420906067],[205,46,0.43562990427017212],[205,47,0.4526614248752594],[206,3,53.508731842041016],[206,5,53.032669067382812],[206,6,53.907768249511719],[206,13,53.893898010253906],[206,14,52.557449340820312],[206,15,53.809852600097656],[206,28,54.240188598632812],[206,29,0.22426807880401611],[206,30,0.11523848026990891],[206,35,0.4881061315536499],[206,42,102.18634796142578],[207,0,51.21832275390625],[207,1,51.156375885009766],[207,4,54.176292419433594],[207,8,51.270488739013672],[207,9,52.896858215332031],[207,11,52.288967132568359],[207,15,54.390640258789062],[207,20,0.0048042153939604759],[207,28,51.240825653076172],[207,42,103.110595703125],[208,3,0.054680503904819489],[208,5,0.15994095802307129],[208,6,50.047206878662109],[208,7,0.40997469425201416],[208,8,51.405136108398438],[208,13,53.288730621337891],[208,15,0.084893539547920227],[208,23,0.15559166669845581],[208,29,53.013538360595703],[208,30,0.14970351755619049],[208,32,0.06919938325881958],[208,34,0.33737727999687195],[208,44,0.25229451060295105],[209,1,50.103015899658203],[209,6,54.165019989013672],[209,7,52.223060607910156],[209,15,0.1143171638250351],[209,29,51.887195587158203],[209,30,0.29943034052848816],[209,37,0.47982215881347656],[210,6,50.554969787597656],[210,7,52.157680511474609],[210,15,52.399253845214844],[210,19,0.17067147791385651],[210,24,0.38117825984954834],[210,25,0.000851770571898669],[210,29,51.020755767822266],[210,40,0.01028074324131012],[210,45,0.008518158458173275],[211,0,50.536258697509766],[211,10,51.981346130371094],[211,12,51.591121673583984],[211,29,53.129642486572266],[211,46,0.40776640176773071],[212,4,0.46998137235641479],[212,9,54.553054809570312],[212,11,50.743118286132812],[212,12,51.315589904785156],[212,15,53.294181823730469],[212,26,0.034202255308628082],[212,29,54.103801727294922],[212,33,0.040960993617773056],[212,37,0.49479383230209351],[212,42,0.38620385527610779],[213,0,0.33994728326797485],[213,6,54.667179107666016],[213,7,0.050131801515817642],[213,8,50.299995422363281],[213,10,51.871322631835938],[213,14,53.294315338134766],[213,15,51.124359130859375],[213,29,50.610130310058594],[214,0,50.011520385742188],[214,5,50.837127685546875],[214,9,54.413986206054688],[214,19,0.30154353380203247],[214,27,0.087291926145553589],[214,28,0.27774399518966675],[214,29,50.317981719970703],[214,44,0.45576402544975281],[215,1,0.45612981915473938],[215,3,53.521640777587891],[215,4,54.88909912109375],[215,5,50.912956237792969],[215,14,53.488094329833984],[215,27,0.19404751062393188],[215,28,0.28034219145774841],[215,29,50.358501434326172],[215,33,0.34588330984115601],[215,34,0.35342556238174438],[215,40,0.20757989585399628],[215,42,0.44938763976097107],[216,1,52.664566040039062],[216,2,54.928630828857422],[216,5,0.35284602642059326],[216,9,53.827190399169922],[216,10,53.286415100097656],[216,13,53.891666412353516],[216,16,0.039039663970470428],[216,28,0.050140336155891418],[216,29,53.910659790039062],[216,43,102.50341796875],[216,46,0.23237238824367523],[217,3,53.818977355957031],[217,10,51.073921203613281],[217,11,51.932571411132812],[217,12,53.658687591552734],[217,14,50.884868621826172],[217,21,0.41347506642341614],[217,22,0.29842096567153931],[217,27,0.3984011709690094],[217,29,50.063747406005859],[217,30,0.17452739179134369],[217,42,0.097008273005485535],[217,43,102.13336944580078],[218,0,53.630901336669922],[218,3,54.199352264404297],[218,5,53.860916137695312],[218,18,0.42304453253746033],[218,25,0.34608793258666992],[218,29,54.032810211181641],[218,40,0.1875089555978775],[218,43,102.74257659912109],[219,2,50.695960998535156],[219,8,52.958332061767578],[219,9,51.206306457519531],[219,14,0.18390923738479614],[219,15,0.1624310314655304],[219,18,0.22174374759197235],[219,29,51.398952484130859],[219,30,0.1469196081161499],[219,33,0.45154839754104614],[219,40,0.36239805817604065],[219,43,103.42655181884766],[220,0,53.138858795166016],[220,1,54.26025390625],[220,2,50.112220764160156],[220,4,53.70526123046875],[220,6,0.47603422403335571],[220,8,51.938503265380859],[220,9,0.19128337502479553],[220,12,51.217540740966797],[220,14,0.25808006525039673],[220,15,50.117458343505859],[220,22,0.11588933318853378],[220,29,53.311115264892578],[220,43,104.43067932128906],[221,1,0.33845359086990356],[221,2,0.41068929433822632],[221,3,52.141147613525391],[221,4,50.524120330810547],[221,7,53.977413177490234],[221,11,52.108203887939453],[221,16,0.41722539067268372],[221,29,50.981712341308594],[221,39,0.42248430848121643],[221,43,102.376953125],[221,44,0.22609518468379974],[221,46,0.30678614974021912],[222,1,52.317245483398438],[222,2,52.647129058837891],[222,4,51.567115783691406],[222,7,53.672454833984375],[222,13,54.242855072021484],[222,14,51.648265838623047],[222,21,0.24258989095687866],[222,29,51.111881256103516],[222,40,0.2951844334602356],[222,43,102.04779052734375],[223,2,0.26003819704055786],[223,5,50.257778167724609],[223,6,0.49925112724304199],[223,11,51.37200927734375],[223,13,53.152656555175781],[223,14,0.38648059964179993],[223,17,0.051977165043354034],[223,23,0.38961988687515259],[223,27,0.44477462768554688],[223,29,51.736705780029297],[223,43,103.39791107177734]],"rows": [{"id": "Root|Taxa_0", "metadata": null},{"id": "Root|Taxa_1", "metadata": null},{"id": "Root|Taxa_2", "metadata": null},{"id": "Root|Taxa_3", "metadata": null},{"id": "Root|Taxa_4", "metadata": null},{"id": "Root|Taxa_5", "metadata": null},{"id": "Root|Taxa_6", "metadata": null},{"id": "Root|Taxa_7", "metadata": null},{"id": "Root|Taxa_8", "metadata": null},{"id": "Root|Taxa_9", "metadata": null},{"id": "Root|Taxa_10", "metadata": null},{"id": "Root|Taxa_11", "metadata": null},{"id": "Root|Taxa_12", "metadata": null},{"id": "Root|Taxa_13", "metadata": null},{"id": "Root|Taxa_14", "metadata": null},{"id": "Root|Taxa_15", "metadata": null},{"id": "Root|Taxa_16", "metadata": null},{"id": "Root|Taxa_17", "metadata": null},{"id": "Root|Taxa_18", "metadata": null},{"id": "Root|Taxa_19", "metadata": null},{"id": "Root|Taxa_20", "metadata": null},{"id": "Root|Taxa_21", "metadata": null},{"id": "Root|Taxa_22", "metadata": null},{"id": "Root|Taxa_23", "metadata": null},{"id": "Root|Taxa_24", "metadata": null},{"id": "Root|Taxa_25", "metadata": null},{"id": "Root|Taxa_26", "metadata": null},{"id": "Root|Taxa_27", "metadata": null},{"id": "Root|Taxa_28", "metadata": null},{"id": "Root|Taxa_29", "metadata": null},{"id": "Root|Taxa_30", "metadata": null},{"id": "Root|Taxa_31", "metadata": null},{"id": "Root|Taxa_32", "metadata": null},{"id": "Root|Taxa_33", "metadata": null},{"id": "Root|Taxa_34", "metadata": null},{"id": "Root|Taxa_35", "metadata": null},{"id": "Root|Taxa_36", "metadata": null},{"id": "Root|Taxa_37", "metadata": null},{"id": "Root|Taxa_38", "metadata": null},{"id": "Root|Taxa_39", "metadata": null},{"id": "Root|Taxa_40", "metadata": null},{"id": "Root|Taxa_41", "metadata": null},{"id": "Root|Taxa_42", "metadata": null},{"id": "Root|Taxa_43", "metadata": null},{"id": "Root|Taxa_44", "metadata": null},{"id": "Root|Taxa_45", "metadata": null},{"id": "Root|Taxa_46", "metadata": null},{"id": "Root|Taxa_47", "metadata": null},{"id": "Root|Taxa_48", "metadata": null},{"id": "Root|Taxa_49", "metadata": null},{"id": "Root|Taxa_50", "metadata": null},{"id": "Root|Taxa_51", "metadata": null},{"id": "Root|Taxa_52", "metadata": null},{"id": "Root|Taxa_53", "metadata": null},{"id": "Root|Taxa_54", "metadata": null},{"id": "Root|Taxa_55", "metadata": null},{"id": "Root|Taxa_56", "metadata": null},{"id": "Root|Taxa_57", "metadata": null},{"id": "Root|Taxa_58", "metadata": null},{"id": "Root|Taxa_59", "metadata": null},{"id": "Root|Taxa_60", "metadata": null},{"id": "Root|Taxa_61", "metadata": null},{"id": "Root|Taxa_62", "metadata": null},{"id": "Root|Taxa_63", "metadata": null},{"id": "Root|Taxa_64", "metadata": null},{"id": "Root|Taxa_65", "metadata": null},{"id": "Root|Taxa_66", "metadata": null},{"id": "Root|Taxa_67", "metadata": null},{"id": "Root|Taxa_68", "metadata": null},{"id": "Root|Taxa_69", "metadata": null},{"id": "Root|Taxa_70", "metadata": null},{"id": "Root|Taxa_71", "metadata": null},{"id": "Root|Taxa_72", "metadata": null},{"id": "Root|Taxa_73", "metadata": null},{"id": "Root|Taxa_74", "metadata": null},{"id": "Root|Taxa_75", "metadata": null},{"id": "Root|Taxa_76", "metadata": null},{"id": "Root|Taxa_77", "metadata": null},{"id": "Root|Taxa_78", "metadata": null},{"id": "Root|Taxa_79", "metadata": null},{"id": "Root|Taxa_80", "metadata": null},{"id": "Root|Taxa_81", "metadata": null},{"id": "Root|Taxa_82", "metadata": null},{"id": "Root|Taxa_83", "metadata": null},{"id": "Root|Taxa_84", "metadata": null},{"id": "Root|Taxa_85", "metadata": null},{"id": "Root|Taxa_86", "metadata": null},{"id": "Root|Taxa_87", "metadata": null},{"id": "Root|Taxa_88", "metadata": null},{"id": "Root|Taxa_89", "metadata": null},{"id": "Root|Taxa_90", "metadata": null},{"id": "Root|Taxa_91", "metadata": null},{"id": "Root|Taxa_92", "metadata": null},{"id": "Root|Taxa_93", "metadata": null},{"id": "Root|Taxa_94", "metadata": null},{"id": "Root|Taxa_95", "metadata": null},{"id": "Root|Taxa_96", "metadata": null},{"id": "Root|Taxa_97", "metadata": null},{"id": "Root|Taxa_98", "metadata": null},{"id": "Root|Taxa_99", "metadata": null},{"id": "Root|Taxa_100", "metadata": null},{"id": "Root|Taxa_101", "metadata": null},{"id": "Root|Taxa_102", "metadata": null},{"id": "Root|Taxa_103", "metadata": null},{"id": "Root|Taxa_104", "metadata": null},{"id": "Root|Taxa_105", "metadata": null},{"id": "Root|Taxa_106", "metadata": null},{"id": "Root|Taxa_107", "metadata": null},{"id": "Root|Taxa_108", "metadata": null},{"id": "Root|Taxa_109", "metadata": null},{"id": "Root|Taxa_110", "metadata": null},{"id": "Root|Taxa_111", "metadata": null},{"id": "Root|Taxa_112", "metadata": null},{"id": "Root|Taxa_113", "metadata": null},{"id": "Root|Taxa_114", "metadata": null},{"id": "Root|Taxa_115", "metadata": null},{"id": "Root|Taxa_116", "metadata": null},{"id": "Root|Taxa_117", "metadata": null},{"id": "Root|Taxa_118", "metadata": null},{"id": "Root|Taxa_119", "metadata": null},{"id": "Root|Taxa_120", "metadata": null},{"id": "Root|Taxa_121", "metadata": null},{"id": "Root|Taxa_122", "metadata": null},{"id": "Root|Taxa_123", "metadata": null},{"id": "Root|Taxa_124", "metadata": null},{"id": "Root|Taxa_125", "metadata": null},{"id": "Root|Taxa_126", "metadata": null},{"id": "Root|Taxa_127", "metadata": null},{"id": "Root|Taxa_128", "metadata": null},{"id": "Root|Taxa_129", "metadata": null},{"id": "Root|Taxa_130", "metadata": null},{"id": "Root|Taxa_131", "metadata": null},{"id": "Root|Taxa_132", "metadata": null},{"id": "Root|Taxa_133", "metadata": null},{"id": "Root|Taxa_134", "metadata": null},{"id": "Root|Taxa_135", "metadata": null},{"id": "Root|Taxa_136", "metadata": null},{"id": "Root|Taxa_137", "metadata": null},{"id": "Root|Taxa_138", "metadata": null},{"id": "Root|Taxa_139", "metadata": null},{"id": "Root|Taxa_140", "metadata": null},{"id": "Root|Taxa_141", "metadata": null},{"id": "Root|Taxa_142", "metadata": null},{"id": "Root|Taxa_143", "metadata": null},{"id": "Root|Taxa_144", "metadata": null},{"id": "Root|Taxa_145", "metadata": null},{"id": "Root|Taxa_146", "metadata": null},{"id": "Root|Taxa_147", "metadata": null},{"id": "Root|Taxa_148", "metadata": null},{"id": "Root|Taxa_149", "metadata": null},{"id": "Root|Taxa_150", "metadata": null},{"id": "Root|Taxa_151", "metadata": null},{"id": "Root|Taxa_152", "metadata": null},{"id": "Root|Taxa_153", "metadata": null},{"id": "Root|Taxa_154", "metadata": null},{"id": "Root|Taxa_155", "metadata": null},{"id": "Root|Taxa_156", "metadata": null},{"id": "Root|Taxa_157", "metadata": null},{"id": "Root|Taxa_158", "metadata": null},{"id": "Root|Taxa_159", "metadata": null},{"id": "Root|Taxa_160", "metadata": null},{"id": "Root|Taxa_161", "metadata": null},{"id": "Root|Taxa_162", "metadata": null},{"id": "Root|Taxa_163", "metadata": null},{"id": "Root|Taxa_164", "metadata": null},{"id": "Root|Taxa_165", "metadata": null},{"id": "Root|Taxa_166", "metadata": null},{"id": "Root|Taxa_167", "metadata": null},{"id": "Root|Taxa_168", "metadata": null},{"id": "Root|Taxa_169", "metadata": null},{"id": "Root|Taxa_170", "metadata": null},{"id": "Root|Taxa_171", "metadata": null},{"id": "Root|Taxa_172", "metadata": null},{"id": "Root|Taxa_173", "metadata": null},{"id": "Root|Taxa_174", "metadata": null},{"id": "Root|Taxa_175", "metadata": null},{"id": "Root|Taxa_176", "metadata": null},{"id": "Root|Taxa_177", "metadata": null},{"id": "Root|Taxa_178", "metadata": null},{"id": "Root|Taxa_179", "metadata": null},{"id": "Root|Taxa_180", "metadata": null},{"id": "Root|Taxa_181", "metadata": null},{"id": "Root|Taxa_182", "metadata": null},{"id": "Root|Taxa_183", "metadata": null},{"id": "Root|Taxa_184", "metadata": null},{"id": "Root|Taxa_185", "metadata": null},{"id": "Root|Taxa_186", "metadata": null},{"id": "Root|Taxa_187", "metadata": null},{"id": "Root|Taxa_188", "metadata": null},{"id": "Root|Taxa_189", "metadata": null},{"id": "Root|Taxa_190", "metadata": null},{"id": "Root|Taxa_191", "metadata": null},{"id": "Root|Taxa_192", "metadata": null},{"id": "Root|Taxa_193", "metadata": null},{"id": "Root|Taxa_194", "metadata": null},{"id": "Root|Taxa_195", "metadata": null},{"id": "Root|Taxa_196", "metadata": null},{"id": "Root|Taxa_197", "metadata": null},{"id": "Root|Taxa_198", "metadata": null},{"id": "Root|Taxa_199", "metadata": null},{"id": "Root|Taxa_200", "metadata": null},{"id": "Root|Taxa_201", "metadata": null},{"id": "Root|Taxa_202", "metadata": null},{"id": "Root|Taxa_203", "metadata": null},{"id": "Root|Taxa_204", "metadata": null},{"id": "Root|Taxa_205", "metadata": null},{"id": "Root|Taxa_206", "metadata": null},{"id": "Root|Taxa_207", "metadata": null},{"id": "Root|Taxa_208", "metadata": null},{"id": "Root|Taxa_209", "metadata": null},{"id": "Root|Taxa_210", "metadata": null},{"id": "Root|Taxa_211", "metadata": null},{"id": "Root|Taxa_212", "metadata": null},{"id": "Root|Taxa_213", "metadata": null},{"id": "Root|Taxa_214", "metadata": null},{"id": "Root|Taxa_215", "metadata": null},{"id": "Root|Taxa_216", "metadata": null},{"id": "Root|Taxa_217", "metadata": null},{"id": "Root|Taxa_218", "metadata": null},{"id": "Root|Taxa_219", "metadata": null},{"id": "Root|Taxa_220", "metadata": null},{"id": "Root|Taxa_221", "metadata": null},{"id": "Root|Taxa_222", "metadata": null},{"id": "Root|Taxa_223", "metadata": null}],"columns": [{"id": "Sample_0_D", "metadata": {"Sample": "Sample_0_D", "alpha_custom": "1", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_0_D", "Label": "Class-Two"}},{"id": "Sample_1_D", "metadata": {"Sample": "Sample_1_D", "alpha_custom": "0.99", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_1_D", "Label": "Class-One"}},{"id": "Sample_2_D", "metadata": {"Sample": "Sample_2_D", "alpha_custom": "0.98", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_2_D", "Label": "Class-Two"}},{"id": "Sample_3_D", "metadata": {"Sample": "Sample_3_D", "alpha_custom": "0.97", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_3_D", "Label": "Class-One"}},{"id": "Sample_4_D", "metadata": {"Sample": "Sample_4_D", "alpha_custom": "0.96", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_4_D", "Label": "Class-One"}},{"id": "Sample_5_D", "metadata": {"Sample": "Sample_5_D", "alpha_custom": "0.95", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_5_D", "Label": "Class-One"}},{"id": "Sample_6_D", "metadata": {"Sample": "Sample_6_D", "alpha_custom": "0.94", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_6_D", "Label": "Class-One"}},{"id": "Sample_7_D", "metadata": {"Sample": "Sample_7_D", "alpha_custom": "0.93", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_7_D", "Label": "Class-Two"}},{"id": "Sample_8_D", "metadata": {"Sample": "Sample_8_D", "alpha_custom": "0.92", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_8_D", "Label": "Class-Two"}},{"id": "Sample_9_D", "metadata": {"Sample": "Sample_9_D", "alpha_custom": "0.91", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_9_D", "Label": "Class-One"}},{"id": "Sample_10_D", "metadata": {"Sample": "Sample_10_D", "alpha_custom": "0.9", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_10_D", "Label": "Class-Two"}},{"id": "Sample_11_D", "metadata": {"Sample": "Sample_11_D", "alpha_custom": "0.89", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_11_D", "Label": "Class-One"}},{"id": "Sample_12_D", "metadata": {"Sample": "Sample_12_D", "alpha_custom": "0.88", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_12_D", "Label": "Class-One"}},{"id": "Sample_13_D", "metadata": {"Sample": "Sample_13_D", "alpha_custom": "0.87", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_13_D", "Label": "Class-Two"}},{"id": "Sample_14_D", "metadata": {"Sample": "Sample_14_D", "alpha_custom": "0.86", "StratifyLabel": "1", "Group": "Complex", "ID": "Sample_14_D", "Label": "Class-Two"}},{"id": "Sample_15_D", "metadata": {"Sample": "Sample_15_D", "alpha_custom": "0.85", "StratifyLabel": "2", "Group": "Complex", "ID": "Sample_15_D", "Label": "Class-Two"}},{"id": "Sample_16_R", "metadata": {"Sample": "Sample_16_R", "alpha_custom": "0.84", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity_Feature", "ID": "Sample_16_R", "Label": "Class-Two"}},{"id": "Sample_17_R", "metadata": {"Sample": "Sample_17_R", "alpha_custom": "0.83", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity_Feature", "ID": "Sample_17_R", "Label": "Class-Two"}},{"id": "Sample_18_R", "metadata": {"Sample": "Sample_18_R", "alpha_custom": "0.82", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity_Feature", "ID": "Sample_18_R", "Label": "Class-Two"}},{"id": "Sample_19_R", "metadata": {"Sample": "Sample_19_R", "alpha_custom": "0.81", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity", "ID": "Sample_19_R", "Label": "Class-Two"}},{"id": "Sample_20_R", "metadata": {"Sample": "Sample_20_R", "alpha_custom": "0.8", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity", "ID": "Sample_20_R", "Label": "Class-One"}},{"id": "Sample_21_R", "metadata": {"Sample": "Sample_21_R", "alpha_custom": "0.79", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity", "ID": "Sample_21_R", "Label": "Class-One"}},{"id": "Sample_22_R", "metadata": {"Sample": "Sample_22_R", "alpha_custom": "0.78", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity", "ID": "Sample_22_R", "Label": "Class-One"}},{"id": "Sample_23_R", "metadata": {"Sample": "Sample_23_R", "alpha_custom": "0.77", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity", "ID": "Sample_23_R", "Label": "Class-One"}},{"id": "Sample_24_R", "metadata": {"Sample": "Sample_24_R", "alpha_custom": "0.76", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity", "ID": "Sample_24_R", "Label": "Class-One"}},{"id": "Sample_25_R", "metadata": {"Sample": "Sample_25_R", "alpha_custom": "0.75", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity", "ID": "Sample_25_R", "Label": "Class-Two"}},{"id": "Sample_26_R", "metadata": {"Sample": "Sample_26_R", "alpha_custom": "0.74", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity", "ID": "Sample_26_R", "Label": "Class-One"}},{"id": "Sample_27_R", "metadata": {"Sample": "Sample_27_R", "alpha_custom": "0.73", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity", "ID": "Sample_27_R", "Label": "Class-Two"}},{"id": "Sample_28_R", "metadata": {"Sample": "Sample_28_R", "alpha_custom": "0.72", "StratifyLabel": "1", "Group": "Moderate_Dissimilarity", "ID": "Sample_28_R", "Label": "Class-Two"}},{"id": "Sample_29_R", "metadata": {"Sample": "Sample_29_R", "alpha_custom": "0.71", "StratifyLabel": "2", "Group": "Moderate_Dissimilarity", "ID": "Sample_29_R", "Label": "Class-One"}},{"id": "Sample_30_E", "metadata": {"Sample": "Sample_30_E", "alpha_custom": "0.7", "StratifyLabel": "1", "Group": "High_Dissimilarity", "ID": "Sample_30_E", "Label": "Class-Two"}},{"id": "Sample_31_E", "metadata": {"Sample": "Sample_31_E", "alpha_custom": "0.69", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_31_E", "Label": "Class-Two"}},{"id": "Sample_32_E", "metadata": {"Sample": "Sample_32_E", "alpha_custom": "0.68", "StratifyLabel": "1", "Group": "High_Dissimilarity_Feature", "ID": "Sample_32_E", "Label": "Class-Two"}},{"id": "Sample_33_E", "metadata": {"Sample": "Sample_33_E", "alpha_custom": "0.67", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_33_E", "Label": "Class-Two"}},{"id": "Sample_34_E", "metadata": {"Sample": "Sample_34_E", "alpha_custom": "0.66", "StratifyLabel": "1", "Group": "High_Dissimilarity", "ID": "Sample_34_E", "Label": "Class-One"}},{"id": "Sample_35_E", "metadata": {"Sample": "Sample_35_E", "alpha_custom": "0.65", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_35_E", "Label": "Class-One"}},{"id": "Sample_36_E", "metadata": {"Sample": "Sample_36_E", "alpha_custom": "0.64", "StratifyLabel": "1", "Group": "High_Dissimilarity", "ID": "Sample_36_E", "Label": "Class-One"}},{"id": "Sample_37_E", "metadata": {"Sample": "Sample_37_E", "alpha_custom": "0.63", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_37_E", "Label": "Class-One"}},{"id": "Sample_38_E", "metadata": {"Sample": "Sample_38_E", "alpha_custom": "0.62", "StratifyLabel": "1", "Group": "High_Dissimilarity", "ID": "Sample_38_E", "Label": "Class-One"}},{"id": "Sample_39_E", "metadata": {"Sample": "Sample_39_E", "alpha_custom": "0.61", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_39_E", "Label": "Class-Two"}},{"id": "Sample_40_E", "metadata": {"Sample": "Sample_40_E", "alpha_custom": "0.6", "StratifyLabel": "1", "Group": "High_Dissimilarity", "ID": "Sample_40_E", "Label": "Class-One"}},{"id": "Sample_41_E", "metadata": {"Sample": "Sample_41_E", "alpha_custom": "0.59", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_41_E", "Label": "Class-Two"}},{"id": "Sample_42_E", "metadata": {"Sample": "Sample_42_E", "alpha_custom": "0.58", "StratifyLabel": "1", "Group": "High_Dissimilarity", "ID": "Sample_42_E", "Label": "Class-Two"}},{"id": "Sample_43_E", "metadata": {"Sample": "Sample_43_E", "alpha_custom": "0.57", "StratifyLabel": "2", "Group": "High_Dissimilarity", "ID": "Sample_43_E", "Label": "Class-One"}},{"id": "Sample_44_T", "metadata": {"Sample": "Sample_44_T", "alpha_custom": "0.56", "StratifyLabel": "1", "Group": "Targeted_Feature", "ID": "Sample_44_T", "Label": "Class-Two"}},{"id": "Sample_45_T", "metadata": {"Sample": "Sample_45_T", "alpha_custom": "0.55", "StratifyLabel": "2", "Group": "Targeted_Feature", "ID": "Sample_45_T", "Label": "Class-Two"}},{"id": "Sample_46_T", "metadata": {"Sample": "Sample_46_T", "alpha_custom": "0.54", "StratifyLabel": "1", "Group": "Targeted_Feature", "ID": "Sample_46_T", "Label": "Class-Two"}},{"id": "Sample_47_T", "metadata": {"Sample": "Sample_47_T", "alpha_custom": "0.53", "StratifyLabel": "2", "Group": "Targeted_Feature", "ID": "Sample_47_T", "Label": "Class-Two"}}]} \ No newline at end of file diff -r 000000000000 -r 0de566f21448 micropita.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropita.xml Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,208 @@ + + +micropita +micropita_prepare.py +--lastmeta $cls_x +-m $cond.method_sel +-n $selected_samples +--input $inp_data +--output $out_file1 +--stratify_value $cls_s + +#if $cond.method_sel == "features": + --feature_method $cond.feature_method + --targets $cond.cls_f +#end if +#if $cond.method_sel == "distinct" or $cond.method_sel == "discriminant" : + --label_value $cond.cls_L +#end if + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + micropita_SCRIPT_PATH + + + + + + + + + + + + + + + + + +microbiome: Picking Interesting Taxonomic Abundance +--------------------------------------------------- + + + +microPITA is a computational tool enabling sample selection in tiered studies. Using tiered-study designs can more efficiently allocate resources, reducing study costs, and maximizing the use of samples. From a survey study, selection of samples can be performed to target various microbial communities including: + +1. Samples with the most diverse community (maximum diversity); +2. Samples dominated by specific microbes (targeted feature); +3. Samples with microbial communities representative of the survey (representative dissimilarity); +4. Samples with the most extreme microbial communities in the survey (most dissimilar); +5. Given a phenotype (like disease state), samples at the border of phenotypes (discriminant) or samples typical of each phenotype (distinct). + +Additionally, methods can leverage clinical metadata by stratifying samples into groups in which samples are subsequently selected. This enables the use of microPITA in cohort studies. + + +.. image:: https://bytebucket.org/biobakery/galaxy_micropita/wiki/HMPStool10PCoA.png + :height: 500 + :width: 600 + +MicroPITA unsupervised method selection in the HMP 16S Gut Microbiome. Selection of 10 samples using targeted feature targeting *Bacteroides* (blue), maximum diversity (orange), representative dissimilarity (purple), and most dissimilar (pink) using Principle Covariance Analysis (PCoA) for ordination. Targeted feature selects samples dominated by *Bacteroides* (upper left) while maximum diversity select more diverse samples away from *Bacteroides* dominant samples. Representative selection selects samples covering the range of samples in the PCoA plot focusing on the higher density central region while maximum dissimilarity selects samples at the periphery of the plot. + + +Intructions to run: +------------------- + +Before running microPita, you must upload your data using Glaxay's **Get Data - Upload File** +Please make sure that you choose **File Format Micropita** +An example can be found at https://bytebucket.org/biobakery/micropita/wiki/micropita_sample_PCL.txt + +Required inputs +--------------- + +microPITA requires an input pcl file of metadata and microbial community measurements. Although some defaults can be changed, microPITA expects a PCL file as an input file. A PCL file is a text delimited file similar to an excel spread sheet with the following characteristics. + +1. **Rows** represent metadata and features (bugs), **columns** represent samples. +2. The **first row** by default should be the sample ids. +3. Metadata rows should be next. +4. Lastly, rows containing features (bugs) measurements (like abundance) should be after metadata rows. +5. The **first column** should contain the ID describing the column. For metadata this may be, for example, "Age" for a row containing the age of the patients donating the samples. For measurements, this should be the feature name (bug name). +6. The file is expected to be TAB delimited. +7. If a consensus lineage or hierarchy of taxonomy is contained in the feature name, the default delimiter between clades is the pipe ("|"). + +**Note** MAC users, please save file as windows formatted text. + +.. image:: https://bytebucket.org/biobakery/galaxy_micropita/wiki/pcl_diagram.png + :height: 500 + :width: 600 + +Outputs +------- + +The Run MicroPITA module will create one output text file. The output will consist of one line starting with a key word for the selection method and then followed by selected samples delimited by tabs. An example of 6 samples selected by the representative: + +representative sample_1 sample_2 sample_3 sample_4 sample_5 sample_6 + + + + +Run microPITA +------------- + +A brief description of the Run micropita module. + +**Input file:** +This should be populated by the Load microPITA module. + +**Last metadata row:** +The row on the input pcl file that is the last metadata. All microbial measurements should follow this row. + +**Select method:** +Select which method to use for sample selection. Selection methods include: + +1. Representative. Samples with microbial communities representative of the survey (representative dissimilarity); +2. Diverse. Samples with the most diverse community (maximum diversity); +3. Extreme. Samples with the most extreme microbial communities in the survey (most dissimilar); +4. Features. Samples dominated by specific microbes (targeted feature); +5. Distinct. Given a phenotype (like disease state), samples typical of each phenotype (Distinct). +6. Discriminant. Given a phenotype (like disease state), samples at the border of phenotypes (Discriminant). + +**Targeted feature(s):** (visible with Features method selection only) +Select 1 or more features to target in sample selection. + +**Selection type:** (visible with Features method selection only) +Rank or Abundance. + +1. Rank indicates selecting samples that have the highest rank of the Targeted features(s), this tends to select sample in which these feature dominant the sample. +2. Abundance indicates selecting samples that have the highest average abundance of the Targeted features(s), this selects samples where features are most abundant but not necessarily dominant in the community. + +**Label:** (visible with supervised method selection only) +The row which contains the label used to classify the samples from supervised methods. + +**Stratify by (optional):** +The row which contains the groupings the samples will first be placed in before running the selection method on each group. If no grouping is selected, selection methods will be performed on the data set as a whole. + +**Number of samples to select:** +The number of samples to select. If samples are stratified, this is per stratification (or group). If supervised methods are used, this is the number of samples selected per classification group (as defined by the label). + +For more information please visit http://huttenhower.sph.harvard.edu/micropita + + +Acknowledgments +--------------- +Special thanks to Eric Franzosa for developing the above PCL figure! + +Citation and Contacts +--------------------- + +For more information please visit http://huttenhower.sph.harvard.edu/micropita +When using MicroPITA please cite: +Tickle T, Segata N, Waldron L, Weingart G, Huttenhower C. Two-stage microbial community experimental design. (Under review) + +Please feel free to contact us at ttickle@hsph.harvard.edu for any questions or comments! + + + + diff -r 000000000000 -r 0de566f21448 micropita_format_input_selector.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropita_format_input_selector.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,138 @@ +#!/usr/bin/env python + +""" +Author: George Weingart +Description: Dynamically read columns from input file for UI +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "George Weingart" +__copyright__ = "Copyright 2012" +__credits__ = ["George Weingart"] +__license__ = "MIT" +__maintainer__ = "George Weingart" +__email__ = "george.weingart@gmail.com" +__status__ = "Development" + +import sys,string,time +from pprint import pprint + +def red(st,l): + if len(st) <= l: return st + l1,l2 = l/2,l/2 + return st[:l1]+".."+st[len(st)-l2:] + +def get_cols(data,full_names): + if data == "": return [] + max_len =32 + fname = data.dataset.file_name + input_file = open(fname,'rU') + input_lines = input_file.readlines() + input_file.close() + table_lines = [] + for x in input_lines: + first_column = x.split('\t')[0] + table_lines.append(first_column) + + opt = [] + rc = '' + lines = [] + try: + lines = [(red((rc+v.split()[0]),max_len),'%d' % (i+1),False) for i,v in enumerate(table_lines) if v] + + except: + l1 = '*ALL*' + l2 = 1 + l3 = False + MyList = [l1,l2,l3] + lines.append(MyList) + return opt+lines + +def get_cols_add_line(data,full_names,lastmeta): + if data == "": return [] + display_to = 1 + try: + display_to = int(lastmeta) + except: + pass + + max_len = 32 + fname = data.dataset.file_name + input_file = open(fname,'rU') + input_lines = input_file.readlines() + input_file.close() + table_lines = [] + for x in input_lines: + first_column = x.split('\t')[0] + table_lines.append(first_column) + table_lines.insert(0,'-') + if not display_to == 1: + del table_lines[display_to + 1:] + + + opt = [] + rc = '' + lines = [] + try: + lines = [(red((rc+v.split()[0]),max_len),'%d' % (i+1),False) for i,v in enumerate(table_lines) if v] + + except: + l1 = '*ALL*' + l2 = 1 + l3 = False + MyList = [l1,l2,l3] + lines.append(MyList) + return opt+lines + +def get_cols_features(data,full_names,lastmeta): + if data == "": return [] + display_from = 1 + try: + display_from = int(lastmeta) + except: + pass + max_len = 32 + fname = data.dataset.file_name + input_file = open(fname,'rU') + + input_lines = input_file.readlines() + input_file.close() + table_lines = [] + for x in input_lines: + first_column = x.split('\t')[0] + table_lines.append(first_column) + + opt = [] + rc = '' + del table_lines[:display_from] + lines = [] + try: + lines = [(red((rc+v.split()[0]),max_len),'%d' % (i+1),False) for i,v in enumerate(table_lines) if v] + + except: + l1 = '*ALL*' + l2 = 1 + l3 = False + MyList = [l1,l2,l3] + lines.append(MyList) + return opt+lines diff -r 000000000000 -r 0de566f21448 micropita_galaxy_ReadMe.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropita_galaxy_ReadMe.txt Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,34 @@ +#Installation instructions for microPITA in a galaxy environment. +These instructions require the Mercurial versioning system, galaxy, and an internet connection. + +#For general reference about microPita please refer to: +``` +https://bitbucket.org/biobakery/micropita +``` + + + + +#Installation Instructions +In the "galaxy-dist/tools" directory install micropita by typing in a terminal: +``` +hg clone https://bitbucket.org/biobakery/micropita +``` + + +Update member tool_conf.xml in the galaxy directory adding the following: +``` +
+ +
+``` + +Update member datatypes_conf.xml in the galaxy directory adding the following: +``` + +``` + +Copy the 2 *.png members to /galaxy/static/images + +Recycle galaxy + diff -r 000000000000 -r 0de566f21448 micropita_prepare.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/micropita_prepare.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,203 @@ +#!/usr/bin/env python + +""" +Author: George Weingart +Description: Prepare parameters to call micropita +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "George Weingart" +__copyright__ = "Copyright 2012" +__credits__ = ["George Weingart"] +__license__ = "MIT" +__maintainer__ = "George Weingart" +__email__ = "george.weingart@gmail.com" +__status__ = "Development" + +import argparse +from cStringIO import StringIO +import sys,string,time +import os +from time import gmtime, strftime +from pprint import pprint +import subprocess +import blist +import shlex +import tempfile + +################################################################################## +# Modification by George Weingart 5/6/2014 # +# Using subprocess to invoke the calls to Micropita # +# and allocating the temporary file using trmpfile # +################################################################################## + + + + +################################################################################## +# Decode Parms # +################################################################################## +def read_params(x): + parser = argparse.ArgumentParser(description='Micropita Annotate Argparser') + parser.add_argument('--input', action="store",dest='inputname') + parser.add_argument('--output', action="store",dest='outputname') + parser.add_argument('-m', action="store",dest='MParameter') + parser.add_argument('-n', action="store",dest='NSamples') + parser.add_argument('--lastmeta', action="store",dest='lastmeta') + parser.add_argument('--stratify_value', action="store",dest='stratify_value') + + + try: + parser.add_argument('--feature_method', action="store",dest='feature_method') + except: + pass + try: + parser.add_argument('--targets', action="store",dest='targets') + except: + pass + try: + parser.add_argument('--label_value', action="store",dest='label_value') + except: + pass + return parser + + +################################################################################## +# Main Program # +################################################################################## +parser = read_params( sys.argv ) +results = parser.parse_args() +#root_dir = os.environ.get('micropita_SCRIPT_PATH') +root_dir = os.path.dirname(os.path.realpath(__file__)) #Find the current directory where the program resides GW 20160810 + + +fname = results.inputname +input_file = open(fname,'rU') +input_lines = input_file.readlines() +input_file.close() +table_lines = [] +for x in input_lines: + first_column = x.split('\t')[0] + table_lines.append(first_column) + + + +FileTimeStamp = strftime("%Y%m%d%H%M%S", gmtime()) +LastMetaInt = 0 +if results.lastmeta and not results.lastmeta == "None": + LastMetaInt = int(results.lastmeta) - 1 + +StratifyValueInt = 0 +if results.stratify_value and not results.stratify_value == "None": + StratifyValueInt = int(results.stratify_value) - 2 + +LabelValueInt = 0 +if results.label_value and not results.label_value == "None": + LabelValueInt = int(results.label_value) - 1 + +stratify_string = "" +q = '"' +if not results.stratify_value == '1': + stratify_string = " --stratify " + q + table_lines[StratifyValueInt] + q + " " + +if results.MParameter == "features": + TBTargets = list() + TableTargets = results.targets.split(',') + for t in TableTargets: + tb_entry = int(t) + LastMetaInt + TBTargets.append(int(tb_entry)) + + + OutTargetsFile = tempfile.NamedTemporaryFile('w', delete=False ) + TempTargetsFileName = OutTargetsFile.name + indx = -1 + for c in table_lines: + indx+=1 + if indx in TBTargets: + OutputString = table_lines[indx] + "\n" + OutTargetsFile.write(OutputString) + OutTargetsFile.close() + os_command = "python " + \ + root_dir + \ + "/MicroPITA.py "+\ + "--lastmeta " + table_lines[LastMetaInt]+ " " +\ + "--feature_method " + results.feature_method + " " + \ + "--target " + TempTargetsFileName + " " +\ + "-m " + results.MParameter + " " + \ + "-n " + results.NSamples + " " +\ + stratify_string + " " +\ + results.inputname + " " +\ + results.outputname + #print os_command + os.system(os_command) + argsx = shlex.split(os_command) #Split the command + try: + subprocess.check_call(argsx , shell=False) + except: + print "The call to micropita failed=============" + sys.exit(0) + + + +if results.MParameter == "representative"\ +or results.MParameter == "diverse"\ +or results.MParameter == "extreme": + os_command = "python " + \ + root_dir + \ + "/MicroPITA.py "+\ + "--lastmeta " + table_lines[LastMetaInt]+ " " +\ + "-m " + results.MParameter + " " + \ + "-n " + results.NSamples + " " +\ + stratify_string + " " + \ + results.inputname + " " +\ + results.outputname + argsx = shlex.split(os_command) #Split the command + try: + ###os.system(os_command) + subprocess.check_call(argsx , shell=False) + except: + print "The call to micropita failed=============" + sys.exit(0) + + + + +if results.MParameter == "distinct"\ +or results.MParameter == "discriminant": + os_command = "python " + \ + root_dir + \ + "/MicroPITA.py "+\ + "--lastmeta " + table_lines[LastMetaInt]+ " " +\ + "--label " + table_lines[LastMetaInt]+ " " +\ + "-m " + results.MParameter + " " + \ + "-n " + results.NSamples + " " +\ + stratify_string + " " + \ + results.inputname + " " +\ + results.outputname + #print os_command + argsx = shlex.split(os_command) #Split the command + try: + subprocess.check_call(argsx , shell=False) + except: + print "The call to micropita failed=============" + sys.exit(0) diff -r 000000000000 -r 0de566f21448 pcl_diagram.png Binary file pcl_diagram.png has changed diff -r 000000000000 -r 0de566f21448 readme.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/readme.md Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,29 @@ +Installation instructions for micropita in a galaxy environment. + +micropita is installed in Galaxy using the Admin tool of Galaxy. + +In the +``` +Admin tool +``` +select +``` +Search and browse tool sheds +``` +select the tool shed and enter +``` +micropita +``` +select +``` +preview and install +``` + +NOTE: You have to be an administrator in your Galaxy instance to install. + +For general reference about micropita refer to: +``` +https://bitbucket.org/biobakery/micropita +``` + + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/README.md Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,106 @@ +# BreadCrumbs # + +BreadCrumbs is an unofficial collection of scripts and code intended to consolidate functions for tool development and contain scripts for command line access to commonly used functions. Breadcrumbs tends to include functionality associated with metagenomics analysis but you never know what you will find! + + +## Dependencies: ## + +1. Cogent https://pypi.python.org/pypi/cogent +2. MatplotLib http://matplotlib.org/downloads.html +3. Mercurial http://mercurial.selenic.com/ (optional for downloading) +4. Numpy http://www.numpy.org/ +5. Python 2.x http://www.python.org/download/ +6. SciPy http://www.scipy.org/install.html +7. biom support http://biom-format.org/ + + +## How to download ## + +To download BreadCrumbs from BitBucket use the command: + +> hg clone https://bitbucket.org/timothyltickle/breadcrumbs + +To update BreadCrumbs, in the BreadCrumbs directory use the 2 commands sequentially: + +> hg pull +> hg update + + +## Scripts: ## + +Scripts are included to expose core functionality through the command line. Currently these scripts center on manipulating and visualizing abundance tables. +A quick description of the scripts include: + +* *Hclust.py* Flexible script to create a visualization of hierarchical clustering of abundance tables (or other matrices). + +* *scriptBiplotTSV.R* Allows one to plot a tsv file as a biplot using nonmetric multidimensional scaling. + +* *scriptPlotFeature.py* Allows one to plot a histogram, boxplot, or scatter plot of a bug or metadata in an abundance table. Will work on any row in a matrix. + +* *scriptManipulateTable.py* Allows one to perform common functions on an abundance table including, summing, normalizing, filtering, stratifying tables. + +* *scriptPcoa.py* Allows one to plot a principle covariance analysis (PCoA) plot of an abundance table. + +* *scriptConvertBetweenBIOMAndPCL.py* Allows one to convert between BIOM and PCL file formats. + + +## Programming Classes: ## + +Brief descriptions of classes are as follows. More detailed descriptions are given in the classes themselves. + +* *AbundanceTable* Data structure to contain and perform operations on an abundance table. + +* *BoxPlot* Wrapper to plot box plots. + +* *CClade* Helper object used in hierarchical summing and normalization + +* *Cladogram* Object that manipulated an early dendrogram visualization. Deprecated, should use the GraPhlan visualization tool on bitbucket instead. + +* *CommandLine* Collection of code to work with command line. Deprecated. Should use sfle calls. + +* *ConstantsBreadCrumbs* Contains generic constants. + +* *ConstantsFiguresBreadCrumbs* Contains constants associated with formatting figures. + +* *KMedoids* Code from MLPY which performs KMedoids sample selection. + +* *MLPYDistanceAdaptor* Used to allow custom distance matrices to be used by KMedoids. + +* *Metric* Difference functions associated with distance and diversity metrics. + +* *PCoA* Functionality surrounding the plotting of a PCoA + +* *PlotMatrix* Allows on to plot a matrix of numbers. + +* *SVM* Support Vector Machine associated scripts. + +* *Utility* Generic functions + +* *UtilityMath* Generic math related functions + +* *ValidateData* Collection of functions to validate data types when needed. + + +## Demo input files: ## + +* *fastunifrac_Ley_et_al_NRM_2_sample_id_map.txt* Example Unifrac Id mapping file (source http://bmf2.colorado.edu/fastunifrac/tutorial.psp) + +* *GreenGenesCore-May09.ref.tre* Example Greengenes core set reference for Unifrac demo (source http://bmf2.colorado.edu/fastunifrac/tutorial.psp) + +* *Test.pcl* Example file / Test PCL file to run scripts on. + +* *Test.biom* Example file / Test BIOM file to run scripts on. + +* *Test_no_metadata.pcl* Example file / Test PCL file to run scripts on which does not have metadata. + +* *Test_no_metadata.biom* Example file / Test BIOM file to run scripts on which does not have metadata. + +* *Test-biplot.tsv* Example file / Test file for the scriptBiplotTSV.R + + +## Contributing Authors: ## +Timothy Tickle, George Weingart, Nicola Segata, Curtis Huttenhower + + +## Contact: ## +Please feel free to contact ttickle@hsph.harvard.edu with questions. diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/GreenGenesCore-May09.ref.tre --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/GreenGenesCore-May09.ref.tre Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,2 @@ +((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((185648:0.04227,190891:0.04565):0.03317,198806:0.03807):0.03385,(189521:0.03381,194180:0.04431):0.00527)adhufec43:0.00520,(107329:0.05109,134265:0.08790):0.00857):0.00412,108139:0.05816):0.01745,(((114813:0.06855,56320:0.05242):0.00845,55854:0.05484):0.00743,115051:0.07172):0.00335):0.00164,2200:0.04835):0.01242,68416:0.04600):0.00247,(((102007:0.06112,104511:0.04475)RC11:0.04600,(229211:0.07581,36896:0.03548):0.00677):0.01000,(114946:0.04569,130804:0.05242):0.01849):0.00330):0.00984,(((102469:0.04858,106618:0.03644):0.00503,2170:0.02916)Prevotella:0.04830,(25004:0.05331,31856:0.06384):0.00761):0.00166):0.02376,192222:0.08949):0.00764,157387:0.06134):0.01165,((((((225558:0.03560,28952:0.02736)DO039:0.03401,73937:0.03641):0.00416,(106015:0.02958,157425:0.07769):0.00759):0.01233,(20534:0.04248,83456:0.03320):0.01333):0.00412,30384:0.08333):0.01347,160648:0.06392):0.03408):0.00167,((((((100912:0.09780,211356:0.08210):0.00434,209576:0.06371):0.03422,83963:0.07264):0.00929,((208212:0.06053,26888:0.06634):0.00847,103876:0.07195):0.02700):0.00417,((((204894:0.09617,234111:0.06063):0.02667,68356:0.08158):0.01185,234668:0.06877):0.00168,(167734:0.03160,189330:0.09330):0.01108):0.00991):0.00990,34809:0.07579):0.00334)Prevotellaceae:0.02712,(((((((((186688:0.03732,195634:0.04082):0.00873,183721:0.04661)RL303_aal70c09:0.02605,(187087:0.05165,191786:0.05178):0.03723):0.00349,(184646:0.12544,187452:0.03393):0.00453)RL304_aal76g04:0.04040,199514:0.04153):0.05699,((162684:0.08071,183870:0.06864):0.00696,198706:0.06107):0.00417):0.00413,190309:0.02792):0.00082,((((((((102620:0.05353,203004:0.03966):0.00172,102407:0.03312):0.01396,114500:0.04604):0.00332,(137514:0.02744,30861:0.06366):0.01596):0.00408,149504:0.01612):0.00489,(175182:0.06277,182569:0.10687):0.01103):0.00490,139167:0.01616):0.00817,2045:0.01942):0.00164):0.01867,((((((((158207:0.07593,158624:0.06624)CFT112E11:0.00689,172821:0.03148):0.00491,(157565:0.05250,158580:0.08165):0.04193):0.01333,210534:0.02421):0.00576,133416:0.03632):0.00905,28056:0.05224):0.01157,174109:0.10602):0.00427,197735:0.10746):0.00677):0.00576):0.00992,(142859:0.04996,142871:0.05771):0.02254):0.02487,102227:0.06388)Bacteroidaceae:0.00587,153657:0.07229):0.00167,((((((((((((((((((((((177371:0.03581,185012:0.04263):0.01242,133805:0.03038):0.00259,191536:0.09941):0.00268,(184028:0.04855,193506:0.05200):0.05775)M1_e11_3:0.01571,97196:0.04784):0.00336,((((179616:0.06356,196985:0.03396):0.00792,179958:0.06706):0.01322,98683:0.03943):0.00500,227615:0.08420):0.01528):0.00581,(157824:0.10914,234795:0.08891):0.02264):0.00501,38438:0.05008):0.02161,99034:0.05578):0.01755,163328:0.03393):0.00579,45604:0.04988):0.01158,((132011:0.03388,135872:0.03365):0.00176,179000:0.09822):0.00356):0.00329,(175763:0.04068,186820:0.05593):0.11838):0.00337,(176174:0.05419,18983:0.03955):0.00671):0.00408,98087:0.04839):0.00248,(((((177014:0.04996,38834:0.01130):0.00746,135808:0.04526):0.00662,(130860:0.06831,131443:0.02448):0.02548)rc5-47:0.00411,(178045:0.08213,182458:0.02957):0.00978):0.01149,(187947:0.05433,197891:0.04576)C13_G12:0.02747):0.00165):0.00408,((((((183750:0.04399,194828:0.02961):0.00439,44489:0.00888):0.02267,212685:0.08555)F5:0.01949,191820:0.12437):0.01716,183477:0.05838):0.01417,(100296:0.06618,97151:0.03759):0.00676):0.00497):0.00491,190369:0.03396):0.01884,188851:0.07022):0.01263,209806:0.10743):0.00513,(45529:0.09465,84125:0.06624):0.00949):0.00664,49015:0.08678)p-184-o5:0.01688):0.00417,((202464:0.08190,32849:0.07173):0.01985,77707:0.07102):0.00332):0.00747,(((((((175210:0.04983,44678:0.06525):0.00980,135739:0.06014):0.00613,148038:0.05417):0.00957,114529:0.05077):0.00662,(((146492:0.03952,149037:0.04725):0.01775,77973:0.07651):0.03110,173598:0.06282):0.00972):0.00413,(149604:0.05082,38316:0.06525):0.00798):0.00412,148392:0.06793)BCf7-02:0.00584):0.00247,45562:0.06149):0.00250,(((((106462:0.03062,224633:0.05560):0.01170,219368:0.04190):0.00410,223582:0.03162):0.00329,(111001:0.03581,135813:0.03101):0.02679)Proteophilaceae:0.01073,109928:0.11666):0.02133):0.00250,(((101086:0.03228,106291:0.04435):0.00417,(191378:0.07223,91816:0.02764):0.01259):0.00410,1966:0.04209)Dysgonomonaceae:0.01072):0.00737,(((((108520:0.06288,63166:0.05157)Porphyromonas_canis:0.03475,(129018:0.04512,1950:0.04435):0.01591):0.00831,((115278:0.02820,138179:0.06158):0.02183,1941:0.02743):0.01395)Porphyromonadaceae:0.02129,(145262:0.05498,19082:0.03729)Tannerellaceae:0.02466):0.00249,(((((153827:0.03952,158914:0.07655)Parabacteroides:0.01439,104613:0.07654):0.01259,188911:0.08291):0.01522,148629:0.04828):0.01161,1929:0.03226)Parabacteroidaceae:0.01144):0.01396):0.00986,(((((180430:0.07373,199354:0.04831):0.04344,180133:0.04746)Barnesiella_viscericola:0.01750,184023:0.04907):0.00699,197517:0.04992)adhufec77-25:0.01133,(160421:0.06559,169813:0.07097):0.00854):0.00331):0.00491,149371:0.06121):0.00415,((((((110885:0.05335,1975:0.03716):0.00838,136506:0.03153):0.00491,224273:0.06441):0.00335,((111319:0.05766,143744:0.06952)COB_P3-5:0.03732,109632:0.06172):0.01009):0.00000,(((102513:0.03941,165982:0.04858):0.04268,152521:0.05372):0.00252,111016:0.06368):0.01000):0.00660,(((46885:0.06306,82191:0.05783):0.00337,135845:0.05675)Rs-P01:0.01161,(141525:0.04828,203425:0.04733):0.01880)AU126:0.00331):0.00330):0.03699,((((197817:0.05763,70461:0.08699):0.01199,114637:0.06290)Odoribacteriaceae:0.00168,113220:0.08115):0.04280,57448:0.06023):0.01349):0.00583,((((((146313:0.04597,224027:0.04257):0.02008,221166:0.09040):0.00593,109485:0.04835):0.00826,51657:0.05318)Marinilabiliaceae:0.00829,109318:0.07033):0.01006,99342:0.06290):0.00250):0.00328,174634:0.03304):0.01731,((((((((114082:0.09495,181596:0.10518):0.04294,215462:0.07792):0.00170,103304:0.04837):0.01157,1913:0.06694):0.00585,((((157367:0.12136,1914:0.04435):0.02076,159129:0.06192):0.01255,107002:0.09048):0.01191,205413:0.05242):0.00415):0.00164,((133282:0.11551,224797:0.08063):0.01140,(136500:0.06210,159287:0.09139):0.03162):0.01092):0.00413,(((((112727:0.04490,62690:0.05579):0.01367,1912:0.03592)Hyd89-65:0.04748,(156416:0.04369,159518:0.03206):0.00750):0.00831,((((159366:0.03787,203238:0.04793):0.00251,201544:0.05078):0.02552,148676:0.09561):0.01610,114375:0.04495):0.00749):0.00328,(101142:0.04847,225841:0.02836):0.00418):0.00490):0.00245,((((((147178:0.04393,99442:0.10242):0.00256,(157758:0.04327,81494:0.07258):0.00253):0.00412,66950:0.05726):0.00749,(((160748:0.03306,70875:0.06371):0.01260,141722:0.04358):0.01075,1907:0.03465):0.00082):0.00824,112872:0.05364):0.01324,((((((((105037:0.10645,144997:0.07655):0.00697,90166:0.06379):0.01832,82372:0.05987):0.01503,44257:0.10645):0.01025,101780:0.07742)BA017:0.03033,1906:0.04271)E1-K9:0.01497,1911:0.02823)Bacteroidetes_bacterium_PPf50E2:0.00000,106304:0.03468):0.01312):0.01070):0.00163):0.00573,(((((((((((((114648:0.03554,148295:0.04145):0.01161,149212:0.04569):0.02063,106447:0.04919):0.01247,111350:0.07587):0.01593,(100440:0.06124,83423:0.05641):0.01780):0.01168,103074:0.07897):0.01180,((((154358:0.07857,154809:0.01467)UNNAMEABLE_1185324687:0.05488,187075:0.05008)UNNAMEABLE_1185324656:0.02089,66189:0.05730)E13:0.02002,62390:0.06420):0.00668):0.00579,(111184:0.03465,145762:0.06034):0.01588):0.00497,(101774:0.03387,131798:0.05241):0.00416):0.00657,((114514:0.07816,134023:0.06235):0.01199,105524:0.08783):0.01101):0.00165,(((103887:0.05165,72909:0.04192):0.01421,214867:0.05560):0.00743,79360:0.06371):0.00334):0.00820,((((((((108502:0.04754,174178:0.08016):0.00510,76499:0.06336):0.00250,(81270:0.03643,83897:0.04194):0.01082):0.00413,(107777:0.03153,205266:0.04369):0.04264):0.00909,(159438:0.03176,57445:0.03226):0.00923):0.00327,151832:0.05592):0.00332,77721:0.03707):0.00573,45081:0.06285):0.01504):0.01153,101918:0.04029):0.00983):0.00327,(((((((((((((((((((101991:0.04439,110799:0.05331):0.02177,(38895:0.06981,70719:0.08445):0.01298):0.00331,(21213:0.04323,68353:0.05157):0.01513):0.00905,(100923:0.04512,23709:0.07494):0.00679):0.00657,((100279:0.05331,101584:0.03304):0.02254,1957:0.04632)RC2:0.03322):0.01493,111138:0.04674)RF14:0.01827,158854:0.05935):0.01167,40523:0.05474):0.00251,((107379:0.03896,162741:0.03787):0.01082,110080:0.06024):0.00250):0.00821,(175099:0.13440,52441:0.05405):0.01593):0.02471,145850:0.04832):0.00333,((110787:0.13065,99097:0.07270):0.00970,205659:0.05641):0.00666):0.01322,28192:0.02337):0.00822,(154549:0.12054,1885:0.03062)vadinBC27:0.00687):0.01149,((145899:0.04897,173359:0.06627):0.00090,162503:0.05250):0.00088):0.02058,((((114668:0.08541,145246:0.07161)BCf9-17:0.02807,136501:0.07010):0.01350,150318:0.07216):0.00508,150102:0.07818):0.00252):0.01335,(((((((((((((175142:0.05415,196291:0.04738):0.00616,179241:0.14298)RL306aal92h06:0.01985,196101:0.05349):0.06055,182806:0.07783):0.00806,((180277:0.16041,183480:0.04315):0.00465,109541:0.02508):0.00489):0.01309,150083:0.05340):0.00581,((148007:0.02346,230403:0.02407):0.01573,150394:0.03793):0.00247):0.00328,149167:0.06169):0.00500,145476:0.04228):0.00496,146034:0.04055):0.01236,150261:0.06475):0.00502,147686:0.07258):0.00167,232888:0.07054)Rikenellaceae:0.01424):0.00666,((16915:0.07780,86555:0.08562):0.02757,108457:0.04915)p-2534-18B5:0.02497):0.00167,109567:0.09347):0.00929)Bacteroidales:0.00247,(((((((((((((((((((213627:0.02839,2446:0.03974):0.00333,195962:0.05954):0.03496,167133:0.05887)Psychroserpens_burtonensis:0.01680,82233:0.03548):0.00654,(101465:0.12377,75119:0.05242):0.00871):0.01643,((116532:0.04110,136935:0.04362):0.00585,(133395:0.05493,2419:0.03977):0.02695):0.00411):0.00575,2401:0.02827):0.00739,((((((100504:0.04289,139292:0.04355):0.02252,(145434:0.04020,83542:0.01778):0.00993):0.01730,98563:0.04530):0.00740,2403:0.06253):0.01755,((2370:0.02015,2390:0.04671)Capnocytophagaceae:0.07389,77851:0.04156):0.01090):0.00661,79767:0.04271):0.00823):0.00490,((105331:0.03877,61189:0.04674):0.01501,118817:0.02579):0.00819)Cytophaga:0.00982,(((143354:0.08150,214543:0.04615):0.07336,155780:0.02262):0.01095,(103662:0.05493,60733:0.03713):0.00251)Sporocytophaga:0.01720):0.00986,(18143:0.04113,2359:0.04271):0.01666):0.00993,142736:0.06058):0.01415,104416:0.05465):0.01670,(((((((104818:0.04987,31516:0.06129):0.02189,45877:0.03626):0.01073,142000:0.04351):0.04294,162739:0.05721):0.00419,((175135:0.05766,28957:0.05806):0.01941,2251:0.04029):0.00830)Flavobacteriaceae:0.00494,168322:0.04870):0.00746,(((147524:0.07099,150191:0.06932):0.02784,2233:0.05817):0.00334,(141251:0.08186,165396:0.08299)Candidatus_Sulcia:0.02156)Blattabacteriaceae:0.04653):0.02180):0.01903,(((((((163423:0.02348,20456:0.04029):0.00578,119242:0.03710):0.00740,26642:0.06860):0.01586,(62693:0.01453,69811:0.03978):0.01071):0.00000,81782:0.04689):0.00909,(102122:0.04762,58645:0.03871):0.03515):0.00743,(((49398:0.03920,65462:0.06890):0.01207,28868:0.05645)PHOS-HE28:0.04470,107797:0.07825):0.00682):0.01240):0.00165,86113:0.05659):0.00582,((((((136894:0.05963,171242:0.13145):0.02900,118269:0.05000):0.02000,101620:0.05650):0.00911,((122531:0.06986,152557:0.10362):0.00174,95582:0.05858)F1CA7:0.01326):0.00497,(79316:0.08058,80284:0.07699):0.00688):0.00912,(((((141750:0.07990,231888:0.04351):0.00255,169777:0.04126)K2-30-6:0.02220,192192:0.04962):0.00668,2474:0.08053):0.00588,((192107:0.04847,82792:0.04762):0.03094,100077:0.05242):0.01334):0.00165):0.00492):0.00164,((102252:0.06930,103940:0.07661):0.01883,223710:0.09927):0.00426)Flavobacteriales:0.00497,((130464:0.08871,62691:0.07097):0.01981,(1878:0.06285,95345:0.06515):0.03305)vadinHA21:0.00666):0.00660):0.02374,((((((((((151251:0.05963,155097:0.03948):0.01259,148790:0.03948):0.01233,(154665:0.06366,43436:0.03062):0.00418):0.00494,46477:0.03626):0.00576,(77995:0.02740,99322:0.03143)Sphingobacterium:0.01814)Sphingobacteriaceae:0.02703,144456:0.05401):0.00335,(((39088:0.01612,40435:0.06935)Pedobacter:0.02592,199804:0.03663):0.00165,(106893:0.03492,81220:0.03716):0.00248):0.00653):0.02288,56235:0.05721):0.01674,((169294:0.06629,194302:0.05242):0.03129,21292:0.07419):0.01179):0.00334,(((((226802:0.07045,85393:0.05916):0.01107,2483:0.04808)Tuber_borchii_symbiont_b-17BO:0.02894,190972:0.05912)truffle_symbionts:0.00847,(227169:0.03160,83335:0.03952):0.01161):0.00578,(((215580:0.06149,29808:0.07273):0.00697,113559:0.05259):0.00677,2488:0.05656)b-17BO:0.00673):0.00414):0.01148):0.00664,((((132160:0.06624,134493:0.07097):0.05104,(138791:0.06220,220359:0.07730):0.02819):0.01015,(2554:0.07977,55727:0.06124):0.04208)LD1:0.01433,(((143679:0.06457,211503:0.10274):0.00690,133672:0.06538)E2aB05:0.02510,((156770:0.06457,56341:0.07611):0.02136,226885:0.11452):0.00602):0.00754):0.00832):0.00579,(((((((((((((((((110868:0.07758,2534:0.06606):0.00427,146854:0.07599):0.01255,229271:0.09310)filamentous_bacterium_Plant1_Iso8:0.03796,((191465:0.02188,19837:0.02512)Ebpr18:0.01569,(2540:0.03241,2563:0.05435):0.01337):0.01820):0.01595,228572:0.04774):0.01408,((109301:0.03079,157643:0.04432):0.02995,175547:0.04135):0.01166):0.00416,((((228176:0.05802,95603:0.04882):0.01555,112888:0.05424):0.00924,(115129:0.05721,230258:0.09924):0.00945):0.00164,139475:0.08452):0.00931):0.00823,(((((((159330:0.05541,194491:0.04274):0.01508,159522:0.04627):0.00743,223322:0.04274):0.00744,2550:0.09177):0.01780,(219002:0.03560,2551:0.03641):0.04424):0.01406,((159352:0.04331,159432:0.03959):0.02496,192697:0.07143):0.00755):0.00660,(149663:0.05816,159244:0.06121):0.01098):0.01238):0.00741,(((90502:0.06530,99828:0.06527):0.00512,150023:0.05970):0.01079,142556:0.05242):0.01324):0.00493,((((112991:0.07949,29244:0.06866):0.04277,63062:0.04443):0.01583,105211:0.06402)Saprospira:0.02759,140122:0.06769):0.01429)Saprospiraceae:0.03458,((((((((((58374:0.03465,91130:0.09220):0.00512,130882:0.03062):0.01061,228870:0.06548)FukuN24:0.01003,((154287:0.01937,156950:0.02663)Niabella:0.01317,(204430:0.05913,206355:0.08796)AKYG516:0.04923)Terrimonaceae:0.00166):0.00823,(((((199043:0.05157,68865:0.03647):0.04530,32581:0.05882):0.00252,141748:0.04298):0.00247,142837:0.06478):0.00834,143099:0.11295):0.01277):0.00492,(((((17675:0.04029,182371:0.04593):0.02506,225192:0.06129)BIsi8:0.02669,101380:0.05705):0.00837,99395:0.04866):0.00415,((225059:0.08952,2508:0.08548)cilia-associated_respiratory_bacterium_R3:0.03663,72978:0.03423):0.00498):0.01734):0.00492,26459:0.05399):0.01163,(198375:0.10227,37034:0.04835):0.00343):0.02148,(100109:0.08226,209573:0.04837)UNNAMEABLE_1185325920:0.04513):0.00421,((((219038:0.07389,224117:0.08104)Elev_16S_978:0.02061,234598:0.11264):0.00599,131471:0.06699):0.00668,90147:0.06613):0.00501):0.01573):0.00335,159170:0.07118)Saprospirales:0.03021,(((58625:0.08783,58627:0.07431):0.01982,58628:0.07270):0.00168,115032:0.09863):0.01954):0.00419,(153017:0.07897,2577:0.04674)JTB248:0.02210):0.00333,((((223012:0.07329,25963:0.05049):0.01627,27784:0.06618)Cardiniaceae:0.04587,19323:0.08049):0.00684,204028:0.06849):0.01587):0.00331,(155396:0.10878,66029:0.06981)Persicobacter:0.01305):0.00828,151512:0.11138):0.00341):0.00413,(((((((((((170751:0.09847,180954:0.05721):0.02157,58624:0.06688):0.00335,((19806:0.08678,32271:0.10170):0.01574,99461:0.11436):0.00602)Flexibacter:0.01585,33001:0.10475):0.00684,((114481:0.05982,65682:0.06044)C0108:0.03820,163626:0.07903):0.00677):0.00671,58637:0.08058):0.00336,153549:0.07143):0.00673,(((((146467:0.06048,150422:0.07183):0.00596,162617:0.08160)Spirosomaceae:0.00589,71316:0.08323):0.05195,(2631:0.11893,80101:0.10968)envOPS17:0.02216):0.00256,(2596:0.08567,99418:0.07380):0.03267):0.01262):0.00833,94046:0.06613):0.00836,(209792:0.07661,58609:0.05157):0.00767):0.00165,(((2588:0.05242,79737:0.07022)BD1-27:0.01863,102384:0.04271):0.00744,(103014:0.05726,140328:0.06205):0.01780):0.00330)Flexibacterales:0.00821):0.00247,(((172214:0.10254,58611:0.06694):0.02257,133292:0.08300):0.00590,58606:0.06538):0.01830):0.00660,((2603:0.06613,42709:0.06266):0.01531,2592:0.10420)Hymenobacterales:0.01020):0.02067,113727:0.07042):0.01516,2630:0.07230):0.02018,((((209483:0.08609,27657:0.08158):0.01383,139436:0.07984):0.00337,134676:0.08952)SM1A07:0.02365,112826:0.07881):0.01102):0.03667,((((((136516:0.05806,2650:0.05242):0.00169,(156811:0.05887,60166:0.04273):0.00756):0.00330,28419:0.08065):0.00503,(148406:0.05932,161044:0.05500):0.02278)Salinibacter:0.02159,2636:0.03465)Crenotrichaceae:0.00911,((((100508:0.04029,139729:0.06366):0.02525,(154451:0.06958,62692:0.04593):0.00762):0.01658,69756:0.08397)KSA1:0.08548,60618:0.07680):0.02155):0.02240):0.01930,2640:0.12591)Bacteroidetes:0.05164,13725:0.08637):0.00855,(217505:0.15649,48617:0.09847):0.01701):0.01175,(((((((((((148646:0.07829,226370:0.07238):0.00943,32172:0.07189):0.00502,109769:0.08966):0.03302,65675:0.07264):0.01017,(((159549:0.05335,166579:0.06462):0.00847,29910:0.04963):0.00658,10543:0.07279):0.00922):0.00332,((((((156555:0.05097,162538:0.05089):0.00589,10538:0.04764):0.00494,(164866:0.06058,65674:0.05165):0.02377):0.00498,100270:0.06144):0.01167,148869:0.05488):0.00416,151687:0.04927):0.00744):0.00493,211026:0.07674):0.03613,151164:0.07027)TM6:0.08464,((134487:0.05731,63107:0.04795):0.01252,131826:0.05656)CD12:0.09331):0.02002,(88848:0.11590,99949:0.11153):0.01271):0.00762,(((((145536:0.06632,1872:0.05963):0.02622,25709:0.05882):0.00920,155851:0.05484):0.01248,57551:0.06124):0.01504,94963:0.10351)Deferribacteres:0.02810):0.00756):0.00832,((((((((((((((110103:0.02907,1456:0.00726):0.01231,141312:0.05106)Nitrospira:0.02074,102418:0.02018):0.02386,183441:0.04113):0.00248,((1443:0.02829,70898:0.04210)N_marina:0.01910,136664:0.03228)Nitrospiraceae:0.00826):0.01805,142598:0.05401)Nitrospirales:0.02492,99346:0.07944):0.00338,123055:0.05569):0.00996,(((138554:0.08219,42281:0.06156):0.01281,(184252:0.05602,41859:0.05255)Leptospirillaceae:0.08911):0.00677,139184:0.07339):0.00335):0.00828,((((((((101911:0.06650,105156:0.05421):0.00763,103056:0.03087):0.01311,(1529:0.04365,46856:0.06543):0.02798):0.00247,((1513:0.05987,160813:0.09205):0.02851,101018:0.04495):0.00582):0.00575,151127:0.07861)Thermodesulfovibrionaceae:0.02687,143240:0.08013):0.01099,154678:0.10625)Thermodesulfovibrionales:0.04328,76967:0.08022):0.00514)Nitrospirae:0.01487,151577:0.06769):0.02190,(223032:0.05730,24509:0.05964)Entotheonella:0.07660):0.00844,((24410:0.03777,38392:0.04358):0.03253,112460:0.03574)WCHB1-27:0.04886):0.00756,((130451:0.05497,160657:0.05335):0.02194,138844:0.05168)KSB3_GN06:0.08746):0.00253):0.00333,(((((((((159082:0.08557,204759:0.07276):0.00171,102906:0.08842)GN04:0.02862,99873:0.07713):0.01272,26077:0.06629):0.00503,(((223977:0.07274,57451:0.11274):0.00718,106690:0.10385):0.00254,(132912:0.08209,99178:0.08939):0.00442):0.00332):0.00747,((((((159197:0.04467,159489:0.05082):0.02058,151268:0.05740):0.00249,159076:0.03800):0.00082,159063:0.05254):0.00579,(159115:0.07706,162879:0.04476):0.00588):0.00910,136895:0.07048):0.00587):0.00083,((((130950:0.01493,164420:0.05897):0.00666,52628:0.07451):0.01337,(166372:0.04194,24135:0.04056):0.01248):0.00328,160424:0.07181)Caldithrixales:0.01513)Caldithrix_KSB1:0.00663,(((((((((137211:0.06947,159771:0.06446):0.00426,159953:0.05403):0.00579,127630:0.04754)ZA3648c:0.00827,227844:0.07736):0.04364,108876:0.06691):0.00594,((105774:0.05479,68979:0.06366):0.00928,123462:0.10878):0.00931):0.02311,(102419:0.08461,18783:0.07677):0.00691):0.00585,130187:0.08437)Marine_group_A:0.02106,((156269:0.13081,1663:0.10079):0.02056,19024:0.11138)OP3:0.02646):0.01536):0.01489,(((((((((((113744:0.03754,212556:0.06730):0.02222,81374:0.05488)N1423WL:0.01077,44017:0.05246):0.00417,114411:0.04244):0.00248,(111580:0.02984,89632:0.04564)Gemmatibacter:0.00083):0.00574,112960:0.05174):0.00661,(((216456:0.13829,55923:0.05825):0.01489,206755:0.06587):0.01331,113452:0.06809):0.01165)Gemmatimonadales:0.05092,(((((((210111:0.03976,216714:0.06649):0.03803,79951:0.05809):0.01374,77189:0.05285)N1118WL:0.01346,221485:0.08664)Gemm-1b:0.01186,130982:0.06661):0.01760,142753:0.04446):0.01757,166488:0.05174):0.01158):0.01590,169264:0.05901):0.01913,(((((((((13726:0.02264,88657:0.07848):0.01183,90275:0.03566):0.00577,113023:0.04433):0.00165,204909:0.03407):0.00984,11278:0.04850)Gemm-2:0.01660,((114114:0.05451,73339:0.03078):0.04353,113526:0.06452)Gemm-5:0.00421):0.00500,136926:0.07120):0.01090,((113199:0.07228,113624:0.05461):0.01693,22051:0.06307)Gemm-3:0.02537):0.00419,((37339:0.03231,88193:0.10187):0.00855,11593:0.03803)Gemm_4:0.04355):0.00418):0.01903,134102:0.08152)Gemmatimonadetes:0.03394):0.01256):0.00413,(((((((((((((((((((((((((((((((182970:0.02344,216941:0.07062):0.01932,179166:0.04584):0.00416,(190238:0.07477,192663:0.07294):0.02893):0.00995,((180127:0.09856,181007:0.05773):0.01649,108381:0.00323):0.00326)Akkermansia:0.09574,45741:0.04674):0.01362,(((104795:0.04722,62479:0.04520):0.01087,(146066:0.05484,152550:0.06406):0.00591):0.00495,150817:0.05873):0.00752):0.00082,159582:0.05551):0.00415,(161403:0.05353,34403:0.04516):0.00841):0.00578,((((106474:0.11803,23971:0.06402):0.00697,102655:0.05839):0.00917,(1588:0.03480,19930:0.04344):0.02920)Prosthecobacter:0.00995,210079:0.10440):0.03414)Verrucomicrobiae:0.01491,((((((((((217644:0.06016,224828:0.06306):0.00171,152808:0.03554):0.00410,(1624:0.03958,205645:0.07528):0.00594):0.01466,1605:0.02341):0.00409,108024:0.02450):0.00245,224503:0.05578):0.00828,(29275:0.05565,45580:0.04432):0.00925):0.00740,60575:0.06494):0.00500,220629:0.07842):0.01003,162638:0.05645)Spartobacteria:0.00500):0.00578,159520:0.09914):0.02807,((137501:0.07033,156660:0.06634):0.00855,230312:0.07963)LD19:0.02615):0.00505,((((((((114550:0.06462,17862:0.07926):0.05139,(24447:0.05493,41135:0.03635):0.01261):0.00420,(135899:0.05811,143442:0.05556):0.04186):0.01168,(111739:0.07351,95864:0.08562):0.01212):0.00248,171401:0.07270):0.00667,105129:0.06784):0.00586,(1563:0.05331,9675:0.04295):0.01175):0.01076,234599:0.06452)Opitutae:0.08160):0.01268,((((((((((226151:0.02583,226796:0.01130):0.00984,152799:0.01935):0.00408,(186160:0.01935,220892:0.04868):0.00581):0.00327,(155778:0.02341,155827:0.03963):0.00083):0.01306,149282:0.04925):0.00663,(17035:0.04619,21798:0.06667):0.00758):0.00576,155970:0.04689):0.00331,((105864:0.04722,1572:0.04516):0.02434,213594:0.07699):0.00337):0.00492,((110302:0.07593,148516:0.09208):0.00953,(156301:0.04874,156535:0.10186):0.00776):0.00249)Pedosphaerae:0.03713,(131435:0.07258,142856:0.04927)RB324:0.02890):0.00834):0.00663,(111904:0.08124,213179:0.06416):0.01184):0.04055,(110143:0.07796,83178:0.05085)TP21:0.04762):0.01176,144734:0.05004):0.00748,223835:0.06750):0.00832,((((((113883:0.04750,61856:0.04963)R76-B18:0.07926,225823:0.06627):0.01793,63784:0.04184):0.00912,(62664:0.04766,63772:0.04858):0.00420):0.01642,70602:0.07374):0.00421,(217200:0.04643,39097:0.06311):0.00606):0.00658):0.00498,(((((((148705:0.05101,99547:0.08202):0.00594,67843:0.07861)F23-G11:0.00753,1537:0.08562)RFP12:0.05239,(221738:0.04216,63777:0.04351):0.00585):0.01008,1547:0.05414):0.00165,172802:0.05250):0.01498,((221823:0.07338,222698:0.04690):0.01586,1540:0.03877):0.01065)Verruco-5:0.02307)Verrucomicrobia:0.02730,(((((((148459:0.09942,79502:0.03952):0.02486,103353:0.07327):0.01175,33009:0.06548):0.01504,(111543:0.10339,113979:0.06613):0.00870):0.00580,10689:0.05242):0.00498,(((100123:0.04843,1809:0.05743):0.04710,131533:0.05004):0.00335,(142604:0.06935,31745:0.07661):0.00515)Victivallales:0.00581)Lentisphaerae:0.05759,(((((((162475:0.06897,171324:0.05800):0.07857,136933:0.05174):0.00338,54620:0.04801):0.00577,(152689:0.04731,89521:0.05497):0.03713):0.00332,3009:0.07010):0.00752,67842:0.06903):0.02258,108673:0.08972)Chlamydiae:0.08921):0.02065):0.01253,(((((((204198:0.06261,88716:0.06964):0.01620,105025:0.07774):0.03796,140666:0.12195)BS1-0-34:0.03050,((233045:0.07266,91224:0.06959):0.01062,155430:0.08627):0.06505):0.00515,(((154140:0.06977,226121:0.06481):0.02626,1681:0.08083)koll11:0.02246,(102531:0.07042,49231:0.07926)PBS-87:0.01407):0.02065):0.02182,89297:0.09539):0.00516,((((121064:0.04754,220692:0.06276):0.05310,(1665:0.07258,40185:0.07449):0.00772)BD4-9:0.00669,(102886:0.07602,231909:0.08826):0.00445):0.01744,(((150264:0.08333,217227:0.07554):0.01176,(204818:0.05202,222877:0.07705):0.00952):0.02212,(107274:0.08110,211556:0.06255):0.06681):0.00169):0.00671)OP3:0.01249):0.01499,63785:0.09121):0.01098,(((((((65686:0.05497,89364:0.05807):0.00252,111995:0.07773):0.00756,111602:0.06785)SC4:0.09034,81510:0.13096):0.01243,(130062:0.07871,41280:0.10252):0.01491)SC4:0.02015,((((12396:0.07045,129392:0.05937):0.01355,132414:0.07347):0.03023,159362:0.08958):0.01363,13727:0.07016)WS1:0.02430):0.00926,(168638:0.11391,65677:0.11703)WS2:0.03934):0.01597):0.00418,((((((((((((((((115016:0.10204,135740:0.07699):0.00517,207653:0.10999):0.02220,218858:0.09659)G18:0.00762,210997:0.06728):0.01012,135863:0.08298):0.00171,((135705:0.05047,138301:0.05712):0.01272,91522:0.06088):0.01174)B86:0.03915,((((((124913:0.04231,230061:0.07792):0.01023,231309:0.03977):0.00498,135881:0.09556)DE613:0.03978,155144:0.07922):0.00083,((135762:0.04441,28705:0.05206):0.03804,91792:0.03339):0.00667):0.01073,159543:0.05758):0.01260)CL120-56:0.04706,((((167650:0.04335,64245:0.06855):0.01095,113548:0.04689):0.01984,141239:0.09943):0.00427,((202106:0.08569,62477:0.05323):0.00855,44762:0.07745):0.01175):0.01741):0.00585,226443:0.06538)CL500-3:0.04149,48167:0.10088):0.00774,((((((((139615:0.06295,200413:0.06860):0.00597,159392:0.04127):0.00497,((218700:0.05782,63101:0.03148)MB-C2-105:0.01675,2842:0.05250):0.00666):0.00986,75622:0.08559):0.00507,2794:0.07893)BD2-16:0.01501,(154445:0.05246,227130:0.08333):0.04007):0.00842,(((((((203588:0.04195,222530:0.12047):0.02607,112280:0.04516):0.00911,55354:0.06053):0.00419,222661:0.05546):0.01324,44216:0.03874)Planctomycetales_bacterium_Ellin6207:0.02309,161593:0.07225)Ellin6207:0.03882,226380:0.11446):0.01901):0.00084,((((191128:0.05085,86075:0.05594):0.01266,138356:0.07536):0.01844,213004:0.11612):0.01372,218455:0.08612):0.00085):0.00415):0.00249,(((((130306:0.05347,136359:0.05254):0.00696,101232:0.07377):0.00431,159328:0.06606):0.00344,(206757:0.07436,226806:0.06159):0.01265)Napoli-3B-72:0.02050,(225770:0.06346,2843:0.07471):0.05359):0.01034)WPS-1:0.04772,((111473:0.07674,86383:0.08684):0.02490,(222300:0.08772,85294:0.06452):0.05509):0.00510):0.00678,((((((((131207:0.06822,221873:0.07130):0.03841,130035:0.09575):0.03420,((161018:0.11750,171614:0.11120):0.02915,165300:0.09393):0.01026):0.00845,(104440:0.07557,226807:0.09473):0.03499):0.00669,156688:0.09213):0.02294,(((((132935:0.13448,39505:0.11167):0.01011,233513:0.12368):0.01291,112905:0.14446):0.01578,(10700:0.10833,215025:0.13006):0.04136):0.01203,(136769:0.06196,98560:0.10579):0.01553):0.00758):0.00333,((217349:0.06958,83746:0.07527):0.02895,1711:0.10994):0.01814):0.00333,(((((159293:0.06592,60573:0.05977):0.00765,201142:0.03716):0.04360,84172:0.10597)CL500-15:0.01032,(((193866:0.04211,2788:0.07466):0.01436,161200:0.05902):0.00501,223894:0.06098)ARKCH2Br2-76:0.03332)agg27:0.00501,(172636:0.11183,83466:0.10602):0.01502):0.01593):0.00911):0.00744,((((((((((((((((217998:0.05227,35556:0.02855):0.01523,50882:0.03997):0.03087,217207:0.05882):0.00920,223634:0.07166)A17:0.01175,204874:0.07607):0.01002,((168811:0.07237,96259:0.05139):0.02490,159463:0.07407):0.00675):0.00248,((((27673:0.05103,2818:0.05481)Pirellula:0.03002,159376:0.07124):0.00922,170512:0.11946):0.01876,2816:0.06159):0.00922):0.00331,(149101:0.03554,71342:0.05455):0.01760):0.00739,((2801:0.07348,2833:0.06928):0.01055,137345:0.07351):0.02258):0.00414,(2823:0.08061,35238:0.06624):0.01362):0.00415,(171695:0.05825,72455:0.04439)Pirellulales:0.00674):0.01072,2799:0.08183):0.01355,((((((((155204:0.06371,48540:0.06655):0.01352,88168:0.07637):0.01082,2851:0.05650):0.01253,2853:0.06941):0.00333,((2857:0.06710,2859:0.06144):0.00681,2868:0.06619):0.01172):0.00663,(2871:0.03795,52232:0.04204):0.01505):0.00494,2840:0.07518)Planctomycetales:0.01093,(101961:0.06569,194531:0.09388):0.02667):0.00586):0.00664,95350:0.06860):0.00503,(((((((((200639:0.04439,202208:0.05506):0.00505,199817:0.07842)Gemmata:0.00588,179045:0.12713):0.00345,(200086:0.06472,212988:0.08430):0.00770):0.01245,200732:0.07326):0.00673,(166220:0.05903,202096:0.04116):0.00168):0.00496,202787:0.07214):0.01928,228825:0.07006)Gemmatales:0.04207,((2879:0.05728,2884:0.06177):0.01603,91695:0.07227)Isosphaeraceae:0.06441):0.00255)Planctomycetacia:0.03651,106208:0.08467):0.01280)Planctomycetes:0.02156,111582:0.11255):0.01806):0.00582,((((((((((((175403:0.08319,209831:0.05695):0.00521,22777:0.05115):0.01265,45661:0.05506)Sediment-1:0.00754,144253:0.04197):0.00910,63782:0.06045):0.01665,(129513:0.04310,159595:0.08805):0.00254):0.00753,136806:0.07031):0.00839,((152083:0.07536,63800:0.05188):0.01020,172911:0.06723):0.00928):0.00747,(((((152729:0.05821,98988:0.04685)Hyd24-12:0.01944,114646:0.05897):0.03936,164293:0.09023):0.01115,(30315:0.05299,63792:0.06192)LCP-67:0.02275):0.00670,(159700:0.06349,161289:0.07230):0.02733):0.00167):0.01475,(106114:0.10013,172015:0.07661):0.00608):0.00249,((((103111:0.05605,90456:0.06386):0.04056,1674:0.07264):0.03381,138963:0.09312):0.01711,131965:0.08466):0.00507):0.00743,(164821:0.07022,63773:0.10261):0.01478)WS3:0.01649):0.00413,((((209828:0.05178,63786:0.04849):0.01090,153560:0.08766):0.03387,74751:0.10089):0.00687,((213171:0.07764,40926:0.05155):0.02521,37044:0.09602):0.01393)BRC1:0.03604):0.00501,(((((211984:0.08127,90217:0.08194):0.01462,66919:0.08604):0.01689,(111894:0.09428,205204:0.07338):0.04629):0.00507,159808:0.06386)WPS-2:0.03849,((((159945:0.09516,161407:0.07864):0.02047,160498:0.07823):0.00175,104566:0.08748):0.01840,111958:0.08264)NB01:0.03489):0.01188):0.00416,(159230:0.09277,201108:0.11549)GN1:0.01944):0.00663,((((((((((((((((((((((((104007:0.03790,182638:0.05705):0.00084,105637:0.05641):0.01487,3810:0.05272):0.00834,113503:0.04244):0.00329,((((105838:0.05122,28706:0.04593):0.00084,99294:0.05835)Sphaerochaeta:0.01985,159670:0.05354)Antarctic:0.03588,128374:0.05798):0.00758):0.00247,112885:0.07385):0.00250,(((103618:0.05323,159183:0.03707):0.00754,106093:0.04113):0.00248,(159135:0.01466,159386:0.10649):0.05025)S_thermophila:0.01320):0.00247,(((104782:0.04090,159327:0.03872):0.01945,131162:0.04329):0.00662,(159087:0.03531,3801:0.06175):0.00979)TM3:0.01561):0.00739,((103769:0.04271,156689:0.03626):0.00333,159291:0.05512):0.00330):0.00739,101654:0.06285):0.00750,((3804:0.04870,79121:0.06930):0.00508,3802:0.05037):0.00330):0.00658,(((103241:0.05041,37803:0.09144):0.01195,(106877:0.03171,162727:0.05016):0.02757):0.01330,79117:0.06780):0.00672)Spirochaetaceae:0.00248,((156152:0.10484,4146:0.12885):0.01418,4156:0.10008)Borreliaceae:0.01774):0.01170,101217:0.08384)Spirochaetales:0.00587,((((((((3838:0.07903,3853:0.04677):0.00340,(71422:0.09205,83155:0.05671):0.01139)saccharolytic_treponemes:0.00742,(101908:0.07829,105718:0.06860):0.03697):0.00916,((22641:0.03790,78163:0.06115):0.00668,3892:0.09496):0.00592):0.02313,(((147950:0.05726,47380:0.06874):0.01189,36733:0.04915)AA_fermenters:0.00578,46681:0.03874):0.01820):0.01409,(((27500:0.07002,4009:0.03707):0.01430,4136:0.06930):0.00837,4015:0.04435):0.00743)Treponema:0.00414,4056:0.06538):0.00416,228504:0.06858)Treponemaceae:0.03086):0.01589,105023:0.09532):0.01025,((((100106:0.04420,159323:0.06638):0.01257,101486:0.06048)GN05_1:0.08573,221327:0.11636):0.00088,159296:0.09107):0.01771):0.00167,(105230:0.09186,105940:0.06964):0.01036):0.00249,((101158:0.07630,139265:0.07989):0.05960,217455:0.09847):0.01560):0.00998,(145173:0.12871,4478:0.09047)Brevinema:0.06483):0.00843,((((((107124:0.02498,201970:0.02969)SHA-4:0.03784,(232521:0.04239,65483:0.03155)SHA-116:0.05329):0.04521,(111287:0.05909,143180:0.06040)GZKB113:0.03809):0.01438,100599:0.05287)PBS-18:0.01987,(200704:0.04603,34546:0.08475)ST-3K10:0.04932)SP_WWE1:0.07152,((159262:0.10387,3792:0.09853):0.03418,37498:0.10968):0.00865):0.00602):0.01678,((((166591:0.06056,4386:0.07677):0.00341,134564:0.11722)Leptonemaceae:0.02639,(148552:0.05250,225567:0.07622)Leptospiraceae:0.04248):0.00337,((13881:0.04358,1720:0.05768)SJA-88:0.03109,144463:0.08401):0.00762)Leptospirales:0.02338)Spirochaetes:0.01172,(((((((((((((155771:0.04777,214114:0.06320):0.00254,60814:0.03880):0.01152,2677:0.06480):0.01086,(212699:0.11674,60815:0.04858):0.00778)PHOS-HC15:0.00825,91225:0.08893)SJA-28:0.01951,218461:0.08598):0.00761,(193893:0.07299,38651:0.07916)Chlorobiales:0.09145):0.00940,(((39165:0.07861,88595:0.05012):0.00937,101263:0.04754):0.00906,145090:0.06694)ZB1:0.05008):0.01092,99903:0.05977):0.00498,(((132552:0.05518,40972:0.08430):0.02541,106633:0.06710):0.00838,222526:0.07352)BSV19:0.04540):0.01341,(((((211730:0.07029,25315:0.10381):0.01121,218132:0.10074):0.01350,107418:0.07206):0.01843,(204272:0.05516,41587:0.07374):0.00000):0.01504,((164804:0.11338,2651:0.04766):0.02068,90050:0.04040):0.00917)OPB56:0.02812):0.01085,((114305:0.11189,30516:0.09627):0.02072,(203705:0.04880,55316:0.04443):0.02424):0.00586)Chlorobi:0.03157,(((((104029:0.08003,166286:0.09991):0.01206,218559:0.08306):0.01179,102749:0.08424)Fibrobacteres:0.05323,(((159095:0.05054,159251:0.05589)GN09:0.00804,172792:0.07439):0.01247,155998:0.05830):0.01163):0.01444,(149860:0.11485,61848:0.06545):0.04604)Fibrobacteres:0.01184):0.01269):0.00922,(((((((115290:0.09351,132371:0.05806):0.01204,(14314:0.06608,202585:0.05410):0.01015)Halanaerobium:0.01323,14309:0.03710)Halanaerobiaceae:0.04075,159172:0.08452):0.00170,(((14292:0.05657,14301:0.04387):0.02362,14302:0.03821):0.01570,(14308:0.05676,45870:0.04476):0.01551)Halobacteroidaceae:0.02567)Haloanaerobiales:0.02167,(((((((110785:0.07439,99289:0.03707):0.00673,42308:0.06930)TSBW08:0.01087,7633:0.03793):0.00248,132726:0.04584)NKB19:0.04544,234397:0.07692):0.02461,156513:0.10968):0.01378,(159295:0.04750,70372:0.05367)BRC1:0.10113):0.01274):0.00502,(((171291:0.08660,231910:0.08313):0.01915,181861:0.06048):0.00501,22125:0.05250)VHS-B5-50:0.09001):0.00341):0.00414):0.00331,(((((((((((((((((((((((((((166013:0.05479,206971:0.04522):0.02666,215036:0.08462):0.01935,153036:0.03707):0.00251,(100017:0.03062,150722:0.05821):0.01087):0.00901,(((159339:0.10671,222456:0.12490):0.04852,205230:0.06997)B74:0.07445,153534:0.01289):0.00929)Solibacteraceae:0.00737,(153348:0.05155,208976:0.04538):0.00339):0.00327,50491:0.03471):0.00164,3677:0.04870):0.00496,((220200:0.08664,3667:0.01694):0.01016,137789:0.02825):0.00491):0.00978,40287:0.03800):0.00991,3678:0.07869):0.00673,3684:0.03984)Solibacteres:0.05764,152594:0.05574):0.02286,217783:0.07593):0.00337,(((((((((156603:0.04685,222398:0.03148)CLi114:0.00416,46444:0.04200):0.00082,105717:0.05580):0.01414,((3732:0.03150,39712:0.03231):0.00661,215574:0.09044)Ellin5121:0.02017):0.00743,161597:0.06063):0.01006,(112838:0.06629,152430:0.07599):0.01117):0.01486,(159642:0.07108,203449:0.06295):0.02891):0.00747,215582:0.10094)Acidobacteria-2:0.02970,136128:0.06876):0.00421):0.00494,((((((((((((((((209888:0.06581,225539:0.05419):0.01954,215579:0.02487):0.01245,(205400:0.06164,216666:0.04110):0.00448)Amb_16S_425:0.03062,152317:0.04391):0.01183,(152837:0.10273,31221:0.02749):0.00256):0.01912,211311:0.15000):0.00791,145786:0.04996):0.00913,((137302:0.02579,223356:0.04572):0.00000,214891:0.07192):0.01834)K-5b2:0.00493,153517:0.03551):0.00658,159436:0.03874):0.00823,218083:0.08201):0.00253,(14397:0.03554,58126:0.01738):0.02314):0.01472,3709:0.04130):0.00910,224515:0.03987):0.00165,((16327:0.02587,3704:0.03231):0.00331,39070:0.02908):0.00903)Acidobacteriae:0.03520,209786:0.08748):0.01192):0.00578,(((161093:0.03713,1740:0.04194)Acidobacteria-5:0.03591,1743:0.05650):0.00669,128109:0.04605):0.01820):0.01979,(((((((((((((((((219881:0.06462,86097:0.02423):0.00336,207011:0.05111):0.00746,55946:0.03473):0.01564,51292:0.02342):0.01151,41153:0.04630)Ellin6075:0.02064,169877:0.05089):0.03745,1791:0.03803):0.00666,(104564:0.03931,208380:0.04373):0.02549):0.00330,113626:0.02302):0.00986,(103701:0.01340,137027:0.02755):0.00906):0.00329,1783:0.02269):0.01476,(221168:0.00891,222882:0.06347):0.00668):0.01564,222283:0.04180):0.03057,((110407:0.07910,1797:0.05421):0.01019,90000:0.06974):0.01674):0.01421,3696:0.10734):0.00596,(204006:0.07302,206059:0.09614):0.03413):0.00838,210240:0.10232)Chloracidobacteria:0.00682):0.00249,((145811:0.04655,222747:0.03824)MVS-40:0.04768,160549:0.07270):0.00760):0.00332,((((((207733:0.06175,216016:0.08826):0.04220,1807:0.02427):0.01076,93838:0.08532):0.00504,((18226:0.04369,51476:0.02751):0.00582,(1857:0.05596,211548:0.12564):0.01485):0.00412)Acidobacteria-6:0.00575,(((((1854:0.04383,88072:0.05064):0.01506,90253:0.07048):0.00999,(1833:0.04801,23209:0.04126):0.02089):0.00000,1847:0.02597):0.01723,222887:0.04975):0.00745)iii1-15:0.03445,(219177:0.03881,95448:0.04619)CCU21:0.02178):0.01668):0.00413,(136924:0.02747,151522:0.02513)BPC015:0.05428):0.00335,(((111532:0.04116,139634:0.04835):0.01751,213210:0.05394)S035:0.03810,(133702:0.05785,218850:0.07779)KM22B-24:0.01482):0.00586):0.00331,((((103470:0.03194,1728:0.03473)32-20:0.03235,1719:0.05562):0.00834,((216196:0.07100,230549:0.06154):0.02520,1718:0.05242):0.00249)iii1-8:0.02656,97685:0.06911):0.00421):0.00329,(((224671:0.05697,97371:0.08140):0.01198,204817:0.05617):0.00250,207151:0.06501):0.00250):0.00909,(((155781:0.06324,1717:0.02585):0.01005,96016:0.05021):0.00249,(154004:0.03557,1710:0.03473):0.01081)RB25:0.02369):0.00329,((((((((((96804:0.03697,97300:0.04834):0.00840,213078:0.04452):0.00914,113092:0.03840):0.00332,204709:0.03492):0.01157,(60180:0.04456,98176:0.04420):0.00503):0.00494,161000:0.04207)Sva0725:0.00824,(146478:0.06002,1724:0.05105)OS-K:0.02883):0.00333,169792:0.07761):0.01690,138615:0.06467):0.00420,(((1732:0.05486,218878:0.06452)Holophagales:0.05922,150151:0.09040):0.01373,95646:0.06336):0.00502):0.00247)Acidobacteria:0.01641,(((((((((154652:0.02991,1701:0.03473):0.00332,160912:0.04362):0.00825,130540:0.02649)HMMVPog-54:0.03209,89676:0.03629):0.01411,134526:0.04273):0.00493,(1684:0.02524,39529:0.03339):0.02004)OP8_1:0.02978,143399:0.07599):0.00678,((165553:0.06388,61847:0.05097)OP8_2:0.07267,110989:0.09668):0.00174)OP8:0.01656,(((113241:0.04075,220959:0.05717)JG37-AG-121:0.06452,217069:0.08219)SPAM:0.02008,(136988:0.04919,45522:0.05766)NC10:0.03286):0.00168):0.00500):0.01240):0.00658):0.00328,(((((((((((((((((((((((((((((((((((((((((((((171306:0.04959,214508:0.05493):0.00838,113239:0.09863):0.00680,(110549:0.08320,155181:0.12611):0.02730):0.03248,9760:0.01210):0.00000,112434:0.04600):0.00975,164097:0.00889):0.00499,210455:0.06520):0.00332,218720:0.05574):0.00421,171922:0.08455):0.00244,10056:0.00968):0.01051,10167:0.00645):0.00324,(10311:0.00645,65383:0.08243):0.00421):0.00164,99056:0.02181):0.00571,(((((148251:0.06564,148658:0.03809):0.01102,146991:0.03662)Arsenophonus:0.01234,210181:0.06683)Candidatus_Phlomobacter_fragariae:0.03534,10212:0.03961):0.00500,46111:0.03228):0.02214):0.00245,((((((((((150571:0.08643,54656:0.05024):0.00856,9965:0.06115):0.01420,(25332:0.07469,53447:0.04719):0.01365):0.00663,49541:0.05020):0.00744,((34972:0.04600,9644:0.03067)Buchnera:0.03417,28528:0.04597):0.01413):0.01316,((32418:0.03548,63939:0.03563):0.00914,79290:0.04430):0.00330):0.01396,144406:0.03180):0.00412,27576:0.03025):0.01558,(25922:0.03191,35772:0.03097):0.02263):0.00247,(145117:0.05354,165882:0.06194)Plesiomonas:0.02322):0.00326)Enterobacteriales_Enterobacteriaceae:0.00826,(((((136385:0.03191,9474:0.04194):0.01160,(30522:0.04516,91016:0.05004):0.00586):0.00979,93878:0.06949)Pasteurellaceae:0.02775,((29865:0.05000,9349:0.04523)Anaerobiospirillum:0.02183,103596:0.08320)Succinivibrionaceae:0.02707):0.00835,27646:0.03710):0.00908):0.02000,(169390:0.06715,171512:0.19774)Aeromonadaceae:0.02880):0.00987,((((((((162614:0.09482,169976:0.00484):0.00843,229081:0.07608):0.00168,((232927:0.07191,233559:0.18644):0.02102,233630:0.02453):0.01386):0.00245,173169:0.15832):0.00350,231968:0.03117):0.00328,207354:0.03790):0.00494,102500:0.03145):0.02126,73788:0.02825)Vibrionaceae:0.01896):0.00827,84842:0.04312):0.00582,(((((((((112879:0.03756,84384:0.05130)Pseudoalteromonas_elyakovii:0.00338,(140716:0.00888,43095:0.07356):0.00168):0.00325,140837:0.04693)Pseudoalteromonas:0.00992,231832:0.05008)Pseudoalteromonadaceae:0.01741,73001:0.04231):0.00665,(((((113435:0.00889,8878:0.04214):0.01818,65357:0.03629):0.01073,45496:0.03566)Alteromonas:0.02967,(131988:0.03148,159977:0.05117):0.01000):0.00331,60316:0.04187):0.00661):0.00660,((110796:0.03309,32525:0.05522):0.03255,(9014:0.04649,9121:0.02342)Psychromonadaceae:0.03645):0.00582):0.00493,20333:0.05259)Alteromonadales:0.00912,(((((229009:0.07209,9053:0.01858):0.01511,141639:0.01290):0.01619,131370:0.06502):0.00167,((89252:0.04947,9008:0.01131):0.01332,106352:0.00969):0.00732)Shewanellaceae:0.01223,(112341:0.04278,20204:0.07888):0.00854):0.00164):0.00576):0.04908,(103272:0.02024,73878:0.03320)whitefly_endosymbionts:0.07539):0.01331,(((((((((143944:0.04611,144159:0.03761):0.02470,142776:0.04292):0.02887,140938:0.06078):0.00336,140764:0.05109):0.00167,30146:0.01623):0.00733,140755:0.04781):0.01660,((144625:0.06803,42325:0.00566)Halomonas:0.01504,46277:0.01780):0.00737):0.00327,((162545:0.11914,55625:0.02994):0.02070,47371:0.02500):0.00575):0.01064,31691:0.03722)Halomonadaceae:0.01229):0.02700,(((((115206:0.01613,8194:0.09061):0.03900,204762:0.06290):0.00336,(154145:0.03306,8190:0.04036)Marinobacterium:0.00748):0.00573,74387:0.05325):0.00748,(155199:0.03809,47332:0.04026):0.01752)Oceanospirillales:0.00082):0.00572,((((((((((((((((114808:0.02742,63760:0.00565):0.02459,110621:0.02920):0.00165,((233613:0.09047,46201:0.00323):0.00169,141206:0.03148):0.00000):0.00162,134438:0.00645):0.00323,140916:0.03551):0.00246,(((143237:0.06215,143525:0.05403):0.03126,65940:0.07830)Dichelobacter_nodosus:0.03030,132521:0.07764):0.00513):0.00567,106711:0.03871):0.00165,(((167146:0.00081,167658:0.12455):0.00513,38217:0.01371):0.01301,(154066:0.06023,8494:0.00726):0.00583):0.00325):0.00081,8562:0.00887):0.02430,234990:0.03624)Pseudomonadaceae:0.00744,223694:0.07543):0.01007,((((107566:0.02445,181469:0.03667):0.00419,(161219:0.02771,174563:0.08933):0.01277):0.00415,(135755:0.04255,98593:0.02759):0.01762)obligately_oligotrophic_bacteria_KI89C:0.03067,111146:0.07102)HTCC2089:0.02787):0.00332,(8406:0.02808,8407:0.03231):0.02403):0.00083,(((((((((101111:0.01130,91777:0.03231)ZA3913c:0.04924,103807:0.07206)environmental_sequence:0.02541,106179:0.05968)SAR86:0.01174,212404:0.07307):0.01770,110246:0.05335):0.02928,98298:0.05008):0.01504,(226754:0.06079,71553:0.05295):0.01609):0.00165,((24515:0.05565,8178:0.04762):0.00590,91272:0.05335):0.00580):0.00986,(137295:0.07984,8229:0.05000):0.01111):0.00082):0.00982,((((154336:0.06775,221659:0.05126):0.00846,87543:0.05242):0.00166,95512:0.04123):0.00330,(35099:0.05081,62941:0.03610):0.01591):0.00493):0.00163,(((163334:0.02989,78569:0.03067):0.00661,150680:0.10518)Marinobacter_hydrocarbonoclasticus:0.00510,8811:0.05977)Marinobacter:0.01906):0.00165):0.00327,(((((138778:0.03473,196857:0.03793)OM60:0.00498,164271:0.08628):0.00590,101880:0.03065):0.00165,((7594:0.03796,7604:0.02341)Achromatium:0.03071,176744:0.04805):0.00582):0.00410,(143093:0.05246,146995:0.04355):0.01430):0.00818):0.01491,225963:0.05583):0.00494,(((188467:0.05847,96014:0.04140):0.00419,146638:0.05546):0.01411,(125759:0.05505,146843:0.05583):0.01517):0.01324):0.00660,((((((((((((((132329:0.02913,232046:0.05445):0.01172,102805:0.02365)Aquicella:0.05890,218114:0.05344):0.01433,(207732:0.02805,47013:0.02098):0.01234):0.00903,137507:0.04123):0.00331,(((156997:0.05882,217655:0.04285):0.02277,8028:0.05122):0.02248,67844:0.05310):0.00251):0.00574,((47939:0.04279,55626:0.06412):0.02023,167895:0.08401):0.01099):0.00412,(145682:0.04850,22253:0.04221):0.01846):0.00574,((((((136741:0.04777,162394:0.04197):0.01090,155317:0.05488):0.00413,221169:0.07374):0.00670,((200671:0.05344,212023:0.07357):0.00851,8011:0.06813):0.00672):0.00416,111025:0.06621):0.00754,(89901:0.10373,91177:0.05673):0.00087):0.00413):0.00819,((((((((150639:0.07062,159255:0.04041):0.03425,(229650:0.05174,229691:0.05004):0.00841):0.00249,(105206:0.09790,132148:0.06134):0.01125):0.00248,149607:0.06129):0.00166,(232669:0.03473,74504:0.05161):0.00335):0.00164,8026:0.02661):0.00164,((((((151090:0.03728,45382:0.01774):0.00743,173611:0.05873):0.00083,134385:0.04279):0.00082,83477:0.01935):0.00814,154082:0.03390):0.00658,((215917:0.03312,32355:0.03199):0.00831,170442:0.05000):0.01076):0.00573):0.01141,147162:0.05356):0.00748):0.00328,(((((((146022:0.05327,64661:0.03234):0.00586,7973:0.04561):0.00413,(129226:0.01050,7931:0.02757):0.00985):0.00407,146337:0.04365):0.00579,(152386:0.04600,52016:0.02556):0.00250):0.00569,(105678:0.03635,158090:0.05746):0.00754)Legionella:0.03930,234971:0.08152):0.00256)Legionellales:0.00247,(((((233692:0.02591,7677:0.02591):0.00739,127094:0.02184)SUP05:0.03265,225001:0.12168):0.00692,46292:0.08959):0.00504,((137423:0.05906,144674:0.05806):0.00677,200745:0.08023)Thiotrichales:0.01678):0.00167):0.01319,(132615:0.04785,153746:0.07500):0.02129):0.00082,((192744:0.09740,7705:0.05977)Thiomicrospira_frisia:0.03266,7733:0.06220):0.00671):0.01406):0.00574,((((124743:0.03635,191569:0.03809):0.00249,7710:0.04925)Methylophaga:0.01405,(61960:0.02258,7719:0.01702)Methylophaga_thalassica:0.02542):0.00494,(100967:0.08137,99225:0.06266)Piscirickettsiaceae:0.00605):0.01155):0.01311,((((114239:0.06690,170060:0.03667):0.01848,89122:0.04653):0.00580,190826:0.05199):0.00831,156379:0.07699):0.01596):0.00409,((128429:0.02903,205808:0.04689)Methylococcaceae:0.00498,158471:0.09319):0.00253):0.00736,((((130603:0.03866,8100:0.02931):0.00796,40043:0.01935):0.00570,8051:0.02419)Methylococcaceae:0.02779,(105115:0.06457,113888:0.06295):0.01363):0.01403):0.00243,(((((((112835:0.04493,7561:0.01784):0.01501,(154272:0.05973,7546:0.02190):0.01252):0.00577,159131:0.05836)BD1-1:0.02254,(126228:0.05093,146270:0.04876):0.00589):0.00656,(((((45930:0.03653,7634:0.02274):0.00166,94878:0.11219):0.01538,185977:0.09829):0.00258,97007:0.03160):0.00574,(40999:0.08481,99301:0.06371):0.02238):0.01070):0.00329,((((215441:0.05242,24022:0.03710)B01R021:0.01339,113920:0.04446)Maorithyas_hadalis_gill_thioautotrophic_symbiont_II:0.05111,(155896:0.06667,96329:0.02920)B2M28:0.00674):0.00333,(108087:0.03629,7587:0.03482):0.01750):0.00165):0.00650,((((7586:0.02419,7624:0.02660):0.00082,67775:0.05053)Symbionts:0.01570,161348:0.02825):0.01645,(60168:0.05839,97596:0.05353):0.00340):0.00245):0.00572):0.00081,((((((((((((((((((((142449:0.10471,227601:0.07320):0.00793,76891:0.02846):0.00744,((7056:0.01456,7088:0.01453):0.01470,28592:0.08874):0.00931):0.00082,(((106627:0.08688,30714:0.07648)Comamonas:0.01813,145100:0.08604):0.00085,78916:0.03710):0.00412):0.00245,105640:0.02903):0.00327,(((((108299:0.02581,87861:0.02500):0.00494,168478:0.02876):0.00164,170117:0.02661):0.00982,6368:0.03306)Chitinibacter_tainanensis:0.06648,6901:0.03790):0.02096):0.00896,82250:0.05109)Comamonadaceae:0.02415,(((((((165591:0.02917,189383:0.06097):0.00337,183819:0.03729):0.00164,189163:0.00424):0.01777,31333:0.01855)HuCA4:0.01636,232516:0.02661):0.05248,104793:0.03740)Sutterellaceae:0.02669,((((20648:0.01377,44156:0.08442)Cupriavidus:0.00253,102985:0.08790):0.00758,50403:0.01613)Ralstoniaceae:0.00816,110609:0.10815):0.00942):0.00247):0.00577,((((170434:0.01214,60324:0.04278):0.00248,89748:0.03153):0.00818,6876:0.02179):0.00982,(18745:0.02419,84552:0.04334):0.00663)Alcaligenaceae:0.02203):0.00247,(((((104381:0.00323,24899:0.03539):0.01068,26099:0.02668):0.00737,(213181:0.07639,55005:0.11475)Burkholderia_cepacia:0.00880):0.00413,28964:0.05037):0.00909,(110392:0.03890,6819:0.02206):0.00993)Burkholderiaceae:0.00328):0.00572,234489:0.04516):0.00247,((((((132286:0.05028,70208:0.05661)Janthinobacterium:0.02032,(207827:0.07290,81187:0.06306):0.01111):0.01810,80181:0.03563):0.00248,105930:0.01935):0.00325,19906:0.05263):0.00414,(((25562:0.08036,6826:0.18718):0.01285,231994:0.10492):0.00598,163065:0.08458):0.01016):0.00166):0.01151,220728:0.14459)Burkholderiales:0.00087,(((((((((((204742:0.06690,212943:0.07094):0.04693,215004:0.08271)Rhodovibrio:0.03306,107729:0.05246)FW119:0.02013,133808:0.03426):0.03580,58397:0.01613):0.00974,(((110393:0.05724,153802:0.05574):0.02705,19374:0.04113)FukuN108:0.05143,129297:0.03871):0.01254):0.00493,36932:0.03808):0.01236,(168055:0.02284,6367:0.03632):0.00579)Neisseriales:0.01471,((124930:0.03790,88207:0.05106)Methylophilales:0.02679,(138944:0.04045,230895:0.06177)Spirillaceae:0.00757):0.00578):0.00739,(((((210246:0.06935,211447:0.10806):0.04348,217293:0.08597):0.00510,((217989:0.04297,69013:0.01213):0.02489,212243:0.03510):0.02068):0.00913,((131723:0.02547,148449:0.06478):0.01872,17553:0.05583):0.00668)MND1:0.00248,(((155255:0.01613,29792:0.03887)Nitrosomonadaceae:0.01404,40156:0.06716):0.02167,(205078:0.06094,216235:0.09099)SRRT52:0.06931)Nitrosomonadales:0.00084):0.00164):0.00409,((29770:0.01291,6702:0.00969):0.04325,(6763:0.03398,93530:0.06518):0.00926)Rhodocyclales:0.00328):0.00327):0.00245,(((((204192:0.07031,214447:0.08801):0.01381,218851:0.09197):0.01568,(113262:0.03510,225240:0.04040):0.00594):0.00170,(217947:0.13131,226615:0.03404):0.02065):0.02508,84214:0.06129):0.00586):0.01311,208819:0.06063):0.00251,150631:0.07370)Betaproteobacteria:0.02257,((((((((((((((175668:0.04446,190751:0.05583):0.01697,7820:0.00407):0.00245,111289:0.01945):0.01389,43798:0.06158):0.00501,18867:0.09504):0.00424,101503:0.04876):0.00166,93199:0.05724):0.00499,152244:0.02344):0.00082,(212772:0.08520,79476:0.02991):0.02367):0.00570,(110983:0.03411,211749:0.09300):0.01104):0.01153,204506:0.04457):0.00166,((101301:0.02922,67341:0.02018):0.01479,81832:0.10286)Frateuria:0.01362):0.00907,60589:0.05372)Xanthomonadales:0.01332,7761:0.06538):0.00584):0.00501,((((((((((60325:0.04435,8384:0.02423):0.00332,179300:0.06780):0.00084,153733:0.04116):0.00164,129982:0.01954):0.00652,128659:0.01292):0.02040,60274:0.04340):0.02547,74353:0.02669):0.02309,101684:0.05165):0.02251,158243:0.05340):0.00911,((((140806:0.05493,140956:0.04538):0.00932,(140807:0.01617,152296:0.09304):0.00256):0.00163,79540:0.02867)Psychrobacter:0.02293,(8250:0.03740,8262:0.01382):0.01660)Moraxella__Psychrobacter:0.04117)Moraxellaceae:0.01905):0.01411,(((((((((((112973:0.01612,27669:0.01855):0.01389,163284:0.02097):0.01796,47916:0.03069):0.00907,93780:0.03115):0.00738,(113312:0.03516,159956:0.02683):0.04315):0.00581,112738:0.04000)Ellin339:0.02473,141364:0.06341)CCD24:0.00504,(113125:0.04863,144987:0.07661):0.00339):0.00000,(((((60451:0.05246,67230:0.04103):0.01339,213779:0.07357):0.02002,7510:0.03952)Thioploca:0.01887,(219763:0.06250,81842:0.05335):0.01184)Thiotrichaceae:0.00579,177523:0.04597):0.01407):0.01070,(((((((53334:0.02184,7494:0.02016):0.02790,138306:0.04516)Acidithiobacillus:0.06390,114709:0.04677):0.00169,96909:0.05151):0.00248,66569:0.03084)Acidithiobacillales:0.01150,32647:0.06171):0.00333,((((142852:0.05730,7772:0.04439):0.00842,192482:0.06376):0.02500,104538:0.05165)Nevskiaceae:0.04693,(207038:0.04677,63326:0.03794)RCP2-96:0.02167):0.02004):0.00743):0.00901,202248:0.06255):0.00084):0.00493):0.00489,(((((7523:0.01856,7555:0.03391):0.01814,7564:0.03401):0.00824,7526:0.02502)Chromatiaceae:0.01633,43810:0.02421):0.01151,(197177:0.07271,93549:0.04036):0.03713):0.00498):0.00406,((((194409:0.03790,7461:0.01856):0.00907,161296:0.04880):0.01822,(70993:0.04047,93813:0.02048)Thialkalivibrio:0.01410):0.00414,203128:0.05209):0.00166):0.00164,((((213189:0.04439,58383:0.02825)PHOS-HE54:0.01658,212541:0.05248):0.02243,(198217:0.03713,64239:0.03740)Salinisphaera:0.04904):0.00334,51207:0.04522):0.00911):0.00163,(((((18746:0.03566,38006:0.03728):0.01925,18561:0.03790)Thiothrix:0.02475,(113554:0.04580,132694:0.04435):0.03753):0.00584,103041:0.06326)Thiotrichales:0.02594,((153768:0.02922,177236:0.13171):0.01225,158977:0.03806)KCM-B-13:0.01488):0.00744):0.00164,((114291:0.08710,157897:0.06351):0.01107,188458:0.05574):0.00587):0.00333,((158843:0.04826,168486:0.03731):0.00421,192261:0.05101):0.00496)Gammaproteobacteria:0.02581,((((((((((((((((((((((((((104638:0.02230,111879:0.02066)Roseovarius:0.00168,5655:0.01899):0.00416,(108110:0.03805,114029:0.13065):0.01394):0.00167,(((105024:0.07627,5685:0.04262):0.00350,136203:0.07267):0.00343,5645:0.03058):0.00672):0.00334,55371:0.03551):0.01090,((((140910:0.07019,44012:0.02064):0.01288,161602:0.11303):0.00691,48787:0.04046):0.00761,(5793:0.03610,5798:0.01734):0.00591):0.00334)Rhodobacter:0.00084,141539:0.05214):0.00423,114078:0.07432):0.01200,62504:0.05172):0.01183,(114474:0.08850,183580:0.06187):0.00086):0.00255,((((((165618:0.02961,167072:0.04860)Hyphomonas:0.02041,118311:0.05489):0.02039,((40525:0.03457,5847:0.02625):0.02269,5852:0.02953):0.01005)Hyphomonadaceae:0.01008,154764:0.05993):0.00424,(18609:0.05590,59347:0.04770)SM1D08:0.02577):0.01011,(226850:0.05168,38255:0.04266)H9:0.04273):0.01431):0.00753,98896:0.06148):0.00593,150590:0.05332):0.00338,204329:0.11922)Rhodobacterales:0.02958,((((((149199:0.08555,153210:0.04923):0.00341,152713:0.09135):0.03118,33426:0.06860):0.00846,(111320:0.05445,166172:0.07277):0.01019)Brucella_spHJ114:0.01078,25832:0.06681):0.02012,(164914:0.04435,226402:0.05901):0.00253):0.00165):0.00748,(((((((168296:0.05511,189244:0.06290):0.00595,105079:0.02258):0.00572,220160:0.06341):0.01170,219107:0.05097):0.00412,128706:0.02097)Phyllobacteriaceae:0.00572,(((107637:0.07979,107789:0.05906):0.01459,5266:0.03595):0.01074,49093:0.04695)Rhizobiaceae:0.01734):0.00572,((5123:0.02998,77907:0.03548)Aurantimonadaceae:0.01738,180618:0.09583):0.00595):0.00492):0.00082,((((((((((105599:0.07022,141661:0.05796)RF32:0.06908,165332:0.12140):0.00867,(165410:0.10877,224817:0.10454):0.00789):0.00425,((100553:0.08059,135520:0.11913):0.00847,5067:0.10256):0.01374):0.01255,((((114901:0.05327,233001:0.07730):0.03667,198250:0.05085):0.01751,208719:0.06941):0.00335,207274:0.09054):0.01263):0.00672,(((((((156145:0.06501,5504:0.07268):0.01286,170743:0.05654):0.01911,5468:0.05650):0.00251,155559:0.05811):0.01579,((146004:0.05811,157988:0.06947):0.01444,99510:0.05515):0.00831):0.01162,(44677:0.06266,50794:0.05081):0.01523):0.01079,19385:0.07425)BD7-3:0.03085):0.00586,39831:0.07673):0.00925,(33175:0.06917,89667:0.09238):0.01721)Oleomonas:0.00917,((((136508:0.05403,215190:0.11290):0.00954,(27926:0.05144,96386:0.05085)KCM-B-56:0.02176):0.00994,(112956:0.06877,216118:0.08910):0.02141):0.01423,(((57724:0.01372,93573:0.05354):0.01329,25284:0.03393)Devosia:0.02219,(143838:0.07184,25420:0.06345):0.02056):0.00661):0.00246):0.01153,((101767:0.07374,20480:0.14450):0.01150,196416:0.05565):0.01665):0.00248):0.00163,((((((((((104129:0.03804,137054:0.01290)Bradyrhizobium_japonicum_USDA_110:0.00082,45478:0.07903)Bradyrhizobium:0.00167,207121:0.06501):0.00417,65460:0.03720):0.00329,56503:0.05435):0.01910,((105004:0.04032,4715:0.02996):0.01081,29077:0.05089):0.01743):0.00901,(((66234:0.03331,78887:0.05207):0.00168,84672:0.02984):0.00901,60394:0.04569):0.02653):0.00330,(((100137:0.01625,4760:0.03404):0.00576,(154041:0.04116,99616:0.03314):0.01833):0.00163,156677:0.04520)Beijerinckiaceae:0.00330):0.00819,((((((212457:0.10043,212484:0.06089):0.00642,206896:0.06838):0.02476,(155239:0.09194,159481:0.02920):0.00509):0.00165,114396:0.03830):0.00083,((213063:0.08469,222209:0.09137):0.01158,213955:0.10120):0.04478)Rhodoplanaceae:0.00670,4688:0.04207):0.01073):0.01147,(((4617:0.01775,4622:0.03234):0.01979,145581:0.07829)Hyphomicrobiaceae2:0.02444,((211314:0.10670,32831:0.05276):0.00518,26724:0.04527):0.00908):0.01070)Bradyrhizobiales:0.00493):0.00652,((((((((((((((136962:0.06457,94511:0.06199)S15B-MN2:0.02215,(217504:0.07314,88702:0.02258)bacterium_Ellin333:0.04469):0.01496,25641:0.03548):0.00827,211913:0.06239):0.00250,((208470:0.06079,214088:0.07870):0.01091,37026:0.07711):0.00921):0.00331,((((212680:0.05023,92371:0.05242):0.00672,182626:0.04358):0.00661,(103846:0.06459,206331:0.05505):0.01440)Candidatus_Alysiosphaera_europeae:0.01801,(93834:0.06925,96012:0.04715):0.00170)Ellin314:0.01319):0.00412,(((206276:0.08426,4588:0.05594):0.01028,18546:0.05004):0.00996,102326:0.03719):0.00083):0.00816,(151468:0.05242,219934:0.08313):0.01284):0.00826,((32841:0.05170,70396:0.03238):0.01840,160662:0.03955)Defluvicoccus:0.01488):0.00492,((129414:0.05331,180810:0.02059)Rhodovibrio_salinarum:0.01082,136616:0.02823):0.02704):0.01155,(((140565:0.04516,153747:0.05811):0.00084,(220363:0.06230,37234:0.07016)ZA3420c:0.01109):0.00082,146291:0.05811):0.00667):0.01063,(((151211:0.06796,35350:0.08979):0.00604,149113:0.05803):0.01413,60184:0.05670):0.00168):0.00984,((100730:0.02823,162509:0.07805):0.04219,103259:0.07345):0.01267):0.00248,((((((((128453:0.05004,210598:0.10318):0.00000,234617:0.08313):0.01271,(6155:0.02502,6161:0.01452)Sphingobium:0.01637):0.00902,99158:0.04608):0.00498,((((154294:0.07422,29534:0.02346):0.00929,159222:0.06288):0.00168,39689:0.01694):0.01387,6294:0.02419)Porphyrobacter:0.01885):0.01958,85152:0.07237):0.00840,6365:0.03788):0.02056,149766:0.04600)Sphingomonadales:0.03151):0.00747):0.00492,(((((((5583:0.01642,5595:0.02217):0.00500,89317:0.03495):0.00751,25580:0.03530)Mycoplana_bullata:0.04448,137102:0.02500)Brevundimonas:0.00082,5508:0.05289):0.01909,30983:0.05977)Caulobacterales:0.02820,((17111:0.05408,206486:0.05948):0.00421,46020:0.05735)H4:0.01988):0.02262):0.00329,(((((((((((114828:0.03669,4596:0.04677):0.00751,146977:0.07097):0.01423,154135:0.07392):0.01593,4594:0.05973):0.00584,((138986:0.06613,149351:0.13982):0.00880,4597:0.07395):0.00251):0.01331,154672:0.07738):0.00421,43239:0.04600)Verorhodospirilla:0.00663,4509:0.06710):0.01169,4513:0.06827):0.01002,159606:0.06134):0.01509,(((222768:0.05366,58110:0.03548):0.02079,102030:0.06172)Ellin329:0.01003,158117:0.09115):0.00510):0.00660):0.01150,((207385:0.06766,63607:0.05024):0.00762,4525:0.06699)Azospirillales:0.00416):0.00165,(((227238:0.02906,58862:0.08003):0.00339,172363:0.03381):0.00328,104372:0.05000)Azospirillaceae:0.00331):0.00329,(((((((((((50807:0.05488,85388:0.04358):0.03458,(5903:0.04516,61204:0.04597)Candidatus_Pelagibacter:0.01422)SAR11:0.03835,230638:0.09451):0.03512,((138694:0.06099,230366:0.05740):0.01348,66229:0.08562)Surf113:0.01012)Pelagibacter:0.00334,68974:0.07016):0.01004,((110224:0.05997,228462:0.06144):0.01105,160817:0.09289):0.01610):0.01328,((((161840:0.07398,224835:0.07428):0.02146,91363:0.04847):0.00749,231758:0.08394):0.01096,(119998:0.07500,120203:0.07189):0.02489):0.00919):0.00747,(((((137271:0.05987,6128:0.06721):0.00853,(153370:0.08468,6010:0.10990):0.00973):0.01324,(141395:0.12425,6052:0.07500):0.04559):0.00421,(136326:0.04681,150441:0.07291):0.06022):0.01511,(((139511:0.04604,148047:0.07027):0.04333,137270:0.06694):0.00253,165687:0.05887):0.00497)Rickettsiales:0.00832)Consistiales:0.01736,((((((((((114856:0.09334,143820:0.06877):0.00694,136690:0.06753):0.00335,((223334:0.05028,89714:0.06397):0.01020,4551:0.05825):0.00166):0.01910,60700:0.08222):0.01440,(((146428:0.15235,99046:0.11228):0.02528,148878:0.08313):0.01959,90210:0.06532):0.00420):0.00830,(((((106778:0.06704,74136:0.07590):0.02577,144338:0.06050):0.02765,206066:0.11292):0.00344,204199:0.07926):0.01523,(22925:0.07692,5921:0.08592):0.05448):0.00252):0.00497,21579:0.05098):0.01166,(234348:0.06315,90268:0.06478):0.02385):0.01003,143692:0.05255)Caedibacteraceae:0.00824,91720:0.07022):0.01090):0.00908,((103709:0.03713,68992:0.04116):0.01169,(68994:0.02179,68995:0.03796)OM25:0.01489)OM38:0.00824):0.01821,135497:0.07513):0.00674):0.00902,((((((104162:0.06678,128717:0.03226):0.00420,8030:0.09165):0.01608,4446:0.06877):0.00505,32348:0.04854):0.01244,(4503:0.02258,51452:0.02339):0.02387)Acetobacteraceae:0.00818,(103476:0.05863,163322:0.04194):0.00923)Acetobacterales:0.05172):0.02417,(((((((11271:0.04839,211078:0.04358):0.01673,11270:0.05403):0.00249,11269:0.03065):0.00823,(11266:0.04355,11267:0.02419):0.01077):0.00573,(11281:0.03790,211407:0.03871):0.00918):0.00902,193562:0.05963):0.00250,212557:0.04113)Magnetococci:0.03295)Alphaproteobacteria:0.02760):0.00758,(163978:0.03629,174207:0.03320)Zetaproteobacteria:0.08202):0.01820,((((((((((((((((((49628:0.03626,60384:0.02995):0.00248,102768:0.04527):0.00988,61870:0.05868):0.00744,91972:0.03761):0.01072,141057:0.03707)Arcobacteraceae:0.03219,93761:0.04050):0.01495,((11211:0.04353,63933:0.03062)Campylobacteraceae:0.05156,(137181:0.03868,19883:0.02740)Sulfurospirillaceae:0.01989):0.00665):0.00165,88092:0.07248):0.01837,((((((((103892:0.03626,11103:0.03454):0.02063,11113:0.04271):0.00082,(170463:0.07742,171332:0.03722):0.03046)Sulfurimonas_autotrophica:0.01155,(156157:0.09285,97648:0.04126):0.00171):0.00247,95078:0.04837):0.00747,(150538:0.07905,221286:0.05367)Sulfuricurvum:0.04097)Sulfuricurvaceae:0.01328,104349:0.07333):0.00251,99878:0.05246)Sulfuricurvales:0.01908):0.00660,(161262:0.08710,225757:0.08871):0.02356):0.00996,(((((((((104091:0.03629,99099:0.05320):0.00923,84230:0.01977):0.00815,(143387:0.04572,154737:0.06621):0.01525):0.00897,(106942:0.02417,94413:0.04754)Rimicaris_exoculata_ectosymbiont:0.01913):0.00817,67794:0.02901):0.01231,142725:0.05565)Sulfurovumaceae:0.01830,171304:0.04274):0.01149,(101619:0.03952,187154:0.03774):0.01166):0.01978,104443:0.04194)Sulfurovumales:0.01888):0.00247,103202:0.05318):0.00332,(((((10973:0.01853,10994:0.02659)Helicobacter_pylori:0.03615,11057:0.02176)Helicobacter:0.03536,160329:0.07012)Helicobacterales:0.01681,82339:0.06738):0.00501,(106770:0.08123,11124:0.06941):0.00861):0.00829):0.00661,((114816:0.07500,34981:0.07108):0.01372,220478:0.08396)MVP-119:0.02773):0.00995,(105826:0.05296,106788:0.04218):0.00428):0.01078,(((35810:0.04919,76523:0.01937):0.01995,51291:0.02903)Nautillaceae:0.04930,106632:0.07754)Nautilliales:0.00595)Epsilonproteobacteria:0.03059,(10573:0.07680,152225:0.09516)Desulfurellales:0.01736):0.02272,213436:0.07910):0.01102):0.00495,(((((((((((((((((105731:0.07812,140314:0.11321):0.00089,1673:0.08081):0.01007,80396:0.07655):0.02942,(112831:0.05315,78912:0.04931):0.03704):0.00168,(151807:0.05968,28255:0.09472):0.01203):0.00330,(((((233484:0.10526,63116:0.07773):0.05238,31924:0.09442):0.00428,(133665:0.04674,159337:0.04969):0.01734):0.00333,111849:0.05892):0.00748,95547:0.05524):0.01497):0.00907,(((106810:0.04254,154329:0.04204):0.02673,10678:0.04029):0.00580,67796:0.04754):0.00744):0.01064,((((((10908:0.04533,171541:0.05811):0.01266,10688:0.03471):0.00245,((136481:0.05455,159353:0.09930):0.00442,10904:0.04886):0.00338):0.00572,113166:0.02977):0.01228,10771:0.06780):0.00420,144719:0.06580)OPB16:0.02674):0.00580,(((((10858:0.02901,99057:0.02994):0.00330,158424:0.03629)Syntrophaceae:0.00410,66927:0.04131):0.00823,(128221:0.04726,34985:0.04056):0.00686)Syntrophaceae:0.03838,(166688:0.04351,91627:0.06790):0.01180):0.00249):0.00164,(((((((((10680:0.04390,111722:0.04634):0.01265,10703:0.09047):0.00760,154831:0.03728)JTB38:0.00993,136961:0.03548):0.00495,(109290:0.04116,200746:0.02989):0.00249)NB1-i:0.00490,((((131057:0.02751,92222:0.03317):0.00249,146422:0.03061):0.02131,136770:0.03555):0.00083,223862:0.05220):0.00413)NB1-j:0.02126,((136925:0.03880,170623:0.03393):0.01167,24543:0.05441)MND4:0.02082):0.02322,(((10731:0.07593,217352:0.07208)NKB15:0.03346,155472:0.07097):0.00505,173034:0.07935):0.00842):0.00331,((((((10750:0.02690,10754:0.02419):0.00903,160699:0.04225):0.01151,219894:0.03560):0.00660,28552:0.03636):0.01070,(10763:0.03387,41940:0.03884):0.04719)Syntrophobacteraceae:0.02232,(163049:0.05399,78365:0.02579)Hyd89-23:0.01415):0.00909):0.00744):0.00492,(((((10775:0.04837,91433:0.04621):0.01341,101699:0.05645):0.00415,158879:0.06488)Desulfovibrio_baarsii:0.02842,((145609:0.04569,146095:0.07745)Rs-K70:0.04253,159091:0.04891)FW113:0.02311):0.00419,188174:0.06058):0.00166):0.00082,((((((((((112183:0.04200,29670:0.04696):0.01173,10892:0.04957):0.00165,((19549:0.02262,73235:0.05495):0.00918,109280:0.07674):0.01424):0.00164,((10876:0.02989,140214:0.07201):0.01856,156341:0.08543):0.00672):0.00082,((10897:0.03808,24077:0.02262):0.00911,10869:0.02725):0.00164):0.00574,103090:0.03961):0.00823,((((((182384:0.06289,67542:0.03384):0.00671,78421:0.02419):0.00082,(159418:0.03783,203184:0.09432):0.00677):0.00908,10943:0.05077)Desulfobacter:0.02570,31848:0.04512):0.00333,((147602:0.08016,161882:0.05493):0.01788,10867:0.09080):0.00762):0.00000):0.00411,159486:0.05503)Desulfobacteraceae:0.00659,(139171:0.06222,67797:0.05161):0.01270):0.00746,((((((((((((10829:0.01794,41043:0.03234):0.01238,113471:0.09939):0.01101,10837:0.04136):0.01072,((10836:0.04527,151052:0.10352)BD1-2:0.00943,(19356:0.03980,44052:0.04193):0.01922)delta_proteobacterium_MLMS-1:0.01159):0.00412,36010:0.09036):0.02609,((10794:0.04052,164536:0.05735):0.00924,145085:0.07496):0.00588):0.00581,10810:0.02825):0.01480,10839:0.03482)Desulfobulbus_rhabdoformis:0.01471,152416:0.03968)Desulfobacterium_catecholicum:0.04645,(157379:0.05546,67799:0.04839)AF420338:0.01095):0.00665,220492:0.05662):0.00584,36808:0.06366):0.00250):0.00329):0.01883,(((((159117:0.09956,227187:0.10632):0.01964,227450:0.09120):0.00254,47722:0.06845):0.00753,156878:0.08226):0.00168,((136793:0.08969,190843:0.07667):0.01991,224506:0.08994):0.01947):0.00999):0.00496,(((((((((((((((((((98504:0.03586,99496:0.03595):0.01695,77008:0.04570):0.00673,168400:0.03306):0.00576,141905:0.04274):0.00578,(113432:0.06985,178451:0.05726):0.00929):0.00083,225442:0.05964)ZA3704c:0.01413,67784:0.04677):0.00000,(10641:0.03160,137363:0.03485):0.01080):0.00820,((221872:0.06132,48487:0.02419):0.02094,114327:0.02891):0.01074):0.00082,222765:0.05960):0.00416,137235:0.03485)Chondromyces_apiculatus:0.01891,((206301:0.06427,206683:0.02734):0.00840,102964:0.04512):0.01317):0.00741,147978:0.03143):0.02299,(106308:0.05476,206719:0.05113):0.02191)Polyangium:0.02406,(159484:0.06644,180089:0.06129):0.01019):0.00494,((111358:0.08400,112458:0.06581):0.02446,101764:0.09170):0.01488)Polyangiaceae:0.00083,((((((((169178:0.08553,66994:0.04681):0.01366,10651:0.05403)xNannocystis:0.06043,(113984:0.03738,209489:0.03231):0.00747):0.00503,((112562:0.02816,204177:0.03548):0.02024,224294:0.05255):0.01082):0.00330,((102303:0.03548,30991:0.03725)JG37-AG-33:0.01912,(10654:0.03473,153433:0.04045):0.01419):0.01566):0.02224,167534:0.07016):0.00084,37853:0.08119):0.01430,((((((136786:0.04608,215863:0.07009):0.00422,155720:0.04113)CTD005-73B-02:0.02877,(136671:0.06774,79548:0.05157):0.00422):0.00414,(160388:0.05925,230324:0.08864):0.01972):0.01248,20395:0.08468)OM27:0.05270,(140840:0.10278,148429:0.06048):0.00856):0.00675):0.01252):0.00083,((((((221410:0.03062,82334:0.08320):0.01523,10585:0.03384)Myxococcus:0.02282,(143436:0.05456,27824:0.03548):0.00669):0.00248,((159288:0.06586,205984:0.07430):0.02263,99302:0.08688):0.00259):0.00994,(((137768:0.04351,219387:0.08313):0.00599,(138971:0.04540,201465:0.03871):0.00167):0.00245,(230200:0.04835,232196:0.04271):0.02673):0.01651):0.00167,(105782:0.06553,136119:0.06022):0.01867):0.01158)Myxococcales:0.01483,((((((((104678:0.06578,60592:0.07183):0.01693,214430:0.14482):0.02773,156732:0.07897):0.00937,(167370:0.03226,222341:0.09544)H16:0.00596):0.00248,((((113101:0.01187,223031:0.10968):0.01792,(88772:0.02910,97823:0.02581):0.03060):0.00412,77057:0.02674)JG37-AG-2:0.04423,168547:0.03793):0.01997):0.01156,147174:0.04516):0.00824,(182959:0.05811,225329:0.04094):0.02343)EB1021:0.01162,212524:0.09650):0.01694):0.01160):0.00329,(((((((((((109277:0.06374,95885:0.07917):0.04490,106487:0.07801):0.02477,(232590:0.05417,33331:0.05327):0.01093):0.00335,(168155:0.06205,74554:0.06699):0.01366):0.01233,228770:0.05721)Bacteriovorax:0.05154,139309:0.09451):0.01805,(((((210838:0.10274,51583:0.06418):0.00358,215478:0.06752):0.00431,(193092:0.07118,203873:0.08006):0.00857):0.00415,(153674:0.04839,52274:0.09216):0.02310)CD15:0.02734,(161221:0.07990,183808:0.06814)UA27:0.03333):0.03287):0.01095,(161941:0.05973,168013:0.05982):0.00594):0.00494,((((((((154325:0.04676,207228:0.11358):0.00263,227331:0.04623):0.00247,147642:0.07943):0.00421,137427:0.06245):0.00418,150309:0.07621):0.01509,(((206735:0.06518,217241:0.07997):0.00513,152494:0.06205):0.00583,(185875:0.08588,220017:0.06073):0.00942):0.00330):0.00659,43604:0.06446)Bdellovibrio_bacteriovorus:0.05525,104242:0.05996):0.01012):0.00247,((((((102448:0.08049,184617:0.05790):0.01269,189151:0.07492):0.00838,213547:0.11303):0.02393,((146556:0.06611,178729:0.07021):0.01833,158641:0.07494):0.02680)CTD005-82B-02:0.01945,((108348:0.09385,207691:0.07407):0.02399,149878:0.08232):0.00339):0.00749,((100291:0.06452,23625:0.05367):0.02201,139451:0.07258):0.03363):0.00251):0.00740,((((((102498:0.05730,141274:0.03719):0.01594,119981:0.04355):0.00658,224375:0.04358)CrystalBog021E5:0.02728,225738:0.08152)MIZ46:0.01859,(109057:0.10699,234001:0.09267):0.00786):0.01174,((((106171:0.04278,160719:0.08738):0.01967,234175:0.05093):0.01744,(10661:0.06721,10691:0.06475):0.01110)Sva0853:0.03393,106876:0.11313):0.00953):0.01173):0.00083):0.00082,(((((10709:0.02339,28110:0.05887):0.00167,225365:0.08434):0.00670,141399:0.02610):0.00490,(151001:0.08225,222205:0.05852):0.00604)Pelobacter_propionicus:0.00823,((10699:0.02258,41059:0.01694):0.01313,10686:0.03948):0.00494)Geobacter:0.03020):0.00330,(((131834:0.06217,135927:0.06192)GMD14H09:0.02028,71753:0.07267):0.00589,150753:0.07016):0.01837)Deltaproteobacteria:0.00995):0.00580,((((((((((((166469:0.02924,170770:0.00656):0.00576,169004:0.16597):0.02020,10489:0.02589)aab58c11:0.04946,143122:0.05174):0.01175,(((135884:0.04696,51535:0.02901):0.00166,192827:0.05250):0.00413,10518:0.02015):0.01717)Desulfovibrio:0.01721,68874:0.05478):0.00752,(10461:0.03626,18998:0.06937)Desulfovibrio_magneticus:0.02449):0.00821,(10452:0.03954,10478:0.04116):0.01003):0.00743,(10436:0.03896,30839:0.04120):0.01415):0.01313,((((((137367:0.03387,145197:0.06045):0.00251,10486:0.03476):0.00987,10485:0.03800):0.00827,145830:0.02554):0.01063,(148915:0.03717,163639:0.05134)LE30:0.01756):0.00823,((10508:0.05446,110905:0.05504):0.01462,28397:0.03868):0.02719):0.01159)Desulfovibrionaceae:0.01153,30927:0.05165):0.00332,(((167257:0.05095,74638:0.04996):0.02275,10404:0.05109):0.00166,10401:0.04754)Desulfohalobiaceae:0.01566)Desulfovibrionales:0.03872)Proteobacteria:0.00082):0.00580,((((((((136570:0.04434,146329:0.03231):0.01749,138901:0.07248):0.00504,1421:0.04766)Deinococcaceae:0.03808,139164:0.05527):0.03366,((136712:0.07561,150553:0.10684):0.00779,147384:0.08431)Trueperaceae:0.01171)Deinococcales:0.01683,(((114835:0.04927,26209:0.05416)Meiothermus:0.02699,1406:0.04116)Meiothermaceae:0.01325,((128421:0.06225,20463:0.04281):0.02365,145116:0.05852):0.00336)Thermales:0.03734)Thermi:0.02598,(((((159265:0.08377,227180:0.07381)LF045:0.04517,62668:0.09636):0.02878,((36070:0.05488,71654:0.07030)SRI-280:0.02115,2660:0.11507):0.00776):0.00170,88612:0.11486)OP5:0.05980,52397:0.11891):0.01760):0.00931,(((((((11297:0.06134,71850:0.03554):0.00840,29566:0.06379)Leptotrichales:0.06583,160204:0.03069):0.01422,((11319:0.04045,173467:0.03574)Fusobacteriaceae:0.02337,52873:0.02991):0.01239):0.03205,(103458:0.11120,99409:0.07345):0.01393)Fusobacteria:0.05924,95046:0.11237):0.00699,1875:0.12023):0.02653):0.00508):0.00583,(((((((((((((((((((((((16320:0.03849,33162:0.06393):0.00256,101247:0.04095):0.00835,39492:0.09076)Euglena_stellata:0.00772,((138007:0.10204,99983:0.05220):0.00876,112364:0.08490):0.00679):0.00584,40174:0.05319):0.00670,((128833:0.06683,128911:0.08797):0.00948,26708:0.09188):0.00850):0.01665,((((20563:0.06275,51136:0.05053):0.05144,133071:0.05246):0.00339,((34641:0.06645,38948:0.08203):0.01483,38279:0.08592):0.00938):0.02179,128298:0.04423):0.00842)Euglena:0.02994,(161126:0.06942,30353:0.10452):0.02812):0.00587,(144777:0.10317,161507:0.10336):0.06344):0.01948,(((175064:0.07395,26799:0.08502):0.00857,170175:0.09342):0.00850,(140606:0.15882,161220:0.14916):0.01785):0.00507):0.00838,((((228263:0.07341,30128:0.07293):0.01457,3447:0.06025):0.00501,((232752:0.05460,3439:0.04685):0.00841,3448:0.06159):0.00332):0.00082,83959:0.08892):0.00674):0.01414,45082:0.07610):0.01004,51285:0.06791):0.00501,3473:0.06467):0.00834,(((((30150:0.08891,47593:0.04523):0.01196,3468:0.06053):0.00083,229102:0.06462):0.00334,51823:0.04523):0.00663,((29775:0.04049,3476:0.06955):0.00763,47228:0.06397):0.01167):0.00329):0.01310,((((((((45967:0.08499,72055:0.12017):0.02117,29047:0.05940):0.01760,(35074:0.05423,69031:0.01616):0.00500):0.02386,3538:0.09120):0.00855,3536:0.04439)vectors:0.01238,(((130918:0.06935,3497:0.07012):0.00939,153722:0.07431):0.02434,((155973:0.11445,79762:0.07474):0.02181,68830:0.12419):0.02161):0.00506):0.00496,((((32850:0.07027,49481:0.01856):0.00925,3490:0.06513)Chlorella:0.03756,225891:0.06063):0.01936,141161:0.05462):0.00250):0.00410,150579:0.11928):0.01285)Chloroplasts:0.02407,((((((188968:0.06494,3265:0.05353):0.00422,105621:0.07345):0.01089,((3299:0.03563,3308:0.05493)Synechococcus_elongatus_PCC7942:0.01425,92037:0.04597):0.03805):0.01084,(45521:0.02749,53707:0.02423):0.04203):0.00247,(((136126:0.06947,207917:0.12541):0.00962,146921:0.12063)Pseudanabaena:0.01203,(3066:0.05735,3231:0.08062)Oscillatoriales:0.01282):0.00084):0.00661,((((137040:0.03961,3112:0.07798):0.02367,3247:0.05085):0.00917,136142:0.05416):0.01658,137124:0.04597):0.00249):0.00982):0.00664,107856:0.04847):0.01407,152906:0.06369):0.00669,97840:0.05408):0.02324,(((111225:0.06707,202289:0.09512)PRD01a012B:0.00344,173910:0.09068):0.00253,(38040:0.12278,46527:0.07667):0.01848):0.00084):0.00584,74727:0.09355):0.02960,(((167052:0.10051,195479:0.07884):0.00352,112746:0.09479):0.01618,102126:0.09685):0.00848)Cyanobacteria:0.02606):0.01399,((((((((((((((((((((23149:0.01780,24150:0.01372):0.00572,103151:0.04681):0.01155,33488:0.04495):0.00497,(((133166:0.10671,137482:0.02510):0.02570,12621:0.04597):0.00995,87249:0.04835)Bifidobacteriaceae:0.07756):0.00337,((((12563:0.05744,33446:0.08145):0.01200,95061:0.05004):0.00165,(60136:0.02256,90040:0.04630):0.00665):0.00246,80785:0.05488)Actinomycineae:0.00995):0.01070,(((((((((136522:0.07811,207791:0.06003):0.04359,176825:0.02107):0.00249,12398:0.04217)Arthrobacter:0.00165,209188:0.06838):0.00834,144771:0.09179):0.01016,141543:0.12675):0.00345,85092:0.02500):0.00655,12522:0.05963):0.00333,78074:0.03952):0.01811):0.00984,(((((((105395:0.03503,23427:0.02339):0.01652,97256:0.02986):0.00575,161159:0.03716)ACK-M1:0.02463,104024:0.06088):0.01087,(144326:0.05091,17507:0.07294):0.02685):0.00416,146972:0.05020):0.00584,(12523:0.01700,12533:0.02350)Brachybacterium:0.02227):0.00908):0.00000,(((((((103201:0.09683,99439:0.05740):0.00524,(137447:0.00567,33272:0.04282):0.00165):0.00326,205717:0.03963):0.00331,169430:0.02803):0.02386,25807:0.01943):0.00823,35440:0.01696):0.00326,(100011:0.10204,108713:0.01707)Leifsonia:0.01191):0.01225):0.00246,(202129:0.07455,43992:0.02915)Intrasporangiaceae:0.00759):0.02044,128032:0.03390):0.00662,(((((((29154:0.03062,49248:0.02659):0.02225,12734:0.05925):0.00500,208120:0.06508):0.01255,108722:0.03270):0.00164,((108724:0.07125,167566:0.02600):0.00422,(12743:0.02901,201697:0.05987):0.00670)Propionibacteriaceae:0.00245)Propionibacterium:0.02675,99987:0.07335)Propionibacterineae:0.01015,((((12127:0.02427,12128:0.03961):0.00747,11985:0.04850)Nonomuraea:0.01157,11972:0.04915):0.00498,(12061:0.04295,72811:0.03228):0.01168)Streptosporangineae:0.01802):0.00412):0.00081,((((((((137492:0.02672,149698:0.07990):0.01097,83558:0.02500):0.00163,137455:0.01862):0.00981,13348:0.01613)Corynebacteriaceae:0.01303,(((214060:0.04522,215442:0.06587):0.01163,209116:0.08348):0.01611,49893:0.01774)Mycobacteriaceae:0.01391)Gordoniaceae:0.01395,132390:0.11373):0.01542,((((12943:0.01731,41449:0.06935):0.00502,12941:0.06009):0.00746,(13016:0.01692,13031:0.04917):0.01245):0.00410,31830:0.03143)Pseudonocardiaceae:0.00246):0.00657,30376:0.02417):0.00653):0.01062,11627:0.03223):0.00986,((((129193:0.00728,132662:0.05137):0.00416,199229:0.04271):0.01407,(11920:0.03716,180775:0.09528):0.00352)Streptomycineae:0.02365,33286:0.02258):0.02708)Actinobacteridae:0.03025,(((((((((((((162176:0.07050,222313:0.03557):0.01014,98009:0.06143):0.02080,53290:0.10968):0.00935,102511:0.04023):0.00500,((((12003:0.02589,12488:0.07333)BD2-10:0.02025,44460:0.02267):0.01479,152182:0.04302):0.00332,80057:0.05097)JTB31:0.01493):0.01158,(136457:0.04229,153946:0.04197):0.00502):0.00164,((100131:0.09040,151290:0.07587):0.04524,172612:0.04557):0.01342)koll13:0.02932,(48363:0.06428,62707:0.04839)Chibacore_1500:0.05070):0.01925,112160:0.04741):0.00831,(((((((11402:0.02661,147336:0.04789):0.01498,11418:0.03152):0.00575,154093:0.08016):0.00252,136346:0.03468):0.01728,174445:0.02260):0.00329,(160075:0.03483,184706:0.03713):0.02249)Microthrixineae:0.00902,((((((155077:0.04527,98733:0.04110):0.01174,191730:0.02354):0.00571,160171:0.03893):0.00577,39516:0.02504):0.01472,180464:0.04197):0.01653,113190:0.03229)CL500-29:0.01564):0.00492):0.02126,(((((122561:0.01937,124815:0.02421):0.00329,(39131:0.02661,91212:0.01455)TM112:0.04686):0.02216,11398:0.02735):0.02387,223372:0.11152)Acidimicrobineae:0.00172,((11419:0.03571,37993:0.02095):0.01486,108652:0.04135):0.01897):0.00246):0.01557,((((156658:0.04362,222035:0.02504):0.00749,204312:0.08319):0.00755,(111151:0.02983,155186:0.03546):0.01326):0.00327,185553:0.04915)Acidimicrobidae_bacterium_Ellin7143:0.01321)Acidimicrobidae:0.01475,216057:0.04868):0.02076):0.00413,(((((104871:0.03961,55589:0.05479):0.00252,128299:0.03222):0.01068,208371:0.12511):0.02240,161585:0.09750):0.00084,214163:0.10504):0.01277):0.00827,153923:0.08083):0.00842,((((((((((((((((178488:0.10455,184604:0.04635):0.04170,179831:0.03945):0.03611,11370:0.03510):0.01086,((179569:0.05419,183173:0.07367):0.04295,180467:0.12532)RL247_aaj22c03:0.03464)Collinsella_aerofaciens:0.02072,23706:0.05492)Collinsella:0.01251,(11367:0.02829,227758:0.03312):0.02486)Coriobacteriales:0.01075,(175752:0.05518,182617:0.09932):0.07286):0.00000,((11382:0.02936,163696:0.03569):0.01243,87668:0.02989)Atopobiales:0.05337):0.01414,11366:0.05973):0.00915,(176181:0.04766,32126:0.05605):0.00589):0.00165,((((((((182930:0.06949,191612:0.04411):0.01871,177876:0.07240):0.04919,136349:0.02437):0.00333,(133178:0.05276,148072:0.05982):0.00762):0.00248,182894:0.03830):0.01733,51091:0.04221)Eggerthella:0.00585,(11376:0.04700,146776:0.06548):0.00169):0.00661,(192576:0.06432,199686:0.01787):0.01769):0.00413):0.02548,((143031:0.06304,232800:0.07299):0.00683,198075:0.04859)Rs-H69:0.02560):0.02168,((135349:0.07427,201230:0.09934):0.03382,94574:0.05863):0.00084):0.02249,76995:0.07173):0.00832,161280:0.05578)Coriobacteridae:0.04493,(((((((((219427:0.07282,223287:0.03719):0.01529,107946:0.04023):0.00660,59998:0.02908):0.01481,((114417:0.02464,214698:0.06923):0.00709,70612:0.03939):0.01416):0.00577,19731:0.02936):0.01313,210684:0.09239)Thermoleiphilaceae:0.01948,(((144055:0.04200,160960:0.06306):0.01101,225982:0.06397):0.00334,220214:0.04967):0.00746):0.00495,((((114390:0.02969,133894:0.02628):0.01042,143480:0.03250):0.01144,121120:0.03557)MC47:0.02308,206038:0.10680):0.00941):0.00986,(11353:0.06608,36731:0.03000)Rubrobacteraceae:0.07116)Rubrobacteridae:0.00754):0.00919):0.02386,((((((11924:0.06139,97060:0.05513):0.01011,(154687:0.06640,50283:0.04696):0.00760)At425_EubF1:0.02142,102556:0.10476)WCHB1-81:0.02913,161014:0.08717):0.01193,11591:0.05081):0.01001,144186:0.06992):0.00753)Actinobacteria:0.01322,104067:0.11219):0.00514):0.00824,(((((((((((((((((((((((((156201:0.08120,226780:0.06017):0.02711,113223:0.03263):0.03273,106423:0.07287)GoM_GC185_546E:0.02227,((133917:0.05511,2722:0.04211):0.01426,91842:0.09040):0.00924):0.00500,(((1299:0.02908,220131:0.04746)Eub_4:0.02829,133577:0.05280)T78:0.01085,(227885:0.03853,42071:0.04700):0.01173):0.00165):0.01154,(((21437:0.04293,2729:0.04696):0.00504,31908:0.04696):0.00990,(44630:0.04197,66944:0.04947)Anaerolinea:0.04361):0.00249):0.00164,208234:0.05839):0.00333,13738:0.05182):0.00497,((((((112747:0.03990,113263:0.03905):0.01052,113726:0.04928):0.00957,114374:0.03562):0.01641,(((144458:0.03664,72863:0.02801):0.02443,113490:0.03336)PpalmC37:0.01332,1295:0.02506):0.03039):0.00166,102775:0.07193):0.00251,((142193:0.05170,67537:0.03871):0.01592,99031:0.05010)envOPS12:0.01076):0.00494):0.01483,(((((207368:0.08748,32511:0.09046):0.03307,100661:0.04604):0.01002,36737:0.04602):0.01158,214957:0.03843):0.01817,51154:0.09312)H39:0.01529):0.00997,(((111450:0.01292,177667:0.07108):0.00672,171909:0.04446):0.00083,(200238:0.03958,2725:0.03985)SB-34:0.00167):0.01878)Anaerolineales:0.02384,((((((((((((131339:0.05335,185950:0.07667):0.01194,60174:0.08550):0.00084,159077:0.06277):0.01254,113259:0.09889)TK32:0.01108,167755:0.09002):0.00849,(114246:0.07461,213780:0.07440):0.02664):0.00000,(((113017:0.07890,44185:0.05987):0.01709,71617:0.07386):0.01935,(207768:0.05973,41419:0.05977):0.01954)H8:0.00989):0.00578,(113524:0.09145,214983:0.12406):0.01671)A4b:0.01500,((1301:0.06852,215689:0.07362):0.00854,154220:0.05973)Elev_16S_588:0.02823):0.00420,(159203:0.07759,22061:0.07027)H5:0.01027):0.00249,((113658:0.03226,88752:0.06706)SHA-31:0.03244,2710:0.05806)SJA-101:0.00334):0.01913,(((((143917:0.05892,165185:0.04439):0.01096,216578:0.09487):0.00761,214216:0.04531):0.04144,213456:0.03234):0.01080,(153171:0.04852,159186:0.04985):0.02100):0.00165):0.00412):0.01234,((((((191839:0.04197,64880:0.05973):0.01177,158589:0.07106):0.00748,159331:0.03103):0.00662,(136708:0.05772,155786:0.06397):0.00341):0.00328,167645:0.05892)Napoli-2B-44__BC07-2B-44:0.04144,(210619:0.06988,2751:0.10824)WCHB1-50:0.04091):0.00589):0.00165,(((((160746:0.04221,228624:0.04862):0.01093,26613:0.04523):0.00744,65693:0.05488)Caldilineaceae:0.05412,((153115:0.09470,217445:0.12075):0.00710,111741:0.07192):0.01598):0.01171,((138900:0.07357,71875:0.07033)CCD21:0.01541,80115:0.06522)S0208:0.00586):0.00665):0.01073,(((((((159347:0.04828,166485:0.04120):0.00756,159140:0.04213):0.00906,(159079:0.03796,159191:0.07186):0.00181):0.00164,2712:0.03961)OPB11:0.00082,70228:0.02186):0.00900,(((159240:0.04836,159523:0.06908):0.00361,(159294:0.04893,159360:0.08594):0.00544):0.00527,34880:0.06621)Eub6:0.04925):0.02669,(2752:0.03325,72094:0.04931)H143f:0.01416):0.00991):0.01733,(((1287:0.06220,65433:0.08078):0.00686,103391:0.06295):0.00501,81161:0.06144):0.01083):0.00914,((((164639:0.08208,90568:0.04591):0.04053,163647:0.05825):0.00756,((19494:0.04925,55534:0.05922):0.01222,164913:0.12144):0.00089):0.00908,((151229:0.04439,170145:0.07419)SHA-20:0.01615,2723:0.06073):0.01418):0.00248):0.00492,(((32174:0.04685,44024:0.05574)S47:0.05793,102361:0.07197)A31:0.06184,169619:0.05488):0.00085):0.01882,(((((105919:0.07118,113232:0.03827):0.00979,114068:0.05281):0.00873,225131:0.10130)SOGA31:0.02126,167200:0.07916):0.01191,217784:0.10824):0.00766)Anaerolineae:0.01326,(((((((((((160254:0.06404,208896:0.10256):0.01651,100738:0.07572):0.00252,159894:0.07149):0.00846,(104930:0.06385,161098:0.05017):0.00869):0.00334,((((218158:0.05114,99957:0.07401):0.01786,220080:0.05901):0.02170,226220:0.04538):0.00916,222240:0.06553):0.00167):0.01073,(100115:0.08544,104491:0.07539):0.00887):0.00579,160935:0.06803)Ktedonobacteria:0.04085,(((111100:0.05093,216017:0.11236):0.00346,130005:0.07386):0.00588,178506:0.06048)Ellin7237:0.01837):0.01859,(207920:0.10445,217717:0.11021):0.01923):0.01447,((((113628:0.05461,203057:0.06423):0.01444,(1282:0.08671,20892:0.04092):0.01023):0.00250,((83414:0.06569,90766:0.06975)HN1-15:0.01456,78862:0.09169):0.00510)Thermomicrobia:0.02680,219686:0.09675):0.01969):0.02095,(((((105982:0.06669,113170:0.05867):0.00519,(113276:0.06372,219565:0.06818):0.00509):0.01570,52036:0.15647):0.02452,231395:0.08376)Bljii12:0.00754,229483:0.06159):0.00673):0.00250):0.00493,(((((((159188:0.04688,159515:0.05769):0.01449,101617:0.05901)Chloroflexaceae:0.00251,((159151:0.05714,193176:0.07530):0.00936,73897:0.07195):0.00334)Chloroflexales:0.02334,(((28650:0.10300,41211:0.06381):0.00950,38489:0.09220):0.00590,(17199:0.03072,18466:0.09942)Roseiflexus:0.04786)Roseiflexales:0.01603):0.01593,((137943:0.06407,184873:0.07997):0.03246,1266:0.11673):0.01819)Chloroflexi:0.05882,51584:0.08662):0.01131,(136311:0.08952,53827:0.07045)RA13C7:0.02502):0.00919):0.01075,(((((((((((13744:0.04851,153904:0.04523):0.01682,143037:0.08340):0.00084,(167417:0.05663,63120:0.03398):0.01094):0.00328,35933:0.06306)H14f:0.00666,(((225673:0.05604,67849:0.08969):0.00525,166367:0.06888):0.01168,(109890:0.05735,13743:0.04762):0.03211):0.00587):0.00989,(((159156:0.08542,63104:0.06078):0.01105,160555:0.07773)MB-A2-101:0.02779,31890:0.06992)GIF9:0.02026):0.00997,143283:0.07052):0.00671,163063:0.07022):0.00167,((111483:0.05936,167995:0.08900):0.00342,(112109:0.06002,146475:0.06373):0.00424):0.00249)Dehalococcoidetes:0.03542,(((((((136672:0.07351,145658:0.06465):0.01027,13752:0.07512):0.00336,(101328:0.05093,105649:0.05977):0.02291):0.01078,(111716:0.06220,21495:0.06802):0.01534)PAWS52f:0.01157,104227:0.10858):0.00941,((((101831:0.05744,111406:0.08091):0.01280,101498:0.07201):0.00417,(101670:0.05335,112306:0.04443):0.00758)SAR261:0.00664,100849:0.05740):0.02734):0.00917,((((101990:0.07027,13753:0.05327)SAR307:0.02125,13754:0.05268):0.00833,(101177:0.06300,90508:0.05574):0.04156):0.01928,99290:0.08394):0.00084)Chloroflexi-4:0.01494):0.01083,(((((144409:0.05323,78515:0.06632)S085:0.01184,(216606:0.05807,65691:0.05344):0.00676):0.01327,(204757:0.06432,71332:0.10352):0.00431):0.00746,221463:0.11679):0.00514,213345:0.08127)TK17:0.01341):0.00913):0.00660,177897:0.09951)Chloroflexi:0.04514,(((161834:0.05443,1654:0.04194):0.00670,66306:0.05170)OP9:0.03552,13720:0.08871)OP9_JS1:0.01876):0.00000,((((((((112259:0.08500,219851:0.10500):0.02851,111544:0.11474):0.01394,105224:0.10513):0.04121,(129486:0.10397,162135:0.08914):0.01220):0.00424,(202214:0.10170,96419:0.08374):0.02422)AC1:0.01780,((((144222:0.05560,161484:0.08584)FS274-7B-03:0.05231,(172280:0.07977,63284:0.08696):0.02564):0.00508,151277:0.09927):0.00342,(159584:0.09532,218058:0.07730)CV51:0.05660):0.00756):0.00252,(((((((107427:0.08252,148318:0.09347):0.02243,(161631:0.05645,95342:0.07288):0.03301):0.01012,220248:0.10464):0.00255,62669:0.07108)ZB3:0.02687,(139838:0.09475,28429:0.10073):0.01222):0.00423,137398:0.12177):0.00688,223314:0.12218):0.00599):0.00167,((101238:0.10554,164402:0.10242)Poribacteria:0.02086,27351:0.10693):0.00855):0.00335):0.00414):0.00578,((((110710:0.07380,74641:0.03790):0.01524,62679:0.03793):0.01399,((132063:0.03900,156996:0.03958):0.03661,156779:0.05479):0.01005):0.01151,(156763:0.04523,23520:0.04036):0.02169)Natronoanaerobium:0.01733):0.00492,(((((((((((((((((((((((((((((((((((((((((((((185760:0.09762,187655:0.08149):0.01367,196916:0.08028)RL187_aao70a10:0.03076,187022:0.04495)RL185_aao70a10:0.06418,(176053:0.08298,191833:0.04677):0.00180):0.00351,58380:0.02097):0.00000,(((186062:0.04490,199699:0.03478):0.03509,181938:0.06780)RL199_aaj44c06:0.04590,(178437:0.05847,184470:0.06774):0.02057):0.01672):0.00164,(14023:0.02585,40768:0.06467):0.01008):0.00409,((((((((109018:0.00161,198122:0.02202):0.00326,81674:0.00404):0.00161,((181606:0.03810,184830:0.07959):0.00896,45296:0.01948):0.00245):0.00406,183843:0.06605):0.01336,195646:0.03302):0.00654,((193107:0.06859,198108:0.06943):0.02534,175650:0.05169):0.00087):0.00326,180523:0.01778):0.00163,52305:0.01306)Eubacterium_rectale:0.01460):0.00245,(14059:0.00726,21769:0.01378):0.02927):0.00164,((((((((176865:0.02969,201772:0.04407):0.01487,190303:0.03048):0.00605,((180687:0.02203,184084:0.02881):0.02248,189445:0.03133):0.01123):0.00688,((164940:0.06139,186918:0.04495):0.01930,188531:0.05768):0.00417):0.00819,185653:0.12966):0.00259,154598:0.06958):0.01005,((175587:0.03316,186104:0.05932):0.00617,188863:0.04492):0.01557)SR5:0.02895,41204:0.04832):0.00671):0.00163,110361:0.02419):0.00654,44493:0.04113):0.00987,((((174974:0.08282,192046:0.03983):0.01880,82283:0.01048):0.00490,130746:0.05585)Lachnospiraceae:0.02490,21881:0.02703):0.00164):0.00409,17494:0.04777):0.00165,((((((((((((((144683:0.06908,195985:0.06618):0.00425,131973:0.05217):0.00912,145643:0.04387)M2PB4-48:0.01651,162211:0.06423):0.00168,135350:0.02267):0.01063,189190:0.07712):0.01857,((134266:0.03460,182323:0.03729):0.00872,130699:0.02705):0.00769)Rs-F27:0.01896,228860:0.03390):0.01729,150575:0.03391):0.00740,((((190061:0.04352,195507:0.08262):0.00629,177444:0.07407):0.00176,(177590:0.04572,230719:0.04398):0.00265):0.00515,(((133228:0.01305,180288:0.03732):0.02942,129841:0.04303):0.00349,175033:0.03562):0.00000):0.00685):0.00491,(((135839:0.10420,166297:0.03244):0.03770,110227:0.04351):0.01332,199567:0.08991):0.00423):0.00490,((((113855:0.06140,162576:0.06058):0.00764,(133650:0.03130,179389:0.05929):0.00269):0.00163,((185234:0.07034,195325:0.09191):0.01095,135391:0.03120):0.00772):0.00000,((132338:0.03176,195431:0.05532)C10_E03:0.03780,146474:0.04483):0.00175):0.00325):0.01302,195159:0.11262):0.00773,110046:0.03487):0.00656):0.00082,(((((72977:0.03473,92144:0.03546):0.01077,34466:0.06129)Eubacterium_cf_saburreum_oral_strain_C27KA:0.02334,70198:0.04441):0.01331,162867:0.08414):0.00252,194136:0.12362)Johnsonella:0.01033):0.00654,((((((((((((((191213:0.11864,193666:0.05673):0.02476,175447:0.07234):0.00353,(188690:0.08851,191129:0.12851)RL302_aal95g03:0.00849):0.00173,175595:0.12224):0.00542,(177176:0.11111,196518:0.05937):0.00644):0.00086,(((((183450:0.05085,193148:0.03898)RL176_aah44h04:0.01931,179615:0.05089)RL245_aai81e03:0.03927,188209:0.06298):0.00267,189605:0.06181):0.00175,(186883:0.04851,197624:0.06383):0.00179)SJTU:0.00086):0.00171,(102704:0.03223,99566:0.03077):0.00578):0.00407,(((184700:0.15319,186379:0.11499):0.00576,193793:0.05622):0.01139,(174747:0.05334,181560:0.06468)UC9-24:0.01607):0.00259):0.00164,((((((((174892:0.05593,179971:0.03390)UC7-35:0.01589,198420:0.04657):0.00697,((192302:0.05763,193598:0.12692):0.01385,175550:0.05277):0.00525):0.00257,191183:0.08898):0.01414,183555:0.07313)Ruminococcus_schinkii:0.00706,13989:0.01631):0.00081,194264:0.05872):0.00332,77371:0.03958):0.00329)Ruminococcus:0.01056,14001:0.02177):0.00654,((183107:0.05787,195893:0.05362):0.04137,198399:0.04572)adhufec52:0.00351):0.01459,(((((((101771:0.05560,175240:0.06486):0.01929,147987:0.07264):0.01166,((110596:0.03662,40639:0.04102):0.02083,48088:0.06046):0.01501):0.00083,((130302:0.03575,163874:0.08127):0.00768,186650:0.07046)C13_P19:0.02008):0.00991,((((176109:0.08014,189038:0.10526):0.07391,81160:0.00971):0.00254,198740:0.03393):0.00082,199122:0.05254)Clostridium_clostridioforme:0.01075):0.01481,110292:0.05197):0.00412,((((164968:0.00726,190389:0.09763):0.00423,114094:0.07789):0.01339,189121:0.05339)adhufec250:0.02641,174269:0.06726):0.00337):0.00164):0.00000,(((((((105647:0.04516,212106:0.04194):0.01920,(105901:0.03226,212281:0.04032):0.00747):0.01397,182658:0.06689):0.00758,94718:0.03734):0.00246,((124698:0.03740,86715:0.03384)bacterium_MDA2477:0.02395,109586:0.03874):0.00744):0.00411,25317:0.03955):0.00494,(((((111923:0.04045,92512:0.02991):0.02999,13933:0.02263):0.00823,46567:0.03231):0.00082,13951:0.04980)Butyrivibrio:0.02483,(109561:0.03790,216735:0.02177):0.01075):0.02303):0.01066):0.00082,(((((((((190311:0.04915,230904:0.01837):0.02349,135462:0.01390)C13_C10:0.01525,189979:0.07064):0.01765,180191:0.04681):0.00873,((130639:0.03986,134379:0.02944)C20_k14:0.01860,234313:0.04119):0.00265):0.00685,((186000:0.08729,195808:0.04085):0.02611,34649:0.02679)BB68:0.00330):0.00328,52311:0.02267):0.01145,18619:0.04180):0.00165,(((109768:0.03871,182787:0.04572):0.00581,(189688:0.03895,192067:0.05080):0.00703):0.03191,((169721:0.06366,83226:0.03482):0.00504,198568:0.05942):0.01079)Clostridium_bolteae:0.00167):0.00245):0.00162):0.00569,((((126220:0.09173,135423:0.06376):0.00652,182857:0.07915):0.00880,(190687:0.13787,198620:0.11940):0.03126):0.00618,((148671:0.06983,68844:0.04444):0.02221,131229:0.06597):0.00335):0.00419):0.00812,(((((((((133091:0.01376,134631:0.05774):0.01065,172220:0.01450):0.00809,50457:0.01777):0.00245,(133375:0.06592,164872:0.05101):0.01102):0.00734,(14035:0.03971,24546:0.02262):0.00497):0.00408,((178515:0.09099,193897:0.05169):0.01261,197460:0.08588):0.00176)Clostridium_fusiformis:0.00490,(((175418:0.02458,197152:0.02034):0.02245,178427:0.06553):0.01670,195100:0.04911):0.01311):0.00574,((((109780:0.03790,111310:0.02823):0.02976,109697:0.06136):0.02844,168711:0.01862):0.00330,(((195221:0.06780,198577:0.07745):0.00634,194077:0.05532):0.00262,193981:0.06611):0.00440):0.00979):0.00408,(174162:0.04936,199456:0.06043):0.00896)Clostridium_nexile:0.00656):0.00573,(((((110168:0.04915,111003:0.05659):0.00926,109967:0.04674):0.00908,51880:0.03306):0.00908,179297:0.04478):0.00906,179432:0.05447):0.00167):0.00328,181689:0.06644):0.00167,(((((((190064:0.09277,192609:0.07155):0.00643,178094:0.03745):0.01218,164869:0.01616)adhufec25:0.00736,((177754:0.04945,194123:0.02213):0.01839,199066:0.05617):0.00351):0.01468,188573:0.06999):0.01172,(181349:0.05932,198526:0.08814)RL245_aai83a09:0.06338):0.00580,16944:0.03530):0.00082):0.00980,(((((((((((((175085:0.08092,196213:0.04322):0.10058,198737:0.04370):0.02395,52410:0.00495):0.00167,184661:0.03928):0.00253,189329:0.03843):0.00168,(177320:0.02288,183836:0.11979)RL179_aao56g08:0.01902):0.00919,181528:0.16909):0.00899,114912:0.01951):0.01892,104752:0.04032):0.02393,168317:0.05726):0.00668,((((100450:0.04000,180185:0.06780):0.00420,196851:0.05508):0.00247,(106366:0.04250,14081:0.02984):0.00829):0.00820,182289:0.04661):0.00579):0.00165,((131143:0.03965,36892:0.01966):0.00671,106258:0.02218)Eubacterium_xylanophilum:0.01657)Coprococcus:0.00411,30334:0.04374):0.00412):0.00329,(((((((((((((((185873:0.04826,235094:0.01131):0.00174,233371:0.01394):0.02303,175114:0.02120)C15_I04:0.08251,((132691:0.01997,169013:0.05254):0.01506,193686:0.09107):0.00000):0.01504,97351:0.06169):0.00415,(((175358:0.05942,197157:0.04460):0.02302,132008:0.01651)M2_d04:0.02478,((132757:0.00522,135750:0.00262)C23_f22:0.01221,133843:0.03130):0.01237):0.01038):0.00082,227914:0.04228):0.00165,((((133675:0.02698,135443:0.04279):0.04210,135429:0.02277):0.03197,((14094:0.00566,162811:0.03638):0.00825,182630:0.05692):0.00499)M3_e04_2:0.02387,149693:0.05172):0.00250):0.00082,(130243:0.03481,134698:0.03562)C19_F10:0.05002):0.00247,((184401:0.15208,196689:0.05169):0.00840,182830:0.06367):0.00349)Eubacterium_plexicaudatum:0.02380,(129921:0.01995,134412:0.08275):0.01891):0.00497,((((((((((184367:0.05429,196844:0.07307)RL305aal85b12:0.00717,(189312:0.06991,196744:0.05106):0.02423):0.00350,177211:0.08666):0.00000,(186120:0.11685,194066:0.12777):0.00474):0.00609,(188962:0.01954,195858:0.02964):0.00692)RL197_aah88c10:0.03273,195619:0.04588):0.01316,198174:0.02966):0.03705,((((((158321:0.07362,178618:0.03562):0.00926,69942:0.05166):0.00830,(197539:0.06446,216356:0.05650):0.01773)C10_I02:0.06141,135807:0.05230):0.01180,51677:0.03848)p-4154-6Wa5:0.04461,(175120:0.05598,179439:0.07124):0.01432):0.00504):0.00249,194236:0.05339):0.01750,(113687:0.08634,184969:0.11600):0.00784):0.00753):0.00412,((((((((130433:0.05420,199059:0.03902):0.00443,133193:0.00867):0.02899,132198:0.01569):0.01120,(131066:0.01826,132451:0.03120):0.01054)C22_o06:0.02749,230409:0.06157)C23_k02:0.00000,(194209:0.12291,33833:0.02666):0.00946):0.01967,((((185961:0.03810,73894:0.02097):0.02967,110678:0.03390):0.01491,14096:0.05982)AH153:0.01588,((193730:0.02202,195029:0.04064):0.01391,188034:0.04488):0.02170):0.00165):0.01070,106687:0.08512):0.00084):0.00411,((183988:0.07192,185606:0.06452):0.01081,187114:0.05277):0.00000):0.00326,(14103:0.03306,222851:0.04996)Clostridium_aminovalericum:0.01667):0.00573):0.03176,(((14120:0.03948,14128:0.01934):0.01988,(14127:0.04110,204581:0.02177)Clostridium_lentocellum:0.02976)Epulopiscium:0.01734,172236:0.05077):0.02163):0.00747,140502:0.04216):0.00660,(((((((((((((112809:0.04754,143496:0.04318):0.01883,(114223:0.06040,141770:0.03368):0.01164):0.00529,42607:0.04850)S-P1-30:0.00996,(113707:0.07752,51807:0.07022):0.01278):0.02329,150056:0.04987):0.01335,135632:0.07223):0.01761,114620:0.07339):0.00253,((((78144:0.05108,78343:0.04887):0.04461,78204:0.04741):0.00716,77667:0.06882):0.01607,141830:0.06207):0.00538):0.00166,(((((26279:0.06870,95195:0.04750):0.00516,104551:0.02661):0.00490,144333:0.05095):0.01664,143715:0.04310):0.01660,37128:0.05004):0.00416):0.00329,(147741:0.05086,98297:0.06956)MgMjR-083:0.01197):0.02068,((198678:0.03986,99977:0.03638):0.00415,14114:0.03015)U29-D06:0.03599):0.01412,((((((132536:0.03400,207833:0.03468):0.00746,31573:0.02867):0.00655,(14109:0.02526,49274:0.01787)Clostridium_neopropionicum:0.03473):0.00246,14108:0.03306):0.00899,186851:0.03898):0.00991,(141010:0.10422,149095:0.10603):0.01897):0.00661):0.01403,19275:0.05835):0.00500):0.00247,((109927:0.07108,177731:0.05932):0.00592,92430:0.07431):0.00419):0.04373,16084:0.07884):0.00340,(((((((((((((((((((((((((((175949:0.18652,186011:0.03051):0.01126,(187764:0.04407,189150:0.03729):0.01575):0.00345,16076:0.01300):0.00245,199640:0.03641):0.01560,101849:0.03304):0.00165,192654:0.08044):0.00334,106698:0.05000)Ruminococcus_bromii:0.02230,174018:0.11205):0.01031,((114730:0.04039,169183:0.03306):0.00000,(185822:0.05598,191542:0.02202):0.02363):0.00657):0.00082,((((132206:0.03916,199379:0.07625):0.00362,(132587:0.06082,25644:0.04708):0.00630):0.00865,192398:0.04576):0.00695,183051:0.08213):0.01680):0.00653,((185995:0.06356,43052:0.08526):0.00173,196061:0.06780):0.01263):0.00657,(145151:0.04996,155577:0.04835):0.00334):0.00899,(((((((((110128:0.04712,112227:0.04674):0.02670,198417:0.12969):0.01465,167256:0.04754)p-1082-a5:0.01902,((110458:0.02420,184940:0.04657):0.02565,109546:0.02016)HuAC35:0.01063):0.00661,((181986:0.05773,21232:0.01068):0.03727,187917:0.06774):0.00428):0.00740,((107272:0.02206,181026:0.04905):0.01768,110824:0.03465):0.00988):0.00164,109841:0.07886):0.00756,143881:0.05255):0.01322,187006:0.04390):0.01735):0.00165,44184:0.07009):0.00418,((((((((176499:0.05688,197250:0.04329):0.01061,198563:0.07034)HuCA1:0.00176,27355:0.05646):0.01244,33112:0.05164):0.00334,(103744:0.04847,114773:0.09201):0.01455):0.00249,(((187845:0.06197,19521:0.04273):0.01767,216710:0.05583)adhufec101:0.00580,(103772:0.02831,158569:0.02903):0.00494):0.00900):0.00492,36271:0.03973):0.00740,81523:0.08192):0.00923):0.00983,((((((((16054:0.01950,192419:0.08566):0.01595,193755:0.03810):0.00897,72970:0.03710):0.00330,16039:0.02600):0.00573,198643:0.08298):0.01009,94496:0.02753):0.01630,50218:0.03231):0.00741,(((174712:0.03814,194816:0.03734):0.06319,16074:0.03412)D287:0.00250,144392:0.07161):0.00333):0.00572)Ruminococcus:0.00576,((((((((((((((176288:0.06803,179068:0.05607):0.00537,186904:0.05017):0.01395,191148:0.03902):0.00695,((195496:0.06367,196384:0.03650):0.02390,185622:0.04831):0.01308):0.02672,(((182431:0.07040,27866:0.01374):0.01254,196132:0.20289):0.00445,179358:0.03735):0.00082):0.00165,(((181426:0.11800,181748:0.06452):0.03702,186081:0.06356):0.00797,18361:0.00808):0.00244):0.00407,206101:0.04446):0.00248,(184985:0.05008,195743:0.10339):0.00636):0.00490,181056:0.02976):0.02543,(((((((175148:0.06213,192323:0.05183):0.00799,195467:0.07325):0.00613,178438:0.14189):0.01644,182920:0.06124):0.00349,198387:0.02712):0.01034,29495:0.03451):0.00835,(((162129:0.00967,170603:0.11097):0.00171,(174675:0.03729,188872:0.05518):0.00265):0.01298,187038:0.06520):0.00585):0.00082)Faecalibacterium:0.01152,176247:0.06632):0.01672,(16058:0.04093,234651:0.04305)Anaerofilum:0.00840):0.01815,52759:0.06766):0.01005,(148075:0.05026,78294:0.03203):0.03587):0.01501):0.00248,((((((186578:0.08383,195947:0.03559):0.00358,43051:0.02437):0.00655,207770:0.02419)p-810-a5:0.00985,114725:0.06608):0.01336,111268:0.03065):0.00816,(141809:0.05685,77791:0.04931):0.04634):0.01160):0.00247,((((((((((((105514:0.03473,37295:0.06882):0.00755,41287:0.04050):0.01399,35786:0.04872):0.00166,((19611:0.04330,215509:0.05802):0.00922,174635:0.05004):0.00248):0.00572,16063:0.04384):0.00330,(((221978:0.04847,224800:0.04770)Rs-A15:0.05276,114686:0.06446):0.00506,(100454:0.03790,102539:0.03320):0.02408):0.00414):0.00411,16073:0.05267):0.00247,(148235:0.07759,151144:0.04754):0.00676):0.01065,171848:0.05085):0.00994,111066:0.08375):0.00927,(191100:0.03726,194526:0.03226):0.04196):0.00917,((16059:0.03713,93647:0.03546):0.02070,(181174:0.02288,187404:0.02879)UC7-69:0.04233):0.01248):0.00747):0.00574,191436:0.11120):0.00598,(183479:0.05588,192598:0.07879):0.01347):0.00249,((((((((((107235:0.03065,209337:0.03638)Oscillospira:0.00414,197139:0.07917):0.01341,132260:0.06894):0.00832,196991:0.08129):0.01009,(197958:0.03814,199119:0.09237)LW58:0.00810):0.00412,((((186812:0.05424,191032:0.12203):0.01109,195102:0.02286):0.00257,27791:0.00164):0.00576,(190263:0.11036,194894:0.07881):0.00651)B190:0.00330):0.00326,(133406:0.01553,231094:0.05463)C18_h03_2:0.01782):0.01060,((((109491:0.03304,110161:0.02820):0.00579,(16559:0.04023,34464:0.01967):0.01010):0.00571,177109:0.05250):0.00909,48325:0.02869):0.00493):0.00163,((((174278:0.05504,176242:0.12278):0.00920,(176038:0.05085,196134:0.06017):0.00177)RL181_aah39a05:0.05375,(111120:0.01935,196210:0.03393):0.01566):0.00332,((((104581:0.02423,16027:0.02556):0.00493,110836:0.04597):0.01567,183592:0.04580):0.00083,155059:0.04850):0.00330):0.00653):0.00572,(((((((((181329:0.03810,193471:0.02545):0.02531,165959:0.02425):0.00246,110192:0.01773):0.00326,32020:0.01773):0.00325,(100135:0.03072,102910:0.05000)UC9-83:0.04015):0.00165,161343:0.07020):0.00501,((131573:0.01744,179116:0.04657):0.00695,16023:0.00886):0.00487):0.00244,(106786:0.03770,16072:0.02665):0.01077)Clostridium_viride:0.01629,((((((((192018:0.01614,194450:0.04333):0.01658,186118:0.03218):0.00778,(173815:0.06803,184850:0.02889):0.01675):0.00257,110060:0.01934):0.00326,16057:0.02498):0.01306,((131250:0.05139,180091:0.04329):0.01765,167514:0.05151):0.00758)butyrate-producing_bacterium_A2-207:0.03028,(216140:0.05077,77816:0.08384):0.01536):0.00166,39700:0.03955):0.00329):0.00491):0.01143):0.01557,(((104439:0.05802,151267:0.07183):0.02211,29902:0.06799):0.00587,145319:0.08597):0.00254):0.01068,(((((((((((((((((((100526:0.01292,113654:0.05509):0.00166,105109:0.04520):0.00165,(105813:0.02337,175967:0.03314):0.00578):0.00163,158833:0.10323):0.01614,49406:0.02734):0.00165,216877:0.08643):0.00589,193703:0.04912):0.00497,((134517:0.04485,144509:0.05857):0.01042,110016:0.03223):0.00082):0.00000,114670:0.04110):0.01236,110924:0.04110):0.00411,99888:0.04929):0.01240,107768:0.04049):0.00905,72439:0.06742):0.01753,110535:0.07897)RF6:0.02278,213290:0.06044):0.00828,(((150161:0.03783,17822:0.05000)D693:0.01115,108481:0.05338):0.02321,158611:0.05833)JW32:0.04264):0.01006,22883:0.05319):0.00834,150308:0.07025)RF30:0.01675,24626:0.06973):0.00924):0.00578,((((((169809:0.01129,198781:0.05593):0.00249,192632:0.04068):0.00328,(22115:0.02730,26704:0.02316):0.00931)p-3024-SwA5:0.01465,107605:0.02661):0.03620,78001:0.05613):0.00501,205747:0.05004):0.00582):0.00164,((((((((((100140:0.04197,69664:0.04685):0.00669,(110347:0.05161,218248:0.05085)B4:0.01260):0.00495,99206:0.05097):0.00665,114854:0.08262):0.00841,(114488:0.02744,98930:0.06058)rumen_bacterium_R-25:0.00920):0.00331,126685:0.04796):0.02650,((111170:0.08183,15976:0.04935):0.01105,172948:0.05825):0.00500):0.00165,((162724:0.02186,40758:0.02510):0.03875,220599:0.10877):0.00773):0.00412,((((135577:0.03098,144881:0.02670):0.03884,(15980:0.00570,162454:0.01599):0.04105):0.01326,77360:0.04078):0.00248,15989:0.02666)Clostridium_cellulolyticum:0.00164)Acetivibrio:0.03185,205944:0.05488):0.00586):0.00245,33987:0.05367):0.01983):0.01158,(((((((((((((((174960:0.03814,182861:0.05362):0.01143,197060:0.04170):0.01042,(163690:0.09038,175234:0.07627):0.02378):0.00674,175192:0.04665):0.01793,((197133:0.03053,198504:0.05004):0.02027,144432:0.06797):0.01849)Clostridium_quinii:0.00676,(171184:0.05212,171437:0.04205)Clostridium_sartagoforme:0.01441):0.00411,167951:0.07114)Clostridium:0.00919,16232:0.02996):0.00082,(16202:0.01777,54834:0.02500):0.00739):0.00815,16199:0.03247):0.00739,179771:0.05654):0.00663,(((((168422:0.04105,90396:0.06908):0.01186,16166:0.04450):0.00413,150000:0.03143):0.00412,(217145:0.04612,26148:0.03390):0.00418):0.00407,((170353:0.03653,180489:0.02026)pDH-A:0.04634,149970:0.04036):0.01498):0.00409):0.01226,16195:0.09685):0.01529,((((16110:0.03150,16111:0.03304)Caloramator_indicus:0.01408,34407:0.02927):0.01637,46414:0.03633):0.00579,32182:0.03384)Caloramator:0.01477)Clostridiaceae:0.01076,(30766:0.03793,49771:0.03512)Oxobacter:0.04149):0.01660)Clostridiales:0.00661,((((((((((((192156:0.07219,193643:0.05254):0.04234,195602:0.08362):0.01428,185339:0.03902):0.02273,(111654:0.04205,112201:0.03227):0.00166):0.00327,104895:0.02903):0.00574,(((((110829:0.04204,30743:0.04459):0.00918,111325:0.05335):0.00248,112186:0.04288):0.02063,99414:0.05565):0.01672,105409:0.07102):0.01175):0.00736,((227245:0.05089,83830:0.04197):0.03435,109894:0.04358)SHA-32:0.00665):0.00655,23926:0.07137)RC6:0.01087,(((189786:0.04831,190559:0.09593):0.03533,33984:0.03060):0.00338,144884:0.06667)p-595:0.00833):0.00412,(102879:0.06499,183822:0.06210):0.00425):0.01404,(101401:0.08389,230405:0.05335):0.03318):0.01006,((13759:0.07950,13762:0.06173):0.00681,106330:0.07667)Thermoanaerobacteriaceae:0.01007):0.00498):0.00495,((((((((((((((((((((177228:0.09831,181466:0.06616)RL203_aai63b12:0.02460,187802:0.04153):0.01997,(149597:0.03697,95791:0.05128):0.00428):0.00928,(149904:0.02923,51211:0.03990):0.02353):0.01092,114601:0.05408):0.01329,((((109624:0.04408,46159:0.02419):0.01326,112328:0.03306)Mogibacterium_timidum:0.01809,(107594:0.05186,72513:0.04854)Eubacterium_minutum:0.05485):0.00251,78354:0.09813):0.00506):0.01151,(43267:0.03431,43339:0.02984)p-648-a5:0.03065):0.00412,(224206:0.05621,52379:0.02581):0.00585):0.00492,(176276:0.04919,180402:0.08475):0.02528)Mogibacterium:0.00328,14149:0.04659):0.01154,14158:0.04785):0.01407,14148:0.04439):0.01575,((14178:0.05921,86132:0.08475)Eubacteriaceae_bacterium_P4P_50_P4:0.02659,51973:0.04677)Frigovirgula_patagoniensis:0.03577):0.01340,(((((((((((184671:0.03223,185952:0.06955):0.03890,174734:0.03814):0.00611,162021:0.10553):0.00174,199413:0.03647):0.00339,44695:0.02258):0.01065,14228:0.03679):0.00740,135311:0.03095)Clostridium_bifermentans:0.01809,(14188:0.05519,33116:0.03782):0.01009)Peptostreptococcus_anaerobius:0.02389,((14169:0.01939,224798:0.02661)Clostridium_formicaceticum:0.01643,14174:0.04194)Clostridium_felsineum:0.01156):0.00497,105499:0.05280):0.01078,51694:0.04479):0.00988):0.00495,(((((107660:0.03445,156470:0.03635):0.01491,113661:0.10053):0.00758,((14135:0.02263,85424:0.02182):0.00903,218303:0.05058):0.00331):0.00658,103172:0.04687):0.00414,((14132:0.02754,41951:0.02924):0.01902,85846:0.06877):0.00671):0.00571):0.00739,(14245:0.04523,28721:0.04958):0.02424):0.00414,((106726:0.01990,33448:0.04443):0.01577,132641:0.05979):0.02418):0.00995,((((((((70787:0.07674,83665:0.06371):0.00682,87735:0.06306):0.00248,84198:0.10092):0.01020,99351:0.08710):0.00589,((75475:0.04919,83175:0.06616):0.00337,99981:0.07509):0.00084)Peptoniphilus:0.00582,(14275:0.03873,68341:0.04116):0.01663):0.00991,(((((114836:0.05697,38592:0.04190):0.01049,103341:0.04113):0.00906,14263:0.02823):0.00816,((223099:0.05244,81585:0.06134)KSC6-33:0.03044,35017:0.04516):0.00916):0.00572,99474:0.05323):0.00582):0.00741,157172:0.06785):0.00921)Peptostreptococcaceae:0.01803,99126:0.05263):0.00250,((((14240:0.03476,46867:0.04843):0.00501,157142:0.08592)Acetobacterium:0.01511,89746:0.04358):0.01492,((135410:0.07745,145515:0.04754):0.01447,104008:0.04793)Rs-H81:0.02385)Eubacteriaceae:0.01657):0.01910):0.00911,((((((((((((((((((((((146761:0.04573,185374:0.05089):0.00438,176925:0.07222):0.00437,146392:0.04400):0.00261,144247:0.07075):0.00353,(114807:0.05488,150154:0.06299):0.00507):0.00248,((142033:0.08737,169989:0.08778)Rs-H35:0.02868,14181:0.09106)Rs-G08:0.02208):0.01334,111211:0.05650):0.00501,(158015:0.03871,183202:0.07385):0.01517):0.01243,165063:0.09445):0.01612,((((220301:0.05124,224204:0.05032):0.00504,78199:0.07074):0.00669,68349:0.07270):0.00503,(111555:0.05675,148781:0.05464):0.02038):0.00659):0.00580,(110842:0.05004,146376:0.05551):0.00168)Clostridiales_bacterium_JN18_A56_K:0.02146,114843:0.07506)Clostridiales_bacterium_JN18_A89_K*:0.03037,(((((108716:0.03150,110296:0.03067):0.00661,(180895:0.07301,190523:0.08085)B380:0.02724):0.00330,188183:0.02545):0.00656,(((180585:0.04333,191738:0.04153):0.03596,107464:0.03632)LG58:0.01244,110847:0.02665):0.00082):0.00327,111250:0.02906)BCf4-07:0.03363):0.01164,(101433:0.05452,99789:0.03482):0.01174):0.00658,14133:0.03955):0.00328,(17311:0.04191,34789:0.07775):0.01806):0.00246,204369:0.05760)Catabacter:0.00582,(144429:0.04248,178394:0.04580)LQ86:0.02108):0.00578,((((181180:0.03556,192164:0.03986):0.01571,189012:0.18718):0.04486,111274:0.06860)RL302_aal95f07:0.02520,(195106:0.06820,195548:0.05768):0.02318)ELAND_60:0.01172):0.01164,210805:0.05663):0.02176,169056:0.06184):0.00333,((109644:0.04204,16087:0.05497):0.01347,93487:0.09620):0.00849):0.02064)Clostridia:0.01402,(((45167:0.05220,93440:0.06746):0.02634,(62673:0.05242,62675:0.05726):0.01686)Anaerobrancaceae:0.01584,136803:0.06543):0.00084):0.01151,((((((((((((((((((((((((((174092:0.07052,190255:0.06525):0.00631,178986:0.02549):0.06521,164187:0.05843):0.00255,(130403:0.00969,133930:0.06866)Lactobacillus_iners:0.02937):0.00986,((21096:0.00968,88892:0.01373)Lactobacillus_delbrueckii:0.04072,51715:0.01868)Lactobacillus:0.01068):0.00737,((178564:0.05532,192175:0.05820):0.01158,193690:0.01951):0.05646):0.00912,177499:0.03958):0.00822,167772:0.07068):0.00502,(((((((131877:0.05148,187332:0.02037):0.05949,15143:0.02670):0.00166,233451:0.03073):0.00246,21772:0.02121)Lactobacillus_reuteri:0.01966,107485:0.01777)Lactobacillus_fermentum:0.03436,(15152:0.02116,15156:0.00810)Pediococcus:0.01964):0.00413,(((108027:0.01777,15164:0.02829):0.00165,58041:0.04355):0.00164,(15179:0.03554,26176:0.04619):0.01088):0.00573):0.00574):0.00327,(((96727:0.03877,97946:0.02097):0.00827,111332:0.02346):0.00408,130744:0.03640):0.01885):0.00904,(107854:0.04694,15221:0.01131)Lactobacillus_casei:0.02150)Lactobacillaceae:0.01712,((((((((((146681:0.10535,14917:0.06866):0.00869,178759:0.06864):0.01089,(110120:0.05740,15693:0.06853):0.01189)NB1-n:0.01741,(153403:0.05008,155994:0.04834):0.01587):0.00500,(134601:0.05893,182108:0.03407):0.01666)Turicibacter_sanguinis:0.01741,50458:0.06159)Turicibacter:0.02012,((15061:0.02984,48276:0.03145):0.01159,28361:0.04176):0.00494):0.00247,48237:0.06063):0.00334,((114822:0.03871,15241:0.04293):0.01498,55895:0.05161):0.02155)Aerococcaceae:0.02060,(((140940:0.04608,151398:0.04450):0.00336,(172032:0.08646,172558:0.01714)aab18f10:0.08576):0.00664,16452:0.02751)Carnobacteriaceae:0.00163):0.00328):0.01148,(((107061:0.06058,136001:0.07411):0.00264,(170087:0.12267,62895:0.01132):0.00258):0.00572,164676:0.07205):0.00251):0.00164,((((((((((((185556:0.03401,196718:0.03144):0.02002,190336:0.06463):0.04930,(205757:0.00242,221080:0.03405):0.00000):0.00165,100119:0.02504):0.00082,55342:0.02777):0.00491,50448:0.01212)Streptococcus:0.01218,15483:0.05063):0.02577,108351:0.02264)Streptococcaceae:0.01319,78015:0.05177):0.00499,110020:0.06031):0.02163,((((15110:0.02661,67688:0.02683):0.01816,67686:0.01545):0.00977,(216931:0.06396,76923:0.01696)Weissella:0.01585):0.01559,134006:0.10280)Leuconostoc:0.02214):0.02667,81912:0.04839):0.00919)Lactobacillales:0.02697,53770:0.02991):0.00994,(((((((142023:0.01278,165813:0.10801):0.00700,146872:0.00809):0.00489,217912:0.02429):0.00164,14894:0.00890):0.01138,(165963:0.04456,173296:0.08860):0.00173):0.02287,64393:0.01618):0.00247,(145617:0.04773,28012:0.03236):0.02598)Staphylococcaceae:0.02625):0.00329,((((((((((14990:0.02668,156286:0.05209):0.00668,147938:0.06066):0.00000,141968:0.02393):0.00898,203909:0.05963):0.00414,48079:0.03756)Planococcaceae:0.02144,(218836:0.04195,224949:0.04034):0.01399):0.00740,(104534:0.08808,216781:0.04021):0.01241):0.01233,(((((14540:0.06446,83619:0.07674):0.01620,151888:0.03630)Geobacillus_debilis:0.02483,58677:0.01855):0.02548,58675:0.03163):0.00742,14575:0.02184):0.00899):0.00245,(((129409:0.07486,211845:0.06089):0.00427,175339:0.06494):0.01507,(173100:0.08799,40011:0.05551)Bacillus_cereus:0.02957):0.00332):0.00327,(((((191576:0.03747,38338:0.06088):0.00762,128618:0.00081):0.01299,14754:0.00405):0.00244,165114:0.09306):0.01267,190564:0.07201)Bacillus:0.01922):0.00984):0.00245,(((137867:0.04693,139299:0.01537)Sporolactobacillus:0.07146,54543:0.02344):0.01084,(206482:0.02689,36300:0.04372)Marinococcus:0.05558):0.01573):0.00164,(((14604:0.01774,14611:0.03079):0.00000,106243:0.02021):0.00570,128093:0.04298)Halobacillus:0.00578):0.00326,15026:0.04839):0.00414,((((112113:0.01535,26206:0.02742):0.01148,22975:0.04040):0.00248,144022:0.03231)Thermoactinomycetaceae:0.02383,14468:0.04927):0.00915):0.00246,((((((((((132693:0.02423,187176:0.02549):0.01154,39053:0.06490):0.00334,(104054:0.01856,163693:0.07342):0.00588):0.00245,(((14405:0.06835,51767:0.02989):0.01762,110470:0.11516):0.00511,91160:0.03808):0.00412):0.00814,96700:0.01776):0.00328,141630:0.04710):0.00165,80984:0.01614):0.01383,(167450:0.08174,210271:0.05013):0.00171):0.00821,137955:0.06710):0.01672,((209244:0.08319,67396:0.06036):0.00345,103446:0.01292)Brevibacillus_brevis:0.05032)Paenibacillaceae:0.00831):0.00820,(109064:0.05908,182726:0.06818):0.02380)Bacilli:0.00828,(((((157680:0.02636,167279:0.04113):0.00249,212471:0.05973)Bacillales:0.04476,14331:0.05897):0.00505,107003:0.03745):0.00659,14348:0.05259)Alicyclobacillus:0.03231):0.00249,(((((((((((((((((114509:0.04443,23473:0.06202):0.02025,(114581:0.06726,43971:0.05782):0.00766)BCf4-19:0.03071,((32823:0.08844,77895:0.03554):0.00768,78268:0.05959):0.00249)BCf7-05:0.04661,15854:0.06063):0.00000,(145549:0.06306,15811:0.05331):0.01954):0.00498,15832:0.05174):0.02155,(((106362:0.07404,15860:0.07820)Mycoplasma_haemocanis:0.03614,15859:0.12355):0.07434,15837:0.07416)Mycoplasma_haemofelis:0.03190)Mycoplasma_haemomuris:0.07254,((102371:0.07934,160113:0.10434):0.04939,(214741:0.10008,219640:0.08536)Elev_16S_1754:0.09807):0.01214):0.00171,((((((103186:0.04766,220788:0.07345)Mycoplasma_ovipneumoniae:0.03152,70042:0.03551):0.02070,(137538:0.05811,58394:0.05565):0.00254):0.00494,((15899:0.06618,79153:0.05950):0.01194,23718:0.03629):0.01399):0.00495,36302:0.06250):0.00249,(108671:0.05726,155625:0.07748):0.01534)Mycoplasma:0.06595)Mycoplasmatales:0.00596,((((150616:0.08814,163407:0.04194):0.05544,101955:0.04194):0.01755,206850:0.06295):0.00669,29463:0.06371)Spiroplasma_citri:0.01501):0.02505,((((((104358:0.06761,210102:0.06093):0.01582,42538:0.07041)adhufec202:0.02750,42395:0.05105):0.00693,((191556:0.08588,52403:0.04268):0.00932,182398:0.06327):0.00666):0.00248,(232434:0.04424,50779:0.04403)p-3870-23G5:0.01467):0.01075,15698:0.08221)RF39:0.04804):0.00677,(((((((55569:0.04681,62523:0.03468)Candidatus_Phytoplasma:0.02501,15696:0.08198):0.00422,166536:0.07168):0.00755,(15695:0.04518,30323:0.05165):0.00588):0.02546,(15708:0.07512,179979:0.05259):0.01360):0.00579,15703:0.07314):0.01758,(15704:0.08602,62666:0.08071):0.01901)Acholeplasmatales:0.00837):0.00416,((((((((((185382:0.10415,193776:0.04835):0.03098,174166:0.02969):0.00261,190225:0.10348):0.00625,50433:0.01306):0.00244,(((176560:0.04153,184230:0.01610):0.01043,(182229:0.04657,199714:0.06037):0.00798):0.00686,175094:0.08149):0.02124)Eubacterium_biforme:0.02469,((((((130091:0.05206,230759:0.05911):0.00254,98789:0.04504):0.00826,132422:0.05345):0.00749,((188898:0.04770,197123:0.05424):0.01675,115098:0.02183)M1_h03_1:0.04025)M3_e09_3:0.02907,49369:0.03734):0.00000,49837:0.03877):0.00739):0.02061,((15734:0.01212,176685:0.05424):0.01828,(199533:0.08282,42372:0.02346):0.01013):0.00904):0.01730,202367:0.04828):0.01410,188919:0.04261)Eubacterium_cylindroides:0.01984,((((((19191:0.04708,31844:0.02744):0.01417,158561:0.09180):0.00508,44294:0.06475):0.01923,54730:0.02705)Bulleidia:0.02887,42560:0.07189):0.00845,(((106981:0.06582,210848:0.09630):0.01070,(142208:0.03647,33753:0.04597):0.00000):0.00494,15728:0.04766):0.00662)Erysipelothrix:0.01154)Clostridium_aff_innocuum_CM970:0.01414):0.00414,(((211927:0.07605,227846:0.07039):0.01641,15710:0.04163):0.02071,158774:0.05421)RFN20:0.05197):0.01003,114644:0.08424):0.01011,(((((((132391:0.06620,221498:0.07581)M2_c03_3:0.02273,45363:0.03398):0.00331,(179471:0.12225,190451:0.04092)UC9-28:0.04230):0.00414,((151870:0.01570,193360:0.04514)AP07K72:0.02998,21822:0.04217):0.00417)Clostridium_ramosum:0.01401,(100351:0.04949,106388:0.05025):0.02261):0.00496,43417:0.05524)Catenibacterium:0.02246,(196583:0.06655,219921:0.09578):0.01541):0.02657):0.01333,145889:0.17764)Mollicutes:0.02650):0.00832,(111399:0.09201,17688:0.08657):0.02075):0.01833):0.00664,(29289:0.04762,98761:0.06066):0.09451):0.01086,((((103560:0.04274,109777:0.02661):0.01329,62670:0.04036):0.01153,((151965:0.05968,153214:0.04032):0.00671,154120:0.06785)1B07:0.01589):0.01404,(166481:0.04878,168487:0.04285)TIBE07:0.04620):0.00917):0.00165,((((((105370:0.03148,108785:0.02647)DR9IPCB16SCT8:0.07273,(108149:0.05077,231581:0.06299):0.00420):0.00752,(89131:0.03707,90083:0.08566):0.01696):0.00579,30024:0.05168):0.00411,(((142065:0.04835,143189:0.05673):0.00673,13659:0.04281):0.00411,13678:0.05984)Desulfotomaculum_nigrificans:0.01506):0.01241,13681:0.06048)Desulfotomaculum:0.01906):0.00083,((((((((((((((((182816:0.04174,85145:0.01216)B259:0.01243,58262:0.03548):0.00989,136415:0.02532)Allisonella_histaminiformans:0.02707,128382:0.05246)Dialister_pneumosintes:0.01419,((133171:0.06721,87238:0.03231):0.00757,169931:0.07535)Megasphaera:0.01585):0.00412,13820:0.04527)Dialister:0.03644,13851:0.04610):0.00250,(((((100212:0.06136,217071:0.08624):0.02208,(41482:0.01210,51109:0.03471):0.01813):0.00659,(31951:0.02500,42483:0.03306):0.01322):0.01227,(140868:0.03632,228113:0.04036)Megamonas:0.01749):0.01069,29533:0.05143):0.01328):0.00658,((((184145:0.03412,218453:0.07997):0.01870,79562:0.06200):0.01004,(128076:0.06011,13840:0.04870):0.02902):0.00414,21147:0.05323):0.00330)Acidaminococcaceae:0.00247,((((((162895:0.07195,194405:0.05678)p-2464-18B5:0.02552,(211008:0.05327,225954:0.07437):0.04471):0.00842,(111235:0.02502,13855:0.03961):0.00498):0.00493,168236:0.07264):0.00755,13862:0.05178):0.00166,66181:0.04036):0.00164):0.01313,(136950:0.08657,34224:0.05629)BSV43:0.02385):0.01920,152353:0.10000):0.01199,((((((((34346:0.03393,92321:0.05004):0.00753,222308:0.03220):0.01557,13692:0.02591):0.01230,13689:0.03251):0.00247,101877:0.03629)Syntrophomonadaceae:0.02625,13691:0.03433):0.03554,173476:0.05348):0.01255,((131268:0.05831,179499:0.03902):0.00176,(145805:0.03145,221790:0.03955):0.01247)ZEBRA_23:0.00982):0.00740):0.00247,((((134013:0.07955,229371:0.01223):0.05018,130034:0.04974):0.01342,29453:0.06081)rc4-4:0.01952,33780:0.04797):0.01912):0.00660,(105619:0.06362,139270:0.03868)Peptococcaceae:0.04214):0.00247,(((((101811:0.06715,81303:0.04928):0.00253,81121:0.01479):0.00490,63750:0.01290):0.02751,38133:0.05335)Desulfitobacterium:0.03677,(((13873:0.02981,165741:0.02581):0.00165,33994:0.02109):0.01874,107750:0.04271):0.01405):0.00496):0.00735):0.01142,13924:0.08232):0.01011,187815:0.05033):0.00663,((((((128362:0.06307,169459:0.07413):0.01023,(145732:0.07742,150758:0.06121):0.00683):0.00998,(((13718:0.03874,179728:0.06290):0.01514,215116:0.05403):0.01161,133929:0.04110):0.00497):0.00826,100452:0.08625):0.01597,((((103678:0.05818,99132:0.03005):0.00586,168737:0.04835):0.00248,(134788:0.03551,136533:0.04527):0.01252):0.00821,168627:0.04685)OPB54:0.03304):0.00995,(((((((136728:0.04362,54890:0.03406):0.05069,220089:0.05537)Symbiobacterium:0.03356,208847:0.05174):0.01005,(110857:0.05157,131119:0.03062)D2:0.05177):0.01162,130916:0.05318):0.00582,(148652:0.05567,25424:0.07581):0.01967)Symbiobacterales:0.00495,((((131486:0.04593,179484:0.05161):0.01846,99836:0.07006):0.00669,131733:0.05645):0.00915,108786:0.05560):0.00833):0.01313)Symbiobacteria:0.00907)Firmicutes:0.01067,134122:0.05399):0.00995,(((((160816:0.06116,94490:0.06613)Sulfobacillus:0.05840,111907:0.07911):0.01967,(13721:0.03714,143589:0.06452):0.00252)Sulfobacilli:0.02395,((134576:0.03783,13717:0.02742)Thermoacetogenium:0.03311,13728:0.06260):0.01593):0.00830,((144825:0.05983,19343:0.04898)Desulfitobacter:0.02126,13708:0.03629):0.01239):0.00247):0.00982):0.00827,(((((13767:0.05276,66680:0.02744):0.01085,66678:0.02975):0.00982,20457:0.04194)Thermoanaerobacteria:0.01733,16088:0.09435):0.00255,((137796:0.07858,139107:0.05263)Thermovenabulae:0.04810,(178547:0.06088,233108:0.05327)Fervidomicrobium:0.01354):0.01586):0.00331):0.01420,(((((((((((((((((((199942:0.11138,200206:0.08765):0.00965,200018:0.07256):0.02753,200668:0.08424)RCP1-27:0.02989,(201072:0.14453,201953:0.13361):0.03417):0.00520,(200336:0.07923,206209:0.09224)J-1:0.06125):0.01204,(142268:0.12979,202139:0.13200):0.03493):0.00259,155335:0.11841)OP11-4:0.01885,((103462:0.11445,201939:0.11526):0.01636,154380:0.10509):0.02982):0.01802,(((((159312:0.05826,69764:0.07330):0.01578,10560:0.08354):0.00930,159237:0.07865)WCHB1-64:0.03374,141197:0.09677):0.03962,(((129580:0.12045,136414:0.08239):0.01146,165344:0.09867):0.00424,136645:0.11057):0.00257):0.03140):0.00508,(((((((((((201335:0.09208,202687:0.09154):0.00432,207211:0.07382):0.00753,202937:0.10259):0.00854,199824:0.07512):0.01187,((201331:0.06629,201813:0.06321):0.01871,202812:0.06715):0.00000):0.00499,202164:0.08252):0.00674,(200048:0.08481,203525:0.12490):0.00353):0.00667,221796:0.11671):0.04283,203454:0.14656)OP11-1:0.05015,(((((80684:0.04882,82149:0.03247):0.02600,155265:0.07230):0.01606,(111795:0.07442,114724:0.08826):0.01732):0.00584,154361:0.06250):0.00337,10567:0.06352)OP11-2:0.12762):0.00531,((173357:0.08612,200948:0.11073):0.01519,200232:0.15812)OP11-3:0.03775):0.01397)OP11:0.03992,107881:0.10788):0.02260,(((((((((110696:0.07731,226078:0.06390):0.01034,99217:0.09748):0.01023,(220016:0.07924,222960:0.05680):0.01824):0.00587,((219692:0.09099,227183:0.08854):0.00740,79187:0.09504):0.00334)ST-12K2:0.02344,(((150593:0.10585,221262:0.12794):0.01686,(204488:0.10188,6321:0.08414):0.01389)KNA6-NB29:0.01520,((135161:0.10994,227270:0.13420):0.00622,105780:0.10895):0.01780):0.00681)FW129:0.01432,((168994:0.08445,227413:0.08003):0.01122,227358:0.08096)MVP-8A-28:0.03537):0.01108,(((151616:0.13606,35504:0.08717):0.00709,42311:0.11237):0.01624,2917:0.12050)WCHB1-58:0.01452):0.02460,(112352:0.14139,200020:0.13144):0.00354)ABY1_OD1:0.00000,(((((((((((106562:0.07212,226676:0.04560):0.01277,143479:0.07792):0.01263,149397:0.07549)FCPT678:0.02622,224312:0.10976):0.01991,65700:0.08910):0.01278,(108872:0.09336,10978:0.08840)BD5-13:0.04506):0.02219,((110455:0.10325,16854:0.09246):0.01145,136443:0.12715):0.01382):0.00851,(105625:0.06023,179710:0.06407):0.03495)BD5-13:0.05717,(201138:0.12278,201556:0.14019):0.04250):0.02842,(((100328:0.10033,104387:0.12252):0.02512,213358:0.14983):0.01704,227206:0.17239):0.00796):0.01023,150632:0.14655)ZB2:0.01135):0.02108):0.00931,((((((((150608:0.10509,159133:0.05771):0.02476,144617:0.10356):0.02559,138924:0.10218):0.01103,((111630:0.06711,65673:0.09121)GKS2-174:0.09239,155524:0.10958):0.01841):0.00926,((159241:0.09121,74066:0.12982):0.00826,159367:0.12838):0.01552):0.00837,((((203683:0.08232,22264:0.08674)VC12-cl13:0.03521,200762:0.13162)VC12-cl04:0.03471,101480:0.12045):0.00956,200775:0.12755):0.01389):0.00589,(159180:0.11004,235128:0.12561):0.00880)GN02:0.02704,((150741:0.12146,151438:0.12338):0.00533,111520:0.13389)SHA-95:0.02235):0.00515):0.00840,((((((205716:0.08062,232597:0.09580)Elev_16S_1084:0.02960,216930:0.12318):0.00985,203529:0.19284)SM2F11:0.02676,(102116:0.12066,216234:0.11952):0.01901):0.01459,(209664:0.16381,211059:0.13882):0.01657):0.00604,(105480:0.10597,220992:0.17257):0.02885):0.00424):0.01009,(((((((((((((111384:0.04770,135876:0.06356):0.00839,135194:0.05872):0.01658,6929:0.07504):0.00591,17020:0.05101):0.00415,(((169822:0.10540,21239:0.03884):0.02404,70379:0.05335):0.01071,22963:0.05737):0.00249):0.00495,114578:0.09197):0.03732,91484:0.09208)TM7-3:0.01758,109782:0.06796):0.00679,((((((((111978:0.04120,49147:0.05578):0.01925,222777:0.07348):0.01009,189880:0.07251):0.01177,30464:0.04890):0.02422,215260:0.10248):0.01028,65695:0.06311):0.00748,14906:0.06947)TM7-1:0.00920,120505:0.08333):0.00931):0.00918,(21860:0.10688,45149:0.09209):0.00875):0.03076,(212739:0.08283,225943:0.06073)MJK10:0.02735)TM7:0.03120,((210046:0.11966,215203:0.09633)O339:0.01638,156900:0.09289):0.00425):0.01103,(33606:0.06951,91082:0.06905)WS5:0.06020):0.01432):0.00924,(154606:0.08145,226293:0.08498):0.07414):0.01281,((((200961:0.05089,203106:0.05403)WCHB1-15:0.01769,202078:0.05726):0.03741,203188:0.08476):0.02810,((202302:0.14159,202979:0.14620):0.05105,203137:0.11907)WM1006:0.02127)WS6:0.04385):0.03234,((((((((((36990:0.07581,94589:0.04221):0.00506,143142:0.07131):0.01671,(104126:0.05587,225856:0.08381):0.00771):0.01000,(23652:0.10150,3685:0.09453):0.01572)Elusimicrobiaceae:0.02434,150714:0.08502):0.00340,(156651:0.07027,219480:0.10226):0.00347):0.00333,114382:0.08429)Elusimicrobiales:0.01846,((114420:0.02498,224190:0.05584)Endomicrobia:0.04513,(157794:0.05399,222488:0.04647):0.01007):0.02568):0.00584,144924:0.08763):0.02276,100774:0.09579)Elusimicrobia_TG1:0.04598):0.00599,214270:0.12965):0.02152):0.01410,((((156538:0.12417,45035:0.13479):0.00823,22143:0.13135):0.02330,1240:0.09504)OP1:0.03161,(1249:0.09813,79191:0.08591)Coprothermobacteria:0.03891):0.01536):0.00334,((((((((109901:0.05781,164887:0.05263):0.03785,147627:0.04832)Synergistes:0.01681,155458:0.05811):0.02925,105533:0.05403):0.00334,110262:0.05412):0.00499,(((165132:0.07114,47490:0.04847):0.01015,221562:0.07282)TG5:0.03270,1651:0.10571):0.00775):0.01162,(1635:0.05828,87292:0.09128)Dethiosulfovibrio:0.02400):0.01582,42039:0.08010)Synergistetes_Aminanaerobia:0.04252):0.00748,(((((((((((155885:0.05511,17343:0.10922):0.02437,100171:0.04608):0.01577,32254:0.07767)CL500-48:0.02024,66917:0.11593):0.01120,163720:0.07494):0.01245,(177553:0.05242,80993:0.07294)SJA-22:0.05015):0.01092,1430:0.06532):0.00585,(((1429:0.06366,159862:0.09584):0.01583,96473:0.11584):0.00615,(79712:0.04998,99075:0.08834)S1a-1H:0.06063):0.00258):0.02162,((((((221811:0.03884,57713:0.05583):0.00841,(39143:0.04538,41561:0.07374):0.01192):0.00991,1442:0.05906):0.01506,159493:0.06908)CH21:0.06876,46343:0.09855):0.01214,208982:0.08078):0.00592)OP10:0.04519,((((113363:0.06618,226892:0.05835)FCPO410:0.03309,204483:0.10360)JG37-AG-4:0.00431,((222095:0.07792,225556:0.02591):0.03387,208292:0.04538)ABS-6:0.04344)AD3:0.02265,(105869:0.12936,208966:0.08674):0.01914):0.00169):0.00000,((159742:0.11842,209019:0.08048):0.01837,213629:0.06661)GAL15:0.01055):0.02499):0.01163,((((((((105372:0.06300,161163:0.04854):0.03469,177656:0.06352):0.03104,(51926:0.11817,79590:0.11964):0.03567):0.00338,((1216:0.09002,1219:0.06465):0.03533,1220:0.07856)Geotogaceae:0.03718):0.01679,((1222:0.04575,18702:0.04439)Fervidobacteriaceae:0.05523,1230:0.05325):0.01175):0.02424,41353:0.04435)Thermotogae:0.03741,((203255:0.06780,35729:0.08574):0.02977,35305:0.07347)EM3:0.05624):0.03212,74652:0.10177):0.01373):0.01160,(((145058:0.05867,185095:0.05504)GAL35:0.02781,168520:0.07345):0.00588,157742:0.09863):0.00338):0.00333,(((101088:0.06732,38917:0.07910):0.01716,(157005:0.03682,38674:0.04910)Thermodesulfobacteria:0.04051):0.01830,102075:0.05833):0.01843):0.01706,((((202057:0.06963,209414:0.06321):0.01194,211918:0.08036):0.03888,200861:0.06230)BH1:0.11670,((200180:0.10575,200608:0.09170):0.06828,203503:0.08710)BD2-14:0.07940)SR1:0.06817):0.01091,(((((191575:0.04725,38815:0.02659)Hydrogenobacter:0.00827,153907:0.04843):0.02150,43828:0.10573)Aquificales:0.02139,((1197:0.02743,138074:0.05726):0.00752,30394:0.02417)Hydrogenothermaceae:0.03426)Aquificae:0.04433,85051:0.07730)Aquificae:0.00256):0.01209,16693:0.11812):0.01757,13724:0.10430)Bacteria:0.13464,(((((((((((((((((((((((((100784:0.10168,516:0.05419):0.04148,108682:0.04149)Methanosarcinaceae:0.02660,143846:0.04489):0.00600,525:0.06965)Methanosarcinales:0.00860,(209322:0.05241,68326:0.05912):0.02696):0.01360,((((152455:0.06484,299:0.07066):0.01143,173672:0.05329):0.00170,267:0.03574)Methanomicrobiales:0.03985,210610:0.05071):0.04739):0.00777,(103626:0.07243,233066:0.08070):0.03082):0.01128,139737:0.10050):0.01050,144407:0.07609)Methanomicrobia_Eury:0.01901,(102902:0.09908,104083:0.08881)ANME1:0.02425):0.01568,((((((((((156943:0.05634,71191:0.06053):0.00174,99537:0.07559):0.00773,85668:0.06058):0.00344,(169691:0.06385,74464:0.04813):0.01481):0.00512,(36299:0.05638,72038:0.06395):0.01395):0.00000,(140077:0.05220,54099:0.06545):0.01389):0.00673,688:0.05892):0.01116,109549:0.06775)Halobacteria_Halobacteriales:0.06274,692:0.17304):0.02396,(100246:0.08555,41854:0.08645)SA1_Eury:0.03657):0.01315):0.01997,((((((((137640:0.11897,33564:0.12375):0.00276,135186:0.11657):0.02795,(21:0.10693,773:0.09642):0.03150)DHVE3:0.00176,201355:0.16696):0.00454,((158830:0.13830,200959:0.20843):0.02903,100020:0.12347):0.01097):0.00793,(211317:0.12422,52:0.13273):0.02180):0.00701,(((1019:0.05822,237:0.08247):0.01263,16284:0.10092)pMC2A36:0.09553,772:0.08438)pMC2A209:0.05495):0.00349,((((203065:0.13973,223034:0.12170):0.05869,200845:0.15524):0.00470,7:0.11604):0.00635,((115256:0.09829,203276:0.09225):0.07057,151811:0.10325)HV1:0.01789)pMC2A384:0.02179):0.00609):0.01036,((((((((140100:0.06865,140261:0.05289):0.01484,24840:0.03802):0.01016,141088:0.06115):0.00342,34194:0.03220):0.02017,111614:0.07279):0.00604,43427:0.04218)SAGMEG-1:0.06397,(131861:0.03742,132875:0.04592)MSBL1:0.08715):0.04464,(128710:0.07967,142642:0.05237)Methanococci_Eury:0.04739):0.00260):0.00000,((((((((((((((139526:0.08382,199554:0.08897):0.00533,(200010:0.07315,711:0.04561):0.02789):0.00341,((209248:0.06634,63282:0.06470)ARCP1-30:0.02188,22294:0.06836):0.00604):0.00424,((52339:0.07155,62881:0.06589)WCHD3-02:0.02188,140032:0.08562):0.01390):0.01529,130029:0.09896):0.01136,137883:0.11111):0.00790,(201596:0.08017,204898:0.07357):0.00360)terrestrial_group:0.00938,((((((109524:0.04908,146447:0.05031)CTD005-13A:0.02677,104364:0.05473):0.02317,112246:0.07250)marine_group_II:0.06502,190786:0.06983):0.02109,141005:0.09860):0.01058,(109663:0.08056,124193:0.05148)marine_group_III:0.03332)E3:0.01986):0.00600,(219191:0.05408,428:0.04246)pMC2A33:0.10254)E2:0.01129,(132046:0.11171,54:0.07827):0.01615):0.00172,((((((104684:0.04971,47977:0.04722):0.01036,161666:0.05329):0.00340,71237:0.05560)Thermoplasmataceae:0.06309,32461:0.07645):0.02896,161417:0.05638)Thermoplasmatales:0.02759,13:0.07721):0.01045)Thermoplasmata_Eury:0.03596,144490:0.13228):0.02507,(((108925:0.08674,202396:0.08197):0.07165,(200115:0.12257,202746:0.11926):0.03705)pMC2A15:0.03934,((137541:0.08229,62886:0.07807):0.01425,39716:0.08236)WSA2:0.01819):0.01147):0.00439,((((245:0.07204,249:0.08326):0.03100,49:0.06754):0.01562,123021:0.05061)pMC1A4:0.02314,188272:0.06162)pMC1:0.03374):0.00602):0.00595,((((130:0.04888,150461:0.05302)Methanobrevibacter_cuticularis:0.00952,194137:0.06186)Methanobrevibacter:0.02651,223:0.02320):0.07546,46:0.03065)Methanobacteria_Eury:0.04903):0.01712,(137113:0.06053,743:0.09310):0.00795):0.00598,((125020:0.06468,77902:0.10741)xNanoarchaeota:0.11191,(223234:0.01369,64372:0.02642)Methanopyri:0.07265):0.00357):0.03674,142674:0.11519):0.02585,((((((((1029:0.07158,108956:0.09699)Cenarchaeum:0.00887,49412:0.07404)Cenarchaeales:0.03873,101095:0.05494):0.01478,71074:0.04406):0.03588,204290:0.04992)Thaumarchaeota:0.02245,39230:0.05800):0.01727,(((((108942:0.09425,111933:0.09353):0.00892,213389:0.08909):0.01897,115168:0.06250):0.01561,1006:0.07092)Sd-NA:0.01726,127052:0.04541):0.00770):0.02056,((43314:0.05877,53714:0.03660)A14:0.08127,888:0.06839):0.00525):0.01706):0.00862,((((((105220:0.04665,835:0.08706):0.01404,(140627:0.04481,201321:0.05932):0.02081):0.00597,138956:0.07600):0.00687,((112707:0.08088,177633:0.03172):0.02442,(126957:0.07800,140689:0.04333):0.02283):0.01031)C2:0.00683,((((193033:0.09039,198509:0.07699):0.01960,142115:0.11287):0.00462,(112651:0.13277,839:0.09609):0.03185):0.01563,(119867:0.04291,122677:0.06486):0.02386):0.00946):0.00845,126878:0.07699):0.01388):0.00341,(((((((((19329:0.05079,978:0.04651)Acidianus:0.01990,54467:0.05653):0.00602,(35548:0.06847,946:0.06934):0.01687)Sulfolobales:0.04344,144945:0.03552):0.00692,(910:0.05083,916:0.02991):0.00086):0.00842,186470:0.02919):0.03197,(((23289:0.03648,872:0.06078):0.00688,877:0.06385)Thermoproteaceae:0.02822,(181142:0.04754,73046:0.05430)Thermofilaceae:0.01653)Thermoproteales:0.00257)Thermoprotei_Cren:0.00085,(179805:0.06119,836:0.07463):0.01662):0.00594,((190468:0.12200,34207:0.11399):0.02507,51122:0.11130)YNPFFA:0.01440):0.00861):0.00591,((136293:0.05106,141677:0.04602)pISA7:0.02551,(137080:0.07756,838:0.06403)pSL4:0.05968):0.00260):0.03304,((200378:0.01500,202338:0.00887)SBAK-mid-13:0.26400,771:0.10870):0.09135):0.01246,883:0.13561):0.00444,(160320:0.03987,51224:0.03904)Korarchaeota:0.05918)Archaea:0.13464); + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test-Biplot.tsv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test-Biplot.tsv Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,9 @@ +TID Continuous STSite Bacteria|Firmicutes|Clostridia|Clostridiales|Clostridiaceae|Clostridium|72 Bacteria|unclassified|4904 Bacteria|Firmicutes|Bacilli|Lactobacillales|Lactobacillaceae|Lactobacillus|1361 Bacteria|3417 Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368 +700098986 1 L_Antecubital_fossa 1 0 3 0 5 +700098984 2 R_Retroauricular_crease 0 10 0 45 0 +700098980 4 Subgingival_plaque 12 43 29 34 2 +700098988 5 R_Antecubital_fossa 0 6 0 3 0 +700037470 6 L_Retroauricular_crease 6 0 45 0 6 +700037472 7 R_Retroauricular_crease 0 23 0 0 0 +700037474 8 L_Antecubital_fossa 2 0 1 0 1 +700037476 9 R_Antecubital_fossa 1 1 1 1 1 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test-BiplotNA.tsv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test-BiplotNA.tsv Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,9 @@ +TID Continuous STSite Bacteria|Firmicutes|Clostridia|Clostridiales|Clostridiaceae|Clostridium|72 Bacteria|unclassified|4904 Bacteria|Firmicutes|Bacilli|Lactobacillales|Lactobacillaceae|Lactobacillus|1361 Bacteria|3417 Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368 +700098986 1 L_Antecubital_fossa 1 0 3 0 5 +700098984 2 NA 0 10 0 45 0 +700098980 4 Subgingival_plaque 12 43 29 34 2 +700098988 5 R_Antecubital_fossa 0 6 0 3 0 +700037470 6 L_Retroauricular_crease 6 0 45 0 6 +700037472 7 R_Retroauricular_crease 0 23 0 0 0 +700037474 8 NA 2 0 1 0 1 +700037476 9 R_Antecubital_fossa 1 1 1 1 1 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test-comma.biom --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test-comma.biom Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +{"id": "None","format": "Biological Observation Matrix 1.0.0","format_url": "http://biom-format.org","type": "OTU table","generated_by": "BreadCrumbs","date": "2013-09-08T17:12:24.307906","matrix_type": "sparse","matrix_element_type": "float","shape": [5, 8],"data": [[0,0,1.0],[0,2,12.0],[0,4,6.0],[0,6,2.0],[0,7,1.0],[1,1,10.0],[1,2,43.0],[1,3,6.0],[1,5,23.0],[1,7,1.0],[2,0,3.0],[2,2,29.0],[2,4,45.0],[2,6,1.0],[2,7,1.0],[3,1,45.0],[3,2,34.0],[3,3,3.0],[3,7,1.0],[4,0,5.0],[4,2,2.0],[4,4,6.0],[4,6,1.0],[4,7,1.0]],"rows": [{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|72", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|4904", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1361", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|3417", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368", "metadata": null}],"columns": [{"id": "700098986", "metadata": {"TID": "700098986", "STSite": "L_Antecubital_fossa", "ID": "700098986"}},{"id": "700098984", "metadata": {"TID": "700098984", "STSite": "R_Retroauricular_crease", "ID": "700098984"}},{"id": "700098980", "metadata": {"TID": "700098980", "STSite": "Subgingival_plaque", "ID": "700098980"}},{"id": "700098988", "metadata": {"TID": "700098988", "STSite": "R_Antecubital_fossa", "ID": "700098988"}},{"id": "700037470", "metadata": {"TID": "700037470", "STSite": "L_Retroauricular_crease", "ID": "700037470"}},{"id": "700037472", "metadata": {"TID": "700037472", "STSite": "R_Retroauricular_crease", "ID": "700037472"}},{"id": "700037474", "metadata": {"TID": "700037474", "STSite": "L_Antecubital_fossa", "ID": "700037474"}},{"id": "700037476", "metadata": {"TID": "700037476", "STSite": "R_Antecubital_fossa", "ID": "700037476"}}]} \ No newline at end of file diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test-comma.pcl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test-comma.pcl Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,8 @@ +ID,700098986,700098984,700098980,700098988,700037470,700037472,700037474,700037476 +TID,700098986,700098984,700098980,700098988,700037470,700037472,700037474,700037476 +STSite,L_Antecubital_fossa,R_Retroauricular_crease,Subgingival_plaque,R_Antecubital_fossa,L_Retroauricular_crease,R_Retroauricular_crease,L_Antecubital_fossa,R_Antecubital_fossa +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|72,1.0,0.0,12.0,0.0,6.0,0.0,2.0,1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|4904,0.0,10.0,43.0,6.0,0.0,23.0,0.0,1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1361,3.0,0.0,29.0,0.0,45.0,0.0,1.0,1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|3417,0.0,45.0,34.0,3.0,0.0,0.0,0.0,1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368,5.0,0.0,2.0,0.0,6.0,0.0,1.0,1.0 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test.biom --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test.biom Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +{"id": "None","format": "Biological Observation Matrix 1.0.0","format_url": "http://biom-format.org","type": "OTU table","generated_by": "BreadCrumbs","date": "2013-09-04T12:40:01.356447","matrix_type": "sparse","matrix_element_type": "float","shape": [5, 8],"data": [[0,0,1.2300000190734863],[0,2,12.0],[0,4,6.0],[0,6,2.0],[0,7,1.0],[1,1,10.0],[1,2,43.0],[1,3,6.0],[1,5,23.0],[1,7,1.0],[2,0,3.0],[2,2,29.0],[2,4,45.0],[2,6,1.0],[2,7,1.0],[3,1,45.0],[3,2,34.0],[3,3,3.0],[3,7,1.0],[4,0,5.0],[4,2,2.0],[4,4,6.0],[4,6,1.0],[4,7,1.0]],"rows": [{"id": "72", "metadata": {"taxonomy": ["Bacteria", "Firmicutes", "Bacilli", "Bacillales", "Bacillaceae", "unclassified"]}},{"id": "4904", "metadata": {"taxonomy": ["Bacteria", "Firmicutes", "Bacilli", "Bacillales", "Bacillaceae", "unclassified"]}},{"id": "1361", "metadata": {"taxonomy": ["Bacteria", "Firmicutes", "Bacilli", "Bacillales", "Bacillaceae", "unclassified"]}},{"id": "3417", "metadata": {"taxonomy": ["Bacteria", "Firmicutes", "Bacilli", "Bacillales", "Bacillaceae", "unclassified"]}},{"id": "1368", "metadata": {"taxonomy": ["Bacteria", "Firmicutes", "Bacilli", "Bacillales", "Bacillaceae", "unclassified"]}}],"columns": [{"id": "700098986", "metadata": {"TID": "700098986", "STSite": "L_Antecubital_fossa", "ID": "700098986"}},{"id": "700098984", "metadata": {"TID": "700098984", "STSite": "R_Retroauricular_crease", "ID": "700098984"}},{"id": "700098980", "metadata": {"TID": "700098980", "STSite": "Subgingival_plaque", "ID": "700098980"}},{"id": "700098988", "metadata": {"TID": "700098988", "STSite": "R_Antecubital_fossa", "ID": "700098988"}},{"id": "700037470", "metadata": {"TID": "700037470", "STSite": "L_Retroauricular_crease", "ID": "700037470"}},{"id": "700037472", "metadata": {"TID": "700037472", "STSite": "R_Retroauricular_crease", "ID": "700037472"}},{"id": "700037474", "metadata": {"TID": "700037474", "STSite": "L_Antecubital_fossa", "ID": "700037474"}},{"id": "700037476", "metadata": {"TID": "700037476", "STSite": "R_Antecubital_fossa", "ID": "700037476"}}]} \ No newline at end of file diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test.pcl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test.pcl Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,8 @@ +ID 700098986 700098984 700098980 700098988 700037470 700037472 700037474 700037476 +TID 700098986 700098984 700098980 700098988 700037470 700037472 700037474 700037476 +STSite L_Antecubital_fossa R_Retroauricular_crease Subgingival_plaque R_Antecubital_fossa L_Retroauricular_crease R_Retroauricular_crease L_Antecubital_fossa R_Antecubital_fossa +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|72 1.23 0.0 12.0 0.0 6.0 0.0 2.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|4904 0.0 10.0 43.0 6.0 0.0 23.0 0.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1361 3.0 0.0 29.0 0.0 45.0 0.0 1.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|3417 0.0 45.0 34.0 3.0 0.0 0.0 0.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368 5.0 0.0 2.0 0.0 6.0 0.0 1.0 1.0 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test.tsv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test.tsv Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,9 @@ +TID STSite Bacteria|Firmicutes|Clostridia|Clostridiales|Clostridiaceae|Clostridium|72 Bacteria|unclassified|4904 Bacteria|Firmicutes|Bacilli|Lactobacillales|Lactobacillaceae|Lactobacillus|1361 Bacteria|3417 Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368 +700098986 L_Antecubital_fossa 1 0 3 0 5 +700098984 R_Retroauricular_crease 0 10 0 45 0 +700098980 Subgingival_plaque 12 43 29 34 2 +700098988 R_Antecubital_fossa 0 6 0 3 0 +700037470 L_Retroauricular_crease 6 0 45 0 6 +700037472 R_Retroauricular_crease 0 23 0 0 0 +700037474 L_Antecubital_fossa 2 0 1 0 1 +700037476 R_Antecubital_fossa 1 1 1 1 1 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/TestForConversion.biom --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/TestForConversion.biom Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +{"id": "None","format": "Biological Observation Matrix 1.0.0","format_url": "http://biom-format.org","type": "OTU table","generated_by": "BreadCrumbs","date": "2013-09-17T04:25:03.670076","matrix_type": "sparse","matrix_element_type": "float","shape": [5, 8],"data": [[0,0,1.2300000190734863],[0,2,12.0],[0,4,6.0],[0,6,2.0],[0,7,1.0],[1,1,10.0],[1,2,43.0],[1,3,6.0],[1,5,23.0],[1,7,1.0],[2,0,3.0],[2,2,29.0],[2,4,45.0],[2,6,1.0],[2,7,1.0],[3,1,45.0],[3,2,34.0],[3,3,3.0],[3,7,1.0],[4,0,5.0],[4,2,2.0],[4,4,6.0],[4,6,1.0],[4,7,1.0]],"rows": [{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|72", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|4904", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1361", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|3417", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368", "metadata": null}],"columns": [{"id": "700098986", "metadata": {"TID": "700098986", "STSite": "L_Antecubital_fossa", "ID": "700098986"}},{"id": "700098984", "metadata": {"TID": "700098984", "STSite": "R_Retroauricular_crease", "ID": "700098984"}},{"id": "700098980", "metadata": {"TID": "700098980", "STSite": "Subgingival_plaque", "ID": "700098980"}},{"id": "700098988", "metadata": {"TID": "700098988", "STSite": "R_Antecubital_fossa", "ID": "700098988"}},{"id": "700037470", "metadata": {"TID": "700037470", "STSite": "L_Retroauricular_crease", "ID": "700037470"}},{"id": "700037472", "metadata": {"TID": "700037472", "STSite": "R_Retroauricular_crease", "ID": "700037472"}},{"id": "700037474", "metadata": {"TID": "700037474", "STSite": "L_Antecubital_fossa", "ID": "700037474"}},{"id": "700037476", "metadata": {"TID": "700037476", "STSite": "R_Antecubital_fossa", "ID": "700037476"}}]} \ No newline at end of file diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test_no_metadata.biom --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test_no_metadata.biom Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +{"id": "None","format": "Biological Observation Matrix 1.0.0","format_url": "http://biom-format.org","type": "OTU table","generated_by": "BreadCrumbs","date": "2013-09-08T21:45:51.197864","matrix_type": "sparse","matrix_element_type": "float","shape": [5, 8],"data": [[0,0,1.2300000190734863],[0,2,12.0],[0,4,6.0],[0,6,2.0],[0,7,1.0],[1,1,10.0],[1,2,43.0],[1,3,6.0],[1,5,23.0],[1,7,1.0],[2,0,3.0],[2,2,29.0],[2,4,45.0],[2,6,1.0],[2,7,1.0],[3,1,45.0],[3,2,34.0],[3,3,3.0],[3,7,1.0],[4,0,5.0],[4,2,2.0],[4,4,6.0],[4,6,1.0],[4,7,1.0]],"rows": [{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|72", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|4904", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1361", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|3417", "metadata": null},{"id": "Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368", "metadata": null}],"columns": [{"id": "700098986", "metadata": {"ID": "700098986"}},{"id": "700098984", "metadata": {"ID": "700098984"}},{"id": "700098980", "metadata": {"ID": "700098980"}},{"id": "700098988", "metadata": {"ID": "700098988"}},{"id": "700037470", "metadata": {"ID": "700037470"}},{"id": "700037472", "metadata": {"ID": "700037472"}},{"id": "700037474", "metadata": {"ID": "700037474"}},{"id": "700037476", "metadata": {"ID": "700037476"}}]} \ No newline at end of file diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/Test_no_metadata.pcl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/Test_no_metadata.pcl Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,6 @@ +ID 700098986 700098984 700098980 700098988 700037470 700037472 700037474 700037476 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|72 1.23 0.0 12.0 0.0 6.0 0.0 2.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|4904 0.0 10.0 43.0 6.0 0.0 23.0 0.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1361 3.0 0.0 29.0 0.0 45.0 0.0 1.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|3417 0.0 45.0 34.0 3.0 0.0 0.0 0.0 1.0 +Bacteria|Firmicutes|Bacilli|Bacillales|Bacillaceae|unclassified|1368 5.0 0.0 2.0 0.0 6.0 0.0 1.0 1.0 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map-colors.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map-colors.txt Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,8 @@ +Group1:Nse#L#150,Nse#L#151,Nse#L#152,Nse#L#153,Nse#L#98 +Group2:Nso#106,Nso#107,Nso#108,Nso#1209,Nso#65 +Group3:Nw#L#119,Nw#L#160,Nw#L#5,Nw#R#189,Nw#R#50 +Group4:Sse#1224,Sse#M#14,Sse#M#169,Sse#M#62,Sse#M#63,Sse#M#64,Sse#M#75 +Group5:Swb#M#137,Swb#M#154,Swb#M#155,Swb#M#156,Swb#M#157 +Group6:Sws#M#1227,Sws#M#1230,Sws#M#1234,Sws#M#163,Sws#M#83 +Group7:Tg#1238,Tg#1249,Tg#1251,Tg#1252 +Group8:Vg#h#1038,Vg#h#1039,Vg#h#1043,Vg#h#1061,Vg#h#1104,Vg#o#1124,Vg#o#1128,Vg#o#1132,Vg#o#1153,Vg#o#1160,Vg#oh#1051,Vg#oh#1055,Vg#oh#1070 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map.txt Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,4844 @@ +150394 Tg#1249 1 +150394 Tg#1251 2 +215260 Nso#65 1 +215260 Nso#1209 4 +16073 Vg#h#1061 1 +16072 Vg#h#1104 1 +16076 Vg#o#1128 2 +16076 Vg#oh#1070 84 +16076 Vg#oh#1055 1 +16076 Vg#o#1132 2 +16076 Tg#1249 2 +16076 Vg#o#1153 2 +35238 Nse#L#151 1 +35238 Nw#R#50 2 +35238 Nso#106 1 +16074 Vg#h#1043 2 +16074 Vg#oh#1070 1 +16074 Vg#oh#1051 1 +16074 Vg#h#1038 6 +16074 Vg#o#1132 2 +101767 Nw#L#5 1 +101767 Nso#108 1 +114078 Nso#65 1 +131443 Vg#h#1039 1 +144338 Tg#1249 1 +111532 Nso#108 1 +111532 Nso#107 7 +169721 Vg#oh#1055 26 +169721 Vg#oh#1051 33 +103892 Sse#M#62 1 +214891 Nso#65 1 +214891 Nso#1209 1 +170442 Sse#M#62 1 +58397 Tg#1252 1 +58397 Nse#L#150 1 +82191 Tg#1238 2 +93780 Nw#L#5 1 +61204 Swb#M#156 42 +61204 Swb#M#157 118 +61204 Swb#M#154 10 +61204 Swb#M#155 2 +61204 Sws#M#1227 10 +61204 Sws#M#163 276 +61204 Nso#108 1 +61204 Sws#M#83 44 +61204 Sws#M#1234 15 +61204 Swb#M#137 2 +61204 Sws#M#1230 27 +130882 Nso#65 1 +130882 Nw#L#5 1 +130882 Nw#L#119 1 +130882 Sse#M#169 1 +130882 Nso#107 1 +130882 Nw#R#50 1 +6155 Nw#R#50 1 +6155 Nso#108 1 +6155 Nso#106 2 +167734 Vg#o#1132 2 +7624 Sws#M#1234 1 +7624 Sse#M#62 1 +7624 Sse#M#63 1 +7624 Sse#M#75 1 +7624 Sws#M#83 1 +182229 Vg#o#1153 1 +131207 Sse#M#169 1 +131207 Sse#M#63 1 +131207 Swb#M#156 2 +112460 Nse#L#151 1 +112460 Sse#M#64 1 +192175 Vg#o#1124 3 +57724 Swb#M#154 8 +57724 Sws#M#163 1 +57724 Nso#108 2 +57724 Nso#106 1 +166488 Nse#L#151 1 +155255 Nso#1209 1 +55589 Nso#65 1 +220301 Vg#o#1124 2 +97151 Vg#h#1039 4 +97151 Vg#h#1104 8 +97151 Vg#h#1043 1 +166485 Sse#M#62 1 +154652 Sse#M#62 1 +154652 Sse#M#63 2 +60136 Vg#oh#1055 2 +6294 Swb#M#156 1 +6294 Swb#M#155 1 +6294 Nse#L#98 1 +6294 Nw#R#50 1 +6294 Sse#M#75 1 +6294 Nso#108 2 +6294 Sse#M#64 1 +191465 Nse#L#98 1 +191465 Nw#L#160 1 +191465 Nso#106 1 +152550 Sws#M#1230 26 +152550 Sws#M#1227 56 +152550 Sws#M#1234 6 +152550 Sse#M#64 2 +152550 Sws#M#83 1 +106352 Swb#M#154 1 +106352 Sws#M#163 2 +106352 Sse#M#62 1 +106352 Sse#M#75 1 +152557 Sws#M#83 5 +105395 Nw#L#5 1 +105395 Nw#L#160 2 +105395 Nw#L#119 5 +105395 Nw#R#189 2 +105004 Nso#1209 3 +105004 Nso#107 1 +105004 Nw#R#50 1 +81674 Vg#oh#1070 127 +81674 Vg#oh#1055 10 +81674 Vg#oh#1051 137 +81674 Vg#o#1132 3 +140328 Nso#1209 1 +101380 Nw#L#119 1 +101380 Nso#107 1 +227238 Nso#65 1 +227238 Nse#L#150 1 +227238 Nso#106 1 +199059 Vg#o#1124 5 +43051 Vg#h#1043 1 +43051 Vg#o#1124 4 +43051 Vg#o#1128 1 +43051 Vg#oh#1070 1 +43051 Vg#o#1132 1 +43051 Vg#o#1153 1 +161296 Sse#M#63 1 +101018 Nse#L#98 1 +159156 Sse#M#75 1 +200413 Sse#M#62 5 +200413 Sse#M#63 2 +10709 Sse#M#169 1 +10709 Nso#108 2 +2446 Sse#M#14 1 +2446 Sws#M#1227 1 +2446 Sws#M#83 4 +2446 Nw#R#189 1 +2446 Swb#M#137 1 +2446 Sws#M#1230 12 +2446 Sse#M#62 1 +2446 Sse#M#64 4 +99206 Nse#L#150 1 +95791 Tg#1238 2 +95791 Tg#1249 1 +128299 Nse#L#151 1 +128299 Nso#1209 1 +102326 Nso#1209 2 +102326 Sws#M#163 1 +102326 Nso#108 1 +150608 Sse#M#63 1 +38438 Vg#o#1124 11 +167514 Vg#o#1124 8 +113170 Nso#108 1 +2045 Vg#o#1124 1 +2045 Vg#h#1061 1 +2045 Vg#oh#1055 44 +2045 Vg#h#1038 1 +2045 Vg#h#1039 1 +2045 Vg#o#1132 1 +84125 Vg#h#1104 7 +84125 Vg#h#1061 9 +190826 Sws#M#83 1 +102448 Sws#M#163 1 +102448 Swb#M#157 1 +102448 Sws#M#83 1 +34809 Vg#h#1061 2 +154272 Sse#M#62 1 +128618 Nso#1209 1 +21292 Nw#L#119 3 +21292 Nso#106 1 +105718 Vg#h#1038 1 +105718 Vg#h#1104 3 +105718 Vg#o#1132 1 +153802 Nw#R#50 1 +80115 Sse#M#62 1 +1783 Nso#108 1 +1783 Nso#1209 1 +1783 Nso#107 10 +1783 Nso#106 13 +45661 Sse#M#75 1 +90000 Nse#L#98 1 +14135 Sws#M#83 1 +164968 Vg#h#1104 1 +164968 Vg#o#1124 3 +164968 Vg#oh#1070 9 +164968 Vg#oh#1055 39 +164968 Vg#oh#1051 33 +164968 Vg#h#1039 3 +164968 Vg#o#1132 1 +164968 Vg#o#1153 2 +63116 Sse#M#169 1 +63116 Nw#R#50 1 +200704 Sse#M#169 1 +46867 Vg#o#1132 1 +142023 Tg#1251 1 +70396 Nso#108 1 +70396 Nso#106 1 +198122 Vg#oh#1070 4 +198122 Vg#oh#1055 2 +198122 Vg#oh#1051 3 +163720 Swb#M#155 13 +48540 Nw#L#160 2 +48540 Nw#R#50 1 +108713 Tg#1249 1 +108713 Sws#M#163 1 +108713 Nso#108 3 +108713 Sws#M#83 2 +108713 Nw#R#189 3 +220892 Nso#108 2 +220892 Nso#107 2 +84672 Tg#1252 1 +84672 Tg#1251 1 +84672 Tg#1249 1 +84672 Sse#1224 1 +40185 Sse#M#62 1 +108716 Vg#o#1128 1 +48787 Sse#1224 17 +217069 Nso#107 1 +15710 Vg#h#1061 1 +49481 Nse#L#153 1 +49481 Sws#M#1230 1 +112956 Sws#M#163 1 +112956 Sws#M#1234 1 +106778 Tg#1249 4 +21437 Nso#108 1 +21437 Nse#L#153 1 +21437 Sse#M#63 1 +1624 Nso#65 7 +1624 Tg#1249 1 +1624 Nso#1209 3 +1624 Nso#106 1 +172948 Sse#M#62 1 +175403 Nse#L#153 1 +146474 Tg#1251 1 +146475 Nse#L#98 1 +188962 Vg#o#1160 2 +41587 Nw#L#160 1 +41587 Tg#1252 1 +112227 Vg#o#1132 1 +83477 Swb#M#156 1 +83477 Swb#M#154 4 +83477 Sws#M#163 1 +83477 Sws#M#1230 1 +83477 Sse#M#63 1 +83477 Nw#R#50 1 +188919 Vg#h#1039 1 +229081 Sws#M#1227 1 +229081 Sws#M#1234 1 +229081 Sws#M#83 1 +101918 Sse#M#62 1 +14240 Vg#oh#1070 1 +14240 Vg#h#1104 1 +14240 Vg#h#1061 1 +146872 Swb#M#155 3 +146872 Vg#h#1104 1 +146872 Vg#o#1132 1 +17822 Vg#o#1132 1 +107660 Sse#M#63 1 +107660 Sse#M#64 1 +171306 Nso#65 8 +171306 Nw#R#189 2 +171306 Vg#o#1124 4 +156651 Nso#1209 1 +156658 Nso#1209 11 +108481 Vg#h#1104 1 +14397 Nse#L#151 2 +30743 Vg#h#1043 4 +30743 Vg#h#1104 5 +30743 Tg#1238 1 +30743 Tg#1251 1 +30743 Vg#o#1128 1 +30743 Vg#h#1061 8 +30743 Vg#oh#1070 20 +30743 Vg#h#1039 5 +30743 Vg#o#1132 1 +109280 Nse#L#153 2 +31691 Swb#M#156 1 +31691 Swb#M#157 3 +31691 Sse#M#75 5 +31691 Swb#M#155 4 +208896 Nso#1209 1 +18143 Swb#M#155 3 +18143 Sws#M#1230 1 +18143 Sse#M#62 3 +18143 Sse#M#64 1 +103391 Nso#108 1 +108139 Vg#h#1038 2 +1287 Nse#L#98 1 +1287 Nse#L#151 2 +110836 Vg#h#1043 1 +196416 Nw#L#5 1 +196416 Nso#108 1 +156811 Sse#1224 1 +94496 Vg#o#1128 3 +94496 Vg#h#1061 1 +94496 Vg#h#1039 2 +94496 Vg#o#1153 1 +94496 Vg#o#1132 3 +77973 Tg#1249 3 +148459 Nse#L#152 1 +148459 Vg#o#1128 1 +148459 Vg#h#1061 3 +79712 Nso#65 5 +79712 Nso#108 1 +79712 Nso#1209 1 +151090 Swb#M#156 18 +151090 Swb#M#157 2 +151090 Swb#M#154 41 +151090 Swb#M#155 18 +145085 Sse#M#14 1 +144509 Tg#1238 2 +144509 Tg#1251 1 +8384 Swb#M#155 2 +8384 Vg#h#1104 7 +8384 Nw#R#50 2 +137514 Vg#oh#1055 230 +51476 Nso#65 2 +51476 Nso#108 4 +51476 Nso#1209 2 +51476 Nso#107 1 +11418 Sse#M#14 1 +11418 Sws#M#163 1 +11418 Swb#M#137 3 +11418 Sws#M#83 1 +188183 Vg#oh#1070 4 +188183 Vg#o#1128 1 +186160 Nso#65 4 +186160 Nso#1209 1 +186160 Nse#L#153 1 +186160 Nse#L#152 1 +186160 Nso#108 1 +132011 Vg#h#1104 8 +132011 Vg#o#1124 2 +132011 Vg#o#1128 1 +132011 Vg#o#1160 1 +132011 Vg#h#1039 6 +132011 Vg#o#1132 1 +132011 Vg#o#1153 1 +24410 Nse#L#150 2 +24410 Swb#M#154 1 +97840 Nw#L#160 1 +151144 Vg#o#1128 1 +151144 Vg#h#1061 1 +151144 Vg#h#1104 1 +151144 Vg#h#1039 2 +151144 Vg#o#1153 1 +151144 Vg#o#1132 1 +180191 Vg#h#1104 1 +180191 Vg#o#1124 9 +180191 Vg#h#1061 1 +111268 Vg#o#1124 1 +111268 Vg#oh#1070 1 +111268 Vg#o#1132 1 +111268 Vg#o#1153 1 +143354 Nso#65 1 +143354 Nw#L#119 5 +143354 Nso#1209 2 +136933 Nso#107 1 +136933 Sse#M#62 1 +136935 Sws#M#163 1 +136935 Sws#M#1230 2 +8494 Sse#M#169 1 +8494 Nso#65 1 +8494 Nso#1209 1 +113658 Nso#108 7 +113658 Nse#L#151 3 +35933 Nse#L#151 1 +28964 Sws#M#163 1 +81121 Nse#L#150 1 +110016 Vg#oh#1070 3 +110016 Vg#o#1124 1 +46567 Vg#o#1153 2 +46567 Vg#oh#1070 1 +46567 Vg#o#1128 3 +13720 Sse#M#75 24 +13720 Sse#M#169 5 +13720 Sse#M#63 1 +43267 Vg#h#1038 2 +43267 Vg#o#1132 1 +43267 Vg#h#1061 1 +13726 Swb#M#154 17 +13726 Swb#M#155 1 +13726 Sse#M#62 1 +13726 Sse#M#63 1 +13727 Nse#L#98 1 +88702 Swb#M#154 1 +88702 Nso#108 2 +88702 Nso#1209 3 +146376 Tg#1249 1 +146376 Tg#1251 1 +192222 Vg#h#1104 1 +193360 Vg#o#1160 1 +11376 Vg#o#1160 1 +11376 Vg#h#1061 1 +161044 Sse#M#64 1 +180288 Vg#o#1124 56 +180288 Vg#h#1039 1 +7523 Sse#1224 1 +131877 Vg#o#1124 1 +91842 Vg#h#1061 1 +228113 Vg#h#1104 1 +214060 Nse#L#152 3 +214060 Nso#1209 21 +100171 Nso#65 1 +110128 Vg#o#1124 1 +51292 Nso#107 1 +51292 Nse#L#151 1 +51292 Nso#108 6 +51292 Nso#1209 1 +51292 Nso#106 4 +213210 Nso#107 2 +104381 Nse#L#152 1 +104381 Sws#M#163 21 +150161 Vg#oh#1070 12 +114686 Tg#1238 2 +214088 Nse#L#151 2 +214088 Nso#1209 2 +145100 Nw#R#189 1 +31908 Sse#M#62 1 +101620 Sws#M#1234 9 +101620 Sws#M#163 1 +101620 Sws#M#1227 2 +101620 Sws#M#83 5 +165618 Sws#M#1230 3 +165344 Sse#M#63 1 +153946 Sse#M#63 1 +211845 Nso#1209 2 +153017 Swb#M#156 1 +153017 Sws#M#83 2 +99987 Nso#1209 8 +177109 Vg#h#1043 7 +177109 Vg#h#1039 2 +155317 Nso#1209 1 +4622 Nso#1209 1 +182289 Vg#oh#1070 2 +182289 Vg#oh#1055 1 +190751 Nse#L#153 1 +43992 Nso#108 4 +43992 Vg#o#1132 1 +43992 Nso#1209 5 +65460 Nso#108 1 +65460 Nso#106 1 +130091 Vg#h#1104 13 +219934 Swb#M#137 1 +28705 Nw#L#160 1 +154445 Sse#M#62 1 +26148 Nse#L#151 1 +26148 Nse#L#153 1 +26148 Nso#1209 1 +12003 Sse#M#62 1 +12003 Sse#M#63 1 +141206 Swb#M#137 3 +141206 Nso#1209 1 +184617 Sws#M#83 1 +81187 Swb#M#156 1 +81187 Nso#1209 1 +81187 Nso#106 1 +201556 Sse#M#169 7 +201556 Sse#M#63 1 +11591 Nse#L#98 1 +51109 Vg#o#1132 2 +139838 Sse#M#62 1 +139838 Nse#L#153 1 +205413 Sws#M#83 1 +119998 Swb#M#157 1 +119998 Swb#M#155 3 +119998 Sws#M#1234 10 +119998 Sws#M#163 5 +119998 Sws#M#1227 4 +119998 Swb#M#137 2 +119998 Sws#M#1230 5 +119998 Sws#M#83 3 +113092 Nso#65 1 +113092 Nso#108 2 +113092 Nso#1209 1 +113092 Nso#106 2 +46277 Sse#M#64 1 +31830 Nse#L#151 1 +31830 Nso#108 2 +31830 Nso#107 1 +31830 Nso#1209 16 +105637 Sse#M#62 1 +105637 Tg#1249 2 +138615 Nso#108 1 +138615 Nso#106 1 +95547 Nse#L#153 1 +95547 Nso#1209 1 +152837 Nso#106 1 +162021 Nso#65 1 +162021 Vg#h#1039 1 +162021 Nw#L#119 1 +162021 Nso#1209 1 +207228 Sws#M#163 1 +29154 Sse#M#169 1 +29154 Nso#106 1 +42483 Vg#h#1104 1 +11627 Nw#R#50 1 +11627 Nso#108 3 +11627 Nso#1209 2 +98298 Swb#M#154 1 +98298 Sws#M#1234 1 +98298 Sws#M#1230 5 +98298 Sws#M#83 1 +202585 Sse#1224 2 +154287 Nw#L#5 5 +154287 Nse#L#98 1 +154287 Nso#108 4 +154287 Nso#106 2 +154287 Nso#65 6 +98297 Tg#1251 1 +6365 Nse#L#150 1 +6365 Nw#L#160 1 +6365 Nso#108 4 +104129 Nso#65 1 +104129 Nse#L#150 1 +104129 Sse#M#75 1 +10401 Sse#M#14 1 +10401 Sse#M#75 1 +62477 Sse#M#63 1 +62479 Nse#L#152 1 +52410 Vg#oh#1070 161 +52410 Vg#h#1061 3 +52410 Vg#oh#1055 2 +52410 Vg#oh#1051 9 +52410 Vg#h#1039 1 +52410 Vg#o#1132 3 +139634 Nse#L#98 1 +139634 Nso#1209 1 +139634 Nso#106 1 +101849 Vg#h#1038 2 +101849 Vg#o#1132 1 +20648 Swb#M#156 2 +20648 Tg#1238 3 +20648 Sse#M#169 1 +20648 Nse#L#153 1 +20648 Nse#L#152 1 +20648 Nse#L#98 1 +20648 Nw#R#50 6 +20648 Nso#108 1 +20648 Nso#1209 7 +100730 Sse#M#62 1 +100730 Sse#M#63 1 +175418 Vg#h#1039 1 +200775 Sws#M#83 1 +186851 Vg#o#1160 2 +186851 Vg#oh#1055 5 +102384 Nso#1209 1 +102384 Sse#M#63 1 +102384 Sse#M#64 1 +102384 Sws#M#83 1 +164402 Sse#M#62 1 +233559 Sws#M#83 3 +134576 Sse#1224 1 +152729 Sse#M#63 1 +6819 Nse#L#153 1 +6819 Nse#L#98 1 +6819 Nso#108 1 +6819 Vg#o#1132 1 +113432 Nso#108 1 +175240 Vg#o#1124 4 +113435 Swb#M#154 37 +113435 Swb#M#155 55 +113435 Sws#M#1234 2 +113435 Sws#M#163 9 +102361 Nse#L#151 1 +102361 Nse#L#150 5 +1605 Nso#107 3 +1605 Nso#1209 1 +1605 Nso#106 4 +49406 Vg#o#1124 1 +49406 Vg#h#1061 1 +49406 Vg#oh#1070 5 +49406 Vg#h#1039 1 +49406 Vg#o#1132 5 +49406 Tg#1249 1 +62895 Vg#h#1104 1 +62895 Vg#o#1128 1 +62895 Vg#o#1132 3 +62895 Tg#1249 1 +99439 Sws#M#1227 1 +194264 Vg#h#1104 1 +194264 Vg#o#1128 1 +194264 Vg#h#1039 1 +223835 Nso#1209 1 +1966 Tg#1251 1 +145319 Tg#1252 2 +145319 Tg#1249 1 +145319 Tg#1238 2 +149597 Tg#1249 4 +149597 Vg#h#1061 1 +102768 Tg#1251 1 +102768 Nw#R#50 2 +118817 Swb#M#157 1 +118817 Sws#M#1227 28 +118817 Sws#M#163 4 +118817 Sws#M#1230 36 +118817 Sws#M#1234 8 +118817 Sws#M#83 26 +83466 Nso#107 1 +148629 Tg#1251 2 +180133 Vg#oh#1055 185 +2871 Nso#1209 2 +132338 Vg#h#1043 3 +137211 Sws#M#163 1 +137211 Swb#M#137 3 +137211 Sws#M#83 1 +160662 Sws#M#163 1 +160662 Sws#M#83 1 +113888 Sws#M#163 1 +137181 Tg#1251 1 +137181 Tg#1252 1 +26099 Tg#1238 4 +26099 Sws#M#163 12 +87543 Sws#M#83 1 +29808 Nso#1209 1 +138986 Sse#M#62 1 +138986 Sse#M#63 1 +138986 Sse#M#64 2 +138986 Nw#R#50 1 +112434 Vg#o#1124 1 +78015 Vg#o#1160 1 +78015 Tg#1238 4 +78015 Tg#1249 1 +29770 Nw#L#160 1 +29770 Tg#1251 1 +29770 Nso#1209 1 +29770 Nso#107 1 +108027 Vg#oh#1070 2 +108027 Vg#o#1132 4 +108027 Vg#o#1124 1 +143180 Tg#1252 4 +68830 Swb#M#157 3 +68830 Sws#M#163 5 +68830 Swb#M#137 12 +68830 Sws#M#83 1 +12488 Sse#M#14 2 +12488 Sse#M#63 1 +109290 Sse#M#62 3 +109290 Sse#M#63 1 +109290 Sse#M#64 2 +109290 Nse#L#150 1 +114305 Nso#108 1 +32831 Swb#M#155 1 +108457 Tg#1251 1 +108457 Tg#1252 1 +67688 Nso#1209 1 +5508 Nso#108 1 +5508 Nso#107 1 +5508 Nso#1209 3 +185234 Vg#o#1124 1 +230366 Swb#M#155 1 +230366 Sws#M#1234 1 +230366 Sws#M#163 1 +230366 Sws#M#1227 1 +187087 Vg#o#1124 12 +187087 Vg#h#1043 1 +193148 Vg#o#1124 1 +1547 Sse#M#14 4 +1547 Swb#M#154 1 +1540 Sse#M#62 1 +100017 Nso#65 1 +132691 Vg#o#1124 27 +11113 Swb#M#156 1 +26704 Vg#o#1132 3 +30334 Tg#1251 1 +136616 Sws#M#1234 1 +156535 Sws#M#1234 2 +156535 Swb#M#156 1 +110796 Swb#M#154 2 +110796 Sse#1224 1 +110796 Sws#M#163 1 +31573 Vg#oh#1051 1 +31573 Vg#o#1132 1 +31573 Vg#h#1043 4 +212772 Nso#65 1 +212772 Nso#1209 3 +143237 Swb#M#154 1 +143237 Nw#R#50 4 +143237 Sws#M#83 1 +142753 Nso#1209 1 +110799 Vg#h#1104 1 +111310 Vg#h#1061 1 +8028 Nse#L#153 1 +47332 Sse#M#63 2 +132063 Sse#1224 1 +112746 Nso#108 1 +8026 Swb#M#156 1 +8026 Swb#M#154 2 +8026 Sse#M#14 1 +8026 Sws#M#163 1 +8026 Sse#M#63 1 +8026 Sse#M#64 1 +212484 Nso#65 1 +212484 Nse#L#151 2 +212484 Nse#L#150 1 +212484 Nso#106 1 +212484 Nso#107 2 +100137 Nw#L#5 5 +100137 Nse#L#151 1 +100137 Nse#L#150 3 +100137 Nse#L#152 1 +100137 Nw#L#160 1 +100137 Nso#108 1 +100137 Tg#1249 1 +100137 Nso#1209 2 +103701 Nso#65 4 +103701 Nso#107 3 +103701 Nso#108 3 +103701 Nso#106 3 +50403 Nse#L#151 1 +50403 Nso#106 1 +50403 Nw#R#189 1 +7561 Sse#M#14 4 +7561 Sse#M#62 5 +213171 Nso#106 1 +213171 Nso#1209 20 +103709 Sws#M#163 3 +103709 Sws#M#1234 2 +103709 Swb#M#155 6 +143436 Nso#65 1 +143436 Nso#1209 1 +208976 Nso#106 2 +122531 Swb#M#156 3 +122531 Swb#M#157 13 +122531 Swb#M#155 2 +122531 Sws#M#163 1 +122531 Sws#M#1227 2 +122531 Sws#M#83 2 +136924 Sse#M#63 1 +136924 Swb#M#154 1 +130403 Vg#o#1132 4 +130403 Vg#o#1124 2 +230719 Vg#o#1124 12 +132451 Vg#o#1124 13 +132662 Nso#1209 1 +22641 Tg#1238 14 +22641 Tg#1249 4 +111555 Vg#o#1124 1 +109277 Sws#M#163 1 +144719 Tg#1252 1 +11366 Vg#o#1128 3 +206038 Nso#1209 2 +16195 Vg#o#1124 4 +197891 Vg#o#1124 5 +159087 Sse#M#75 1 +230405 Sse#M#63 1 +230405 Tg#1251 1 +105625 Nso#108 1 +105625 Nso#107 1 +105625 Nso#106 1 +230409 Vg#o#1124 4 +131268 Vg#o#1124 2 +154672 Sws#M#163 1 +222882 Nso#65 2 +222882 Nso#1209 1 +222882 Nso#107 5 +222882 Nso#106 4 +137295 Sws#M#83 1 +214430 Nso#106 1 +65677 Nse#L#152 1 +65677 Nso#107 3 +150575 Vg#o#1124 3 +65675 Nso#107 1 +65674 Nso#107 1 +65695 Nso#106 1 +103056 Nse#L#98 3 +65691 Nso#107 2 +65691 Sws#M#163 1 +65691 Nso#1209 3 +65693 Nw#L#119 1 +65693 Nso#108 5 +65693 Nso#106 4 +100923 Vg#h#1039 1 +100923 Vg#h#1038 1 +100923 Vg#h#1061 7 +51823 Nw#L#5 2 +51823 Sws#M#83 4 +16232 Vg#o#1124 2 +16232 Vg#o#1128 2 +16232 Vg#h#1061 13 +16232 Vg#oh#1070 29 +16232 Vg#o#1132 10 +16232 Vg#h#1039 5 +2200 Vg#h#1038 11 +2200 Vg#o#1132 2 +98593 Sws#M#163 2 +98593 Sws#M#83 2 +154451 Swb#M#157 1 +154451 Sse#1224 1 +154451 Sws#M#83 1 +135161 Sse#M#63 1 +105024 Sws#M#163 1 +16054 Vg#h#1043 1 +16054 Vg#o#1128 1 +16054 Vg#o#1153 1 +16054 Vg#o#1132 3 +220214 Nse#L#152 1 +166220 Nso#65 1 +166220 Nse#L#151 1 +166220 Nso#106 2 +46201 Nse#L#150 1 +46201 Nse#L#152 2 +46201 Sws#M#163 1 +46201 Sse#M#64 1 +46201 Nso#1209 1 +19731 Nso#107 1 +19731 Nso#1209 16 +99888 Tg#1249 1 +205400 Nse#L#151 1 +105678 Swb#M#155 3 +185648 Vg#h#1039 1 +118311 Sse#M#64 1 +118311 Sws#M#1230 1 +85294 Sse#M#14 1 +85294 Sse#M#169 1 +153827 Vg#h#1104 1 +153827 Tg#1238 1 +153827 Vg#h#1043 5 +153827 Vg#oh#1055 12 +153827 Vg#o#1132 3 +153827 Tg#1249 2 +154294 Nw#R#189 2 +154294 Swb#M#156 3 +154294 Swb#M#155 1 +42325 Sse#M#75 1 +225963 Sws#M#1230 3 +225963 Sse#M#62 1 +225963 Sws#M#83 2 +169004 Vg#o#1124 1 +70228 Sse#M#63 1 +105514 Vg#o#1128 1 +105514 Vg#h#1061 3 +135410 Tg#1249 1 +135410 Tg#1238 3 +10167 Sws#M#163 2 +48237 Vg#h#1104 1 +194302 Sws#M#83 8 +172363 Nso#65 1 +172363 Nse#L#150 1 +46681 Vg#h#1104 1 +46681 Tg#1251 2 +46681 Tg#1249 2 +3536 Nw#L#160 1 +3536 Sws#M#1227 1 +149693 Tg#1249 1 +149693 Tg#1251 1 +190311 Vg#o#1124 2 +43095 Swb#M#155 2 +222765 Nse#L#150 1 +12743 Tg#1238 3 +12743 Nso#108 1 +12743 Tg#1249 1 +80396 Sse#M#14 1 +80396 Nse#L#152 1 +80396 Nse#L#98 2 +128706 Swb#M#154 1 +128706 Nso#106 1 +128706 Nso#1209 5 +128706 Nso#108 4 +128706 Nso#65 1 +128706 Swb#M#137 1 +128706 Sse#M#62 1 +128706 Sse#M#64 1 +8229 Swb#M#154 5 +8229 Swb#M#155 7 +8229 Sws#M#163 3 +8229 Sws#M#1230 4 +172612 Sse#M#62 1 +172612 Sse#M#63 1 +172612 Sse#M#64 1 +102556 Sse#M#75 1 +41211 Nso#108 1 +41211 Nso#1209 1 +41211 Nso#107 2 +41211 Nso#106 1 +159953 Swb#M#156 20 +159953 Swb#M#154 5 +159956 Nse#L#98 2 +159956 Nso#107 1 +18546 Nw#L#5 1 +18546 Sws#M#1234 1 +179615 Vg#h#1038 1 +179615 Vg#o#1153 1 +14081 Vg#oh#1070 1 +14081 Vg#o#1132 5 +14081 Vg#h#1061 1 +9760 Swb#M#155 1 +9760 Vg#h#1104 2 +9760 Tg#1251 2 +9760 Vg#o#1124 1 +9760 Vg#o#1132 2 +95195 Tg#1238 1 +99158 Nw#R#189 3 +99158 Nw#L#119 2 +99158 Nse#L#153 1 +99158 Nso#108 1 +99409 Sws#M#163 1 +99409 Swb#M#157 1 +148392 Tg#1249 1 +211447 Nso#65 5 +211447 Nse#L#151 1 +211447 Nse#L#152 1 +211447 Nso#1209 1 +1975 Vg#h#1104 4 +1975 Tg#1251 1 +87292 Vg#h#1104 1 +87292 Vg#o#1132 1 +87292 Vg#h#1061 1 +98930 Vg#o#1128 11 +146291 Sws#M#163 1 +199567 Vg#o#1124 8 +78268 Tg#1249 1 +198568 Vg#o#1124 2 +112201 Tg#1251 1 +112201 Vg#h#1061 1 +112201 Vg#oh#1070 2 +198563 Vg#oh#1051 3 +146854 Nw#L#119 1 +108024 Nw#L#5 1 +108024 Nse#L#150 1 +108024 Nso#108 1 +108024 Nso#107 2 +108024 Nso#106 2 +108024 Nso#65 2 +108024 Nso#1209 1 +132329 Nso#1209 1 +232752 Sws#M#163 1 +2842 Sse#M#62 3 +2842 Sse#M#63 3 +2843 Sse#M#14 1 +2840 Nso#65 1 +2840 Nso#107 1 +10212 Vg#h#1104 1 +1673 Swb#M#156 2 +1673 Sws#M#163 1 +219686 Nso#1209 2 +191839 Nso#108 6 +33833 Vg#o#1124 1 +33833 Vg#h#1043 1 +33833 Vg#h#1061 1 +33833 Vg#h#1039 1 +33833 Vg#o#1132 6 +174207 Sse#M#62 1 +7056 Nw#L#5 4 +7056 Nse#L#98 1 +7056 Nw#R#50 2 +7056 Sws#M#163 2 +7056 Nso#108 3 +7056 Nw#R#189 10 +221168 Nso#107 5 +221168 Nso#65 7 +175339 Nso#107 1 +107605 Vg#o#1132 2 +107605 Vg#h#1039 1 +33987 Tg#1252 1 +33987 Vg#oh#1070 12 +33987 Vg#oh#1051 1 +33987 Vg#o#1132 1 +33987 Tg#1249 2 +33987 Vg#o#1153 1 +33984 Tg#1238 1 +33984 Vg#o#1128 2 +33984 Vg#o#1160 1 +33984 Vg#oh#1070 4 +33984 Vg#h#1104 1 +33984 Vg#oh#1055 7 +33984 Vg#oh#1051 8 +33984 Vg#h#1039 13 +33984 Vg#o#1132 1 +33984 Tg#1249 1 +112113 Nso#107 1 +78001 Tg#1249 2 +182959 Sse#1224 1 +182959 Sws#M#1234 1 +182959 Swb#M#154 1 +137447 Sws#M#1227 1 +30983 Nw#L#5 1 +30983 Nse#L#150 1 +34403 Nso#1209 1 +99057 Nse#L#150 1 +99057 Nse#L#98 1 +167995 Sse#M#62 1 +167995 Sse#M#75 1 +146921 Nw#L#160 1 +161631 Sws#M#163 1 +108110 Nse#L#152 1 +108110 Sse#M#75 1 +18361 Vg#o#1128 2 +18361 Vg#oh#1070 24 +18361 Vg#oh#1055 148 +18361 Vg#oh#1051 227 +18361 Vg#o#1132 4 +222209 Nse#L#151 2 +222209 Nse#L#150 1 +222209 Nse#L#153 1 +222209 Sse#M#75 1 +222209 Nso#1209 1 +159771 Swb#M#155 1 +11103 Sse#M#62 1 +217998 Nse#L#151 1 +217998 Nso#107 2 +217998 Nso#106 1 +142598 Nso#107 1 +142598 Nso#106 2 +77851 Sws#M#1230 1 +31745 Vg#h#1104 1 +171614 Nso#65 1 +13855 Vg#oh#1055 4 +13855 Vg#o#1132 1 +13855 Vg#h#1061 2 +143240 Sse#M#62 1 +143240 Sse#M#63 1 +67784 Swb#M#155 1 +107272 Vg#h#1043 14 +107272 Vg#o#1128 1 +107272 Vg#h#1061 3 +107272 Vg#oh#1070 2 +107272 Vg#h#1039 4 +107272 Vg#o#1132 3 +51452 Nso#107 1 +103772 Vg#h#1104 1 +103772 Vg#oh#1070 1 +103772 Vg#oh#1055 1 +103772 Vg#o#1132 2 +91225 Tg#1252 3 +77721 Nse#L#98 1 +77721 Sse#M#169 1 +151164 Sse#M#62 1 +151164 Sws#M#83 1 +225329 Nso#108 1 +225329 Nse#L#153 1 +217200 Nse#L#98 1 +101831 Nso#106 1 +106893 Nw#L#160 1 +22963 Nso#106 1 +136950 Nso#1209 1 +156732 Nse#L#150 1 +213181 Nso#1209 4 +149904 Tg#1249 1 +149904 Tg#1238 2 +63933 Vg#o#1132 2 +145762 Tg#1251 1 +47013 Nso#106 2 +23709 Vg#h#1061 7 +228860 Vg#o#1124 7 +76995 Tg#1238 1 +166172 Swb#M#155 1 +13989 Vg#h#1038 1 +13989 Vg#o#1132 10 +13989 Vg#h#1043 4 +13989 Vg#h#1104 1 +13989 Vg#o#1128 3 +13989 Vg#o#1160 1 +13989 Vg#oh#1070 179 +13989 Vg#oh#1055 139 +13989 Vg#oh#1051 136 +13989 Vg#h#1039 2 +96329 Nse#L#150 1 +96329 Sse#M#62 5 +96329 Sse#M#63 1 +96329 Sse#M#64 2 +96329 Sse#M#75 1 +130744 Vg#o#1124 5 +130744 Vg#o#1128 1 +130744 Vg#oh#1070 10 +130744 Vg#o#1132 1 +130744 Vg#o#1153 30 +11353 Nso#106 2 +89122 Sws#M#1234 1 +89122 Sse#M#63 1 +89122 Sse#M#75 1 +26077 Nso#106 1 +228462 Swb#M#155 6 +228462 Sws#M#163 3 +228462 Sws#M#1234 3 +228462 Swb#M#137 2 +228462 Sws#M#83 1 +223314 Nse#L#151 1 +223314 Nse#L#153 1 +60324 Nse#L#151 1 +60325 Nso#65 1 +150102 Tg#1249 1 +19611 Vg#o#1132 1 +19611 Vg#o#1128 1 +19611 Vg#h#1061 1 +131250 Vg#h#1061 1 +149878 Sws#M#1234 2 +149878 Swb#M#156 2 +110361 Vg#h#1061 5 +110361 Vg#oh#1070 1 +110361 Vg#h#1039 2 +110361 Vg#o#1132 7 +154598 Vg#h#1039 1 +154598 Vg#h#1061 1 +130306 Sse#M#169 1 +130306 Sse#M#62 1 +60815 Nse#L#98 2 +196134 Vg#o#1124 2 +55316 Nse#L#151 1 +55316 Nso#107 1 +151211 Swb#M#154 5 +151211 Swb#M#155 4 +151211 Sws#M#83 1 +215917 Nse#L#153 1 +187655 Vg#o#1124 9 +135577 Tg#1238 1 +26888 Vg#o#1128 1 +26888 Vg#o#1132 1 +26888 Vg#h#1061 1 +153036 Nse#L#151 2 +153036 Nso#106 1 +153036 Nso#65 1 +153036 Nso#1209 5 +65686 Nso#107 4 +14001 Vg#h#1104 1 +14001 Vg#o#1128 1 +14001 Vg#o#1124 8 +14001 Vg#oh#1070 30 +14001 Vg#oh#1055 28 +14001 Vg#oh#1051 18 +14001 Vg#h#1043 7 +14001 Vg#o#1132 5 +14001 Vg#o#1153 1 +184671 Vg#h#1039 4 +65682 Nso#107 1 +65682 Nw#L#160 3 +65682 Nso#108 3 +65682 Nso#106 4 +78365 Sse#M#14 1 +78365 Nse#L#98 1 +78365 Sse#M#62 2 +78365 Sse#M#63 1 +101771 Vg#h#1038 1 +101771 Vg#h#1043 1 +23926 Tg#1238 1 +23926 Tg#1249 9 +227245 Tg#1251 1 +192192 Swb#M#137 1 +192192 Sws#M#83 1 +204874 Nso#65 1 +204874 Sse#M#14 1 +204874 Nse#L#150 2 +204874 Nse#L#153 1 +105037 Vg#h#1043 9 +105037 Vg#h#1061 13 +105037 Vg#o#1128 1 +105037 Vg#h#1038 1 +220599 Nse#L#152 1 +21881 Vg#o#1124 2 +21881 Vg#o#1132 1 +84198 Vg#oh#1051 1 +81160 Vg#o#1124 2 +81160 Vg#oh#1070 34 +81160 Vg#oh#1055 55 +81160 Vg#oh#1051 46 +81160 Vg#o#1132 2 +191542 Vg#o#1160 1 +191542 Vg#h#1104 2 +69031 Nso#106 1 +101990 Sws#M#163 2 +101991 Vg#h#1039 4 +101991 Vg#h#1061 4 +104581 Vg#h#1038 2 +104581 Vg#h#1104 6 +104581 Vg#o#1124 1 +104581 Vg#o#1128 4 +104581 Vg#h#1061 8 +104581 Vg#o#1160 1 +104581 Vg#oh#1051 1 +104581 Vg#h#1043 2 +104581 Vg#o#1132 2 +25562 Vg#o#1132 1 +191786 Vg#o#1124 1 +191786 Vg#h#1043 1 +106258 Vg#o#1153 2 +106258 Vg#o#1132 3 +106258 Vg#o#1128 1 +102964 Nso#1209 3 +148429 Swb#M#156 1 +148429 Sws#M#1234 2 +148429 Swb#M#155 1 +149113 Sws#M#1234 1 +149113 Sws#M#163 1 +102418 Nso#108 2 +102418 Nso#1209 1 +102418 Nso#107 1 +102418 Nso#106 1 +90275 Swb#M#154 14 +152521 Vg#h#1061 1 +167279 Nso#1209 1 +17199 Nso#1209 1 +141661 Tg#1238 3 +162576 Vg#o#1160 1 +162576 Vg#o#1124 5 +2722 Nso#108 3 +155885 Nso#106 1 +153214 Nso#1209 1 +1807 Nse#L#153 1 +1807 Nso#108 11 +1807 Nso#107 9 +1807 Nso#106 3 +1807 Nso#65 14 +1807 Nso#1209 6 +112328 Vg#h#1104 2 +112328 Vg#h#1061 1 +146066 Swb#M#155 2 +146066 Sws#M#1234 1 +146066 Sws#M#1227 37 +146066 Sws#M#1230 4 +16915 Vg#o#1132 1 +16915 Vg#o#1128 1 +63782 Nso#108 1 +63785 Sse#M#169 1 +63785 Nse#L#153 1 +63784 Sse#M#62 1 +199379 Vg#o#1153 2 +199379 Vg#o#1124 2 +45529 Vg#o#1132 1 +113524 Nso#108 3 +128717 Nse#L#150 4 +215478 Sws#M#1234 1 +215478 Nw#L#160 1 +167417 Sse#M#75 1 +45522 Nse#L#152 2 +69013 Nso#1209 1 +69013 Nw#R#50 2 +45741 Sws#M#1227 1 +45741 Sws#M#1230 1 +45741 Sws#M#1234 6 +45741 Sws#M#83 2 +113452 Nse#L#151 1 +113452 Nse#L#150 1 +113452 Nso#108 5 +113452 Nso#1209 1 +44695 Vg#o#1132 1 +99031 Nse#L#150 1 +137054 Nse#L#152 1 +137054 Nso#108 2 +137054 Vg#o#1132 1 +137054 Nso#107 1 +137054 Nso#65 1 +137054 Nso#1209 4 +99414 Vg#o#1160 1 +99414 Vg#oh#1070 1 +193893 Nse#L#152 1 +136522 Sws#M#1227 2 +136522 Nso#1209 1 +172220 Vg#o#1124 9 +172220 Vg#h#1039 1 +172220 Vg#o#1132 1 +39712 Nse#L#150 3 +99097 Vg#h#1104 1 +152182 Swb#M#137 2 +211927 Vg#h#1061 1 +179045 Nse#L#153 1 +173598 Tg#1238 8 +173598 Tg#1252 1 +173598 Tg#1249 5 +163423 Nw#L#119 3 +163423 Sws#M#83 1 +2851 Sws#M#1230 1 +2851 Sse#M#63 1 +2853 Sws#M#83 1 +191820 Vg#h#1038 1 +136481 Sse#M#75 2 +136481 Sse#M#169 5 +129392 Sse#M#169 1 +1701 Sse#M#63 1 +129409 Nso#65 2 +129409 Nso#107 1 +129409 Nso#106 1 +174747 Vg#oh#1070 23 +174747 Vg#oh#1051 8 +156603 Nse#L#151 2 +156603 Nse#L#150 1 +41153 Nso#108 4 +41153 Nso#106 1 +202289 Nw#L#160 1 +175182 Vg#h#1043 1 +175182 Vg#h#1061 1 +175182 Vg#oh#1055 1 +169013 Vg#o#1124 6 +137302 Nso#65 2 +137302 Nso#1209 1 +210111 Nso#107 5 +30991 Sse#M#14 1 +30991 Nso#108 1 +30991 Nso#1209 1 +111151 Sws#M#1230 1 +111151 Nso#1209 2 +189163 Vg#oh#1055 14 +83155 Vg#h#1043 2 +83155 Vg#h#1061 1 +26724 Sse#M#64 1 +26724 Sse#M#75 1 +26724 Nso#108 2 +26724 Sse#M#63 2 +26724 Nso#1209 23 +226151 Nse#L#151 4 +226151 Nse#L#150 3 +226151 Nse#L#153 1 +225539 Nso#106 1 +225539 Nw#R#50 1 +143525 Nw#R#50 3 +90166 Nse#L#150 1 +225240 Nse#L#98 3 +114856 Tg#1238 1 +114856 Tg#1249 2 +174109 Vg#h#1043 14 +207653 Nw#L#119 1 +207653 Nse#L#152 1 +137235 Nso#106 1 +137235 Nso#1209 4 +108876 Swb#M#154 15 +108876 Sws#M#1234 1 +175763 Vg#o#1124 6 +111025 Nw#L#160 1 +156513 Swb#M#154 1 +217505 Nse#L#98 1 +217505 Nso#106 1 +217505 Nse#L#150 1 +131066 Vg#o#1124 9 +175967 Vg#oh#1070 2 +175967 Tg#1238 1 +175967 Vg#oh#1055 1 +175967 Vg#o#1132 3 +175967 Vg#o#1153 1 +175967 Vg#h#1043 1 +199804 Nw#L#160 1 +188174 Nse#L#151 1 +66950 Nse#L#98 3 +197250 Vg#o#1132 1 +197250 Vg#o#1124 1 +67799 Sse#M#62 1 +161159 Nw#L#160 1 +161159 Nw#R#50 3 +156770 Sse#1224 2 +2488 Nso#108 1 +133808 Nse#L#153 1 +133808 Nse#L#152 1 +221327 Tg#1252 1 +156779 Sse#1224 2 +26613 Nso#108 1 +136126 Nso#1209 1 +111978 Vg#h#1061 1 +111978 Nso#1209 2 +111978 Nso#107 1 +111978 Nso#106 1 +170060 Swb#M#157 1 +170060 Swb#M#155 2 +170060 Sse#M#62 3 +170060 Sse#M#75 2 +15143 Vg#o#1124 2 +189151 Sws#M#1234 1 +189151 Nso#106 1 +189151 Nso#1209 1 +3802 Swb#M#154 2 +114509 Tg#1249 1 +43239 Nse#L#150 1 +136769 Sse#M#62 1 +136769 Sse#M#63 1 +8190 Sws#M#1234 4 +8190 Swb#M#137 1 +8190 Sws#M#1230 1 +8190 Sws#M#83 1 +39143 Nw#L#119 1 +2868 Sse#M#63 1 +111483 Sse#M#62 1 +207833 Tg#1249 1 +88848 Sse#M#63 1 +88848 Sse#M#64 2 +150714 Sse#M#62 1 +224117 Nso#65 3 +224117 Nso#1209 1 +145151 Tg#1251 1 +145151 Vg#oh#1051 2 +227601 Nw#L#119 2 +227601 Nse#L#150 1 +227601 Nse#L#153 3 +227601 Nso#106 1 +227601 Nw#R#189 13 +166688 Nse#L#153 1 +166688 Nse#L#98 1 +166688 Nse#L#152 2 +110192 Vg#o#1124 3 +110192 Vg#h#1061 1 +110192 Vg#h#1039 1 +110192 Vg#o#1132 1 +32581 Nso#65 1 +32581 Nso#108 4 +32581 Nso#106 1 +13881 Sse#1224 1 +13881 Nse#L#152 1 +104242 Nse#L#150 1 +104242 Nso#1209 1 +86097 Nse#L#151 1 +86097 Nso#108 1 +204909 Sse#1224 1 +204909 Nse#L#152 1 +49893 Swb#M#155 1 +155778 Nw#L#119 2 +73937 Vg#o#1132 1 +16039 Vg#o#1124 1 +16039 Vg#h#1061 3 +16039 Vg#oh#1070 1 +16039 Vg#h#1038 4 +16039 Vg#o#1153 1 +16039 Vg#o#1132 1 +155524 Sse#M#62 1 +46444 Nse#L#151 1 +46444 Nse#L#150 1 +189445 Vg#o#1124 3 +189445 Vg#h#1043 1 +141274 Nso#65 2 +141274 Nse#L#151 1 +141274 Nso#1209 1 +141274 Nso#107 1 +110227 Vg#o#1124 1 +110227 Vg#h#1043 1 +110227 Vg#oh#1070 1 +73339 Nso#108 2 +73339 Nso#107 2 +4617 Swb#M#154 5 +4617 Sse#M#169 1 +4617 Nso#108 1 +4617 Nso#65 1 +4617 Nso#1209 3 +4617 Sse#M#64 1 +99547 Vg#h#1061 37 +101503 Nse#L#153 1 +205645 Nso#65 2 +205645 Nso#107 1 +131339 Nso#108 2 +104024 Nw#L#160 1 +192046 Vg#h#1104 1 +192046 Vg#o#1124 1 +57451 Nse#L#152 1 +81374 Nse#L#150 1 +162211 Vg#o#1124 2 +10837 Sse#M#14 2 +10836 Sse#M#14 3 +10836 Nse#L#152 1 +10836 Sse#M#62 5 +10836 Sse#M#63 1 +10836 Sse#M#75 1 +177731 Vg#o#1128 1 +10056 Tg#1238 2 +10056 Sws#M#163 1 +10056 Tg#1249 1 +153733 Sws#M#1227 1 +153733 Nw#R#50 1 +153733 Sws#M#163 7 +10839 Sse#M#14 1 +85388 Nw#L#5 6 +85388 Sws#M#163 1 +85388 Nw#L#160 11 +85388 Nw#R#50 8 +191556 Vg#h#1061 1 +222698 Sse#M#14 1 +222698 Nse#L#151 1 +222698 Nw#R#50 1 +222698 Nse#L#152 1 +63062 Nw#R#50 1 +154329 Nse#L#151 1 +154329 Nw#R#50 1 +154329 Nse#L#153 1 +154329 Nso#1209 1 +185622 Vg#h#1039 1 +21096 Vg#o#1124 3 +21096 Vg#o#1132 8 +32126 Vg#o#1132 1 +4503 Tg#1249 1 +4503 Nso#1209 1 +149101 Nse#L#98 1 +149101 Sws#M#1234 1 +149101 Sse#M#63 1 +192598 Vg#o#1124 2 +148295 Tg#1251 2 +27791 Vg#h#1043 17 +27791 Vg#o#1128 7 +27791 Vg#oh#1070 6 +27791 Vg#oh#1055 12 +27791 Vg#oh#1051 6 +27791 Vg#h#1061 3 +27791 Vg#h#1038 17 +27791 Vg#h#1039 23 +27791 Vg#o#1132 5 +27791 Vg#o#1153 5 +42308 Nse#L#153 1 +218058 Nse#L#151 1 +7546 Sse#M#14 3 +7546 Sse#M#169 1 +7546 Sse#M#62 7 +7546 Sse#M#63 1 +7546 Nw#R#50 1 +217947 Nso#107 1 +141905 Sse#M#14 1 +141905 Swb#M#155 1 +200746 Swb#M#155 1 +200746 Sse#M#64 1 +200746 Sse#M#62 2 +200746 Nso#107 1 +113199 Nso#108 1 +113624 Nso#108 2 +113626 Nso#108 1 +113626 Nso#107 24 +113626 Nso#106 15 +113190 Nso#108 1 +113190 Nso#1209 1 +113190 Nso#106 1 +10699 Sse#M#14 1 +10699 Sse#M#62 1 +10699 Sse#M#63 1 +99395 Nso#1209 1 +93813 Sse#1224 1 +93813 Nse#L#152 1 +93813 Sse#M#62 1 +93813 Sse#M#63 1 +10680 Sse#M#62 1 +222747 Nso#107 2 +36808 Nse#L#150 1 +36808 Nse#L#98 1 +146972 Nso#1209 1 +146972 Sws#M#83 1 +2401 Sws#M#1227 2 +2401 Sws#M#83 14 +2401 Sws#M#1234 1 +2401 Sws#M#1230 4 +2401 Sse#M#62 1 +2401 Sse#M#64 3 +135899 Tg#1238 1 +48325 Vg#o#1132 2 +48325 Tg#1238 2 +48325 Tg#1251 3 +48325 Vg#h#1043 3 +48325 Vg#h#1061 6 +48325 Vg#oh#1070 7 +48325 Tg#1249 5 +48325 Vg#h#1039 5 +53827 Nso#106 1 +53827 Swb#M#154 14 +20456 Swb#M#157 1 +20456 Swb#M#155 1 +20456 Nw#L#119 5 +20456 Sws#M#163 1 +20456 Sws#M#83 2 +159977 Sse#M#62 1 +159977 Sse#M#75 1 +159977 Sws#M#83 3 +152083 Nso#65 1 +210246 Nso#65 2 +210246 Nw#L#119 1 +210246 Nso#107 1 +216666 Nso#107 1 +216666 Nse#L#150 1 +1957 Vg#h#1038 1 +152244 Nso#65 1 +152244 Nso#1209 1 +152244 Nso#106 1 +200639 Nso#106 1 +182857 Vg#oh#1070 1 +10794 Sse#M#14 1 +10794 Sse#M#169 1 +10794 Sse#M#62 1 +210997 Nse#L#152 1 +172792 Tg#1252 8 +169809 Tg#1238 1 +169809 Vg#oh#1070 5 +169809 Vg#oh#1055 1 +169809 Tg#1249 1 +39700 Vg#o#1124 4 +39700 Tg#1251 1 +39700 Vg#h#1043 2 +182398 Vg#h#1038 1 +182398 Vg#oh#1055 1 +182398 Vg#o#1124 1 +70208 Tg#1251 1 +70208 Nso#108 3 +148671 Tg#1251 1 +148676 Tg#1251 1 +133650 Tg#1249 1 +134438 Swb#M#156 1 +134438 Swb#M#154 4 +134438 Nso#1209 1 +134438 Sse#M#62 1 +110046 Vg#h#1061 1 +110046 Vg#oh#1051 1 +18983 Vg#o#1124 23 +67542 Sse#M#14 1 +67542 Sse#M#62 1 +88752 Nse#L#152 1 +9014 Sse#M#75 2 +6161 Nse#L#150 1 +6161 Nse#L#152 1 +6161 Nw#R#50 1 +6161 Sse#M#75 1 +6161 Nso#108 3 +6161 Nso#107 1 +6161 Nso#106 2 +6161 Nso#1209 1 +6161 Sse#M#62 1 +186650 Vg#h#1104 1 +186650 Vg#o#1124 11 +48079 Nso#65 1 +48079 Nso#1209 1 +129513 Sse#M#63 1 +27824 Nse#L#151 1 +27824 Nso#108 3 +125759 Swb#M#156 4 +125759 Sws#M#1230 2 +125759 Swb#M#154 73 +125759 Swb#M#155 11 +1710 Swb#M#156 1 +1710 Nse#L#151 1 +1710 Nso#1209 3 +1717 Sse#M#14 1 +1717 Nse#L#98 1 +1717 Sse#M#62 1 +1717 Nso#106 1 +143881 Vg#h#1038 1 +24022 Sse#M#63 1 +1719 Nse#L#152 3 +1719 Nse#L#151 1 +1719 Nse#L#150 1 +1718 Swb#M#154 1 +1718 Nso#107 4 +1718 Nso#108 6 +1718 Nso#106 5 +175192 Nse#L#150 1 +175192 Vg#o#1124 2 +175192 Vg#h#1039 1 +148705 Nse#L#152 2 +148705 Vg#h#1061 2 +137040 Nw#L#160 5 +137040 Nso#1209 1 +137040 Sws#M#83 1 +102469 Vg#o#1153 1 +102469 Vg#h#1038 63 +102469 Vg#h#1039 1 +102469 Vg#h#1043 2 +102469 Vg#h#1104 2 +102469 Vg#o#1132 1 +83542 Sse#M#64 3 +40523 Vg#o#1132 1 +40523 Vg#h#1061 5 +47490 Tg#1252 2 +47490 Tg#1251 2 +47490 Tg#1249 2 +15483 Vg#o#1153 1 +67797 Sse#M#62 1 +68865 Nso#65 1 +68865 Nw#L#5 1 +68865 Nso#1209 1 +2419 Sws#M#1227 1 +103111 Swb#M#154 18 +103111 Swb#M#155 5 +103111 Sws#M#163 1 +50218 Vg#o#1128 1 +50218 Vg#h#1061 1 +50218 Vg#h#1039 1 +50218 Vg#o#1132 5 +50218 Tg#1249 1 +95342 Sws#M#163 1 +95342 Sws#M#83 1 +169976 Sws#M#83 22 +212106 Vg#o#1124 1 +35786 Vg#o#1132 1 +35786 Vg#h#1043 3 +18746 Sws#M#83 3 +18745 Nw#L#119 1 +18745 Nse#L#150 1 +18745 Nse#L#153 1 +18745 Vg#o#1132 1 +18745 Sse#M#75 1 +216118 Nse#L#151 3 +216118 Nse#L#152 1 +216118 Nw#L#160 2 +216118 Nw#R#189 1 +216118 Nso#1209 1 +157367 Nse#L#152 1 +144617 Sws#M#1234 1 +144617 Sse#M#63 1 +114420 Tg#1238 8 +114420 Tg#1249 6 +66944 Nso#108 2 +66944 Nse#L#152 1 +126228 Sws#M#1234 4 +126228 Nse#L#152 1 +126228 Sws#M#163 4 +126228 Sws#M#1227 1 +126228 Sws#M#1230 2 +126228 Sws#M#83 7 +8051 Swb#M#155 3 +8051 Nse#L#151 1 +8051 Sws#M#163 1 +8051 Sse#M#62 1 +183592 Vg#h#1038 2 +6763 Tg#1238 1 +6763 Tg#1249 1 +6763 Nw#R#50 1 +23652 Tg#1238 2 +114223 Tg#1252 1 +109318 Vg#h#1061 3 +156763 Sse#1224 2 +207733 Nso#107 2 +207733 Nse#L#151 1 +207733 Nse#L#150 1 +207733 Nso#106 1 +207733 Nso#65 15 +207733 Nso#1209 9 +50458 Vg#oh#1055 3 +50458 Vg#o#1132 1 +50457 Vg#oh#1070 2 +50457 Vg#oh#1051 2 +50457 Vg#h#1104 8 +50457 Vg#o#1124 1 +38392 Nse#L#98 1 +219480 Nso#1209 1 +219480 Nse#L#152 1 +219640 Nso#107 2 +91082 Nso#106 2 +182970 Vg#h#1061 1 +106877 Sse#M#62 1 +66234 Nso#107 1 +66234 Nso#1209 1 +66234 Nso#108 1 +155199 Swb#M#154 2 +155199 Swb#M#155 5 +155199 Sse#M#63 1 +131435 Nse#L#151 1 +131435 Nse#L#150 1 +131435 Nse#L#153 1 +72455 Nse#L#98 1 +72455 Nse#L#151 1 +72455 Nse#L#152 2 +182626 Nse#L#152 1 +182626 Sse#M#62 1 +111580 Nso#1209 3 +111580 Nso#108 3 +111580 Nw#R#50 1 +111580 Nso#106 1 +90147 Nw#L#119 1 +197817 Vg#oh#1055 16 +7564 Sse#1224 1 +197123 Vg#o#1124 5 +100135 Vg#oh#1070 9 +100135 Vg#o#1124 1 +192261 Sse#M#64 1 +207827 Nso#1209 1 +191612 Vg#o#1124 6 +110161 Vg#h#1043 1 +110161 Vg#o#1124 1 +110161 Vg#o#1128 1 +110161 Vg#h#1061 3 +110161 Vg#oh#1070 2 +110161 Vg#h#1038 3 +110161 Vg#h#1039 5 +110161 Tg#1249 1 +145811 Tg#1251 1 +145811 Tg#1252 1 +223372 Nso#65 1 +223372 Nso#107 3 +223372 Nso#106 2 +103807 Swb#M#155 9 +103807 Sws#M#163 12 +103807 Sws#M#1234 8 +103807 Swb#M#137 10 +19323 Swb#M#156 1 +19323 Swb#M#155 2 +19323 Sws#M#163 2 +19323 Sws#M#1234 10 +19323 Swb#M#137 3 +7719 Sse#M#75 1 +4760 Nse#L#150 2 +4760 Nso#108 1 +187006 Vg#h#1104 6 +187006 Tg#1251 1 +187006 Vg#h#1061 2 +187006 Tg#1249 1 +187006 Vg#h#1039 7 +187006 Vg#o#1132 1 +188034 Vg#o#1160 2 +189521 Vg#o#1160 13 +189521 Vg#o#1132 2 +189521 Vg#o#1153 3 +77791 Tg#1249 1 +196210 Vg#o#1128 4 +49369 Vg#oh#1070 2 +49369 Vg#oh#1055 2 +49369 Vg#o#1132 2 +148072 Vg#o#1128 2 +148075 Tg#1251 2 +26459 Nw#L#5 2 +153904 Sse#M#75 2 +55342 Vg#o#1153 1 +109841 Vg#h#1043 1 +16023 Vg#o#1124 17 +16023 Vg#oh#1051 6 +16023 Vg#h#1039 1 +155204 Sse#M#63 1 +100912 Vg#o#1128 1 +100912 Vg#h#1043 1 +100912 Vg#h#1039 1 +100912 Vg#o#1160 1 +16202 Vg#o#1128 32 +16202 Vg#h#1061 3 +16202 Vg#h#1039 1 +16202 Vg#o#1132 1 +16202 Vg#o#1153 3 +27355 Vg#o#1124 1 +27355 Vg#h#1043 1 +27355 Tg#1249 1 +27355 Vg#oh#1055 7 +27355 Vg#oh#1051 2 +27355 Vg#o#1132 1 +77667 Tg#1249 1 +209576 Vg#h#1061 1 +88072 Nse#L#98 1 +88072 Nso#108 2 +187802 Vg#h#1039 1 +187802 Vg#oh#1070 1 +187802 Vg#oh#1055 1 +187802 Vg#o#1132 1 +187802 Vg#o#1124 1 +63607 Nso#107 3 +113559 Nso#108 2 +113559 Nso#1209 1 +55946 Nso#107 1 +193506 Vg#o#1124 34 +128429 Swb#M#156 1 +128429 Swb#M#154 7 +128429 Swb#M#155 1 +128429 Sse#M#64 2 +128429 Nso#106 1 +227914 Vg#h#1043 1 +227914 Vg#oh#1070 1 +227914 Vg#oh#1055 5 +227914 Vg#oh#1051 10 +155335 Nso#107 2 +159328 Sse#M#169 1 +16111 Sse#1224 1 +205078 Nso#65 1 +205078 Nw#L#5 1 +101263 Nse#L#152 1 +101263 Sse#M#62 1 +101263 Sse#M#63 2 +101263 Nw#R#50 1 +130699 Vg#h#1061 1 +130699 Vg#o#1124 6 +130699 Vg#h#1043 6 +101086 Tg#1249 1 +120203 Swb#M#156 5 +120203 Swb#M#157 1 +120203 Swb#M#154 8 +120203 Swb#M#155 4 +120203 Sws#M#163 38 +120203 Sws#M#1234 1 +120203 Swb#M#137 1 +120203 Sws#M#1230 2 +140868 Vg#h#1104 1 +140868 Vg#o#1160 1 +85393 Nw#L#119 1 +85393 Nw#L#160 2 +85393 Nso#106 1 +204581 Vg#h#1061 2 +148449 Nso#107 2 +148449 Nso#106 1 +175114 Vg#o#1124 8 +136346 Sws#M#163 1 +136346 Swb#M#137 1 +136346 Nso#106 1 +167256 Vg#o#1124 2 +167256 Vg#oh#1070 29 +167256 Vg#o#1132 3 +42372 Vg#oh#1051 1 +42372 Vg#o#1124 2 +97256 Nw#R#189 5 +97256 Nw#L#5 11 +195479 Nse#L#153 1 +195479 Nso#1209 1 +61870 Sse#M#64 1 +61870 Sws#M#83 3 +141399 Nso#65 1 +141399 Nso#108 2 +141399 Nso#1209 1 +141399 Nw#R#50 1 +79540 Sse#M#75 1 +90456 Swb#M#156 1 +90456 Swb#M#154 18 +26279 Tg#1249 1 +128374 Sse#M#63 1 +190369 Vg#h#1104 5 +190369 Vg#oh#1055 2 +190369 Vg#o#1124 21 +101880 Swb#M#155 1 +140565 Swb#M#137 6 +195947 Vg#h#1038 1 +195947 Tg#1238 1 +195947 Vg#h#1043 2 +195947 Tg#1249 2 +10688 Nse#L#151 1 +10688 Nse#L#153 1 +10688 Sse#M#63 2 +10689 Swb#M#154 1 +215025 Nso#107 1 +49628 Nw#R#189 1 +49628 Sse#M#64 1 +49628 Sws#M#83 4 +10686 Nse#L#98 1 +10686 Sse#M#64 1 +146034 Tg#1251 3 +173910 Vg#o#1124 1 +173910 Tg#1249 1 +173910 Vg#h#1104 1 +173910 Vg#h#1038 1 +102500 Swb#M#154 2 +102500 Swb#M#155 1 +102500 Sws#M#163 2 +102500 Sse#M#75 1 +199354 Vg#o#1124 2 +173611 Sws#M#1234 1 +173611 Nso#1209 1 +135881 Nso#108 1 +210619 Nse#L#98 1 +210619 Nse#L#151 1 +10750 Nse#L#150 1 +159436 Nso#1209 1 +159436 Nso#106 1 +10754 Nse#L#153 1 +113276 Nso#108 7 +113276 Nso#1209 2 +232669 Sse#M#62 1 +103662 Nw#R#50 1 +112905 Nso#108 1 +14059 Vg#h#1061 1 +45296 Vg#o#1132 1 +106698 Vg#h#1038 1 +1929 Vg#oh#1055 7 +1929 Tg#1251 1 +1929 Vg#h#1043 3 +83959 Nw#L#5 5 +83959 Nse#L#150 1 +83959 Nse#L#153 2 +83959 Nw#L#160 1 +83959 Sws#M#1230 1 +219107 Sws#M#163 1 +163334 Swb#M#154 4 +163334 Sse#M#75 1 +168811 Tg#1249 2 +164536 Sse#M#14 1 +164536 Sse#M#62 1 +154687 Sse#M#62 1 +54834 Vg#o#1128 1 +54834 Vg#o#1132 1 +70198 Vg#oh#1070 2 +70198 Vg#o#1124 2 +70198 Vg#o#1128 2 +44216 Nso#107 1 +112872 Sse#M#169 2 +112872 Sse#M#62 1 +102030 Swb#M#154 1 +179710 Nw#L#160 1 +179710 Sse#M#63 1 +179710 Nso#107 3 +157680 Nse#L#152 1 +207038 Sse#1224 1 +40043 Swb#M#156 1 +40043 Nw#L#119 1 +40043 Nse#L#153 1 +40043 Nse#L#152 1 +40043 Nse#L#98 1 +40043 Nso#108 3 +40043 Sse#M#62 1 +123462 Swb#M#156 2 +123462 Swb#M#155 2 +123462 Sws#M#1234 10 +123462 Sws#M#163 6 +123462 Sws#M#83 2 +58374 Nw#L#119 1 +58374 Nw#R#50 1 +9008 Sws#M#163 1 +9008 Sse#M#64 3 +9008 Sse#M#75 6 +37234 Sse#M#64 1 +37234 Sws#M#1230 1 +37234 Sws#M#83 1 +1728 Nse#L#150 1 +1728 Nso#1209 1 +1728 Nso#107 1 +1724 Sse#M#14 1 +1724 Sse#M#62 4 +1724 Sse#M#63 3 +216057 Nso#65 1 +216057 Nso#1209 8 +216057 Nso#106 1 +1720 Tg#1249 1 +1720 Sse#M#62 1 +1720 Tg#1252 1 +62691 Sws#M#83 2 +62692 Sws#M#1234 2 +62692 Sws#M#83 1 +62693 Sws#M#1227 7 +62693 Sws#M#1234 1 +62693 Tg#1238 1 +62693 Sws#M#1230 2 +62693 Sse#M#62 1 +62693 Sws#M#83 1 +78343 Tg#1249 1 +115129 Nw#L#119 2 +115129 Nw#L#160 2 +136786 Nso#65 1 +136786 Nso#1209 1 +205984 Nw#L#160 1 +147178 Tg#1251 1 +105982 Nso#1209 5 +137492 Vg#h#1104 4 +78569 Swb#M#154 14 +78569 Sws#M#163 1 +78569 Swb#M#137 1 +78569 Sws#M#83 1 +212281 Vg#h#1038 3 +138924 Sse#M#62 1 +138924 Sse#M#63 2 +14301 Sse#1224 1 +10678 Sse#1224 1 +202248 Sse#M#62 5 +202248 Sse#M#63 1 +222341 Nso#1209 1 +99616 Nse#L#150 1 +48487 Nso#106 1 +174162 Vg#oh#1070 2 +174166 Vg#o#1153 1 +143093 Nso#107 1 +135311 Tg#1238 1 +137789 Nso#1209 1 +137789 Nso#107 3 +137789 Nso#106 1 +38217 Swb#M#155 3 +78163 Tg#1249 2 +115051 Vg#h#1038 6 +115051 Vg#o#1132 2 +30376 Nso#1209 5 +114239 Sse#M#75 11 +178759 Vg#h#1043 1 +73897 Nso#108 1 +173357 Sse#M#63 1 +5655 Sws#M#1227 1 +5655 Sse#M#75 2 +5655 Sws#M#163 1 +5655 Sws#M#83 13 +5655 Sws#M#1234 6 +5655 Swb#M#137 4 +5655 Sws#M#1230 9 +209888 Nso#65 1 +209888 Nse#L#150 1 +50448 Vg#o#1160 1 +50448 Vg#oh#1070 9 +50448 Vg#o#1132 3 +50448 Vg#o#1153 17 +136962 Nw#L#160 1 +77816 Tg#1249 1 +77816 Tg#1238 1 +187114 Vg#oh#1055 6 +187114 Vg#o#1132 1 +187114 Vg#h#1061 1 +103744 Tg#1251 8 +103744 Tg#1249 1 +144777 Nw#L#5 1 +225192 Nso#1209 1 +1456 Nso#65 5 +1456 Nse#L#151 1 +1456 Nso#108 2 +1456 Nso#1209 3 +132198 Vg#h#1039 1 +132198 Vg#o#1124 12 +159606 Nso#1209 1 +114529 Tg#1238 9 +114529 Tg#1249 5 +154831 Swb#M#155 5 +147602 Nso#106 3 +147602 Nw#R#50 1 +196213 Vg#o#1124 1 +216941 Vg#h#1061 4 +185556 Vg#o#1160 1 +185556 Vg#o#1153 3 +133917 Nse#L#151 1 +86113 Sse#M#64 1 +70461 Vg#h#1104 1 +70461 Vg#oh#1055 72 +70461 Vg#o#1124 1 +70461 Vg#h#1043 2 +100450 Vg#oh#1070 64 +100450 Vg#h#1061 1 +100450 Vg#h#1039 2 +100450 Vg#o#1132 3 +155059 Tg#1238 1 +111350 Tg#1251 1 +234651 Tg#1251 1 +234651 Tg#1249 1 +130804 Vg#o#1153 1 +130804 Vg#h#1038 1 +130804 Vg#h#1104 1 +130804 Vg#o#1132 3 +130804 Vg#h#1043 1 +130804 Vg#oh#1055 1 +130804 Vg#h#1039 1 +95864 Vg#o#1128 3 +95864 Nw#R#189 1 +95864 Nw#R#50 1 +83558 Vg#h#1104 1 +73001 Swb#M#154 1 +73001 Swb#M#155 41 +110407 Nw#L#119 1 +110407 Nso#1209 1 +82283 Vg#o#1124 4 +82283 Vg#h#1104 1 +82283 Vg#oh#1055 3 +82283 Vg#oh#1051 31 +82283 Vg#o#1132 11 +114417 Nso#1209 7 +114417 Nso#108 2 +114417 Nso#106 1 +86075 Nse#L#153 1 +155239 Nso#108 1 +155239 Nso#107 1 +110535 Vg#h#1039 1 +110535 Vg#h#1104 1 +110535 Vg#o#1128 1 +110535 Vg#h#1061 1 +187332 Vg#o#1153 1 +187332 Vg#o#1124 8 +28110 Nse#L#151 1 +28110 Nse#L#150 2 +28110 Nse#L#153 1 +28110 Nse#L#98 1 +28110 Nso#108 2 +28110 Nso#1209 3 +39053 Nso#65 1 +201697 Tg#1238 10 +201697 Nso#1209 8 +22777 Nso#65 2 +22777 Nse#L#151 1 +22777 Nse#L#153 1 +22777 Nso#1209 4 +55854 Vg#h#1038 2 +56320 Vg#o#1128 1 +56320 Vg#h#1038 3 +180091 Vg#o#1124 9 +109632 Nw#L#5 1 +109632 Vg#h#1061 1 +42560 Vg#h#1104 5 +42560 Vg#h#1043 1 +42560 Vg#h#1061 2 +42560 Vg#o#1132 1 +147174 Nso#65 1 +147174 Swb#M#155 3 +147174 Nse#L#151 1 +147174 Nse#L#153 1 +161941 Nse#L#151 1 +100077 Swb#M#156 1 +100077 Sws#M#163 1 +100077 Sse#M#64 1 +100077 Sws#M#83 3 +97007 Sse#M#62 5 +97007 Sse#M#63 1 +97007 Sse#M#64 2 +95646 Nso#1209 1 +192067 Vg#o#1124 7 +5903 Swb#M#156 18 +5903 Swb#M#157 2 +5903 Swb#M#154 47 +5903 Swb#M#155 3 +5903 Sws#M#1227 16 +5903 Sws#M#163 102 +5903 Sws#M#83 17 +5903 Sws#M#1234 70 +5903 Swb#M#137 11 +5903 Sws#M#1230 4 +10810 Swb#M#157 1 +10810 Sse#M#14 5 +10810 Nse#L#152 1 +10810 Sse#M#62 2 +10810 Sse#M#63 2 +10810 Sse#M#64 1 +10810 Sws#M#83 2 +104358 Vg#h#1043 3 +137363 Nso#65 5 +137363 Nse#L#153 1 +137363 Nso#108 1 +137363 Nso#1209 1 +227450 Nso#1209 2 +206896 Nse#L#151 4 +206896 Nse#L#150 3 +206896 Nso#107 1 +206896 Nso#106 2 +206896 Nso#65 1 +206896 Nso#1209 13 +107946 Swb#M#155 1 +159337 Nse#L#98 3 +43810 Nw#R#50 1 +135632 Tg#1249 1 +135632 Tg#1238 1 +6901 Swb#M#155 2 +6901 Nw#L#119 1 +6901 Nw#R#50 1 +6901 Sws#M#163 2 +6901 Nso#107 1 +6901 Sse#M#75 1 +137367 Tg#1249 1 +137367 Tg#1251 4 +146638 Sws#M#1234 2 +146638 Sws#M#163 3 +146638 Swb#M#137 3 +146638 Sws#M#1230 2 +146638 Sws#M#83 2 +30714 Sws#M#1234 2 +98087 Vg#h#1104 1 +98087 Vg#o#1124 21 +187845 Vg#o#1124 2 +12533 Vg#oh#1055 1 +12533 Tg#1252 1 +2554 Nw#L#119 2 +105774 Swb#M#157 1 +105774 Swb#M#154 32 +105774 Swb#M#155 2 +105774 Swb#M#156 6 +105774 Sws#M#163 14 +105774 Sse#M#62 3 +105774 Sse#M#63 3 +201230 Nso#1209 2 +193730 Vg#h#1043 1 +193730 Vg#oh#1070 8 +193730 Vg#oh#1055 6 +193730 Vg#oh#1051 2 +193730 Vg#o#1153 2 +193730 Vg#o#1132 1 +99977 Vg#o#1153 1 +52379 Vg#h#1061 1 +52379 Vg#oh#1070 2 +52379 Vg#oh#1055 1 +52379 Vg#o#1132 2 +52379 Nso#1209 1 +220492 Nse#L#151 2 +220492 Nse#L#150 1 +220492 Nse#L#152 2 +121120 Nso#65 1 +121120 Nso#107 1 +121120 Nse#L#151 1 +121120 Nso#1209 2 +212457 Nso#65 2 +212457 Nso#1209 21 +200018 Nw#R#50 1 +20534 Vg#h#1061 1 +20534 Vg#o#1132 1 +112259 Sse#M#75 1 +46885 Tg#1251 1 +11370 Vg#oh#1055 3 +11370 Vg#oh#1051 7 +11370 Vg#o#1128 1 +93838 Nso#107 1 +93838 Nso#1209 1 +4136 Tg#1252 89 +4136 Tg#1238 1 +4136 Tg#1251 5 +4136 Tg#1249 1 +38834 Vg#h#1104 1 +38834 Vg#h#1043 1 +38834 Vg#o#1124 63 +190303 Vg#h#1039 1 +21213 Vg#h#1104 3 +21213 Vg#h#1039 2 +21213 Vg#o#1132 2 +230324 Swb#M#155 2 +58041 Vg#o#1132 2 +102511 Sse#M#64 1 +102513 Vg#o#1132 1 +102513 Vg#h#1104 2 +130005 Nso#65 1 +152799 Nso#65 2 +152799 Nw#L#5 2 +152799 Nse#L#150 6 +152799 Nse#L#153 1 +152799 Nso#1209 3 +152799 Nso#106 2 +1854 Swb#M#154 5 +1854 Nso#107 4 +1854 Nso#108 14 +1854 Nso#1209 1 +1854 Nso#106 5 +1857 Nso#65 10 +1857 Nse#L#153 1 +1857 Nso#108 5 +1857 Nso#1209 2 +1857 Nw#R#50 1 +2170 Vg#h#1104 1 +2170 Vg#h#1043 2 +2170 Vg#h#1038 89 +2170 Vg#o#1153 3 +101301 Nse#L#152 1 +101301 Nso#108 3 +119242 Nw#R#189 1 +119242 Swb#M#156 1 +119242 Swb#M#154 2 +119242 Sws#M#1234 1 +119242 Sws#M#1227 3 +119242 Sws#M#1230 1 +119242 Sws#M#83 3 +119242 Nw#R#50 1 +167133 Sws#M#1230 1 +167133 Sws#M#83 1 +204312 Nso#1209 8 +113263 Nso#108 1 +113263 Nse#L#150 1 +113262 Nso#108 5 +113262 Nso#107 1 +80993 Nso#65 1 +1588 Nso#108 1 +2603 Nse#L#153 1 +2603 Sws#M#1227 1 +2603 Nso#106 1 +49274 Vg#o#1124 1 +49274 Vg#h#1043 1 +49274 Vg#oh#1070 1 +49274 Vg#oh#1055 39 +49274 Vg#oh#1051 43 +49274 Vg#o#1132 1 +49274 Tg#1249 2 +190309 Vg#h#1043 8 +190309 Vg#oh#1055 311 +190309 Vg#oh#1051 1133 +106687 Vg#h#1104 1 +83963 Vg#h#1104 2 +83963 Vg#h#1038 2 +163322 Nw#L#119 1 +163322 Nse#L#153 1 +163322 Nse#L#152 1 +163322 Nw#L#160 2 +163322 Nw#R#50 1 +163322 Nso#108 2 +107464 Vg#h#1039 1 +107464 Vg#oh#1070 3 +107464 Vg#o#1132 1 +163328 Vg#h#1104 17 +163328 Vg#o#1124 3 +134102 Nso#65 1 +134102 Nso#108 1 +105224 Nse#L#98 2 +45604 Vg#h#1104 3 +45604 Vg#o#1132 1 +45604 Vg#o#1124 1 +45604 Vg#o#1128 1 +11920 Nso#1209 3 +141722 Nse#L#150 1 +141722 Nse#L#153 4 +141722 Nw#L#160 1 +179728 Nso#107 1 +34789 Vg#oh#1070 1 +34789 Vg#h#1104 1 +34789 Vg#o#1132 1 +41059 Swb#M#156 1 +41059 Sse#1224 1 +41059 Sse#M#14 5 +41059 Sse#M#62 2 +30146 Vg#o#1132 1 +30146 Swb#M#154 6 +112885 Tg#1249 1 +107485 Vg#o#1153 2 +107485 Vg#o#1132 1 +44489 Vg#h#1104 6 +44489 Vg#o#1124 1 +1732 Nse#L#151 1 +1732 Nse#L#150 3 +1732 Nse#L#153 3 +1732 Nse#L#152 2 +1732 Nw#R#50 1 +1732 Tg#1252 2 +137027 Nso#107 2 +137027 Nso#106 7 +137102 Nso#108 1 +78354 Tg#1249 1 +71553 Nw#R#50 1 +59998 Nso#1209 2 +15156 Vg#o#1132 2 +112738 Nse#L#98 3 +112738 Nso#108 4 +112738 Nso#1209 1 +112738 Nso#107 1 +112738 Nso#106 1 +202096 Nse#L#150 1 +202096 Nso#108 1 +202096 Nso#107 1 +147162 Nso#1209 2 +136770 Sse#M#62 1 +7088 Nw#R#189 1 +7088 Vg#h#1104 1 +7088 Nw#L#160 1 +7088 Nw#R#50 2 +129297 Tg#1249 1 +14314 Sse#1224 3 +156900 Nso#108 1 +156900 Nso#1209 1 +156900 Nso#106 5 +211730 Nw#L#119 3 +168486 Sws#M#1230 1 +168486 Sse#M#62 2 +168486 Sws#M#83 2 +164097 Nw#L#5 2 +164097 Nso#107 1 +164097 Vg#h#1043 39 +164097 Vg#o#1128 1 +164097 Vg#o#1132 6 +164097 Nso#106 1 +66927 Nse#L#150 1 +171541 Sse#M#62 1 +131988 Swb#M#156 2 +131988 Swb#M#154 1 +131988 Swb#M#157 13 +131988 Sse#M#75 7 +131988 Sse#M#62 3 +131988 Sse#M#63 1 +131988 Sse#M#64 5 +131988 Sws#M#83 1 +173359 Tg#1252 1 +173359 Tg#1251 1 +18998 Sws#M#163 1 +18998 Nw#R#50 1 +59347 Nw#L#119 1 +5645 Sws#M#1227 1 +5645 Sse#M#75 4 +5645 Sws#M#83 4 +5645 Sse#M#63 1 +5645 Sse#M#64 1 +185339 Vg#o#1153 1 +185339 Vg#oh#1070 1 +143442 Tg#1249 1 +143442 Tg#1238 3 +225365 Nso#1209 4 +222887 Nse#L#98 2 +222887 Nse#L#151 1 +222887 Nse#L#152 1 +129580 Sse#M#63 1 +132391 Vg#o#1124 1 +162895 Vg#h#1061 1 +162895 Vg#oh#1070 6 +162895 Vg#o#1153 3 +162895 Vg#o#1132 3 +68844 Vg#h#1043 5 +68844 Tg#1251 3 +68844 Vg#o#1124 10 +68844 Vg#o#1160 3 +68844 Vg#oh#1070 1 +68844 Vg#h#1061 5 +68844 Vg#oh#1051 1 +68844 Vg#h#1039 8 +68844 Vg#o#1132 12 +68844 Tg#1249 1 +1443 Swb#M#154 83 +137482 Vg#oh#1070 3 +137482 Vg#o#1132 1 +137482 Vg#oh#1051 2 +156416 Swb#M#156 1 +156416 Vg#oh#1055 1 +156416 Sse#M#62 2 +156416 Sse#M#63 1 +79316 Sws#M#163 2 +50491 Nso#108 1 +50491 Nso#106 2 +228825 Nso#107 2 +136359 Sse#M#169 1 +136359 Sse#M#62 1 +136359 Sse#M#64 1 +206971 Nso#1209 5 +150817 Sws#M#1234 7 +150817 Swb#M#155 7 +114396 Nw#L#160 1 +114396 Nso#108 12 +114396 Nso#106 2 +114390 Nso#108 1 +114390 Nso#1209 9 +19521 Vg#h#1104 1 +19521 Tg#1251 1 +19521 Vg#o#1128 3 +19521 Vg#h#1043 1 +19521 Vg#h#1061 1 +19521 Vg#oh#1070 4 +19521 Vg#h#1039 1 +19521 Vg#o#1132 2 +111722 Sse#M#64 1 +101158 Swb#M#154 2 +126685 Vg#h#1061 1 +126685 Nse#L#153 1 +131723 Nw#L#119 1 +131723 Nse#L#150 3 +131723 Nse#L#151 1 +131723 Nse#L#153 1 +131723 Nse#L#152 1 +131723 Nse#L#98 1 +131723 Nso#108 8 +131723 Nso#107 2 +131723 Nso#106 4 +131723 Nso#65 7 +131723 Nso#1209 3 +131723 Sse#M#62 1 +100119 Vg#o#1153 1 +145830 Tg#1251 1 +32841 Nso#106 1 +32841 Nso#1209 2 +7586 Sse#M#14 2 +7586 Swb#M#157 1 +7587 Sse#M#14 1 +7587 Swb#M#137 1 +7587 Sse#M#62 1 +7587 Sse#M#64 1 +100440 Vg#o#1128 19 +100440 Vg#h#1061 1 +234313 Vg#o#1124 3 +187038 Vg#o#1153 1 +187038 Tg#1249 1 +174445 Nse#L#152 1 +174445 Nso#106 1 +174445 Nso#1209 1 +192164 Vg#h#1043 1 +229483 Nso#106 1 +229483 Swb#M#154 1 +78916 Swb#M#154 3 +78916 Swb#M#155 2 +78916 Nse#L#150 1 +78916 Sws#M#163 1 +78916 Nso#108 2 +78916 Nw#R#50 2 +78912 Sse#M#14 1 +78912 Sse#M#64 1 +96386 Sse#M#63 1 +96386 Sse#M#75 1 +192107 Nw#R#189 3 +192107 Sws#M#83 2 +167951 Vg#h#1061 1 +132726 Sse#M#64 1 +132726 Nse#L#153 1 +12523 Vg#h#1104 4 +69811 Sws#M#83 1 +195602 Vg#h#1039 1 +195602 Vg#o#1160 1 +181469 Nso#1209 3 +181466 Tg#1238 4 +181466 Vg#h#1043 1 +181466 Vg#o#1132 1 +220629 Nso#106 1 +113984 Nso#108 2 +222530 Nso#106 1 +230549 Nse#L#153 1 +9675 Sws#M#83 1 +111450 Sse#M#62 1 +111450 Sse#M#64 1 +111450 Sse#M#75 1 +111450 Nse#L#152 1 +161200 Swb#M#155 1 +96014 Swb#M#157 6 +96014 Sse#M#75 1 +96014 Sws#M#83 4 +135194 Tg#1238 2 +17553 Nso#65 1 +17553 Nse#L#150 1 +17553 Nso#1209 2 +17553 Nso#107 1 +98504 Sse#M#14 2 +98504 Sse#M#63 1 +140807 Swb#M#156 2 +61856 Sse#M#14 1 +61856 Sse#M#62 1 +61856 Sws#M#1234 1 +3678 Swb#M#154 3 +3677 Nso#1209 1 +3677 Nse#L#151 1 +3677 Nse#L#150 1 +3677 Nse#L#152 2 +231309 Nw#L#160 1 +231309 Sws#M#1234 1 +114601 Vg#h#1104 1 +199119 Vg#h#1043 2 +199119 Vg#h#1061 1 +58637 Nw#L#119 1 +17311 Vg#h#1039 1 +17311 Vg#o#1132 1 +17311 Tg#1249 1 +136142 Sse#1224 1 +52305 Vg#o#1124 1 +52305 Vg#oh#1070 1 +52305 Vg#oh#1051 16 +52305 Vg#h#1061 1 +52305 Vg#o#1132 4 +60184 Sws#M#163 1 +60184 Nse#L#153 1 +60184 Sse#M#64 1 +65483 Nse#L#153 1 +140716 Swb#M#157 1 +140716 Swb#M#155 134 +140716 Swb#M#154 12 +140716 Sws#M#163 2 +140716 Sws#M#83 7 +140716 Sws#M#1234 2 +140716 Sse#M#62 1 +140716 Sse#M#63 1 +140716 Sse#M#64 1 +203683 Tg#1249 1 +162538 Sse#M#169 1 +162538 Nse#L#151 1 +105780 Sse#M#63 2 +105782 Nw#L#160 1 +105782 Nso#1209 1 +105782 Nso#106 1 +105782 Sws#M#83 1 +191128 Sse#M#63 3 +99566 Vg#h#1104 3 +99566 Vg#o#1128 2 +99566 Vg#oh#1070 31 +99566 Vg#oh#1055 2 +99566 Vg#h#1061 1 +99566 Vg#oh#1051 1 +99566 Vg#o#1153 1 +99566 Vg#o#1132 2 +52403 Vg#h#1043 1 +52403 Vg#o#1128 2 +52403 Vg#h#1061 1 +52403 Vg#oh#1055 4 +52403 Vg#h#1038 4 +52403 Vg#o#1132 5 +234617 Sws#M#1227 66 +234617 Sws#M#163 59 +234617 Nso#107 1 +234617 Sws#M#1234 134 +234617 Swb#M#137 28 +234617 Sws#M#1230 119 +45562 Tg#1251 3 +134266 Tg#1251 1 +134266 Vg#o#1124 3 +134265 Vg#h#1043 1 +1847 Nso#65 1 +1847 Swb#M#154 1 +1847 Nso#107 1 +1847 Nse#L#151 1 +1847 Nse#L#150 1 +1847 Nse#L#152 2 +1847 Nse#L#98 1 +1847 Nso#108 3 +1847 Nso#106 1 +58110 Nse#L#151 1 +58110 Nso#108 1 +58110 Nso#65 3 +43604 Swb#M#155 1 +105109 Vg#o#1124 2 +105109 Vg#o#1132 2 +193703 Vg#oh#1070 3 +141539 Nso#65 1 +141539 Sse#M#75 1 +137270 Nw#L#160 2 +104782 Sse#M#63 1 +197517 Vg#oh#1055 1 +14035 Vg#o#1124 7 +113707 Tg#1252 1 +190238 Vg#o#1124 1 +135845 Tg#1238 2 +135845 Tg#1249 2 +98988 Sse#M#63 1 +1906 Tg#1249 1 +1907 Nse#L#150 1 +175547 Sws#M#1227 1 +198806 Vg#o#1160 5 +198806 Vg#h#1038 3 +198806 Vg#o#1132 4 +198806 Vg#o#1153 4 +147978 Nse#L#150 1 +147978 Nso#1209 1 +188911 Tg#1238 3 +188911 Vg#h#1043 4 +188911 Vg#o#1128 1 +44762 Nse#L#151 1 +75119 Sws#M#1227 1 +75119 Sws#M#1230 3 +144253 Nso#65 3 +144253 Nso#1209 1 +99294 Vg#o#1132 1 +146392 Tg#1251 1 +102985 Nw#R#189 5 +102985 Nw#L#5 11 +102985 Nw#L#160 1 +102985 Nw#L#119 2 +163049 Nse#L#150 1 +10585 Nse#L#98 1 +10585 Nso#108 2 +10585 Nso#1209 2 +141750 Nw#L#160 1 +138694 Sws#M#163 2 +45363 Vg#o#1160 1 +45363 Vg#oh#1070 5 +45363 Vg#oh#1055 37 +45363 Vg#oh#1051 1 +198643 Vg#o#1128 1 +41043 Nse#L#150 1 +25807 Tg#1249 1 +25807 Nso#106 2 +25807 Sws#M#83 4 +44017 Nso#108 5 +44012 Nse#L#153 1 +44012 Nse#L#152 1 +44012 Nw#R#50 1 +44012 Sse#1224 2 +44012 Sws#M#1230 1 +134487 Sse#M#169 1 +99034 Vg#o#1124 20 +14148 Sse#M#14 1 +45382 Sws#M#1234 2 +108652 Sws#M#1234 2 +144392 Tg#1252 6 +149604 Tg#1251 1 +44493 Vg#o#1160 1 +33286 Nse#L#153 1 +33286 Nso#108 1 +33286 Nso#1209 1 +77008 Nse#L#152 1 +155827 Nse#L#150 1 +155827 Nse#L#152 1 +155827 Nso#1209 1 +175148 Vg#o#1124 1 +102303 Nso#65 1 +102303 Nso#108 4 +102303 Nso#1209 3 +102303 Nso#106 1 +70898 Swb#M#154 1 +71617 Nso#106 1 +71617 Nso#1209 1 +4588 Nso#65 1 +4588 Nso#1209 1 +113490 Sse#M#62 1 +113490 Sse#M#63 3 +157794 Sse#M#62 1 +109928 Vg#h#1061 3 +112727 Nse#L#153 2 +93647 Vg#o#1132 1 +44460 Sse#M#14 2 +44460 Swb#M#137 4 +44460 Sws#M#83 1 +106447 Vg#h#1061 1 +106447 Tg#1249 1 +138900 Nso#1209 1 +10641 Nso#108 4 +10641 Nse#L#150 1 +216140 Tg#1251 1 +78144 Tg#1249 1 +169989 Tg#1249 2 +169989 Tg#1251 1 +169989 Tg#1252 3 +174635 Vg#h#1043 1 +174635 Vg#h#1061 3 +174635 Vg#h#1039 1 +174634 Sse#1224 1 +174634 Sws#M#83 2 +79762 Nw#L#160 1 +79767 Sws#M#163 1 +67537 Nso#65 2 +67537 Nse#L#151 5 +67537 Nse#L#150 1 +67537 Nse#L#152 3 +67537 Nse#L#98 3 +67537 Nso#108 7 +67537 Nso#1209 2 +1791 Nso#107 13 +1791 Nso#106 4 +169430 Sws#M#1227 17 +169430 Nso#1209 2 +169430 Sws#M#83 1 +136506 Tg#1238 1 +133843 Vg#o#1124 3 +169183 Vg#h#1104 3 +169183 Vg#o#1160 1 +169183 Vg#oh#1070 5 +169183 Vg#oh#1055 6 +169183 Vg#h#1061 1 +169183 Vg#oh#1051 13 +169183 Vg#o#1132 1 +115098 Vg#o#1124 4 +136690 Tg#1249 1 +136690 Tg#1238 1 +207768 Nso#108 1 +136988 Nse#L#150 1 +111289 Nw#L#5 1 +3112 Nw#L#160 6 +3112 Nse#L#152 10 +137345 Sws#M#1234 3 +137345 Sws#M#1230 4 +175033 Vg#h#1043 5 +175033 Vg#o#1128 1 +175033 Vg#h#1061 1 +226370 Nw#R#189 1 +114946 Vg#h#1038 1 +114946 Vg#o#1132 1 +229650 Swb#M#137 5 +100454 Vg#h#1039 2 +100454 Vg#h#1061 1 +222877 Nse#L#150 2 +222877 Nso#1209 1 +9121 Sse#M#63 1 +9121 Sse#M#75 2 +23209 Swb#M#156 2 +23209 Swb#M#154 20 +23209 Swb#M#155 3 +23209 Nse#L#153 1 +23209 Nso#108 22 +23209 Nso#107 1 +23209 Nso#106 2 +23209 Nso#65 2 +23209 Nso#1209 1 +136349 Vg#h#1104 1 +136349 Vg#o#1124 1 +136349 Vg#o#1128 1 +136349 Vg#h#1061 1 +111739 Swb#M#154 1 +111739 Swb#M#155 2 +111739 Sws#M#163 1 +111739 Sws#M#1234 2 +111739 Sws#M#83 2 +114382 Sse#M#14 1 +114382 Nso#108 1 +147627 Tg#1249 1 +147627 Tg#1251 2 +13933 Vg#oh#1070 1 +67849 Nse#L#98 1 +5583 Swb#M#157 1 +5583 Nw#R#50 1 +103304 Sws#M#83 1 +160817 Sws#M#163 1 +160817 Swb#M#156 1 +160817 Swb#M#154 12 +160817 Swb#M#155 1 +109697 Vg#h#1061 1 +145536 Tg#1251 1 +91363 Nw#L#119 1 +91363 Nso#1209 1 +212243 Nso#65 1 +212243 Nse#L#151 1 +212243 Nse#L#153 2 +212243 Nse#L#98 1 +221978 Tg#1238 1 +221978 Tg#1251 1 +221978 Tg#1249 1 +79548 Swb#M#156 2 +79548 Swb#M#155 1 +7772 Sws#M#1230 4 +170512 Tg#1252 1 +170512 Vg#o#1132 2 +170512 Vg#h#1043 1 +170512 Tg#1249 1 +77057 Nso#1209 1 +110392 Nse#L#98 1 +110392 Nw#L#160 1 +110392 Nso#108 2 +136443 Nw#L#160 1 +145197 Tg#1249 2 +145197 Tg#1251 1 +222851 Vg#o#1124 1 +144222 Sse#M#75 1 +11278 Swb#M#155 1 +11278 Sse#M#62 1 +39070 Nw#L#5 1 +39070 Tg#1249 1 +100270 Swb#M#156 1 +100270 Nse#L#153 1 +100270 Swb#M#155 1 +127094 Swb#M#156 1 +127094 Swb#M#157 3 +127094 Sws#M#163 1 +127094 Swb#M#137 3 +127094 Sws#M#1230 1 +127094 Sws#M#83 4 +155780 Swb#M#156 2 +155780 Swb#M#157 5 +155780 Swb#M#155 1 +155780 Nw#L#119 5 +155780 Nse#L#153 1 +155780 Nw#R#50 4 +155780 Nso#108 1 +155780 Nw#R#189 19 +155780 Nso#1209 1 +148007 Tg#1251 2 +146761 Tg#1251 1 +50433 Vg#o#1128 4 +50433 Vg#oh#1070 89 +50433 Vg#o#1132 1 +177553 Nso#65 2 +177553 Nso#1209 1 +224027 Vg#oh#1055 29 +222526 Nse#L#98 3 +185760 Vg#o#1124 16 +145262 Tg#1252 2 +110060 Vg#o#1124 3 +110060 Vg#o#1128 1 +110060 Vg#o#1132 1 +110060 Tg#1249 2 +221872 Nso#1209 1 +213063 Nso#1209 1 +13753 Nso#106 2 +23971 Nso#65 1 +138306 Nw#R#50 1 +92037 Swb#M#155 4 +92037 Sws#M#1227 23 +92037 Nse#L#153 1 +92037 Nw#L#160 22 +92037 Nw#R#50 2 +92037 Sws#M#163 39 +92037 Sws#M#83 1 +92037 Sws#M#1234 41 +92037 Swb#M#137 4 +92037 Sws#M#1230 109 +145609 Tg#1249 1 +145609 Tg#1251 2 +145609 Tg#1252 2 +82334 Nso#108 1 +82334 Nse#L#151 1 +10876 Nse#L#151 1 +10876 Nse#L#150 1 +3667 Nso#65 1 +3667 Nso#108 2 +219387 Nso#1209 1 +90253 Nso#65 5 +90253 Nse#L#98 1 +90253 Nso#108 7 +104551 Tg#1238 4 +104551 Tg#1252 1 +104551 Tg#1249 5 +150441 Sws#M#1227 1 +61847 Sse#M#62 1 +61847 Sse#M#63 1 +61848 Sws#M#83 1 +1665 Sse#M#63 1 +1665 Sse#M#75 1 +149351 Nw#R#50 1 +141639 Swb#M#157 2 +141639 Swb#M#155 1 +141639 Sws#M#163 54 +2577 Sse#M#63 1 +114411 Nso#108 13 +114411 Nso#1209 2 +56341 Sse#1224 3 +52311 Vg#o#1132 7 +52311 Tg#1251 3 +52311 Vg#o#1124 10 +52311 Vg#oh#1070 3 +52311 Vg#h#1039 3 +52311 Vg#h#1043 10 +52311 Vg#h#1104 2 +52311 Vg#h#1061 1 +52311 Vg#o#1160 2 +52311 Vg#o#1153 4 +52311 Vg#oh#1055 12 +52311 Vg#oh#1051 7 +46159 Vg#o#1128 4 +46159 Vg#oh#1070 26 +46159 Vg#h#1104 1 +46159 Vg#o#1132 1 +60592 Nso#107 1 +10908 Sse#M#14 1 +153534 Nse#L#153 1 +153534 Nso#106 1 +153534 Nso#1209 1 +21232 Vg#h#1104 3 +21232 Vg#h#1043 1 +21232 Vg#o#1128 8 +21232 Vg#h#1061 3 +21232 Vg#o#1132 1 +21232 Tg#1249 4 +2592 Nw#L#5 1 +2592 Nw#R#50 5 +1872 Vg#o#1132 1 +1872 Vg#o#1124 5 +105156 Nse#L#98 11 +105156 Nw#R#50 1 +105156 Nse#L#152 1 +221873 Sse#M#169 1 +1878 Sws#M#1234 1 +1878 Tg#1251 1 +36733 Tg#1249 1 +102539 Vg#h#1061 1 +36731 Nse#L#152 1 +105331 Swb#M#156 5 +105331 Swb#M#157 1 +105331 Swb#M#155 8 +105331 Sws#M#1234 6 +105331 Sws#M#163 1 +105331 Sws#M#1227 39 +105331 Sws#M#1230 25 +105331 Sws#M#83 14 +134379 Vg#o#1124 1 +102531 Nse#L#151 1 +176174 Vg#o#1124 6 +130982 Nso#65 1 +130982 Nso#108 1 +104795 Nw#R#189 2 +104795 Nw#L#119 1 +104795 Nse#L#152 1 +104795 Sse#M#62 1 +104795 Sse#M#64 1 +104795 Nw#R#50 1 +135755 Sws#M#163 1 +141830 Tg#1238 1 +141830 Tg#1252 6 +104793 Vg#o#1160 1 +14023 Vg#o#1124 2 +14023 Vg#oh#1070 3 +14023 Vg#h#1039 1 +14023 Vg#o#1132 2 +200671 Nw#L#5 1 +199413 Vg#o#1128 4 +199413 Vg#h#1039 23 +199413 Vg#oh#1070 37 +199413 Vg#h#1061 2 +199413 Vg#o#1132 2 +128032 Nso#108 13 +128032 Nso#107 1 +128032 Nso#1209 15 +99132 Nse#L#152 1 +210805 Sse#1224 1 +113241 Nse#L#151 1 +113241 Nso#108 1 +113241 Nso#1209 6 +113241 Nso#107 1 +113241 Nso#106 2 +141525 Tg#1238 1 +141525 Tg#1252 1 +135876 Tg#1238 2 +1911 Nse#L#150 5 +1911 Nse#L#153 4 +1911 Nse#L#152 1 +1911 Nse#L#98 1 +1911 Nso#108 1 +1911 Sse#M#63 1 +1911 Sse#M#75 1 +1912 Sse#M#62 1 +1912 Sse#M#63 2 +1912 Sse#M#64 1 +1912 Sse#M#75 1 +1914 Sse#M#14 1 +110924 Vg#h#1104 1 +110924 Vg#o#1124 1 +110924 Vg#oh#1055 1 +110924 Vg#o#1132 1 +102227 Nse#L#153 1 +167370 Nso#65 11 +167370 Nse#L#151 1 +167370 Nse#L#153 1 +167370 Nse#L#98 2 +167370 Nso#1209 1 +78204 Tg#1249 1 +163696 Vg#o#1132 1 +163696 Vg#o#1128 1 +2751 Nso#65 2 +2751 Nso#108 1 +2751 Nso#107 2 +227169 Tg#1238 1 +227169 Nse#L#150 3 +227169 Nso#106 2 +227169 Nw#R#50 2 +210240 Nso#106 2 +112280 Nso#108 1 +156950 Nw#L#119 1 +27866 Vg#oh#1070 35 +27866 Vg#oh#1055 449 +27866 Vg#oh#1051 170 +27866 Vg#o#1132 8 +231994 Nw#L#160 1 +156301 Swb#M#157 1 +156301 Swb#M#155 1 +33448 Nse#L#150 1 +14158 Vg#o#1132 1 +216710 Vg#h#1043 1 +216710 Vg#o#1153 1 +174734 Vg#o#1128 1 +174734 Vg#oh#1070 2 +174734 Vg#h#1039 2 +174734 Nso#1209 2 +9053 Swb#M#154 3 +9053 Swb#M#155 31 +9053 Sse#M#75 1 +9053 Sws#M#83 1 +164271 Sws#M#1230 2 +29275 Nw#L#119 1 +103041 Sws#M#83 2 +176825 Swb#M#156 1 +176825 Nse#L#152 2 +176825 Nso#108 1 +176825 Nso#106 1 +232196 Sse#M#62 1 +230312 Nw#L#160 1 +184023 Vg#oh#1055 1 +74504 Sws#M#1234 1 +194409 Sse#1224 1 +107418 Nw#L#119 1 +107777 Sws#M#1234 1 +68992 Swb#M#157 8 +68992 Sws#M#1234 2 +68992 Swb#M#155 4 +68992 Sws#M#1227 3 +68992 Sws#M#163 4 +68992 Swb#M#137 1 +68992 Sws#M#1230 3 +68992 Sws#M#83 2 +68995 Sws#M#1227 1 +68995 Sws#M#83 3 +68994 Sws#M#163 3 +68994 Sws#M#1234 1 +68994 Swb#M#137 1 +68994 Sws#M#1230 1 +68994 Sws#M#83 11 +4597 Sse#M#62 1 +1681 Sse#M#62 1 +143480 Nso#65 1 +143480 Nso#1209 4 +143480 Nso#108 1 +178618 Vg#o#1124 2 +178618 Vg#h#1043 1 +142268 Nso#108 1 +149504 Vg#h#1104 1 +149504 Vg#o#1124 1 +149504 Vg#h#1043 3 +149504 Vg#o#1160 1 +149504 Vg#oh#1055 584 +149504 Vg#oh#1051 472 +130187 Sse#M#169 1 +192827 Vg#o#1128 1 +192398 Vg#oh#1070 1 +192398 Vg#oh#1055 4 +192398 Vg#oh#1051 1 +131965 Sse#M#169 3 +131965 Sse#M#63 1 +131965 Sse#M#75 1 +1266 Nso#108 1 +24546 Vg#o#1124 4 +24546 Vg#h#1043 1 +24546 Vg#h#1061 3 +24546 Vg#oh#1070 16 +24546 Vg#oh#1051 3 +24546 Vg#o#1153 1 +24546 Vg#o#1132 2 +178986 Vg#o#1124 15 +11057 Vg#o#1132 1 +207121 Nse#L#152 1 +202129 Nw#L#160 1 +202129 Nso#1209 2 +209831 Nso#65 1 +106786 Vg#h#1039 1 +106786 Vg#o#1132 1 +106786 Vg#h#1104 1 +106786 Tg#1251 2 +106786 Vg#oh#1055 3 +106786 Vg#o#1124 1 +207770 Vg#o#1124 4 +183480 Vg#oh#1055 103 +166372 Sse#M#62 1 +234489 Swb#M#155 1 +234489 Sws#M#1227 1 +234489 Nso#108 1 +34464 Vg#h#1043 21 +34464 Vg#h#1104 2 +34464 Vg#o#1128 6 +34464 Vg#oh#1070 1 +34464 Vg#oh#1055 1 +34464 Vg#h#1061 2 +34464 Vg#h#1039 12 +34464 Vg#o#1132 13 +34464 Vg#o#1153 11 +94878 Sse#M#14 1 +94878 Sse#M#62 2 +94878 Sse#M#64 2 +207791 Nso#1209 2 +218700 Sse#M#169 1 +111250 Vg#h#1038 1 +111250 Tg#1238 1 +73788 Swb#M#155 3 +73788 Sws#M#163 2 +73788 Sse#M#62 1 +73788 Vg#o#1132 2 +34649 Vg#o#1124 2 +34649 Vg#oh#1070 6 +34649 Vg#oh#1055 23 +34649 Vg#oh#1051 5 +34649 Vg#o#1132 1 +34649 Vg#o#1153 1 +2833 Sse#M#64 1 +2833 Sws#M#1230 3 +2833 Sws#M#83 3 +132414 Sse#M#169 1 +53290 Swb#M#156 14 +53290 Swb#M#157 4 +53290 Swb#M#155 3 +53290 Sws#M#1234 67 +53290 Sws#M#163 15 +53290 Sws#M#1227 19 +53290 Swb#M#137 7 +53290 Sws#M#1230 10 +53290 Sws#M#83 13 +190891 Vg#h#1043 1 +114375 Sws#M#1234 2 +135740 Nso#108 2 +135740 Nse#L#153 1 +145850 Tg#1251 1 +108351 Vg#oh#1070 3 +108351 Tg#1249 1 +105930 Tg#1252 1 +105930 Tg#1251 1 +105930 Nso#107 3 +105930 Nse#L#150 1 +105930 Nse#L#152 1 +105930 Nse#L#98 1 +105930 Nw#R#50 1 +105930 Nso#108 1 +27673 Nso#107 1 +27673 Nso#106 1 +106388 Vg#o#1132 1 +129921 Vg#h#1043 5 +129921 Tg#1251 2 +129921 Vg#o#1124 6 +129921 Vg#h#1061 2 +129921 Vg#oh#1055 9 +129921 Vg#oh#1051 5 +129921 Vg#h#1039 3 +180687 Vg#h#1039 1 +222283 Nso#107 2 +222283 Nso#106 3 +111384 Tg#1238 1 +111384 Vg#h#1061 1 +221485 Swb#M#155 2 +183441 Nse#L#151 1 +133665 Sse#M#169 1 +71332 Nso#65 1 +226806 Sse#M#63 1 +159255 Sse#M#63 1 +6702 Tg#1238 2 +6702 Tg#1252 1 +6702 Nse#L#151 1 +6702 Nse#L#150 1 +6702 Nse#L#153 1 +6702 Nse#L#152 2 +6702 Nse#L#98 1 +6702 Nw#R#50 2 +6702 Nso#107 2 +6702 Tg#1249 2 +6702 Vg#o#1153 1 +6702 Nw#R#189 1 +148038 Tg#1249 1 +148038 Tg#1252 1 +193471 Tg#1249 1 +193471 Vg#h#1104 1 +193471 Vg#o#1124 1 +114114 Nso#108 9 +114114 Nso#107 1 +114114 Nso#106 1 +15221 Vg#oh#1070 3 +15221 Vg#o#1132 1 +39088 Swb#M#154 2 +39088 Nso#108 1 +39088 Nso#107 1 +141748 Nso#107 2 +141748 Nw#R#50 1 +141748 Nso#108 3 +141748 Nso#106 5 +141748 Nso#65 1 +61189 Swb#M#157 2 +61189 Sws#M#1234 5 +61189 Sws#M#163 9 +61189 Sws#M#1227 4 +61189 Sws#M#1230 24 +61189 Sws#M#83 4 +226807 Nse#L#98 1 +226807 Sse#M#63 1 +226807 Sse#M#169 1 +96259 Tg#1238 2 +96259 Sse#M#63 1 +96259 Sse#M#64 1 +150308 Tg#1251 1 +180464 Nso#108 1 +180464 Sws#M#1230 1 +180464 Nso#1209 4 +49541 Sws#M#83 1 +87861 Nw#L#160 1 +116532 Sws#M#1230 17 +116532 Sws#M#1227 21 +116532 Sse#M#64 2 +155771 Sse#M#62 1 +7633 Nse#L#153 1 +221738 Sse#M#14 1 +221738 Nse#L#152 1 +215442 Nso#1209 1 +16559 Vg#h#1043 3 +16559 Vg#h#1038 1 +16559 Vg#o#1132 2 +16559 Vg#o#1153 1 +229371 Vg#o#1124 2 +229371 Vg#h#1043 1 +185977 Sse#M#64 1 +185977 Swb#M#154 2 +185977 Swb#M#155 1 +16087 Sse#M#14 1 +114029 Sse#M#62 1 +4056 Tg#1238 1 +4056 Nse#L#153 1 +4056 Tg#1252 4 +4056 Tg#1249 2 +114709 Nse#L#150 1 +109894 Vg#h#1043 1 +109894 Vg#o#1128 1 +109894 Vg#h#1061 4 +109894 Vg#oh#1070 1 +109894 Vg#h#1038 1 +109894 Vg#h#1039 3 +109894 Tg#1249 1 +161221 Nso#107 1 +163874 Vg#o#1124 6 +103846 Nso#106 1 +10869 Nse#L#98 2 +10869 Sse#M#169 1 +10869 Sse#M#62 1 +51211 Vg#h#1104 1 +198075 Tg#1238 1 +198075 Tg#1251 2 +198075 Tg#1249 3 +198075 Vg#h#1039 1 +207011 Nso#106 1 +166297 Vg#o#1124 10 +183808 Sse#M#64 1 +195431 Vg#o#1124 2 +154082 Swb#M#155 1 +162741 Vg#h#1038 2 +130639 Vg#o#1124 9 +114620 Tg#1249 4 +114620 Tg#1238 1 +155577 Vg#h#1061 1 +155577 Vg#o#1124 3 +150056 Tg#1249 1 +82250 Nw#L#119 1 +82250 Nse#L#151 3 +82250 Nse#L#153 2 +82250 Nse#L#152 4 +82250 Nw#L#160 2 +82250 Vg#o#1132 1 +82250 Nso#1209 1 +65357 Swb#M#155 1 +154220 Nse#L#150 1 +154220 Nso#1209 2 +42395 Tg#1251 2 +42395 Vg#oh#1070 1 +42395 Vg#o#1132 1 +42395 Vg#o#1124 1 +10311 Swb#M#156 1 +10311 Nw#L#119 1 +10311 Sws#M#163 1 +10311 Nso#1209 1 +10311 Sse#M#64 1 +214543 Nw#L#119 1 +214543 Nso#65 2 +214543 Nso#1209 1 +45496 Sws#M#1227 1 +45496 Swb#M#155 6 +106308 Nso#1209 1 +63760 Sws#M#163 1 +106304 Nse#L#153 2 +106304 Nse#L#152 2 +106304 Nw#L#160 1 +51677 Vg#o#1124 5 +113017 Nso#108 1 +113017 Nso#1209 1 +113017 Nse#L#152 1 +195548 Vg#o#1128 1 +195548 Vg#o#1124 1 +130035 Sse#M#169 2 +130034 Vg#oh#1055 1 +4688 Nso#65 3 +4688 Nse#L#152 1 +4688 Nso#108 3 +4688 Sws#M#83 1 +90568 Nse#L#98 1 +153433 Sse#M#14 1 +99836 Nse#L#152 1 +98733 Nse#L#152 1 +98733 Nw#L#160 6 +98733 Nw#R#50 14 +224206 Vg#oh#1070 3 +224206 Tg#1238 1 +224206 Vg#h#1104 2 +224206 Vg#o#1132 1 +224206 Vg#h#1043 1 +224206 Tg#1249 2 +224206 Vg#h#1061 1 +6052 Sse#M#62 1 +6052 Tg#1249 1 +204369 Tg#1249 1 +36896 Vg#o#1124 1 +36896 Vg#h#1043 14 +36896 Vg#o#1160 7 +36896 Vg#oh#1055 1 +36896 Vg#h#1039 9 +36896 Vg#o#1132 6 +36896 Vg#o#1153 1 +113239 Nso#65 1 +113239 Tg#1238 7 +190255 Vg#o#1124 5 +36892 Vg#h#1061 2 +36892 Vg#o#1132 1 +36892 Vg#o#1124 2 +41287 Vg#h#1043 1 +41287 Vg#h#1104 2 +41287 Vg#h#1039 2 +41287 Vg#o#1132 1 +113232 Nse#L#151 2 +113232 Nse#L#150 3 +113232 Nso#108 7 +113232 Sse#M#64 1 +113726 Nso#108 1 +113726 Nse#L#151 2 +113726 Nse#L#150 1 +106015 Vg#o#1132 2 +164869 Vg#oh#1070 155 +164869 Vg#oh#1055 2 +164869 Vg#oh#1051 206 +164869 Vg#h#1061 1 +164869 Vg#h#1039 1 +164869 Vg#o#1132 3 +3439 Nse#L#151 20 +3439 Nse#L#150 1 +3439 Nse#L#153 20 +3439 Nse#L#152 8 +3439 Nse#L#98 1 +3439 Sws#M#1227 11 +3439 Nso#108 1 +3439 Sws#M#83 3 +3439 Nw#L#160 2 +3439 Sws#M#1230 2 +33112 Vg#o#1132 2 +141770 Tg#1252 1 +135462 Vg#h#1104 2 +135462 Vg#o#1124 5 +135462 Vg#h#1043 1 +135462 Vg#oh#1055 1 +135462 Vg#oh#1051 15 +167566 Tg#1238 6 +167566 Nso#108 1 +167566 Nso#1209 2 +200608 Tg#1249 1 +4594 Sws#M#83 1 +72863 Sse#M#63 1 +135927 Sws#M#83 1 +114828 Sws#M#1227 1 +33331 Swb#M#156 1 +33331 Sse#M#14 1 +33331 Sws#M#1234 1 +33331 Sws#M#83 1 +196718 Vg#o#1160 3 +196718 Vg#o#1153 2 +94718 Vg#h#1038 1 +94718 Vg#oh#1070 23 +94718 Vg#o#1132 3 +94718 Vg#o#1124 6 +94718 Vg#h#1043 1 +14169 Sse#1224 1 +10461 Vg#oh#1055 6 +10461 Sse#1224 1 +155973 Nw#L#160 15 +179116 Vg#o#1124 5 +168296 Sws#M#1234 1 +24077 Sse#1224 1 +134023 Tg#1249 1 +134023 Tg#1251 1 +111879 Sws#M#163 6 +111879 Sws#M#83 3 +111879 Sws#M#1234 8 +111879 Sws#M#1230 1 +232434 Vg#o#1128 1 +232434 Vg#oh#1070 1 +232434 Vg#o#1124 1 +146995 Nse#L#98 1 +146995 Sws#M#83 1 +29865 Vg#o#1160 1 +29865 Vg#o#1128 1 +15164 Vg#o#1132 2 +112747 Nso#108 2 +83335 Nw#L#119 1 +91160 Nso#107 2 +131573 Vg#oh#1070 1 +131573 Vg#oh#1055 4 +131573 Vg#o#1124 4 +131573 Vg#h#1061 1 +158854 Vg#h#1061 1 +228870 Nw#R#189 3 +228870 Nw#L#119 1 +228870 Nw#R#50 3 +104895 Tg#1238 1 +104895 Vg#o#1128 1 +104895 Vg#h#1061 2 +211749 Nso#65 1 +18226 Nse#L#153 1 +18226 Nso#108 5 +18226 Nso#107 4 +18226 Nso#106 4 +18226 Nso#65 5 +177590 Vg#o#1124 3 +1674 Swb#M#154 4 +1674 Swb#M#155 1 +155970 Nso#65 1 +155970 Nse#L#151 3 +155970 Nse#L#150 2 +132552 Nse#L#151 1 +132552 Nse#L#150 1 +143031 Tg#1249 1 +143031 Tg#1238 1 +131973 Vg#h#1104 1 +209828 Nso#106 1 +179971 Vg#oh#1055 17 +179971 Vg#oh#1051 3 +179971 Vg#o#1132 1 +159670 Vg#o#1128 1 +159670 Vg#h#1104 1 +20204 Sse#M#75 3 +15698 Vg#o#1153 1 +15698 Vg#o#1128 2 +7604 Nse#L#150 1 +95046 Sse#M#63 1 +95046 Swb#M#154 5 +129486 Sse#M#169 1 +111138 Vg#o#1124 1 +109901 Vg#h#1104 5 +109901 Tg#1252 1 +15989 Vg#h#1043 1 +15989 Nse#L#150 1 +15989 Tg#1238 1 +157425 Vg#o#1128 1 +156688 Sse#M#62 2 +156688 Sse#M#64 1 +156689 Sse#1224 1 +15980 Nse#L#150 1 +183477 Vg#h#1039 6 +136415 Vg#o#1128 1 +136415 Vg#h#1038 1 +136415 Vg#o#1132 1 +183479 Tg#1251 1 +183479 Vg#h#1043 1 +183479 Vg#o#1128 1 +183479 Vg#h#1039 3 +183479 Tg#1249 2 +229691 Sws#M#1234 1 +184028 Vg#o#1124 6 +217293 Nso#108 1 +217293 Nso#107 2 +217293 Nso#106 2 +107379 Vg#h#1061 5 +107379 Vg#h#1104 1 +107379 Vg#h#1038 33 +160748 Nse#L#153 2 +23149 Vg#oh#1055 1 +151001 Nso#1209 2 +151001 Nso#106 1 +151001 Nse#L#150 1 +27669 Tg#1238 1 +27669 Nso#108 1 +27669 Nw#R#50 1 +72978 Nw#L#5 1 +72978 Nso#108 1 +229211 Vg#h#1104 1 +229211 Vg#o#1160 2 +50779 Vg#o#1132 2 +50779 Vg#o#1124 1 +50779 Vg#h#1043 1 +106487 Sws#M#83 1 +145515 Tg#1251 1 +50807 Swb#M#157 1 +50807 Swb#M#154 1 +50807 Sws#M#1227 1 +50807 Sws#M#163 3 +50807 Swb#M#137 2 +50807 Sws#M#83 2 +131798 Nso#1209 1 +131798 Nse#L#150 5 +131798 Nse#L#152 3 +19191 Vg#o#1153 1 +19191 Vg#oh#1070 1 +55727 Nso#1209 1 +161348 Nso#65 1 +161348 Swb#M#154 1 +161348 Sse#M#75 1 +159244 Sws#M#1227 1 +161343 Vg#o#1124 1 +224515 Nse#L#151 1 +159481 Nso#107 1 +159481 Nw#L#5 1 +159481 Nso#108 4 +159481 Nso#106 3 +151267 Vg#o#1132 1 +151267 Vg#h#1061 1 +159484 Nse#L#152 1 +100526 Vg#o#1124 3 +100526 Vg#h#1039 1 +100526 Vg#o#1153 2 +228504 Tg#1252 2 +36737 Nso#108 1 +77189 Nso#65 1 +77189 Nso#108 10 +77189 Nso#1209 1 +130950 Sse#M#169 1 +151468 Sse#M#62 1 +66029 Nw#L#5 1 +66029 Nw#R#189 4 +82792 Swb#M#157 1 +82792 Swb#M#155 1 +82792 Nw#L#119 1 +82792 Sws#M#1234 4 +82792 Sws#M#1227 3 +82792 Sws#M#83 18 +98683 Vg#o#1124 3 +88207 Nse#L#98 2 +2474 Swb#M#156 1 +145246 Tg#1238 1 +145246 Tg#1252 2 +145246 Tg#1251 3 +135350 Tg#1251 1 +135350 Vg#h#1104 5 +135350 Vg#o#1124 1 +76923 Vg#o#1132 2 +114581 Tg#1249 2 +101238 Swb#M#154 7 +101238 Swb#M#155 1 +109018 Vg#oh#1070 3 +109018 Vg#o#1132 2 +100328 Sse#M#75 1 +145643 Tg#1251 2 +154764 Nso#108 1 +191730 Nse#L#153 1 +191730 Nse#L#98 1 +191730 Nw#L#160 2 +191730 Nso#108 2 +191730 Nso#106 1 +191730 Sws#M#1234 1 +191730 Nso#1209 1 +166286 Tg#1252 11 +224294 Nso#65 1 +224294 Nso#1209 4 +165959 Vg#o#1132 1 +165959 Vg#o#1124 5 +165959 Vg#o#1128 2 +165959 Vg#h#1061 1 +165959 Vg#oh#1070 4 +165959 Vg#oh#1055 3 +165959 Vg#oh#1051 4 +165959 Vg#h#1039 1 +97371 Swb#M#154 1 +97371 Sse#M#62 1 +97371 Sse#M#63 1 +130603 Nse#L#153 2 +130603 Nse#L#152 1 +158015 Vg#h#1061 1 +148235 Tg#1251 1 +113312 Nso#65 7 +113312 Nso#108 1 +186820 Vg#o#1124 3 +104538 Nso#1209 1 +149371 Tg#1252 1 +149371 Tg#1251 1 +144924 Nso#65 1 +182787 Vg#o#1160 6 +182787 Vg#o#1153 6 +114637 Tg#1238 2 +114637 Vg#o#1124 2 +114637 Tg#1249 1 +97196 Vg#o#1124 1 +2359 Swb#M#156 4 +2359 Swb#M#157 2 +2359 Swb#M#155 1 +2359 Sws#M#1230 3 +2359 Sse#M#62 1 +2359 Sse#M#64 1 +2359 Sws#M#83 5 +177228 Vg#o#1124 1 +152594 Nso#1209 3 +63772 Sse#M#62 1 +95448 Nse#L#152 1 +95448 Nso#1209 1 +153517 Nso#1209 1 +196857 Nso#65 2 +196857 Sws#M#1227 2 +196857 Swb#M#137 1 +196857 Sws#M#1230 3 +196857 Nso#1209 2 +196857 Sws#M#83 4 +102805 Nw#R#50 1 +118269 Swb#M#157 5 +118269 Swb#M#155 1 +118269 Sws#M#1234 3 +118269 Sws#M#1230 1 +118269 Sws#M#83 3 +21147 Nso#1209 1 +3732 Nse#L#151 1 +3732 Nso#1209 1 +138778 Swb#M#157 1 +138778 Swb#M#155 1 +138778 Nse#L#151 1 +138778 Sws#M#1227 2 +138778 Nse#L#152 1 +138778 Nw#R#50 1 +138778 Sse#M#75 1 +138778 Sws#M#163 1 +138778 Sws#M#83 4 +138778 Sws#M#1230 2 +138778 Nso#1209 2 +138778 Sse#M#62 6 +138778 Sse#M#64 1 +42709 Vg#h#1061 1 +209786 Nse#L#151 1 +152494 Nso#107 1 +159115 Tg#1249 1 +49015 Vg#o#1132 2 +49015 Vg#h#1061 2 +215462 Nw#R#189 3 +204192 Nso#65 5 +204192 Nso#1209 1 +96804 Sse#M#14 1 +96804 Nso#107 1 +43052 Tg#1249 1 +43052 Vg#o#1132 1 +113223 Sse#M#62 1 +10700 Tg#1251 4 +101908 Vg#h#1061 1 +49231 Nso#1209 1 +110292 Vg#h#1043 2 +85145 Vg#o#1128 3 +85145 Vg#oh#1070 72 +85145 Vg#oh#1055 6 +85145 Vg#oh#1051 51 +85145 Vg#o#1132 2 +12398 Nso#108 1 +13820 Vg#o#1132 1 +211356 Vg#h#1104 1 +43417 Vg#oh#1070 119 +43417 Vg#o#1132 2 +43417 Vg#o#1128 27 +105599 Tg#1238 1 +105599 Tg#1252 1 +105599 Tg#1251 1 +105599 Tg#1249 3 +141161 Sws#M#83 1 +216016 Nso#65 3 +216016 Nso#1209 3 +63800 Sse#M#62 1 +227183 Sse#M#63 1 +115290 Sse#1224 3 +128659 Vg#h#1104 22 +128659 Nw#R#50 2 +14174 Sse#1224 2 +31951 Vg#h#1104 1 +161507 Nw#L#160 1 +142193 Nse#L#153 1 +142193 Nso#1209 1 +58380 Vg#h#1043 2 +58380 Vg#o#1124 5 +58380 Vg#o#1160 1 +58380 Vg#oh#1070 7 +58380 Vg#oh#1055 22 +58380 Vg#oh#1051 104 +58380 Vg#h#1039 1 +58380 Vg#o#1132 15 +58380 Vg#o#1153 1 +16057 Vg#o#1128 1 +16057 Vg#oh#1070 1 +16057 Vg#o#1132 1 +159808 Nse#L#150 1 +159808 Vg#o#1128 1 +180895 Vg#h#1039 2 +180895 Vg#h#1043 1 +180895 Vg#o#1128 1 +63777 Sse#M#62 1 +106179 Swb#M#156 1 +106179 Swb#M#155 1 +106179 Sws#M#163 9 +21769 Vg#h#1038 1 +21769 Vg#h#1061 1 +106171 Swb#M#156 27 +106171 Swb#M#157 1 +106171 Swb#M#154 1 +106171 Swb#M#155 1 +106171 Sws#M#1234 3 +106171 Sws#M#163 18 +217644 Nso#107 5 +133702 Sse#M#169 2 +22125 Sse#M#64 1 +112352 Nso#106 1 +188898 Vg#o#1124 1 +37044 Nse#L#153 1 +102116 Nw#R#50 1 +54730 Vg#o#1153 2 +54730 Vg#oh#1070 26 +54730 Vg#o#1132 1 +54730 Sse#M#62 1 +54730 Vg#o#1128 4 +2917 Nso#108 1 +1797 Nso#107 6 +1797 Nso#108 1 +1797 Nso#1209 1 +1797 Nso#106 1 +133091 Vg#o#1124 1 +133091 Vg#o#1128 1 +133091 Vg#oh#1070 89 +133091 Vg#oh#1055 104 +133091 Vg#oh#1051 68 +133091 Vg#o#1153 1 +133091 Vg#o#1132 1 +207151 Nse#L#153 1 +45930 Swb#M#157 2 +45930 Sse#M#14 1 +45930 Sws#M#163 1 +45930 Sws#M#1230 1 +45930 Sse#M#62 8 +45930 Sse#M#64 2 +29453 Vg#o#1128 3 +29453 Vg#oh#1070 1 +29453 Vg#o#1132 1 +153549 Nso#106 2 +13016 Nso#108 3 +133416 Vg#o#1160 1 +84230 Sws#M#83 1 +70993 Sse#1224 2 +14575 Nso#65 2 +14575 Nso#1209 14 +216196 Nso#107 2 +216196 Nse#L#150 1 +216196 Nso#106 2 +169390 Sws#M#163 1 +182806 Vg#o#1124 5 +40639 Vg#oh#1070 18 +40639 Vg#oh#1055 6 +40639 Vg#o#1132 1 +202106 Sse#M#64 1 +205808 Nw#R#189 1 +205808 Nw#R#50 1 +133894 Nse#L#151 3 +133894 Nso#65 2 +133894 Nso#1209 14 +114912 Vg#o#1160 1 +94046 Nw#L#119 4 +94046 Nw#L#160 1 +94046 Sse#1224 1 +94046 Nso#107 1 +94046 Nw#R#50 1 +14754 Nso#65 1 +58645 Swb#M#156 1 +58645 Swb#M#155 1 +58645 Sws#M#83 2 +106810 Nse#L#98 7 +7634 Sse#M#14 4 +7634 Sws#M#1230 1 +7634 Sse#M#62 9 +7634 Sse#M#64 5 +178564 Vg#o#1153 1 +178564 Vg#o#1124 1 +226615 Nso#65 4 +226615 Nso#1209 2 +226615 Nse#L#152 3 +226615 Nso#108 3 +226615 Nso#106 1 +5793 Sse#M#75 1 +142449 Nso#65 1 +142449 Sws#M#163 1 +142449 Nw#R#50 4 +136894 Sws#M#1234 4 +136894 Sws#M#83 1 +28056 Vg#h#1104 1 +28056 Vg#o#1132 2 +13348 Vg#h#1104 7 +13348 Vg#oh#1055 1 +13348 Tg#1249 1 +212699 Nse#L#151 1 +137427 Nw#R#189 1 +197152 Vg#o#1128 1 +137423 Sws#M#1227 1 +137423 Swb#M#154 1 +105919 Nw#L#160 1 +105919 Nw#R#50 1 +7705 Sse#1224 2 +213627 Sws#M#1234 3 +213627 Swb#M#137 3 +213627 Sws#M#1227 7 +213627 Sws#M#1230 16 +213627 Sws#M#83 7 +213627 Sse#M#64 3 +109485 Nse#L#153 1 +1537 Tg#1251 1 +144055 Nso#1209 1 +91627 Nse#L#150 1 +91627 Nse#L#152 1 +171695 Nso#107 1 +103446 Nso#1209 1 +194236 Vg#o#1124 6 +63101 Sse#M#75 1 +63101 Sse#M#169 12 +63101 Sse#M#62 3 +63101 Sse#M#63 2 +130540 Sse#M#169 4 +130540 Sse#M#62 1 +109624 Vg#h#1104 1 +109624 Vg#h#1061 1 +109624 Vg#h#1043 1 +109624 Vg#o#1128 1 +189786 Vg#h#1039 3 +189786 Tg#1238 1 +224503 Nso#106 1 +233613 Nw#R#189 1 +15241 Swb#M#155 1 +151290 Swb#M#156 1 +145476 Tg#1251 2 +206683 Nso#1209 1 +55371 Sse#M#75 6 +55371 Sws#M#83 4 +55371 Sws#M#163 3 +55371 Sws#M#1234 8 +55371 Sws#M#1230 2 +55371 Sse#M#64 1 +214114 Nso#107 2 +145090 Nse#L#153 1 +223894 Nse#L#153 1 +181056 Vg#o#1132 1 +158569 Vg#h#1039 1 +158569 Vg#oh#1070 1 +158569 Tg#1251 2 +158569 Vg#o#1124 1 +31890 Sse#M#169 1 +31890 Sse#M#75 1 +112831 Sse#M#14 1 +112831 Sse#M#62 2 +112831 Sse#M#63 4 +44024 Nso#107 2 +60733 Nso#108 1 +111120 Tg#1252 1 +111120 Vg#h#1043 1 +111120 Vg#h#1061 1 +111120 Vg#h#1039 5 +215863 Nse#L#153 1 +217207 Sws#M#1227 6 +217207 Sws#M#163 3 +217207 Nw#L#160 5 +217207 Sws#M#1230 10 +217207 Nw#R#50 2 +39131 Nw#L#5 1 +159582 Sws#M#1234 1 +214163 Nso#1209 5 +186812 Vg#o#1128 1 +153747 Swb#M#137 1 +50882 Sws#M#163 1 +50882 Nso#65 1 +32355 Sse#M#64 1 +32355 Nse#L#150 1 +32355 Nso#107 1 +3468 Sws#M#163 2 +160549 Nse#L#98 1 +57713 Nw#R#50 1 +11402 Nso#107 1 +230904 Vg#o#1124 3 +105129 Sse#M#62 1 +209489 Nse#L#151 1 +209489 Nso#1209 2 +204006 Nso#65 6 +204006 Nso#1209 3 +101111 Swb#M#156 3 +101111 Swb#M#157 57 +101111 Swb#M#155 12 +101111 Sws#M#1227 5 +101111 Sws#M#163 63 +101111 Sws#M#83 36 +101111 Sws#M#1234 31 +101111 Swb#M#137 29 +101111 Sws#M#1230 6 +21798 Nso#65 1 +21798 Nw#L#160 1 +21798 Nw#L#5 1 +114725 Tg#1252 1 +114725 Tg#1249 3 +106366 Vg#h#1038 1 +106366 Vg#o#1132 1 +64393 Nso#107 2 +64393 Nso#65 3 +150722 Nse#L#151 1 +1885 Nse#L#153 1 +135391 Vg#o#1132 2 +135391 Vg#o#1124 3 +199640 Vg#h#1039 1 +128221 Sse#M#169 2 +36271 Tg#1251 1 +36271 Vg#h#1061 1 +36271 Vg#h#1039 1 +218114 Nse#L#150 1 +133675 Vg#o#1124 8 +104054 Swb#M#154 1 +104054 Nw#L#119 2 +104054 Nso#65 5 +104054 Nw#R#189 3 +104054 Nso#1209 1 +154140 Nso#107 1 +195102 Vg#o#1128 1 +195102 Vg#h#1039 3 +195102 Vg#h#1043 2 +195102 Vg#h#1061 1 +104029 Tg#1251 1 +204762 Sws#M#163 3 +204762 Sws#M#1234 2 +204762 Swb#M#137 9 +57445 Nse#L#153 1 +57445 Sws#M#83 3 +107566 Swb#M#156 3 +107566 Swb#M#154 1 +45580 Nw#L#5 2 +45580 Nso#1209 2 +45580 Nso#107 1 +45580 Nw#L#119 2 +42281 Nse#L#98 1 +42281 Nse#L#150 1 +142208 Vg#h#1061 1 +53707 Nw#L#160 1 +53707 Sse#M#169 1 +57448 Sws#M#83 6 +104871 Nso#107 1 +104871 Nso#1209 2 +135808 Vg#o#1124 10 +18609 Nso#108 1 +18609 Nse#L#151 1 +147938 Nso#1209 1 +114374 Nso#108 4 +85152 Nso#1209 1 +176038 Vg#h#1104 2 +176038 Vg#h#1043 1 +176038 Vg#o#1128 1 +104439 Vg#h#1061 2 +95582 Sws#M#83 2 +23625 Vg#o#1132 1 +176744 Sws#M#1234 1 +176744 Sws#M#1230 7 +196583 Vg#o#1124 2 +196583 Vg#o#1128 1 +71422 Vg#h#1038 1 +71422 Vg#h#1104 5 +71422 Tg#1252 1 +113101 Nse#L#151 3 +113101 Nse#L#150 1 +113101 Nso#108 2 +113101 Nso#106 2 +113101 Nso#65 2 +113101 Nso#1209 7 +90217 Vg#o#1128 7 +41204 Vg#o#1132 1 +204506 Sse#M#62 1 +44294 Vg#oh#1070 1 +44294 Vg#o#1132 2 +44294 Vg#o#1128 1 +211984 Vg#o#1128 2 +14108 Vg#o#1124 4 +99789 Vg#h#1061 1 +165132 Vg#h#1104 3 +96909 Sse#M#64 1 +143838 Sws#M#83 3 +213179 Tg#1238 1 +21772 Vg#o#1132 6 +21772 Vg#o#1124 2 +112183 Sse#M#169 1 +112183 Sse#M#62 1 +112183 Sse#M#63 1 +63120 Sse#M#169 6 +63120 Sse#M#62 2 +63120 Sse#M#63 1 +63120 Sse#M#75 24 +112186 Vg#h#1043 3 +112186 Vg#h#1061 2 +112186 Vg#h#1039 1 +112186 Vg#o#1132 1 +51154 Swb#M#156 2 +51154 Swb#M#154 2 +221463 Nso#65 1 +221463 Nso#1209 7 +204430 Nso#65 3 +204430 Nso#1209 1 +204430 Nse#L#153 1 +113744 Nso#108 3 +113744 Nso#106 1 +231910 Sws#M#163 5 +112973 Nso#65 4 +112973 Nso#1209 1 +112973 Nse#L#151 1 +112973 Nso#108 1 +112973 Nso#106 1 +112341 Swb#M#155 1 +99322 Nso#1209 1 +41135 Swb#M#156 1 +41135 Swb#M#157 1 +41135 Swb#M#155 2 +41135 Sws#M#1227 1 +112160 Nso#108 3 +112160 Nso#1209 1 +138944 Nw#R#189 1 +138944 Vg#o#1132 1 +138944 Nso#108 5 +95512 Swb#M#155 3 +95512 Sws#M#163 1 +95512 Sws#M#1230 1 +41482 Vg#o#1128 2 +41482 Vg#oh#1070 11 +41482 Vg#o#1132 6 +44184 Vg#o#1132 1 +44185 Sws#M#163 1 +14611 Nso#65 2 +14611 Sse#1224 1 +14611 Sse#M#169 1 +83226 Vg#h#1043 8 +83226 Vg#h#1104 8 +83226 Vg#o#1124 2 +83226 Vg#o#1128 1 +83226 Tg#1251 2 +83226 Vg#o#1160 1 +83226 Vg#oh#1070 2 +83226 Vg#oh#1055 4 +83226 Vg#oh#1051 15 +83226 Vg#o#1132 3 +83226 Tg#1249 1 +83226 Vg#o#1153 3 +83226 Tg#1252 1 +79121 Sse#M#63 1 +79121 Nse#L#153 1 +205747 Vg#h#1061 1 +106618 Vg#h#1038 46 +106618 Vg#h#1104 1 +44052 Nse#L#150 1 +133406 Vg#oh#1070 1 +133406 Vg#o#1124 15 +133406 Vg#oh#1055 1 +109780 Vg#h#1043 2 +179958 Vg#o#1124 5 +111225 Vg#h#1061 1 +184145 Nso#65 1 +34346 Tg#1252 1 +143496 Tg#1252 1 +196518 Vg#o#1128 1 +29534 Swb#M#155 1 +27500 Tg#1249 4 +143122 Tg#1249 1 +1430 Nso#1209 1 +29533 Nw#R#50 1 +114291 Swb#M#154 2 +114291 Swb#M#155 2 +28552 Sse#M#169 1 +216735 Vg#h#1038 5 +216735 Vg#oh#1070 16 +216735 Vg#o#1132 1 +216735 Vg#h#1043 3 +216735 Vg#o#1128 5 +37128 Tg#1249 1 +172802 Sse#M#14 1 +172802 Sse#M#62 1 +172802 Sse#M#63 1 +136728 Nso#1209 2 +40287 Nso#1209 1 +40287 Nse#L#153 1 +40287 Nso#107 1 +156660 Nso#107 1 +156660 Nw#R#50 2 +212685 Vg#h#1104 1 +212685 Vg#o#1124 12 +209792 Nw#R#189 1 +77895 Tg#1249 3 +61960 Sse#M#75 1 +195962 Nw#R#189 2 +195962 Sws#M#1234 3 +195962 Swb#M#137 2 +195962 Sws#M#83 13 +188863 Vg#o#1124 2 +51535 Tg#1238 6 +51535 Vg#o#1132 2 +51535 Tg#1249 1 +74641 Sse#1224 5 +7710 Sws#M#83 4 +7820 Vg#h#1104 1 +7820 Nw#L#5 1 +7820 Nse#L#152 1 +7820 Nso#108 1 +109491 Vg#o#1128 1 +109491 Vg#h#1043 2 +109491 Vg#o#1160 1 +109491 Vg#oh#1070 1 +109491 Vg#h#1038 1 +109491 Vg#h#1039 2 +109491 Vg#o#1132 1 +132286 Nw#L#119 1 +132286 Nse#L#151 1 +132286 Nse#L#150 2 +132286 Nso#1209 4 +132286 Nso#65 1 +132286 Nw#R#189 1 +188467 Sws#M#1230 1 +188467 Nse#L#153 1 +148915 Vg#o#1160 1 +148915 Tg#1251 3 +148915 Vg#o#1128 1 +78294 Tg#1238 2 +78294 Tg#1249 1 +78294 Vg#h#1061 1 +216234 Nso#107 1 +216235 Nw#L#160 1 +50283 Sse#M#75 1 +50283 Sse#M#169 1 +50283 Sse#M#62 1 +110842 Vg#h#1043 4 +219565 Nso#1209 1 +151687 Sse#M#63 1 +110847 Vg#h#1038 1 +110847 Vg#o#1132 1 +110847 Vg#h#1061 1 +31333 Vg#o#1124 2 +90268 Tg#1249 1 +131826 Sse#M#169 1 +214698 Nso#1209 2 +206850 Sws#M#163 1 +88595 Nse#L#152 1 +132757 Vg#h#1104 1 +132757 Vg#o#1124 2 +230638 Swb#M#156 3 +230638 Swb#M#157 2 +230638 Swb#M#154 1 +230638 Sws#M#163 4 +230638 Sws#M#1234 2 +230638 Sws#M#1230 1 +136385 Swb#M#155 1 +136385 Vg#oh#1055 4 +136385 Vg#o#1132 4 +28952 Vg#h#1043 2 +129841 Vg#o#1124 1 +79476 Nso#106 1 +79476 Nse#L#98 1 +79476 Nso#108 6 +79476 Nw#R#50 1 +167534 Nse#L#151 1 +110609 Nso#1209 1 +210102 Vg#oh#1070 1 +210102 Vg#o#1132 1 +19082 Tg#1249 1 +22115 Vg#o#1132 1 +16058 Vg#h#1039 1 +100296 Vg#h#1039 1 +109546 Tg#1238 1 +109546 Tg#1249 1 +109546 Vg#h#1043 1 +155720 Swb#M#155 4 +149860 Tg#1252 2 +149860 Tg#1251 1 +25317 Vg#o#1132 2 +25317 Vg#o#1124 1 +88772 Swb#M#154 1 +128382 Vg#h#1038 1 +182658 Vg#o#1128 1 +189688 Vg#o#1124 16 +110020 Tg#1251 2 +23427 Nw#L#119 1 +23427 Nw#L#160 2 +23427 Nw#L#5 5 +23427 Nw#R#189 7 +23427 Nw#R#50 6 +230759 Vg#o#1124 4 +161093 Swb#M#154 3 +4009 Tg#1238 27 +4009 Tg#1251 5 +4009 Tg#1249 24 +4009 Tg#1252 17 +195985 Vg#o#1124 5 +226796 Nso#65 2 +226796 Nse#L#152 2 +226796 Nso#1209 1 +226796 Nso#106 2 +144987 Nse#L#152 1 +144987 Nso#107 1 +144987 Nso#106 1 +233630 Sws#M#83 6 +223032 Nso#1209 1 +223032 Nso#107 1 +159595 Sse#M#62 1 +159595 Nse#L#153 1 +25004 Vg#h#1104 5 +25004 Vg#o#1124 2 +25004 Vg#h#1043 2 +25004 Vg#h#1061 2 +25004 Vg#o#1132 4 +183870 Vg#o#1124 1 +150154 Tg#1251 2 +30315 Sse#M#63 1 +65700 Nso#107 2 +65700 Nso#106 2 +60575 Nso#1209 1 +60573 Swb#M#155 2 +60573 Nse#L#152 2 +60573 Sws#M#83 1 +139167 Vg#oh#1055 58 +139167 Vg#h#1039 1 +179297 Vg#o#1124 1 +179297 Vg#o#1153 2 +4715 Nse#L#150 1 +4715 Sse#M#75 1 +58609 Nw#R#189 2 +58609 Swb#M#156 1 +58609 Nw#L#119 1 +58609 Sse#M#64 1 +58609 Sws#M#83 1 +84842 Swb#M#156 1 +84842 Sws#M#83 1 +140764 Sse#1224 1 +140764 Sse#M#75 12 +105115 Sws#M#1234 4 +105115 Sws#M#163 8 +105115 Sws#M#1227 1 +105115 Swb#M#137 2 +105115 Sws#M#1230 4 +105115 Sws#M#83 3 +77707 Tg#1238 1 +77707 Tg#1249 1 +142837 Nw#L#119 3 +142837 Nso#108 1 +142837 Nso#107 1 +142837 Nw#R#50 1 +124930 Nw#L#5 3 +124930 Nw#L#160 2 +124930 Nw#R#50 3 +124930 Sws#M#1227 3 +124930 Sws#M#83 17 +124930 Nw#R#189 3 +124930 Sws#M#1234 1 +124930 Sws#M#1230 28 +150083 Tg#1251 32 +220363 Swb#M#157 1 +51880 Vg#oh#1070 5 +51880 Vg#oh#1055 1 +51880 Vg#o#1124 5 +38895 Vg#o#1153 9 +38895 Vg#h#1038 2 +38895 Vg#h#1039 62 +38895 Vg#h#1043 5 +38895 Vg#o#1132 1 +38895 Vg#o#1128 14 +9474 Vg#o#1132 1 +85092 Nso#108 1 +130062 Sse#M#169 1 +74387 Nw#L#5 1 +74387 Sws#M#83 3 +225891 Nw#L#160 1 +113628 Nso#108 1 +113628 Nso#1209 2 +196985 Vg#o#1124 3 +113023 Nso#65 1 +113023 Nso#108 1 +113023 Sse#1224 1 +135349 Tg#1249 1 +2483 Nw#L#160 1 +111332 Vg#o#1124 2 +104511 Vg#h#1104 5 +104511 Vg#h#1061 1 +52441 Vg#o#1124 8 +52441 Vg#o#1132 1 +70612 Nso#108 1 +70612 Nso#107 2 +70612 Nso#1209 8 +235094 Vg#o#1124 1 +159133 Sws#M#1234 2 +159131 Sse#1224 2 +42071 Nso#108 1 +105640 Nse#L#151 2 +105640 Nse#L#153 1 +105640 Nse#L#152 2 +105640 Nso#108 1 +105640 Sws#M#83 8 +105640 Nso#1209 1 +105647 Vg#h#1038 3 +134526 Nse#L#98 1 +134526 Nse#L#151 1 +158424 Sse#M#169 1 +158424 Sse#M#63 1 +158424 Nse#L#150 1 +159353 Sse#M#63 1 +231888 Swb#M#156 4 +231888 Swb#M#157 8 +231888 Sws#M#1234 2 +231888 Swb#M#155 4 +231888 Sws#M#1227 1 +231888 Nw#L#160 2 +231888 Sws#M#1230 3 +231888 Sws#M#83 1 +149167 Tg#1238 2 +149167 Tg#1251 1 +134631 Vg#o#1124 3 +68979 Swb#M#156 27 +68979 Swb#M#157 4 +68979 Swb#M#154 6 +68979 Swb#M#155 1 +68979 Sws#M#1234 1 +68979 Sws#M#163 16 +68979 Swb#M#137 6 +231395 Nse#L#151 1 +152808 Nso#65 5 +152808 Nso#107 4 +152808 Nso#1209 1 +210598 Nso#107 1 +210598 Nso#1209 2 +163639 Vg#o#1124 9 +159862 Nso#1209 1 +164940 Vg#h#1038 1 +164940 Vg#o#1124 1 +101811 Nse#L#151 1 +101811 Nse#L#150 1 +107427 Tg#1238 1 +107427 Tg#1249 2 +17494 Vg#h#1061 1 +17494 Vg#o#1132 2 +17494 Vg#o#1124 2 +197958 Vg#h#1043 4 +197958 Vg#o#1128 1 +197958 Vg#h#1061 2 +197958 Vg#oh#1055 1 +197958 Vg#oh#1051 3 +197958 Vg#h#1039 1 +106114 Swb#M#156 1 +106114 Swb#M#154 1 +148781 Tg#1251 1 +203188 Sse#1224 1 +149095 Tg#1251 1 +162129 Vg#o#1128 1 +162129 Vg#o#1160 13 +162129 Vg#oh#1070 28 +162129 Vg#oh#1055 9 +162129 Vg#oh#1051 31 +162129 Vg#o#1132 7 +18619 Vg#o#1132 1 +18619 Vg#o#1124 2 +18619 Vg#oh#1055 1 +168711 Vg#h#1104 2 +168711 Vg#o#1124 12 +168711 Vg#h#1061 1 +168711 Vg#o#1160 1 +168711 Vg#oh#1070 91 +168711 Vg#oh#1055 109 +168711 Vg#oh#1051 179 +168711 Vg#o#1132 7 +168711 Vg#o#1153 1 +15734 Vg#o#1124 2 +1833 Nso#65 6 +1833 Nso#108 13 +1833 Nso#1209 3 +1833 Nso#107 2 +1833 Nso#106 1 +40972 Nse#L#153 2 +33272 Vg#h#1104 2 +33272 Nso#108 1 +176865 Vg#h#1104 1 +2879 Nso#108 1 +62668 Nse#L#152 1 +14604 Swb#M#155 1 +14604 Nso#1209 2 +78199 Tg#1249 1 +62666 Sse#1224 1 +17111 Nse#L#153 2 +17111 Nse#L#152 1 +78421 Nse#L#150 1 +160388 Swb#M#156 4 +160388 Swb#M#155 5 +219177 Nso#107 1 +219177 Nse#L#151 1 +219177 Nse#L#153 2 +219177 Nse#L#152 1 +219177 Nso#1209 1 +37339 Sse#M#63 2 +230403 Tg#1251 2 +223862 Sse#M#62 1 +144409 Nse#L#151 1 +144409 Nso#1209 1 +89632 Nse#L#153 1 +29495 Vg#o#1132 3 +68974 Swb#M#156 2 +68974 Swb#M#157 5 +68974 Swb#M#155 1 +68974 Sws#M#163 14 +68974 Sws#M#1234 3 +68974 Swb#M#137 15 +68974 Sws#M#1230 4 +68974 Sws#M#83 6 +179439 Vg#o#1124 1 +144674 Swb#M#157 15 +144674 Swb#M#154 2 +42538 Vg#o#1153 1 +42538 Vg#h#1043 2 +136664 Nso#1209 2 +142556 Sws#M#1227 4 +142556 Sws#M#1230 1 +151616 Nso#1209 1 +151616 Sws#M#83 1 +2801 Nw#L#119 4 +2801 Nse#L#153 1 +112888 Nso#65 4 +112888 Nso#108 2 +112888 Nso#107 1 +1429 Nso#1209 1 +28012 Vg#o#1153 1 +156470 Sws#M#83 1 +10661 Sse#M#14 1 +10661 Sws#M#163 2 +23520 Sse#1224 2 +67341 Nso#108 2 +87238 Vg#oh#1070 7 +87238 Vg#o#1132 1 +224671 Nse#L#98 1 +197133 Vg#oh#1070 28 +197133 Vg#o#1132 1 +197133 Vg#o#1128 1 +165982 Vg#o#1160 1 +165982 Vg#h#1061 2 +62941 Sws#M#1230 1 +197139 Vg#h#1043 1 +197139 Vg#o#1124 11 +230895 Nw#R#189 2 +111319 Tg#1238 3 +111319 Tg#1252 1 +111319 Tg#1251 1 +111319 Tg#1249 1 +111184 Nso#1209 1 +13743 Tg#1251 1 +205944 Tg#1249 1 +184084 Vg#oh#1070 2 +156878 Sse#M#63 1 +156878 Nse#L#151 1 +216930 Nso#108 1 +111325 Vg#h#1038 1 +111325 Vg#h#1039 1 +111325 Tg#1249 1 +111325 Vg#h#1043 2 +114488 Nse#L#153 1 +114488 Nse#L#152 1 +114488 Vg#h#1061 2 +199514 Vg#o#1124 2 +199514 Vg#oh#1055 1 +199514 Vg#oh#1051 2 +212541 Nse#L#98 3 +114481 Nw#L#160 1 +110103 Nse#L#153 1 +107235 Vg#o#1124 1 +8562 Sws#M#163 2 +8562 Nso#108 1 +114246 Nso#108 2 +182630 Vg#o#1124 7 +132587 Vg#o#1124 1 +25641 Swb#M#156 3 +25641 Swb#M#154 7 +25641 Swb#M#155 1 +25641 Sws#M#163 1 +25641 Nso#108 2 +25641 Nso#107 1 +25641 Nso#106 1 +145786 Nso#65 1 +165410 Vg#o#1132 1 +165410 Tg#1238 2 +165410 Tg#1251 1 +165410 Vg#h#1043 5 +177523 Sws#M#163 2 +151438 Nse#L#153 1 +108381 Vg#h#1043 2 +108381 Vg#oh#1055 2 +206486 Nw#L#5 1 +35556 Nso#108 1 +35556 Sse#M#63 1 +35556 Nso#107 2 +35556 Tg#1249 1 +55354 Nso#108 2 +55354 Nso#106 1 +170434 Swb#M#154 7 +170434 Nse#L#153 1 +170434 Nso#108 1 +170434 Nw#R#50 2 +114068 Nso#108 2 +69942 Vg#h#1104 1 +69942 Vg#o#1132 1 +69942 Vg#o#1128 1 +144326 Tg#1249 4 +13708 Nse#L#150 1 +52873 Sse#M#64 1 +65673 Sse#M#63 1 +65673 Nso#107 1 +4015 Vg#h#1039 1 +4015 Tg#1238 1 +4015 Tg#1251 2 +4015 Tg#1249 17 +4015 Tg#1252 11 +11398 Nse#L#150 2 +11398 Nso#1209 1 +213547 Nw#L#160 1 +19549 Sse#M#14 3 +19549 Nse#L#153 1 +19549 Sse#M#62 15 +19549 Sse#M#63 9 +177014 Vg#h#1104 1 +177014 Vg#o#1124 11 +181174 Vg#oh#1055 1 +141312 Nse#L#151 2 +141312 Nse#L#150 2 +141312 Nso#1209 2 +114668 Tg#1238 4 +114668 Tg#1249 2 +6128 Nw#L#119 1 +193690 Vg#o#1124 18 +111100 Nso#1209 1 +81523 Tg#1249 2 +104678 Nso#107 3 +99301 Swb#M#156 3 +99301 Sws#M#163 12 +99301 Swb#M#137 7 +227331 Nso#106 1 +119981 Nso#65 1 +119981 Nse#L#151 1 +119981 Nso#106 1 +52016 Sws#M#1234 1 +52016 Nw#L#5 1 +32020 Vg#o#1124 28 +32020 Vg#h#1043 1 +32020 Vg#oh#1055 1 +32020 Vg#oh#1051 1 +32020 Vg#o#1132 1 +211503 Nso#1209 1 +14094 Vg#o#1124 4 +1563 Nse#L#150 1 +1563 Nso#108 2 +1563 Nso#106 1 +1563 Sws#M#83 1 +99302 Nso#107 1 +204742 Nse#L#151 1 +204742 Nso#1209 1 +9349 Vg#o#1153 1 +52759 Vg#o#1132 1 +52759 Vg#o#1124 1 +167146 Tg#1249 1 +167146 Nso#108 2 +105079 Nso#108 1 +105079 Sse#M#63 1 +25580 Swb#M#137 2 +161280 Nse#L#151 1 +161280 Nse#L#152 1 +199686 Vg#o#1128 1 +17020 Nso#106 1 +17020 Tg#1252 1 +124743 Swb#M#157 1 +208234 Nso#108 1 +144247 Tg#1252 1 +25420 Swb#M#157 7 +25420 Swb#M#155 1 +25420 Sws#M#163 8 +25420 Sws#M#1234 1 +25420 Swb#M#137 12 +25420 Sws#M#1230 5 +25420 Sse#M#62 1 +25420 Sse#M#63 2 +25420 Sse#M#64 1 +25420 Sws#M#83 4 +113166 Nse#L#152 2 +113166 Nse#L#98 1 +113166 Sse#M#169 1 +12396 Nw#R#50 1 +109586 Vg#o#1124 1 +109586 Vg#h#1061 1 +190451 Vg#oh#1070 57 +190451 Vg#oh#1055 45 +190451 Vg#oh#1051 30 +155077 Nw#L#160 1 +155077 Nw#R#50 1 +134601 Vg#o#1124 23 +135739 Tg#1238 1 +135739 Tg#1252 2 +203425 Tg#1238 1 +203425 Tg#1251 1 +203425 Tg#1252 1 +14128 Vg#o#1132 1 +63107 Sse#M#169 3 +39689 Tg#1251 1 +39689 Sse#M#169 2 +39689 Sse#M#75 1 +39689 Sws#M#1230 1 +39689 Nso#108 3 +39689 Nso#65 1 +39689 Sse#1224 1 +63104 Sse#M#169 3 +63104 Sse#M#75 4 +205757 Swb#M#155 3 +205757 Vg#o#1160 138 +205757 Vg#oh#1070 3 +205757 Vg#oh#1055 7 +205757 Vg#oh#1051 1 +205757 Vg#o#1132 5 +205757 Vg#o#1153 244 +142033 Tg#1249 4 +142033 Tg#1252 1 +147741 Tg#1251 5 +37034 Nso#108 1 +37034 Nso#107 1 +37034 Nso#106 1 +163284 Nw#L#160 2 +163284 Nso#1209 2 +163284 Nso#106 1 +148790 Nso#106 1 +68416 Vg#h#1038 1 +68416 Vg#o#1132 3 +193755 Vg#h#1038 1 +66994 Nso#108 1 +184850 Vg#oh#1070 3 +184850 Vg#oh#1055 1 +184850 Vg#oh#1051 8 +184850 Vg#o#1132 1 +160555 Sse#M#75 1 +195325 Vg#o#1124 1 +71875 Nso#107 2 +62679 Sse#1224 2 +222035 Nso#1209 1 +112960 Nso#108 2 +62675 Sse#1224 1 +132912 Sse#M#169 2 +106633 Nso#65 1 +106633 Nse#L#150 1 +182108 Vg#o#1124 6 +182108 Vg#h#1039 8 +182108 Vg#o#1132 3 +162867 Vg#o#1128 1 +137271 Sws#M#1230 1 +109541 Tg#1251 1 +109541 Vg#oh#1055 24 +69764 Sse#M#62 1 +115032 Nw#L#5 1 +2818 Sse#M#62 1 +2818 Sse#M#63 4 +133193 Vg#h#1039 1 +133193 Vg#o#1124 13 +128453 Swb#M#155 1 +128453 Nse#L#152 1 +128453 Sws#M#163 1 +128453 Nso#108 1 +128453 Nw#R#50 1 +175099 Vg#h#1038 1 +210079 Sws#M#1230 1 +175094 Vg#o#1124 3 +175650 Vg#h#1039 1 +216781 Nso#65 1 +216781 Nso#1209 1 +29792 Nse#L#152 1 +29792 Nse#L#98 1 +136708 Sse#M#63 1 +78074 Tg#1249 1 +129193 Nso#108 2 +129193 Nso#1209 8 +1740 Nse#L#151 1 +1740 Nse#L#153 1 +1740 Nso#108 1 +1740 Nso#107 1 +1740 Nso#65 4 +1740 Nso#1209 4 +1743 Swb#M#154 6 +1743 Nse#L#151 1 +1743 Nso#1209 2 +114327 Nso#108 1 +114327 Nso#1209 7 +8178 Sse#M#75 1 +169294 Nw#R#50 1 +169294 Sws#M#83 5 +169777 Sws#M#1227 1 +86555 Vg#h#1043 26 +113259 Nso#108 2 +113125 Nso#65 2 +113125 Sse#M#64 1 +113125 Nso#108 6 +113125 Nw#R#50 1 +114578 Tg#1238 1 +114816 Tg#1249 3 +114816 Tg#1238 3 +129226 Swb#M#155 1 +129226 Sws#M#1234 1 +129226 Sws#M#163 2 +114813 Vg#h#1038 1 +114813 Vg#o#1128 1 +99342 Nse#L#150 1 +110824 Vg#h#1104 26 +110824 Vg#h#1061 1 +97946 Vg#o#1153 3 +97946 Vg#oh#1070 6 +97946 Vg#o#1132 3 +97946 Vg#o#1124 1 +110829 Tg#1251 1 +110829 Vg#h#1061 3 +110829 Vg#h#1039 3 +110829 Vg#o#1132 1 +144881 Tg#1252 1 +48088 Vg#h#1104 5 +48088 Vg#o#1132 1 +5798 Nw#L#119 1 +5798 Sse#M#14 1 +5798 Nse#L#152 1 +5798 Nw#R#50 3 +5798 Sws#M#163 3 +5798 Sse#M#62 2 +5798 Sse#M#64 1 +136672 Sws#M#163 6 +136672 Swb#M#137 2 +111849 Nse#L#98 2 +111849 Sse#M#62 1 +111849 Sse#M#169 1 +1295 Sse#M#62 1 +1295 Sse#M#63 2 +1295 Sse#M#64 1 +155851 Vg#h#1104 2 +137507 Nso#1209 2 +137507 Nso#106 1 +154145 Swb#M#154 4 +154145 Sws#M#163 2 +154145 Sws#M#1234 1 +154145 Sws#M#83 2 +104638 Sws#M#1230 1 +104638 Sse#M#75 1 +104638 Sws#M#83 6 +132008 Vg#o#1124 10 +76891 Nse#L#153 1 +2588 Sse#M#62 1 +216714 Nso#107 1 +105813 Vg#o#1153 9 +105813 Vg#oh#1070 15 +105813 Vg#h#1104 1 +105813 Vg#h#1061 1 +105813 Vg#o#1132 2 +105813 Vg#o#1124 1 +28192 Vg#h#1104 1 +7461 Sse#1224 2 +7461 Sse#M#62 2 +7461 Nso#107 1 +91272 Swb#M#156 9 +91272 Swb#M#157 1 +91272 Sse#M#62 1 +91272 Sws#M#83 2 +103090 Nse#L#98 1 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/demo_input/testFeatureMetadata.pcl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/demo_input/testFeatureMetadata.pcl Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,8 @@ +ID taxonomy_0 taxonomy_1 taxonomy_2 taxonomy_3 taxonomy_4 taxonomy_5 700098986 700098984 700098980 700098988 700037470 700037472 700037474 700037476 +TID NA NA NA NA NA NA 700098986 700098984 700098980 700098988 700037470 700037472 700037474 700037476 +STSite NA NA NA NA NA NA L_Antecubital_fossa R_Retroauricular_crease Subgingival_plaque R_Antecubital_fossa L_Retroauricular_crease R_Retroauricular_crease L_Antecubital_fossa R_Antecubital_fossa +72 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 1.23 0 12 0 6 0 2 1 +4904 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 0 10 43 6 0 23 0 1 +1361 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 3 0 29 0 45 0 1 1 +3417 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 0 45 34 3 0 0 0 1 +1368 Bacteria Firmicutes Bacilli Bacillales Bacillaceae unclassified 5 0 2 0 6 0 1 1 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/docs/PCL-Description.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/docs/PCL-Description.txt Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,17 @@ +Although this example is delimited with spaces for consistent visualization, in a PCL file each element would be delimited by one tab. + +Here is an example of a very small PCL file. + +ID Kingdom Genus Sample 1 Sample 2 +Cohort NA NA Test Control +Age NA NA 34 43 +1232 Bacteria Bacteroides .23 .16 +543 Bacteria Dorea .001 .0021 + +These are the different parts of the PCL file. + +ID Feature metadata ID Last feature metadata ID sample ID sample ID +Metadata ID NA NA sample metadata sample metadata +Last metadata ID NA NA sample metadata sample metadata +Feature ID Feature (row) metadata Feature (row) metadata Data measurement Data measurement +Feature ID Feature (row) metadata Feature (row) metadata Data measurement Data measurement diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/docs/Tutorial-BreadCrumbs.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/docs/Tutorial-BreadCrumbs.md Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,306 @@ +# BreadCrumbs Tutorial # + +This is a brief tutorial to get you acquainted with the scripts provided in breadcrumbs. This tutorial is oragnized by script and task. Examples are given using files in the demo_input folder which is included in the BreadCrumbs package. Each of these commands should work from the command line in the breadcrumbs directory. + +Please note all of the following calls expect you to be in the breadcrumbs directory and to have both the ./breadcrumbs/src and ./breadcrumbs/scripts in your path and or python path. + +Enjoy and happy research! + +## Contents: ## +1. scriptPCoA +2. scriptManipulateTable.py +I. Manipulating the measurements +II. Filtering +III. Filtering with knowledge of feature hierarchical relationship +IV. Manipulate samples by metadata +V. Manipulate the feature names +3. scriptPlotFeature.py +4. scriptBiplotTSV.R +5. scriptConvertBetweenBIOMAndPCL.py + +## scriptPCoA.py ## +This script allows one to plot a PCoA of an abundance table. In the plot each sample is one marker. The marker shape and color is determined by a metadata (of your choice). The distances between each sample is determined by a specific beta-diversity distance metric. By default Bray-curtis distance is used. This can be changed as needed. You will notice for every call you must give it the sample id (-i) and the last metadata which should be the row before your first data (-l). This helps the scripts understand what is a data measurement and what is a metadata. + +A. How do I make a PCoA of an abundance table, painting (coloring) it by a specific metadata? + +> scripts/scriptPcoa.py -i TID -l STSite -p STSite demo_input/Test.pcl + +B. How do I make a series of PCoAs of an abundance table, one PCoA for every metadata? + +If nothing is specified with -p then all metadata are painted. Note there are a max of 9 shapes to use, a metadata will be skipped if it has more than 9 levels (specific values which can be used many times). Don't worry, the script will let you know if this happens and will just skip to the next metadata. + +> scripts/scriptPcoa.py -i TID -l STSite demo_input/Test.pcl + +C. How do I use a different beta-diversity distance metric instead of Bray-curtis distance? +The following metrics can be choosen: braycurtis, canberra, chebyshev, cityblock, correlation, cosine, euclidean, hamming, sqeuclidean, unifrac_unweighted, unifrac_weighted + +> scripts/scriptPcoa.py -i TID -l STSite -m sqeuclidean demo_input/Test.pcl + +D. How do I get the coordinates of the points in the PCoA plot? Use -C and give a file path to which to write. + +> scripts/scriptPcoa.py -i TID -l STSite -C coordinates.txt demo_input/Test.pcl + +E. How do I get the distance matrix represented by the PCoA plot? Use -D and give a file path to which to write. + +> scripts/scriptPcoa.py -i TID -l STSite -D distances.txt demo_input/Test.pcl + +F. How do I make a PCoA using unifrac type metrics. + +> scripts/scriptPcoa.py -m unifrac_weighted -t demo_input/GreenGenesCore-May09.ref.tre -e demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map.txt -c demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map-colors.txt +> scripts/scriptPcoa.py -m unifrac_unweighted -t demo_input/GreenGenesCore-May09.ref.tre -e demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map.txt -c demo_input/fastunifrac_Ley_et_al_NRM_2_sample_id_map-colors.txt + +There already exists a collection of functionality surrounding unifrac distances in Qiime and related software. We support these metrics here for completeness, if your need is not met here, please look into Qiime and related software for a solutions with a more rich collection of functionality. + +## scriptManipulateTable.py ## +Abundance tables can be difficult to manipulate. This script captures frequent tasks that may be important to manipulating an abundance table including normalization, summing, filtering, stratifying the tables into subsets (for instance breaking up a large HMP table into tables, one for each body site), and other functionality. You will notice for every call you must give it the sample id (-i) and the last metadata which should be the row before your first data (-l). This helps the scripts understand what is a data measurement and what is a metadata. + +_Remember you can do multiple tasks or use multiple arguments at the same time._ +_Here is an example of summing, normalizing, adding on clade prefixes, and stratifies the tables based on the STSite metadata_ + +> scripts/scriptManipulateTable.py -i TID -l STSite -s -n -x -y STSite demo_input/Test.pcl + +Please look at the detailed description of normalization and summation for a clear understanding of how the data is being manipulated. + +*Manipulating the measurements* +A. How do I sum a table based on clade names? + +> scripts/scriptManipulateTable.py -i TID -l STSite -s demo_input/Test.pcl + +B. How do I normalize a table? + +> scripts/scriptManipulateTable.py -i TID -l STSite -n demo_input/Test.pcl + +*Filtering* +C. How do I filter a normalized table by percentage? + +This filters out bugs that are not in the top 0.95 percentage of at least 0.05 percent of the samples (a good default). + +> scripts/scriptManipulateTable.py -i TID -l STSite -P 0.95,0.05 demo_input/Test.pcl + +D. How do I filter a normalized table by a minimum abundance? + +This filters out bugs that do not have at least a certain number of bugs in a certain number of samples. Here we show +the call to filter out all bugs which do not have at least 3 samples with at least 0.0001 abundance (a good initial default). + +> scripts/scriptManipulateTable.py -i TID -l STSite -A 0.0001,3 demo_input/Test.pcl + +E. How do I filter a count table by count occurrence? + +This removes samples that do not have at least 5 counts in at least 3 samples (an initial default to use could be 2,2). + +> scripts/scriptManipulateTable.py -i TID -l STSite -O 5,3 demo_input/Test.pcl + +F. How do I filter a table by standard deviation? + +> scripts/scriptManipulateTable.py -i TID -l STSite -D 1 demo_input/Test.pcl + +*Filtering with knowledge of feature hierarchical relationship.* +F. How do I make the table have only terminal nodes? + +> scripts/scriptManipulateTable.py -i TID -l STSite -t demo_input/Test.pcl + +G. How do I remove all the OTUs from a table? + +> scripts/scriptManipulateTable.py -i TID -l STSite -u demo_input/Test.pcl + +H. How do I reduce all bugs more specific than a certain clade? Aka, how do I reset a table to be only a clade (genus) or higher? + +This reduces the bugs to bugs with 3 levels of hierarchy or less (class on a standard biological taxonomy). + +> scripts/scriptManipulateTable.py -i TID -l STSite -c 3 demo_input/Test.pcl + +You may want to hierarchically sum all of you bugs before reducing the table to a certain level, just in case you are missing some. + +> scripts/scriptManipulateTable.py -i TID -l STSite -s -c 3 demo_input/Test.pcl + +Detail. OTUs or taxonomic clades are terminal nodes of a dendrogram representing the full taxonomy or phylogeny of a study. Biology may happen at these terminal clades or at higher level clades. Hierarchical summation uses the name of the bug (containing the consensus lineage) to add bugs together at different levels of their ancestral state and represent additional higher level clades or bigger groupings of bugs. + +More plainly, imagine if we have 2 bugs in a sample with 5 and 10 counts. These two bugs differ as species but share the rest of their ancestry. In this case, an additional bug is added for the genus level group. + +k__kingdom1|p__phylum2|c__class1|o__order1|f__family1|g__genus1|s__species1 5 +k__kingdom1|p__phylum2|c__class1|o__order1|f__family1|g__genus1|s__species2 10 +add +k__kingdom1|p__phylum2|c__class1|o__order1|f__family1|g__genus1 15 + +A new kingdom, phylum, class, order, and family is not entered because they would be the same grouping of counts as the new genus level entry. + +If we had an additional bug +k__kingdom1|p__phylum2|c__class1|o__order2|f__family12|g__genus23|s__species14 2 +k__kingdom1|p__phylum2|c__class1|o__order1|f__family1|g__genus1|s__species1 5 +k__kingdom1|p__phylum2|c__class1|o__order1|f__family1|g__genus1|s__species2 10 +add +k__kingdom1|p__phylum2|c__class1 17 +k__kingdom1|p__phylum2|c__class1|o__order1|f__family1|g__genus1 15 + +Two new bugs are added because o__order1 and o__order2 can be combined at the c__class1 grouping and s__species1 and s__species2 can be combined at the g__genus1 level. Other groupings at other clade levels are not made because they represent the same groupings of counts already accounted for in the data by bugs and would be redundant. For instance, having a k__kingdom 17 count entry would be the same grouping as the c_class1 bug that was added and so is not created and added. + +*How do I reduce the table to a list of bugs?* + +> scripts/scriptManipulateTable.py -i TID -l STSite -b features.txt demo_input/Test.pcl +> scripts/scriptManipulateTable.py -i TID -l STSite -b 'Bacteria|3417,Bacteria|unclassified|4904' demo_input/Test.pcl + +IV. Manipulate samples by metadata. +J. How do I stratify the table to subtables based on a metadata? (Example. How do I take the HMP table and break it up by body site or time point?) + +> scripts/scriptManipulateTable.py -i TID -l STSite -y STSite demo_input/Test.pcl + +K. How do I remove all samples of a certain metadata value? (Example, How do I remove all gut HMP body site samples but leave the rest in the table?) + +> scripts/scriptManipulateTable.py -i TID -l STSite -r STSite,R_Retroauricular_crease, L_Retroauricular_crease demo_input/Test.pcl + +V. Manipulate the feature names +L. How do I add on the 'k__' and 's__' on the names of my bugs? + +> scripts/scriptManipulateTable.py -i TID -l STSite -x demo_input/Test.pcl + +## scriptPlotFeature.py ## +This script allows you to plot a row of an abundance table, metadata or data. This assumes the first column is the id and the remaining columns are values to be plotted. Three different plots can be generated based on the input arguments and the type of data given to the script. A boxplot is made if two features are given, one numeric and one categorical. A scatterplot is made if two numeric features are given. A histogram is made if one numeric feature is given. + +A. How do I plot a box plot of two data. +A box plot requires two features, one not categorical and one that is categorical. The script detects this automatically and will plot the correct plot for you as you go. + +> scripts/scriptPlotFeature.py demo_input/Test.pcl STSite 'Bacteria|3417' + +B. How do I plot a scatter plot of two data? +A box plot requires two features, both not categorical. The script detects this automatically and will plot the correct plot for you as you go. + +> scripts/scriptPlotFeature.py demo_input/Test.pcl 'Bacteria|unclassified|4904' 'Bacteria|3417' + +C. How do I plot a histogram of a feature? +Just plot one numeric feature. + +> scripts/scriptPlotFeature.py demo_input/Test.pcl 'Bacteria|3417' + +D. How do I change the title or axes? + +> scripts/scriptPlotFeature.py -t Title -x Xaxis -y Yaxis demo_input/Test.pcl 'Bacteria|3417' + +E. How do I change the color? Use -c and a hex color. + +> scripts/scriptPlotFeature.py -c '#333333' demo_input/Test.pcl 'Bacteria|3417' + +F. How do I invert the colors for a black background? Use -r . + +> scripts/scriptPlotFeature.py -r demo_input/Test.pcl 'Bacteria|3417' + +## scriptBiplotTSV.R ## +This script allows one to plot a tsv file as a biplot. A tsv file is a transposed PCL file (demo files are found in demo_input). The positioning of sample markers and bug text are generated by nonmetric multidimensional scaling. The metadata are represented by arrows and then a text at the head of the arrow. The coordinates of the arrows are determined by the center/average of the coordinates of the samples with that metadata showing a central tendency of where that metadata is located. More specifically, discontinuous metadata are broken down to levels (values), then each level is made into it's own binary metadata (0 for not having that value and 1 for having that value). Then for each new metadata, samples with the value of 1 are selected and have their coordinates in the ordination are averaged. This average coordinate set is then used as the coordinates for that metadata level. For continuous data, using the ordination coordinates for all the sample points, the value of the continuous metadata is placed in a landscape using the sample coordiantes as x and y and the z as the metadata value. This is then smoothed with a lowess and then the maximum fitted value's coordinates are used as the central tendency of the metadata. + +This is a customizable plot there the metadata plotted, the bugs plotted, the arrow color, the bug and metadata text color, the sample colors, the sample shapes, and the title can be changed. Below are examples of how to use the commandline for this script; although options are shown seperately here, many options can be used together. + +A. This is the minimal call. The call to the script must include the following positional arguments lastmetadata and inputPCLFile after any optional arguments (given with flags preceeded by - or --). + +> ./scripts/scriptBiplotTSV.R STSite demo_input/Test-Biplot.tsv + +This consists of the script call, the last metadata value, and the input file. + +B. How do I specify an output file name? Use -o and then a name ending with the extension .pdf . + +> ./scripts/scriptBiplotTSV.R -o Test2Biplot.pdf STSite demo_input/Test-Biplot.tsv + +C. How do I specify bug names to plot? Use -b and the names of the bugs to plot (as written in your pcl file) seperated by commas. + +> ./scripts/scriptBiplotTSV.R -b 'Bacteria|3417' STSite demo_input/Test-Biplot.tsv +> ./scripts/scriptBiplotTSV.R -b 'Bacteria|3417,Bacteria|unclassified|4904' STSite demo_input/Test-Biplot.tsv + +D. How do I specify metadata to plot? Using the -m option, if plotting a continuous metadata, give the id of the metadata (first column entry of metadata), for any other metadata concatonate the metadata ID and the value of interest with "_" . Here are a working examples. + +> ./scripts/scriptBiplotTSV.R -m 'STSite_L_Antecubital_fossa,STSite_R_Antecubital_fossa' STSite demo_input/Test-Biplot.tsv +> ./scripts/scriptBiplotTSV.R -m 'Continuous' STSite demo_input/Test-Biplot.tsv + +E. How do I specify a title? Use -i and the title text. Use ' to surround the text. This helps the command line understand that the text is together and not a series of commands. These should be used when you are giving a flag a value that has spaces or anything but alphanumeric characters. + +> ./scripts/scriptBiplotTSV.R -i 'Test Title' STSite demo_input/Test-Biplot.tsv + +F. How do I specify a metadata to shape markers by? Use -y and the id for your metadata in your pcl file (the entry for your metadata in the first column). + +> ./scripts/scriptBiplotTSV.R -y STSite STSite demo_input/Test-Biplot.tsv + +G. How do I specify specific shapes to use? This requires the combination of -y and -s . Use -y to specify the metadata to use. Use -s to specify what shapes should be used for what metadata values. These are given as metadatavalue:shape,metadataValue:shape + +> ./scripts/scriptBiplotTSV.R -y STSite -s 'L_Antecubital_fossa:15,R_Antecubital_fossa:23' STSite demo_input/Test-Biplot.tsv + +H. How do I specify a metadata to color markers by? Use -c and the id for your metadata in your pcl file (the entry for your metadata in the first column). + +> ./scripts/scriptBiplotTSV.R -c STSite STSite demo_input/Test-Biplot.tsv + +I. How do I specify a default marker shape to use instead of using a metadata? Use -d and a number recognized by R's pch parameter (number between 1-25). For more information http://www.statmethods.net/advgraphs/parameters.html + +> ./scripts/scriptBiplotTSV.R -d 1 STSite demo_input/Test-Biplot.tsv + +J. How do I specify a color range to use when coloring? Use -r with two (R supported) colors seperated by a comma. R supported colors can be found in many sources including this one http://www.stats4stem.org/r-colors.html + +> ./scripts/scriptBiplotTSV.R -r 'red,cyan' STSite demo_input/Test-Biplot.tsv + +K. How do I specify a color to use when drawing arrows? Use -a and a (R supported) color. R supported colors can be found in many sources including this one http://www.stats4stem.org/r-colors.html + +> ./scripts/scriptBiplotTSV.R -a red STSite demo_input/Test-Biplot.tsv + +L. How do I specify a color to use for arrow text? Use -w and a (R supported) color. R supported colors can be found in many sources including this one http://www.stats4stem.org/r-colors.html + +> ./scripts/scriptBiplotTSV.R -w orange STSite demo_input/Test-Biplot.tsv + +M. How do I specify a color to use for bug text? Use -t and a (R supported) color. Make sure to use -b to plot bugs. R supported colors can be found in many sources including this one http://www.stats4stem.org/r-colors.html + +> ./scripts/scriptBiplotTSV.R -t pink -b 'Bacteria|3417' STSite demo_input/Test-Biplot.tsv + +N. How do I rotate the projection (plot) in reference to a specific metadata? Use the -e option and give the plot a metadata and a weight for that metadata, the larger the weight, the more the rotation takes into account the metadata. You may have to experiement with different weights and see how the rotation is affected. The weights can be from 0 (no rotation by the metadata) to a very large number. The metadata name should be the metadata id if the value is continuous or the metadata id and the value (level) of interest seperated by a _ if the metadata is not continuous. Below are two examples, one using a continuous metadata and one using a discontinuous metadata. + +> ./scripts/scriptBiplotTSV.R -e 'Continuous,2' STSite demo_input/Test-Biplot.tsv +> ./scripts/scriptBiplotTSV.R -e 'STSite_L_Antecubital_fossa,.5' STSite demo_input/Test-Biplot.tsv + +O. How do I color NAs a specific color, no matter other coloring in the plot? Use -n and a color supported by R. R supported colors can be found in many sources including this one http://www.stats4stem.org/r-colors.html This requires you to be coloring the plot by a metadata (option -c). + +> ./scripts/scriptBiplotTSV.R -n grey -c STSite STSite demo_input/Test-BiplotNA.tsv + +P. How do I scale arrows in the plot. Use -z and a number to weight how much the metadata influences the rotation (number between 0 and very large). + +> ./scripts/scriptBiplotTSV.R -z 2 STSite demo_input/Test-Biplot.tsv + +Q. How do I plot metadata labels without the arrows? + +> ./scripts/scriptBiplotTSV.R -A STSite demo_input/Test-Biplot.tsv + +R. How do I plot the biplot without metadata? + +> ./scripts/scriptBiplotTSV.R -m "" STSite demo_input/Test-Biplot.tsv + +## scriptConvertBetweenBIOMAndPCL.py ## +The script allows one to convert between PCL and BIOM file formats. ID, last feature (row) metadata, and last sample metadata are optional information in the script call (when converting from PCL to BIOM). These are used to dictate placement of certain key sample metadata in the PCL file. Typically, it is helpful to set these arguments. This aids in the consistent and reliable manipulation of these files. If the are not given, a guess will be made to the ID and it will be assumed no metadata exist. + +A quick definition: +*ID or sample id* - typically your first row in the PCL file (the Ids of all your samples) in the example below "ID" +*Feature (row) metadata* - columns in your PCL file which describe your features. These come after your feature IDs but before your measurements. +*Sample metadata* - rows in your PCL file which come before your measurements and describe your samples + +For a description of a PCL and it's parts please look in the docs folder for PCL-Description.txt + +A. The minimal call to convert from BIOM file to a PCL file or visa versa. This call indicates the sample metadata entry which is the sample id and which is the last listed metadata in a pcl file (before the data measurements). When converting a PCL file, if there are no metadata and only a metadata id, -l and -i is not required. If there are multiple metadata in a pcl file the -l (last metadata) field is required. Neither of these fields are required for biom file conversion to pcl. + +> ./scripts/scriptConvertBetweenBIOMAndPCL.py demo_input/Test_no_metadata.pcl example1.biom +> ./scripts/scriptConvertBetweenBIOMAndPCL.py demo_input/Test.biom example2.pcl +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -l STSite demo_input/Test.pcl example3.biom + +B. Specifying ID and lastmetadata + +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i TID -l STSite demo_input/Test.pcl example4.biom +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i TID -l STSite demo_input/Test.biom example5.pcl + +C. The case where there are no sample metadata, just sample IDs. Indicate the ID and if no last metadata is indicated (-l) it is assumed no sample metadata exist. + +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i ID demo_input/Test_no_metadata.pcl example6.biom +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i ID demo_input/Test_no_metadata.biom example7.pcl + +D. The case when converting a PCL file with Feature (row) metadata (for example taxonomy_5). Include the last column with feature metadata. + +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i ID -r taxonomy_5 -l STSite ./demo_input/testFeatureMetadata.pcl testFeatureMetadata.biom + +E. Although the output file name can be automatically generated, the output file name can be given if needed. + +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i TID -l STSite demo_input/Test.biom CustomFileName.pcl +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i TID -l STSite demo_input/Test.pcl CustomFileName.biom + +F. Indicate the use of a pcl file using a delimiter that is not tab or indicate the creation of a pcl file using a delimier that is not tab. + +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i TID -l STSite -f , demo_input/Test-comma.pcl +> ./scripts/scriptConvertBetweenBIOMAndPCL.py -i TID -l STSite -f , demo_input/Test-comma.biom diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/hclust/hclust.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/hclust/hclust.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,603 @@ +#!/usr/bin/env python + +import sys +import numpy as np +import matplotlib +matplotlib.use('Agg') +import scipy +import pylab +import scipy.cluster.hierarchy as sch +import scipy.spatial.distance as dis +from scipy import stats + +# User defined color maps (in addition to matplotlib ones) +bbcyr = {'red': ( (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.50, 0.0, 0.0), + (0.75, 1.0, 1.0), + (1.0, 1.0, 1.0)), + 'green': ( (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.50, 1.0, 1.0), + (0.75, 1.0, 1.0), + (1.0, 0.0, 1.0)), + 'blue': ( (0.0, 0.0, 0.0), + (0.25, 1.0, 1.0), + (0.50, 1.0, 1.0), + (0.75, 0.0, 0.0), + (1.0, 0.0, 1.0))} + +bbcry = {'red': ( (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.50, 0.0, 0.0), + (0.75, 1.0, 1.0), + (1.0, 1.0, 1.0)), + 'green': ( (0.0, 0.0, 0.0), + (0.25, 0.0, 0.0), + (0.50, 1.0, 1.0), + (0.75, 0.0, 0.0), + (1.0, 1.0, 1.0)), + 'blue': ( (0.0, 0.0, 0.0), + (0.25, 1.0, 1.0), + (0.50, 1.0, 1.0), + (0.75, 0.0, 0.0), + (1.0, 0.0, 1.0))} +my_colormaps = [ ('bbcyr',bbcyr), + ('bbcry',bbcry)] + + + +def read_params(args): + import argparse as ap + import textwrap + + p = ap.ArgumentParser( description= "TBA" ) + + p.add_argument( '--in', '--inp', metavar='INPUT_FILE', type=str, + nargs='?', default=sys.stdin, + help= "the input archive " ) + + p.add_argument( '--out', metavar='OUTPUT_FILE', type=str, + nargs = '?', default=None, + help= " the output file, image on screen" + " if not specified. " ) + + p.add_argument( '-m', metavar='method', type=str, + choices=[ "single","complete","average", + "weighted","centroid","median", + "ward" ], + default="average" ) + + dist_funcs = [ "euclidean","minkowski","cityblock","seuclidean", + "sqeuclidean","cosine","correlation","hamming", + "jaccard","chebyshev","canberra","braycurtis", + "mahalanobis","yule","matching","dice", + "kulsinski","rogerstanimoto","russellrao","sokalmichener", + "sokalsneath","wminkowski","ward"] + p.add_argument( '-d', metavar='distance function', type=str, + choices=dist_funcs, + default="euclidean" ) + p.add_argument( '-f', metavar='distance function for features', type=str, + choices=dist_funcs, + default="d" ) + + p.add_argument( '--dmf', metavar='distance matrix for features', type=str, + default = None ) + p.add_argument( '--dms', metavar='distance matrix for samples', type=str, + default = None ) + + + p.add_argument( '-l', metavar='sample label', type=str, + default = None ) + + p.add_argument( '-s', metavar='scale norm', type=str, + default = 'lin', choices = ['log','lin']) + + p.add_argument( '-x', metavar='x cell width', type=float, + default = 0.1) + p.add_argument( '-y', metavar='y cell width', type=float, + default = 0.1 ) + + p.add_argument( '--minv', metavar='min value', type=float, + default = 0.0 ) + p.add_argument( '--maxv', metavar='max value', type=float, + default = None ) + + p.add_argument( '--xstart', metavar='x coordinate of the top left cell ' + 'of the values', + type=int, default=1 ) + p.add_argument( '--ystart', metavar='y coordinate of the top left cell ' + 'of the values', + type=int, default=1 ) + p.add_argument( '--xstop', metavar='x coordinate of the bottom right cell ' + 'of the values (default None = last row)', + type=int, default=None ) + p.add_argument( '--ystop', metavar='y coordinate of the bottom right cell ' + 'of the values (default None = last column)', + type=int, default=None ) + + p.add_argument( '--perc', metavar='percentile for ordering and rows selection', type=int, default=None ) + p.add_argument( '--top', metavar='selection of the top N rows', type=int, default=None ) + p.add_argument( '--norm', metavar='whether to normalize columns (default 0)', type=int, default=0 ) + + p.add_argument( '--sdend_h', metavar='height of the sample dendrogram', type=float, + default = 0.1 ) + p.add_argument( '--fdend_w', metavar='width of the feature dendrogram', type=float, + default = 0.1 ) + p.add_argument( '--cm_h', metavar='height of the colormap', type=float, + default = 0.03 ) + p.add_argument( '--cm_ticks', metavar='label for ticks of the colormap', type=str, + default = None ) + + p.add_argument( '--font_size', metavar='label_font_size', type=int, + default = 7 ) + p.add_argument( '--feat_dend_col_th', metavar='Color threshold for feature dendrogram', type=float, + default = None ) + p.add_argument( '--sample_dend_col_th', metavar='Color threshold for sample dendrogram', type=float, + default = None ) + p.add_argument( '--clust_ncols', metavar='Number of colors for clusters', type=int, + default = 7 ) + p.add_argument( '--clust_line_w', metavar='Cluster line width', type=float, + default = 1.0 ) + p.add_argument( '--label_cols', metavar='Label colors', type=str, + default = None ) + p.add_argument( '--label2cols', metavar='Label to colors mapping file', type=str, + default = None ) + p.add_argument( '--sdend_out', metavar='File for storing the samples dendrogram in PhyloXML format', type=str, + default = None ) + p.add_argument( '--fdend_out', metavar='File for storing the features dendrogram in PhyloXML format', type=str, + default = None ) + + + p.add_argument( '--pad_inches', metavar='Proportion of figure to be left blank around the plot', type=float, + default = 0.1 ) + + + p.add_argument( '--flabel', metavar='Whether to show the labels for the features', type=int, + default = 0 ) + p.add_argument( '--slabel', metavar='Whether to show the labels for the samples', type=int, + default = 0 ) + + p.add_argument( '--legend', metavar='Whether to show the samples to label legend', type=int, + default = 0 ) + p.add_argument( '--legend_font_size', metavar='Legend font size', type=int, + default = 7 ) + p.add_argument( '--legend_ncol', metavar='Number of columns for the legend', type=int, + default = 3 ) + p.add_argument( '--grid', metavar='Whether to show the grid (only black for now)', type=int, + default = 0 ) + + col_maps = ['Accent', 'Blues', 'BrBG', 'BuGn', 'BuPu', 'Dark2', 'GnBu', + 'Greens', 'Greys', 'OrRd', 'Oranges', 'PRGn', 'Paired', + 'Pastel1', 'Pastel2', 'PiYG', 'PuBu', 'PuBuGn', 'PuOr', + 'PuRd', 'Purples', 'RdBu', 'RdGy', 'RdPu', 'RdYlBu', 'RdYlGn', + 'Reds', 'Set1', 'Set2', 'Set3', 'Spectral', 'YlGn', 'YlGnBu', + 'YlOrBr', 'YlOrRd', 'afmhot', 'autumn', 'binary', 'bone', + 'brg', 'bwr', 'cool', 'copper', 'flag', 'gist_earth', + 'gist_gray', 'gist_heat', 'gist_ncar', 'gist_rainbow', + 'gist_stern', 'gist_yarg', 'gnuplot', 'gnuplot2', 'gray', + 'hot', 'hsv', 'jet', 'ocean', 'pink', 'prism', 'rainbow', + 'seismic', 'spectral', 'spring', 'summer', 'terrain', 'winter'] + [n for n,c in my_colormaps] + p.add_argument( '-c', metavar='colormap', type=str, + choices = col_maps, default = 'jet' ) + + return vars(p.parse_args()) + +# Predefined colors for dendrograms brances and class labels +colors = [ "#B22222","#006400","#0000CD","#9400D3","#696969","#8B4513", + "#FF1493","#FF8C00","#3CB371","#00Bfff","#CDC9C9","#FFD700", + "#2F4F4F","#FF0000","#ADFF2F","#B03060" ] + +def samples2classes_panel(fig, samples, s2l, idx1, idx2, height, xsize, cols, legendon, fontsize, label2cols, legend_ncol ): + from matplotlib.patches import Rectangle + samples2labels = dict([(l[0],l[1]) + for l in [ll.strip().split('\t') + for ll in open(s2l)] if len(l) > 1]) + + if label2cols: + labels2colors = dict([(l[0],l[1]) for l in [ll.strip().split('\t') for ll in open(label2cols)]]) + else: + cs = cols if cols else colors + labels2colors = dict([(l,cs[i%len(cs)]) for i,l in enumerate(set(samples2labels.values()))]) + ax1 = fig.add_axes([0.,1.0,1.0,height],frameon=False) + ax1.set_xticks([]) + ax1.set_yticks([]) + ax1.set_ylim( [0.0, height] ) + ax1.set_xlim( [0.0, xsize] ) + step = xsize / float(len(samples)) + labels = set() + added_labels = set() + for i,ind in enumerate(idx2): + if not samples[ind] in samples2labels or \ + not samples2labels[samples[ind]] in labels2colors: + fc, ll = "k", None + else: + ll = samples2labels[samples[ind]] + ll = None if ll in added_labels else ll + added_labels.add( ll ) + fc = labels2colors[samples2labels[samples[ind]]] + + rect = Rectangle( [float(i)*step, 0.0], step, height, + facecolor = fc, + label = ll, + edgecolor='b', lw = 0.0) + labels.add( ll ) + ax1.add_patch(rect) + ax1.autoscale_view() + + if legendon: + ax1.legend( loc = 2, ncol = legend_ncol, bbox_to_anchor=(1.01, 3.), + borderpad = 0.0, labelspacing = 0.0, + handlelength = 0.5, handletextpad = 0.3, + borderaxespad = 0.0, columnspacing = 0.3, + prop = {'size':fontsize}, frameon = False) + +def samples_dend_panel( fig, Z, Z2, ystart, ylen, lw ): + ax2 = fig.add_axes([0.0,1.0+ystart,1.0,ylen], frameon=False) + Z2['color_list'] = [c.replace('b','k') for c in Z2['color_list']] + mh = max(Z[:,2]) + sch._plot_dendrogram( Z2['icoord'], Z2['dcoord'], Z2['ivl'], + Z.shape[0] + 1, Z.shape[0] + 1, + mh, 'top', no_labels=True, + color_list=Z2['color_list'] ) + for coll in ax2.collections: + coll._linewidths = (lw,) + ax2.set_xticks([]) + ax2.set_yticks([]) + ax2.set_xticklabels([]) + +def features_dend_panel( fig, Z, Z2, width, lw ): + ax1 = fig.add_axes([-width,0.0,width,1.0], frameon=False) + Z2['color_list'] = [c.replace('b','k').replace('x','b') for c in Z2['color_list']] + mh = max(Z[:,2]) + sch._plot_dendrogram(Z2['icoord'], Z2['dcoord'], Z2['ivl'], Z.shape[0] + 1, Z.shape[0] + 1, mh, 'right', no_labels=True, color_list=Z2['color_list']) + for coll in ax1.collections: + coll._linewidths = (lw,) + ax1.set_xticks([]) + ax1.set_yticks([]) + ax1.set_xticklabels([]) + + +def add_cmap( cmapdict, name ): + my_cmap = matplotlib.colors.LinearSegmentedColormap(name,cmapdict,256) + pylab.register_cmap(name=name,cmap=my_cmap) + +def init_fig(xsize,ysize,ncol): + fig = pylab.figure(figsize=(xsize,ysize)) + sch._link_line_colors = colors[:ncol] + return fig + +def heatmap_panel( fig, D, minv, maxv, idx1, idx2, cm_name, scale, cols, rows, label_font_size, cb_offset, cb_l, flabelson, slabelson, cm_ticks, gridon, bar_offset ): + cm = pylab.get_cmap(cm_name) + bottom_col = [ cm._segmentdata['red'][0][1], + cm._segmentdata['green'][0][1], + cm._segmentdata['blue'][0][1] ] + axmatrix = fig.add_axes( [0.0,0.0,1.0,1.0], + axisbg=bottom_col) + if any([c < 0.95 for c in bottom_col]): + axmatrix.spines['right'].set_color('none') + axmatrix.spines['left'].set_color('none') + axmatrix.spines['top'].set_color('none') + axmatrix.spines['bottom'].set_color('none') + norm_f = matplotlib.colors.LogNorm if scale == 'log' else matplotlib.colors.Normalize + im = axmatrix.matshow( D, norm = norm_f( vmin=minv if minv > 0.0 else None, + vmax=maxv), + aspect='auto', origin='lower', cmap=cm, vmax=maxv) + + axmatrix2 = axmatrix.twinx() + axmatrix3 = axmatrix.twiny() + + axmatrix.set_xticks([]) + axmatrix2.set_xticks([]) + axmatrix3.set_xticks([]) + axmatrix.set_yticks([]) + axmatrix2.set_yticks([]) + axmatrix3.set_yticks([]) + + axmatrix.set_xticklabels([]) + axmatrix2.set_xticklabels([]) + axmatrix3.set_xticklabels([]) + axmatrix.set_yticklabels([]) + axmatrix2.set_yticklabels([]) + axmatrix3.set_yticklabels([]) + + if any([c < 0.95 for c in bottom_col]): + axmatrix2.spines['right'].set_color('none') + axmatrix2.spines['left'].set_color('none') + axmatrix2.spines['top'].set_color('none') + axmatrix2.spines['bottom'].set_color('none') + if any([c < 0.95 for c in bottom_col]): + axmatrix3.spines['right'].set_color('none') + axmatrix3.spines['left'].set_color('none') + axmatrix3.spines['top'].set_color('none') + axmatrix3.spines['bottom'].set_color('none') + if flabelson: + axmatrix2.set_yticks(np.arange(len(rows))+0.5) + axmatrix2.set_yticklabels([rows[r] for r in idx1],size=label_font_size,va='center') + if slabelson: + axmatrix.set_xticks(np.arange(len(cols))) + axmatrix.set_xticklabels([cols[r] for r in idx2],size=label_font_size,rotation=90,va='top',ha='center') + axmatrix.tick_params(length=0) + axmatrix2.tick_params(length=0) + axmatrix3.tick_params(length=0) + axmatrix2.set_ylim(0,len(rows)) + + if gridon: + axmatrix.set_yticks(np.arange(len(idx1)-1)+0.5) + axmatrix.set_xticks(np.arange(len(idx2))+0.5) + axmatrix.grid( True ) + ticklines = axmatrix.get_xticklines() + ticklines.extend( axmatrix.get_yticklines() ) + #gridlines = axmatrix.get_xgridlines() + #gridlines.extend( axmatrix.get_ygridlines() ) + + for line in ticklines: + line.set_linewidth(3) + + if cb_l > 0.0: + axcolor = fig.add_axes([0.0,1.0+bar_offset*1.25,1.0,cb_l]) + cbar = fig.colorbar(im, cax=axcolor, orientation='horizontal') + cbar.ax.tick_params(labelsize=label_font_size) + if cm_ticks: + cbar.ax.set_xticklabels( cm_ticks.split(":") ) + + +def read_table( fin, xstart,xstop,ystart,ystop, percentile = None, top = None, norm = False ): + mat = [l.rstrip().split('\t') for l in open( fin )] + + if fin.endswith(".biom"): + sample_labels = mat[1][1:-1] + m = [(mm[-1]+"; OTU"+mm[0],np.array([float(f) for f in mm[1:-1]])) for mm in mat[2:]] + #feat_labels = [m[-1].replace(";","_").replace(" ","")+m[0] for m in mat[2:]] + #print len(feat_labels) + #print len(sample_labels) + else: + sample_labels = mat[0][xstart:xstop] + m = [(mm[xstart-1],np.array([float(f) for f in mm[xstart:xstop]])) for mm in mat[ystart:ystop]] + + if norm: + msums = [0.0 for l in m[0][1]] + for mm in m: + for i,v in enumerate(mm[1]): + msums[i] += v + + if top and not percentile: + percentile = 90 + + if percentile: + m = sorted(m,key=lambda x:-stats.scoreatpercentile(x[1],percentile)) + if top: + if fin.endswith(".biom"): + #feat_labels = [mm[-1].replace(";","_").replace(" ","")+mm[0] for mm in m[:top]] + feat_labels = [mm[0] for mm in m[:top]] + else: + feat_labels = [mm[0] for mm in m[:top]] + if norm: + m = [np.array([n/v for n,v in zip(mm[1],msums)]) for mm in m[:top]] + else: + m = [mm[1] for mm in m[:top]] + else: + if fin.endswith(".biom"): + feat_labels = [mm[0] for mm in m] + else: + feat_labels = [mm[0] for mm in m] + if norm: + m = [np.array([n/v for n,v in zip(mm[1],msums)]) for mm in m] + else: + m = [mm[1] for mm in m] + #m = [mm[1] for mm in m] + + D = np.matrix( np.array( m ) ) + + return D, feat_labels, sample_labels + +def read_dm( fin, n ): + mat = [[float(f) for f in l.strip().split('\t')] for l in open( fin )] + nc = sum([len(r) for r in mat]) + + if nc == n*n: + dm = [] + for i in range(n): + dm += mat[i][i+1:] + return np.array(dm) + if nc == (n*n-n)/2: + dm = [] + for i in range(n): + dm += mat[i] + return np.array(dm) + sys.stderr.write( "Error in reading the distance matrix\n" ) + sys.exit() + + +def exp_newick( inp, labels, outfile, tree_format = 'phyloxml' ): + n_leaves = int(inp[-1][-1]) + from Bio import Phylo + import collections + from Bio.Phylo.BaseTree import Tree as BTree + from Bio.Phylo.BaseTree import Clade as BClade + tree = BTree() + tree.root = BClade() + + subclades = {} + sb_cbl = {} + + for i,(fr,to,bl,nsub) in enumerate( inp ): + if fr < n_leaves: + fr_c = BClade(branch_length=-1.0,name=labels[int(fr)]) + subclades[fr] = fr_c + sb_cbl[fr] = bl + if to < n_leaves: + to_c = BClade(branch_length=-1.0,name=labels[int(to)]) + subclades[to] = to_c + sb_cbl[to] = bl + for i,(fr,to,bl,nsub) in enumerate( inp ): + fr_c = subclades[fr] + to_c = subclades[to] + cur_c = BClade(branch_length=bl) + cur_c.clades.append( fr_c ) + cur_c.clades.append( to_c ) + subclades[i+n_leaves] = cur_c + + def reset_rec( clade, fath_bl ): + if clade.branch_length < 0: + clade.branch_length = fath_bl + return + for c in clade.clades: + reset_rec( c, clade.branch_length ) + clade.branch_length = fath_bl-clade.branch_length + + tree.root = cur_c + reset_rec( tree.root, 0.0 ) + tree.root.branch_length = 0.0 + Phylo.write(tree, outfile, tree_format ) + +def hclust( fin, fout, + method = "average", + dist_func = "euclidean", + feat_dist_func = "d", + xcw = 0.1, + ycw = 0.1, + scale = 'lin', + minv = 0.0, + maxv = None, + xstart = 1, + ystart = 1, + xstop = None, + ystop = None, + percentile = None, + top = None, + norm = False, + cm_name = 'jet', + s2l = None, + label_font_size = 7, + feat_dend_col_th = None, + sample_dend_col_th = None, + clust_ncols = 7, + clust_line_w = 1.0, + label_cols = None, + sdend_h = 0.1, + fdend_w = 0.1, + cm_h = 0.03, + dmf = None, + dms = None, + legendon = False, + label2cols = None, + flabelon = True, + slabelon = True, + cm_ticks = None, + legend_ncol = 3, + pad_inches = None, + legend_font_size = 7, + gridon = 0, + sdend_out = None, + fdend_out= None): + + if label_cols and label_cols.count("-"): + label_cols = label_cols.split("-") + + for n,c in my_colormaps: + add_cmap( c, n ) + + if feat_dist_func == 'd': + feat_dist_func = dist_func + + D, feat_labels, sample_labels = read_table(fin,xstart,xstop,ystart,ystop,percentile,top,norm) + + ylen,xlen = D[:].shape + Dt = D.transpose() + + size_cx, size_cy = xcw, ycw + + xsize, ysize = max(xlen*size_cx,2.0), max(ylen*size_cy,2.0) + ydend_offset = 0.025*8.0/ysize if s2l else 0.0 + + fig = init_fig(xsize,ysize,clust_ncols) + + nfeats, nsamples = len(D), len(Dt) + + if dmf: + p1 = read_dm( dmf, nfeats ) + Y1 = sch.linkage( p1, method=method ) + else: + p1 = dis.pdist( D, feat_dist_func ) + Y1 = sch.linkage( p1, method=method ) # , metric=feat_dist_func ) + #Y1 = sch.linkage( D, method=method, metric=feat_dist_func ) + Z1 = sch.dendrogram(Y1, no_plot=True, color_threshold=feat_dend_col_th) + + if fdend_out: + exp_newick( Y1, feat_labels, fdend_out ) + + if dms: + p2 = read_dm( dms, nsamples ) + Y2 = sch.linkage( p2, method=method ) + else: + p2 = dis.pdist( Dt, dist_func ) + Y2 = sch.linkage( p2, method=method ) # , metric=dist_func ) + #Y2 = sch.linkage( Dt, method=method, metric=dist_func ) + Z2 = sch.dendrogram(Y2, no_plot=True, color_threshold=sample_dend_col_th) + + if sdend_out: + exp_newick( Y2, sample_labels, sdend_out ) + + if fdend_w > 0.0: + features_dend_panel(fig, Y1, Z1, fdend_w*8.0/xsize, clust_line_w ) + if sdend_h > 0.0: + samples_dend_panel(fig, Y2, Z2, ydend_offset, sdend_h*8.0/ysize, clust_line_w) + + idx1, idx2 = Z1['leaves'], Z2['leaves'] + D = D[idx1,:][:,idx2] + + if s2l: + samples2classes_panel( fig, sample_labels, s2l, idx1, idx2, 0.025*8.0/ysize, xsize, label_cols, legendon, legend_font_size, label2cols, legend_ncol ) + heatmap_panel( fig, D, minv, maxv, idx1, idx2, cm_name, scale, sample_labels, feat_labels, label_font_size, -cm_h*8.0/ysize, cm_h*0.8*8.0/ysize, flabelon, slabelon, cm_ticks, gridon, ydend_offset+sdend_h*8.0/ysize ) + + fig.savefig( fout, bbox_inches='tight', + pad_inches = pad_inches, + dpi=300) if fout else pylab.show() + +if __name__ == '__main__': + pars = read_params( sys.argv ) + + hclust( fin = pars['in'], + fout = pars['out'], + method = pars['m'], + dist_func = pars['d'], + feat_dist_func = pars['f'], + xcw = pars['x'], + ycw = pars['y'], + scale = pars['s'], + minv = pars['minv'], + maxv = pars['maxv'], + xstart = pars['xstart'], + ystart = pars['ystart'], + xstop = pars['xstop'], + ystop = pars['ystop'], + percentile = pars['perc'], + top = pars['top'], + norm = pars['norm'], + cm_name = pars['c'], + s2l = pars['l'], + label_font_size = pars['font_size'], + feat_dend_col_th = pars['feat_dend_col_th'], + sample_dend_col_th = pars['sample_dend_col_th'], + clust_ncols = pars['clust_ncols'], + clust_line_w = pars['clust_line_w'], + label_cols = pars['label_cols'], + sdend_h = pars['sdend_h'], + fdend_w = pars['fdend_w'], + cm_h = pars['cm_h'], + dmf = pars['dmf'], + dms = pars['dms'], + legendon = pars['legend'], + label2cols = pars['label2cols'], + flabelon = pars['flabel'], + slabelon = pars['slabel'], + cm_ticks = pars['cm_ticks'], + legend_ncol = pars['legend_ncol'], + pad_inches = pars['pad_inches'], + legend_font_size = pars['legend_font_size'], + gridon = pars['grid'], + sdend_out = pars['sdend_out'], + fdend_out = pars['fdend_out'], + ) + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/scripts/scriptBiplotTSV.R --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/scripts/scriptBiplotTSV.R Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,496 @@ +#!/usr/bin/env Rscript + +library(vegan) +library(optparse) + +funcGetCentroidForMetadatum <- function( +### Given a binary metadatum, calculate the centroid of the samples associated with the metadata value of 1 +# 1. Get all samples that have the metadata value of 1 +# 2. Get the x and y coordinates of the selected samples +# 3. Get the median value for the x and ys +# 4. Return those coordinates as the centroid's X and Y value +vfMetadata, +### Logical or integer (0,1) vector, TRUE or 1 values indicate correspoinding samples in the +### mSamplePoints which will be used to define the centroid +mSamplePoints +### Coordinates (columns;n=2) of samples (rows) corresponding to the vfMetadata +){ + # Check the lengths which should be equal + if(length(vfMetadata)!=nrow(mSamplePoints)) + { + print(paste("funcGetCentroidForMetadata::Error: Should have received metadata and samples of the same length, received metadata length ",length(vfMetadata)," and sample ",nrow(mSamplePoints)," length.",sep="")) + return( FALSE ) + } + + # Get all the samples that have the metadata value of 1 + viMetadataSamples = which(as.integer(vfMetadata)==1) + + # Get the x and y coordinates for the selected samples + mSelectedPoints = mSamplePoints[viMetadataSamples,] + + # Get the median value for the x and the ys + if(!is.null(nrow(mSelectedPoints))) + { + return( list(x=median(mSelectedPoints[,1],na.rm = TRUE),y=median(mSelectedPoints[,2],na.rm = TRUE)) ) + } else { + return( list(x=mSelectedPoints[1],y=mSelectedPoints[2]) ) + } +} + +funcGetMaximumForMetadatum <- function( +### Given a continuous metadata +### 1. Use the x and ys from mSamplePoints for coordinates and the metadata value as a height (z) +### 2. Use lowess to smooth the landscape +### 3. Take the maximum of the landscape +### 4. Return the coordiantes for the maximum as the centroid +vdMetadata, +### Continuous (numeric or integer) metadata +mSamplePoints +### Coordinates (columns;n=2) of samples (rows) corresponding to the vfMetadata +){ + # Work with data frame + if(class(mSamplePoints)=="matrix") + { + mSamplePoints = data.frame(mSamplePoints) + } + # Check the lengths of the dataframes and the metadata + if(length(vdMetadata)!=nrow(mSamplePoints)) + { + print(paste("funcGetMaximumForMetadatum::Error: Should have received metadata and samples of the same length, received metadata length ",length(vdMetadata)," and sample ",nrow(mSamplePoints)," length.",sep="")) + return( FALSE ) + } + + # Add the metadata value to the points + mSamplePoints[3] = vdMetadata + names(mSamplePoints) = c("x","y","z") + + # Create lowess to smooth the surface + # And calculate the fitted heights + # x = sample coordinate 1 + # y = sample coordinate 2 + # z = metadata value + loessSamples = loess(z~x*y, data=mSamplePoints, degree = 1, normalize = FALSE, na.action=na.omit) + + # Naively get the max + vdCoordinates = loessSamples$x[which(loessSamples$y==max(loessSamples$y)),] + return(list(lsmod = loessSamples, x=vdCoordinates[1],y=vdCoordinates[2])) +} + +funcMakeShapes <- function( +### Takes care of defining shapes for the plot +dfInput, +### Data frame of metadata measurements +sShapeBy, +### The metadata to shape by +sShapes, +### List of custom metadata (per level if factor). +### Should correspond to the number of levels in shapeBy; the format is level:shape,level:shape for example HighLuminosity:14,LowLuminosity:2,HighPH:10,LowPH:18 +cDefaultShape +### Shape to default to if custom shapes are not used +){ + lShapes = list() + vsShapeValues = c() + vsShapeShapes = c() + vsShapes = c() + sMetadataId = sShapeBy + + # Set default shape, color, and color ranges + if(!is.null(cDefaultShape)) + { + # Default shape should be an int for the int pch options + if(!is.na(as.integer(cDefaultShape))) + { + cDefaultShape = as.integer(cDefaultShape) + } + } else { + cDefaultShape = 16 + } + + # Make shapes + vsShapes = rep(cDefaultShape,nrow(dfInput)) + + if(!is.null(sMetadataId)) + { + if(is.null(sShapes)) + { + vsShapeValues = unique(dfInput[[sMetadataId]]) + vsShapeShapes = 1:length(vsShapeValues) + } else { + # Put the markers in the order of the values) + vsShapeBy = unlist(strsplit(sShapes,",")) + for(sShapeBy in vsShapeBy) + { + vsShapeByPieces = unlist(strsplit(sShapeBy,":")) + lShapes[vsShapeByPieces[1]] = as.integer(vsShapeByPieces[2]) + } + vsShapeValues = names(lShapes) + } + + # Shapes in the correct order + if(!is.null(sShapes)) + { + vsShapeShapes = unlist(lapply(vsShapeValues,function(x) lShapes[[x]])) + } + vsShapeValues = paste(vsShapeValues) + + # Make the list of shapes + for(iShape in 1:length(vsShapeValues)) + { + vsShapes[which(paste(dfInput[[sMetadataId]])==vsShapeValues[iShape])]=vsShapeShapes[iShape] + } + + # If they are all numeric characters, make numeric + viIntNas = which(is.na(as.integer(vsShapes))) + viNas = which(is.na(vsShapes)) + if(length(setdiff(viIntNas,viNas))==0) + { + vsShapes = as.integer(vsShapes) + } else { + print("funcMakeShapes::Error: Please supply numbers 1-25 for shape in the -y,--shapeBy option") + vsShapeValues = c() + vsShapeShapes = c() + } + } + return(list(PlotShapes=vsShapes,Values=vsShapeValues,Shapes=vsShapeShapes,ID=sMetadataId,DefaultShape=cDefaultShape)) +} + +### Global defaults +c_sDefaultColorBy = NULL +c_sDefaultColorRange = "orange,cyan" +c_sDefaultTextColor = "black" +c_sDefaultArrowColor = "cyan" +c_sDefaultArrowTextColor = "Blue" +c_sDefaultNAColor = NULL +c_sDefaultShapeBy = NULL +c_sDefaultShapes = NULL +c_sDefaultMarker = "16" +c_fDefaultPlotArrows = TRUE +c_sDefaultRotateByMetadata = NULL +c_sDefaultResizeArrow = 1 +c_sDefaultTitle = "Custom Biplot of Bugs and Samples - Metadata Plotted with Centroids" +c_sDefaultOutputFile = NULL + +### Create command line argument parser +pArgs <- OptionParser( usage = "%prog last_metadata input.tsv" ) + +# Selecting features to plot +pArgs <- add_option( pArgs, c("-b", "--bugs"), type="character", action="store", default=NULL, dest="sBugs", metavar="BugsToPlot", help="Comma delimited list of data to plot as text. To not plot metadata pass a blank or empty space. Bug|1,Bug|2") +# The default needs to stay null for metadata or code needs to be changed in the plotting for a check to see if the metadata was default. Search for "#!# metadata default dependent" +pArgs <- add_option( pArgs, c("-m", "--metadata"), type="character", action="store", default=NULL, dest="sMetadata", metavar="MetadataToPlot", help="Comma delimited list of metadata to plot as arrows. To not plot metadata pass a blank or empty space. metadata1,metadata2,metadata3") + +# Colors +pArgs <- add_option( pArgs, c("-c", "--colorBy"), type="character", action="store", default=c_sDefaultColorBy, dest="sColorBy", metavar="MetadataToColorBy", help="The id of the metadatum to use to make the marker colors. Expected to be a continuous metadata.") +pArgs <- add_option( pArgs, c("-r", "--colorRange"), type="character", action="store", default=c_sDefaultColorRange, dest="sColorRange", metavar="ColorRange", help=paste("Colors used to color the samples; a gradient will be formed between the color.Default=", c_sDefaultColorRange)) +pArgs <- add_option( pArgs, c("-t", "--textColor"), type="character", action="store", default=c_sDefaultTextColor, dest="sTextColor", metavar="TextColor", help=paste("The color bug features will be plotted with as text. Default =", c_sDefaultTextColor)) +pArgs <- add_option( pArgs, c("-a", "--arrowColor"), type="character", action="store", default=c_sDefaultArrowColor, dest="sArrowColor", metavar="ArrowColor", help=paste("The color metadata features will be plotted with as an arrow and text. Default", c_sDefaultArrowColor)) +pArgs <- add_option( pArgs, c("-w", "--arrowTextColor"), type="character", action="store", default=c_sDefaultArrowTextColor, dest="sArrowTextColor", metavar="ArrowTextColor", help=paste("The color for the metadata text ploted by the head of the metadata arrow. Default", c_sDefaultArrowTextColor)) +pArgs <- add_option(pArgs, c("-n","--plotNAColor"), type="character", action="store", default=c_sDefaultNAColor, dest="sPlotNAColor", metavar="PlotNAColor", help=paste("Plot NA values as this color. Example -n", c_sDefaultNAColor)) + +# Shapes +pArgs <- add_option( pArgs, c("-y", "--shapeby"), type="character", action="store", default=c_sDefaultShapeBy, dest="sShapeBy", metavar="MetadataToShapeBy", help="The metadata to use to make marker shapes. Expected to be a discrete metadatum. An example would be -y Environment") +pArgs <- add_option( pArgs, c("-s", "--shapes"), type="character", action="store", default=c_sDefaultShapes, dest="sShapes", metavar="ShapesForPlotting", help="This is to be used to specify the shapes to use for plotting. Can use numbers recognized by R as shapes (see pch). Should correspond to the number of levels in shapeBy; the format is level:shape,level:shape for example HighLuminosity:14,LowLuminosity:2,HighPH:10,LowPH:18 . Need to specify -y/--shapeBy for this option to work.") +pArgs <- add_option( pArgs, c("-d", "--defaultMarker"), type="character", action="store", default=c_sDefaultMarker, dest="sDefaultMarker", metavar="DefaultColorMarker", help="Default shape for markers which are not otherwise indicated in --shapes, can be used for unspecified values or NA. Must not be a shape in --shapes.") + +# Plot manipulations +pArgs <- add_option( pArgs, c("-e","--rotateByMetadata"), type="character", action="store", default=c_sDefaultRotateByMetadata, dest="sRotateByMetadata", metavar="RotateByMetadata", help="Rotate the ordination by a metadata. Give both the metadata and value to weight it by. The larger the weight, the more the ordination is influenced by the metadata. If the metadata is continuous, use the metadata id; if the metadata is discrete, the ordination will be by one of the levels so use the metadata ID and level seperated by a '_'. Discrete example -e Environment_HighLumninosity,100 ; Continuous example -e Environment,100 .") +pArgs <- add_option( pArgs, c("-z","--resizeArrow"), type="numeric", action="store", default=c_sDefaultResizeArrow, dest="dResizeArrow", metavar="ArrowScaleFactor", help="A constant to multiple the length of the arrow to expand or shorten all arrows together. This will not change the angle of the arrow nor the relative length of arrows to each other.") +pArgs <- add_option( pArgs, c("-A", "--noArrows"), type="logical", action="store_true", default=FALSE, dest="fNoPlotMetadataArrows", metavar="DoNotPlotArrows", help="Adding this flag allows one to plot metadata labels without the arrows.") + +# Misc +pArgs <- add_option( pArgs, c("-i", "--title"), type="character", action="store", default=c_sDefaultTitle, dest="sTitle", metavar="Title", help="This is the title text to add to the plot.") +pArgs <- add_option( pArgs, c("-o", "--outputfile"), type="character", action="store", default=c_sDefaultOutputFile, dest="sOutputFileName", metavar="OutputFile", help="This is the name for the output pdf file. If an output file is not given, an output file name is made based on the input file name.") + +funcDoBiplot <- function( +### Perform biplot. Samples are markers, bugs are text, and metadata are text with arrows. Markers and bugs are dtermined usiing NMDS and Bray-Curtis dissimilarity. Metadata are placed on the ordination in one of two ways: 1. Factor data - for each level take the ordination points for the samples that have that level and plot the metadata text at the average orindation point. 2. For continuous data - make a landscape (x and y form ordination of the points) and z (height) as the metadata value. Use a lowess line to get the fitted values for z and take the max of the landscape. Plot the metadata text at that smoothed max. +sBugs, +### Comma delimited list of data to plot as text. Bug|1,Bug|2 +sMetadata, +### Comma delimited list of metadata to plot as arrows. metadata1,metadata2,metadata3. +sColorBy = c_sDefaultColorBy, +### The id of the metadatum to use to make the marker colors. Expected to be a continuous metadata. +sColorRange = c_sDefaultColorRange, +### Colors used to color the samples; a gradient will be formed between the color. Example orange,cyan +sTextColor = c_sDefaultTextColor, +### The color bug features will be plotted with as text. Example black +sArrowColor = c_sDefaultArrowColor, +### The color metadata features will be plotted with as an arrow and text. Example cyan +sArrowTextColor = c_sDefaultArrowTextColor, +### The color for the metadata text ploted by the head of the metadat arrow. Example Blue +sPlotNAColor = c_sDefaultNAColor, +### Plot NA values as this color. Example grey +sShapeBy = c_sDefaultShapeBy, +### The metadata to use to make marker shapes. Expected to be a discrete metadatum. +sShapes = c_sDefaultShapes, +### This is to be used to specify the shapes to use for plotting. Can use numbers recognized by R as shapes (see pch). Should correspond to the number of levels in shapeBy; the format is level:shape,level:shape for example HighLuminosity:14,LowLuminosity:2,HighPH:10,LowPH:18 . Works with sShapesBy. +sDefaultMarker = c_sDefaultMarker, +### The default marker shape to use if shapes are not otherwise indicated. +sRotateByMetadata = c_sDefaultRotateByMetadata, +### Metadata and value to rotate by. example Environment_HighLumninosity,100 +dResizeArrow = c_sDefaultResizeArrow, +### Scale factor to resize tthe metadata arrows +fPlotArrow = c_fDefaultPlotArrows, +### A flag which can be used to turn off arrow plotting. +sTitle = c_sDefaultTitle, +### The title for the figure. +sInputFileName, +### File to input (tsv file: tab seperated, row = sample file) +sLastMetadata, +### Last metadata that seperates data and metadata +sOutputFileName = c_sDefaultOutputFile +### The file name to save the figure. +){ + # Define the colors + vsColorRange = c("blue","orange") + if(!is.null(sColorRange)) + { + vsColorRange = unlist(strsplit(sColorRange,",")) + } + cDefaultColor = "grey" + if(!is.null(vsColorRange) && length(vsColorRange)>0) + { + cDefaultColor = vsColorRange[1] + } + + # List of bugs to plot + # If there is a list it needs to be more than one. + vsBugsToPlot = c() + if(!is.null(sBugs)) + { + vsBugsToPlot = unlist(strsplit(sBugs,",")) + } + # Metadata to plot + vsMetadata = c() + if(!is.null(sMetadata)) + { + vsMetadata = unlist(strsplit(sMetadata,",")) + } + + ### Load table + dfInput = sInputFileName + if(class(sInputFileName)=="character") + { + dfInput = read.table(sInputFileName, sep = "\t", header=TRUE) + names(dfInput) = unlist(lapply(names(dfInput),function(x) gsub(".","|",x,fixed=TRUE))) + row.names(dfInput) = dfInput[,1] + dfInput = dfInput[-1] + } + + iLastMetadata = which(names(dfInput)==sLastMetadata) + viMetadata = 1:iLastMetadata + viData = (iLastMetadata+1):ncol(dfInput) + + ### Dummy the metadata if discontinuous + ### Leave the continous metadata alone but include + listMetadata = list() + vsRowNames = c() + viContinuousMetadata = c() + for(i in viMetadata) + { + vCurMetadata = unlist(dfInput[i]) + if(is.numeric(vCurMetadata)||is.integer(vCurMetadata)) + { + vCurMetadata[which(is.na(vCurMetadata))] = mean(vCurMetadata,na.rm=TRUE) + listMetadata[[length(listMetadata)+1]] = vCurMetadata + vsRowNames = c(vsRowNames,names(dfInput)[i]) + viContinuousMetadata = c(viContinuousMetadata,length(listMetadata)) + } else { + vCurMetadata = as.factor(vCurMetadata) + vsLevels = levels(vCurMetadata) + for(sLevel in vsLevels) + { + vNewMetadata = rep(0,length(vCurMetadata)) + vNewMetadata[which(vCurMetadata == sLevel)] = 1 + listMetadata[[length(listMetadata)+1]] = vNewMetadata + vsRowNames = c(vsRowNames,paste(names(dfInput)[i],sLevel,sep="_")) + } + } + } + + # Convert to data frame + dfDummyMetadata = as.data.frame(sapply(listMetadata,rbind)) + names(dfDummyMetadata) = vsRowNames + iNumberMetadata = ncol(dfDummyMetadata) + + # Data to use in ordination in NMDS + dfData = dfInput[viData] + + # If rotating the ordination by a metadata + # 1. Add in the metadata as a bug + # 2. Multiply the bug by the weight + # 3. Push this through the NMDS + if(!is.null(sRotateByMetadata)) + { + vsRotateMetadata = unlist(strsplit(sRotateByMetadata,",")) + sMetadata = vsRotateMetadata[1] + dWeight = as.numeric(vsRotateMetadata[2]) + sOrdinationMetadata = dfDummyMetadata[sMetadata]*dWeight + dfData[sMetadata] = sOrdinationMetadata + } + + # Run NMDS on bug data (Default B-C) + # Will have species and points because working off of raw data + mNMDSData = metaMDS(dfData,k=2) + + ## Make shapes + # Defines thes shapes and the metadata they are based on + # Metadata to use as shapes + lShapeInfo = funcMakeShapes(dfInput=dfInput, sShapeBy=sShapeBy, sShapes=sShapes, cDefaultShape=sDefaultMarker) + + sMetadataShape = lShapeInfo[["ID"]] + vsShapeValues = lShapeInfo[["Values"]] + vsShapeShapes = lShapeInfo[["Shapes"]] + vsShapes = lShapeInfo[["PlotShapes"]] + cDefaultShape = lShapeInfo[["DefaultShape"]] + + # Colors + vsColors = rep(cDefaultColor,nrow(dfInput)) + vsColorValues = c() + vsColorRBG = c() + if(!is.null(sColorBy)) + { + vsColorValues = paste(sort(unique(unlist(dfInput[[sColorBy]])),na.last=TRUE)) + iLengthColorValues = length(vsColorValues) + + vsColorRBG = lapply(1:iLengthColorValues/iLengthColorValues,colorRamp(vsColorRange)) + vsColorRBG = unlist(lapply(vsColorRBG, function(x) rgb(x[1]/255,x[2]/255,x[3]/255))) + + for(iColor in 1:length(vsColorRBG)) + { + vsColors[which(paste(dfInput[[sColorBy]])==vsColorValues[iColor])]=vsColorRBG[iColor] + } + + #If NAs are seperately given color, then color here + if(!is.null(sPlotNAColor)) + { + vsColors[which(is.na(dfInput[[sColorBy]]))] = sPlotNAColor + vsColorRBG[which(vsColorValues=="NA")] = sPlotNAColor + } + } + + # Reduce the bugs down to the ones in the list to be plotted + viBugsToPlot = which(row.names(mNMDSData$species) %in% vsBugsToPlot) + viMetadataDummy = which(names(dfDummyMetadata) %in% vsMetadata) + + # Build the matrix of metadata coordinates + mMetadataCoordinates = matrix(rep(NA, iNumberMetadata*2),nrow=iNumberMetadata) + + for( i in 1:iNumberMetadata ) + { + lxReturn = NA + if( i %in% viContinuousMetadata ) + { + lxReturn = funcGetMaximumForMetadatum(dfDummyMetadata[[i]],mNMDSData$points) + } else { + lxReturn = funcGetCentroidForMetadatum(dfDummyMetadata[[i]],mNMDSData$points) + } + mMetadataCoordinates[i,] = c(lxReturn$x,lxReturn$y) + } + row.names(mMetadataCoordinates) = vsRowNames + + #!# metadata default dependent + # Plot the biplot with the centroid constructed metadata coordinates + if( ( length( viMetadataDummy ) == 0 ) && ( is.null( sMetadata ) ) ) + { + viMetadataDummy = 1:nrow(mMetadataCoordinates) + } + + # Plot samples + # Make output name + if(is.null(sOutputFileName)) + { + viPeriods = which(sInputFileName==".") + if(length(viPeriods)>0) + { + sOutputFileName = paste(OutputFileName[1:viPeriods[length(viPeriods)]],"pdf",sep=".") + } else { + sOutputFileName = paste(sInputFileName,"pdf",sep=".") + } + } + + pdf(sOutputFileName,useDingbats=FALSE) + plot(mNMDSData$points, xlab=paste("NMDS1","Stress=",mNMDSData$stress), ylab="NMDS2", pch=vsShapes, col=vsColors) + title(sTitle,line=3) + # Plot Bugs + mPlotBugs = mNMDSData$species[viBugsToPlot,] + if(length(viBugsToPlot)==1) + { + text(x=mPlotBugs[1],y=mPlotBugs[2],labels=row.names(mNMDSData$species)[viBugsToPlot],col=sTextColor) + } else if(length(viBugsToPlot)>1){ + text(x=mPlotBugs[,1],y=mPlotBugs[,2],labels=row.names(mNMDSData$species)[viBugsToPlot],col=sTextColor) + } + + # Add alternative axes + axis(3, col=sArrowColor) + axis(4, col=sArrowColor) + box(col = "black") + + # Plot Metadata + if(length(viMetadataDummy)>0) + { + if(fPlotArrow) + { + # Plot arrows + for(i in viMetadataDummy) + { + curCoordinates = mMetadataCoordinates[i,] + curCoordinates = curCoordinates * dResizeArrow + # Plot Arrow + arrows(0,0, curCoordinates[1] * 0.8, curCoordinates[2] * 0.8, col=sArrowColor, length=0.1 ) + } + } + # Plot text + if(length(viMetadataDummy)==1) + { + text(x=mMetadataCoordinates[viMetadataDummy,][1]*dResizeArrow*0.8, y=mMetadataCoordinates[viMetadataDummy,][2]*dResizeArrow*0.8, labels=row.names(mMetadataCoordinates)[viMetadataDummy],col=sArrowTextColor) + } else { + text(x=mMetadataCoordinates[viMetadataDummy,1]*dResizeArrow*0.8, y=mMetadataCoordinates[viMetadataDummy,2]*dResizeArrow*0.8, labels=row.names(mMetadataCoordinates)[viMetadataDummy],col=sArrowTextColor) + } + } + + sLegendText = c(paste(vsColorValues,sColorBy,sep="_"),paste(vsShapeValues,sMetadataShape,sep="_")) + sLegendShapes = c(rep(cDefaultShape,length(vsColorValues)),vsShapeShapes) + sLegendColors = c(vsColorRBG,rep(cDefaultColor,length(vsShapeValues))) + if(length(sLegendText)>0) + { + legend("topright",legend=sLegendText,pch=sLegendShapes,col=sLegendColors) + } + + # Original biplot call if you want to check the custom ploting of the script + # There will be one difference where the biplot call scales an axis, this one does not. In relation to the axes, the points, text and arrows should still match. + # Axes to the top and right are for the arrow, otherse are for markers and bug names. + #biplot(mNMDSData$points,mMetadataCoordinates[viMetadataDummy,],xlabs=vsShapes,xlab=paste("MDS1","Stress=",mNMDSData$stress),main="Biplot function Bugs and Sampes - Metadata Plotted with Centroids") + dev.off() +} + +# This is the equivalent of __name__ == "__main__" in Python. +# That is, if it's true we're being called as a command line script; +# if it's false, we're being sourced or otherwise included, such as for +# library or inlinedocs. +if( identical( environment( ), globalenv( ) ) && + !length( grep( "^source\\(", sys.calls( ) ) ) ) +{ + lsArgs <- parse_args( pArgs, positional_arguments=TRUE ) + + print("lsArgs") + print(lsArgs) + + funcDoBiplot( + sBugs = lsArgs$options$sBugs, + sMetadata = lsArgs$options$sMetadata, + sColorBy = lsArgs$options$sColorBy, + sColorRange = lsArgs$options$sColorRange, + sTextColor = lsArgs$options$sTextColor, + sArrowColor = lsArgs$options$sArrowColor, + sArrowTextColor = lsArgs$options$sArrowTextColor, + sPlotNAColor = lsArgs$options$sPlotNAColor, + sShapeBy = lsArgs$options$sShapeBy, + sShapes = lsArgs$options$sShapes, + sDefaultMarker = lsArgs$options$sDefaultMarker, + sRotateByMetadata = lsArgs$options$sRotateByMetadata, + dResizeArrow = lsArgs$options$dResizeArrow, + fPlotArrow = !lsArgs$options$fNoPlotMetadataArrows, + sTitle = lsArgs$options$sTitle, + sInputFileName = lsArgs$args[2], + sLastMetadata = lsArgs$args[1], + sOutputFileName = lsArgs$options$sOutputFileName) +} diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/scripts/scriptConvertBetweenBIOMAndPCL.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/scripts/scriptConvertBetweenBIOMAndPCL.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,55 @@ +#!/usr/bin/env python +""" +Author: Timothy Tickle +Description: Converts between BIOM and PCL files. If a PCL file is read, an equivalent BIOM file will be written; if a BIOM file is read, an equivalent pcl file will be written. +""" + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2013" +__credits__ = ["Timothy Tickle","George Weingart"] +__license__ = "" +__version__ = "" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@hsph.harvard.edu" +__status__ = "Development" + +from AbundanceTable import AbundanceTable +import argparse +from ConstantsBreadCrumbs import ConstantsBreadCrumbs +import os +import sys + + +#Set up arguments reader +argp = argparse.ArgumentParser( prog = "convertBetweenBIOMAndPCL.py", + description = """Converts a PCL file to a BIOM file and visa versa.""" ) + +#Arguments +#For table +argp.add_argument("-i","--id", dest="sID", default = None, metavar= "Sample ID", help="The metadata indicating the sample ID.") +argp.add_argument("-l","--meta", dest = "sLastMetadataName", default = None, metavar= "Last Metadata Name", help="The last listed metadata before the first data measurement in the pcl file or to be in the pcl file.") +argp.add_argument("-r","--rowMetadataID", dest = "sLastMetadataRow", default = None, metavar = "Last Row Metadata Column", help = "If row metadata is present in a PCL file, what is the id of the last row metadata column (most right column that contains row metadata). PCL file only.") +argp.add_argument("-f","--delim", dest = "cFileDelimiter", action= "store", metavar="File Delimiter", default="\t", help="File delimiter, default tab") +argp.add_argument("strFileAbund", metavar = "Abundance file", help ="Input data file") +argp.add_argument("strOutputFile", default = "", nargs="?", metavar = "Selection Output File", help ="Output file") + +args = argp.parse_args( ) + +# Make the output file name (if not given) and get the type of output file name +# Change the extension from BIOM to pcl +lsFilePieces = os.path.splitext(args.strFileAbund) +strOutputFileType = ConstantsBreadCrumbs.c_strPCLFile if lsFilePieces[-1]=="."+ConstantsBreadCrumbs.c_strBiomFile else ConstantsBreadCrumbs.c_strBiomFile + +if not args.strOutputFile: + args.strOutputFile = lsFilePieces[0] + "." + strOutputFileType + +# Set the last metadata to the id if not given. +if not args.sLastMetadataName: + args.sLastMetadataName = args.sID + +# Read in abundance table +abndTable = AbundanceTable.funcMakeFromFile(args.strFileAbund, cDelimiter=args.cFileDelimiter, sMetadataID=args.sID, sLastMetadataRow = args.sLastMetadataRow, sLastMetadata=args.sLastMetadataName, xOutputFile=args.strOutputFile) +if not abndTable: + print("Could not create an abundance table from the given file and settings.") +else: + abndTable.funcWriteToFile(args.strOutputFile, cDelimiter=args.cFileDelimiter, cFileType=strOutputFileType) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/scripts/scriptEnvToTable.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/scripts/scriptEnvToTable.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,56 @@ +#!/usr/bin/env python +""" +Author: Timothy Tickle +Description: Convert Env file to table +""" + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "" +__version__ = "" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import sys +import argparse +import csv + + +#Set up arguments reader +argp = argparse.ArgumentParser( prog = "scriptEnvToTable.py", + description = """Convert Env file to table""" ) + +#Arguments +#For table +argp.add_argument("strEnvFile", metavar = "EnvFile", help ="EnvFile data file") +argp.add_argument("strOutputFile", metavar = "OutputFile", help ="Output File") +args = argp.parse_args( ) + +hndlReader = csv.reader(open(args.strEnvFile,'rU'), delimiter="\t") + +lsListOfIDs = [] +lsListOfFeatures = [] +dictValues = {} +for lsLine in hndlReader: + print(lsLine) + lsListOfIDs.append(lsLine[1]) + lsListOfFeatures.append(lsLine[0]) + tpleKey = tuple([lsLine[1],lsLine[0]]) + if tpleKey in dictValues: + print("Error:: Duplicate key entries found") + exit(1) + dictValues[tpleKey] = lsLine[2] + +lsListOfIDs = list(set(lsListOfIDs)) +lsListOfFeatures = list(set(lsListOfFeatures)) +print(lsListOfIDs) +print(lsListOfFeatures) +hndlWrite = csv.writer(open(args.strOutputFile,'w'), delimiter="\t") +hndlWrite.writerow(["ID"]+lsListOfIDs) +for sFeature in lsListOfFeatures: + lsFeatureLine = [sFeature] + for sSample in lsListOfIDs: + lsFeatureLine.append(dictValues.get(tuple([sSample,sFeature]),0)) + hndlWrite.writerow(lsFeatureLine) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/scripts/scriptManipulateTable.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/scripts/scriptManipulateTable.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,295 @@ +#!/usr/bin/env python +""" +Author: Timothy Tickle +Description: Performs common manipulations on tables +""" + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "" +__version__ = "" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import argparse +import csv +import sys +import re +import os +import numpy as np +from src.AbundanceTable import AbundanceTable +#from src.PCA import PCA +from src.ValidateData import ValidateData + +#Set up arguments reader +argp = argparse.ArgumentParser( prog = "scriptManipulateTable.py", + description = """Performs common manipulations on tables.\nExample: python scriptManipulateTable.py -i TID -l STSite Test.pcl""" ) + +#Arguments +#Describe table +argp.add_argument("-i","--id", dest="sIDName", default="ID", help="Abundance Table ID") +argp.add_argument("-l","--meta", dest="sLastMetadataName", help="Last metadata name") +argp.add_argument("-d","--fileDelim", dest= "cFileDelimiter", action= "store", default="\t", help="File delimiter, default tab") +argp.add_argument("-f","--featureDelim", dest= "cFeatureDelimiter", action= "store", default="|", help="Feature (eg. bug or function) delimiter, default '|'") + +#Checked x 2 +argp.add_argument("-n","--doNorm", dest="fNormalize", action="store_true", default=False, help="Flag to turn on normalization") +argp.add_argument("-s","--doSum", dest="fSum", action="store_true", default=False, help="Flag to turn on summation") + +#Unsupervised filtering +argp.add_argument("-A","--doFilterAbundance", dest="strFilterAbundance", action="store", default=None, help="Turns on filtering by abundance (remove features that do not have a minimum abundance in a minimum number of samples); Should be a real number and an integer in the form 'minAbundance,minSamples', (should be performed on a normalized file).") +argp.add_argument("-P","--doFilterPercentile", dest="strFilterPercentile", action="store", default=None, help="Turns on filtering by percentile Should be two numbers between 0 and 1 in the form 'percentile,percentage'. (should be performed on a normalized file).") +argp.add_argument("-O","--doFilterOccurrence", dest="strFilterOccurence", action="store", default=None, help="Turns on filtering by occurrence. Should be two integers in the form 'minSequence,minSample' (should NOT be performed on a normalized file).") +#argp.add_argument("-D","--doFilterDeviation", dest="dCuttOff", action="store", type=float, default=None, help="Flag to turn on filtering by standard deviation (should NOT be performed on a normalized file).") + +#Change bug membership +argp.add_argument("-t","--makeTerminal", dest="fMakeTerminal", action="store_true", default=False, help="Works reduces the file to teminal features in the original file.") +argp.add_argument("-u","--reduceOTUs", dest="fRemoveOTUs", action="store_true", default=False, help="Remove otu entries from file.") +argp.add_argument("-c","--reduceToClade", dest="iClade", action="store", type=int, default=None, help="Specify a level of clade to reduce to [].") +argp.add_argument("-b","--reduceToFeatures", dest="strFeatures", action="store", default=None, help="Reduce measurements to certain features (bugs or functions). This can be a comma delimited string (of atleast 2 bugs) or a file.") + +#Manipulate based on metadata +#Checked +argp.add_argument("-y","--stratifyBy", dest="strStratifyBy", action="store", default=None, help="Metadata to stratify tables by.") +argp.add_argument("-r","--removeMetadata", dest="strRemoveMetadata", action="store", default=None, help="Remove samples of this metadata and value (format comma delimited string with metadata id first and the values to remove after 'id,lvalue1,value2').") + +#Manipulate lineage +#Checked +argp.add_argument("-x","--doPrefixClades", dest="fPrefixClades", action="store_true", default=False, help="Flag to turn on adding prefixes to clades to better identify them, for example s__ will be placed infront of each species.") + +#Combine tables +#argp.add_argument("-m","--combineIntersect", dest="fCombineIntersect", action="store_true", default=False, help="Combine two tables including only common features/metadata (intersection).") +#argp.add_argument("-e","--combineUnion", dest="fCombineUnion", action="store_true", default=False, help="Combine two tables (union).") + +#Dimensionality Reduction +#argp.add_argument("-p","--doPCA", dest="fDoPCA",action="store_true", default=False, help="Flag to turn on adding metabugs and metametadata by performing PCA on each of bug relative abundance and continuous metadata and add the resulting components") + +#Checked +argp.add_argument("-o","--output", dest="strOutFile", action="store", default=None, help="Indicate output pcl file.") +argp.add_argument("strFileAbund", help ="Input data file") + +args = argp.parse_args( ) + +# Creat output file if needed. +if not args.strOutFile: + args.strOutFile = os.path.splitext(args.strFileAbund)[0]+"-mod.pcl" +lsPieces = os.path.splitext(args.strOutFile) + +#List of abundance tables +lsTables = [] + +#Read in abundance table +abndTable = AbundanceTable.funcMakeFromFile(xInputFile=args.strFileAbund, + cDelimiter = args.cFileDelimiter, + sMetadataID = args.sIDName, + sLastMetadata = args.sLastMetadataName, + lOccurenceFilter = None, + cFeatureNameDelimiter=args.cFeatureDelimiter, + xOutputFile = args.strOutFile) + +#TODO Check filtering, can not have some filtering together + +# Make feature list +lsFeatures = [] +if args.strFeatures: + print "Get features not completed" +# if "," in args.strFeatures: +# lsFeatures = args.strFeatures.split(",") +# print "ManipulateTable::Reading in feature list "+str(len(lsFeatures))+"." +# else: +# csvr = csv.reader(open(args.strFeatures, "rU")) +# print "ManipulateTable::Reading in feature file "+args.strFeatures+"." +# for lsLine in csvr: +# lsFeatures.extend(lsLine) + +lsTables.append(abndTable) + +# Do summing +#Sum if need +if args.fSum: + for abndTable in lsTables: + print "ManipulateTable::"+abndTable.funcGetName()+" had "+str(len(abndTable.funcGetFeatureNames()))+" features before summing." + fResult = abndTable.funcSumClades() + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" was summed." + print "ManipulateTable::"+abndTable.funcGetName()+" has "+str(len(abndTable.funcGetFeatureNames()))+" features after summing." + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" was NOT summed." + +# Filter on counts +if args.strFilterOccurence: + iMinimumSequence,iMinimumSample = args.strFilterOccurence.split(",") + for abndTable in lsTables: + if abndTable.funcIsNormalized(): + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" is normalized and can not be filtered by occurence. This filter needs counts." + else: + fResult = abndTable.funcFilterAbundanceBySequenceOccurence(iMinSequence = int(iMinimumSequence), iMinSamples = int(iMinimumSample)) + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" was filtered by occurence and now has "+str(len(abndTable.funcGetFeatureNames()))+" features." + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" was NOT filtered by occurence." + +# Change bug membership +if args.fMakeTerminal: + lsTerminalTables = [] + for abndTable in lsTables: + print "ManipulateTable::"+abndTable.funcGetName()+" had "+str(len(abndTable.funcGetFeatureNames()))+" features before making terminal." + abndTable = abndTable.funcGetFeatureAbundanceTable(abndTable.funcGetTerminalNodes()) + if abndTable: + print "ManipulateTable::"+abndTable.funcGetName()+" has "+str(len(abndTable.funcGetFeatureNames()))+" terminal features." + lsTerminalTables.append(abndTable) + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" was not made terminal." + lsTables = lsTerminalTables + +if args.fRemoveOTUs: + lsNotOTUs = [] + for abndTable in lsTables: + print "ManipulateTable::"+abndTable.funcGetName()+" had "+str(len(abndTable.funcGetFeatureNames()))+" features before removing OTUs." + abndTable = abndTable.funcGetWithoutOTUs() + if abndTable: + print "ManipulateTable::"+abndTable.funcGetName()+" had OTUs removed and now has "+str(len(abndTable.funcGetFeatureNames()))+" features." + lsNotOTUs.append(abndTable) + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" OTUs were not removed." + lsTables = lsNotOTUs + +if args.iClade: + for abndTable in lsTables: + fResult = abndTable.funcReduceFeaturesToCladeLevel(args.iClade) + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" was reduced to clade level "+str(args.iClade)+"." + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" was NOT reduced in clade levels." + +if args.strFeatures: + for abndTable in lsTables: + fResult = abndTable.funcGetFeatureAbundanceTable(lsFeatures) + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" has been reduced to given features and now has "+str(len(abndTable.funcGetFeatureNames()))+" features." + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" could not be reduced to the given list." + +if args.strRemoveMetadata: + lsMetadata = args.strRemoveMetadata.split(",") + for abndTable in lsTables: + fResult = abndTable.funcRemoveSamplesByMetadata(sMetadata=lsMetadata[0], lValuesToRemove=lsMetadata[1:]) + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" has had samples removed and now has "+str(len(abndTable.funcGetSampleNames()))+" samples." + else: + print "ManipulateTable::ERROR. Could not remove samples from "+abndTable.funcGetName()+"." + +# Normalize if needed +if args.fNormalize: + for abndTable in lsTables: + fResult = abndTable.funcNormalize() + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" was normalized." + else: + print "ManipulateTable::"+abndTable.funcGetName()+" was NOT normalized." + +# Filter on percentile +if args.strFilterPercentile: + dPercentile,dPercentage = args.strFilterPercentile.split(",") + for abndTable in lsTables: + if abndTable.funcIsNormalized(): + fResult = abndTable.funcFilterAbundanceByPercentile(dPercentileCutOff = float(dPercentile), dPercentageAbovePercentile = float(dPercentage)) + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" has been reduced by percentile and now has "+str(len(abndTable.funcGetFeatureNames()))+" features." + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" could not be reduced by percentile." + else: + print "ManipulateTable::"+abndTable.funcGetName()+" was NOT normalized and so the percentile filter is invalid, please indicate to normalize the table." + +# Filter on abundance (should go after filter on percentile because the filter on percentile +# needs the full distribution of features in a sample +if args.strFilterAbundance: + dAbundance,iMinSamples = args.strFilterAbundance.split(",") + dAbundance = float(dAbundance) + iMinSamples = int(iMinSamples) + for abndTable in lsTables: + if abndTable.funcIsNormalized(): + fResult = abndTable.funcFilterAbundanceByMinValue(dMinAbundance=dAbundance,iMinSamples=iMinSamples) + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" has been reduced by minimum relative abundance value and now has "+str(len(abndTable.funcGetFeatureNames()))+" features." + else: + print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" could not be reduced by percentile." + else: + print "ManipulateTable::"+abndTable.funcGetName()+" was NOT normalized and so the abundance filter is invalid, please indicate to normalize the table." + +#if args.dCuttOff: +# print "Standard deviation filtering not completed" +# for abndTable in lsTables: +# abndTable.funcFilterFeatureBySD(dMinSDCuttOff=args.dCuttOff) +# if fResult: +# print "ManipulateTable::"+abndTable.funcGetName()+" has been reduced by standard deviation and now has "+str(len(abndTable.funcGetFeatureNames()))+" features." +# else: +# print "ManipulateTable::ERROR. "+abndTable.funcGetName()+" could not be reduced by standard devation." + +# Need to normalize again after abundance data filtering given removing features breaks the normalization +# This happends twice because normalization is required to make the abundance data to filter on ;-) +# Normalize if needed +if args.fNormalize: + for abndTable in lsTables: + fResult = abndTable.funcNormalize() + if fResult: + print "ManipulateTable::"+abndTable.funcGetName()+" was normalized after filtering on abundance data." + +#Manipulate lineage +if args.fPrefixClades: + for abndTable in lsTables: + fResult = abndTable.funcAddCladePrefixToFeatures() + if fResult: + print "ManipulateTable::Clade Prefix was added to "+abndTable.funcGetName() + else: + print "ManipulateTable::ERROR. Clade Prefix was NOT added to "+abndTable.funcGetName() + +# Under development +# Reduce dimensionality +#if args.fDoPCA: +# pcaCur = PCA() +# for abndTable in lsTables: +# +# # Add data features +# # Make data components and add to abundance table +# pcaCur.loadData(abndTable,True) +# pcaCur.run(fASTransform=True) +# ldVariance = pcaCur.getVariance() +# lldComponents = pcaCur.getComponents() +# # Make Names +# lsNamesData = ["Data_PC"+str((tpleVariance[0]+1))+"_"+re.sub("[\.|-]","_",str(tpleVariance[1])) for tpleVariance in enumerate(ldVariance)] +# abndTable.funcAddDataFeature(lsNamesData,lldComponents) +# +# # Add metadata features +# # Convert metadata to an input for PCA +# pcaCur.loadData(pcaCur.convertMetadataForPCA(abndTable),False) +# fSuccessful = pcaCur.run(fASTransform=False) +# if(fSuccessful): +# ldVariance = pcaCur.getVariance() +# lldComponents = pcaCur.getComponents() +# # Make Names +# lsNamesMetadata = ["Metadata_PC"+str((tpleVariance[0]+1))+"_"+re.sub("[\.|-]","_",str(tpleVariance[1])) for tpleVariance in enumerate(ldVariance)] +# # Make metadata components and add to abundance +# llsMetadata = [list(npdRow) for npdRow in lldComponents] +# abndTable.funcAddMetadataFeature(lsNamesMetadata, llsMetadata) +# else: +# print "ManipulateTable::No metadata to PCA, no PCA components added to file based on metadata" + +#Manipulate based on metadata +if args.strStratifyBy: + labndStratifiedTables = [] + for abndTable in lsTables: + labndResult = abndTable.funcStratifyByMetadata(strMetadata=args.strStratifyBy) + print "ManipulateTable::"+abndTable.funcGetName()+" was stratified by "+args.strStratifyBy+" in to "+str(len(labndResult))+" tables." + labndStratifiedTables.extend(labndResult) + lsTables = labndStratifiedTables + +if len(lsTables) == 1: + lsTables[0].funcWriteToFile(args.strOutFile) +else: + iIndex = 1 + for abndManTable in lsTables: + abndManTable.funcWriteToFile(lsPieces[0]+str(iIndex)+lsPieces[1]) + iIndex = iIndex + 1 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/scripts/scriptPcoa.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/scripts/scriptPcoa.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,143 @@ +#!/usr/bin/env python +""" +Author: Timothy Tickle +Description: Make PCoA of an abundance file +""" + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "" +__version__ = "" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import sys +import argparse +from src.AbundanceTable import AbundanceTable +from src.Metric import Metric +import csv +import os +from src.PCoA import PCoA + +#Set up arguments reader +argp = argparse.ArgumentParser( prog = "scriptPcoa.py", + description = """PCoAs an abundance file given a metadata.\nExample:python scriptPcoa.py -i TID -l STSite""" ) + +#Arguments +#For table +argp.add_argument("-i","--id", dest="sIDName", default="ID", help="Abundance Table ID") +argp.add_argument("-l","--meta", dest="sLastMetadataName", help="Last metadata name") +argp.add_argument("-d","--fDelim", dest= "cFileDelimiter", action= "store", default="\t", help="File delimiter, default tab") +argp.add_argument("-f","--featureDelim", dest="cFeatureNameDelimiter", action= "store", metavar="Feature Name Delimiter", default="|", help="Feature delimiter") + +argp.add_argument("-n","--doNorm", dest="fDoNormData", action="store_true", default=False, help="Flag to turn on normalization") +argp.add_argument("-s","--doSum", dest="fDoSumData", action="store_true", default=False, help="Flag to turn on summation") + +argp.add_argument("-p","--paint", dest="sLabel", metavar= "Label", default=None, help="Label to paint in the PCoA") +argp.add_argument("-m","--metric", dest="strMetric", metavar = "distance", default = PCoA.c_BRAY_CURTIS, help ="Distance metric to use. Pick from braycurtis, canberra, chebyshev, cityblock, correlation, cosine, euclidean, hamming, spearman, sqeuclidean, unifrac_unweighted, unifrac_weighted") +argp.add_argument("-o","--outputFile", dest="strOutFile", metavar= "outputFile", default=None, help="Specify the path for the output figure.") +argp.add_argument("-D","--DistanceMatrix", dest="strFileDistanceMatrix", metavar= "strFileDistanceMatrix", default=None, help="Specify the path for outputing the distance matrix (if interested). Default this will not output.") +argp.add_argument("-C","--CoordinatesMatrix", dest="strFileCoordinatesMatrix", metavar= "strFileCoordinatesMatrix", default=None, help="Specify the path for outputing the x,y coordinates matrix (Dim 1 and 2). Default this will not output.") + +# Unifrac arguments +argp.add_argument("-t","--unifracTree", dest="istrmTree", metavar="UnifracTreeFile", default=None, help="Optional file only needed for UniFrac calculations.") +argp.add_argument("-e","--unifracEnv", dest="istrmEnvr", metavar="UnifracEnvFile", default=None, help="Optional file only needed for UniFrac calculations.") +argp.add_argument("-c","--unifracColor", dest="fileUnifracColor", metavar="UnifracColorFile", default = None, help="A text file indicating the groupings of metadata to color. Each line in the file is a group to color. An example file line would be 'GroupName:ID,ID,ID,ID'") + +argp.add_argument("strFileAbund", metavar = "Abundance file", nargs="?", help ="Input data file") + +args = argp.parse_args( ) + +#Read in abundance table +abndTable = None +if args.strFileAbund: + abndTable = AbundanceTable.funcMakeFromFile(args.strFileAbund, + cDelimiter = args.cFileDelimiter, + sMetadataID = args.sIDName, + sLastMetadata = args.sLastMetadataName, + cFeatureNameDelimiter= args.cFeatureNameDelimiter) + + #Normalize if need + if args.fDoSumData: + abndTable.funcSumClades() + + #Sum if needed + if args.fDoNormData: + abndTable.funcNormalize() + +#Get the metadata to paint +lsKeys = None +if abndTable: + lsKeys = abndTable.funcGetMetadataCopy().keys() if not args.sLabel else [args.sLabel] + +#Get pieces of output file +if not args.strOutFile: + if not args.strFileAbund: + args.strOutFile = os.path.splitext(os.path.basename(args.istrmEnvr))[0]+"-pcoa.pdf" + else: + args.strOutFile = os.path.splitext(os.path.basename(args.strFileAbund))[0]+"-pcoa.pdf" +lsFilePieces = os.path.splitext(args.strOutFile) + +# Make PCoA object +# Get PCoA object and plot +pcoa = PCoA() +if(not args.strMetric in [Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]) and abndTable: + pcoa.loadData(abndTable,True) +# Optional args.strFileDistanceMatrix if not none will force a printing of the distance measures to the path in args.strFileDistanceMatrix +pcoa.run(tempDistanceMetric=args.strMetric, iDims=2, strDistanceMatrixFile=args.strFileDistanceMatrix, istrmTree=args.istrmTree, istrmEnvr=args.istrmEnvr) + +# Write dim 1 and 2 coordinates to file +if args.strFileCoordinatesMatrix: + lsIds = pcoa.funcGetIDs() + mtrxCoordinates = pcoa.funcGetCoordinates() + csvrCoordinates = csv.writer(open(args.strFileCoordinatesMatrix, 'w')) + csvrCoordinates.writerow(["ID","Dimension_1","Dimension_2"]) + for x in xrange(mtrxCoordinates.shape[0]): + strId = lsIds[x] if lsIds else "" + csvrCoordinates.writerow([strId]+mtrxCoordinates[x].tolist()) + +# Paint metadata +if lsKeys: + for iIndex in xrange(len(lsKeys)): + lsMetadata = abndTable.funcGetMetadata(lsKeys[iIndex]) + + pcoa.plotList(lsLabelList = lsMetadata, + strOutputFileName = lsFilePieces[0]+"-"+lsKeys[iIndex]+lsFilePieces[1], + iSize=20, + dAlpha=1.0, + charForceColor=None, + charForceShape=None, + fInvert=False, + iDim1=1, + iDim2=2) + +if args.strMetric in [Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]: + + c_sNotGiven = "Not_specified" + + lsIds = pcoa.funcGetIDs() + lsGroupLabels = [c_sNotGiven for s in lsIds] + + if args.fileUnifracColor: + + # Read color file and make a dictionary to convert ids + lsColorLines = csv.reader(open(args.fileUnifracColor)) + dictConvertIDToGroup = {} + for lsLine in lsColorLines: + if lsLine: + sGroupID, sFirstID = lsLine[0].split(":") + dictConvertIDToGroup.update(dict([(sID,sGroupID) for sID in [sFirstID]+lsLine[1:]])) + + lsGroupLabels = [dictConvertIDToGroup.get(sID,c_sNotGiven) for sID in lsIds] + + pcoa.plotList(lsLabelList = lsGroupLabels, + strOutputFileName = lsFilePieces[0]+"-"+args.strMetric+lsFilePieces[1], + iSize=20, + dAlpha=1.0, + charForceColor=None, + charForceShape=None, + fInvert=False, + iDim1=1, + iDim2=2) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/scripts/scriptPlotFeature.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/scripts/scriptPlotFeature.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,146 @@ +#!/usr/bin/env python +""" +Author: Timothy Tickle +Description: Plots feaures +""" + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "" +__version__ = "" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import sys +import argparse +import csv +import os +from src.BoxPlot import BoxPlot +from src.Histogram import Histogram +from src.ScatterPlot import ScatterPlot + +def funcPlotBoxPlot(lxVariable1,lxVariable2,fOneIsNumeric): + + ly,lsLabels = [lxVariable1,lxVariable2] if fOneIsNumeric else [lxVariable2,lxVariable1] + + # Group data + dictGroups = {} + for iIndex in xrange(len(ly)): + lsList = dictGroups.get(lsLabels[iIndex],[]) + lsList.append(ly[iIndex]) + dictGroups.setdefault(lsLabels[iIndex],lsList) + ly = [dictGroups[sKey] for sKey in dictGroups.keys()] + lsLabels = dictGroups.keys() + + BoxPlot.funcPlot(ly=ly, lsLabels=lsLabels, strOutputFigurePath=args.strOutputFile, strTitle=args.strTitle, strXTitle=args.strX, strYTitle=args.strY, strColor=args.strColor, fJitter=True, fInvert=args.fColor, fInvertY=args.fAxis) + + +#Set up arguments reader +argp = argparse.ArgumentParser( prog = "scriptBoxPlot.py\nExample: python scriptBoxPlot.py Input.pcl valuesID groupID", + description = "Make a box plot from an abundance table.") + +#Sepecify output if needed +argp.add_argument("-o","--output", dest="strOutputFile", action="store", default=None, help="Output file name.") + +# Text annotation +argp.add_argument("-t","--title", dest="strTitle", action="store", default=None, help="Test for the title.") +argp.add_argument("-x","--xaxis", dest="strX", action="store", default=None, help="Text for the x-axis.") +argp.add_argument("-y","--yaxis", dest="strY", action="store", default=None, help="Text for the y-axis.") + +# Color options +argp.add_argument("-c","--color", dest="strColor", action="store", default="#83C8F9", help="Fill color as a Hex number (including the #).") +argp.add_argument("-r","--invertcolor", dest="fColor", action="store_true", default=False, help="Flag to invert the background to black.") + +# Axis adjustments +argp.add_argument("-s","--invertyaxis", dest="fAxis", action="store_true", default=False, help="Flag to invert the y axis.") + +# Required +argp.add_argument("strFileAbund", help ="Input data file") +argp.add_argument("strFeatures", nargs = "+", help="Features to plot (from one to two metadata).") + +args = argp.parse_args( ) + +#Holds the data +lxVariable1 = None +lxVariable2 = None +fOneIsNumeric = False +fTwoIsNumeric = False + +strFeatureOneID = args.strFeatures[0] +strFeatureTwoID = None if len(args.strFeatures)<2 else args.strFeatures[1] + +# If the output file is not specified, make it up +if not args.strOutputFile: + lsPieces = os.path.splitext(args.strFileAbund) + args.strOutputFile = [lsPieces[0],strFeatureOneID] + if strFeatureTwoID: + args.strOutputFile = args.strOutputFile+[strFeatureTwoID] + args.strOutputFile = "-".join(args.strOutputFile+["plotfeature.pdf"]) + +if not args.strTitle: + args.strTitle = [strFeatureOneID] + if strFeatureTwoID: + args.strTitle = args.strTitle+[strFeatureTwoID] + args.strTitle = " vs ".join(args.strTitle) + +csvReader = csv.reader(open(args.strFileAbund, 'rU') if isinstance(args.strFileAbund,str) else args.strFileAbund, delimiter="\t") + +if args.strX is None: + args.strX = strFeatureOneID + +if args.strY is None: + args.strY = strFeatureTwoID + +# Get values and groupings +for lsLine in csvReader: + if lsLine[0] == strFeatureOneID: + lxVariable1 = lsLine[1:] + if not strFeatureTwoID is None: + if lsLine[0] == strFeatureTwoID: + lxVariable2 = lsLine[1:] + +# Remove NAs +liNAs = [i for i,x in enumerate(lxVariable1) if x.lower() == "na"] +liNAs = set([i for i,x in enumerate(lxVariable1) if x.lower() == "na"]+liNAs) +lxVariable1 = [x for i,x in enumerate(lxVariable1) if not i in liNAs] + +if not lxVariable2 is None: + lxVariable2 = [x for i,x in enumerate(lxVariable2) if not i in liNAs] + +# Type variables +if not lxVariable1 is None: + try: + float(lxVariable1[0]) + lxVariable1 = [float(xItem) for xItem in lxVariable1] + fOneIsNumeric = True + except ValueError: + pass + +if not lxVariable2 is None: + try: + float(lxVariable2[0]) + lxVariable2 = [float(xItem) for xItem in lxVariable2] + fTwoIsNumeric = True + except ValueError: + pass + +if lxVariable1 is None: + print("scriptPlotFeature:: Sorry, could not find the feature "+ strFeatureOneID +" in the file "+args.strFileAbund+" .") +elif( (lxVariable2 is None) and (not strFeatureTwoID is None) ): + print("scriptPlotFeature:: Sorry, could not find the feature "+ strFeatureTwoID +" in the file "+args.strFileAbund+" .") +else: + # Plot as needed + if((not lxVariable1 is None ) and (not lxVariable2 is None)): + if(sum([fOneIsNumeric, fTwoIsNumeric])==0): + print "scriptPlotFeature:: Error, If plotting 2 variables, atleast 1 should be numeric." + elif(sum([fOneIsNumeric, fTwoIsNumeric])==1): + funcPlotBoxPlot(lxVariable1,lxVariable2, fOneIsNumeric=fOneIsNumeric) + elif(sum([fOneIsNumeric, fTwoIsNumeric])==2): + ScatterPlot.funcPlot(lxVariable1, lxVariable2, args.strOutputFile, strTitle=args.strTitle, strXTitle=args.strX, strYTitle=args.strY, strColor=args.strColor, fInvert=args.fColor) + elif(not lxVariable1 is None ): + if fOneIsNumeric: + Histogram.funcPlot(lxVariable1, args.strOutputFile, strTitle=args.strTitle, strXTitle=args.strX, strYTitle="Frequency", strColor=args.strColor, fInvert=args.fColor) + else: + print "Sorry currently histograms are support for only numeric data." diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/AbundanceTable.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/AbundanceTable.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,2439 @@ +""" +Author: Timothy Tickle +Description: Class to abstract an abundance table and methods to run on such a table. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import csv +import sys +import blist +from CClade import CClade +from ConstantsBreadCrumbs import ConstantsBreadCrumbs +import copy +from datetime import date +import numpy as np +import os +import re +import scipy.stats +import string +from ValidateData import ValidateData +from biom.parse import * +from biom.table import * + +c_dTarget = 1.0 +c_fRound = False +c_iSumAllCladeLevels = -1 +c_fOutputLeavesOnly = False + +import warnings +warnings.simplefilter(action = "ignore", category = FutureWarning) + + +class RowMetadata: + """ + Holds the row (feature) metadata and associated functions. + """ + + def __init__(self, dictRowMetadata, iLongestMetadataEntry=None, lsRowMetadataIDs=None): + """ Constructor requires a dictionary or row metadata. + :param dictRowMetadata: The row metadata values with the ids as the keys, must be stable (keep order) + :type: {string feature id: {'metadata': {'taxonomy': [list of metadata values]}}} + """ + + self.dictRowMetadata = dictRowMetadata + self.iLongestMetadataEntry = iLongestMetadataEntry + self.lsRowMetadataIDs = lsRowMetadataIDs + + self.dictMetadataIDs = {} + # Get the ids for the metadata + if self.dictRowMetadata: + for dictMetadata in self.dictRowMetadata.values(): + dictMetadata = dictMetadata.get(ConstantsBreadCrumbs.c_metadata_lowercase, None) + + if dictMetadata: + for key,value in dictMetadata.items(): + if self.dictMetadataIDs.get(key, None): + self.dictMetadataIDs[key] = max(self.dictMetadataIDs[key],len(dictMetadata[key])) + else: + self.dictMetadataIDs[key] = len(dictMetadata[key]) + + def funcMakeIDs(self): + """ There should be a one to one mapping of row metadata ids and the values associated here with a feature ID. + If not make ids from the key by appending numbers. + """ + + # If there exists a ids list already return (this allows ID order to be given and preserved) + # Else make a list of IDs + if self.lsRowMetadataIDs: + return self.lsRowMetadataIDs + + lsIDs = [] + lsKeys = [] + + for key, value in self.dictMetadataIDs.items(): + lsKeys.append( key ) + if value > 1: + lsIDs.extend( [ "_".join( [ key, str( iIndex ) ] ) for iIndex in xrange( value ) ] ) + else: + lsIDs.append( key ) + return [ lsIDs, lsKeys ] + + def funGetFeatureMetadata(self, sFeature, sMetadata): + """ + Returns a list of values in the order of row metadta ids for a microbial feature given an id. + + :param sFeature: Feature id to get metadata + :type: string + :param sMetadata: Metadata to get + :type: string + :return: list of metadata associated with the metadata + """ + lsMetadata = [] + if self.dictRowMetadata: + dictFeature = self.dictRowMetadata.get( sFeature, None ) + if dictFeature: + dictFeatureMetadata = dictFeature.get(ConstantsBreadCrumbs.c_metadata_lowercase, None) + if dictFeatureMetadata: + lsMetadata = dictFeatureMetadata.get(sMetadata, None) + return lsMetadata + + +class AbundanceTable: + """ + Represents an abundance table and contains common function to perform on the object. + + This class is made from an abundance data file. What is expected is a text file delimited by + a character (which is given to the object). The first column is expected to be the id column + for each of the rows. Metadata is expected before measurement data. Columns are samples and + rows are features (bugs). + + This object is currently not hashable. + """ + + def __init__(self, npaAbundance, dictMetadata, strName, strLastMetadata, rwmtRowMetadata = None, dictFileMetadata = None, lOccurenceFilter = None, cFileDelimiter = ConstantsBreadCrumbs.c_cTab, cFeatureNameDelimiter = "|"): + """ + Constructor for an abundance table. + + :param npaAbundance: Structured Array of abundance data (Row=Features, Columns=Samples) + :type: Numpy Structured Array abundance data (Row=Features, Columns=Samples) + :param dictMetadata: Dictionary of metadata {"String ID":["strValue","strValue","strValue","strValue","strValue"]} + :type: Dictionary Dictionary + :param npaRowMetdata Structured Array of row (feature) metadata (optional) + :type: Numpy Structured Array abundance data (Row=Features, Columns=Feature metadata) + :param strName: The name of the metadata that serves as the ID for the columns (For example a sample ID) + :type: string + :param strLastMetadata: The string last metadata name + :type: string + :param lOccurenceFilter: List of integers used in an occurence filter. [Min abundance, Min sample] + :type: List of integers + :param cFileDelimiter: Character used as the delimiter of the file that is read in to create the abundance table. + Will also be used to write the abudance table file to a file to keep file consistency. + :type: Character delimiter for reading the data in (default = TAB) + :param cFeatureNameDelimiter: Character used as the delimiter of the feature names (column 1). This is useful if the name are complex, for instance consensus lineages in metagenomics. + :type: Character delimiter for feature names (default = |) + """ + + ### File Metadata + + #Date + self.dateCreationDate = dictFileMetadata.get(ConstantsBreadCrumbs.c_strDateKey,None) if dictFileMetadata else None + + #Indicates if the table has been filtered and how + self._strCurrentFilterState = "" + + #The delimiter from the source file + self._cDelimiter = cFileDelimiter + + #The feature name delimiter + self._cFeatureDelimiter = cFeatureNameDelimiter + + #File type + self.strFileFormatType = dictFileMetadata.get(ConstantsBreadCrumbs.c_strFormatKey,None) if dictFileMetadata else None + + #File generation source + self.strFileGenerationSource = dictFileMetadata.get(ConstantsBreadCrumbs.c_strSourceKey,None) if dictFileMetadata else None + + #File type + self.strFileType = dictFileMetadata.get(ConstantsBreadCrumbs.c_strTypekey,None) if dictFileMetadata else None + + #File url + self.strFileURL = dictFileMetadata.get(ConstantsBreadCrumbs.c_strURLKey,None) if dictFileMetadata else None + + #The id of the file + self.strId = dictFileMetadata.get(ConstantsBreadCrumbs.c_strIDKey,None) if dictFileMetadata else None + + #The lastmetadata name (which should be preserved when writing the file) + # Can be a None if biom file is read in. + self._strLastMetadataName = strLastMetadata + + #The original number of features in the table + self._iOriginalFeatureCount = -1 + + #The name of the object relating to the file it was read from or would have been read from if it exists + #Keeps tract of changes to the file through the name + #Will be used to write out the object to a file as needed + self._strOriginalName = strName + + #The original number of samples in the table + self._iOriginalSampleCount = -1 + + #Data sparsity type + self.fSparseMatrix = dictFileMetadata.get(ConstantsBreadCrumbs.c_strSparsityKey,False) if dictFileMetadata else False + + ### Data metadata + #The column (sample) metdata + self._dictTableMetadata = dictMetadata + + #The row (feature) metadata (Row Metadata object) + self.rwmtRowMetadata = rwmtRowMetadata + + ### Data + + #The abundance data + self._npaFeatureAbundance = npaAbundance + + + ### Logistical + + #Clade prefixes for biological samples + self._lsCladePrefixes = ["k__","p__","c__","o__","f__","g__","s__"] + + #This is not a hashable object + self.__hash__ = None + + + ### Prep the object + + self._fIsNormalized = self._fIsSummed = None + #If contents is not a false then set contents to appropriate objects + # Checking to see if the data is normalized, summed and if we need to run a filter on it. + if ( self._npaFeatureAbundance != None ) and self._dictTableMetadata: + self._iOriginalFeatureCount = self._npaFeatureAbundance.shape[0] + self._iOriginalSampleCount = len(self.funcGetSampleNames()) + + self._fIsNormalized = ( max( [max( list(a)[1:] or [0] ) for a in self._npaFeatureAbundance] or [0] ) <= 1 ) + + lsLeaves = AbundanceTable.funcGetTerminalNodesFromList( [a[0] for a in self._npaFeatureAbundance], self._cFeatureDelimiter ) + self._fIsSummed = ( len( lsLeaves ) != len( self._npaFeatureAbundance ) ) + + #Occurence filtering + #Removes features that do not have a given level iLowestAbundance in a given amount of samples iLowestSampleOccurence + if ( not self._fIsNormalized ) and lOccurenceFilter: + iLowestAbundance, iLowestSampleOccurrence = lOccurenceFilter + self.funcFilterAbundanceBySequenceOccurence( iLowestAbundance, iLowestSampleOccurrence ) +# else: +# sys.stderr.write( "Abundance or metadata was None, should be atleast an empty object\n" ) + + @staticmethod + def funcMakeFromFile(xInputFile, cDelimiter = ConstantsBreadCrumbs.c_cTab, sMetadataID = None, sLastMetadataRow = None, sLastMetadata = None, + lOccurenceFilter = None, cFeatureNameDelimiter="|", xOutputFile = None): + """ + Creates an abundance table from a table file. + + :param xInputFile: Path to input file. + :type: String String path. + :param cDelimiter: Delimiter for parsing the input file. + :type: Character Character + :param sMetadataID: String ID that is a metadata row ID (found on the first column) and used as an ID for samples + :type: String String ID + :param sLastRowMetadata: The id of the last (most right column) row metadata + :type: String String ID + :param sLastMetadata: The ID of the metadata that is the last metadata before measurement or feature rows. + :type: String String ID + :param lOccurenceFilter: List of integers used in an occurence filter. [Min abundance, Min sample] + :type: List of integers + :param cFeatureNameDelimiter: Used to parse feature (bug) names if they are complex. + For example if they are consensus lineages and contain parent clade information. + :type: Character Delimiting letter + :param xOutputFile: File to output the abundance table which was read in. + :type: FileStream or String file path + :return AbundanceTable: Will return an AbundanceTable object on no error. Returns False on error. + """ + + #Get output file and remove if existing + outputFile = open( xOutputFile, "w" ) if isinstance(xOutputFile, str) else xOutputFile + + ################################################################################# + # Check if file is a biom file - if so invoke the biom routine # + ################################################################################# + strFileName = xInputFile if isinstance(xInputFile, str) else xInputFile.name + # Determine the file read function by file extension + if strFileName.endswith(ConstantsBreadCrumbs.c_strBiomFile): + BiomCommonArea = AbundanceTable._funcBiomToStructuredArray(xInputFile) + if BiomCommonArea: + lContents = [BiomCommonArea[ConstantsBreadCrumbs.c_BiomTaxData], + BiomCommonArea[ConstantsBreadCrumbs.c_Metadata], + BiomCommonArea[ ConstantsBreadCrumbs.c_dRowsMetadata], + BiomCommonArea[ConstantsBreadCrumbs.c_BiomFileInfo] + ] + + # Update last metadata and id if given + if not sLastMetadata: + strLastMetadata = BiomCommonArea[ConstantsBreadCrumbs.c_sLastMetadata] + else: + # return false on failure + lContents = False + else: + #Read in from text file to create the abundance and metadata structures + lContents = AbundanceTable._funcTextToStructuredArray(xInputFile=xInputFile, cDelimiter=cDelimiter, + sMetadataID = sMetadataID, sLastMetadataRow = sLastMetadataRow, sLastMetadata = sLastMetadata, ostmOutputFile = outputFile) + + #If contents is not a false then set contents to appropriate objects + return AbundanceTable(npaAbundance=lContents[0], dictMetadata=lContents[1], strName=str(xInputFile), strLastMetadata=sLastMetadata, rwmtRowMetadata = lContents[2], + dictFileMetadata = lContents[3], lOccurenceFilter = lOccurenceFilter, cFileDelimiter=cDelimiter, cFeatureNameDelimiter=cFeatureNameDelimiter) if lContents else False + + #Testing Status: Light happy path testing + @staticmethod + def funcCheckRawDataFile(strReadDataFileName, iFirstDataIndex = -1, sLastMetadataName = None, lOccurenceFilter = None, strOutputFileName = "", cDelimiter = ConstantsBreadCrumbs.c_cTab): + """ + Check the input abundance table. + Currently reduces the features that have no occurence. + Also inserts a NA for blank metadata and a 0 for blank abundance data. + Gives the option to filter features through an occurence filter (a feature must have a level of abundance in a minimal number of samples to be included). + Either iFirstDataIndex or sLastMetadataName must be given + + :param strReadDataFileName: File path of file to read and check. + :type: String File path. + :param iFirstDataIndex: First (row) index of data not metadata in the abundance file. + :type: Integer Index starting at 0. + :param sLastMetadataName: The ID of the last metadata in the file. Rows of measurements should follow this metadata. + :type: String + :param lOccurenceFilter: The lowest number of occurences in the lowest number of samples needed for a feature to be kept + :type: List[2] List length 2 [lowest abundance (not normalized), lowest number of samples to occur in] (eg. [2.0,2.0]) + :param strOutputFileName: File path of out put file. + :type: String File path. + :param cDelimiter: Character delimiter for reading and writing files. + :type: Character Delimiter. + :return Output Path: Output path for written checked file. + """ + + #Validate parameters + if (iFirstDataIndex == -1) and (sLastMetadataName == None): + print "AbundanceTable:checkRawDataFile::Error, either iFirstDataIndex or sLastMetadataNamemust be given." + return False + + #Get output file and remove if existing + outputFile = strOutputFileName + if not strOutputFileName: + outputFile = os.path.splitext(strReadDataFileName)[0]+ConstantsBreadCrumbs.OUTPUT_SUFFIX + + #Read input file lines + #Drop blank lines + readData = "" + with open(strReadDataFileName,'rU') as f: + readData = f.read() + readData = filter(None,readData.split(ConstantsBreadCrumbs.c_strEndline)) + + #Read the length of each line and make sure there is no jagged data + #Also hold row count for the metadata + iLongestLength = len(readData[0].split(cDelimiter)) + iMetadataRow = -1 + if not sLastMetadataName: + sLastMetadataName = "None" + for iIndex, strLine in enumerate(readData): + sLineElements = strLine.split(cDelimiter) + if sLineElements[0] == sLastMetadataName: + iMetadataRow = iIndex + iLongestLength = max(iLongestLength, len(sLineElements)) + + #If not already set, set iFirstDataIndex + if iFirstDataIndex < 0: + iFirstDataIndex = iMetadataRow + 1 + + #Used to substitute . to - + reSubPeriod = re.compile('\.') + + #File writer + with open(outputFile,'w') as f: + + #Write metadata + #Empty data is changed to a default + #Jagged ends are filled with a default + for strDataLine in readData[:iFirstDataIndex]: + lsLineElements = strDataLine.split(cDelimiter) + for iindex, sElement in enumerate(lsLineElements): + if not sElement.strip(): + lsLineElements[iindex] = ConstantsBreadCrumbs.c_strEmptyDataMetadata + if len(lsLineElements) < iLongestLength: + lsLineElements = lsLineElements + ([ConstantsBreadCrumbs.c_strEmptyDataMetadata]*(iLongestLength-len(lsLineElements))) + f.write(cDelimiter.join(lsLineElements)+ConstantsBreadCrumbs.c_strEndline) + + #For each data line in the table + for line in readData[iFirstDataIndex:]: + writeToFile = False + cleanLine = list() + #Break line into delimited elements + lineElements = line.split(cDelimiter) + + #Clean feature name + sCleanFeatureName = reSubPeriod.sub("-",lineElements[0]) + + #For each element but the first (taxa name) + #Element check to see if not == zero + #If so add to output + for element in lineElements[1:]: + if(element.strip() in string.whitespace): + cleanLine.append(ConstantsBreadCrumbs.c_strEmptyAbundanceData) + #Set abundance of 0 but do not indicate the line should be saved + elif(element == "0"): + cleanLine.append(element) + #If an abundance is found set the line to be saved. + else: + cleanLine.append(element) + writeToFile = True + + #Occurence filtering + #Removes features that do not have a given level iLowestAbundance in a given amount of samples iLowestSampleOccurence + if lOccurenceFilter: + iLowestAbundance, iLowestSampleOccurence = lOccurenceFilter + if iLowestSampleOccurence > sum([1 if float(sEntry) >= iLowestAbundance else 0 for sEntry in cleanLine]): + writeToFile = False + + #Write to file + if writeToFile: + f.write(sCleanFeatureName+cDelimiter+cDelimiter.join(cleanLine)+ConstantsBreadCrumbs.c_strEndline) + return outputFile + + def __repr__(self): + """ + Represent or print object. + """ + return "AbundanceTable" + + def __str__(self): + """ + Create a string representation of the Abundance Table. + """ + + return "".join(["Sample count:", str(len(self._npaFeatureAbundance.dtype.names[1:])), + os.linesep+"Feature count:", str(len(self._npaFeatureAbundance[self._npaFeatureAbundance.dtype.names[0]])), + os.linesep+"Id Metadata:", self._npaFeatureAbundance.dtype.names[0], + os.linesep+"Metadata ids:", str(self._dictTableMetadata.keys()), + os.linesep+"Metadata count:", str(len(self._dictTableMetadata.keys())), + os.linesep+"Originating source:",self._strOriginalName, + os.linesep+"Original feature count:", str(self._iOriginalFeatureCount), + os.linesep+"Original sample count:", str(self._iOriginalSampleCount), + os.linesep+"Is normalized:", str(self._fIsNormalized), + os.linesep+"Is summed:", str(self._fIsSummed), + os.linesep+"Current filtering state:", str(self._strCurrentFilterState), + os.linesep+"Feature delimiter:", self._cFeatureDelimiter, + os.linesep+"File delimiter:",self._cDelimiter]) + + def __eq__(self, objOther): + """ + Check if an object is equivalent in data to this object + Check to make sure that objOther is not None + Check to make sure objOther is the correct class type + Check to make sure self and other internal data are the same (exclusing file name) + Check data and make sure the npa arrays are the same + Check the metdata to make sure the dicts are the same + (will need to sort the keys of the dicts before comparing, they do not guarentee any order. + """ + # Check for none + if objOther is None: + return False + + #Check for object type + if isinstance(objOther,AbundanceTable) != True: + return False + + #Check feature delimiter + if self.funcGetFeatureDelimiter() != objOther.funcGetFeatureDelimiter(): + return False + + #Check file delimiter + if self.funcGetFileDelimiter() != objOther.funcGetFileDelimiter(): + return False + + + + + #************************************************** + #* Commented out * + #************************************************** + #Check name - Commented out by GW on 2013/09/14 because + #If we import pcl file into biom file and then export to pcl, the file names might be different but the tables might be the same + #Check name + #if self.funcGetName() != objOther.funcGetName(): + #return False + + + #Check sample metadata + #Go through the metadata + result1 = self.funcGetMetadataCopy() + result2 = objOther.funcGetMetadataCopy() + if sorted(result1.keys()) != sorted(result2.keys()): + return False + for strKey in result1.keys(): + if strKey not in result2: + return False + if result1[strKey] != result2[strKey]: + return False + + #TODO check the row (feature) metadata + + #TODO check the file metadata + #Check the ID + if self.funcGetFileDelimiter() != objOther.funcGetFileDelimiter(): + return False + + #Check the date + if self.dateCreationDate != objOther.dateCreationDate: + return False + + #Check the format + if self.strFileFormatType != objOther.strFileFormatType: + return False + + + + #************************************************** + #* Commented out * + #************************************************** + #Check source - Commented out by GW on 2013/09/14 because + #If we import pcl file into biom file and then export to pcl, the file names might be different but the tables might be the same + #Check the source + #if self.strFileGenerationSource != objOther.strFileGenerationSource: + #return False + + #Check the type + if self.strFileType != objOther.strFileType: + return False + + #Check the URL + if self.strFileURL != objOther.strFileURL: + return False + + #Check data + #TODO go through the data + #TODO also check the data type + result1 = self.funcGetAbundanceCopy() + result2 = objOther.funcGetAbundanceCopy() + if len(result1) != len(result2): + return False + + sorted_result1 = sorted(result1, key=lambda tup: tup[0]) + sorted_result2 = sorted(result2, key=lambda tup: tup[0]) + + if sorted_result1 != sorted_result2 : + return False + + + #************************************************** + #* Commented out * + #************************************************** + #Check AbundanceTable.__str__(self) - Commented out by GW on 2013/09/14 because + #If we import pcl file into biom file and then export to pcl, the file names might be different but the tables might be the same + + #Check string representation + #if AbundanceTable.__str__(self) != AbundanceTable.__str__(objOther): + #return False + + #Check if sample ids are the same and in the same order + if self.funcGetSampleNames() != objOther.funcGetSampleNames(): + return False + + return True + + + def __ne__(self, objOther): + return not self == objOther + + + #Testing Status: Light happy path testing + #TODO: Tim change static to class methods + @staticmethod + def _funcTextToStructuredArray(xInputFile = None, cDelimiter = ConstantsBreadCrumbs.c_cTab, sMetadataID = None, sLastMetadataRow = None, sLastMetadata = None, ostmOutputFile = None): + """ + Private method + Used to read in a file that is samples (column) and taxa (rows) into a structured array. + + :param xInputFile: File stream or path to input file. + :type: String File stream or string path. + :param cDelimiter: Delimiter for parsing the input file. + :type: Character Character. + :param sMetadataID: String ID that is a metadata row ID (found on the first column) and used as an ID for samples. + If not given it is assumed to be position 0 + :type: String String ID + :param sLastMetadataRow: String ID that is the last row metadat id (id of the most right column with row/feature metadata) + :type: String String ID + :param sLastMetadata: The ID of the metadata that is the last metadata before measurement or feature rows. + :type: String String ID + :param ostmOutputFile: Output File to write to if needed. None does not write the file. + :type: FileStream or String + :return [taxData,metadata,rowmetadata]: Numpy Structured Array of abundance data and dictionary of metadata. + Metadata is a dictionary as such {"ID", [value,value,values...]} + Values are in the order thety are read in (and the order of the sample names). + ID is the first column in each metadata row. +- rowmetadata is a optional Numpy strucured array (can be None if not made) ++ rowmetadata is a optional RowMetadata object (can be None if not made) + The rowmetadata and taxData row Ids should match +- [Numpy structured Array, Dictionary, Numpy structured array] ++ The last dict is a collection of BIOM fielparameters when converting from a BIOM file ++ [Numpy structured Array, Dictionary, Numpy structured array, dict] + """ + + # Open file from a stream or file path + istmInput = open( xInputFile, 'rU' ) if isinstance(xInputFile, str) else xInputFile + # Flag that when incremented will switch from metadata parsing to data parsing + iFirstDataRow = -1 + # Sample id row + namesRow = None + # Row metadata names + lsRowMetadataIDs = None + # Index of the last row metadata + iIndexLastMetadataRow = None + # Holds metadata {ID:[list of values]} + metadata = dict() + # Holds the data measurements [(tuple fo values)] + dataMatrix = [] + # Holds row metadata { sID : [ list of values ] } + dictRowMetadata = {} + # Positional index + iIndex = -1 + # File handle + csvw = None + + # Read in files + if ostmOutputFile: + csvw = csv.writer( open(ostmOutputFile,'w') if isinstance(ostmOutputFile, str) else ostmOutputFile, csv.excel_tab, delimiter = cDelimiter ) + # For each line in the file, and assume the tax id is the first element and the data follows + for lsLineElements in csv.reader( istmInput, dialect = csv.excel_tab, delimiter = cDelimiter ): + iIndex += 1 + taxId, sampleReads = lsLineElements[0], lsLineElements[1:] + + # Read through data measurements + # Process them as a list of tuples (needed for structured array) + if iFirstDataRow > 0: + try: + # Parse the sample reads, removing row metadata and storing row metadata if it exists + if lsRowMetadataIDs: + # Build expected dict for row metadata dictionary {string feature id: {'metadata': {metadatakey: [list of metadata values]}}} + dictFeature = dict([ [sID, [sKey]] for sID, sKey in zip( lsRowMetadataIDs, sampleReads[ 0 : iIndexLastMetadataRow ]) ]) + if len( dictFeature ): + dictRowMetadata[ taxId ] = { ConstantsBreadCrumbs.c_metadata_lowercase: dictFeature } + dataMatrix.append(tuple([taxId] + [( float(s) if s.strip( ) else 0 ) for s in sampleReads[ iIndexLastMetadataRow: ]])) + else: + dataMatrix.append(tuple([taxId] + [( float(s) if s.strip( ) else 0 ) for s in sampleReads])) + except ValueError: + sys.stderr.write( "AbundanceTable:textToStructuredArray::Error, non-numerical value on data row. File:" + str(xInputFile) + + " Row:" + str(lsLineElements) + "\n" ) + return False + # Go through study measurements + else: + # Read in metadata values, if the entry is blank then give it the default empty metadata value. + for i, s in enumerate( sampleReads ): + if not s.strip( ): + sampleReads[i] = ConstantsBreadCrumbs.c_strEmptyDataMetadata + + # If no id metadata (sample ids) is given then the first row is assumed to be the id row, otherwise look for the id for the metadata. + # Add the metadata to the containing dict + if ( ( not sMetadataID ) and ( iIndex == 0 ) ) or ( taxId == sMetadataID ): + namesRow = lsLineElements + # Remove the row metadata ids, these names are for the column ID and the samples ids + if sLastMetadataRow: + iIndexLastMetadataRow = lsLineElements.index(sLastMetadataRow) + lsRowMetadataIDs = namesRow[ 1 : iIndexLastMetadataRow + 1 ] + namesRow = [ namesRow[ 0 ] ] + namesRow[ iIndexLastMetadataRow + 1: ] + + # If the sample metadata dictionary already has entries then remove the row metadata info from it. + if len( metadata ) and len( lsRowMetadataIDs ): + for sKey, lsValues in metadata.items(): + metadata[ sKey ] = lsValues[ iIndexLastMetadataRow: ] + + # Set the metadata without row metadata entries + metadata[taxId] = sampleReads[ iIndexLastMetadataRow: ] if (lsRowMetadataIDs and len( lsRowMetadataIDs )) else sampleReads + + # If the last metadata was just processed switch to data processing + # If the last metadata name is not given it is assumed that there is only one metadata + if ( not sLastMetadata ) or ( taxId == sLastMetadata ): + iFirstDataRow = iIndex + 1 + + # If writing out the data write back out the line read in. + # This happens at the end so that the above cleaning is captured and written. + if csvw: + csvw.writerow( [taxId] + sampleReads ) + + if sLastMetadata and ( not dataMatrix ): + sys.stderr.write( "AbundanceTable:textToStructuredArray::Error, did not find the row for the last metadata ID. File:" + str(xInputFile) + + " Identifier:" + sLastMetadata + "\n" ) + return False + + # Make sure the names are found + if namesRow == None: + sys.stderr.write( "AbundanceTable:textToStructuredArray::Error, did not find the row for the unique sample/column. File:" + str(xInputFile) + + " Identifier:" + sMetadataID + "\n" ) + return False + + # Now we know the longest taxId we can define the first column holding the tax id + # Gross requirement of Numpy structured arrays, a = ASCII followed by max # of characters (as a string) + longestTaxId = max( len(a[0]) for a in dataMatrix ) + dataTypeVector = [(namesRow[0],'a' + str(longestTaxId*2))] + [(s, "f4") for s in namesRow[1:]] + # Create structured array + taxData = np.array(dataMatrix,dtype=np.dtype(dataTypeVector)) + + # Returns a none currently because the PCL file specification this originally worked on did not have feature metadata + # Can be updated in the future. + # [Data (structured array), column metadata (dict), row metadata (structured array), file metadata (dict)] + return [taxData, metadata, RowMetadata(dictRowMetadata = dictRowMetadata, lsRowMetadataIDs = lsRowMetadataIDs), { + ConstantsBreadCrumbs.c_strIDKey:ConstantsBreadCrumbs.c_strDefaultPCLID, + ConstantsBreadCrumbs.c_strDateKey:str(date.today()), + ConstantsBreadCrumbs.c_strFormatKey:ConstantsBreadCrumbs.c_strDefaultPCLFileFormateType, + ConstantsBreadCrumbs.c_strSourceKey:ConstantsBreadCrumbs.c_strDefaultPCLGenerationSource, + ConstantsBreadCrumbs.c_strTypekey:ConstantsBreadCrumbs.c_strDefaultPCLFileTpe, + ConstantsBreadCrumbs.c_strURLKey:ConstantsBreadCrumbs.c_strDefaultPCLURL, + ConstantsBreadCrumbs.c_strSparsityKey:ConstantsBreadCrumbs. c_fDefaultPCLSparsity}] + +# def funcAdd(self,abndTwo,strFileName=None): +# """ +# Allows one to add an abundance table to an abundance table. They both must be the same state of normalization or summation +# or they will be summed or normalized if one of the two are. +# +# :param abndTwo: AbundanceTable object 2 +# :type: AbundanceTable +# :return AbudanceTable: +# """ +# +# #Check summation and normalization +# if(self.funcIsSummed() or abndTwo.funcIsSummed()): +# self.funcSum() +# abndTwo.funcSum() +# if(self.funcIsNormalized() or abndTwo.funcIsNormalized()): +# self.funcNormalize() +# abndTwo.funcNormalize() +# +# #Normalize Feature names +# #Get if the abundance tables have clades +# fAbndInputHasClades = self.funcHasFeatureHierarchy() +# fAbndCompareHasClades = abndTwo.funcHasFeatureHierarchy() +# +# if(fAbndInputHasClades or fAbndCompareHasClades): +# #If feature delimiters do not match, switch +# if not self.funcGetFeatureDelimiter() == abndTwo.funcGetFeatureDelimiter(): +# abndTwo.funcSetFeatureDelimiter(self.funcGetFeatureDelimiter()) +# +# #Add prefixes if needed. +# self.funcAddCladePrefixToFeatures() +# abndTwo.funcAddCladePrefixToFeatures() +# +# #Get feature Names +# lsFeatures1 = self.funcGetFeatureNames() +# lsFeatures2 = abndTwo.funcGetFeatureNames() +# +# #Make one feature name list +# lsFeaturesCombined = list(set(lsFeatures1+lsFeature2)) +# +# #Add samples by features (Use 0.0 for empty data features, use NA for empty metadata features) +# +# +# #Combine metadata +# dictMetadata1 = self.funcGetMetadataCopy() +# dictMetadata2 = abndTwo.funcGetMetadataCopy() +# +# #Get first table metadata and add NA for metadata it is missing for the length of the current metadata +# lsMetadataOnlyInTwo = list(set(dictMetadata2.keys())-set(dictMetadata1.keys())) +# dictCombinedMetadata = dictMetadata1 +# lsEmptyMetadata = ["NA" for i in xrange(self.funcGetSampleCount())] +# for sKey in lsMetadataOnlyInTwo: +# dictCombinedMetadata[sKey]=lsEmptyMetadata +# #Add in the other metadata dictionary +# lsCombinedKeys = dictCombinedMetadata.keys() +# lsEmptyMetadata = ["NA" for i in xrange(abndTwo.funcGetSampleCount())] +# for sKey in lsCombinedKeys(): +# if sKey in dictMetadata2: +# dictCombinedMetadata[sKey] = dictCombinedMetadata[sKey]+dictMetadata2[sKey] +# else: +# dictCombinedMetadata[sKey] = dictCombinedMetadata[sKey]+lsEmptyMetadata +# +# #Make Abundance table +# return AbundanceTable(npaAbundance=npaAbundance, +# dictMetadata = dictCombinedMetadata, +# strName = strFileName if strFileName else os.path.splitext(self)[0]+"_combined_"+os.path.splitext(abndTwo)[0], +# strLastMetadata = self.funcGetLastMetadataName(), +# cFileDelimiter = self.funcGetFileDelimiter(), cFeatureNameDelimiter=self.funcGetFeatureDelimiter()) + + #TODO This does not adjust for sample ordering, needs to + def funcAddDataFeature(self, lsNames, npdData): + """ + Adds a data or group of data to the underlying table. + Names should be in the order of the data + Each row is considered a feature (not sample). + + :param lsNames: Names of the features being added to the data of the table + :type: List List of string names + :param npdData: Rows of features to add to the table + :type: Numpy array accessed by row. + """ + if ( self._npaFeatureAbundance == None ): + return False + + # Check number of input data rows + iDataRows = npdData.shape[0] + if (len(lsNames) != iDataRows): + print "Error:The names and the rows of data features to add must be of equal length" + + # Grow the array by the neccessary amount and add the new rows + iTableRowCount = self.funcGetFeatureCount() + iRowElementCount = self.funcGetSampleCount() + self._npaFeatureAbundance.resize(iTableRowCount+iDataRows) + for iIndexData in xrange(iDataRows): + self._npaFeatureAbundance[iTableRowCount+iIndexData] = tuple([lsNames[iIndexData]]+list(npdData[iIndexData])) + + return True + + #TODO This does not adjust for sample ordering, needs to + def funcAddMetadataFeature(self,lsNames,llsMetadata): + """ + Adds metadata feature to the underlying table. + Names should be in the order of the lists of metadata + Each internal list is considered a metadata and paired to a name + """ + if ( self._dictTableMetadata == None ): + return False + + # Check number of input data rows + iMetadataCount = len(llsMetadata) + if (len(lsNames) != iMetadataCount): + print "Error:The names and the rows of metadata features to add must be of equal length" + + # Add the metadata + for tpleMetadata in zip(lsNames,llsMetadata): + self._dictTableMetadata[tpleMetadata[0]]=tpleMetadata[1] + return True + + #2 test Cases + def funcSetFeatureDelimiter(self, cDelimiter): + """ + Changes the feature delimiter to the one provided. + Updates the feature names. + + :param cDelimiter: Character feature delimiter + :type: Character + :return Boolean: Indicator of success or not (false) + """ + if ( self._npaFeatureAbundance == None ): + return False + cDelimiterCurrent = self.funcGetFeatureDelimiter() + if ( not cDelimiter or not cDelimiterCurrent): + return False + + #Make new feature names + lsNewFeatureNames = [sFeatureName.replace(cDelimiterCurrent,cDelimiter) for sFeatureName in self.funcGetFeatureNames()] + + #Update new feature names to abundance table + if (not self.funcGetIDMetadataName() == None): + self._npaFeatureAbundance[self.funcGetIDMetadataName()] = np.array(lsNewFeatureNames) + + #Update delimiter + self._cFeatureDelimiter = cDelimiter + return True + + #Happy path tested + def funcGetSampleNames(self): + """ + Returns the sample names (IDs) contained in the abundance table. + + :return Sample Name: A List of sample names indicated by the metadata associated with the sMetadataId given in table creation. + A list of string names or empty list on error as well as no underlying table. + """ + + return self._npaFeatureAbundance.dtype.names[1:] if ( self._npaFeatureAbundance != None ) else [] + + #Happy Path Tested + def funcGetIDMetadataName(self): + """ + Returns the metadata id. + + :return ID: The metadata id (the sample Id). + Returns none on error. + """ + + return self._npaFeatureAbundance.dtype.names[0] if ( self._npaFeatureAbundance != None ) else None + + #Happy path tested + def funcGetAbundanceCopy(self): + """ + Returns a deep copy of the abundance table. + + :return Numpy Structured Array: The measurement data in the Abundance table. Can use sample names to access each column of measurements. + Returns none on error. + """ + + return self._npaFeatureAbundance.copy() if ( self._npaFeatureAbundance != None ) else None + + #Happy path tested + def funcGetAverageAbundancePerSample(self, lsTargetedFeatures): + """ + Averages feature abundance within a sample. + + :param lsTargetedFeatures: String names of features to average + :type: List of string names of features which are measured + :return List: List of lists or boolean (False on error). One internal list per sample indicating the sample and the feature's average abudance + [[sample,average abundance of selected taxa]] or False on error + """ + + #Sample rank averages [[sample,average abundance of selected taxa]] + sampleAbundanceAverages = [] + + sampleNames = self.funcGetSampleNames() + allTaxaNames = self.funcGetFeatureNames() + #Get an abundance table compressed to features of interest + abndReducedTable = self.funcGetFeatureAbundanceTable(lsTargetedFeatures) + if abndReducedTable == None: + return False + + #If the taxa to be selected are not in the list, Return nothing and log + lsMissing = [] + for sFeature in lsTargetedFeatures: + if not sFeature in allTaxaNames: + lsMissing.append(sFeature) + else: + #Check to make sure the taxa of interest is not average abundance of 0 + if not abndReducedTable.funcGetFeatureSumAcrossSamples(sFeature): + lsMissing.append(sFeature) + if len(lsMissing) > 0: + sys.stderr.write( "Could not find features for averaging: " + str(lsMissing) ) + return False + + #For each sample name get average abundance + for sName in sampleNames: + npaFeaturesSample = abndReducedTable.funcGetSample(sName) + sampleAbundanceAverages.append([sName,sum(npaFeaturesSample)/float(len(npaFeaturesSample))]) + + #Sort based on average + return sorted(sampleAbundanceAverages, key = lambda sampleData: sampleData[1], reverse = True) + + #Happy path tested 1 + def funcGetAverageSample(self): + """ + Returns the average sample of the abundance table. + This average sample is made of the average of each feature. + :return list: A list of averages in the order of the feature names. + """ + + ldAverageSample = [] + #If there are no samples then return empty list. + if len(self.funcGetSampleNames()) < 1: + return ldAverageSample + + #If there are samples return the average of each feature in the order of the feature names. + for sFeature in self._npaFeatureAbundance: + npFeaturesAbundance = list(sFeature)[1:] + ldAverageSample.append(sum(npFeaturesAbundance)/float(len(npFeaturesAbundance))) + + return ldAverageSample + + #Tested 2 cases + def funcHasFeatureHierarchy(self): + """ + Returns an indicator of having a hierarchy in the features indicated by the existance of the + feature delimiter. + + :return Boolean: True (Has a hierarchy) or False (Does not have a hierarchy) + """ + + if ( self._npaFeatureAbundance == None ): + return None + cDelimiter = self.funcGetFeatureDelimiter() + if ( not cDelimiter ): + return False + + #For each feature name, check to see if the delimiter is in the name + for sFeature in self.funcGetFeatureNames(): + if cDelimiter in sFeature: + return True + return False + + def funcGetCladePrefixes(self): + """ + Returns the list of prefixes to use on biological sample hierarchy + + :return List: List of strings + """ + return self._lsCladePrefixes + + #3 test cases + def funcAddCladePrefixToFeatures(self): + """ + As a standardized clade prefix to indicate biological clade given hierarchy. + Will not add a prefix to already prefixes feature names. + Will add prefix to feature names that do not have them or clades in a feature name that + do not have them while leaving ones that do as is. + + :return Boolean: True (Has a hierarchy) or False (Does not have a hierarchy) + """ + + if ( self._npaFeatureAbundance == None ): + return None + cDelimiter = self.funcGetFeatureDelimiter() + lsPrefixes = self.funcGetCladePrefixes() + iPrefixLength = len(lsPrefixes) + if ( not cDelimiter ): + return False + + #Append prefixes to feature names + lsUpdatedFeatureNames = [] + lsFeatureNames = self.funcGetFeatureNames() + + for sFeatureName in lsFeatureNames: + lsClades = sFeatureName.split(cDelimiter) + #If there are not enough then error + if(len(lsClades) > iPrefixLength): + print "Error:: Too many clades given to be biologically meaningful" + return False + lsUpdatedFeatureNames.append(cDelimiter.join([lsPrefixes[iClade]+lsClades[iClade] if not(lsClades[iClade][0:len(lsPrefixes[iClade])]==lsPrefixes[iClade]) else lsClades[iClade] for iClade in xrange(len(lsClades))])) + + #Update new feature names to abundance table + if not self.funcGetIDMetadataName() == None: + self._npaFeatureAbundance[self.funcGetIDMetadataName()] = np.array(lsUpdatedFeatureNames) + + return True + + #Happy Path Tested + def funcGetFeatureAbundanceTable(self, lsFeatures): + """ + Returns a copy of the current abundance table with the abundance of just the given features. + + :param lsFeatures: String Feature IDs that are kept in the compressed abundance table. + :type: List of strings Feature IDs (found as the first entry of a filter in the input file. + :return AbundanceTable: A compressed version of the abundance table. + On an error None is returned. + """ + + if ( self._npaFeatureAbundance == None ) or ( lsFeatures == None ): + return None + + #Get a list of boolean indicators that the row is from the features list + lfFeatureData = [sRowID in lsFeatures for sRowID in self.funcGetFeatureNames()] + #compressed version as an Abundance table + lsNamePieces = os.path.splitext(self._strOriginalName) + abndFeature = AbundanceTable(npaAbundance=np.compress(lfFeatureData, self._npaFeatureAbundance, axis = 0), + dictMetadata = self.funcGetMetadataCopy(), + strName = lsNamePieces[0] + "-" + str(len(lsFeatures)) +"-Features"+lsNamePieces[1], + strLastMetadata=self.funcGetLastMetadataName(), + cFileDelimiter = self.funcGetFileDelimiter(), cFeatureNameDelimiter= self.funcGetFeatureDelimiter()) + #Table is no longer normalized + abndFeature._fIsNormalized = False + return abndFeature + + #Happy path tested + def funcGetFeatureDelimiter(self): + """ + The delimiter of the feature names (For example to use on concensus lineages). + + :return Character: Delimiter for the feature name pieces if it is complex. + """ + + return self._cFeatureDelimiter + + #Happy path tested + def funcGetFeatureCount(self): + """ + Returns the current feature count. + + :return Count: Returns the int count of features in the abundance table. + Returns None on error. + """ + + return self._npaFeatureAbundance.shape[0] if not self._npaFeatureAbundance is None else 0 + + #Happy path tested + def funcGetFeatureSumAcrossSamples(self,sFeatureName): + """ + Returns float sum of feature values across the samples. + + :param sFeatureName: The feature ID to get the sum from. + :type: String. + :return Double: Sum of one feature across samples. + """ + return sum(self.funcGetFeature(sFeatureName)) + + def funcGetFeature(self,sFeatureName): + """ + Returns feature values across the samples. + + :param sFeatureName: The feature ID to get the sum from. + :type: String. + :return Double: Feature across samples. + """ + + for sFeature in self._npaFeatureAbundance: + if sFeature[0] == sFeatureName: + return list(sFeature)[1:] + return None + + #Happy path tested + def funcGetFeatureNames(self): + """ + Return the feature names as a list. + + :return Feature Names: List of feature names (or IDs) as strings. + As an error returns empty list. + """ + + if (not self._npaFeatureAbundance == None): + return self._npaFeatureAbundance[self.funcGetIDMetadataName()] + return [] + + #Happy path tested + def funcGetFileDelimiter(self): + """ + The delimiter of the file the data was read from and which is also the delimiter which would be used to write the data to a file. + + :return Character: Delimiter for the parsing and writing the file. + """ + + return self._cDelimiter + + def funcGetLastMetadataName(self): + """ + Get the last metadata name that seperates abundance and metadata measurements. + + :return string: Metadata name + """ + return self._strLastMetadataName + + #Happy path tested + def funcGetSample(self,sSampleName): + """ + Return a copy of the feature measurements of a sample. + + :param sSampleName: Name of sample to return. + :type: String + :return Sample: Measurements Feature measurements of a sample. + Empty numpy array returned on error. + """ + + if (not self._npaFeatureAbundance == None): + return self._npaFeatureAbundance[sSampleName].copy() + return np.array([]) + + #Happy path tested + def funcGetMetadata(self, strMetadataName): + """ + Returns a list of metadata that is associated with the given metadata name (id). + + :param strMetadataName: String metadata ID to be returned + :type: String ID + :return Metadata: List of metadata + """ + + return copy.deepcopy( self._dictTableMetadata.get(strMetadataName) ) \ + if self._dictTableMetadata else None + + #Happy path tested + def funcGetMetadataCopy(self): + """ + Returns a deep copy of the metadata. + + :return Metadata copy: {"ID":[value,value...]} + """ + + return copy.deepcopy(self._dictTableMetadata) + + #Happy path tested + def funcGetName(self): + """ + Returns the name of the object which is the file name that generated it. + If the object was generated from an Abundance Table (for instance through stratification) + the name is still in the form of a file that could be written to which is informative + of the changes that have occurred on the data set. + :return string: Name + """ + return self._strOriginalName + + #Happy path tested. could do more + def funcGetTerminalNodes(self): + """ + Returns the terminal nodes given the current feature names in the abundance table. The + features must contain a consensus lineage or all will be returned. + :return List: List of strings of the terminal nodes given the abundance table. + """ + return AbundanceTable.funcGetTerminalNodesFromList(lsNames=self.funcGetFeatureNames(),cNameDelimiter=self.funcGetFeatureDelimiter()) + + #Tested 2 test cases + @staticmethod + def funcGetTerminalNodesFromList(lsNames,cNameDelimiter): + """ + Returns the terminal nodes given the current feature names in the abundance table. The + features must contain a consensus lineage or all will be returned. + + :param lsNames: The list of string names to parse and filter. + :type: List of strings + :param cNameDelimiter: The delimiter for the name of the features. + :type: Character Delimiter + :return list: A list of terminal elements in the list (given only the list). + """ + + #Build hash + dictCounts = dict() + for strTaxaName in lsNames: + #Split into the elements of the clades + lsClades = filter(None,strTaxaName.split(cNameDelimiter)) + #Count clade levels + iCladeLength = len(lsClades) + + #Evaluate first element + sClade = lsClades[0] + dictCounts[sClade] = sClade not in dictCounts + + #Evaluate the rest of the elements + if iCladeLength < 2: + continue + for iIndex in xrange(1,iCladeLength): + prevClade = sClade + sClade = cNameDelimiter.join([sClade,lsClades[iIndex]]) + if sClade in dictCounts: + dictCounts[sClade] = dictCounts[prevClade] = False + else: + dictCounts[sClade] = True + dictCounts[prevClade] = False + + #Return only the elements that were of count 1 + return filter( lambda s: dictCounts[s] == True, dictCounts ) + + #Happy path tested + def funcIsNormalized(self): + """ + Returns if the data has been normalized. + + :return Boolean: Indicates if the data is normalized. + True indicates it the data is normalized. + """ + + return self._fIsNormalized + + #Happy path tested + def funcIsPrimaryIdMetadata(self,sMetadataName): + """ + Checks the metadata data associated with the sMetadatName and returns if the metadata is unique. + This is important to some of the functions in the Abundance Table specifically when translating from one metadata to another. + + :param sMetadataName: ID of metadata to check for uniqueness. + :type: String Metadata ID. + :return Boolean: Returns indicator of uniqueness. + True indicates unique. + """ + + lMetadata = self.funcGetMetadata(sMetadataName) + if not lMetadata: + return False + return (len(lMetadata) == len(set(lMetadata))) + + #Happy path tested + def funcIsSummed(self): + """ + Return is the data is summed. + + :return Boolean: Indicator of being summed. True indicates summed. + """ + + return self._fIsSummed + + #Happy path tested + def funcFilterAbundanceByPercentile(self, dPercentileCutOff = 95.0, dPercentageAbovePercentile=1.0): + """ + Filter on features. + A feature is removed if it's abundance is not found in the top X percentile a certain percentage of the samples. + + :param dPercentileCutOff: The percentile used for filtering. + :type: double A double between 0.0 and 100.0 + :param dPercentageAbovePercentile: The percentage above the given percentile (dPercentileCutOff) that must exist to keep the feature. + :type: double Between 0.0 and 100.0 + :return Boolean: Indicator of filtering occuring without error. True indicates filtering occuring. + """ + + #No need to do anything + if(dPercentileCutOff==0.0) or (dPercentageAbovePercentile==0.0): + return True + + #Sample names + lsSampleNames = self.funcGetSampleNames() + + #Scale percentage out of 100 + dPercentageAbovePercentile = dPercentageAbovePercentile/100.0 + + #Sample count + iSampleCount = len(lsSampleNames) + + #Get a threshold score of the value at the specified percentile for each sample + #In the order of the sample names + ldScoreAtPercentile = [scipy.stats.scoreatpercentile(self._npaFeatureAbundance[lsSampleNames[iIndex]],dPercentileCutOff) for iIndex in xrange(iSampleCount)] + + #Record how many entries for each feature have a value equal to or greater than the dPercentileCutOff + #If the percentile of entries passing the criteria are above the dPercentageAbovePercentile put index in list to keep + liKeepIndices = [] + iSampleCount = float(iSampleCount) + for iRowIndex, npaRow in enumerate(self._npaFeatureAbundance): + iCountPass = sum([1 if dValue >= ldScoreAtPercentile[iValueIndex] else 0 for iValueIndex, dValue in enumerate(list(npaRow)[1:])]) + if (iCountPass / iSampleCount) >= dPercentageAbovePercentile: + liKeepIndices.append(iRowIndex) + + #Compress array + self._npaFeatureAbundance = self._npaFeatureAbundance[liKeepIndices,:] + + #Update filter state + self._strCurrentFilterState += ":dPercentileCutOff=" + str(dPercentileCutOff) + ",dPercentageAbovePercentile=" + str(dPercentageAbovePercentile) + + #Table is no longer normalized + self._fIsNormalized = False + + return True + + def funcFilterAbundanceByMinValue(self, dMinAbundance = 0.0001, iMinSamples = 3): + """ + Filter abundance by requiring features to have a minimum relative abundance in a minimum number of samples. + Will evaluate greater than or equal to the dMinAbundance and iMinSamples. + + :param dMinAbundance: Minimum relative abundance. + :type: Real Number Less than 1. + :param iMinSamples: Minimum samples to have the relative abundnace or greater in. + :type: Integer Number greater than 1. + :return Boolean: Indicator of the filter running without error. False indicates error. + """ + + #No need to do anything + if(dMinAbundance==0) or (iMinSamples==0): + return True + + #This normalization requires the data to be relative abundance + if not self._fIsNormalized: + #sys.stderr.write( "Could not filter by sequence occurence because the data is already normalized.\n" ) + return False + + #Holds which indexes are kept + liKeepFeatures = [] + for iRowIndex, dataRow in enumerate( self._npaFeatureAbundance ): + #See which rows meet the criteria and keep the index if needed. + if len( filter( lambda d: d >= dMinAbundance, list(dataRow)[1:] ) ) >= iMinSamples: + liKeepFeatures.append(iRowIndex) + + #Compress array + self._npaFeatureAbundance = self._npaFeatureAbundance[liKeepFeatures,:] + #Update filter state + self._strCurrentFilterState += ":dMinAbundance=" + str(dMinAbundance) + ",iMinSamples=" + str(iMinSamples) + + return True + + #Happy path tested + def funcFilterAbundanceBySequenceOccurence(self, iMinSequence = 2, iMinSamples = 2): + """ + Filter occurence by requiring features to have a minimum sequence occurence in a minimum number of samples. + Will evaluate greater than or equal to the iMinSequence and iMinSamples. + + :param iMinSequence: Minimum sequence to occur. + :type: Integer Number Greater than 1. + :param iMinSamples: Minimum samples to occur in. + :type: Integer Number greater than 1. + :return Boolean: Indicator of the filter running without error. False indicates error. + """ + + #No need to do anything + if(iMinSequence==0) or (iMinSamples==0): + return True + + #This normalization requires the data to be reads + if self._fIsNormalized: + #sys.stderr.write( "Could not filter by sequence occurence because the data is already normalized.\n" ) + return False + + #Holds which indexes are kept + liKeepFeatures = [] + for iRowIndex, dataRow in enumerate( self._npaFeatureAbundance ): + #See which rows meet the criteria and keep the index if needed. + if len( filter( lambda d: d >= iMinSequence, list(dataRow)[1:] ) ) >= iMinSamples: + liKeepFeatures.append(iRowIndex) + + #Compress array + self._npaFeatureAbundance = self._npaFeatureAbundance[liKeepFeatures,:] + #Update filter state + self._strCurrentFilterState += ":iMinSequence=" + str(iMinSequence) + ",iMinSamples=" + str(iMinSamples) + + return True + + #1 Happy path test + def funcFilterFeatureBySD(self, dMinSDCuttOff = 0.0): + """ + A feature is removed if it's abundance is not found to have standard deviation more than the given dMinSDCutoff. + + :param dMinSDCuttOff: Standard deviation threshold. + :type: Double A double greater than 0.0. + :return Boolean: Indicator of success. False indicates error. + """ + + #No need to do anything + if(dMinSDCuttOff==0.0): + return True + + #Holds which indexes are kept + liKeepFeatures = [] + + #Evaluate each sample + for iRowIndex, dataRow in enumerate(self._npaFeatureAbundance): + if(np.std(list(dataRow)[1:])>=dMinSDCuttOff): + liKeepFeatures.append(iRowIndex) + + #Compress array + self._npaFeatureAbundance = self._npaFeatureAbundance[liKeepFeatures,:] + + #Update filter state + self._strCurrentFilterState += ":dMinSDCuttOff=" + str(dMinSDCuttOff) + + #Table is no longer normalized + self._fIsNormalized = False + + return True + + #Happy path tested 2 tests + def funcGetWithoutOTUs(self): + """ + Remove features that are terminal otus. Terminal otus are identified as being an integer. + """ + + #Get the feature names + lsFeatures = self.funcGetFeatureNames() + + #Reduce, filter the feature names + lsFeatures = [sFeature for sFeature in lsFeatures if not (ValidateData.funcIsValidStringInt(sFeature.split(self.funcGetFeatureDelimiter())[-1]))] + + return self.funcGetFeatureAbundanceTable(lsFeatures) + + #Happy path tested + def funcNormalize(self): + """ + Convenience method which will call which ever normalization is approriate on the data. + :return Boolean: Indicator of success (true). + """ + + if self._fIsSummed: + return self.funcNormalizeColumnsWithSummedClades() + else: + return self.funcNormalizeColumnsBySum() + + #Testing Status: Light happy path testing + def funcNormalizeColumnsBySum(self): + """ + Normalize the data in a manner that is approrpiate for NOT summed data. + Normalize the columns (samples) of the abundance table. + Normalizes as a fraction of the total (number/(sum of all numbers in the column)). + Will not act on summed tables. + + :return Boolean: Indicator of success. False indicates error. + """ + + if self._fIsNormalized: +# sys.stderr.write( "This table is already normalized, did not perform new normalization request.\n" ) + return False + + if self._fIsSummed: + sys.stderr.write( "This table has clades summed, this normalization is not appropriate. Did not perform.\n" ) + return False + + #Normalize + for columnName in self.funcGetSampleNames(): + column = self._npaFeatureAbundance[columnName] + columnTotal = sum(column) + if(columnTotal > 0.0): + column = column/columnTotal + self._npaFeatureAbundance[columnName] = column + + #Indicate normalization has occured + self._fIsNormalized = True + + return True + + #Happy path tested + def funcNormalizeColumnsWithSummedClades(self): + """ + Normalizes a summed Abundance Table. + If this is called on a dataset which is not summed and not normalized. + The data will be summed first and then normalized. + If already normalized, the current normalization is kept. + + :return Boolean: Indicator of success. False indicates error. + """ + + if self._fIsNormalized: +# sys.stderr.write( "This table is already normalized, did not perform new summed normalization request.\n" ) + return False + + if not self._fIsSummed: + sys.stderr.write( "This table does not have clades summed, this normalization is not appropriate until the clades are summed. The clades are being summed now before normalization.\n" ) + self.funcSumClades() + + #Load a hash table with root data {sKey: npaAbundances} + hashRoots = {} + for npaRow in self._npaFeatureAbundance: + + curldAbundance = np.array(list(npaRow)[1:]) + curFeatureNameLength = len(npaRow[0].split(self._cFeatureDelimiter)) + curlRootData = hashRoots.get(npaRow[0].split(self._cFeatureDelimiter)[0]) + + if not curlRootData: + hashRoots[npaRow[0].split(self._cFeatureDelimiter)[0]] = [curFeatureNameLength, curldAbundance] + elif curlRootData[0] > curFeatureNameLength: + hashRoots[npaRow[0].split(self._cFeatureDelimiter)[0]] = [curFeatureNameLength, curldAbundance] + + #Normalize each feature by thier root feature + dataMatrix = list() + for npaRow in self._npaFeatureAbundance: + + curHashRoot = list(hashRoots[npaRow[0].split(self._cFeatureDelimiter)[0]][1]) + dataMatrix.append(tuple([npaRow[0]]+[npaRow[i+1]/curHashRoot[i] if curHashRoot[i] > 0 else 0 for i in xrange(len(curHashRoot))])) + + self._npaFeatureAbundance = np.array(dataMatrix,self._npaFeatureAbundance.dtype) + + #Indicate normalization has occured + self._fIsNormalized = True + + return True + + def _funcRankAbundanceHelper( self, aaTodo, iRank, lRankAbundance ): + """ + Helper method for ranking abudance which are tied. + + :params aaTodo: List of tied ranks to change to a rank. + :type: List of Enumerates of samples. + :params iRank: Current Rank + :type: Integer + :params lRankAbundance: Sample of abundance + :type: List of integers + """ + + # Subtract one from iRank (each time) to account for next loop iteration + # Then average it with itself minus (the length of aaTodo + 1) + dRank = ( iRank + iRank - len( aaTodo ) - 1 ) / 2.0 + for a in aaTodo: + lRankAbundance[a[0]] = dRank + + #1 Happy path test + def funcRankAbundance(self): + """ + Rank abundances of features with in a sample. + + :return AbundanceTable: Abundance table data ranked (Features with in samples). + None is returned on error. + """ + + if self._npaFeatureAbundance == None: + return None + + lsSampleNames = self.funcGetSampleNames() + npRankAbundance = self.funcGetAbundanceCopy() + liRanks = [] + #For each sample name get the ranks + for sName in lsSampleNames: + #Enumerate for order and sort abundances + lfSample = list(enumerate(npRankAbundance[sName])) + lfSample = sorted(lfSample, key = lambda a: a[1], reverse = True) + + # Accumulate indices until a new value is encountered to detect + handle ties + aaTodo = [] + for i, a in enumerate( lfSample ): + if ( not aaTodo ) or ( a[1] == aaTodo[-1][1] ): + aaTodo.append( a ) + else: + # Make multiple tied ranks = average of first and last + self._funcRankAbundanceHelper( aaTodo, i, npRankAbundance[sName] ) + aaTodo = [a] + self._funcRankAbundanceHelper( aaTodo, i + 1, npRankAbundance[sName] ) + + abndRanked = AbundanceTable(npaAbundance=npRankAbundance, dictMetadata=self.funcGetMetadataCopy(), + strName= self.funcGetName() + "-Ranked", + strLastMetadata=self.funcGetLastMetadataName(), + cFileDelimiter=self.funcGetFileDelimiter(), + cFeatureNameDelimiter=self.funcGetFeatureDelimiter()) + + #Table is no longer normalized + abndRanked._fIsNormalized = False + return abndRanked + + def funcGetSampleCount(self): + """ + Returns the sample count of the abundance table. + """ + return len(self.funcGetSampleNames()) + + #Happy Path Tested + def funcReduceFeaturesToCladeLevel(self, iCladeLevel): + """ + Reduce the current table to a certain clade level. + + :param iCladeLevel: The level of the clade to trim the features to. + :type: Integer The higher the number the more clades are presevered in the consensus lineage contained in the feature name. + :return Boolean: Indicator of success. False indicates error. + """ + + if iCladeLevel < 1: return False + if not self._npaFeatureAbundance == None: + liFeatureKeep = [] + [liFeatureKeep.append(tplFeature[0]) if (len(tplFeature[1][0].split(self.funcGetFeatureDelimiter())) <= iCladeLevel) else 0 + for tplFeature in enumerate(self._npaFeatureAbundance)] + #Compress array + self._npaFeatureAbundance = self._npaFeatureAbundance[liFeatureKeep,:] + + #Update filter state + self._strCurrentFilterState += ":iCladeLevel=" + str(iCladeLevel) + return True + else: + return False + + #Happy path tested + def funcRemoveSamples(self,lsSampleNames): + """ + Removes the samples given in the list. + + :param lsSampleNames: A list of string names of samples to remove. + :type: List of strings Unique values + :return Boolean: Indicator of success (True = success, no error) + """ + + #Samples to remove + setSamples = set(lsSampleNames) + + #Get orignal sample count + iOriginalCount = self._iOriginalSampleCount + + #The samples to keep + lsKeepSamples = [sSample for sSample in self.funcGetSampleNames() if not sSample in setSamples] + #The sample to keep as boolean flags for compressing the metadata + lfKeepSamples = [not sSample in setSamples for sSample in self.funcGetSampleNames()] + + #Reduce the abundance data and update + self._npaFeatureAbundance = self._npaFeatureAbundance[[self.funcGetIDMetadataName()]+lsKeepSamples] + + #Reduce the metadata and update + for sKey in self._dictTableMetadata: + self._dictTableMetadata[sKey] = [value for iindex, value in enumerate(self._dictTableMetadata[sKey]) if lfKeepSamples[iindex]] + + #Update sample number count + self._iOriginalSampleCount = len(self.funcGetSampleNames()) + + return self._iOriginalSampleCount == (iOriginalCount-len(setSamples)) + + #Happy path tested + def funcRemoveSamplesByMetadata(self, sMetadata, lValuesToRemove): + """ + Removes samples from the abundance table based on values of a metadata. + If a metadata has any value given the associated sample is removed. + + :param sMetadata: ID of the metdata to check the given values. + :type: String Metadata ID + :param lValuesToRemove: A list of values which if equal to a metadata entry indicate to remove the associated sample. + :type: List of values: List + :return Boolean: Indicator of success (True = success, no error) + """ + + lsSampleNames = self.funcGetSampleNames() + return self.funcRemoveSamples([lsSampleNames[iindex] for iindex, sValue in enumerate(self.funcGetMetadata(sMetadata)) if sValue in lValuesToRemove]) + + #Happy path testing + def funcSumClades(self): + """ + Sums abundance data by clades indicated in the feature name (as consensus lineages). + + :return Boolean: Indicator of success. + False indicates an error. + """ + + if not self.funcIsSummed(): + + #Read in the data + #Find the header column (iCol) assumed to be 1 or 2 depending on the location of "NAME" + #Create a list (adSeq) that will eventually hold the sum of the columns of data + astrHeaders = iCol = None + adSeqs = np.array([0] * len(self.funcGetSampleNames())) + pTree = CClade( ) + aastrRaw = [] + + #For each row in the npaAbundance + #Get the feature name, feature abundances, and sum up the abudance columns + #Keep the sum for later normalization + #Give a tree the feature name and abundance + for dataRow in self._npaFeatureAbundance: + + sFeatureName = dataRow[0] + ldAbundances = list(dataRow)[1:] + + #Add to the sum of the columns (samples) + adSeqs = adSeqs + np.array(list(dataRow)[1:]) + + #Build tree + pTree.get( sFeatureName.split(self._cFeatureDelimiter) ).set( ldAbundances ) + + #Create tree of data + #Input missing data + #Fill hashFeatures with the clade name (key) and a blist of values (value) of the specified level interested. + pTree.impute( ) + hashFeatures = {} + pTree.freeze( hashFeatures, c_iSumAllCladeLevels, c_fOutputLeavesOnly ) + setstrFeatures = hashFeatures.keys( ) + + #Remove parent clades that are identical to child clades + for strFeature, adCounts in hashFeatures.items( ): + astrFeature = strFeature.strip( ).split( "|" ) + while len( astrFeature ) > 1: + astrFeature = astrFeature[:-1] + strParent = "|".join( astrFeature ) + adParent = hashFeatures.get( strParent ) + if adParent == adCounts: + del hashFeatures[strParent] + setstrFeatures.remove( strParent ) + + #Sort features to be nice + astrFeatures = sorted( setstrFeatures ) + + #Change the hash table to an array + dataMatrix = list() + for sFeature in astrFeatures: + dataMatrix.append(tuple([sFeature]+list(hashFeatures[sFeature]))) + self._npaFeatureAbundance=np.array(dataMatrix,self._npaFeatureAbundance.dtype) + + #Indicate summation has occured + self._fIsSummed = True + + return True + + #Happy path tested + def funcStratifyByMetadata(self, strMetadata, fWriteToFile=False): + """ + Stratifies the AbundanceTable by the given metadata. + Will write each stratified abundance table to file + if fWriteToFile is True the object will used it's internally stored name as a file to write to + if fWriteToFile is a string then it should be a directory and end with "." This will rebase the file + and store it in a different directory but with an otherwise unchanged name. + Note: If the metadata used for stratification has NAs, they will be segregated to thier own table and returned. + + :param strMetadata: Metadata ID to stratify data with. + :type: String ID for a metadata. + :param fWriteToFile: Indicator to write to file. + :type: Boolean True indicates to write to file. + :return List: List of AbundanceTables which are deep copies of the original. + Empty list on error. + """ + + if self._npaFeatureAbundance is None or self._dictTableMetadata is None: + return [] + + #Get unique metadata values to stratify by + lsMetadata = self._dictTableMetadata.get(strMetadata,[]) + setValues = set(lsMetadata) + #If there is only one metadata value then no need to stratify so return the original in the list (and write if needed) + if len(setValues) == 0: + return [] + + retlAbundanceTables = [] + dictAbundanceBlocks = dict() + #Given here there are multiple metadata values, continue to stratify + lsNames = self.funcGetSampleNames() + #Get index of values to break up + for value in setValues: + lfDataIndex = [sData==value for sData in lsMetadata] + #Get abundance data for the metadata value + #The true is added to keep the first column which should be the feature id + npaStratfiedAbundance = self._npaFeatureAbundance[[self.funcGetIDMetadataName()]+list(np.compress(lfDataIndex,lsNames))] + + #Get metadata for the metadata value + dictStratifiedMetadata = dict() + for metadataType in self._dictTableMetadata: + dictValues = self.funcGetMetadata(metadataType) + dictStratifiedMetadata[metadataType] = np.compress(lfDataIndex,dictValues).tolist() + + #Make abundance table + #Add abundance table to the list + lsNamePieces = os.path.splitext(self._strOriginalName) + objStratifiedAbundanceTable = AbundanceTable(npaAbundance=npaStratfiedAbundance, dictMetadata=dictStratifiedMetadata, + strName=lsNamePieces[0] + "-StratBy-" + value+lsNamePieces[1], + strLastMetadata=self.funcGetLastMetadataName(), + cFeatureNameDelimiter=self._cFeatureDelimiter, cFileDelimiter = self._cDelimiter) + if fWriteToFile: + objStratifiedAbundanceTable.funcWriteToFile(lsNamePieces[0] + "-StratBy-" + value+lsNamePieces[1]) + #Append abundance table to returning list + retlAbundanceTables.append(objStratifiedAbundanceTable) + + return retlAbundanceTables + + #Happy Path Tested + def funcTranslateIntoMetadata(self, lsValues, sMetadataFrom, sMetadataTo, fFromPrimaryIds=True): + """ + Takes the given data values in one metadata and translates it to values in another + metadata of the sample samples holding the values of the first metadata + FPrimaryIds, if true the sMetadataFrom are checked for unique values, + If FPrimaryIds is not true, duplicate values can stop the preservation of order + Or may cause duplication in the "to" group. This is not advised. + if the sMetadataFrom has any duplicates the function fails and return false. + + :param lsValues: Values to translate. + :type: List List of values. + :param sMetadataFrom: The metadata the lsValues come from. + :type: String ID for the metadata. + :param sMetadataTo: The metadata the lsValues will be translated into keeping the samples the same. + :type: String ID for the metadata. + :param fFromPrimaryIds: The metadata that are in the from metadata list must be unique in each sample. + :type: Boolean True indicates the metadata list should be unique in each sample. Otherwise a false will return. + :return List: List of new values or False on error. + """ + + #Get metadata + lFromMetadata = self.funcGetMetadata(sMetadataFrom) + if not lFromMetadata: + sys.stderr.write( "Abundancetable::funcTranlateIntoMetadata. Did not receive lFromMetadata.\n" ) + return False + + lToMetadata = self.funcGetMetadata(sMetadataTo) + if not lToMetadata: + sys.stderr.write( "Abundancetable::funcTranlateIntoMetadata. Did not receive lToMetadata.\n" ) + return False + + #Check to see if the values are unique if indicated to do so + if fFromPrimaryIds: + if not len(lFromMetadata) == len(set(lFromMetadata)): + sys.stderr.write( "Abundancetable::funcTranlateIntoMetadata. sMetadataFrom did not have unique values.\n" ) + return False + + #Translate over + if lFromMetadata and lToMetadata: + return [lToMetadata[iIndex] for iIndex in [lFromMetadata.index(value) for value in lsValues]] + + return False + + #Happy path tested + def funcToArray(self): + """ + Returns a numpy array of the current Abundance Table. + Removes the first ID head column and the numpy array is + Made of lists, not tuples. + + :return Numpy Array: np.array([[float,float,...],[float,float,...],[float,float,...]]) + None is returned on error. + """ + + if not self._npaFeatureAbundance == None: + return np.array([list(tplRow)[1:] for tplRow in self._npaFeatureAbundance],'float') + return None + + #Happy Path tested + def funcWriteToFile(self, xOutputFile, cDelimiter=None, cFileType=ConstantsBreadCrumbs.c_strPCLFile): + """ + Writes the AbundanceTable to a file strOutputFile. + Will rewrite over a file as needed. + Will use the cDelimiter to delimit columns if provided. + + :param xOutputFile: File stream or File path to write the file to. + :type: String File Path + :param cDelimiter: Delimiter for the output file. + :type: Character If cDlimiter is not specified, the internally stored file delimiter is used. + """ + + if not xOutputFile: + return + # Check delimiter argument + if not cDelimiter: + cDelimiter = self._cDelimiter + + # Check file type: If pcl: Write pcl file; If biom: write biom file; If None - write pcl file + if(cFileType == None): + cFileType == ConstantsBreadCrumbs.c_strPCLFile + + if(cFileType == ConstantsBreadCrumbs.c_strPCLFile): + # Write as a pcl file + self._funcWritePCLFile(xOutputFile, cDelimiter=cDelimiter) + elif(cFileType == ConstantsBreadCrumbs.c_strBiomFile): + #Write as a biom file + self._funcWriteBiomFile(xOutputFile) + return + + def _funcWritePCLFile(self, xOutputFile, cDelimiter=None): + """ + Write an abundance table object as a PCL file. + + :param xOutputFile: File stream or File path to write the file to. + :type: String File Path + :param cDelimiter: Delimiter for the output file. + :type: Character If cDlimiter is not specified, the internally stored file delimiter is used. + """ + + f = csv.writer(open( xOutputFile, "w" ) if isinstance(xOutputFile, str) else xOutputFile, csv.excel_tab, delimiter=cDelimiter) + + # Get Row metadata id info (IDs for column header, keys that line up with the ids) + lsRowMetadataIDs, lsRowMetadataIDKeys = self.rwmtRowMetadata.funcMakeIDs() if self.rwmtRowMetadata else [[],[]] + + #Write Ids + f.writerows([[self.funcGetIDMetadataName()]+lsRowMetadataIDs+list(self.funcGetSampleNames())]) + + #Write column metadata + lsKeys = list(set(self._dictTableMetadata.keys())-set([self.funcGetIDMetadataName(),self.funcGetLastMetadataName()])) + lMetadataIterations = list(set(lsKeys+[self.funcGetLastMetadataName()] )) + + f.writerows([[sMetaKey]+([ConstantsBreadCrumbs.c_strEmptyDataMetadata]*len(lsRowMetadataIDs))+self.funcGetMetadata(sMetaKey) for sMetaKey in lMetadataIterations if sMetaKey != self.funcGetIDMetadataName() and not sMetaKey is None]) + + #Write abundance + lsOutput = list() + curAbundance = self._npaFeatureAbundance.tolist() + + for curAbundanceRow in curAbundance: + # Make feature metadata, padding with NA as needed + lsMetadata = [] + for sMetadataId in lsRowMetadataIDKeys: + lsMetadata = lsMetadata + self.rwmtRowMetadata.funGetFeatureMetadata( curAbundanceRow[0], sMetadataId ) + lsMetadata = lsMetadata + ( [ ConstantsBreadCrumbs.c_strEmptyDataMetadata ] * + ( self.rwmtRowMetadata.dictMetadataIDs.get( sMetadataId, 0 ) - len( lsMetadata ) ) ) + f.writerows([[curAbundanceRow[0]]+lsMetadata+[str(curAbundanceElement) for curAbundanceElement in curAbundanceRow[1:]]]) + return + + def _funcWriteBiomFile(self, xOutputFile): + """ + Write an abundance table object as a Biom file. + :param xOutputFile: File stream or File path to write the file to. + :type: String File Path + """ + + #************************** + # Get Sample Names * + #************************** + lSampNames = list(self.funcGetSampleNames()) + + #************************** + # Metadata Names * + #************************** + + dictMetadataCopy = self.funcGetMetadataCopy() + lMetaData = list() + iKeysCounter = 0 + for lMetadataCopyEntry in dictMetadataCopy.iteritems(): + iKeysCounter +=1 + sMetadataName = lMetadataCopyEntry[0] + lMetadataEntries = lMetadataCopyEntry[1] + iMetadataEntryCounter = -1 + for sMetadataEntry in lMetadataEntries: + iMetadataEntryCounter+=1 + dictMetadataNames = dict() + dictMetadataNames[sMetadataName ] = sMetadataEntry + if iKeysCounter == 1: + lMetaData.append(dictMetadataNames) + else: + lMetaData[iMetadataEntryCounter][sMetadataName ] = sMetadataEntry + + + #************************** + # Observation Ids * + # and row metadata * + #************************** + bTaxonomyInRowsFlag = False + if self.rwmtRowMetadata.dictRowMetadata is not None: + bTaxonomyInRowsFlag = True + + lObservationMetadataTable = list() + + lObservationIds = list() + lFeatureNamesResultArray = self.funcGetFeatureNames() + for sFeatureName in lFeatureNamesResultArray: + lObservationIds.append(sFeatureName) + + if self.rwmtRowMetadata and self.rwmtRowMetadata.dictRowMetadata: + RowMetadataEntry = self.rwmtRowMetadata.dictRowMetadata[sFeatureName][ConstantsBreadCrumbs.c_metadata_lowercase] + lObservationMetadataTable.append( RowMetadataEntry ) + + #************************** + # Data * + #************************** + + lData = list() + lAbundanceCopyResultArray = self.funcGetAbundanceCopy() + + for r in lAbundanceCopyResultArray: + lr = list(r) + lr.pop(0) #Remove metadata + lAbundanceValues = list() + for AbundanceEntry in lr: + flAbundanceEntry = float(AbundanceEntry) + lAbundanceValues.append(flAbundanceEntry) + lData.append(lAbundanceValues) + arrData = array(lData) #Convert list to array + + + + #************************** + # Invoke the * + # biom table factory * + #************************** + if bTaxonomyInRowsFlag == False: + BiomTable = table_factory(arrData, + lSampNames, + lObservationIds, + lMetaData, + constructor=SparseOTUTable) + else: #There was metadata in the rows + BiomTable = table_factory(arrData, + lSampNames, + lObservationIds, + lMetaData, + lObservationMetadataTable if len(lObservationMetadataTable) > 0 else None, + constructor=SparseOTUTable) + + #************************** + # Generate biom Output * + #************************** + f = open( xOutputFile, "w" ) if isinstance(xOutputFile, str) else xOutputFile + f.write(BiomTable.getBiomFormatJsonString(ConstantsBreadCrumbs.c_biom_file_generated_by)) + f.close() + return + + #Testing Status: 1 Happy path test + @staticmethod + def funcPairTables(strFileOne, strFileTwo, strIdentifier, cDelimiter, strOutFileOne, strOutFileTwo, lsIgnoreValues=None): + """ + This method will read in two files and abridge both files (saved as new files) + to just the samples in common between the two files given a common identifier. + ***If the identifier is not unique in each data set, the first sample with the pairing id is taken so make sure the ID is unique. + Expects the files to have the sample delimiters. + + :param strFileOne: Path to file one to be paired. + :type: String File path. + :param strFileTwo: Path to file two to be paired. + :type: String File path. + :param strIdentifier: Metadata ID that is used for pairing. + :type: String Metadata ID. + :param cDelimiter: Character delimiter to read the files. + :type: Character Delimiter. + :param strOutFileOne: The output file for the paired version of the first file. + :type: String File path. + :param strOutFileTwo: The output file for the paired version of the second file. + :type: String File path. + :param lsIgnoreValues: These values are ignored even if common IDs between the two files. + :type: List List of strings. + :return Boolean: Indicator of no errors. + False indicates errors. + """ + + #Validate parameters + if(not ValidateData.funcIsValidFileName(strFileOne)): + sys.stderr.write( "AbundanceTable:checkRawDataFile::Error, file not valid. File:" + strFileOne + "\n" ) + return False + #Validate parameters + if(not ValidateData.funcIsValidFileName(strFileTwo)): + sys.stderr.write( "AbundanceTable:checkRawDataFile::Error, file not valid. File:"+ strFileTwo + "\n" ) + return False + + #Make file one + #Read in file + istm = csv.reader(open(strFileOne,'rU'), csv.excel_tab, delimiter=cDelimiter) + lsContentsOne = [lsRow for lsRow in istm] + + #Get the file identifier for file one + fileOneIdentifier = None + for sLine in lsContentsOne: + if sLine[0] == strIdentifier: + fileOneIdentifier = sLine + break + + #Make file two + #Read in file + istm = csv.reader(open(strFileTwo,'rU'), csv.excel_tab, delimiter=cDelimiter) + lsContentsTwo = [lsRow for lsRow in istm] + + #Get the file identifier for file two + fileTwoIdentifier = None + for sLine in lsContentsTwo: + if sLine[0] == strIdentifier: + fileTwoIdentifier = sLine + break + + #Get what is in common between the identifiers + #And find which columns to keep in the tables based on the common elements + setsCommonIdentifiers = set(fileOneIdentifier) & set(fileTwoIdentifier) + if lsIgnoreValues: + setsCommonIdentifiers = setsCommonIdentifiers - set(lsIgnoreValues) + + #Get positions of common identifiers in each data set, if the identifier is not unique in a date set just take the first index + lfFileOneIDIndexes = [fileOneIdentifier.index(sCommonID) for sCommonID in setsCommonIdentifiers] + lfFileTwoIDIndexes = [fileTwoIdentifier.index(sCommonID) for sCommonID in setsCommonIdentifiers] + + #Convert index list to list of boolean + lfFileOneElements = [iIndex in lfFileOneIDIndexes for iIndex, sIdentifier in enumerate(fileOneIdentifier)] + lfFileTwoElements = [iIndex in lfFileTwoIDIndexes for iIndex, sIdentifier in enumerate(fileTwoIdentifier)] + + #Write out file one + ostm = csv.writer(open(strOutFileOne,'w'), csv.excel_tab, delimiter=cDelimiter) + (ostm.writerows([np.compress(lfFileOneElements,sLine) for sLine in lsContentsOne])) + + #Write out file two + ostm = csv.writer(open(strOutFileTwo,'w'), csv.excel_tab, delimiter=cDelimiter) + (ostm.writerows([np.compress(lfFileTwoElements,sLine) for sLine in lsContentsTwo])) + + return True + + #Testing Status: Light happy path testing + @staticmethod + def funcStratifyAbundanceTableByMetadata(strInputFile = None, strDirectory = "", cDelimiter = ConstantsBreadCrumbs.c_cTab, iStratifyByRow = 1, llsGroupings = []): + """ + Splits an abundance table into multiple abundance tables stratified by the metadata + + :param strInputFile: String file path to read in and stratify. + :type: String File path. + :param strDirectory: Output directory to write stratified files. + :type: String Output directory path. + :param cDelimiter: The delimiter used in the adundance file. + :type: Character Delimiter. + :param iStratifyByRow: The row which contains the metadata to use in stratification. + :type: Integer Positive integer index. + :param llsGroupings: A list of string lists where each string list holds values that are equal and should be grouped together. + So for example, if you wanted to group metadata "1", "2", and "3" seperately but "4" and "5" together you would + Give the following [["4","5"]]. + If you know what "1" and "3" also together you would give [["1","3"],["4","5"]] + :type List List of list of strings + :return Boolean: Indicator of NO error. + False indicates an error. + """ + + #Validate parameters + if(not ValidateData.funcIsValidFileName(strInputFile)): + sys.stderr.write( "AbundanceTable:stratifyAbundanceTableByMetadata::Error, file not valid. File:" + strInputFile + "\n" ) + return False + if(not ValidateData.funcIsValidStringType(cDelimiter)): + sys.stderr.write( "AbundanceTable:stratifyAbundanceTableByMetadata::Error, Delimiter is not a valid string/char type. Delimiter =" + cDelimiter + "\n" ) + return False + if(not ValidateData.funcIsValidPositiveInteger(iStratifyByRow, tempZero = True) and (not ValidateData.funcIsValidString(iStratifyByRow))): + sys.stderr.write( "AbundanceTable:stratifyAbundanceTableByMetadata::Error, Stratify by row is not a positive integer or string keyword. Row =" + + str(iStratifyByRow) + ".\n" ) + return False + + #Get the base of the file path + #This is dependent on the given output directory and the prefix of the file name of the input file + #If no output file is given then the input file directory is used. + baseFilePath = strDirectory + lsFilePiecesExt = os.path.splitext(strInputFile) + if baseFilePath: + baseFilePath = baseFilePath + os.path.splitext(os.path.split(strInputFile)[1])[0] + else: + baseFilePath = lsFilePiecesExt[0] + + #Read in file + istm = csv.reader(open(strInputFile,'rU'), csv.excel_tab, delimiter=cDelimiter) + sFileContents = [lsRow for lsRow in istm] + + #Collect metadata + metadataInformation = dict() + + #If the tempStratifyRow is by key word than find the index + if ValidateData.funcIsValidString(iStratifyByRow): + for iLineIndex, strLine in enumerate(sFileContents): + if strLine[0].strip("\"") == iStratifyByRow: + iStratifyByRow = iLineIndex + break + + #Stratify by metadata row + #Split metadata row into metadata entries + #And put in a dictionary containing {"variable":[1,2,3,4 column index]} + stratifyByRow = sFileContents[iStratifyByRow] + for metaDataIndex in xrange(1,len(stratifyByRow)): + metadata = stratifyByRow[metaDataIndex] + #Put all wierd categories, none, whitespace, blank space metadata cases into one bin + if not metadata or metadata in string.whitespace: + metadata = "Blank" + #Remove any extraneous formatting + metadata = metadata.strip(string.whitespace) + #Store processed metadata with column occurence in dictionary + if(not metadata in metadataInformation): + metadataInformation[metadata] = [] + metadataInformation[metadata].append(metaDataIndex) + + #For each of the groupings + #Use the first value as the primary value which the rest of the values in the list are placed into + #Go through the dict holding the indices and extend the list for the primary value with the secondary values + #Then set the secondary value list to empty so that it will be ignored. + if llsGroupings: + for lSKeyGroups in llsGroupings: + if len(lSKeyGroups) > 1: + for sGroup in lSKeyGroups[1:]: + if sGroup in metadataInformation: + metadataInformation[lSKeyGroups[0]].extend(metadataInformation[sGroup]) + metadataInformation[sGroup] = [] + + #Stratify data + stratifiedAbundanceTables = dict() + for tableRow in sFileContents: + if(len(tableRow)> 1): + for metadata in metadataInformation: + #[0] includes the taxa line + columns = metadataInformation[metadata] + if columns: + columns = [0] + columns + lineList = list() + for column in columns: + lineList.append(tableRow[column]) + stratifiedAbundanceTables.setdefault(metadata,[]).append(lineList) + + #Write to file + lsFilesWritten = [] + for metadata in stratifiedAbundanceTables: + sOutputFile = baseFilePath+"-by-"+metadata.strip("\"")+lsFilePiecesExt[1] + f = csv.writer(open(sOutputFile,'w'), csv.excel_tab, delimiter = cDelimiter ) + f.writerows(stratifiedAbundanceTables[metadata]) + lsFilesWritten.append(sOutputFile) + + return lsFilesWritten + + + + #******************************************* + #* biom interface functions: * + #* 1. _funcBiomToStructuredArray * + #* 2. _funcDecodeBiomMetadata * + #******************************************* + @staticmethod + def _funcBiomToStructuredArray(xInputFile = None): + """ + Reads the biom input file and builds a "BiomCommonArea" that contains: + 1.BiomCommonArea['sLastMetadata'] - This is the name of the last Metadata (String) + 2.BiomCommonArea['BiomTaxData']- dict() - going to be used as lcontents[0]==TaxData + 3.BiomCommonArea['Metadata'] - dict() - going to be used as lcontents[1]==MetaData + 4.BiomCommonArea['BiomFileInfo'] - dict() - going to be used as lcontents[2]==FileInfo (id, format:eg. Biological Observation Matrix 0.9.1) etc. + 5.BiomCommonArea['column_metadata_id'] - This is a string which is the name of the column id + :param xInputFile: File path of biom file to read. + :type: String File path. + :return: BiomCommonArea (See description above) + :type: dict() + """ + + #******************************************* + #* Build the metadata * + #******************************************* + try: + BiomTable = parse_biom_table(open(xInputFile,'U') if isinstance(xInputFile, str) else xInputFile) #Import the biom file + except: + print("Failure decoding biom file - please check your input biom file and rerun") + BiomCommonArea = None + return BiomCommonArea + + BiomCommonArea = dict() + dBugNames = list() #Bug Names Table + dRowsMetadata = None #Initialize the np.array of the Rows metadata + BiomElements = BiomTable.getBiomFormatObject('') + for BiomKey, BiomValue in BiomElements.iteritems(): + #**************************************************** + #* Checking the different keys: format, * + #* rows, columns, date, generated_by * + #**************************************************** + if (BiomKey == ConstantsBreadCrumbs.c_strFormatKey + or BiomKey == ConstantsBreadCrumbs.c_strFormatUrl + or BiomKey == ConstantsBreadCrumbs.c_MatrixTtype + or BiomKey == ConstantsBreadCrumbs.c_strTypekey + or BiomKey == ConstantsBreadCrumbs.c_strIDKey #Same as below + or BiomKey == ConstantsBreadCrumbs.c_GeneratedBy #<---Need to follow up with Biom as always BiomValue = "" even though in the file has a value + or BiomKey == ConstantsBreadCrumbs.c_strDateKey): #Same as above + BiomCommonArea = AbundanceTable._funcInsertKeyToCommonArea(BiomCommonArea, BiomKey, BiomValue) + + + if BiomKey == ConstantsBreadCrumbs.c_rows: + iMaxIdLen = 0 + for iIndexRowMetaData in range(0, len(BiomValue)): + if ConstantsBreadCrumbs.c_id_lowercase in BiomValue[iIndexRowMetaData]: + sBugName = BiomValue[iIndexRowMetaData][ConstantsBreadCrumbs.c_id_lowercase] + dBugNames.append(sBugName) #Post to the bug table + if len(sBugName) > iMaxIdLen: #We are calculating dynamically the length of the ID + iMaxIdLen = len(sBugName) + + if ConstantsBreadCrumbs.c_metadata_lowercase in BiomValue[0] and BiomValue[0][ConstantsBreadCrumbs.c_metadata_lowercase] != None : + dRowsMetadata = AbundanceTable._funcBiomBuildRowMetadata(BiomValue, iMaxIdLen ) + + + if BiomKey == ConstantsBreadCrumbs.c_columns: + BiomCommonArea = AbundanceTable._funcDecodeBiomMetadata(BiomCommonArea, BiomValue, iMaxIdLen) #Call the subroutine to Build the metadata + + + #******************************************* + #* Build the TaxData * + #******************************************* + + BiomTaxDataWork = list() #Initlialize TaxData + BiomObservations = BiomTable.iterObservations(conv_to_np=True) #Invoke biom method to fetch data from the biom file + for BiomObservationData in BiomObservations: + sBugName = str( BiomObservationData[1]) + BiomTaxDataEntry = list() + BiomTaxDataEntry.append(sBugName) + BiomObservationsValues = BiomObservationData[0] + for BiomDataValue in BiomObservationsValues: + BiomTaxDataEntry.append(BiomDataValue) + BiomTaxDataWork.append(tuple(BiomTaxDataEntry)) + + BiomCommonArea[ConstantsBreadCrumbs.c_BiomTaxData] = np.array(BiomTaxDataWork,dtype=np.dtype(BiomCommonArea[ConstantsBreadCrumbs.c_Dtype])) + BiomCommonArea[ConstantsBreadCrumbs.c_dRowsMetadata] = RowMetadata(dRowsMetadata) + del(BiomCommonArea[ConstantsBreadCrumbs.c_Dtype]) #Not needed anymore + + return BiomCommonArea + + + @staticmethod + def _funcDecodeBiomMetadata(BiomCommonArea, BiomValue = None, iMaxIdLen=0 ): + """ + Decode the Biom Metadata and build: + 1. BiomCommonArea['Metadata'] + 2. BiomCommonArea['Dtype'] + 3. BiomCommonArea['sLastMetadata'] + 4. BiomCommonArea['column_metadata_id'] - This is a string which is the name of the column id + These elements will be formatted and passed down the line to build the AbundanceTable + :param BiomValue: The "columns" Metadata from the biom file (Contains the Metadata information) + :type: dict() + :param iMaxIdLen: The maximum length of a row ID + :type: Integer + :return: BiomCommonArea + :type: dict() + """ + + BiomCommonArea[ConstantsBreadCrumbs.c_sLastMetadata] = None #Initialize the LastMetadata element + BiomCommonArea['dRowsMetadata'] = None #Initialize for cases that there is no metadata in the rows + + strLastMetadata = None + strIDMetadata = None + + lenBiomValue = len(BiomValue) + BiomMetadata = dict() + for cntMetadata in range(0, lenBiomValue): + BiomMetadataEntry = BiomValue[cntMetadata] + + for key, value in BiomMetadataEntry.iteritems(): #Loop on the entries + if key == ConstantsBreadCrumbs.c_id_lowercase: #If id - process it + strIDMetadata = ConstantsBreadCrumbs.c_ID + if ConstantsBreadCrumbs.c_ID not in BiomMetadata: #If ID not in the common area - initalize it + BiomMetadata[ConstantsBreadCrumbs.c_ID] = list() #Initialize a list + for indx in range(0, lenBiomValue): #And post the values + BiomMetadata[ConstantsBreadCrumbs.c_ID].append(None) + BiomMetadata[ConstantsBreadCrumbs.c_ID][cntMetadata] = value.encode(ConstantsBreadCrumbs.c_ascii,ConstantsBreadCrumbs.c_ignore) + + if key == ConstantsBreadCrumbs.c_metadata_lowercase: #If key = metadata + if not value is None: #And value is not empty + MetadataDict = value #Initialize a dictionary and post the values + for MDkey, MDvalue in MetadataDict.iteritems(): + if type(MDkey) == unicode : + MDkeyAscii = MDkey.encode(ConstantsBreadCrumbs.c_ascii,ConstantsBreadCrumbs.c_ignore) + else: + MDkeyAscii = MDkey + if type(MDvalue) == unicode: + MDvalueAscii = MDvalue.encode(ConstantsBreadCrumbs.c_ascii,ConstantsBreadCrumbs.c_ignore) + else: + MDvalueAscii = MDvalue + + if len(MDkeyAscii) > 0: #Search for the last metadata + if not strIDMetadata: + strIDMetadata = MDkeyAscii + BiomCommonArea[ConstantsBreadCrumbs.c_sLastMetadata] = MDkeyAscii #Set the last Metadata + if MDkeyAscii not in BiomMetadata: + BiomMetadata[MDkeyAscii] = list() + for indx in range(0, lenBiomValue): + BiomMetadata[MDkeyAscii].append(None) + BiomMetadata[MDkeyAscii][cntMetadata] = MDvalueAscii + + + BiomCommonArea[ConstantsBreadCrumbs.c_Metadata] = BiomMetadata + BiomCommonArea[ConstantsBreadCrumbs.c_MetadataID] = strIDMetadata + + #********************************************** + #* Build dtype * + #********************************************** + + BiomDtype = list() + iMaxIdLen+=10 #Increase it by 10 + BiomDtypeEntry = list() + FirstValue = ConstantsBreadCrumbs.c_ID + SecondValue = "a" + str(iMaxIdLen) + BiomDtypeEntry.append(FirstValue) + BiomDtypeEntry.append(SecondValue) + BiomDtype.append(tuple(BiomDtypeEntry)) + + for a in BiomMetadata[ConstantsBreadCrumbs.c_ID]: + BiomDtypeEntry = list() + FirstValue = a.encode(ConstantsBreadCrumbs.c_ascii,ConstantsBreadCrumbs.c_ignore) + SecondValue = ConstantsBreadCrumbs.c_f4 + BiomDtypeEntry.append(FirstValue) + BiomDtypeEntry.append(SecondValue) + BiomDtype.append(tuple(BiomDtypeEntry)) + + BiomCommonArea[ConstantsBreadCrumbs.c_Dtype] = BiomDtype + return BiomCommonArea + + @staticmethod + def _funcBiomBuildRowMetadata( BiomValue, iMaxIdLen ): + """ + Builds the row metadata from a BIOM value + + :param BiomValue: BIOM Value from the BIOM JSON parsing + :type: Complex dict of string pairs and dicts + :param iMaxIdLen: Maximum length of all the IDs + :type: int + :return: dictRowsMetadata - np Array containing the rows metadata + :type: {string feature id: {'metadata': {'taxonomy': [list of metadata values]}}} + """ + # Build the input dict for RowMetadata from a dict of dicts from a BIOM file + dictRowsMetadata = dict() + for iIndexRowMetaData in range(0, len(BiomValue)): + dictRowsMetadata[str(BiomValue[iIndexRowMetaData][ConstantsBreadCrumbs.c_id_lowercase])] = dict() + RowMetadataEntryFromTable = BiomValue[iIndexRowMetaData][ConstantsBreadCrumbs.c_metadata_lowercase] + dMetadataTempDict = dict() + for key, value in RowMetadataEntryFromTable.iteritems(): + dMetadataTempDict[key] = value + dictRowsMetadata[str(BiomValue[iIndexRowMetaData][ConstantsBreadCrumbs.c_id_lowercase])][ConstantsBreadCrumbs.c_metadata_lowercase] = dMetadataTempDict + return dictRowsMetadata + + @staticmethod + def _funcInsertKeyToCommonArea(BiomCommonArea, BiomKey, BiomValue): + """ + Inserts the keys into the BiomCommonArea["BiomFileInfo"] + :param BiomCommonArea - The common area that has been built before + :type: dict() + :param BiomKey - The current key (eg. format, date, generated by) + :type: str + :param BiomValue - The current value of the key (eg. for format: "Biological Observation Matrix 0.9.1") + :type: str + :return: BiomCommonArea - The updated common area + :type: dict() + """ + + if ConstantsBreadCrumbs.c_BiomFileInfo not in BiomCommonArea: + BiomCommonArea[ConstantsBreadCrumbs.c_BiomFileInfo] = dict() + + strInsertKey = BiomKey #Set Default - But it is now always the same... (eg. URL is not: format_url -->url and others) + PostBiomValue = BiomValue #The default value to be posted + if BiomKey == ConstantsBreadCrumbs.c_strFormatUrl: + strInsertKey = ConstantsBreadCrumbs.c_strURLKey + + if BiomKey == ConstantsBreadCrumbs.c_MatrixTtype: + strInsertKey = ConstantsBreadCrumbs.c_strSparsityKey + + if BiomKey == ConstantsBreadCrumbs.c_GeneratedBy: + PostBiomValue = None + + if BiomKey == ConstantsBreadCrumbs.c_strDateKey: + PostBiomValue = None + + BiomCommonArea[ConstantsBreadCrumbs.c_BiomFileInfo][strInsertKey] = PostBiomValue + return BiomCommonArea + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/AbundanceTable.pyc Binary file src/breadcrumbs/src/AbundanceTable.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/BoxPlot.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/BoxPlot.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,130 @@ +""" +Author: Timothy Tickle +Description: Class to create box plots. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +import matplotlib.pyplot as plt +from pylab import * + +#Plots a matrix +class BoxPlot: + + @staticmethod + def funcPlot(ly, lsLabels, strOutputFigurePath, strTitle = "Title", strXTitle="X Axis", strYTitle="Y Axis", strColor = "#83C8F9", fJitter=False, fInvert=False, fInvertY=False): + """ + Plot a box plot with optional jittering. + + :params ly: List of y values + :type: List of doubles + :params lsLabels: List of labels (x tick lables) + :type: List string + :params strOutputFigurePath: File path to make figure + :type: String file path + :params strTitle: Title of figure + :type: String + :params strXTitle: Label of x axis + :type: String + :params strYTitle: Label of y axis + :type: String + :params strColor: Hex color for the face of the boxplots + :type: String + :params fJitter: Indicator of jittering (true) or not (false) + :type: Boolean + :params fInvert: Invert colors (true) + :type: Boolean + :params fInvertY: Invert y axis + :type: Boolean + """ + + #Start plot + #Get plot object + imgFigure = plt.figure() + + #Get plot colorsstrOutFigure + objFigureControl = ConstantsFiguresBreadCrumbs() + #Boxplots have to be plotted over the scatter so the alpha can not go to 1.0 + #In this case capturing the alpha before inversion + #Inversion automoatically sets it to 1. + dAlpha=objFigureControl.c_dAlpha + objFigureControl.invertColors(fInvert=fInvert) + + #Color/Invert figure + imgFigure.set_facecolor(objFigureControl.c_strBackgroundColorWord) + imgSubplot = imgFigure.add_subplot(111,axisbg=objFigureControl.c_strBackgroundColorLetter) + imgSubplot.set_xlabel(strXTitle) + imgSubplot.set_ylabel(strYTitle) + imgSubplot.spines['top'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['bottom'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['left'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['right'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.xaxis.label.set_color(objFigureControl.c_strDetailsColorLetter) + + #Adds light grid for numbers and puts them in the background + imgSubplot.yaxis.grid(True, linestyle='-', which='major', color=objFigureControl.c_strGridLineColor, alpha=objFigureControl.c_dAlpha) + imgSubplot.set_axisbelow(True) + imgSubplot.yaxis.label.set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='x', colors=objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='y', colors=objFigureControl.c_strDetailsColorLetter) + charMarkerEdgeColor = objFigureControl.c_strDetailsColorLetter + + #Make box plot + bp = plt.boxplot(x=ly, notch=1, patch_artist=True) + for iindex, ldData in enumerate(ly): + ldX = None + if fJitter: + ldX = [float(iindex+1)+ uniform(-.05,.05) for x in xrange(len(ldData))] + else: + ldX = [float(iindex+1) for x in xrange(len(ldData))] + plt.scatter(x=ldX,y=ly[iindex],c=strColor,marker="o",alpha=objFigureControl.c_dAlpha) + + #Color boxes + plt.setp(bp['boxes'], color=objFigureControl.c_strDetailsColorLetter, facecolor=strColor, alpha=dAlpha) + plt.setp(bp['whiskers'], color=objFigureControl.c_strDetailsColorLetter) + + #Set ticks and title + lsLabelsWithCounts = [] + for iindex,sCurLabel in enumerate(lsLabels): + lsLabelsWithCounts.append(sCurLabel+" ( "+str(len(ly[iindex]))+" )") + xtickNames = plt.setp(imgSubplot, xticklabels=lsLabelsWithCounts) + imgSubplot.set_title(strTitle) + imgSubplot.title.set_color(objFigureControl.c_strDetailsColorLetter) + + #Invert Y axis + if fInvertY: + ax = plt.gca() + ax.set_ylim(ax.get_ylim()[::-1]) + + #End plot + #Save to a file + imgFigure.savefig(strOutputFigurePath, facecolor=imgFigure.get_facecolor()) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/CClade.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/CClade.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,181 @@ +""" +Author: Curtis Huttenhower +Description: Used to create tree structures to hierarchically normalize abundance tables. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Curtis Huttenhower" +__copyright__ = "Copyright 2012" +__credits__ = ["Curtis Huttenhower"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +import blist +import sys + +class CClade: + + def __init__( self ): + """ + Initialize CClade + Dictionary to hold the children nodes from feature consensus lineages. + adValues is a list of the abundance value. + """ + + self.m_hashChildren = {} + self.m_adValues = None + + def get( self, astrClade ): + """ + Recursively travel the length of a tree until you find the terminal node + (where astrClade == Falseor actually []) + or a dict key that matches the clade call. + If at any time a clade is given that is not currently know, return a new clade + which is set to the current Clade as a child. + """ + + return self.m_hashChildren.setdefault( + astrClade[0], CClade( ) ).get( astrClade[1:] ) if astrClade else self + + def set( self, adValues ): + """ + Set all the values given as a list in the same order given. + """ + + self.m_adValues = blist.blist( [0] ) * len( adValues ) + for i, d in enumerate( adValues ): + if d: + self.m_adValues[i] = d + + def impute( self ): + """ + This allows you to recursively impute values for clades without values given their children counts. + Assumably this should be called only once and after all clade abundances have been added. + If the m_adValues already exist return the stored m_adValues. (No imputation needed). + Otherwise call impute for all children and take the sum of the values from all the children by column + Not a sum of a list but summing a list with lists by element. + """ + + #If values do not exist + if not self.m_adValues: + #Call impute on all children + #If the parent clade has no abundance values + #Then take a copy of the child's + #If they now have a copy of a child's but have other children + #Sum their children with thier current values + for pChild in self.m_hashChildren.values( ): + adChild = pChild.impute( ) + if self.m_adValues: + for i in range( len( adChild or [] ) ): + if adChild[i]: + self.m_adValues[i] += adChild[i] + elif adChild: + self.m_adValues = adChild[:] + #If values exist return + return self.m_adValues + + def _freeze( self, hashValues, iTarget, astrClade, iDepth, fLeaves ): + """ + Update the given hashValues dict with clade abundances given depth specifications + Return a set of integers returning an indicator of the structure of the tree preserved in the dict/hash + When the appropriate level of the tree is reached + Hashvalue is updated with the clade (including lineage) and the abundance. looks like {"clade":blist(["0.0","0.1"...])} + """ + + #fHit is true on atleast one of the following conditions: + #iTarget is not 0 indicating no changes + #Leaves are indicated to be only given and the target depth for the leaves is reached. + #The required depth is reached. + fHit = ( not iTarget ) or ( ( fLeaves and ( iDepth == iTarget ) ) or ( ( not fLeaves ) and ( iDepth <= iTarget ) ) ) + #Increment depth + iDepth += 1 + #Returns a set + setiRet = set() + #If there are children build integer set indicating structure of the tree preserved in the dict + if self.m_hashChildren: + #Union all the results from freeze of all children + #Call freeze but append the child clade to the clade in the call. + #And give an incremented depth + for strChild, pChild in self.m_hashChildren.items( ): + setiRet |= pChild._freeze( hashValues, iTarget, astrClade + [strChild], iDepth, fLeaves ) + setiRet = set( ( i + 1 ) for i in setiRet ) + else: + setiRet.add( 0 ) + #Indicate if the correct level is reached + if iTarget < 0: + if fLeaves: + fHit = -( iTarget + 1 ) in setiRet + else: + fHit = -( iTarget + 1 ) <= max( setiRet ) + #if astrClade is not == [] (so you are actually in a clade of the tree) + #And the clade has values (should be true, if not impute should have been callded before running this method) + #And we are at the correct level of the tree then + #Add to the dict the clade and the abundance values + if astrClade and self.m_adValues and fHit: + hashValues["|".join( astrClade )] = self.m_adValues + return setiRet + + def freeze( self, hashValues, iTarget, fLeaves ): + """ + Call helper function setting the clade and depth to defaults (start positions) + The important result of this method is hashValues is updated with clade and abundance information + """ + self._freeze( hashValues, iTarget, [], 0, fLeaves ) + + def _repr( self, strClade ): + """ + Represent tree clade for debugging. Helper function for recursive repr. + """ + + strRet = "<" + if strClade: + strRet += "%s %s" % (strClade, self.m_adValues) + if self.m_hashChildren: + strRet += " " + if self.m_hashChildren: + strRet += " ".join( p._repr( s ) for (s, p) in self.m_hashChildren.items( ) ) + + return ( strRet + ">" ) + + def __repr__( self ): + """ + Represent tree clade for debugging. + """ + return self._repr( "" ) + +""" +pTree = CClade( ) +pTree.get( ("A", "B") ).set( [1, 2, 3] ) +pTree.get( ("A", "C") ).set( [4, 5, 6] ) +pTree.get( ("D", "E") ).set( [7, 8, 9] ) +iTaxa = 0 +if iTaxa: + pTree.impute( ) +hashFeatures = {} +pTree.freeze( hashFeatures, iTaxa ) +print( pTree ) +print( hashFeatures ) +sys.exit( 0 ) +#""" diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/CClade.pyc Binary file src/breadcrumbs/src/CClade.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/Cladogram.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/Cladogram.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,950 @@ +""" +Author: Timothy Tickle +Description: Class to call circlader and create dendrograms. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from AbundanceTable import AbundanceTable +from CommandLine import CommandLine +from ConstantsBreadCrumbs import ConstantsBreadCrumbs +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +import math +import numpy as np +import os +import re +import scipy.stats +from ValidateData import ValidateData +#import scipy.stats.stats as stats + +class Cladogram: + """ + This class manages creating files for Circlader and calling circulator. + """ + + #Script name + circladerScript=None + + #Constants + c_sTaxa="Taxa" + c_sCircle="Circle" + c_sBorder="Border" + c_sShape="Shape" + c_sAlpha="Alpha" + c_sForced="Forced" + + #Numpy array (structured array) holding data + #Should be SampleID, Sample Abundances/Data (samples = columns)..... + npaAbundance = None + #List of sample names + lsSampleNames = None + #Name of output image + strImageName = "Cladogram.png" + #String used to call the sample id column + strSampleID = "ID" + strUnclassified = "unclassified" + + #Minimum size of clade (terminal node count for clade) + iMinCladeSize = 1 + #Level of ancestry to filter at (starts with 0 and based on the input file) + iCladeLevelToMeasure = 1 + iCladeLevelToReduce = 1 + cFeatureDelimiter = "|" + + #Flags + #Turns on (True) or off (False) abundance-based filtering + fAbundanceFilter = False + #Turns on (True) or off (False) clade size-based filtering + fCladeSizeFilter = False + #Indicate if the following files were made + fSizeFileMade=False + fCircleFileMade=False + fColorFileMade=False + fTickFileMade=False + fHighlightFileMade=False + + #Circlader files + strTreeFilePath="_Taxa.txt" + strCircleFilePath = "_Circle.txt" + strColorFilePath="_Color.txt" + strTickFilePath="_Tick.txt" + strHighLightFilePath="_HighLight.txt" + strSizeFilePath="_Size.txt" + strStyleFilePath="" + + #Thresholds + #Controls the showing of taxa + c_dPercentileCutOff = 90.0 + c_dPercentageAbovePercentile = 1.0 + + #Minimum average abundance score when using log scale + c_dMinLogSize = 0.0000000001 + #Constant used to maginfy the size difference in the taxa (log only) + c_dLogScale = 1000000 + #When after log10, an addition scaling adjustment (use this) + c_dCircleScale = 3 + + #Data for circular files + #Used to change IDs to proper labels + dictConvertIDs = None + #Labels to be relabeled + dictRelabels = None + #Colors + dictColors = None + #Elements that are forced to be highlighted + dictForcedHighLights = None + #Ticks + llsTicks = None + #Forced root of the tree, discarding data as needed. + strRoot = None + #Holds circle data as a list of dictionaries + #One dictionary per circle + ldictCircleData = None + + def __init__(self): + self.dictForcedHighLights = dict() + + #Happy Path Tested + def addHighLights(self, dictClades,fOverwrite): + """ + This methods allows highlighting to be added. + When an element is added in this manner it will not be filtered out. + These elements, if existing in the tree will be highlighted the named color given. + This color name should be supplied in the set Color Data method + {strName1:strColorName1,strName2:strColorName2,...} + + :param dictClades: Names of elements, if found in the tree which should be highlighted + :type: dictClades Dictionary of element name (string) and element color (string) + :param fOverwrite: If element is already indicated to be highlighted, overwrite the color to the one provided here. + :type: fOverwrite boolean (True == overwrite color) + """ + if ValidateData.funcIsValidDictionary(dictClades): + if ValidateData.funcIsValidBoolean(fOverwrite): + for strElement in dictClades: + if(strElement in self.dictForcedHighLights): + if(fOverwrite): + self.dictForcedHighLights[strElement] = dictClades[strElement] + else: + self.dictForcedHighLights[strElement] = dictClades[strElement] + + #Not tested + def getHighLights(self): + return self.dictForcedHighLights + + #Not tested + def forceRoot(self, strRoot): + """ + This method allows one to root the tree at a certain level and value + Only taxa that contain this value in their ancestry will be plotted + The root will be the value given, any previous heirachy will be ignored + This will remove highlighted data if indicated to do so + + :params strRoot: Where to root the tree + :type: strRoot String + """ + self.strRoot = strRoot + + def generate(self, strImageName, strStyleFile, sTaxaFileName, strCircladerScript = ConstantsBreadCrumbs.c_strCircladerScript, iTerminalCladeLevel = 10, sColorFileName=None, sTickFileName=None, sHighlightFileName=None, sSizeFileName=None, sCircleFileName=None): + """ + This is the method to call to generate a cladogram using circlader. + The default data file is an abundance table unless the getDa function is overwritten. + + :param strImageName: File name to save the output cladogram image + :type: strImageName File name (string) + :param strStyleFile: File path indicating the style file to use + :type: strStyleFile File path (string) + :param sTaxaFileName: File path indicating the taxa file to use + :type: sTaxaFileName File path (string) + :param strCircladerScript: File path to the Circlader script + :type: String + :param iTerminalCladeLevel: Clade level to use as terminal in plotting + :type: iTerminalCladeLevel integer starting with 1 + :param strColorFile: File path indicating the color file to use + :type: strColorFile File path (string) + :param strTickFile: File path indicating the tick file to use + :type: strTickFile File path (string) + :param strHighlightFile: File path indicating the highlight file to use + :type: strHighlightFile File path (string) + :param strSizeFile: File path indicating the size file to use + :type: strSizeFile File path (string) + :param sCircleFileName: File path of circlader circle file. + :type: String + """ + + if self.npaAbundance == None: + print "Cladogram::generate. The data was not set so an image could not be generated" + return False + + #Set script + self.circladerScript = strCircladerScript + + #Set output file name + self.strImageName = strImageName + + #Check files exist and remove files which will be written + self.manageFilePaths(sTaxaFileName, strStyleFile, sColorFileName, sTickFileName, sHighlightFileName, sSizeFileName, sCircleFileName) + + #Get IDs + lsIDs = [strId for strId in list(self.npaAbundance[self.strSampleID])] + + #Generate a dictionary to convert the ids to correct format + #Fix unclassified names + #Make numeric labels as indicated + self.dictConvertIDs = self.generateLabels(lsIDs) + + #Remove taxa lower than the display clade level + lsCladeAndAboveFeatures = [] + for sFeature in lsIDs: + if len(sFeature.split(self.cFeatureDelimiter)) <= iTerminalCladeLevel: + lsCladeAndAboveFeatures.append(sFeature) + lsIDs = lsCladeAndAboveFeatures + + #Filter by abundance + if(self.fAbundanceFilter): + lsIDs = self.filterByAbundance(lsIDs) + + #Update to the correct root + lsIDs = self.updateToRoot(lsIDs) + + #Set highlights to root for consistency + if(not self.strRoot == None): + dictRootedHighLights = dict() + if not self.dictForcedHighLights == None: + for sKey in self.dictForcedHighLights.keys(): + strUpdatedKey = self.updateToRoot([sKey]) + dictRootedHighLights[strUpdatedKey[0]]=self.dictForcedHighLights[sKey] + self.dictForcedHighLights = dictRootedHighLights + + #Set relabels to root for consistency + if(not self.strRoot == None): + dictRootedLabels = dict() + if not self.dictRelabels == None: + for sKey in self.dictRelabels.keys(): + strUpdatedKey = self.updateToRoot([sKey]) + dictRootedLabels[strUpdatedKey[0]]=self.dictRelabels[sKey] + self.dictRelabels = dictRootedLabels + + #Filter by clade size Should be the last filter. + #It is not a strong filter but cleans up images + if(self.fCladeSizeFilter): + lsIDs = self.filterByCladeSize(lsIDs) + + #Add in forced highlighting + lsIDs.extend(self.dictForcedHighLights.keys()) + lsIDs = list(set(lsIDs)) + + #Add in forced circle data + for dictCircleData in self.ldictCircleData: + if(dictCircleData[self.c_sForced]): + lsTaxa = dictCircleData[self.c_sTaxa] + lsAlpha = dictCircleData[self.c_sAlpha] + lsAddTaxa = [] + [lsAddTaxa.append(lsTaxa[tpleAlpha[0]]) if not tpleAlpha[1] == '0.0' else 0 for tpleAlpha in enumerate(lsAlpha)] + lsIDs.extend(lsAddTaxa) + lsIDs = list(set(lsIDs)) + + #Create circle files (needs to be after any filtering because it has a forcing option). + if not self.createCircleFile(lsIDs): + return False + + #Generate / Write Tree file + if not self.createTreeFile(lsIDs): + return False + + #Generate / Write Highlight file + if not self.createHighlightFile(lsIDs): + return False + + #Generate / write color file + if(self.dictColors is not None): + lsColorData = [ConstantsBreadCrumbs.c_cTab.join([sColorKey,self.dictColors[sColorKey]]) for sColorKey in self.dictColors] + self.writeToFile(self.strColorFilePath, ConstantsBreadCrumbs.c_strEndline.join(lsColorData), False) + self.fColorFileMade=True + + #Generate / write tick file + if(self.llsTicks is not None): + lsTickData = [ConstantsBreadCrumbs.c_cTab.join(lsTicks) for lsTicks in self.llsTicks] + self.writeToFile(self.strTickFilePath, ConstantsBreadCrumbs.c_strEndline.join(lsTickData), False) + self.fTickFileMade=True + + #Generate / Write size data + if not self.createSizeFile(lsIDs): + return False + + #Call commandline + lsCommand = [self.circladerScript, self.strTreeFilePath, self.strImageName, "--style_file", self.strStyleFilePath, "--tree_format", "tabular"] + if(self.fSizeFileMade): + lsCommand.extend(["--size_file", self.strSizeFilePath]) + if(self.fColorFileMade): + lsCommand.extend(["--color_file", self.strColorFilePath]) + if(self.fTickFileMade): + lsCommand.extend(["--tick_file", self.strTickFilePath]) + if(self.fHighlightFileMade): + lsCommand.extend(["--highlight_file", self.strHighLightFilePath]) + if(self.fCircleFileMade): + lsCommand.extend(["--circle_file", self.strCircleFilePath]) + CommandLine().runCommandLine(lsCommand) + + #Happy path tested + def setColorData(self, dictColors): + """ + This methods allows color information to be specified. + Need to give a dictionary having a name (key)(string) and color (value)(string RGB)data + {strName1:Color,strName2:Color...} + Name will be a string name that references what needs to be this color + Color data should be a string in the RGB format 0-255,0-255,0-255 + + :param dictColors: Color Name and RGB specification + :type: dictColorsDictionary strings + """ + if ValidateData.funcIsValidDictionary(dictColors): + self.dictColors = dictColors + if not ConstantsFiguresBreadCrumbs.c_strBackgroundColorName in self.dictColors: + self.dictColors[ConstantsFiguresBreadCrumbs.c_strBackgroundColorName]=ConstantsFiguresBreadCrumbs.c_strBackgroundColor + + #Not tested + def setAbundanceData(self, abtbAbundanceTable): + """ + Sets the abundance data the Cladogram will use to plot + + :params abtAbundanceTable: AbundanceTable to set + :type: AbundanceTable + """ + self.npaAbundance = abtbAbundanceTable.funcGetAbundanceCopy() + self.strSampleID = abtbAbundanceTable.funcGetIDMetadataName() + self.lsSampleNames = abtbAbundanceTable.funcGetSampleNames() + + #Not tested + def setFilterByAbundance(self, fAbundanceFilter, dPercentileCutOff = 90.0, dPercentageAbovePercentile = 1.0): + """ + Switch filtering by abundance on and off. + fAbundanceFilter == True indicates filtering is on + + :param fAbundanceFilter: Switch to turn on (true) and off (false) abundance-based filtering + :type: fAbundanceFilter boolean + :param dPercentileCutOff: Percentage between 100.0 to 0.0. + :type: double + :param dPercentageAbovePercentile: Percentage between 100.0 to 1.0. + :type: double + """ + self.fAbundanceFilter = fAbundanceFilter + self.c_dPercentileCutOff = dPercentileCutOff + self.c_dPercentageAbovePercentile = dPercentageAbovePercentile + + #Not Tested + def setCircleScale(self, iScale): + """ + Is a scale used to increase or decrease node sizes in the the cladogram to make more visible + iScale default is 3 + + :param iScale: Integer to increase the relative sizes of nodes + :type: iScale integer + """ + self.c_dCircleScale = iScale + + #Not tested + def setFeatureDelimiter(self, cDelimiter): + """ + Set the delimiter used to parse the consensus lineages of features. + + :param cDelimiter: The delimiter used to parse the consensus lineage of features. + :type: Character + """ + if cDelimiter: + self.cFeatureDelimiter = cDelimiter + + #Not tested + def setFilterByCladeSize(self, fCladeSizeFilter, iCladeLevelToMeasure = 3, iCladeLevelToReduce = 1, iMinimumCladeSize = 5, cFeatureDelimiter = None, strUnclassified="unclassified"): + """ + Switch filtering by clade size on and off. + fCladeSizeFilter == True indicates filtering is on + NOT 0 based. + + :param fCladeSizeFilter: Switch to turn on (true) and off (false) clade size-based filtering + :type: fCladeSizeFilter boolean + :param iCladeLevelToMeasure: The level of the concensus lineage that is measure or counted. Should be greater than iCladeLevelToReduce (Root is 1) + :type: iCladeLevelToMeasure int + :param iCladeLevelToReduce: The level of the concensus lineage that is reduced if the measured level are not the correct count (Root is 1) + :type: iCladeLevelToReduce int + :param iMinimumCladeSize: Minimum count of the measured clade for the clade to be kept + :type: iMinimumCladeSize int + :param cFeatureDelimiter: One may set the feature delimiter if needed. + :type: Character + :param strUnclassified: String indicating unclassifed features + :type: String + """ + self.fCladeSizeFilter = fCladeSizeFilter + if iCladeLevelToMeasure > 0: + self.iCladeLevelToMeasure = iCladeLevelToMeasure + if iCladeLevelToReduce > 0: + self.iCladeLevelToReduce = iCladeLevelToReduce + if iMinimumCladeSize > 0: + self.iMinCladeSize = iMinimumCladeSize + if cFeatureDelimiter: + self.cFeatureDelimiter = cFeatureDelimiter + if strUnclassified: + self.strUnclassified = strUnclassified + + #Not tested + def setTicks(self, llsTicks): + """ + This methods allows tick information to be specified. + Need to generate a list of lists each having a tick level (number starting at 0 as a string), and tick name + #Lowest numbers are closest to the center of the tree + [[#,Name1],[#,Name2]...] + + :param llsTicks: Level # and Name of level + :type: llsTicks List of lists of strings + """ + self.llsTicks = llsTicks + + #Happy Path tested with createCircleFile + def addCircle(self, lsTaxa, strCircle, dBorder=0.0, strShape="R", dAlpha=1.0, fForced=False): + """ + This methods allows one to add a circle to the outside of the cladogram. + + :param lsTaxa: Taxa to highlight with this circle + :type: lsTaxa List of strings (taxa names) + :param strCircle: Circle the elements will be in, indicates color and circle level. + :type: strCircle String circle + :param dBorder: Border size for the circle element border (between 0.0 and 1.0) + can also be a list of dBorders. If list, position must match lsTaxa. + :type: dBorder Float of border size (or list of floats). + :param strShape: String Indicator of shape or method to determine shape. + Can also be a list of shapes. If list, position must match lsTaxa. + :type: strShape String to indicate the shape (may also be a list of strings). + Default value is square. + Valid shapes are R(Square), v(inward pointing triangle), ^(outward pointing triangle) + :param dAlpha: The transparency of the circle element (between 0.0[clear] and 1.0[solid]). + Can also be a list of floats. If list, position must match lsTaxa. + :type: dAlpha Float to indicate the transparency of the shape (may also be a list of strings). + :param fForced: Forces item in the features in the circle to be displayed in the cladogram no matter thier passing filters. + :type: Boolean + """ + if(self.ldictCircleData == None): + self.ldictCircleData = list() + dictCircleData = dict() + dictCircleData[self.c_sTaxa]=lsTaxa + dictCircleData[self.c_sCircle]=strCircle + dictCircleData[self.c_sBorder]=dBorder + dictCircleData[self.c_sShape]=strShape + dictCircleData[self.c_sAlpha]=dAlpha + dictCircleData[self.c_sForced]=fForced + + self.ldictCircleData.append(dictCircleData) + return True + + #Happy Path tested with AddCircle + def createCircleFile(self, lsIDs): + """ + Write circle data to file. + + :param lsIDs: Ids to include in the circle file + :type: lsIDs List of strings + """ + #If there is circle data + if(not self.ldictCircleData == None): + if self.strCircleFilePath == None: + print("Error, there is no circle file specified to write to.") + return False + #Holds circle data {Taxaname:string updates correctly for output to file} + dictCircleDataMethods = dict() + lsCircleData = list() + + for dictCircleData in self.ldictCircleData: + lsTaxa = dictCircleData[self.c_sTaxa] + #Shape/s for taxa + datShape = dictCircleData[self.c_sShape] + fShapeIsList = (str(type(datShape)) == "") + #Border/s for taxa + datBorder = dictCircleData[self.c_sBorder] + fBorderIsList = (str(type(datBorder)) == "") + #Alpha/s for taxa + datAlpha = dictCircleData[self.c_sAlpha] + fAlphaIsList = (str(type(datAlpha)) == "") + #Circle name + sCircleMethod = dictCircleData[self.c_sCircle] + + #Check to make sure the lengths of the array match up + if(fShapeIsList): + if not len(datShape) == len(lsTaxa): + print("".join(["Error, Shapes were given as an list not of the size of the taxa list. Shape list length: ",str(len(datShape)),". Taxa list length: ",str(len(lsTaxa)),"."])) + return False + if(fBorderIsList): + if not len(datBorder) == len(lsTaxa): + print("".join(["Error, Border sizes were given as an list not of the size of the taxa list. Border list length: ",str(len(datBorder)),". Taxa list length: ",str(len(lsTaxa)),"."])) + return False + if(fAlphaIsList): + if not len(datAlpha) == len(lsTaxa): + print("".join(["Error, Alpha sizes were given as an list not of the size of the taxa list. Alpha list length: ",str(len(datAlpha)),". Taxa list length: ",str(len(lsTaxa)),"."])) + return False + + #Update taxa to root if needed + #When doing this if any of the other data is an array we have to edit them + #as the taxa are edited for updating root + if((not fShapeIsList) and (not fBorderIsList) and (not fAlphaIsList)): + lsTaxa = self.updateToRoot(dictCircleData[self.c_sTaxa]) + else: + #Initilize as lists or as the string value they already are + lsUpdatedTaxa = list() + datUpdatedShapes=list() + if(not fShapeIsList): + datUpdatedShapes = datShape + datUpdatedBorders=list() + if(not fBorderIsList): + datUpdatedBorders = datBorder + datUpdatedAlphas=list() + if(not fAlphaIsList): + datUpdatedAlphas = datAlpha + + #If a taxa is kept, keep associated list information + #If not a list data, leave alone, it will be used globally for all taxa. + iTaxaIndex = -1 + for sTaxa in lsTaxa: + iTaxaIndex = iTaxaIndex + 1 + sUpdatedTaxa=self.updateToRoot([sTaxa]) + + if len(sUpdatedTaxa)==1: + lsUpdatedTaxa.append(sUpdatedTaxa[0]) + if(fShapeIsList): + datUpdatedShapes.append(datShape[iTaxaIndex]) + if(fBorderIsList): + datUpdatedBorders.append(datBorder[iTaxaIndex]) + if(fAlphaIsList): + datUpdatedAlphas.append(datAlpha[iTaxaIndex]) + + #Reset data to rooted data + lsTaxa=lsUpdatedTaxa + datShape=datUpdatedShapes + datBorder=datUpdatedBorders + datAlpha=datUpdatedAlphas + + #QC passes so we will add the circle to the figure and the ticks. + #If there are ticks and if the circle is not already in the ticks. + if(not self.llsTicks == None): + strCircleName = dictCircleData[self.c_sCircle] + fFound = False + iHighestNumber = -1 + for tick in self.llsTicks: + #Look for name + if tick[1] == strCircleName: + fFound = True + #Find highest count + if int(tick[0]) > iHighestNumber: + iHighestNumber = int(tick[0]) + if not fFound: + self.llsTicks.append([str(iHighestNumber+1),strCircleName]) + + #If the circle is forced, add the taxa to the lsIDs + #Otherwise we will only plot those that are matching + #the lsIDs and the circle taxa list. + if dictCircleData[self.c_sForced]: + for iAlpha in xrange(0,len(datAlpha)): + if(not datAlpha[iAlpha] == "0.0"): + lsIDs.append(lsTaxa[iAlpha]) + lsIDs = list(set(lsIDs)) + + #For all taxa in the cladogram + for sTaxa in lsTaxa: + #Store circle content name in dictionary + if not sTaxa in dictCircleDataMethods: + #Reset name to . delimited + asNameElements = filter(None,re.split("\|",sTaxa)) + + sCurTaxaName = asNameElements[len(asNameElements)-1] + if(len(asNameElements)>1): + if(sCurTaxaName=="unclassified"): + sCurTaxaName = ".".join([asNameElements[len(asNameElements)-2],sCurTaxaName]) + sCurTaxa = ".".join(asNameElements) + #Add to dictionary + dictCircleDataMethods[sTaxa] = sCurTaxa + + #If the taxa is in the selected method + if sTaxa in lsTaxa: + #Index of the id in the circle data + iTaxaIndex = lsTaxa.index(sTaxa) + #Get border + sBorder = "" + if(fBorderIsList): + sBorder = str(datBorder[iTaxaIndex]) + else: + sBorder = str(datBorder) + #Get shape + sShape = "" + if(fShapeIsList): + sShape = datShape[iTaxaIndex] + else: + sShape = datShape + #Get alpha + sAlpha = "" + if(fAlphaIsList): + sAlpha = str(datAlpha[iTaxaIndex]) + else: + sAlpha = str(datAlpha) + dictCircleDataMethods[sTaxa]=dictCircleDataMethods[sTaxa]+"".join([ConstantsBreadCrumbs.c_cTab,sCircleMethod,":",sAlpha,"!",sShape,"#",sBorder]) + else: + dictCircleDataMethods[sTaxa]=dictCircleDataMethods[sTaxa]+"".join([ConstantsBreadCrumbs.c_cTab,sCircleMethod,":0.0!R#0.0"]) + + if len(dictCircleDataMethods)>0: + lsTaxaKeys = dictCircleDataMethods.keys() + sCircleContent = dictCircleDataMethods[lsTaxaKeys[0]] + for sTaxaKey in lsTaxaKeys[1:len(lsTaxaKeys)]: + sCircleContent = ConstantsBreadCrumbs.c_strEndline.join([sCircleContent,dictCircleDataMethods[sTaxaKey]]) + self.writeToFile(self.strCircleFilePath, sCircleContent, False) + self.fCircleFileMade=True + + return True + self.fCircleFileMade=False + return False + + #Happy Path tested + def createHighlightFile(self, lsIDs): + """ + Write highlight data to file + + :param lsIDs: Ids to include in the highlight file + :type: lsIDs List of strings + """ + lsHighLightData = list() + #Each taxa name + for sID in lsIDs: + sCurColor = "" + #Rename taxa to be consisten with the . delimit format + asNameElements = filter(None,re.split("\|",sID)) + sCurTaxaName = asNameElements[len(asNameElements)-1] + if(len(asNameElements)>1): + if(sCurTaxaName=="unclassified"): + sCurTaxaName = ".".join([asNameElements[len(asNameElements)-2],sCurTaxaName]) + sCurTaxa = ".".join(asNameElements) + + sCurLabel = "" + #Get color + sColorKey = "" + if(sID in self.dictForcedHighLights): + sColorKey = self.dictForcedHighLights[sID] + if(sColorKey in self.dictColors): + sCurColor = self.formatRGB(self.dictColors[sColorKey]) + #Get label + if(self.dictRelabels is not None): + if(sID in self.dictRelabels): + sCurLabel = self.dictRelabels[sID] + if(sCurLabel == ""): + lsHighLightData.append(ConstantsBreadCrumbs.c_cTab.join([sCurTaxa,sCurTaxaName,sCurLabel,sCurColor])) + else: + lsHighLightData.append(ConstantsBreadCrumbs.c_cTab.join([sCurTaxa,sCurLabel,sCurLabel,sCurColor])) + + if len(lsHighLightData)>0: + self.writeToFile(self.strHighLightFilePath, ConstantsBreadCrumbs.c_strEndline.join(lsHighLightData), False) + self.fHighlightFileMade=True + return True + + #Happy path tested + def createSizeFile(self, lsIDs): + """ + Write size data to file + + :param lsIDs: Ids to include in the size file + :type: lsIDs List of strings + """ + if self.npaAbundance is not None: + dMinimumValue = (self.c_dMinLogSize*self.c_dLogScale)+1 + lsWriteData = list() + for rowData in self.npaAbundance: + strCurrentId = rowData[0] + #Reset to root if needed to match current data + if(not self.strRoot == None): + strCurrentId = self.updateToRoot([strCurrentId]) + if(len(strCurrentId) > 0): + strCurrentId = strCurrentId[0] + if(strCurrentId in lsIDs): + dAverage = np.average(list(rowData)[1:]) + dSize = max([dMinimumValue,(dAverage*self.c_dLogScale)+1]) + lsWriteData.append(".".join(re.split("\|",strCurrentId))+ConstantsBreadCrumbs.c_cTab+str(math.log10(dSize)*self.c_dCircleScale)) + if len(lsWriteData)>0: + self.writeToFile(self.strSizeFilePath, ConstantsBreadCrumbs.c_strEndline.join(lsWriteData), False) + self.fSizeFileMade=True + return True + + #Happy path tested 1 + def createTreeFile(self, lsIDs): + """ + Write tree data to file. The tree file defines the internal cladogram and all it's points. + + :param lsIDs: Ids to include in the tree file as well as their ancestors + :type: lsIDs List of strings + """ + lsFullTree = list() + for sID in lsIDs: + lsIDElements = filter(None,re.split("\|",sID)) + sElementCur = lsIDElements[0] + if(not sElementCur in lsFullTree): + lsFullTree.append(sElementCur) + if(len(lsIDElements) > 1): + sNodePath = "" + for iEndLevel in xrange(1,len(lsIDElements)+1): + sCurAncestry = lsIDElements[0:iEndLevel] + sNodePath = ".".join(sCurAncestry) + if(not sNodePath in lsFullTree): + lsFullTree.append(sNodePath) + + if len(lsFullTree)>0: + self.writeToFile(self.strTreeFilePath, ConstantsBreadCrumbs.c_strEndline.join(lsFullTree), False) + return True + + #Happy Path tested + def filterByAbundance(self, lsIDs): + """ + Filter by abundance. Specifically this version requires elements of + the tree to have a certain percentage of a certain percentile in samples. + + :param lsIDs: Ids to filter + :type: lsIDs List of strings + """ + #list of ids to return that survived the filtering + retls = list() + if not self.npaAbundance is None: + #Hold the cuttoff score (threshold) for the percentile of interest {SampleName(string):score(double)} + dictPercentiles = dict() + for index in xrange(1,len(self.npaAbundance.dtype.names)): + dScore = scipy.stats.scoreatpercentile(self.npaAbundance[self.npaAbundance.dtype.names[index]],self.c_dPercentileCutOff) + dictPercentiles[self.npaAbundance.dtype.names[index]] = dScore + + #Sample count (Ignore sample id [position 0] which is not a name) + dSampleCount = float(len(self.npaAbundance.dtype.names[1:])) + + #Check each taxa + for rowTaxaData in self.npaAbundance: + sCurTaxaName = rowTaxaData[0] + #Only look at the IDs given + if(sCurTaxaName in lsIDs): + dCountAbovePercentile = 0.0 + ldAbundanceMeasures = list(rowTaxaData)[1:] + #Check to see if the abundance score meets the threshold and count if it does + for iScoreIndex in xrange(0,len(ldAbundanceMeasures)): + if(ldAbundanceMeasures[iScoreIndex] >= dictPercentiles[self.lsSampleNames[iScoreIndex]]): + dCountAbovePercentile = dCountAbovePercentile + 1.0 + dPercentOverPercentile = dCountAbovePercentile / dSampleCount + if(dPercentOverPercentile >= (self.c_dPercentageAbovePercentile/100.0)): + retls.append(sCurTaxaName) + return retls + + #Happy Path Tested + def filterByCladeSize(self, lsIDs): + """ + Filter by the count of individuals in the clade. + + :param lsIDs: Ids to filter + :type: lsIDs List of strings + """ + #First get terminal nodes + lsTerminalNodes = AbundanceTable.funcGetTerminalNodesFromList(lsIDs,self.cFeatureDelimiter) + + #Count up clades + cladeCounts = dict() + + #For each terminal node count the + #Clades at clade levels + for sTerminalNode in lsTerminalNodes: + lsLineage = sTerminalNode.split(self.cFeatureDelimiter) + iLineageCount = len(lsLineage) + #If the lineage is shorter than the reduced clade level then no need to filter it + if iLineageCount >= self.iCladeLevelToReduce: + #If the lineage is longer than the reduced clade level and measuring clade level then count + #or If the lineage is longer than the reduced clade level but shorter than the measuring clade, + #only count if the last element is unclassified + if (iLineageCount >= self.iCladeLevelToMeasure) or (lsLineage[-1] == self.strUnclassified): + sLineage = self.cFeatureDelimiter.join(lsLineage[0:self.iCladeLevelToReduce]) + cladeCounts[sLineage] = cladeCounts.get(sLineage,0) + 1 + + #Go through the IDs and reduce as needed using the clade counts + retls = list() + for sID in lsIDs: + lsID = sID.split(self.cFeatureDelimiter) + iIDCount = len(lsID) + + #Too short to filter + if iLineageCount < self.iCladeLevelToReduce: + retls.append(sID) + #Check to see if the clade which is being reduced made the cut + if iIDCount >= self.iCladeLevelToReduce: + if (iIDCount >= self.iCladeLevelToMeasure) or (lsID[-1] == self.strUnclassified): + if cladeCounts[self.cFeatureDelimiter.join(lsID[0:self.iCladeLevelToReduce])] >= self.iMinCladeSize: + retls.append(sID) + + return retls + + #Happy path tested + def formatRGB(self, sColor): + """ + Takes a string that is of the format 0-255,0-255,0-255 and converts it to the + color format of circlader _c_[0-1,0-1,0-1] + + :param sColor: String RGB format + :type: sColor String + """ + sCircladerColor = "_c_[1,1,1]" + if(sColor is not None): + sColorElements = filter(None,re.split(",",sColor)) + if(len(sColorElements)==3): + iR = int(sColorElements[0])/255.0 + iG = int(sColorElements[1])/255.0 + iB = int(sColorElements[2])/255.0 + sCircladerColor = "".join(["_c_[",str(iR),",",str(iG),",",str(iB),"]"]) + return sCircladerColor + + #Happy path tested + def generateLabels(self, lsIDs): + """ + Labels for visualization. + Changes unclassified to one_level_higher.unclassified and enables numeric labeling / relabeling. + Will only rename, will not add the label. The key must exist for the value to be used in replacing. + + :param lsIDs: Ids to include in the labels file + :type: lsIDs List of strings + """ + dictRet = dict() + for sID in lsIDs: + lsIDElements = filter(None,re.split("\|",sID)) + iIDElementsCount = len(lsIDElements) + sLabel = lsIDElements[iIDElementsCount-1] + #Fix unclassified + if((sLabel == "unclassified") and (iIDElementsCount > 1)): + sLabel = ".".join([lsIDElements[iIDElementsCount-2],sLabel]) + #Change to relabels if given + if(self.dictRelabels is not None): + if(sLabel in self.dictRelabels): + sLabel = self.dictRelabels[sLabel] + #Store lable + dictRet[sID] = sLabel + return dictRet + + #Happy path tested + def manageFilePaths(self, sTaxaFileName, strStyleFile, sColorFileName=None, sTickFileName=None, sHighlightFileName=None, sSizeFileName=None, sCircleFileName=None): + """ + This method sets the naming to the files generated that Circlader acts on. + These files include the tree, color, highlight, tick, circle, and size files. + Checks to make sure the file path to the syle file provided is an existing file. + Deletes any existing files with these generated names (except for the style files). + + :param sStyleFile: File path indicating the style file to use + :type: String + :param strTaxaFile: File path indicating the taxa file to use + :type: String + :param sColorFile: File path indicating the color file to use + :type: String + :param sTickFile: File path indicating the tick file to use + :type: String + :param sHighlightFile: File path indicating the highlight file to use + :type: String + :param sSizeFile: File path indicating the size file to use + :type: String + :param sCircleFileName: File path for circle files + :type: String + :return boolean: True indicates success, false indicates error + """ + #Do not remove the style file, it is static + if strStyleFile is None: + print("Error, style file is None") + return(False) + if not os.path.exists(strStyleFile): + print("Error, no style file found.") + return(False) + else: + self.strStyleFilePath = strStyleFile + + #Set output files and remove if needed + self.strTreeFilePath = sTaxaFileName + self.strColorFilePath = sColorFileName + self.strTickFilePath = sTickFileName + self.strHighLightFilePath = sHighlightFileName + self.strSizeFilePath = sSizeFileName + self.strCircleFilePath = sCircleFileName + for sFile in [self.strTreeFilePath,self.strColorFilePath,self.strTickFilePath, + self.strHighLightFilePath,self.strSizeFilePath,self.strCircleFilePath]: + if not sFile is None: + if(os.path.exists(sFile)): + os.remove(sFile) + return True + + #Not tested + def relabelIDs(self, dictLabels): + """ + Allows the relabeling of ids. Can be used to make numeric labeling of ids or renaming + + :param dictLabels: Should label (key) (after unclassified is modified) and new label (value) + :type: dictLabels Dictionary of string (key:label to replace) string (value:new label to use in replacing) + """ + self.dictRelabels = dictLabels + + #Happy path tested + def updateToRoot(self, lsIDs): + """ + Updates the clade to the root given. The clade must contain the root and the level of the + root in the clade will be rest to it's first level, ignoring the previous levels of the clade. + + :param lsIDs: List of Clades that will be reset to the root specified by setRoot + :type: lsIDs List of strings. Each string representing a clade. + """ + + if(self.strRoot is None): + return lsIDs + #Force root tree if indicated to do so + lsRootedIDs = list() + for sID in lsIDs: + sIDElements = filter(None,re.split("\|",sID)) + if(self.strRoot in sIDElements): + iRootIndex = sIDElements.index(self.strRoot) + #If multiple levels of the clade exist after the new root merge them. + if(len(sIDElements)>iRootIndex+2): + lsRootedIDs.append("|".join(sIDElements[iRootIndex+1:])) + #If only one level of the clade exists after the new root, return it. + elif(len(sIDElements)>iRootIndex+1): + lsRootedIDs.append(sIDElements[iRootIndex+1]) + return(lsRootedIDs) + + #Testing: Used extensively in other tests + def writeToFile(self, strFileName, strDataToWrite, fAppend): + """ + Helper function that writes a string to a file + + :param strFileName: File to write to + :type: strFileName File path (string) + :param strDataToWrite: Data to write to file + :type: strDataToWrite String + :param fAppend: Indicates if an append should occur (True == Append) + :type: fAppend boolean + """ + + cMode = 'w' + if fAppend: + cMode = 'a' + with open(strFileName,cMode) as f: + f.write(strDataToWrite) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/CommandLine.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/CommandLine.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,137 @@ +""" +Author: Timothy Tickle +Description: Manages calling commandline from within code. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#Import libaries +from subprocess import call, Popen, PIPE +from ValidateData import ValidateData +import traceback + +class CommandLine(): + """ + Manages calling commandline from within code. + """ + + ## + #Contructor + def __init__(self): pass + + def runCommandLine(self,tempCommand = None): + """ + Sends a command to command line interface. + + :param tempCommand: Must be an list of command key word and string arguments, no whitespaces + :type: List of strings + :return: boolean indicator of success (True = Success) + """ + + #Makes sure the the input data is a list of strings + if(not ValidateData.funcIsValidStringList(tempCommand)): + print "Error:: tempCommand must be an array of strings. Received="+str(tempCommand)+"." + return False + + #Run command + try: + returnCode = call(tempCommand) + print "Return="+str(returnCode) + if returnCode > 0: + print "Error:: Error during command call. Script stopped." + print "Error:: Error Code "+str(returnCode)+"." + print "Error:: Command ="+str(tempCommand)+"." + return False + except (OSError,TypeError), e: + print "Error:: Error during command call. Script stopped." + print "Error:: Command ="+str(tempCommand)+"." + print "Error:: OS error: "+str(traceback.format_exc(e))+"." + return False + return True + + def runPipedCommandLine(self,tempCommand = None): + """ + Sends a command to command line interface. + Create new array of string elements instead of white spacing + Put file names in escaped quotation marks. + This uses shell == true so make sure the commandline is not malicious + This should wait for process completion + + :param tempCommand: Must be an list of command key word and string arguments, no whitespaces. + :type: List of strings + :return: Boolean (False = Failure or the return code from the subprocess) + """ + + #Makes sure the the input data is a list of strings + if(not ValidateData.funcIsValidStringList(tempCommand)): + print "Error:: tempCommand must be an array of strings. Received="+str(tempCommand)+"." + return False + + #Run command + tempCommand = " ".join(tempCommand) + try: + returnCode = Popen(tempCommand, shell = True, stdout = PIPE).communicate() + return returnCode + except (OSError,TypeError), e: + print "Error:: Error during command call. Script stopped." + print "Error:: Command ="+str(tempCommand)+"." + print "Error:: OS error: "+str(traceback.format_exc(e))+"." + return False + + def runBatchCommandline(self,tempArrayOfCommands = None): + """ + Sends a an array of commands to the commandline. + + :param tempArrayOfCommands: Must be an list of commands, parsing and removing whitespace is handled internally. + Do not send mkdir and rm commands, use the appropriate os.* method call + :type: List of strings + :return: boolean indicator of success (True = Success) + """ + + #Holds commands + parsedCommmands = [] + + #Indicates if success or error occured + success = True + + #Makes sure the the input data is list of strings + if(not ValidateData.funcIsValidStringList(tempArrayOfCommands)): + print "Error:: tempCommand must be an array of strings. Received="+str(tempArrayOfCommands)+"." + return False + + #Parse commands into an array and call + #On an error break and return False + for command in tempArrayOfCommands: + commandElements = command.split(" ") + if(not self.runCommandLine(commandElements)): + success = False + break + return success + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/ConstantsBreadCrumbs.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/ConstantsBreadCrumbs.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,155 @@ +""" +Author: Timothy Tickle +Description: Project constants. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +## +#Used to test the FileIO class +class ConstantsBreadCrumbs(): + """ + Class to hold project constants. + """ + + #Character Constants + c_strComma = ',' + c_strColon = ':' + c_strConfigFileHeaderChar = '[' + c_strConfigFileCommentChar = '#' + c_strEndline = '\n' + c_strExtDelim = '.' + c_cFastaIDLineStart = '>' + c_strPathDelim = '/' + c_cPipe = '|' + c_cQuote = '\"' + c_cTab = '\t' + c_strWhiteSpace = ' ' + c_matrixFileDelim = '\t' + + c_strBreadCrumbsSVMSpace = c_strWhiteSpace + + #Default values for missing data in the Abundance Table + c_strEmptyAbundanceData = "0" + c_strEmptyDataMetadata = "NA" + c_strSVMNoSample = "-" + + lNAs = list(set(["NA","na","Na","nA",c_strEmptyDataMetadata])) + + #TODO remove + #Reference to circlader + c_strCircladerScript = "circlader/circlader.py" + + #AbundanceTable + #Suffix given to a file that is check with the checkRawDataFile method + OUTPUT_SUFFIX = "-checked.pcl" + + #BIOM related + #PCL File metadata defaults (many of these come from biom file requirements + #ID + c_strIDKey = "id" + c_strDefaultPCLID = None + + #File date + c_strDateKey = "date" + + #File format type + c_strFormatKey = "format" + c_strDefaultPCLFileFormateType = "PCL" + + #File generation source + c_strSourceKey = "source" + c_strDefaultPCLGenerationSource = None + + #File type + c_strTypekey = "type" + c_strDefaultPCLFileTpe = None + + #Allowable file types for biom files + c_strOTUType = "OTU" + c_strOTUBIOMType = "OTU table" + c_strPathwayType = "Pathway" + c_strPathwayBIOMType = "Pathway table" + c_strFunctionType = "Function" + c_strFunctionBIOMType = "Function table" + c_strOrthologType = "Ortholog" + c_strOrthologBIOMType = "Ortholog table" + c_strGeneType = "Gene" + c_strGeneBIOMType = "Gene table" + c_strMetaboliteType = "Metabolite" + c_strMetaboliteBIOMType = "Metabolite table" + c_strTaxonType = "Taxon" + c_strTaxonBIOMType = "Taxon table" + c_dictFileType = {c_strOTUType:c_strOTUBIOMType, c_strPathwayType:c_strPathwayBIOMType, c_strFunctionType:c_strFunctionBIOMType, c_strOrthologType:c_strOrthologBIOMType, c_strGeneType:c_strGeneBIOMType, c_strMetaboliteType:c_strMetaboliteBIOMType, c_strTaxonType:c_strTaxonType} + + #File URL + c_strURLKey = "url" + c_strDefaultPCLURL = None + c_strFormatUrl = "format_url" + + #File sparse matrix + c_strSparsityKey = "sparsity" + c_fDefaultPCLSparsity = False + + # BIOM related Data + # Data shape + c_strDataShapeKey = "shape" + + ###################################################################### + # Constants related to biom import and export files # + ###################################################################### + # Biom file extension + c_strBiomFile = "biom" + c_BiomTaxData = "BiomTaxData" + c_MetadataID = "column_metadata_id" + c_Metadata = "Metadata" + c_metadata_lowercase = "metadata" + c_sLastMetadata = "sLastMetadata" + c_columns = "columns" + c_rows = "rows" + c_ascii = "ascii" + c_ignore = "ignore" + c_Dtype = "Dtype" + c_ID = "ID" + c_id_lowercase = "id" + c_f4 = "f8" + c_biom_file_generated_by = "BreadCrumbs" + c_strPCLFile = "pcl" + c_taxonomy = "taxonomy" + c_dRowsMetadata = "dRowsMetadata" + c_BiomFileInfo = "BiomFileInfo" + c_MatrixTtype = "matrix_type" + c_GeneratedBy = "generated_by" + c_MetadataEntriesTotal = "MetadataEntriesTotal" + c_MaximumLength = "MaximumLength" + + + def __init__(self): + pass diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/ConstantsBreadCrumbs.pyc Binary file src/breadcrumbs/src/ConstantsBreadCrumbs.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/ConstantsFiguresBreadCrumbs.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/ConstantsFiguresBreadCrumbs.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,99 @@ +""" +Author: Timothy Tickle +Description: Constants for figures. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +## +#Holds global configuration constants +class ConstantsFiguresBreadCrumbs(): + + #Figure oriented + c_strBackgroundColorName = "Invisible" + c_strBackgroundColor = "255,255,255" + c_strBackgroundColorWord = "white" + c_strBackgroundColorLetter = "w" + c_strDetailsColorWord = "black" + c_strDetailsColorLetter = "k" + + #PCOA Markers + c_charPCOAPieChart = "o" + c_charPCOASquarePieChart = "s" + iMarkerSize = 100 + + #PCoA defaults + c_strPCoALabelDefault = "Label" + c_cPCoAColorDefault = 'g' + c_cPCoAShapeDefault = 'o' + c_cPCoASizeDefault = 20 + + #General plotting + c_strGridLineColor = "#CCCCCC" + + c_fInverted = False + c_dAlpha = 0.5 + + def invertColors(self,fInvert): + if fInvert==True: + #General colors + self.c_strBackgroundColor = "0,0,0" + self.c_strBackgroundColorTuple = (0,0,0) + self.c_strBackgroundColorWord = "black" + self.c_strBackgroundColorLetter = "k" + self.c_strDetailsColorWord = "white" + self.c_strDetailsColorLetter = "w" + + #Invert no select color + self.c_charNoSelect = "#000000" # black + + #Record that it is inverted + self.c_fInverted = True + + #Alpha looks best at full in inversion + self.c_dAlpha = 1.0 + + else: + #General colors + self.c_strBackgroundColor = "255,255,255" + self.c_strBackgroundColorTuple = (255,255,255) + self.c_strBackgroundColorWord = "white" + self.c_strBackgroundColorLetter = "w" + self.c_strDetailsColorWord = "black" + self.c_strDetailsColorLetter = "k" + + #No select color + self.c_charNoSelect = "#FFFFFF" # White + + #Record that it is not inverted + self.c_fInverted = False + + #Alpha looks best at full in inversion + self.c_dAlpha = 0.5 diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/Histogram.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/Histogram.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,103 @@ +""" +Author: Timothy Tickle +Description: Class to create scatter plots. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +import matplotlib.pyplot as plt +from pylab import * + +#Plots a matrix +class Histogram: + + @staticmethod + def funcPlot(lx, strOutputFigurePath, strTitle = "Title", strXTitle="X Axis", strYTitle="Y Axis", strColor = "#83C8F9", fInvert=False): + """ + Plot a box plot with optional jittering. + + :params lx: List of x values + :type: List of doubles + :params strOutputFigurePath: File path to make figure + :type: String file path + :params strTitle: Title of figure + :type: String + :params strXTitle: Label of x axis + :type: String + :params strYTitle: Label of y axis + :type: String + :params strColor: Hex color for the face of the boxplots + :type: String + :params fInvert: Invert colors (true) + :type: Boolean + """ + + #Start plot + #Get plot object + imgFigure = plt.figure() + + #Get plot colorsstrOutFigure + objFigureControl = ConstantsFiguresBreadCrumbs() + #Boxplots have to be plotted over the scatter so the alpha can not go to 1.0 + #In this case capturing the alpha before inversion + #Inversion automoatically sets it to 1. + dAlpha=objFigureControl.c_dAlpha + objFigureControl.invertColors(fInvert=fInvert) + + #Color/Invert figure + imgFigure.set_facecolor(objFigureControl.c_strBackgroundColorWord) + imgSubplot = imgFigure.add_subplot(111,axisbg=objFigureControl.c_strBackgroundColorLetter) + imgSubplot.set_xlabel(strXTitle) + imgSubplot.set_ylabel(strYTitle) + imgSubplot.spines['top'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['bottom'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['left'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['right'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.xaxis.label.set_color(objFigureControl.c_strDetailsColorLetter) + + #Adds light grid for numbers and puts them in the background + imgSubplot.yaxis.grid(True, linestyle='-', which='major', color=objFigureControl.c_strGridLineColor, alpha=objFigureControl.c_dAlpha) + imgSubplot.set_axisbelow(True) + imgSubplot.yaxis.label.set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='x', colors=objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='y', colors=objFigureControl.c_strDetailsColorLetter) + charMarkerEdgeColor = objFigureControl.c_strDetailsColorLetter + + #Make scatter plot + plt.hist(x=lx,histtype='bar',color=strColor) + #Set ticks and title + imgSubplot.set_title(strTitle) + imgSubplot.title.set_color(objFigureControl.c_strDetailsColorLetter) + + #End plot + #Save to a file + imgFigure.savefig(strOutputFigurePath, facecolor=imgFigure.get_facecolor()) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/KMedoids.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/KMedoids.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,208 @@ +## Included from MLPY build 2.2.0 +## Attempts were made to contact Davide Albanese on 08-10-2012 and 09-19-2012 at albanese@fbk.eu + +## This code is written by Davide Albanese, +## (C) 2009 Fondazione Bruno Kessler - Via Santa Croce 77, 38100 Trento, ITALY. + +## 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 . + + +__all__= ['Kmedoids', 'Minkowski'] + + +import numpy as np +import matplotlib +matplotlib.use( "Agg" ) +import mlpy + + +def kmedoids_core(x, med, oth, clust, cost, dist): + """ + * for each mediod m + * for each non-mediod data point n + Swap m and n and compute the total cost of the configuration + Select the configuration with the lowest cost + """ + + d = np.empty((oth.shape[0], med.shape[0]), dtype=float) + med_n = np.empty_like(med) + oth_n = np.empty_like(oth) + idx = np.arange(oth.shape[0]) + + med_cur = med.copy() + oth_cur = oth.copy() + clust_cur = clust.copy() + cost_cur = cost + + for i, m in enumerate(med): + for j, n in enumerate(oth[clust == i]): + + med_n, oth_n = med.copy(), oth.copy() + + med_n[i] = n + tmp = oth_n[clust == i] + tmp[j] = m + oth_n[clust == i] = tmp + + for ii, nn in enumerate(oth_n): + for jj, mm in enumerate(med_n): + d[ii, jj] = dist.compute(x[mm], x[nn]) + + clust_n = np.argmin(d, axis=1) # clusters + cost_n = np.sum(d[idx, clust_n]) # total cost of configuration + + if cost_n <= cost_cur: + med_cur = med_n.copy() + oth_cur = oth_n.copy() + clust_cur = clust_n.copy() + cost_cur = cost_n + + return med_cur, oth_cur, clust_cur, cost_cur + + +class Kmedoids: + """k-medoids algorithm. + """ + + def __init__(self, k, dist, maxloops=100, rs=0): + """Initialize Kmedoids. + + :Parameters: + + k : int + Number of clusters/medoids + dist : class + class with a .compute(x, y) method which + returns a distance + maxloops : int + maximum number of loops + rs : int + random seed + + Example: + + >>> import numpy as np + >>> import mlpy + >>> x = np.array([[ 1. , 1.5], + ... [ 1.1, 1.8], + ... [ 2. , 2.8], + ... [ 3.2, 3.1], + ... [ 3.4, 3.2]]) + >>> dtw = mlpy.Dtw(onlydist=True) + >>> km = mlpy.Kmedoids(k=3, dist=dtw) + >>> km.compute(x) + (array([4, 0, 2]), array([3, 1]), array([0, 1]), 0.072499999999999981) + + Samples 4, 0, 2 are medoids and represent cluster 0, 1, 2 respectively. + + * cluster 0: samples 4 (medoid) and 3 + * cluster 1: samples 0 (medoid) and 1 + * cluster 2: sample 2 (medoid) + """ + + self.__k = k + self.__maxloops = maxloops + self.__rs = rs + self.__dist = dist + + np.random.seed(self.__rs) + + + def compute(self, x): + """Compute Kmedoids. + + :Parameters: + x : ndarray + An 2-dimensional vector (sample x features). + + :Returns: + m : ndarray (1-dimensional vector) + medoids indexes + n : ndarray (1-dimensional vector) + non-medoids indexes + cl : ndarray 1-dimensional vector) + cluster membership for non-medoids. + Groups are in 0, ..., k-1 + co : double + total cost of configuration + """ + + # randomly select k of the n data points as the mediods + idx = np.arange(x.shape[0]) + np.random.shuffle(idx) + med = idx[0:self.__k] + oth = idx[self.__k::] + + # compute distances + d = np.empty((oth.shape[0], med.shape[0]), dtype=float) + for i, n in enumerate(oth): + for j, m in enumerate(med): + d[i, j] = self.__dist.compute(x[m], x[n]) + + # associate each data point to the closest medoid + clust = np.argmin(d, axis=1) + + # total cost of configuration + cost = np.sum(d[np.arange(d.shape[0]), clust]) + + # repeat kmedoids_core until there is no change in the medoid + for l in range(self.__maxloops): + + med_n, oth_n, clust_n, cost_n = kmedoids_core(x, med, oth, clust, cost, self.__dist) + + if (cost_n < cost): + med, oth, clust, cost = med_n, oth_n, clust_n, cost_n + else: + break + + return med, oth, clust, cost + + +class Minkowski: + """ + Computes the Minkowski distance between two vectors ``x`` and ``y``. + + .. math:: + + {||x-y||}_p = (\sum{|x_i - y_i|^p})^{1/p}. + """ + + def __init__(self, p): + """ + Initialize Minkowski class. + + :Parameters: + p : float + The norm of the difference :math:`{||x-y||}_p` + """ + + self.__p = p + + + def compute(self, x, y): + """ + Compute Minkowski distance + + :Parameters: + x : ndarray + An 1-dimensional vector. + y : ndarray + An 1-dimensional vector. + + :Returns: + d : float + The Minkowski distance between vectors ``x`` and ``y`` + """ + + return (abs(x - y)**self.__p).sum() ** (1.0 / self.__p) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/KMedoids.pyc Binary file src/breadcrumbs/src/KMedoids.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/MLPYDistanceAdaptor.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/MLPYDistanceAdaptor.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,76 @@ +""" +Author: Timothy Tickle +Description: Allows KMedoids on a custom metric space. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from scipy.spatial.distance import squareform + +class MLPYDistanceAdaptor: + """ + Allows one to use custom distance metrics with KMedoids in the MLPY package. + """ + + npaMatrix = None + """ + Distance matrix to reference. + """ + + def __init__(self, npaDistanceMatrix, fIsCondensedMatrix): + """ + Constructor requires a matrix of distances, could be condensed or square matrices + + :param npaDistanceMatrix: The distance matrix to be used + :type Numpy array + :param fIsCondensedMatrix: Indicator of the matrix being square (true = condensed; false = square) + :type Boolean + """ + + if fIsCondensedMatrix: + self.npaMatrix = squareform(npaDistanceMatrix) + else: + self.npaMatrix = npaDistanceMatrix + + def compute(self,x,y): + """ + This is the only method required in the interface to MLPY to be a distance metric. + Does NOT want values but positions, the positions will be used for accessing the distance matrix already provided. + + :param x: X position as a array of 1 number + :type Numpy array + :param y: Y position as a array of 1 number + :type Boolean + """ + + if(self.npaMatrix == None): + raise Exception("".join(["MLPYDistanceAdaptor. Attempted to compute distance with out a distance matrix passed in during construction."])) + return self.npaMatrix[x[0],y[0]] diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/MLPYDistanceAdaptor.pyc Binary file src/breadcrumbs/src/MLPYDistanceAdaptor.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/Metric.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/Metric.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,504 @@ +""" +Author: Timothy Tickle +Description: Calculates Metrics. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#Update path +from ConstantsBreadCrumbs import ConstantsBreadCrumbs +import csv +import numpy as np +from types import * +from ValidateData import ValidateData + +#External libraries +from cogent.maths.unifrac.fast_unifrac import fast_unifrac_file +import cogent.maths.stats.alpha_diversity +import scipy.spatial.distance + +class Metric: + """ + Performs ecological measurements. + """ + + #Diversity metrics Alpha + c_strSimpsonDiversity = "SimpsonD" + c_strInvSimpsonDiversity = "InSimpsonD" + c_strChao1Diversity = "Chao1" + + #Diversity metrics Beta + c_strBrayCurtisDissimilarity = "B_Curtis" + c_strUnifracUnweighted = "unifrac_unweighted" + c_strUnifracWeighted = "unifrac_weighted" + + #Additive inverses of beta metrics + c_strInvBrayCurtisDissimilarity = "InB_Curtis" + + #Richness + c_strShannonRichness = "ShannonR" + c_strObservedCount = "Observed_Count" + + #Different alpha diversity metrics + setAlphaDiversities = set(["observed_species","margalef","menhinick", + "dominance","reciprocal_simpson","shannon","equitability","berger_parker_d", + "mcintosh_d","brillouin_d","strong","fisher_alpha","simpson", + "mcintosh_e","heip_e","simpson_e","robbins","michaelis_menten_fit","chao1","ACE"]) + + #Different beta diversity metrics + setBetaDiversities = set(["braycurtis","canberra","chebyshev","cityblock", + "correlation","cosine","euclidean","hamming","sqeuclidean"]) + + #Tested 4 + @staticmethod + def funcGetSimpsonsDiversityIndex(ldSampleTaxaAbundancies=None): + """ + Calculates the Simpsons diversity index as defined as sum(Pi*Pi). + Note***: Assumes that the abundance measurements are already normalized by the total population N. + + :param ldSampleTaxaAbundancies: List of measurements to calculate metric on (a sample). + :type: List of doubles + :return Double: Diversity metric + """ + + #Calculate metric + return sum((ldSampleTaxaAbundancies)*(ldSampleTaxaAbundancies)) + + #Tested 4 + @staticmethod + def funcGetInverseSimpsonsDiversityIndex(ldSampleTaxaAbundancies=None): + """ + Calculates Inverse Simpsons diversity index 1/sum(Pi*Pi). + This is multiplicative inverse which reverses the order of the simpsons diversity index. + Note***: Assumes that the abundance measurements are already normalized by the total population N. + + :param ldSampleTaxaAbundancies: List of measurements to calculate metric on (a sample). + :type: List of doubles + :return Double: Diversity metric + """ + + simpsons = Metric.funcGetSimpsonsDiversityIndex(ldSampleTaxaAbundancies) + #If simpsons is false return false, else return inverse + if not simpsons: + return False + return 1.0/simpsons + + #Tested 4 + @staticmethod + def funcGetShannonRichnessIndex(ldSampleTaxaAbundancies=None): + """ + Calculates the Shannon richness index. + Note***: Assumes that the abundance measurements are already normalized by the total population N. + If not normalized, include N in the parameter tempTotalN and it will be. + This is in base exp(1) like the default R Vegan package. Cogent is by defaul in bits (base=2) + Both options are here for your use. See Metric.funcGetAlphaDiversity() to access cogent + + :param ldSampleTaxaAbundancies: List of measurements to calculate metric on (a sample). + :type: List of doubles + :return Double: Richness metric + """ + + #Calculate metric + ldSampleTaxaAbundancies = ldSampleTaxaAbundancies[np.where(ldSampleTaxaAbundancies != 0)] + tempIntermediateNumber = sum(ldSampleTaxaAbundancies*(np.log(ldSampleTaxaAbundancies))) + if(tempIntermediateNumber == 0.0): + return 0.0 + return -1 * tempIntermediateNumber + + #Test 3 + @staticmethod + def funcGetChao1DiversityIndex(ldSampleTaxaAbundancies=None, fCorrectForBias=False): + """ + Calculates the Chao1 diversity index. + Note***: Not normalized by abundance. + + :param ldSampleTaxaAbundancies: List of measurements to calculate metric on (a sample). + :type: List of doubles + :param fCorrectForBias: Indicator to use bias correction. + :type: Boolean False indicates uncorrected for bias (uncorrected = Chao 1984, corrected = Chao 1987, Eq. 2) + :return Double: Diversity metric + """ + #If not counts return false + if [num for num in ldSampleTaxaAbundancies if((num<1) and (not num==0))]: return False + + #Observed = total number of species observed in all samples pooled + totalObservedSpecies = len(ldSampleTaxaAbundancies)-len(ldSampleTaxaAbundancies[ldSampleTaxaAbundancies == 0]) + + #Singles = number of species that occur in exactly 1 sample + singlesObserved = len(ldSampleTaxaAbundancies[ldSampleTaxaAbundancies == 1.0]) + + #Doubles = number of species that occue in exactly 2 samples + doublesObserved = len(ldSampleTaxaAbundancies[ldSampleTaxaAbundancies == 2.0]) + + #If singles or doubles = 0, return observations so that a divided by zero error does not occur + if((singlesObserved == 0) or (doublesObserved == 0)): + return totalObservedSpecies + + #Calculate metric + if fCorrectForBias: + return cogent.maths.stats.alpha_diversity.chao1_bias_corrected(observed = totalObservedSpecies, singles = singlesObserved, doubles = doublesObserved) + else: + return cogent.maths.stats.alpha_diversity.chao1_uncorrected(observed = totalObservedSpecies, singles = singlesObserved, doubles = doublesObserved) + + #Test 3 + @staticmethod + def funcGetObservedCount(ldSampleAbundances, dThreshold = 0.0): + """ + Count how many bugs / features have a value of greater than 0 or the threshold given. + Expects a vector of abundances. + ****Do not normalize data if using the threshold. + + :param ldSampleAbundances: List of measurements to calculate metric on (a sample). + :type: List of doubles + :param dThreshold: The lowest number the measurement can be to be counted as an observation. + :type: Double + :return Count: Number of features observed in a sample. + """ + + return sum([1 for observation in ldSampleAbundances if observation > dThreshold]) + + #Test Cases 6 + @staticmethod + def funcGetAlphaDiversity(liCounts,strMetric): + """ + Passes counts to cogent for an alpha diversity metric. + setAlphaDiversities are the names supported + + :param liCount: List of counts to calculate metric on (a sample). + :type: List of ints + :return Diversity: Double diversity metric. + """ + + return getattr(cogent.maths.stats.alpha_diversity,strMetric)(liCounts) + + #Happy path tested 1 + @staticmethod + def funcGetDissimilarity(ldSampleTaxaAbundancies, funcDistanceFunction): + """ + Calculates the distance between samples given a function. + + If you have 5 rows (labeled r1,r2,r3,r4,r5) the vector are the distances in this order. + condensed form = [d(r1,r2), d(r1,r3), d(r1,r4), d(r1,r5), d(r2,r3), d(r2,r4), d(r2,r5), d(r3,r4), d(r3,r5), d(r4,r5)]. + Note***: Assumes that the abundance measurements are already normalized by the total population N. + + :param ldSampleTaxaAbundancies: + :type: List of doubles + :param funcDistanceFunction: Distance function used to calculate distances + :type: Function + :return Double: Dissimilarity metric + """ + + #Calculate metric + try: + return scipy.spatial.distance.pdist(ldSampleTaxaAbundancies, funcDistanceFunction) + except ValueError as error: + print "".join(["Metric.funcGetDissimilarity. Error=",str(error)]) + return False + + #Test case 1 + @staticmethod + def funcGetDissimilarityByName(ldSampleTaxaAbundancies, strMetric): + """ + Calculates beta-diversity metrics between lists of abundances + setBetaDiversities are the names supported + + :param ldSampleTaxaAbundancies: + :type: List of doubles + :param strMetric: Name of the distance function used to calculate distances + :type: String + :return list double: Dissimilarity metrics between each sample + """ + + return scipy.spatial.distance.pdist(ldSampleTaxaAbundancies,strMetric) + + #Test 3 + @staticmethod + def funcGetBrayCurtisDissimilarity(ldSampleTaxaAbundancies): + """ + Calculates the BrayCurtis Beta dissimilarity index. + d(u,v)=sum(abs(row1-row2))/sum(row1+row2). + This is scale invariant. + If you have 5 rows (labeled r1,r2,r3,r4,r5) the vector are the distances in this order. + condensed form = [d(r1,r2), d(r1,r3), d(r1,r4), d(r1,r5), d(r2,r3), d(r2,r4), d(r2,r5), d(r3,r4), d(r3,r5), d(r4,r5)]. + Note***: Assumes that the abundance measurements are already normalized by the total population N. + + :param ldSampleTaxaAbundancies: + :type: List of doubles + :return Double Matrix: Dissimilarity metric + """ + + #Calculate metric + try: + return scipy.spatial.distance.pdist(X=ldSampleTaxaAbundancies, metric='braycurtis') + except ValueError as error: + print "".join(["Metric.getBrayCurtisDissimilarity. Error=",str(error)]) + return False + + #Test 3 + @staticmethod + def funcGetInverseBrayCurtisDissimilarity(ldSampleTaxaAbundancies): + """ + Calculates 1 - the BrayCurtis Beta dissimilarity index. + d(u,v)=1-(sum(abs(row1-row2))/sum(row1+row2)). + This is scale invariant and ranges between 0 and 1. + If you have 5 rows (labeled r1,r2,r3,r4,r5) the vector are the distances in this order. + condensed form = [d(r1,r2), d(r1,r3), d(r1,r4), d(r1,r5), d(r2,r3), d(r2,r4), d(r2,r5), d(r3,r4), d(r3,r5), d(r4,r5)]. + Note***: Assumes that the abundance measurements are already normalized by the total population N. + + :param ldSampleTaxaAbundancies: An np.array of samples (rows) x measurements (columns) in which distance is measured between rows + :type: List List of doubles + :return Double Matrix: 1 - Bray-Curtis dissimilarity. + """ + + bcValue = Metric.funcGetBrayCurtisDissimilarity(ldSampleTaxaAbundancies = ldSampleTaxaAbundancies) + if not type(bcValue) is BooleanType: + return 1.0-bcValue + return False + + #Test cases 8 + @staticmethod + def funcGetUnifracDistance(istrmTree,istrmEnvr,lsSampleOrder=None,fWeighted=True): + """ + Gets a unifrac distance from files/filestreams. + + :param istrmTree: File path or stream which is a Newick format file + :type: String of file stream + :param istrmEnvr: File path or stream which is a Newick format file + :type: String of file stream + """ + npaDist, lsSampleNames = fast_unifrac_file(open(istrmTree,"r") if isinstance(istrmTree, str) else istrmTree, + open(istrmEnvr,"r") if isinstance(istrmEnvr, str) else istrmEnvr, weighted=fWeighted).get("distance_matrix",False) + + #Was trying to avoid preallocating a matrix but if you only need a subset of the samples then it + #is simpler to preallocate so this is what I am doing but making a condensed matrix and not a full matrix + + #Dictionary to translate the current order of the samples to what is expected if given an input order + if lsSampleOrder: + #{NewOrder:OriginalOrder} way to convert from old to new sample location + dictTranslate = dict([[lsSampleOrder.index(sSampleName),lsSampleNames.index(sSampleName)] for sSampleName in lsSampleNames if sSampleName in lsSampleOrder]) + + #Check to make sure all samples requested were found + if not len(dictTranslate.keys()) == len(lsSampleOrder): + print "Metric.funcGetUnifracDistance. Error= The some or all sample names given (lsSampleOrder) were not contained in the matrix." + return False + + #Length of data + iLengthOfData = len(lsSampleOrder) + + #Preallocate matrix and shuffle + mtrxData = np.zeros(shape=(iLengthOfData,iLengthOfData)) + for x in xrange(iLengthOfData): + for y in xrange(iLengthOfData): + mtrxData[x,y] = npaDist[dictTranslate[x],dictTranslate[y]] + npaDist = mtrxData + + lsSampleNames = lsSampleOrder + + #If no sample order is given, condense the matrix and return + return (scipy.spatial.distance.squareform(npaDist),lsSampleNames) + + + #Test 7 + @staticmethod + def funcGetAlphaMetric(ldAbundancies, strMetric): + """ + Get alpha abundance of the metric for the vector. + Note: Shannon is measured with base 2 ("shannon") or base exp(1) (Metric.c_strShannonRichness) depending which method is called. + + :param ldAbundancies: List of values to compute metric (a sample). + :type: List List of doubles. + :param strMetric: The metric to measure. + :type: String Metric name (Use from constants above). + :return Double: Metric specified by strMetric derived from ldAbundancies. + """ + + if(strMetric == Metric.c_strShannonRichness): + return Metric.funcGetShannonRichnessIndex(ldSampleTaxaAbundancies=ldAbundancies) + elif(strMetric == Metric.c_strSimpsonDiversity): + return Metric.funcGetSimpsonsDiversityIndex(ldSampleTaxaAbundancies=ldAbundancies) + elif(strMetric == Metric.c_strInvSimpsonDiversity): + return Metric.funcGetInverseSimpsonsDiversityIndex(ldSampleTaxaAbundancies=ldAbundancies) + elif(strMetric == Metric.c_strObservedCount): + return Metric.funcGetObservedCount(ldSampleAbundances=ldAbundancies) + #Chao1 Needs NOT Normalized Abundance (Counts) + elif(strMetric == Metric.c_strChao1Diversity): + return Metric.funcGetChao1DiversityIndex(ldSampleTaxaAbundancies=ldAbundancies) + elif(strMetric in Metric.setAlphaDiversities): + return Metric.funcGetAlphaDiversity(liCounts=ldAbundancies, strMetric=strMetric) + else: + return False + + #Test 5 + @staticmethod + def funcBuildAlphaMetricsMatrix(npaSampleAbundance = None, lsSampleNames = None, lsDiversityMetricAlpha = None): + """ + Build a matrix of alpha diversity metrics for each sample + Row = metric, column = sample + + :param npaSampleAbundance: Observations (Taxa (row) x sample (column)) + :type: Numpy Array + :param lsSampleNames: List of sample names of samples to measure (do not include the taxa id column name or other column names which should not be read). + :type: List of strings Strings being samples to measure from the npaSampleAbundance. + :param lsDiversityMetricAlpha: List of diversity metrics to use in measuring. + :type: List of strings Strings being metrics to derived from the indicated samples. + :return List of List of doubles: Each internal list is a list of (floats) indicating a specific metric measurement method measuring multiple samples + [[metric1-sample1, metric1-sample2, metric1-sample3],[metric1-sample1, metric1-sample2, metric1-sample3]] + """ + + if not ValidateData.funcIsValidList(lsDiversityMetricAlpha): + lsDiversityMetricAlpha = [lsDiversityMetricAlpha] + + #Get amount of metrics + metricsCount = len(lsDiversityMetricAlpha) + + #Create return + returnMetricsMatrixRet = [[] for index in lsDiversityMetricAlpha] + + #For each sample get all metrics + #Place in list of lists + #[[metric1-sample1, metric1-sample2, metric1-sample3],[metric1-sample1, metric1-sample2, metric1-sample3]] + for sample in lsSampleNames: + sampleAbundance = npaSampleAbundance[sample] + for metricIndex in xrange(0,metricsCount): + returnMetricsMatrixRet[metricIndex].append(Metric.funcGetAlphaMetric(ldAbundancies = sampleAbundance, strMetric = lsDiversityMetricAlpha[metricIndex])) + return returnMetricsMatrixRet + + #Testing 6 cases + @staticmethod + def funcGetBetaMetric(npadAbundancies=None, sMetric=None, istrmTree=None, istrmEnvr=None, lsSampleOrder=None, fAdditiveInverse = False): + """ + Takes a matrix of values and returns a beta metric matrix. The metric returned is indicated by name (sMetric). + + :param npadAbundancies: Numpy array of sample abundances to measure against. + :type: Numpy Array Numpy array where row=samples and columns = features. + :param sMetric: String name of beta metric. Possibilities are listed in microPITA. + :type: String String name of beta metric. Possibilities are listed in microPITA. + :return Double: Measurement indicated by metric for given abundance list + """ + + if sMetric == Metric.c_strBrayCurtisDissimilarity: + mtrxDistance = Metric.funcGetBrayCurtisDissimilarity(ldSampleTaxaAbundancies=npadAbundancies) + elif sMetric == Metric.c_strInvBrayCurtisDissimilarity: + mtrxDistance = Metric.funcGetInverseBrayCurtisDissimilarity(ldSampleTaxaAbundancies=npadAbundancies) + elif sMetric in Metric.setBetaDiversities: + mtrxDistance = Metric.funcGetDissimilarityByName(ldSampleTaxaAbundancies=npadAbundancies, strMetric=sMetric) + elif sMetric == Metric.c_strUnifracUnweighted: + mtrxDistance = Metric.funcGetUnifracDistance(istrmTree=istrmTree,istrmEnvr=istrmEnvr,lsSampleOrder=lsSampleOrder,fWeighted=False) +# mtrxDistance = xReturn[0] if not type(xReturn) is BooleanType else xReturn + elif sMetric == Metric.c_strUnifracWeighted: + mtrxDistance = Metric.funcGetUnifracDistance(istrmTree=istrmTree,istrmEnvr=istrmEnvr,lsSampleOrder=lsSampleOrder,fWeighted=True) +# mtrxDistance = xReturn[0] if not type(xReturn) is BooleanType else xReturn + else: + mtrxDistance = False + if fAdditiveInverse and not type(mtrxDistance) is BooleanType: + if sMetric in [Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]: + mtrxDistance = (1.0 - mtrxDistance[0],mtrxDistance[1]) + else: + mtrxDistance = 1.0 - mtrxDistance + return mtrxDistance + + #Test Cases 11 + @staticmethod + def funcReadMatrixFile(istmMatrixFile, lsSampleOrder=None): + """ + Reads in a file with a precalculated beta-diversty matrix. + + :param istmMatrixFile: File with beta-diversity matrix + :type: FileStream of String file path + """ + + #Read in data + f = csv.reader(open(istmMatrixFile,"r") if isinstance(istmMatrixFile, str) else istmMatrixFile, delimiter=ConstantsBreadCrumbs.c_matrixFileDelim ) + + #Get header + try: + lsHeader = f.next() + except StopIteration: + return (False,False) + lsHeaderReducedToSamples = [sHeader for sHeader in lsHeader if sHeader in lsSampleOrder] if lsSampleOrder else lsHeader[1:] + + #If no sample ordering is given, set the ordering to what is in the file + if not lsSampleOrder: + lsSampleOrder = lsHeaderReducedToSamples + + #Preallocate matrix + mtrxData = np.zeros(shape=(len(lsSampleOrder),len(lsSampleOrder))) + + #Make sure all samples requested are in the file + if(not len(lsSampleOrder) == len(lsHeaderReducedToSamples)): return False + + for lsLine in f: + if lsLine[0] in lsSampleOrder: + iRowIndex = lsSampleOrder.index(lsLine[0]) + + for i in xrange(1,len(lsSampleOrder)): + iColumnIndexComing = lsHeader.index(lsSampleOrder[i]) + iColumnIndexGoing = lsSampleOrder.index(lsSampleOrder[i]) + mtrxData[iRowIndex,iColumnIndexGoing] = lsLine[iColumnIndexComing] + mtrxData[iColumnIndexGoing,iRowIndex] = lsLine[iColumnIndexComing] + tpleMData = mtrxData.shape + mtrxData = mtrxData if any(sum(ld)>0 for ld in mtrxData) or ((tpleMData[0]==1) and (tpleMData[1]==1)) else [] + return (mtrxData,lsSampleOrder) + + #Test cases 2 + @staticmethod + def funcWriteMatrixFile(mtrxMatrix, ostmMatrixFile, lsSampleNames=None): + """ + Writes a square matrix to file. + + :param mtrxMatrix: Matrix to write to file + :type: Numpy array + :lsSampleNames: The names of the samples in the order of the matrix + :type: List of strings + :ostmBetaMatrixFile: File to write to + :type: String or file stream + """ + + if not sum(mtrxMatrix.shape)>0 or not ostmMatrixFile: + return False + + #Check to make sure the sample names are the correct length + tpleiShape = mtrxMatrix.shape + if not lsSampleNames: + lsSampleNames = range(tpleiShape[0]) + if not(len(lsSampleNames) == tpleiShape[0]): + print "".join(["Metric.funcWriteMatrixFile. Error= Length of sample names ("+str(len(lsSampleNames))+") and matrix ("+str(mtrxMatrix.shape)+") not equal."]) + return False + + #Write to file + ostmOut = csv.writer(open(ostmMatrixFile,"w") if isinstance(ostmMatrixFile,str) else ostmMatrixFile, delimiter=ConstantsBreadCrumbs.c_matrixFileDelim ) + + #Add the additional space at the beginning of the sample names to represent the id row/column + lsSampleNames = [""]+list(lsSampleNames) + + #Write header and each row to file + ostmOut.writerow(lsSampleNames) + [ostmOut.writerow([lsSampleNames[iIndex+1]]+mtrxMatrix[iIndex,].tolist()) for iIndex in xrange(tpleiShape[0])] + return True diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/Metric.pyc Binary file src/breadcrumbs/src/Metric.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/Ordination.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/Ordination.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,94 @@ +""" +Author: Timothy Tickle +Description: Base class for ordination plots. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2013" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +import AbundanceTable +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +import matplotlib.cm as cm +from matplotlib import pyplot as plt +from UtilityMath import UtilityMath +from ValidateData import ValidateData + +class Ordination: + """ + Base class for ordination methods and plots. + """ + + def __init__(self): + # Rows = Samples + self.dataMatrix = None + self.isRawData = None + self.lsIDs = [] + self.dataProcessed = None + + #Happy path tested + def loadData(self, xData, fIsRawData): + """ + Loads data into the object (given a matrix or an abundance table) + Data can be the Abundance Table to be converted to a distance matrix or a distance matrix + If it is the AbundanceTable, indicate that it is rawData (tempIsRawData=True) + If it is the distance matrix already generated indicate (tempIsRawData=False) + and no conversion will occur in subsequent methods. + + :params xData: AbundanceTable or Distance matrix . Taxa (columns) by samples (rows)(lists) + :type: AbundanceTable or DistanceMatrix + :param fIsRawData: Indicates if the xData is an AbudanceTable (True) or distance matrix (False; numpy array) + :type: boolean + :return boolean: indicator of success (True=Was able to load data) + """ + + if fIsRawData: + #Read in the file data to a numpy array. + #Samples (column) by Taxa (rows)(lists) without the column + data = xData.funcToArray() + if data==None: + print("Ordination:loadData::Error when converting AbundanceTable to Array, did not perform ordination.") + return False + + #Transpose data to be Taxa (columns) by samples (rows)(lists) + data = UtilityMath.funcTransposeDataMatrix(data,fRemoveAdornments=False) + if(ValidateData.funcIsFalse(data)): + print("Ordination:loadData::Error when transposing data file, did not perform ordination.") + return False + else: + self.dataMatrix=data + self.isRawData=fIsRawData + self.lsIDs=xData.funcGetMetadata(xData.funcGetIDMetadataName()) + + #Otherwise load the data directly as passed. + else: + self.dataMatrix=xData + self.isRawData=fIsRawData + return True + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/PCoA.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/PCoA.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,809 @@ +""" +Author: Timothy Tickle +Description: Perfroms and plots Principle Coordinates Analysis. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +from cogent.cluster.nmds import NMDS +import csv +import math +import matplotlib.cm as cm +from Metric import Metric +import numpy as np +from scipy.spatial.distance import squareform +from scipy.stats.stats import spearmanr +from Utility import Utility +from UtilityMath import UtilityMath +from ValidateData import ValidateData +from matplotlib import pyplot as plt + +class PCoA: + """ + Class to Run Principle Coordinates Analysis. + + To run PCoA first load the AbundanceTable or distance matrix using the "load" method, + then use the "run" method to derive points, and then use "plot" to plot the graph. + The process is structured in this way so that data is read once but can be transformed to different + distance matricies and after analysis can be plotted with multiple sample highlighting. + One can always reload or rerun data by calling the appropriate function. + + Supported beta diversity metrics include "braycurtis","canberra","chebyshev","cityblock","correlation", + "cosine","euclidean","hamming","sqeuclidean",unifrac_unweighted","unifrac_weighted" + """ + + #Supported distance metrics + c_BRAY_CURTIS="B_Curtis" + c_SPEARMAN="spearman" + + #Holds the data Matrix + dataMatrix=None + #Indicates if the data matrix is raw data (True) or a distance matrix (False) + isRawData=None + # Holds current matrix ids + lsIDs = None + + #Current pcoa object + pcoa = None + + #Holds the most recently successful distance metric + strRecentMetric = None + + #Current dimensions + _iDimensions = 2 + + #Get plot colors + objFigureControl = ConstantsFiguresBreadCrumbs() + + #Forced X Axis + ldForcedXAxis = None + + #Indices for the plot group dictionary + c_iXPointIndex = 0 + c_iYPointIndex = 1 + c_iColorIndex = 2 + c_iMarkerIndex = 3 + c_iAlphaIndex = 4 + c_iLabelIndex = 5 + c_iShapeIndex = 6 + c_iEdgeColorIndex = 7 + c_strTiesKey = "Ties" + + #Happy path tested + def loadData(self, xData, fIsRawData): + """ + Loads data into PCoA (given the matrix or an abundance table) + Data can be the Abundance Table to be converted to a distance matrix or a distance matrix + If it is the AbundanceTable, indicate that it is rawData (tempIsRawData=True) + If it is the distance matrix already generated indicate (tempIsRawData=False) + and no conversion will occur in subsequent methods. + + :params xData: AbundanceTable or Distance matrix . Taxa (columns) by samples (rows)(lists) + :type: AbundanceTable or DistanceMatrix + :param fIsRawData: Indicates if the xData is an AbudanceTable (True) or distance matrix (False; numpy array) + :type: boolean + :return boolean: indicator of success (True=Was able to load data) + """ + + if fIsRawData: + #Read in the file data to a numpy array. + #Samples (column) by Taxa (rows)(lists) without the column + data = xData.funcToArray() + if data==None: + print("PCoA:loadData::Error when converting AbundanceTable to Array, did not perform PCoA.") + return False + + #Transpose data to be Taxa (columns) by samples (rows)(lists) + data = UtilityMath.funcTransposeDataMatrix(data,fRemoveAdornments=False) + if(ValidateData.funcIsFalse(data)): + print("PCoA:loadData::Error when transposing data file, did not perform PCoA.") + return False + else: + self.dataMatrix=data + self.isRawData=fIsRawData + self.lsIDs=xData.funcGetMetadata(xData.funcGetIDMetadataName()) + + #Otherwise load the data directly as passed. + else: + self.dataMatrix=xData + self.isRawData=fIsRawData + return True + + def run(self, tempDistanceMetric=None, iDims=2, strDistanceMatrixFile=None, istrmTree=None, istrmEnvr=None): + """ + Runs analysis on loaded data. + + :param tempDistanceMetric: The name of the distance metric to use when performing PCoA. + None indicates a distance matrix was already given when loading and will be used. + Supports "braycurtis","canberra","chebyshev","cityblock","correlation", + "cosine","euclidean","hamming","sqeuclidean",unifrac_unweighted","unifrac_weighted" + :type: String Distance matrix name + :param iDims: How many dimension to plot the PCoA graphs. + (This can be minimally 2; all combinations of dimensions are plotted). + iDims start with 1 (not index-based). + :type: Integer Positive integer 2 or greater. + :param strDistanceMatrixFile: If the underlying distance matrix should be output, this is the file to output to. + :type: String Output file for distances of None for indicating it shoudl not be done. + :param istrmTree: One of two files needed for unifrac calculations, this is the phylogeny of the features. + :type: String Path to file + :param istrmEnvr: One of two files needed for unifrac calculations, this is the environment file for the features. + :type: String Path to file + :return boolean: Indicator of success (True) + """ + + if iDims > 1: + self._iDimensions = iDims + + #If distance metric is none, check to see if the matrix is a distance matrix + #If so, run NMDS on the distance matrix + #Otherwise return a false and do not run + if(tempDistanceMetric==None): + if(ValidateData.funcIsTrue(self.isRawData)): + print("PCoA:run::Error, no distance metric was specified but the previous load was not of a distance matrix.") + return False + elif(ValidateData.funcIsFalse(self.isRawData)): + self.pcoa = NMDS(dataMatrix, verbosity=0) + return True + + #Make sure the distance metric was a valid string type + if(not ValidateData.funcIsValidString(tempDistanceMetric)): + print("PCoA:run::Error, distance metric was not a valid string type.") + return False + + #Supported distances + + distanceMatrix = None + if(tempDistanceMetric==self.c_SPEARMAN): + distanceMatrix = Metric().funcGetDissimilarity(ldSampleTaxaAbundancies=self.dataMatrix, funcDistanceFunction=lambda u,v: spearmanr(u,v)[0]) + if(tempDistanceMetric in [Metric.c_strUnifracUnweighted,Metric.c_strUnifracWeighted]): + distanceMatrix,lsLabels = Metric().funcGetBetaMetric(sMetric=tempDistanceMetric, istrmTree=istrmTree, istrmEnvr=istrmEnvr) + self.lsIDs = lsLabels + else: + distanceMatrix = Metric().funcGetBetaMetric(npadAbundancies=self.dataMatrix, sMetric=tempDistanceMetric) + if(ValidateData.funcIsFalse(distanceMatrix)): + print "PCoA:run::Error, when generating distance matrix." + return False + + # Make squareform + distanceMatrix = squareform(distanceMatrix) + + # Writes distance measures if needed. + if strDistanceMatrixFile: + csvrDistance = csv.writer(open(strDistanceMatrixFile, 'w')) + if self.lsIDs: + csvrDistance.writerow(["ID"]+self.lsIDs) + + for x in xrange(distanceMatrix.shape[0]): + strId = [self.lsIDs[x]] if self.lsIDs else [] + csvrDistance.writerow(strId+distanceMatrix[x].tolist()) + + self.pcoa = NMDS(distanceMatrix, dimension=max(self._iDimensions,2), verbosity=0) + self.strRecentMetric = tempDistanceMetric + return True + + #TODO Test + def funcGetCoordinates(self): + return(self.pcoa.getPoints()) + + #TODO Test + def funcGetIDs(self): + return(self.lsIDs) + + #Happy path tested + def plot(self, tempPlotName="PCOA.png", tempColorGrouping=None, tempShape=None, tempLabels=None, tempShapeSize=None, tempAlpha = 1.0, tempLegendLocation="upper right", tempInvert=False, iDim1 = 1, iDim2 = 2): + """ + Plots the provided data by the given distance matrix in the file. + All lists should be in order in relation to each other. + + :param tempPlotName: Path of file to save figure. + :type: String File path. + :param tempColorGrouping: Colors for markers. + If you want a marker with multiple colors (piewedges) for that marker give a list in the list of colors. + For example ['r','r','r',['r','g','b']] This would make 3 red markers and 1 split into 3 wedges (red, green, and blue). + This is only possible if you are using circle shapes ('o') or square shapes ('s'). + :type: Character or list of characters: Characters should be useable by matplotlib as a color. + :param tempShape: Marker shapes. If you want to specify one shape for all markers then just pass a char/str for the marker not a list. + :type: Character or list of characters. Characters should be useable by matplotlib as shapes. + :param tempLabels: Labels associated with the coloring. Should be consistent with tempColorGrouping (both should be strings or lists of equal length). + :type: String or list of Strings. + :param tempShapeSize: Sizes of markers (points). If no list is given, all markers are given the same size. + :type: Integer of list of integers: 1 or greater. + :param tempAlpha: Value between 0.0 and 1.0 (0.0 being completely transparent, 1.0 being opaque). + :type: Float 0.0-1.0. + :param tempLegendLocation: Indicates where to put the legend. + :type: String Either "upper right", "lower right", "upper left", "lower left". + :param tempInvert: Allows the inverting of the figure. + :type: boolean True inverts. + :param iDim1: First dimension to plot. + :type: Integer Greater than 1. + :param iDim2: Second dimension to plot. + :type: Integer Greater than 1. + :return boolean: Indicator of success (True) + """ + + if(not self.pcoa == None): + + #Get point count + iDimensionOne = max(0,min(self._iDimensions-2, iDim1-1)) + iDimensionTwo = max(1,min(self._iDimensions-1, iDim2-1)) + adPoints = self.pcoa.getPoints() + + #This is 1-stress which is the amount of variance not explained by all dimensions + #There is no precent variance, so I am trying this as a substitute + dPercentVariance = int((1.0-self.pcoa.getStress())*100) + ldXPoints = list(adPoints[:,iDimensionOne]) + if not (self.ldForcedXAxis == None): + ldXPoints = self.ldForcedXAxis + ldYPoints = list(adPoints[:,iDimensionTwo]) + iPointCount = len(ldXPoints) + + #Get plot object + imgFigure = plt.figure() + self.objFigureControl.invertColors(fInvert=tempInvert) + + #Manage Labels + if tempLabels is None: + tempLabels = [self.objFigureControl.c_strPCoALabelDefault] * iPointCount + elif(ValidateData.funcIsValidList(tempLabels)): + if not len(tempLabels) == iPointCount: + print "PCoA::plot:Error, the list of labels was given but was not the same length as the points so nothing was plotted." + print "PCoA::plot:tempLabels=", tempLabels + print "PCoA::plot:Label list length=", len(tempLabels) + print "PCoA::plot:iPointCount=", iPointCount + return False + elif ValidateData.funcIsValidString(tempLabels): + tempLabels = [tempLabels] * iPointCount + else: + print "PCoA::plot:tempLabels was of an unexpected type. Expecting None, List, string, or char." + print tempLabels + return False + + #Manage Colors + if tempColorGrouping is None: + tempColorGrouping = [self.objFigureControl.c_cPCoAColorDefault] * iPointCount + elif(ValidateData.funcIsValidList(tempColorGrouping)): + if not len(tempColorGrouping) == iPointCount: + print "PCoA::plot:Error, the list of colors was given but was not the same length as the points so nothing was plotted." + print "PCoA::plot:tempColorGrouping=", tempColorGrouping + print "PCoA::plot:Color list length=", len(tempColorGrouping) + print "PCoA::plot:iPointCount=", iPointCount + return False + elif ValidateData.funcIsValidString(tempColorGrouping): + tempColorGrouping = [tempColorGrouping] * iPointCount + else: + print "PCoA::plot:tempColorGrouping was of an unexpected type. Expecting None, List, string, or char." + print tempColorGrouping + return False + + #Manage tempShape + if tempShape is None: + tempShape = [self.objFigureControl.c_cPCoAShapeDefault] * iPointCount + elif(ValidateData.funcIsValidList(tempShape)): + if not len(tempShape) == iPointCount: + print "PCoA::plot:Error, the list of shapes was given but was not the same length as the points so nothing was plotted." + print "PCoA::plot:tempShape=", tempShape + print "PCoA::plot:Shape list length=", len(tempShape) + print "PCoA::plot:iPointCount=", iPointCount + return False + elif ValidateData.funcIsValidString(tempShape): + tempShape = [tempShape] * iPointCount + else: + print("PCoA::plot:tempShape was of an unexpected type. Expecting None, List, string, or char.") + print tempShape + return False + + #Manage tempShapeSize + if tempShapeSize is None: + tempShapeSize = [self.objFigureControl.c_cPCoASizeDefault] * iPointCount + elif(ValidateData.funcIsValidList(tempShapeSize)): + if not len(tempShapeSize) == iPointCount: + print "PCoA::plot:Error, the list of sizes was given but was not the same length as the points so nothing was plotted." + print "PCoA::plot:tempShapeSize=", tempShapeSize + print "PCoA::plot:Size list length=", len(tempShapeSize) + print "PCoA::plot:iPointCount=", iPointCount + return False + elif ValidateData.funcIsValidInteger(tempShapeSize): + tempShapeSize = [tempShapeSize] * iPointCount + else: + print "PCoA::plot:tempShapeSize was of an unexpected type. Expecting None, List, string, or char." + print tempShapeSize + return False + + #Color/Invert figure + imgFigure.set_facecolor(self.objFigureControl.c_strBackgroundColorWord) + imgSubplot = imgFigure.add_subplot(111,axisbg=self.objFigureControl.c_strBackgroundColorLetter) + imgSubplot.set_xlabel("Dimension "+str(iDimensionOne+1)+" (1-Stress = "+str(dPercentVariance)+"% )") + imgSubplot.set_ylabel("Dimension "+str(iDimensionTwo+1)) + imgSubplot.spines['top'].set_color(self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['bottom'].set_color(self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['left'].set_color(self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['right'].set_color(self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.xaxis.label.set_color(self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.yaxis.label.set_color(self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='x', colors=self.objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='y', colors=self.objFigureControl.c_strDetailsColorLetter) + charMarkerEdgeColor = self.objFigureControl.c_strDetailsColorLetter + + #If given a list of colors, each color will be plotted individually stratified by shape + #Plot colors seperately so the legend will pick up on the labels and make a legend + if(ValidateData.funcIsValidList(tempColorGrouping)): + if len(tempColorGrouping) == iPointCount: + + #Dictionary to hold plotting groups + #Logistical to plot points as layers in an intelligent fashion + #{CountofPoints: [[plot info list]]} The list happends so ties can occur in the key + dictPlotGroups = dict() + + #Check for lists in the list which indicate the need to plot pie charts + lfAreLists = [ValidateData.funcIsValidList(objColor) for objIndex, objColor in enumerate(tempColorGrouping)] + + #Pie chart data seperated out + lsColorsPieCharts = None + lcShapesPieCharts = None + lsLabelsPieCharts = None + lsSizesPieCharts = None + ldXPointsPieCharts = None + ldYPointsPieCharts = None + + #Split out piechart data + if sum(lfAreLists) > 0: + #Get lists of index that are and are not lists + liAreLists = [] + liAreNotLists = [] + curIndex = 0 + for fIsList in lfAreLists: + if fIsList: liAreLists.append(curIndex) + else: liAreNotLists.append(curIndex) + curIndex = curIndex + 1 + + lsColorsPieCharts = Utility.reduceList(tempColorGrouping, liAreLists) + tempColorGrouping = Utility.reduceList(tempColorGrouping, liAreNotLists) + + #Split out shapes + lcShapesPieCharts = Utility.reduceList(tempShape, liAreLists) + tempShape = Utility.reduceList(tempShape, liAreNotLists) + + #Split out labels + lsLabelsPieCharts = Utility.reduceList(tempLabels, liAreLists) + tempLabels = Utility.reduceList(tempLabels, liAreNotLists) + + #Split out sizes + lsSizesPieCharts = Utility.reduceList(tempShapeSize, liAreLists) + tempShapeSize = Utility.reduceList(tempShapeSize, liAreNotLists) + + #Split out xpoints + ldXPointsPieCharts = Utility.reduceList(ldXPoints, liAreLists) + ldXPoints = Utility.reduceList(ldXPoints, liAreNotLists) + + #Split out ypoints + ldYPointsPieCharts = Utility.reduceList(ldYPoints, liAreLists) + ldYPoints = Utility.reduceList(ldYPoints, liAreNotLists) + + #Get unique colors and plot each individually + acharUniqueColors = list(set(tempColorGrouping)) + for iColorIndex in xrange(0,len(acharUniqueColors)): + #Get the color + charColor = acharUniqueColors[iColorIndex] + + #Get indices of colors + aiColorPointPositions = Utility.getIndices(tempColorGrouping,charColor) + + #Reduce the labels by color + acharLabelsByColor = Utility.reduceList(tempLabels,aiColorPointPositions) + + #Reduces sizes to indices if a list + reducedSizes = tempShapeSize + #Reduce sizes if a list + if(ValidateData.funcIsValidList(reducedSizes)): + reducedSizes = Utility.reduceList(reducedSizes,aiColorPointPositions) + + #Reduce to the current color grouping + aiXPoints = Utility.reduceList(ldXPoints,aiColorPointPositions) + aiYPoints = Utility.reduceList(ldYPoints,aiColorPointPositions) + + #There are 3 options for shapes which are checked in this order. + #1. 1 shape character is given which is used for all markers + #2. A list is given of marker characters or lists of decimals which will be used to make pie chart markers + #This is handled after the rest this block of code + #3. A list of char are given each indicating the marker for a sample + #If the shapes are not a list plot + #Otherwise plot per shape per color (can not plot list of shapes in matplotlib) + reducedShapes = tempShape + if(not ValidateData.funcIsValidList(reducedShapes)): + reducedShapes = reducedShapes[0] + dictPlotGroups.setdefault(len(aiXPoints), []).append([aiXPoints,aiYPoints,[charColor],reducedShapes,tempAlpha,tempLabels[tempColorGrouping.index(charColor)],reducedSizes,charMarkerEdgeColor]) + #Shapes are supplied as a list so plot each shape + else: + #Reduce to shapes of the current colors + reducedShapes = Utility.reduceList(reducedShapes,aiColorPointPositions) + acharReducedShapesElements = list(set(reducedShapes)) + #If there are multiple shapes, plot seperately because one is not allowed to plot them as a list + for aCharShapeElement in acharReducedShapesElements: + #Get indices + aiShapeIndices = Utility.getIndices(reducedShapes,aCharShapeElement) + #Reduce label by shapes + strShapeLabel = Utility.reduceList(acharLabelsByColor,aiShapeIndices) + #Reduce sizes by shapes + strShapeSizes = reducedSizes + if ValidateData.funcIsValidList(reducedSizes): + strShapeSizes = Utility.reduceList(reducedSizes,aiShapeIndices) + #Get points per shape + aiXPointsPerShape = Utility.reduceList(aiXPoints,aiShapeIndices) + aiYPointsPerShape = Utility.reduceList(aiYPoints,aiShapeIndices) + #Get sizes per shape + #Reduce sizes if a list + reducedSizesPerShape = reducedSizes + if(ValidateData.funcIsValidList(reducedSizes)): + reducedSizesPerShape = Utility.reduceList(reducedSizes,aiShapeIndices) + #Put plot data in dict of lists for later plotting + #Separate out the background printing + dictPlotGroups.setdefault(len(aiXPointsPerShape), []).append([aiXPointsPerShape,aiYPointsPerShape,[charColor],aCharShapeElement,tempAlpha,strShapeLabel[0],strShapeSizes,charMarkerEdgeColor]) + + #Plot each color starting with largest color amount to smallest color anmount so small groups will not be covered up by larger groups + #Plot other colors in increasing order + for sPlotGroupKey in sorted(list(dictPlotGroups.keys()), reverse=True): + lslsCurPlotGroup = dictPlotGroups[sPlotGroupKey] + #Plot + for lsGroup in lslsCurPlotGroup: + imgSubplot.scatter(lsGroup[self.c_iXPointIndex], + lsGroup[self.c_iYPointIndex], + c = lsGroup[self.c_iColorIndex], + marker = lsGroup[self.c_iMarkerIndex], + alpha = lsGroup[self.c_iAlphaIndex], + label = lsGroup[self.c_iLabelIndex], + s = lsGroup[self.c_iShapeIndex], + edgecolor = lsGroup[self.c_iEdgeColorIndex]) + + #Plot pie charts + if not lsColorsPieCharts is None: + self.plotWithPieMarkers(imgSubplot=imgSubplot, aiXPoints=ldXPointsPieCharts, aiYPoints=ldYPointsPieCharts, dSize=lsSizesPieCharts, llColors=lsColorsPieCharts, lsLabels=lsLabelsPieCharts, lcShapes=lcShapesPieCharts, edgeColor=charMarkerEdgeColor, dAlpha=tempAlpha) + + objLegend = imgSubplot.legend(loc=tempLegendLocation, scatterpoints=1, prop={'size':10}) + + #Invert legend + if(tempInvert): + if objLegend: + objLegend.legendPatch.set_fc(self.objFigureControl.c_strBackgroundColorWord) + objLegend.legendPatch.set_ec(self.objFigureControl.c_strDetailsColorLetter) + plt.setp(objLegend.get_texts(),color=self.objFigureControl.c_strDetailsColorLetter) + + #Make legend background transparent + if objLegend: + objLegendFrame = objLegend.get_frame() + objLegendFrame.set_alpha(self.objFigureControl.c_dAlpha) + + imgFigure.savefig(tempPlotName, facecolor=imgFigure.get_facecolor()) + return True + + #Indirectly tested + def plotWithPieMarkers(self, imgSubplot, aiXPoints, aiYPoints, dSize, llColors, lsLabels, lcShapes, edgeColor, dAlpha): + """ + The all lists should be in the same order + + :param imgSubPlot: Image to plot to + :type: Image + :param aiXPoints: List of X axis points (one element per color list) + :type: List of Floats + :param aiYPoints: List of X axis points (one element per color list) + :type: List of Floats + :param dSize: double or List of doubles (one element per color list) + :type: List of Floats + :param llColors: List of Lists of colors, one list of colors is for 1 piechart/multiply highlighted feature + Example ["red","blue","green"] for a marker with 3 sections. + :type: List of strings + :param lsLabels: List of labels (one element per color list). + :type: List of Floats + :param lcShapes: Indicates which shape of a pie chart to use, currently supported 'o' and 's' (one element per color list). + :type: List of characters + :param edgeColor: One color entry for the edge of the piechart. + :type: List of characters + :param dAlpha: Value between 0.0 and 1.0 (0.0 being completely transparent, 1.0 being opaque). + :type: Float 0.0-1.0. + """ + + #Zip up points to pairs + xyPoints = zip(aiXPoints,aiYPoints) + #For each pair of points + for iIndex,dXY in enumerate(xyPoints): + ldWedges = [] + #Get colors + lcurColors = llColors[iIndex] + #Get pie cut shape + cPieChartType = lcShapes[iIndex] + if cPieChartType == ConstantsFiguresBreadCrumbs().c_charPCOAPieChart: + ldWedges = self.makePieWedges(len(lcurColors),20) + elif cPieChartType == ConstantsFiguresBreadCrumbs().c_charPCOASquarePieChart: + ldWedges = self.makeSquarePieWedges(len(lcurColors)) + for iWedgeIndex,dWedge in enumerate(ldWedges): + imgSubplot.scatter(x=dXY[0], y=dXY[1], marker=(dWedge,0), s=dSize[iIndex], label=lsLabels[iIndex], facecolor=lcurColors[iWedgeIndex], edgecolor=edgeColor, alpha=dAlpha) + + #Indirectly tested + def makePieWedges(self, iWedgeCount, iSplineResolution = 10): + """ + Generate a list of tuple points which will draw a square broken up into pie cuts. + + :param iWedgeCount: The number of piecuts in the square. + :type: Integer Number greater than 1. + :param iSplineResolution: The amount of smoothing to the circle's outer edge, the higher the number the more smooth. + :type: integer Greater than 1. + :return list List of tuples. Each tuple is a point, formatted for direct plotting of the marker. + """ + + ldWedge = [] + dLastValue = 0.0 + + #Create a list of equal percentages for all wedges + #Do not include a last wedge it gets all the space from the 2nd to last wedge to the end + #Which should still be equal to the others + ldPercentages = [1.0/iWedgeCount]*(iWedgeCount-1) + + for dPercentage in ldPercentages: + ldX = [0] + np.cos(np.linspace(2*math.pi*dLastValue,2*math.pi*(dLastValue+dPercentage),iSplineResolution)).tolist() + ldY = [0] + np.sin(np.linspace(2*math.pi*dLastValue,2*math.pi*(dLastValue+dPercentage),iSplineResolution)).tolist() + ldWedge.append(zip(ldX,ldY)) + dLastValue = dLastValue+dPercentage + ldX = [0] + np.cos(np.linspace(2*math.pi*dLastValue,2*math.pi,iSplineResolution)).tolist() + ldY = [0] + np.sin(np.linspace(2*math.pi*dLastValue,2*math.pi,iSplineResolution)).tolist() + ldWedge.append(zip(ldX,ldY)) + return ldWedge + + #Indirectly tested + def makeSquarePieWedges(self, iWedgeCount): + """ + Generate a list of tuple points which will draw a square broken up into pie cuts. + + :param iWedgeCount: The number of piecuts in the square. + :type: Integer Number greater than 1. + :return list List of tuples. Each tuple is a point, formatted for direct plotting of the marker. + """ + + ldWedge = [] + dLastPercentageValue = 0.0 + dLastSquareValue = 0.0 + dCumulativePercentageValue = 0.0 + dRadius = None + fXYSwitched = False + fAfterCorner = False + iSwitchCounts = 0 + iMagicNumber =(1.0/4) + + #Create a list of equal percentages for all wedges + #Do not include a last wedge it gets all the space from the 2nd to last wedge to the end + #Which should still be equal to the others + ldPercentages = [1.0/iWedgeCount]*(iWedgeCount) + + for dPercentage in ldPercentages: + ldCircleXs = np.cos([2*math.pi*dLastPercentageValue,2*math.pi*(dLastPercentageValue+dPercentage)]) + ldCircleYs = np.sin([2*math.pi*dLastPercentageValue,2*math.pi*(dLastPercentageValue+dPercentage)]) + + if dRadius == None: + dRadius = ldCircleXs[0] + + #Check to see if at corner + fAtCorner = False + iDistance = int((dLastPercentageValue+dPercentage+(iMagicNumber/2))/iMagicNumber + ) - int((dLastPercentageValue+(iMagicNumber/2))/iMagicNumber) + if(iDistance > 0): + fAtCorner = True + if iDistance > 1: + fXYSwitched = not fXYSwitched + iSwitchCounts = iSwitchCounts + 1 + + #Check to see if at a side center + fAtSide = False + if (int((dLastPercentageValue+dPercentage)/iMagicNumber) > int(dLastPercentageValue/iMagicNumber)): + fAtSide = True + + #Handle corner xy switching + if fAtCorner: + fXYSwitched = not fXYSwitched + iSwitchCounts = iSwitchCounts + 1 + #Make sure the xy switching occurs to vary the slope at the corner. + if fXYSwitched: + ldCircleXs,ldCircleYs = ldCircleYs,ldCircleXs + + dSquarePoint = dRadius * (ldCircleYs[1]/float(ldCircleXs[1])) + dRadiusSq1 = dRadius + dRadiusSq2 = dRadius + dLastSquareValueSq = dLastSquareValue + dSquarePointSq = dSquarePoint + + #If in quadrants 2,3 make sign changes + if iSwitchCounts in [2,3]: + if iSwitchCounts == 2: + dRadiusSq1 = dRadiusSq1 *-1 + elif iSwitchCounts == 3: + dRadiusSq1 = dRadiusSq1 * -1 + dRadiusSq2 = dRadiusSq2 * -1 + dLastSquareValueSq = dLastSquareValueSq * -1.0 + dSquarePointSq = dSquarePointSq * -1.0 + + if fAtCorner: + #Corner 1 + if iSwitchCounts==1: + ldWedge.append(zip([0,dRadiusSq1,dRadiusSq1,dSquarePointSq,0],[0,dLastSquareValueSq,dRadiusSq2,dRadiusSq2,0])) + #Corner 2 + elif iSwitchCounts==2: + if iDistance > 1: + ldWedge.append(zip([0,-dRadiusSq1,-dRadiusSq1,dRadiusSq1,dRadiusSq1,0],[0,-dLastSquareValueSq,dRadiusSq2,dRadiusSq2,dSquarePointSq,0])) + else: + ldWedge.append(zip([0,-dLastSquareValueSq,dRadiusSq1,dRadiusSq1,0],[0,dRadiusSq2,dRadiusSq2,dSquarePointSq,0])) + #Corner 3 + elif iSwitchCounts==3: + if iDistance > 1: + ldWedge.append(zip([0,-dLastSquareValueSq,dRadiusSq1,dRadiusSq1,dSquarePointSq,0],[0,-dRadiusSq2,-dRadiusSq2,dRadiusSq2,dRadiusSq2,0])) + else: + ldWedge.append(zip([0,dRadiusSq1,dRadiusSq1,dSquarePointSq,0],[0,dLastSquareValueSq,dRadiusSq2,dRadiusSq2,0])) + #Corner 4 + elif iSwitchCounts==4: + if iDistance > 1: + ldWedge.append(zip([0,-dRadiusSq1,-dRadiusSq1,dRadiusSq1,dRadiusSq1,0],[0,-dLastSquareValueSq,-dRadiusSq2,-dRadiusSq2,dSquarePointSq,0])) + else: + ldWedge.append(zip([0,(-1.0*dLastSquareValueSq),dRadiusSq1,dRadiusSq1,0],[0,(-1.0*dRadiusSq2),(-1.0*dRadiusSq2),dSquarePointSq,0])) + + fAfterCorner = True + else: + if iSwitchCounts%2: + ldWedge.append(zip([0,dLastSquareValueSq,dSquarePointSq,0],[0,dRadiusSq2,dRadiusSq2,0])) + else: + ldWedge.append(zip([0,dRadiusSq1,dRadiusSq1,0],[0,dLastSquareValueSq,dSquarePointSq,0])) + + dLastSquareValue = dSquarePoint + dCumulativePercentageValue = dCumulativePercentageValue + dLastSquareValue + dLastPercentageValue = dLastPercentageValue+dPercentage + + return ldWedge + + #Happy Path Tested + def plotList(self, lsLabelList, strOutputFileName, iSize=20, dAlpha=1.0, charForceColor=None, charForceShape=None, fInvert=False, iDim1=1, iDim2=2): + """ + Convenience method used to plot data in the PCoA given a label list (which is in order of the underlying data). + This is for the scenario where you do not care that the color or shape of the data will be as long as it varies + with the label. + This method does allow forcing color or shape to 1 character so that they do not vary with the label but are one value. + This is helpful when you have a large number of labels to plot given the shapes in the PCoA are limited but not the coloring. + + :param lsLabelList: List of string labels which are in order of the data in the PCoA object (as the data was loaded the PCoA object). + :type: List of strings + :param strOutputFileName: File path to save figure. + :type: String + :param iSize: Size of marker. Default 20. + :type: Integer + :param dAlpha: Alpha for the markers. (0.0 tranparent, 1.0 opaque) + :type: Double between 0.0 and 1.0 + :param charForceColor: Color to force the points to. (Must be understandable by matplotlib as a color [ie. 'k','m','c','r','g','b','y','w']) + :type: Character + :param charForceShape: Shape to force the points to. (Must be understandable by matplotlib as a shape [ie. 'o','s','^','v','<','>','8','p','h']) + :type: Character + :param fInvert: Allows one to invert the background and plot details from white to black (True == background is black). + :type: Boolean + :param iDim1: The first dimension to plot + :type: Integer starting at 1 + :param iDim2: The second dimension to plot + :type: Integer starting at 2 + :return boolean: Indicator of success (True) + """ + + #Get uniqueValues for labels + acharUniqueValues = list(set(lsLabelList)) + iCountUniqueValues = len(acharUniqueValues) + + #Set colors + atupldLabelColors = None + + #Set shapes + alLabelShapes = None + if charForceShape == None: + #Get shapes + acharShapes = PCoA.getShapes(iCountUniqueValues) + if len(acharShapes) == 0: + return False + #Make label shapes + alLabelShapes = [ acharShapes[acharUniqueValues.index(sMetadata)] for sMetadata in lsLabelList ] + else: + alLabelShapes = charForceShape + + #If the coloring is not forced, color so it is based on the labels + if charForceColor == None: + #Get colors based on labels + atupldColors = [Utility.RGBToHex(cm.jet(float(iUniqueValueIndex)/float(iCountUniqueValues))) for iUniqueValueIndex in xrange(0,iCountUniqueValues)] + #Make sure generated colors are unique + if not iCountUniqueValues == len(set(atupldColors)): + print "PCoA::plotList:Error, generated colors were not unique for each unique label value." + print "Labels" + print lsLabelList + print len(lsLabelList) + print "Unique Labels" + print set(lsLabelList) + print len(set(lsLabelList)) + print "Colors" + print atupldColors + print len(atupldColors) + print "Unique Colors" + print set(atupldColors) + print len(set(atupldColors)) + return False + #Make label coloring + atupldLabelColors = [ atupldColors[acharUniqueValues.index(sMetadata)] for sMetadata in lsLabelList ] + #If the coloring is forced, color so it is based on the charForcedColor list + elif(ValidateData.funcIsValidList(charForceColor)): + atupldLabelColors = charForceColor[0] + if not len(lsLabelList) == len(atupldLabelColors): + print "PCoA::plotList:Error, label and forced color lengths were not the same." + print "Labels" + print lsLabelList + print len(lsLabelList) + print "Forced Colors" + print charForceColor[0] + print len(charForceColor[0]) + return False + lsLabelList = [ "".join([charForceColor[1][iLabelIndex], "_", lsLabelList[iLabelIndex]]) for iLabelIndex in xrange(0,len(charForceColor[1]))] + #If the color is forced but the color does not vary, color all markers are the same. + else: + atupldLabelColors = charForceColor + + #Call plot + self.plot(tempPlotName=strOutputFileName, tempColorGrouping=atupldLabelColors, tempShape=alLabelShapes, tempLabels=lsLabelList, tempShapeSize = iSize, tempAlpha=dAlpha, tempInvert = fInvert, iDim1=iDim1, iDim2=iDim2) + + def funcForceXAxis(self, dList): + """ + Force the X axis to the given list. + + :param dList: List of values to force the x axis of the plot (floats). + :type: List of floats + """ + + self.ldForcedXAxis = dList + + def funcUnforceXAxis(self): + """ + Return the X axis to the values derived from the loaded data. + """ + + self.ldForcedXAxis = None + + #Happy Path Tested + @staticmethod + def getShapes(intShapeCount): + """ + Returns a list of characters which are valid shapes for markers. + + :param intShapeCount: The number of shapes to return. + :type: Integer (min 1, max 9) + :return: A list of characters to use as markers. [] is returned on error + """ + + lsPointShapes = ['o','s','^','v','<','>','8','p','h'] + if intShapeCount > len(lsPointShapes): + print("".join(["Error, PCoA.getShapes. Do not have enough shapes to give. Received request for ",str(intShapeCount)," shapes. Max available shape count is ",str(len(lsPointShapes)),"."])) + return [] + return lsPointShapes[0:intShapeCount] diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/PlotMatrix.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/PlotMatrix.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,96 @@ +""" +Author: Timothy Tickle +Description: Plots matrices. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +import matplotlib.pyplot as plt +import numpy as np +from pylab import * + +#Plots a matrix +class PlotMatrix: + + #Given a matrix and labels consistent to the matrix, plot a matrix + @staticmethod + def funcPlotMatrix(npMatrix, lsXLabels, strOutputFigurePath, strXTitle="X Axis", strYTitle="Y Axis", fFlipYLabels=False): + """ + Given a matrix and labels consistent to the matrix, plot a matrix. + + :param npMatrix: Numpy Array (matrix) to plot. + :type: Numpy Array + :param lsXLabels: X Labels + :type: List of strings + :param strOutputFigurePath: File to create the figure file. + :type: String + :param strXTitle: X Axis label. + :type: String + :param strYTitle: Y axis label. + :type: String + :param fFlipYLabels: Flip the Y labels so they are opposite order of x axis. + :type: Boolean + """ + + #Get canvas/figure + plt.clf() + figConfusionMatrix = plt.figure() + objAxis = figConfusionMatrix.add_subplot(111) + + #Get y labels + lNewYLabels = list(lsXLabels) + if fFlipYLabels: + lNewYLabels.reverse() + + #Set x axis and position + objAxis.xaxis.set_ticklabels([""]+lsXLabels) + objAxis.xaxis.set_ticks_position('top') + + #Set y axis + objAxis.yaxis.set_ticklabels([""]+lNewYLabels) + + #Set axis titles + ylabel(strYTitle) + plt.suptitle(strXTitle) + + #Plot matrix values + objPlot = objAxis.imshow(np.array(npMatrix), cmap=get_cmap("Blues"), interpolation='nearest') + + #Plot text values + for yIndex, ldRow in enumerate(npMatrix): + for xIndex, dValue in enumerate(ldRow): + plt.text(xIndex, yIndex, dValue, fontdict = {'size':18,'weight':'bold'} ) + + #Add color bar + figConfusionMatrix.colorbar(objPlot, ticks=range(int(min(np.array(npMatrix).ravel())),int(max(np.array(npMatrix).ravel())))) + + #Save to a file + savefig(strOutputFigurePath) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/SVM.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/SVM.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,306 @@ +""" +Author: Timothy Tickle +Description: Class to Allow Support Vector Machine analysis and to contain associated scripts +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#Libraries +from AbundanceTable import AbundanceTable +from ConstantsBreadCrumbs import ConstantsBreadCrumbs +import csv +import os +from random import shuffle +from ValidateData import ValidateData + +class SVM: + """ + Class which holds generic methods for SVM use. + """ + + #1 Happy Path tested + @staticmethod + def funcConvertAbundanceTableToSVMFile(abndAbundanceTable, xOutputSVMFile, sMetadataLabel, lsOriginalLabels = None, lsSampleOrdering = None): + """ + Converts abundance files to input SVM files. + + :param abndAbundanceTable: AbudanceTable object to turn to input SVM file. + :type: AbundanceTable + :param xOutputSVMFile: File to save SVM data to when converted from the abundance table. + :type: FileStream or string file path + :param sMetadataLabel: The name of the last row in the abundance table representing metadata. + :type: String + :param: lsOriginalLabels The original labels. + :type: List of strings + :param lsSampleOrdering: Order of samples to output to output file. If none, the order in the abundance table is used. + :type: List of strings + :return lsUniqueLabels: List of unique labels. + """ + + #Create data matrix + dataMatrix = zip(*abndAbundanceTable.funcGetAbundanceCopy()) + + #Add labels + llData = [] + lsLabels = lsOriginalLabels if lsOriginalLabels else SVM.funcMakeLabels(abndAbundanceTable.funcGetMetadata(sMetadataLabel)) + if not isinstance(xOutputSVMFile,str): + if xOutputSVMFile.closed: + xOutputSVMFile = open(xOutputSVMFile.name,"w") + ostm = open(xOutputSVMFile,"w") if isinstance(xOutputSVMFile, str) else xOutputSVMFile + f = csv.writer(ostm, csv.excel_tab, delimiter = ConstantsBreadCrumbs.c_strBreadCrumbsSVMSpace) + + #This allows the creation of partially known files for stratification purposes + lsCurrentSamples = abndAbundanceTable.funcGetSampleNames() + lsOrderingSamples = lsSampleOrdering if lsSampleOrdering else lsCurrentSamples[:] + + iLabelIndex = 0 + iSize = len(dataMatrix[0]) + iIndexSample = 1 + for sSample in lsOrderingSamples: + if sSample in lsCurrentSamples: + f.writerow([lsLabels[iLabelIndex]]+ + [ConstantsBreadCrumbs.c_strColon.join([str(tpleFeature[0]+1),str(tpleFeature[1])]) for tpleFeature in enumerate(dataMatrix[iIndexSample])]) + iLabelIndex += 1 + iIndexSample += 1 + #Make blank entry + else: + f.writerow([ConstantsBreadCrumbs.c_strSVMNoSample]+[ConstantsBreadCrumbs.c_strColon.join([str(tpleNas[0]+1),str(tpleNas[1])]) + for tpleNas in enumerate([ConstantsBreadCrumbs.c_strSVMNoSample]*iSize)]) + if lsOriginalLabels: + iLabelIndex += 1 + ostm.close() + return set(lsLabels) + + @staticmethod + def funcUpdateSVMFileWithAbundanceTable(abndAbundanceTable, xOutputSVMFile, lsOriginalLabels, lsSampleOrdering): + """ + Takes a SVM input file and updates it with an abundance table. + lsOriginalLabels and lsSampleOrdering should be consistent to the input file. + Samples in the abundance table will be used to update the file if the sample name in the abundace table is also in the lsSampleOrdering. + lsOriginalLabels and lsSampleOrdering should be in the same order. + + :param abndAbundanceTable: AbudanceTable object to turn to input SVM file. + :type: AbundanceTable + :param xOutputSVMFile: File to save SVM data to when converted from the abundance table. + :type: FileStream or string file path + :param lsOriginalLabels: The list of the original labels (as numerics 0,1,2,3,4...as should be in the file). + :type: List of strings + :param lsSampleOrdering: Order of samples in the output file. + :type: List of strings + :return lsUniqueLabels: List of unique labels. + """ + + #Read in old file + if not isinstance(xOutputSVMFile,str): + if xOutputSVMFile.closed: + xOutputSVMFile = open(xOutputSVMFile.name,"r") + ostm = open(xOutputSVMFile,"r") if isinstance(xOutputSVMFile, str) else xOutputSVMFile + fin = csv.reader(ostm, csv.excel_tab, delimiter = ConstantsBreadCrumbs.c_strBreadCrumbsSVMSpace) + #Read in contents of file + llsOldContents = [lsRow for lsRow in fin] + ostm.close() + + #Check to make sure this ordering covers all positions in the old file + if not len(llsOldContents) == len(lsSampleOrdering): + print "The length of the original file ("+str(len(llsOldContents))+") does not match the length of the ordering given ("+str(len(lsSampleOrdering))+")." + return False + + #Create data matrix from new data + dataMatrix = zip(*abndAbundanceTable.funcGetAbundanceCopy()) + + #Add labels + llData = [] + + #Write to file + if not isinstance(xOutputSVMFile,str): + if xOutputSVMFile.closed: + xOutputSVMFile = open(xOutputSVMFile.name,"w") + ostm = open(xOutputSVMFile,"w") if isinstance(xOutputSVMFile, str) else xOutputSVMFile + f = csv.writer(ostm, csv.excel_tab, delimiter = ConstantsBreadCrumbs.c_strBreadCrumbsSVMSpace) + + #This allows to know what position to place the new lines + lsCurrentSamples = abndAbundanceTable.funcGetSampleNames() + + iSize = len(dataMatrix[0]) + iIndexSample = 1 + iIndexOriginalOrder = 0 + for sSample in lsSampleOrdering: + if sSample in lsCurrentSamples: + f.writerow([lsOriginalLabels[iIndexOriginalOrder]]+ + [ConstantsBreadCrumbs.c_strColon.join([str(tpleFeature[0]+1),str(tpleFeature[1])]) for tpleFeature in enumerate(dataMatrix[iIndexSample])]) + iIndexSample += 1 + #Make blank entry + else: + f.writerow(llsOldContents[iIndexOriginalOrder]) + iIndexOriginalOrder += 1 + ostm.close() + return True + + #Tested 5 + @staticmethod + def funcMakeLabels(lsMetadata): + """ + Given a list of metadata, labels are assigned. This is function represents a central location to make labels so all are consistent. + + :param lsMetafdata: List of metadata to turn into labels based on the metadata's values. + :type: List of integer labels + """ + #Do not use a set to make elements unique. Need to preserve order. + #First label should be 0 + lsUniqueLabels = [] + [lsUniqueLabels.append(sElement) for sElement in lsMetadata if not (sElement in lsUniqueLabels)] + + dictLabels = dict([[str(lenuLabels[1]),str(lenuLabels[0])] for lenuLabels in enumerate(lsUniqueLabels)]) + return [dictLabels[sLabel] for sLabel in lsMetadata] + + #Tested + @staticmethod + def funcReadLabelsFromFile(xSVMFile, lsAllSampleNames, isPredictFile): + """ + Reads in the labels from the input file or prediction output file of a LibSVM formatted file + and associates them in order with the given sample names. + + Prediction file expected format: Labels declared in first line with labels keyword. + Each following row a sample with the first entry the predicted label + Prediction file example: + labels 0 1 + 0 0.3 0.4 0.6 + 1 0.1 0.2 0.3 + 1 0.2 0.2 0.2 + 0 0.2 0.4 0.3 + + Input file expected format: + Each row a sample with the first entry the predicted label + Input file example: + 0 0.3 0.4 0.6 + 1 0.1 0.2 0.3 + 1 0.2 0.2 0.2 + 0 0.2 0.4 0.3 + + :param xSVMFile: File path to read in prediction labels. + :type String + :param lsAllSampleNames List of sample ids in the order of the labels. + :type List of Strings + :param isPredictFile: Indicates if the file is the input (False) or prediction (True) file + :type boolean + :return: Dictionary {label:["sampleName1", "sampleName2"...],...} or False on error + """ + #Open prediction file and input file and get labels to compare to the predictions + g = csv.reader( open(xSVMFile, 'r') if isinstance(xSVMFile, str) else xSVMFile, csv.excel_tab, delimiter = ConstantsBreadCrumbs.c_strBreadCrumbsSVMSpace ) + lsOriginalLabels = [lsLineElements[0] for lsLineElements in g if not lsLineElements[0] == ConstantsBreadCrumbs.c_strSVMNoSample] + + if isPredictFile: + lsOriginalLabels = lsOriginalLabels[1:] + + #Check sample name length + if not len(lsAllSampleNames) == len(lsOriginalLabels): + print "SVM::funcReadLabelsFromFile. Error, the length of sample names did not match the original labels length. Samples ("+str(len(lsAllSampleNames))+"):"+str(lsAllSampleNames)+" Labels ("+str(len(lsOriginalLabels))+"):"+str(lsOriginalLabels) + return False + + #Change to {label:["sampleName1", "sampleName2"...],...} + dictSampleLabelsRet = dict() + for sValue in set(lsOriginalLabels): + dictSampleLabelsRet[sValue] = set([lsAllSampleNames[iindex] for iindex, sLabel in enumerate(lsOriginalLabels) if sLabel == sValue]) + return dictSampleLabelsRet + + #Tested + @staticmethod + def funcScaleFeature(npdData): + """ + Scale a feature between 0 and 1. Using 01 and not 01,1 because it keeps the sparsity of the data and may save time. + + :param npdData: Feature data to scale. + :type Numpy Array Scaled feature data. + :return npaFloat: A numpy array of floats. + """ + if sum(npdData) == 0 or len(set(npdData))==1: + return npdData + dMin = min(npdData) + return (npdData-dMin)/float(max(npdData-dMin)) + + #Tested + @staticmethod + def funcWeightLabels(lLabels): + """ + Returns weights for labels based on how balanced the labels are. Weights try to balance unbalanced results. + + :params lLabels: List of labels to use for measure how balanced the comparison is. + :type List + :return List: [dictWeights ({"label":weight}),lUniqueLabels (unique occurences of original labels)] + """ + #Convert to dict + #Do not use set to make elements unique. Need to preserve order. + #First label should be 0 + lUniqueLabels = [] + for sElement in lLabels: + if sElement not in lUniqueLabels: + lUniqueLabels.append(sElement) + dictLabels = dict(zip(lUniqueLabels, range(len(lUniqueLabels)))) + + #Build a dict of weights per label {label:weight, label:weight} + #Get the occurrence of each label + dictWeights = dict() + for sLabelKey in dictLabels: + sCurLabel = dictLabels[sLabelKey] + dictWeights[sCurLabel] = lLabels.count(sLabelKey) + + #Divide the highest occurrence each occurrence + iMaxOccurence = max(dictWeights.values()) + for sWeightKey in dictWeights: + dictWeights[sWeightKey]=iMaxOccurence/float(dictWeights[sWeightKey]) + + return [dictWeights,lUniqueLabels] + + #Tested 3/4 cases could add in test 12 with randomize True + def func10FoldCrossvalidation(self, iTotalSampleCount, fRandomise = False): + """ + Generator. + Generates the indexes for a 10 fold cross validation given a sample count. + If there are less than 10 samples, it uses the sample count as the K-fold cross validation + as a leave one out method. + + :param iTotalSampleCount: Total Sample Count + :type Integer Sample Count + :param fRandomise: Random sample indices + :type Boolean True indicates randomise (Default False) + """ + #Make indices and shuffle if needed + liindices = range(iTotalSampleCount) + if fRandomise: + shuffle(liindices) + + #For 10 times + iKFold = 10 + if iTotalSampleCount < iKFold: + iKFold = iTotalSampleCount + for iiteration in xrange(iKFold): + lfTraining = [iindex % iKFold != iiteration for iindex in liindices] + lfValidation = [not iindex for iindex in lfTraining] + yield lfTraining, lfValidation diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/SVM.pyc Binary file src/breadcrumbs/src/SVM.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/ScatterPlot.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/ScatterPlot.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,106 @@ +""" +Author: Timothy Tickle +Description: Class to create scatter plots. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +import matplotlib.pyplot as plt +from pylab import * + +#Plots a matrix +class ScatterPlot: + + @staticmethod + def funcPlot(lx, ly, strOutputFigurePath, strTitle = "Title", strXTitle="X Axis", strYTitle="Y Axis", strColor = "#83C8F9", fInvert=False): + """ + Plot a scatter plot. + + :params lx: List of x values + :type: List of doubles + :params ly: List of y values + :type: List of doubles + :params strOutputFigurePath: File path to make figure + :type: String file path + :params strTitle: Title of figure + :type: String + :params strXTitle: Label of x axis + :type: String + :params strYTitle: Label of y axis + :type: String + :params strColor: Hex color for the face of the boxplots + :type: String + :params fInvert: Invert colors (true) + :type: Boolean + """ + + #Start plot + #Get plot object + imgFigure = plt.figure() + + #Get plot colorsstrOutFigure + objFigureControl = ConstantsFiguresBreadCrumbs() + #Boxplots have to be plotted over the scatter so the alpha can not go to 1.0 + #In this case capturing the alpha before inversion + #Inversion automoatically sets it to 1. + dAlpha=objFigureControl.c_dAlpha + objFigureControl.invertColors(fInvert=fInvert) + + #Color/Invert figure + imgFigure.set_facecolor(objFigureControl.c_strBackgroundColorWord) + imgSubplot = imgFigure.add_subplot(111,axisbg=objFigureControl.c_strBackgroundColorLetter) + imgSubplot.set_xlabel(strXTitle) + imgSubplot.set_ylabel(strYTitle) + imgSubplot.spines['top'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['bottom'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['left'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.spines['right'].set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.xaxis.label.set_color(objFigureControl.c_strDetailsColorLetter) + + #Adds light grid for numbers and puts them in the background + imgSubplot.yaxis.grid(True, linestyle='-', which='major', color=objFigureControl.c_strGridLineColor, alpha=objFigureControl.c_dAlpha) + imgSubplot.set_axisbelow(True) + imgSubplot.yaxis.label.set_color(objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='x', colors=objFigureControl.c_strDetailsColorLetter) + imgSubplot.tick_params(axis='y', colors=objFigureControl.c_strDetailsColorLetter) + charMarkerEdgeColor = objFigureControl.c_strDetailsColorLetter + + #Make scatter plot + plt.scatter(x=lx,y=ly,c=strColor,marker="o",alpha=objFigureControl.c_dAlpha) + + #Set ticks and title + imgSubplot.set_title(strTitle) + imgSubplot.title.set_color(objFigureControl.c_strDetailsColorLetter) + + #End plot + #Save to a file + imgFigure.savefig(strOutputFigurePath, facecolor=imgFigure.get_facecolor()) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/Utility.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/Utility.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,93 @@ +""" +Author: Timothy Tickle +Description: Utility class for generic functions. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +class Utility(): + """ + Class to perform misc methods. + """ + + #Tested 6 + @staticmethod + def getIndices(aList, dataElement): + """ + Returns the index or indicies of the element in the list. + + :param aList: List ot search for element. + :type List. + :param dataElement: Element for which to search. + :type Object of the same type as is found in the list. + :return: List of indicies indicating where the element occurs in the list. Returns [] when the element is not in the list. + """ + + aretIndices = [] + for dataIndex in xrange(0,len(aList)): + if(aList[dataIndex] == dataElement): + aretIndices.append(dataIndex) + return aretIndices + + #Tested 6 + @staticmethod + def reduceList(aList, dataIndicies): + """ + Reduces a list to just the data indicies given. + + :param aList: List to reduce. + :type List + :param dataIndicies: list of indicies to keep. + :type List of integers + :return: Reduced list. Returns [] when the and empty index list is given. + """ + return [aList[dataIndicies[dataIndex]] for dataIndex in xrange(0,len(dataIndicies))] + + #Tested 8 + @staticmethod + def RGBToHex(adColor): + """ + Change a RGB float to hex. + + :param adColor: A list of 3 elements which are floats between 0.0 and 1.0 + :type A list of floats + :return: A string (HEX formatted) representation of the RGB color + """ + + charR = (hex(int(adColor[0]*255)))[2:] + if(str(charR) == "0"): + charR = "00" + charG = (hex(int(adColor[1]*255)))[2:] + if(str(charG) == "0"): + charG = "00" + charB = (hex(int(adColor[2]*255)))[2:] + if(str(charB) == "0"): + charB = "00" + return "".join(["#",charR, charG, charB]) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/UtilityMath.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/UtilityMath.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,156 @@ +""" +Author: Timothy Tickle +Description: Utility class for generic math functions. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#Import libaries +import itertools +import numpy as np +import operator +import random +from ValidateData import ValidateData + +class UtilityMath(): + """ + Class to perform misc math methods. + """ + + ## + #Happy path test 2 + @staticmethod + def funcConvertToBHQValue(ldPValues, iNumberOfTests=None): + """ + Convert a list of p-value to a list of q-values. + + :param ldPValues: List of doubles (p-values) to convert. + :type List + :param iNumberOfTests: Number of (multiple) tests if different than the ldValue length. If not set the length of ldPValues is used. + :type Integer + :return List: List of Q-values made with a BH modification. + """ + + #If the number of tests is not specified, use the number of pvalues + if(iNumberOfTests == None): + iNumberOfTests = len(ldPValues) + #Used to hold the pvalues as they are being manipulated + lsConvertToQValues = list() + #Is used to set the ordr of the pvalues as they are placed in the lsConvertToQValues + dOrder = 1 + for dValue in ldPValues: + lsConvertToQValues.append([dValue,dOrder,None]) + dOrder = dOrder + 1 + + #Sort by pvalue + lsConvertToQValues.sort(key=lambda x: x[0]) + + #Used to keep track of the current test number + iTest = 1 + for dConvValue in lsConvertToQValues: + dConvValue[2] = dConvValue[0] * iNumberOfTests / iTest + iTest = iTest + 1 + + #Sort by original order + lsConvertToQValues.sort(key=lambda x: x[1]) + + #return just 1 dimension (the qvalue) + return [ldValues[2] for ldValues in lsConvertToQValues] + + #Happy path tested 5 + @staticmethod + def funcSampleWithReplacement(aData, iSelect): + """ + Sample from a vector of data (aData) with replacement iSelect many objects. + + :param aData: Data to sample from with replacement. + :type List + :param iSelect: Amount of data to select from the original data population. + :type Integer. + :return List: List of sampled data. + Returns an empty list on error. + """ + + if iSelect and aData: + iDataSize = len(aData) + funcRandom, funcInt = random.random, int + lsSampling = operator.itemgetter(*[funcInt(funcRandom() * iDataSize) for selected in itertools.repeat(None, iSelect)])(aData) + if isinstance(lsSampling, basestring): + lsSampling = [lsSampling] + return lsSampling + return [] + + #Happy Path Tested 2 + @staticmethod + def funcSumRowsOfColumns(npaAbundance, lsSampleNames): + """ + Takes the column names of a npArray and sums the rows into one column. + + :param npaAbundance: Array of data to sum. + :type Numpy Array + :param lsSampleNames: List of sample names. + :type List List of strings. + :return List List of data summed at each row. + """ + + #Compress by data name + npPooledSample = npaAbundance[lsSampleNames[0]] + for strSampleName in lsSampleNames[1:]: + #When combining, combine counts by summing + npPooledSample = npPooledSample + npaAbundance[strSampleName] + return list(npPooledSample) + + #Testing Status: Light happy path testing 2 + @staticmethod + def funcTransposeDataMatrix(npaMatrix, fRemoveAdornments=False): + """ + Transposes a numpy array. + + :param npaMatrix: Data matrix to transpose. + :type Numpy Array + :param fRemoveAdornments: Remove the first column before transposing. + :type Boolean True indicates removing the column. + :return Boolean or Numpy Array: Transposed array or a boolean indicating error. + Boolean False is returned on error. + """ + + #Validate parameters + if(not ValidateData.funcIsValidNPArray(npaMatrix)): + print "".join(["Utility_Math:transposeDataMatrix::Error, transposeDataMatrix was an invalid structured array. Value =",str(npaMatrix)]) + return False + if(not ValidateData.funcIsValidBoolean(fRemoveAdornments)): + print "".join(["Utility_Math:transposeDataMatrix::Error, fRemoveAdornments was an invalid boolean. Value =",str(fRemoveAdornments)]) + return False + + #Change to samples x taxa as is needed for the compute method below + #Also remove the first row which is taxa identification + conversionMatrix = [list(row)[fRemoveAdornments:] for row in npaMatrix] + return np.array(conversionMatrix).transpose() + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/UtilityMath.pyc Binary file src/breadcrumbs/src/UtilityMath.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/ValidateData.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/ValidateData.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,624 @@ +""" +Author: Timothy Tickle +Description: Validate Data containing methods for testing variables. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2012" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#Import local code +from types import * +import decimal +import os +import re +import string + +class ValidateData: + + #Tested 5 + @staticmethod + def funcIsValidBoolean(parameterValue): + """ + Validates a parameter as a valid boolean. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid boolean. + :type Boolean + """ + + #Check to make sure it is not null + if parameterValue == None: + return False + + #Check to make sure it is a string + if not type(parameterValue) is BooleanType: + return False + return True + + #Tested 6 + @staticmethod + def funcIsTrue(parameterValue): + """ + Validates a parameter as true. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is True. + :type Boolean + """ + + if(ValidateData.funcIsValidBoolean(parameterValue)): + if(parameterValue == True): + return True + return False + + #Tested 6 + @staticmethod + def funcIsFalse(parameterValue): + """ + Validates a parameter as false. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is False. + :type Boolean + """ + + if(ValidateData.funcIsValidBoolean(parameterValue)): + if(parameterValue == False): + return True + return False + + #Tested 5 + @staticmethod + def funcIsValidInteger(parameterValue): + """ + Validates a parameter as an integer. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is an integer. + :type Boolean + """ + + #Check to make sure it is not null + if (parameterValue == None): + return False + + #Check to make sure it is an integer + if not type(parameterValue) is IntType: + return False + + return True + + #Tested 5 + @staticmethod + def funcIsValidPositiveInteger(parameterValue, tempZero = False): + """ + Validates a parameter as false. + + :param parameterValue: Value to be evaluated. + :type Unknown + :param tempZero: Allows one to set what the value for zero should return. + :type Boolean The return value for zero. + :return Boolean: True indicates the parameter is a positive integer. + :type Boolean + """ + + #Check to make sure it is not null + if not ValidateData.funcIsValidInteger(parameterValue): + return False + + #Check to see it is positive + if (parameterValue < 0): + return False + + #Check for zero value + if(parameterValue == 0): + return tempZero + return True + + #Tested 14 + @staticmethod + def funcIsValidNumeric(parameterValue): + """ + Validates a parameter as an integer. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a numeric. + :type Boolean + """ + + #Check to make sure it is not null + if (parameterValue == None): + return False + #Check to make sure it is an integer + if((type(parameterValue) == IntType)or(type(parameterValue) == LongType)or(type(parameterValue) == FloatType)or(type(parameterValue) == ComplexType)or(str(type(parameterValue)) == "")): + if(not type(parameterValue) == BooleanType): + return True + return False + + #Tested 5 + @staticmethod + def funcIsValidStringType(parameterValue): + """ + Validates a parameter as a string. This allows the string to be blank or empty. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a string type. + :type Boolean + """ + + #Check to make sure it is not null + if parameterValue == None: + return False + + #Check to make sure it is a string + if not type(parameterValue) is StringType: + return False + + return True + + #Tested 5 + @staticmethod + def funcIsValidString(parameterValue): + """ + Validates a parameter as a string. Does NOT allow string to be blank or empty. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a string. + :type Boolean + """ + + #Type check + if not ValidateData.funcIsValidStringType(parameterValue): + return False + + #Check to see it is not blank + if parameterValue.strip() == "": + return False + return True + + @staticmethod + def funcIsValidStringInt(parameterValue): + """ + Validates a parameter that is a string as a format which is an integer. + + :param parameterValue: Value to be evaluated. + :type Unknown + """ + + #Type string check + if not ValidateData.funcIsValidStringType(parameterValue): + return False + + #Check to see if the string can be converted to an integer + try: + int(parameterValue) + except: + return False + return True + + @staticmethod + def funcIsValidStringFloat(parameterValue): + """ + Validates a parameter that is a string as a format which is a numeric. + + :param parameterValue: Value to be evaluated. + :type Unknown + """ + + #Type string check + if not ValidateData.funcIsValidStringType(parameterValue): + return False + + #Check to see if the string can be converted to a double + try: + float(parameterValue) + except: + return False + return True + + #Tested 6 + @staticmethod + def funcIsValidFormatString(parameterValue): + """ + Validates a parameter as a valid format string. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + lettersValid = False + if ValidateData.funcIsValidString(parameterValue): + validChars = "BbcdfHhIiLlPpsx0123456789" + for letter in parameterValue: + lettersValid = letter in validChars + if(not lettersValid): + break + return lettersValid + + #Tested 5 + @staticmethod + def funcIsValidChar(parameterValue): + """ + Validates a parameter as a valid character. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + return ValidateData.funcIsValidString(parameterValue) + + #Tested 13 + @staticmethod + def funcIsValidPositiveNumberChar(parameterValue): + """ + Validates a parameter as a valid character representing a number. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + #Check to make sure is a valid string + if not ValidateData.funcIsValidString(parameterValue): + return False + + #Try to convert to decimal + try: + decimalConversion = decimal.Decimal(parameterValue) + if decimalConversion < 0: + return False + except: + return False + return True + + #Tested 9 + @staticmethod + def funcIsValidFlagChar(parameterValue): + """ + Validates a parameter as a valid character representing a boolean. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + if parameterValue == '0' or parameterValue == "0" or parameterValue == '1' or parameterValue == "1": + return True + return False + + #Tested 15 + @staticmethod + def funcIsValidBoundedIntegerChar(parameterValue, iValueOne, iValueTwo): + """ + Validates a parameter as a valid characater that represents an integer inclusively bounded by two given values. + + :param parameterValue: Value to be evaluated. + :type Unknown + :param iValueOne: One bound for the value. + :type Integer + :param iValueTwo: The other bound for the data. + :type Integer + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + #Check to make sure is a valid string + if not ValidateData.funcIsValidString(parameterValue): + return False + + #Check to make sure is a valid integer + if not ValidateData.funcIsValidInteger(iValueOne): + return False + + #Check to make sure is a valid integer + if not ValidateData.funcIsValidInteger(iValueTwo): + return False + + #Try to convert to decimal + try: + intConversion = int(parameterValue) + if(iValueOne < iValueTwo): + if ((intConversion >= iValueOne) and (intConversion <= iValueTwo)): + return True + return False + if(iValueTwo < iValueOne): + if ((intConversion >= iValueTwo) and (intConversion <= iValueOne)): + return True + return False + if(iValueOne == iValueTwo): + if (intConversion == iValueOne): + return True + return False + except: + return False + + #Tested 9 + @staticmethod + def funcIsValidList(parameterValue): + """ + Validates a parameter as a list. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a list + :type Boolean + """ + + #Check to make sure it is not null + if parameterValue == None: + return False + + #Check to make sure it is a list + if not type(parameterValue) is ListType: + return False + + #Check elements + listSize = len(parameterValue) + for i in range(0,listSize): + if parameterValue[i] == None: + return False + if type(parameterValue[i]) is ListType: + if ValidateData.funcIsValidList(parameterValue[i]) == False: + return False + return True + + #Tested 9 + @staticmethod + def funcIsValidTuple(parameterValue): + """ + Validates a parameter as a tuple. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a tuple + :type Boolean + """ + + #Check to make sure it is not null + if parameterValue == None: + return False + + #Check to make sure it is a string + if not type(parameterValue) is TupleType: + return False + + #Check elements + tupleSize = len(parameterValue) + for i in range(0,tupleSize): + if parameterValue[i] == None: + return False + if type(parameterValue[i]) is TupleType: + if ValidateData.funcIsValidTuple(parameterValue[i]) == False: + return False + return True + + #Tested 7 + @staticmethod + def funcIsValidNumericList(parameterValue): + """ + Validates a parameter as a list of numeric values. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a list of numeric values. + :type Boolean + """ + + #Check is valid list + if(not ValidateData.funcIsValidList(parameterValue)): + return False + + #Check elements + listSize = len(parameterValue) + for i in xrange(0,listSize): + if(not ValidateData.funcIsValidNumeric(parameterValue[i])): + return False + return True + + #Tested 7 + @staticmethod + def funcIsValidStringList(parameterValue): + """ + Validates a parameter as a list of string values. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a list of string values. + :type Boolean + """ + + #Check is valid list + if(not ValidateData.funcIsValidList(parameterValue)): + return False + + #Check elements + listSize = len(parameterValue) + for i in xrange(0,listSize): + if(not ValidateData.funcIsValidString(parameterValue[i])): + return False + return True + + #Tested 4 + @staticmethod + def funcIsValidNPArray(parameterValue): + """ + Validates a parameter as a numpy array. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a numpy array. + :type Boolean + """ + + #Check to make sure it is not null + if parameterValue == None: + return False + + #Check to make sure it is a structure array + if not str(type(parameterValue)) == "": + return False + + return True + + #Tested 9 + @staticmethod + def funcIsValidDictionary(parameterValue): + """ + Validates a parameter as a dictionary. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a dictionary. + :type Boolean + """ + + #Check to make sure it is not null + if parameterValue == None: + return False + + #Check to make sure it is a string + if not type(parameterValue) is DictType: + return False + + #Check key elements + keyList = parameterValue.keys() + keyListSize = len(keyList) + for i in range(0,keyListSize): + if keyList[i] == None: + return False + if type(keyList[i]) is ListType: + if validateData.funcIsValidList(keyList[i]) == False: + return False + + #Check key elements + itemList = parameterValue.values() + itemListSize = len(itemList) + + for i in range(0,itemListSize): + if itemList[i] == None: + return False + if type(itemList[i]) is ListType: + if ValidateData.funcIsValidList(itemList[i]) == False: + return False + return True + + #Tested 18 + @staticmethod + def funcIsValidDNASequence(parameterValue): + """ + Validates a parameter as a valid DNA sequence. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + if ValidateData.funcIsValidString(parameterValue): + expression = re.compile(r'[^atcgATCG]') + if not None == expression.search(parameterValue): + return False + return True + return False + + #Tested 15 + @staticmethod + def funcIsValidNucleotideBase(parameterValue): + """ + Validates a parameter as a character which is a valid nucleotide representation. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + if (ValidateData.funcIsValidDNASequence(parameterValue) or (parameterValue == 'u') or (parameterValue == "U")): + if (len(parameterValue) == 1): + return True + return False + + #Testing 4 + @staticmethod + def funcIsValidFileName(parameterValue): + """ + Validates a parameter as a valid file name. + + :param parameterValue: Value to be evaluated. + :type Unknown + :return Boolean: True indicates the parameter is a valid file path. + :type Boolean + """ + + if parameterValue is None: + return False + elif(ValidateData.funcIsValidString(parameterValue)): + return os.path.exists(parameterValue) + return False + + #Tested 5 + @staticmethod + def funcIsValidClass(parameterValue, strCorrectName): + """ + Validates a parameter as a valid class (of a specifc type given by name). + + :param parameterValue: Value to be evaluated. + :type Unknown + :param strCorrectName: Name of te class the parameter should be. + :type Unknown + :return Boolean: True indicates the parameter is a valid value. + :type Boolean + """ + + if(parameterValue==None): + return False + if not ValidateData.funcIsValidString(strCorrectName): + return False + classType = type(parameterValue).__name__ + if(classType == strCorrectName): + return True + if(classType == 'instance'): + if(parameterValue.__class__.__name__==strCorrectName): + return True + else: + return False + return False diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/ValidateData.pyc Binary file src/breadcrumbs/src/ValidateData.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/__init__.py diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/__init__.pyc Binary file src/breadcrumbs/src/__init__.pyc has changed diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/circlader/circlader.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/circlader/circlader.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,70 @@ +#!/usr/bin/env python + +#----------------------------------------------------------------------------- +# NAME: circlader.py +# DESCRIPTION: Circlader (circular cladogram buider) is a python script for +# creating images of circular cladogram starting from any guide +# tree in tabular or Newick format +# +# Author: Nicola Segata +# email: nsegata@hsph.harvard.edu +# +# Copyright: (c) 2011 +# Licence: +# +#----------------------------------------------------------------------------- + + +import os +import sys,argparse +import circlader_lib as cir + +def read_params(args): + parser = argparse.ArgumentParser(description='Circlader') + + parser.add_argument('tree_file', nargs='?', default=None, type=str, + help= "the input tree in Newick format (unless --tf is specified)" + "[stdin if not present]") + parser.add_argument('out_image', nargs='?', default=None, type=str, + help= "the output image (the format is guessed from the extension " + "[windows visualization if not present]") + parser.add_argument('--tree_format', choices=['newick','tabular'], + default='newick', type=str, + help= "specifies the input tree format (default \"newick\", " + "other choice is \"tabular\")") + parser.add_argument('--style_file', nargs='?', default=os.getcwd()+"/default_styles/style.txt", type=str, + help= "set the style file (default_styles/style.txt if not specified)") + parser.add_argument('--color_file', nargs='?', default=None, type=str, + help= "set the color file (default_styles/colors.txt if not specified)") + parser.add_argument('--highlight_file', nargs='?', default=None, type=str, + help= "set the highlight file (default none)") + parser.add_argument('--tick_file', nargs='?', default=None, type=str, + help= "set the label file for level's names (default none)") + parser.add_argument('--size_file', nargs='?', default=None, type=str, + help= "set the file containing the dimentison of the circles (default none)") + parser.add_argument('--circle_file', nargs='?', default=None, type=str, + help= "set the external circles file (default none) [BETA FEATURE]") + parser.add_argument('--format', choices=['png','pdf','ps','eps','svg'], default=None, type=str, + help= "set the format of the output image (default none " + "meaning that the format is guessed from the output " + "file extension)") + parser.add_argument('--dpi', default=300, type=int ) + + return vars(parser.parse_args()) + +params = read_params(sys.argv) + +cladogram = cir.Tree() +cladogram.read_colors(params['color_file']) +cladogram.read_style(params['style_file']) +cladogram.read_sizes(params['size_file']) +cladogram.read_circles(params['circle_file']) +cladogram.read_highlights(params['highlight_file']) +cladogram.read_tick_labels(params['tick_file']) +if params['tree_format'] == 'newick': + cladogram.load_newick(params['tree_file']) +else: + cladogram.load_lefse(params['tree_file']) +cladogram.pos_rad_leaves() +cladogram.set_pos() +cladogram.draw(params['out_image'],outformat=params['format'],dpi=params['dpi']) diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/circlader/circlader_lib.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/circlader/circlader_lib.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,726 @@ +#----------------------------------------------------------------------------- +# NAME: circlader_lib.py +# DESCRIPTION: Circlader (CIRcular CLADogram buidER) is a python script for +# creating images of circular cladogram starting from any guide +# tree in tabular or Newick format +# +# Author: Nicola Segata +# email: nsegata@hsph.harvard.edu +# +# Copyright: (c) 2011 +# Licence: +# +#----------------------------------------------------------------------------- + +import sys,os,math,matplotlib +#matplotlib.use('TkAgg') +matplotlib.use('Agg') +#matplotlib.use('PDF') +from matplotlib import collections +from Bio import Phylo +import numpy as np +from pylab import * +import operator +import matplotlib.patches as mpatches + +class Tree: + max_rad_dist = math.pi*0.15 + +# Class specifying clade with strutural characteristics and +# associated information + class Clade: + def __init__( self,taxa_id=0, name='', + br_len=-1.0, root_br_dist=-1.0, + highlighted=False, + ext_seg = False, + ext_seg_vec = None): + self.id = taxa_id + self.name = name + self.label = name + self.label_cat = '' + self.col = 'w' + self.br_len = br_len + self.tot_br_len = 0.0 + self.root_br_dist = root_br_dist + self.is_leaf = True + self.is_highlighted = highlighted + self.__children = {} + self.pos = Tree.VisPos() + self.nleaves = 0 + self.size = 0 + self.ext_seg = ext_seg + self.ext_seg_vec = ext_seg_vec + + def add_child(self,cl): + self.__children[cl.id] = cl + self.tot_br_len += (cl.br_len + + (0 if cl.is_leaf else cl.tot_br_len)) + self.is_leaf = False + + def get_children(self): + return self.__children.values() + +# Class decribing graphical information associated with clades + class VisPos: + def __init__( self, r=0.0, rad=0.0, rmin=0.0, + rmax=0.0, lab_lev = 0.0): + self.rad = rad + self.r = r + self.rad_min = rmin + self.rad_max = rmax + self.lab_lev = lab_lev + def __init__(self): + self.__all_taxa = set([]) + self.__noname_gen = self.__unique_none_id() + self.__max_br_depth = 0.0 + self.__min_br_depth = float(sys.maxint) + self.__leaves = [] + self.min_high = float(sys.maxint) + self.max_high = -1.0 + self.added_label_cats = [] + self.vis_cats = [] + self.wing_ext_max = 1.0 + self.cseg = None + + def __unique_none_id(self): + for i in xrange(2**31-1): + yield "_"+str(i) + + + def add_clade(self,cl,fn,br_depth): + cl.size = self.sizes[cl.id] if hasattr(self, 'sizes') and cl.id in self.sizes else self.opt['default_taxa_size'] + if cl.is_highlighted: + cl.pos.lab_lev = br_depth+1.0 + if self.min_high > cl.pos.lab_lev: + self.min_high = cl.pos.lab_lev + if self.max_high < min( cl.pos.lab_lev, + float(self.opt['highlighting_bar_stop_level'])*1.001): + self.max_high = cl.pos.lab_lev + cl.label = self.labels[fn] + cl.col = self.label_color[fn] + cl.label_cat = self.label_cat[fn] + cl.size *= self.opt['highlight_taxa_size_magnifier'] + + + def load_lefse(self,inp_f): + with open(inp_f, 'r') as inp: + rows = ["root."+l.rstrip().split("\t")[0] for l in inp.readlines()] + rows.append("root") + + self.opt['ignore_branch_len'] = 1 + + def rec_add(clade_name,rows,br_depth=0.0, first = False): + self.__all_taxa.add(clade_name) + fn = clade_name if not clade_name.startswith("root.") \ + else clade_name.split("root.")[1] + highlighted = (fn in self.labels) + ext_seg = (self.cseg and fn in self.cseg) + ext_seg_v = self.cseg[fn] if ext_seg else None + cl = Tree.Clade( clade_name, clade_name.split(".")[-1],1.0, + br_depth+1.0, highlighted = highlighted, ext_seg = ext_seg, ext_seg_vec = ext_seg_v ) + self.add_clade(cl,fn,br_depth) + rows.remove(clade_name) + np = clade_name.count(".") + children = [r for r in rows if r.count(".") == np+1 and \ + r.count(".") > 0 and \ + ".".join(r.split(".")[:-1]) == clade_name] + if not children: + self.__leaves.append(cl) + if cl.root_br_dist > self.__max_br_depth: + self.__max_br_depth = cl.root_br_dist + if cl.root_br_dist > self.__max_br_depth: + self.__max_br_depth = cl.root_br_dist + cl.nleaves = 1 + nleav = len(self.__leaves) + for c in children: + cl.add_child(rec_add(c,rows,br_depth+1.0 if not first else 0.0)) + sep = 'sep_clades' in self.opt and self.opt['sep_clades'] + if sep and children and all([c.is_leaf for c in cl.get_children()]): + self.__leaves.insert(nleav,None) + self.__leaves.append(None) + cl.nleaves = sum([c.nleaves for c in cl.get_children()]) + + return cl + + self.root = rec_add(rows[-1],rows, first = True) + + def load_newick(self,inp_f): + if os.path.splitext(inp_f)[-1] == '.nwk': + bio_tree = Phylo.read(inp_f, "newick") + elif os.path.splitext(inp_f)[-1] == '.xml': + bio_tree = Phylo.read(inp_f, "phyloxml") + else: + sys.stderr.write( "Unrecognized tree extensions: "+os.path.splitext(inp_f)[-1]+"\n" ) + sys.exit(0) + + def rec_add(clade,br_depth=-0.0): + nam = clade.name if clade.name else "noname"+self.__noname_gen.next() + if nam in self.__all_taxa: + oldn = nam + nam = nam+self.__noname_gen.next() + print "Warning: "+oldn+" non unique, renamed to "+nam + self.__all_taxa.add(nam) + if self.opt['ignore_branch_len']: clade.branch_length = 1.0 + fn = nam if not nam.startswith("root.") else nam.split("root.")[1] + highlighted = (fn in self.labels) + ext_seg = (self.cseg and fn in self.cseg) + ext_seg_v = self.cseg[fn] if ext_seg else None + cl = Tree.Clade( nam, + nam, + clade.branch_length, + br_depth+clade.branch_length, + highlighted=highlighted, + ext_seg = ext_seg, ext_seg_vec = ext_seg_v) + self.add_clade(cl,fn,br_depth) + + if not clade.clades: + cl.nleaves = 1 + return cl + + if br_depth+cl.br_len < self.opt['max_branch_depth']: + nleav = len(clade.clades) + for c in clade.clades: + cl.add_child(rec_add(c,br_depth+cl.br_len)) + cl.nleaves = sum([c.nleaves for c in cl.get_children()]) + return cl + + def rec_leaves( cl ): + children = cl.get_children() + if 'size_sorted' in self.opt and self.opt['size_sorted']: + children = sorted(children, key=lambda x: x.nleaves,reverse= True) + + for c in children: + rec_leaves( c ) + + if not children: + self.__leaves.append(cl) + if cl.root_br_dist > self.__max_br_depth: + self.__max_br_depth = cl.root_br_dist + if cl.root_br_dist < self.__min_br_depth: + self.__min_br_depth = cl.root_br_dist + cl.nleaves = 1 + return + nleav = len(self.__leaves) + sep = 'sep_clades' in self.opt and self.opt['sep_clades'] + + if sep and all([c.is_leaf for c in children]): + self.__leaves.insert(nleav,None) + self.__leaves.append(None) + + self.root = rec_add(bio_tree.root,-1.0) + + rec_leaves( self.root ) + + def pos_rad_leaves(self): + nl = len(self.__leaves) + cir_width = float(self.opt['total_rotation'])/360.0 + self.cir_offset = np.pi*0.5+(1.0-cir_width)*np.pi + dist = math.pi*cir_width*2.0/nl + if dist > self.__class__.max_rad_dist: + dist = self.__class__.max_rad_dist + prev = self.cir_offset-dist + ln = False + for i,c in enumerate(self.__leaves): + if not c: + ln = True + continue + c.pos.rad = dist*(i if ln else i)+self.cir_offset + c.pos.rad_min = (c.pos.rad + prev)*0.5 + next_none = self.__leaves[(i+1)%len(self.__leaves)] + c.pos.rad_max = (c.pos.rad + dist*(i+1 if next_none else i+3) +self.cir_offset)*0.5 + prev = c.pos.rad + ln = False + + def set_pos(self): + cl = self.root + def rec_set_pos(clade): + children = clade.get_children() + #children = sorted(children, lambda x,y: cmp(x.nleaves,y.nleaves),reverse= True) + clade.pos.r = clade.root_br_dist / self.__max_br_depth + if children: + rad_mm = [rec_set_pos(c) for c in children] + rads = [lcl.pos.rad for lcl in children] + clade.pos.rad = (min(rads)+max(rads))*0.5 + clade.pos.rad_min,clade.pos.rad_max = min([r[0] for r in rad_mm]), max([r[1] for r in rad_mm]) + return clade.pos.rad_min,clade.pos.rad_max + return clade.pos.rad_min,clade.pos.rad_max + rec_set_pos(cl) + + + def draw(self,out_img_file,outformat=None,dpi=300): + fig = plt.figure(figsize=(7,7)) + ax = fig.add_subplot( 111, + polar=True, + frame_on=False ) + + plt.subplots_adjust( right=1.0-self.opt['right_space'], + left=self.opt['left_space'], + top=1.0-self.opt['top_space'], + bottom=self.opt['bottom_space'] ) + xticks([]) + yticks([]) + + circle_taxa_draw = [] + + ign_bl = self.opt['ignore_branch_len'] + ext_unit = self.opt['annotation_wing_sep'] + high_start = float(self.opt['highlighting_bar_start_level'])/self.__max_br_depth + high_stop = float(self.opt['highlighting_bar_stop_level']+1)/self.__max_br_depth*1.001 + + taxa_circle, taxa_circle_h = {}, {} + taxa_circle_attr = [ 'rad', 'r', 'shape', 'col', 'size', 'linew' ] + for a in taxa_circle_attr: + taxa_circle[a], taxa_circle_h[a] = [], [] + + branch_line_1, branch_line_2, branch_line_3 = {}, {}, {} + branch_line_attr = [ 'frto', 'col' ] + for a in branch_line_attr: + branch_line_1[a], branch_line_2[a], branch_line_3[a] = [], [], [] + + def draw_branch(clade,children,fcol): + rads = [lcl.pos.rad for lcl in children] + min_rads,max_rads = min(rads),max(rads) + sbl = self.opt['sub_branches_opening_point'] if ign_bl else 1.0 + sb,rsb = sbl,1-sbl + redf = 1.0-self.opt['sub_branches_angle_reduction'] + red,nred = (redf,1-redf) if abs(max_rads - min_rads) < math.pi else (1.0,0.0) + + mid = ( max_rads + min_rads ) * 0.5 + + rads_l = list(arange( min_rads*red+mid*nred, max_rads*red+mid*nred,0.05)) \ + + [max_rads*red+mid*nred] + + + if self.opt['highligh_branch_color']: + col = fcol + if clade.is_highlighted: + col = clade.col + else: col = self.opt['branch_color'] + + if clade != self.root: + branch_line_1['frto'].append( + np.array( + [np.array( + [c, sb*clade.pos.r+rsb*children[0].pos.r] ) + for c in rads_l] ) ) + branch_line_1['col'].append(col) + + branch_line_2['frto'].append( + np.array( + [np.array( [clade.pos.rad*red+mid*nred, sb*clade.pos.r+rsb*children[0].pos.r]), + np.array( [clade.pos.rad, clade.pos.r])] ) ) + + branch_line_2['col'].append(col) + + """ + if clade == self.root: + for c in children: + branch_line_3['col'].append(col) + print [c.pos.rad, c.pos.r] + branch_line_3['frto'].append( np.array( [ np.array( [c.pos.rad*red+mid*nred, sb*clade.pos.r+rsb*c.pos.r] ), + np.array( [c.pos.rad, c.pos.r] ) ] ) ) +#branch_line_3['frto'].append( np.array( [ np.array( [c.pos.rad, c.pos.r] ), # it should be [0.0,0.0] but with that the branches disappear +# np.array( [c.pos.rad, 0.0] ) ] ) ) + """ + if clade == self.root: + for c in children: + branch_line_3['col'].append(col) + branch_line_3['frto'].append( np.array( [ np.array( [c.pos.rad*red+mid*nred, 0.0] ), + np.array( [c.pos.rad, c.pos.r] ) ] ) ) + else: + for c in children: + branch_line_3['col'].append(col) + branch_line_3['frto'].append( np.array( [ np.array( [c.pos.rad*red+mid*nred, sb*clade.pos.r+rsb*c.pos.r] ), + np.array( [c.pos.rad, c.pos.r] ) ] ) ) + return col + + + def draw_taxa_circle(clade): + if clade.is_leaf and not self.opt['draw_leaf_taxa']: + return + if not clade.is_leaf and not self.opt['draw_internal_taxa']: + return + tc = taxa_circle_h if clade.is_highlighted else taxa_circle + tc['rad'].append( clade.pos.rad ) + tc['r'].append( clade.pos.r ) + tc['shape'].append( 'o' ) + tc['col'].append( clade.col ) + tc['size'].append( clade.size ) + tc['linew'].append( self.opt['taxa_circle_edge_width'] ) + + def draw_extended_leaves(clade): + ax.plot( [clade.pos.rad,clade.pos.rad], + [clade.pos.r,1.0],"-",color=[0.7,0.7,0.7]) + + def draw_wing(clade): + ext = self.opt['fixed_wing_stop'] if 'fixed_wing_stop' in self.opt and self.opt['fixed_wing_stop'] >= 0 else self.max_high-clade.pos.lab_lev+1 + ext_max = ext*ext_unit + + if not clade.label_cat in [v['label'] for v in self.vis_cats] and clade.label_cat: + self.vis_cats.append( { 'color':clade.col, + 'alpha':self.opt['bar_alpha'], + 'label':clade.label_cat} ) + + if not clade.label and 'no_wind_if_no_label' in self.opt and self.opt['no_wind_if_no_label']: + return + + if high_start < clade.pos.r <= high_stop: + wlab = clade.label.split("_r_")[-1] if not clade.label.count(":") else clade.label.split(":")[0].split("_r_")[-1] + + ax.bar( clade.pos.rad_min, + 1-clade.pos.r+ext_max+ext_unit*0.25, + width = abs(clade.pos.rad_min-clade.pos.rad_max), + bottom = clade.pos.r, + alpha = self.opt['bar_alpha'], + color=clade.col, + edgecolor=clade.col ) + if 1.0+ext_max+ext_unit*0.25 > self.wing_ext_max: + self.wing_ext_max = 1.0+ext_max+ext_unit*0.25 + + + des = float(180.0*(clade.pos.rad_min+clade.pos.rad_max)/np.pi)*0.5 # -(0 if clade.label.count("_r_") else 90) + if not clade.label.count("_r_"): + des += (90 if 180 < des < 360 else -90) + + lro = 0.5 if 'radial_label_wing_offset' not in self.opt else self.opt['radial_label_wing_offset'] + + ax.text( ((clade.pos.rad_min+clade.pos.rad_max)*0.5), + 1+ext_max-ext_unit*lro+ext_unit*0.25, + wlab, + rotation=des, + ha="center", + va="center", + fontsize=self.opt['label_font_size'], + fontstretch=0 ) + if clade.label.count(":"): + ax.bar( 0.0, + 0.0, + width = 0.0, + bottom = 0.0, + alpha = 1.0, + color=clade.col, + label=clade.label.split("_r_")[-1] ) + + + def draw_sectors(clade): + for i,v in enumerate(clade.ext_seg_vec): + if v[1] <= 0.0: continue + height = self.opt['seg_radial_depth'] * ( v[3] if v[3] else 1.0) + width = abs(clade.pos.rad_min-clade.pos.rad_max)*self.opt['seg_width'] + startx = clade.pos.rad_min+abs(clade.pos.rad_min-clade.pos.rad_max)*((1.0-self.opt['seg_width'])*0.5) + starty = self.wing_ext_max+self.opt['dist_from_tree']+self.opt['seg_radial_depth']*i + + art2 = None + lw = v[4] if v[4] else self.opt['seg_line_width'] + if not v[2] or v[2] == 'R': + if lw > 0.0: + art2 = mpatches.Rectangle( (startx,starty), width = width, height = height, + fc = 'none', ec='k', linewidth= lw ) + art = mpatches.Rectangle( (startx,starty), + width = width, + height = height, + alpha=v[1], + color=v[0], + linewidth= 0.0 + ) + + elif v[2] == '^': + if lw > 0.0: + art2 = mpatches.Polygon( [ [startx,starty],[startx+width/2, starty+height], [startx+width,starty ] ], + fc = 'none', ec='k', linewidth= lw) + art = mpatches.Polygon( [ [startx,starty], + [startx+width/2, starty+height], + [startx+width,starty ] ], + alpha=v[1], color=v[0], + linewidth= 0.0) + elif v[2] == 'v': + if lw > 0.0: + art2 = mpatches.Polygon( [ [startx,starty + height], [startx+width/2, starty], [startx+width,starty + height ] ], + fc = 'none', ec='k', linewidth= lw) + art = mpatches.Polygon( [ [startx,starty + height], + [startx+width/2, starty], + [startx+width,starty + height ] ], + alpha=v[1], color=v[0], + linewidth= 0.0) + if art2: + ax.add_patch(art2) + ax.add_patch(art) + + #ax.bar( clade.pos.rad_min+abs(clade.pos.rad_min-clade.pos.rad_max)*((1.0-self.opt['seg_width'])*0.5), + # self.opt['seg_radial_depth'], + # width = abs(clade.pos.rad_min-clade.pos.rad_max)*self.opt['seg_width'], + # bottom = self.wing_ext_max+self.opt['dist_from_tree']+self.opt['seg_radial_depth']*i, + # alpha=v[1], + # color=v[0], + # linewidth= self.opt['seg_line_width']) + + def draw_scale(): + nticks = self.opt['branch_length_n_ticks'] + round_prec = 2 + if self.opt['ignore_branch_len']: + nlev = int(self.__max_br_depth) + tick_step = 1.0/float(nlev) + while not ( nlev <= self.opt['branch_length_n_ticks'] + and not int(self.__max_br_depth) % nlev): + nlev -= 1 + tick_step = 1.0/float(nlev) + else: + tick_step = (1.0 - self.root.pos.r)/float(nticks) + tick_pos = np.arange(self.root.pos.r,1.000001,tick_step) + if self.opt['show_branch_length_ticks']: + yticks(tick_pos,[]) + if self.opt['show_branch_length_labels']: + for t in tick_pos: + tv = int(round(t*self.__max_br_depth-1.0,2)) \ + if self.opt['ignore_branch_len'] \ + else round(t*self.__max_br_depth,round_prec) + if self.tick_labels: + tv = self.tick_labels[tv] + else: tv = str(tv) + ax.text( np.pi*0.5,t, + tv, + ha="center", + fontsize = self.opt['branch_length_ticks_font_size']) + + def draw_legend(): + ret = [] + h, lleg = ax.get_legend_handles_labels() + if len(lleg) > 0: + hl = sorted(zip(h, lleg),key=operator.itemgetter(1)) + h2, l2 = zip(*hl) + + leg = ax.legend( h2, + l2, + bbox_to_anchor=(1.03, 1), + frameon=False, + prop={'size':self.opt['label_font_size']}, + labelspacing=self.opt['legend_label_distance'], + loc=2, + ncol=self.opt['legend_n_col'], + borderaxespad=0. ) + ret.append( leg ) + if self.vis_cats: + labs = [] + for l in self.vis_cats: + ll, = ax.bar( 0.0, + 0.0, + width=0.0, + bottom=0.0, + alpha=l['alpha'], + color=l['color'], + edgecolor=l['color'], + label=l['label'] ) + labs.append((ll,l['label'])) + + leg2 = ax.legend( [lp for (lp,la) in labs], + [la for (lp,la) in labs], + bbox_to_anchor=(0.0, 1), + prop={'size':self.opt['category_font_size']}, + labelspacing=self.opt['legend_label_distance'], + # prop={'size':self.opt['label_font_size']}, + frameon=False, + loc=2, + borderaxespad=0. ) + ret.append( leg2 ) + if len(lleg) > 0: + gca().add_artist(leg) + return ret + + def rec_draw(clade,fcol=self.opt['branch_color']): + children = clade.get_children() + if children: + draw_branch(clade,children,fcol) + + rec_col = clade.col if clade.is_highlighted else fcol + + fcls = [rec_draw(c,rec_col) for c in children] + + if not self.opt['draw_taxa']: return clade + + for fc in fcls: + if fc.is_leaf and self.opt['extend_leaves']: + draw_extended_leaves(fc) + + highlighted_clade = draw_taxa_circle(fc) + if highlighted_clade: + circle_taxa_draw.append(highlighted_clade) + + if fc.is_highlighted: + draw_wing(fc) + + return clade + + rec_draw(self.root) + + + def rec_draw_sectors(clade,fcol=self.opt['branch_color']): + children = clade.get_children() + fcls = [rec_draw_sectors(c) for c in children] + if not self.opt['draw_taxa']: return clade + if clade.ext_seg: + draw_sectors(clade) + return clade + + + rec_draw_sectors(self.root) + coll_b1 = collections.LineCollection( branch_line_1['frto'], + color = branch_line_1['col'], + linewidths = self.opt['branch_tickness']) + ax.add_collection(coll_b1) + + coll_b2 = collections.LineCollection( branch_line_2['frto'], + color = branch_line_2['col'], + linewidths = self.opt['branch_tickness']) + ax.add_collection(coll_b2) + + coll_b3 = collections.LineCollection( branch_line_3['frto'], + color = branch_line_3['col'], + linewidths = self.opt['branch_tickness']) + ax.add_collection(coll_b3) + + + + for tc in [taxa_circle, taxa_circle_h]: + if not tc['rad']: continue + ax.scatter( tc['rad'], + tc['r'], + #taxa_circle_shape + c = tc['col'], + s = tc['size'], + alpha = 1.0, + linewidths = tc['linew'], + zorder=12) + + + if self.opt['show_branch_length_ticks'] or self.opt['show_branch_length_labels']: + draw_scale() + + legs = [] + if 'legend_on' not in self.opt or self.opt['legend_on']: + legs = draw_legend() + + fc = 'w' + if 'background_color' in self.opt and self.opt['background_color'] == 'k': + def get_col_attr(x): + return hasattr(x, 'set_color') and hasattr(x, 'get_color') # and not hasattr(x, 'set_facecolor') + + for o in ax.findobj(get_col_attr): + col = o.get_color() + if col == 'k': + o.set_color('w') + fc = 'k' + + a,b = ax.get_ylim() + ylim((0,b)) + if out_img_file: + plt.savefig( out_img_file, + dpi=dpi, + facecolor=fc, + #bbox_inches='tight', + #bbox_extra_artists = [l.legendPatch for l in legs], + #pad_inches=0.45 + format = outformat, + edgecolor=fc) #,format=self.opt['img_format']) + + plt.close() + else: + plt.show() + + def read_highlights(self,highlights_file): + self.labels = {} + self.label_color = {} + self.label_cat = {} + if not highlights_file: + return + with open(highlights_file) as inp_f: + labels = [l.rstrip().split('\t') + for l in inp_f.readlines() if not l.startswith("#")] + for l in labels: + self.labels[l[0]] = l[1] + self.label_cat[l[0]] = l[2] + if l[3].startswith("_c_"): + self.label_color[l[0]] = [float(v) for v in l[3].split("_c_")[-1].split("[")[-1].split("]")[0].split(",")] + else: + self.label_color[l[0]] = self.colors[l[3]] + + def read_circles(self,circles_file): + self.cseg = {} + if not circles_file: + return + with open(circles_file) as inp_f: + mat = [l.rstrip().split('\t') + for l in inp_f.readlines() if not l.startswith("#")] + for m in mat: + cv = [] + cs = [] + for v in m[1:]: + v00,bor = v.split("#") if "#" in v else (v,None) + v0,dep = v00.split("$") if "$" in v00 else (v00,None) + v1,shape = v0.split("!") if "!" in v0 else (v0,None) + col,alpha = v1.split(":") if ":" in v1 else [v1,"1.0"] + + if col.startswith("_c_"): + c = [float(v) for v in col.split("_c_")[-1].split("[")[-1].split("]")[0].split(",")] + else: + c = self.colors[col] + a = float(alpha) + cv.append((c,a,shape,float(dep) if dep else None,float(bor) if bor else None)) + self.cseg[m[0]] = cv + + def read_sizes(self,size_file): + self.sizes = {} + if not size_file: + return + with open(size_file) as inp_f: + rows = [l.rstrip().split('\t') + for l in inp_f.readlines() if not l.startswith("#")] + for l in rows: + self.sizes["root."+l[0]] = float(l[1]) + + def read_tick_labels(self,ticks_file): + self.tick_labels = {} + if not ticks_file: + return + with open(ticks_file) as inp_f: + labels = [l.rstrip().split('\t') + for l in inp_f.readlines() if not l.startswith("#")] + for l in labels: + self.tick_labels[int(l[0])-1] = l[1] + + + default_colors = 'bgrcmy' + + def read_colors(self,colors_file): + if not colors_file: + self.opt = {} + for c in self.default_colors: + self.opt[c] = c + return + + self.color_list = [] + self.colors = {} + with open(colors_file) as inp_f: + col = [l.rstrip().split('\t') + for l in inp_f.readlines() if not l.startswith("#")] + for c in col: + self.color_list.append(c[0]) + self.colors[c[0]] = [float(cc)/255.0 for cc in c[1].split(',')] + + def read_style(self,style_file): + with open(style_file) as inp_f: + self.opt = dict([(l.rstrip().split()[0],l.split("#")[0].split()[1:]) + for l in inp_f.readlines() + if l.strip() and not l.startswith("#")]) + for o in self.opt: + try: + v= int(self.opt[o][0]) + except ValueError: + try: + v= float(self.opt[o][0]) + except ValueError: + try: + v = str(self.opt[o][0])[0] + except ValueError: + print "not a valid input",self.opt[o][0] + self.opt[o] = v + diff -r 000000000000 -r 0de566f21448 src/breadcrumbs/src/under_development/PCA.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/breadcrumbs/src/under_development/PCA.py Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,133 @@ +""" +Author: Timothy Tickle +Description: Performs and plots Principle Components Analysis. +""" + +##################################################################################### +#Copyright (C) <2012> +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of +#this software and associated documentation files (the "Software"), to deal in the +#Software without restriction, including without limitation the rights to use, copy, +#modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, +#and to permit persons to whom the Software is furnished to do so, subject to +#the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies +#or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +#INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +#PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +#HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +#OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +#SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +##################################################################################### + +__author__ = "Timothy Tickle" +__copyright__ = "Copyright 2013" +__credits__ = ["Timothy Tickle"] +__license__ = "MIT" +__maintainer__ = "Timothy Tickle" +__email__ = "ttickle@sph.harvard.edu" +__status__ = "Development" + +#External libraries +from AbundanceTable import AbundanceTable +from ConstantsFiguresBreadCrumbs import ConstantsFiguresBreadCrumbs +from Ordination import Ordination +import matplotlib.cm as cm +from math import sqrt,asin +from matplotlib.mlab import PCA as mplPCA +from matplotlib import pyplot as plt +from numpy import * +from UtilityMath import UtilityMath +from ValidateData import ValidateData + +class PCA(Ordination): + """ + Class to Run Principle Components Analysis on an abundance table object + """ + + def __init__(self): + Ordination.__init__(self) + self.c_strComponents = "components" + self.c_strVariance = "percent_variance" + + def run(self,fScale=True,fCenter=True,fASTransform=False): + if not self.dataMatrix is None: + mtrxPrepped = self.dataMatrix.T + if fASTransform: + mtrxPrepped = array([self.doAsinOnList(row) for row in sqrt(mtrxPrepped)]) + if fCenter: + mtrxPrepped = mtrxPrepped-mean(mtrxPrepped,0) + if fScale: + # This is consistent to R's prcomp method. + vStd = std(a=mtrxPrepped,axis=0) if fCenter else [sqrt(sum(square(ldRow))/len(ldRow)) for ldRow in mtrxPrepped.T] + mtrxPrepped /= vStd + iRows, iCols = mtrxPrepped.shape + U,S,V = linalg.svd(a=mtrxPrepped,full_matrices=False) + ldVariance = square(S*(iCols-1)) + ldVariance = ldVariance/sum(ldVariance) + # Here components are row-wise so each component is a row. + # Here percent variance is given and it is in the order of the components. + self.dataProcessed = {self.c_strComponents:V, self.c_strVariance:ldVariance} + return True + else: + print("PCA:run::Error Tried to run analysis on no data load data first.") + return False + + def getVariance(self,iIndex=None): + if not self.dataProcessed is None: + if not iIndex is None: + return self.dataProcessed[self.c_strVariance][iIndex] + return self.dataProcessed[self.c_strVariance] + else: + print("PCA:getVariance::Error Tried to run analysis on no data load data first.") + return False + + def getComponents(self,iIndex=None): + if not self.dataProcessed is None: + if not iIndex is None: + return self.dataProcessed[self.c_strComponents].T[iIndex] + return self.dataProcessed[self.c_strComponents].T + else: + print("PCA:getComponents::Error Tried to run analysis on no data load data first.") + return False + + def doAsinOnList(self, lsValues): + return([asin(element) for element in lsValues]) + + def convertMetadataForPCA(self,abndTable): + """ This takes a metadata dictionary from an abundance table and formats the metadata for use in the PCA. + This formatting includes reducing discontinuous data to leveles and replacing NA values to the means of the value (continuous data only) + This returns a numpy array of the format needed for this PCA object. + """ + + # Replace missing values with the mean + # dummy the discrete data + dictMetadata = abndTable.funcGetMetadataCopy() + if(len(dictMetadata) < 2): + return None + + ## Remove the metadata id + dictMetadata.pop(abndTable.funcGetIDMetadataName(),None) + lMetadata = [] + for lxItem in dictMetadata.values(): + ## If this is not numeric data then dummy + ## Treat NA as a seperate category + if not (sum([ ValidateData.funcIsValidStringFloat(xItem) for xItem in lxItem]) == len(lxItem)): + # Get levels + setsLevels = set(lxItem) + # Go through each level and dummy the metadata + for sLevel in setsLevels: + lMetadata.append([1.0 if xItem==sLevel else 0.0 for xItem in lxItem]) + else: + # Change NA to Mean and store numeric data as float + # Also add to the metadata so that there are no negative numbers + ldNONA = [float(xItem) for xItem in lxItem if not xItem.strip().lower() in ["na",""]] + dMean = sum(ldNONA)/float(len(ldNONA)) + lsMetadataValues = [dMean if xItem.strip().lower() in ["na",""] else float(xItem) for xItem in lxItem] + dMinValueAdj = abs(min(lsMetadataValues)) + lMetadata.append([sValue + dMinValueAdj for sValue in lsMetadataValues]) + return(array(lMetadata).T) diff -r 000000000000 -r 0de566f21448 test-data/micropita_input --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/micropita_input Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,229 @@ +ID Sample_0_D Sample_1_D Sample_2_D Sample_3_D Sample_4_D Sample_5_D Sample_6_D Sample_7_D Sample_8_D Sample_9_D Sample_10_D Sample_11_D Sample_12_D Sample_13_D Sample_14_D Sample_15_D Sample_16_R Sample_17_R Sample_18_R Sample_19_R Sample_20_R Sample_21_R Sample_22_R Sample_23_R Sample_24_R Sample_25_R Sample_26_R Sample_27_R Sample_28_R Sample_29_R Sample_30_E Sample_31_E Sample_32_E Sample_33_E Sample_34_E Sample_35_E Sample_36_E Sample_37_E Sample_38_E Sample_39_E Sample_40_E Sample_41_E Sample_42_E Sample_43_E Sample_44_T Sample_45_T Sample_46_T Sample_47_T +Sample Sample_0_D Sample_1_D Sample_2_D Sample_3_D Sample_4_D Sample_5_D Sample_6_D Sample_7_D Sample_8_D Sample_9_D Sample_10_D Sample_11_D Sample_12_D Sample_13_D Sample_14_D Sample_15_D Sample_16_R Sample_17_R Sample_18_R Sample_19_R Sample_20_R Sample_21_R Sample_22_R Sample_23_R Sample_24_R Sample_25_R Sample_26_R Sample_27_R Sample_28_R Sample_29_R Sample_30_E Sample_31_E Sample_32_E Sample_33_E Sample_34_E Sample_35_E Sample_36_E Sample_37_E Sample_38_E Sample_39_E Sample_40_E Sample_41_E Sample_42_E Sample_43_E Sample_44_T Sample_45_T Sample_46_T Sample_47_T +Group Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Complex Moderate_Dissimilarity_Feature Moderate_Dissimilarity_Feature Moderate_Dissimilarity_Feature Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity Moderate_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity_Feature High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity High_Dissimilarity Targeted_Feature Targeted_Feature Targeted_Feature Targeted_Feature +StratifyLabel 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 +Label Class-Two Class-One Class-Two Class-One Class-One Class-One Class-One Class-Two Class-Two Class-One Class-Two Class-One Class-One Class-Two Class-Two Class-Two Class-Two Class-Two Class-Two Class-Two Class-One Class-One Class-One Class-One Class-One Class-Two Class-One Class-Two Class-Two Class-One Class-Two Class-Two Class-Two Class-Two Class-One Class-One Class-One Class-One Class-One Class-Two Class-One Class-Two Class-Two Class-One Class-Two Class-Two Class-Two Class-Two +Root|Taxa_0 0 0.132639108 51.67205155 0 0 0 51.7444670649 0 0 0 0 0 51.2083349844 52.1495033914 0 54.2809813981 51.6829297536 0 0.3123676392 0 0 0 0 0 0.2166953032 0 0.2411828448 0 0 0 0 0 0 0.3122296644 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1772302872 +Root|Taxa_1 54.0993873098 0 0.441962075 0 0 0 50.6647838005 53.447223597 50.5817481168 0 53.5412967792 0 0 0 0 0 54.5327122192 0 0 0 0 0 0 0 0 0 0 0 0.3066743566 0 0.4312838574 0 0.3731209223 0 0 0 0 0 0 0 0 0 0 0 0 0.1274865184 0 0 +Root|Taxa_2 51.461026897 0 0 0 0 53.0265676376 0 0 0 0 54.047444278 0 52.5264375555 0 0 0 54.9402852499 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4223345514 0 0 0 0 0.4607446961 0 0.3442454432 0 0 0 0 0 0 0 0 +Root|Taxa_3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 53.7506605469 0 0.4148157079 0 0 0 0.2685767173 0 0 0 0 0 0.3663062251 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50.5269874981 51.8543446876 53.7681811219 52.6344247487 +Root|Taxa_4 50.2405694418 51.7777654529 0 54.8458795552 0 0 0 0 0 0 0 53.8808302665 0 0 0 0 50.0282264237 0 0 0 0.0980723897 0 0.3886813644 0 0 0 0 0 0.4286598254 0 0 0 0.4731642927 0 0 0 0 0.1568392012 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_5 0 0 0 0 0 51.9510168529 0.2296159024 52.9698629485 0 0 0 52.1974377835 0 0 0.2252690679 0 53.653338634 0 0 0 0 0 0 0 0 0.3963056725 0 0 0.0678330435 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_6 0 0 0 0 0 54.5673895399 0 0 0 0 51.1909575326 0.2522232281 0 52.4007159288 54.537199915 0 51.3239204408 0 0 0 0 0 0 0 0 0 0 0 0 0.174038431 0 0 0 0 0 0 0 0 0 0 0 0.0525796302 0 0 0 0 0 0 +Root|Taxa_7 0 0 54.88999104 50.5925030784 0 0 0 54.6763349395 0.1189223448 50.2393175313 0 0 0 0 0 0 54.6969237057 0 0 0.2640195362 0 0 0 0 0 0.0939317767 0 0 0 0.0068426746 0 0 0 0.1884530288 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_8 50.1672106262 0 52.3303780427 0 0 0 0 0.2608503236 51.234970106 53.8379188167 0 0 0 0 0 0 50.1581385397 0.0006708697 0 0 0 0 0 0 0 0 0 0 0 0 102.3508238 0 0 0 0 0 0 0.2592061142 0.2733698621 0 0 0 0 0 0 0 0 0 +Root|Taxa_9 0 0 54.2954191601 0.3900482594 53.497720447 0 52.8536155873 0 0 0 0 0 0 53.0705186084 53.9973207505 52.56799841 52.4336682846 0 0.4420715363 0 0 0 0 0 0 0 0.206258829 0 0.2217424642 0 102.360729463 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_10 0 52.0300437903 0 0 50.8347498672 0 54.6878325497 0 52.7748472684 0 0.1728499078 0 0 0.3494023242 0 53.2923245881 52.0944101248 0 0 0 0 0 0 0 0 0 0 0 0 0 103.408803256 0 0 0 0 0 0 0 0 0.2798474092 0 0 0 0 0 0 0 0 +Root|Taxa_11 0 0 0 0 0 50.2266430142 0 0 0 0 54.8005510793 0 0 0.2614548375 54.6340123255 0 53.3054007305 0 0 0 0 0 0 0 0 0 0 0 0 0 104.297495933 0.4147771248 0 0 0 0 0 0 0 0 0 0 0 0.2095019599 0.2636646004 0 0.14962244 0 +Root|Taxa_12 0 51.2451248947 0 0 52.1894997513 0 0 0 0.1485402206 0 0 51.4578345044 0 0 0 53.7144038587 52.4887800004 0 0.4912827723 0 0 0.0408387312 0 0.1298874355 0 0 0 0 0.406059988 0 100.324803499 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1119222201 0 0 +Root|Taxa_13 0.3049766554 0 0 0 0 0.2577053654 0 0.0339606845 54.7690836618 50.697970653 0 0 53.8640699725 0 52.0035776407 0 53.6428696687 0 0 0 0 0 0 0 0.4254096216 0.2249415069 0 0.1878483248 0 0 102.107460191 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_14 0 52.2259912051 0 50.2457134484 0 0 0 50.1864151028 0 0 0 0 0 0 0 0 50.9709023399 0.2492557784 0 0 0 0 0 0 0 0 0 0 0 0 101.397444005 0 0 0 0 0 0 0 0 0 0 0 0 0.1555731341 0 0 0 0 +Root|Taxa_15 0 0 0 51.4281949608 52.6962274694 0 0.3201392865 0 0 54.7756797792 0 50.5018107598 54.1937447792 53.2064670747 0 0 54.2549432464 0 0 0 0 0 0 0 0 0 0 0 0 0 102.286601935 0 0 0 0 0 0 0 0.4705889836 0 0 0 0 0 0 0 0 0 +Root|Taxa_16 51.0029739062 0 0 0 51.0607335169 53.2862118709 0 0 0 51.9134794272 54.854099933 0 0 0 54.0630985892 54.5356379274 0 50.5822786088 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_17 0 0 0 0 0 0 0.2048863855 0 0 0 0 0 0 0 0 0 0 53.088151158 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0350232592 0 0 0 0.3510405665 0 0 0 0 0 0 0 0 +Root|Taxa_18 0 51.9909876648 54.2742154768 53.1653297993 0 0 50.8771925078 50.9077149464 54.9729403134 0 0.1353582563 0 0 50.2703191505 0 0 0 53.4546555778 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1975913675 0 0 0 0 0 0 0.252506998 0 0 0 0 0 0 0 0 0.3668075477 +Root|Taxa_19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 53.2084286662 0.0317855187 0 0 0 0 0.0731542793 0 0.279365334 0 0 0 0.3232868601 0 0 0 0 0 0 0 0 0 0 0 0 0 0 51.3273493399 51.7950595639 51.5148741732 50.1446039379 +Root|Taxa_20 0 0 0 54.2901512603 0 52.5606309423 52.3731376332 54.4526691389 0 0 0 0 0 0 52.0984154658 0 0 51.7513288419 0 0 0 0.3622193485 0 0 0 0 0 0 0 0 0.1691945034 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_21 50.2825468404 0 0 0 0 0 50.9821712326 0 0 0 0 51.3809679697 0 52.2721137248 0 0 0 50.870859988 0 0 0.1977675671 0 0 0 0 0 0.4552416279 0 0 0 0 0.3435864481 0 0 0.4880371356 0 0.0536765879 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_22 0 0 0 0 52.3029086027 52.5983394759 54.5052639743 0 0 54.1888891315 50.0266182403 50.2221118781 50.6633223951 51.3579357417 0 52.1345899387 0 53.3732609456 0 0 0 0 0 0.3609204935 0 0 0 0.4993218153 0 0 0 0 0 0 0 0 0 0 0.1591353836 0 0 0 0 0 0 0 0 0 +Root|Taxa_23 54.6156171564 0.4886273801 0 0 0 0 0 52.5107503356 52.9460850983 0 0.3957098383 0 0 0 0 0 0 52.6539251409 0 0 0 0 0 0 0 0 0 0 0 0.2303307173 0.2311735324 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0630202217 +Root|Taxa_24 52.5265835651 0 0 0 0 0 0 51.6181213067 0 52.5277460465 52.5621645367 0.4212381405 0 0 0 0 0 50.0368297309 0 0 0 0 0 0 0 0 0 0 0 0 0 102.75434916 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_25 0 0 0 0 0 0 0 0.1372806711 0 0 0 52.0166897294 0 0 0 0 0 53.9284449204 0 0 0 0.2143980699 0 0 0 0 0 0 0 0 0.4006338331 100.175399372 0 0 0 0 0 0 0 0.460244722 0 0 0 0 0 0 0 0 +Root|Taxa_26 0 54.7808897118 0 0 0 0 0 0.2249763103 0 0.4844513576 0 0 0 0 50.6980071274 0 0 51.8963562506 0 0 0 0 0 0 0 0 0 0 0 0 0 104.480318071 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_27 0 52.1818437283 0 0.0091068777 0 0 0 0 0 0 0 0 0 0 0 0 0 52.0711732439 0.3445929637 0 0 0 0 0 0 0 0 0 0 0 0 101.492137287 0 0 0.2358311064 0 0 0.3478219918 0 0 0 0 0.0292885238 0 0 0 0 0 +Root|Taxa_28 0 0 52.0901812605 52.2730517877 50.0978668154 0 0 0 0 0 0 0 51.1042973997 50.3396791485 0 51.8579185538 0 54.1478490943 0 0 0 0 0 0.1924004269 0 0 0 0 0 0 0 103.860589722 0 0 0 0 0.2958273103 0 0 0 0 0 0 0 0 0.2432510884 0 0 +Root|Taxa_29 0 0 50.6253941664 0 0 0 0 0 51.4636945289 0 0 0 0 0 0 0 0 52.4434570833 0 0 0 0 0 0 0 0 0.0206172628 0 0.4787711449 0 0 103.664216721 0 0 0 0 0 0.0896190717 0.0482937601 0.3775884668 0 0 0 0 0 0 0 0 +Root|Taxa_30 0 0 0 53.2702698702 0 50.9530439909 0.3906002073 0 52.564962527 0 0 0 50.5342042489 0 0 53.3525431291 0.3955450675 52.1377811142 0 0 0 0 0 0 0 0 0 0 0 0 0 104.569105127 0 0 0 0 0 0.0711511496 0 0 0 0.2969135198 0 0 0 0 0.0820027166 0 +Root|Taxa_31 0 53.4160907647 50.2412640143 0 52.7503772737 0.266001 0 0 0 51.4156779274 52.3495772244 50.4296303411 53.0746552584 0.2312726671 50.1591453136 0 0 52.6740375005 0.0684119121 0.3554946578 0.22106748 0 0 0 0 0 0 0 0 0.4044619717 0 103.683580625 0 0 0 0.1031302462 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_32 0 50.5756083483 0 0 52.0313756582 53.7110562743 52.3907208953 53.8846639811 0 0 0.0046756583 0 52.4749313611 0.2397819191 0 54.0544476992 0 0 54.8355397676 0.1297890121 0 0 0 0 0 0 0.4593982017 0 0 0 0 0 0 0 0 0.3506631122 0.4629464165 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_33 0 0 0 50.878130145 0.4999058566 0 0 0 0 51.3518277314 0.3761236625 0.3944068153 0 0 0 0 0 0 50.3174548238 0 0 0.1504482951 0 0.127140027 0 0 0 0 0 0 0 0 0 0.2548248911 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_34 53.3206087196 0 0 0 0 53.3594257978 54.7550426596 0 0 0 0.2037470024 54.1086095437 0 0 0 0 0 0 54.1113517717 0.3138467421 0 0 0 0 0 0 0 0 0 0 0 0.3490515927 0 0 0 0 0.1981740901 0 0 0.1208211414 0 0 0 0 0 0 0 0 +Root|Taxa_35 51.5748239395 51.8559498273 0 54.0804785553 0 0 0 0 0 0 54.739358927 53.3751470718 53.0003988321 50.9511660413 0 0 0 0.4262712797 51.4526566441 0 0 0 0 0 0 0.2979733489 0 0 0.4111736556 0 0 0 0 0 0 0 0 0.0689009855 0 0 0 0 0 0.380858705 0.3682137697 0 0.346123284 0 +Root|Taxa_36 0 0 0 0 51.7955322468 50.7825938818 0.0748252557 0 0 54.9524236749 0 0.3548118535 51.8703689184 0 0 51.0568246533 0 0 53.3875472963 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0529775051 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_37 0 0 52.9933793712 0 0 0 54.6905079521 0 52.7922091401 0 0 0 0 0 50.3044859311 0 0 0 54.747541461 0 0 0 0 0 0 0.0457887715 0.3809741474 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2279874575 0 0 0 0 0 0 +Root|Taxa_38 0 0 51.4270643055 0 54.6785989992 0 54.8206987373 0 53.9385865565 0 0 0 0 51.4129548343 53.1265043809 0 0 0 54.0071260493 0 0 0 0 0.0939906902 0.3673230157 0 0 0 0 0 0 0 0 0 0 0 0.4789676492 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_39 0 0 0 0 0 0 0 0 0 0.2387940384 0 0 0 0 0 0 0 0 54.1627418823 0 0 0 0 0 0 0.2795966978 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_40 0 0 0 50.0210852211 0 0 0 0 0.2150901129 0 0 0 0 52.1867569927 53.6264651202 0 0 0 52.9491874829 0 0 0 0 0 0 0 0 0.2507515189 0 0 0 0 100.862557832 0 0 0 0 0 0 0 0 0 0 0 0 0 0.210881571 0 +Root|Taxa_41 51.4977031121 52.9164968998 0.3534159391 0 0 0.1157486694 0 0 51.7018934192 0.1345887328 0 51.9984923274 0.3081525427 0 0.0615519713 0 0 0 53.3008194045 0 0 0 0 0 0 0 0 0 0 0 0 0 104.430795814 0 0 0 0 0 0 0.2867404482 0 0 0 0 0 0 0 0 +Root|Taxa_42 0 0 0 0 0 0 0 50.1007007713 0 54.9461220565 0 0 0 0 0 0 0 0 53.0375057479 0 0 0 0.4319383958 0 0.171622271 0 0 0 0 0 0 0 102.196402398 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_43 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 51.0446210947 0 0 0 0 0 0 0 0.0792792689 0 0 0 0 0 104.880373605 0 0 0 0 0 0 0 0 0 0 0 51.3715760597 52.9044112969 51.740910549 50.5004381267 +Root|Taxa_44 0 53.9514218116 53.7974560811 0 0 0.2172397029 0 54.4604334223 0 0 54.6862061224 0 0 0 0 0 0 0 53.1944500806 0 0.1702076899 0 0 0 0 0 0 0 0.1000241075 0 0 0.4379056345 102.222128629 0 0 0 0 0 0 0 0 0.0702419447 0 0 0 0 0 0 +Root|Taxa_45 0.4414396255 0 0 0 0 53.3442434459 0 0 52.0433499114 0 0 0 0 0 0 0 0 0 51.0099383616 0 0 0 0 0 0 0 0 0 0 0 0 0 102.926221483 0 0 0 0 0 0 0 0 0.0083155843 0 0 0 0 0 0 +Root|Taxa_46 50.8759769798 0 53.2075254666 50.6746294731 51.236316237 0.0529739748 0 0 0 51.7118414842 52.4785335361 0 0 0 52.8668573736 52.6248831998 0 0 53.2532692581 0 0 0 0.2068341838 0 0.0509557931 0 0 0 0 0 0 0 101.581986405 0 0 0.3929810805 0 0 0 0 0.4084069824 0 0 0 0 0 0 0 +Root|Taxa_47 0 0 0 0 0 0 0 51.7620853649 0 0 51.3185303073 50.1321408484 51.7645853205 53.2729757957 0 53.2368893169 0 0 54.6339039774 0 0 0 0 0 0.2721644329 0 0 0 0 0 0 0.1529640426 102.170619684 0 0 0 0 0 0 0 0.4808707791 0 0 0 0.0259939588 0 0 0 +Root|Taxa_48 52.4267089962 0 0 54.1214115467 50.0928272167 0 53.4772363871 54.7860576144 0 0 0 51.9697613142 0 0 0 0 0.2912755603 0 0 52.4029555007 0 0 0 0 0 0 0 0 0 0 0 0 0.1730979361 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_49 0 54.141972569 0 0 0 0.3109906772 0 0.2574068121 0 0 0 0 51.8320818925 0 0 0 0 0 0 54.6830485117 0.2736483385 0 0 0.1530487213 0 0 0 0 0 0 0 0 0 0 0 0.4823438429 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_50 0 0.4124418207 52.6693177921 0 0.4591352641 0 0 50.0076295469 0 0 0 0 0 0 0 0 0.0891656276 0 0 52.060661218 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.456119945 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_51 0 0 0.4290217531 0.3651461426 0 52.193712592 0 0 0 0 0 54.8820040231 50.9889836189 0 0 0 0 0 0 53.729079995 0 0 0 0 0 0 0.1077332822 0 0 0 0 0 0.2396906645 0 0 0 0 0.1208910872 0 0 0 0 0 0 0 0.1170604419 0 0 +Root|Taxa_52 54.8376652197 0 50.6807988659 0 0 0 50.2423356742 0 0 53.1215806153 0 0 53.0799270537 0 0 0 0 0 0 51.035020958 0 0 0 0 0 0 0 0.2183915943 0 0.0377484647 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_53 0.4082266649 53.8883783744 0.4415559686 0 0 54.6386307308 0 0 51.2693532809 0 54.6221640864 0 0 0 0 0 0.3041499927 0 0.3724731651 54.412795112 0.1367159827 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3980338784 0 0 0 0 0 0 +Root|Taxa_54 0.3273113474 0 0 54.926390833 0 0 0 0 0 0.3088645786 0 0 0 0.0605177339 0 50.044487362 0 0 0 54.6371458056 0 0 0 0 0 0 0.4771918399 0.2768604633 0 0 0 0 0 0 0 0 0 0.0335516302 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_55 0 0 50.0656052232 0 0 0.0179441975 0 0 0 0 0 0 0 0 0.138961788 54.9330831299 0 0 0 52.5769169001 0 0 0 0 0 0 0 0.1782178292 0 0 0 0 0.3407637923 0 0 0 0 0 0 0 0.4782188759 0.4542681566 0 0 0 0.4875039412 0 0 +Root|Taxa_56 54.5989539227 0 0 0 53.2426950573 50.250740261 0 0 0 53.9546687981 0 0 0 53.8898181275 52.0200822419 0 0 0 0 50.1281331335 0 0 0 0.3264303961 0.2427488814 0 0 0 0 0 0 0 0.2980135425 102.341991379 0 0 0 0 0 0 0 0.2057570112 0.1926807528 0 0 0 0 0 +Root|Taxa_57 0 51.354416492 0 0 0 50.4552746731 0 0 53.9138353446 53.4965113987 0 0 0.4402856364 0 50.9875219614 0 0 0.2843309221 0 52.8337602214 0 0 0 0 0 0 0 0 0 0 0 0 0 100.009745288 0 0 0 0 0 0 0 0 0 0 0 0.484674028 0 0 +Root|Taxa_58 0.4370935688 0 0 0 0 0 0 53.5695657696 53.5922767311 0 52.4207781953 0 0.0828238371 54.0436231227 0 54.1439337916 0 0 0 53.6109641407 0 0 0.247319441 0 0 0 0 0 0.1293668424 0 0 0 0 102.817247688 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_59 54.1877803261 54.8734852288 51.9631978735 0 53.7893179854 0 0 0 0 0 0 52.9662052797 0.3843803475 0 0 0 0 0 0 50.5938366041 0 0 0 0 0 0 0 0 0 0 0 0 0 103.811586753 0 0 0 0 0 0 0 0.1697259821 0 0 0.4337967626 0 0 0 +Root|Taxa_60 0 0 0 51.6462568319 0 0 52.1950606158 52.2145629515 53.8111850302 54.6277290472 0 0 0 0.4097991131 0 0 0 0 0 54.4787895985 0 0 0 0 0 0 0 0 0 0 0 0 0 100.148275143 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_61 0 0 0 0 0 0.2040732361 0 0 0 0.2783150788 0 0 0 50.5350340247 51.0309419327 0 0 0 0 51.8822943576 0 0 0 0 0 0 0 0 0 0 0 0.0622838924 0.3612372393 102.431353618 0 0 0 0 0.1492745202 0 0 0 0 0 0 0.1621554116 0 0.4346019485 +Root|Taxa_62 0 0 0 50.6703344071 0 0 54.4857512291 0 0 0 54.0871158771 50.599265154 53.7767317456 0 0 51.3898411169 0 0 0 50.3643983668 0 0 0 0 0 0 0 0 0 0.4853324113 0 0.4473445252 0 102.425622639 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_63 0.4529478175 0 0 0 54.2379721914 0 0 0 0 0 53.7525110896 0 0.4360800234 53.3175262291 54.2646984279 0 0 0 0 54.0018428816 0 0 0 0 0 0 0 0 0 0 0 0 0 103.879540704 0 0 0 0 0 0 0 0 0 0 0.200286561 0 0 0 +Root|Taxa_64 0 0 0 0 0 0 0 51.0672277557 52.7285706827 0 0 0 0 0 0 0 0 0 0 0 53.3133856033 0 0 0 0 0.122468849 0 0 0 0.2195248661 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4512989848 0 0 +Root|Taxa_65 0 0 0 0 0 54.2826532163 0.0546722913 50.5873967928 0 0 0 0 50.7408726249 51.9223310392 0 51.4656070102 0 0 0 0 50.605962135 0 0 0 0 0 0 0 0 0 0 0.0313052349 0 0.0248505033 0 0 0 0 0.1801267876 0 0 0 0 0 0 0 0 0 +Root|Taxa_66 0 0 0 0 50.1694459571 0 0.2098148433 0 54.8130942577 52.1816782427 0 0 0 0 0 0.0087912135 0 0 0 0 52.4555738805 0 0.1946081847 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.231151394 0 0 0 0 0 +Root|Taxa_67 53.0846900346 54.412517058 54.5238051166 0 0 0 0 0 0 54.595576556 0 0 0 0 0 0 0 0.2415041929 0 0 54.7069813167 0.2996548558 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0425357392 0 0 0 0.4645055552 0 0 0 0 +Root|Taxa_68 51.3995469362 0 0 50.331795309 51.0448312155 53.6201946543 0 0 53.4099339516 0 50.770176676 54.2683984233 0 52.1643426732 54.0442901397 0.1032379248 0 0 0.3522214367 0 54.4540657102 0 0.110175445 0 0 0 0 0 0 0 0 0 0 0.433013846 0 0 0 0 0.1886158766 0 0 0 0 0 0 0 0 0 +Root|Taxa_69 50.9648348214 53.6736175822 51.590495556 51.8412088782 0 54.2987533453 52.3634972313 0 0 0 51.151811413 53.7359358267 0 0 54.7917255302 0 0.4325211547 0 0 0 53.3545695108 0 0 0 0 0 0 0.069227689 0 0 0 0 0 0.3882377702 0 0 0 0 0.2976571254 0 0 0 0.0263550726 0 0 0 0 0 +Root|Taxa_70 0 0 0 0 50.7217921254 0 54.7066155956 0 0 0 0 0 0 0 0 0 0 0 0 0 50.6315693012 0 0 0 0 0 0 0 0.3724946615 0 0.2673542736 0 0 0.3328376298 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_71 0.1620439572 0.0674540068 0.0359000069 0 0 52.036928436 0 0 0 0 53.2771147688 0 0 50.2197978468 0 0.1980547765 0 0 0 0 52.6803755712 0 0 0 0.4341710263 0 0 0 0 0 0 0 0 0.2646443485 0 0 0 0 0 0 0.1923538669 0 0 0 0 0 0 0.0887843003 +Root|Taxa_72 0 0 0 0.208477186 0.191122797 0 53.3877940796 0 0 0 0 54.8605306811 52.8806578443 0.4634942747 0 53.7859248539 0 0 0 0 54.0390981091 0 0 0 0.2015376913 0 0 0 0 0 0 0 0 0 104.81826701 0 0 0 0 0 0 0 0.470124035 0 0 0 0 0 +Root|Taxa_73 0 0 54.0543538675 0 0 0 0 0 0 0 0 0 52.5074922807 0 51.8833322526 0 0 0 0 0 50.7807937909 0 0 0.232942393 0 0 0.1220451999 0 0 0 0 0 0 0.2828927528 100.872762323 0 0 0 0 0 0 0 0 0.4539101431 0.443320587 0 0.1132961507 0.3185656437 +Root|Taxa_74 0 0 54.3809381001 0 0 0 0 0 0 0 0 0.2304558325 0.3130208717 0 0 0 0 0 0 0 53.825957476 0 0 0.0572096867 0 0 0 0 0 0 0 0 0.0168392183 0 103.904238553 0.311377563 0 0 0 0 0.4866264421 0 0 0 0 0.3384127287 0 0 +Root|Taxa_75 0.4307504192 51.7633538316 0 0 51.4215501537 0 0 53.6131499522 0.0308805275 0 0 0 52.3514348753 0 0 0 0 0.1696651501 0 0 53.9066979693 0 0 0.1281638803 0 0 0 0 0 0 0 0 0 0 102.958441755 0 0 0 0.0609186756 0 0 0 0 0 0 0 0 0.0993970743 +Root|Taxa_76 0 0 0 0 0 0 0 0.1206515095 0 51.8281885745 0.1220819529 0 0 0 0 50.8290781889 0 0 0 0 54.9709648675 0 0 0 0 0 0 0 0 0 0 0 0 0.4297384241 104.083448416 0 0 0 0 0 0 0 0 0 0 0.3426330327 0 0 +Root|Taxa_77 0 0 0.3601206776 53.0516805067 0 0 0 0 0 0 52.427138658 53.0537708068 0 0 51.9809609217 51.4358398959 0 0 0 0 53.8140262772 0 0.2518970133 0 0 0 0 0 0 0.4379598295 0 0 0 0 101.154787919 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_78 0 50.9977659171 0.205815553 50.3018855792 0.2485134475 0 54.8612742676 0 0 54.7417532517 0 0 0 0 0 0 0 0 0 0 53.2633197891 0 0 0 0 0 0 0 0 0 0 0.1158681467 0 0 103.568400155 0 0 0 0.2837734628 0 0 0 0 0 0 0 0 0 +Root|Taxa_79 53.2388661668 0 0 0 0 0 0 54.4991602286 52.8373936886 0 0 0 0 50.3435784568 0 0 0 0 0 0 53.1761666666 0.179916926 0 0 0 0 0.3216605414 0.0902658359 0 0 0 0 0 0 102.053964001 0 0 0 0.3941161454 0 0.4024567987 0 0 0 0 0 0 0 +Root|Taxa_80 54.7892618987 54.5318181169 0 0 52.5024244557 0 0.2824605846 54.1520416938 0 51.6347900016 54.2899104182 52.9606082444 51.9671909833 0 0 53.5378565436 0 0 0 0.1663700836 0 50.401981588 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_81 0 51.8337862969 54.4049970921 0 51.8557420992 52.5229095783 0.0436287387 0 54.5758600794 0 0 0.191168701 0.0239379812 51.9482488619 0 0 0 0 0.4139979892 0 0 52.9118524132 0 0 0 0 0 0 0 0.366252635 0 0 0 0 0 0 0 0 0 0 0 0 0.3986870557 0 0 0 0 0 +Root|Taxa_82 0 0 0 0 0 0 50.3984918842 0 0 0 54.4976071698 0 0 0 51.0106416962 0 0 0 0 0 0 52.0283746591 0 0 0 0 0 0 0 0 0.1361657687 0 0 0 0 0 0 0 0 0 0 0 0 0.0758464948 0 0 0 0 +Root|Taxa_83 0 0 0 50.3794761616 0 52.7343387153 53.0193553248 51.2257869732 0.0006319949 0 0 0.2720348266 52.9907821351 0 0 0 0 0.2233355579 0 0 0.1050158817 54.9458704446 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2172519645 0 0 0 0 0 0 +Root|Taxa_84 0 0 0 0 0 0 0 0 54.1408497487 52.8337996475 0 0 0 0 0 0 0 0 0 0 0 51.6022529984 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_85 0 52.3235728903 0 0 0.3320989701 0.4248255219 0 0 51.7466072582 50.4503592933 0 54.5914510381 0 0 0 0 0 0 0 0 0 52.4345973615 0 0 0 0 0 0 0 0 0 0 0 0.2927939436 0 0 0 0 0 0 0 0 0 0 0 0.0847785671 0 0 +Root|Taxa_86 0 0 0 0 0 0 0 50.8900286515 0 0 0 0 0 0 0 0 0 0 0 0 0 54.7211930279 0 0 0 0.1860511137 0 0 0 0 0.4099157235 0 0.2539220727 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_87 54.1801851168 0.2755052968 54.4929431324 53.8377295485 0 0 0 52.4040288729 0 0 0 54.7801295644 54.0400263047 0 0 0.1966747301 0 0 0 0 0 51.8162873023 0 0 0 0 0 0 0 0 0 0 0.4679406891 0 0 0 0.2959793419 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_88 0 0 0 53.9390998937 0 51.5633425952 51.2958523861 0 0 53.5152813263 0 0 0 0 54.4016767058 53.2390333136 0.4146855389 0 0 0 0 54.4130820735 0 0 0 0 0 0 0 0 0 0 0 0 0.0204961504 101.326823224 0 0 0 0 0 0 0 0 0 0.1066233614 0.0363300378 0 +Root|Taxa_89 53.0543892529 0 0 0 0 0 53.674266645 0 0 0 0 50.7712795605 0 0 0.1019179568 0 0 0 0 0 0 53.7975387016 0 0.0977898128 0 0 0 0 0 0.0993452942 0 0 0 0 0 100.997769091 0.3619621508 0 0 0 0.3904948159 0 0 0.0872044741 0 0 0 0 +Root|Taxa_90 0 0 50.8115416489 0 50.754506055 0 0 0 52.3941131969 0 0 0 51.8243341099 0 0 51.3772399507 0 0 0 0 0 52.4678412328 0 0 0 0 0 0 0 0 0 0 0 0 0 100.520314429 0 0 0.2133005765 0.1264533195 0 0.1775747368 0 0 0 0 0 0 +Root|Taxa_91 0 0.1440747388 0 52.2420346752 0 0 0 0 0 0 51.306304352 0 0 54.5023389653 51.6143705277 0 0 0 0 0 0 53.2618330615 0 0 0 0 0 0 0 0 0 0 0 0 0 103.306676868 0 0 0 0 0 0 0 0 0 0 0.2591604764 0 +Root|Taxa_92 0 54.6210630241 0 0 53.5305422245 0 0 0 0 0 54.3568545147 0 0.3760358617 53.614501565 0 0 0 0 0 0 0 50.1740916731 0 0 0 0 0.1862491638 0 0 0 0 0 0 0 0 102.868597317 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_93 0 0 52.1871299484 0 0 0 0 0.4391172051 0 0 0 0.376050992 0 0 0 0 0 0 0 0.2089241073 0.1723162287 51.9279788044 0 0 0 0 0 0 0 0 0 0 0 0 0 103.481228475 0 0 0.2200556727 0 0 0 0 0 0 0 0 0 +Root|Taxa_94 0 0 0 0 0 0 0 0 0.3256636158 0 0 0 0 0.364965086 0 0 0 0 0 0 0 53.1229798984 0 0 0 0.1559587637 0.4001034299 0 0 0 0.3262818835 0 0 0.2076639482 0 101.02718778 0 0 0 0 0.2707225645 0.417074225 0 0 0 0 0 0 +Root|Taxa_95 50.6928865646 0 0 0 0 53.8301341769 0 0 0.0103564044 0.3986304957 0 0 0 54.2377241737 52.3696486305 53.0287787824 0 0 0 0.0081133227 0 51.3118465046 0 0 0 0 0 0 0 0 0 0 0 0 0 100.436094872 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_96 0 0 0 0.0306082277 0 0 53.4258105193 0 0 53.4595268013 0 0 54.111068736 0 0 0 0 0 0 0 0 0 51.1151885409 0 0 0 0 0 0 0 0 0.0718316031 0 0 0 0.0856188686 0 0 0 0 0 0 0 0 0 0.1088778111 0 0 +Root|Taxa_97 54.0131831358 0.3162587064 0 0 0 0 0 0 53.5541897884 0 50.8535862291 0 0 0 52.5365058191 0 0.1993715632 0 0 0 0 0 51.2674686071 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3301504415 0 0 0 0 0 0 0 +Root|Taxa_98 0 0 53.7149011582 0.4173513921 51.1073102759 52.9646951786 50.2964122406 50.8655941987 0 0 50.6709458088 0 0 52.5329187655 0 0 0 0 0.1295957195 0 0 0 50.697495499 0 0 0 0 0 0 0 0 0 0.4731593744 0 0 0.1248322964 0 0 0 0 0 0 0 0.2822300821 0 0 0 0.4313599044 +Root|Taxa_99 0 0 0 0 53.9606157979 0 0 0 53.2300749713 0 0 53.3830729082 0 0 0 0 0 0 0 0.2475868847 0 0 52.8931864333 0 0 0.4867202557 0 0 0 0 0 0 0 0 0 0.3688932196 0.0563714613 0 0 0 0 0 0 0 0 0 0.3712734193 0.4418757976 +Root|Taxa_100 50.056868693 53.8826557038 51.7558450346 0 0 0 0 0 52.1246108797 0 0 0 52.5289140861 0 0 0.1157214473 0 0 0 0 0 0 54.1241903946 0 0 0 0 0 0 0 0 0 0 0 0.2108212732 0 0 0 0 0.0601948469 0 0 0 0 0 0 0 0 +Root|Taxa_101 0 53.2749401798 0.3457314983 0 0 54.4227088415 0 0 0 0 52.6300612332 0 54.4154008017 0 0.0671882326 0 0 0 0 0 0 0 53.1007193022 0 0 0 0 0 0 0 0 0 0 0 0.0900018692 0 0.175114791 0.473746692 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_102 52.6950430067 0 0.4354763357 0 54.9986822481 0.053637061 0.3994923336 0 0 0 0 51.4154828937 0.4664348065 53.2002848034 51.817857542 0.2712106096 0 0 0 0 0 0 54.8655468121 0 0.3710474144 0 0 0 0 0 0 0 0 0.3822684551 0 0 0.006423485 0 0 0 0 0 0 0.0162876791 0 0 0.1017937381 0 +Root|Taxa_103 0.3425931453 52.9993627033 0 0 0 0 0 0 0 0 0 52.3092110352 0 0 0.2767992224 0 0 0 0 0 0 0 50.5254117447 0 0 0 0 0 0 0 0 0 0.015074463 0 0 0 0 0 0 0 0.3959814799 0 0 0.1943574615 0 0 0 0 +Root|Taxa_104 0 0 0 53.4713033884 54.3579475688 52.6955685823 50.7596515057 0 0.1731414916 52.1442151554 0 0 0 50.3835988803 0 0 0 0 0 0.4060476246 0 0 50.733284076 0 0 0 0 0 0 0 0 0 0 0 0 0 104.214620416 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_105 0 54.7806460339 0 0 0 54.0826678158 0 0 0.0672647388 52.1675841037 0 0 0 53.9554537185 0 52.4303251482 0 0 0 0.2334350482 0 0.4098819176 54.7028473218 0 0.2667853728 0 0 0 0 0 0 0 0 0 0 0 101.795431091 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_106 0 0 52.5565456759 0 0 0 0 0 0 53.6619334569 0 0 0 0 0 51.3961540827 0 0.2140895941 0 0 0.2995571882 0.221779498 52.5708897858 0 0 0 0 0 0 0 0 0.1233866431 0 0 0 0 102.570605443 0 0 0 0 0 0.4118529029 0 0.3766700102 0 0 0 +Root|Taxa_107 54.5303707299 0 0 51.0579585274 0 0 0 0 0 0 52.9392581011 0 0 0 54.6683053936 0.3356273709 0 0.4393523569 0 0 0 0 53.8546643238 0 0 0.490563499 0 0 0 0 0 0.1035835818 0 0 0 0 101.68701225 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_108 0 0.0831253556 0 50.9377309168 0 0 0 53.5728270552 0 0 0 0 0 0 0 0 0 0 0 0.4250388828 0 0 50.474152487 0 0 0 0 0 0 0 0 0 0 0 0 0 101.552599433 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_109 0 0 0 50.7291731582 0.1487014569 0 50.3194645485 54.8863005781 51.0698065958 0 0 0.3224429939 54.070754096 0 0 52.748043897 0 0 0.2273795381 0 0 0 54.8240827687 0 0 0 0 0 0 0 0 0 0 0 0 0 104.106043157 0 0.4804401716 0 0 0 0 0 0 0 0 0 +Root|Taxa_110 0 0.2551561223 52.683061103 0 0 0 0 0 0.3193054438 0.2135924862 0 52.2430501794 0 0 51.1526587024 50.841997407 0 0 0 0 0 0 50.4151489286 0.1306208862 0 0 0.218684585 0 0 0.4787247711 0 0 0 0 0 0 102.8864492 0 0.2203846269 0.2282855691 0 0 0 0 0.0824536039 0 0 0.1350548782 +Root|Taxa_111 0 0 0 0.4533598523 0 0 0 54.0952514858 0 0.246414477 0 0 0 0 0 0.2734378482 0 0 0 0 0 0 54.3493366089 0 0 0 0 0 0 0 0 0 0 0 0 0 100.442925155 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_112 52.8796511547 0 0 0 0 0 0 0 52.8431676735 52.5435996612 0 0 0.25953187 52.1281290498 0 50.3434325139 0 0.2009750675 0.2712162231 0 0 0 0 52.6989684504 0.3289420094 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1775970696 0 0 0 0 0.2978109744 0 0 0 +Root|Taxa_113 0 0 0 0 53.8573505274 54.100587941 0 0 0 0 0 0 0.4647473082 0.0778451335 0 0 0 0 0 0 0 0 0 50.8835256873 0 0 0 0 0 0.0377493305 0 0 0 0 0 0.2198580664 0 0 0 0 0 0 0 0 0 0.0487963934 0 0 +Root|Taxa_114 0 0 0 51.3010891504 50.6235825947 0 0 0 54.3294385847 0 52.4915264128 0 50.2204943195 0 54.672706648 0 0.102503602 0 0 0 0 0.4953540541 0 53.5948742929 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2376060834 0 0 0 0 0 0 +Root|Taxa_115 0 50.3846457133 0.0453624707 52.2810809921 0 0 0 0 0 0 0.2962109532 52.356551925 0 0 0 53.2473750995 0 0 0 0 0 0 0 50.4842530361 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_116 0 0 50.7893793239 0 0 0 0 0 0 50.3047078447 52.7281058343 0 0 0 53.1172663716 0 0 0 0 0 0 0 0 51.5151956714 0 0 0 0 0 0 0.4667210276 0 0 0 0 0 0 0.0901319476 0 0 0 0 0 0 0 0 0.4911531549 0 +Root|Taxa_117 54.4214998557 51.706627646 0 52.7351162212 0 0 53.5635180636 50.6309452706 0 0 0 50.3299983355 0 0 0 0 0 0 0 0 0 0 0 50.3673919695 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0622798374 0 0 0 +Root|Taxa_118 0 0 0 50.050071526 51.2057127172 0.3221579782 0 0 54.6853641877 0 0 53.1801030862 0.3483857771 0 0 0 0 0 0.0251496664 0 0 0.4456743928 0.1322548859 50.3810409073 0.3577083992 0 0 0.1275077605 0 0 0 0 0 0 0 0.0602159539 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_119 51.922082888 0 0 0 0 54.6252344439 51.1848430863 0 0 52.4525135577 0.0056796712 52.6744844414 54.9720726431 0 0 54.1421484212 0 0 0 0 0 0 0 53.3926322948 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0022038827 0 0 0 0 0 0.1747333445 0 0 0 0 +Root|Taxa_120 0 0.0535385325 0 0 0 0 53.9885983831 51.1715526045 0 50.7359508767 0 0 0 0.057468086 0 0 0 0 0 0 0.1300280471 0 0 51.1346821396 0.0705595777 0 0 0 0 0 0 0 0 0 0.125181148 0 0 101.991997388 0 0 0.1003367696 0 0 0 0 0 0 0 +Root|Taxa_121 0.0008154986 0 0.3280314362 0 0.1820039777 0 0 0 0.4577127314 0 54.1095884096 0 53.524176304 0 0 0 0 0 0 0 0 0 0 50.2073699901 0 0 0 0 0 0 0.1840049668 0 0 0 0 0 0 102.909580903 0 0 0.181993672 0 0 0 0 0 0 0 +Root|Taxa_122 50.8681143585 0 51.320734447 0.0095748392 0 53.3636168816 0 0 0 0 0 0 52.1852430741 51.262329877 0 0 0 0.0622433267 0 0 0 0 0 53.7047465055 0 0 0 0 0 0 0 0 0 0 0 0 0 100.034546601 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_123 0 0 0 0 0 0 0 0 0.0133316433 0 50.8087609677 0 0.2245461255 0 0 0 0 0.4556854398 0 0 0 0 0 54.3209051793 0 0.3736911327 0 0 0 0 0 0 0 0 0 0 0 104.076577343 0 0 0 0 0 0 0 0.1563644404 0 0.1938368617 +Root|Taxa_124 0 54.159458256 0 0 0 0 0 0 52.4862177718 0 0 0 0 52.496544274 0 54.9051403008 0 0 0 0.4490948229 0 0 0 52.2518193792 0 0 0 0 0 0.0384124203 0.0511539478 0 0 0 0 0 0 101.020590712 0 0 0 0 0 0 0.3109116096 0 0 0 +Root|Taxa_125 0 0 54.433585035 0 0 0.4950654415 54.7477615553 0 0 0 0 0 0 53.1122769028 54.023921379 0 0 0 0 0 0 0 0 53.1890587578 0.4471825391 0 0 0 0 0 0 0 0 0 0 0 0 101.147356821 0 0.2035924438 0 0 0 0 0 0 0 0.1910229169 +Root|Taxa_126 0 52.1375779443 52.1865837244 0 0 52.1370101292 0.2221869542 54.704837352 0 0 0 0.2606668186 0 0 52.935749446 0 0.456212443 0 0 0 0.059009802 0 0 53.5136073383 0 0 0 0 0 0.2580221564 0 0 0 0 0 0 0 101.602167074 0 0 0 0 0.0384759088 0 0 0 0.0426162704 0 +Root|Taxa_127 0 0 0 0 51.6721722493 0 0 50.0454865499 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 51.0920304632 0 0 0 0 0 0 0 0 0 0 0 0 0 104.950389985 0 0 0 0 0.1667482347 0 0 0 0 0 +Root|Taxa_128 0.1331336535 0 0 0 0 0 0 0 53.1804152394 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50.4100979573 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4921252139 0 0 0 0 +Root|Taxa_129 0 0 0 0 0 0 0 0 0 0 0 0 53.299465996 0 50.8592848841 0 0 0 0 0 0 0 0 0 53.3264729709 0.0137091047 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4537700047 0 0 0 0 0 0.2579709315 +Root|Taxa_130 0 50.2573135552 54.3323301317 0 0 0 53.3188760975 52.486926787 0 52.1968656152 0 51.7574049244 0 52.9868373017 0 0 0 0 0 0.4673611098 0 0 0 0 53.0377205693 0 0 0 0.016580682 0 0 0 0 0 0 0 0.0335388511 0 0 0 0 0 0 0.1914559368 0 0 0 0 +Root|Taxa_131 52.0537010278 0 0 0 0.3107571236 0 0 53.7766034349 51.1158811827 0 50.8621611349 0 0 0 0 54.4723584233 0 0 0 0 0 0 0 0 54.7479994972 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4448196373 0 0 0 0 +Root|Taxa_132 0 0 0 0 0 0.172789176 0 50.8806545977 0 51.4070126504 0 0 51.5052899747 0 0 50.3473589335 0 0 0 0 0 0 0 0 53.4325984047 0 0 0 0 0.3105304607 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2583166482 0 +Root|Taxa_133 0 52.7654105634 0 50.0632713856 0 52.0514206023 50.6029858176 50.7861005586 0 52.4097818001 0 51.7341909396 0 52.0623266294 51.8588365816 53.9426808897 0 0 0 0 0 0 0 0 50.2340335763 0 0 0 0 0 0 0 0.2940476931 0 0 0 0 0.2795160082 0 0 0 0 0.4342558817 0 0 0 0 0 +Root|Taxa_134 54.276968809 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.237846912 0 0 0 0 52.4128774358 0 0 0.2731953573 0 0 0 0 0 0 0 0 0 0 0 0.0488747015 0 0 0.076239614 0 0 0 0 0 +Root|Taxa_135 0 0 52.7355757954 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1575747261 0 0 0 0 0 53.4331203286 0 0 0 0 0 0 0 0 0 0 0.4680498471 0 0 0 0 0 0 0 0 0.2991610257 0 0 0 +Root|Taxa_136 50.1663935409 50.8493833059 0 52.9072919333 0 0 0 0 0 0 52.6624415517 0 52.909339742 52.3463756458 0 0 0 0 0 0 0 0 0 0 52.9310570222 0 0 0 0.0192803969 0.1048063969 0 0 0 0 0 0 0 0 103.643993471 0.3463790228 0 0 0 0 0 0 0 0.4044988888 +Root|Taxa_137 0 0 50.3271812384 54.6107876926 54.705408862 0 53.3726497122 0.4384243337 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 54.8353816372 0 0 0 0 0 0 0 0 0 0 0 0 0 104.59642024 0 0 0 0 0 0 0 0 0 +Root|Taxa_138 0 0 0 0 50.3508366293 53.6026619085 0.2754542254 0 0 0 0 50.1171609828 50.1887506526 0.3372854117 0 0 0 0 0 0.111696425 0 0 0 0 53.1285946944 0 0 0 0 0 0 0 0 0 0 0 0.1244802407 0 102.391488906 0 0 0 0 0 0 0 0 0 +Root|Taxa_139 0 0 0 50.0851651917 53.0605762996 0 0 0 0 0.129094429 0 0 0 52.6006306021 0 0 0 0 0 0 0 0 0 0 54.3550166147 0 0 0 0 0 0 0 0 0 0 0 0 0 102.410658174 0 0 0 0.1707530269 0 0 0 0 0 +Root|Taxa_140 54.9509896394 0.2643862525 0 0 0 53.3036799045 0 0.4734565241 0 51.9407821206 54.7631097044 0 0 0 53.214956066 0 0 0 0 0 0 0.4481639127 0 0 52.6356438545 0 0 0 0 0 0.4017381726 0 0 0 0 0 0 0.342668534 101.128726959 0 0 0 0 0 0 0 0 0 +Root|Taxa_141 0 0 50.4557417684 0 0 0 0 0 0 0 0 53.7723044977 0 0.4715730335 0 0 0 0 0 0 0.4829783113 0.2970710556 0 0 53.8805006607 0 0 0 0 0 0 0 0 0 0 0 0 0 101.641222915 0 0 0 0 0 0 0 0 0.3372654319 +Root|Taxa_142 0 52.6370559114 0 0 51.2203811896 54.0425062776 51.7800027177 0 54.9863155754 0 0.0443837671 0 0 0 0 0 0.1782547418 0 0 0 0.0976464757 0 0 0 50.5885233525 0 0.2188058424 0 0 0 0 0 0 0.3333792751 0 0 0 0 102.063304696 0 0 0 0 0 0 0 0 0 +Root|Taxa_143 0 0 0.2903019444 0.355206109 0.3868817776 0 0 0 52.350206994 0 50.6961451488 0 0.3403821611 0 52.3388756388 53.2885492361 0 0 0 0 0 0 0 0 54.0373695539 0 0.4911168798 0 0 0 0 0 0.4874580754 0 0 0 0 0 102.204406541 0 0 0 0 0 0 0 0 0 +Root|Taxa_144 0 0 51.8506264628 0 0 0 0 0 0 0.1037747282 0 0 52.8869772838 0 52.2264077052 0 0.1189331919 0 0 0 0 0 0 0 0 51.8338015039 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3940431239 0 0 +Root|Taxa_145 0 52.7823443475 0 0 0 0 52.4609240421 0 0 0 50.0987051436 0 0 50.968244462 0 50.9263535704 0 0 0 0 0 0 0 0 0 54.8807366247 0 0 0 0 0 0 0 0 0 0 0.4132385692 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_146 0 0 0 0 53.7530389298 52.9631250672 0 0 53.5665153562 0 0 52.9559048601 0 0 0 0 0 0 0 0 0 0 0 0 0 50.5760856857 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.0801642474 0 0 0 0 +Root|Taxa_147 0 0 54.2907613529 0 0 0 0 54.286398784 0 0 0 52.2872431861 0 0 0 52.0786440961 0 0 0 0 0 0 0 0 0 50.8528614118 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_148 0 0 0 0 0.1047768904 0 0 0 0 0 0 0.2939648053 0 52.4551515426 0 0 0 0 0 0 0 0 0 0 0 53.4002141385 0 0 0 0 0 0 0.1929436104 0 0 0 0.1053827705 0 0 0 0 0 0 0 0 0 0.4051583407 0 +Root|Taxa_149 54.2777035359 0 0 54.187394364 0 0 0 0 0 53.443367591 51.4130125124 0 0 0 0 0 0 0 0 0.4145177727 0 0 0.1005042838 0 0 53.2067688306 0 0.3207376689 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_150 0 0 0 0 0 0 0 0.0612681294 0.4244032829 50.5626683266 0 0.0111672322 53.9739733465 54.2175708967 0 0 0 0 0 0 0.3781355911 0 0 0 0 51.471972695 0 0.3851659467 0 0 0 0 0 0 0 0 0 0.3979675191 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_151 0 0 0 52.7431585634 0 0 0 50.2017836015 0 0 0 53.0748043966 50.5224853183 0 0 0 0 0.1754280507 0 0 0 0 0 0 0 52.4546382453 0.3528670196 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3672557933 0 0 0 0 0 0 0 +Root|Taxa_152 53.0678431488 0 0 0.0546989371 0 0 51.2315217005 0 0 0 0 50.4114866073 0 53.7070251388 52.7692539832 0 0 0 0 0 0 0 0 0 0 50.3134097985 0 0 0 0 0 0 0.1112163285 0 0 0 0 0 0 102.15706607 0 0 0.0546144515 0 0 0 0 0.411126339 +Root|Taxa_153 54.9629484736 50.5573652099 54.2746441994 51.6232779595 0 50.9204894262 52.1654321914 0.1884089049 50.7479203266 0.0221645359 53.1077653448 0 0 0 0 52.6512164947 0 0.2981989018 0 0 0 0.0807715368 0 0 0 50.1370901086 0 0.4542572973 0 0 0 0 0 0.4192882307 0 0 0 0 0 103.662801847 0 0.3979888829 0 0 0 0 0 0 +Root|Taxa_154 0 0 0 50.163331859 54.3225166315 0 0 0 54.2741962951 0.3987782149 0 0.3906929996 0 0 0 0 0 0 0 0 0 0 0 0 0 50.1163702243 0 0 0 0 0 0 0 0 0.3365017002 0 0 0 0 103.671105895 0 0 0 0 0 0 0 0 +Root|Taxa_155 0 54.1850524251 0 0.4907963023 54.9825970045 52.2360487216 0 0.2119051547 52.5585979852 51.509361986 0 0 0 0 53.3575243815 0 0 0 0 0 0.4358898121 0 0 0 0.3494253042 50.397495396 0 0 0 0 0.1242609805 0 0 0 0 0.4671507012 0 0 0.1868497381 104.22255511 0 0 0 0 0.4712259955 0 0 0 +Root|Taxa_156 0 0.1488298437 0 0.1426635063 0 0 53.2356214553 52.332511369 0 51.5725150026 0 0 50.0507645562 0.4215629634 0 0 0 0 0 0 0 0 0.3357119997 0 0 53.1939946532 0 0 0 0 0 0 0 0 0 0.3648066076 0 0 0 103.458287072 0 0 0 0 0 0 0 0 +Root|Taxa_157 50.6978732494 0 50.6734842361 0 0 0 0 51.7777257263 0 0 0 0 0 0 53.7294750771 0 0.1644620726 0 0 0 0 0 0 0 0 54.1941881246 0 0 0 0 0 0.3476189713 0 0 0.3611656822 0 0 0 0 102.508893906 0 0 0 0 0 0 0.3394288541 0.3707488064 +Root|Taxa_158 0 53.7561622573 0 0.1709683787 51.9863822632 52.1013896181 0 0 0 0 0 0 0 0 0 52.5375657735 0 0 0 0 0 0 0 0 0 50.2073591757 0 0 0.1038712117 0 0 0 0 0 0 0 0 0 0 104.721558927 0 0 0 0 0 0.2370944335 0 0 +Root|Taxa_159 0.3874316195 0 0 0 0.0552919119 0 0 0 0 0 54.4124660215 0 0 0 0.2227858482 0 0 0 0.3861346603 0 0 0 0 0 0 51.0208960264 0 0 0 0 0 0 0 0 0 0 0 0 0 103.997856026 0.2930440729 0.2454792928 0 0 0 0 0 0 +Root|Taxa_160 0 0 52.9419023174 0 0 0 52.4349614444 53.1019048862 0 0 0 0 53.768020292 50.9370694265 0 0 0 0 0.4617021973 0 0 0 0 0 0 0 53.8639548458 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_161 0 0 0 0 0 0 50.9463874577 0 50.2973248145 52.3586283584 54.0246650833 53.5797750183 52.58628782 0 54.6614480918 0 0.1450967381 0 0 0 0 0 0 0 0 0 50.3155658458 0 0 0.4296255491 0 0 0 0 0 0.4629821219 0 0 0 0 0.1737198594 0.2686717286 0 0.0978520167 0 0 0 0 +Root|Taxa_162 53.1480600841 0 0 0 0 0 0 0 0 0 52.3645492168 0 0 53.907858137 0.0830708636 0 0 0 0 0 0 0 0.0522518002 0.0998390594 0 0 54.6418044878 0.1071592541 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_163 0 0 0 0 0 0 0 0 0.1032253511 0 0 0 0 0 0.1375874333 51.6200703686 0 0 0 0 0 0 0 0.4511394821 0 0 53.7873572369 0 0 0 0 0.2951931824 0 0 0.1880146955 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_164 0.4835113024 0 0 50.3719699426 0 51.9771225761 0 0 0 0 0 50.3547158319 50.2502208924 0 53.8459195753 54.3741304475 0.024577205 0 0 0 0 0 0 0 0 0 50.3286767397 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3925771428 0 0 0 0 0 +Root|Taxa_165 52.0968027182 0.1727382739 0 0 52.0064350501 0 0 50.5583883437 53.6043075055 53.0323951087 0.015868495 0.2276317641 0 0 0 0 0 0 0 0.4527866454 0 0 0.3027420952 0 0.384873724 0 53.9270526894 0 0 0 0 0 0 0.1809349348 0.3820501581 0 0 0 0 0.4145347686 0 0 0 0 0 0 0 0 +Root|Taxa_166 52.7505897873 53.9549468598 51.4271330184 52.6350719286 0 0 0 51.4072420959 0.1322200828 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3235715088 0 0 51.7533492233 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.3218342588 0 +Root|Taxa_167 0 0 0 0.3558686048 0 0 0 0 0 0 0 0 0 0 0.1432562186 50.6823351017 0 0 0 0 0 0 0.2934262859 0 0 0 50.0240967706 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_168 0 0 0 0 0 54.3451420666 0 51.3494668429 51.5131678207 52.5733542268 0.3945348022 0 53.826644509 0 0 0 0 0 0 0 0 0 0 0 0 0.0624962977 50.2705591379 0 0 0 0 0 0 0 0 0 0.3168581661 0 0 0 100.792598893 0 0 0 0.1824407078 0 0 0 +Root|Taxa_169 0 52.3081498196 52.0504650623 0 0 0 0 0 0.20752284 0 0 50.1886994174 0 0.4728794387 54.1324005816 0 0.4331534803 0 0 0 0.0642689439 0 0.1251965139 0 0 0 53.9445664526 0 0 0 0 0 0 0 0.399145223 0 0 0 0 0 100.661367081 0 0 0 0 0 0 0 +Root|Taxa_170 0 53.7805706154 0 50.9944363379 51.9866603495 53.6859953206 0 0 0 0 51.983336686 0 0 0 0 0 0.4572159306 0.0492165376 0 0 0 0 0 0 0 0 50.915460038 0 0 0 0 0 0 0 0 0 0 0 0 0 102.196365333 0 0 0.1419077746 0 0 0 0 +Root|Taxa_171 0 53.2094792455 0.1489580956 0 52.2630749516 0 0 0 50.1948292546 0 0 0 0 0 0 54.2354745265 0 0.0333639634 0 0 0 0 0.1990114174 0 0 0 54.8033722735 0.4511957743 0 0 0 0.232926566 0 0 0 0 0 0 0 0 102.98051989 0 0 0.3016712375 0 0 0 0 +Root|Taxa_172 51.0671898877 0 0 0 0 0.0511781296 0 0 0 0 0.2109176808 50.4738026808 0 0 0 0 0 0 0 0 0 0 0 0 0 0 50.735860776 0 0 0 0 0 0 0 0 0 0.4304878063 0 0 0 100.325004789 0 0 0 0 0 0 0 +Root|Taxa_173 0 0 0 0 54.0128674588 0 54.3605479719 0 0 0.468473056 53.7053016943 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 52.4806040921 0 0 0 0.4440176055 0 0 0 0 0 0 0 0 0 102.327508124 0.1299130454 0 0 0 0 0 0 +Root|Taxa_174 0 0 54.5156851 52.5986848711 0 51.4073193408 0 0 0 52.0673966016 0 0.2029731535 0 54.7115612862 0 0.1249151498 0 0.0041863337 0 0 0 0 0 0 0 0 53.3096691132 0 0.2252071989 0 0 0 0 0 0 0 0 0 0 0 101.371606613 0 0 0 0 0 0 0 +Root|Taxa_175 0 0 0 0 0 0 50.3537914845 0 0 0 0 0 0 51.0325020147 51.5109616955 0.4444300716 0 0 0 0 0 0 0.0713851372 0 0 0 51.4884897233 0.1621744325 0.0650189427 0 0 0 0 0 0.2974779045 0 0.4862985628 0 0 0 103.478234958 0 0 0 0 0 0 0 +Root|Taxa_176 0 0.4555503772 0 0 0 0 53.8636925608 0 52.6708232365 0 0 0 0 0 0 0 0.4237214796 0.3767939371 0 0 0 0 0 0 0 0 0 52.0523308197 0 0 0 0 0 0 0 0 0 0 0.2257095009 0 0 0 0 0 0 0 0 0 +Root|Taxa_177 50.475411618 0 0 52.2346780318 0 0 0.3436295501 0 53.7249478704 51.0031927895 53.9509039005 53.833396097 0 0 0 0 0 0 0 0 0 0.3597522784 0 0 0 0 0 50.109383045 0 0 0 0 0 0.3478571808 0.2179063686 0 0.2819056479 0 0 0 0 0 0.0854901409 0 0.2940278032 0 0 0 +Root|Taxa_178 51.1271601449 54.8860173667 0 0 0 0 0 0 0 0 0 0 0 0 0 51.6268134565 0 0 0 0 0 0 0 0 0 0 0 51.3849503597 0 0.4858649487 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_179 0 0 0 0 52.166291339 0 0 53.765176324 0 0 0.1970422822 0 0 0 0 0 0.4265009899 0 0 0 0 0 0 0 0 0 0 52.5279778091 0 0 0 0 0 0 0.3352695026 0 0 0.33414374 0 0 0 0.3020387723 0 0 0.2431528132 0 0 0 +Root|Taxa_180 0 0 0 0 0.3200819933 0 0 0 0 0 0 50.583385197 0 0 0 0 0 0 0 0 0.0013638078 0 0 0 0 0 0 53.4349148139 0 0 0 0 0 0 0.4186021993 0 0 0.1217993021 0 0.3372202371 0 0 0 0 0 0 0 0 +Root|Taxa_181 0.2819251852 0 0 54.7493143772 54.2492072498 0 0 50.8945849016 0 0 0 0 0.4230922399 52.4557762201 0 50.5470429792 0 0 0 0 0 0 0 0 0 0 0 50.262423604 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_182 0 0 0 0 0 0 0 0 52.8860624056 0 0 0 0 0 0 0 0 0 0 0 0.2177694838 0 0 0.0426182111 0 0 0 51.7431170066 0 0 0 0 0 0.4120160848 0 0 0 0 0 0 0 0 0 0 0 0 0.4202458646 0 +Root|Taxa_183 0 0 51.4057895825 0 0.348959037 51.813387383 53.0094547995 0 0 0 54.1428713129 0 51.868728964 0 0 52.2561040348 0 0 0 0 0 0.2599077514 0 0 0 0 0 54.7085060955 0 0 0 0 0 0 0 0 0 0 0 0.0300257631 0 0.058526713 0 0 0 0 0 0 +Root|Taxa_184 0 0 0 0.3137102393 0 52.1430757063 0 0 0 0 0 51.3195918888 54.8671168984 0 54.4710598727 0 0 0 0 0.2815828512 0 0 0 0.4589160147 0 0.3225932485 0 51.5141825365 0 0 0 0 0 0 0 0 0 0 0 0 0 103.059342486 0 0 0 0.2181513139 0 0.2214639224 +Root|Taxa_185 0 0 54.389314375 0 0 0 0 0 0 54.7267292213 0 0 51.5915698536 0 50.219987935 0 0 0 0.4246763691 0 0 0 0 0.3108703032 0 0 0 50.6423249736 0 0 0 0.3046097687 0 0 0 0 0 0.1816515957 0 0 0 104.657049771 0 0 0 0 0 0 +Root|Taxa_186 0.2578890924 0 0 54.9417630492 0 0 52.5251964762 50.6241253824 0 54.336635758 0 0 53.8959566907 50.6069116272 0 0 0 0 0 0.3393123192 0 0.2282115619 0 0 0 0 0 52.3706346328 0 0 0.3694309992 0 0 0 0 0 0 0 0.0214825341 0 0 100.213819152 0.4798189848 0 0 0 0 0 +Root|Taxa_187 52.9225564174 50.9556974217 0 51.9632176621 53.041227125 52.1350513686 0 0 0 0.0109429699 0 52.6077372192 0 0 52.6016736512 0 0 0 0 0 0 0 0 0 0.1115057321 0 0 53.6644244189 0 0.0444891502 0 0.2264456126 0 0 0 0 0 0 0 0 0.4351778245 104.24137393 0 0 0 0.302465445 0 0.003393226 +Root|Taxa_188 0 0 50.7929335187 0 0 0 0 0.0230501905 0 53.6741909148 50.5908790468 0 0 54.3781425253 53.9150822838 51.9440621712 0 0 0 0 0 0 0 0 0 0 0 51.3050243267 0.3083766005 0 0 0 0 0 0 0 0 0 0 0 0 101.222657064 0 0 0 0 0 0 +Root|Taxa_189 53.1156860216 0 50.2004019551 0 53.5393781093 0 0 0.4767799759 0 0 0 0 0 0 0 0 0 0 0.2510714584 0 0 0 0 0 0.0313474882 0 0 54.3585907576 0 0 0 0 0 0 0 0.3815601461 0 0 0.0661751984 0 0 103.093749372 0 0.3328062103 0 0 0 0 +Root|Taxa_190 0 51.8421062419 0 0 0 53.3427531356 52.5952915076 0 52.2138916423 0 0.1584601307 0 0 0 0 0 0 0.1606301123 0 0 0 0 0 0 0 0 0 52.6852858362 0 0 0 0 0 0 0 0 0 0 0 0 0 104.906827283 0.2424074586 0 0 0 0 0 +Root|Taxa_191 0 51.2781148402 0 0 0.2445757987 0 0 52.2653477791 0 0 50.7773450711 0 0.096988651 52.9500712259 0 0 0 0 0 0 0 0 0 0 0 0 0 50.3546323716 0.3642238437 0 0 0.2580494612 0 0 0 0 0 0 0 0.4405137253 0 102.645104533 0 0 0 0 0 0 +Root|Taxa_192 52.0827037946 0 0 53.3591565492 0 0 52.3984658047 0 0 0 0 0 53.1365027963 52.1224468617 0.2413320858 0 0 0.4208501086 0 0 0 0 0 0 0 0 0 0 54.1591071637 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_193 51.1294803083 54.000378627 0 0 0 52.1201148141 0 51.4793102236 0 52.7028030721 0 0.2554842302 0 51.0278208653 0 0 0 0 0 0 0 0 0 0 0 0 0 0 52.5398921345 0 0 0 0.2921036679 0 0 0.0121176867 0 0 0 0 0 0 0.1737701306 0 0 0 0 0 +Root|Taxa_194 0 53.3175217655 54.3162751517 0 50.1223851156 0 0.4184720644 53.8254943468 0 53.7365842832 0 50.3085076248 0 0.0440271517 54.2949627388 0 0 0 0 0 0.202831309 0 0 0 0 0.1944977516 0.0278157737 0 51.0262433359 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2768269464 0 0.1355769703 0 +Root|Taxa_195 0 0 0 0 0 0 0 0 0 54.2177701685 52.9208199615 0 51.3350564705 0 0 53.6987592548 0 0 0 0 0 0 0 0 0 0 0 0 50.4505941005 0 0 0 0.2366040567 0 0 0.0706983798 0 0.4048068191 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_196 0 0 50.6109850879 0 0 51.0658949888 0 0 0 0.4576835496 52.2501412607 0 53.2433525996 0 0.0866719841 0.1044734051 0 0 0 0 0 0.496363439 0 0 0 0 0 0 53.8202104672 0 0 0 0 0 0 0 0.4776304856 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_197 0 0 51.6313839246 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 52.7963856942 0 0 0 0 0 0 0.2973905015 0.0408162681 0 0 0 0 0 0 0.0982300275 0 0 0 0 +Root|Taxa_198 0 53.047207326 0 0 0 53.2380971568 54.5651723179 0 0 0 0 50.0759819974 0 0 0 0 0 0 0 0 0 0.191167896 0 0 0 0 0 0 50.0878653178 0 0.1127647836 0 0 0 0.3103576187 0 0 0 0 0 0 0 0 0 0.3988252675 0 0 0 +Root|Taxa_199 0 0 0 0 52.0714878521 0 0 51.3096678644 0 0 50.4793163079 0 0 0 53.2342697162 0 0 0 0 0 0 0.322882781 0 0 0 0.0518037931 0 0 53.2300848959 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2133714863 0 0 0 0.4607870842 +Root|Taxa_200 0 0 0 0 0 0 0 0 54.8676932307 0 0 0 54.8465490773 0 50.0099690264 50.7561511906 0 0 0 0 0.1307286405 0 0 0 0 0.0415073515 0 0 52.6545608425 0 0 0 0 0 0 0.184158767 0 0 0 0 0 0 104.026411169 0 0 0 0 0 +Root|Taxa_201 0 0 0.0684272076 0 0 0 0 0 0 0 0 0 0 0 0 0.3333347799 0 0 0 0 0 0 0.0661544997 0 0 0 0 0 51.4443606906 0 0 0.0771979655 0 0 0 0 0 0 0 0 0 0.2761105027 102.275033805 0 0 0 0 0 +Root|Taxa_202 54.5088647863 0 0 0 0.0321756373 0 0 0 52.0726025305 0 0 53.0253269521 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2083645807 0.1062779642 54.2981710912 0 0 0 0 0 0 0 0.3699929419 0 0 0 0 0 101.947183747 0 0 0 0 0 +Root|Taxa_203 0 0 0 53.0890034691 53.4478307116 0 0 52.0807007841 0 0 0.3932551686 0 0 50.8859238684 0 0 0 0 0 0 0 0 0.4819659907 0 0 0 0 0 50.4463062145 0 0 0 0 0 0.0022291218 0 0 0 0.0260392603 0 0 0 103.245769713 0 0 0 0 0 +Root|Taxa_204 0 0 0 50.2568349213 0 0 0 0 52.4975993007 0 0 0 0 0 0 0 0.3938938146 0 0 0 0 0 0 0 0.1752154 0 0 0 50.8254339282 0 0 0 0 0 0 0 0 0 0 0.2911676263 0 0 102.001279284 0 0 0 0 0 +Root|Taxa_205 0 0 52.7163994001 0 0 0 52.2676963135 0 0 0 53.5958965115 0 0 0.3119935484 0.476493088 0.0382216967 0 0 0 0 0 0 0 0 0 0 0.2807420398 0 50.4720582791 0 0 0.0450551832 0.3918770618 0 0 0 0 0 0 0 0 0 102.486209016 0 0 0.4059267464 0.4356299041 0.4526614144 +Root|Taxa_206 0 0 0 53.5087326257 0 53.0326686943 53.9077681483 0 0 0 0 0 0 53.8938971864 52.5574477678 53.8098522525 0 0 0 0 0 0 0 0 0 0 0 0 54.2401876661 0.2242680732 0.115238483 0 0 0 0 0.4881061169 0 0 0 0 0 0 102.186344705 0 0 0 0 0 +Root|Taxa_207 51.2183210273 51.1563774994 0 0 54.1762942805 0 0 0 51.2704897195 52.8968592985 0 52.288966104 0 0 0 54.3906412249 0 0 0 0 0.0048042154 0 0 0 0 0 0 0 51.2408250309 0 0 0 0 0 0 0 0 0 0 0 0 0 103.110597368 0 0 0 0 0 +Root|Taxa_208 0 0 0 0.0546805022 0 0.1599409596 50.0472074245 0.4099746803 51.4051352965 0 0 0 0 53.2887290406 0 0.0848935397 0 0 0 0 0 0 0 0.1555916655 0 0 0 0 0 53.0135385478 0.1497035187 0 0.0691993821 0 0.3373772825 0 0 0 0 0 0 0 0 0 0.2522945018 0 0 0 +Root|Taxa_209 0 50.1030172793 0 0 0 0 54.1650217307 52.2230603121 0 0 0 0 0 0 0 0.1143171628 0 0 0 0 0 0 0 0 0 0 0 0 0 51.8871962649 0.299430337 0 0 0 0 0 0 0.4798221512 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_210 0 0 0 0 0 0 50.5549695516 52.1576796275 0 0 0 0 0 0 0 52.3992524271 0 0 0 0.1706714754 0 0 0 0 0.3811782646 0.0008517706 0 0 0 51.0207565229 0 0 0 0 0 0 0 0 0 0 0.0102807436 0 0 0 0 0.0085181585 0 0 +Root|Taxa_211 50.536257303 0 0 0 0 0 0 0 0 0 51.9813477573 0 51.5911214861 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 53.1296406718 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4077664043 0 +Root|Taxa_212 0 0 0 0 0.4699813783 0 0 0 0 54.5530559822 0 50.743119663 51.3155881447 0 0 53.2941828392 0 0 0 0 0 0 0 0 0 0 0.0342022552 0 0 54.1038020638 0 0 0 0.040960994 0 0 0 0.4947938425 0 0 0 0 0.3862038599 0 0 0 0 0 +Root|Taxa_213 0.3399472909 0 0 0 0 0 54.6671775034 0.050131803 50.2999962265 0 51.8713213189 0 0 0 53.2943170335 51.1243603164 0 0 0 0 0 0 0 0 0 0 0 0 0 50.6101294432 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +Root|Taxa_214 50.0115213059 0 0 0 0 50.8371273498 0 0 0 54.4139864126 0 0 0 0 0 0 0 0 0 0.3015435222 0 0 0 0 0 0 0 0.0872919265 0.2777439996 50.3179817753 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.4557640334 0 0 0 +Root|Taxa_215 0 0.4561298327 0 53.5216413012 54.8891010063 50.9129555637 0 0 0 0 0 0 0 0 53.4880926895 0 0 0 0 0 0 0 0 0 0 0 0 0.1940475073 0.2803421878 50.3585027491 0 0 0 0.345883306 0.3534255574 0 0 0 0 0 0.2075798899 0 0.4493876473 0 0 0 0 0 +Root|Taxa_216 0 52.664567111 54.9286304319 0 0 0.3528460271 0 0 0 53.8271900954 53.2864160864 0 0 53.8916659856 0 0 0.0390396631 0 0 0 0 0 0 0 0 0 0 0 0.0501403367 53.9106611035 0 0 0 0 0 0 0 0 0 0 0 0 0 102.503416561 0 0 0.2323723816 0 +Root|Taxa_217 0 0 0 53.8189770697 0 0 0 0 0 0 51.0739213403 51.9325727684 53.6586894371 0 50.8848681615 0 0 0 0 0 0 0.4134750657 0.2984209586 0 0 0 0 0.3984011824 0 50.0637489202 0.1745273923 0 0 0 0 0 0 0 0 0 0 0 0.0970082737 102.133370457 0 0 0 0 +Root|Taxa_218 53.6309027759 0 0 54.1993531611 0 53.8609164351 0 0 0 0 0 0 0 0 0 0 0 0 0.4230445366 0 0 0 0 0 0 0.3460879368 0 0 0 54.0328089965 0 0 0 0 0 0 0 0 0 0 0.1875089533 0 0 102.742579014 0 0 0 0 +Root|Taxa_219 0 0 50.6959593123 0 0 0 0 0 52.9583336932 51.2063060177 0 0 0 0 0.1839092432 0.1624310372 0 0 0.2217437424 0 0 0 0 0 0 0 0 0 0 51.3989543421 0.1469196015 0 0 0.4515484111 0 0 0 0 0 0 0.362398058 0 0 103.426555217 0 0 0 0 +Root|Taxa_220 53.138860148 54.260252989 50.1122213715 0 53.7052604162 0 0.4760342302 0 51.9385050693 0.191283382 0 0 51.217538964 0 0.2580800578 50.1174597734 0 0 0 0 0 0 0.115889335 0 0 0 0 0 0 53.3111145776 0 0 0 0 0 0 0 0 0 0 0 0 0 104.430682236 0 0 0 0 +Root|Taxa_221 0 0.3384535892 0.4106893002 52.1411487055 50.5241190477 0 0 53.9774121283 0 0 0 52.1082021353 0 0 0 0 0.4172253944 0 0 0 0 0 0 0 0 0 0 0 0 50.9817139231 0 0 0 0 0 0 0 0 0 0.4224842988 0 0 0 102.376952212 0.2260951816 0 0.3067861355 0 +Root|Taxa_222 0 52.3172435891 52.6471280765 0 51.567115685 0 0 53.6724555185 0 0 0 0 0 54.2428549188 51.6482654065 0 0 0 0 0 0 0.2425898966 0 0 0 0 0 0 0 51.1118820396 0 0 0 0 0 0 0 0 0 0 0.2951844288 0 0 102.047792613 0 0 0 0 +Root|Taxa_223 0 0 0.2600381862 0 0 50.2577764475 0.4992511416 0 0 0 0 51.3720073928 0 53.1526569712 0.3864806128 0 0 0.0519771654 0 0 0 0 0 0.3896198936 0 0 0 0.4447746424 0 51.736707456 0 0 0 0 0 0 0 0 0 0 0 0 0 103.397909634 0 0 0 0 diff -r 000000000000 -r 0de566f21448 test-data/micropita_output --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/micropita_output Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,1 @@ +representative Sample_22_R Sample_20_R Sample_44_T Sample_28_R Sample_40_E Sample_10_D Sample_26_R Sample_46_T Sample_32_E Sample_16_R Sample_23_R Sample_21_R Sample_45_T Sample_29_R Sample_41_E Sample_1_D Sample_27_R Sample_47_T Sample_33_E Sample_17_R diff -r 000000000000 -r 0de566f21448 tool_dependencies.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tool_dependencies.xml Thu Jun 03 18:13:32 2021 +0000 @@ -0,0 +1,6 @@ + + + + $REPOSITORY_INSTALL_DIR + +