Pretty printing of the numpy ndarrays

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
6
down vote

favorite
1












Following this idea for pretty printing of numpy ndarrays, I have developed a very primitive prototype:



def ndtotext(A, w=None, h=None):
if A.ndim==1:
if w == None :
return str(A)
else:
s ='['+' '*(max(w[-1],len(str(A[0])))-len(str(A[0]))) +str(A[0])
for i,AA in enumerate(A[1:]):
s += ' '*(max(w[i],len(str(AA)))-len(str(AA))+1)+str(AA)
s +='] '
elif A.ndim==2:
w1 = [max([len(str(s)) for s in A[:,i]]) for i in range(A.shape[1])]
w0 = sum(w1)+len(w1)+1
s= u'u250c'+u'u2500'*w0+u'u2510' +'n'
for AA in A:
s += ' ' + ndtotext(AA, w=w1) +'n'
s += u'u2514'+u'u2500'*w0+u'u2518'
elif A.ndim==3:
h=A.shape[1]
s1=u'u250c' +'n' + (u'u2502'+'n')*h + u'u2514'+'n'
s2=u'u2510' +'n' + (u'u2502'+'n')*h + u'u2518'+'n'
strings=[ndtotext(a)+'n' for a in A]
strings.append(s2)
strings.insert(0,s1)
s='n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
return s


for example:



shape = 4, 5, 3
C=np.random.randint(10000, size=np.prod(shape)).reshape(shape)
print(ndtotext(C))

┌┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐┐
│ [9298 4404 1759] [5426 3488 9267] [8884 7721 579] [6872 4226 1858] │
│ [6723 271 8466] [9885 6760 8949] [ 295 7422 5659] [5322 4239 7446] │
│ [7156 6077 9390] [2712 6379 2832] [6956 626 5534] [ 142 4090 6390] │
│ [9377 9033 1953] [8986 3791 4538] [2466 8572 662] [1528 8922 9656] │
│ [1449 7319 3939] [7350 9619 928] [7542 4704 1477] [ 980 6037 869] │
└└────────────────┘└────────────────┘└────────────────┘└────────────────┘┘


I would appreciate if you could review this code and let me know how I can improve it.



What I hope to see:



  • possible mistakes or cases to break the code

  • how to make it faster, more performant, pythonic

  • how to extend it to higher dimensions









share|improve this question









New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.















  • 1




    How many dimensions does this work with? 1-3.
    – Peilonrayz
    2 hours ago











  • @Peilonrayz this one works 1-3 but the HTML version developed by others might work for further dimensions too. I haven't checked. It would be great if we could extend this to all dimensions. I guess doing some text/string work plus recursion this should be doable.
    – Foad
    2 hours ago






  • 1




    Can you modify your example to actually include how to call your function? I.e. what should w look like?
    – Graipher
    1 hour ago






  • 1




    @Foad: Well, it is mostly trivial, but for the 1D case it is not so clear to me what and if anything should be passed for w.
    – Graipher
    1 hour ago







  • 1




    @Graipher my apologies. yeah the w thing is very badly implemented. I'm actually very embarrassed by it. please help me know how if you can think of a better approach.
    – Foad
    1 hour ago














up vote
6
down vote

favorite
1












Following this idea for pretty printing of numpy ndarrays, I have developed a very primitive prototype:



def ndtotext(A, w=None, h=None):
if A.ndim==1:
if w == None :
return str(A)
else:
s ='['+' '*(max(w[-1],len(str(A[0])))-len(str(A[0]))) +str(A[0])
for i,AA in enumerate(A[1:]):
s += ' '*(max(w[i],len(str(AA)))-len(str(AA))+1)+str(AA)
s +='] '
elif A.ndim==2:
w1 = [max([len(str(s)) for s in A[:,i]]) for i in range(A.shape[1])]
w0 = sum(w1)+len(w1)+1
s= u'u250c'+u'u2500'*w0+u'u2510' +'n'
for AA in A:
s += ' ' + ndtotext(AA, w=w1) +'n'
s += u'u2514'+u'u2500'*w0+u'u2518'
elif A.ndim==3:
h=A.shape[1]
s1=u'u250c' +'n' + (u'u2502'+'n')*h + u'u2514'+'n'
s2=u'u2510' +'n' + (u'u2502'+'n')*h + u'u2518'+'n'
strings=[ndtotext(a)+'n' for a in A]
strings.append(s2)
strings.insert(0,s1)
s='n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
return s


