Each place in the grid is associated with a tuple consisting of one value from asp , slp and elv . For example, the upper left corner has a tuple (8,9,13) . We would like to match this tuple with a number that uniquely identifies this set.
One way to do this is to think of (8,9,13) as an index in the np.arange(9*13*17).reshape(9,13,17) 3D array. This particular array was chosen to hold the largest values โโin asp , slp and elv :
In [107]: asp.max()+1 Out[107]: 9 In [108]: slp.max()+1 Out[108]: 13 In [110]: elv.max()+1 Out[110]: 17
Now we can match the set (8,9,13) with the number 1934:
In [113]: x = np.arange(9*13*17).reshape(9,13,17) In [114]: x[8,9,13] Out[114]: 1934
If we do this for each place in the grid, we will get a unique number for each location. We could end up here by allowing these unique numbers to serve as tags.
Or, we can generate smaller labels (starting at 0 and increasing by 1) using np.unique with return_inverse=True :
uniqs, labels = np.unique(vals, return_inverse=True) labels = labels.reshape(vals.shape)
So for example
import numpy as np asp = np.array([8,1,1,2,7,8,2,3,7,6,4,3,6,5,5,4]).reshape((4,4)) #aspect slp = np.array([9,10,10,9,9,12,12,9,10,11,11,9,9,9,9,9]).reshape((4,4)) #slope elv = np.array([13,14,14,13,14,15,16,14,14,15,16,14,13,14,14,13]).reshape((4,4)) #elevation x = np.arange(9*13*17).reshape(9,13,17) vals = x[asp, slp, elv] uniqs, labels = np.unique(vals, return_inverse=True) labels = labels.reshape(vals.shape)
gives
array([[11, 0, 0, 1], [ 9, 12, 2, 3], [10, 8, 5, 3], [ 7, 6, 6, 4]])
This method works fine as long as the values โโin asp , slp and elv are small integers. If the integers were too large, the product of their maxima could overwhelm the maximum allowable value that can be transferred to np.arange . Moreover, creating such a large array would be inefficient. If the values โโwere float, then they could not be interpreted as indices in the 3D array x .
So, to solve these problems, use np.unique to convert the values โโin asp , slp and elv to unique integer labels:
indices = [ np.unique(arr, return_inverse=True)[1].reshape(arr.shape) for arr in [asp, slp, elv] ] M = np.array([item.max()+1 for item in indices]) x = np.arange(M.prod()).reshape(M) vals = x[indices] uniqs, labels = np.unique(vals, return_inverse=True) labels = labels.reshape(vals.shape)
which gives the same result as above, but works even if asp , slp , elv were float and / or large integers.
Finally, we can avoid generating np.arange :
x = np.arange(M.prod()).reshape(M) vals = x[indices]
by computing vals as the product of indices and steps:
M = np.r_[1, M[:-1]] strides = M.cumprod() indices = np.stack(indices, axis=-1) vals = (indices * strides).sum(axis=-1)
So, all together:
import numpy as np asp = np.array([8,1,1,2,7,8,2,3,7,6,4,3,6,5,5,4]).reshape((4,4)) #aspect slp = np.array([9,10,10,9,9,12,12,9,10,11,11,9,9,9,9,9]).reshape((4,4)) #slope elv = np.array([13,14,14,13,14,15,16,14,14,15,16,14,13,14,14,13]).reshape((4,4)) #elevation def find_labels(*arrs): indices = [np.unique(arr, return_inverse=True)[1] for arr in arrs] M = np.array([item.max()+1 for item in indices]) M = np.r_[1, M[:-1]] strides = M.cumprod() indices = np.stack(indices, axis=-1) vals = (indices * strides).sum(axis=-1) uniqs, labels = np.unique(vals, return_inverse=True) labels = labels.reshape(arrs[0].shape) return labels print(find_labels(asp, slp, elv)) # [[ 3 7 7 0] # [ 6 10 12 4] # [ 8 9 11 4] # [ 2 5 5 1]]