MATLAB: create new matrix from existing matrix according to specifications - matlab

Assume we have the following data:
H_T = [36 66 21 65 52 67 73; 31 23 19 33 36 39 42]
P = [40 38 39 40 35 32 37]
Using MATLAB 7.0, I want to create three new matrices that have the following properties:
The matrix H (the first part in matrix H_T) will be divided to 3 intervals:
Matrix 1: the 1st interval contains the H values between 20 to 40
Matrix 2: the 2nd interval contains the H values between 40 to 60
Matrix 3: the 3rd interval contains the H values between 60 to 80
The important thing is that the corresponding T and P will also be included in their new matrices meaning that H will control the new matrices depending on the specifications defined above.
So, the resultant matrices will be:
H_T_1 = [36 21; 31 19]
P_1 = [40 39]
H_T_2 = [52; 36]
P_2 = [35]
H_T_3 = [66 65 67 73; 23 33 39 42]
P_3 = [38 40 32 37]
Actually, this is a simple example and it is easy by looking to create the new matrices depending on the specifications, BUT in my values I have thousands of numbers which makes it very difficult to do that.

Here's a quick solution
[~,bins] = histc(H_T(1,:), [20 40 60 80]);
outHT = cell(3,1);
outP = cell(3,1);
for i=1:3
idx = (bins == i);
outHT{i} = H_T(:,idx);
outP{i} = P(idx);
end
then you access the matrices as:
>> outHT{3}
ans =
66 65 67 73
23 33 39 42
>> outP{3}
ans =
38 40 32 37

Related

Matlab - Create a vector using another vector as the limits

