UNPKG

patristic

Version:

Patristic Inference library for Node and Browser

2 lines (1 loc) 16.1 kB
!function(global,factory){"object"==typeof exports&&"undefined"!=typeof module?factory(exports):"function"==typeof define&&define.amd?define(["exports"],factory):factory((global="undefined"!=typeof globalThis?globalThis:global||self).patristic={})}(this,(function(exports){"use strict";function Branch(data,children){data||(data={}),children||(children=d=>d.children),Object.assign(this,{_guid:([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(a=>(a^16*Math.random()>>a/4).toString(16))),id:data.id||"",data:data,depth:data.depth||0,height:data.height||0,length:data.length||0,parent:data.parent||null,children:children(data)||[],value:data.value||1,respresenting:1})}function parseJSON(json,idLabel,lengthLabel,childrenLabel){idLabel||(idLabel="id"),lengthLabel||(lengthLabel="length"),childrenLabel||(childrenLabel="children"),"string"==typeof json&&(json=JSON.parse(json));let root=new Branch({id:json[idLabel],length:json[lengthLabel]});return json[childrenLabel]instanceof Array&&json[childrenLabel].forEach((child=>{root.addChild(parseJSON(child))})),root.fixDistances()}function search(t){let q,c2,qMin=1/0,D=t.D,n2=t.cN-2,S=t.S,I=t.I,rowSums=t.rowSums,removedColumns=t.removedIndices,uMax=t.rowSumMax,minI=-1,minJ=-1;for(let r=0;r<t.N;r++)removedColumns.has(r)||(c2=I[r][0],removedColumns.has(c2)||(q=D[r][c2]*n2-rowSums[r]-rowSums[c2],q<qMin&&(qMin=q,minI=r,minJ=c2)));for(let r=0;r<t.N;r++)if(!removedColumns.has(r))for(let c=0;c<S[r].length;c++)if(c2=I[r][c],!removedColumns.has(c2)){if(S[r][c]*n2-rowSums[r]-uMax>qMin)break;q=D[r][c2]*n2-rowSums[r]-rowSums[c2],q<qMin&&(qMin=q,minI=r,minJ=c2)}return{minI:minI,minJ:minJ}}function recalculateDistanceMatrix(t,joinedIndex1,joinedIndex2){let aux,aux2,D=t.D,n=D.length,sum=0,removedIndices=t.removedIndices,rowSums=t.rowSums,newRow=t.newRow,rowChange=t.rowChange,newMax=0;removedIndices.add(joinedIndex1);for(let i=0;i<n;i++)removedIndices.has(i)||(aux=D[joinedIndex1][i]+D[joinedIndex2][i],aux2=D[joinedIndex1][joinedIndex2],newRow[i]=.5*(aux-aux2),sum+=newRow[i],rowChange[i]=-.5*(aux+aux2));for(let i=0;i<n;i++)D[joinedIndex1][i]=-1,D[i][joinedIndex1]=-1,removedIndices.has(i)||(D[joinedIndex2][i]=newRow[i],D[i][joinedIndex2]=newRow[i],rowSums[i]+=rowChange[i],rowSums[i]>newMax&&(newMax=rowSums[i]));rowSums[joinedIndex1]=0,rowSums[joinedIndex2]=sum,sum>newMax&&(newMax=sum),t.rowSumMax=newMax,t.indicesLeft.delete(joinedIndex1)}function sortWithIndices(toSort,skip){void 0===skip&&(skip=-1);let n=toSort.length,indexCopy=new Array(n),valueCopy=new Array(n),i2=0;for(let i=0;i<n;i++)-1!==toSort[i]&&i!==skip&&(indexCopy[i2]=i,valueCopy[i2++]=toSort[i]);indexCopy.length=i2,valueCopy.length=i2,indexCopy.sort(((a,b)=>toSort[a]-toSort[b])),valueCopy.sortIndices=indexCopy;for(let j=0;j<i2;j++)valueCopy[j]=toSort[indexCopy[j]];return valueCopy}Branch.prototype.addChild=function(data){let c;return data instanceof Branch?(c=data,c.parent=this):(data||(data={}),c=new Branch(Object.assign(data,{parent:this}))),this.children.push(c),c},Branch.prototype.addParent=function(data,siblings){let c;return siblings||(siblings=[]),data instanceof Branch?c=data:(data||(data={}),c=new Branch(Object.assign(data))),siblings.forEach((sib=>sib.setParent(c))),c.children=[this].concat(siblings),this.parent=c,this},Branch.prototype.ancestors=function(){return this.getAncestors(!0)},Branch.prototype.clone=function(){return parseJSON(this.toObject())},Branch.prototype.consolidate=function(){return this.eachAfter((branch=>{branch.isRoot()||branch.length>=5e-4||(""==branch.parent.id?branch.parent.id=branch.id:branch.parent.id+="+"+branch.id,branch.excise())})).fixDistances()},Branch.prototype.copy=function(){let newThis=parseJSON(JSON.stringify(this));return newThis.parent=null,newThis.fixDistances()},Branch.prototype.count=function(){return this.sum((()=>1))},Branch.prototype.descendants=function(){return this.getDescendants(!0)},Branch.prototype.depthOf=function(descendant){let distance=0;if("string"==typeof descendant&&(descendant=this.getDescendant(descendant)),void 0===descendant)throw Error("Cannot compute depth of undefined descendant!");let current=descendant;for(;current!=this;)distance+=current.length,current=current.parent;return distance},Branch.prototype.distanceBetween=function(descendantA,descendantB){let mrca=descendantA.getMRCA(descendantB);return mrca.depthOf(descendantA)+mrca.depthOf(descendantB)},Branch.prototype.distanceTo=function(cousin){let mrca=this.getMRCA(cousin);return mrca.depthOf(this)+mrca.depthOf(cousin)},Branch.prototype.each=function(callback){let current,branch=this,next=[branch];for(;next.length;)for(current=next.reverse(),next=[];branch=current.pop();)callback(branch),branch.eachChild((child=>next.push(child)));return this},Branch.prototype.eachAfter=function(callback){return this.eachChild((child=>child.eachAfter(callback))),callback(this),this},Branch.prototype.eachBefore=function(callback){return callback(this),this.eachChild((child=>child.eachBefore(callback))),this},Branch.prototype.eachChild=function(callback){return this.children.forEach(callback),this},Branch.prototype.excise=function(){if(this.isRoot()&&this.children.length>1)throw new Error("Cannot excise a root Branch with multiple children.");return this.eachChild((child=>{child.length+=this.length,child.parent=this.parent,this.isRoot()||this.parent.children.push(child)})),this.parent.children.splice(this.parent.children.indexOf(this),1),this.parent.representing++,this.parent},Branch.prototype.fixDistances=function(){let maxdepth=0;return this.getRoot().depth=0,this.eachBefore((d=>{d.isRoot()||(d.depth=d.parent.depth+1,d.depth>maxdepth&&(maxdepth=d.depth))})).eachAfter((d=>{d.height=maxdepth-d.depth,d.value=d.value+d.children.reduce(((a,c)=>a+c.value),0)})),this},Branch.prototype.fixParenthood=function(nonrecursive){return this.children.forEach((child=>{child.parent||(child.parent=this),child.parent!==this&&(child.parent=this),!nonrecursive&&child.children.length>0&&child.fixParenthood()})),this},Branch.prototype.flip=function(){return this.each((c=>c.rotate()))},Branch.prototype.getAncestors=function(includeSelf){let ancestors=includeSelf?[this]:[],current=this;for(;current=current.parent;)ancestors.push(current);return ancestors},Branch.prototype.getChild=function(childID){return this.children.find((c=>c.id===childID))},Branch.prototype.getDescendant=function(id){if(this.id===id)return this;let children=this.children,n=children.length;if(children)for(let i=0;i<n;i++){let descendant=children[i].getDescendant(id);if(descendant)return descendant}},Branch.prototype.getDescendants=function(includeSelf){let descendants=includeSelf?[this]:[];return this.isLeaf()||this.children.forEach((child=>{child.getDescendants(!0).forEach((d=>descendants.push(d)))})),descendants},Branch.prototype.getLeafs=function(){return this.getLeaves()},Branch.prototype.getLeaves=function(){if(this.isLeaf())return[this];{let descendants=[];return this.children.forEach((child=>{child.getLeaves().forEach((d=>descendants.push(d)))})),descendants}},Branch.prototype.getMRCA=function(cousin){let mrca=this;for(;!mrca.hasDescendant(cousin);){if(mrca.isRoot())throw Error("Branch and cousin do not appear to share a common ancestor!");mrca=mrca.parent}return mrca},Branch.prototype.getRoot=function(){let branch=this;for(;!branch.isRoot();)branch=branch.parent;return branch},Branch.prototype.hasChild=function(child){if(child instanceof Branch)return this.children.includes(child);if("string"==typeof child)return this.children.some((c=>c.id===child));throw Error(`Unknown type of child (${typeof child}) passed to Branch.hasChild!`)},Branch.prototype.hasDescendant=function(descendant){let descendants=this.getDescendants();if(descendant instanceof Branch)return descendants.some((d=>d===descendant));if("string"==typeof descendant)return descendants.some((d=>d.id===descendant));throw Error("Unknown type of descendant passed to Branch.hasDescendant!")},Branch.prototype.hasLeaf=function(leaf){let leaves=this.getleaves();if(leaf instanceof Branch)return leaves.includes(leaf);if("string"==typeof leaf)return leaves.some((d=>d.id===leaf));throw Error("Unknown type of leaf passed to Branch.hasLeaf.")},Branch.prototype.invert=function(){let oldParent=this.parent;if(!oldParent)throw Error("Cannot invert root node!");{let temp=this.parent.length;this.parent.length=this.length,this.length=temp,this.parent=oldParent.parent,this.children.push(oldParent),oldParent.parent=this,oldParent.children.splice(oldParent.children.indexOf(this),1)}return this},Branch.prototype.isChildOf=function(parent){if(parent instanceof Branch)return this.parent===parent;if("string"==typeof parent)return this.parent.id===parent;throw Error("Unknown parent type passed to Branch.isChildOf")},Branch.prototype.isConsistent=function(){return!(!this.isRoot()&&!this.parent.children.includes(this))&&(!!this.isLeaf()||!this.children.some((c=>c.parent!==this))&&this.children.every((c=>c.isConsistent())))},Branch.prototype.isDescendantOf=function(ancestor){return!(!ancestor||!this.parent)&&(this.parent===ancestor||this.parent.id===ancestor||this.parent.isDescendantOf(ancestor))},Branch.prototype.isLeaf=function(){return 0===this.children.length},Branch.prototype.isolate=function(){let index=this.parent.children.indexOf(this);return this.parent.children.splice(index,1),this.setParent(null),this},Branch.prototype.isRoot=function(){return null===this.parent},Branch.prototype.leafs=function(){return this.getLeaves()},Branch.prototype.leaves=function(){return this.getLeaves()},Branch.prototype.links=function(){let links=[];return this.each((d=>{d.isRoot()||links.push({source:d.parent,target:d})})),links},Branch.prototype.normalize=function(newmin,newmax){"number"!=typeof newmax&&(newmax=1),"number"!=typeof newmin&&(newmin=0);let min=1/0,max=-1/0;this.each((d=>{d.value<min&&(min=d.value),d.value>max&&(max=d.value)}));let ratio=(newmax-newmin)/(max-min);return this.each((d=>d.value=(d.value-min)*ratio+newmin))},Branch.prototype.path=function(target){let current=this,branches=[this],mrca=this.getMRCA(target);for(;current!==mrca;)current=current.parent,branches.push(current);let k=branches.length;for(current=target;current!==mrca;)branches.splice(k,0,current),current=current.parent;return branches},Branch.prototype.remove=function(pruneAncestors){let root=this.getRoot();return this.isolate(),pruneAncestors&&this.parent?.removeIfNoChildren(),root},Branch.prototype.removeIfNoChildren=function(nonrecursive){let root=this.getRoot();return 0===this.children.length&&(this.remove(),nonrecursive||this.parent.removeIfNoChildren()),root},Branch.prototype.replace=function(replacement){let root=this.getRoot();this.parent;let index=this.parent.children.indexOf(this);return this.parent.children.splice(index,1,replacement),root},Branch.prototype.reroot=function(){let current=this,toInvert=[];for(;!current.isRoot();)toInvert.push(current),current=current.parent;return toInvert.reverse().forEach((c=>c.invert())),this.fixDistances()},Branch.prototype.rotate=function(recursive){return this.children?(this.children.reverse(),this):this},Branch.prototype.setLength=function(length){return this.length=length,this},Branch.prototype.setParent=function(parent){if(!parent instanceof Branch&&null!==parent)throw Error("Cannot set parent to non-Branch object!");return this.parent=parent,this},Branch.prototype.simplify=function(){return this.eachAfter((branch=>{if(1==branch.children.length){let child=branch.children[0];""==child.id?child.id=branch.id:child.id=branch.id+"+"+child.id,branch.excise()}})),this.fixDistances()},Branch.prototype.sort=function(comparator){return comparator||(comparator=(a,b)=>a.value-b.value),this.eachBefore((d=>d.children.sort(comparator)))},Branch.prototype.sources=function(cousin){let mrca=this.getMRCA(cousin);return mrca.depthOf(this)<mrca.depthOf(cousin)},Branch.prototype.sum=function(value){return value||(value=d=>d.value),this.eachAfter((d=>d.value=value(d)+d.children.reduce(((a,c)=>a+c.value),0)))},Branch.prototype.targets=function(cousin){return cousin.sources(this)},Branch.prototype.toJSON=function(){return this.toObject()},Branch.prototype.toMatrix=function(){let leafs=this.getLeaves(),n=leafs.length,matrix=new Array(n);for(let i=0;i<n;i++){matrix[i]=new Array(n),matrix[i][i]=0;for(let j=0;j<i;j++){let distance=leafs[i].distanceTo(leafs[j]);matrix[i][j]=distance,matrix[j][i]=distance}}return{matrix:matrix,ids:leafs.map((d=>d.id))}},Branch.prototype.toNewick=function(nonterminus){let out="";return this.isLeaf()||(out+="("+this.children.map((child=>child.toNewick(!0))).join(",")+")"),out+=this.id,this.length&&(out+=":"+function(num){let numStr=String(num);if(Math.abs(num)<1){let e=parseInt(num.toString().split("e-")[1]);if(e){let negative=num<0;negative&&(num*=-1),num*=Math.pow(10,e-1),numStr="0."+new Array(e).join("0")+num.toString().substring(2),negative&&(numStr="-"+numStr)}}else{let e=parseInt(num.toString().split("+")[1]);e>20&&(e-=20,numStr=(num/=Math.pow(10,e)).toString()+new Array(e+1).join("0"))}return numStr}(this.length)),nonterminus||(out+=";"),out},Branch.prototype.toObject=function(){let output={id:this.id,length:this.length};return this.children.length>0&&(output.children=this.children.map((c=>c.toObject()))),output},Branch.prototype.toString=function(replacer,width){return replacer||(replacer=null),width||(width=0),JSON.stringify(this,replacer,width)},exports.Branch=Branch,exports.parseJSON=parseJSON,exports.parseMatrix=function(matrix,labels){let minI,minJ,d1,d2,l1,l2,node1,node2,node3,that={},N=that.N=matrix.length;labels||(labels=[...Array(N).keys()]),that.cN=that.N,that.D=matrix,that.labels=labels,that.labelToTaxon={},that.currIndexToLabel=new Array(N),that.rowChange=new Array(N),that.newRow=new Array(N),that.labelToNode=new Array(2*N),that.nextIndex=N,that.I=new Array(that.N),that.S=new Array(that.N);for(let i=0;i<that.N;i++){let sortedRow=sortWithIndices(that.D[i],i);that.S[i]=sortedRow,that.I[i]=sortedRow.sortIndices}that.removedIndices=new Set,that.indicesLeft=new Set;for(let i=0;i<N;i++)that.currIndexToLabel[i]=i,that.indicesLeft.add(i);function setUpNode(labelIndex,distance){let node;return labelIndex<that.N?(node=new Branch({id:that.labels[labelIndex],length:distance}),that.labelToNode[labelIndex]=node):(node=that.labelToNode[labelIndex],node.setLength(distance)),node}that.rowSumMax=0,that.PNewick="",that.rowSums=function(a){let n=a.length,sums=new Array(n);for(let i=0;i<n;i++){let sum=0;for(let j=0;j<n;j++){"number"==typeof parseFloat(a[i][j])&&(sum+=a[i][j])}sums[i]=sum}return sums}(that.D);for(let i=0;i<that.cN;i++)that.rowSums[i]>that.rowSumMax&&(that.rowSumMax=that.rowSums[i]);for(;that.cN>2;){({minI:minI,minJ:minJ}=search(that)),d1=.5*that.D[minI][minJ]+(that.rowSums[minI]-that.rowSums[minJ])/(2*that.cN-4),d2=that.D[minI][minJ]-d1,l1=that.currIndexToLabel[minI],l2=that.currIndexToLabel[minJ],node1=setUpNode(l1,d1),node2=setUpNode(l2,d2),node3=new Branch({children:[node1,node2]}),recalculateDistanceMatrix(that,minI,minJ);let sorted=sortWithIndices(that.D[minJ],minJ);that.S[minJ]=sorted,that.I[minJ]=sorted.sortIndices,that.S[minI]=that.I[minI]=[],that.cN--,that.labelToNode[that.nextIndex]=node3,that.currIndexToLabel[minI]=-1,that.currIndexToLabel[minJ]=that.nextIndex++}let left=that.indicesLeft.values();minI=left.next().value,minJ=left.next().value,l1=that.currIndexToLabel[minI],l2=that.currIndexToLabel[minJ],d1=d2=that.D[minI][minJ]/2,node1=setUpNode(l1,d1),node2=setUpNode(l2,d2);let tree=new Branch({children:[node1,node2]});return tree.fixParenthood(),tree.fixDistances()},exports.parseNewick=function(newick){let ancestors=[],tree=new Branch,tokens=newick.split(/\s*(;|\(|\)|,|:)\s*/),n=tokens.length;for(let t=0;t<n;t++){let c,token=tokens[t];switch(token){case"(":c=tree.addChild(),ancestors.push(tree),tree=c;break;case",":c=ancestors[ancestors.length-1].addChild(),tree=c;break;case")":tree=ancestors.pop();break;case":":break;default:let x=tokens[t-1];")"==x||"("==x||","==x?tree.id=token:":"==x&&(tree.length=parseFloat(token))}}return tree.fixDistances()},exports.version="0.5.7"}));