來源:Python數據科學
作者:東哥起飛
調用API和文檔資料庫會返回嵌套的JSON對象,當我們使用Python嘗試將嵌套結構中的鍵轉換為列時,數據加載到pandas中往往會得到如下結果:
df = pd.DataFrame.from_records(results [「 issues」],columns = [「 key」,「 fields」])
說明:這裡results是一個大的字典,issues是results其中的一個鍵,issues的值為一個嵌套JSON對象字典的列表,後面會看到JSON嵌套結構。
問題在於API返回了嵌套的JSON結構,而我們關心的鍵在對象中確處於不同級別。
嵌套的JSON結構張成這樣的。
而我們想要的是下面這樣的。
下面以一個API返回的數據為例,API通常包含有關欄位的元數據。假設下面這些是我們想要的欄位。
如上,我們選擇要提取的欄位在issues列表內的JSON結構中分別處於4個不同的嵌套級別,一環扣一環。
{
"expand": "schema,names",
"issues": [
{
"fields": {
"issuetype": {
"avatarId": 10300,
"description": "",
"id": "10005",
"name": "New Feature",
"subtask": False
},
"status": {
"description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.",
"id": "5",
"name": "Resolved",
"statusCategory": {
"colorName": "green",
"id": 3,
"key": "done",
"name": "Done",
}
},
"summary": "Recovered data collection Defraglar $MFT problem"
},
"id": "11861",
"key": "CAE-160",
},
{
"fields": {
... more issues],
"maxResults": 5,
"startAt": 0,
"total": 160
}
一種選擇是直接擼碼,寫一個查找特定欄位的函數,但問題是必須對每個嵌套欄位調用此函數,然後再調用.apply到DataFrame中的新列。
為獲取我們想要的幾個欄位,首先我們提取fields鍵內的對象至列:
df = (
df["fields"]
.apply(pd.Series)
.merge(df, left_index=True, right_index = True)
)
從上表看出,只有summary是可用的,issuetype、status等仍然埋在嵌套對象中。
下面是提取issuetype中的name的一種方法。
# 提取issue type的name到一個新列叫"issue_type"
df_issue_type = (
df["issuetype"]
.apply(pd.Series)
.rename(columns={"name": "issue_type_name"})["issue_type_name"]
)
df = df.assign(issue_type_name = df_issue_type)
像上面這樣,如果嵌套層級特別多,就需要自己手擼一個遞歸來實現了,因為每層嵌套都需要調用一個像上面解析並添加到新列的方法。
對於編程基礎薄弱的朋友,手擼一個其實還挺麻煩的,尤其是對於數據分析師,著急想用數據的時候,希望可以快速拿到結構化的數據進行分析。
下面東哥分享一個pandas的內置解決方案。
內置的解決方案pandas中有一個牛逼的內置功能叫 .json_normalize。
pandas的文檔中提到:將半結構化JSON數據規範化為平面表。
前面方案的所有代碼,用這個內置功能僅需要3行就可搞定。步驟很簡單,懂了下面幾個用法即可。
FIELDS = ["key", "fields.summary", "fields.issuetype.name", "fields.status.name", "fields.status.statusCategory.name"]
df = pd.json_normalize(results["issues"])
df[FIELDS]
除了像上面那樣傳遞results["issues"]列表之外,我們還使用record_path參數在JSON對象中指定列表的路徑。
# 使用路徑而不是直接用results["issues"]
pd.json_normalize(results, record_path="issues")[FIELDS]
還可以使用sep參數自定義嵌套結構連接的分隔符,比如下面將默認的「.」替換「-」。
# 用 "-" 替換默認的 "."
FIELDS = ["key", "fields-summary", "fields-issuetype-name", "fields-status-name", "fields-status-statusCategory-name"]
pd.json_normalize(results["issues"], sep = "-")[FIELDS]
如果不想遞歸到每個子對象,可以使用max_level參數控制深度。在這種情況下,由於statusCategory.name欄位位於JSON對象的第4級,因此不會包含在結果DataFrame中。
# 只深入到嵌套第二級
pd.json_normalize(results, record_path="issues", max_level = 2)
下面是.json_normalize的pandas官方文檔說明,如有不明白可自行學習,本次東哥就介紹到這裡。
pandas官方文檔:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.json_normalize.html
原創不易,來個三連支持下。
獲取本站知識星球優惠券,複製連結直接打開:
https://t.zsxq.com/y7uvZF6
本站qq群704220115。
加入微信群請掃碼: