在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];
有更好的方法吗?
答案 0 :(得分:5)
正如@High Performance Mark和@Nicholas Wilson所说,我首先要通过Transpose
或Thread
将两个列表合并在一起。在这种情况下,
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
中的第二个参数是一个接受一个参数的函数,它可以与你想要使用的任何函数互换。因此,它非常有用。但是,如果您希望将数据转换为收集数据,我会查看Reap
和Sow
。
修改: Reap
和Sow
有些使用不足,相当强大。它们使用起来有点混乱,但我怀疑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
。