使用unicode字符打印numpy ndarrays很漂亮

2024-10-02 18:17:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近注意到Python打印功能对于numpyndaray是不一致的。例如,它水平打印水平1D数组:

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

但是一个1D水平阵列,垂直方向有冗余的支架:

^{pr2}$

水平一维垂直阵列:

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

以及一个二维阵列:

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

第一个维度现在是垂直的。当所有尺寸都是垂直打印时,情况会变得更糟:

C=np.array([[[111,112],[121,122]],[[211,212],[221,222]]])
print(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\u2518 
B -->  ┌┌──┐┌──┐┌──┐┐ 
       │ 11  21  31 │
       │ 12  22  32 │
       │ 13  23  33 │
       └└──┘└──┘└──┘┘ 

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

我找到了这个gist,它处理不同的位数。我试图建立一个递归函数的原型来实现上述概念:

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

它有点适用于A1A2A3和{},但不适用于C。如果您能帮助我知道npprint应该如何实现上述任意维numpy ndarrays的输出,我将不胜感激?在

p.S.1.在Jupyter环境中,可以在降价中使用乳胶\mathtools\underbracket和{}。Sympy漂亮的打印功能也是一个很好的起点。它可以使用ASCII,Unicode,LaTeX。。。在

p.S.2.有人告诉我,ndarray的印刷方式确实是一致的。不过,我觉得这是有线和非直观的。有一个灵活的漂亮的打印功能可以帮助以不同的形式显示日期。在

p.S.3.同伙已经考虑了我在这里提到的两点。它们的矩阵模是非常一致的(A1A2是相同的),而且它们还有一个pprint函数,它做了同样的事情,我希望从npprint这里得到。在

p.S.4.对于那些跟进这个想法的人,我在这里整合了所有的东西this Jupyter Notebook


Tags: 函数功能numpya2a1npunicode水平
2条回答

对我来说,理解numpy数组与我心目中的MATLAB矩阵或多维数学数组完全不同,这是一个相当大的启示。它们是相当同质和统一的嵌套Python列表。我还了解到,numpy数组的第一个维度是最深的/内部的一对方括号,它水平打印,然后从那里垂直打印第二个维度,垂直打印第三个空间线。。。在

总之,我的东西有一个ppring函数(灵感来自Sympy的命名约定)可以帮助很多。因此,我将把一个非常糟糕的实现放在这里,希望它能激励其他高级Python想出更好的解决方案:

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')  

对于一维和二维阵列,其结果在某种程度上是可以接受的:

^{pr2}$

这确实是一个非常糟糕的代码,它只适用于整数。希望其他人能想出更好的解决办法。在

p.S.1.Eric Wieser已经为IPython/Jupiter实现了一个非常好的HTML原型,可以看到here

enter image description here

您可以关注numpy邮件列表here上的讨论。在

p.S.2.我也发布了这个想法here on Reddit。在

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

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

举个例子:

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] │
└└───────────────────┘└───────────────────┘└───────────────────┘└───────────────────┘┘

在每种情况下,最终尺寸的每个实例都打印在一行上。这里没有不一致的地方。在

尝试各种形式:

a = np.random.rand(5, 4, 3)
print(a)

更改a中的维数(例如,通过添加更多由逗号分隔的整数)。您会发现,每次打印a,打印对象中的每一行都有k值,其中k是{}形状中的最后一个整数。在

相关问题 更多 >