Как эффективно и правильно реализовать декоратор numba jit или применить векторизацию вместо цикла for, чтобы ускорить выполнение программы?

Попытка реализовать декоратор jit для увеличения скорости выполнения моего кода. Не получение должного результата. Он проходит через все виды ошибок. Ключевая ошибка, ошибки типа и т. д. Фактический код без numba работает без проблем.

# The Code without numba is:
df = pd.DataFrame()
df['Serial'] = [865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880]
df['Value'] = [586,586.45,585.95,585.85,585.45,585.5,586,585.7,585.7,585.5,585.5,585.45,585.3,584,584,585]
df['Ref'] = [586.35,586.1,586.01,586.44,586.04,585.91,585.47,585.99,585.35,585.27,585.32,584.86,585.36,584.18,583.53,585]
df['Base'] = [0,-1,1,1,1,1,-1,0,1,1,1,0,1,1,0,-1]

df['A'] = 0.0
df['B'] = 0.0
df['Counter'] = 0
df['Counter'][0] = df['Serial'][0]

for i in range(0,len(df)-1):
    # Filling Column 'A'
    if (df.iloc[1+i,2] > df.iloc[1+i,1]) & (df.iloc[i,5] > df.iloc[1+i,1]) & (df.iloc[1+i,3] >0):
        df.iloc[1+i,4] = round((df.iloc[1+i,1]*1.02),2)
    elif (df.iloc[1+i,2] < df.iloc[1+i,1]) & (df.iloc[i,5] < df.iloc[1+i,1]) & (df.iloc[1+i,3] <0):
        df.iloc[1+i,4] = round((df.iloc[1+i,1]*0.98),2)
    else:
        df.iloc[1+i,4] = df.iloc[i,4]
    # Filling Column 'B'
    df.iloc[1+i,5] = round(((df.iloc[1+i,1] + df.iloc[1+i,2])/2),2) 
    # Filling Column 'Counter'
    if (df.iloc[1+i,5] > df.iloc[1+i,1]):
        df.iloc[1+i,6] = df.iloc[1+i,0]
    else:
        df.iloc[1+i,6] = df.iloc[i,6]
df

Код ниже дает мне ошибку. где я попытался реализовать декоратор numba jit, чтобы ускорить исходный код Python.

#The code with numba jit which is throwing error is:
df = pd.DataFrame()
df['Serial']=[865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880]
df['Value']=[586,586.45,585.95,585.85,585.45,585.5,586,585.7,585.7,585.5,585.5,585.45,585.3,584,584,585]
df['Ref']=[586.35,586.1,586.01,586.44,586.04,585.91,585.47,585.99,585.35,585.27,585.32,584.86,585.36,584.18,583.53,585]
df['Base'] = [0,-1,1,1,1,1,-1,0,1,1,1,0,1,1,0,-1]
from numba import jit
@jit(nopython=True)
def Calcs(Serial,Value,Ref,Base):
    n = Base.size
    A = np.empty(n, dtype='f8')
    B = np.empty(n, dtype='f8')
    Counter = np.empty(n, dtype='f8')
    A[0] = 0.0
    B[0] = 0.0
    Counter[0] = Serial[0]
    for i in range(0,n-1):
        # Filling Column 'A'
        if (Ref[i+1] > Value[i+1]) & (B[i] > Value[i+1]) & (Base[i+1] > 0):
            A[i+1] = round((Value[i+1]*1.02),2)
        elif (Ref[i+1] < Value[i+1]) & (B[i] < Value[i+1]) & (Base[i+1] < 0):  
            A[i+1] = round((Value[i+1]*0.98),2)
        else:
            A[i+1] = A[i]
        # Filling Column 'B'
        B[i+1] = round(((Value[i+1] + Ref[i+1])/2),2)
        # Filling Column 'Counter'
        if (B[i+1] > Value[i+1]):
            Counter[i+1] = Serial[i+1]
        else:
            Counter[i+1] = Counter[i]   
    List = [A,B,Counter]        
    return List

