Eliminazione dei duplicati dopo n occorrenze

Aug 19 2020

In generale DeleteDuplicates, desidero eliminare i duplicati da un elenco, ma solo dopo il nnumero di duplicati.

Dire, n = 3significa che sono consentiti tre duplicati.

Ho creato la mia funzione:

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}

Esiste un metodo migliore: più veloce o più elegante?

Ad esempio, utilizzando solo DeleteDuplicateso DeleteDuplicatesBy?

Risposte

21 ciao Aug 19 2020 at 22:41

Penso che lo troverai più velocemente:

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

Usando RandomInteger[20000, 20000]come elenco di test e consentendo 3 duplicati, il codice ha impiegato ~ 37 secondi, questo ha richiesto ~ 0,03 secondi.

Paragonabile in velocità, più semplice:

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

Per elenchi di grandi dimensioni che non sono elementi grossolanamente duplicati, questo offre un vantaggio in termini di prestazioni (ad esempio, con l' RandomInteger[10000000,20000000]elenco di test, oltre 6 volte la velocità dei metodi sopra):

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