使用 unicode 字符漂亮地打印 numpy ndarrays [英] pretty printing numpy ndarrays using unicode characters

查看:22
本文介绍了使用 unicode 字符漂亮地打印 numpy ndarrays的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近注意到 NumPy ndarays 的 Python 打印功能不一致.例如,它水平打印一个水平一维数组:

将 numpy 导入为 npA1=np.array([1,2,3])打印(A1)#-->[1 2 3]

但是一个一维水平阵列,垂直方向有多余的括号:

A2=np.array([[1],[2],[3]])打印(A2)#-->[[1]# [2]# [3]]

水平一维垂直阵列:

A3=np.array([[1,2,3]])打印(A3)#-->[[1 2 3]]

和一个二维数组:

B=np.array([[11,12,13],[21,22,23],[31,32,32]])打印(B)# -->[[11 12 13]# [21 22 23]# [31 32 32]]

第一个维度现在是垂直的.更高维度的情况更糟,因为它们都是垂直打印的:

C=np.array([[[111,112],[121,122]],[[211,212],[221,222]]])打印(C)#-->[[[111 112]# [121 122]]## [[211 212]# [221 222]]]

在我看来,一致的行为是水平打印偶数维度,垂直打印奇数维度.使用 Unicode 字符可以很好地格式化它.我想知道是否可以创建一个函数来将上面的数组打印为:

A1 -->[1 2 3]A2 -->┌┌─┐┌─┐┌─┐┐│ 1 2 3 │└└─┘└─┘└─┘┘A3 -->┌┌─┐┐#\u250c\u2500\u2510│ 1 │ # \u2502│ 2 ││ 3 │└└─┘┘ # \u2514\u2500\u2518B -->┌┌──┐┌──┐┌──┐┐│ 11 21 31 ││ 12 22 32 ││ 13 23 33 │└└──┘└──┘└──┘┘C-->┌┌───────────┐┌─────────┐┐│ [111 112] [211 212] ││ [121 122] [221 222] │└└───────────┘└─────────┘┘

我发现了这个

您可以在此处.

PS2.我也发布了这个想法在 Reddit 上.

P.S.3 我花了一些时间将代码扩展到 3D 维数组:

def ndtotext(A, w=None, h=None):如果 A.ndim==1:如果 w == 无:返回 str(A)别的:s = '['对于 enumerate(A[:-1]) 中的 i,AA:s += str(AA)+' '*(max(w[i],len(str(AA)))-len(str(AA))+1)s += str(A[-1])+' '*(max(w[-1],len(str(A[-1])))-len(str(A[-1]))) +'] '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)+1s= u'\u250c'+u'\u2500'*w0+u'\u2510' +'\n'对于 A 中的 AA: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]字符串.append(s2)字符串.插入(0,s1)s='\n'.join(''.join(pair) for pair in zip(*map(str.splitlines, strings)))返回

举个例子:

shape = 4, 3, 6B2=np.arange(np.prod(shape)).reshape(shape)打印(B2)打印(ndtotext(B2))[[[ 0 1 2 3 4 5][ 6 7 8 9 10 11][12 13 14 15 16 17]][[18 19 20 21 22 23][24 25 26 27 28 29][30 31 32 33 34 35]][[36 37 38 39 40 41][42 43 44 45 46 47][48 49 50 51 52 53]][[54 55 56 57 58 59][60 61 62 63 64 65][66 67 68 69 70 71]]]┌┌───────────────────┐┌───────────────────┐┌──────────────────────┐┌────────────────────┐┐│ [0 1 2 3 4 5 ] [18 19 20 21 22 23] [36 37 38 39 40 41] [54 55 56 57 58 59] ││ [6 7 8 9 10 11] [24 25 26 27 28 29] [42 43 44 45 46 47] [60 61 62 63 64 65] ││ [12 13 14 15 16 17] [30 31 32 33 34 35] [48 49 50 51 52 53] [66 67 68 69 70 71] │└└─────────────────────┘└────────────────────┘└──────────────────────┘└────────────────────┘┘

I have recently noticed that Python printing functionality is not consistent for NumPy ndarays. For example it prints a horizontal 1D array horizontally:

import numpy as np
A1=np.array([1,2,3])
print(A1)
#--> [1 2 3]

but a 1D horizontal array with redundant brackets vertically:

A2=np.array([[1],[2],[3]])
print(A2)
#--> [[1]
#     [2]
#     [3]]

a 1D vertical array horizontally:

A3=np.array([[1,2,3]])
print(A3)
#--> [[1 2 3]]

and a 2D array:

B=np.array([[11,12,13],[21,22,23],[31,32,32]])
print(B)
# --> [[11 12 13]
#      [21 22 23]
#      [31 32 32]]

where the first dimension is now vertical. It gets even worse for higher dimensions as all of them are printed vertically:

C=np.array([[[111,112],[121,122]],[[211,212],[221,222]]])
print(C)
#--> [[[111 112]
#      [121 122]]
#
#     [[211 212]
#      [221 222]]]

A consistent behavior in my opinion would be to print the even dimensions horizontally and odd ones vertically. Using Unicode characters it would be possible to format it nicely. I was wondering if it is possible to create a function to print above arrays as:

A1 --> [1 2 3]
A2 --> ┌┌─┐┌─┐┌─┐┐
       │ 1  2  3 │
       └└─┘└─┘└─┘┘
