I have a 256 x 256 matrix M, and have produced some linear indexes L.
Also I have a vector of weights, of numels same as L, to be added to the elements of M indexed by L.
Problem is, with the expression
M(L) = M(L) + weights;
For duplicate values in L, only the last corresponding element in weights will be added.
Is there a simple way to resolve this/am I missing something?
I think the way to go here is using accumarray:
% The 'data'
M = zeros(10,5); % Suppose this is your matrix
L = [46 47 47 46 48 49 48 48 48]'; % The linear index numbers
weights = [4 7 4 6 4 9 48 8 48]'; % The weights for these index numbers
% Make sure the indices are in ascending order
Y = SORTROWS([L weights]);
% Determining the weights to be added
idx = unique(Y(:,1));
weights_unique = accumarray(Y(:,1),Y(:,2));
% The addition
M(idx) = M(idx) + weights_unique(weights_unique>0);
Related
I am trying to index (not get) the diagonals of a matrix in matlab.
Say I have a matrix "M", that is n by n. Then I want to obtain all indeces of all possible diagonals in the matrix "M".
I know that the center diagonal is indexed by
M(1:(n+1):end)
and all the following diagonals above it are indexed as:
M((1+1*n):(n+1):end)
M((1+2*n):(n+1):end)...
M((1+n*n):(n+1):end)
Now I also want to get the diagonals below. I cannot for the life of me figure out how to however.
Reproducible example:
rng(1); % set seed
n = 4;
M = rand(n);
yielding
M =
0.562408 0.947364 0.655088 0.181702
0.960604 0.268834 0.469042 0.089167
0.578719 0.657845 0.516215 0.419000
0.226410 0.601666 0.169212 0.378740
where I would like to index the lower diagonals, e.g. the subdiagonal:
0.960604 0.657845 0.169212
That is, I don't need to get the diagonal by e.g. the diags function, but access the index (since I ultimately want to replace the matrix entries diagonal by diagonal).
As you already noted, you can use the diag function to get the main diagonal and other diagonals above or below the main diagonals,
M = magic(4) % Test data
M =
16 2 3 13
5 11 10 8
9 7 6 12
4 14 15 1
diag(M, -1)
ans =
5
7
15
but you can not assign values to the diagonal with the diag function:
diag(M, -1) = [3; 2; 1]
Index in position 2 is invalid. Array indices must be positive integers or logical values.
Instead, we can use logical indexing by indexing the array M with a logical matrix of the same size. We can easily create this matrix using the diag function, by creating a diagonal matrix with ones on the specified diagonal:
diag(ones(1, 3), -1)
ans =
0 0 0 0
1 0 0 0
0 1 0 0
0 0 1 0
To use this matrix for logical indexing, we need to convert it from double to logical with the logical function.
M(logical(diag(ones(1, 3), -1)))
ans =
5
7
15
or assign new values to it with
M(logical(diag(ones(1, 3), -1))) = [99, 98, 97]
M =
16 2 3 13
99 11 10 8
9 98 6 12
4 14 97 1
There is a slightly more performant way of using diag to get indices to a diagonal:
n = 5; % matrix size
M = reshape(1:n*n,n,n); % matrix with linear indices
indices = diag(M, ii); % indices to diagonal ii
However, it is much easier to just compute the right indices directly. As discovered by OP, the upper diagonal elements are given by:
indices = (1+ii*n):(n+1):(n*n);
(Note that the parenthesis are not necessary, as the colon operator has the lowest precedence.)
The lower diagonal elements are given by:
indices = (1+ii):(n+1):((n-ii)*n);
Both series are identical for the main diagonal, where ii=0.
We can verify correctness of these calculations by using the first method:
n = 5; % matrix size
M = reshape(1:n*n,n,n); % matrix with linear indices
for ii=1:n-1
indices = (1+ii*n):(n+1):(n*n);
assert(isequal(indices, diag(M, ii).'))
indices = (1+ii):(n+1):((n-ii)*n);
assert(isequal(indices, diag(M, -ii).'))
end
Given a matrix Z(i,j) such that it maps to two arrays X(i), and Y(j).
I am trying to find elements of Z (and hence the corresponding X and Y) within a certain range.
I am now doing the following using logical indexing. Given this example
X = 1:5;
Y = 1:5;
Z = [17 24 1 8 15
23 5 6 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9]
Z((X>1 & X<4),Y==3)
This works fine, but now I wish to find the minimum of the returned values from this particular range,
Which I do with
min(Z((X>1 & X<4),Y==3))
But now how I get back the corresponding X and Y values of the minimum? Since my logical indexing returns an array, all methods I have tried so far returns the index of the min in the answer array, not the original Z matrix.
I can't use
[row col] = find(Z==min(Z((X>1 & X<4),Y==3)))
Because of the repeats. What are my alternatives?
To retrieve the original indices, you have to keep the memory of the indices of your two conditions on x and y (which I put in the arrays cX and cY) and then use the function ind2sub.
NB: your code is a little bit confusing since x stands for the lines
and y for the columns, but I have kept the same convention in my
answer.
In practice, this gives:
% --- Definition
X = 1:5;
Y = 1:5;
Z = [17 24 1 8 15
23 5 6 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9];
% --- Get the values of interest
cX = find(X>1 & X<4);
cY = find(Y==3);
v = Z(cX,cY);
% --- Get position of the minimum in the initial array
[~, I] = min(v(:));
[Ix, Iy] = ind2sub([numel(cX) numel(cY)], I);
i = cX(Ix); % i = 2
j = cY(Iy); % j = 3
Best,
One approach -
%// Calculate all indices of the grid created with those two X-Y conditions
idx = bsxfun(#plus,(find(Y==3)-1)*size(Z,1),find((X>1 & X<4)).') %//'
%// Get the index corresponding to minimum from that grided Z
[~,min_idx] = min(Z(idx(:)))
%// Get corresponding X-Y indices by using indices calculated earlier
[indX,indY] = ind2sub([numel(X) numel(Y)],idx(min_idx))
How to select a submatrix (not in any pattern) in Matlab? For example, for a matrix of size 10 by 10, how to select the submatrix consisting of intersection of the 1st 2nd and 9th rows and the 4th and 6th columns?
Thanks for any helpful answers!
TLDR: Short Answer
As for your question, suppose you have an arbitrary 10-by-10 matrix A. The simplest way to extract the desired sub-matrix would be with an index vector:
B = A([1 2 9], [4 6]);
Indexing in MATLAB
There's an interesting article in the official documentation that comprehensively explains indexing in MATLAB.
Basically, there are several ways to extract a subset of values, I'll summarize them for you:
1. Indexing Vectors
Indexing vectors indicate the indices of the element to be extracted. They can either contain a single index or several, like so:
A = [10 20 30 40 50 60 70 80 90]
%# Extracts the third and the ninth element
B = A([3 9]) %# B = [30 90]
Indexing vectors can be specified for each dimension separately, for instance:
A = [10 20 30; 40 50 60; 70 80 90];
%# Extract the first and third rows, and the first and second columns
B = A([1 3], [1 2]) %# B = [10 30; 40 60]
There are also two special subscripts: end and the colon (:):
end simply indicates the last index in that dimension.
The colon is just a short-hand notation for "1:end".
For example, instead of writing A([1 2 3], [2 3]), you can write A(:, 2:end). This is especially useful for large matrices.
2. Linear Indexing
Linear indexing treats any matrix as if it were a column vector by concatenating the columns into one column vector and assigning indices to the elements respectively. For instance, we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to compute b = A(2). The equivalent column vector is:
A = [10;
40;
70;
20;
50;
80;
30;
60;
90]
and thus b equals 40.
The special colon and end subscripts are also allowed, of course. For that reason, A(:) converts any matrix A into a column vector.
Linear indexing with matrix subscripts:
It is also possible to use another matrix for linear indexing. The subscript matrix is simply converted into a column vector, and used for linear indexing. The resulting matrix is, however always of the same dimensions as the subscript matrix.
For instance, if I = [1 3; 1 2], then A(I) is the same as writing reshape(A(I(:)), size(I)).
Converting from matrix subscripts to linear indices and vice versa:
For that you have sub2ind and ind2sub, respectively. For example, if you want to convert the subscripts [1, 3] in matrix A (corresponding to element 30) into a linear index, you can write sub2ind(size(A), 1, 3) (the result in this case should be 7, of course).
3. Logical Indexing
In logical indexing the subscripts are binary, where a logical 1 indicates that the corresponding element is selected, and 0 means it is not. The subscript vector must be either of the same dimensions as the original matrix or a vector with the same number of elements. For instance, if we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to extract A([1 3], [1 2]) using logical indexing, we can do either this:
Ir = logical([1 1 0]);
Ic = logical([1 0 1]);
B = A(Ir, Ic)
or this:
I = logical([1 0 1; 1 0 1; 0 0 0]);
B = A(I)
or this:
I = logical([1 1 0 0 0 0 1 1 0]);
B = A(I)
Note that in the latter two cases is a one-dimensional vector, and should be reshaped back into a matrix if necessary (for example, using reshape).
Let me explain with an example:
Let's define a 6x6 matrix
A = magic(6)
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
From this matrix you want the elements in rows 1, 2 and 5, and in the columns 4 and 6
B = A([1 2 5],[4 6])
B =
26 24
21 25
12 16
Hope this helps.
function f = sub(A,i,j)
[m,n] = size(A);
row = 1:m;
col = 1:n;
x = row;
x(i) = [];
y=col;
y(j) = [];
f= A(x,y);
Returns the matrix A, with the ith row and jth column removed.
How to select a submatrix (not in any pattern) in Matlab? For example, for a matrix of size 10 by 10, how to select the submatrix consisting of intersection of the 1st 2nd and 9th rows and the 4th and 6th columns?
Thanks for any helpful answers!
TLDR: Short Answer
As for your question, suppose you have an arbitrary 10-by-10 matrix A. The simplest way to extract the desired sub-matrix would be with an index vector:
B = A([1 2 9], [4 6]);
Indexing in MATLAB
There's an interesting article in the official documentation that comprehensively explains indexing in MATLAB.
Basically, there are several ways to extract a subset of values, I'll summarize them for you:
1. Indexing Vectors
Indexing vectors indicate the indices of the element to be extracted. They can either contain a single index or several, like so:
A = [10 20 30 40 50 60 70 80 90]
%# Extracts the third and the ninth element
B = A([3 9]) %# B = [30 90]
Indexing vectors can be specified for each dimension separately, for instance:
A = [10 20 30; 40 50 60; 70 80 90];
%# Extract the first and third rows, and the first and second columns
B = A([1 3], [1 2]) %# B = [10 30; 40 60]
There are also two special subscripts: end and the colon (:):
end simply indicates the last index in that dimension.
The colon is just a short-hand notation for "1:end".
For example, instead of writing A([1 2 3], [2 3]), you can write A(:, 2:end). This is especially useful for large matrices.
2. Linear Indexing
Linear indexing treats any matrix as if it were a column vector by concatenating the columns into one column vector and assigning indices to the elements respectively. For instance, we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to compute b = A(2). The equivalent column vector is:
A = [10;
40;
70;
20;
50;
80;
30;
60;
90]
and thus b equals 40.
The special colon and end subscripts are also allowed, of course. For that reason, A(:) converts any matrix A into a column vector.
Linear indexing with matrix subscripts:
It is also possible to use another matrix for linear indexing. The subscript matrix is simply converted into a column vector, and used for linear indexing. The resulting matrix is, however always of the same dimensions as the subscript matrix.
For instance, if I = [1 3; 1 2], then A(I) is the same as writing reshape(A(I(:)), size(I)).
Converting from matrix subscripts to linear indices and vice versa:
For that you have sub2ind and ind2sub, respectively. For example, if you want to convert the subscripts [1, 3] in matrix A (corresponding to element 30) into a linear index, you can write sub2ind(size(A), 1, 3) (the result in this case should be 7, of course).
3. Logical Indexing
In logical indexing the subscripts are binary, where a logical 1 indicates that the corresponding element is selected, and 0 means it is not. The subscript vector must be either of the same dimensions as the original matrix or a vector with the same number of elements. For instance, if we have:
A = [10 20 30; 40 50 60; 70 80 90];
and we want to extract A([1 3], [1 2]) using logical indexing, we can do either this:
Ir = logical([1 1 0]);
Ic = logical([1 0 1]);
B = A(Ir, Ic)
or this:
I = logical([1 0 1; 1 0 1; 0 0 0]);
B = A(I)
or this:
I = logical([1 1 0 0 0 0 1 1 0]);
B = A(I)
Note that in the latter two cases is a one-dimensional vector, and should be reshaped back into a matrix if necessary (for example, using reshape).
Let me explain with an example:
Let's define a 6x6 matrix
A = magic(6)
A =
35 1 6 26 19 24
3 32 7 21 23 25
31 9 2 22 27 20
8 28 33 17 10 15
30 5 34 12 14 16
4 36 29 13 18 11
From this matrix you want the elements in rows 1, 2 and 5, and in the columns 4 and 6
B = A([1 2 5],[4 6])
B =
26 24
21 25
12 16
Hope this helps.
function f = sub(A,i,j)
[m,n] = size(A);
row = 1:m;
col = 1:n;
x = row;
x(i) = [];
y=col;
y(j) = [];
f= A(x,y);
Returns the matrix A, with the ith row and jth column removed.
I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:
(image from Wikipedia)
I would like the algorithm to be generic such that I can pass in a 2D matrix with any dimensions. I am a C++ programmer by trade and am very tempted to write an old school loop to accomplish this, but I suspect there is a better way to do it in MATLAB.
I'd be rather want an algorithm that worked on an NxN matrix and go from there.
Example:
1 2 3
4 5 6 --> 1 2 4 7 5 3 6 8 9
7 8 9
Consider the code:
M = randi(100, [3 4]); %# input matrix
ind = reshape(1:numel(M), size(M)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals
ind(:,1:2:end) = flipud( ind(:,1:2:end) ); %# reverse order of odd columns
ind(ind==0) = []; %# keep non-zero indices
M(ind) %# get elements in zigzag order
An example with a 4x4 matrix:
» M
M =
17 35 26 96
12 59 51 55
50 23 70 14
96 76 90 15
» M(ind)
ans =
17 35 12 50 59 26 96 51 23 96 76 70 55 14 90 15
and an example with a non-square matrix:
M =
69 9 16 100
75 23 83 8
46 92 54 45
ans =
69 9 75 46 23 16 100 83 92 54 8 45
This approach is pretty fast:
X = randn(500,2000); %// example input matrix
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).'; %'// output row vector
Benchmarking
The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).
%// Amro's approach
function y = zigzag_Amro(M)
ind = reshape(1:numel(M), size(M));
ind = fliplr( spdiags( fliplr(ind) ) );
ind(:,1:2:end) = flipud( ind(:,1:2:end) );
ind(ind==0) = [];
y = M(ind);
%// Luis' approach
function y = zigzag_Luis(X)
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).';
%// Benchmarking code:
S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
%// f = [0.5 2]; %// plotted with '--'
%// f = [2 0.5]; %// plotted with ':'
t_Amro = NaN(size(S));
t_Luis = NaN(size(S));
for n = 1:numel(S)
X = rand(f(1)*S(n), f(2)*S(n));
f_Amro = #() zigzag_Amro(X);
f_Luis = #() zigzag_Luis(X);
t_Amro(n) = timeit(f_Amro);
t_Luis(n) = timeit(f_Luis);
end
loglog(S.^2*prod(f), t_Amro, '.b-');
hold on
loglog(S.^2*prod(f), t_Luis, '.r-');
xlabel('number of matrix entries')
ylabel('time')
The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.
Here's a non-loop solution zig_zag.m. It looks ugly but it works!:
function [M,index] = zig_zag(M)
[r,c] = size(M);
checker = rem(hankel(1:r,r-1+(1:c)),2);
[rEven,cEven] = find(checker);
[cOdd,rOdd] = find(~checker.'); %'#
rTotal = [rEven; rOdd];
cTotal = [cEven; cOdd];
[junk,sortIndex] = sort(rTotal+cTotal);
rSort = rTotal(sortIndex);
cSort = cTotal(sortIndex);
index = sub2ind([r c],rSort,cSort);
M = M(index);
end
And a test matrix:
>> M = [magic(4) zeros(4,1)];
M =
16 2 3 13 0
5 11 10 8 0
9 7 6 12 0
4 14 15 1 0
>> newM = zig_zag(M) %# Zig-zag sampled elements
newM =
16
2
5
9
11
3
13
10
7
4
14
6
8
0
0
12
15
1
0
0
Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.
I think this should be generalizeable to a non-square array.
% for a 3x3 array
n=3;
numElementsPerDiagonal = [1:n,n-1:-1:1];
hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
% loop through the hankel array and add numbers counting either up or down
% if they are even or odd
for d = 1:(2*n-1)
if floor(d/2)==d/2
% even, count down
array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
else
% odd, count up
array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
end
end
% now flip to get the result
indexMatrix = fliplr(array2add)
result =
1 2 6
3 5 7
4 8 9
Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.
EDIT
Ok, from your comment it looks like you need to use sort like Marc suggested.
indexMatrixT = indexMatrix'; % ' SO formatting
[dummy,sortedIdx] = sort(indexMatrixT(:));
sortedIdx =
1 2 4 7 5 3 6 8 9
Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.
Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -
[m,n] = size(X);
nlim = m*n;
n = n+mod(n-m,2);
mask = bsxfun(#le,[1:m]',[n:-1:1]);
start_vec = m:m-1:m*(m-1)+1;
a = bsxfun(#plus,start_vec',[0:n-1]*m);
offset_startcol = 2- mod(m+1,2);
[~,idx] = min(mask,[],1);
idx = idx - 1;
idx(idx==0) = m;
end_ind = a([0:n-1]*m + idx);
offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
a(:,offset_startcol:2:end) = bsxfun(#minus,offsets,a(:,offset_startcol:2:end));
out = a(mask);
out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
result = X([out2 ; out(out<=nlim)]);
Quick runtime tests against Luis's approach -
Datasize: 500 x 2000
------------------------------------- With Proposed Approach
Elapsed time is 0.037145 seconds.
------------------------------------- With Luis Approach
Elapsed time is 0.045900 seconds.
Datasize: 5000 x 20000
------------------------------------- With Proposed Approach
Elapsed time is 3.947325 seconds.
------------------------------------- With Luis Approach
Elapsed time is 6.370463 seconds.
Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be
[~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
reorderedim = im(I);
I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.