Ploting a nested cell as a tree using treeplot: MATLAB

后端 未结 1 944
孤城傲影
孤城傲影 2021-01-06 06:58

I have a complex cell that represents a tree structure:

CellArray = {1,1,1,{1,1,1,{1,1,{1,{1 1 1 1 1 1 1 1}, 1,1}, 1,1},1,1,1},1,1,1,{1,1,1,1}};
相关标签:
1条回答
  • 2021-01-06 07:15

    We can create a recursive function, which explores your cell array and creates a tree pointer array (as described in the docs) to each node's parent.

    This function takes a cell array (like the one in your question) which contains either scalars or nested cell arrays.


    treebuilder logic:

    1. If an item is a scalar, assign it a parent node number, increment node number by 1
    2. If an item is a cell array, run treebuilder on that cell, returning the maximum node number which was reached (along with the generated sub-tree).
    3. Recursive function because of step 2, so repeat until every element done

    Function:

    function treearray = getTreeArray(cellarray)
        % initialise the array construction from node 0
        treearray = [0, treebuilder(cellarray, 1)]; 
        % recursive tree building function, pass it a cell array and root node
        function [out, node] = treebuilder(cellarray, rnode)
            % Set up variables to be populated whilst looping
            out = []; 
            % Start node off at root node
            node = rnode;
            % Loop over cell array elements, either recurse or add node
            for ii = 1:numel(cellarray)
                tb = []; node = node + 1;
                if iscell(cellarray{ii})
                    [tb, node] = treebuilder(cellarray{ii}, node);
                end
                out = [out, rnode, tb];   
            end
        end
    end
    

    Usage with simple example

    Here is a more simple example than yours, so we can check logic works easily.

    myCellArray = {1 1 {1 1 1 {1 1 1}}};
    % This cell array has 3 levels: 
    % - 3 child nodes (2,3,4) of the root node (1) 
    % - Last node on the first level (4) has 4 children:
    %     - 4 child nodes on second level (5,6,7,8) 
    %     - Last node on the first level (8) has 3 children:
    %         - 3 child nodes on third level (9,10,11)
    myTreeArray = getTreeArray(myCellArray);
    % Output, we see the corresponding nodes as listed above:
    % [0 1 1 1 4 4 4 4 8 8 8]
    treeplot(myTreeArray)
    


    Your cell array

    I think this works as expected, note you don't have to define the myCellArray or myTreeArray variables:

    treeplot(getTreeArray({1,1,1,{1,1,1,{1,1,{1,{1 1 1 1 1 1 1 1}, 1,1}, 1,1},1,1,1},1,1,1,{1,1,1,1}}))
    

    Here is the output image, showing that the algorithm can cope with the more complicated tree. Speed doesn't seem too bad either, although displaying extremely complicated trees will be fairly redundant anyway!


    Edit: Labelling the nodes

    You can label the nodes by getting their position using treelayout and keeping track of the values as you encounter them when building the tree array. The function should be tweaked for this "keeping track" like so:

    function [treearray, nodevals] = getTreeArray(cellarray)
        % initialise the array construction from node 0
        [nodes, ~, nodevals] = treebuilder(cellarray, 1); 
        treearray = [0, nodes];
        % recursive tree building function, pass it a cell array and root node
        function [out, node, nodevals] = treebuilder(cellarray, rnode)
            % 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
            for ii = 1:numel(cellarray)
                node = node + 1;
                if iscell(cellarray{ii})
                    [tb, node, nv] = treebuilder(cellarray{ii}, node);
                    out = [out, rnode, tb];  
                    nodevals = [nodevals, nv];
                else
                    out = [out, rnode];
                    nodevals = [nodevals, {node; cellarray{ii}}];
                end 
            end
        end
    end
    

    Note: You could use a similar adaptation to keep track of the node number instead of the node value if you wanted to number each node on the plot.

    I have used a cell array here so that you could have text or numerical values on each node. If you only ever want numerical values, it may shorten the post-formatting to store nodevals in a matrix instead.

    Then to plot this you can use

    % Run the tree building script above
    [treearray, nodevals] = getTreeArray(myCellArray);
    % Plot
    treeplot(treearray);
    % Get the position of each node on the plot  
    [x,y] = treelayout(treearray);
    % Get the indices of the nodes which have values stored
    nodeidx = cell2mat(nodevals(1,:));
    % Get the labels (values) corresponding to those nodes. Must be strings in cell array
    labels = cellfun(@num2str, nodevals(2,:), 'uniformoutput', 0);
    % Add labels, with a vertical offset to the y coords so that labels don't sit on nodes
    text(x(nodeidx), y(nodeidx) - 0.03, labels);
    

    Example output for the cell myCellArray = {{17, 99.9}, 50}, where I've chosen those numbers to make it clear they're not the actual "node number"!

    0 讨论(0)
提交回复
热议问题