Technical Notes: py4stats.eda_tools における narwhals ベースの実装

概要

py4stats.eda_tools モジュールは、複数の DataFrame バックエンドに対して共通の API を提供することを目的として、narwhals ライブラリを用いて実装されています。

本ドキュメントでは、本モジュールの内部実装に関する前提条件や、バックエンドの違いに起因する挙動上の注意点について説明します。

通常の利用にあたって本ドキュメントを読む必要はありませんが、実装の詳細や挙動の違いが気になる場合には参考にしてください。

対応している DataFrame バックエンドについて

 py4stats.eda_tools モジュールの関数は、第一引数として narwhals.from_native() によって nw.DataFrame 型へ変換可能な DataFrame オブジェクトを受け取ります。

具体的には、以下のようなバックエンドを想定しています。

  • pandas.DataFrame(主に動作検証を行っているバックエンド)
  • polars.DataFrame(簡易的な動作確認のみ)
  • pyarrow.Table(簡易的な動作確認のみ)

本ライブラリの動作確認は、基本的に pandas.DataFrame を用いて実施しています。そのため、polarspyarrow を使用した場合には、バックエンド固有の仕様差や未検証の挙動により、一部の関数でエラーが発生する可能性があります。そのような挙動が確認された場合は、Issue 等での報告を歓迎します。

 また、バックエンド別の実装状況については eda_tools開発状況 も参照して下さい。

narwhals を用いた関数の返り値の型について

py4stats.eda_tools モジュールの関数のうち、py4stats.diagnose() など、第一引数にデータフレームを取る関数の返り値の型は、to_native 引数の値によって変化します。
 初期設定である to_nativ = True の場合には、第一引数に入力されたデータフレームと同じ型のデータフレームが出力され、to_nativ = False の場合には narwhals.DataFrame 型のデータフレームが出力されます。to_nativ = False のオプションは、主にライブラリ内部での利用や、データフレームのバックエンドに依存しない後続処理を行いたい場合を想定したオプションです。

import py4stats as py4st
import pandas as pd
import polars as pl
import pyarrow as pa
import wooldridge
mroz_pd = wooldridge.data('mroz')       # pd.DataFrame
mroz_pl = pl.from_pandas(mroz_pd)       # pl.DataFrame
mroz_pa = pa.Table.from_pandas(mroz_pd) # pyarrow.lib.Table
# to_nativ = True の場合(初期設定): 入力されたデータフレームと同じ型

print(type(py4st.diagnose(mroz_pd, to_native = True)))
#> <class 'pandas.core.frame.DataFrame'>

print(type(py4st.diagnose(mroz_pl, to_native = True)))
#> <class 'polars.dataframe.frame.DataFrame'>

print(type(py4st.diagnose(mroz_pa, to_native = True)))
#> <class 'pyarrow.lib.Table'>
# to_nativ = False の場合: narwhals.DataFrame 型

print(type(py4st.diagnose(mroz_pd, to_native = False)))
#> <class 'narwhals.dataframe.DataFrame'>

print(type(py4st.diagnose(mroz_pl, to_native = False)))
#> <class 'narwhals.dataframe.DataFrame'>

print(type(py4st.diagnose(mroz_pa, to_native = False)))
#> <class 'narwhals.dataframe.DataFrame'>

narwhals を用いた実装方針について

 内部実装では、関数の冒頭で

nw.from_native(data)

を用いて入力データを nw.DataFrame に変換し、以降の処理を narwhals の抽象 API 上で行っています。

この設計により、DataFrame バックエンドごとの差異を最小限に抑えつつ、将来的な拡張性を確保することを目的としています。

一方で、narwhals は各バックエンドの完全な互換性を保証するものではないため、特定の操作や型変換についてはバックエンドごとに挙動が異なる場合があります。

pandas_flavor を用いた DataFrame メソッドの登録について

py4stats.eda_tools の関数のうち、単一の DataFrame オブジェクトを引数として受け取る関数については、pandas_flavor.register_dataframe_method を用いて DataFrame メソッドとして登録されています。その結果、以下のような使い方が可能です。

df.diagnose()

ただし、pandas_flavor は pandas の拡張を前提とした仕組みであるため、このメソッド形式の呼び出しは、pandas.DataFrame を対象としています。  polars.DataFrame や pyarrow ベースのオブジェクトを使用する場合には、関数として直接呼び出す形での利用を推奨します。

import py4stats as py4st

py4st.diagnose(df)

今後について

 py4stats.eda_tools モジュールは、今後も narwhals ベースの実装を主軸として改良・拡張を行っていく予定です。一方で、従来の pandas ベースの実装については、互換性のために当面は保持される予定ですが、機能追加は行わない予定です。バックエンドごとの挙動差や制限事項については、必要に応じて本ドキュメントを更新していきます。