web-dev-qa-db-ja.com

PythonマルチレベルJSONをフラット化する

JSONをCSVファイルに変換しようとしています。これをさらに分析するために使用できます。私の構造の問題は、JSONファイルを変換するときにかなりのネストされたdict /リストがあることです。

私はpandas json_normalize()_を使おうとしましたが、最初のレベルのみがフラットになります。

import json
import pandas as pd
from pandas.io.json import json_normalize
from cs import CloudStack

api_key = xxxx
secret = xxxx
endpoint = xxxx

cs = CloudStack(endpoint=endpoint,
                key=api_key,
                secret=secret)

virtual_machines = cs.virtMach()

test = json_normalize(virtual_machines["virtualmachine"])

test.to_csv("test.csv", sep="|", index=False)

JSONファイル全体をフラット化する方法はありますか?単一(この場合は仮想マシン)エントリのCSVファイルへの単一行入力を作成できますか?私はここに投稿されたいくつかの解決策を試しましたが、私の結果は常に最初のレベルだけが平坦化されていました。

これはサンプルのJSONです(この場合も、「securitygroup」と「nic」の出力がJSON形式で表示されます。

{
    "count": 13,
    "virtualmachine": [
        {
            "id": "1082e2ed-ff66-40b1-a41b-26061afd4a0b",
            "name": "test-2",
            "displayname": "test-2",
            "securitygroup": [
                {
                    "id": "9e649fbc-3e64-4395-9629-5e1215b34e58",
                    "name": "test",
                    "tags": []
                }
            ],
            "nic": [
                {
                    "id": "79568b14-b377-4d4f-b024-87dc22492b8e",
                    "networkid": "05c0e278-7ab4-4a6d-aa9c-3158620b6471"
                },
                {
                    "id": "3d7f2818-1f19-46e7-aa98-956526c5b1ad",
                    "networkid": "b4648cfd-0795-43fc-9e50-6ee9ddefc5bd"
                    "traffictype": "Guest"
                }
            ],
            "hypervisor": "KVM",
            "affinitygroup": [],
            "isdynamicallyscalable": false
        }
    ]
}

よろしくお願いいたします。Bostjan

10
Bostjan

他の誰かがここで自分を見つけ、その後のプログラムによる処理により適したソリューションを探している場合:

リストをフラット化すると、リストの長さなどの見出しを処理する必要が生じます。たとえば、2つのリストがある場合、 2つの要素があると、4つの行が生成され、有効な各データ行が生成されます(実際の例については以下を参照してください)。

class MapFlattener:

    def __init__(self):
        self.headings = []
        self.rows = []

    def add_rows(self, headings, rows):
        self.headings = [*self.headings, *headings]
        if self.rows:
            new_rows = []
            for base_row in self.rows:
                for row in rows:
                    new_rows.append([*base_row, *row])
            self.rows = new_rows
        else:
            self.rows = rows

    def __call__(self, mapping):
        for heading, value in mapping.items():
            if isinstance(value, Mapping):
                sub_headings, sub_rows = MapFlattener()(value)
                sub_headings = [f'{heading}:{sub_heading}' for sub_heading in sub_headings]
                self.add_rows(sub_headings, sub_rows)
                continue

            if isinstance(value, list):
                self.add_rows([heading], [[e] for e in value])
                continue

            self.add_rows([heading], [[value]])

        return self.headings, self.rows


def map_flatten(mapping):
    return MapFlattener()(mapping)

これにより、リレーショナルデータに沿った出力が作成されます。

In [22]: map_flatten({'l': [1,2]})                                                                                                          
Out[22]: (['l'], [[1], [2]])

In [23]: map_flatten({'l': [1,2], 'n': 7})                                                                                                  
Out[23]: (['l', 'n'], [[1, 7], [2, 7]])

In [24]: map_flatten({'l': [1,2], 'n': 7, 'o': {'a': 1, 'b': 2}})                                                                           
Out[24]: (['l', 'n', 'o:a', 'o:b'], [[1, 7, 1, 2], [2, 7, 1, 2]])

これは、スプレッドシートなどでcsvを使用していて、フラット化されたデータを処理する必要がある場合に特に便利です。

0
Paul Whipp