Mathematica:列表上的条件操作

时间:2011-05-25 14:09:03

标签: loops wolfram-mathematica conditional

我想在一列中对“行”进行平均。这是在另一列中具有相同值的行。

例如:

e= {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, 
   {69, 7, 30, 38, 16, 70, 97, 50, 97, 31, 81, 96, 60, 52, 35, 6, 
    24, 65, 76, 100}}

enter image description here

我想平均第二列中所有在第一列中具有相同值的值。

所以这里:Col 1的平均值= 1& Col 1 = 2

然后使用此操作的结果创建第三列。因此,对于前10行和下10行,该列中的值应该相同。

非常感谢您提供的任何帮助!

LA

输出理想格式:

enter image description here

5 个答案:

答案 0 :(得分:5)

有趣的问题。这是我想到的第一件事:

e[[All, {1}]] /. Reap[Sow[#2, #] & @@@ e, _, # -> Mean@#2 &][[2]];

ArrayFlatten[{{e, %}}] // TableForm

要进行四舍五入,您只需在Round@之前的代码中添加MeanRound@Mean@#2

这是一种稍微快一点的方法,但实际上我更喜欢上面的Sow / Reap方法:

#[[1, 1]] -> Round@Mean@#[[All, 2]] & /@ GatherBy[e, First];

ArrayFlatten[{{e, e[[All, {1}]] /. %}}] // TableForm

如果第一列中有许多不同的元素,则可以在替换(/.)完成之前将Dispatch应用于生成的规则列表,从而加快上述任一解决方案的速度。此命令告诉Mathematica为规则列表构建和使用优化的内部格式。

这是一个较慢的变体,但我还是喜欢分享它:

Module[{q},
  Reap[{#, Sow[#2,#], q@#} & @@@ e, _, (q@# = Mean@#2) &][[1]]
]

另外,一般提示,您可以替换:

带有Table[RandomInteger[{1, 100}], {20}]

RandomInteger[{1, 100}, 20]

Join[{c}, {d}] // TransposeTranspose[{c, d}]

答案 1 :(得分:4)

到底是什么,我会加入聚会。这是我的版本:

Flatten/@Flatten[Thread/@Transpose@{#,Mean/@#[[All,All,2]]}&@GatherBy[e,First],1]

我猜应该足够快。

修改

为了回应@ Mr.Wizard的批评(我的第一个解决方案是重新排序列表),并探讨问题的高性能角落,这里有两个替代解决方案:

getMeans[e_] := 
Module[{temp = ConstantArray[0, Max[#[[All, 1, 1]]]]},
  temp[[#[[All, 1, 1]]]] = Mean /@ #[[All, All, 2]];
  List /@ temp[[e[[All, 1]]]]] &[GatherBy[e, First]];

getMeansSparse[e_] := 
Module[{temp = SparseArray[{Max[#[[All, 1, 1]]] -> 0}]},
  temp[[#[[All, 1, 1]]]] = Mean /@ #[[All, All, 2]];
  List /@ Normal@temp[[e[[All, 1]]]]] &[GatherBy[e, First]];

第一个是速度最快的交易记忆,可以在键全部为整数时应用,你的最大“键”值(在你的例子中为2)不是太大。第二种解决方案没有后一种限制,但速度较慢。这是一个很大的对列表:

In[303]:= 
tst = RandomSample[#, Length[#]] &@
   Flatten[Map[Thread[{#, RandomInteger[{1, 100}, 300]}] &, 
      RandomSample[Range[1000], 500]], 1];

In[310]:= Length[tst]

Out[310]= 150000

In[311]:= tst[[;; 10]]

Out[311]= {{947, 52}, {597, 81}, {508, 20}, {891, 81}, {414, 47}, 
{849, 45}, {659, 69}, {841, 29}, {700, 98}, {858, 35}}

这里的密钥可以是1到1000,其中500个,每个密钥有300个随机数。现在,一些基准:

In[314]:= (res0 = getMeans[tst]); // Timing

Out[314]= {0.109, Null}

In[317]:= (res1 = getMeansSparse[tst]); // Timing

Out[317]= {0.219, Null}

In[318]:= (res2 =  tst[[All, {1}]] /. 
 Reap[Sow[#2, #] & @@@ tst, _, # -> Mean@#2 &][[2]]); // Timing

Out[318]= {5.687, Null}

In[319]:= (res3 = tst[[All, {1}]] /. 
 Dispatch[
  Reap[Sow[#2, #] & @@@ tst, _, # -> Mean@#2 &][[2]]]); // Timing

Out[319]= {0.391, Null}

In[320]:= res0 === res1 === res2 === res3

Out[320]= True

我们可以看到getMeans是最快的,getMeansSparse是第二快的,@ Mr.Wizard的解决方案有点慢,但只有当我们使用Dispatch时,否则它会慢得多。我和@ Mr.Wizard的解决方案(使用Dispatch)在精神上是相似的,速度差异是由于(稀疏)数组索引比散列查找更有效。当然,只有当你的清单真的很大时,这一切才有意义。

编辑2

这是getMeans的一个版本,它使用带有C目标的Compile并返回数值(而不是有理数)。它比getMeans快两倍,是我解决方案中最快的。

getMeansComp = 
 Compile[{{e, _Integer, 2}},
   Module[{keys = e[[All, 1]], values = e[[All, 2]], sums = {0.} ,
      lengths = {0}, , i = 1, means = {0.} , max = 0, key = -1 , 
      len = Length[e]},
    max = Max[keys];
    sums = Table[0., {max}];
    lengths = Table[0, {max}];
    means = sums;
    Do[key = keys[[i]];
      sums[[key]] += values[[i]];
      lengths[[key]]++, {i, len}];
    means = sums/(lengths + (1 - Unitize[lengths]));
    means[[keys]]], CompilationTarget -> "C", RuntimeOptions -> "Speed"]

getMeansC[e_] := List /@ getMeansComp[e];

代码1 - Unitize[lengths]可防止未使用的密钥除以零。我们需要单独的子列表中的每个数字,因此我们应该直接调用getMeansC,而不是getMeansComp。以下是一些测量结果:

In[180]:= (res1 = getMeans[tst]); // Timing

Out[180]= {0.11, Null}

In[181]:= (res2 = getMeansC[tst]); // Timing

Out[181]= {0.062, Null}

In[182]:= N@res1 == res2

Out[182]= True

这可能被认为是一种高度优化的数值解决方案。事实上,@ Mr.Wizard的完全通用,简洁和美观的解决方案只有大约6-8倍的速度,对于后一种简洁的解决方案说得非常好,所以,除非你想要挤出每微秒,我会坚持@ Mr.Wizard的一个(Dispatch)。但重要的是要知道如何优化代码,以及优化代码的程度(您可以期待什么)。

答案 2 :(得分:3)

一种天真的方法可能是:

Table[
  Join[ i, {Select[Mean /@ SplitBy[e, First], First@# == First@i &][[1, 2]]}]
, {i, e}] // TableForm

(*
1   59  297/5
1   72  297/5
1   90  297/5
1   63  297/5
1   77  297/5
1   98  297/5
1   3   297/5
1   99  297/5
1   28  297/5
1   5   297/5
2   87  127/2
2   80  127/2
2   29  127/2
2   70  127/2
2   83  127/2
2   75  127/2
2   68  127/2
2   65  127/2
2   1   127/2
2   77  127/2
*)

您还可以使用例如:

创建原始列表
e = Array[{Ceiling[#/10], RandomInteger[{1, 100}]} &, {20}]

修改

回答@ Mr.先生的评论

如果列表未按其第一个元素排序,您可以执行以下操作:

Table[Join[
  i, {Select[
     Mean /@ SplitBy[SortBy[e, First], First], First@# == First@i &][[1,2]]}],
{i, e}] //TableForm

但是在你的例子中没有必要

答案 3 :(得分:2)

为什么不坚持下去?

我认为这是最直接/易于阅读的答案,但不一定是最快的。但是,在Mathematica中你能想到多少种类似问题的方法真是太神奇了。

先生。正如其他人指出的那样,巫师显然非常酷。

@Nasser,你的解决方案并没有推广到n-class,尽管很容易修改它。

meanbygroup[table_] := Join @@ Table[
   Module[
     {sublistmean},
     sublistmean = Mean[sublist[[All, 2]]];
     Table[Append[item, sublistmean], {item, sublist}]
   ]
   , {sublist, GatherBy[table, #[[1]] &]}
       ]
(* On this dataset: *) 
meanbygroup[e] 

答案 4 :(得分:1)

哇,这里的答案非常先进,看起来很酷,需要更多的时间来学习它们。

这是我的答案,我仍然是矩阵/矢量/ Matlab'恢复和过渡的人,所以我的解决方案不像这里的专家解决方案那样功能,我将数据视为矩阵和向量(对我来说比看起来更容易在他们作为列表等列表......)所以这里是


sizeOfList=10; (*given from the problem, along with e vector*)
m1 = Mean[e[[1;;sizeOfList,2]]];
m2 = Mean[e[[sizeOfList+1;;2 sizeOfList,2]]];
r  = {Flatten[{a,b}], d , Flatten[{Table[m1,{sizeOfList}],Table[m2,{sizeOfList}]}]} //Transpose;

MatrixForm[r]

显然不是功能性的解决方案。

好的,我现在就去隐藏功能程序员:)

- 纳赛尔