ourMELONS/matlab/graph/@phyTree/phyTree.m

236 lines
8 KiB
Mathematica
Raw Normal View History

2019-12-16 16:47:21 +01:00
function tr = phyTree(varargin)
%PHYTREE Phylogenetic tree object.
%
% TREE = PHYTREE(B) creates an ultrametric phylogenetic tree object. B is
% a numeric array of size [NUMBRANCHES X 2] in which every row represents
% a branch of the tree and it contains two pointers to the branches
% or leaves nodes which are its children. Leaf nodes are numbered from 1
% to NUMLEAVES and branch nodes are numbered from NUMLEAVES + 1 to
% NUMLEAVES + NUMBRANCHES. Note that since only binary trees are allowed,
% then NUMLEAVES = NUMBRANCHES + 1. Branches are defined in chronological
% order, i.e. B(i,:) > NUMLEAVES + i. As a consequence, the first row can
% only have pointers to leaves and the last row must represent the 'root'
% branch. Parent-child distances are set to the unit or by the ultrametric
% condition if child is a leaf.
%
% TREE = PHYTREE(B,D) creates an additive phylogenetic tree object with
% branch distances defined by D. D is a numeric array of size [NUMNODES X
% 1] with the distances of every child node (leaf or branch) to its parent
% branch. NUMNODES = NUMLEAVES + NUMBRANCHES. D(end), the distance
% associated to the root node, is meaningless.
%
% TREE = PHYTREE(B,C) creates an ultrametric phylogenetic tree object with
% branch distances defined by C. C is a numeric array of size [NUMBRANCHES
% X 1] with the coordinates of every branch node. In ultrametric tress all
% the leaves are at the same location (i.e. same distance to the root).
%
% TREE = PHYTREE(BC) creates an ultrametric phylogenetic binary tree
% object with branch pointers in BC(:,[1 2]) and branch coordinates in
% BC(:,3). Same as PHYTREE(B,C).
%
% TREE = PHYTREE(...,N) specifies the names for the leaves and/or the
% branches. N is a cell of strings. If NUMEL(N)==NUMLEAVES then the names
% are assigned chronologically to the leaves. If NUMEL(N)==NUMBRANCHES the
% names are assigned to the branch nodes. If NUMEL(N)==NUMLEAVES +
% NUMBRANCHES all the nodes are named. Unassigned names default to 'Leaf
% #' and/or 'Branch #' as required.
%
% TREE = PHYTREE creates an empty phylogenectic tree object
%
% Example:
%
% % create an ultrametric tree
% b = [1 2; 3 4; 5 6; 7 8;9 10];
% t = phytree(b);
% view(t)
%
% % create an ultrametric tree with specified branch distances
% bd = [.1 .2 .3 .3 .4 ]';
% b = [1 2; 3 4; 5 6; 7 8;9 10];
% t = phytree(b,bd);
% view(t)
%
% See also PHYTREE/GET, PHYTREE/SELECT, PHYTREEREAD, PHYTREETOOL,
% PHYTREEWRITE, SEQLINKAGE, SEQNEIGHJOIN, SEQPDIST.
% Copyright 2003-2006 The MathWorks, Inc.
% $Revision: 1.1.6.11.2.1 $ $Author: batserve $ $Date: 2006/07/27 21:37:49 $
justVerifyValidity = false;
switch nargin
case 0
tr.tree = zeros(0,2);
tr.dist = zeros(0,1);
tr.names = {};
case 1
B = varargin{1};
case 2
B = varargin{1};
if iscell(varargin{2})
N = varargin{2};
else
D = varargin{2};
end
case 3
B = varargin{1};
D = varargin{2};
N = varargin{3};
otherwise
error('Bioinfo:phytree:IncorrectNumberOfArguments',...
'Incorrect number of arguments to %s.',mfilename);
end
if nargin==1 && isstruct(B) && isfield(B,'tree') && isfield(B,'names') && isfield(B,'dist')
N = B.names;
D = B.dist;
B = B.tree;
tr.tree = B;
tr.dist = D;
tr.names = N;
justVerifyValidity = true;
end
if nargin
if isnumeric(B)
switch size(B,2)
case 2
% ok
case 3
D = B(:,3);
B(:,3)=[];
otherwise
error('Bioinfo:phytree:IncorrectSize','Incorrect size for B or BC')
end
else
error('Bioinfo:phytree:IncorrectType','Incorrect type for B or BC')
end
% test B
if sum(diff(sort(B(:)))~=1) || (min(B(:))~=1)
error('Bioinfo:phytree:IncompleteTree','Branch architecture is not complete')
end
numBranches = size(B,1);
numLeaves = numBranches + 1;
numLabels = numBranches + numLeaves;
h=all(B'>=repmat(numLeaves+1:numLabels,2,1));
if any(h)
error('Bioinfo:phytree:NonChronologicalTree',...
['Branch(es) not in chronological order: [' num2str(find(h)) ']'])
end
if exist('D','var')
if ~isnumeric(D) || any(D(:)<0) || ~all(isreal(D)) || size(D,2)~=1
error('Bioinfo:phytree:DistancesNotValid',...
'Distances must be a column vector of real positive numbers')
end
switch size(D,1)
case numBranches
D = [zeros(numLeaves,1); D]; % add ultrametric distances of leaves
D(B) = D((numLeaves+(1:numBranches))'*[1 1])-D(B); %dist of edges
D(end) = 0; % set root at zero
case numLabels
% ok
otherwise
error('Bioinfo:phytree:DistancesNotValid',...
'Distances must agree either with the number of branches (C) or total nodes (D)')
end
else % set defaut D
% look for parents
P = zeros(numLabels,1);
P(B) = repmat((1:numBranches)',1,2);
P(end) = numBranches;
% look at which level is every branch
L = zeros(numLabels,1);
for ind = 1:numBranches
L(ind+numLeaves) = max(L(B(ind,:))+1);
end
D = L(P+numLeaves)-L;
end
% set default names
for ind = 1:numLeaves
names{ind}=['Leaf ' num2str(ind)]; %#ok
end
for ind = 1:numBranches
names{ind+numLeaves}=['Branch ' num2str(ind)];
end
if exist('N','var')
if ~iscell(N)
error('Bioinfo:phytree:NamesNotValid',...
'Names must be supplied with a cell of strings')
end
switch numel(N)
case numLabels
h = 1:numLabels;
case numLeaves
h = 1:numLeaves;
case numBranches
h = numLeaves+1:numLabels;
otherwise
error('Bioinfo:phytree:NamesNotValid',...
'Names must agree either with the number of branches, number of leaves, or total nodes')
end
for ind = 1:length(h);
str = N{ind};
if ~ischar(str)
error('Bioinfo:phytree:NamesNotValid',...
'Names must be valid strings')
end
names{h(ind)}=str;
end
% check that none of the names is empty
for ind = 1:numLabels
if isempty(names{ind})
if ind > numLeaves
names{ind} = ['Branch ' num2str(ind-numLeaves)];
else
names{ind} = ['Leaf ' num2str(ind)];
end
end
end
if numel(unique(names))~=numLabels
error('Bioinfo:phytree:NamesNotUnique',...
'Names for leaves and branches must be unique')
end
end
% check and corrects a non-monotonic tree
monotonicWarning = false;
for ind = 1:numBranches
if any(D(B(ind,:))<0)
monotonicWarning = true;
tmp = min(D(B(ind,:)));
D(B(ind,:)) = D(B(ind,:)) - tmp;
D(numLeaves+ind) = D(numLeaves+ind) + tmp;
end
end
if monotonicWarning
warning('Bioinfo:phytree:NonMonotonicTree',...
'Non consistent branch distances; \n Incremented branch lengths to hold a Monotonic Phylogenetic Tree')
end
if justVerifyValidity
tr = class(tr,'phytree');
return
end
tr.tree = B;
tr.dist = D;
tr.names = names(:);
% reorder such that there will be no crossings in the displayed tree
tr = prettyOrder(tr);
end %if nargin
% for trees of only one branch correct dimensions
% if size(tr.tree,2) <2 tr.tree = tr.tree'; end
% Makes the tree a class
tr = class(tr,'phyTree');