for example:



shape = 4, 5, 3
C=np.random.randint(10000, size=np.prod(shape)).reshape(shape)
print(ndtotext(C))

┌┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐┐
│ [9298 4404 1759] [5426 3488 9267] [8884 7721 579] [6872 4226 1858] │
│ [6723 271 8466] [9885 6760 8949] [ 295 7422 5659] [5322 4239 7446] │
│ [7156 6077 9390] [2712 6379 2832] [6956 626 5534] [ 142 4090 6390] │
│ [9377 9033 1953] [8986 3791 4538] [2466 8572 662] [1528 8922 9656] │
│ [1449 7319 3939] [7350 9619 928] [7542 4704 1477] [ 980 6037 869] │
└└────────────────┘└────────────────┘└────────────────┘└────────────────┘┘


I would appreciate if you could review this code and let me know how I can improve it.



What I hope to see:



  • possible mistakes or cases to break the code

  • how to make it faster, more performant, pythonic

  • how to extend it to higher dimensions









share|improve this question









New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.















  • 1




    How many dimensions does this work with? 1-3.
    – Peilonrayz
    2 hours ago











  • @Peilonrayz this one works 1-3 but the HTML version developed by others might work for further dimensions too. I haven't checked. It would be great if we could extend this to all dimensions. I guess doing some text/string work plus recursion this should be doable.
    – Foad
    2 hours ago






  • 1




    Can you modify your example to actually include how to call your function? I.e. what should w look like?
    – Graipher
    1 hour ago






  • 1




    @Foad: Well, it is mostly trivial, but for the 1D case it is not so clear to me what and if anything should be passed for w.
    – Graipher
    1 hour ago







  • 1




    @Graipher my apologies. yeah the w thing is very badly implemented. I'm actually very embarrassed by it. please help me know how if you can think of a better approach.
    – Foad
    1 hour ago












up vote
6
down vote

favorite
1









up vote
6
down vote

favorite
1






1





Following this idea for pretty printing of numpy ndarrays, I have developed a very primitive prototype:



def ndtotext(A, w=None, h=None):
if A.ndim==1:
if w == None :
return str(A)
else:
s ='['+' '*(max(w[-1],len(str(A[0])))-len(str(A[0]))) +str(A[0])
for i,AA in enumerate(A[1:]):
s += ' '*(max(w[i],len(str(AA)))-len(str(AA))+1)+str(AA)
s +='] '
elif A.ndim==2:
w1 = [max([len(str(s)) for s in A[:,i]]) for i in range(A.shape[1])]
w0 = sum(w1)+len(w1)+1
s= u'u250c'+u'u2500'*w0+u'u2510' +'n'
for AA in A:
s += ' ' + ndtotext(AA, w=w1) +'n'
s += u'u2514'+u'u2500'*w0+u'u2518'
elif A.ndim==3:
h=A.shape[1]
s1=u'u250c' +'n' + (u'u2502'+'n')*h + u'u2514'+'n'
s2=u'u2510' +'n' + (u'u2502'+'n')*h + u'u2518'+'n'
strings=[ndtotext(a)+'n' for a in A]
strings.append(s2)
strings.insert(0,s1)
s='n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
return s


for example:



shape = 4, 5, 3
C=np.random.randint(10000, size=np.prod(shape)).reshape(shape)
print(ndtotext(C))

┌┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐┐
│ [9298 4404 1759] [5426 3488 9267] [8884 7721 579] [6872 4226 1858] │
│ [6723 271 8466] [9885 6760 8949] [ 295 7422 5659] [5322 4239 7446] │
│ [7156 6077 9390] [2712 6379 2832] [6956 626 5534] [ 142 4090 6390] │
│ [9377 9033 1953] [8986 3791 4538] [2466 8572 662] [1528 8922 9656] │
│ [1449 7319 3939] [7350 9619 928] [7542 4704 1477] [ 980 6037 869] │
└└────────────────┘└────────────────┘└────────────────┘└────────────────┘┘


