Suppression des doublons après n occurrences

Aug 19 2020

En général DeleteDuplicates, je souhaite supprimer les doublons d'une liste, mais seulement après le nnombre de doublons.

Dites, n = 3signifie que trois doublons sont autorisés.

J'ai fait ma propre fonction:

DeleteDuplicatesN[x_, n_] := 
  x[[
    Sort[
      Flatten[#[[1 ;; Min[Length[#], n]]]& /@ 
       (Flatten[Position[x, #]]& /@ DeleteDuplicates[x])]]]]

DeleteDuplicatesN[{1, 2, 3, 2, 1, 1, 1, 2, 3, 5, 5, 5, 5, 1, 7, 4, 7, 1}, 3]
{1, 2, 3, 2, 1, 1, 2, 3, 5, 5, 5, 7, 4, 7}

Existe-t-il une meilleure méthode - plus rapide ou plus élégante?

Par exemple, en utilisant uniquement DeleteDuplicatesou DeleteDuplicatesBy?

Réponses

21 ciao Aug 19 2020 at 22:41

Je pense que vous trouverez cela plus rapidement:

dd[list_, n_] := 
  Module[{pi = Flatten[Values[PositionIndex[list][[All, ;; UpTo@n]]]]},
   list[[Sort@pi]]];

En utilisant RandomInteger[20000, 20000]comme liste de test et en autorisant 3 doublons, votre code a pris ~ 37 secondes, cela a nécessité ~ 0,03 secondes.

Comparable en vitesse, plus simple:

dd2[list_, n_] := 
  list[[Union @@ 
     GatherBy[Range@Length@list, list[[#]] &][[All, ;; UpTo@n]]]];

Pour les grandes listes qui ne sont pas des éléments grossièrement dupliqués, cela offre un avantage de performance (par exemple, avec une RandomInteger[10000000,20000000]liste de tests, une vitesse supérieure à 6X des méthodes ci-dessus):

dd=Module[{o = Ordering@#},
 o[[o]] = Join @@ Range[Tally[#[[o]]][[All, 2]]];
 Pick[#, UnitStep[#2 - o], 1]]&;