I have a list which looks like:

test = {{0.01,3}, {0.001,3.1}, {0.002,3.2}, ..., {0.01,7}, {0.001,7.1}, {0.002,7.2}, ...,     
        {0.01,10}, {0.001,10.1}, {0.002,10.2}, ..., {0.01,13}, {0.001,13.1}, {0.002,13.2}}

What I would like to do is for every value of the second coordinate that is close to a centre value (near 3, 7, 10 or 13) I only want to keep the ones (pairs) that have the smallest value of the first coordinate. After this transformation the list would become:

{{0.001,3.1}, {0.001,7.1}, {0.001,10.1}, {0.001,13.1}}

Note that when I have entered a break in the test list there is also a spacing in the 2nd coordinate values, so the list runs from say 3 - 3.5 and then skips to 7 - 7.5 and so on; it is not a continuous count.

Also, the values of the first coordinates are not equal, as those given in the example above, so the list may end up looking like

{{0.001, 3.1}, {0.005, 7.2}, {0.00001, 10.3}, {0.007, 13.4}}

which is still a correct result.

  • 1
    $\begingroup$ How are you defining elements that are close together? Are you interested in IntegerPart or Round or something else? $\endgroup$ – bobthechemist Feb 6 '14 at 4:12

I assume from your post the values of the second elements are to be grouped in ranges like [N,N+1), if so this should do what you want:

Gather[SortBy[yourListHere, First], IntegerPart[#1] == IntegerPart[#2] &][[All, 1]]

For a different way of looking at "close", this treats things within +/- .5 as "close", you can adjust as needed.

Gather[SortBy[yourListHere, First], (Abs[#1[[2]] - #2[[2]]] < .5) &][[All, 1]]

Beyond that, you'll need to better define what "close" means.

  • $\begingroup$ I just posted my edit a few seconds after yours and I guess we were thinking along the same line. $\endgroup$ – RunnyKine Feb 6 '14 at 4:01
  • $\begingroup$ @RunnyKine: That's weird - your post was not even showing though older - unless the time is when it was started and the post doesn't show while being edited, but yes, like minds! $\endgroup$ – ciao Feb 6 '14 at 4:05
  • $\begingroup$ Thankx mate this worked perfectly, sorry for all the running round in circles, greatly appreciated though :) $\endgroup$ – Colin Feb 6 '14 at 4:17
  • $\begingroup$ @Colin: No worries. Welcome to SE, take the time to review the FAQ, it has help on posting code properly, and pointers to how to pose questions. This fora has a tremendous group of experts, the better you specify questions in the future, the more likely you'll get proper answers. $\endgroup$ – ciao Feb 6 '14 at 4:19
  • $\begingroup$ +1. I ended up deleting my answer since the edit was just too similar to yours. $\endgroup$ – RunnyKine Feb 6 '14 at 4:28

When possible you should try to cast this kind of problem in a form that can be handled by GatherBy. This is much faster than Gather with a custom comparator as I attempted to explain in: Underlying Algorithms for List Manipulation Functions. (Still unfinished, but at least Gather is covered.) Also see Checking for duplicates in sublists and the posts linked therein.

You can also efficiently, and often more conveniently, make use of Sow and Reap, though performance usually lags a bit behind GatherBy.

It is important to understand that GatherBy or Sow/Reap will produce slightly different results from Gather because they do not perform a full pairwise comparison; this is also the reason they are more efficient. Likewise any other binning method will have better computational complexity than Gather or Union or DeleteDuplicates with a custom comparator, as the latter group all operate pairwise in that mode.

If your data is not conducive to regular bins as produced by Round, Floor, etc. (with a second parameter setting width) you may wish to do some analysis of the data beforehand to discover the "centers" of your data and then use Nearest, Interpolation etc. for binning.

If you give a larger data set and a clear description of your target I can probably provide you with a useful example.


I'm still not terribly clear on what the criteria are, but here's another possible route which allows for defining criteria:

First@Sort[Cases[test, {_, x_} /; 
      IntegerPart[x] == #], #1[[1]] < #2[[1]] &] & /@ {3, 7, 10, 13}

You'll have to replace {3, 7, 10, 13} with the complete list. As alluded to in some of the comments, you may want Round instead, which can be done:

First@Sort[Cases[test, {_, x_} /; 
      Round[x] == #], #1[[1]] < #2[[1]] &] & /@ {3, 7, 10, 13}

Which in this case yields the same result.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.