見出し画像

【コード徹底併記】 PandasからPolarsに移行するための関数対応まとめ

この記事に掲載しているコードは, 以下のリンク先からnotebook形式のファイルとしてダウンロードできます。

https://github.com/Tomoki-Osaki/pandas_to_polars

この記事の特長

pandasからpolarsへの乗り換えがスムーズにできるように, pandasでの処理をpolarsで実現するための関数の対応をまとめました。

なお, この記事ではあくまでpandasとpolarsの対応コードに焦点を当てているため, pandasではできないpolarsの機能にはほとんど触れません。そちらは参考記事セクションに載せている他記事をご覧ください。

polarsは既に参考になる記事が多くありますが, 一応この記事の差別化点は, pandasでの処理をpolarsで再現するということを重点に置き, 両者のコードと処理結果の併記を徹底していることです (自分がpolarsに乗り換えるときに意外に少なかったので)。
また, 実データを想定したダミーデータを用意することで, 実際にpolarsを使うイメージが掴みやすくなっているかと思います。別途csvをダウンロードする必要もありません。

import numpy as np
import pandas as pd
import polars as pl
print("pandas version: ", pd.__version__)
print("polars version: ", pl.__version__)
# [out]
# pandas version:  3.0.3
# polars version:  1.41.2

ダミーデータの作成

今回は顧客の購買行動のダミーデータを利用します。(ダミーデータを生成するコードはGeminiに書いてもらいました)
この関数は, write_csvにパスを指定してcsvに書き出すか, そうでなければpandasやpolarsのDataFrame関数に渡すことのできる辞書型データを返します。
データ列は, 顧客番号, 契約年月日, 出身地, 年齢, 性別, 年齢, 購買金額, 購買年月日があり, 欠損値がランダムに含まれるようにしています。

def generate_dict_for_dataframe(n_rows=10000, seed=42, write_csv=None):
    """
    データフレーム作成用のダミーデータの辞書を返す。
    write_csvにパスを指定すると、そのパスにcsvとして出力する。
    """
    num_unique_customers = 2000
    
    # 乱数シードの固定
    np.random.seed(42)
    
    # 一般的な地域分類のリスト
    regions = ["北海道", "東北", "関東", "中部", "近畿", "中国", "四国", "九州"]
    
    # 顧客マスタデータの作成(ユニーク2000人分)
    unique_customer_ids = [f"C{i:04d}" for i in range(1, num_unique_customers + 1)]
    
    # 各顧客の一意な属性を生成
    genders_master = np.random.choice([1, 2], size=num_unique_customers)
    ages_master = np.random.randint(18, 81, size=num_unique_customers)
    regions_master = np.random.choice(regions, size=num_unique_customers)  # 地域分類マスタ
    
    # 契約年月日の生成
    start_contract = pd.to_datetime("2020-01-01")
    end_contract = pd.to_datetime("2024-12-31")
    contract_range = (end_contract - start_contract).days
    random_days_contract = np.random.randint(
        0, contract_range + 1, size=num_unique_customers
    )
    contract_dates_master = (
        (start_contract + pd.to_timedelta(random_days_contract, unit="D"))
        .strftime("%Y%m%d")
        .astype(int)
        .tolist()
    )
    
    # マスタ辞書(顧客番号をキーに、各属性を固定)
    customer_master = {
        cid: {"性別": g, "年齢": a, "契約年月日": c, "出身地": r}
        for cid, g, a, c, r in zip(
            unique_customer_ids,
            genders_master,
            ages_master,
            contract_dates_master,
            regions_master,
        )
    }
    
    # 1万行の購買トランザクションデータの作成
    # 1万行分の顧客番号をランダムに割り当て
    customer_ids = np.random.choice(unique_customer_ids, size=n_rows)
    
    # 購買金額のベース生成
    purchase_amounts_base = np.random.randint(-5000, 50000, size=n_rows).astype(float)
    
    # 購買金額の1%(100行)をランダムに np.nan にする
    missing_amount_indices = np.random.choice(n_rows, size=int(n_rows * 0.01), replace=False)
    purchase_amounts = [
        np.nan if i in missing_amount_indices else amt
        for i, amt in enumerate(purchase_amounts_base)
    ]
    
    # 購買年月日(整数8桁)のベース生成
    start_date = pd.to_datetime("2025-01-01")
    end_date = pd.to_datetime("2025-12-31")
    date_range = (end_date - start_date).days
    random_days_purchase = np.random.randint(0, date_range + 1, size=n_rows)
    purchase_dates_base = (
        (start_date + pd.to_timedelta(random_days_purchase, unit="D"))
        .strftime("%Y%m%d")
        .astype(int)
        .tolist()
    )
    
    # 購買年月日の1%(100行)をランダムに欠損値(None)にする
    missing_purchase_indices = np.random.choice(n_rows, size=int(n_rows * 0.01), replace=False)
    purchase_dates = [
        None if i in missing_purchase_indices else date
        for i, date in enumerate(purchase_dates_base)
    ]
    
    # 顧客番号をベースにマスタの属性を1万行に紐付け
    genders = [customer_master[cid]["性別"] for cid in customer_ids]
    ages = [customer_master[cid]["年齢"] for cid in customer_ids]
    contract_dates = [customer_master[cid]["契約年月日"] for cid in customer_ids]
    regions_base = [customer_master[cid]["出身地"] for cid in customer_ids]
    
    # 出身地(地域)の1%(100行)をランダムに欠損値(None)にする
    missing_region_indices = np.random.choice(n_rows, size=int(n_rows * 0.01), replace=False)
    birthplaces = [
        None if i in missing_region_indices else rg for i, rg in enumerate(regions_base)
    ]
    
    # pandasに渡せる辞書データの作成
    dummy_data_dict = {
        "顧客番号": customer_ids,
        "契約年月日": contract_dates,
        "年齢": ages,
        "性別": genders,
        "出身地": birthplaces,
        "購入年月日": purchase_dates,
        "購買金額": purchase_amounts,
    }
        
    if write_csv:
        pl.DataFrame(dummy_data_dict).write_csv(write_csv) # デフォルトの1万行で約440KB
    elif not write_csv:
        return dummy_data_dict

1. データフレームの作成

pandasでもpolarsでもDataFrame関数が利用できます。

# 辞書型のデータの雛形
dummy_data_dict = generate_dict_for_dataframe(n_rows=10000, seed=42)
# pandas
df_pd = pd.DataFrame(dummy_data_dict)
print(df_pd)

# [out]
       顧客番号     契約年月日  年齢  性別  出身地       購入年月日     購買金額
0     C0699  20230711  75   2   関東  20250824.0  28044.0
1     C0965  20240723  36   1   関東  20251116.0  22883.0
2     C1476  20241223  44   2   中部  20250119.0   9063.0
3     C0661  20240212  73   2  北海道  20250803.0  28649.0
4     C1128  20201110  50   2   近畿  20250424.0  29828.0
...     ...       ...  ..  ..  ...         ...      ...
9995  C0788  20200618  25   2   中部  20250129.0   5219.0
9996  C0907  20240712  36   2   九州  20250118.0  18481.0
9997  C0864  20220714  69   2   東北  20250901.0  29536.0
9998  C1404  20220121  19   2   関東  20250624.0   1096.0
9999  C0667  20200722  68   2   東北  20250330.0  32953.0

[10000 rows x 7 columns]
# polars
df_pl = pl.DataFrame(dummy_data_dict)
print(df_pl)

# [out]
shape: (10_000, 7)
┌──────────┬────────────┬──────┬──────┬────────┬────────────┬──────────┐
│ 顧客番号 ┆ 契約年月日 ┆ 年齢 ┆ 性別 ┆ 出身地 ┆ 購入年月日 ┆ 購買金額 │
│ ---      ┆ ---        ┆ ---  ┆ ---  ┆ ---    ┆ ---        ┆ ---      │
│ str      ┆ i64        ┆ i64  ┆ i64  ┆ str    ┆ i64        ┆ f64      │
╞══════════╪════════════╪══════╪══════╪════════╪════════════╪══════════╡
│ C0699    ┆ 20230711   ┆ 75   ┆ 2    ┆ 関東   ┆ 20250824   ┆ 28044.0  │
│ C0965    ┆ 20240723   ┆ 36   ┆ 1    ┆ 関東   ┆ 20251116   ┆ 22883.0  │
│ C1476    ┆ 20241223   ┆ 44   ┆ 2    ┆ 中部   ┆ 20250119   ┆ 9063.0   │
│ C0661    ┆ 20240212   ┆ 73   ┆ 2    ┆ 北海道 ┆ 20250803   ┆ 28649.0  │
│ C1128    ┆ 20201110   ┆ 50   ┆ 2    ┆ 近畿   ┆ 20250424   ┆ 29828.0  │
│ …        ┆ …          ┆ …    ┆ …    ┆ …      ┆ …          ┆ …        │
│ C0788    ┆ 20200618   ┆ 25   ┆ 2    ┆ 中部   ┆ 20250129   ┆ 5219.0   │
│ C0907    ┆ 20240712   ┆ 36   ┆ 2    ┆ 九州   ┆ 20250118   ┆ 18481.0  │
│ C0864    ┆ 20220714   ┆ 69   ┆ 2    ┆ 東北   ┆ 20250901   ┆ 29536.0  │
│ C1404    ┆ 20220121   ┆ 19   ┆ 2    ┆ 関東   ┆ 20250624   ┆ 1096.0   │
│ C0667    ┆ 20200722   ┆ 68   ┆ 2    ┆ 東北   ┆ 20250330   ┆ 32953.0  │
└──────────┴────────────┴──────┴──────┴────────┴────────────┴──────────┘