A3 --> ┌┌─┐┐ # \u250c\u2500\u2510 
       │ 1 │ # \u2502
       │ 2 │
       │ 3 │
       └└─┘┘ # \u2514\u2500\u2518 
B -->  ┌┌──┐┌──┐┌──┐┐ 
       │ 11  21  31 │
       │ 12  22  32 │
       │ 13  23  33 │
       └└──┘└──┘└──┘┘ 

C -->  ┌┌─────────┐┌─────────┐┐
       │ [111 112]  [211 212] │
       │ [121 122]  [221 222] │
       └└─────────┘└─────────┘┘ 

I found this gist which takes care of the different number of digits. I tried to prototype a recursive function to implement the above concept:

 def npprint(A):
     assert isinstance(A, np.ndarray), "input of npprint must be array like"
     if A.ndim==1 :
         print(A)
     else:
         for i in range(A.shape[1]):
             npprint(A[:,i]) 

It kinda works for A1, A2, A3 and B but not for C. I would appreciate if you could help me know how the npprint should be to achieve above output for arbitrary dimension numpy ndarrays?

P.S.1. In Jupyter environment one can use LaTeX \mathtools \underbracket and \overbracket in Markdown. Sympy's pretty printing functionality is also a great start point. It can use ASCII, Unicode, LaTeX...

P.S.2. I'm being told that there is indeed a consistency in the way ndarrays are being printed. however IMHO it is kind of wired and non-intuitive. Having a flexible pretty printing function could help a lot to display ndarrays in different forms.

P.S.3. Sympy guys have already considered both points I have mentioned here. their Matrix module is pretty consistent (A1 and A2 are the same) and they also have a pprint function which does kind of the same thing and I expect from npprint here.

P.S.4. For those who follow up this idea I have integrated everythin here in this Jupyter Notebook

解决方案

It was quite a revelation to me understanding numpy arrays are not anything like MATLAB matrices or multidimensional mathematical arrays I had in mind. They are rather homogeneous and uniform nested Python lists. I also understood that the first dimension of a numpy array is the most deepest/inner pairs of square brackets which is printed horizontally and then from there second dimension is printed vertically, Third vertically with a spaced line...

Anyways I thing having an ppring function (inspired by Sympy's naming convention) could help a lot. so I'm going to put a very bad implementation here hoping it will inspire other advanced Pythoners to come up with better solutions:

def pprint(A):
    if A.ndim==1:
        print(A)
    else:
        w = max([len(str(s)) for s in A]) 
        print(u'\u250c'+u'\u2500'*w+u'\u2510') 
        for AA in A:
            print(' ', end='')
            print('[', end='')
            for i,AAA in enumerate(AA[:-1]):
                w1=max([len(str(s)) for s in A[:,i]])
                print(str(AAA)+' '*(w1-len(str(AAA))+1),end='')
            w1=max([len(str(s)) for s in A[:,-1]])
            print(str(AA[-1])+' '*(w1-len(str(AA[-1]))),end='')
            print(']')
        print(u'\u2514'+u'\u2500'*w+u'\u2518')  

and the result is somewhat acceptable for 1D and 2D arrays:

B1=np.array([[111,122,133],[21,22,23],[31,32,33]])
pprint(B1)

#┌─────────────┐
# [111 122 133]
# [21  22  23 ]
# [31  32  33 ]
#└─────────────┘

this is indeed a very bad code, it only works for integers. hopefully others will come up with better solutions.

P.S.1. Eric Wieser has already implemented a very nice HTML prototype for IPython/Jupiter which can seen here:

You may follow the discussion on numpy mailing list here.

P.S.2. I also posted this idea here on Reddit.

P.S.3 I spent some time to extend the code to 3D dimensional arrays:

def ndtotext(A, w=None, h=None):
    if A.ndim==1:
        if w == None :
            return str(A)
        else:
            s= '['
            for i,AA in enumerate(A[:-1]):
                s += str(AA)+' '*(max(w[i],len(str(AA)))-len(str(AA))+1)
            s += str(A[-1])+' '*(max(w[-1],len(str(A[-1])))-len(str(A[-1]))) +'] '
    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

and as an example:

shape = 4, 3, 6
B2=np.arange(np.prod(shape)).reshape(shape)
print(B2)
print(ndtotext(B2))        


[[[ 0  1  2  3  4  5]
  [ 6  7  8  9 10 11]
  [12 13 14 15 16 17]]

 [[18 19 20 21 22 23]
  [24 25 26 27 28 29]
  [30 31 32 33 34 35]]

 [[36 37 38 39 40 41]
  [42 43 44 45 46 47]
  [48 49 50 51 52 53]]

 [[54 55 56 57 58 59]
  [60 61 62 63 64 65]
  [66 67 68 69 70 71]]]
┌┌───────────────────┐┌───────────────────┐┌───────────────────┐┌───────────────────┐┐
│ [0  1  2  3  4  5 ]  [18 19 20 21 22 23]  [36 37 38 39 40 41]  [54 55 56 57 58 59] │
│ [6  7  8  9  10 11]  [24 25 26 27 28 29]  [42 43 44 45 46 47]  [60 61 62 63 64 65] │
│ [12 13 14 15 16 17]  [30 31 32 33 34 35]  [48 49 50 51 52 53]  [66 67 68 69 70 71] │
└└───────────────────┘└───────────────────┘└───────────────────┘└───────────────────┘┘

这篇关于使用 unicode 字符漂亮地打印 numpy ndarrays的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