It seems to me that all of the other answers produce incorrect results. Consider:
data = {{1., 1.}, {1.+$MachineEpsilon,-1.}, {1.+$MachineEpsilon,1.}};
Clearly the first and last points should be considered duplicates, yet none of the other answers remove this duplicate. For example:
SetOptions[EvaluationNotebook[], PrintPrecision->20];
compiledUnion[data]
Split[Sort[data]][[All, 1]]
Pick[data, deleteDuplicatesC[data], 1]
Tally[data][[All, 1]]
{{1., 1.}, {1.0000000000000002, -1.}, {1.0000000000000002, 1.}}
{{1., 1.}, {1.0000000000000002, -1.}, {1.0000000000000002, 1.}}
{{1., 1.}, {1.0000000000000002, -1.}, {1.0000000000000002, 1.}}
{{1., 1.}, {1.0000000000000002, -1.}, {1.0000000000000002, 1.}}
The other answers don't work because they basically sort and then remove adjacent duplicates. In the example above, the duplicates are not adjacent after sorting. The only suggested answer that works for this example is:
Union[data, SameTest->SameQ]
{{1., 1.}, {1.0000000000000002, -1.}}
However, as mentioned by the OP, this method has $O(n^2)$ complexity, and moreover it uses a relative tolerance instead of an absolute tolerance. For example:
data2 = {{1., 1.}, {1.*^-10, 1.*^-10}, {1.*^-10+$MachineEpsilon, 1.*^-10}};
One might wish that the second and third points are considered duplicates, yet:
Union[data2, SameTest->SameQ]
{{1.*10^-10, 1.*10^-10}, {1.0000022204460493*10^-10, 1.*10^-10}, {1., 1.}}
This is because the absolute difference is $MachineEpsilon
, while the relative difference is only $\approx 10^{-6}$, and SameQ
is based on relative difference.
Here is an approach that uses absolute differences, and is orders of magnitude faster than Union
(with the SameTest
option) for large datasets:
deleteDuplicatePoints[data_, tol_] := With[{n = Nearest[data->"Index", data, {All, tol}]},
data[[DeleteDuplicates[Min/@n]]]
]
There are two important differences between deleteDuplicatePoints
and Union
. First, deleteDuplicatePoints
uses an absolute tolerance, while Union
uses a relative tolerance. Second, deleteDuplicatePoints
uses euclidean distance, while Union
uses chessboard distance (see ChessboardDistance
).
Let's check deleteDuplicatePoints
on our two previous examples:
deleteDuplicatePoints[data, $MachineEpsilon]
deleteDuplicatePoints[data2, $MachineEpsilon]
{{1., 1.}, {1.0000000000000002, -1.}}
{{1., 1.}, {1.*10^-10, 1.*10^-10}}
In both cases, the duplicate is removed. What about larger datasets? Here
is a comparison between deleteDuplicatePoints
and Union
with a SameTest
option for largish dataset with lots of duplicates:
data = RandomReal[{1, 1 + 100 $MachineEpsilon}, {10^5, 2}];
r1 = deleteDuplicatePoints[data, $MachineEpsilon];//AbsoluteTiming
r2 = Union[data, SameTest->SameQ]; //AbsoluteTiming
{0.053882, Null}
{1.065719, Null}
And a largish datasets with few duplicates:
data = RandomReal[{1, 1 + 10^5 $MachineEpsilon}, {10^4, 2}];
r1 = deleteDuplicatePoints[data, $MachineEpsilon];//AbsoluteTiming
r2 = Union[data, SameTest->SameQ]; //AbsoluteTiming
{0.005078, Null}
{10.17658, Null}
When there are lots of duplicates, then Union
with a SameTest
option becomes competitive on timing because many results get pruned, and so less comparisons are needed. When there aren't a lot of duplicates, then the $O\left(n^2\right)$ behavior becomes clear. My tests indicate that the Nearest
approach appears to be $O(n \ln(n))$.
Finally, a speed comparison with the other answers. I will just compare deleteDuplicatePoints
with Union
(without the SameTest
option):
data = RandomReal[{1, 1 + 10^7 $MachineEpsilon}, {10^7, 2}];
r1 = deleteDuplicatePoints[data, $MachineEpsilon];//AbsoluteTiming
r2 = Union[data]; //AbsoluteTiming
{10.612259, Null}
{3.048912, Null}
So, not too much slower than Union
.
DeleteDuplicates
seems faster than single argumentUnion
here $\endgroup$Union
takes about 7 seconds andDeleteDuplicates
slightly less than 3. So, 10000000 length and 3677844 duplicates this time $\endgroup$DeleteDuplicates
is usually the fastest solution. There are some threads on mathgroup why this should be so, too... $\endgroup$nice question
badge! :) $\endgroup$