You calculate some values multiple times (like
str(A[0])
), save those to a variable.If you want to compare to
None
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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'' \n' for AA in A]) w0 = sum(widths) + len(widths) - 1 + 2 # spaces between elements and corners return upper_line(w0) + '\n' + 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")
Example usage:
x = np.arange(12)
print(ndtotext(x))
[ 0 1 2 3 4 5 6 7 8 9 10 11]
print(ndtotext(x.reshape(3, 4)))
┌───────────┐
[0 1 2 3]
[4 5 6 7]
[8 9 10 11]
└───────────┘
print(ndtotext(x.reshape(3, 2, 2)))
┌┌─────┐┌─────┐┌───────┐┐
│ [0 1] [4 5] [ 8 9] │
│ [2 3] [6 7] [10 11] │
└└─────┘└─────┘└───────┘┘
You calculate some values multiple times (like
str(A[0])
), save those to a variable.If you want to compare to
None
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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")
You calculate some values multiple times (like
str(A[0])
), save those to a variable.If you want to compare to
None
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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 + UPPER_RIGHT def lower_line(width): return LOWER_LEFT + HORIZONTAL * width + LOWER_RIGHT def left_line(height): return "\n".join([UPPER_LEFT] + [VERTICAL] * height + [LOWER_LEFT]) def right_line(height): return "\n".join([UPPER_RIGHT] + [VERTICAL] * height + [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) + '\n' + 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")
Example usage:
x = np.arange(12)
print(ndtotext(x))
[ 0 1 2 3 4 5 6 7 8 9 10 11]
print(ndtotext(x.reshape(3, 4)))
┌───────────┐
[0 1 2 3]
[4 5 6 7]
[8 9 10 11]
└───────────┘
print(ndtotext(x.reshape(3, 2, 2)))
┌┌─────┐┌─────┐┌───────┐┐
│ [0 1] [4 5] [ 8 9] │
│ [2 3] [6 7] [10 11] │
└└─────┘└─────┘└───────┘┘
You calculate some values multiple times (like
str(A[0])
), save those to a variable.If you want to compare to
None
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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)] + [symbols['lower_right']][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")
You calculate some values multiple times (like
str(A[0])
), save those to a variable.If you want to compare to
None
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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)] + [symbols['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")
You calculate some values multiple times (like
str(A[0])
), save those to a variable.If you want to compare to
None
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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")
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
, useis
(since it is a singleton).No
else
needed after anif...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 usestr.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.join
ing 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)] + [symbols['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.