Получите каждый хвост быстрее с математикой / логи c, примененными в массиве numpy 1D - для устранения бесконечности - PullRequest
0 голосов
/ 17 февраля 2020

У меня есть двумерный массив, в котором мне нужно, чтобы каждый «столбец» (ось = 1) обрабатывался независимо. Для каждого столбца (массива 1D) мне нужна функция, примененная к каждому хвосту длины массива 1D, чтобы обеспечить массив длины N следующим образом. Как мне сделать это быстрее? Возможно, удалив за l oop. Я делал это с map / lambda / hstack до тех пор, пока не добился ошибки деления на ноль, что требовало условия if / elif / else для устранения перехода к бесконечности.

import numpy as np
x = np.random.rand(20,5)

def get_updown_array(loc_array):
    return np.array([updown(loc_array[x:]) for x in list(range(loc_array.shape[0]))])

def updown(local_array):
    sub_length = ((local_array.shape[0]) + 1) // 2 # to get the middle value when length is uneven
    a = local_array[:sub_length].mean() # first half mean
    b = local_array[-sub_length:].mean() # second half mean
    if not a == 0:
        return ( b - a ) / abs(a)
    elif not b == 0:
        return ( b - a ) / abs(( a + b ) / 2)
    else:
        return 0

result = np.apply_along_axis(get_updown_array, 0, x)

Кажется, что if / else условная логика c исключает возможность применения функции к нескольким значениям в одном измерении numpy, все одновременно.

  1. Я рассмотрел использование векторизации, хотя, похоже, для этого все еще потребуется для l oop в первой функции.
  2. Я смотрел на pandas .apply, хотя он кажется медленнее и все еще требует впоследствии сцепления или vstack / hstack?
  3. Cython считался хотя это по-прежнему оставляет неумелым для l oop слоняясь.
  4. Я пробовал np.where, хотя это все еще нужно для l oop.

Есть ли способ применить математику / логи c к каждой длине хвоста без значения для l oop? Какой самый быстрый подход? Сообщество stackoverflow.

1 Ответ

0 голосов
/ 17 февраля 2020

Я бы сделал что-то вроде этого:

def updown(x):
    result = np.zeros_like(x)
    for i in range(len(x)):
        mid = (len(x)+1)//2
        a = x[:mid].mean(axis=0)
        b = x[-mid:].mean(axis=0)
        mask_a = a != 0
        mask_b = (a == 0) & (b != 0)
        result[i, mask_a] = (b[mask_a] - a[mask_a]) / abs(a[mask_a])
        result[i, mask_b] = (b[mask_b] - a[mask_b]) / abs((a[mask_b] + b[mask_b])/2)
        x = x[1:]
    return result

В тех случаях, когда al oop довольно неизбежно, как здесь, лучше предварительно выделить массив результатов (если размеры известны заранее) и заполните значения в l oop. Это также имеет то преимущество, что позволяет предварительно заполнить значение по умолчанию, равное нулю.

Нет необходимости применять что-либо к столбцам x в качестве других операций (mean и arithmeti * 1032). *) может быть легко применен к выбранной оси.

Условный лог c достигается с помощью маскировки.

Вы также можете использовать аргументы ключевых слов out и where из np.divide:

def updown(x):
    result = np.zeros_like(x)
    for i in range(len(x)):
        mid = (len(x)+1)//2
        a = x[:mid].mean(axis=0)
        b = x[-mid:].mean(axis=0)
        np.divide(b - a, abs(a), out=result[i], where=(a != 0))
        np.divide(b - a, abs((a + b)/2), out=result[i], where=(a == 0) & (b != 0))
        x = x[1:]
    return result

Давайте посмотрим некоторые сравнения производительности (я завернул весь ваш исходный код в функцию с именем updown_orig)

Для измерений (20, 5) мы видим улучшение только в 2 раза:

In []: %timeit updown_orig(np.random.rand(20, 5))
1 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In []: %timeit updown(np.random.rand(20, 5))
508 µs ± 10.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Давайте масштабируем оба измерения на 10 (увеличив размер x в 100 раз):

In []: %timeit updown_orig(np.random.rand(200, 50))                                                                 
96.4 ms ± 1.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In []: %timeit updown(np.random.rand(200, 50))                                                                      
5.84 ms ± 64 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Теперь разница коэффициент около 16,5.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...