制作 pandas 系列安全的 YAML

Making a pandas series safe YAML

我正在使用一个将 pandas 系列转储到 yaml 文件的脚本:

with open('ex.py','w') as f:
    yaml.dump(a_series,f)

然后是打开 pandas 系列的 yaml 文件的另一个脚本:

with open('ex.py','r') as f:
    yaml.safe_load(a_series,f)

我正在尝试 safe_load 该系列,但出现构造函数错误。如何指定 pandas 系列可以安全加载?

当您使用 PyYAML 的 load 时,您指定正在加载的 YAML 文档中的 所有内容 都是安全的。这就是为什么你需要使用 yaml.safe_load

在您的情况下,这会导致错误,因为 safe_load 不知道如何构建 pandas 在 YAML 文档中具有标签的内部构件,例如:

!!python/name:pandas.core.indexes.base.Index

!!python/tuple

等等

您需要为所有对象提供构造函数,将它们添加到 SafeLoader,然后执行 a_series = yaml.load(f)。 这样做可能需要做很多工作,尤其是因为对您的系列中使用的数据所做的看似很小的更改可能需要您添加构造函数。

您可以转储 Series 的 dict 表示并将其加载回来。当然在这个过程中丢失了一些信息,我不确定是否可以接受:

import sys
import yaml
from pandas import Series

def series_representer(dumper, data):
    return dumper.represent_mapping(u'!pandas.series', data.to_dict())

yaml.add_representer(Series, series_representer, Dumper=yaml.SafeDumper)

def series_constructor(loader, node):
    d = loader.construct_mapping(node)
    return Series(data)

yaml.add_constructor(u'!pandas.series', series_constructor, Loader=yaml.SafeLoader)

data = Series([1,2,3,4,5], index=['a', 'b', 'c', 'd', 'e'])

with open('ex.yaml', 'w') as f:
    yaml.safe_dump(data, f)

with open('ex.yaml') as f:
    s = yaml.safe_load(f)

print(s)
print(type(s))

给出:

a    1
b    2
c    3
d    4
e    5
dtype: int64
<class 'pandas.core.series.Series'>

并且 ex.yaml 文件包含:

!pandas.series {a: 1, b: 2, c: 3, d: 4, e: 5}

有几点需要注意:

  • YAML 文档通常写入扩展名为 .yaml 的文件。使用 .py 一定会让您感到困惑,或者您在某些时候覆盖了一些程序源文件。

  • yaml.load()yaml.safe_load() 将流作为您使用它们的第一个参数:

    data = yaml.safe_load(stream)
    

    不喜欢:

    yaml.safe_load(data, stream)
    
  • 有一个就更好了,可以构造自引用数据结构。然而 Series.append() 似乎并不适用于此:

    def series_constructor(loader, node):
        d = Series()
        yield d
        d.append(Series(loader.construct_mapping(node)))
    

如果通过字典转储 Series 不够好(因为它简化了系列的数据),并且如果您不关心生成的 YAML 的可读性,您可以代替 .to_dict() 用于 to_pickle() 但您必须使用临时文件,因为该方法不够灵活,无法处理类似对象的文件,并且需要文件名字符串作为参数。