【问题标题】:How to flatten nested dataclass while serializing to pandas dataframe?如何在序列化为熊猫数据框时展平嵌套数据类?
【发布时间】:2021-07-07 12:39:30
【问题描述】:

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

@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) 但我不知道如何正确编写它。

【问题讨论】:

    标签: python pandas python-dataclasses


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-11-29
          • 2019-03-18
          • 2021-06-13
          • 2020-09-06
          • 1970-01-01
          • 1970-01-01
          • 2019-03-02
          • 2019-08-03
          相关资源
          最近更新 更多