I have an n x m array and a function 'switch(A,J)' that takes array (A) and integer(J) input and outputs an array of size n x m. I wish to split my n x m array into arrays of dimension c x c and apply the function with a fixed J to each c x c array and output the resulting array.c may not be a factor of n or m. Would anyone know how to execute this please.
I have tried np.block to split the array and apply to each individual block but then i had trouble reconstructing the matrix. I also attempted to use the slice indexing and store the values in a new array but the issue is my function outputs complex values so these are all discarded when i try and append the new array,
CodePudding user response:
import numpy as np
array = np.array([[1, 2, 3, 1], [4, 5, 6, 4], [7, 8, 9, 7], [11, 22, 33, 44]])
def somefunc(some_array, some_integer):
return some_array*3
# say that your blocks needs to be 2X2
for i in range(array.shape[0]):
for j in range(array.shape[1]):
array[i*2:(i 1)*2, j*2:(j 1)*2] = somefunc(array[i*2:(i 1)*2, j*2:(j 1)*2], 3)
CodePudding user response:
A rather technical way to do is, but really efficient, is to use stride_tricks
, which provides "views" of your array.
c=4
N=12 # Note that c is a factor of N
# Just an example array
A=np.arange(1,N 1).reshape(1,-1) np.arange(100,100*(N 1),100).reshape(-1,1)
stR,stC=A.strides
View = np.lib.stride_tricks.as_strided(A, (N//c,N//c,c,c), (c*stR, c*stC,stR,stC))
# You can now vectorize your function to work on such data
def switch(X,J):
return X.T-J # Just an example
switchv=np.vectorize(switch, signature='(c,c),()->(c,c)')
# And now switchv can be called on your data
J=1
result=switchv(View,J)
Explanation. A here is
array([[ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112],
[ 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212],
[ 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312],
[ 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412],
[ 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512],
[ 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612],
[ 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712],
[ 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812],
[ 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912],
[1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012],
[1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112],
[1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212]])
A.strides
gives the number of bytes separating each level of the array. So here, each lines and then each elements in the lines.
In my example A.strides
is (96,8)
, because there are 8 bytes between two consecutive number (we don't really need to bother about bytes. we'll just copy those strides), and 96 between 2 lines (since there are 12 elements per lines, that is not surprising, but again, we don't really care. There could have been some gap between the lines, but that is not our problem).
np.lib.stride_tricks.as_strided
give a new view of A
, with a different shape, and a different way to switch from one level to another. It can even lead to some value being repeated. Note that it is just a view. No array is created here. It is a virtual array.
Here, we say that, instead of a NxN array, we want a (N/c)x(N/c)xcxc array. So a (N/c)x(N/c) arrays of blocks, block being cxc arrays of elements.
And each we provide a stride of (c*stR, c*stC,stR,stC)
. Reading that from right to left, that means that inside a row of a block, there are the same amount of bytes than between 2 elements of a line of A. So we will have c succesive elements of A. stR
means likewise that between two rows of a block, there is the same gap than between 2 rows of A. So we will also have c subsequent (incomplete but subsequent) rows of A. Then c*stC
means that two subsequent blocks are separated by c numbers. And c*stR
likewise.
So result is
array([[[[ 101, 102, 103, 104],
[ 201, 202, 203, 204],
[ 301, 302, 303, 304],
[ 401, 402, 403, 404]],
[[ 105, 106, 107, 108],
[ 205, 206, 207, 208],
[ 305, 306, 307, 308],
[ 405, 406, 407, 408]],
[[ 109, 110, 111, 112],
[ 209, 210, 211, 212],
[ 309, 310, 311, 312],
[ 409, 410, 411, 412]]],
[[[ 501, 502, 503, 504],
[ 601, 602, 603, 604],
[ 701, 702, 703, 704],
[ 801, 802, 803, 804]],
[[ 505, 506, 507, 508],
[ 605, 606, 607, 608],
[ 705, 706, 707, 708],
[ 805, 806, 807, 808]],
[[ 509, 510, 511, 512],
[ 609, 610, 611, 612],
[ 709, 710, 711, 712],
[ 809, 810, 811, 812]]],
[[[ 901, 902, 903, 904],
[1001, 1002, 1003, 1004],
[1101, 1102, 1103, 1104],
[1201, 1202, 1203, 1204]],
[[ 905, 906, 907, 908],
[1005, 1006, 1007, 1008],
[1105, 1106, 1107, 1108],
[1205, 1206, 1207, 1208]],
[[ 909, 910, 911, 912],
[1009, 1010, 1011, 1012],
[1109, 1110, 1111, 1112],
[1209, 1210, 1211, 1212]]]])
Same array. But viewed as a 3x3 arrays of 4x4 numbers.
Again, no copy, no array were created here. It is just another organisation.
And here come the second technicality: vectorize. vectorize
is a way to tell numpy to call a function on each elements of an array.
For a basic example
def f(a,b):
return a*b
fv=np.vectorize(f)
fv
is a function that can work on array of all shapes.
fv(np.arange(10), 2)
returns
array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
So it sees that the first argument is an array, iterates it, calls f on each of them, and produce an array of the results.
Exactly as *
already does on numpy arrays.
Adding a signature stop iterations at a certain level: since I said that signature of switch was (c,c),()->(c,c)
if I call the vectorized version with a (N/c,N/c,c,c)
array, it ill iterates the (c,c)
subarrays of it, but won't descend through all (c,) subarrays of them, nor all elements, because the atom here is (c,c)
arrays.
Result is
array([[[[ 100, 200, 300, 400],
[ 101, 201, 301, 401],
[ 102, 202, 302, 402],
[ 103, 203, 303, 403]],
[[ 104, 204, 304, 404],
[ 105, 205, 305, 405],
[ 106, 206, 306, 406],
[ 107, 207, 307, 407]],
[[ 108, 208, 308, 408],
[ 109, 209, 309, 409],
[ 110, 210, 310, 410],
[ 111, 211, 311, 411]]],
[[[ 500, 600, 700, 800],
[ 501, 601, 701, 801],
[ 502, 602, 702, 802],
[ 503, 603, 703, 803]],
[[ 504, 604, 704, 804],
[ 505, 605, 705, 805],
[ 506, 606, 706, 806],
[ 507, 607, 707, 807]],
[[ 508, 608, 708, 808],
[ 509, 609, 709, 809],
[ 510, 610, 710, 810],
[ 511, 611, 711, 811]]],
[[[ 900, 1000, 1100, 1200],
[ 901, 1001, 1101, 1201],
[ 902, 1002, 1102, 1202],
[ 903, 1003, 1103, 1203]],
[[ 904, 1004, 1104, 1204],
[ 905, 1005, 1105, 1205],
[ 906, 1006, 1106, 1206],
[ 907, 1007, 1107, 1207]],
[[ 908, 1008, 1108, 1208],
[ 909, 1009, 1109, 1209],
[ 910, 1010, 1110, 1210],
[ 911, 1011, 1111, 1211]]]])
So as you can see, all an array of 3x3 4x4 blocks, whose all blocks are the blocks from A, transposed, and subtracted with 1, as switch does
For example the second block on the first line of A is
View[0,1]
array([[105, 106, 107, 108],
[205, 206, 207, 208],
[305, 306, 307, 308],
[405, 406, 407, 408]])
Which is consistent with A (numbers starting with 1,2,3,4 are indeed on the 1st, 2nd, 3rd and 4th lines, that is the 1st row of blocks. And numbers ending with 05,06,07,08, are in the 5th, 6th,7th, 8th column, that is the second column of blocks)
And the second block of the 1st line of blocks of the result is
result[0,1]
array([[104, 204, 304, 404],
[105, 205, 305, 405],
[106, 206, 306, 406],
[107, 207, 307, 407]])
Which is indeed, with a difference of J=1 the same thing, transposed.
If you wish to recreate a 12x12 arrays with all the subresults, well, there you can use np.block
np.block([[result[i,j] for j in range(N//c)] for i in range(N//c)])
returns
array([[ 100, 200, 300, 400, 104, 204, 304, 404, 108, 208, 308, 408],
[ 101, 201, 301, 401, 105, 205, 305, 405, 109, 209, 309, 409],
[ 102, 202, 302, 402, 106, 206, 306, 406, 110, 210, 310, 410],
[ 103, 203, 303, 403, 107, 207, 307, 407, 111, 211, 311, 411],
[ 500, 600, 700, 800, 504, 604, 704, 804, 508, 608, 708, 808],
[ 501, 601, 701, 801, 505, 605, 705, 805, 509, 609, 709, 809],
[ 502, 602, 702, 802, 506, 606, 706, 806, 510, 610, 710, 810],
[ 503, 603, 703, 803, 507, 607, 707, 807, 511, 611, 711, 811],
[ 900, 1000, 1100, 1200, 904, 1004, 1104, 1204, 908, 1008, 1108, 1208],
[ 901, 1001, 1101, 1201, 905, 1005, 1105, 1205, 909, 1009, 1109, 1209],
[ 902, 1002, 1102, 1202, 906, 1006, 1106, 1206, 910, 1010, 1110, 1210],
[ 903, 1003, 1103, 1203, 907, 1007, 1107, 1207, 911, 1011, 1111, 1211]])