Две функции Cython; почему один работает, а другой выдает NameError? - PullRequest
0 голосов
/ 11 марта 2020

Я пытаюсь использовать Cython для ускорения некоторых частей моего Python скрипта. Один ключевой раздел применяет функции к Pandas кадру данных; так как это делается много раз, я хотел написать эти функции с помощью Cython для более быстрых вычислений. Функции приведены ниже и находятся в той же ячейке записной книжки Jupyter:

%%cython
cimport numpy as np
import numpy as np

cdef double breadth_c_type(np.ndarray[np.float64_t, ndim=1] arr):
    """ Calculates range between the maximum and minimum values of a given list. """
    return (max(arr) - min(arr))

cdef double evenness_c_type(np.ndarray[np.float64_t, ndim=1] arr):
    """ Calculates the sample variance of differences between values in a sorted list. """
    cdef np.ndarray[double] sorted_arr
    cdef list desc_diff
    cdef double m
    cdef double var_res
    sorted_arr = sorted(arr)
    desc_diff = []
    for x in range(len(arr)-1):
        desc_diff.append(sorted_arr[x+1]-sorted_arr[x])
    # following used to avoid usage of numpy
    m = sum(desc_diff) / len(desc_diff)
    var_res = sum((xi - m)**2 for xi in desc_diff) / len(desc_diff)
    return var_res

Ячейка записной книжки работает успешно, как написано, поэтому я подумал, что обе функции скомпилированы успешно. Однако этот код выполняется должным образом:

%timeit rand_df.apply(breadth_c_type, raw=True)

, тогда как этот код:

%timeit rand_df.apply(evenness_c_type, raw=True)

не запускается и возвращает «NameError: name 'evenness_c_type' не определено». Я получаю те же результаты без декоратора% timeit, и функции не компилируются при использовании «cpdef» или «def» вместо «cdef». Поскольку я пытался следовать одинаковому синтаксису для обеих функций, я не знаю, что вызывает ошибку для evenness_c_type.

EDIT Благодаря @DavidW я выяснил проблемы с evenness_c_type() функция. Он компилируется и работает хорошо, хотя и не так быстро, как простая версия Cython.

cdef double evenness_c_type(np.ndarray[np.float64_t, ndim=1] arr):
    """ Calculates the population variance of differences between values in a sorted list. """
    cdef np.ndarray [double] desc_diff=np.empty(len(arr)-1, dtype = np.float64)
    arr.sort()
    for x in range(len(arr)-1):
        desc_diff[x]=(arr[x+1]-arr[x])
    return np.var(desc_diff)

1 Ответ

3 голосов
/ 11 марта 2020

В принципе ни один не должен работать с timeit. timeit принимает объект Python, а функция cdef не является объектом Python. Однако в некоторых случаях Cython автоматически создает преобразование из функции cdef -> Python object (фактически делает его cpdef).

Причина, по которой он не компилируется с cpdef, связана с выражением генератора ("closures inside cpdef functions not yet supported")

var_res = sum((xi - m)**2 for xi in desc_diff) / len(desc_diff)

Я получаю сообщения об ошибках, в которых говорится об этом, хотя есть компилятор cra sh, поэтому они не наиболее понятны.

Замените это на понимание списков, и все будет хорошо (хотя он не выглядит особенно хорошо для оптимизации)

var_res = sum([(xi - m)**2 for xi in desc_diff]) / len(desc_diff)

Я подозреваю, что причина автоматического преобразования не была сгенерирована для функции cdef это было выражение генератора.

Причина, по которой она не компилируется как функция def, заключается в том, что вы указываете тип возвращаемого значения.

Учитываете ли вы действительно нужно сделать это cdef/cpdef. В большинстве случаев мало пользы.

...