Search code examples
matlabmultidimensional-arraygraphmatlab-figurecells

Plotting a nested cell as a predefined structure: MATLAB


I have series of nested cells which represent tree structures. An example of one of these cells with its corresponding structure is the image below:

I need to know what are the possible methods to draw the corresponding structure in Matlab. So far, I have found this method here that might be the best solution available, but still I am struggling how to implement it. Thanks.


Solution

  • just so this doesn't remain unanswered...

    myCellArray = {0,0,{1,0,0,{1,0},0,0},{1,0,{1,0},0},{1,1},0,0};
    [myTreeArray,myTreeEvals] = getTreeArray(myCellArray);
    MYtreeplot(myTreeArray,myTreeEvals)
    

    For the fourth example output, you can modify to suit your needs: enter image description here

    Two functions, slightly adapted from SO answers, shown below.

    First based on related answer by @Wolfie:

    function [treearray, nodevals]  = getTreeArray(cellarray)
        % initialise the array construction from node 0
        [nodes, ~, nodevals] = treebuilder(cellarray, 1,0); 
        nodevals = decellify(nodevals);
        treearray = nodes-1;
        % recursive tree building function, pass it a cell array and root node
        function [out, node, nodevals] = treebuilder(cellarray, rnode,skipFirst)
            % Set up variables to be populated whilst looping
            out = []; nodevals = [];
            % Start node off at root node
            node = rnode;
            % Loop over cell array elements, either recurse or add node
            num2Loop = 1:numel(cellarray);
            % ADDED, with input to function everywhere used
            if skipFirst
                num2Loop = 2:numel(cellarray);
            end
            %End ADDED
            for ii = num2Loop
                tb = []; node = node + 1;
                if iscell(cellarray{ii})
                    [tb, node] = treebuilder(cellarray{ii}, node,1);
                end
                out = [out, rnode, tb];
            end
            nodevals = [nodevals,cellarray];
        end
    
        function data = decellify(data)
            try
                data = cellfun(@decellify,data,'un',0);
                if any(cellfun(@iscell,data))
                    data = [data{:}];
                end
            catch
                % a non-cell node, so simply return node data as-is
            end
        end
    end
    

    And based on this SO answer for treeplots:

    function MYtreeplot(treearray,nodevals)
    % At first we need to get the get the x and y coordinates of every node in the original tree plot and find all leaves in it
    [x,y] = treelayout(treearray);
    leaves = find( y == min(y) );
    %ADDED
    if nargin < 2
        leaveParents = find( y ~= min(y) );
        leaveChilds = leaves;
    else
        leaveParents = find([nodevals{:}]==1);
        leaveChilds = find([nodevals{:}]~=1);
    end
    %END ADDED
    num_layers = 1/min(y)-1;
    chains = zeros(num_layers, length(leaves));
    % Next, we reconstruct every chain in the tree plot and store it in a matrix (by doing so, we can later change the y position of the nodes)
    for l=1:length(leaves)
        index = leaves(l);
        chain = [];
        chain(1) = index;
        parent_index = treearray(index);
        j = 2;
        while (parent_index ~= 0)
            chain(j) = parent_index;
            parent_index = treearray(parent_index);
            j = j+1;
        end
        chains(:,l) = padarray(flip(chain), [0, num_layers-length(chain)], 'post');
    end
    % Now we compute the new y-coordinates determined by the row index in the matrix and dependent on the number of layers in the tree:
    y_new = zeros(size(y));
    for i=1:length(treearray)
        [r,c] = find(chains==i, 1);
        y_new(i) = max(y) - (r-1)*1/(num_layers+1);
    end
    % We can now plot the re-positioned nodes and add the connecting lines:
    figure;plot(x(leaveParents), y_new(leaveParents), '.r','MarkerSize',16);
    hold on
    plot(x(leaveChilds), y_new(leaveChilds), '.k','MarkerSize',16);
    for c=1:size(chains, 2)
        line_x = x(chains(chains(:,c)>0, c));
        line_y = y_new(chains(chains(:,c)>0, c));
        line(line_x, line_y);
    end
    axis([0 1 0 1]);