以下, 公式の関数の説明です。

pandas.DataFrame

class pandas.DataFrame(
    data = None, # ndarray (structured or homogeneous), Iterable, dict, or DataFrame
    index = None, # Index or array-like
    columns = None, # Index or array-like
    dtype = None, # dtype, default None
    copy = None # bool or None, default None
)
"""
2次元で、サイズ変更が可能であり、潜在的に異なる型(ヘテロジニアス)のデータを保持し得る表形式のデータ。

データ構造には、ラベル付きの軸(行および列)も含まれる。
算術演算は、行と列の両方のラベルに基づいて整列される。
Seriesオブジェクトを格納する、辞書(dict)風のコンテナと捉えることが可能。
pandasにおける主要なデータ構造。
"""

polars.DataFrame

class polars.DataFrame(
    data: FrameInitTypes | None = None, # dict, Sequence, ndarray, Series, or pandas.DataFrame
    schema: SchemaDefinition | None = None, # Sequence of str, (str,DataType) pairs, or a {str:DataType,} dict
    *,
    schema_overrides: SchemaDict | None = None, # dict, default None
    strict: bool = True, # bool, default True
    orient: Orientation | None = None, # {‘col’, ‘row’}, default None
    infer_schema_length: int | None = 100, # int or None
    nan_to_null: bool = False, # bool, default False
    height: int | None = None, # int or None, default None
)
"""
2次元で、データを行と列を伴う表として表現するデータ構造。
"""

2. データフレームの作成: csv読み込み

pandasでもpolarsでもread_csv関数が利用できます。(出力はDataFrameの時と同じなので割)愛)

# csvファイルの生成 (デフォルトの1万行で約440KB)
generate_dict_for_dataframe(n_rows=10000, seed=42, write_csv="dummy_data.csv")
# pandas
df_pd = pd.read_csv("dummy_data.csv")
print(df_pd)
# polars
df_pl = pl.read_csv("dummy_data.csv")
print(df_pl)

以下, 公式の関数の説明です (長いです) 。

pandas.read_csv

pandas.read_csv(
    filepath_or_buffer, # str, path object or file-like object
    *, 
    sep = <no_default>, # str, default ‘,’
    delimiter = None, # str, optional
    header = 'infer', # int, Sequence of int, ‘infer’ or None, default ‘infer’
    names = <no_default>, # Sequence of Hashable, optional
    index_col = None, # Hashable, Sequence of Hashable or False, optional
    usecols = None, # Sequence of Hashable or Callable, optional
    dtype = None, # dtype or dict of {Hashable : dtype}, optional
    engine = None, # {‘c’, ‘python’, ‘pyarrow’}, optional
    converters = None, # dict of {Hashable : Callable}, optional
    true_values = None, # list, optional
    false_values = None, # list, optional
    skipinitialspace = False, # bool, default False
    skiprows = None, # int, list of int or Callable, optional
    skipfooter = 0, # int, default 0
    nrows = None, # int, optional
    na_values = None, # Hashable, Iterable of Hashable or dict of {Hashable : Iterable},
    keep_default_na = True, # bool, default True
    na_filter = True, # bool, default True
    skip_blank_lines = True, # bool, default True
    parse_dates = None, # bool, None, list of Hashable, default None
    date_format = None, # str or dict of column -> format, optional
    dayfirst = False, # bool, default False
    cache_dates = True, # bool, default True
    iterator = False, # bool, default False
    chunksize = None, # int, optional 
    compression = 'infer', # str or dict, default ‘infer’
    thousands = None, # str (length 1), optional
    decimal = '.', # str (length 1), default ‘.’
    lineterminator = None, # str (length 1), optional
    quotechar = '"', # str (length 1), optional
    quoting = 0, # {0 or csv.QUOTE_MINIMAL, 1 or csv.QUOTE_ALL
    doublequote = True, # bool, default True
    escapechar = None, # str (length 1), optional
    comment = None, # str (length 1), optional
    encoding = None, # str, optional, default ‘utf-8’
    encoding_errors = 'strict', # str, optional, default ‘strict’
    dialect = None, # str or csv.Dialect, optional
    on_bad_lines = 'error', # {‘error’, ‘warn’, ‘skip’} or Callable, default ‘error’
    low_memory = True, # bool, default True
    memory_map = False, # bool, default False
    float_precision = None, # {‘high’, ‘legacy’, ‘round_trip’}, optional
    storage_options = None, # dict, optional
    dtype_backend = <no_default> # {‘numpy_nullable’, ‘pyarrow’}
) → DataFrame or TextFileReader
"""
カンマ区切り値(CSV)ファイルを読み込んで、DataFrameに変換する。

オプションで、ファイルの反復処理(イテレーション)や、チャンク(塊)への分割読み込みにも対応している。
"""

polars.read_csv

polars.read_csv(
    source: str | Path | IO[str] | IO[bytes] | bytes,
    *,
    has_header: bool = True,
    columns: Sequence[int] | Sequence[str] | None = None,
    new_columns: Sequence[str] | None = None,
    separator: str = ',',
    comment_prefix: str | None = None,
    quote_char: str | None = '"',
    skip_rows: int = 0,
    skip_lines: int = 0,
    schema: SchemaDict | None = None,
    schema_overrides: Mapping[str, polarsDataType] | Sequence[polarsDataType] | None = None,
    null_values: str | Sequence[str] | dict[str, str] | None = None,
    missing_utf8_is_empty_string: bool = False,
    ignore_errors: bool = False,
    try_parse_dates: bool = False,
    n_threads: int | None = None,
    infer_schema: bool = True,
    infer_schema_length: int | None = 100,
    batch_size: int = 8192,
    n_rows: int | None = None,
    encoding: CsvEncoding | str = 'utf8',
    low_memory: bool = False,
    rechunk: bool = False,
    use_pyarrow: bool = False,
    storage_options: StorageOptionsDict | None = None,
    skip_rows_after_header: int = 0,
    row_index_name: str | None = None,
    row_index_offset: int = 0,
    sample_size: int = 1024,
    eol_char: str = '\n',
    raise_if_empty: bool = True,
    truncate_ragged_lines: bool = False,
    decimal_comma: bool = False,
    glob: bool = True,
) → DataFrame
"""
CSVファイルを読み込んで、DataFrameに変換する。

ドキュメントに特記されていない限り、polarsはCSVデータがRFC 4180に厳格に準拠していることを前提としている。
不整形な(フォーマットが崩れた)データは、一般によく見られるものではあるが、未定義の動作を引き起こす可能性がある。
"""

3. インデックス列の追加

pandasとpolarsのデータフレームの出力を比較すると分かるように, polarsにはインデックスが存在しません。 インデックスに相当する列が必要な場合は, with_row_indexを使います。

この記事では, pandasとの出力結果の比較がしやすいように, インデックスを追加したデータフレームを使います。
ご自身でpolarsのデータフレームを新しく作成された場合はインデックス列は存在しませんので, ご注意ください。

df_pl = pl.DataFrame(dummy_data_dict).with_row_index()
print(df_pl)

# [out]
shape: (10_000, 8)
┌───────┬──────────┬────────────┬──────┬──────┬────────┬────────────┬──────────┐
│ index ┆ 顧客番号 ┆ 契約年月日 ┆ 年齢 ┆ 性別 ┆ 出身地 ┆ 購入年月日 ┆ 購買金額 │
│ ---   ┆ ---      ┆ ---        ┆ ---  ┆ ---  ┆ ---    ┆ ---        ┆ ---      │
│ u32   ┆ str      ┆ i64        ┆ i64  ┆ i64  ┆ str    ┆ i64        ┆ f64      │
╞═══════╪══════════╪════════════╪══════╪══════╪════════╪════════════╪══════════╡
│ 0     ┆ C0699    ┆ 20230711   ┆ 75   ┆ 2    ┆ 関東   ┆ 20250824   ┆ 28044.0  │
│ 1     ┆ C0965    ┆ 20240723   ┆ 36   ┆ 1    ┆ 関東   ┆ 20251116   ┆ 22883.0  │
│ 2     ┆ C1476    ┆ 20241223   ┆ 44   ┆ 2    ┆ 中部   ┆ 20250119   ┆ 9063.0   │
│ 3     ┆ C0661    ┆ 20240212   ┆ 73   ┆ 2    ┆ 北海道 ┆ 20250803   ┆ 28649.0  │
│ 4     ┆ C1128    ┆ 20201110   ┆ 50   ┆ 2    ┆ 近畿   ┆ 20250424   ┆ 29828.0  │
│ …     ┆ …        ┆ …          ┆ …    ┆ …    ┆ …      ┆ …          ┆ …        │
│ 9995  ┆ C0788    ┆ 20200618   ┆ 25   ┆ 2    ┆ 中部   ┆ 20250129   ┆ 5219.0   │
│ 9996  ┆ C0907    ┆ 20240712   ┆ 36   ┆ 2    ┆ 九州   ┆ 20250118   ┆ 18481.0  │
│ 9997  ┆ C0864    ┆ 20220714   ┆ 69   ┆ 2    ┆ 東北   ┆ 20250901   ┆ 29536.0  │
│ 9998  ┆ C1404    ┆ 20220121   ┆ 19   ┆ 2    ┆ 関東   ┆ 20250624   ┆ 1096.0   │
│ 9999  ┆ C0667    ┆ 20200722   ┆ 68   ┆ 2    ┆ 東北   ┆ 20250330   ┆ 32953.0  │
└───────┴──────────┴────────────┴──────┴──────┴────────┴────────────┴──────────┘

