NumPy的新手,可能没有正确搜索,所以如果这是一个常见的问题,我会采取肿块......
我正在研究一个问题,我需要为相对较大的数字计算log(n!) - 即。为了大而先计算阶乘,所以我写了以下函数:
def log_fact(n):
x = 0
for i in range(1,n+1):
x += log(i)
return x
现在的问题是我想将它作为传递给curve_fit的函数的一部分来使用:
def logfactfunc(x, a, b, c):
return a*log_fact(x) + b*x + c
from scipy.optimize import curve_fit
curve_fit(logfactfunc, x, y)
但是,这会产生以下错误:
File "./fit2.py", line 16, in log_fact
for i in range(1,n+1):
TypeError: only length-1 arrays can be converted to Python scalars
一点点搜索建议将numpy.frompyfunc()转换为ufunc
curve_fit(np.frompyfunc(logfactfunc, 1, 1), data[k].step, data[k].sieve)
TypeError: <ufunc 'logfactfunc (vectorized)'> is not a Python function
也试过这个:
def logfactfunc(x, a, b, c):
return a*np.frompyfunc(log_fact, 1, 1)(x) + b*x + c
File "./fit2.py", line 30, in logfactfunc
return a*np.frompyfunc(log_fact, 1, 1)(x) + b*x + c
TypeError: unsupported operand type(s) for +: 'numpy.ndarray' and 'numpy.float64
关于如何在curve_fit()函数中使用log_fact()函数的任何想法?
谢谢!
答案 0 :(得分:6)
您的log_fact
函数与gammaln
函数密切相关,scipy.special
函数在log_fact(n) == scipy.special.gammaln(n+1)
中定义为ufunc。具体来说,n
。对于In [15]: %timeit log_fact(19)
10000 loops, best of 3: 24.4 us per loop
In [16]: %timeit scipy.special.gammaln(20)
1000000 loops, best of 3: 1.13 us per loop
的适度值,这个速度要快得多:
gammaln
此外,与n
不同,log_fact
的运行时间与{{1}}无关。
答案 1 :(得分:3)
正在使用数组作为输入参数调用您的log_fact
函数,这就是抛弃您的方法的原因。矢量化代码的可能方法如下:
def log_fact(n):
n = np.asarray(n)
m = np.max(n)
return np.take(np.cumsum(np.log(np.arange(1, m+1))), n-1)
参加试驾:
>>> log_fact(3)
1.791759469228055
>>> log_fact([10, 15, 23])
array([ 15.10441257, 27.89927138, 51.60667557])
>>> log_fact([[10, 15, 23], [14, 15, 8]])
array([[ 15.10441257, 27.89927138, 51.60667557],
[ 25.19122118, 27.89927138, 10.6046029 ]])
这种方法唯一需要注意的是,只要您调用它的最大值,它就会存储一个数组。如果你的n
达到数十亿,你可能会破坏它。除此之外,如果你用很多值调用它,它实际上避免了重复计算。
答案 2 :(得分:2)
如果n
确实很大(比如说大于10左右)那么多更好的方法是使用 Stirling的近似值。这将更有效率。它也很容易矢量化。
对于您正在采用的方法,log_fact(n)
函数可以更有效,更紧凑地编写
def log_fact(n) :
return np.sum(np.log(np.arange(1,n+1)))
但这对您的问题没有帮助。我们可以在@Isaac显示时将其向量化,或者只使用np.vectorize()
这是一个基本相同的便利包装器。请注意,它不提供速度优势,您仍然使用缓慢的Python循环。
话虽如此,请使用斯特林的近似值!
答案 3 :(得分:0)
据我所知,创建一个ufunc
是相当复杂的,我需要在c中编写你的函数。有关创建ufunc的文档,请参阅here。
您可以考虑只编写一个函数版本,然后返回ndarray
。例如:
def logfact_arr(a):
return np.array([log_fact(x) for x in a.flat]).reshape(a.shape)
答案 4 :(得分:0)
之前的答案显示了解决问题的有效方法。但是,您问题的准确答案,即如何对log_fact
函数进行向量化,则使用np.vectorize
:
vlog_fact=np.vectorize(log_fact)
def vlogfactfunc(x, a, b, c):
return a*vlog_fact(x) + b*x + c
有了这个,你可以拨打curve_fit(vlogfactfunc, np.array([1,2,3]), np.array([ -1. , 4.465 , 11.958]))
正如你的建议,你也可以使用np.frompyfunc
,但正如你可以在其文档中看到的那样,它总是返回python对象,因为curve_fit
抱怨:
TypeError: Cannot cast array data from dtype('O') to dtype('float64') according to the rule 'safe'
解决方法是将返回的数组转换为浮点数组:
ulog_fact = np.frompyfunc(log_fact, 1,1)
def ulogfactfunc(x, a, b, c):
return a*ulog_fact(x).astype(np.float) + b*x + c
因此,您也可以使用curve_fit
致电ulogfactfunc
希望这有帮助!