Subset array to members of another array in Matlab - matlab

I am looking for a functionality i Matlab to subset and array like the IN function in SQL or %in% in R. I.e. I have the following data:
a = 1:3;
b = 2:4;
Then I want to write something like:
(a %in% b)
And it should produce:
ans =
0 1 1
However using %in% clearly doesn't work.

You are probably looking for the function ismember, which checks which members of one matrix are also member of a second matrix. It returns true/false for each entry in your matrix a if it can/cannot be found in your matrix b.
ismember(a, b)
ans =
0 1 1
As a side note: % is the character, which starts a comment.

You could also do it with bsxfun:
result = sum(bsxfun(#eq, a(:).', b(:)), 1);
This has the advantage that it tells you how many elements of b equal each element of a. For example,
>> a = [1 2 3];
>> b = [2 3 4 2];
>> result = sum(bsxfun(#eq, a(:).', b(:)),1)
result =
0 2 1

Related

For-loop exits too early, need help as to why

I've written a simple function that takes a vector vec, iterates through it, performing an operation whose result is stored in another vector vecRes of same size at same index, and returns vecRes upon completing the loop. Below is function code:
function [ vecRes ] = squareTerms( vec )
vecSize = size(vec);
vecRes = zeros(vecSize);
for i = 1:vecSize
vecRes(i) = vec(i)^2;
end
end
Problem is that it seems to exit too early, after only one iteration in fact as the output appears as:
vecRes = 1 0 0 0 0 0 0 0 0 0
For input:
vec = 1 2 3 4 5 6 7 8 9 10
I can't figure out why it does so. Any help is greatly appreciated.
Size returns 2 values, rows and columns. Probably you are a having a 1xN vector. So size returns [1 N] and your loop runs 1 time.
>>> size ([1 2 3])
>
> ans =
>
> 1 3
>
>>> 1:size ([1 2 3])
>
> ans =
>
> 1
Others have pointed out the problem. My preferred solution in this sort of case is to use numel, i.e.
vecRes = zeros(size(vec));
for i = 1:numel(vec)
vecRes(i) = vec(i) ^ 2;
end
Of course, in this case, vectorisation is better still:
vecRes = vec .^ 2;
Replace
for i = 1:vecSize
with
for i = 1:vecSize(2)
vecSize is an array of numbers, not just a single value. For example, if vec is a 1 by 8 vector, then size(vec) will return [1, 8].
Therefore, your for-loop-statement,
for i = 1:vecSize
, is actually equivalent to something like:
for i = 1:[1, 8]
This doesn't make much sense. There are a number of ways to fix the problem. You could write:
for i = 1:length(vec)
or
for i = 1:numel(vec) % "numel" stands for "number of elements"
If the vector is 1xn instead of nx1, you can write:
for i = 1:size(vec, 2)
Yet another alternative is:
for i = 1:max(vecSize)
However, the most sensible option is not to write the squareTerms function at all and simply write
vecRes = vec.^2;
Note the dot before the caret. vec^2 and vec . ^2 are not the same thing.
If you put a dot before an operator sign, the operation will be performed element-wise. For example,
C = A * B
performs matrix multiplication, but
C = A .* B
will cause the first element of A to by multiplied by the first element of B, and the result will be assigned to the first element of C. Then, the product of the second elements of A and B will be taken, and the result will be stuck in the second element of C, and so on.
vecRes = vec.^2;

find row indices of different values in matrix

Having matrix A (n*2) as the source and B as a vector containing a subset of elements A, I'd like to find the row index of items.
A=[1 2;1 3; 4 5];
B=[1 5];
F=arrayfun(#(x)(find(B(x)==A)),1:numel(B),'UniformOutput',false)
gives the following outputs in a cell according to this help page
[2x1 double] [6]
indicating the indices of all occurrence in column-wise. But I'd like to have the indices of rows. i.e. I'd like to know that element 1 happens in row 1 and row 2 and element 5 happens just in row 3. If the indices were row-wise I could use ceil(F{x}/2) to have the desired output. Now with the variable number of rows, what's your suggested solution? As it may happens that there's no complete inclusion tag 'rows' in ismember function does not work. Besides, I'd like to know all indices of specified elements.
Thanks in advance for any help.
Approach 1
To convert F from its current linear-index form into row indices, use mod:
rows = cellfun(#(x) mod(x-1,size(A,1))+1, F, 'UniformOutput', false);
You can combine this with your code into a single line. Note also that you can directly use B as an input to arrayfun, and you avoid one stage of indexing:
rows = arrayfun(#(x) mod(find(x==A)-1,size(A,1))+1, B(:), 'UniformOutput', false);
How this works:
F as given by your code is a linear index in column-major form. This means the index runs down the first column of B, the begins at the top of the second column and runs down again, etc. So the row number can be obtained with just a modulo (mod) operation.
Approach 2
Using bsxfun and accumarray:
t = any(bsxfun(#eq, B(:), reshape(A, 1, size(A,1), size(A,2))), 3); %// occurrence pattern
[ii, jj] = find(t); %// ii indicates an element of B, and jj is row of A where it occurs
rows = accumarray(ii, jj, [], #(x) {x}); %// group results according to ii
How this works:
Assuming A and B as in your example, t is the 2x3 matrix
t =
1 1 0
0 0 1
The m-th row of t contains 1 at column n if the m-th element of B occurs at the n-th row of B. These values are converted into row and column form with find:
ii =
1
1
2
jj =
1
2
3
This means the first element of B ocurrs at rows 1 and 2 of A; and the second occurs at row 3 of B.
Lastly, the values of jj are grouped (with accumarray) according to their corresponding value of ii to generate the desired result.
One approach with bsxfun & accumarray -
%// Create a match of B's in A's with each column of matches representing the
%// rows in A where there is at least one match for each element in B
matches = squeeze(any(bsxfun(#eq,A,permute(B(:),[3 2 1])),2))
%// Get the indices values and the corresponding IDs of B
[indices,B_id] = find(matches)
%// Or directly for performance:
%// [indices,B_id] = find(any(bsxfun(#eq,A,permute(B(:),[3 2 1])),2))
%// Accumulate the indices values using B_id as subscripts
out = accumarray(B_id(:),indices(:),[],#(x) {x})
Sample run -
>> A
A =
1 2
1 3
4 5
>> B
B =
1 5
>> celldisp(out) %// To display the output, out
out{1} =
1
2
out{2} =
3
With arrayfun,ismember and find
[r,c] = arrayfun(#(x) find(ismember(A,x)) , B, 'uni',0);
Where r gives your desired results, you could also use the c variable to get the column of each number in B
Results for the sample input:
>> celldisp(r)
r{1} =
1
2
r{2} =
3
>> celldisp(c)
c{1} =
1
1
c{2} =
2

Recurring Function with Matrix Input

I believe most functions in MATLAB should be able to receive matrix input and return the output in the form of matrix.
For example sqrt([1 4 9]) would return [1 2 3].
However, when I tried this recurring factorial function:
function k = fact(z)
if z ~= 0
k = z * fact(z-1);
else
k = 1;
end
end
It works perfectly when a number is input into fact. However, when a matrix is input into fact, it returns the matrix itself, without performing the factorial function.
E.g.
fact(3) returns 6
fact([1 2 3]) returns [1 2 3] instead of [1 2 6].
Any help is appreciated. Thank you very much!
Since MATLAB is not known to be good with recursive functions, how about a vectorized approach? Try this for a vector input -
mat1 = repmat([1:max(z)],[numel(z) 1])
mat1(bsxfun(#gt,1:max(z),z'))=1
output1 = prod(mat1,2)
Sample run -
z =
1 2 7
output1 =
1
2
5040
For the sake of answering your original question, here's the annoying loopy code for a vector or 2D matrix as input -
function k1 = fact1(z1)
k1 = zeros(size(z1));
for ii = 1:size(z1,1)
for jj = 1:size(z1,2)
z = z1(ii,jj);
if z ~= 0
k1(ii,jj) = z .* fact1(z-1);
else
k1(ii,jj) = 1;
end
end
end
return
Sample run -
>> fact1([1 2 7;3 2 1])
ans =
1 2 5040
6 2 1
You can use the gamma function to compute the factorial without recursion:
function k = fact(z)
k = gamma(z+1);
Example:
>> fact([1 2 3 4])
ans =
1 2 6 24
Not sure if all of you know, but there is an actual factorial function defined in MATLAB that can take in arrays / matrices of any size, and computes the factorial element-wise. For example:
k = factorial([1 2 3 4; 5 6 7 8])
k =
1 2 6 24
120 720 5040 40320
Even though this post is looking for a recursive implementation, and Divakar has provided a solution, I'd still like to put my two cents in and suggest an alternative. Also, let's say that we don't have access to factorial, and we want to compute this from first principles. What I would personally do is create a cell array that's the same size as the input matrix, but each element in this cell array would be a linear index array from 1 up to the number defined for each location in the original matrix. You would then apply prod to each cell element to compute the factorial. A precondition is that no number is less than 1, and that all elements are integers. As such:
z1 = ... ; %// Define input matrix here
z1_matr = arrayfun(#(x) 1:x, z1, 'uni', 0);
out = cellfun(#prod, z1_matr);
If z1 = [1 2 3 4; 5 6 7 8];, from my previous example, we get the same output with the above code:
out =
1 2 6 24
120 720 5040 40320
This will obviously be slower as there is an arrayfun then cellfun call immediately after, but I figured I'd add another method for the sake of just adding in another method :) Not sure how constructive this is, but I figured I'd add my own method and join Divakar and Luis Mendo :)

compare multiple matrices matlab

I have multiple matrices of the same size and want to compare them.
As a result I need a matrix which gives me the biggest of the 3 for every value.
I will clarify what i mean with an example:
I have 3 matrices with data of 3 persons.
I would like to compare these 3 and get a matrix as result.
In that matrix every cell/value should be the name of the matrix who had the highest value for that cell. So if in the 3 matrices the first value (1 colum, 1 row) is accordingly 2, 5, 8 the first value of the result matrix should be 3 (or the name of the 3 matrix).
If the three matrices are A, B, C, do this:
[~, M] = max(cat(3,A,B,C),[],3);
It creates a 3D "matrix" and maximizes across the third dimension.
Concatenate them on the 3rd dimension, and the use the SECOND output from max to get exactly what you want
A = rand(3,3);
B = rand(3,3);
C = rand(3,3);
D = cat(3, A, B, C)
[~, Solution] = max(D, [], 3)
e.g.:
D =
ans(:,:,1) =
0.70101 0.31706 0.83874
0.89421 0.33783 0.55681
0.68520 0.11697 0.45631
ans(:,:,2) =
0.268715 0.213200 0.124450
0.869847 0.999649 0.153353
0.345447 0.023523 0.338099
ans(:,:,3) =
0.216665 0.297900 0.604734
0.103340 0.767206 0.660668
0.127052 0.430861 0.021584
Solution =
1 1 1
1 2 3
1 3 1
edit
As I did not know about the second argument of the max-function, here is what you should NOT use:
old
Well, quick&dirty:
x=[2 5 8];
w=max(x)
[~,loc] = ismember(w,x)

Creating matrix from two vectors of (duplicated) indices in MATLAB

Suppose now I have two vectors of same length:
A = [1 2 2 1];
B = [2 1 2 2];
I would like to create a matrix C whose dim=m*n, m=max(A), n=max(B).
C = zeros(m,n);
for i = 1:length(A)
u = A(i);
v = B(i);
C(u,v)=C(u,v)+1;
end
and get
C =[0 2;
1 1]
More precisely, we treat the according indices in A and B as rows and columns in C, and C(u,v) is the number of elements in {k | A(i)=u and B(i)=v, i = 1,2,...,length(A)}
Is there a faster way to do that?
Yes. Use sparse. It assembles (i.e., sums up) the matrix values for repeating row-column pairs for you. You need an additional vector with the values that will be assembled into the matrix entries. If you use ones(size(A)), you will have exactly what you need - counting of repeated row-column pairs
spA=sparse(A, B, ones(size(A)));
full(spA)
ans =
0 2
1 1
The same can be obtained by simply passing scalar 1 to sparse function instead of a vector of values.
For matrices that have a large number of zero entries this is absolutely crucial that you use sparse storage. Another function you could use is accumarray. It can essentially do the same thing, but also works on dense matrix structure:
AA=accumarray([A;B]', 1);
AA =
0 2
1 1
You can pass size argument to accumarray if you want to create a matrix of specific size
AA=accumarray([A;B]', 1, [2 3]);
AA =
0 2 0
1 1 0
Note that you can actually also make it produce sparse matrices, and use a different operator in assembly (i.e., not necessarily a sum)
AA=accumarray([A;B]', 1, [2 3], #sum, 0, true)
will produce a sparse matrix (last parameter set to true) using sum for assembly and 0 as a fill value, i.e. a value which is used in cases a given row-column pair does not exist in A/B.