1

Say, I have an N dimensional array my_array[D1][D2]...[DN]

For a certain application, like sensitivity analysis, I need to fix a point p=(d1, d2, ..., dN) and iterate along each dimension at a time. The resulting behavior is

for x1 in range(0, D1):
    do_something(my_array[x1][d2][d3]...[dN])
for x2 in range(0, D2):
    do_something(my_array[d1][x2][d3]...[dN])
.
.
.
for xN in range(0, DN):
    do_something(my_array[d1][d2][d3]...[xN])

As you can see, there are many duplicated code here. How can I reduce the work and write some elegant code instead?

For example, is there any approach to the generation of code similar to the below?

for d in range(0, N):
    iterate along the (d+1)th dimension of my_array, denoting the element as x:
        do_something(x)
cyfex
  • 55
  • 10
  • 1
    Why tagged with `c` ? – Odysseus Feb 25 '20 at 08:10
  • Write one additional for loop outside, would it fit your need? – Phung Duy Phong Feb 25 '20 at 08:11
  • @Odysseus I used tag "c" because I think this is a general problem and it happens in c, too. – cyfex Feb 25 '20 at 08:26
  • @PhungDuyPhong Yes, but how? How to iterate through different index positions? I'm dreaming something like ```for i in range(1,N): iterate through index position i of my_array: do_something()``` – cyfex Feb 25 '20 at 08:27
  • You can use itertools instead of nested loops. Please refer the below link https://stackoverflow.com/questions/38362368/using-itertools-product-in-place-of-double-nested-for-loop-in-python-3 – Anupam Chaplot Feb 25 '20 at 09:06

3 Answers3

0

You can use numpy.take and do something like the following. Go through the documentation for reference.

https://docs.scipy.org/doc/numpy/reference/generated/numpy.take.html

N = len(my_array)
for i in range(N):
  n = len(my_array(i))
  indices = p
  indices[i] = x[i]
  for j in range(n):
     do_something(np.take(my_array,indices))
Nishant Agarwal
  • 430
  • 5
  • 17
0

I don't understand what are d1 d2 d3, but I guess you can do something like this:

def get_list_item_by_indexes_list(in_list, indexes_list):
    if len(indexes_list) <= 1:
        return in_list[indexes_list[0]]
    else:
        return get_list_item_by_indexes_list(in_list[indexes_list[0]], indexes_list[1:])

def do_to_each_dimension(multi_list, func, dimensions_lens):
    d0_to_dN_list = [l - 1 for l in dimensions_lens] # I dont know what is it
    for dimension_index in range(0, len(dimensions_lens)):
        dimension_len = dimensions_lens[dimension_index]
        for x in range(0, dimension_len):
            curr_d0_to_dN_list = d0_to_dN_list.copy()
            curr_d0_to_dN_list[dimension_index] = x
            func(get_list_item_by_indexes_list(multi_list, curr_d0_to_dN_list))


def do_something(n):
    print(n)

dimensions_lens = [3, 5]
my_array = [
    [1, 2, 3, 4, 5], 
    [6, 7, 8, 9, 10], 
    [11, 12, 13, 14, 15]
    ]

do_to_each_dimension(my_array, do_something, dimensions_lens)

Output:

5  10  15  11  12  13  14  15

This code iterates through the last column and the last row of a 2d array.

Now, to iterate through the last line of each dimension of 3d array:

dimensions_lens = [2, 4, 3]
my_array = [
    [
        [1, 2, 3], 
        [4, 5, 6], 
        [7, 8, 9], 
        [10, 11, 12]
        ], 
    [
        [13, 14, 15], 
        [16, 17, 18], 
        [19, 20, 21], 
        [22, 23, 24]
        ], 
    ]

do_to_each_dimension(my_array, do_something, dimensions_lens)

Output:

12  24  15  18  21  24  22  23  24

(Note: don't use zero-length dimensions with this code)

Community
  • 1
  • 1
Noam Nol
  • 570
  • 4
  • 11
0

You could mess with the string representation of your array access (my_arr[d1][d2]...[dN]) and eval that afterwards to get the values you want. This is fairly "hacky", but it will work on arrays with arbitrary dimensions and allows you to supply the indices as a list while handling the nested array access under the hood, allowing for a clean double for loop .

def access_at(arr, point):
    # build 'arr[p1][p2]...[pN]'
    access_str = 'arr' + ''.join([f'[{p}]' for p in point])
    return eval(access_str)

Using this access method is pretty straight forward:

p = [p1, ..., pN]
D = [D1, ..., DN]
for i in range(N):
    # deep copy p
    pt = p[:]
    for x in range(D[i]):
        pt[i] = x
        do_something(access_at(my_array, pt))
Lukas Thaler
  • 2,672
  • 5
  • 15
  • 31