Matlab: Odd linear indexing into array with singleton dimensions - matlab

I'm having trouble understanding the circumstances under which linear indexing applies.
For a 2D array, it seems intuitive:
>> clear x
>> x=[1 2;3 4]
x =
1 2
3 4
>> x([1 2])
ans =
1 3
>> x([1;2])
ans =
1
3
For a 3D matrix, it seems intuitive:
>> clear y
>> y(1:2,1:2,1)=x
y =
1 2
3 4
>> y(1:2,1:2,2)=x+10
y(:,:,1) =
1 2
3 4
y(:,:,2) =
11 12
13 14
>> y([1 2])
ans =
1 3
>> y([1;2])
ans =
1
3
For a 3D matrix in which the 1st two dimensions are singletons, it's not what I would expect:
>> clear y
>> y(1,1,1)=1
y =
1
>> y(1,1,2)=2
y(:,:,1) =
1
y(:,:,2) =
2
>> y([1 2])
ans(:,:,1) =
1
ans(:,:,2) =
2
>> y([1;2])
ans(:,:,1) =
1
ans(:,:,2) =
2
I would have expected exactly the same as the 3D matrix without singleton dimensions.
Is there a rule that can be relied on to predict the behaviour of linear indexing?

The rule for linear indexing of an array x with an array ind is as follows (taken from this great post by Loren Shure):
If at least one of x and ind is not a vector (that is, if x or ind have more than one non-singleton dimension) the output has the same shape (size) as ind:
>> x = rand(3,4);
>> ind = [9 3];
>> x(ind)
ans =
0.276922984960890 0.743132468124916
>> x(ind.')
ans =
0.276922984960890
0.743132468124916
>> x = rand(2,3,4);
>> ind = [1 2; 5 6];
>> x(ind)
ans =
0.814723686393179 0.905791937075619
0.632359246225410 0.097540404999410
If both x and ind are vectors, the output is a vector that has the same orientation as x (i.e. the output dimension which is non-singleton is that in x), and the same number of elements as ind:
>> x = 10:10:70;
>> ind = [1 3 5];
>> x(ind)
ans =
10 30 50
>> x(ind.')
ans =
10 30 50
>> x = reshape(10:10:70,1,1,[]); % 1×1×7
>> ind = reshape([2 3 4],1,1,1,1,[]); % 1×1×1×1×3
>> x(ind)
ans(:,:,1) =
20
ans(:,:,2) =
30
ans(:,:,3) =
40
For completeness, if two or more indexing arrays are applied (so this is not linear indexing anymore), the shape of the output is determined by which dimensions of the original array are being indexed and by the number of elements of each indexing array. It is independent of the shape of the indexing arrays, which are simply read in column-major order as usual:
>> x = [10 20 30 40; 50 60 70 80];
>> x(2, [1 2; 3 4])
ans =
50 70 60 80
>> x(2, [1 2 3 4])
ans =
50 60 70 80
>> x(2, reshape([1 2 3 4],1,1,1,[]))
ans =
50 60 70 80
If there are fewer indices than the number of dimensions of x, the trailing dimensions of x are implicitly collapsed into one before the indexing is applied:
>> x = [10 20 30 40; 50 60 70 80];
>> x(:,:,2) = x+100;
>> x([1 2], [1; 5; 7; 8])
ans =
10 110 130 140
50 150 170 180

Related

How to manipulate matrix addition and multiplication for Euclidean distance computation?

I have the following:
A = [1 2 3; 4 5 6; 7 8 9];
B = [10 11 12; 13 14 15];
[N1, D1] = size(A);
[N2, D2] = size(B);
A_sq = sum(A.^2, 2);
B_sq = sum(B.^2, 2)';
D = A_sq(:,ones(1,N2)) + B_sq(ones(1,N1),:) - 2.*(A*B');
where D is N1 x D1 matrix.
I want to write expression for D in one single step, i.e., something like this (this is for illustration purpose, but it should compute the same Euclidean distance as the code above):
D = sum(A - B).^2;
I will appreciate any advise.
If you have the Statistics Toolbox you can use pdist2, which does just that:
D = pdist2(A,B).^2
Or you can do it manually with bsxfun and permute:
D = permute((sum(bsxfun(#minus, A, permute(B, [3 2 1])).^2,2)), [1 3 2]);
For your example matrices
A = [1 2 3; 4 5 6; 7 8 9];
B = [10 11 12; 13 14 15];
either of the above gives
D =
243 432
108 243
27 108

creating diagonal matrix with selected elements

I have a 4x5 matrix called A from which I want to select randomly 3 rows, then 4 random columns and then select those elements which coincide in those selected rows and columns so that I have 12 selected elements.Then I want to create a diagonal matrix called B which will have entries either 1 or 0 so that multiplication of that B matrix with reshaped A matrix (20x1) will give me those selected 12 elements of A.
How can I create that B matrix? Here is my code:
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
It's a little hard to understand what you want and your code isn't much help but I think I've a solution for you.
I create a matrix (vector) of zeros of the same size as A and then use bsxfun to determine the indexes in this vector (which will be the diagonal of B) that should be 1.
>> A = reshape(1:20, 4, 5);
>> R = [1 2 3]; % Random rows
>> C = [2 3 4 5]; % Random columns
>> B = zeros(size(A));
>> B(bsxfun(#plus, C, size(A, 1)*(R-1).')) = 1;
>> B = diag(B(:));
>> V = B*A(:);
>> V = V(V ~= 0)
V =
2
3
4
5
6
7
8
9
10
11
12
13
Note: There is no need for B = diag(B(:)); we could have simply used element by element multiplication in Matlab.
>> V = B(:).*A(:);
>> V = V(V ~= 0)
Note: This may be overly complex or very poorly put together and there is probably a better way of doing it. It's my first real attempt at using bsxfun on my own.
Here is a hack but since you are creating y2 you might as well just use it instead of creating the useless B matrix. The bsxfun answer is much better.
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
idx = reshape(y2 ~= 0, numel(y2), []);
B = diag(idx);
% "diagonal" matrix 12x20
B = B(all(B==0,2),:) = [];
output = B * A(:)
output =
1
3
4
9
11
12
13
15
16
17
19
20
y2 from example.
y2 =
1 0 9 13 17
0 0 0 0 0
3 0 11 15 19
4 0 12 16 20

Find top n elements in matrix

I have a matrix which contains values and I wish to find the index of the top n minimum values.
I use the following code for finding the minimum most value:
[r,c]=find(Result==min(min(Result)));
I cant find any other questions on stack overflow which answer the question, please help
Maybe you could do something like this:
sorted = sort(Result(:));
topten = sorted(1:10);
[~,ia,~] = intersect(Result(:),topten(:)); % // Get the indices of the top ten values
[r,c]=ind2sub(size(Result),ia); % // Convert the indices to rows and columns
Or without Intersect in the other answer
[sorted,I] = sort(Result(:));
[r,c] = ind2sub(size(Result),I(1:10)); %//Change 10 to any other required value
Use prctile (Statistics Toolbox) to find the appropriate threshold, and then use indexing to select the elements above that threshold:
x = magic(4); %// example
n = 5; %// we want the top n elements
M = numel(x);
p = prctile(x(:), (M-n)/M*100);
indices = find(x>p); %// result in the form linear indices
[row, col] = find(x>p); %// result in the form of row and column indices
In this example:
>> x
x =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
>> indices.'
ans =
1 8 12 13 15
>> row.'
ans =
1 4 4 1 3
>> col.'
ans =
1 2 3 4 4
>> x(indices).'
ans =
16 14 15 13 12
Example with repeated elements:
>> x = [1 1 2 5; 3 4 3 5];
>> n = 5;
gives
>> indices.'
ans =
2 4 6 7 8
>> row.'
ans =
2 2 2 1 2
>> col.'
ans =
1 2 3 4 4
>> x(indices).'
ans =
3 4 3 5 5

Creating Matrix with a loop in Matlab

I want to create a matrix of the following form
Y = [1 x x.^2 x.^3 x.^4 x.^5 ... x.^100]
Let x be a column vector.
or even some more variants such as
Y = [1 x1 x2 x3 (x1).^2 (x2).^2 (x3).^2 (x1.x2) (x2.x3) (x3.x1)]
Let x1,x2 and x3 be column vectors
Let us consider the first one. I tried using something like
Y = [1 : x : x.^100]
But this also didn't work because it means take Y = [1 x 2.*x 3.*x ... x.^100] ?
(ie all values between 1 to x.^100 with difference x)
So, this also cannot be used to generate such a matrix.
Please consider x = [1; 2; 3; 4];
and suggest a way to generate this matrix
Y = [1 1 1 1 1;
1 2 4 8 16;
1 3 9 27 81;
1 4 16 64 256];
without manually having to write
Y = [ones(size(x,1)) x x.^2 x.^3 x.^4]
Use this bsxfun technique -
N = 5; %// Number of columns needed in output
x = [1; 2; 3; 4]; %// or [1:4]'
Y = bsxfun(#power,x,[0:N-1])
Output -
Y =
1 1 1 1 1
1 2 4 8 16
1 3 9 27 81
1 4 16 64 256
If you have x = [1 2; 3 4; 5 6] and you want Y = [1 1 1 2 4; 1 3 9 4 16; 1 5 25 6 36] i.e. Y = [ 1 x1 x1.^2 x2 x2.^2 ] for column vectors x1, x2 ..., you can use this one-liner -
[ones(size(x,1),1) reshape(bsxfun(#power,permute(x,[1 3 2]),1:2),size(x,1),[])]
Using an adapted Version of the code found in Matlabs vander()-Function (which is also to be found in the polyfit-function) one can get a significant speedup compared to Divakars nice and short solution if you use something like this:
N = 5;
x = [1:4]';
V(:,n+1) = ones(length(x),1);
for j = n:-1:1
V(:,j) = x.*V(:,j+1);
end
V = V(:,end:-1:1);
It is about twice as fast for the example given and it gets about 20 times as fast if i set N=50 and x = [1:40]'. Although I state that is not easy to compare the times, just as an option if speed is an issue, you might have a look at this solution.
in octave, broadcasting allows to write
N=5;
x = [1; 2; 3; 4];
y = x.^(0:N-1)
output -
y =
1 1 1 1 1
1 2 4 8 16
1 3 9 27 81
1 4 16 64 256

MATLAB plot with sorted order

I have two vectors in MATLAB, say:
x = [1 20 3 7 10]
and
y = [2 51 1 9 18]
How can I plot y vs K where x has sorted value order (1 3 7 10 20) with their respective y values like the following?
x = [1 3 7 10 20]
y = [2 1 9 18 51]
Call sort with a second output argument.
x = [1 20 3 7 10]
y = [2 51 1 9 18]
[xsorted, I] = sort(x)
ysorted = y(I)
XY = sortrows([x ; y]');
plot(XY(:,1), XY(:,2));
Concatenate the matrices, transpose them and then you can use sortrows to order by X