# Centre all columns of an array

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!

• 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"). – Toby Speight Oct 4 '18 at 15:03
• Thanks a lot ! I've changed the title and added the fact that the code is working. – spooktober Oct 4 '18 at 15:12
• In your example, why is the second row rearranged so that 0.618 comes first? – 200_success Oct 4 '18 at 17:46
• It isn't suppose to come out like that, i corrected it. Thanks for the question. – spooktober Oct 5 '18 at 7:17

# 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)])

• 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. – spooktober Oct 8 '18 at 15:26
• when used correctly, python can be very elegant – Maarten Fabré Oct 8 '18 at 16:13