polars.DataFrame.with_row_index

# polars
DataFrame.with_row_index(
    name: str = 'index', 
    offset: int = 0
) → Self
"""
データフレームの最初の列にインデックスを追加する。
"""

4. 列の追加

pandasではassignで, polarsではwith_columnsで列を追加できます。
その際, .aliasで列名を指定できます。
polarsでは, 関数やデータフレームのメソッドで列を指定するときは, エクスプレッション (pl.Expr) という記法を用いることで, 柔軟で高速な処理を実現することができます (列同士の計算結果を新しい列として追加したり)。これは例えば, pl.col("列名")や, pl.all()といった書き方です。この記事で詳細には解説しませんが, こちらの記事で詳しく説明されていますので, ご参照ください。

# pandas
df_pd.assign(
    集計用=1, 
    出身地方=df_pd["出身地"] + "地方",
)

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額	集計用	出身地方
0	C0699	20230711	75	2	関東	20250824.0	28044.0	1	関東地方
1	C0965	20240723	36	1	関東	20251116.0	22883.0	1	関東地方
2	C1476	20241223	44	2	中部	20250119.0	9063.0	1	中部地方
3	C0661	20240212	73	2	北海道	20250803.0	28649.0	1	北海道地方
4	C1128	20201110	50	2	近畿	20250424.0	29828.0	1	近畿地方
...	...	...	...	...	...	...	...	...	...
9995	C0788	20200618	25	2	中部	20250129.0	5219.0	1	中部地方
9996	C0907	20240712	36	2	九州	20250118.0	18481.0	1	九州地方
9997	C0864	20220714	69	2	東北	20250901.0	29536.0	1	東北地方
9998	C1404	20220121	19	2	関東	20250624.0	1096.0	1	関東地方
9999	C0667	20200722	68	2	東北	20250330.0	32953.0	1	東北地方
10000 rows × 9 columns
# polars
df_pl.with_columns(
    pl.lit(1).alias("集計用"),
    (pl.col("出身地") + "地方").alias("出身地方"),
)

# [out]
shape: (10_000, 10)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額	集計用	出身地方
u32	str	i64	i64	i64	str	i64	f64	i32	str
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0	1	"関東地方"
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0	1	"関東地方"
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0	1	"中部地方"
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0	1	"北海道地方"
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0	1	"近畿地方"
…	…	…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0	1	"中部地方"
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0	1	"九州地方"
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0	1	"東北地方"
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0	1	"関東地方"
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0	1	"東北地方"

以下, 公式の関数説明です。

pandas.DataFrame.assign

DataFrame.assign(
    **kwargs # callable or Series
) → DataFrame
"""
DataFrameに新しい列を割り当てる。

新しい列に加え、すべての元の列を含んだ新しいオブジェクトを返す。
再割り当てされた既存の列は上書きされる。
"""

polars.DataFrame.with_columns

DataFrame.with_columns(
    *exprs: IntoExpr | Iterable[IntoExpr],
    **named_exprs: IntoExpr,
) → DataFrame
"""
このDataFrameに列を追加する。

追加された列は、同じ名前を持つ既存の列を置き換える。
"""

5. 列名の変更

pandasでもpolarsでもrename関数を使って列名を変更できます。

# pandas
df_pd.rename(columns={"購買金額": "利用額"})

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	利用額
0	C0699	20230711	75	2	関東	20250824.0	28044.0
1	C0965	20240723	36	1	関東	20251116.0	22883.0
2	C1476	20241223	44	2	中部	20250119.0	9063.0
3	C0661	20240212	73	2	北海道	20250803.0	28649.0
4	C1128	20201110	50	2	近畿	20250424.0	29828.0
...	...	...	...	...	...	...	...
9995	C0788	20200618	25	2	中部	20250129.0	5219.0
9996	C0907	20240712	36	2	九州	20250118.0	18481.0
9997	C0864	20220714	69	2	東北	20250901.0	29536.0
9998	C1404	20220121	19	2	関東	20250624.0	1096.0
9999	C0667	20200722	68	2	東北	20250330.0	32953.0
10000 rows × 7 columns
# polars
df_pl.rename({"購買金額": "利用額"})

