What's the best Matlab/Octave idiom, given idx a vector of indices, to get the sorted vector of idx +/-1 ?
I have an n x 7 data matrix, column 3 is an integer label, and I'm interested in viewing the neighborhood of discontinuities on it.
Hence I get the corresponding indices:
idx = find(diff(data(:,3)) > 0)
5297
6275
6832
...
20187
Then if I want to view that neighborhood +/- 1 on my column (e.g. on the (mx2) matrix [idx-1; idx+1]), I need to form the vector of idx-1, idx+1 either concatenated in-order, or resorted.
I found some clunky ways of doing this, what's the proper way?
(I tried all of the octave chapter on Rearranging Matrices)
% WAY 1: this works, but is ugly - a needless O(n) sort
sort([idx-1; idx+1])
% horzcat,vertcat,vec only stack it vertically
horzcat([idx-1; idx+1])
horzcat([idx-1; idx+1]')
% WAY 2?
%One of vec([idx-1; idx+1]) or vec([idx-1; idx+1]') should work? but doesn't, they always stack columnwise
horzcat([idx-1; idx+1]')
ans =
Columns 1 through ...
5297 6275 6832 ... 20187 5299 6277 6834 ... 20189
% TRY 3...
reshape([idx-1; idx+1], [36,1]) doesn't work either
You would expect there are only two ways to unstack a 2xm matrix, but ...
You can do this with implicit singleton expansion (R2016b or newer MATLAB, native to Octave)
idx = [2, 6, 9]; % some vector of integers
% Use reshape with [] to tell MATLAB "however many rows it takes"
neighbours = reshape( idx + [-1;1], [], 1 );
>> neighbours = [1; 3; 6; 8; 8; 10];
If you don't know whether idx is a row or column, you can be more robust by using
neighbours = reshape( idx(:)' + [-1,1], [], 1)
If you don't want to use implicit expansion (and again coping with either row or column idx), you can use reshape like so
neighbours = reshape( [idx(:)-1, idx(:)+1]', [], 1 )
Note: you may also want to wrap the whole thing in a call to unique. In my example, you get the index 8 twice, I'm not sure if this is desirable or not in your situation.
However, unique performs a sort (unless you use the 'stable' flag but that can make it even slower), so you might as well use your original approach if you want to remove duplicates:
% Remove duplicates and sort the result using unique
neighbours = unique( [idx-1, idx+1] );
Hmm, I finally found this octave matrix manipulation:
vec([idx-1, idx+1]')
ans =
5297
5299
6275
6277
6832
6834
...
20187
20189
Adapting Wolfie's solution into the shortest Octave-only code:
[idx-1, idx+1]' (:)
( idx(:)' + [-1; 1] )(:)
idx = ( find(diff(data(:,3)) > 0 )' + [-1; 1] )(:) works as a one-liner
... and [idx , data(idx,3)] displays the indices and data, side-by-side
Related
I have a situation analogous to the following
z = magic(3) % Data matrix
y = [1 2 2]' % Column indices
So,
z =
8 1 6
3 5 7
4 9 2
y represents the column index I want for each row. It's saying I should take row 1 column 1, row 2 column 2, and row 3 column 2. The correct output is therefore 8 5 9.
I worked out I can get the correct output with the following
x = 1:3;
for i = 1:3
result(i) = z(x(i),y(i));
end
However, is it possible to do this without looping?
Two other possible ways I can suggest is to use sub2ind to find the linear indices that you can use to sample the matrix directly:
z = magic(3);
y = [1 2 2];
ind = sub2ind(size(z), 1:size(z,1), y);
result = z(ind);
We get:
>> result
result =
8 5 9
Another way is to use sparse to create a sparse matrix which you can turn into a logical matrix and then sample from the matrix with this logical matrix.
s = sparse(1:size(z,1), y, 1, size(z,1), size(z,2)) == 1; % Turn into logical
result = z(s);
We also get:
>> result
result =
8
5
9
Be advised that this only works provided that each row index linearly increases from 1 up to the end of the rows. This conveniently allows you to read the elements in the right order taking advantage of the column-major readout that MATLAB is based on. Also note that the output is also a column vector as opposed to a row vector.
The link posted by Adriaan is a great read for the next steps in accessing elements in a vectorized way: Linear indexing, logical indexing, and all that.
there are many ways to do this, one interesting way is to directly work out the indexes you want:
v = 0:size(y,2)-1; %generates a number from 0 to the size of your y vector -1
ind = y+v*size(z,2); %generates the indices you are looking for in each row
zinv = z';
zinv(ind)
>> ans =
8 5 9
I'm trying to find an idiomatic way to do this.
Essentially I have an Nx2 matrix of points of the form
A = [3 4; 3 5; 4 5; 4, 6; 7 3]
I'd like my output to be [3 5; 4 6; 7 3]. In other words I would like each unique x value along with the maximum y value associated with that x.
I was hoping there would be some sort of
unique(A, 'rows', 'highestterm', 2)
method for accomplishing this, but couldn't find anything. Can anyone think of a vectorized way to solve this problem? I can do it pretty easily in a for loop, but would like to avoid that if possible.
I don't know of any single call, like you hoped. But, it can be done fairly tightly (and fully vectorized) like the code below.
%sort by first and then second column
A = sortrows(A,[1 2]);
%find each change in the first column of A
inds = find(diff(A(:,1)) > 0);
%add the last point...because find(diff) doesn't get the last point
inds(end+1) = size(A,1);
%get just those rows that meet the desired criteria
A = A(inds,:);
So, this works by sorting the data and looking for the values in the first column that don't repeat. If there are repeated values, this code grabs the last of the repeating values. Finally, because we sorted by both columns via sortrows(A,[1 2]), the last entry for a repeating value will have the biggest corresponding value from the 2nd column. I think that this hits all of your requirements.
Using accumarray and unique:
[r1, ~, u] = unique(A(:,1));
r2 = accumarray(u, A(:,2), [], #max);
result = [r1 r2];
I have two vectors xx and yy holding the x and y indices of certain pixels respectively in matrix A . What I want to do is to check the values of the pixels with those indices and count how many of those pixels have the value 0. For example, if xx=[1 2 3] and y=[2 5 8], I want to check how many of these pixels(x,y) (1,2), (2,5), (3,8) have the value 0. I can do this with for loops but I think it can be done easier in Matlab, so if anyone could please advise.
The following should work:
sum(A(sub2ind(size(A),xx,yy)) == 0)
First, you convert the row and column indices into single indices into the matrix A. Then, you check where A is zero for these indices (which will result in ones). Then you simply sum up the ones.
A dirtier way than sub2ind is
sum( A( [1 size(A,1)]*( [ yy; xx ] - 1 ) + 1 ) == 0 )
You can check here and see that the dirty method is ~x4 times faster than sub2ind. So, if you are in need for speed, use the dirty method ;)
I have two vectors with the same elements but their order is not same. For eg
A
10
9
8
B
8
9
10
I want to find the mapping between the two
B2A
3
2
1
How can I do this in matlab efficiently?
I think the Matlab sort is efficient. So:
[~,I]=sort(A); %sort A; we want the indices, not the values
[~,J]=sort(B); %same with B
%I(1) and J(1) both point to the smallest value, and a similar statement is true
%for other pairs, even with repeated values.
%Now, find the index vector that sorts I
[~,K]=sort(I);
%if K(1) is k, then A(k) is the kth smallest entry in A, and the kth smallest
%entry in B is J(k)
%so B2A(1)=J(k)=J(K(1)), where BSA is the desired permutation vector
% A similar statement holds for the other entries
%so finally
B2A=J(K);
if the above were in script "findB2A" the following should be a check for it
N=1e4;
M=100;
A=floor(M*rand(1,N));
[~,I]=sort(rand(1,N));
B=A(I);
findB2A;
all(A==B(B2A))
There are a couple of ways of doing this. The most efficient in terms of lines of code is probably using ismember(). The return values are [Lia,Locb] = ismember(A,B), where Locb are the indices in B which correspond to the elements of A. You can do [~, B2A] = ismember(A, B) to get the result you want. If your version of MATLAB does not allow ~, supply a throwaway argument for the first output.
You must ensure that there is a 1-to-1 mapping to get meaningful results, otherwise the index will always point to the first matching element.
Here a solution :
arrayfun(#(x)find(x == B), A)
I tried with bigger arrays :
A = [ 7 5 2 9 1];
B = [ 1 9 7 5 2];
It gives the following result :
ans =
3 4 5 2 1
Edit
Because arrayfun is usually slower than the equivalent loop, here a solution with a loop:
T = length(A);
B2A = zeros(1, length(A));
for tt = 1:T
B2A(1, tt) = find(A(tt) == B);
end
I would go for Joe Serrano's answer using three chained sort's.
Another approach is to test all combinations for equality with bsxfun:
[~, B2A] = max(bsxfun(#eq, B(:), A(:).'));
This gives B2A such that B(B2A) equals A. If you want it the other way around (not clear from your example), simply reverse A and B within bsxfun.
I have a matrix A = [1 2 3;2 5 9;2 3 4]. Now I want to make a search on all the elements of the matrix. Any element found greater than 8 should be detected and whole row pertaining to that element should be deleted.
As in this example A(2,3)>8. Hence in the final output matrix row 2 should be deleted and the output matrix be B = [1,2,3;2,3,4]
The inverse of Shai's answer is usually faster in loops:
B = A( all(A<=8,2), : );
or
B = A( all(A<9,2), : );
if you desire.
Note that this may not be true on newer Matlab versions (R2012a I believe has specific JIT optimizations for loops with matrix deletions). Nevertheless it's a safer bet, and may be more intuitive.
use logical indexing and any command
>> selRowToDelete = any( A > 8, 2 ); % any value on dim 2 (rows)
>> A( selRowToDelete, : ) = []; % remove the rows