3
$\begingroup$

I have a list of pairs, for example like this:

list = {{1, 1.1}, {1.00001, 1.1000001}, {2, 1}}

I would like to delete a pair which is lies within a certain range of a previous pair (comparing the first element of each pair would be enough). So I would like somehow to implement a SameTest for the first element of each pair.

In my example the first two pairs should be consideres the same and one of them should be deleted. So my wished output would be:

{{1, 1.1}, {2, 1}}

I tried to use Union and DeleteDuplicates with a SameTest, but they only work if I flatten the list, but I would like to keep the structure.

Then I tried

DeleteDuplicatesBy[list, First]

with works fine (keeps the structure), but it only works if the elements of the list are exactly the same.

I failed to combine a SameTest with DeleteDuplicatesBy.

Is there any neat way to do this?

Edit: note that I have a list of pairs

$\endgroup$

marked as duplicate by Jason B., MarcoB, user9660, Mr.Wizard list-manipulation Feb 19 '16 at 21:57

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 4
    $\begingroup$ DeleteDuplicatesBy[list, Round@*First]? $\endgroup$ – Kuba Feb 19 '16 at 10:55
  • 1
    $\begingroup$ Like @Kuba said: delDuplicates[ list_, rd_:0.1 ] := DeleteDuplicatesBy[ list, Round[ First @ #, rd ]&? $\endgroup$ – gwr Feb 19 '16 at 10:59
  • $\begingroup$ Thanks, that almost works ;) However I would like to control the approximation: Everything which is the same within plus/minus 10^(-4) or so $\endgroup$ – Luke Feb 19 '16 at 11:01
  • 2
    $\begingroup$ @Luke In the above delDuplicates[ ] function you can specify rdto do this, e.g. delDuplicates[ list, 0.0001 ] - is that what you want? $\endgroup$ – gwr Feb 19 '16 at 11:02
  • 1
    $\begingroup$ not exactly as I have a list of pairs, The question you indicate I could have solved with a SameTest and Union. $\endgroup$ – Luke Feb 19 '16 at 11:08
3
$\begingroup$

If you want to compare the relative difference, instead of the absolute difference, here is an easy way. Use SetPrecision to set how many digits should agree for two quantities to be considered the same.

DeleteDuplicatesBy[list, SetPrecision[#, 6] &]  (* fails *)
DeleteDuplicatesBy[list, SetPrecision[#, 5] &]  (* succeeds *)
(*
  {{1, 1.1}, {1.00001, 1.1}, {2, 1}}
  {{1, 1.1}, {2, 1}}
*)

Originally I thought I could use Internal`$SameQTolerance or Internal`$EqualTolerance, but DeleteDuplicates must do something like auto-compile (or reset the tolerance) on arrays that can be packed. While the approach works on the OP's example, it does not work on the following.

SeedRandom[0];
list2 = RandomReal[{1, 2}, {2000, 2}];

Block[{Internal`$EqualTolerance = $MachinePrecision - 2},
 DeleteDuplicates[Prepend[list2, {1., 1.}], Equal] // Length
 ]
(*  2001  *)

Block[{Internal`$EqualTolerance = $MachinePrecision - 2},
 DeleteDuplicates[Prepend[list2, {1, 1}], Equal] // Length
 ]
(*  1074  *)

Also Equal and SameQ are special cases when passed as a second argument, but not when wrapped in Function:

Block[{Internal`$EqualTolerance = $MachinePrecision - 2},
 DeleteDuplicates[Prepend[list2, {1., 1.}], Equal[##] &] // Length
 ]
(*  1074  *)
$\endgroup$
2
$\begingroup$

There are many ways to accomplish your task. Here is one.

pairs = {{1, 1.1}, {1.00001, 1.1000001}, {2, 1}};
DeleteDuplicates[pairs, Norm[#1 - #2] < .001 &]

{{1, 1.1}, {2, 1}}

$\endgroup$

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