I would appreciate if you could review this code and let me know how I can improve it.



What I hope to see:



  • possible mistakes or cases to break the code

  • how to make it faster, more performant, pythonic

  • how to extend it to higher dimensions









share|improve this question









New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











Following this idea for pretty printing of numpy ndarrays, I have developed a very primitive prototype:



def ndtotext(A, w=None, h=None):
if A.ndim==1:
if w == None :
return str(A)
else:
s ='['+' '*(max(w[-1],len(str(A[0])))-len(str(A[0]))) +str(A[0])
for i,AA in enumerate(A[1:]):
s += ' '*(max(w[i],len(str(AA)))-len(str(AA))+1)+str(AA)
s +='] '
elif A.ndim==2:
w1 = [max([len(str(s)) for s in A[:,i]]) for i in range(A.shape[1])]
w0 = sum(w1)+len(w1)+1
s= u'u250c'+u'u2500'*w0+u'u2510' +'n'
for AA in A:
s += ' ' + ndtotext(AA, w=w1) +'n'
s += u'u2514'+u'u2500'*w0+u'u2518'
elif A.ndim==3:
h=A.shape[1]
s1=u'u250c' +'n' + (u'u2502'+'n')*h + u'u2514'+'n'
s2=u'u2510' +'n' + (u'u2502'+'n')*h + u'u2518'+'n'
strings=[ndtotext(a)+'n' for a in A]
strings.append(s2)
strings.insert(0,s1)
s='n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
return s


for example:



shape = 4, 5, 3
C=np.random.randint(10000, size=np.prod(shape)).reshape(shape)
print(ndtotext(C))

┌┌────────────────┐┌────────────────┐┌────────────────┐┌────────────────┐┐
│ [9298 4404 1759] [5426 3488 9267] [8884 7721 579] [6872 4226 1858] │
│ [6723 271 8466] [9885 6760 8949] [ 295 7422 5659] [5322 4239 7446] │
│ [7156 6077 9390] [2712 6379 2832] [6956 626 5534] [ 142 4090 6390] │
│ [9377 9033 1953] [8986 3791 4538] [2466 8572 662] [1528 8922 9656] │
│ [1449 7319 3939] [7350 9619 928] [7542 4704 1477] [ 980 6037 869] │
└└────────────────┘└────────────────┘└────────────────┘└────────────────┘┘


I would appreciate if you could review this code and let me know how I can improve it.



What I hope to see:



  • possible mistakes or cases to break the code

  • how to make it faster, more performant, pythonic

  • how to extend it to higher dimensions






python numpy formatting ascii-art unicode






share|improve this question









New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 1 hour ago





















New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 2 hours ago









Foad

1313




1313




New contributor




Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Foad is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







  • 1




    How many dimensions does this work with? 1-3.
    – Peilonrayz
    2 hours ago











  • @Peilonrayz this one works 1-3 but the HTML version developed by others might work for further dimensions too. I haven't checked. It would be great if we could extend this to all dimensions. I guess doing some text/string work plus recursion this should be doable.
    – Foad
    2 hours ago






  • 1




    Can you modify your example to actually include how to call your function? I.e. what should w look like?
    – Graipher
    1 hour ago






  • 1




    @Foad: Well, it is mostly trivial, but for the 1D case it is not so clear to me what and if anything should be passed for w.
    – Graipher
    1 hour ago







  • 1




    @Graipher my apologies. yeah the w thing is very badly implemented. I'm actually very embarrassed by it. please help me know how if you can think of a better approach.
    – Foad
    1 hour ago












  • 1




    How many dimensions does this work with? 1-3.
    – Peilonrayz
    2 hours ago











  • @Peilonrayz this one works 1-3 but the HTML version developed by others might work for further dimensions too. I haven't checked. It would be great if we could extend this to all dimensions. I guess doing some text/string work plus recursion this should be doable.
    – Foad
    2 hours ago






  • 1




    Can you modify your example to actually include how to call your function? I.e. what should w look like?
    – Graipher
    1 hour ago






  • 1




    @Foad: Well, it is mostly trivial, but for the 1D case it is not so clear to me what and if anything should be passed for w.
    – Graipher
    1 hour ago







  • 1




    @Graipher my apologies. yeah the w thing is very badly implemented. I'm actually very embarrassed by it. please help me know how if you can think of a better approach.
    – Foad
    1 hour ago







