It may need similar method like in answer for
python - How to place tables next to each other with tabulate - Stack Overflow
it needs to convert all arrays to strings and split them to lists of lines, and next it needs zip() or better zip_longest(..., fillvalue="") to group first line (and print them as one string, second lines (and print them as string, with * and =), etc.
Because last line has longer (because it has closing ] at the end) so I use f-string with {variable:length} to set extra space in previous lines.
It also adds empty lines below array A to put array B in correct place
Because arrays may have different number of lines so it is good to use zip_longest() instead of zip() and use fillvalue='' to put missing lines.
To show this problem I use 4x6 and 6x4 instead of 4x4
import numpy as np
from itertools import zip_longest
A = np.random.randint(10, size = (4, 6))
B = np.random.randint(10, size = (6, 4))
C = A@B
a = str(A).split('\n')
b = str(B).split('\n')
c = str(C).split('\n')
a_len = len(a[0]) + 1
b_len = len(b[0]) + 1
#c_len = len(c[0]) + 1
for index, (x, y, z) in enumerate(zip_longest(a, b, c, fillvalue='')):
if index == 1:
print(f'{x:{a_len}} * {y:{b_len}} = {z}')
else:
print(f'{x:{a_len}} {y:{b_len}} {z}')
Result:
[[4 3 0 5 1 6] [[8 3 5 8] [[ 89 137 104 94]
[6 3 5 7 3 0] * [5 9 8 6] = [128 148 104 149]
[6 6 6 1 7 6] [9 5 6 5] [178 194 178 148]
[9 8 0 3 2 8]] [2 9 2 7] [162 200 183 155]]
[2 5 2 3]
[5 8 8 1]]
EDIT:
If you want to center operator then you need height and calculate center and use it with index
height = max(len(a), len(b), len(c))
center = round(height/2)-1 # `round(height/2)` gives better output than `height//2`
# ... code
if index == center:
print(f'{x:<{a_len}} * {y:{b_len}} = {z}')
# ... code
Result:
[[4 8 0 0 5 2] [[5 9 9 4] [[128 96 87 122]
[9 4 0 6 8 3] [9 3 2 7] [161 191 180 155]
[4 9 7 8 9 3] * [0 7 8 9] = [193 228 218 245]
[7 6 6 6 8 1]] [4 7 6 2] [153 205 204 205]]
[4 4 5 8]
[8 8 5 5]]
BTW: You could try to use height-len(a), etc. add empty rows before array - to center also arrays.
Because it works with strings so it doesn't matter if you have integer of float values
# float values
A = np.random.randint(10, size = (4, 6)) / 10
B = np.random.randint(10, size = (6, 4)) / 10
Result:
[[0.3 0.3 0.3 0.9 0.2 0.6] [[0.5 0.3 0.2 0.5] [[0.3 0.9 0.65 1.31]
[0.2 0.7 0.2 0.5 0.4 0.8] [0.1 0.6 0.6 0.9] [0.33 1.3 1. 1.79]
[0.3 0. 0.1 0. 0.9 0.9] * [0. 0.7 0.1 0.9] = [0.33 1.33 0.7 1.41]
[0.4 0. 0.4 0.6 0.4 0.5]] [0. 0. 0. 0. ] [0.3 0.96 0.46 1.17]]
[0. 0.9 0.1 0.4]
[0.2 0.4 0.6 0.9]]
I don't have example with n-dimention array but it should also work.
It may need to get lenght of last element in list (and without +1)
a_len = len(a[-1])
b_len = len(b[-1])
Full working code used for tests with different shapes, integer or float values and with N-dimension
import numpy as np
from itertools import zip_longest
def niceprint(A, op, B, C):
a = str(A).split('\n')
b = str(B).split('\n')
c = str(C).split('\n')
a_len = len(a[-1])
b_len = len(b[-1])
height = max(len(a), len(b), len(c))
center = round(height/2)-1 # `round(height/2)` gives better position than `height//2`
for index, (x, y, z) in enumerate(zip_longest(a, b, c, fillvalue='')):
if index == center:
print(f'{x:<{a_len}} {op} {y:{b_len}} = {z}')
else:
print(f'{x:<{a_len}} {y:{b_len}} {z}')
# ---
def example1():
A = np.random.randint(10, size = (4, 6))
B = np.random.randint(10, size = (6, 4))
C = A@B
niceprint(A, '*', B, C)
def example2():
A = np.random.randint(10, size = (3, 3, 3)) / 10
B = np.random.randint(10, size = (3, 3, 3)) / 10
C = A+B
niceprint(A, '+', B, C)
def example3():
A = np.random.randint(10, size = (2, 3, 3, 2)) / 10
B = np.random.randint(10, size = (2, 3, 3, 2)) / 10
C = A+B
niceprint(A, '+', B, C)
#example1()
#example2()
example3()
zip()to group first lines from all arrays, second lines, third lines, etc. and display all first lines as one string, next all second lines as one string, etc.