在這篇博文中,我們的目標是解決數據愛好者提出的一個常見問題:如何有效地從Polars DataFrame中創建匯總視圖,以便在不同時間段或類別之間輕松進行比較。我們將使用一個實際的數據集示例來探索實現這一目標的各種方法。
Polars簡介
Polars 是一個用 Rust 編寫的高性能數據處理庫,用于 Python 和 R 等語言。它在處理大型數據集時能夠提供高效的數據處理能力,并且具有類似于 Pandas 的數據處理接口,方便數據科學家和分析師使用。
性能優勢
并行計算:Polars 能夠利用多核處理器進行并行計算。例如,在進行數據聚合操作(如計算列的平均值、總和等)或者數據篩選操作時,它可以將任務分配到多個核心上同時執行,大大提高了計算速度。相比傳統的數據處理庫,在處理大規模數據時這種并行計算的優勢更加明顯。
高效的內存管理:它對內存的使用非常高效,通過優化數據存儲結構和算法,減少了不必要的內存占用。例如,在處理包含大量重復數據或者稀疏數據的數據集時,Polars 能夠以更緊湊的方式存儲數據,從而節省內存資源,并且能夠更快地進行數據讀寫操作。
編譯時優化:由于是用 Rust 編寫,在編譯階段就可以進行許多性能優化。Rust 的編譯器能夠對代碼進行諸如消除冗余計算、優化循環等操作,使得生成的機器碼在執行時能夠更高效地處理數據。
適用場景
大數據處理:在處理海量數據(如日志數據、物聯網數據等)時,Polars 的高性能和高效內存管理能夠發揮巨大優勢,快速地進行數據清洗、轉換和分析。
數據科學和分析:無論是進行探索性數據分析、數據建模還是數據可視化的前期數據處理,Polars 都可以作為一個高效的數據處理工具,幫助數據科學家更快地獲取數據洞察。
數據管道構建:在構建數據管道時,需要對數據進行一系列的轉換和處理操作。Polars 的高效性和豐富的數據操作方法使其成為構建數據管道的有力工具,可以確保數據在不同處理階段的快速流動和處理。
數據聚合與旋轉案例
為了說明聚合和旋轉技術,讓我們考慮一個簡單的數據集。該數據集在幾個月內跟蹤不同渠道的發送和唯一id。這是我們初始數據集的樣子:
import polars as pl
df = pl.DataFrame({"Channel": ["X", "X", "Y", "Y", "X", "X", "Y", "Y", "X", "X", "Y", "Y", "X", "X", "Y", "Y"],"ID": ["a", "b", "b", "a", "e", "b", "g", "h", "a", "a", "k", "a", "b", "n", "o", "p"],"Month": ["1", "2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2", "1", "2"]
})
轉換目標
我們的目標是聚合數據并計算值,例如每個通道和每個月的唯一id數量和發送總數,并以一種方便進行月與月比較的方式顯示它們。
所需的格式是數據透視表,顯示不同的聚合功能,如“唯一ID”和“總發送”,每月作為列:
| Channels | agg_func | 1 | 2 |
|----------|-------------|---|---|
| X | Uniques ID | 3 | 3 |
| X | Total sends | 4 | 4 |
| Y | Uniques ID | 4 | 3 |
| Y | Total sends | 4 | 4 |
實現轉換
- 使用
Pivot
和Aggregate
函數
使用polar實現這一目標的強大方法是利用pivot函數與聚合函數相結合來生成所需格式。下面將深入介紹如何有效地執行這些操作。
pv = df.pivot(on="Month",values="ID",aggregate_function=pl.concat_list(pl.element().n_unique().alias("value"),pl.element().count().alias("value"))
).with_columns(agg_func=["Uniques ID","Total sends"]).explode(pl.exclude("Channel"))
pv
該腳本在“Month”列上執行旋轉操作,其中多個聚合函數連接在一個列表中。將結果展開,以便分離每個聚合值,輸出結果如下:
shape: (4, 4)
┌─────────┬─────┬─────┬─────────────┐
│ Channel ┆ 1 ┆ 2 ┆ agg_func │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ u32 ┆ u32 ┆ str │
╞═════════╪═════╪═════╪═════════════╡
│ X ┆ 3 ┆ 3 ┆ Uniques ID │
│ X ┆ 4 ┆ 4 ┆ Total sends │
│ Y ┆ 4 ┆ 3 ┆ Uniques ID │
│ Y ┆ 4 ┆ 4 ┆ Total sends │
└─────────┴─────┴─────┴─────────────┘
- 使用多個Pivot 函數
另一種方法(手動但有效)是為每個想要應用的聚合函數執行單獨的樞軸:
pl.concat([df.pivot(on="Month",values="ID",aggregate_function=agg_func).with_columns(pl.lit(agg_func_name).alias("agg_func"))for agg_func, agg_func_name in [(pl.element().n_unique(), "Uniques ID"), (pl.element().count(), "Total sends")]
])
數據結果如下:
shape: (4, 4)
┌─────────┬─────┬─────┬─────────────┐
│ Channel ┆ 1 ┆ 2 ┆ agg_func │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ u32 ┆ u32 ┆ str │
╞═════════╪═════╪═════╪═════════════╡
│ X ┆ 3 ┆ 3 ┆ Uniques ID │
│ Y ┆ 4 ┆ 3 ┆ Uniques ID │
│ X ┆ 4 ┆ 4 ┆ Total sends │
│ Y ┆ 4 ┆ 4 ┆ Total sends │
└─────────┴─────┴─────┴─────────────┘
- 旋轉之前分組
或者,你可以首先使用group_by操作,在pivot之前基于“Month”和“Channel”預聚合數據:
(df.group_by("Month","Channel").agg(pl.col("ID").n_unique().alias("Uniques ID"),pl.col("ID").count().alias("Total sends")).unpivot(index=["Month","Channel"], variable_name="agg_func").pivot(on="Month", values="value")
)
總結
使用這些方法,可以在polar中有效地轉換和匯總大型數據集,從而提高你的數據分析能力。無論是使用聚合列表的pivot函數,還是執行多個pivot以提高清晰度,這些策略都可以增強輸出的可讀性和可用性,特別是在處理大容量數據時。