Показать дубликаты в Mathematica

в Mathematica у меня есть список:

x = {1,2,3,3,4,5,5,6}

как я сделаю список с дубликатами? Например:

{3,5}

Я смотрел на списки наборов, если есть что-то вроде Except[] для списков, поэтому я мог бы сделать:

unique = Union[x]
duplicates = MyExcept[x,unique]

(конечно, если x будет иметь более двух дубликатов-скажем, {1,2,2,2,3,4,4}, там выход будет {2,2,4}, но дополнительное объединение[] решит это.)

но там не было что-нибудь в этом роде (если бы я хорошо понимал все функции).

Итак, как это сделать?

7 ответов


много способов сделать извлечение списка, как это; вот первое, что пришло мне на ум:

Part[Select[Tally@x, Part[#, 2] > 1 &], All, 1]

или, более читабельно в частях:

Tally@x
Select[%, Part[#, 2] > 1 &]
Part[%, All, 1]

что дает, соответственно,

{{1, 1}, {2, 1}, {3, 2}, {4, 1}, {5, 2}, {6, 1}}
{{3, 2}, {5, 2}}
{3, 5}

возможно, вы можете придумать более эффективный (во времени или пространстве кода) способ :)

кстати, если список несортирован, вам нужно запустить Sort на нем сначала перед этим будет работать.


вот способ сделать это в один проход по списку:

collectDups[l_] := Block[{i}, i[n_]:= (i[n] = n; Unevaluated@Sequence[]); i /@ l]

например:

collectDups[{1, 1, 6, 1, 3, 4, 4, 5, 4, 4, 2, 2}] --> {1, 1, 4, 4, 4, 2}

если вы хотите список уникальных дубликатов -- {1, 4, 2} -- затем оберните выше в DeleteDuplicates, что является еще одним одиночным проходом через список (Union менее эффективен, поскольку он также сортирует результат).

collectDups[l_] := 
  DeleteDuplicates@Block[{i}, i[n_]:= (i[n] = n; Unevaluated@Sequence[]); i /@ l]

решение Уилла Робертсона, вероятно, лучше только потому, что оно более простое, но я думаю, что если вы хотите получить больше скорости, это должен победить. Но если бы вы заботились об этом, вы бы не программировали в Mathematica! :)


вот несколько более быстрых вариантов метода подсчета.

f4 использует "трюки", данные Карлом Воллом и Оливером Рубенкоенигом в MathGroup.

f2 = Tally@# /. {{_, 1} :> Sequence[], {a_, _} :> a} &;

f3 = Pick[#, Unitize[#2 - 1], 1] & @@ Transpose@Tally@# &;

f4 = # ~Extract~ SparseArray[Unitize[#2 - 1]]["NonzeroPositions"] & @@ Transpose@Tally@# &;

сравнение скорости (f1 Для справки)

a = RandomInteger[100000, 25000];

f1 = Part[Select[Tally@#, Part[#, 2] > 1 &], All, 1] &;

First@Timing@Do[#@a, {50}] & /@ {f1, f2, f3, f4, Tally}

SameQ @@ (#@a &) /@ {f1, f2, f3, f4}

Out[]= {3.188, 1.296, 0.719, 0.375, 0.36}

Out[]= True

это удивительно для меня, что f4 почти не имеет накладных расходов относительно чистого Tally!


использование решения, такого как dreeves, но только возврат одного экземпляра каждого дублированного элемента, немного сложнее. Один из способов сделать это заключается в следующем:

collectDups1[l_] :=
  Module[{i, j},
    i[n_] := (i[n] := j[n]; Unevaluated@Sequence[]);
    j[n_] := (j[n] = Unevaluated@Sequence[]; n);
    i /@ l];

Это не точно соответствует выходу, полученному решением Уилла Робертсона (IMO superior), потому что элементы будут отображаться в возвращаемом списке в том порядке, в котором можно определить, что они дублируются. Я не уверен, что это действительно можно сделать за один проход, все способы, которые я могу придумать, включают, фактически, по крайней мере два прохода, хотя один может быть только над дублированными элементами.


вот версия ответа Робертсона, которая использует 100% "постфиксную нотацию" для вызовов функций.

identifyDuplicates[list_List, test_:SameQ] :=
 list //
    Tally[#, test] & //
   Select[#, #[[2]] > 1 &] & //
  Map[#[[1]] &, #] &

Mathematica // аналогично точке для вызовов методов на других языках. Например, если бы это было написано в стиле C# / LINQ, это было бы похоже на

list.Tally(test).Where(x => x[2] > 1).Select(x => x[1])

обратите внимание, что C# ' S Where это как у ММА Select и C#'ы Select это как у ММА Map.

EDIT: добавлен необязательный аргумент тестовой функции, по умолчанию SameQ.

EDIT: вот версия, которая обращается к моему комментарию ниже и сообщает обо всех эквивалентах в группе, заданной функцией проектора, которая создает значение, такое, что элементы списка считаются эквивалентными, если значение равно. Это по существу находит классы эквивалентности длиннее заданного размера:

reportDuplicateClusters[list_List, projector_: (# &), 
  minimumClusterSize_: 2] :=
 GatherBy[list, projector] //
  Select[#, Length@# >= minimumClusterSize &] &

вот пример, который проверяет пары целых чисел на их первых элементах, считая две пары эквивалентными, если их первые элементы равный

reportDuplicateClusters[RandomInteger[10, {10, 2}], #[[1]] &]

этот поток кажется старым, но я должен был решить это сам.

Это довольно грубо, но делает ли это это?

Union[Select[Table[If[tt[[n]] == tt[[n + 1]], tt[[n]], ""], {n, Length[tt] - 1}], IntegerQ]]

учитывая список A,
вам не повторяющиеся значения в B
B = DeleteDuplicates[A]
получить повторяющиеся значения в C
C = Дополнение[A, B]
получите не повторяющиеся значения из списка дубликатов в D
D = DeleteDuplicates[C]

вот пример:
А = 1, 2, 2, 2, 3, 4, 4
B = 1, 2, 3, 4
C = 2, 2, 4
D = 2, 4

таким образом, ваш ответ будет DeleteDuplicates[дополнение[x, DeleteDuplicates[x]]] где x это твой список. Я не знаю mathematica, поэтому синтаксис может быть или не быть идеальным здесь. Просто врачи на странице вы связаны.