我有一个输入向量列表,我需要将其用作使用SymPy生成的函数列表的输入 . 在实际应用中,输入向量的数量约为100k,并且有~5M组符号函数 . 这是我的代码中的瓶颈,所以我试图加快速度 .
我已经通过使用Sympy的lambdify来创建基于numpy的lambda函数做了很大的改进,但是我不禁想到有一种方法可以对此进行向量化并将for循环变为numpy / C而不是python .
我最初认为numpy.apply_along_axis()会有所帮助,但它仍然在python中进行循环 .
这是我现在正在做的简化版本:
import time
import sympy as sp
import numpy as np
#Input for performance testing
# sampleSize = 200000
# inputVector = [1.2, -0.33]
# inputArray = np.array(inputVector*np.ones((sampleSize,1)))
#This array would have ~100k rows in actual data set
inputArray = [[-.333, -.558],
[-.454, -.367],
[-.568, -.678]]
start = time.time()
#These are the equations of motion of a mechanical system. Each row represents
#a unique arrangement of components. There may be a better way to handle this,
#but I haven't understood the system well enough to do so yet.
#This array would have ~5M rows in actual data set
symEqns = [['(R_1 - 1)/(R_0 - 1)', '0', '-R_1 + 1', '(R_1 - 1)/(R_0*R_1 - 1)','1'],
['R_1/R_0', '0', '-1/(R_0 - 1)', '(R_1 - 1)/(R_0 - 1)', '1']]
for eqnSet in symEqns:
#Create lambda functions
lambdaFuncs = []
for eqn in eqnSet:
func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
#This is ~5x slower, due to use of pure python vs. numpy ??
# func = lambda R_0, R_1: eval(eqn)
lambdaFuncs.append(func)
#Evaluate each lambda func for each input set
# in my actual code, this is a parameter of an object. forgot to store it in my example code
outputList = []
for row in inputArray:
results = []
for func in lambdaFuncs:
results.append(func(*row))
outputList.append(results)
end = time.time()
print "\nTotal Time Elapsed: {:d}:{:0>5.2f}".format(int((end-start)/60), (end-start)%60)
如果它有帮助,我还可以构建评估以独立计算每个函数,为每个函数创建一列结果 . 这是一个在这种情况下评估块的例子(使用for循环进行说明,我想使用numpy进行矢量化评估):
#Evaluate each lambda func for each input set
outputList = []
for func in lambdaFuncs:
results = []
for row in inputArray:
results.append(func(*row))
outputList.append(results)
[编辑]为了将来参考,这是我改进的这个问题的工作示例代码 . 我已经从Oliver的响应中调整了一些内容,主要是允许可变长度的输入向量:
import time
import sympy as sp
import numpy as np
# This array would have ~100k rows in actual data set
input_array = np.array([[-.333, -.558],
[-.454, -.367],
[-.568, -.678]])
#This array would have ~5M rows in actual data set (generated via Sympy linear algebraic solns)
sym_eqns = [['(R_1 - 1)/(R_0 - 1)', '0', '-R_1 + 1', '(R_1 - 1)/(R_0*R_1 - 1)','1'],
['R_1/R_0', '0', '-1/(R_0 - 1)', '(R_1 - 1)/(R_0 - 1)', '1']]
for eqn_set in sym_eqns:
output_list = []
for eqn in eqn_set:
func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
results = func(*[input_array[:,n] for n in range(input_array.shape[1])])
output_list.append(results)
1 回答
没有实际的方程式很难做出任何具体的时间,但是有一些关于你的代码的建议 .
首先,我们来谈谈方程式:
如果总是有一列零和一列,为什么还要费心去评估?
方程似乎是对称的:你的
symEqns[0][0] == symEqns[1][3]
. 再次,为什么评估?这些方程的起源是什么?我看到
R_1 - 1
是一个相当普遍的因素 . 也许你原来的问题要容易解决 .其次,我们来谈谈循环 . 你可以从4中删除一个循环结构:
这个:
可以改成这个:
除非你真的需要存储所有lambdified numpy函数,我非常怀疑 .
你可以通过实现lambdified函数就像numpy函数一样工作来摆脱另一个循环结构:它们也是矢量化的 .
相同的输出,没有for循环 . 这将使您的四重换环降至:
这会快得多,因为现在基本上你只是循环遍历sympy函数列表,而不是遍及数据(现在是连续的,因此具有缓存优势)或lambdified sympy函数列表 . 如果您在应用这些技术后可以在注释中添加一些计时结果,那将是非常好的 .
另外,一个温和的提示:在python编程语言中,大多数程序员都遵循PEP8 coding style,这意味着变量都是小写的,下划线分隔单词 .