数值Python - 我如何使这成为一个ufunc?

时间:2014-01-08 20:46:37

标签: python numpy

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()函数的任何想法?

谢谢!

5 个答案:

答案 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 希望这有帮助!