根据Mathematica中的其他列表值拆分列表

时间:2010-04-22 23:16:25

标签: list wolfram-mathematica

在Mathematica中,我有一个点坐标列表

size = 50;
points = Table[{RandomInteger[{0, size}], RandomInteger[{0, size}]}, {i, 1, n}];

以及这些点属于

的集群索引列表
clusterIndices = {1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1};

根据clusterIndices值将点拆分为两个单独列表的最简单方法是什么?

编辑: 我提出的解决方案:

pointIndices =
  Map[#[[2]] &,
    GatherBy[MapIndexed[{#1, #2[[1]]} &, clusterIndices], First],
    {2}];
pointsByCluster = Map[Part[points, #] &, pointIndices];

有更好的方法吗?

6 个答案:

答案 0 :(得分:5)

正如@High Performance Mark和@Nicholas Wilson所说,我首先要通过TransposeThread将两个列表合并在一起。在这种情况下,

In[1]:= Transpose[{clusterIndices, points}]==Thread[{clusterIndices, points}]
Out[1]:= True

有一次,我看着哪个更快,我认为Thread稍快一点。但是,只有在使用很长的列表时才真正重要。

@High Performance Mark在建议Select时提出了一个很好的观点。但是,它只允许您一次拉出一个群集。选择集群1的代码如下:

Select[Transpose[{clusterIndices, points}], #[[1]]==1& ][[All, All, 2]]

由于您似乎想要生成所有群集,我建议您执行以下操作:

GatherBy[Transpose[{clusterIndices, points}], #[[1]]& ][[All, All, 2]]

具有成为单行的优势,唯一棘手的部分是选择结果列表的正确Part。确定需要多少All个术语的诀窍是注意

Transpose[{clusterIndices, points}][[All,2]]

需要从转置列表中取回点数。但是,“聚集”列表有一个额外的级别,因此第二个All

应该注意GatherBy中的第二个参数是一个接受一个参数的函数,它可以与你想要使用的任何函数互换。因此,它非常有用。但是,如果您希望将数据转换为收集数据,我会查看ReapSow

修改: ReapSow有些使用不足,相当强大。它们使用起来有点混乱,但我怀疑GatherBy是在内部使用它们实现的。例如,

Reap[ Sow[#[[2]], #[[1]] ]& /@ Transpose[{clusterIndices, points}], _, #2& ]

与我之前的代码做同样的事情,没有从点上剥离索引的麻烦。基本上,Sow使用其索引标记每个点,然后Reap收集所有标记(第2个参数的_)并仅输出点。就个人而言,我使用它代替GatherBy,并且我将它编码为我加载的函数,如下所示:

SelectEquivalents[x_List,f_:Identity, g_:Identity, h_:(#2&)]:=
   Reap[Sow[g[#],{f[#]}]&/@x, _, h][[2]];

注意:此代码是5.x中帮助文件中的修改形式。但是,6.0和7.0帮助文件删除了许多有用的示例,这就是其中之一。

答案 1 :(得分:5)

这是使用7.0版中新的SplitBy函数执行此操作的简洁方法,该函数应该非常快:

SplitBy[Transpose[{points, clusterIndices}], Last][[All, All, 1]]

如果您不使用7.0,则可以将其实现为:

Split[Transpose[{points, clusterIndices}], Last[#]==Last[#2]& ][[All, All, 1]]

更新

很抱歉,我没有看到你只想要两个组,我认为这些组是聚类,而不是分裂。这是一些代码:

FindClusters[Thread[Rule[clusterIndices, points]]]

答案 2 :(得分:4)

这个怎么样?

points[[
    Flatten[Position[clusterIndices, #]]
    ]] & /@
 Union[clusterIndices]

答案 3 :(得分:1)

我不知道'更好',但是在函数式语言中更常用的方法不是添加索引来标记每个元素(你的MapIndexed),而是只是沿着每个列表运行:

Map[#1[[2]] &, 
 Sort[GatherBy[
   Thread[ {#1, #2} &[clusterIndices, points]],
   #1[[1]] &], #1[[1]][[1]] < #2[[1]][[1]] &], {2}]

在Lisp / ML / etc中长大的大多数人会立即编写Thread函数来实现这些语言的zip创意。

我添加了Sort,因为如果clusterIndices = {2[...,2],1,...}看起来您的实施会遇到麻烦。另一方面,我仍然需要添加一行来解决问题,如果clusterIndices有3但没有2,输出索引将是错误的。从你的片段中不清楚你打算如何检索东西。

我认为如果你使用像Haskell这样的语言构建一个简单CAS的业余爱好项目,你会发现列表处理变得更加容易,其中语法比Mathematica更适合功能列表处理。

答案 4 :(得分:1)

如果我想到一些更简单的东西,我会添加到帖子中。

Map[#[[1]] &, GatherBy[Thread[{points, clusterIndices}], #[[2]] &], {2}]

答案 5 :(得分:1)

我的第一步是执行

Transpose[{clusterIndices, points}]

我的下一步将取决于你想要做什么;想到Select