1

Im having a problem I am struggling with.

I have an array with shape (3,4,5) which contains the values across 3 timesteps, for 4 indicators and 5 regions. For the purposes of this example lets say this array is:

values = np.arange(60).reshape(3,4,5)

I also have a 'map' which shows the location of the 5 regions on a 5x5 grid. e.g.

map = np.array([[1,1,1,2,2],
               [1,1,2,2,2],
               [3,3,3,2,2],
               [3,3,4,4,4],
               [3,3,5,5,4]])

What I want is to end up with an array (result) which presents the values on a 5x5 grid (similar to map), per timestep (3) and indicator (4). so

>>>result.shape 
(3,4,5,5)

I hope this makes sense.

2 Answers 2

1

If I understand correctly, the 5 in values.shape is not related to the (5, 5) in map.shape, but only to the range of values in map. The first thing to do is to make the values in map follow the conventional python indexing convention: start counting from zero, not from one.

Here is how to do it with the map having a different shape than the values:

import numpy as np

m, n, p = 2, 3, 4
values = np.arange(m*n*p).reshape(m, n, p)

map = np.array([
     [0, 0, 0, 3, 3, 3],
     [0, 0, 1, 3, 3, 2],
     [1, 1, 1, 3, 2, 2],
     [2, 1, 2, 2, 2, 1],
     [2, 2, 2, 2, 1, 1]
     ])

q, r = map.shape

map_r = map.reshape(q*r)

# result_r: shape (q*r, m, n)
# If you want to keep the 1-based numbering, index as [:, :, i-1].
result_r = np.array([values[:, :, i] for i in map_r])
result = result_r.transpose(1, 2, 0).reshape(m, n, q, r)

# test
jq, jr = 4, 5
mapval = map[jq, jr]
print(f'map[{jq}, {jr}] = {mapval}')
print(f'result[:, :, {jq}, {jr}] =\n{result[:, :, jq, jr]}')
print(f'values[:, :, {mapval}] =\n{values[:, :, mapval]}')

Output:

map[4, 5] = 1
result[:, :, 4, 5] =
[[ 1  5  9]
 [13 17 21]]
values[:, :, 1] =
[[ 1  5  9]
 [13 17 21]]

As an aside: multidimensional arrays often work more cleanly if you define the left-most index to represent a higher level concept. If you can phrase it as "a list of N lists of M widgets", the natural array shape would be (N, M), not (M,N). So, you would have values.shape = (p, m, n) and result.shape (q,r,m,n) (and maybe swap m and n as well, depending on how you will process the data later on). You would avoid a lot of [:, :] indices and transpose operations.

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

1 Comment

Thanks, that works like a charm! Gonna spend some time playing around with this method as it will be very useful for me.
0

Here is an implementation using np.pad:

values = np.arange(60).reshape(3,4,5)

# I renamed map to mapping, because map is a basic function in python

mapping = np.array([[1,1,1,2,2],
                  [1,1,2,2,2],
                  [3,3,3,2,2],
                  [3,3,4,4,4],
                  [3,3,5,5,4]])

v0,v1,v2 = values.shape
m0,m1 = mapping.shape

new = np.zeros((v0,v1,m0,m1))

for a in range(v2):
    q = values[:,:,a].reshape(v0,v1,1)
    r,c = np.where((mapping-1)==a)
    npad = ((0,0), (0,0), (0,len(c)-1))
    new[:,:,r,c] = np.pad(q, pad_width=npad, mode='symmetric')

1 Comment

The np.where is a slow operation if you do this repeatedly (inside a loop) on a large dataset.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.