negative and positive distance in matlab - matlab

I have a 1 by N vector for example (N is 5):
x=[1,2,3,4,5];
I want to create an N-by-N matrix, M, where M(i,j)=x(i)-x(j) and M(j,i) = -1 * M(i,j)
Could anyone give a simple way without a loop?

You can use binary singleton expansion bsxfun:
bsxfun(#minus,x.',x)
or (in version 2016b or later) implicit expansion:
x.'-x
both will result in:
ans =
0 -1 -2 -3 -4
1 0 -1 -2 -3
2 1 0 -1 -2
3 2 1 0 -1
4 3 2 1 0
This method does exactly what you asked for - apply a function to all the combinations of a with b. In your case, a and b are simply x and itself, and the function is minus (which is written with # in bsxfun, or as simple operator - in an implicit expansion).
Since you want to subtract the column j from the row i, you should first write the column x (i.e. x.') that represents the row index, and then row x, that represents the column index.

Related

How does Y = eye(K)(y, :); replace a "for" loop? Coursera

Working on an assignment from Coursera Machine Learning. I'm curious how this works... From an example, this much simpler code:
% K is the number of classes.
K = num_labels;
Y = eye(K)(y, :);
seems to be a substitute for the following:
I = eye(num_labels);
Y = zeros(m, num_labels);
for i=1:m
Y(i, :)= I(y(i), :);
end
and I have no idea how. I'm having some difficulty Googling this info as well.
Thanks!
Your variable y in this case must be an m-element vector containing integers in the range of 1 to num_labels. The goal of the code is to create a matrix Y that is m-by-num_labels where each row k will contain all zeros except for a 1 in column y(k).
A way to generate Y is to first create an identity matrix using the function eye. This is a square matrix of all zeroes except for ones along the main diagonal. Row k of the identity matrix will therefore have one non-zero element in column k. We can therefore build matrix Y out of rows indexed from the identity matrix, using y as the row index. We could do this with a for loop (as in your second code sample), but that's not as simple and efficient as using a single indexing operation (as in your first code sample).
Let's look at an example (in MATLAB):
>> num_labels = 5;
>> y = [2 3 3 1 5 4 4 4]; % The columns where the ones will be for each row
>> I = eye(num_labels)
I =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
>> Y = I(y, :)
Y =
% 1 in column ...
0 1 0 0 0 % 2
0 0 1 0 0 % 3
0 0 1 0 0 % 3
1 0 0 0 0 % 1
0 0 0 0 1 % 5
0 0 0 1 0 % 4
0 0 0 1 0 % 4
0 0 0 1 0 % 4
NOTE: Octave allows you to index function return arguments without first placing them in a variable, but MATLAB does not (at least, not very easily). Therefore, the syntax:
Y = eye(num_labels)(y, :);
only works in Octave. In MATLAB, you have to do it as in my example above, or use one of the other options here.
The first set of code is Octave, which has some additional indexing functionality that MATLAB does not have. The second set of code is how the operation would be performed in MATLAB.
In both cases Y is a matrix generated by re-arranging the rows of an identity matrix. In both cases it may also be posible to calculate Y = T*y for a suitable linear transformation matrix T.
(The above assumes that y is a vector of integers that are being used as an indexing variables for the rows. If that's not the case then the code most likely throws an error.)

How to find number of occurrences of a subset of elements in a vector without using loops in MATLAB?

Say X is the given vector:
X=[1
2
4
2
3
1
4
5
2
4
5];
And Y is the given subset of elements from X:
Y=[3
4
5];
The required output is the number of times the elements in Y occur in X:
out=[1
3
2];
My solution to do this would be to use for loop:
for i=1:size(X,1)
temp = X(X(:,1)==Y(i,1),:);
out(i,1) = size(temp,1);
end
But when X and Y are large, this is inefficient. So, how to do it faster making use of vectorization? I know about hist and histc, but I can't think of how to use them in this case to get the desired output.
A Fast Option
You could use bsxfun combined with sum to compute this
sum(bsxfun(#eq, Y, X.'), 2)
Explanation
In this example, bsxfun performs a given operation on every combination of elements in X and Y. The operation we're gonig to use is eq (a check for equality). The result is a matrix that has a row for each element in Y and a column for each element in X. It will have a 1 value if the element in X equals the element in Y that corresponds to a given row.
bsxfun(#eq, Y, X.')
% 0 0 0 0 1 0 0 0 0 0 0
% 0 0 1 0 0 0 1 0 0 1 0
% 0 0 0 0 0 0 0 1 0 0 1
We can then sum across the columns to count the number of elements in X that were equal to a given value in Y.
sum(bsxfun(#eq, Y, X.'), 2)
% 1
% 3
% 2
On newer versions of MATLAB (since R2016b), you can omit the bsxfun since the equality operation will automatically broadcast.
sum(Y - X.', 2)
A Memory-Efficient Option
The first option isn't the most efficient since it requires creating a matrix that is [numel(Y), numel(X)] elements large. Another way which may be more memory efficient may be to use the second output of ismember combined with accumarray
[tf, ind] = ismember(X, Y);
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], #numel);
Explanation
ismember is used to determine if the values in one array are in another. The first input tells us if each element of the first input is in the second input and the second output tells you where in the second input each element of the first input was found.
[tf, ind] = ismember(X, Y);
% 0 0 1 0 1 0 1 1 0 1 1
% 0 0 2 0 1 0 2 3 0 2 3
We can use the second input to "group" the same values together. The accumarray function does exactly this, it uses the ind variable above to determine groups and then applies a given operation to each group. In our case, we want to simply determine the number of element within each group. So to do that we can pass a second input the size of the ind input (minus the ones that didn't match) of ones, and then use numel as the operation (counts the number in each group)
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], #numel);
% 1
% 3
% 2

Making Tridiagonal matrix in matlab

I want to make a triadiagonal matrix with matlab, using
full(gallery('tridiag', 10, 1, -4, 6, -4, 1))
and i take that i have too many arguments in the function. Is there another way to do this?
I am trying to make the following matrix:
6 -4 1 0 0
-4 6 -4 1 0
1 -4 6 -4 1
0 1 -4 6 -4
0 0 1 -4 6
Since your matrix is pentadiagonal, I think the best solution is to use spdiags:
>> n = 5;
>> full(spdiags(ones(n,1)*[1,-4,6,-4,1],[-2,-1,0,1,2],n,n));
ans =
6 -4 1 0 0
-4 6 -4 1 0
1 -4 6 -4 1
0 1 -4 6 -4
0 0 1 -4 6
The full is optional and not recommended for large n.
Since there are 5 non-zero diagonals this is not a tridiagonal matrix so you cannot use the tridiag option. You have to manually generate such matrix by means of the diag() function, which allows you to create a matrix with a given diagonal and you can as well select which diagonal you want to write.
You can achieve this therefore by creating 5 different matrices, each of them will have a given non-zero diagonal:
n=5;
B=diag(6*ones(n,1),0)+diag(-4*ones(n-1,1),1)+diag(-4*ones(n-1,1),-1)+diag(1*ones(n-2,1),2)+diag(1*ones(n-2,1),-2);
In this code n=5 is the order of your matrix, then diag(6*ones(n,1),0) will create a vector (length n) with all 6 and such vector will be placed in the 0-th diagonal. Such matrix will have zero elsewhere.
Similarly diag(-4*ones(n-1,1),1) will create a vector (length n-1) with all -4 and such vector will be placed in the 1st superdiagonal. Such matrix will have zero elsewhere and we sum such matrix to the previous one.
And such "chain reaction" goes on until the matrix is fully generated.
Update: I've been looking around the gallery() help and there is indeed an option for a Toeplitz pentadiagonal. You might want to use then
full(gallery('toeppen',5,1,-4,6,-4,1))
I agree that for your huge case a sparse-based solution such as that of Troy Haskin is best. However, it's worth noting that you're precisely constructing a Toeplitz matrix (as Alessiox hinted), and you can use the built-in toeplitz() to do that. All that is needed is to figure out the number of zeros needed for padding the input nonzero elements of the first row and column (this is necessary since toeplitz asserts the size of the matrix to construct from the dimensions of the input vector):
n = 5; %// linear size of result
v = [1,-4,6,-4,1]; %// nonzero diagonal elements symmetrically
mid = ceil(length(v)/2); %// index of diagonal in the input vector
zerosvec = zeros(1,n-mid); %// zeros for padding the rest
colvec = [v(mid:-1:1), zerosvec]; %// first column of the result
rowvec = [v(mid:end), zerosvec]; %// first row of the result
toeplitz(colvec,rowvec) %// toeplitz does the heavy lifting

Count the number of the first zero elements

I would line to find the number of the first consecutive zero elements. For example in [0 0 1 -5 3 0] we have two zero consecutive elements that appear first in the vector.
could you please suggest a way without using for loops?
V=[0 0 1 -5 3 0] ;
k=find(V);
Number_of_first_zeros=k(1)-1;
Or,
Number_of_first_zeros=find(V,1,'first')-1;
To solve #The minion comment (if that was the purpose):
Number_of_first_zeros=find(V(find(~V,1,'first'):end),1,'first')-find(~V,1,'first');
Use a logical array to find the zeros and then look at where the zeros and ones are alternating.
V=[1 2 0 0 0 3 5123];
diff(V==0)
ans =
0 1 0 0 -1 0
Create sample data
V=[1 2 0 0 0 3 5123];
Find the zeros. The result will be a logical array where 1 represents the location of the zeros
D=V==0
D =
0 0 1 1 1 0 0
Take the difference of that array. 1 would then represent the start and -1 would represent the end.
T= diff(D)
ans =
0 1 0 0 -1 0
find(T==1) would give you the start and find(T==-1) would give you the end. The first index+1 of T==1 would be the start of the first set of zeros and the first index of T==-1 would be the end of the first set of zeros.
You could find position the first nonzero element using find.
I=find(A, 1);
The number of leading zeros is then I-1.
My solution is quite complex yet it doesn't use the loops and it does the trick. I am pretty sure, that there is a more direct approach.
Just in case no one else posts a working solution here my idea.
x=[1 2 4 0 20 0 10 1 23 45];
x1=find(x==0);
if numel(x1)>1
x2=[x1(2:end), 0];
x3=x2-x1;
y=find(x3~=1);
y(1)
elseif numel(x1)==1
display(1)
else
display('No zero found')
end
x is the dataset. x1 contains the index of all zero elements. x2 contains all those indices except the first one (because matrix dimensions must agree, one zero is added. x3 is the difference between the index and the previous index of zeros in your dataset. Now I find all those differences which are not 1 (do not correspond to sequences of zeros) and the first index (of this data is the required result. The if case is needed in case you have only one or no zero at all.
I'm assuming your question is the following: for the following vector [0 0 1 -5 3 0], I would like to find the index of the first element of a pair of 0 values. Is this correct? Therefore, the desired output for your vector would be '1'?
To extend the other answers to find any such pairs, not just 0 0 (eg. 0 1, 0 2, 3 4 etc), then this might help.
% define the pattern
ptrn = [ 0 0 ];
difference = ptrn(2) - ptrn(1)
V = [0 0 1 -5 3 0 0 2 3 4 0 0 1 0 0 0]
x = diff(V) == difference
indices = find(x)
indices =
1 6 11 14 15

Cumulative Sum with >=0 Restriction in Matlab

I want to calculate the cumulative sum of a vector, but stop summing up once the sum becomes negative, and start again at positive elements.
Example:
We have a vector:
[1 1 -1 -1 -1 -1 1 1 1 1]
The normal cumulative sum would then be:
[1 2 1 0 -1 -2 -1 0 1 2]
But i want:
[1 2 1 0 0 0 1 2 3 4]
The only solution i could come up with was to loop over the elements of the vector like this:
test = [1 1 -1 -1 -1 -1 1 1 1 1];
testCumsum = zeros(size(test));
for i=1:length(test)
if i==1
testCumsum(i) = test(i);
else
testCumsum(i) = testCumsum(i-1) + test(i);
end
if testCumsum(i)<0
testCumsum(i) = 0;
end
end
Is the a more matlab-ish solution?
(The sum can become negative an arbitrary number of times, the vectors can become pretty large, and the elements can be any number, not just 1 and -1)
You won't be able to vectorize it since you have to decide on each elemenet based on previous ones. You can find regions of positive and negative runs but it would be unnecessarily complex and I don't know if you can gain over your own solution.
Here is a simplification of your code for input A and output C:
C=A;
C(1) = max(C(1), 0);
for k=2:numel(C)
C(k) = max(C(k-1)+C(k), 0);
end
call your vector x,
y=x >0
z=x.*y
sum(z)
the y vector is 0 / 1 where the elemnts of x are greater than 0 the dot product to get z sets your negative values to 0, and then you can sum
_Ah i see more clearly what you want to do now, - looping is probably going to be quickest, you could break into block segments if the array is large and use parfor to speed it up