Say I have the following columns vector Z
1 53 55 57 60 64 68 70 71 72 74 76 77 78 79 80 255
I want to use it to create a matrix such that each row would contain all the number between (and including) 2 adjacent elements in Z
So the output matrix should be something like this:
1 2 3 .... 53
53 54 55
55 56 57
57 58 60
....
80 81 ... 255
I've been searching for something similar but couldn't find it.
Thanks
See if this works for you -
lens = diff(Z)+1;
mask1 = bsxfun(#le,[1:max(lens)]',lens); %//'
array1 = zeros(size(mask1));
array1(mask1) = sort([1:255 Z(2:end-1)]);
out = array1.'; %//'# out is the desired output
Try this to break the monotony of bsxfun :) :
d = diff(Z);
N = max(d)+1;
R = zeros(length(Z)-1,N);
for i = 1:length(Z)-1
R(i,1:1+d(i)) = Z(i):Z(i+1);
end
EDIT:
I know that the general consensus is that one always should try to avoid loops in Matlab, but is this valid for this example? I know that this is a broad question, so lets focus on this particular problem and compare bsxfun to JIT loop. Comparing the two proposed solutions:
the code used for testing:
Z = [1 53 55 57 60 64 68 70 71 72 74 76 77 78 79 80 255];
%[1 3 4, 6];
nn = round(logspace(1,4,10));
tm1_nn = zeros(length(nn),1);
tm2_nn = zeros(length(nn),1);
for o = 1:length(nn)
tm1 = zeros(nn(o),1);
tm2 = zeros(nn(o),1);
% approach1
for k = 1:nn(o)+1
tic
d = diff(Z);
N = max(d)+1;
R = zeros(length(Z)-1,N);
for i = 1:length(Z)-1
R(i,1:1+d(i)) = Z(i):Z(i+1);
end
tm1(k) = toc;
end
%approach 2
for k = 1:nn(o)+1
tic
lens = diff(Z)+1;
mask1 = bsxfun(#le,[1:max(lens)]',lens); %//'
array1 = zeros(size(mask1));
array1(mask1) = sort([1:255 Z(2:end-1)]);
out = array1.';
tm2(k) = toc;
end
tm1_nn(o) = mean(tm1);%sum(tm1);%mean(tm1);%
tm2_nn(o) = mean(tm2);%sum(tm2);%mean(tm2);%
end
semilogx(nn,tm1_nn, '-ro', nn,tm2_nn, '-bo')
legend('JIT loop', 'bsxfun')
xlabel('log_1_0(Number of runs)')
%ylabel('Sum execution time')
ylabel('Mean execution time')
grid on
I encountered other tasks previously where the loop was faster. (or I mess up the comparison?)

Conditional coloring of histogram graph in MATLAB

I have a histogram that I want conditional coloring in it with this rule :
Values that are upper than 50 have red bars and values lower than 50 have blue bars.
Suppose that we have this input matrix:
X = [32 64 32 12 56 76 65 44 89 87 78 56 96 90 86 95 100 65];
I want default bins of MATLAB and applying this coloring on X-axes (bins). I'm using GUIDE to design my GUI and this histogram is an axes in my GUI.
This is our normal graph. Bars with upper values than 50 should be red and bars with lower values than 50 should be green (X-axes). Bars with upper values than 50 should be red and ?
I think this does what you want (as per comments). The bar around 50 is split into the two colors. This is done by using a patch to change the color of part of that bar.
%// Data:
X = [32 64 32 12 56 76 65 44 89 87 78 56 96 90 86 95 100 65]; %// data values
D = 50; %// where to divide into two colors
%// Histogram plot:
[y n] = hist(X); %// y: values; n: bin centers
ind = n>50; %// bin centers: greater or smaller than D?
bar(n(ind), y(ind), 1, 'r'); %// for greater: use red
hold on %// keep graph, Or use hold(your_axis_handle, 'on')
bar(n(~ind), y(~ind), 1, 'b'); %// for smaller: use blue
[~, nd] = min(abs(n-D)); %// locate bar around D: it needs the two colors
patch([(n(nd-1)+n(nd))/2 D D (n(nd-1)+n(nd))/2], [0 0 y(nd) y(nd)], 'b');
%// take care of that bar with a suitable patch
X = [32 64 32 12 56 76 65 44 89 87 78 56 96 90 86 95 100 65];
then you create an histogram, but you are only going to use this to get the numbers of bins, the numbers of elements and positions:
[N,XX]=hist(X);
close all
and finally here is the code where you use the Number of elements (N) and the position (XX) of the previous hist and color them
figure;
hold on;
width=8;
for i=1:length(N)
h = bar(XX(i), N(i),8);
if XX(i)>50
col = 'r';
else
col = 'b';
end
set(h, 'FaceColor', col)
end
here you can consider using more than one if and then you can set multiple colors
cheers
First sort X:
X = [32 64 32 12 56 76 65 44 89 87 78 56 96 90 86 95 100 65];
sorted_X = sort(X)
sorted_X :
sorted_X =
Columns 1 through 14
12 32 32 44 56 56 64 65 65 76 78 86 87 89
Columns 15 through 18
90 95 96 100
Then split the data based on 50:
idx1 = find(sorted_X<=50,1,'last');
A = sorted_X(1:idx1);
B = sorted_X(idx1+1:end);
Display it as two different histograms.
hist(A);
hold on;
hist(B);
h = findobj(gca,’Type’,’patch’);
display(h)
set(h(1),’FaceColor’,’g’,’EdgeColor’,’k’);
set(h(2),’FaceColor’,’r’,’EdgeColor’,’k’);

Extract matrix elements using a vector of column indices per row

I have an MxN matrix and I want a column vector v, using the vector s that tells me for each row in the matrix what column I will take.
Here's an example:
Matrix =
[ 4 13 93 20 42;
31 18 94 64 02;
7 44 24 91 15;
11 20 43 38 31;
21 42 72 60 99;
13 81 31 87 50;
32 22 83 24 04]
s = [4 4 5 4 4 4 3].'
And the desired output is:
v = [20 64 15 38 60 87 83].'
I thought using the expression
Matrix(:,s)
would've work but it doesn't. Is there a solution without using for loops to access the rows separately?
It's not pretty, and there might be better solutions, but you can use the function sub2ind like this:
M(sub2ind(size(M),1:numel(s),s'))
You can also do it with linear indexing, here is an example:
M=M'; s=s';
M([0:size(M,1):numel(M)-1]+s)

Matlab: find mode in range

I have a matrix like:
A=
10 31 32 22
32 35 52 77
68 42 84 32
I need a function like mode but with range, for example mymode(A,10) that return 30, find most frequent number in range 0-10, 10-20, 20-30, .... and return most number in range.
You can use histc to bin your data into the ranges of your desire and then find the bin with the most members using max on the output of histc
ranges = 0:10:50; % your desired ranges
[n, bins] = histc(A(:), ranges); % bin the data
[v,i] = max(n); % find the bin with most occurrences
[ranges(i) ranges(i+1)] % edges of the most frequent bin
For your specific example this returns
ans =
30 40
which matches with your required output, as the most values in A lay between 30 and 40.
[M,F] = mode( A((A>=2) & (A<=5)) ) %//only interested in range 2 to 5
...where M will give you the mode and F will give you frequency of occurence
> A = [10 31 32 22; 32 35 52 77; 68 42 84 32]
A =
10 31 32 22
32 35 52 77
68 42 84 32
> min = 10
min = 10
> max = 40
max = 40
> mode(A(A >= min & A <= max))
ans = 32
>
I guess by the number of different answers that we may be missing your goal. Here is my interpretation.
If you want to have many ranges and you want to output most frequent number for every range, create a cell containing all desired ranges (they could overlap) and use cellfun to run mode() for every range. You can also create a cell with desired ranges using arrayfun in a similar manner:
A = [10 31 32 22; 32 35 52 77; 68 42 84 32];
% create ranges
range_step = 10;
range_start=[0:range_step:40];
range=arrayfun(#(r)([r r+range_step]), range_start, 'UniformOutput', false)
% analyze ranges
o = cellfun(#(r)(mode(A(A>=r(1) & A<=r(2)))), range, 'UniformOutput', false)
o =
[10] [10] [22] [32] [42]

Remove the minimum values per each column of a Matrix

If I had a matrix A such as:
63 55 85 21 71
80 65 85 48 53
55 60 93 71 66
21 65 40 33 21
61 90 80 48 50
... and so on how would I find the minimum values of each column and remove those numbers from the matrix completely, meaning essentially I would have one less row overall.
I though about using:
[C,I] = min(A);
A(I) = [];
but that wouldn't remove the necessary numbers, and also reshape would not work either. I would like for this to work with an arbitrary number of rows and columns.
A = [
63 55 85 21 71
80 65 85 48 53
55 60 93 71 66
21 65 40 33 21
61 90 80 48 50
];
B = zeros( size(A,1)-1, size(A,2));
for i=1:size(A,2)
x = A(:,i);
maxIndex = find(x==min(x(:)),1,'first');
x(maxIndex) = [];
B(:,i) = x;
end
disp(B);
Another vectorized solution:
M = mat2cell(A,5,ones(1,size(A,2)));
z = cellfun(#RemoveMin,M);
B = cell2mat(z);
disp(B);
function x = RemoveMin(x)
minIndex = find(x==min(x(:)),1,'first');
x(minIndex) = [];
x = {x};
end
Another solution:
[~,I] = min(A);
indexes = sub2ind(size(A),I,1:size(A,2));
B = A;
B(indexes) = [];
out = reshape(B,size(A)-[1 0]);
disp(out);
Personally I prefer the first because:
For loops aren't evil - many times they are actually faster (By using JIT optimizer)
The algorithm is clearer to the developer who reads your code.
But of course, its up to you.
Your original approach works if you convert the row indices resulting from min into linear indices:
[m, n] = size(A);
[~, row] = min(A,[],1);
A(row + (0:n-1)*m) = [];
A = reshape(A, m-1, n);