0

For example I have a matrix array

a=np.arrange(25).shape(5,5)
[[ 0  1  2  3  4]
 [ 5  6  7  8  9]
 [10 11 12 13 14]
 [15 16 17 18 19]
 [20 21 22 23 24]]

How do I make an 1D array of elements that I would like to choose manually? For example [2,3], [4,1], [1,0] and [2,2], so I get the following:

b=[13, 21, 5, 12]

The array should a reference rather than a copy.

MrCheatak
  • 159
  • 7
  • Are you asking if it is possible to create a discontiguous view of an array? – John Coleman Feb 24 '21 at 12:24
  • Yes, I've looked into Numpy indexing routines and non of the functions seem to provide such an option – MrCheatak Feb 24 '21 at 12:34
  • 1
    You could perhaps write your own class called something like `CustomView` for this as a sort of wrapper around `a.view()`, though that would obviously be slower than a built-in approach. While it seems like it would be a nice thing to have, I agree that there isn't an obvious built-in solution (which doesn't mean that there isn't a built-in solution). – John Coleman Feb 24 '21 at 13:36
  • the results for b must be wrong for 2,3 and 4,1 – Golden Lion Feb 24 '21 at 16:22
  • @GoldenLion [4,1] was wrong indeed, thanks – MrCheatak Feb 25 '21 at 09:19
  • [Here](https://stackoverflow.com/questions/47181092/numpy-views-vs-copy-by-slicing) it is explained why it is not natively possible. In short, only contiguous chunks of data can be 'viewed' in Numpy, otherwise if referenced data is not stored regularly in the memory, all the magic speed of array manipulation is lost. – MrCheatak Feb 26 '21 at 13:48

3 Answers3

1

You can make a function for this.

# defining the function
def get_value(matrix, row_list, col_list):
    for i, j in zip(row_list, col_list):
        return matrix[row_list, col_list]

# initializing the array
a = np.arange(0, 25, 1).reshape(5, 5)

# getting the required values and printing
b = get_value(a, [2,4,1,0], [3,1,0,2])

# output
print(b)

Edit

I'll let the previous answer be as is, just in case if anyone else stumbles upon that and needs it.

What the question wants is to give a value from b (i.e. b[0] which is 13) and change the value from the original matrix a based on the index of that passed value from b in a.

def change_the_value(old_mat, val_to_change, new_val):
    mat_coor = np.array(np.matrix(np.where(old_mat == val_to_change)).T)[0]
    old_mat[mat_coor[0], mat_coor[1]] = new_val
 
a = np.arange(0, 25, 1).reshape(5,5)
b = [13, 16, 5, 12]

change_the_value(a, b[0], 0)
0
a=np.arange(25).reshape(5,5)
search=[[2,3], [4,1], [1,0], [2,2]]
for row,col in search:
    print(row,col, a[row][col])

output:

r c result
2 3 13
4 1 21
1 0 5
2 2 12
SHR
  • 7,940
  • 9
  • 38
  • 57
Golden Lion
  • 3,840
  • 2
  • 26
  • 35
  • Strictly that works, but involves lists, which are not as quick as Numpy arrays. I am using same solution now though, but with a list of tuples as they qualify as ready-to-go indices. I was thinking about a more native Numpy solution. – MrCheatak Feb 25 '21 at 09:16
0

First of all, I've found that constructing a non-contiguous view to a Numpy array is not natively possible, because Numpy efficiently utilises contiguous memory layout of an array, which enables dramatic speed increase.

Here's a solution I found that works the best so far: Instead of having a view to an array, I construct a collection indices, that I would like to process, [2,3], [4,1], [1,0], [2,2]. The collection type I have chosen are Sets, due to exclusion of duplicates and set().add and set().discard methods that do not require search. Keeping order was not necessary.

To use them for indexing an array they have to be casted from a set of tuples set{(2,3),(4,1),(1,0),(2,2)} to a tuple of arrays (ndarray([2,4,1,2], ndarray[3,1,0,2]).

Which can be achieved by unzipping a set and constructing a tuple of arrays:

import numpy as np
a=np.arrange(25).shape(5,5)
>>>[[ 0  1  2  3  4]
    [ 5  6  7  8  9]
    [10 11 12 13 14]
    [15 16 17 18 19]
    [20 21 22 23 24]]

my_set = {(2,3),(4,1),(1,0),(2,2)}
uzip_set = list(zip(*my_set))
seq_from_set = (np.asarray(uzip_set[0]),np.asarray(uzip_set[1]))
print(seq_from_set)
>>>(array[2,4,1,2], array[3,1,0,2])

And array a can be manipulated by providing such a sequence of indices:

b = a[seq_from_set]
print(b)
>>>array[13,21,5,12]  
a[seq_from_set] = 0
print(a)   
 >>>[[ 0  1  2  3  4]
     [ 0  6  7  8  9]
     [10 11  0  0 14]
     [15 16 17 18 19]
     [20  0 22 23 24]]

The solution is a bit sophisticated compared to something native, but works surprisingly fast. This allows an easy management of the collection of indices and supports quick conversion to a stream of indices on demand.

MrCheatak
  • 159
  • 7