1
\$\begingroup\$

I have this working code tested over lots of np.arrays. It is a for-loop that walks through all the values of a np.array (x,y).

For each y-row, it finds the first x-column for which the value is different to zero. Afterward, it finds the last x-column for which value is different to 0.

Then all the columns between first x-column and last x-column are centred.

This is then repeated for all y-rows. Example:

#Input :
array([[ 0.0,  0.149,  0.064, 0.736,  0.0],
       [ 0.0,  0.0,  0.258,   0.979,  0.618 ],
       [ 0.0,  0.0,  0.0,     0.786,  0.666],
       [ 0.0,  0.0,  0.0,     0.782,  0.954],

 #Output :
array([[ 0.0,  0.149, 0.064, 0.736, 0.0],
       [ 0.0,  0.258, 0.979, 0.618, 0.0],
       [ 0.0,  0.786, 0.666, 0.0,   0.0],
       [ 0.0,  0.782, 0.954, 0.0,   0.0],

Also:

  • Not all the values between first and last columns are different than zero.

    for y in range(len(array)):
    
        begin = False
        inside = False
        end = False
    
        for x in range(len(array[0])):
    
    
            if (array[y][x] == 0) & (begin == True) & (end == False):
                boundary_two = ( x - 1 )
                inside = False
                end = True
    
            elif (array[y][x] != 0) & (inside == False):
                boundary_one = x
                begin = True
                inside = True
    
        y_position.append(y)
        m = np.split(array[y],[boundary_one,boundary_two])
        zeros = len(array[0])-len(m[1])
        array[y] = np.concatenate((np.zeros(zeros//2),m[1],np.zeros(int(np.ceil(zeros/2)))))
    

Furthermore, I added a variable(count) inside the function (which I erase for the upper code example, to simplify lecture) which count how many empty rows since last non empty rows. When count ==10, we break out of the loop. This is to save time. Once we have +/- 10 empty rows after the non-empty ones, it is sure all other y-rows will be empty as well.

Finally, I must save the value for the last y-row non-empty.

This is my script most time demanding calculation, so I was wondering if there is a way of to improve it, either by making it clearer and/or faster.

Thanks a lot, hope it is clear!

\$\endgroup\$
  • \$\begingroup\$ Welcome to Code Review! Does the code function correctly? If not, it isn't ready for review (see help center) and the question may be deleted. If you've tested it, I recommend that you edit to add a summary of the testing (ideally as reproducible unit-test code), and update the title to simply state the task accomplished by the code, which is the Code Review standard for titles (suggestion: "Centre all columns of an array"). \$\endgroup\$ – Toby Speight Oct 4 '18 at 15:03
  • \$\begingroup\$ Thanks a lot ! I've changed the title and added the fact that the code is working. \$\endgroup\$ – spooktober Oct 4 '18 at 15:12
  • 1
    \$\begingroup\$ In your example, why is the second row rearranged so that 0.618 comes first? \$\endgroup\$ – 200_success Oct 4 '18 at 17:46
  • \$\begingroup\$ It isn't suppose to come out like that, i corrected it. Thanks for the question. \$\endgroup\$ – spooktober Oct 5 '18 at 7:17
1
\$\begingroup\$

review

comparison

no need to compare against True or False. (begin == True) can be easier expressed as begin and (end == False) as not end. and comparison chaining is done with and, not &(bitwise and)

iteration

In python, it is very seldomly needed to iterate over the indices. Instead of for y in range(len(array)): and for x in range(len(array[0])), you can do

for row in array:
    ...
    for x, element in enumerate(row):
        if element == 0 and begin and not end:

alternative solution

try to vectorise as much as possible. The easiest thing to do is count the zeroes in front and at the back:

def count_zeroes(array):
    return (array.cumsum(axis=1) == 0).sum(axis=1)

zeroes_front = count_zeroes(array)

and then the same for the reverse:

zeroes_back = count_zeroes(test_data[:,::-1])

The amount each row needs to roll is:

roll = (zeroes_front + zeroes_back) //2 - zeroes_front
array([ 0, -1, -2, -2])

and then you apply this roll over each row:

np.array([np.roll(row, r) for row, r in zip(test_data, roll)])
array([[0.   , 0.149, 0.064, 0.736, 0.   ],
       [0.   , 0.258, 0.979, 0.618, 0.   ],
       [0.   , 0.786, 0.666, 0.   , 0.   ],
       [0.   , 0.782, 0.954, 0.   , 0.   ]])

in total:

def centre(array):
    zeroes_front = count_zeroes(array)
    zeroes_back = count_zeroes(array[:,::-1])
    roll = (zeroes_front + zeroes_back) //2 - zeroes_front
    return np.array([np.roll(row, r) for row, r in zip(array, roll)])
\$\endgroup\$
  • \$\begingroup\$ Wow, I can express how incredibly simple your solution is...Thanks a lot !! I'm taking my time to process all the new functions i didn't knew about. \$\endgroup\$ – spooktober Oct 8 '18 at 15:26
  • \$\begingroup\$ when used correctly, python can be very elegant \$\endgroup\$ – Maarten Fabré Oct 8 '18 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.