Удаление дубликатов после n вхождений
В качестве обобщения DeleteDuplicates
я хочу удалить дубликаты из списка, но только после n
количества дубликатов.
Скажем, n = 3
означает, что разрешены три дубликата.
Я сделал свою функцию:
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}
Есть ли способ лучше - быстрее или элегантнее?
Например, используя только DeleteDuplicates
или DeleteDuplicatesBy
?
Ответы
Думаю, вы быстрее найдете это:
dd[list_, n_] :=
Module[{pi = Flatten[Values[PositionIndex[list][[All, ;; UpTo@n]]]]},
list[[Sort@pi]]];
При использовании RandomInteger[20000, 20000]
в качестве тестового списка и разрешении 3 дубликатов ваш код занял ~ 37 секунд, на это ~ 0,03 секунды.
Сравним по скорости, проще:
dd2[list_, n_] :=
list[[Union @@
GatherBy[Range@Length@list, list[[#]] &][[All, ;; UpTo@n]]]];
Для больших списков, которые не являются сильно дублированными элементами, это дает преимущество в производительности (например, с RandomInteger[10000000,20000000]
тестовым списком, скорость более чем в 6 раз выше, чем у вышеуказанных методов):
dd=Module[{o = Ordering@#},
o[[o]] = Join @@ Range[Tally[#[[o]]][[All, 2]]];
Pick[#, UnitStep[#2 - o], 1]]&;