JSON 在資料科學領域已成為重要的資料交換格式,pandas 提供了便捷的 JSON 資料處理功能。orient 引數控制著 DataFrame 與 JSON 資料之間的轉換方式,不同的 orient 值會影響 JSON 的結構和資料的完整性。columns 適合需要保留索引和欄標籤的場景,records 則適用於簡單的資料交換,split 能完整重建 DataFrame,index 適用於索引資訊較重要的情況,values 只保留資料內容,table 遵循 JSON Table Schema 標準,適用於需要嚴格資料結構的應用。選擇合適的 orient 值能有效提升資料交換效率。此外,pd.json_normalize 可將複雜的巢狀 JSON 資料扁平化,方便資料分析。使用 JSON Table Schema 可保留資料型別等元資訊,確保資料在轉換過程中不丟失重要資訊。
pandas I/O 系統中的 JSON 資料交換:深入解析與應用
在資料科學和資料分析的領域中,JSON(JavaScript Object Notation)已成為一種廣泛使用的資料交換格式。pandas 函式庫提供了強大的 JSON 資料處理功能,使得 DataFrame 與 JSON 資料之間的轉換變得簡單高效。本文將探討 pandas 中 JSON 資料交換的各種方式,分析不同 orient 引數對資料儲存和還原的影響,並提供實務應用中的最佳實踐建議。
JSON 資料表示的多樣性
在實際應用中,JSON 資料可以用多種方式表示表格資料。有些使用者希望將 DataFrame 的每一行表示為 JSON 陣列,而其他使用者可能希望將每一欄表示為陣列。還有一些使用者希望將列索引、欄標籤和資料分別列出為不同的 JSON 物件。為了滿足這些不同的需求,pandas 提供了 orient 引數來控制 JSON 資料的佈局。
orient 引數的各種選項
pandas 中的 to_json 方法支援多種 orient 引數,用於控制輸出 JSON 資料的格式。以下將詳細介紹各個選項的特點和應用場景:
1. columns (預設值)
使用 orient="columns" 會產生 JSON 物件,其中鍵是欄標籤,值是另一個物件,該物件將列標籤對映到資料點。
serialized = df.to_json(orient="columns")
輸出範例:
{
"first": {"row 0": "Paul", "row 1": "John", "row 2": "Richard", "row 3": "George"},
"last": {"row 0": "McCartney", "row 1": "Lennon", "row 2": "Starkey", "row 3": "Harrison"},
"birth": {"row 0": 1942, "row 1": 1940, "row 2": 1940, "row 3": 1943}
}
這種格式相對冗長,因為它會為每個欄重複列索引標籤。然而,pandas 可以很好地從這種格式重建原始的 DataFrame。
2. records
使用 orient="records" 會將 DataFrame 的每一行表示為 JSON 物件的陣列。
serialized = df.to_json(orient="records")
輸出範例:
[
{"first": "Paul", "last": "McCartney", "birth": 1942},
{"first": "John", "last": "Lennon", "birth": 1940},
{"first": "Richard", "last": "Starkey", "birth": 1940},
{"first": "George", "last": "Harrison", "birth": 1943}
]
這種表示方式比 columns 更緊湊,但不儲存列標籤。在重建 DataFrame 時,會產生新的 RangeIndex。
3. split
使用 orient="split" 會將列索引標籤、欄標籤和資料分別儲存。
serialized = df.to_json(orient="split")
輸出範例:
{
"columns": ["first", "last", "birth"],
"index": ["row 0", "row 1", "row 2", "row 3"],
"data": [
["Paul", "McCartney", 1942],
["John", "Lennon", 1940],
["Richard", "Starkey", 1940],
["George", "Harrison", 1943]
]
}
這種格式使用相對較少的字元,並且可以很好地重建原始 DataFrame。它類別似於使用建構函式建立 DataFrame 的方式。
4. index
使用 orient="index" 與 columns 相似,但行和列標籤的角色相反。
serialized = df.to_json(orient="index")
輸出範例:
{
"row 0": {"first": "Paul", "last": "McCartney", "birth": 1942},
"row 1": {"first": "John", "last": "Lennon", "birth": 1940},
"row 2": {"first": "Richard", "last": "Starkey", "birth": 1940},
"row 3": {"first": "George", "last": "Harrison", "birth": 1943}
}
這種格式通常比 columns 更佔空間,除非欄標籤比索引標籤更簡潔。
5. values
使用 orient="values" 只儲存資料,不保留行或列標籤。
serialized = df.to_json(orient="values")
輸出範例:
[
["Paul", "McCartney", 1942],
["John", "Lennon", 1940],
["Richard", "Starkey", 1940],
["George", "Harrison", 1943]
]
這種表示方式最為簡潔,但重建 DataFrame 時會丟失原始的索引資訊。
6. table
使用 orient="table" 符合 JSON Table Schema 標準,是最冗長但最完整的表示方式。
serialized = df.to_json(orient="table")
輸出範例:
{
"schema": {
"fields": [
{"name": "index", "type": "string"},
{"name": "first", "type": "any", "extDtype": "string"},
{"name": "last", "type": "any", "extDtype": "string"},
{"name": "birth", "type": "integer"}
]
},
# ... 其他內容
}
這種格式雖然最冗長,但遵循標準規範,適合需要嚴格資料結構的應用場景。
不同 orient 的比較與選擇
orient |
資料完整性 | 空間效率 | 重建 DataFrame | 使用場景 |
|---|---|---|---|---|
columns |
高 | 低 | 好 | 需要保留索引和欄標籤 |
records |
中 | 中 | 一般(丟失索引) | 簡單資料交換 |
split |
高 | 中 | 好 | 需要完整重建 DataFrame |
index |
高 | 低 | 好 | 索引資訊更重要的情況 |
values |
低 | 高 | 差(丟失所有標籤) | 只關心資料內容 |
table |
最高 | 最低 | 好 | 需要遵循 JSON Table Schema |
使用 pandas 處理 JSON 及 HTML 資料
JSON 資料處理
JSON(JavaScript Object Notation)是一種輕量級的資料交換格式,廣泛用於 Web API 及資料儲存。pandas 提供了多種方法來處理 JSON 資料,包括 to_json() 和 read_json()。
JSON Table Schema
JSON Table Schema 是 pandas 用於儲存資料的一種格式,能夠保留資料的元資訊(metadata),如資料型別等。這使得在讀取資料時無需額外指定資料型別。
import pandas as pd
import io
# 建立 DataFrame 並轉換為 JSON Table Schema 格式
df = pd.DataFrame({
"first": ["John", "Jane"],
"last": ["Doe", "Smith"],
"birth": [1990, 1995]
})
df["birth"] = df["birth"].astype(pd.UInt16Dtype())
serialized = df.to_json(orient="table")
# 讀取 JSON Table Schema 格式的資料
read_df = pd.read_json(io.StringIO(serialized), orient="table")
print(read_df.dtypes)
內容解密:
- 建立 DataFrame:首先建立一個簡單的 DataFrame,包含姓名和出生年份。
- 資料型別轉換:將出生年份轉換為
UInt16Dtype(),以示範如何使用 pandas 的擴充套件型別。 - 序列化:使用
to_json()方法將 DataFrame 序列化為 JSON Table Schema 格式。 - 反序列化:使用
read_json()方法讀取 JSON 資料,並指定orient="table"以保留資料型別資訊。
處理複雜 JSON 結構
對於複雜的 JSON 結構,pd.json_normalize() 是一個非常有用的工具,能夠將巢狀的 JSON 資料轉換為扁平的表格格式。
data = {
"records": [
{"name": "human", "characteristics": {"num_leg": 2, "num_eyes": 2}},
{"name": "dog", "characteristics": {"num_leg": 4, "num_eyes": 2}},
{"name": "horseshoe crab", "characteristics": {"num_leg": 10, "num_eyes": 10}}
],
"type": "animal",
"pagination": {"next": "23978sdlkusdf97234u2io", "has_more": 1}
}
# 使用 pd.json_normalize() 處理巢狀 JSON 資料
normalized_df = pd.json_normalize(data, record_path="records", meta="type")
print(normalized_df)
內容解密:
pd.json_normalize():用於處理巢狀的 JSON 資料,將records中的資料提取出來並扁平化。record_path引數:指定要處理的 JSON 路徑,在此例中為"records"。meta引數:用於保留額外的元資訊,在此例中保留了"type"欄位。
HTML 資料抓取
pandas 的 read_html() 方法可以用來抓取網頁中的表格資料。以下範例展示如何從維基百科頁面抓取 The Beatles Discography 中的表格。
import pandas as pd
url = "https://en.wikipedia.org/wiki/The_Beatles_discography"
dfs = pd.read_html(url, dtype_backend="numpy_nullable")
print(len(dfs)) # 輸出抓取到的表格數量
內容解密:
pd.read_html():用於從網頁中抓取表格資料,傳回一個包含多個 DataFrame 的列表。dtype_backend="numpy_nullable":指定資料型別的後端,以支援 pandas 的擴充套件型別。
篩選所需的表格
由於 read_html() 傳回多個表格,我們需要根據具體情況篩選出所需的表格。可以使用 attrs 引數來指定 HTML 屬性,以精確定位目標表格。
# 使用 attrs 引數篩選表格
dfs = pd.read_html(url, attrs={"class": "wikitable plainrowheaders"})
target_df = dfs[0] # 假設第一個匹配的表格是目標表格
print(target_df.head())
內容解密:
attrs引數:用於指定 HTML 表格的屬性,以過濾出符合條件的表格。- 篩選目標表格:根據 HTML 特性(如 class 或 id)來定位所需的表格。
pandas 的輸入輸出系統
在資料分析的過程中,資料的讀取與儲存是非常重要的一環。pandas 提供了多種方法來處理不同格式的資料,包括 HTML、Pickle 等。本章節將探討如何使用 pandas 來讀取和寫入這些資料格式。
從 HTML 中讀取表格
HTML 是一種常見的資料格式,特別是在網路爬蟲中。pandas 提供了 read_html 方法,可以方便地從 HTML 中提取表格資料。
使用 match 引數篩選表格
在某些情況下,HTML 檔案中可能包含多個表格。我們可以使用 match 引數來篩選特定的表格。例如,從維基百科上的披頭士樂隊唱片目錄頁面中提取錄音室專輯列表:
url = "https://en.wikipedia.org/wiki/The_Beatles_discography"
dfs = pd.read_html(
url,
match=r"List of studio albums",
dtype_backend="numpy_nullable",
)
print(f"Number of tables returned was: {len(dfs)}")
dfs[0].filter(regex=r"Title|UK|AUS|CAN").head()
處理多層級欄位名稱
有時,從 HTML 表格中讀取的資料可能會包含多層級的欄位名稱。我們可以透過設定 header 引數來調整欄位名稱的處理方式。例如:
url = "https://en.wikipedia.org/wiki/The_Beatles_discography"
dfs = pd.read_html(
url,
match="List of studio albums",
header=1,
dtype_backend="numpy_nullable",
)
dfs[0].filter(regex=r"Title|UK|AUS|CAN").head()
#### 內容解密:
此段程式碼的作用是從指定的 URL 中讀取 HTML 表格,並篩選出包含「List of studio albums」文字的表格。透過設定 header=1,我們可以忽略第一層的欄位名稱,直接使用第二層的欄位名稱。這樣可以使表格更容易閱讀。
使用 na_values 處理缺失值
在資料分析中,缺失值的處理是非常重要的。pandas 允許我們透過 na_values 引數來指定哪些值應該被視為缺失值。例如,在上述的維基百科表格中,「—」符號代表缺失值:
url = "https://en.wikipedia.org/wiki/The_Beatles_discography"
dfs = pd.read_html(
url,
match="List of studio albums",
header=1,
na_values=["—"],
dtype_backend="numpy_nullable",
)
dfs[0].filter(regex=r"Title|UK|AUS|CAN").head()
#### 內容解密:
此段程式碼的作用是將「—」符號視為缺失值,並將其轉換為 <NA>。這樣可以使資料更加乾淨,便於後續的分析。
Pickle 格式的讀寫
Pickle 是 Python 的內建序列化格式,可以用來儲存和讀取 Python 物件。pandas 提供了 to_pickle 和 read_pickle 方法來處理 Pickle 格式的資料。
使用 Pickle 儲存和讀取資料
以下是一個使用 Pickle 儲存和讀取 pd.Series 物件的例子:
from collections import namedtuple
import io
Member = namedtuple("Member", ["first", "last", "birth"])
ser = pd.Series([
Member("Paul", "McCartney", 1942),
Member("John", "Lennon", 1940),
Member("Richard", "Starkey", 1940),
Member("George", "Harrison", 1943),
])
buf = io.BytesIO()
ser.to_pickle(buf)
buf.seek(0)
ser = pd.read_pickle(buf)
ser
#### 內容解密:
此段程式碼的作用是將一個包含 namedtuple 物件的 pd.Series 儲存到 Pickle 格式,並再次讀取出來。由於 Pickle 格式可以儲存 Python 物件,因此它非常適合用於儲存包含複雜 Python 物件的 pandas 資料結構。
第三方輸入輸出函式庫
雖然 pandas 支援多種資料格式,但仍有一些格式需要藉助第三方函式庫來處理。以下是一些常見的第三方函式庫:
- pandas-gbq:用於與 Google BigQuery 交換資料
- AWS SDK for pandas:用於與 Redshift 和 AWS 生態系統交換資料
- Snowflake Connector for Python:用於與 Snowflake 資料函式庫交換資料
- pantab:用於將 pandas 資料結構轉換為 Tableau 的 Hyper 資料函式庫格式
這些函式庫通常遵循相同的模式,即提供讀取函式傳回 pd.DataFrame 物件,並提供寫入方法接受 pd.DataFrame 引數。