如何在序列化为pandas数据框时展平嵌套数据类?

我有包含其他数据类作为其字段的数据类:

@dataclass
class Bar:
    abc: int
    bed: int
    asd: int

@dataclass
class Foo:
    xy: int
    yz: Bar

然后我尝试通过 pandas 将其序列化为 csv,如下所示:

dataset = [Foo(xy=1, yz=Bar(abc=1, bed=2, asd=3))]
pd_dataset = pandas.DataFrame(vars(row) for row in dataset)
pd_dataset.to_csv('dataset_example.csv', index=False)

但我得到的结果与我想要达到的结果有点不同。准确地说,我现在得到:

xy,yz
1,"Bar(abc=1, bed=2, asd=3)"

而且我要:

xy,yz_abc,yz_bed,yz_asd
1,1,2,3

你能帮我把它弄好吗?我尝试编写自己的序列化函数并执行以下操作: pandas.DataFrame(asdict(row, dict_factory=row_to_dict) for row in dataset) 但我不知道如何正确编写它。

stack overflow How to flatten nested dataclass while serializing to pandas dataframe?
原文答案

答案:

作者头像

vars(Bar) 仔细创建所需的密钥可以做你想做的事。

dataset = [Foo(xy=1, yz=Bar(abc=1, bed=2, asd=3))]

res = []

for obj in dataset:
    d = {}
    for k, v in vars(obj).items():
        if isinstance(v, Bar):
            for k_, v_ in vars(vars(obj)[k]).items():
                d[f'{k}_{k_}'] = v_
        else:
            d[k] = v
    res.append(d)

print(res)
'''
[{'xy': 1, 'yz_abc': 1, 'yz_bed': 2, 'yz_asd': 3}]
'''
pd_dataset = pd.DataFrame.from_records(res)

print(pd_dataset)
'''
   xy  yz_abc  yz_bed  yz_asd
0   1       1       2       3
'''
作者头像

不需要像 this answer 中的外部库一样使用外部库,PANDAS为您提供所需的一切,形式为 pd.json_normalize

>>> import pandas as pd
... from dataclasses import asdict, dataclass
... 
... @dataclass
... class Bar:
...     abc: int
...     bed: int
...     asd: int
... 
... @dataclass
... class Foo:
...     xy: int
...     yz: Bar
... 
... dataset = [
...     Foo(xy=1, yz=Bar(abc=1, bed=2, asd=3)),
...     Foo(xy=10, yz=Bar(abc=10, bed=20, asd=30)),
... ]

>>> dataset
[Foo(xy=1, yz=Bar(abc=1, bed=2, asd=3)),
 Foo(xy=10, yz=Bar(abc=10, bed=20, asd=30))]

>>> df = pd.json_normalize(asdict(obj) for obj in dataset)
>>> df
   xy  yz.abc  yz.bed  yz.asd
0   1       1       2       3
1  10      10      20      30

>>> print(df.to_csv(index=False))
xy,yz.abc,yz.bed,yz.asd
1,1,2,3
10,10,20,30

我个人更喜欢上述默认 "." 分隔仪,但是如果您对下划线有强烈的感觉,Pandas也会为您覆盖:

>>> pd.json_normalize((asdict(obj) for obj in dataset), sep="_")
   xy  yz_abc  yz_bed  yz_asd
0   1       1       2       3
1  10      10      20      30
作者头像

好的,我在发布问题后自己想通了。为了解决这个问题,我需要下载一个名为 flatten-dict 的库。然后像这样使用它:

pd_dataset = pandas.DataFrame(flatten(asdict(row), reducer='underscore') for row in dataset)

如果这种方法还有改进的余地,请告诉我,但我发现它非常干净和简单。