# [out]
shape: (10_000, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	利用額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0

以下, 公式の関数の説明です。

pandas.DataFrame.rename

DataFrame.rename(
    mapper = None, # dict-like or function
    *,  
    index = None, # dict-like or function
    columns = None, # dict-like or function
    axis = None, # {0 or ‘index’, 1 or ‘columns’}, default 0
    copy = <no_default>, # bool, default False
    inplace = False, # bool, default False
    level = None, # int or level name, default None
    errors = 'ignore' # {‘ignore’, ‘raise’}, default ‘ignore’
) → DataFrame or None
"""
列名またはインデックスのラベルを変更する。

関数または辞書(dict)の値はユニーク(1対1)である必要がある。
辞書またはSeriesに含まれないラベルはそのまま残される。
指定された余分なラベルによってエラーが発生することはない。
"""

polars.DataFrame.rename

DataFrame.rename(
    mapping: Mapping[str, str] | Callable[[str], str],
    *,
    strict: bool = True,
) → DataFrame
"""
列名を変更する。
"""

6. 列の選択

pandasではfilter, polarsではselectが利用できます。polarsでは, pl.colを用いる記法により, 既存の列に処理を行った新しい列を作成することもできます。

# pandas
df_pd.filter(items=["性別", "顧客番号"])

# [out]
	性別	顧客番号
0	2	C0699
1	1	C0965
2	2	C1476
3	2	C0661
4	2	C1128
...	...	...
9995	2	C0788
9996	2	C0907
9997	2	C0864
9998	2	C1404
9999	2	C0667
10000 rows × 2 columns
# polars
df_pl.select(
    pl.col("性別"), 
    pl.col("顧客番号"),
)

# [out]
shape: (10_000, 2)
性別	顧客番号
i64	str
2	"C0699"
1	"C0965"
2	"C1476"
2	"C0661"
2	"C1128"
…	…
2	"C0788"
2	"C0907"
2	"C0864"
2	"C1404"
2	"C0667"
# polars
# こういう書き方もできる
df_pl.select(["性別", "顧客番号"])
df_pl[["性別", "顧客番号"]]

以下, 公式の関数の説明です。

pandas.DataFrame.filter

DataFrame.filter(
    items = None, # list-like
    like = None, # str
    regex = None, # str (regular expression)
    axis = None # {0 or ‘index’, 1 or ‘columns’, None}, default None
) → Series or DataFrame
"""
指定されたインデックスのラベルに従って、DataFrameまたはSeriesの部分集合(サブセット)を抽出する。

DataFrameの場合、axis引数に応じて行または列をフィルタリングする。
なお、このルーチンはデータの内容(コンテンツ)に基づいてフィルタリングを行うものではないことに注意。
フィルターはインデックスのラベルに対して適用される。
"""

polars.select

polars.select(
    *exprs: IntoExpr | Iterable[IntoExpr],
    eager: bool = True,
    **named_exprs: IntoExpr,
) → DataFrame | LazyFrame
"""
コンテキスト(DataFrameなどの枠組み)を伴わずにpolarsの式(Expressions)を実行する。

これは、空のDataFrame(または eager=False の場合はLazyFrame)に対して df.select を実行するためのシンタックスシュガー(構文糖衣)。
"""

7. 列の削除

pandasでもpolarsでもdropが利用できます。

# pandas
df_pd.drop(columns=["性別"])

# [out]
	顧客番号	契約年月日	年齢	出身地	購入年月日	購買金額
0	C0699	20230711	75	関東	20250824.0	28044.0
1	C0965	20240723	36	関東	20251116.0	22883.0
2	C1476	20241223	44	中部	20250119.0	9063.0
3	C0661	20240212	73	北海道	20250803.0	28649.0
4	C1128	20201110	50	近畿	20250424.0	29828.0
...	...	...	...	...	...	...
9995	C0788	20200618	25	中部	20250129.0	5219.0
9996	C0907	20240712	36	九州	20250118.0	18481.0
9997	C0864	20220714	69	東北	20250901.0	29536.0
9998	C1404	20220121	19	関東	20250624.0	1096.0
9999	C0667	20200722	68	東北	20250330.0	32953.0
10000 rows × 6 columns
# polars
df_pl.drop(pl.col("性別"))

# [out]
shape: (10_000, 7)
index	顧客番号	契約年月日	年齢	出身地	購入年月日	購買金額
u32	str	i64	i64	str	i64	f64
0	"C0699"	20230711	75	"関東"	20250824	28044.0
1	"C0965"	20240723	36	"関東"	20251116	22883.0
2	"C1476"	20241223	44	"中部"	20250119	9063.0
3	"C0661"	20240212	73	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	"近畿"	20250424	29828.0
…	…	…	…	…	…	…
9995	"C0788"	20200618	25	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	"東北"	20250330	32953.0

以下, 公式の関数の説明です。

pandas.DataFrame.drop

DataFrame.drop(
    labels = None, # single label or iterable of labels
    *, 
    axis = 0, # {0 or ‘index’, 1 or ‘columns’}, default 0
    index = None, # single label or iterable of labels
    columns = None, # single label or iterable of labels
    level = None, # int or level name, optional
    inplace = False, # bool, default False
    errors = 'raise' # {‘ignore’, ‘raise’}, default ‘raise’
) → DataFrame or None
"""
行または列から指定されたラベルを削除する。

ラベル名と対応する軸を指定するか、あるいはインデックス名や列名を直接指定することによって、行または列を削除する。
マルチインデックスを使用する場合、レベルを指定することで異なるレベルのラベルを削除できる。
"""

polars.DataFrame.drop

DataFrame.drop(
    *columns: ColumnNameOrSelector | Iterable[ColumnNameOrSelector],
    strict: bool = True,
) → DataFrame
"""
データフレームから列を削除する。
"""

8. 行の絞り込み

pandasではquery, polarsではfilter関数が利用できます。

# pandas
df_pd.query('性別 == 1 & 年齢 <= 50')
# ↓ query関数を使わない形
# df_pd[
#     (df_pd["性別"] == 1) & (df_pd["年齢"] <= 50)
# ]

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
1	C0965	20240723	36	1	関東	20251116.0	22883.0
17	C1735	20200112	20	1	NaN	20251223.0	19187.0
25	C0495	20200518	37	1	四国	20251106.0	45290.0
33	C0616	20240617	37	1	近畿	20250909.0	46571.0
36	C0206	20201126	26	1	中部	20250131.0	6278.0
...	...	...	...	...	...	...	...
9975	C0011	20221218	40	1	九州	20250707.0	13755.0
9976	C1606	20230520	25	1	中国	20250904.0	27311.0
9983	C1283	20210104	33	1	近畿	20250624.0	40370.0
9990	C1962	20210607	48	1	関東	20250512.0	656.0
9992	C0619	20211011	23	1	近畿	20251002.0	37203.0
2557 rows × 7 columns
# polars
df_pl.filter(
    (pl.col("性別") == 1) & (pl.col("年齢") <= 50)
)

# [out]
shape: (2_557, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
17	"C1735"	20200112	20	1	null	20251223	19187.0
25	"C0495"	20200518	37	1	"四国"	20251106	45290.0
33	"C0616"	20240617	37	1	"近畿"	20250909	46571.0
36	"C0206"	20201126	26	1	"中部"	20250131	6278.0
…	…	…	…	…	…	…	…
9975	"C0011"	20221218	40	1	"九州"	20250707	13755.0
9976	"C1606"	20230520	25	1	"中国"	20250904	27311.0
9983	"C1283"	20210104	33	1	"近畿"	20250624	40370.0
9990	"C1962"	20210607	48	1	"関東"	20250512	656.0
9992	"C0619"	20211011	23	1	"近畿"	20251002	37203.0

以下, 公式の関数の説明です。

pandas.DataFrame.query

DataFrame.query(
    expr, # str
    *, 
    parser = 'pandas', # {‘pandas’, ‘python’}, default ‘pandas’
    engine = None, # {‘python’, ‘numexpr’}, default ‘numexpr’
    local_dict = None, # dict or None, optional
    global_dict = None, # dict or None, optional
    resolvers = None, # list of dict-like or None, optional
    level = 0, # int, optional
    inplace = False # bool
) → DataFrame or None
"""
ブール式(真偽値を返す条件式)を使って、DataFrameの列をクエリ(条件指定による行の抽出)する。

このメソッドは任意のコードを実行できるため、関数の引数にユーザー入力をそのまま渡すと、コードインジェクション(不正コードの注入・実行)に対して脆弱になる危険性がある。
"""

polars.DataFrame.filter

DataFrame.filter(
    *predicates: IntoExprColumn | Iterable[IntoExprColumn] | bool | list[bool] | np.ndarray[Any, Any],
    **constraints: Any,
) → DataFrame
"""
指定された述語式(条件式)にマッチする行を保持し、行をフィルタリングする。

残された行の元の順序は維持される。
述語(条件)の結果が True になる行のみが保持され、結果が False または null(欠損値)の行は破棄される。
"""

9. 重複の削除

pandasではdrop_duplicates, polarsではunique関数が利用できます。

# pandas
df_pd.drop_duplicates(subset=["年齢"])

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
0	C0699	20230711	75	2	関東	20250824.0	28044.0
1	C0965	20240723	36	1	関東	20251116.0	22883.0
2	C1476	20241223	44	2	中部	20250119.0	9063.0
3	C0661	20240212	73	2	北海道	20250803.0	28649.0
4	C1128	20201110	50	2	近畿	20250424.0	29828.0
...	...	...	...	...	...	...	...
219	C1658	20200130	35	1	関東	20250911.0	19102.0
232	C0921	20241230	80	1	中国	20251216.0	2377.0
239	C0334	20200406	30	1	中国	20250305.0	49920.0
249	C1117	20210425	41	1	中国	20250721.0	49607.0
260	C0576	20240923	19	2	北海道	20250702.0	18130.0
63 rows × 7 columns
# polars
df_pl.unique(subset=pl.col("年齢"), keep="first", maintain_order=True)

# [out]
shape: (63, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
219	"C1658"	20200130	35	1	"関東"	20250911	19102.0
232	"C0921"	20241230	80	1	"中国"	20251216	2377.0
239	"C0334"	20200406	30	1	"中国"	20250305	49920.0
249	"C1117"	20210425	41	1	"中国"	20250721	49607.0
260	"C0576"	20240923	19	2	"北海道"	20250702	18130.0

以下, 公式の関数説明です。

pandas.DataFrame.drop_duplicates

DataFrame.drop_duplicates(
    subset = None, # column label or iterable of labels, optional
    *, 
    keep = 'first', # {‘first’, ‘last’, False}, default ‘first’
    inplace = False, # bool, default False
    ignore_index = False # bool, default False
) → DataFrame or None
"""
重複した行が削除されたDataFrameを返す。

特定の列のみを考慮して(重複判定を行って)削除することも可能(オプション)。
タイムインデックスを含むインデックス情報は無視される。
"""

polars.DataFrame.unique

DataFrame.unique(
    subset: IntoExpr | Collection[IntoExpr] | None = None,
    *,
    keep: UniqueKeepStrategy = 'any', # {‘first’, ‘last’, ‘any’, ‘none’}
    maintain_order: bool = False,
) → DataFrame
"""
このデータフレームから重複した行を削除する。
"""

10. 欠損値: nullとNaNの説明

polarsでは欠損値にnullとNaNがあり, これらは区別されています。
公式の説明はこちら。
nullは全てのデータ型に対して用いられる欠損値です。
NaN (Not a Number) は, 小数点の値を持つ列に対して用いられる値で, 厳密には欠損値ではなく, 有効な小数点の値としてみなされます。
今回のダミーデータでいうと, 購買年月日や出身地はデータ生成時にNoneを含むようにしていて, これらはpolarsではnullになります。一方, 購買金額はnumpy.nanを含むようにしていて, これはNaNとして扱われます。
なお, pandasのデータフレームやシリーズのメソッドでは.isnan()とisnull()がありますが, これらは単なる別名で, 処理は全く同じです。

11. 欠損値の検出

pandasではis_nullおよびisnaが利用できます。これらはブール値のデータフレームやシリーズを返すので, 合計することで欠損値の個数を数えることができます。
polarsでは, pl.Expr表現とis_nullおよびis_nanを組み合わせます。注意点として, is_nullは数値型でない列に対して実行しようとするとエラーが発生するので, polars.selectorsを用いたり列を明示的に選択することで, データフレームのうち数値型の列だけに対して処理を行うようにします。

# pandas
df_pd.isnull().sum()

# [out]
顧客番号       0
契約年月日      0
年齢         0
性別         0
出身地      100
購入年月日    100
購買金額     100
dtype: int64
# polars: is_null どのデータ型に対しても利用できる
df_pl.select(
    pl.all().is_null()
).sum()

# [out]
shape: (1, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	u32	u32	u32	u32	u32	u32	u32
0	0	0	0	0	100	100	0
# polars: is_nan 数値型に対してのみ利用できる
import polars.selectors as cs
df_pl.select(
    cs.numeric().is_nan()
).sum()

# [out]
shape: (1, 6)
index	契約年月日	年齢	性別	購入年月日	購買金額
u32	u32	u32	u32	u32	u32
0	0	0	0	0	100

以下, 公式の関数の説明です。

pandas.DataFrame.isnull

DataFrame.isnull() → Series or DataFrame
"""
欠損値を検出する。

DataFrame.isnull は DataFrame.isna のエイリアス(別名)である。
値がNA(欠損値)であるかどうかを示す、元のオブジェクトと同じサイズのブール値(真偽値)のオブジェクトを返す。
None や numpy.NaN などのNA値は True にマッピングされ、それ以外のすべての値は False にマッピングされる。
空文字列 '' や numpy.inf(無限大)などの文字や値は、NA値とはみなされない。
"""

polars.Expr.is_null

Expr.is_null() → Expr
"""
値がnull(欠損値)であるかどうかを示す、ブール値(真偽値)のSeriesを返す。
"""

polars.Expr.is_nan

Expr.is_nan() → Expr
"""
値がNaN(Not a Number / 非数)であるかどうかを示す、ブール値(真偽値)のSeriesを返す。
"""

12. 欠損値の削除

pandasではdropna, polarsでは例によってdrop_nullsとdrop_nansがあります。

# pandas
df_pd.dropna()

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
0	C0699	20230711	75	2	関東	20250824.0	28044.0
1	C0965	20240723	36	1	関東	20251116.0	22883.0
2	C1476	20241223	44	2	中部	20250119.0	9063.0
3	C0661	20240212	73	2	北海道	20250803.0	28649.0
4	C1128	20201110	50	2	近畿	20250424.0	29828.0
...	...	...	...	...	...	...	...
9995	C0788	20200618	25	2	中部	20250129.0	5219.0
9996	C0907	20240712	36	2	九州	20250118.0	18481.0
9997	C0864	20220714	69	2	東北	20250901.0	29536.0
9998	C1404	20220121	19	2	関東	20250624.0	1096.0
9999	C0667	20200722	68	2	東北	20250330.0	32953.0
9702 rows × 7 columns
# polars (drop_nulls)
df_pl.drop_nulls()

# [out]
shape: (9_800, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0
# polars (drop_nans)
df_pl.drop_nans()

# [out]
shape: (9_900, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0
# polars (drop_nulls + drop_nans) pandasのdropnaと同じ
df_pl.drop_nulls().drop_nans()

# [out]
shape: (9_702, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東地方"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東地方"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部地方"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道地方"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿地方"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部地方"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州地方"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北地方"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東地方"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北地方"	20250330	32953.0

以下, 公式の関数の説明です。

pandas.DataFrame.dropna

DataFrame.dropna(
    *, 
    axis = 0, # {0 or ‘index’, 1 or ‘columns’}, default 0
    how = <no_default>, # {‘any’, ‘all’}, default ‘any’
    thresh = <no_default>, # int, optional
    subset = None, # column label or iterable of labels, optional
    inplace = False, # bool, default False
    ignore_index = False # bool, default False
) → DataFrame or None
"""
欠損値を除去する。
"""

polars.DataFrame.drop_nulls

DataFrame.drop_nulls(
    subset: ColumnNameOrSelector | Collection[ColumnNameOrSelector] | None = None,
) → DataFrame
"""
1つ以上のnull値を含むすべての行を削除する。

残された行の元の順序は維持される。
"""

polars.DataFrame.drop_nans

DataFrame.drop_nans(
    subset: ColumnNameOrSelector | Collection[ColumnNameOrSelector] | None = None,
) → DataFrame
"""
1つ以上のNaN値を含むすべての行を削除する。

残された行の元の順序は維持される。
"""

13. 欠損値の値埋め

pandasではfillna, polarsではNaN値とnull値についてそれぞれfill_nan, fill_nullが利用できます。

# pandas
df_pd.fillna(0)

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
0	C0699	20230711	75	2	関東	20250824.0	28044.0
1	C0965	20240723	36	1	関東	20251116.0	22883.0
2	C1476	20241223	44	2	中部	20250119.0	9063.0
3	C0661	20240212	73	2	北海道	20250803.0	28649.0
4	C1128	20201110	50	2	近畿	20250424.0	29828.0
...	...	...	...	...	...	...	...
9995	C0788	20200618	25	2	中部	20250129.0	5219.0
9996	C0907	20240712	36	2	九州	20250118.0	18481.0
9997	C0864	20220714	69	2	東北	20250901.0	29536.0
9998	C1404	20220121	19	2	関東	20250624.0	1096.0
9999	C0667	20200722	68	2	東北	20250330.0	32953.0
10000 rows × 7 columns
# polars (fill_null)
df_pl.fill_null(0)

# [out]
shape: (10_000, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0
# polars (fill_nan)
df_pl.fill_nan(0)

# [out]
shape: (10_000, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0

以下, 公式の関数の説明です。

pandas.fillna

DataFrame.fillna(
    value, # scalar, dict, Series, or DataFrame
    *, 
    axis = None, # {0 or ‘index’} for Series, {0 or ‘index’, 1 or ‘columns’} for DataFrame
    inplace = False, # bool, default False
    limit = None # int, default None
) → Series or DataFrame
"""
NA/NaN値を指定した値で埋める。
"""

polars.DataFrame.fill_null

DataFrame.fill_null(
    value: Any | Expr | None = None,
    strategy: FillNullStrategy | None = None, # {None, ‘forward’, ‘backward’, ‘min’, ‘max’, ‘mean’, ‘zero’, ‘one’}
    limit: int | None = None,
    *,
    matches_supertype: bool = True,
) → DataFrame
"""
指定された値またはストラテジー(手法)を用いて、null値を埋める。
"""

polars.DataFrame.fill_nan

polars.DataFrame.fill_nan

DataFrame.fill_nan(
    value: Expr | int | float | None
) → DataFrame
"""
式の評価によって、浮動小数点数のNaN値(欠損値)を埋める。
"""

14. データフレームの結合: 縦方向

pandasでもpolarsでもconcatが利用できます。

# 結合用の新しいデータフレームの用意
dummy_dict_data2 = generate_dict_for_dataframe(seed=1)
df_pd2 = pd.DataFrame(dummy_dict_data2)
df_pl2 = pl.DataFrame(dummy_dict_data2).with_row_index()
# pandas
pd.concat([df_pd, df_pd2])

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
0	C0699	20230711	75	2	関東	20250824.0	28044.0
1	C0965	20240723	36	1	関東	20251116.0	22883.0
2	C1476	20241223	44	2	中部	20250119.0	9063.0
3	C0661	20240212	73	2	北海道	20250803.0	28649.0
4	C1128	20201110	50	2	近畿	20250424.0	29828.0
...	...	...	...	...	...	...	...
9995	C0788	20200618	25	2	中部	20250129.0	5219.0
9996	C0907	20240712	36	2	九州	20250118.0	18481.0
9997	C0864	20220714	69	2	東北	20250901.0	29536.0
9998	C1404	20220121	19	2	関東	20250624.0	1096.0
9999	C0667	20200722	68	2	東北	20250330.0	32953.0
20000 rows × 7 columns
# polars
pl.concat([df_pl, df_pl2])

# [out]
shape: (20_000, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0
…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0

以下, 公式の関数説明です。

pandas.concat

pandas.concat(
    objs, # an iterable or mapping of Series or DataFrame objects
    *, 
    axis = 0, # {0/’index’, 1/’columns’}, default 0
    join = 'outer', # {‘inner’, ‘outer’}, default ‘outer’
    ignore_index = False, # bool, default False
    keys = None, # sequence, default None
    levels = None, # list of sequences, default None
    names = None, # list, default None
    verify_integrity = False, # bool, default False
    sort = <no_default>, # bool, default False
    copy = <no_default> # bool, default False
) → Series or DataFrame
"""
特定の軸に沿ってpandasオブジェクトを結合(連結)する。

他の軸に沿った任意の集合論理(set logic、積集合や和集合など)を適用できる。
また、結合する軸に対して階層型インデックス(MultiIndex)のレイヤーを追加することも可能。
これは、渡された軸の番号上でラベルが同じ(または重複している)場合に有用。
"""

polars.concat

polars.concat(
    items: Iterable[polarsType],
    *,
    how: ConcatMethod = 'vertical', # {‘vertical’, ‘vertical_relaxed’, ‘diagonal’, ‘diagonal_relaxed’, ‘horizontal’, ‘align’, ‘align_full’, ‘align_inner’, ‘align_left’, ‘align_right’}
    rechunk: bool = False,
    parallel: bool = True,
    strict: bool = False,
) → polarsType
"""
複数のDataFrame、LazyFrame、またはSeriesを単一のオブジェクトに結合(連結)する。
"""

15. データフレームの結合: 横方向

pandasではmerge, polarsではjoinが利用できます。

# pandas
pd.merge(df_pd, df_pd2, on="顧客番号", how="left")

# [out}
	顧客番号	契約年月日_x	年齢_x	性別_x	出身地_x	購入年月日_x	購買金額_x	契約年月日_y	年齢_y	性別_y	出身地_y	購入年月日_y	購買金額_y
0	C0699	20230711	75	2	関東	20250824.0	28044.0	20230711	75	2	関東	20250824.0	28044.0
1	C0699	20230711	75	2	関東	20250824.0	28044.0	20230711	75	2	関東	20250710.0	42899.0
2	C0699	20230711	75	2	関東	20250824.0	28044.0	20230711	75	2	関東	20251008.0	15055.0
3	C0699	20230711	75	2	関東	20250824.0	28044.0	20230711	75	2	関東	20251023.0	27726.0
4	C0965	20240723	36	1	関東	20251116.0	22883.0	20240723	36	1	関東	20251116.0	22883.0
...	...	...	...	...	...	...	...	...	...	...	...	...	...
59895	C1404	20220121	19	2	関東	20250624.0	1096.0	20220121	19	2	関東	20250804.0	7442.0
59896	C1404	20220121	19	2	関東	20250624.0	1096.0	20220121	19	2	関東	20250624.0	1096.0
59897	C0667	20200722	68	2	東北	20250330.0	32953.0	20200722	68	2	東北	20250204.0	4808.0
59898	C0667	20200722	68	2	東北	20250330.0	32953.0	20200722	68	2	東北	20250930.0	23874.0
59899	C0667	20200722	68	2	東北	20250330.0	32953.0	20200722	68	2	東北	20250330.0	32953.0
59900 rows × 13 columns
# polars
df_pl.join(df_pl2, on="顧客番号", how="left")

# [out]
shape: (59_900, 15)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額	index_right	契約年月日_right	年齢_right	性別_right	出身地_right	購入年月日_right	購買金額_right
u32	str	i64	i64	i64	str	i64	f64	u32	i64	i64	i64	str	i64	f64
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0	0	20230711	75	2	"関東"	20250824	28044.0
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0	621	20230711	75	2	"関東"	20250710	42899.0
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0	8480	20230711	75	2	"関東"	20251008	15055.0
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0	9560	20230711	75	2	"関東"	20251023	27726.0
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0	1	20240723	36	1	"関東"	20251116	22883.0
…	…	…	…	…	…	…	…	…	…	…	…	…	…	…
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0	6048	20220121	19	2	"関東"	20250804	7442.0
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0	9998	20220121	19	2	"関東"	20250624	1096.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0	1463	20200722	68	2	"東北"	20250204	4808.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0	2623	20200722	68	2	"東北"	20250930	23874.0
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0	9999	20200722	68	2	"東北"	20250330	32953.0

以下, 公式の関数説明です。

pandas.merge

pandas.merge(
    left, # DataFrame or named Series
    right, # DataFrame or named Series
    how = 'inner', # {‘left’, ‘right’, ‘outer’, ‘inner’, ‘cross’, ‘left_anti’, ‘right_anti}
    on = None, # Hashable or a sequence of the previous
    left_on = None, # Hashable or a sequence of the previous, or array-like
    right_on = None, # Hashable or a sequence of the previous, or array-like
    left_index = False, # bool, default False
    right_index = False, # bool, default False
    sort = False, # bool, default False
    suffixes = ('_x', '_y'), # list-like, default is (“_x”, “_y”)
    copy = <no_default>, # bool, default False
    indicator = False, # bool or str, default False
    validate = None # str, optional {'one_to_one', 'one_to_many', 'many_to_one', 'many_to_many'}
) → DataFrame
"""
DataFrameまたは名前付きSeriesオブジェクトを、データベーススタイルの結合(Join)でマージする。

名前付きSeriesオブジェクトは、名前付きの列を1つだけ持つDataFrameとして処理される。
結合は列またはインデックスに基づいて行われる。
列同士を結合する場合、DataFrameのインデックスは無視される。
インデックス同士、またはインデックスと列を結合する場合は、インデックスの情報が引き継がれる。
クロス結合(クロス・マージ)を実行する場合、結合キーとなる列の指定は不可。
"""

polars.DataFrame.join

DataFrame.join(
    other: DataFrame,
    on: str | Expr | Sequence[str | Expr] | None = None,
    how: JoinStrategy = 'inner', # {‘inner’, ‘left’, ‘right’, ‘full’, ‘semi’, ‘anti’, ‘cross’}
    *,
    left_on: str | Expr | Sequence[str | Expr] | None = None,
    right_on: str | Expr | Sequence[str | Expr] | None = None,
    suffix: str = '_right',
    validate: JoinValidation = 'm:m', # {‘m:m’, ‘m:1’, ‘1:m’, ‘1:1’}
    nulls_equal: bool = False,
    coalesce: bool | None = None,
    maintain_order: MaintainOrderJoin | None = None, # {‘none’, ‘left’, ‘right’, ‘left_right’, ‘right_left’}
) → DataFrame
"""
データフレームをSQL風に結合する。
"""

16. 並び替え

pandasではsort_values, polarsではsortが利用できます。pandasは関数の引数がascending(昇順)なのに対し, polarsではdescending(降順)のブール値を指定することに気をつけてください。

また, NaN値がpolarsのデータフレームに存在する場合, pandasのsort_valuesでna_position="first" (デフォルト値) を指定した時と, polarsのsortでnulls_last=False (デフォルト値) を指定したときで, 出力が一致しません。

これは, pandasではNaNはna_positionに反応して頭に来ますが, polarsではNaNはnullではないので, nulls_lastには反応しないからです。

このように, polarsでは欠損値がnullなのかNaNなのかが重要な違いを持ちます。

# pandas
df_pd.sort_values(
    ["年齢", "購買金額"], 
    ascending=[True, False],
    na_position="first",
)

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
9887	C0873	20241105	18	2	四国	20250602.0	NaN
6037	C0825	20220731	18	2	東北	20250715.0	49422.0
3420	C0791	20221103	18	2	中国	20250906.0	48494.0
2912	C0456	20200711	18	1	中国	20250322.0	47913.0
3697	C0884	20200328	18	2	九州	20251129.0	47627.0
...	...	...	...	...	...	...	...
2455	C1071	20211220	80	1	四国	20250905.0	-2221.0
6787	C0623	20230921	80	1	東北	20250109.0	-2301.0
4193	C0183	20200208	80	2	東北	20250111.0	-3690.0
5610	C0623	20230921	80	1	東北	20250609.0	-3822.0
9958	C0168	20211005	80	1	近畿	20250517.0	-4865.0
10000 rows × 7 columns
# polars
df_pl.sort(
    [pl.col("年齢"), pl.col("購買金額")], 
    descending=[False, True],
    maintain_order=True,
    nulls_last=True
)

# [out]
shape: (10_000, 8)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
u32	str	i64	i64	i64	str	i64	f64
9887	"C0873"	20241105	18	2	"四国"	20250602	NaN
6037	"C0825"	20220731	18	2	"東北"	20250715	49422.0
3420	"C0791"	20221103	18	2	"中国"	20250906	48494.0
2912	"C0456"	20200711	18	1	"中国"	20250322	47913.0
3697	"C0884"	20200328	18	2	"九州"	20251129	47627.0
…	…	…	…	…	…	…	…
2455	"C1071"	20211220	80	1	"四国"	20250905	-2221.0
6787	"C0623"	20230921	80	1	"東北"	20250109	-2301.0
4193	"C0183"	20200208	80	2	"東北"	20250111	-3690.0
5610	"C0623"	20230921	80	1	"東北"	20250609	-3822.0
9958	"C0168"	20211005	80	1	"近畿"	20250517	-4865.0

以下, 公式の関数の説明です。

pandas.DataFrame.sort_values

DataFrame.sort_values(
    by, # str or list of str
    *, 
    axis = 0, # “{0 or ‘index’, 1 or ‘columns’}”, default 0
    ascending = True, # bool or list of bool, default True
    inplace = False, # bool, default False
    kind = 'quicksort', # {‘quicksort’, ‘mergesort’, ‘heapsort’, ‘stable’}, default ‘quicksort’
    na_position = 'last', # {‘first’, ‘last’}, default ‘last’
    ignore_index = False, # bool, default False
    key = None # callable, optional
) → DataFrame or None
"""
いずれかの軸に沿って、値ベースでソート(並べ替え)を行う。
"""

polars.DataFrame.sort

DataFrame.sort(
    by: IntoExpr | Iterable[IntoExpr],
    *more_by: IntoExpr,
    descending: bool | Sequence[bool] = False,
    nulls_last: bool | Sequence[bool] = False,
    multithreaded: bool = True,
    maintain_order: bool = False,
) → DataFrame
"""
指定した列に沿ってデータフレームをソートする。
"""

17. グループバイ

pandasではgroupby, polarsではgroup_byが利用できます。polarsでは, 列の追加時と同様に.aliasで列名を指定できます。
なお, polarsではmaintain_order=Trueを設定しないと, 実行のたびに行の順番が変わります (その分早い)。

# pandas
df_pd.groupby("顧客番号", as_index=False, sort=False).agg(
    年齢=("年齢", "last"),
    契約年月日=("契約年月日", "last"),
    合計購買金額=("購買金額", "sum"),
)

# [out]
	顧客番号	年齢	契約年月日	合計購買金額
0	C0699	75	20230711	113724.0
1	C0965	36	20240723	180273.0
2	C1476	44	20241223	120477.0
3	C0661	73	20240212	147906.0
4	C1128	50	20201110	173258.0
...	...	...	...	...
1981	C1842	73	20240618	31415.0
1982	C0034	43	20240209	47087.0
1983	C0370	21	20230624	80221.0
1984	C0120	33	20230107	63534.0
1985	C0136	62	20201027	30809.0
1986 rows × 4 columns
# polars
df_pl.group_by(
    pl.col("顧客番号"),
    maintain_order=True
).agg(
    pl.last("年齢"),
    pl.last("契約年月日"),
    pl.sum("購買金額").alias("合計購入金額"),
)

# [out]
shape: (1_986, 4)
顧客番号	年齢	契約年月日	合計購入金額
str	i64	i64	f64
"C0699"	75	20230711	113724.0
"C0965"	36	20240723	180273.0
"C1476"	44	20241223	120477.0
"C0661"	73	20240212	147906.0
"C1128"	50	20201110	173258.0
…	…	…	…
"C1842"	73	20240618	31415.0
"C0034"	43	20240209	47087.0
"C0370"	21	20230624	80221.0
"C0120"	33	20230107	63534.0
"C0136"	62	20201027	30809.0

以下, 公式の関数の説明です。

pandas.DataFrame.groupby

DataFrame.groupby(
    by = None, # mapping, function, label, pd.Grouper or list of such
    level = None, # int, level name, or sequence of such, default None
    *, 
    as_index = True, # bool, default True
    sort = True, # bool, default True
    group_keys = True, # bool, default True
    observed = True, # bool, default True
    dropna = True # bool, default True
) → pandas.api.typing.DataFrameGroupBy
"""
マッパー(関数や辞書など)またはSeriesの列を用いてDataFrameをグループ化する。

groupby操作は、オブジェクトの分割(splitting)、関数の適用(applying)、および結果の結合(combining)の組み合わせを伴う。
これは、大量のデータをグループ化し、それらのグループに対して演算を計算するために使用できる。
"""

polars.DataFrame.group_by

polars.DataFrame.group_by

DataFrame.group_by(
    *by: IntoExpr | Iterable[IntoExpr],
    maintain_order: bool = False,
    **named_by: IntoExpr,
) → GroupBy
"""
groupby操作を開始する。
"""

18. 記述統計

pandasでもpolarsでもdescribeが利用できます。
ただし, polarsのdescribeは将来的に挙動が変更される可能性があるため, 定期実行を行う処理などには組み込まないようにと公式から注意があります。
なお, polarsのdescribeでは文字列も集計対象で, 最小値はアルファベット順で一番最初, 最大値は一番最後の値となります。

また, polarsではNaNが含まれる列の集計では, 平均値や標準偏差はNaNになります。

これは, polarsではNaNは欠損値ではなく有効な値とみなされるからです。

# pandas
df_pd.describe()

# [out]
	契約年月日	年齢	性別	購入年月日	購買金額
count	1.000000e+04	10000.000000	10000.000000	9.900000e+03	9900.000000
mean	2.022051e+07	48.714800	1.498000	2.025066e+07	22289.060606
std	1.398674e+04	17.922683	0.500021	3.478658e+02	15827.597946
min	2.020010e+07	18.000000	1.000000	2.025010e+07	-4995.000000
25%	2.021040e+07	33.000000	1.000000	2.025033e+07	8724.000000
50%	2.022062e+07	49.000000	1.000000	2.025063e+07	21808.000000
75%	2.023092e+07	64.000000	2.000000	2.025093e+07	36058.250000
max	2.024123e+07	80.000000	2.000000	2.025123e+07	49986.000000
# polars
df_pl.describe()

# [out]
shape: (9, 9)
statistic	index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額
str	f64	str	f64	f64	f64	str	f64	f64
"count"	10000.0	"10000"	10000.0	10000.0	10000.0	"9900"	9900.0	10000.0
"null_count"	0.0	"0"	0.0	0.0	0.0	"100"	100.0	0.0
"mean"	4999.5	null	2.0221e7	48.7148	1.498	null	2.0251e7	NaN
"std"	2886.89568	null	13986.740832	17.922683	0.500021	null	347.865757	NaN
"min"	0.0	"C0001"	2.0200103e7	18.0	1.0	"中国"	2.0250101e7	-4995.0
"25%"	2500.0	null	2.0210404e7	33.0	1.0	null	2.0250329e7	8890.0
"50%"	5000.0	null	2.0220615e7	49.0	1.0	null	2.0250629e7	22115.0
"75%"	7499.0	null	2.0230916e7	64.0	2.0	null	2.0250929e7	36462.0
"max"	9999.0	"C2000"	2.0241231e7	80.0	2.0	"関東"	2.0251231e7	49986.0

以下, 公式の関数説明です。

pandas.DataFrame.describe

DataFrame.describe(
    percentiles = None, # list-like of numbers, optional
    include = None, # ‘all’, list-like of dtypes or None (default), optional
    exclude = None # list-like of dtypes or None (default), optional
) → Series or DataFrame
"""
記述統計(要約統計量)を生成する。

記述統計には、データセットの分布の中心傾向(中央値や平均値など)、分散(ばらつき)、および形状を要約する統計量が含まれ、NaN値(欠損値)は除外される。
数値データとオブジェクト(文字列など)データの両方のSeries、および異なるデータ型が混在するDataFrameの列セットを分析できる。
出力結果は提供されたデータの内容によって異なる。
"""

polars.DataFrame.descirbe

DataFrame.describe(
    percentiles: Sequence[float] | float | None = (0.25, 0.5, 0.75),
    *,
    interpolation: QuantileMethod = 'nearest', # {‘nearest’, ‘higher’, ‘lower’, ‘midpoint’, ‘linear’, ‘equiprobable’}
) → DataFrame
"""
データフレームの記述統計。

describe の出力結果は、将来にわたって不変(安定的)であることを保証していない。
polars開発チームが有益であると判断した統計量が表示され、これは将来的に更新(変更)される可能性がある。
そのため、プログラム内(自動化された処理など)で describe を使用することは推奨されない(対話的なデータ探索・確認での使用にとどめるべき)。
"""

19. 関数の適用

pandasではapply関数, polarsではwith_columnsやselectでmap_elementsを使用することにより実現できます。
ただし, いずれも実行速度が遅くなるため, 必要性を見極めることが重要。

# 適用する関数
def label_sex(sex):
    if sex == 1: return "男性"
    elif sex == 2: return "女性"
# pandas
df_pd.assign(
    性別_男女=df_pd["性別"].apply(label_sex)
)

# [out]
	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額	性別_男女
0	C0699	20230711	75	2	関東	20250824.0	28044.0	女性
1	C0965	20240723	36	1	関東	20251116.0	22883.0	男性
2	C1476	20241223	44	2	中部	20250119.0	9063.0	女性
3	C0661	20240212	73	2	北海道	20250803.0	28649.0	女性
4	C1128	20201110	50	2	近畿	20250424.0	29828.0	女性
...	...	...	...	...	...	...	...	...
9995	C0788	20200618	25	2	中部	20250129.0	5219.0	女性
9996	C0907	20240712	36	2	九州	20250118.0	18481.0	女性
9997	C0864	20220714	69	2	東北	20250901.0	29536.0	女性
9998	C1404	20220121	19	2	関東	20250624.0	1096.0	女性
9999	C0667	20200722	68	2	東北	20250330.0	32953.0	女性
10000 rows × 8 columns
# polars
df_pl.with_columns(
    pl.col("性別").map_elements(label_sex, return_dtype=pl.String).alias("性別_男女")
)

# [out]
shape: (10_000, 9)
index	顧客番号	契約年月日	年齢	性別	出身地	購入年月日	購買金額	性別_男女
u32	str	i64	i64	i64	str	i64	f64	str
0	"C0699"	20230711	75	2	"関東"	20250824	28044.0	"女性"
1	"C0965"	20240723	36	1	"関東"	20251116	22883.0	"男性"
2	"C1476"	20241223	44	2	"中部"	20250119	9063.0	"女性"
3	"C0661"	20240212	73	2	"北海道"	20250803	28649.0	"女性"
4	"C1128"	20201110	50	2	"近畿"	20250424	29828.0	"女性"
…	…	…	…	…	…	…	…	…
9995	"C0788"	20200618	25	2	"中部"	20250129	5219.0	"女性"
9996	"C0907"	20240712	36	2	"九州"	20250118	18481.0	"女性"
9997	"C0864"	20220714	69	2	"東北"	20250901	29536.0	"女性"
9998	"C1404"	20220121	19	2	"関東"	20250624	1096.0	"女性"
9999	"C0667"	20200722	68	2	"東北"	20250330	32953.0	"女性"

polarsではreturn_dtypeを指定することが強く推奨されます。
また, 例えば以下のようにできるだけpolarsの既存の表現で処理を行うことが進められています。

# できるだけpolarsの既存の表現で処理できるようにする
df_pl.with_columns(
    pl.when(pl.col("性別") == 1).then(pl.lit("男性"))
      .when(pl.col("性別") == 2).then(pl.lit("女性"))
    .alias("性別_男女")
)
# 出力は割愛

以下, 公式の関数の説明です。

pandas.DataFrame.apply

DataFrame.apply(
    func, # function
    axis = 0, # {0 or ‘index’, 1 or ‘columns’}, default 0
    raw = False, # bool, default False
    result_type = None, # {‘expand’, ‘reduce’, ‘broadcast’, None}, default None
    args = (), # tuple
    by_row = 'compat', # False or “compat”, default “compat”
    engine = None, # decorator or {‘python’, ‘numba’}, optional
    engine_kwargs = None, # dict
    **kwargs
) → Series or DataFrame
"""
DataFrameの軸(axis)に沿って関数を適用する。

関数に渡されるオブジェクトは、インデックスがDataFrameのインデックス(axis=0)またはDataFrameの列(axis=1)のいずれかであるSeriesオブジェクト。
デフォルト(result_type=None)では、最終的な戻り値の型は適用された関数の戻り値の型から推論される。
それ以外の場合は、result_type 引数に依存する。
適用された関数の戻り値の型は、Seriesオブジェクトに関数を適用した後に得られる最初の計算結果に基づいて推論される。
"""

polars.Expr.map_elements

polars.Expr.map_elements

Expr.map_elements(
    function: Callable[[Any], Any],
    return_dtype: polarsDataType | DataTypeExpr | None = None,
    *,
    skip_nulls: bool = True,
    pass_name: bool = False,
    strategy: MapElementsStrategy = 'thread_local' # {‘thread_local’, ‘threading’}
) → Expr
"""
列の各要素に対して、カスタム関数(ユーザー定義関数 / UDF)をマッピング(適用)する。

このメソッドは、ネイティブのExpressions APIよりも大幅に低速。
ほかの方法でロジックを実装できない場合にのみ使用すること。
"""

20. parquetファイルの書き出し・読み込み

parquetファイルはcsvよりも軽量で, pandasやpolarsでの読み込み処理も高速に行うことのできるファイル形式です。

ファイルサイズについて, 今回のダミーデータだと, 6000万行を生成した場合, csvだと3.2GB, parquetだと705MBです。

読み込み速度は, 6000万行のダミーデータの場合, pandasのread_csvで29.11秒, pandasのread_parquetで1.73秒, polarsのread_csvで1.69秒, polarsのread_parquetで0.62秒でした。(環境は私の他記事にあるthinkpadのものです)

なお, pandasではparquetファイルを扱うのに, pyarrowライブラリのインストールが必要です。pipまたはcondaでインストールしてください。

# pandas
# pip install pyarrow
df_pd.to_parquet("dummy_data.parquet") # 書き出し
df_pd = pd.read_parquet("dummy_data.parquet") # 読み込み
# polars
df_pl.write_parquet("dummy_data.parquet") # 書き出し
df_pl = pl.read_parquet("dummy_data.parquet") # 読み込み

pandas.DataFrame.to_parquet

DataFrame.to_parquet(
    path = None, # str, path object, file-like object, or None, default None
    *, 
    engine = 'auto', # {‘auto’, ‘pyarrow’, ‘fastparquet’}, default ‘auto’
    compression = 'snappy', # str or None, default ‘snappy’
    index = None, # bool, default None
    partition_cols = None, # list, optional, default None
    storage_options = None, # dict, optional
    filesystem = None, # fsspec or pyarrow filesystem, default None
    **kwargs
)
"""
DataFrameをバイナリのParquet(パーケ)形式で書き出す。

この関数は、DataFrameをParquetファイルとして保存する。
異なるParquetバックエンド(エンジン)を選択することが可能であり、圧縮のオプションも指定できる。
"""

pandas.read_parquet

pandas.read_parquet(
    path, # str, path object or file-like object
    engine = 'auto', # {‘auto’, ‘pyarrow’, ‘fastparquet’}, default ‘auto’
    columns = None, # list, default=None
    storage_options = None, # dict, optional
    dtype_backend = <no_default>, # {‘numpy_nullable’, ‘pyarrow’}
    filesystem = None, # fsspec or pyarrow filesystem, default None
    filters = None, # List[Tuple] or List[List[Tuple]], default None
    to_pandas_kwargs = None, # dict | None, default None
    **kwargs
) → DataFrame
"""
ファイルパスからParquetオブジェクトを読み込み、DataFrameを返す。

この関数は、Parquetファイルからのデータ読み込みを自動的に処理し、適切な構造を持つDataFrameを作成する。
"""

polars.read_parquet

polars.read_parquet(
    source: FileSource,
    *,
    columns: list[int] | list[str] | None = None,
    n_rows: int | None = None,
    row_index_name: str | None = None,
    row_index_offset: int = 0,
    parallel: ParallelStrategy = 'auto',
    use_statistics: bool = True,
    hive_partitioning: bool | None = None,
    glob: bool = True,
    schema: SchemaDict | None = None,
    hive_schema: SchemaDict | None = None,
    try_parse_hive_dates: bool = True,
    rechunk: bool = False,
    low_memory: bool = False,
    storage_options: StorageOptionsDict | None = None,
    credential_provider: CredentialProviderFunction | Literal['auto'] | None = 'auto',
    retries: int | None = None,
    use_pyarrow: bool = False,
    pyarrow_options: dict[str, Any] | None = None,
    memory_map: bool = True,
    include_file_paths: str | None = None,
    missing_columns: Literal['insert', 'raise'] = 'raise',
    allow_missing_columns: bool | None = None,
) → DataFrame

"""
Parquetファイルを読み込み, データフレームを返す。
"""

polars.DataFrame.write_parquet

DataFrame.write_parquet(
    file: str | Path | IO[bytes],
    *,
    compression: ParquetCompression = 'zstd',
    compression_level: int | None = None,
    statistics: bool | str | dict[str, bool] = True,
    row_group_size: int | None = None,
    data_page_size: int | None = None,
    use_pyarrow: bool = False,
    pyarrow_options: dict[str, Any] | None = None,
    partition_by: str | Sequence[str] | None = None,
    partition_chunk_size_bytes: int = 4294967296,
    storage_options: StorageOptionsDict | None = None,
    credential_provider: CredentialProviderFunction | Literal['auto'] | None = 'auto',
    retries: int | None = None,
    metadata: ParquetMetadata | None = None,
    arrow_schema: ArrowSchemaExportable | None = None,
    mkdir: bool = False,
)
"""
Parquetファイルへ書き出す。
"""

おわりに

pandasからpolarsにすることで前処理のサイクルのスピードが速くなるだけでなく, Amazon SageMakerなどのクラウドコンピューティングサービスを使っている人なら, 利用するインスタンスのグレードを下げて料金を節約できる可能性もあります。

実用性が高いだけでなく非常に楽しいライブラリなので, ぜひこの機会に移行を検討してみてください。

参考文献

↑ (最終更新日: 2023/11/06) polarsとpandasのコードだけでなく基本的な仕様の違いにも触れながら, polarsに乗り換えることによるメリットを理解できる記事。他の記事へのリンクも非常に充実しています。また, 文章が非常に読みやすいです。

↑ (最終更新日: 2024/08/06) 非常に丁寧に, 網羅的にpolarsで利用できる機能を紹介した記事。大量のコード例が載っているので, 自分の行いたい処理を
実現しているコード例に出会えるはず。

↑ (最終更新日: 2023/05/19) 簡潔にpandasとpolarsの関数の対応を確認することができます。

↑ (最終更新日: 2024/12/25) 1.6GBのKaggleデータを使って, pandasとpolarsのコードの書き方および処理速度を比較した非常に実践的な記事。

↑ (最終更新日: 2023/12/12) 列や行に対しての操作が分かりやすく簡潔に説明されています。

↑ (最終更新日: 2023/12/25) polars特有のエクスプレッション (pl.Expr) について詳細に解説した記事。

↑ (最終更新日: 2024/10/22) 「他のデータに含まれないレコードを抽出したい」「データフレームごとに連番を振りたい」など, タイトルの通りやりたいことから逆引きでpolarsの関数の説明をしている記事。かなり網羅的に説明していて, 大量のpolarsのコード例を学ぶことができます。また, コード内のコメントも非常に丁寧に書かれていて, コードを追っていて迷子になりません。使用しているデータもかなり実務データに近いです。また, 日付データの扱い方も説明されている素晴らしい記事です。


いいなと思ったら応援しよう!