我有一个输入向量列表,我需要将其用作使用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)
答案 0 :(得分:2)
如果没有实际的公式,很难做出任何具体的时间,但是有一些关于你的代码的建议。
首先,让我们谈谈方程式:
symEqns[0][0] == symEqns[1][3]
。再次,为什么评估? 这些方程的起源是什么?我看到R_1 - 1
是一个相当普遍的因素。也许你原来的问题要容易解决。
其次,让我们谈谈循环。你可以从4中删除一个循环结构:
此:
for eqnSet in symEqns:
lambdaFuncs = []
for eqn in eqnSet:
func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
lambdaFuncs.append(func)
# This seems out of place in your example code: whenever a
# new row of functions is being processed, you lose all the
# data from outputList, because you're not storing it anywhere.
outputList = []
for row in inputArray:
results = []
for func in lambdaFuncs:
results.append(func(*row))
outputList.append(results)
可以改成这个:
outputlist = [] # Better position for outputList
for eqnSet in symEqns:
for eqn in eqnSet:
func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
for row in inputArray:
results = []
results.append(func(*row))
outputList.append(results)
除非您确实需要存储所有 lambdified numpy函数,否则我非常怀疑。
你可以通过实现lambdified函数就像numpy函数一样工作来摆脱另一个循环结构:它们也被矢量化了。
>>> for row in inputArray:
... print(f(*row)),
1.16879219805 0.940165061898 1.07015306122
>>> arr = np.array(inputArray)
>>> f(arr[:,0], arr[:,1])
array([ 1.1687922 , 0.94016506, 1.07015306])
相同的输出,没有for循环。 这将使您的四重换环降至:
input_data = np.array(inputArray)
outputlist = [] # Better position for outputList
for eqnSet in symEqns:
for eqn in eqnSet:
func = sp.lambdify(['R_0', 'R_1'], eqn, 'numpy')
outputList.append(func(input_data[:,0], input_data[:,1]))
这会快得多,因为现在基本上你只是循环遍历sympy函数列表,而不是遍及数据(现在是连续的,因此具有缓存优势)或者列表lambdified sympy功能。如果您可以在评论中添加一些时间结果,那么一旦您应用了这些技术,这将是非常好的。
另外,一个温和的提示:在python编程语言中,大多数程序员都遵循PEP8 coding style,这意味着变量都是小写的,下划线分隔单词。