Serial = df['Serial'].values.astype(np.float64)
Value = df['Value'].values.astype(np.float64)
Ref = df['Ref'].values.astype(np.float64)
Base = df['Base'].values.astype(np.float64)

VCal = Calcs(Serial,Value,Ref,Base)

df['A'].values[:] = VCal[0].astype(object)
df['B'].values[:] = VCal[1].astype(object)
df['Counter'].values[:] = VCal[2].astype(object)
df

Я попытался изменить код в соответствии с руководством, предоставленным @Jérôme Richard для вопроса Как устранить цикл for в Pandas Dataframe при заполнении значений каждой строки столбца на основе нескольких операторов if, elif.

Но получаю ошибки и не могу исправить код. Ищу помощи от сообщества в исправлении и улучшении приведенного выше кода или в поиске еще лучшего кода для повышения скорости выполнения. Ожидаемый результат кода показан на рисунке ниже. Как эффективно и правильно реализовать декоратор numba jit или применить векторизацию вместо цикла for, чтобы ускорить выполнение программы?

Укажите ошибку (поскольку люди могут получить другую ошибку в отношении версии пакетов или даже отсутствие ошибки в некоторых случаях)

Jérôme Richard 15.03.2022 12:41
0
1
40
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать df['A'].values[:] только в том случае, если столбец A существует в фрейме данных. В противном случае вам нужно создать новый, возможно, с помощью df['A'] = ....

Более того, трюк с astype(object) применим к строке, но не к числам. Действительно, столбцы фреймов данных на основе строк, по-видимому, не используют массив на основе строк Numpy, а массивы на основе объектов Numpy, содержащие строки CPython. Для чисел Pandas правильно использует числовые массивы. Преобразование чисел обратно в объект неэффективно. То же самое относится и к astype(np.float64): он не нужен, если время и так нормальное. Дело обстоит именно так. Если вы не уверены в типе ввода, вы можете разрешить их, поскольку они не очень дорогие.

Сама функция Numba в порядке (по крайней мере, с последней версией Numba). Обратите внимание, что вы можете указать подпись для скомпилируйте функцию с нетерпением. Эта функция также поможет вам быстрее обнаружить опечатки и сделать их немного более четкими. Недостатком является то, что это делает функцию менее универсальной, поскольку поддерживаются только определенные типы (хотя вы можете указать несколько подписей).

from numba import njit

@njit('List(float64[:])(float64[:], float64[:], float64[:], float64[:])')
def Calcs(Serial,Value,Ref,Base):
    [...]

Serial = df['Serial'].values
Value = df['Value'].values
Ref = df['Ref'].values
Base = df['Base'].values

VCal = Calcs(Serial, Value, Ref, Base)

df['A'] = VCal[0]
df['B'] = VCal[1]
df['Counter'] = VCal[2]

Обратите внимание, что вы можете использовать флаг декоратора Numba fastmath=True для ускорения вычислений, если вы уверены, что входной массив никогда не содержит пространственных значений, таких как NaN или Inf или -0, и вы не полагаетесь на ассоциативность FP-математики.

Спасибо, Жером Ришар. Я попробовал упомянутые изменения. Но получаю эту ошибку. TypingError: Ошибка в конвейере режима nopython (шаг: интерфейс nopython) Нет преобразования из списка (массив (float64, 1d, C)) <iv = None> в список (массив (float64, 1d, A)) <iv = None> для '$420return_value.5', определено как None

mrsprawin 15.03.2022 15:08

Во время: ввод назначения в <ipython-input-3-c8fe9f386d86> (37) Файл "<ipython-input-3-c8fe9f386d86>", строка 37: def Calcs(Serial,Value,Ref,Base): <источник опущен> Список = [A,B,Счетчик] вернуть список ^

mrsprawin 15.03.2022 15:20

