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 引數。