Comparing Vectors of Different Length - matlab

I am trying to compare two vectors of different size. For instance when I run the code below:
A = [1 4 3 7 9];
B = [1 2 3 4 5 6 7 8 9];
myPadded = [A zeros(1,4)];
C = ismember(myPadded,B)
I get the following output:
C = 1 1 1 1 1 0 0 0 0
However, I want an output that will reflect the positions of the compared values, hence, I would like an output that is displayed as follows:
C = 1 0 1 1 0 0 1 0 1
Please, I need some help :)

There are 2 points. First, you are writing the inputs of ismember in the wrong order. Additionally, you do not need to grow your matrix. Simply try ismember(B, A) and you will get what you expect.

The function ismember(myPadded, B) returns a vector the same size of myPadded, indicating if the i-th element of myPadded is present in B.
To get what you want, just invert parameter order: ismember(B, myPadded).

A quick way of doing this is to use logical indexing. This will only work if the last digit of B is included in A.
A = [1 4 3 7 9];
c(A) = 1; % or true.
An assumption here is that you want to subindex a vector 1:N, so that B always is B = 1:N. In case the last digit is not one this is easy to fix. Just remember to return all to its previous state after you are done. It will be 2 rows extra though.
This solution is meant as a special case working on a very common problem.

Related

Output a matrix size n x m, 1 when the sum of the indices is even, 0 otherwise

I'm attempting the following as a hobby, not as homework. In Computer Programming with MATLAB: J. Michael Fitpatrick and Akos Ledeczi, there is a practice problem that asks this:
Write a function called alternate that takes two positive integers, n and m, as input arguments (the function does not have to check the format of the input) and returns one matrix as an output argument. Each element of the n-by-m output matrix for which the sum of its indices is even is 1.
All other elements are zero.
A previous problem was similar, and I wrote a very simple function that does what it asks:
function A = alternate(n,m)
A(1:n,1:m)=0;
A(2:2:n,2:2:m)=1;
A(1:2:n,1:2:m)=1;
end
Now my question is, is that good enough? It outputs exactly what it asks for, but it's not checking for the sum. So far we haven't discussed nested if statements or anything of that sort, we just started going over very basic functions. I feel like giving it more functionality would allow it to be recycled better for future use.
Great to see you're learning, step 1 in learning any programming language should be to ensure you always add relevant comments! This helps you, and anyone reading your code. So the first improvement would be this:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
A(1:n,1:m)=0; % Create the n*m array of zeros
A(2:2:n,2:2:m)=1; % All elements with even row and col indices: even+even=even
A(1:2:n,1:2:m)=1; % All elements with odd row and col indicies: odd+odd=even
end
You can, however, make this more concise (discounting comments), and perhaps more clearly relate to the brief:
function A = alternate(n,m)
% Function to return an n*m matrix, which is 1 when the sum of the indices is even
% Sum of row and col indices. Uses implicit expansion (R2016b+) to form
% a matrix from a row and column array
idx = (1:n).' + (1:m);
% We want 1 when x is even, 0 when odd. mod(x,2) is the opposite, so 1-mod(x,2) works:
A = 1 - mod( idx, 2 );
end
Both functions do the same thing, and it's personal preference (and performance related for large problems) which you should use.
I'd argue that, even without comments, the alternative I've written more clearly does what it says on the tin. You don't have to know the brief to understand you're looking for the even index sums, since I've done the sum and tested if even. Your code requires interpretation.
It can also be written as a one-liner, whereas the indexing approach can't be (as you've done it).
A = 1 - mod( (1:n).' + (1:m), 2 ); % 1 when row + column index is even
Your function works fine and output the desired result, let me propose you an alternative:
function A = alternate(n,m)
A = zeros( n , m ) ; % pre-allocate result (all elements at 0)
[x,y] = meshgrid(1:m,1:n) ; % define a grid of indices
A(mod(x+y,2)==0) = 1 ; % modify elements of "A" whose indices verify the condition
end
Which returns:
>> alternate(4,5)
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
initialisation:
The first line is the equivalent to your first line, but it is the cannonical MATLAB way of creating a new matrix.
It uses the function zeros(n,m).
Note that MATLAB has similar functions to create and preallocate matrices for different types, for examples:
ones(n,m) Create
a matrix of double, size [n,m] with all elements set to 1
nan(n,m) Create a
matrix of double, size [n,m] with all elements set to NaN
false(n,m) Create a
matrix of boolean size [n,m] with all elements set to false
There are several other matrix construction predefined function, some more specialised (like eye), so before trying hard to generate your initial matrix, you can look in the documentation if a specialised function exist for your case.
indices
The second line generate 2 matrices x and y which will be the indices of A. It uses the function meshgrid. For example in the case shown above, x and y look like:
| x = | y = |
| 1 2 3 4 5 | 1 1 1 1 1 |
| 1 2 3 4 5 | 2 2 2 2 2 |
| 1 2 3 4 5 | 3 3 3 3 3 |
| 1 2 3 4 5 | 4 4 4 4 4 |
odd/even indices
To calculate the sum of the indices, it is now trivial in MATLAB, as easy as:
>> x+y
ans =
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8
5 6 7 8 9
Now we just need to know which ones are even. For this we'll use the modulo operator (mod) on this summed matrix:
>> mod(x+y,2)==0
ans =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
This result logical matrix is the same size as A and contain 1 where the sum of the indices is even, and 0 otherwise. We can use this logical matrix to modify only the elements of A which satisfied the condition:
>> A(mod(x+y,2)==0) = 1
A =
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
Note that in this case the logical matrix found in the previous step would have been ok since the value to assign to the special indices is 1, which is the same as the numeric representation of true for MATLAB. In case you wanted to assign a different value, but the same indices condition, simply replace the last assignment:
A(mod(x+y,2)==0) = your_target_value ;
I don't like spoiling the learning. So let me just give you some hints.
Matlab is very efficient if you do operations on vectors, not on individual elements. So, why not creating two matrices (e.g. N, M) that holds all the indices? Have a look at the meshgrid() function.
Than you might be able find all positions with an even sum of indices in one line.
Second hint is that the outputs of a logic operation, e.g. B = A==4, yields a logic matrix. You can convert this to a matrix of zeros by using B = double(B).
Have fun!

Extending ismember to cells

I have searched the forum and have not found enough info to help me solve this problem.
Consider the set (cell of vectors)
A = {[1],[1 2],[2],[1 2 3],[1 2 3 4],[1 3]}
I want to construct a matrix B that looks like
B = [1 1 0 1 1 1
0 1 0 1 1 0
0 1 1 1 1 0
0 0 0 1 1 0
0 0 0 0 1 0
0 0 0 1 1 1]
The matrix B specifies membership of vectors with respect to each other. That is, the first row looks at the first element in A, [1], and checks if it is a member of the other vectors, placing a 1 if it is a member and a 0 otherwise.
I can do this using two for loops: one over the elements of A, and another nested, for each element of A, that checks membership with respect to every other member of A.
I want to avoid using for loops. Is there a vectorized solution for obtaining B from A?
With cell arrays it's hard to avoid loops, or their cousin cellfun. This is how I would do it:
[ii, jj] = ndgrid(1:numel(A)); % indices of all possible pairs
result = cellfun(#(x,y) all(ismember(x,y)), A(ii), A(jj)); % see if all elements in the
% first are present in the second
Well you asked for it, so here's an almost* vectorized solution using bsxfun and permute -
lens = cellfun('length',A)
vals = [A{:}]
mask = bsxfun(#ge,lens,[1:max(vals)]')
a = nan(size(mask))
a(mask) = vals
matches = bsxfun(#eq,a,permute(a,[3,4,1,2]));
out = bsxfun(#eq,squeeze(sum(any(matches,3),1)),lens(:))
*: Almost because of the use of cellfun at the start with cellfun('length',A), but since its just getting the length of the cells there, so computationally would be negligible .
Also, please note that this approach would use a lot of memory resources, so might not be beneficial, but just respecting the requirements of a vectorized solution as much as possible!

How to multiply matrix having different size (without knowing exactly the size they will have)?

I have to multiply 2 matrices (scalar multiplication) that have different sizes. For instance, the 1st has size n-by-m and the 2nd n+1-by-m+1. The fact is that it is not always the case. I mean, sometimes the first has size n+1-by-m+1 and the 2nd n-by-m or n+2-by-m+2 etc...
Example:
a = [ 1 2 3;
4 5 6];
b = [ 1 2 3;
4 5 6;
7 8 9]
I would like Matlab to check the size of each matrix, then multiply them using the smallest size available between the 2 i.e. ignoring the last rows and columns of the bigger matrix (or similarly, adding rows and columns of 0 to the smaller matrix).
With the example inputs I would like to obtain:
c = [1 4 9;
16 25 36]
or
c = [1 4 9;
16 25 36;
0 0 0]
How can I write this?
Find the number of rows and columns of your final matrix:
n = min(size(a,1), size(b,1));
m = min(size(a,2), size(b,2));
Then extract only the relevant sections of a and b (using the : operator) for your multiplication:
c = a(1:n,1:m).*b(1:n,1:m)
If you only consider dot product, it means that size(a) must equal size(b), which allows to simply restrain the size of b, you can use a simple if statement if you like. For example:
if all(size(b) == size(a))
answer = a.*b
else
minsize(:,1) = min(size(a,1),size(b,1));
minsize(:,2) = min(size(a,2),size(b,2));
answer = a(1:minsize(:,1),1:minsize(:,2)).*a(1:minsize(:,1),1:minsize(:,2));
end
I don't think this is easiest way to do it, but it is simple to understand :)
I am not always sure which one of the matrix is bigger, then I thought to use:
if size (a) > size (b)
a = a(1:size(b,1),1:size(b,2));
elseif size (a) < size (b)
b = b (1: size (a,1),1:size(a,2));
end
It seems to work like this.

Finding the most recent indices with different values

I am familiar with Matlab but am still having trouble with vectorized methods in my intuition, so I was wondering if anyone could demonstrate how they would manage this problem.
I have an array, for example A = [1 1 2 2 1 3 3 3 4 3 4 4 5].
I want to return an array B such that each element is the index of A's most 'recent' element with a different value than the previous ones.
So for our array A, B would equal [x x 2 2 4 5 5 5 8 9 10 10 12], where the x's can be any consistent value you like, because there is no previous index satisfying those characteristics.
I know how I would code it as a for-loop, and I bet the for-loop is probably faster, but can anyone vectorize this to faster than the for-loop?
Here's my for-loop:
prev=0;
B=zeros(length(A),1);
for i=2:length(A)
if A(i-1)~=A(i)
prev=i-1;
end
B(i)=prev;
end
Find the indices of the entries where the value changes:
ind = find(diff(A) ~= 0);
The values that should appear in B are therefore:
val = [0 ind];
Construct the diff of B: fill in the difference between the values that should appear at the right places:
Bd = zeros(size(B))';
Bd(ind + 1) = diff(val);
Now use cumsum to construct B:
B = cumsum(Bd)
Not sure whether this results in a speed-up though.

Change values in a matrix non-destructively

Assume that B is some large matrix with integers, both zero and non-zero. I want to call the function my_function with B as an argument, but with the zero-values set to 1. Is there a way to do that without creating a temporary variable, as A in this case?
A = B;
A(A==0) = 1;
my_function( A );
Actually, in this specific case it is possible with the expression B + ~B.
Example
>> B = fix(5 * rand(5))
B =
4 0 3 2 3
0 3 3 0 2
1 4 0 0 2
1 4 3 4 3
3 1 1 1 1
>> B + ~B
ans =
4 1 3 2 3
1 3 3 1 2
1 4 1 1 2
1 4 3 4 3
3 1 1 1 1
There you go.
Matlab does not really allows to do that.
But you can use the following simple trick:
idx=find(B==0);
B(idx) = 1;
my_function(B);
B(idx) = 0;
Since you seem to be concerned about the memory consumption, the short answer is - no. Matlab uses explicit index variables for about everything. What others suggested before:
idx = find(B==0)
silently allocates a matrix of size(B) logical values for the expression B==0. Depending on the matlab version, logical type may be 4 bytes or 1 byte, which saves you some memory compared to creating a copy of B:
A = B;
However, find again returns an array of doubles. Hence, depending on how many non-zero entries you actually have in B, you may end up using a lot of RAM: you need the memory to store B==0, and the memory to store the result of find at the same time.
So, depending on your problem, it may be actually cheaper memory-wise to just to a copy of the variable.
No, if you want to retain a copy of the original B, then you will have to make a second variable. Either duplicate it as you are doing, or replace the 0s with 1s after storing the indices to those elements in a second variable, i.e.
idx = find(B==0);
B(idx) = 1;
If numel(idx) is considerably smaller than numel(B), then that would save you some memory overhead if that's your concern.