Эта ошибка означает, что непрерывный массив, выделенный в функции Numba (имеющий тип array(float64, 1d, C)), не может быть преобразован в несмежный из-за подтипизации списка. Я не вижу причин, по которым Numba не может преобразовать непрерывный массив в несмежный. Я думаю, что это ошибка вывода типа. Вы наверняка используете устаревшую реализацию Numba. Пожалуйста, проверьте версию с помощью nb.version_info.full и обновите Numba, если у вас версия старше 0.54.1 (последняя версия — 0.55.1).

Jérôme Richard 15.03.2022 19:19

Обратите внимание, что простым обходным решением должно быть удаление List(float64[:]) в коде или использование вместо него List(float64[::1]) (чтобы сказать, что массивы непрерывны).

Jérôme Richard 15.03.2022 19:19

Сейчас работает Жером Ришар. Я внес изменения в подпись как @njit('List(float64[::1])(int64[::1], float64[::1], float64[::1], int64[::1])', fastmath=Истина). Спасибо большое.

mrsprawin 16.03.2022 04:33

Этот код дает список ошибок типа преобразования списка.

from numba import njit
df = pd.DataFrame()
df['Serial'] = [865,866,867,868,869,870,871,872,873,874,875,876,877,878,879,880]
df['Value'] = [586,586.45,585.95,585.85,585.45,585.5,586,585.7,585.7,585.5,585.5,585.45,585.3,584,584,585]
df['Ref'] = [586.35,586.1,586.01,586.44,586.04,585.91,585.47,585.99,585.35,585.27,585.32,584.86,585.36,584.18,583.53,585]
df['Base'] = [0,-1,1,1,1,1,-1,0,1,1,1,0,1,1,0,-1]

@njit('List(float64[:])(float64[:], float64[:], float64[:], float64[:])',fastmath=True)
def Calcs(Serial,Value,Ref,Base):
    n = Base.size
    A = np.empty(n)
    B = np.empty(n)
    Counter = np.empty(n)
    A[0] = 0.0
    B[0] = 0.0
    Counter[0] = Serial[0]
    for i in range(0,n-1):
    # Filling Column 'A'
        if (Ref[i+1] > Value[i+1]) & (B[i] > Value[i+1]) & (Base[i+1] > 0):
            A[i+1] = round((Value[i+1]*1.02),2)
        elif (Ref[i+1] < Value[i+1]) & (B[i] < Value[i+1]) & (Base[i+1] < 0):  
            A[i+1] = round((Value[i+1]*0.98),2)
        else:
            A[i+1] = A[i]
    # Filling Column 'B'
        B[i+1] = round(((Value[i+1] + Ref[i+1])/2),2)
    # Filling Column 'Counter'
        if (B[i+1] > Value[i+1]):
            Counter[i+1] = Serial[i+1]
        else:
           Counter[i+1] = Counter[i]   
    List = [A,B,Counter]        
    return List

Serial = df['Serial'].values
Value = df['Value'].values
Ref = df['Ref'].values
Base = df['Base'].values
VCal = Calcs(Serial, Value, Ref, Base)
df['A'] = VCal[0]
df['B'] = VCal[1]
df['Counter'] = VCal[2]
df

Получение приведенной ниже ошибки.

TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No conversion from list(array(float64, 1d, C))<iv=None> to 
list(array(float64, 1d, A))<iv=None> for '$408return_value.5', defined at None

File "<ipython-input-9-3c9e0fe02b75>", line 33:
def Calcs(Serial,Value,Ref,Base):
    <source elided>
    List = [A,B,Counter]        
    return List
    ^

During: typing of assignment at <ipython-input-9-3c9e0fe02b75> (33)

File "<ipython-input-9-3c9e0fe02b75>", line 33:
def Calcs(Serial,Value,Ref,Base):
    <source elided>
    List = [A,B,Counter]        
    return List
    ^

Другие вопросы по теме