Connor
Connor

Reputation: 1026

How do you efficiently repeat a 2D numpy array with an offset?

Say I have a 2D numpy array of points with three columns, and I want to repeat these points in the x direction, producing a new array which has the original set of points, plus a number of 'repeated' points which are translated (1, 2, 3, ..., n) times in the x direction by some vector <c, 0, 0>.

To be clear, say I had the point [0.1, 2.0, 5.0] in the original array, and I wanted to 'repeat' it 5 times using a vector <2, 0, 0>, I would have the following in the final array:

new_array = [[0.1, 2.0, 5.0], [2.1, 2.0, 5.0], [4.1, 2.0, 5.0], [6.1, 2.0, 5.0], [8.1, 2.0, 5.0], [10.1, 2.0, 5.0]]

Is there a way of doing this efficiently using inbuilt numpy vectorisation?

Upvotes: 1

Views: 688

Answers (3)

Connor
Connor

Reputation: 1026

My own answer, thanks to some prompting from the two answers already given. I have the answer for a structured array also - if anyone is interested I can edit this post and add it below.

Given a starting array: array = np.array([[1, 2, 3], ..., [10, 9, 8]]), a vector: vector = np.array([1, 3, 5]) and a desired number of repetitions: repeat = 5. You can use the following code:

new_array = np.repeat(array, repeat)
vector_shifts = np.array([vector*index for index in range(0, repeat)])
vector_shifts = vector_shifts[np.newaxis, :]
vector_shifts = np.repeat(vector_shifts, array.shape[0], axis=0)

np.newaxis creates an extra axis which stops shifts grouping when repeated along, which is the standard np.repeat() behaviour. For example np.repeat(np.array([[1, 2, 4], [2, 3, 5]], 2) gives an output: np.array([[1, 2, 4], [1, 2, 4], [2, 3, 5], [2, 3, 5]].

vector_shifts = vector_shifts.reshape(vector_shifts.shape[0]*vector_shifts.shape[1], 3)

This removes the additional axis added for np.repeat() to perform as desired. Finally, the vector shifts can be added to the new array to give an expanded array of repeated points.

new_array += vector_shift

Upvotes: 0

hpaulj
hpaulj

Reputation: 231355

A variation on the accepted answer:

In [33]: point = np.array([0.1, 2.0, 5.0])
In [34]: vector = np.array([2,0,0])
In [35]: np.arange(5)[:,None]*vector
Out[35]: 
array([[0, 0, 0],
       [2, 0, 0],
       [4, 0, 0],
       [6, 0, 0],
       [8, 0, 0]])
In [36]: np.arange(5)[:,None]*vector + point
Out[36]: 
array([[0.1, 2. , 5. ],
       [2.1, 2. , 5. ],
       [4.1, 2. , 5. ],
       [6.1, 2. , 5. ],
       [8.1, 2. , 5. ]])

Your comment mentions a structured array. For example if point has 3 fields, and you want to modify the first:

In [37]: arr = np.array([(0.1, 2.0, 5.0)], dtype='f,f,f')
In [38]: arr
Out[38]: array([(0.1, 2., 5.)], dtype=[('f0', '<f4'), ('f1', '<f4'), ('f2', '<f4')])
In [41]: arr = arr.repeat(5)
In [42]: arr
Out[42]: 
array([(0.1, 2., 5.), (0.1, 2., 5.), (0.1, 2., 5.), (0.1, 2., 5.),
       (0.1, 2., 5.)], dtype=[('f0', '<f4'), ('f1', '<f4'), ('f2', '<f4')])
In [43]: arr['f0']
Out[43]: array([0.1, 0.1, 0.1, 0.1, 0.1], dtype=float32)
In [44]: arr['f0'] += np.arange(5)*2
In [45]: arr
Out[45]: 
array([(0.1, 2., 5.), (2.1, 2., 5.), (4.1, 2., 5.), (6.1, 2., 5.),
       (8.1, 2., 5.)], dtype=[('f0', '<f4'), ('f1', '<f4'), ('f2', '<f4')])

You can't modify all 3 fields at the same time. Math does not work across fields.

Upvotes: 1

Reti43
Reti43

Reputation: 9796

import numpy as np

point = [0.1, 2, 5]
vector = [2, 0, 0]
n = 5

a = np.zeros((n, len(vector)))
a[1:] = vector
a = np.cumsum(a, axis=0) + point

a[1:] = vector will create the array

[[0. 0. 0.]
 [2. 0. 0.]
 [2. 0. 0.]
 [2. 0. 0.]
 [2. 0. 0.]]

which is the difference you want per row. You then cumulatively summate them and add the point as offset.

Upvotes: 1

Related Questions