I was trying to get the min values of a matrix before the max values of the matrix occurred. I have two matrices: matrix data and matrix a. Matrix a is a subset of matrix data and is composed of the max values of matrix data. I have the following code but obviously doing something wrong.
edit:
Matrix a are the max values of matrix data. I derived it from:
for x=1:size(data,1)
a(x)=max(data(x,:));
end
a=a'
clear x
matrix b code:
for x=1:size(data,1)
b(x)=min(data(x,(x<data==a)));
end
b=b'
clear x
matrix data matrix a matrix b
1 2 3 4 4 1
6 5 4 7 7 4
9 6 12 5 12 6
I need all the min values that occurred before to matrix a occurred in matrix data
Short and simple:
[a,idxmax] = max(data,[],2);
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii))), 1:size(data,1));
which is the same as
b=NaN(1,size(data,1)); % preallocation!
for ii=1:size(data,1)
b(ii) = min(data(ii,1:idxmax(ii)));
end
Ignore maximum itself
If you want minimum of everything really before (and not including the maximum), it's possible that the maximum is the first number, and you try taking minimum of an empty matrix. Solution then is to use cell output, which can be empty:
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii)-1)), 1:size(data,1),'uni',false);
Replace empty cells with NaN
If you want to replace empty cells to Nan and then back to a matrix use this:
b(cellfun(#isempty,b))={NaN};
b=cell2mat(b);
or simply use the earlier version and replace b(ii) with NaN when it is equal to a(ii) same outcome:
b = arrayfun(#(ii) min(data(ii,1:idxmax(ii))), 1:size(data,1));
b(b'==a) = NaN
Example:
data=magic(4)
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
outputs:
a' = 16 11 12 15
b =
16 5 6 4
and
b =[1x0 double] [5] [6] [4]
for the 2nd solution using cell output and ignoring the maximum itself also.
And btw:
for x=1:size(data,1)
a(x)=max(data(x,:));
end
a=a'
clear x
can be replaced with
a=max(data,[],2);
It's not pretty but this is the only way I found so far of doing this kind of thing without a loop.
If loops are ok I would recommend Gunther Struyf answer as the most compact use of matlab's in-built array looping function, arrayfun.
Some of the transposition etc may be superfluous if you're wanting column mins instead of row...
[mx, imx] = max(data');
inds = repmat(1:size(data,2), [size(data,1),1]);
imx2 = repmat(imx', [1, size(data,2)]);
data2 = data;
data2(inds >= imx2) = inf;
min(data2');
NOTE: if data is not needed we can remove the additional data2 variable, and reduce the line count.
So to demonstrate what this does, (and see if I understood the question correctly):
for input
>> data = [1,3,-1; 5,2,1]
I get minima:
>> min(data2')
ans = [1, inf]
I.e. it only found the min values before the max values for each row, and anything else was set to inf.
In words:
For each row get index of maximum
Generate matrix of column indices
Use repmat to generate a matrix, same size as data where each row is index of maximum
Set data to infinity where column index > max_index matrix
find min as usual.
Related
I have an input matrix as below
all = [12 16;12 13;8 14;14 19;3 6;8 6;13 25;25 14;7 2];
I need the following output
output = [12 16;8 14;3 6;13 25;7 2];
The explanation for the output is as follows.
First row of input i.e. 12 16 is the first row in output as both the numbers have never been repeated before in the output matrix (obviously).
Second row of input i.e 12 13 is not needed as the number 12 is present in first row of output i.e repeated
Third row of input i.e 8 14 is second row of output as both the numbers have never been repeated before in the output matrix.
Fourth row of input i.e 14 19 is not needed as the number 14 is present in output i.e repeated
On similar lines
3 6 needed as both are not repeated,
8 6 not needed as both 8 and 6 are repeated,
13 25 needed as both are not repeated
25 14 not needed as both are repeated
7 2 needed as both are not repeated
I am not able to get any ideas to start. Any help will be appreciated.
Thanks!
One Liner Solution
res = all(arrayfun(#(ii) isempty(intersect(all(1:ii-1,:),all(ii,:))),1:size(all,1)),:);
Result
res =
12 16
8 14
3 6
7 2
Explanation
let's divide the one-liner into a more detailed and documented chunk of code:
%defines a function which validates for each index wheter the row is
%completely unique are not.
uniqueRowIndicator = #(ii) isempty(intersect(all(1:ii-1,:),all(ii,:)));
%finds all the unique row in the matrix
inds = arrayfun(uniqueRowIndicator,1:size(all,1));
%extracts the result from the returned indices
res = all(inds,:);
This assumes that if a row contains two equal values they count as repeated and thus the row should be removed.
Don't use all as a variable name, because that shadows a function:
A = [12 16;12 13;8 14;14 19;3 6;8 6;13 25;25 14;7 2]; % input matrix
[~, u] = unique(A.', 'first'); % unique values of linearized transposed A.
% In recent Matlab versions you an remove 'first '
M = false(flip(size(A))); % initiallize mask of values to be kept
M(u) = true; % fill values
output = A(all(M,1),:); % keep rows that only have non-repeated values
This gives
output =
12 16
8 14
3 6
7 2
I would like to create a column vector from the elements of a matrix A of size (3,3) that are not on the diagonal. Thus, I would have 6 elements in that output vector. How can I do this?
Use eye and logical negation, although this is no better than Divakar's original answer, and possibly significantly slower for very large matrices.
>> A = magic(4)
A =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> A(~eye(size(A)))
ans =
5
9
4
2
7
14
3
10
15
13
8
12
Use this to get such a column vector, assuming A is the input matrix -
column_vector = A(eye(size(A))==0)
If you don't care about the order of the elements in the output, you can also use a combination of setdiff and diag -
column_vector = setdiff(A,diag(A))
You can also use linear indexing to access the diagonal elements and null them. This will automatically reshape itself to a single vector:
A(1:size(A,1)+1:end) = [];
Bear in mind that this will mutate the original matrix A. If you don't want this to happen, make a copy of your matrix then perform the above operation on that copy. In other words:
Acopy = A;
Acopy(1:size(A,1)+1:end) = [];
Acopy will contain the final result. You need to create a vector starting from 1 and going to the end in increments of the rows of the matrix A added with 1 due to the fact that linear indices are column-major, so the linear indices used to access a matrix progress down each row first for a particular column. size(A,1) will allow us to offset by each column and we add 1 each time to ensure we get the diagonal coefficient for each column in the matrix.
Assuming that the matrix is square,
v = A(mod(0:numel(A)-1, size(A,1)+1) > 0).';
Imagine the following matrix is defined in MATLAB:
>> matrix=10:18
matrix =
10 11 12 13 14 15 16 17 18
Now I want to use another matrix to index the first one
>> index=[1,2;3,4]
index =
1 2
3 4
>> matrix(index)
ans =
10 11
12 13
So far so good, the size of the answer matches that of the matrix 'index'. If I use a row vector as the indexing matrix, the output is a row vector too. But the problem appears when I use a column vector as the indexing matrix:
>> index=(1:3)'
index =
1
2
3
>> matrix(index)
ans =
10 11 12
As you can see, here the size of the answer does not agree with the size of the matrix 'index'. This inconsistency in the sizes of the indexing matrix and the ans matrix prevents me from writing a piece of code accepting an indexing matrix of an arbitrary size.
I wonder if someone else has come across this problem before and has found some sort of solution to it; in other words, how can I force MATLAB to give me an ans matrix of the same size as the arbitrarily sized indexing matrix?
Cheers
Solution
#Dev-iL has nicely explained here why this is the way Matlab behaves, and #Dan has presented a general solution here. However, there was a simpler ad-hoc workaround for my code which I've explained here.
The reason comes from the function subsref.m, which is called when using parentheses:
%SUBSREF Subscripted reference.
% A(I) is an array formed from the elements of A specified by the
% subscript vector I. The resulting array is the same size as I except
% for the special case where A and I are both vectors. In this case,
% A(I) has the same number of elements as I but has the orientation of A.
As you can see, in the special case of vectors, the shape of the result will be the same as that of matrix in your example.
As for a workaround, IMHO Dan has the cleanest solution.
Preallocate the output matrix with the size of index matrix.
Then assign corresponding indices the corresponding values
out = zeros(size(index));
out(index) = matrix(index);
Example
index = (1:3)'
index =
1
2
3
>> out = zeros(size(index))
out =
0
0
0
>> in = magic(3)
in =
8 1 6
3 5 7
4 9 2
>> out(index) = in(index)
out =
8
3
4
>>
Dev-iL has explained why it is like that. Here is a potential workaround:
reshape(matrix(index),size(index))
I have a <206x193> matrix A. It contains the values of a parameter at 206 different locations at 193 time steps. I am interested in the maximum value at each location over all times as well as the corresponding indices. I have another matrix B with the same dimensions of A and I'm interested in values for each location at the time that A's value at that location was maximal.
I've tried [max_val pos] = max(A,[],2), which gives the right maximum values, but A(pos) does not equal max_val.
How exactly does this function work?
I tried a smaller example as well. Still I don't understand the meaning of the indices....
>> H
H(:,:,1) =
1 2
3 4
H(:,:,2) =
5 6
7 8
>> [val pos] = max(H,[],2)
val(:,:,1) =
2
4
val(:,:,2) =
6
8
pos(:,:,1) =
2
2
pos(:,:,2) =
2
2
The indices in idx represent the index of the max value in the corresponding row. You can use sub2ind to create a linear index if you want to test if A(pos)=max_val
A=rand(206, 193);
[max_val, idx]=max(A, [], 2);
A_max=A(sub2ind(size(A), (1:size(A,1))', idx));
Similarly, you can access the values of B with:
B_Amax=B(sub2ind(size(A), (1:size(A,1))', idx));
From your example:
H(:,:,2) =
5 6
7 8
[val pos] = max(H,[],2)
val(:,:,2) =
6
8
pos(:,:,2) =
2
2
The reason why pos(:,:,2) is [2; 2] is because the maximum is at position 2 for both rows.
max is a primarily intended for use with vectors. In normal mode, even the multi-dimensional arrays are treated as a series of vectors along which the max function is applied.
So, to get the values in B at each location at the time where A is maximum, you should
// find the maximum values and positions in A
[c,i] = max(A, [], 2);
// iterate along the first dimension, to retrieve the corresponding values in B
C = [];
for k=1:size(A,1)
C(k) = B(k,i(k));
end
You can refer to #Jigg's answer for a more concise way of creating matrix C
I would like to average every 3 values of an vector in Matlab, and then assign the average to the elements that produced it.
Examples:
x=[1:12];
y=%The averaging operation;
After the operation,
y=
[2 2 2 5 5 5 8 8 8 11 11 11]
Therefore the produced vector is the same size, and the jumping average every 3 values replaces the values that were used to produce the average (i.e. 1 2 3 are replaced by the average of the three values, 2 2 2). Is there a way of doing this without a loop?
I hope that makes sense.
Thanks.
I would go this way:
Reshape the vector so that it is a 3×x matrix:
x=[1:12];
xx=reshape(x,3,[]);
% xx is now [1 4 7 10; 2 5 8 11; 3 6 9 12]
after that
yy = sum(xx,1)./size(xx,1)
and now
y = reshape(repmat(yy, size(xx,1),1),1,[])
produces exactly your wanted result.
Your parameter 3, denoting the number of values, is only used at one place and can easily be modified if needed.
You may find the mean of each trio using:
x = 1:12;
m = mean(reshape(x, 3, []));
To duplicate the mean and reshape to match the original vector size, use:
y = m(ones(3,1), :) % duplicates row vector 3 times
y = y(:)'; % vector representation of array using linear indices