簡體   English   中英

通過 pandas 將復雜的嵌套 json 轉換為 csv

[英]Converting complex nested json to csv via pandas

我有以下 json 文件

{
    "matches": [
        {
            "team": "Sunrisers Hyderabad",
            "overallResult": "Won",
            "totalMatches": 3,
            "margins": [
                {
                    "bar": 290
                },
                {
                    "bar": 90
                }
            ]
        },
        {
            "team": "Pune Warriors",
            "overallResult": "None",
            "totalMatches": 0,
            "margins": null
        }
    ],
    "totalMatches": 70
}

注意 - 上面的 json 是原始 json 的片段。 實際文件在“邊距”之后包含更多屬性,其中一些是嵌套的,而另一些則不是。 我只是為了簡潔起見並給出期望的概念。

我的目標是展平數據並將其加載到 CSV 中。 這是我到目前為止編寫的代碼 -

import json
import pandas as pd

path = r"/Users/samt/Downloads/test_data.json"

with open(path) as f:
    t_data = {}
    data = json.load(f)
    for team in data['matches']:
        if team['margins']:
            for idx, margin in enumerate(team['margins']):
                t_data['team'] = team['team']
                t_data['overallResult'] = team['overallResult']
                t_data['totalMatches'] = team['totalMatches']
                t_data['margin'] = margin.get('bar')
        else:
            t_data['team'] = team['team']
            t_data['overallResult'] = team['overallResult']
            t_data['totalMatches'] = team['totalMatches']
            t_data['margin'] = margin.get('bar')

    df = pd.DataFrame.from_dict(t_data, orient='index')
    print(df)            

我知道數據被覆蓋並且循環結構不正確。我對使用 Python 處理 JSON 對象有點陌生,我無法理解如何連接結果。

我的目標是一次,附加所有結果,使用 to_csv 並將它們轉換為行。 對於每個邊距,整個數據將被復制為單獨的行。 這是我期望的輸出。 有人可以幫忙翻譯一下嗎?

從我在網上找到的任何內容來看,它是關於首先收集字典項目,但如何將其轉換為行是我無法理解的。 此外,有沒有比為一個屬性(即邊距)循環兩次更好的方法來解析 json?

我不能使用 json_normalize 因為我們的環境不支持該庫。

[輸出數據]

1

您可以使用pd.DataFrame創建 DataFrame 並展開margins

import json
import pandas as pd

with open('data.json', 'r', encoding='utf-8') as f:
    data = json.loads(f.read())

df = pd.DataFrame(data['matches']).explode('margins', ignore_index=True)
print(df)

                  team overallResult  totalMatches       margins
0  Sunrisers Hyderabad           Won             3  {'bar': 290}
1  Sunrisers Hyderabad           Won             3   {'bar': 90}
2        Pune Warriors          None             0          None

然后將margins列中的None值填充到字典並將其轉換為列

bar = df['margins'].apply(lambda x: x if x else {'bar': pd.NA}).apply(pd.Series)
print(bar)

    bar
0   290
1    90
2  <NA>

最后,將系列加入原始數據框

df = df.join(bar).drop(columns='margins')
print(df)

                  team overallResult  totalMatches   bar
0  Sunrisers Hyderabad           Won             3   290
1  Sunrisers Hyderabad           Won             3    90
2        Pune Warriors          None             0  <NA>

使用 json 和 csv 模塊:為每個團隊創建一個字典,如果有一個邊距,則為每個邊距

import json, csv

s = '''{
    "matches": [
        {
            "team": "Sunrisers Hyderabad",
            "overallResult": "Won",
            "totalMatches": 3,
            "margins": [
                {
                    "bar": 290
                },
                {
                    "bar": 90
                }
            ]
        },
        {
            "team": "Pune Warriors",
            "overallResult": "None",
            "totalMatches": 0,
            "margins": null
        }
    ],
    "totalMatches": 70
}'''

j = json.loads(s)

matches = j['matches']
rows = []
for thing in matches:
    # print(thing)
    if not thing['margins']:
        rows.append(thing)
    else:
        for bar in (b['bar'] for b in thing['margins']):
            d = dict((k,thing[k]) for k in ('team','overallResult','totalMatches'))
            d['margins'] = bar
            rows.append(d)

# for row in rows: print(row)            

# using an in-memory stream for this example instead of an actual file
import io
f = io.StringIO(newline='')

fieldnames=('team','overallResult','totalMatches','margins')
writer = csv.DictWriter(f,fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
f.seek(0)
print(f.read())

team,overallResult,totalMatches,margins
Sunrisers Hyderabad,Won,3,290
Sunrisers Hyderabad,Won,3,90
Pune Warriors,None,0,

使用operator.itemgetter()可以幫助從字典中獲取多個項目值

>>> import operator
>>> items = operator.itemgetter(*('team','overallResult','totalMatches'))
>>> #items = operator.itemgetter('team','overallResult','totalMatches')
>>> #stuff = ('team','overallResult','totalMatches'))
>>> #items = operator.itemgetter(*stuff)
>>> d = {'margins': 90,
...   'overallResult': 'Won',
...   'team': 'Sunrisers Hyderabad',
...   'totalMatches': 3}
>>> items(d)
('Sunrisers Hyderabad', 'Won', 3)
>>>

我喜歡使用它並給可調用對象一個描述性的名稱,但我認為它在 SO 上的使用並不多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM