How to legend Plot Groups of Stacked Bars in Matlab? - matlab

I have some data I want to draw in bars.
Let says I have a group of 3 values for two conditions and a group of 2 values for the same conditions
y3 = [ 1, 1, 1; 0, 1, 2];
y2 = [1 , 1 ; 0, 2];
I know how to use plotBarStackGroups (https://fr.mathworks.com/matlabcentral/fileexchange/32884-plot-groups-of-stacked-bars) to build the bars.
The code to build the bars (maybe it's not the best way)
B = floor(rand(2,2,3));
B(1,:,:) = [[y2(1,:),0] ; y3(1,:)];
B(2,:,:) = [[y2(2,:),0] ; y3(2,:)];
label = {'condition 1', 'condition 2'};
plotBarStackGroups(B,label);
But I want to obtain something like this
and I see two problems : legend by groups (or not prevent the colors to be shared) and the fact that the groups don't have the same length.
I could do two different figures but if I can regroup them, I think it's better

If I correctly got your question
vals = {rand(3,3) rand(4,3)}; %sample data
max_len = max(cellfun(#(x) size(x,1), vals));
pad_vals = cellfun(#(x) cat(1,x,nan(max_len-size(x,1),size(x,2))), vals, 'un',0);
pad_vals = cat(3,pad_vals{:});
figure,
subplot(1,2,1), bar(vals{1},'stacked'), set(gca,'Ylim',[0 3]),
subplot(1,2,2),bar(vals{2},'stacked'), set(gca,'Ylim',[0 3])
%I slightly modified plotBarStackGroups to output bar handles
h=plotBarStackGroups(permute(pad_vals,[1 3 2]),{'1','2','3','4'}) %reorder the matrix according to the function needs
cgroup={'r','m','y';'k','b','c'};
cgroup=cgroup'; h=h'; % For comfortable iteration
for k=1:numel(h),
set(h(k),'Facecolor',cgroup{k}),
end
legend({'1','2','3','4','5','6'})

Related

Simplify a Matlab code involving finding the position of maximum elements of array

I would like your help to make more efficient (maybe, by vectorising) the Matlab code below. The code below does essentially the following: take a row vector A; consider the maximum elements of such a row vector and let, for example, be i and j their positions; construct two columns vectors, the first with all zeros but a 1 positioned at i, the second with all zeros but a 1 positioned at j.
This is my attempt with loops, but it looks more complicated than needed.
clear
rng default
A=[3 2 3];
max_idx=ismember(A,max(A));
vertex=cell(size(A,2),1);
for j=1:size(max_idx,2)
if max_idx(j)>0
position=find(max_idx(j));
vertex_temp=zeros(size(A,2),1);
vertex_temp(position)=1;
vertex{j}=vertex_temp;
else
vertex{j}=[];
end
end
vertex=vertex(~cellfun('isempty',vertex));
Still using a for loop, but more readable:
A = [3 2 3];
% find max indices
max_idx = find(A == max(A));
vertex = cell(numel(max_idx),1);
for k = 1:numel(max_idx)
vertex{k} = zeros(size(A,2),1); % init zeros
vertex{k}(max_idx(k)) = 1; % set value in vector to 1
end
If you really wanted to avoid a for loop, you could probably also use something like this:
A=[3 2 3];
max_idx = find(A==max(A));
outMat = zeros(numel(A), numel(max_idx));
outMat((0:(numel(max_idx)-1)) * numel(A) + max_idx) = 1;
then optionally, if you want them in separate cells rather than columns of a matrix:
outCell = mat2cell(outMat, numel(A), ones(1,numel(max_idx)))';
However, I think this might be less simple and readable than the existing answers.
Is there a specific reason you want a cell array rather than a matrix?
If you can have it all in one vector:
A = [3 2 3]
B_rowvec = A == max(A)
B_colvec = B_rowvec'
If you need them separated into separate vectors:
A = [3 2 3]
Nmaxval = sum(A==max(A))
outmat = zeros(length(A),Nmaxval)
for i = 1:Nmaxval
outmat(find(A==max(A),i),i)=1;
end
outvec1 = outmat(:,1)
outvec2 = outmat(:,2)
Basically, the second input for find will specify which satisfactory instance of the first input you want.
so therefore
example = [ 1 2 3 1 2 3 1 2 3 ]
first = find(example == 1, 1) % returns 1
second = find(example == 1, 2) % returns 4
third = find(example == 1, 3) % returns 7

Using boxplot with different length of vectors

Hi I try to make a boxplot for hourly values of data for differnt months. So in one diagramm I have a boxplot for January Feb March and so on... As the amount of hours of each month vary boxplot always gives me an error.
code
X=[N11(:,9) D12(:,9) J1(:,9) F2(:,9) ];
G=[1 2 3 4];
boxplot(X,G)
size of data:
J1=744
F2=624
D12=744
N11=720
thanks matthias
You can manually append all of your data together in a single vector and then create a grouping variable g whose label indicates to which group a data point belongs on the corresponding row. For example:
A = randn(10, 1); B = randn(12, 1); C = randn(4, 1);
g = [repmat(1, [10, 1]) ; repmat(2, [12, 1]); repmat(3, [4, 1])];
figure; boxplot([A; B; C], g);
Similar questions have been asked before. See:
http://www.mathworks.com/matlabcentral/answers/60818-boxplot-with-vectors-of-different-lengths
Basically, you put all the data in a 1-D array and use another 1-D array (of the same length) to label the groups.

Efficient aggregation of high dimensional arrays

I have a 3 dimensional (or higher) array that I want to aggregate by another vector. The specific application is to take daily observations of spatial data and average them to get monthly values. So, I have an array with dimensions <Lat, Lon, Day> and I want to create an array with dimensions <Lat, Lon, Month>.
Here is a mock example of what I want. Currently, I can get the correct output using a loop, but in practice, my data is very large, so I was hoping for a more efficient solution than the second loop:
% Make the mock data
A = [1 2 3; 4 5 6];
X = zeros(2, 3, 9);
for j = 1:9
X(:, :, j) = A;
A = A + 1;
end
% Aggregate the X values in groups of 3 -- This is the part I would like help on
T = [1 1 1 2 2 2 3 3 3];
X_agg = zeros(2, 3, 3);
for i = 1:3
X_agg(:,:,i) = mean(X(:,:,T==i),3);
end
In 2 dimensions, I would use accumarray, but that does not accept higher dimension inputs.
Before getting to your answer let's first rewrite your code in a more general way:
ag = 3; % or agg_size
X_agg = zeros(size(X)./[1 1 ag]);
for i = 1:ag
X_agg(:,:,i) = mean(X(:,:,(i-1)*ag+1:i*ag), 3);
end
To avoid using the for loop one idea is to reshape your X matrix to something that you can use the mean function directly on.
splited_X = reshape(X(:), [size(X_agg), ag]);
So now splited_X(:,:,:,i) is the i-th part
that contains all the matrices that should be aggregated which is X(:,:,(i-1)*ag+1:i*ag)) (like above)
Now you just need to find the mean in the 3rd dimension of splited_X:
temp = mean(splited_X, 3);
However this results in a 4D matrix (where its 3rd dimension size is 1). You can again turn it into 3D matrix using reshape function:
X_agg = reshape(temp, size(X_agg))
I have not tried it to see how much more efficient it is, but it should do better for large matrices since it doesn't use for loops.

Creating a vector with random sampling of two vectors in matlab

How does one create a vector that is composed of a random sampling of two other vectors?
For example
Vector 1 [1, 3, 4, 7], Vector 2 [2, 5, 6, 8]
Random Vector [random draw from vector 1 or 2 (value 1 or 2), random draw from vector 1 or 2 (value 3 or 5)... etc]
Finally, how can one ask matlab to repeat this process n times to draw a distribution of results?
Thank you,
There are many ways you could do this. One possibility is:
tmp=round(rand(size(vector1)))
res = tmp.*vector1 + (1-tmp).*vector2
To get one mixed sample, you may use the idea of the following code snippet (not the optimal one, but maybe clear enough):
a = [1, 3, 4, 7];
b = [2, 5, 6, 8];
selector = randn(size(a));
sample = a.*(selector>0) + b.*(selector<=0);
For n samples put the above code in a for loop:
for k=1:n
% Sample code (without initial "samplee" assignments)
% Here do stuff with the sample
end;
More generally, if X is a matrix and for each row you want to take a sample from a column chosen at random, you can do this with a loop:
y = zeros(size(X,1),1);
for ii = 1:size(X,1)
y(ii) = X(ii,ceil(rand*size(X,2)));
end
You can avoid the loop using clever indexing via sub2ind:
idx_n = ceil(rand(size(X,1),1)*size(X,2));
idx = sub2ind(size(X),(1:size(X,1))',idx_n);
y = X(idx);
If I understand your question, you are choosing two random numbers. First you decide whether to select vector 1 or vector 2; next you pick an element from the chosen vector.
The following code takes advantage of the fact that vector1 and vector2 are the same length:
N = 1000;
sampleMatrix = [vector1 vector2];
M = numel(sampleMatrix);
randIndex = ceil(rand(1,N)*M); % N random numbers from 1 to M
randomNumbers = sampleMatrix(randIndex); % sample N times from the matrix
You can then display the result with, for instance
figure; hist(randomNumbers); % draw a histogram of numbers drawn
When vector1 and vector2 have different elements, you run into a problem. If you concatenate them, you will end up picking elements from the longer vector more often. One way around this is to create random samplings from both arrays, then choose between them:
M1 = numel(vector1);
M2 = numel(vector2);
r1 = ceil(rand(1,N)*M1);
r2 = ceil(rand(1,N)*M2);
randMat = [vector1(r1(:)) vector2(r2(:))]; % two columns, now pick one or the other
randPick = ceil(rand(1,N)*2);
randomNumbers = [randMat(randPick==1, 1); randMat(randPick==2, 2)];
On re-reading, maybe you just want to pick "element 1 from either 1 or 2", then "element 2 from either 1 or 2", etc for all the elements of the vector. In that case, do
N=numel(vector1);
randPick = ceil(rand(1,N)*2);
randMat=[vector1(:) vector2(:)];
randomNumbers = [randMat(randPick==1, 1); randMat(randPick==2, 2)];
This problem can be solved using the function datasample.
Combine both vectors into one and apply the function. I like this approach more than the handcrafted versions in the other answers. It gives you much more flexibility in choosing what you actually want, while being a one-liner.

Large, mostly empty data structure for tetrahedron faces

I'm trying to link the node id of every face in a tetrahedron with it's corresponding tetra id.
tetras = [1 2 3 4 % Tetra 1
5 6 7 8] % Tetra 2
For tetra 1, there are four faces:
faces = [1 2 3; 1 2 4; 1 3 4; 2 3 4] % Notice these are sorted
Then I'd like to store these in a data structure:
tet_for_face = cell(8,8,8) % 8 allows for the maximum node id
tet_for_face{1,2,3} = 1;
tet_for_face{1,2,4} = 1;
tet_for_face{1,3,4} = 1;
tet_for_face{2,3,4} = 1;
This means that I can find the tetra ID of any particular face in O(1):
tet_for_face{2,3,3}
ans = []
tet_for_face{2,3,4}
ans = 1
The problem with this approach is that it requires contiguous memory. As my mesh gets larger, I run out of memory:
cell(1000, 1000, 1000)
??? Error using ==> cell
Out of memory. Type HELP MEMORY for your options.
I've also played around with using nested cells:
tet = cell(num_nodes, 1);
tet2 = cellfun(#(x) cell(num_nodes, 1), tet, 'UniformOutput', 0);
tet3 = cellfun(#(x) cellfun(#(y) cell(num_nodes, 1), x, 'UniformOutput', 0), tet2, 'UniformOutput', 0);
tet3{2}{3}{4} = 1;
...
Although this works for small meshes, and doesn't require contiguous memory (AFAIK), it has a nasty habit of crashing MATLAB with N=1000.
Any ideas?
After a bit of playing with sparse arrays (which can only be 1D or 2D, not 3D), and not getting anywhere, I decided to go with containers.Map (HashMap).
I used string keys, and the fastest way I found of producing them I found was using sprintf (rather than int2str or mat2str)
Sample code:
tet = containers.Map;
for tetra_id in tetras
for face in faces
face_key = sprintf('%d ', face);
tet(face_key) = tetra_id;
This gives me a map like so:
tet('1 2 3') = 1
You can use sparse matrices to deal with many problems arising with meshes. It depends on what you want to do with this data structure in practice, but here is one example:
% tetras and faces are transposed - column-wise storage
tetras = [1 2 3 4; 5 6 7 8]';
faces = [1 2 3; 1 2 4; 1 3 4; 2 3 4]';
ntetras = size(tetras, 2);
nfaces = size(faces, 2);
nfacenodes = size(faces, 1);
% construct face definitions for all tetras
tetras_faces = reshape(tetras(faces, :), nfacenodes, ntetras*nfaces);
% assign the faces to tetras keeping the face id within the tetra, if you need it
enum_faces = repmat(1:ntetras*nfaces, nfacenodes, 1);
% create a sparse matrix connecting tetra faces to tetras.
% Every column contains 3 non-zeros - 1 for every node in a face
% The number of matrix columns is ntetras*nfaces - 4 columns for every element.
A = sparse(tetras_faces, enum_faces, 1);
Now to extract the information you need you can multiply A by a vector holding the information about the face you are looking for:
v = sparse(ntetras*nfaces, 1);
v([1 2 3]) = 1;
tetra_id = ceil(find(A*v==nfacenodes)/nfaces)
Note that this is just an example. You can extract much more useful information this way, and you could perform more sophisticated searches using matrix-matrix multiplication instead of matrix-vector multiplication.