【问题标题】:Mathematica : Conditional Operations on ListsMathematica : 列表上的条件运算
【发布时间】:2011-09-01 19:36:44
【问题描述】:

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

例如:

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}}

我想对第二列中与第一列具有相同值的所有值进行平均。

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

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

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

洛杉矶

输出理想格式:

【问题讨论】:

  • 我相信你的代码有错误:d[[10 ;; 20]] 应该是d[[11 ;; 20]]
  • 谢谢!对不起,我必须做错事,但你的或贝利撒留命题都没有产生与上述相同的输出:(
  • 只是为了确定,您不会在试验之间重新生成随机数,是吗?
  • @Mr.向导:抱歉没有看到最后一条评论。我复制了我需要的所有 cmets 和代码,您可以删除任何您认为不必要的内容。再次感谢您!

标签: loops wolfram-mathematica conditional


【解决方案1】:

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

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

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

要四舍五入,您只需在上面的代码中的Mean 之前添加Round@Round@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}]

【讨论】:

  • @500,如果您需要澄清您的问题,请编辑它,而不是在 cmets 中发布示例。
  • 这应该不言自明吗?试过了,它有效。紧凑的代码。但我真的不明白。 - 有一天,在编写代码时必须考虑到必须维护它的可怜的灵魂。
  • @ndroock1 我承认我写过不透明的代码,但这并不复杂。事实上,一旦您熟悉了SowReap,它就非常简单。基本上,Sow[x, t] 将项目 x 存储在名为 t 的 bin 中,然后 Reap 收集所有 bin,并且(使用第三个参数)对每个 bin 执行一些操作。在我的第一种方法中,# -> Mean@#2 & 分解为将#(标签名称)规则分解为Mean@#2(bin 内容的平均值)。之后,它只是将此规则应用于第一列的副本,并将其附加到表中。
  • 谢谢@Mr.Wizard,帮了大忙。
  • @Mr.Wizzard :我正在努力在更大的表格上调整您上面的第一个代码:如何根据第二列计算其范围内的第 5 列值的平均值例如 ?截至目前,它是基于第一列的第二个。我得到了必须计算平均值的列的 Snow[5#,#],但找不到如何在希望定义范围时修改列号。我希望这很清楚。提前感谢您的关注。
【解决方案2】:

什么鬼,我去参加聚会。这是我的版本:

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

我猜应该够快了。

编辑

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

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}}

这里的key可以是1到1000,其中500个,每个key有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)。但重要的是要知道如何优化代码,以及可以优化到什么程度(您可以期待什么)。

【讨论】:

  • 您的代码重新排列了列表。也许这不是问题,但我避免了。
  • @Mr.Wizard True。你的方法很酷,但我会另外将规则包装在Dispatch 中,否则它的性能会因(非常)大量不同的“键”而降低。
  • 我担心我的代码已经够复杂了,但这当然是正确的。
  • 顺便说一句,我把你说的“非常酷”作为一种高度的补充。
  • @500 好的,太好了。我使用了您对e 的初始定义。你显然把它移到了页面的下方。
【解决方案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.'s cmets

如果列表不是按第一个元素排序的,你可以这样做:

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

但在您的示例中这不是必需的

【讨论】:

  • 你们太棒了。我是 Mathematica 的新手,您的帮助真的很宝贵!
  • belisarius,我用你的代码得到了有趣的结果。设e = {{3, 77}, {3, 34}, {1, 57}, {2, 90}, {2, 21}, {2, 36}, {1, 34}} 那么1 类型的平均值应该是(57 + 34)/2 而不是57
  • @Mr.它假设一个有序列表(通过它的第一个元素)作为输入
  • 我在规范中没有看到。
【解决方案4】:

为什么不加仓?

我认为这是最直接/易于阅读的答案,但不一定是最快的。但是在 Mathematica 中你可以用多少种方法来思考这样的问题,真是令人惊讶。

先生。正如其他人所指出的那样,Wizard's 显然非常酷。

@Nasser,您的解决方案不能推广到 n 类,尽管可以很容易地对其进行修改。

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] 

【讨论】:

    【解决方案5】:

    哇,这里的答案看起来很高级很酷,需要更多时间来学习它们。

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


    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]
    

    显然不如功能性解决方案好。

    好的,我现在就走,躲开函数式程序员:)

    --纳赛尔

    【讨论】:

    • 感谢您的贡献
    • 祝您恢复/过渡好运。我向你保证,这是值得的。在您取得进展时,请随时寻求帮助发布问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多