2

So I was wondering of a pythonic way of doing this computation. I have a 2D numpy array with all zeros. Now depending on the co-ordinate I want to fill some values in it. To be specific let us say we have 2 integers (a,b), then in the array I want to fill each place with max(abs(index1-a), abs(index2-b)).

I was thinking of some way to solve this in a single line like, for example if we want to replace all negative elements with 0 then:

array[array<0] = 0

Any tips on how to solve it in a single line? I am not looking for single line loops or lambda expressions.

5
  • so the result should be m[i, j] = max(i, j)? Commented Sep 10, 2018 at 19:53
  • @WillemVanOnsem no (a,b) is fixed while index1 and index2 are the position of the element in the array I am looking at....to be specific the array is currently filled with zero's..now I want to fill it based on the current condition Commented Sep 10, 2018 at 19:54
  • And index1, and index2 are the "indices" of the matrix for that element, right? Commented Sep 10, 2018 at 19:56
  • @WillemVanOnsem yes Commented Sep 10, 2018 at 19:57
  • I don't know if that helps, but I would do some analysis on the function f(a,b) = max(abs(index1-a), abs(index2-b)) and try to find a way to predict the results for all indexes. For example, it seems like the function has a minimum in (index1=a,index2=b), where it corresponds to zero, and that from there for each increment of ±1 of one or both of the variables, f(a±1,b)=f(a,b±1)= f(a,b)+1. Edit: i mixed (index1,index2) with (a,b), but the result is the same given the abs() Commented Sep 10, 2018 at 20:38

2 Answers 2

1

Use np.indices + np.maximum:

a, b = 3, 5
i, j = np.indices((10, 7))
np.maximum(np.abs(i - a), np.abs(j - b)))

array([[5, 4, 3, 3, 3, 3, 3],
       [5, 4, 3, 2, 2, 2, 2],
       [5, 4, 3, 2, 1, 1, 1],
       [5, 4, 3, 2, 1, 0, 1],
       [5, 4, 3, 2, 1, 1, 1],
       [5, 4, 3, 2, 2, 2, 2],
       [5, 4, 3, 3, 3, 3, 3],
       [5, 4, 4, 4, 4, 4, 4],
       [5, 5, 5, 5, 5, 5, 5],
       [6, 6, 6, 6, 6, 6, 6]])

np.indices return the row and column indices as a 2D matrix; no reshaping required.

Sign up to request clarification or add additional context in comments.

3 Comments

@DuttaA This answer was written based on the discussion under the other answer.
Check the array provided by the other user...I want something like that
@DuttaA Okay, that should fix it
0

We can design such matrix with:

a = 3  # sample a
b = 5  # sample b
arr = np.zeros((10, 7))  # sample zeros
m, n = arr.shape
arr[:,:] = np.maximum(np.arange(m).reshape(-1,1) - a, np.arange(n) - b)

But if you do not need to modify the matrix itself, we can simply construct a new one (with arr = np.maximum(..)).

For a 10×7 matrix with a = 3, and b = 5, we then obtain:

>>> arr
array([[-3., -3., -3., -2., -1.,  0.,  1.],
       [-2., -2., -2., -2., -1.,  0.,  1.],
       [-1., -1., -1., -1., -1.,  0.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  1.],
       [ 1.,  1.,  1.,  1.,  1.,  1.,  1.],
       [ 2.,  2.,  2.,  2.,  2.,  2.,  2.],
       [ 3.,  3.,  3.,  3.,  3.,  3.,  3.],
       [ 4.,  4.,  4.,  4.,  4.,  4.,  4.],
       [ 5.,  5.,  5.,  5.,  5.,  5.,  5.],
       [ 6.,  6.,  6.,  6.,  6.,  6.,  6.]])

EDIT: if we want absolute values np.abs(..):

arr[:,:] = np.maximum(np.abs(np.arange(m).reshape(-1,1) - a), np.abs(np.arange(n) - b))

then the result is:

>>> arr
array([[5, 4, 3, 3, 3, 3, 3],
       [5, 4, 3, 2, 2, 2, 2],
       [5, 4, 3, 2, 1, 1, 1],
       [5, 4, 3, 2, 1, 0, 1],
       [5, 4, 3, 2, 1, 1, 1],
       [5, 4, 3, 2, 2, 2, 2],
       [5, 4, 3, 3, 3, 3, 3],
       [5, 4, 4, 4, 4, 4, 4],
       [5, 5, 5, 5, 5, 5, 5],
       [6, 6, 6, 6, 6, 6, 6]])

Performance

We defined two functions f and g:

>>> def f():
...   a, b = 3, 5
...   m, n = 10, 7
...   arr = np.zeros((m, n))
...   arr[:,:] = np.maximum(np.arange(m).reshape(-1,1) - a, np.arange(n) - b)
...
>>> def g():
...   a, b = 3, 5
...   m, n = 10, 7
...   arr = np.zeros((m, n))
...   i, j = np.indices((m, n))
...   arr[:,:] = np.maximum(np.abs(i - a), np.abs(j - b))

then we run both functions 1'000'000 times:

>>> timeit(f, number=1000000)
7.471106000011787
>>> timeit(g, number=1000000)
18.07209299999522

We also ran it 1'000 times for an 1000×700 matrix:

>>> timeit(f, number=1000)
2.5362389999936568
>>> timeit(g, number=1000)
43.055561000001035

My hypothesis for this "performance gap" is that the np.indices() creates a 2×m×n matrix, which thus allocates a similar amount of memory. By constructing two 1d arrays, we take linear time in the intermediate matrices, and make use of broadcasting to calculate the elements of the final matrix.

9 Comments

How is it giving those zeros though?
@DuttaA: well that is where the column index is 5, and the row index is 3, since then max(i-3, j-5) is 0, since if i is smaller, but j=5, then the result is 5, and the same for the transpose.
You are technically correct...it is a mistake in my question, I actually wanted the positive distance..that means mod(index1-a)..sorry my bad..your answer is quite nice
Well then you should wrap it within np.abs(..)
but the 0's will remain 0's, ..I modified my question
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.