1




1




How many dimensions does this work with? 1-3.
– Peilonrayz
2 hours ago





How many dimensions does this work with? 1-3.
– Peilonrayz
2 hours ago













@Peilonrayz this one works 1-3 but the HTML version developed by others might work for further dimensions too. I haven't checked. It would be great if we could extend this to all dimensions. I guess doing some text/string work plus recursion this should be doable.
– Foad
2 hours ago




@Peilonrayz this one works 1-3 but the HTML version developed by others might work for further dimensions too. I haven't checked. It would be great if we could extend this to all dimensions. I guess doing some text/string work plus recursion this should be doable.
– Foad
2 hours ago




1




1




Can you modify your example to actually include how to call your function? I.e. what should w look like?
– Graipher
1 hour ago




Can you modify your example to actually include how to call your function? I.e. what should w look like?
– Graipher
1 hour ago




1




1




@Foad: Well, it is mostly trivial, but for the 1D case it is not so clear to me what and if anything should be passed for w.
– Graipher
1 hour ago





@Foad: Well, it is mostly trivial, but for the 1D case it is not so clear to me what and if anything should be passed for w.
– Graipher
1 hour ago





1




1




@Graipher my apologies. yeah the w thing is very badly implemented. I'm actually very embarrassed by it. please help me know how if you can think of a better approach.
– Foad
1 hour ago




@Graipher my apologies. yeah the w thing is very badly implemented. I'm actually very embarrassed by it. please help me know how if you can think of a better approach.
– Foad
1 hour ago










1 Answer
1






active

oldest

votes

















up vote
2
down vote













If A.ndim is not in 1, 2, 3, your code tries to return a non-existing string s. It would be better to be explicit about what your code supports atm:



def ndtotext(A, w=None, h=None):
...
else:
raise NotImplementedError("Currently only 1 - 3 dimensions are supported")
return s


While we are at the point of having your code be clear about what is happening, you should add a docstring explaining what your code does:



def ndtotext(A, w=None, h=None):
"""Returns a string to pretty print the numpy.ndarray `A`.

Currently supports 1 - 3 dimensions only.
Raises a NotImplementedError if an array with more dimensions is passed.

Describe `w` and `h`.
"""
...


Next, Python has an official style-guide, PEP8, which programmers are encouraged to follow. One of the things it recommends is surrounding operators with spaces (which I fixed in the rest of the code) and using lower_case for variables and functions (which I left as is for now).



Now, let's come to your actual code:



  • You calculate some values multiple times (like str(A[0])), save those to a variable.

  • If you want to compare to None, use is (since it is a singleton).

  • No else needed after an if...return (this is a matter of personal style, I prefer not having the additional level of indentation).

  • Use str.rjust to add enough whitespace in front of your strings. You could also use str.format for this, but it looks less nice.

  • Your w has a weird structure, with the width of the first column being the last entry and the rest starting at zero.

  • Give the unicode values names. And then add functions to draw a line of specified length.


  • String addition is costly and slow. Try to consistently use building a list and str.joining it.



    UPPER_LEFT = u'u250c'
    UPPER_RIGHT = u'u2510'
    LOWER_LEFT = u'u2514'
    LOWER_RIGHT = u'u2518'
    HORIZONTAL = u'u2500'
    VERTICAL = u'u2502'

    def upper_line(width):
    return UPPER_LEFT + HORIZONTAL * (width - 2) + UPPER_RIGHT

    def lower_line(width):
    return LOWER_LEFT + HORIZONTAL * (width - 2) + LOWER_RIGHT

    def left_line(height):
    return "n".join([UPPER_LEFT] + [VERTICAL] * (height - 2)] + [LOWER_LEFT])

    def right_line(height):
    return "n".join([UPPER_RIGHT] + [VERTICAL] * (height - 2)] + [LOWER_RIGHT])

    def ndtotext(A, w=None, h=None):
    """Returns a string to pretty print the numpy.ndarray `A`.

    Currently supports 1 - 3 dimensions only.
    Raises a NotImplementedError if an array with more dimensions is passed.

    Describe `w` and `h`.
    """
    if A.ndim == 1:
    if w is None:
    return str(A)
    s = " ".join([str(value).rjust(width) for value, width in zip(A, w)])
    return '[]'.format(s)
    elif A.ndim == 2:
    widths = [max([len(str(s)) for s in A[:, i]]) for i in range(A.shape[1])]
    s = "".join([' ' + ndtotext(AA, w=widths) + 'n' for AA in A])
    w0 = sum(widths) + len(widths) - 1 + 2 # spaces between elements and corners
    return upper_line(w0) + s + lower_line(w0)
    elif A.ndim == 3:
    h = A.shape[1]
    strings = [left_line(h)]
    strings.extend(ndtotext(a) + 'n' for a in A)
    strings.append(right_line(h))
    return 'n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
    raise NotImplementedError("Currently only 1 - 3 dimensions are supported")


