Pandas 作為 Python 資料處理的利器,搭配 SQLite 輕量級資料函式庫,能有效處理小型應用和原型設計。透過 SQLAlchemy 或 ADBC,我們能更簡便地操作資料在 Pandas DataFrame 與 SQLite 之間的轉換。SQLAlchemy 提供物件關聯對映,讓資料函式庫操作更 Pythonic,而 ADBC 則以其高效能著稱,尤其在大型資料集的處理上更具優勢。選擇哪種方式取決於專案規模和效能需求。
SQLite 與 Pandas 的資料函式庫操作
SQLite 是一種輕量級的資料函式倉管理系統,適合用於快速原型設計和小型應用。它不需要複雜的設定,並且可以完全在記憶體中運作。結合 Pandas 和 SQLAlchemy 或 ADBC(Arrow Database Connectivity),我們可以輕鬆地將資料框 (DataFrame) 寫入和讀取 SQLite 資料函式庫。
環境設定
首先,我們需要安裝 SQLAlchemy 和 ADBC 套件。這些套件可以透過 pip 安裝:
pip install sqlalchemy adbc-driver-sqlite
使用 SQLAlchemy 操作 SQLite
建立 SQLAlchemy 引擎
我們可以使用 sa.create_engine 建立一個 SQLite 引擎,並將其連線到記憶體中的資料函式庫:
import sqlalchemy as sa
engine = sa.create_engine("sqlite:///:memory:")
將 Pandas DataFrame 寫入 SQLite 資料函式庫
接下來,我們可以使用 pd.DataFrame.to_sql 方法將 Pandas DataFrame 寫入 SQLite 資料函式庫:
import pandas as pd
df = pd.DataFrame([
["dog", 4],
["cat", 4],
], columns=["animal", "num_legs"])
df = df.convert_dtypes(dtype_backend="numpy_nullable")
df.to_sql("table_name", engine, index=False)
從 SQLite 資料函式庫讀取資料
我們可以使用 pd.read_sql 方法從 SQLite 資料函式庫中讀取資料:
result = pd.read_sql("table_name", engine, dtype_backend="numpy_nullable")
print(result)
此圖示
內容解密:
- 建立 DataFrame:我們首先建立了一個包含動物名稱和腿數的簡單 DataFrame。
- 轉換資料型別:使用
convert_dtypes方法將 DataFrame 的資料型別轉換為numpy_nullable,這樣可以更好地處理空值。 - 使用 to_sql 寫入 SQLite:將 DataFrame 寫入 SQLite 資料函式庫,指定表名為
"table_name"。 - 使用 read_sql 讀取資料:從 SQLite 資料函式庫中讀取資料,並列印預出來。
替換或附加資料
如果表已存在,我們可以使用 if_exists 引數來替換或附加資料:
new_data = pd.DataFrame([["centipede", 100]], columns=["animal", "num_legs"])
new_data.to_sql("table_name", engine, index=False, if_exists="append")
result = pd.read_sql("table_name", engine, dtype_backend="numpy_nullable")
print(result)
此圖示
內容解密:
- 檢查表是否存在:在寫入資料之前,先檢查表是否已存在。
- 表已存在:如果表已存在,則使用
if_exists="append"引數將新資料附加到現有表中。 - 直接寫入新資料:如果表不存在,則直接寫入新資料。
使用 ADBC 操作 SQLite
ADBC 是 Apache Arrow 專案的一部分,提供了更高效的 SQL 資料函式庫互動方式。它支援更好的效能和型別安全性。以下是如何使用 ADBC 與 Pandas 一起操作 SQLite 的示例:
安裝 ADBC 驅動程式
首先,我們需要安裝 ADBC 的 SQLite 驅動程式:
pip install adbc-driver-sqlite
建立 ADBC 連線
接下來,我們可以使用 ADBC 建立一個 SQLite 連線:
from adbc_driver_sqlite import dbapi
with dbapi.connect("file::memory:") as conn:
df.to_sql("table_name", conn, index=False, if_exists="replace")
result = pd.read_sql("SELECT * FROM table_name", conn, dtype_backend="numpy_nullable")
print(result)
此圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Pandas 與 SQLite 資料函式庫高效互動操作
package "資料庫架構" {
package "應用層" {
component [連線池] as pool
component [ORM 框架] as orm
}
package "資料庫引擎" {
component [查詢解析器] as parser
component [優化器] as optimizer
component [執行引擎] as executor
}
package "儲存層" {
database [主資料庫] as master
database [讀取副本] as replica
database [快取層] as cache
}
}
pool --> orm : 管理連線
orm --> parser : SQL 查詢
parser --> optimizer : 解析樹
optimizer --> executor : 執行計畫
executor --> master : 寫入操作
executor --> replica : 讀取操作
cache --> executor : 快取命中
master --> replica : 資料同步
note right of cache
Redis/Memcached
減少資料庫負載
end note
@enduml
內容解密:
- 建立 ADBC 連線:使用
dbapi.connect建立一個記憶體中的 SQLite 資料函式庫連線。 - 使用 with … as 語法:這樣可以自動管理連線的開啟和關閉。
- 將 DataFrame 寫入 SQLite:將 DataFrame 寫入 SQLite 資料函式庫。
- 從 SQLite 読取資料:從 SQLite 資料函式庫中讀取資料。
效能比較
ADBC 在處理大型資料集時會顯著提升效能。以下是一個效能比較示例:
import timeit
# 準備測試資料
np.random.seed(42)
df = pd.DataFrame(np.random.randn(10_000, 10), columns=list("abcdefghij"))
# 測試 SQLAlchemy 效能
with sa.create_engine("sqlite:///:memory:").connect() as conn:
func = lambda: df.to_sql("test_table", conn, if_exists="replace")
print(timeit.timeit(func, number=100))
# 測試 ADBC 效能
with dbapi.connect("file::memory:") as conn:
func = lambda: df.to_sql("test_table", conn, if_exists="replace")
print(timeit.timeit(func, number=100))
內容解密:
- 準備測試資料:生成一個包含 10,000 行和 10 個欄位的隨機資料集。
- 測試 SQLAlchemy 效能:測試將 DataFrame 寫入 SQLite 資料函式庫所需的時間。
- 測試 ADBC 效能:測試使用 ADBC 快速向大型 DataFrame 周轉儲存。
未來趨勢與改進點
ADBC 的出現為 SQL 資料函式庫互動提供了更高效的選擇。未來,隨著更多驅動程式的支援和效能最佳化,ADBC 有望成為主流的 SQL 資料互動方式。玄貓認為在處理大型資料集時,ADBC 的效能優勢會更加明顯。
最佳化與反思
在實際應用中,選擇合適的資料函式庫驅動程式和工具對於提升效能和可維護性至關重要。玄貓建議根據具體需求選擇最合適的工具,並不斷最佳化程式碼以提升效率。此外,深入理解資料函式庫的內部工作原理和伺服器組態也能幫助我們更好地進行最佳化。
停止錯誤教訓與分析
在實際應用中,玄貓曾經遇到過因為不當設定造成的記憶體溢位問題。這提醒我們在處理大型資料集時,必須特別注意記憶體管理和資源最佳化。此外,合理選擇資料儲存方式(如記憶體中儲存 vs. 離線儲存)也是至關重要的。
透過以上分析與經驗分享,希望讀者能夠更好地理解如何利用 SQLAlchemy 和 ADBC 高效地進行 Pandas 與 SQLite 的互動操作。
從 ADBC 到 Apache Parquet:現代資料函式庫標準與高效資料儲存
在探討 ADBC(Apache DataBase Connectivity)及其對現代資料函式庫應用的影響之前,我們先來回顧一下資料函式庫標準的演變歷史。這樣的歷史背景能夠幫助我們理解 ADBC 的重要性及其在當今資料處理中的應用。
資料函式庫標準的演變
在 1990 年代,ODBC(Open Database Connectivity)和 JDBC(Java Database Connectivity)標準的引入,標誌著資料函式庫連線方式的重要進步。這些標準使得不同的客戶端能夠以統一的方式與各種資料函式庫進行通訊,從而避免了之前每個資料函式庫都需要特定語言進行互動的問題。
假設有一個應用程式需要與多個不同的資料函式庫進行互動,例如 PostgreSQL 和 SQLite。在沒有 ODBC 或 JDBC 的情況下,這個應用程式需要了解每個資料函式庫的特定語言和結構來取得資料,例如表格名稱和儲存方式。這種方法不僅繁瑣,還難以維護,因為每當資料函式庫結構發生變化時,應用程式都需要重新釋出。
ODBC 和 JDBC 的引入改變了這一局面。應用程式只需與驅動程式通訊,告知驅動程式需要什麼資料,驅動程式則負責與具體資料函式庫進行互動。這樣的設計將互動的複雜性從應用程式轉移到驅動程式上,為應用程式提供了一層抽象。
然而,隨著時間的推移,行列式資料函式庫(row-oriented databases)逐漸被列式資料函式庫(column-oriented databases)所取代。列式資料函式庫在分析領域表現出色,但由於缺乏專門的列式資料傳輸標準,許多列式資料函式庫不得不進行反向相容設計以支援 ODBC 和 JDBC。這種設計雖然能夠與現有工具相容,但會犧牲一定的效能和效率。
ADBC 的誕生
為瞭解決這一問題,ADBC 作為一種列式資料傳輸標準應運而生。ADBC 專門針對列式資料函式庫設計,能夠顯著提升資料傳輸效率。例如,pandas 函式庫及其他類別似工具在設計上也是列式的。當與 BigQuery、Redshift 或 Snowflake 等列式資料函式庫互動時,ADBC 驅動可以帶來顯著的效能提升。
即使在不使用列式資料函式庫的情況下,ADBC 驅動仍然可以透過 Apache Arrow 的最佳化技術提供更好的分析效能,相比於 SQLAlchemy 使用的 ODBC/JDBC 驅動來說。
Apache Parquet:現代資料儲存格式
在現代資料處理中,Apache Parquet 是一種非常優秀的通用儲存格式。它具有以下特點:
- 元資料儲存:能夠追蹤資料型別等資訊。
- 分割槽:支援將資料分割槽儲存,而非全部放在一個檔案中。
- 查詢支援:Parquet 檔案可以在磁碟上進行查詢,無需將所有資料載入記憶體。
- 平行化:支援平行讀取資料以提高吞吐量。
- 緊湊性:資料經過壓縮後儲存得非常高效。
對於那些不是在處理遺留系統的人來說,Apache Parquet 格式應該取代 CSV 檔案在工作流程中的使用。無論是本地持久化資料、團隊成員間分享還是跨系統交換資料,Apache Parquet 都能提供更好的效能和靈活性。
使用 Apache Parquet 的範例
以下是如何使用 pandas 函式庫讀寫 Apache Parquet 格式的範例:
import io
import pandas as pd
# 建立一些範例資料
buf = io.BytesIO()
df = pd.DataFrame([
["Paul", "McCartney", 1942],
["John", "Lennon", 1940],
["Richard", "Starkey", 1940],
["George", "Harrison", 1943],
], columns=["first", "last", "birth"])
df = df.convert_dtypes(dtype_backend="numpy_nullable")
df
寫入檔案
df.to_parquet(buf, index=False)
讀取檔案
buf.seek(0)
pd.read_parquet(buf)
這段程式碼展示瞭如何將 DataFrame 資料寫入和讀取 Apache Parquet 格式。因為 Parquet 格式包含了元資料(例如資料型別),所以在讀取時不需要額外指定 dtype_backend。
df["birth"] = df["birth"].astype(pd.UInt16Dtype())
df.dtypes
寫入和讀取帶有指定資料型別
buf = io.BytesIO()
df.to_parquet(buf, index=False)
buf.seek(0)
pd.read_parquet(buf).dtypes
這樣可以確保資料型別在寫入和讀取過程中保持一致。如果需要更嚴格地控制資料型別,可以使用 dtype_backend="numpy_nullable" 在讀取時也確保最佳型別支援。
主題標題:Pandas I/O 系統與資料格式處理
段落標題:Apache Parquet 格式的優勢
Pandas 是 Python 中一個強大的資料處理函式庫,特別是其 I/O 系統,讓我們能夠方便地進行資料的讀取和寫入。其中,Apache Parquet 格式因其高效的壓縮和快速的查詢能力,成為許多資料科學家和工程師的首選。Parquet 格式不僅支援分割槽(partitioning),還能有效地組織和最佳化資料存取。這些特性使得在處理大量資料時,能夠顯著提升效率。
次段落標題:分割槽功能
分割槽功能允許我們將資料分散在不同的目錄和檔案中,這樣可以更靈活地組織資料,並且在進行查詢時,只需要讀取相關的部分,而不必載入整個資料集。例如,我們可以根據時間來分割槽,每個時間段的資料存放在不同的目錄中。以下是一個示例資料結構:
Partitions
2022/
q1_sales.parquet
q2_sales.parquet
2023/
q1_sales.parquet
q2_sales.parquet
每個 Parquet 檔案中包含了年份、季度、區域以及銷售資料等資訊。當我們想要檢視所有資料時,可以直接使用 Pandas 的 pd.read_parquet 函式來讀取整個目錄下的所有檔案:
pd.read_parquet("data/partitions/")
year quarter region sales
0 2022 Q1 America 1
1 2022 Q1 Europe 2
2 2022 Q2 America 4
3 2022 Q2 Europe 8
4 2023 Q1 America 16
5 2023 Q1 Europe 32
6 2023 Q2 America 64
7 2023 Q2 Europe 128
次段落標題:過濾功能
在處理大量資料時,直接將所有資料載入記憶體可能會導致 MemoryError。為瞭解決這個問題,Parquet 支援即時過濾記錄的功能。我們可以在讀取資料時指定過濾條件,只載入符合條件的部分。例如,如果我們只想檢視歐洲區域的銷售資料,可以這樣做:
pd.read_parquet(
"data/partitions/",
filters=[("region", "==", "Europe")]
)
year quarter region sales
0 2022 Q1 Europe 2
1 2022 Q2 Europe 8
2 2023 Q1 Europe 32
3 2023 Q2 Europe 128
段落標題:JSON 資料格式
除了 Parquet 外,JSON 是另一種常見的資料格式。它廣泛應用於網路資料傳輸中。Python 的標準函式庫提供了 json 模組來進行 JSON 資料的序列化和反序列化操作。
次段落標題:JSON 基本操作
以下是一個簡單的 JSON 操作範例:
import json
beatles = {
"first": ["Paul", "John", "Richard", "George"],
"last": ["McCartney", "Lennon", "Starkey", "Harrison"],
"birth": [1942, 1940, 1940, 1943],
}
serialized = json.dumps(beatles)
print(f"serialized values are: {serialized}")
deserialized = json.loads(serialized)
print(f"deserialized values are: {deserialized}")
serialized values are: {"first": ["Paul", "John", "Richard", "George"], "last": ["McCartney", "Lennon", "Starkey", "Harrison"], "birth": [1942, 1940, 1940, 1943]}
deserialized values are: {'first': ['Paul', 'John', 'Richard', 'George'], 'last': ['McCartney', 'Lennon', 'Starkey', 'Harrison'], 'birth': [1942, 1940, 1940, 1943]}
儘管 Python 標準函式庫能夠處理 JSON 資料,但對於 Pandas 物件來說,Pandas 提供了專門的 I/O 函式來進行 JSON 資料的讀寫操作。
import io
import pandas as pd
data = io.StringIO(serialized)
df = pd.read_json(data, dtype_backend="numpy_nullable")
print(df)
first last birth
0 Paul McCartney 1942
1 John Lennon 1940
2 Richard Starkey 1940
3 George Harrison 1943
次段落標題:Pandas 的 JSON 支援
Pandas 提供了 pd.DataFrame.to_json 和 pd.read_json 函式來方便地進行 JSON 資料的讀寫操作。以下是一個範例:
df = pd.DataFrame(beatles)
print(df.to_json())
{"first":{"0":"Paul","1":"John","2":"Richard","3":"George"},"last":{"0":"McCartney","1":"Lennon","2":"Starkey","3":"Harrison"},"birth":{"0":1942,"1":-1940,"-<id":-<}...}}