Показать дубликаты в Mathematica - PullRequest
12 голосов
/ 27 октября 2009

В 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}, но дополнительный Union [] решит это.)

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

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

Ответы [ 7 ]

11 голосов
/ 27 октября 2009

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

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, прежде чем это сработает.

6 голосов
/ 27 октября 2009

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

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! :)

5 голосов
/ 10 марта 2011

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

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!

3 голосов
/ 27 октября 2009

Использование решения, такого как 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), потому что элементы появятся в возвращенном списке в том порядке, в котором можно будет определить, что они являются дубликатами. Я не уверен, что это действительно можно сделать за один проход, все способы, которые я могу придумать, включают, по крайней мере, два прохода, хотя один может быть только над дублированными элементами.

1 голос
/ 30 июля 2012

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

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

Union[Select[Table[If[tt[[n]] == tt[[n + 1]], tt[[n]], ""], {n, Length[tt] - 1}], IntegerQ]]
1 голос
/ 29 декабря 2011

Вот версия ответа Робертсона, которая использует 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 # Where походит на MMA Select, а C # Select походит на MMA Map.

РЕДАКТИРОВАТЬ: добавлен необязательный аргумент тестовой функции, по умолчанию SameQ.

РЕДАКТИРОВАТЬ: вот версия, которая касается моего комментария ниже и сообщает все эквиваленты в группе, учитывая функцию проектора, которая выдает значение, такое, что элементы списка считаются эквивалентными, если значение равно По сути, это находит классы эквивалентности длиннее заданного размера:

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

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

reportDuplicateClusters[RandomInteger[10, {10, 2}], #[[1]] &]
0 голосов
/ 27 октября 2009

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

Итак, для вашего примера:
A = 1, 2, 2, 2, 3, 4, 4
B = 1, 2, 3, 4
С = 2, 2, 4
D = 2,4

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

...