This can probably be even more compactified, but I think it is a good start.






share|improve this answer






















  • wow. awesome. thanks a lot. I'm gonna try your points and get back here.
    – Foad
    6 mins ago










Your Answer





StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);






Foad is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207139%2fpretty-printing-of-the-numpy-ndarrays%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
2
down vote













If A.ndim is not in 1, 2, 3, your code tries to return a non-existing string s. It would be better to be explicit about what your code supports atm:



def ndtotext(A, w=None, h=None):
...
else:
raise NotImplementedError("Currently only 1 - 3 dimensions are supported")
return s


While we are at the point of having your code be clear about what is happening, you should add a docstring explaining what your code does:



def ndtotext(A, w=None, h=None):
"""Returns a string to pretty print the numpy.ndarray `A`.

Currently supports 1 - 3 dimensions only.
Raises a NotImplementedError if an array with more dimensions is passed.

Describe `w` and `h`.
"""
...


Next, Python has an official style-guide, PEP8, which programmers are encouraged to follow. One of the things it recommends is surrounding operators with spaces (which I fixed in the rest of the code) and using lower_case for variables and functions (which I left as is for now).



Now, let's come to your actual code:



  • You calculate some values multiple times (like str(A[0])), save those to a variable.

  • If you want to compare to None, use is (since it is a singleton).

  • No else needed after an if...return (this is a matter of personal style, I prefer not having the additional level of indentation).

  • Use str.rjust to add enough whitespace in front of your strings. You could also use str.format for this, but it looks less nice.

  • Your w has a weird structure, with the width of the first column being the last entry and the rest starting at zero.

  • Give the unicode values names. And then add functions to draw a line of specified length.


  • String addition is costly and slow. Try to consistently use building a list and str.joining it.



    UPPER_LEFT = u'u250c'
    UPPER_RIGHT = u'u2510'
    LOWER_LEFT = u'u2514'
    LOWER_RIGHT = u'u2518'
    HORIZONTAL = u'u2500'
    VERTICAL = u'u2502'

    def upper_line(width):
    return UPPER_LEFT + HORIZONTAL * (width - 2) + UPPER_RIGHT

    def lower_line(width):
    return LOWER_LEFT + HORIZONTAL * (width - 2) + LOWER_RIGHT

    def left_line(height):
    return "n".join([UPPER_LEFT] + [VERTICAL] * (height - 2)] + [LOWER_LEFT])

    def right_line(height):
    return "n".join([UPPER_RIGHT] + [VERTICAL] * (height - 2)] + [LOWER_RIGHT])

    def ndtotext(A, w=None, h=None):
    """Returns a string to pretty print the numpy.ndarray `A`.

    Currently supports 1 - 3 dimensions only.
    Raises a NotImplementedError if an array with more dimensions is passed.

    Describe `w` and `h`.
    """
    if A.ndim == 1:
    if w is None:
    return str(A)
    s = " ".join([str(value).rjust(width) for value, width in zip(A, w)])
    return '[]'.format(s)
    elif A.ndim == 2:
    widths = [max([len(str(s)) for s in A[:, i]]) for i in range(A.shape[1])]
    s = "".join([' ' + ndtotext(AA, w=widths) + 'n' for AA in A])
    w0 = sum(widths) + len(widths) - 1 + 2 # spaces between elements and corners
    return upper_line(w0) + s + lower_line(w0)
    elif A.ndim == 3:
    h = A.shape[1]
    strings = [left_line(h)]
    strings.extend(ndtotext(a) + 'n' for a in A)
    strings.append(right_line(h))
    return 'n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
    raise NotImplementedError("Currently only 1 - 3 dimensions are supported")


