Python: How to assign values in n arbitrary dimensions?

Asked

Viewed 71 times

0

I have two n-dimensional arrays (i.e. Len(A.Shape)==Len(B.Shape)), and one 'fits' in the other (be the first A, the second B, for all dimensao i of A and B, A.Shape[i]>=B.Shape[i]) I want to assign the values of B to the initial indices of A.

For n fixed, say, 3, it is trivial:

s=B.shape
A[:s[0],:s[1],:s[2]]=B

However, for arbitrary n (dependent on the dimension of the arrays, passed by the user), how can I do it? There is no rating

s=B.shape
A[:s[0],:s[1],:s[2],...,:s[n]]=B

Some insight?

  • That’s what, it’s the Pandas package, Numpy?

  • numpy. But I can change if there is solution in some other package.

  • You don’t need to switch just to identify which documentation to read when looking for the solution.

1 answer

1

Come on -

Numpy makes an interesting use of the syntax of objeto[ ...] Python, and can use it to address multidimensional indexes and even index ranges in each dimension - as well as other uses (as element mask, if the object within the index is a Boolean array, for example).

What allows all this is Python’s operator overload mechanism - basically what’s inside the brackets in a phrase like objeto[...] is passed to the method __getitem__ of the same object, if it is an access for reading, and for the method __setitem__ if it is an allocation access (objeto[...] = ...).

And there are basically two things more defined in the syntax of language: elements inside the bracket separated by , are passed as a tuple to the __getitem__/__setitem__,and "slices" defined with the use of : are passed as an object of the type slice. An access of the kind objeto[0:5, 2:7] is passed as a 2-position tuple, each containing an object of type slice. A slice in turn is a very simple object - which basically has three attributes start, stop and step.

All this to say that: you can build this High School tuple programmatically, as you create any tuple, and call explicitly the method __setitem__.

So you can do it like this -


In [1]: import numpy as np                                                                                                                          

In [2]: a np.zeros((5,5,5))                                                                                                                         
  File "<ipython-input-2-9dd98a1448d1>", line 1
    a np.zeros((5,5,5))
       ^
SyntaxError: invalid syntax


In [3]: a =np.zeros((5,5,5))                                                                                                                        

In [4]: b = np.zeros((3, 3, 3))                                                                                                                     

In [5]: b += 5                                                                                                                                      

In [6]: b                                                                                                                                           
Out[6]: 
array([[[5., 5., 5.],
        [5., 5., 5.],
        [5., 5., 5.]],

       [[5., 5., 5.],
        [5., 5., 5.],
        [5., 5., 5.]],

       [[5., 5., 5.],
        [5., 5., 5.],
        [5., 5., 5.]]])

In [7]: a                                                                                                                                           
Out[7]: 
array([[[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],
...
       [[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]]])


In [10]: indices = tuple(slice(0, b.shape[i]) for i in range(len(b.shape)))                                                                        

In [11]: a.__setitem__(indices, b)                                                                                                                  

In [12]: a                                                                                                                                          
Out[12]: 
array([[[5., 5., 5., 0., 0.],
        [5., 5., 5., 0., 0.],
        [5., 5., 5., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[5., 5., 5., 0., 0.],
        [5., 5., 5., 0., 0.],
        [5., 5., 5., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[5., 5., 5., 0., 0.],
        [5., 5., 5., 0., 0.],
        [5., 5., 5., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]]])

The expression indices = tuple(slice(0, b.shape[i]) for i in range(len(b.shape))) does what I described above: for each dimension in the Shape of b (in case 3x3x3 and 5x5x5 for a: dimension and length of Shape are 3), creates an object "Slice" saying the size of that dimension we want to have as "target" in "a" - Slice(0, 3) inside the tuple is the same as [0:3, ...] inside the square bracket.

I suppose you understand the for inline there in that expression - the whole slice(0, b.shape[i]) for i in range(len(b.shape)) is a "Generator Expression", which when passed as parameter to the class constructor tuple creates a tuple with each generated value.

And in sequence, the method a.__setitem__ is called, as described above: with the explicit Slices tuple behaving exactly like the expression [0:3, 0:3, 0:3] - and the second parameter the "value" that we will assign to the selected schools. (As the Slices reproduce the Shape of b, it 'fits' right).


Now, assuming this mechanism of __setitem__ did not exist, or it was much more complicated - another way would be to programmatically generate the "a[0:3, 0:3, [0:3] = b" as a string, and make a call to exec- which compiles and executes a string containing Python code. The use of eval and exec however it is not recommended if there is no need: it is easy to introduce security vulnerabilities in code using it, and their performance is also very bad (because it involves compiling the Python code in the string).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.