1

Is there a way to let accumarray drop every observation but the last of each group?

What I had in mind was something similar:

lastobs=accumarray(bin,x,[],@(x){pick the observation with the max index in each group};  

To give you an example, suppose I have the following:

bin=[1 2 3  3 3 4 4];  %#The bin where the observations should be put
x= [21 3 12 5 6 8 31]; %#The vector of observations
%#The output I would like is as follow  
lastobs=[21 3 6 31];  

I am actually thinking of accumarray only because I just used it to compute the mean of the observations for each bin. So every function that could make the trick would be fine for me.

chappjc
  • 30,359
  • 6
  • 75
  • 132
CAPSLOCK
  • 6,243
  • 3
  • 33
  • 56

2 Answers2

4

Of course you can do this with accumarray. x(end) is the last observation in an array. Note that bin needs to be sorted for this to work, so if it isn't, run [bin,sortIdx]=sort(bin);x = x(sortIdx); first.

lastobs = accumarray(bin(:),x(:),[],@(x)x(end)); %# bin, x, should be n-by-1
Jonas
  • 74,690
  • 10
  • 137
  • 177
  • Thank you Jonas, I am still a mess with indexing. I tried tons of combinations but not the easiest one! Glad you are always present to help – CAPSLOCK Feb 24 '14 at 16:34
  • For future readers: `bin` needs to be sorted for this to work correctly! – knedlsepp Feb 11 '15 at 20:02
  • @knedlsepp: no, `bin` can be any order, as long as its order corresponds to the order of the elements in `x`. Or can you give an example where it doesn't work? – Jonas Feb 12 '15 at 06:15
  • For me: `bin = repmat(1:10,1,2); accumarray(bin(:),1:length(bin),[],@(x)x(end))` will output `11 12 13 14 5 6 7 18 19 20` instead of `11:20`. I did write a short [Q&A](http://stackoverflow.com/questions/28463433) because of this recent [question](http://stackoverflow.com/questions/28460280/) and Luis' answer. The `doc accumarray` lists this behaviour under Input Arguments-fun. It is quite hidden... – knedlsepp Feb 12 '15 at 09:37
  • @knedlsepp: Well, TIL. Edited my answer. – Jonas Feb 12 '15 at 09:58
2

You already got your accumarray answer, but since you are looking for any solution that will do the job, consider the following application of unique.

Using unique with the 'legacy' option gives the index of the last occurrence of each value, as you need:

>> [~,ia] = unique(bin,'legacy')
ia =
     1     2     5     7
>> lastobs = x(ia)
lastobs =
    21     3     6    31

Now, I love accumarray, as many here are aware, but I actually prefer this solution.

chappjc
  • 30,359
  • 6
  • 75
  • 132
  • Thanks chappjc, I am pretty new to matlab and every answer is something I can learn from. Anyway, I will work with the biggest series tomorrow, I will check both solutions and see which one is the fastest. Thank you again for your answer. – CAPSLOCK Feb 24 '14 at 22:17
  • @Gio `accumarray` is _really fast_ in most cases, but perhaps with this specific `fun` it could be a bit slower. – chappjc Feb 24 '14 at 22:19