This can probably be even more compactified, but I think it is a good start.






share|improve this answer






















  • wow. awesome. thanks a lot. I'm gonna try your points and get back here.
    – Foad
    6 mins ago














up vote
2
down vote













If A.ndim is not in 1, 2, 3, your code tries to return a non-existing string s. It would be better to be explicit about what your code supports atm:



def ndtotext(A, w=None, h=None):
...
else:
raise NotImplementedError("Currently only 1 - 3 dimensions are supported")
return s


While we are at the point of having your code be clear about what is happening, you should add a docstring explaining what your code does:



def ndtotext(A, w=None, h=None):
"""Returns a string to pretty print the numpy.ndarray `A`.

Currently supports 1 - 3 dimensions only.
Raises a NotImplementedError if an array with more dimensions is passed.

Describe `w` and `h`.
"""
...


Next, Python has an official style-guide, PEP8, which programmers are encouraged to follow. One of the things it recommends is surrounding operators with spaces (which I fixed in the rest of the code) and using lower_case for variables and functions (which I left as is for now).



Now, let's come to your actual code:



  • You calculate some values multiple times (like str(A[0])), save those to a variable.

  • If you want to compare to None, use is (since it is a singleton).

  • No else needed after an if...return (this is a matter of personal style, I prefer not having the additional level of indentation).

  • Use str.rjust to add enough whitespace in front of your strings. You could also use str.format for this, but it looks less nice.

  • Your w has a weird structure, with the width of the first column being the last entry and the rest starting at zero.

  • Give the unicode values names. And then add functions to draw a line of specified length.


  • String addition is costly and slow. Try to consistently use building a list and str.joining it.



    UPPER_LEFT = u'u250c'
    UPPER_RIGHT = u'u2510'
    LOWER_LEFT = u'u2514'
    LOWER_RIGHT = u'u2518'
    HORIZONTAL = u'u2500'
    VERTICAL = u'u2502'

    def upper_line(width):
    return UPPER_LEFT + HORIZONTAL * (width - 2) + UPPER_RIGHT

    def lower_line(width):
    return LOWER_LEFT + HORIZONTAL * (width - 2) + LOWER_RIGHT

    def left_line(height):
    return "n".join([UPPER_LEFT] + [VERTICAL] * (height - 2)] + [LOWER_LEFT])

    def right_line(height):
    return "n".join([UPPER_RIGHT] + [VERTICAL] * (height - 2)] + [LOWER_RIGHT])

    def ndtotext(A, w=None, h=None):
    """Returns a string to pretty print the numpy.ndarray `A`.

    Currently supports 1 - 3 dimensions only.
    Raises a NotImplementedError if an array with more dimensions is passed.

    Describe `w` and `h`.
    """
    if A.ndim == 1:
    if w is None:
    return str(A)
    s = " ".join([str(value).rjust(width) for value, width in zip(A, w)])
    return '[]'.format(s)
    elif A.ndim == 2:
    widths = [max([len(str(s)) for s in A[:, i]]) for i in range(A.shape[1])]
    s = "".join([' ' + ndtotext(AA, w=widths) + 'n' for AA in A])
    w0 = sum(widths) + len(widths) - 1 + 2 # spaces between elements and corners
    return upper_line(w0) + s + lower_line(w0)
    elif A.ndim == 3:
    h = A.shape[1]
    strings = [left_line(h)]
    strings.extend(ndtotext(a) + 'n' for a in A)
    strings.append(right_line(h))
    return 'n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
    raise NotImplementedError("Currently only 1 - 3 dimensions are supported")


