numpy 为什么ruaml.yaml不能表示np数组?

jc3wubiy  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(136)

我刚开始使用Pydantic和ruaml.yaml,实际上我正在使用这两个工具做一个项目。
当我尝试加载一个模型配置并用mkdocs表示它时,我遇到了一个错误,它似乎ruaml.yaml无法转储到我的Pydantic模式的np.array对象上。

File "/usr/local/lib/python3.10/site-packages/ruamel/yaml/representer.py", line 337, in represent_undefined
                raise RepresenterError(f'cannot represent an object: {data!s}')
            ruamel.yaml.representer.RepresenterError: cannot represent an object: [0 0 0 0]

当我试图表达这一点时,它就会发生:

std: Optional[np.ndarray] = np.array([1.0, 1.0, 1.0, 1.0])
mean: Optional[np.ndarray] = np.array([0, 0, 0, 0])

有没有一个解决方案或我可以改变的东西?先谢谢你了!
测试:我尝试用以下代码替换它:

std: Optional[np.ndarray] = [1.0, 1.0, 1.0, 1.0]
mean: Optional[np.ndarray] = [0, 0, 0, 0]

但需要这些是np。数组对象为我的代码的其余部分。

50few1ms

50few1ms1#

YAML在Language Independent types for YAML 1.1中定义了几种类型。numpy.array不在这个列表中,主要是因为它不是独立于语言的,所以没有像这样的非标准表示。
所以你必须自己提供一个表示器,如果你用一个标记来表示它,如果你为那个标记提供了一个构造函数,你就可以加载回结果表示。
但是,为numpy.array创建一个表示器是不够的,因为stdmean都不是该类型,因为您已经通过提供的类型信息表明了自己。您必须为numpy.ndarraynumpy.float64(用于std的元素和numpy.int(用于mean的元素))提供表示器
一旦你这样做,你可以转储stdmean

import sys
from pathlib import Path
import numpy
import ruamel.yaml

path = Path('numpy.yaml')

np = numpy
data = dict(
  std = np.array([1.0, 1.0, 1.0, 1.0]),
  mean = np.array([0, 0, 0, 0]),
)

def represent_numpy_float64(self, value):
    return self.represent_float(value)  # alternatively dump as a tagged float

def represent_numpy_int64(self, value):
    return self.represent_int(value)  # alternatively dump as a tagged int

def represent_numpy_array(self, array, flow_style=None):
    tag = '!numpy.ndarray'
    value = []
    node = ruamel.yaml.nodes.SequenceNode(tag, value, flow_style=flow_style)
    for elem in array:
        node_elem = self.represent_data(elem)
        value.append(node_elem)
    if flow_style is None:
        node.flow_style = True
    return node

yaml = ruamel.yaml.YAML()
yaml.Representer.add_representer(numpy.ndarray, represent_numpy_array)
yaml.Representer.add_representer(numpy.float64, represent_numpy_float64)
yaml.Representer.add_representer(numpy.int64, represent_numpy_int64)
yaml.dump(data, path)
print(path.read_text(), end='')  # the file ends in a newline, don't add another one

其给出:

std: !numpy.ndarray [1.0, 1.0, 1.0, 1.0]
mean: !numpy.ndarray [0, 0, 0, 0]

ruamel.yaml的默认往返加载器,可以加载结果文件:

import sys

yaml = ruamel.yaml.YAML()
rtd = yaml.load(path)
print(f'{rtd}')
print('type of std', type(rtd['std']))
yaml.dump(rtd, sys.stdout)

其给出:

{'std': [1.0, 1.0, 1.0, 1.0], 'mean': [0, 0, 0, 0]}
type of std <class 'ruamel.yaml.comments.CommentedSeq'>
std: !numpy.ndarray [1.0, 1.0, 1.0, 1.0]
mean: !numpy.ndarray [0, 0, 0, 0]

正如你所看到的,这并没有将文件加载为numpy.ndarray。为此,你需要为标记提供一个构造函数(因为我们不转储标记的floats/int,所以你不需要为它们提供构造函数):

def construct_numpy_array(self, node):
    return numpy.array(self.construct_sequence(node))

yaml.Constructor.add_constructor('!numpy.ndarray', construct_numpy_array)

rtd = yaml.load(path)
print(f'{rtd}')
print('type of std', type(rtd['std']))

其给出:

{'std': array([1., 1., 1., 1.]), 'mean': array([0, 0, 0, 0])}
type of std <class 'numpy.ndarray'>

请注意,上述方法不适用于(间接)递归转储,因为construct_numpy_array没有yield语句。最好做一些类似的事情:

def construct_numpy_array(self, node):
    data = numpy.array([])
    yield data
    numpy.append(data, numpy.array(self.construct_sequence(node)))

但这并没有给予相同的结果(因为我对numpy不熟悉)。

相关问题