Избегайте 3 для циклов с numpy, чтобы иметь более эффективный код

#python #performance #numpy #time

Вопрос:

Я разработал код, и я ищу более эффективный метод, потому что он очень медленный. Можно ли изменить этот код таким образом, чтобы он работал быстрее?

Код действительно сложен для объяснения, заранее извините, если мое объяснение не столь удовлетворительно:

Я работаю с этой большой матрицей, которая содержит 7 матриц размером 50×1000. Код работает таким образом:

  1. Я беру первый элемент каждой матрицы (содержащейся в большой матрице), создавая список этих элементов—> [a1b1, a2b1, … a7b1]
  2. После создания этого списка я интерполирую его, создавая новый список из 50 элементов
  3. Теперь мне нужно повторить пункты (1) и (2), но для первого элемента второй строки для всех матриц, до первого элемента последней строки.
  4. После завершения всех элементов первого столбца мы можем переключиться на второй столбец всех матриц и повторить точки (1),(2),(3)

Если что-то не понятно, пожалуйста, скажите мне, я постараюсь объяснить! введите описание изображения здесь

 import numpy as np
from scipy.interpolate import barycentric_interpolate

matrix = np.random.rand(7, 50, 1000)

X = np.linspace(0.1,0.8,7)
intervals = np.linspace(0.5,1.5,50)

matrix_tot = []
for col in range(len(matrix[0][0])):
    matrix_i = []
    for row in range(len(matrix[0])):
        interp_1 = []
        for m in range(len(matrix)):
            values = matrix[m][row][col]
            interp_1.append(values)
        row_interpolated = barycentric_interpolate(X,np.array(interp_1),intervals)
        matrix_i.append(row_interpolated)
    matrix_tot.append(matrix_i)
matrix_tot = np.array(matrix_tot)
print(matrix_tot.shape)
 

Комментарии:

1. вы можете избежать одного цикла , но два других цикла (более 50 и 1000) неизбежны, так как barycentric_interpolate принимает только массивы 1D в качестве входных данных

2. @Murali Как я могу избежать хотя бы одного цикла for? Для меня очень хорошо, если есть какой-то способ избежать одного цикла, потому что с тремя это слишком дорого

3. вы можете удалить «для m в диапазоне(len(матрица))» . вы можете вычислить np.массив(interp_1) с помощью матрицы[:, строка ,col] , которая даст вам массив из всех 7 (строка ,col) элементов

Ответ №1:

Понял. К счастью, вы можете предоставить несколько y векторов в одном вызове функции. Кажется, что он векторизован, потому что прирост производительности составляет 2 порядка.

 import numpy as np
from scipy.interpolate import barycentric_interpolate
import time

matrix = np.random.rand(7, 50, 1000)
X = np.linspace(0.1,0.8,7)
intervals = np.linspace(0.5,1.5,50)


start = time.process_time()
matrix_tot = []
for col in range(len(matrix[0][0])):
    matrix_i = []
    for row in range(len(matrix[0])):
        interp_1 = []
        for m in range(len(matrix)):
            values = matrix[m][row][col]
            interp_1.append(values)
        row_interpolated = barycentric_interpolate(X,np.array(interp_1),intervals)
        matrix_i.append(row_interpolated)
    matrix_tot.append(matrix_i)
matrix_tot = np.array(matrix_tot)
print('baseline: ', time.process_time() - start)

## ANSWER HERE:
start = time.process_time()
matrix_reshaped = matrix.reshape(7, 50 * 1000)
matrix_tot2 = barycentric_interpolate(X, matrix_reshaped, intervals).reshape(50, 50, 1000)
# this is only for comparison with matrix_tot, you may want to remove line below:
matrix_tot2 = matrix_tot2.transpose([2, 1, 0])
print('vectorised: ', time.process_time() - start)

assert np.allclose(matrix_tot, matrix_tot2, atol=1e-8)
 

Результат:

 baseline:  5.796875
vectorised:  0.015625
 

И ни одного цикла for 🙂

Комментарии:

1. Вау, это потрясающе..

Ответ №2:

Я получаю незначительное повышение производительности, делая следующее

 import numpy as np
from scipy.interpolate import barycentric_interpolate
import time

matrix = np.random.rand(7, 50, 1000)
X = np.linspace(0.1,0.8,7)
intervals = np.linspace(0.5,1.5,50)

def interpolate(a):
    return barycentric_interpolate(X, a, intervals)
start = time.process_time()
out = np.apply_along_axis(interpolate, 0, matrix)
print(time.process_time() - start)
 

Что возвращает время ~3,52 секунды по сравнению с 3,76 секундами на моей машине.
Как примечание, выходная матрица здесь (50, 50, 1000). Чтобы получить размеры вашей матрицы, просто переместите ее.

 np.all(out.T == matrix_tot)