This can probably be even more compactified, but I think it is a good start.






share|improve this answer






















  • wow. awesome. thanks a lot. I'm gonna try your points and get back here.
    – Foad
    6 mins ago












up vote
2
down vote










up vote
2
down vote









If A.ndim is not in 1, 2, 3, your code tries to return a non-existing string s. It would be better to be explicit about what your code supports atm:



def ndtotext(A, w=None, h=None):
...
else:
raise NotImplementedError("Currently only 1 - 3 dimensions are supported")
return s


While we are at the point of having your code be clear about what is happening, you should add a docstring explaining what your code does:



def ndtotext(A, w=None, h=None):
"""Returns a string to pretty print the numpy.ndarray `A`.

Currently supports 1 - 3 dimensions only.
Raises a NotImplementedError if an array with more dimensions is passed.

Describe `w` and `h`.
"""
...


Next, Python has an official style-guide, PEP8, which programmers are encouraged to follow. One of the things it recommends is surrounding operators with spaces (which I fixed in the rest of the code) and using lower_case for variables and functions (which I left as is for now).



Now, let's come to your actual code:



  • You calculate some values multiple times (like str(A[0])), save those to a variable.

  • If you want to compare to None, use is (since it is a singleton).

  • No else needed after an if...return (this is a matter of personal style, I prefer not having the additional level of indentation).

  • Use str.rjust to add enough whitespace in front of your strings. You could also use str.format for this, but it looks less nice.

  • Your w has a weird structure, with the width of the first column being the last entry and the rest starting at zero.

  • Give the unicode values names. And then add functions to draw a line of specified length.


  • String addition is costly and slow. Try to consistently use building a list and str.joining it.



    UPPER_LEFT = u'u250c'
    UPPER_RIGHT = u'u2510'
    LOWER_LEFT = u'u2514'
    LOWER_RIGHT = u'u2518'
    HORIZONTAL = u'u2500'
    VERTICAL = u'u2502'

    def upper_line(width):
    return UPPER_LEFT + HORIZONTAL * (width - 2) + UPPER_RIGHT

    def lower_line(width):
    return LOWER_LEFT + HORIZONTAL * (width - 2) + LOWER_RIGHT

    def left_line(height):
    return "n".join([UPPER_LEFT] + [VERTICAL] * (height - 2)] + [LOWER_LEFT])

    def right_line(height):
    return "n".join([UPPER_RIGHT] + [VERTICAL] * (height - 2)] + [LOWER_RIGHT])

    def ndtotext(A, w=None, h=None):
    """Returns a string to pretty print the numpy.ndarray `A`.

    Currently supports 1 - 3 dimensions only.
    Raises a NotImplementedError if an array with more dimensions is passed.

    Describe `w` and `h`.
    """
    if A.ndim == 1:
    if w is None:
    return str(A)
    s = " ".join([str(value).rjust(width) for value, width in zip(A, w)])
    return '[]'.format(s)
    elif A.ndim == 2:
    widths = [max([len(str(s)) for s in A[:, i]]) for i in range(A.shape[1])]
    s = "".join([' ' + ndtotext(AA, w=widths) + 'n' for AA in A])
    w0 = sum(widths) + len(widths) - 1 + 2 # spaces between elements and corners
    return upper_line(w0) + s + lower_line(w0)
    elif A.ndim == 3:
    h = A.shape[1]
    strings = [left_line(h)]
    strings.extend(ndtotext(a) + 'n' for a in A)
    strings.append(right_line(h))
    return 'n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
    raise NotImplementedError("Currently only 1 - 3 dimensions are supported")


This can probably be even more compactified, but I think it is a good start.






share|improve this answer














If A.ndim is not in 1, 2, 3, your code tries to return a non-existing string s. It would be better to be explicit about what your code supports atm:



def ndtotext(A, w=None, h=None):
...
else:
raise NotImplementedError("Currently only 1 - 3 dimensions are supported")
return s


While we are at the point of having your code be clear about what is happening, you should add a docstring explaining what your code does:



