2013-12-03 3 views
1

Давайте представим пустой массив NumPy 3x4, где у вас есть координата верхнего левого угла и размер шага в горизонтальном и вертикальном направлениях. Теперь я хотел бы знать координаты для середины каждой ячейки для всего массива. Как это:Оптимизация вложенного цикла for, который использует индексы массива для функции

enter image description here

Для этого я реализовал вложенную для цикла.

In [12]: 
import numpy as np 
# extent(topleft_x, stepsize_x, 0, topleft_y, 0, stepsize_y (negative since it's top-left) 
extent = (5530000.0, 5000.0, 0.0, 807000.0, 0.0, -5000.0) 

array = np.zeros([3,4],object) 
cols = array.shape[0] 
rows = array.shape[1] 

# function to apply to each cell 
def f(x,y): 
return x*extent[1]+extent[0]+extent[1]/2, y*extent[5]+extent[3]+extent[5]/2 

# nested for-loop 
def nestloop(cols,rows): 
    for col in range(cols): 
     for row in range(rows):   
      array[col,row] = f(col,row)    

In [13]: 
%timeit nestloop(cols,rows) 
100000 loops, best of 3: 17.4 µs per loop 

In [14]: 
array.T 
Out[14]: 
array([[(5532500.0, 804500.0), (5537500.0, 804500.0), (5542500.0, 804500.0)], 
     [(5532500.0, 799500.0), (5537500.0, 799500.0), (5542500.0, 799500.0)], 
     [(5532500.0, 794500.0), (5537500.0, 794500.0), (5542500.0, 794500.0)], 
     [(5532500.0, 789500.0), (5537500.0, 789500.0), (5542500.0, 789500.0)]], dtype=object) 

Но как узнать, как я могу это оптимизировать? Я думал о векторизации или использовании лямбды. Я попытался его векторизовать следующим образом:

array[:,:] = np.vectorize(check)(cols,rows) 
ValueError: could not broadcast input array from shape (2) into shape (3,4) 

Но, чем я получил ошибку вещания. В настоящее время массив равен 3 на 4, но это также может стать 3000 на 4000.

ответ

3

Несомненно, способ, которым вы вычисляете координаты x и y, крайне неэффективен, потому что он вообще не векторизован. Вы можете сделать:

In [1]: import numpy as np 

In [2]: extent = (5530000.0, 5000.0, 0.0, 807000.0, 0.0, -5000.0) 
    ...: x_steps = np.array([0,1,2]) * extent[1] 
    ...: y_steps = np.array([0,1,2,3]) * extent[-1] 
    ...: 

In [3]: x_coords = extent[0] + x_steps + extent[1]/2 
    ...: y_coords = extent[3] + y_steps + extent[-1]/2 
    ...: 

In [4]: x_coords 
Out[4]: array([ 5532500., 5537500., 5542500.]) 

In [5]: y_coords 
Out[5]: array([ 804500., 799500., 794500., 789500.]) 

На данный момент координаты точек задаются в декартовой product() этих двух массивов:

In [5]: list(it.product(x_coords, y_coords)) 
Out[5]: [(5532500.0, 804500.0), (5532500.0, 799500.0), (5532500.0, 794500.0), (5532500.0, 789500.0), (5537500.0, 804500.0), (5537500.0, 799500.0), (5537500.0, 794500.0), (5537500.0, 789500.0), (5542500.0, 804500.0), (5542500.0, 799500.0), (5542500.0, 794500.0), (5542500.0, 789500.0)] 

Вы просто должны сгруппировать их 4 на 4.

Чтобы получить продукт с numpy вы можете сделать (на основе this ответа):

In [6]: np.transpose([np.tile(x_coords, len(y_coords)), np.repeat(y_coords, len(x_coords))]) 
Out[6]: 
array([[ 5532500., 804500.], 
     [ 5537500., 804500.], 
     [ 5542500., 804500.], 
     [ 5532500., 799500.], 
     [ 5537500., 799500.], 
     [ 5542500., 799500.], 
     [ 5532500., 794500.], 
     [ 5537500., 794500.], 
     [ 5542500., 794500.], 
     [ 5532500., 789500.], 
     [ 5537500., 789500.], 
     [ 5542500., 789500.]]) 

Что может быть изменена:

In [8]: product.reshape((3,4,2)) # product is the result of the above 
Out[8]: 
array([[[ 5532500., 804500.], 
     [ 5537500., 804500.], 
     [ 5542500., 804500.], 
     [ 5532500., 799500.]], 

     [[ 5537500., 799500.], 
     [ 5542500., 799500.], 
     [ 5532500., 794500.], 
     [ 5537500., 794500.]], 

     [[ 5542500., 794500.], 
     [ 5532500., 789500.], 
     [ 5537500., 789500.], 
     [ 5542500., 789500.]]]) 

Если это не заказ вы хотите, вы можете сделать что-то вроде:

In [9]: ar = np.zeros((3,4,2), float) 
    ...: ar[0] = product[::3] 
    ...: ar[1] = product[1::3] 
    ...: ar[2] = product[2::3] 
    ...: 

In [10]: ar 
Out[10]: 
array([[[ 5532500., 804500.], 
     [ 5532500., 799500.], 
     [ 5532500., 794500.], 
     [ 5532500., 789500.]], 

     [[ 5537500., 804500.], 
     [ 5537500., 799500.], 
     [ 5537500., 794500.], 
     [ 5537500., 789500.]], 

     [[ 5542500., 804500.], 
     [ 5542500., 799500.], 
     [ 5542500., 794500.], 
     [ 5542500., 789500.]]]) 

Я считаю, есть лучшие способы сделать это последнее изменение формы, но я m не numpy эксперт.

Обратите внимание, что при использовании в качестве object DTYPE этого снижения производительности огромного, поскольку numpy ничего не может оптимизировать (и иногда медленнее, чем с помощью обычных list с). Вместо этого я использовал массив (3,4,2), который позволяет быстрее выполнять операции.

+0

Это довольно большое улучшение. Спасибо, что вы показали это – Mattijn

Смежные вопросы