Pythonでデータの集計や加工に広く使われているライブラリのpandasは、2026年1月21日にバージョン3.0を公開しました。2.xの系列からのメジャーバージョンの更新で、公式の変更点の説明によると、以前のバージョンで非推奨とされていた機能の多くが削除されています。

本記事では、変更点のうち既存のコードへの影響が大きいものを整理したうえで、分析のコードやデータのパイプラインを運用している企業の実務への影響を説明します。内容は2026年2月時点の公式の資料に基づきます。

主な変更点

公式の発表では、最も大きな変更として、文字列の型と、データのコピーの扱いの2点が挙げられています。主な変更点は次のとおりです。

  • 文字列の専用の型文字列の列は、これまでの object 型ではなく、文字列の専用の型(str)として扱われます。この型には文字列と欠損値しか入れられず、文字列以外の値を代入するとエラーになります。欠損値は、None を入れても常に NaN として扱われます。
  • Copy-on-Write の既定化データの抽出や加工の結果は、常に元のデータのコピーとして振る舞います。元のデータを変更するには、そのデータを直接変更する必要があります。これに伴い、従来の SettingWithCopyWarning は廃止されました。
  • 日時の精度文字列やPythonの datetime から変換した日時は、これまでのナノ秒ではなく、原則としてマイクロ秒の精度で保持されます。
  • 非推奨の機能の削除月末を表す "M" などの期間の別名("ME" などに置き換え)をはじめ、以前のバージョンで非推奨とされていた機能が削除されました。

あわせて、動作に必要なPythonのバージョンは3.11以上になりました。

実務への影響

処理が止まらないまま結果が変わる箇所

移行で最も注意が必要なのは、エラーで処理が止まる箇所ではなく、処理が止まらないまま結果が変わる箇所です。削除された機能を使っている箇所はエラーになるため、実行すれば見つかります。一方で、次のような箇所は、実行しても処理が止まらないため、出力を確認しなければ気づけません。

抽出を重ねた代入

抽出を重ねて値を代入する書き方では、3.0では元のデータが更新されません。警告(ChainedAssignmentError)は出ますが、処理は続きます。移行の手引きでは、1回の操作で代入する書き方に改めるよう示されています。

変更前:3.0では df が更新されない
df["price"][df["category"] == "A"] = 0
変更後:2.xでも3.0でも df が更新される
df.loc[df["category"] == "A", "price"] = 0

日時の整数への変換

日時がマイクロ秒の精度で保持される場合、日時を整数に変換した値は、従来の1,000分の1になります。公式の変更点の説明でも、この点が注意事項として挙げられています。経過時間の計算や、日時を整数として保存する処理で問題になります。

変更前:3.0では整数の値が1,000分の1になる
s = pd.to_datetime(pd.Series(["2026-02-01 09:00:00", "2026-02-01 09:30:00"]))
s.dtype              # 2.x: datetime64[ns] 3.0: datetime64[us]
s.astype("int64")    # 2.x はナノ秒、3.0 はマイクロ秒の単位の整数になる
変更後:単位を明示する
s.dt.as_unit("ns").astype("int64")        # 整数にする前に単位を揃える
(s - s.iloc[0]).dt.total_seconds()        # 経過時間は単位に依存しない方法で計算する

列の型による判定

列の型が object かどうかで文字列の列を判定している処理は、3.0では文字列の列を判定の対象から外してしまいます。移行の手引きでは、どちらのバージョンでも文字列の列を判定できる関数が示されています。

変更前:3.0では文字列の列が含まれない
str_cols = [c for c in df.columns if df[c].dtype == "object"]
変更後:2.xでも3.0でも文字列の列を判定できる
str_cols = [c for c in df.columns if pd.api.types.is_string_dtype(df[c].dtype)]

欠損値の判定

文字列の列では、欠損値として None を入れても NaN として扱われます。欠損値が None であることを前提にした比較は成り立たなくなるため、移行の手引きでは pd.isna() で判定するよう示されています。

変更前:3.0では False になる
s = pd.Series(["a", None, "c"])
s[1] is None         # 2.x: True 3.0: False
変更後:2.xでも3.0でも True になる
pd.isna(s[1])

移行の手順

公式の資料では、3.0に移行する前に、まずバージョン2.3に更新し、警告が出ない状態にすることが推奨されています。2.3では、非推奨の機能を使っている箇所に警告が出るほか、新しい文字列の型を先に有効にして動作を確認できます。当社では、移行を次の手順で進めることをお勧めしています。

  • 使用しているpandasのバージョンと、pandasを内部で使っているライブラリが3.0に対応しているかを確認する
  • 2.3に更新して警告を解消し、新しい文字列の型を有効にした状態で動作を確認する
  • 3.0に更新し、移行の前後で、同じ入力に対する集計の結果や出力するファイルを比較する
  • 定期的に実行している処理では、使用するライブラリのバージョンを固定し、更新は上記の手順を経てから行う

2.3で新しい文字列の型を先に有効にするには、次の設定を使います。

2.3で新しい文字列の型を先に有効にする
import pandas as pd
pd.options.future.infer_string = True

ライブラリのバージョンの管理

今回の更新が示しているのは、同じコードでも、実行する環境のライブラリのバージョンによって結果が変わりうるということです。分析の担当者の手元の環境と、定期的に処理を実行する環境とで、pandasのバージョンが異なれば、同じコードから異なる集計の結果が出るおそれがあります。

そのため、プロジェクトごとにPythonの仮想環境を分け、使用するライブラリのバージョンを、間接的に依存するライブラリまで含めて固定する管理が、これまで以上に重要になります。uvやpipenvなどのツールは、実際に導入したバージョンをロックファイル(uvでは uv.lock、pipenvでは Pipfile.lock)に記録し、別の環境でも同じバージョンを再現できます。

uv でバージョンを固定する例
uv add "pandas>=2.3,<3"   # 許容する範囲を pyproject.toml に、実際の版を uv.lock に記録する
uv sync --locked          # uv.lock のとおりに環境を作る(uv.lock が古ければ止まる)

ロックファイルをコードとあわせて版管理しておけば、ライブラリの更新は、環境を作り直したときに意図せず起きるものではなく、上記の移行の手順を経て意図して行う作業になります。