はじめに
Pythonでデータ処理を行う際、多くの開発者がPandasを利用していますが、より高速で効率的な選択肢があることをご存知でしょうか?本記事では、Rust言語で実装された高性能なデータフレームライブラリ「Polars」について詳しく解説します。Polarsは、大規模データセットの処理に優れ、直感的なAPIを提供しながら、驚異的な処理速度を実現します。以下の章では、Polarsの基本的な使い方から応用的なテクニックまで、実践的なコード例を交えて紹介していきます。
第1章:Polarsのインストールと基本設定
Polarsは簡単にインストールできます。まずは、pipを使ってPolarsをインストールしましょう。
pip install polars
インストールが完了したら、Pythonスクリプトで以下のようにインポートします。
import polars as pl
# バージョン確認
print(pl.__version__)
Polarsは継続的に更新されているため、最新バージョンを使用することをお勧めします。バージョンを確認し、必要に応じて更新してください。
第2章:データフレームの作成
Polarsでデータフレームを作成する方法はいくつかあります。最も一般的な方法は、辞書やリストから作成する方法です。
# 辞書からデータフレームを作成
data = {
"名前": ["田中", "佐藤", "鈴木"],
"年齢": [25, 30, 28],
"都市": ["東京", "大阪", "名古屋"]
}
df = pl.DataFrame(data)
print(df)
# リストからデータフレームを作成
data_list = [
["田中", 25, "東京"],
["佐藤", 30, "大阪"],
["鈴木", 28, "名古屋"]
]
df_list = pl.DataFrame(data_list, columns=["名前", "年齢", "都市"])
print(df_list)
Polarsのデータフレームは、PandasのDataFrameと似ていますが、より高速で効率的です。列指向のデータ構造を採用しており、大規模なデータセットでも優れたパフォーマンスを発揮します。
第3章:データの読み込みと書き込み
Polarsは様々なファイル形式からデータを読み込むことができます。CSVファイルを例に見てみましょう。
# CSVファイルの読み込み
df = pl.read_csv("data.csv")
# データの確認
print(df.head())
# CSVファイルへの書き込み
df.write_csv("output.csv")
# Parquetファイルの読み込みと書き込み
df_parquet = pl.read_parquet("data.parquet")
df_parquet.write_parquet("output.parquet")
Polarsは、CSVやParquet以外にも、JSON、Excel、SQLデータベースなど、多様なデータソースに対応しています。大規模なデータセットを扱う場合は、特にParquet形式が推奨されます。
第4章:データの選択と絞り込み
Polarsでは、データの選択や絞り込みを直感的に行うことができます。
# 特定の列を選択
selected_df = df.select(["名前", "年齢"])
# 条件に基づいて行を絞り込む
filtered_df = df.filter(pl.col("年齢") > 25)
# 複数の条件を組み合わせる
complex_filter = df.filter((pl.col("年齢") > 25) & (pl.col("都市") == "東京"))
print(selected_df)
print(filtered_df)
print(complex_filter)
select
メソッドで列を選択し、filter
メソッドで条件に基づいて行を絞り込むことができます。Polarsの強力な点は、これらの操作が非常に高速であることです。
第5章:データの変換と新しい列の追加
Polarsでは、既存の列を変換したり、新しい列を追加したりするのも簡単です。
# 年齢を基に新しい列を作成
df_with_category = df.with_columns(
pl.when(pl.col("年齢") < 30).then("若年層")
.otherwise("中年層")
.alias("年齢層")
)
# 複数の列を同時に追加
df_multi_columns = df.with_columns([
(pl.col("年齢") + 1).alias("来年の年齢"),
(pl.col("名前").str.lengths()).alias("名前の長さ")
])
print(df_with_category)
print(df_multi_columns)
with_columns
メソッドを使用すると、既存の列を変換したり、新しい列を追加したりできます。条件分岐や文字列操作など、複雑な処理も簡潔に記述できます。
第6章:グループ化と集計
データ分析では、グループ化と集計操作が頻繁に行われます。Polarsでは、これらの操作を効率的に実行できます。
# 都市ごとの平均年齢を計算
grouped_df = df.group_by("都市").agg(
pl.col("年齢").mean().alias("平均年齢")
)
# 複数の集計操作を同時に行う
multi_agg_df = df.group_by("都市").agg([
pl.col("年齢").mean().alias("平均年齢"),
pl.col("年齢").max().alias("最高年齢"),
pl.col("年齢").min().alias("最低年齢"),
pl.count("名前").alias("人数")
])
print(grouped_df)
print(multi_agg_df)
group_by
メソッドでグループ化し、agg
メソッドで集計操作を行います。複数の集計操作を同時に行うことで、処理効率が向上します。
第7章:欠損値の処理
実際のデータセットでは、欠損値の処理が重要になります。Polarsでは、欠損値を効率的に処理できます。
# 欠損値を含むデータフレームを作成
df_with_null = pl.DataFrame({
"A": [1, 2, None, 4],
"B": [None, 2.5, 3.0, 4.5],
"C": ["a", None, "c", "d"]
})
# 欠損値を含む行を削除
df_dropna = df_with_null.drop_nulls()
# 特定の列の欠損値を埋める
df_fillna = df_with_null.with_columns([
pl.col("A").fill_null(0),
pl.col("B").fill_null(pl.col("B").mean()),
pl.col("C").fill_null("不明")
])
print(df_dropna)
print(df_fillna)
drop_nulls
メソッドで欠損値を含む行を削除したり、fill_null
メソッドで欠損値を特定の値で埋めたりできます。Polarsは、大規模なデータセットでも高速に欠損値処理を行えます。
第8章:データの結合
複数のデータセットを結合する操作も、Polarsで簡単に行えます。
# 2つのデータフレームを作成
df1 = pl.DataFrame({
"ID": [1, 2, 3],
"名前": ["田中", "佐藤", "鈴木"]
})
df2 = pl.DataFrame({
"ID": [2, 3, 4],
"年齢": [25, 30, 35]
})
# 内部結合
inner_join = df1.join(df2, on="ID", how="inner")
# 左外部結合
left_join = df1.join(df2, on="ID", how="left")
# 完全外部結合
outer_join = df1.join(df2, on="ID", how="outer")
print(inner_join)
print(left_join)
print(outer_join)
join
メソッドを使用して、様々な種類の結合操作を行うことができます。結合のキーや方法を指定することで、柔軟なデータ統合が可能です。
第9章:時系列データの処理
Polarsは時系列データの処理にも強みを持っています。日付や時刻に関する操作を効率的に行えます。
import polars as pl
# 時系列データを含むデータフレームを作成
df_time = pl.DataFrame({
"日付": pl.date_range(start="2023-01-01", end="2023-12-31", interval="1d"),
"値": pl.arange(0, 365)
})
# 月ごとの集計
monthly_agg = df_time.group_by_dynamic("日付", every="1mo").agg([
pl.col("値").mean().alias("平均値"),
pl.col("値").max().alias("最大値")
])
# 週ごとの移動平均
weekly_moving_avg = df_time.with_columns(
pl.col("値").rolling_mean(window_size="7d").alias("7日移動平均")
)
print(monthly_agg)
print(weekly_moving_avg)
group_by_dynamic
メソッドを使用して時間間隔ごとの集計を行ったり、rolling_mean
などの関数で移動平均を計算したりできます。Polarsの時系列機能は、金融データ分析や需要予測など、様々な分野で活用できます。
第10章:データの可視化
Polarsは直接的な可視化機能を持っていませんが、他のライブラリと組み合わせることで、効果的なデータ可視化が可能です。ここでは、matplotlibとの連携例を紹介します。
import polars as pl
import matplotlib.pyplot as plt
# サンプルデータの作成
df = pl.DataFrame({
"x": range(10),
"y": [i**2 for i in range(10)]
})
# matplotlibでプロット
plt.figure(figsize=(10, 6))
plt.plot(df["x"], df["y"], marker='o')
plt.title("Polarsデータフレームの可視化")
plt.xlabel("X軸")
plt.ylabel("Y軸")
plt.grid(True)
plt.show()
Polarsのデータフレームは、簡単にPythonのリストや配列に変換できるため、matplotlibやseabornなどの可視化ライブラリと相性が良いです。大規模データセットの場合、Polarsで前処理や集計を行ってから可視化すると、効率的にデータを探索できます。
第11章:高度なデータ操作:Window関数
Polarsは、SQLのWindow関数に相当する強力な機能を提供しています。これにより、複雑なデータ分析タスクを簡単に実行できます。
import polars as pl
# サンプルデータの作成
df = pl.DataFrame({
"部門": ["A", "A", "B", "B", "C", "C"],
"売上": [100, 150, 200, 250, 300, 350]
})
# 部門ごとの累積和を計算
df_cumsum = df.with_columns([
pl.col("売上").cum_sum().over("部門").alias("部門内累積売上"),
pl.col("売上").cum_sum().alias("全体累積売上")
])
# 部門ごとのランキングを計算
df_rank = df.with_columns([
pl.col("売上").rank().over("部門").alias("部門内ランク"),
pl.col("売上").rank().alias("全体ランク")
])
print(df_cumsum)
print(df_rank)
over
メソッドを使用することで、特定のグループ内での計算や、全体に対する計算を柔軟に行うことができます。これは、売上分析や顧客セグメンテーションなど、ビジネス分析で頻繁に必要とされる操作です。
第12章:大規模データセットの効率的な処理
Polarsの強みの一つは、大規模データセットを効率的に処理できることです。ここでは、遅延評価(Lazy Evaluation)を使用した例を紹介します。
import polars as pl
# 大規模CSVファイルを想定
# 実際には大きなファイルへのパスを指定します
file_path = "large_dataset.csv"
# 遅延評価を使用してクエリを構築
lazy_df = pl.scan_csv(file_path)
result = (
lazy_df
.filter(pl.col("age") > 30)
.group_by("city")
.agg([
pl.col("salary").mean().alias("平均給与"),
pl.col("salary").max().alias("最高給与")
])
.sort("平均給与", descending=True)
.limit(10)
.collect()
)
print(result)
scan_csv
関数を使用することで、ファイル全体をメモリに読み込むことなく処理を行えます。遅延評価により、Polarsは最適化されたクエリプランを生成し、効率的にデータを処理します。
第13章:カスタム関数の適用
Polarsでは、カスタム関数を適用して複雑なデータ処理を行うことができます。ここでは、UDFを使用した例を紹介します。
import polars as pl
# サンプルデータの作成
df = pl.DataFrame({
"名前": ["田中", "佐藤", "鈴木"],
"年齢": [25, 30, 35],
"給与": [300000, 400000, 500000]
})
# カスタム関数の定義
def salary_category(salary):
if salary < 350000:
return "低"
elif salary < 450000:
return "中"
else:
return "高"
# UDFを使用して新しい列を追加
df_with_category = df.with_columns(
pl.col("給与").apply(salary_category).alias("給与カテゴリ")
)
print(df_with_category)
apply
メソッドを使用することで、Pythonの関数をPolarsのデータフレームに適用できます。これにより、複雑なビジネスロジックや条件分岐を含む処理も実現可能です。
第14章:メモリ効率の最適化
Polarsは、メモリ効率を重視して設計されていますが、さらに最適化するテクニックがあります。
import polars as pl
# 大規模データセットを想定
large_df = pl.DataFrame({
"ID": range(1000000),
"値": [i * 2 for i in range(1000000)]
})
# メモリ使用量の確認
print(large_df.estimated_size())
# 整数型の最適化
optimized_df = large_df.with_columns([
pl.col("ID").cast(pl.UInt32),
pl.col("値").cast(pl.Int32)
])
print(optimized_df.estimated_size())
# 必要な列のみを選択
selected_df = optimized_df.select(["ID"])
print(selected_df.estimated_size())
データ型の最適化や不要な列の削除により、メモリ使用量を大幅に削減できます。estimated_size
メソッドを使用して、データフレームのメモリ使用量を確認し、最適化の効果を測定できます。
第15章:Polarsの拡張機能と高度な使用法
Polarsには、より高度な機能や拡張機能があります。ここでは、文字列操作と正規表現の使用例を紹介します。
import polars as pl
# サンプルデータの作成
df = pl.DataFrame({
"テキスト": ["Hello, World!", "Python 3.9", "Polars 0.15.1", "Data Science"]
})
# 文字列操作
df_string_ops = df.with_columns([
pl.col("テキスト").str.to_uppercase().alias("大文字"),
pl.col("テキスト").str.contains("Python").alias("Pythonを含む"),
pl.col("テキスト").str.extract(r"(\d+\.\d+)", 1).alias("バージョン")
])
print(df_string_ops)
# 正規表現を使用した複雑な操作
df_regex = df.with_columns([
pl.col("テキスト").str.extract(r"([A-Za-z]+)", 1).alias("最初の単語"),
pl.col("テキスト").str.count_match(r"\b\w+\b").alias("単語数")
])
print(df_regex)
Polarsの文字列操作機能は非常に強力で、大規模なテキストデータの処理にも適しています。正規表現を使用することで、複雑なパターンマッチングや抽出操作も簡単に行えます。
以上、Polarsライブラリの基本から応用まで、15章にわたって詳しく解説しました。Polarsは高速で効率的なデータ処理を可能にし、大規模データセットの分析に特に適しています。Pythonでのデータ分析作業を効率化したい方は、ぜひPolarsを試してみてください。その速度と使いやすさに、きっと驚かれることでしょう。