Как написать случай, когда оператор like в массиве numpy - PullRequest
6 голосов
/ 10 января 2020
def custom_asymmetric_train(y_true, y_pred):
    residual = (y_true - y_pred).astype("float")
    grad = np.where(residual>0, -2*10.0*residual, -2*residual)
    hess = np.where(residual>0, 2*10.0, 2.0)
    return grad, hess

Я хочу написать это утверждение:

    case when residual>=0 and residual<=0.5 then -2*1.2*residual
    when residual>=0.5 and residual<=0.7 then -2*1.*residual
    when residual>0.7 then -2*2*residual end ) 

однако np.where не может написать & (и) logi c. Как мне написать этот случай, когда лог c в np.where в python.

Спасибо

Ответы [ 3 ]

7 голосов
/ 10 января 2020

Этот оператор может быть записан с использованием np.select как

import numpy as np

residual = np.random.rand(10) -0.3 # -0.3 to get some negative values
condlist = [(residual>=0.0)&(residual<=0.5), (residual>=0.5)&(residual<=0.7), residual>0.7]
choicelist = [-2*1.2*residual, -2*1.0*residual,-2*2.0*residual]

residual = np.select(condlist, choicelist, default=residual)

. Обратите внимание, что при выполнении нескольких условий в condlist используется первое обнаруженное условие. Когда все условия оцениваются как False, он будет использовать значение default. Более того, для вашей информации вам нужно использовать побитовый оператор & для логических массивов numpy, так как ключевое слово and python не будет работать с ними.

Давайте проведем сравнение этих ответов:

residual = np.random.rand(10000) -0.3

def charl_3where(residual):
    residual = np.where((residual>=0.0)&(residual<=0.5), -2*1.2*residual, residual)
    residual = np.where((residual>=0.5)&(residual<=0.7), -2*1.0*residual, residual)
    residual = np.where(residual>0.7, -2*2.0*residual, residual)
    return residual

def yaco_select(residual):
    condlist = [(residual>=0.0)&(residual<=0.5), (residual>=0.5)&(residual<=0.7), residual>0.7]
    choicelist = [-2*1.2*residual, -2*1.0*residual,-2*2.0*residual]
    residual = np.select(condlist, choicelist, default=residual)
    return residual


%timeit charl_3where(residual)
>>> 112 µs ± 1.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit yaco_select(residual)
>>> 141 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

давайте попробуем оптимизировать их с помощью numba

from numba import jit

@jit(nopython=True)
def yaco_numba(residual):
    out = np.empty_like(residual)
    for i in range(residual.shape[0]):
        if residual[i]<0.0 :
            out[i] = residual[i]
        elif residual[i]<=0.5 :
            out[i] = -2*1.2*residual[i]
        elif residual[i]<=0.7:
            out[i] = -2*1.0*residual[i]
        else: # residual>0.7
            out[i] = -2*2.0*residual[i]        
    return out

%timeit yaco_numba(residual)
>>> 6.65 µs ± 123 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

Окончательная проверка

res1 = charl_3where(residual)
res2 = yaco_select(residual)
res3 = yaco_numba(residual)
np.allclose(res1,res3)
>>> True
np.allclose(res2,res3)
>>> True

Эта примерно на 15x быстрее, чем предыдущая лучшая. Надеюсь, это поможет.

4 голосов
/ 10 января 2020

Вы можете использовать синтаксис (condition1) & (condition2) в np.where() вызовах, поэтому вы можете изменить np.where() вызовы вашей функции следующим образом:

def custom_asymmetric_train(y_true, y_pred):
    residual = (y_true - y_pred).astype("float")
    residual = np.where((residual>=0.0)&(residual<=0.5), -2*1.2*residual, residual)
    residual = np.where((residual>=0.5)&(residual<=0.7), -2*1.0*residual, residual)
    residual = np.where(residual>0.7, -2*2.0*residual, residual)
    ...

Первый аргумент - это условие для удовлетворения, второй Аргумент - это значение, используемое, если условие выполнено, третий аргумент - это значение, используемое, если условие не выполняется.

2 голосов
/ 19 января 2020

Вы также можете использовать векторизацию, поскольку условия являются взаимоисключающими:

residual = (y_true - y_pred).astype(float)
m1 = (residual>=0.0)&(residual<=0.5)
m2 = (residual>=0.5)&(residual<=0.7)
m3 = (residual >0.7)

new_residual = -2*(m1 *1.2 *residual + m2*residual + m3*2.0*residual)
return new_residual 

Это будет иметь следующие характеристики:

residual = np.random.rand(10000) -0.3
def speed_test(residual):

    residual = (y_true - y_pred).astype(float)

    m1 = (residual>=0.0)&(residual<=0.5)
    m2 = (residual>=0.5)&(residual<=0.7)
    m3 = residual >0.7
    return -2*(m1 *1.2 *residual + m2*residual + m3*2.0*residual)

%timeit speed_test(residual)
123 µs ± 35.3 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
...