Что является более быстрым способом перебора массива в Python? - PullRequest
1 голос
/ 28 июня 2019

Мне интересно, есть ли лучший способ перебирать массивы numpy?Я рассчитал свои вложенные итерации, и это занимает примерно 40-50 секунд на цикл, и мне интересно, есть ли более быстрый способ сделать это?Я знаю, что циклически перебирать массивы не идеально, но я в конце!Я просмотрел много вопросов о stackoverflow, но все они в итоге приводят меня в замешательство еще больше.

Я попытался преобразовать массив numpy в список с помощью функции tolist(), однако время выполнения равно медленнее, еслине хуже.

def euc_distance(array1, array2):
    return np.power(np.sum((array1 - array2)**2) , 0.5)

for i in range(N):
    for j,n in enumerate(data2.values): 
        distance = euc_distance(n, D[i]) 
        if distance < Dradius[i] and NormAttListTest[j] == "Attack":
            TP += 1

Моя функция euc_distance передает в виде массива (в моем случае, 5-мерных) входные данные для вывода 1-мерного значения.Мой data2.values - это мой способ доступа к массиву пустышек через платформу pandas, которая представляет собой [500 000, 5] фрейм данных.(Обратите внимание, что NormAttListTest - это список, в котором категориальные данные «Атака» и «Нормальный» помечены для каждого отдельного тестового данных)

1 Ответ

0 голосов
/ 28 июня 2019

Ваша проблема в том, что вы используете numpy неправильно, потому что numpy - это все векторизованные вычисления, такие как MATLAB.Рассмотрим следующую модификацию вашего кода.Я заменил ваш цикл над массивом Numpy на простой код Numpy, который эффективно использует векторизацию для 2d массивов.В результате код работает в 100 раз быстрее.

import functools
import numpy as np
import time

# decorator to measure running time
def measure_running_time(echo=True):
    def decorator(func):
        @functools.wraps(func)
        def wrapped(*args, **kwargs):
            t_1 = time.time()
            ans = func(*args, **kwargs)
            t_2 = time.time()
            if echo:
                print(f'{func.__name__}() running time is {t_2 - t_1:.2f} s')
            return ans
        return wrapped
    return decorator


def euc_distance(array1, array2):
    return np.power(np.sum((array1 - array2) ** 2), 0.5)

# original function
@measure_running_time()
def calculate_TP_1(N, data2, D, Dradius, NormAttListTest, TP=0):
    for i in range(N):
        for j, n in enumerate(data2):
            distance = euc_distance(n, D[i])
            if distance < Dradius[i] and NormAttListTest[j] == "Attack":
                TP += 1
    return TP

# new version
@measure_running_time()
def calculate_TP_2(N, data2, D, Dradius, NormAttListTest, TP=0):

    # this condition is the same for every i value
    NormAttListTest = np.array([val == 'Attack' for val in NormAttListTest])

    for i in range(N):

        # don't use loop over numpy arrays

        # compute distance for all the rows
        distance = np.sum((data2 - D[i]) ** 2, axis=1) ** .5
        # check conditions for all the row
        TP += np.sum((distance < Dradius[i]) & (NormAttListTest))

    return TP


if __name__ == '__main__':

    N = 10
    NN = 100_000
    D = np.random.randint(0, 10, (N, 5))
    Dradius = np.random.randint(0, 10, (N,))
    NormAttListTest = ['Attack'] * NN
    NormAttListTest[:NN // 2] = ['Defence'] * (NN // 2)
    data2 = np.random.randint(0, 10, (NN, 5))

    print(calculate_TP_1(N, data2, D, Dradius, NormAttListTest))

    print(calculate_TP_2(N, data2, D, Dradius, NormAttListTest))

Вывод:

calculate_TP_1() running time is 7.24 s
96476
calculate_TP_2() running time is 0.06 s
96476
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...