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)

內容解密:

  1. 建立 DataFrame:首先建立一個簡單的 DataFrame,包含姓名和出生年份。
  2. 資料型別轉換:將出生年份轉換為 UInt16Dtype(),以示範如何使用 pandas 的擴充套件型別。
  3. 序列化:使用 to_json() 方法將 DataFrame 序列化為 JSON Table Schema 格式。
  4. 反序列化:使用 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)

內容解密:

  1. pd.json_normalize():用於處理巢狀的 JSON 資料,將 records 中的資料提取出來並扁平化。
  2. record_path 引數:指定要處理的 JSON 路徑,在此例中為 "records"
  3. 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))  # 輸出抓取到的表格數量

內容解密:

  1. pd.read_html():用於從網頁中抓取表格資料,傳回一個包含多個 DataFrame 的列表。
  2. 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())

內容解密:

  1. attrs 引數:用於指定 HTML 表格的屬性,以過濾出符合條件的表格。
  2. 篩選目標表格:根據 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_pickleread_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 引數。