【问题标题】:Optimizing calculation of frequencies of gametes in populations优化种群中配子频率的计算
【发布时间】:2011-08-25 08:35:32
【问题描述】:

我需要优化种群中配子频率的计算。

我在每个群体中有np 个群体和Ne 个个体。每个个体由两个配子(雄性和雌性)组成。每个配子包含三个基因。每一代可能是01。所以每个人都是一个 2x3 矩阵。矩阵的每一行都是一个父母给的一个配子。每个种群中的个体集可能是任意的(但总是Ne 长度)。为简单起见,具有个体的初始种群可以如下给出:

Ne = 300; np = 3^7;
(*This table may be arbitrary with the same shape*)
ind = Table[{{0, 0, 0}, {1, 1, 1}}, {np}, {Ne}]

全套所有可能的配子:

allGam = Tuples[{0, 1}, 3]

每个人可以通过 8 种可能的方式以相等的概率生成一个配子。这些配子是:Tuples@Transpose@ind[[iPop, iInd]](其中iPopiInd - 人口指数和人口指数)。我需要计算每个人口中个体产生的配子的频率。

此时我的解决方案如下。

首先,我将每个个体转化为它可以产生的配子:

gamsInPop = Map[Sequence @@ Tuples@Transpose@# &, ind, {2}]

但更有效的方法是:

gamsInPop = 
 Table[Join @@ Table[Tuples@Transpose@ind[[i, j]], {j, 1, Ne}], {i, 1, np}]

其次,我计算了产生的配子的频率,包括可能但在种群中不存在的配子的零频率:

gamFrq = Table[Count[pop, gam]/(8 Ne), {pop, gamInPop}, {gam, allGam}]

此代码的更高效版本:

gamFrq = Total[
   Developer`ToPackedArray[
    gamInPop /. Table[
      allGam[[i]] -> Insert[{0, 0, 0, 0, 0, 0, 0}, 1, i], {i, 1, 
       8}]], {2}]/(8 Ne)

不幸的是,代码仍然太慢。谁能帮我加快速度?

【问题讨论】:

  • 我添加了combinatorics标签;我认为这会有所帮助。我现在没有时间做这个。

标签: performance optimization combinatorics wolfram-mathematica


【解决方案1】:

这段代码:

Clear[getFrequencies];
Module[{t = 
   Developer`ToPackedArray[
     Table[FromDigits[#, 2] & /@ 
         Tuples[Transpose[{
            PadLeft[IntegerDigits[i, 2], 3], 
            PadLeft[IntegerDigits[j, 2], 3]}]], 
       {i, 0, 7}, {j, 0, 7}]
    ]},
   getFrequencies[ind_] :=
    With[{extracted = 
       Partition[
          Flatten@Extract[t, Flatten[ind.(2^Range[0, 2]) + 1, 1]], 
          Ne*8]},
        Map[
         Sort@Join[#, Thread[{Complement[Range[0, 7], #[[All, 1]]], 0}]] &@Tally[#] &, 
         extracted
        ][[All, All, 2]]/(Ne*8)
    ]
]

利用转换为十进制数和压缩数组,并在我的机器上将您的代码提高 40 倍。基准:

In[372]:= Ne=300;np=3^7;
(*This table may be arbitrary with the same shape*)
inds=Table[{{0,0,0},{1,1,1}},{np},{Ne}];

In[374]:= 
getFrequencies[inds]//Short//Timing
Out[374]= {0.282,{{1/8,1/8,1/8,1/8,1/8,1/8,1/8,1/8},<<2185>>,
{1/8,1/8,1/8,1/8,1/8,1/8,1/8,1/8}}}

In[375]:= 
Timing[
  gamsInPop=Table[Join@@Table[Tuples@Transpose@inds[[i,j]],{j,1,Ne}],{i,1,np}];
  gamFrq=Total[Developer`ToPackedArray[gamsInPop/.Table[allGam[[i]]->
         Insert[{0,0,0,0,0,0,0},1,i],{i,1,8}]],{2}]/(8 Ne)//Short]

Out[375]= {10.563,{{1/8,1/8,1/8,1/8,1/8,1/8,1/8,1/8},<<2185>>,
  {1/8,1/8,1/8,1/8,1/8,1/8,1/8,1/8}}}

请注意,通常(对于随机人群),您和我的解决方案中的频率排序由于某种原因是不同的,并且

In[393]:= fr[[All,{1,5,3,7,2,6,4,8}]] == gamFrq
Out[393]= True

现在,一些解释:首先,我们创建一个表t,其构造如下:每个配子分配一个从 0 到 7 的数字,对应于其中的 0 和 1 被视为二进制数字。然后该表有一个人可能产生的配子,存储在位置{i,j},其中i 是母亲配子的小数(比如),j - 父亲的配子,对于那个人(每个人都是唯一的由一对{i,j} 标识)。个人产生的配子也转换为小数。这是它的外观:

In[396]:= t//Short[#,5]&
Out[396]//Short= {{{0,0,0,0,0,0,0,0},{0,1,0,1,0,1,0,1},{0,0,2,2,0,0,2,2},
{0,1,2,3,0,1,2,3},{0,0,0,0,4,4,4,4},{0,1,0,1,4,5,4,5},{0,0,2,2,4,4,6,6},
{0,1,2,3,4,5,6,7}},<<6>>,{{7,6,5,4,3,2,1,0},{7,7,5,5,3,3,1,1},{7,6,7,6,3,2,3,2},
<<2>>,{7,7,5,5,7,7,5,5},{7,6,7,6,7,6,7,6},{7,7,7,7,7,7,7,7}}}

一个非常重要(关键)的步骤是将此表转换为打包数组。

Flatten[ind.(2^Range[0, 2]) + 1, 1]] 行将所有种群中所有个体的父母配子从二进制转换为十进制,并加 1,以便这些成为索引,在该索引处,可能产生的配子列表存储在表中 @987654332 @对于给定的个人。然后,我们同时为所有人口Extract 全部,并使用FlattenPartition 恢复人口结构。然后,我们用Tally 计算频率,添加频率为零的缺失配子(由Join[#, Thread[{Complement[Range[0, 7], #[[All, 1]]], 0}]] 行完成)和Sort 固定种群的每个频率列表。最后,我们提取频率并丢弃配子十进制索引。

所有操作都非常快,因为在打包数组上执行。加速是由于问题的矢量化公式和打包数组的使用。它的内存效率也更高。

【讨论】:

  • @Sjoerd 这是一个错误,尚未测试,我刚刚修复它。
  • 你能解释一下为什么你把它全部打包在Module中吗?这只是为了有一个本地化的 t$398339 变量吗?
  • @Sjoerd 是的,仅此而已。每次调用函数时都不必重新计算它,并且允许函数引用(隐式依赖)全局变量是 IMO 一种不好的做法 - 我在这里详细解释了我对这个问题的看法:stackoverflow.com/questions/6236458/…
  • @Leonid fr[[All,{1,5,3,7,2,6,4,8}]] 中的索引列表是否总是给出与最初相同的频率顺序?配子与其频率之间的精确对应是必要的。
  • @Alexey 我没有进行广泛的测试,但它应该,因为配子与其索引之间的对应关系,以及排序过程,不依赖于数据集。
猜你喜欢
  • 2021-02-20
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多