Convert multidimensional matrix of frequencies (counts) to a 2d list

2 views (last 30 days)
I have a 6d matrix, N . Each entry of this matrix is a non-negative integer. N represents a population of individuals, each characterized by 6 traits (= dimensions of the matrix). The number inside each entry is the number (count) of individuals that are characterized by that specific trait combination. Thus, the total number of individuals is given by Nb = sum(N(:)).
I would like to be able to rearrange N in the form of a 2d matrix, with Nb rows and number of columns equal to the number dimensions of N (so, in this case, Nb×6). A given entry ( x,y) would be equal to the y th trait value of individual x. (The order of the rows is irrelevant and in fact it would be even better if it were random.) Of course, some of the rows would be repeated, because several individuals are "equal".
Could you please help me do this? I would also like to reverse it afterwards, i.e., return it to the original 6d shape. Thank you!
  1 Comment
Guillaume
Guillaume on 27 Apr 2015
Could you show a small example with only two traits (so the input matrix is 2D, it's easier to understand). I'm particularly unclear on what is the size of each dimension of N.
My guess would be N is 2 x 2 x 2 x 2 x 2 x 2, where when the index of dimension is 1 the individual does not have a trait, and when index of dimension is 2 the individual has the trait.
Similarly, in the output, do you want 1 for have trait and 0 for have not?

Sign in to comment.

Accepted Answer

Guillaume
Guillaume on 27 Apr 2015
Edited: Guillaume on 27 Apr 2015
Going on the assumption that N is a 6d matrix of size 2 x 2 x 2 x 2 x 2 x 2, where for example
N(1, 1, 2, 2, 1, 1) = 5
means 5 individuals have trait 3 and 4 (dim = 2), and going on the assumption you want 1 for has trait and 0 for not:
N = randi(10, ones(1, 6)*2) %demo data
onoff = cell(1, ndims(N)); %to receive result of ndgrid
[onoff{:}] = ndgrid([0 1]); %0 for hasn't, 1 for has
%for each pop redistribute the onoff into a row vector and replicate that many rows:
newN = arrayfun(@(pop, varargin) repmat(cell2mat(varargin), pop, 1), N, onoff{:}, 'UniformOutput', false);
newN = vertcat(newN{:}); %flatten the 6D cell array into a matrix
To make the order of rows randow:
newN = newN(randperm(size(newN, 1)), :);
To go back the other way is easier, just using accumarray:
oldN = accumarray(newN + 1, 1)
isequal(oldN, N) %return true
Note that the code makes no assumption about the number of traits, so will work with any number.
  3 Comments
Guillaume
Guillaume on 27 Apr 2015
Ok, It's much clearer now. The algorithm is pretty much the same. In my original answer there's only two states per trait, but it can easily be extended to any number of states:
N = [2 0 3 1 0; 2 7 1 2 0; 8 4 5 0 1; 10 2 1 0 2; 2 3 6 4 9]; %demo data
states = cell(1, ndims(N)); %to receive result of ndgrid
%ndgrid the states. The following makes no assumption on the number of
%state per trait. There can even be a different number of state for each
%trait:
traitstates = arrayfun(@(nt) 1:nt, size(N), 'UniformOutput', false); %range of each trait
[states{:}] = ndgrid(traitstates{:});
%if all trait have the same number of states s, the two lines can be
%replaced with:
%[states{:}] = ndgrid(1:s);
%redistribute the states in row vectors replicated by pop size:
newN = arrayfun(@(pop, varargin) repmat(cell2mat(varargin), pop, 1), N, states{:}, 'UniformOutput', false);
newN = vertcat(newN{:}) %and flatten into a matrix
The transformation back is still the same:
oldN = accumarray(newN, 1)

Sign in to comment.

More Answers (0)

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!