在 Mathematica 中显示重复项

Show duplicates in Mathematica

提问人:Martin Janiczek 提问时间:10/27/2009 更新时间:10/8/2021 访问量:7736

问:

在 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[] 将解决这个问题。

但是没有这样的东西(如果我确实很好地理解了那里的所有功能)。

那么,如何做到这一点呢?

列出 重复项 Wolfram-Mathematica

评论

1赞 Cascabel 10/27/2009
你要找的“除了”是补语,正如下面 Brian Schroth 的回答所用的那样。你当然可以把它用成 - 如果你使用的是 DeleteDuplicates 引入之前的 Mathematica 版本(我不认为它在 v6 中),这将很有用。Union[Complement[x,Union[x]]
0赞 Will Robertson 10/27/2009
可以肯定的是,这始终是空的。Complement[x,Union[x]]

答:

13赞 Will Robertson 10/27/2009 #1

有很多方法可以像这样进行列表提取;这是我想到的第一件事:

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

评论

0赞 Martin Janiczek 10/27/2009
呃,我不是完全理解它,但它有效,所以谢谢!我会再深入研究一下:)
1赞 dreeves 10/27/2009
您有什么理由不对 Part 使用 [[]] 表示法吗?即,Select[Tally[x], #[[2]]>1&][[All,1]]
0赞 Will Robertson 10/27/2009
(稍作思考后继续。所以,实际上,在分解的例子中,我通常很乐意写 .我想这取决于函数嵌套的线性度。我想说,这只是一种直觉,而且会随着时间的推移而改变。%[[All,1]]
0赞 dreeves 10/28/2009
我和你在一起,威尔。事实上,我经常使用而不是来避免类似 Lisp 的嵌套括号,因为我发现它们很难阅读。在其他新闻中,请查看我刚刚添加的解决方案!f@xf[x]
1赞 Brian Schroth 10/27/2009 #2

给定一个列表 A,
获取 B 中的非重复值 B = DeleteDuplicates[A] 获取 C 中的重复值 C = Complement[A,B
] 从 D 中的重复列表中获取非重复值 D
= DeleteDuplicates[C
]

因此,对于您的示例:
A = 1, 2, 2, 2, 3, 4, 4
B = 1, 2, 3, 4
C = 2, 2, 4
D = 2, 4

所以你的答案是 DeleteDuplicates[Complement[x,DeleteDuplicates[x]]],其中 x 是你的列表。我不懂 mathematica,所以这里的语法可能完美,也可能不完美。只需查看您链接到的页面上的文档即可。

评论

0赞 Martin Janiczek 10/27/2009
Complement[A,B] 返回 {},而不是 {2,2,4}。问题在于它带走了所有的 2 和 4,而不仅仅是其中之一。
0赞 Brian Schroth 10/28/2009
我担心情况可能是这样:(
7赞 dreeves 10/28/2009 #3

以下是在列表中单次完成此操作的方法:

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}DeleteDuplicatesUnion

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

威尔·罗伯逊(Will Robertson)的解决方案可能更好,因为它更直接,但我认为如果你想提高速度,这应该会赢。但是,如果你关心这一点,你就不会用 Mathematica 编程了!:)

评论

0赞 Pillsy 10/28/2009
那行不通。您需要将 的定义更改为 。此外,快速测试表明,它比 Will Robertson 的解决方案慢得多;很难击败像 这样的内置函数,它们是用 C 或 C++ 编写的,可以利用数组打包之类的东西。ii[n_]:=(i[n]=Unevaluated@Sequence[];n)Tally
0赞 dreeves 10/28/2009
你为什么说它不起作用?如上所述将函数粘贴到 Mathematica 中,可以为该示例生成正确的输出。你有没有没有的例子?
0赞 Pillsy 10/28/2009
所需的行为是返回一个列表,其中包含所有重复元素的单个副本,而不是所有重复元素的每个副本。对于您使用的示例,返回的值应为 {1, 4, 2}。
0赞 Pillsy 10/28/2009
当然,我的解决方案也行不通。我真的希望你能编辑评论,但既然你不能,我就把它添加为答案。
0赞 dreeves 10/28/2009
我也做了一些测试。我相信我的解决方案可以像书面的那样工作,但你是对的,威尔的解决方案大约是原来的两倍。我坚持这样的说法,即这个传球是单次传球,而威尔是多次传球。但你是对的,也许阵列打包或其他东西弥补了差异。
4赞 Pillsy 10/28/2009 #4

使用像 dreeves 这样的解决方案,但只返回每个重复元素的单个实例,这有点棘手。一种方法如下:

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

这与 Will Robertson (IMO superior) 解决方案生成的输出并不完全匹配,因为元素将按照可以确定它们是重复项的顺序出现在返回的列表中。我不确定它是否真的可以在一次传递中完成,我能想到的所有方法实际上都涉及至少两次传递,尽管一次可能只在重复的元素上。

7赞 Mr.Wizard 3/10/2011 #5

以下是 Tally 方法的几种更快变体。

f4使用 Carl Woll 和 Oliver Ruebenkoenig 在 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

对我来说,令人惊讶的是,相对于纯净的!f4Tally

评论

0赞 mmorris 10/10/2012
很好的答案!我确实喜欢剖析你的答案,除了部分之外,我已经弄清楚了 f4。我似乎在联机帮助中找不到它。你能给我指出正确的方向吗?["NonzeroPositions"]
0赞 Mr.Wizard 10/10/2012
@mmorris 我很高兴你喜欢我的回答。你找不到它,因为它没有记录。Oliver Ruebenkoenig 只是在 MathGroup 的回复中使用了它,没有太多解释,IIRC。还有其他 SparseArray 属性,每个属性都访问 SparseArray 内部格式的一部分。(以前我使用另一种方法来挖掘这些内部结构,但这要好得多。我在这个答案中收集了这些属性的几种用法的链接(每个带下划线的单词都是一个单独的链接)。
0赞 Mr.Wizard 10/10/2012
@mmorris我必须弄清楚每个属性为自己做了什么。先看一下它们,然后玩一玩,但如果你需要描述它们中的任何一个(“AdjacencyLists”、“Background”、“NonzeroPositions”、“NonzeroValues”、“PatternArray”、“Properties”)是什么,请询问。
0赞 Mr.Wizard 10/10/2012
@mmorris以下是我使用该属性的几个答案:(1(2)"AdjacencyLists"
0赞 mmorris 10/11/2012
谢谢。它解释了它。
2赞 Reb.Cabin 12/30/2011 #6

这是 Robertson 回答的一个版本,它使用 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# 类似于 MMA,而 C# 类似于 MMA。WhereSelectSelectMap

编辑:添加了可选的测试函数参数,默认为 .SameQ

编辑:这是一个版本,它解决了我在下面的评论,并报告了给定投影仪功能的组中的所有等效项,该投影仪功能产生一个值,如果该值相等,则列表的元素被视为等效的。这实质上是查找比给定大小更长的等价类:

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

下面是一个示例,用于检查整数对的第一个元素,如果它们的第一个元素相等,则认为两对整数相等

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

评论

0赞 Reb.Cabin 12/30/2011
刚刚意识到,使用可选的测试功能,这可能无法完成您想要的所有功能。假设您尝试在数据库中查找在某些非键属性中重复的记录,因此您编写了一个测试函数,如果两个记录在您关心的属性上没有差异,则该函数将两个记录视为相同。理货将只记录其中一条匹配记录。在该应用程序中,您真正想要的是类似于 LINQ 或 SQL 的 GroupBy,Mathematica 中的 GatherBy。
2赞 magrew 7/31/2012 #7

这个线程看起来很旧,但我不得不自己解决这个问题。

这有点粗糙,但这能做到吗?

Union[Select[Table[If[tt[[n]] == tt[[n + 1]], tt[[n]], ""], {n, Length[tt] - 1}], IntegerQ]]
2赞 Matt F. 10/8/2021 #8

另一个短暂的可能性是

Last /@ Select[Gather[x], Length[#] > 1 &]