Mathematica - 将列表转换为元素位置的分区

时间:2012-06-16 21:36:02

标签: wolfram-mathematica

我想创建一个函数,它接受一个整数列表,并根据列表的元素返回列表索引的分区。

例如:

{1,3,3,7} --> { {1}, {2,3}, {4}}
{3,1,3} --> {{1,3}, {2}}

我可以想到这样做的混乱方法,但在Mathematica中有一种自然的方法吗?

3 个答案:

答案 0 :(得分:2)

我会用:

indicies[x_] := Reap[MapIndexed[Sow[#2, #] &, x]][[2, All, All, 1]]

这比在具有许多独特元素的长列表中重复使用Position更快 。例如:

list = RandomInteger[9999, 10000];

Timing[
  result1 =
    Function[x, (Flatten@Position[x, #] &) /@ DeleteDuplicates[x]]@list;
]
{3.463, Null}
Timing[
  result2 = indicies @ list;
]
{0.031, Null}
result1 === result2
True

TomD建议使用较新的GatherBy代替SowReap。在没有重复的长列表中,这甚至更快。

indicies2[x_] := GatherBy[List ~MapIndexed~ x, First][[All, All, 2, 1]]

list2 = RandomInteger[99999, 100000];

Do[indicies @ list2, {10}] // Timing
Do[indicies2 @ list2, {10}] // Timing
{5.523, Null}

{2.823, Null}

在重复次数更多的列表中速度更相似:

list3 = RandomInteger[99, 100000];

Do[indicies @ list3, {10}] // Timing
Do[indicies2 @ list3, {10}] // Timing
{1.716, Null}

{1.607, Null}

如果选择纯粹的速度,必须认识到MapIndexed未针对打包数组进行优化,因此RangeTranspose在这种情况下会更快:

indicies3[x_] := GatherBy[{x, Range@Length@x}\[Transpose], First][[All, All, 2]]

Do[indicies3 @ list2, {10}] // Timing
{1.981, Null}
Do[indicies3@list3, {10}] // Timing  (* big difference here *)
{0.125, Null}

答案 1 :(得分:1)

一种可能性:

Function[x, (Flatten@Position[x, #] &) /@ DeleteDuplicates[x]]@{1, 3, 
  3, 7}

(*  {{1}, {2, 3}, {4}  *)

另一个例子:

lst = {1, 3, 3, 3, 7, 4, 3};
Function[x, (Flatten@Position[x, #] &) /@ DeleteDuplicates[x]]@lst

,并提供:

(*{{1}, {2, 3, 4, 7}, {5}, {6}}*)

答案 2 :(得分:0)

又一种方法:

f[x_] := Flatten[x~Position~First@#] & /@ Gather@x