在Python中加速Beta Pert分布的计算

2 投票
1 回答
1644 浏览
提问于 2025-04-18 13:49

我正在计算每次循环迭代的beta PERT分布(还有其他一些事情,但计算这个分布是最耗时的)。

最开始我是在R语言中写的这个代码,但花了太长时间,所以我尝试用一个更快的工具。

我的一些数据集可能会很大,比如我刚刚处理了一个有153413个案例的情况,结果在Python中运行了大约8个小时(比R快,但还是有点慢)。

我对Python还很陌生,想知道有没有什么方法可以加快这样的计算?

示例代码:

af = lambda pmu, pmin, pmode, pmax: (pmu-pmin)*(2*pmode-pmin-pmax)/((pmode-pmu)*(pmax-pmin))
bf = lambda pmu, pmin, pmode, pmax: (pmax-pmu)/(pmu-pmin)*((pmu-pmin)*(2*pmode-pmin-pmax)/((pmode-pmu)*(pmax-pmin)))

e=5.
shape=4.
max=10.
mu_d = np.arange(0, 10, 0.05)                
d = np.arange(0.025, 60.025, 0.05)
nlocs=153413  # number of rows in dataset


f0_dist = np.zeros(len(mu_d))
f1_dist = np.zeros(len(mu_d))
f2_dist = np.zeros(len(mu_d))

f0 = st.norm.cdf(d, 0.9/2., 0.9/6.)
f1 = st.uniform.cdf(d, 0.001, 0.9)

tic = time.clock()     
    for i in xrange(nlocs):
       for j in xrange(len(mu_d)): # mu_d has 121 values
            Rp_min = mu_d[j] - 1.96*e
            Rp_mode = mu_d[j] - 0.75*e
            Rp_max = max
            Rp_mu=(Rp_min+Rp_max+shape*Rp_mode)/(shape+2)
   dist = st.beta.cdf(d, a=af(Rp_mu, Rp_min, Rp_mode, Rp_max), b=bf(Rp_mu, Rp_min, Rp_mode, Rp_max), loc=Rp_min, scale=1-Rp_min)

    f0_dist[j] = 1 - np.sum(dist*f0*0.05)
    f1_dist[j] = 1- np.sum(dist*f1*0.05)
    f2_dist[j] = 1 - np.sum(dist*0.05)
    temp = 0.4*f0_dist + 0.5*f1_dist + 0.1*f1_dist
    aggr_dist = aggr_dist + temp

toc = time.clock() - tic
print '\nTime elapsed: %.3f seconds\n' % toc

1 个回答

1

这里有一段稍微修改过的代码:

af = lambda pmu, pmin, pmode, pmax: (pmu-pmin)*(2*pmode-pmin-pmax)/((pmode-pmu)*(pmax-pmin))
bf = lambda pmu, pmin, pmode, pmax: (pmax-pmu)/(pmu-pmin)*((pmu-pmin)*(2*pmode-pmin-pmax)/((pmode-pmu)*(pmax-pmin)))

e=5.
shape=4.
max=10.
mu_d = np.arange(0, 10, 0.05)                
d = np.arange(0.025, 60.025, 0.05)

Rp_max = max
e1_96 = 1.96 * e
e0_75 = 0.75 * e
for i in xrange(nlocs): # e.g 153413
   for mu_d_j in mu_d: # mu_d has 121 values
        Rp_min = mu_d_j - e1_96
        Rp_mode = mu_d_j - e0_75
        Rp_mu=(Rp_min+Rp_max+shape*Rp_mode)/(shape+2)

   dist = st.beta.cdf(d, a=af(Rp_mu, Rp_min, Rp_mode, Rp_max), b=bf(Rp_mu, Rp_min, Rp_mode, Rp_max), loc=Rp_min, scale=1-Rp_min)

接下来是解释:

在循环内保存每个指令

  • Rp_max = max 移到循环外面
  • 把常量(比如 e1_96e0_75)的计算放到循环外面

避免深层引用

  • 只执行一次 mu_d[j],然后用一个局部变量来保存这个值,深层次的取值会浪费时间

for 循环来获取值,而不是用 lst[i]

下面的内容:

for j in xrange(len(mu_d)): # mu_d has 121 values
    mu_d_j = mu_d[j]

应该改成更高效(而且符合Python风格)的:

for mu_d_j in mu_d: # mu_d has 121 values
    #now use mu_d_j

测量时间

这是基本原则,每次修改都要评估一下效果。如果你设定了期望的速度(处理时间),那么你可能会很快停止优化。

免责声明

因为我无法运行这段代码,所以不能保证所有的修改都是正确的。有几行代码我不太确定它们的作用:

最后一行 dist =

   dist = st.beta.cdf(d, a=af(Rp_mu, Rp_min, Rp_mode, Rp_max), b=bf(Rp_mu, Rp_min, Rp_mode, Rp_max), loc=Rp_min, scale=1-Rp_min)

这个缩进正确吗?现在的情况是,它在每次 nloc 循环中都会执行一次。

结果的 dist 值在哪里使用呢?

如果它是最深层循环的一部分,那么还可以做更多的优化(比如减少变量名,合并一些代码)。

撰写回答