List 基于Mathematica中的另一个列表值拆分列表

List 基于Mathematica中的另一个列表值拆分列表,list,wolfram-mathematica,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}; 根据ClusterIndications值将点拆分为两个单独列表的最简单方法

在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};
根据ClusterIndications值将点拆分为两个单独列表的最简单方法是什么

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

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

有更好的方法吗?

我不知道“更好”,但函数式语言中更常见的方法不是添加索引来标记每个元素(您的MapIndex),而是沿着每个列表运行:

Map[#1[[2]] &, 
 Sort[GatherBy[
   Thread[ {#1, #2} &[clusterIndices, points]],
   #1[[1]] &], #1[[1]][[1]] < #2[[1]][[1]] &], {2}]
Map[#1[[2]]和,
分类[收集][
线程[{1,{2}&[ClusterIndications,points]],
#1[[1]] &], #1[[1]][[1]] < #2[[1]][[1]] &], {2}]
大多数在Lisp/ML/etc中长大的人会立即编写
线程
函数,这是实现这些语言的zip思想的方法

我添加了
排序
,因为如果
clusterIndicates={2[…,2],1,…}
,您的实现可能会遇到问题。另一方面,我仍然需要添加一行来解决这样一个问题,即如果clusterIndices有3但没有2,那么输出索引将是错误的。然而,从你的片段中不清楚你打算如何取回东西

我想如果你有一个爱好项目,比如用Haskell这样的语言构建一个简单的CAS,语法比Mathematica更适合函数式列表处理,你会发现列表处理会容易得多。

这个怎么样

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

如果我想到更简单的东西,我会把它添加到帖子中

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

我的第一步是执行

Transpose[{clusterIndices, points}]

我的下一步将取决于你想用它做什么<我想到了代码>选择。

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

In[1]:= Transpose[{clusterIndices, points}]==Thread[{clusterIndices, points}]
Out[1]:= True
在某一点上,我看了看哪个更快,我认为
线程
稍微快一点。但是,只有当您使用很长的列表时,它才真正重要

@高性能标记在建议
选择
方面非常重要。但是,它只允许您一次拉出一个集群。选择群集1的代码如下所示:

Select[Transpose[{clusterIndices, points}], #[[1]]==1& ][[All, All, 2]]
SelectEquivalents[x_List,f_:Identity, g_:Identity, h_:(#2&)]:=
   Reap[Sow[g[#],{f[#]}]&/@x, _, h][[2]];
由于您似乎希望生成所有集群,我建议您执行以下操作:

GatherBy[Transpose[{clusterIndices, points}], #[[1]]& ][[All, All, 2]]
它的优点是只包含一行,唯一棘手的部分是选择结果列表中正确的
部分。确定需要多少个
所有
术语的诀窍是注意

Transpose[{clusterIndices, points}][[All,2]]
需要将点从转置列表中恢复。但是,“集群”列表有一个附加级别,因此第二个
All

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

编辑:
收获
播种
使用不足,功能相当强大。它们的使用有些混乱,但我怀疑
GatherBy
是在内部使用它们实现的。比如说,

Reap[ Sow[#[[2]], #[[1]] ]& /@ Transpose[{clusterIndices, points}], _, #2& ]
执行与我以前的代码相同的操作,而无需从点中剥离索引。本质上,
Sow
用索引标记每个点,然后Reap收集所有标记(
\uu
用于第二个参数)并仅输出点。就我个人而言,我使用它而不是GathereBy,并将其编码到一个我加载的函数中,如下所示:

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

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

使用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]]]

有意思,你怎么会想到这个?不知道。我经常使用这种东西(定位列表来提取我想要的东西)。这与其他人的建议非常不同。Thank我怀疑排序(隐含在
联合中)和列表遍历的次数(通过
定位
部分
)会导致效率相当低。然而,对于一个简短的列表,它肯定是一个有趣的使用
位置
。您将两个列表组合的部分可以简化为
线程[{clusterIndicates,points}]
,因为纯函数部分(
{1,{2}
)在这种情况下只是浪费了按键。+1这是一个比@Mark Fisher更好的答案,还有其他几个。我也会加入@Michael Pilat关于SplitBy的建议。非常详细的解释。谢谢@麦克斯:不客气。
eaw
Sow
的另一个优点是,您可以
Sow
多个标记,即,如果您的数据适合多个类别,您可以将数据分组。要执行此操作,请在
SelectEquivalents
中删除
f
周围的花括号,并让
f
返回数据所属标记的列表。对于否决此操作的人,为什么?让对方知道答案是什么,这是一种常见的礼貌。有人会承认这一点吗?在7.0中添加了很多列表实用程序,比如这一个,由于我对
eaw
Sow
的依赖,我从未使用过。但是,这是一个很好的方法,我必须记住。起初我试着自己使用Split,但这不起作用。尝试执行SplitBy[{1,1,1,2,2,1,1,2,2}],您将得到{1,1,1},{2,2},{1,1},{2,2}和len