def ndtotext(A, w=None, h=None):
"""Returns a string to pretty print the numpy.ndarray `A`.

Currently supports 1 - 3 dimensions only.
Raises a NotImplementedError if an array with more dimensions is passed.

Describe `w` and `h`.
"""
...


Next, Python has an official style-guide, PEP8, which programmers are encouraged to follow. One of the things it recommends is surrounding operators with spaces (which I fixed in the rest of the code) and using lower_case for variables and functions (which I left as is for now).



Now, let's come to your actual code:



  • You calculate some values multiple times (like str(A[0])), save those to a variable.

  • If you want to compare to None, use is (since it is a singleton).

  • No else needed after an if...return (this is a matter of personal style, I prefer not having the additional level of indentation).

  • Use str.rjust to add enough whitespace in front of your strings. You could also use str.format for this, but it looks less nice.

  • Your w has a weird structure, with the width of the first column being the last entry and the rest starting at zero.

  • Give the unicode values names. And then add functions to draw a line of specified length.


  • String addition is costly and slow. Try to consistently use building a list and str.joining it.



    UPPER_LEFT = u'u250c'
    UPPER_RIGHT = u'u2510'
    LOWER_LEFT = u'u2514'
    LOWER_RIGHT = u'u2518'
    HORIZONTAL = u'u2500'
    VERTICAL = u'u2502'

    def upper_line(width):
    return UPPER_LEFT + HORIZONTAL * (width - 2) + UPPER_RIGHT

    def lower_line(width):
    return LOWER_LEFT + HORIZONTAL * (width - 2) + LOWER_RIGHT

    def left_line(height):
    return "n".join([UPPER_LEFT] + [VERTICAL] * (height - 2)] + [LOWER_LEFT])

    def right_line(height):
    return "n".join([UPPER_RIGHT] + [VERTICAL] * (height - 2)] + [LOWER_RIGHT])

    def ndtotext(A, w=None, h=None):
    """Returns a string to pretty print the numpy.ndarray `A`.

    Currently supports 1 - 3 dimensions only.
    Raises a NotImplementedError if an array with more dimensions is passed.

    Describe `w` and `h`.
    """
    if A.ndim == 1:
    if w is None:
    return str(A)
    s = " ".join([str(value).rjust(width) for value, width in zip(A, w)])
    return '[]'.format(s)
    elif A.ndim == 2:
    widths = [max([len(str(s)) for s in A[:, i]]) for i in range(A.shape[1])]
    s = "".join([' ' + ndtotext(AA, w=widths) + 'n' for AA in A])
    w0 = sum(widths) + len(widths) - 1 + 2 # spaces between elements and corners
    return upper_line(w0) + s + lower_line(w0)
    elif A.ndim == 3:
    h = A.shape[1]
    strings = [left_line(h)]
    strings.extend(ndtotext(a) + 'n' for a in A)
    strings.append(right_line(h))
    return 'n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))
    raise NotImplementedError("Currently only 1 - 3 dimensions are supported")


This can probably be even more compactified, but I think it is a good start.







share|improve this answer














share|improve this answer



share|improve this answer








edited 42 mins ago

























answered 47 mins ago









Graipher

21.7k53183




21.7k53183











  • wow. awesome. thanks a lot. I'm gonna try your points and get back here.
    – Foad
    6 mins ago
















  • wow. awesome. thanks a lot. I'm gonna try your points and get back here.
    – Foad
    6 mins ago















wow. awesome. thanks a lot. I'm gonna try your points and get back here.
– Foad
6 mins ago




wow. awesome. thanks a lot. I'm gonna try your points and get back here.
– Foad
6 mins ago










Foad is a new contributor. Be nice, and check out our Code of Conduct.









 

draft saved


draft discarded


















Foad is a new contributor. Be nice, and check out our Code of Conduct.












Foad is a new contributor. Be nice, and check out our Code of Conduct.











Foad is a new contributor. Be nice, and check out our Code of Conduct.













 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f207139%2fpretty-printing-of-the-numpy-ndarrays%23new-answer', 'question_page');

);

Post as a guest













































































Comments

Popular posts from this blog

What does second last employer means? [closed]

List of Gilmore Girls characters

Confectionery