DuckDB 是一款高效能的嵌入式分析型資料函式庫,尤其擅長處理 Parquet 等格式的大型資料集。本文以紐約計程車資料集為例,示範如何利用 DuckDB 進行資料探索和分析。首先,我們透過建立檢視 allRidesView 來載入儲存在 S3 的 Parquet 檔案,並使用 SUMMARIZE 函式快速概覽資料的統計資訊,例如最小值、最大值和資料型別。接著,我們示範如何過濾異常資料,例如 trip_distance 為負值的資料,並重新定義檢視以提升分析準確性。為了進一步提升查詢效能,我們展示瞭如何針對特定欄位進行分析,例如只查詢 trip_distance 的統計資訊,以及如何利用 Parquet 的元資料特性加速聚合運算,例如計算行程距離的平均值和標準差。此外,文章也介紹了 DuckDB 的進階 SQL 功能,例如使用 Star Expressions 和正規表示式對多個欄位進行聚合分析,以及如何使用 SUMMARIZE 作為子查詢。最後,文章討論了 DuckDB 在處理大型資料集時的效能考量,並提供了一些效能最佳化的建議。
探索紐約計程車資料集:來自Parquet檔案的洞察
為了保持從S3讀取的資料量合理,我們僅包含了2020年及之後的檔案,這仍然包含了1.18億行資料。當然,如果您希望查詢所有資料,可以擴充套件萬用字元以包含更多檔案。
這個查詢應該立即傳回,因為它並沒有讀取任何資料——它只是定義了一個檢視。這個檢視涵蓋了大約1.18億行資料,因此它不是一個小資料集,但也不是巨大的。
分析資料
為了獲得資料值及其分佈的概覽,並測試DuckDB在從S3檢索資料時的讀取效能,我們將使用以下查詢,再次使用SUMMARIZE。
.timer on
SUMMARIZE allRidesView;
這個查詢需要讀取實際資料以取得所有統計資訊,因此需要一些時間才能傳回結果。即使在靠近資料的AWS EC2例項上執行查詢,也需要超過30秒才能從1.18億行資料中產生結果:
┌──────────────────────┬─────────────┬────────────┬────────────┬────────────┐
│ column_name │ column_type │ min │ max │approx_uniq │
│ varchar │ varchar │ varchar │ varchar │ varchar │
├──────────────────────┼─────────────┼────────────┼────────────┼────────────┤
│ VendorID │ BIGINT │ 1 │ 6 │ 4 │
│ tpep_pickup_datetime │ TIMESTAMP │ 2001-01-01 │ 2098-09-11 │ 59777057 │
│ tpep_dropoff_datetime│ TIMESTAMP │ 2001-01-01 │ 2098-09-11 │ 60115892 │
│ passenger_count │ DOUBLE │ 0.0 │ 112.0 │ 12 │
│ trip_distance │ DOUBLE │ -30.62 │ 389678.46 │ 14254 │
│ RatecodeID │ DOUBLE │ 1.0 │ 99.0 │ 7 │
│ store_and_fwd_flag │ VARCHAR │ N │ Y │ 2 │
│ PULocationID │ BIGINT │ 1 │ 265 │ 264 │
│ DOLocationID │ BIGINT │ 1 │ 265 │ 265 │
│ payment_type │ BIGINT │ 0 │ 5 │ 6 │
│ fare_amount │ DOUBLE │ -133391414 │ 998310.03 │ 17618 │
│ extra │ DOUBLE │ -27.0 │ 500000.8 │ 671 │
│ mta_tax │ DOUBLE │ -0.55 │ 500000.5 │ 82 │
│ tip_amount │ DOUBLE │ -493.22 │ 133391363 │ 9064 │
│ tolls_amount │ DOUBLE │ -99.99 │ 956.55 │ 3897 │
│ improvement_surcharge│ DOUBLE │ -1.0 │ 1.0 │ 5 │
│ total_amount │ DOUBLE │ -2567.8 │ 1000003.8 │ 36092 │
│ congestion_surcharge │ DOUBLE │ -2.5 │ 3.0 │ 16 │
│ airport_fee │ INTEGER │ -2 │ 2 │ 5 │
├──────────────────────┴─────────────┴────────────┴────────────┴────────────┤
│ 19 rows │
└───────────────────────────────────────────────────────────────────────────┘
Run Time (s): real 33.907 user 586.582679 sys 8.706259
程式碼解密:
此查詢使用SUMMARIZE命令對allRidesView檢視進行統計分析,包括欄位名稱、資料型別、最小值、最大值和近似唯一值等。由於需要讀取實際資料,因此查詢耗時較長。
資料過濾與再分析
從結果中可以看到,有些行程的距離為負值,因此在進行進一步分析之前過濾掉這些異常值是有意義的。
CREATE OR REPLACE VIEW allRidesView AS
SELECT *
FROM 's3://us-prd-md-duckdb-in-action/nyc-taxis/yellow_tripdata_202*.parquet'
WHERE trip_distance > 0;
程式碼解密:
此查詢重新定義了allRidesView檢視,排除了行程距離小於或等於零的資料,從而避免了異常值對後續分析的影響。
對單一欄位進行匯總分析
如果我們只讀取trip_distance這一欄位,查詢速度會大大提高,只需要約3到4秒。
.timer on
.mode line
SUMMARIZE (SELECT trip_distance FROM allRidesView);
結果如下:
column_name = trip_distance
column_type = DOUBLE
min = 0.01
max = 389678.46
approx_unique = 13114
avg = 5.430654112761637
std = 536.0306563795983
q25 = 1.093394143132663
q50 = 1.8271836758492157
q75 = 3.389607008136687
count = 115976028
null_percentage = 0.0%
Run Time (s): real 3.033 user 49.924611 sys 1.228218
程式碼解密:
此查詢對allRidesView檢視中的trip_distance欄位進行匯總分析,提供了包括最小值、最大值、平均值、標準差等在內的多項統計指標。由於只讀取了一個欄位,因此查詢速度明顯加快。
使用SUMMARIZE進行靈活查詢
從DuckDB版本0.10.0開始,SUMMARIZE可以用作SELECT陳述式的來源,因此您可以根據需要選擇特定的欄位進行查詢。
SELECT column_name, column_type, count, max FROM SUMMARIZE allRidesView;
程式碼解密:
此查詢展示瞭如何使用SUMMARIZE作為子查詢,選取特定的欄位進行進一步分析。
高效能的聚合查詢
如果我們只執行某些可作為Parquet中元資料的聚合操作,可以獲得更快的結果,耗時不到一秒。
SELECT
min(trip_distance),
max(trip_distance),
avg(trip_distance),
stddev(trip_distance),
count(trip_distance) AS nonNull,
count(*) as total,
1-(nonNull/total) AS nullPercentage
FROM allRidesView;
程式碼解密:
此查詢對trip_distance欄位進行多種聚合運算,包括最小值、最大值、平均值、標準差等。由於這些運算可以直接利用Parquet的元資料,因此查詢速度非常快。
對多個欄位進行聚合分析
DuckDB的高階SQL功能允許我們對多個欄位進行聚合運算,如下所示:
.mode line
SELECT
count(*),
min(columns(*)),
max(columns(*)),
round(avg(columns('_(distance|amount|tax|surcharge|fee)')),2),
round(stddev(columns(c -> c SIMILAR TO '.+(distance|amount|tax|surcharge|fee)')),2)
FROM allRidesView;
程式碼解密:
此查詢展示瞭如何使用DuckDB的Star Expressions功能對多個欄位進行聚合分析。透過正規表示式篩選出包含特定關鍵字的欄位,並計算它們的平均值和標準差。這種方法大大簡化了對多個相關欄位進行統計分析的過程。
大型資料集的效能考量:以紐約計程車資料集為例
在處理大型資料集時,查詢效能是至關重要的。本章節將探討如何使用DuckDB最佳化查詢效能,並以紐約計程車資料集作為範例進行詳細分析。
資料集概述與初步探索
首先,我們使用SUMMARIZE子句對資料集進行初步探索,以瞭解資料的基本統計資訊。透過對allRidesView檢視執行SUMMARIZE,我們能夠快速獲得各欄位的最小值、最大值、平均值和標準差等重要統計資料。
內容解密:
- 使用
SUMMARIZE子句能夠快速概覽資料集的整體狀況。 - 同時對所有欄位進行聚合運算,大幅提升處理效率。
SUMMARIZE allRidesView;
執行結果顯示,聚合運算被一次性應用於所有欄位,處理時間僅需約7秒,展現出極高的效率。
深入分析:各年度計程車行駛資料
接下來,我們針對2020年至2024年間的計程車資料進行深入分析,計算每年的平均行駛距離、車費金額以及每英里的車費費率。
程式碼範例
SELECT
year(tpep_pickup_datetime) AS year,
round(avg(trip_distance)) AS dist,
round(avg(fare_amount), 2) AS fare,
round(avg(fare_amount / trip_distance), 2) AS rate,
count(*) AS trips
FROM
allRidesView
GROUP BY
year
HAVING
year BETWEEN 2020 AND 2024
ORDER BY
year;
內容解密:
- 透過
year(tpep_pickup_datetime)提取年份資訊,並依此進行分組統計。 - 使用
avg函式計算平均行駛距離和平均車費金額。 HAVING子句用於篩選特定年份範圍內的資料。- 結果按年份排序,便於觀察趨勢變化。
查詢結果顯示,平均車費金額隨時間逐漸上漲,這可能與通貨膨脹有關。同時,我們觀察到2020年的資料出現明顯下降,這很可能是受到COVID-19疫情的影響。
乘客數量分佈分析
進一步地,我們分析了乘客數量少於10人且行駛距離超過10英里的旅程資料,以瞭解乘客數量的分佈情況。
程式碼範例
SELECT
passenger_count,
count(*)
FROM
allRidesView
WHERE
passenger_count < 10
GROUP BY
passenger_count
ORDER BY
count(*) DESC;
內容解密:
- 使用
WHERE子句篩選乘客數量少於10人的旅程。 - 按乘客數量進行分組,並統計每組的旅程次數。
- 結果按旅程次數降序排列,以突出主要的分佈特徵。
結果表明,大多數旅程僅有一名乘客,呈現出冪律分佈的特徵。
本文學習重點
本文首先介紹瞭如何安裝和使用DuckDB,包括其CLI和Python API。接著,我們展示瞭如何從CSV、JSON和Parquet檔案載入資料,並使用SQL進行分析,而無需建立資料表。同時,我們也探討瞭如何透過Python API使用SQL和fluent查詢,以及與pandas DataFrame的緊密整合。
在SQL的使用上,我們從基礎到進階功能,如視窗函式和CTE,進行了詳細的說明。DuckDB對標準SQL的擴充套件,如支援JSON、巢狀資料結構、進階JOIN和靈活的選擇、分組和聚合功能,也得到了深入的探討。
此外,我們還展示瞭如何將DuckDB整合到資料架構中,構建資料管道,並使用Streamlit和Apache Superset進行資料視覺化。最後,我們討論了處理大型資料集時的效能考量。
未涵蓋的內容
儘管本文對DuckDB進行了全面的介紹,但仍有一些領域未被探討。首先,DuckDB的內部實作和工作原理未被詳細說明。讀者可以透過研究DuckDB的原始碼、部落格文章、影片和檔案來進一步瞭解其架構、查詢執行引擎、索引功能、儲存層和向量化執行模型。
此外,DuckDB還支援多種語言的API,如C、R、Rust、Go和JavaScript等,讀者可以根據自己的需求選擇合適的環境進行開發。效能最佳化也是未來可以進一步探索的領域,儘管DuckDB在預設情況下表現良好,但仍有許多方法可以最佳化查詢和資料。
未來發展
隨著DuckDB 1.0版本的即將發布,我們可以期待更多的穩定性和相容性。未來的發展將繼續圍繞著擴充套件其功能和最佳化效能展開,包括開發新的擴充套件,如空間索引等,以滿足更多樣化的應用需求。
內容解密:
本文總結了本文的主要內容,並對未來的發展方向進行了展望。DuckDB作為一個強大的資料分析工具,不僅能夠處理大型資料集,還能與多種工具和語言整合,為資料工程師提供了極大的便利。未來,我們可以期待DuckDB在效能最佳化和功能擴充套件方面的進一步發展。
DuckDB的未來與擴充套件
隨著資料工程領域的不斷發展,DuckDB作為一個高效、靈活的資料分析工具,將繼續在多個方面進行擴充套件和最佳化。
效能最佳化
DuckDB在預設情況下已經具備出色的效能,但仍有許多方法可以進一步最佳化查詢和資料處理流程。例如,透過合理設計索引、使用適當的儲存格式等,可以顯著提升查詢效能。
擴充套件開發
DuckDB支援使用C++和Rust開發擴充套件,這為開發者提供了豐富的功能擴充可能性。例如,空間索引擴充套件可以為地理空間資料分析提供強大的支援。
多語言支援
除了Python API外,DuckDB還支援多種語言的API,如C、R、Rust、Go和JavaScript等。這使得開發者可以在自己熟悉的環境中使用DuckDB,進一步提升開發效率。
內容解密:
本文對DuckDB的未來發展進行了展望,包括效能最佳化、擴充套件開發和多語言支援等方面。這些發展將進一步鞏固DuckDB在資料分析領域的地位,為使用者提供更強大的功能和更靈活的使用方式。
SELECT * FROM 'https://example.com/data.parquet'
WHERE column_name = 'value';
內容解密:
此SQL查詢陳述式示範瞭如何直接從遠端Parquet檔案中查詢資料。透過指設定檔案URL和查詢條件,可以高效地檢索所需資料,而無需將整個檔案下載到本地。這種能力對於處理大型資料集尤其有用,因為它減少了不必要的網路傳輸,從而提高了查詢效能。
資料工程的未來與 DuckDB
我們認為 DuckDB 是一種非常有前途的技術,將在未來的資料工程中發揮重要作用。它不僅可以用於本地的中型資料集分析,也能夠在雲端進行大規模的資料處理,特別是在資料儲存位置附近進行處理,能夠提高效率並降低成本。
隨著本地資料的增長,例如來自健康監測、家庭自動化和個人資訊系統的資料,高效處理私人資料的需求將日益增加。由於 DuckDB 與 SQLite 的相似性,我們預計它將被廣泛應用於目前使用 SQLite 的領域,如應用程式、遊戲、瀏覽器、手機、物聯網和邊緣裝置,用於分析、資料聚合、預過濾和向其他系統及終端使用者提供資料。
在商業應用中,DuckDB 可以作為雲端資料倉儲(如 BigQuery、Redshift、Snowflake 等)的補充方案。許多使用這些系統的使用者並不需要處理 PB 級的資料,而是處理 GB 或 TB 級的資料,這些資料可以使用 DuckDB 以更低的成本、更少的資源和更簡單的操作來處理。
未來值得關注的創新領域包括:
- 在生成式 AI 應用中的整合,如高效的嵌入向量儲存和索引,以及支援串流資料處理。
- DuckDB 團隊在提升 SQL 和資料處理的易用性和靈活性方面的工作,預計將帶來更多可組合性,使系統的各個方面能夠被靈活查詢和組合。
現在,是時候讓您參與進來,塑造 DuckDB 在資料工程未來的角色了!