【问题标题】:Show duplicates in Mathematica在 Mathematica 中显示重复项
【发布时间】:2010-12-10 12:09:12
【问题描述】:

在 Mathematica 我有一个列表:

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

我将如何制作包含重复项的列表?喜欢:

{3,5}

我一直在查看Lists as Sets,如果列表中有类似的 except[],那么我可以这样做:

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

(当然,如果 x 有两个以上的重复项 - 例如 {1,2,2,2,3,4,4},则输出将是 {2, 2,4},但额外的 Union[] 可以解决这个问题。)

但没有类似的东西(如果我确实了解那里的所有功能)。

那么,该怎么做呢?

【问题讨论】:

  • 您正在寻找的“例外”是 Complement,如下面的 Brian Schroth 的回答中所使用的。您当然可以将其用作 Union[Complement[x,Union[x]] - 如果您使用的是在引入 DeleteDuplicates 之前的 Mathematica 版本(我认为它不在 v6 中),这将很有用。
  • 很确定Complement[x,Union[x]] 始终是空集。

标签: list duplicates wolfram-mathematica


【解决方案1】:

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

部分[选择[Tally@x, 部分[#, 2] > 1 &], 全部, 1]

或者,更易读的片段:

理货@x 选择[%, 部分[#, 2] > 1 &] 部分[%, 全部, 1]

分别给出,

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

或许你可以想一个更高效(在时间或代码空间上)的方式:)

顺便说一句,如果列表未排序,那么您需要先在其上运行Sort,然后才能使用。

【讨论】:

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

下面是一种在列表中一次性完成的方法:

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]

Will Robertson 的解决方案可能会更好,因为它更简单,但我认为如果你想获得更快的速度,这应该会赢。但是,如果您关心这一点,您就不会在 Mathematica 中编程了! :)

【讨论】:

  • 那行不通。您需要将i 的定义更改为i[n_]:=(i[n]=Unevaluated@Sequence[];n)。此外,快速测试表明它比 Will Robertson 的解决方案慢很多。很难击败像 Tally 这样的内置函数,它们是用 C 或 C++ 编写的,可以利用数组打包等功能。
  • 为什么说它不起作用?完全按照上面的方法将函数粘贴到 Mathematica 中会产生该示例的正确输出。你有没有的例子吗?
  • 期望的行为是返回一个包含所有重复元素的单个副本的列表,而不是所有重复元素的每个副本。对于您使用的示例,返回值应为 {1, 4, 2}。
  • 当然,我的解决方案也不能正常工作。我真的希望你能编辑 cmets,但既然你不能,我会添加它作为答案。
  • 我也做了一些测试。我相信我的解决方案可以按照书面说明运行,但您说得对,Will 的解决方案速度大约是原来的两倍。我坚持这样的说法,即这个人只通过一次,而威尔的人通过多次。但你是对的,也许数组包装或其他东西弥补了差异。
【解决方案3】:

这里是 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

令我惊讶的是,f4 相对于纯 Tally 几乎没有开销!

【讨论】:

  • 很好的答案!我很喜欢剖析你的答案,除了["NonzeroPositions"] 部分外,我已经弄清楚了 f4。我似乎无法在联机帮助中找到它。你能指出我正确的方向吗?
  • @mmorris 很高兴您喜欢我的回答。你找不到它,因为它没有记录。 Oliver Ruebenkoenig 只是简单地在 MathGroup 的回复中使用了它,IIRC 没有太多解释。还有其他 SparseArray 属性,每个属性都访问 SparseArray 内部格式的一部分。 (以前我使用另一种方法来挖掘这些内部结构,但现在要好得多。)我在this answer 中收集了这些属性的几种用途的链接(每个带下划线的单词都是一个单独的链接)。
  • @mmorris 我必须弄清楚每个属性为自己做了什么。首先看一下它们,然后玩一下,但是如果您需要描述它们中的任何一个(“AdjacencyLists”、“Background”、“NonzeroPositions”、“NonzeroValues”、“PatternArray”、“Properties”)是,问。
  • @mmorris 这里有几个答案,我使用了"AdjacencyLists" 属性:(1)(2)
  • 谢谢。它解释了它。
【解决方案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 高级)解决方案产生的输出不完全匹配,因为元素将按照可以确定它们是重复的顺序出现在返回的列表中。我不确定它是否真的可以一次完成,我能想到的所有方式实际上都涉及至少两次,尽管一次可能只在重复的元素上。

【讨论】:

    【解决方案5】:

    这是罗伯逊答案的一个版本,它使用 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]] &]
    

    【讨论】:

    • 刚刚意识到,使用可选的测试功能,这可能无法满足您的所有需求。假设您试图在数据库中查找在某些非关键属性中重复的记录,因此您编写了一个测试函数,如果两条记录在您关心的属性上没有区别,则它们认为它们是相同的。 Tally 将只记录一个匹配的记录。您在该应用程序中真正想要的是 LINQ 或 SQL 的 GroupBy,Mathematica 中的 GatherBy。
    【解决方案6】:

    这个帖子似乎很旧,但我必须自己解决这个问题。

    这有点粗鲁,但这样做可以吗?

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

    【讨论】:

      【解决方案7】:

      给定一个列表 A,
      获取 B 中的非重复值
      B = DeleteDuplicates[A]
      获取 C 中的重复值
      C = 补 [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,所以这里的语法可能完美也可能不完美。只需查看您链接到的页面上的文档即可。

      【讨论】:

      • Complement[A,B] 返回 {},而不是 {2,2,4}。问题是它带走了所有的 2 和 4,而不仅仅是其中一个。
      • 我担心可能是这种情况:(
      【解决方案8】:

      另一个简短的可能性是

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

      【讨论】:

        猜你喜欢
        • 2021-02-22
        • 1970-01-01
        • 1970-01-01
        • 2019-06-29
        • 1970-01-01
        • 1970-01-01
        • 2019-02-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多