2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonのデータ処理を高速化する:Polarsライブラリ入門

Posted at

はじめに

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を試してみてください。その速度と使いやすさに、きっと驚かれることでしょう。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?