Эффективно генерировать решетку точек в python
помогите сделать мой код быстрее: мой Python код должен генерировать 2D решетки точек, попадающих в прямоугольник. Я kludged вместе какой-то код (как показано ниже), что создает эту решетку. Однако эта функция вызывается много раз и стала серьезным узким местом в моем приложении.
Я уверен, что есть более быстрый способ сделать это, вероятно, связанных массивов numpy вместо списков. Любые предложения для более быстрого, более элегантного способа сделать это?
описание функции: У меня есть два 2D вектора, v1 и v2. Эти векторы определить решетки. В моем случае мои векторы определяют решетку, которая почти, но не совсем, шестиугольная. Я хочу создать набор всех 2D точек на этой решетке, которые находятся в некотором ограничивающем прямоугольнике. В моем случае, один из углов прямоугольника находится в точке (0, 0), а другие углы при положительных координатах.
пример: Если далеко угол моего ограничивающего прямоугольника был в (3, 3), а мои векторы решетки были:
v1 = (1.2, 0.1)
v2 = (0.2, 1.1)
Я бы хотел, чтобы моя функция возвращала точки:
(1.2, 0.1) #v1
(2.4, 0.2) #2*v1
(0.2, 1.1) #v2
(0.4, 2.2) #2*v2
(1.4, 1.2) #v1 + v2
(2.6, 1.3) #2*v1 + v2
(1.6, 2.3) #v1 + 2*v2
(2.8, 2.4) #2*v1 + 2*v2
меня не интересуют граничные случаи; не имеет значения, возвращает ли функция (0, 0), например.
медленный способ, которым я сейчас это делаю:
import numpy, pylab
def generate_lattice( #Help me speed up this function, please!
image_shape, lattice_vectors, center_pix='image', edge_buffer=2):
##Preprocessing. Not much of a bottleneck:
if center_pix == 'image':
center_pix = numpy.array(image_shape) // 2
else: ##Express the center pixel in terms of the lattice vectors
center_pix = numpy.array(center_pix) - (numpy.array(image_shape) // 2)
lattice_components = numpy.linalg.solve(
numpy.vstack(lattice_vectors[:2]).T,
center_pix)
lattice_components -= lattice_components // 1
center_pix = (lattice_vectors[0] * lattice_components[0] +
lattice_vectors[1] * lattice_components[1] +
numpy.array(image_shape)//2)
num_vectors = int( ##Estimate how many lattice points we need
max(image_shape) / numpy.sqrt(lattice_vectors[0]**2).sum())
lattice_points = []
lower_bounds = numpy.array((edge_buffer, edge_buffer))
upper_bounds = numpy.array(image_shape) - edge_buffer
##SLOW LOOP HERE. 'num_vectors' is often quite large.
for i in range(-num_vectors, num_vectors):
for j in range(-num_vectors, num_vectors):
lp = i * lattice_vectors[0] + j * lattice_vectors[1] + center_pix
if all(lower_bounds < lp) and all(lp < upper_bounds):
lattice_points.append(lp)
return lattice_points
##Test the function and display the output.
##No optimization needed past this point.
lattice_vectors = [
numpy.array([-40., -1.]),
numpy.array([ 18., -37.])]
image_shape = (1000, 1000)
spots = generate_lattice(image_shape, lattice_vectors)
fig=pylab.figure()
pylab.plot([p[1] for p in spots], [p[0] for p in spots], '.')
pylab.axis('equal')
fig.show()
4 ответов
Если вы хотите векторизовать все это, создайте квадратную решетку,а затем срежьте ее. Затем отрежьте края, которые приземляются вне вашего ящика.
вот что я придумал. Есть еще много улучшений, которые можно было бы сделать, но это основная идея.
def generate_lattice(image_shape, lattice_vectors) :
center_pix = numpy.array(image_shape) // 2
# Get the lower limit on the cell size.
dx_cell = max(abs(lattice_vectors[0][0]), abs(lattice_vectors[1][0]))
dy_cell = max(abs(lattice_vectors[0][1]), abs(lattice_vectors[1][1]))
# Get an over estimate of how many cells across and up.
nx = image_shape[0]//dx_cell
ny = image_shape[1]//dy_cell
# Generate a square lattice, with too many points.
# Here I generate a factor of 4 more points than I need, which ensures
# coverage for highly sheared lattices. If your lattice is not highly
# sheared, than you can generate fewer points.
x_sq = np.arange(-nx, nx, dtype=float)
y_sq = np.arange(-ny, nx, dtype=float)
x_sq.shape = x_sq.shape + (1,)
y_sq.shape = (1,) + y_sq.shape
# Now shear the whole thing using the lattice vectors
x_lattice = lattice_vectors[0][0]*x_sq + lattice_vectors[1][0]*y_sq
y_lattice = lattice_vectors[0][1]*x_sq + lattice_vectors[1][1]*y_sq
# Trim to fit in box.
mask = ((x_lattice < image_shape[0]/2.0)
& (x_lattice > -image_shape[0]/2.0))
mask = mask & ((y_lattice < image_shape[1]/2.0)
& (y_lattice > -image_shape[1]/2.0))
x_lattice = x_lattice[mask]
y_lattice = y_lattice[mask]
# Translate to the centre pix.
x_lattice += center_pix[0]
y_lattice += center_pix[1]
# Make output compatible with original version.
out = np.empty((len(x_lattice), 2), dtype=float)
out[:, 0] = y_lattice
out[:, 1] = x_lattice
return out
С lower_bounds
и upper_bounds
только 2-элемент массива, включает в себя не может быть правильный выбор. Попробуйте заменить
if all(lower_bounds < lp) and all(lp < upper_bounds):
с базовым материалом Python:
if lower1 < lp and lower2 < lp and lp < upper1 and lp < upper2:
по данным timeit, второй подход намного быстрее:
>>> timeit.timeit('all(lower < lp)', 'import numpy\nlp=4\nlower = numpy.array((1,5))')
3.7948939800262451
>>> timeit.timeit('lower1 < 4 and lower2 < 4', 'lp = 4\nlower1, lower2 = 1,5')
0.074192047119140625
по моему опыту, пока вам не нужно обрабатывать N-уменьшенные данные, и если вам не нужны поплавки с двойной точностью, обычно быстрее использовать основные типы данных Python и конструкции вместо включает в себя, что немного перегрузка в таких случаях -- посмотрите на это другой вопрос.
еще одним незначительным улучшением может быть вычисление range(-num_vectors, num_vectors)
только один раз и затем повторно использовать его. Кроме того, вы можете использовать продукт итератор вместо вложенного цикла for-хотя я не думаю, что эти изменения окажут значительное влияние на производительность.
может быть, вы можете заменить два цикла для этого.
i,j = numpy.mgrid[-num_vectors:num_vectors, -num_vectors:num_vectors]
numel = num_vectors ** 2;
i = i.reshape(numel, 1)
j = j.reshape(numel, 1)
lp = i * lattice_vectors[0] + j * lattice_vectors[1] + center_pix
valid = numpy.all(lower_bounds < lp, 1) and numpy.all(lp < upper_bounds, 1)
lattice_points = lp[valid]
там могут быть некоторые незначительные ошибки, но вы получите идею..
редактировать
Я внес изменения в " numpy.все(lower_bounds..) "для учета правильного измерения.
Я получил более чем 2x ускорение, заменив ваше вычисление lp
с повторными дополнениями, а не умножениями. The xrange
оптимизация кажется несущественной (хотя, вероятно, это не повредит); повторные добавления кажутся более эффективными, чем умножения. Сочетание этого с другими оптимизациями, упомянутыми выше, должно дать вам больше ускорения. Но, конечно, лучшее, что вы можете получить, - это ускорение постоянным фактором, так как размер вашего выхода квадратичный, как и ваш исходный код.
lv0, lv1 = lattice_vectors[0], lattice_vectors[1]
lv0_i = -num_vectors * lv0 + center_pix
lv1_orig = -num_vectors * lv1
lv1_j = lv1_orig
for i in xrange(-num_vectors, num_vectors):
for j in xrange(-num_vectors, num_vectors):
lp = lv0_i + lv1_j
if all(lower_bounds < lp) and all(lp < upper_bounds):
lattice_points.append(lp)
lv1_j += lv1
lv0_i += lv0
lv1_j = lv1_orig
таймер результаты:
>>> t = Timer("generate_lattice(image_shape, lattice_vectors, orig=True)", "from __main__ import generate_lattice, lattice_vectors, image_shape")
>>> print t.timeit(number=50)
5.20121788979
>>> t = Timer("generate_lattice(image_shape, lattice_vectors, orig=False)", "from __main__ import generate_lattice, lattice_vectors, image_shape")
>>> print t.timeit(number=50)
2.17463898659