0

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 ...

smci
  • 32,567
  • 20
  • 113
  • 146
  • (@Wolfie) Belatedly I found a near-duplicate, although the language used was different [Vectorizing computing array indexing/subsetting window in Matlab](https://stackoverflow.com/questions/35001037/vectorizing-array-indexing-subsetting-in-matlab) – smci Feb 21 '18 at 14:53
  • That's alright, it the accepted (only) answer shows how to use `bsxfun`, which is redundant with the implicit expansion now available (and shown below). – Wolfie Feb 21 '18 at 15:00
  • @Wolfie: best to post that over there. I don't mean to be littering SO with duplicates. And their answer is obsolete. Perhaps should close that into this. – smci Feb 21 '18 at 15:05
  • You can't flag a question as duplicate of a newer question... and by redundant, I mean you don't *need* it in MATLAB R2016b+ or Octave, but you can still use it. – Wolfie Feb 21 '18 at 15:42
  • @Wolfie yes you can close towards newer. Since that question and its accepted answer are obsolete and not vectorized (and bsxfun is obscure), arguably we should close. Unless you want to repost this answer there, and note existing answer is obsolete. – smci Feb 21 '18 at 23:41

2 Answers2

1

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] );
Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • `[idx-1, idx+1]' (:)` does it too – smci Feb 20 '18 at 09:43
  • You can safely assume `idx` is a row vector (it's the result of the `find()` command) – smci Feb 20 '18 at 09:44
  • `( idx(:)' + [-1; 1] )(:)` works, without needing to call `reshape()` – smci Feb 20 '18 at 09:46
  • What kills me is why do all octave's matrix-manipulation functions `horzcat()/vertcat()/cat()` not work? Surely there are only two damn ways to unstack a 2D matrix... but they only do column-wise – smci Feb 20 '18 at 09:48
  • You cannot assume the output of `find` is a row vector, `find([1;3;4;0])` returns a column vector as the input was a column vector. As mentioned in my other comment, you cannot use `(:)` in the same expression as a matrix creation in MATLAB, that is exclusive to Octave. `horzcat/vertcat/cat` all work in MATLAB and Octave, and the first two are equivalent like `[1, 2] == horzcat([1], [2])` and `[1; 2] == vertcat([1], [2])` so I'm not sure what you meant there. – Wolfie Feb 20 '18 at 09:51
  • I should have said You can safely assume idx is a **column** vector (it's the result of the `find()` command on column `data(:,3)`) . In my context it's guaranteed idx is a column vector. But yes your generalized approach is going further. – smci Feb 20 '18 at 10:20
  • Right. In my case `find(diff(data(:,3)) > 0)` is guaranteed to give unique indices. Also it looks like `horzcat()/vertcat()/cat()` are for concatenating matrices into matrices, not into vectors. The octave chapter on Rearranging Matrices is not that clear. – smci Feb 20 '18 at 10:38
0

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

smci
  • 32,567
  • 20
  • 113
  • 146
  • 1
    Note that I don't believe `vec` is a MATLAB function, it is exclusive to Octave. In Matlab, you would assign to a temp variable, then use `x(:)` instead of `vec(x)`... this approach works in Octave too – Wolfie Feb 20 '18 at 09:33
  • @Wolfie and `[idx-1, idx+1]' (:)` does this too – smci Feb 20 '18 at 09:43
  • 2
    That also won't work in MATLAB, as you cannot index an array as you create it (which you're doing with `[numbers](:)`. Only Octave allows for this sort of instant indexing. – Wolfie Feb 20 '18 at 09:47
  • Whoa, is Octave better than Matlab in some ways? – smci Feb 20 '18 at 09:48
  • So `( find(diff(data(:,3)) > 0 )' + [-1; 1] )(:)` works as a one-liner in Octave – smci Feb 20 '18 at 09:50
  • I acknowledged your solution, and accepted your answer. Thanks. Was trying to refine your answer into the shortest octave syntax, also given can assume column vector in my context. – smci Feb 21 '18 at 13:46
  • Ah I understand, glad I could help – Wolfie Feb 21 '18 at 13:48