yaml.dump在Python中对象的numpy.array类型属性抛出错误

时间:2015-11-19 03:41:38

标签: python arrays python-3.x numpy yaml

我喜欢我的对象紧凑地打印出来(不需要加载),因此numpy.array被打印为常规元组(在本例中)。相反,我看到一条错误消息TypeError: data type not understood

知道导致错误消息的原因和(一旦解决)如何

class A:
    def __init__(self):
        from numpy import array
        self.a_array = array([1,2,3])

    def __repr__(self):
        from yaml import dump
        return dump(self, default_flow_style=False)

A()

所需的输出类似于:

object:A
a_array: 
- 1, 2, 3

有什么想法吗?

更新:这可能有效(如果可实现):有没有办法让yaml表示器将任何array变量x替换为x.tolist()表示?

2 个答案:

答案 0 :(得分:3)

您是否有兴趣生成有效的yaml,或仅使用yaml作为展示对象的方式?需要没有负载的短语'建议后者。

但为什么要专注于yaml?它是否以您想要的方式本地处理列表或序列?

如果我使用tolist将数组转换为yaml可以转储的列表,我会得到:

In [130]: a = np.arange(3)
In [131]: print(yaml.dump({'a':a.tolist()},default_flow_style=False))
a:
- 0
- 1
- 2

In [132]: print(yaml.dump({'a':a.tolist()},default_flow_style=True))
{a: [0, 1, 2]}

我可以删掉字典部分。但无论哪种方式,列表部分都不会显示为:

- 1, 2, 3

我不知道yaml.dump对默认数组显示有何改进:

In [133]: print(a)
[0 1 2]
In [134]: print(repr(a))
array([0, 1, 2])

对于2D阵列(以及可以转换为2d的阵列),np.savetxt提供紧凑的显示,并使用fmt选项来控制细节:

In [139]: np.savetxt('test',a[None,:], fmt='%d')
In [140]: cat 'test'
0 1 2

这里我实际上写了一个文件,并用系统cat显示,但我也可以写入字符串缓冲区。

但我可以做得更好。 savetxt只是将数组一次一行写入文件。我可以直接使用相同的格式样式。

我为fmt(此处为1d数组)中的每个项目创建了一个%字符串,其a规范。然后fmt%tuple(...)格式化它。这只是直接的Python字符串格式化。

In [144]: fmt = ', '.join(['%d']*a.shape[0])
In [145]: fmt
Out[145]: '%d, %d, %d'
In [146]: fmt%tuple(a.tolist())
Out[146]: '0, 1, 2'

我可以为该格式添加-和缩进,冒号等。

================================

import numpy as np

class A:
    def __init__(self, anArray):
        self.a_array = anArray

    def __repr__(self):
        astr = ['object: %s'%self.__class__]
        astr.append('a_array:')
        astr.append(self.repr_array())
        return '\n'.join(astr)

    def repr_array(self):
        a = self.a_array
        if a.ndim==1:
            a = a[None,:]
        fmt = ', '.join(['%d']*a.shape[1])
        fmt = '- '+fmt
        astr = []
        for row in a:
             astr.append(fmt%tuple(row))
        astr = '\n'.join(astr)
        return astr

print A(np.arange(3))

print A(np.ones((3,2)))

生成

object: __main__.A
a_array:
- 0, 1, 2

表示1d数组,

object: __main__.A
a_array:
- 1, 1
- 1, 1
- 1, 1

表示2d数组。

=======================================

import yaml
def numpy_representer_str(dumper, data):
    # first cut ndarray yaml representer
    astr = ', '.join(['%s']*data.shape[0])%tuple(data)
    return dumper.represent_scalar('!ndarray:', astr)

def numpy_representer_seq(dumper, data):
    return dumper.represent_sequence('!ndarray:', data.tolist())

yaml.add_representer(np.ndarray, numpy_representer_str)
print (yaml.dump({'a':np.arange(4)},default_flow_style=False))

yaml.add_representer(np.ndarray, numpy_representer_seq)
print (yaml.dump({'a':np.arange(4)},default_flow_style=False))

class A:
    def __init__(self, anArray):
        self.a_array = anArray

    def __repr__(self):
        astr = ['object: %s'%self.__class__]
        astr.append('a_array:')
        astr.append(self.repr_array())
        return '\n'.join(astr)

    def repr_array(self):
        return yaml.dump(self.a_array)
print (A(np.arange(3)))
print (A(np.arange(6).reshape(2,3)))

对于不同风格的numpy代表,我得到的印象如下:

a: !ndarray: '0, 1, 2, 3'   # the string version

a: !ndarray:         # the sequence version
- 0
- 1
- 2
- 3

object: <class '__main__.A'>     # sequence version with 1d
a_array:
!ndarray: [0, 1, 2]

object: <class '__main__.A'>    # sequence version with 2d
a_array:
!ndarray:
- [0, 1, 2]
- [3, 4, 5]

答案 1 :(得分:1)

在A对象中表示时,可以将numpy数组封送到列表中。然后在从对象中检索时解组它:

class A:
    def __init__(self):
        from numpy import array
        self.a_lst = [1,2,3]

    def __repr__(self):
        from yaml import dump
        return dump(self, default_flow_style=False)

    # convert internal list to numpy array before returning.
    @property
    def my_arr(self):
        return array(self.a_lst)

    # convert array to list before storing internally.
    @my_arr.setter
    def my_arr(self, array):
        self.a_lst = array.tolist()

print(repr(A()))

关键是要确保在对象内部将数组存储为普通的python列表,这样就可以确保可以进行yaml转储。

可能更好的选择是使用numpy提供的内置dump功能。请参阅answer here