Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

pandasの最新バージョン、pandas 2.0.0を紹介します

こんにちはPyQサポートです。

2023年4月3日にpandas 2.0.0がリリースされました。
約3年ぶりのメジャーバージョンアップであり、新機能や既存APIの変更、パフォーマンスの改善など、多くの変更がありました。

本記事ではカテゴリ別に、以下の変更点をピックアップして紹介します。

  • 新機能:IndexがNumPyの数値型に対応
  • APIの変更:value_counts() の仕様変更
  • パフォーマンスの改善:isna() の速度向上

pandas 2.0.0についてより詳しく知りたい方は、pandas 公式ドキュメントの pandas 2.0.0のリリースノート をお読みください。

pandas 2.0.0のリリースの概要

名称pandas
リリースバージョンpandas 2.0.0
リリース日2023年4月3日
サポートしているPython3.8、3.9、3.10、3.11
リリースノートhttps://pandas.pydata.org/docs/dev/whatsnew/v2.0.0.html

新機能:IndexがNumPyの数値型に対応

pandas 2.0.0では、処理速度の向上やメモリ使用量の改善など、特にパフォーマンス面に関する新機能がいくつかありました。
その中から、インデックス( Index )に関する変更点をピックアップして紹介します。

従来のpandasでは、インデックスに指定可能なNumPyの型は一部(int64、uint64、float64)だけでした。

pandas 2.0.0ではあらゆるNumPyの数値型が指定可能になり、 これまでに使えなかった型(int8、int32、uint8、uint16、float32など)も使える ようになりました。
これにより、 メモリ使用量を抑えて効率よくデータを扱える ようになりました。

具体的な例を見てみましょう。
次のコードでは、np.int8(符号あり8ビット整数型)を指定してインデックスを生成しています。

import numpy as np
import pandas as pd

# 符号あり8ビット整数型を指定してインデックスを生成
index = pd.Index([1, 2, 3, 4, 5], dtype=np.int8)
print(index)  # 生成したインデックスの確認
print("メモリ使用量:", index.memory_usage(deep=True))  # メモリ使用量の確認

このコードをpandas 1.5.3で実行すると次のような結果になります。
従来のpandasのインデックス(Index)はint8に対応していないため、int64用のインデックス( Int64Index )として生成されていることがわかります。

pandas 1.5.3で実行した結果:int8を指定したのにint64用のインデックスになっている

Int64Index([1, 2, 3, 4, 5], dtype='int64')
メモリ使用量:40

pandas 2.0.0で実行すると、次のような結果になります。
インデックス(Index)があらゆるNumPyの数値型に対応したため、 期待通りint8型のインデックスが生成されている ことがわかります。メモリ使用量も40バイトから5バイトに減りました。

pandas 2.0.0で実行した結果:int8で生成され、メモリ使用量も減っている

Index([1, 2, 3, 4, 5], dtype='int8')
メモリ使用量:5

この他にも、pandas 2.0.0ではPyArrowへの対応やCopy-on-Write機能の改善など、特に処理性能の向上に関する新機能や改善がありました。
詳しく知りたい方は、リリースノートの「機能強化(Enhancements)」の節を参照してください。

APIの変更:value_counts() の仕様変更

pandas 2.0.0では新機能以外に、既存の機能にも変更がありました。
その中から、Seriesの value_counts() の仕様変更をピックアップして紹介します。

Seriesの value_counts() は、Series内の各値の個数をカウントするメソッドです。
pandas 2.0.0では、実行結果として得られるSeriesの名前とインデックス名が次のように変わりました。

pandasのバージョン Seriesの名前 インデックス名
1.5.3以前 元のSeriesの名前
2.0.0 元のSeriesの名前 count (引数でnoramalizeTrueに指定した場合はproportion

これにより、 結果として得られるデータが「個数(count)」を表すことがわかりやすく なりました。

具体的な例を見てみましょう。
下記のコードでは、フルーツの名前が格納されたデータフレームを生成し、value_counts() を使ってどのフルーツが何個あるかカウントしています。

# サンプルデータを作成
df = pd.DataFrame(
    ["apple", "apple", "apple", "banana", "banana", "cherry"],
    columns=["fruits"],
)
# 各値の個数を確認
df["fruits"].value_counts()

このコードをpandas 1.5.3と2.0.0で実行した時の違いを、下図に示します。
pandas 1.5.3で実行すると、結果のSeriesの名前は空で、インデックス名には元の列名の fruits が使われます。
一方で、同じコードをpandas 2.0.0で実行すると、結果のSeriesの名前は fruits になり、インデックス名は count になります。

従来の value_counts() の仕様では、 reset_index() などでインデックスをリセットした時に列名と実際に入っているデータの内容が一致せず、わかりづらくなっていました(下図の左側参照)。
pandas 2.0.0の変更により、実態に合った列名がつくようになり、使い勝手が良くなりました(下図の右側参照)。

value_counts() 以外でも、pandas 2.0.0では既存機能に関する仕様変更がありました。
詳しく知りたい方は、リリースノートの「後方互換性のないAPI変更(Backwards incompatible API changes)」の節を参照してください。

パフォーマンスの改善:isna() の速度向上

最後に、パフォーマンスの改善例について紹介します。
pandas 2.0.0では、既存機能の処理速度の改善が行われました。
その中から、欠損値の判定を行う isna() をピックアップして紹介します。

実際に、pandas 1.5.3と2.0.0で isna()の実行速度の違いを確認してみましょう。
まず、次のように1万行あるデータを作成します。

# 1万行あるデータを作成
df = pd.DataFrame(
    {"A": ["a"] * 10000, "B": ["b", np.nan] * 5000}
)

timeitコマンドを使って、isna() を使った時の速度を比較してみましょう。

%timeit df.isna()

筆者の環境では、それぞれ次のような結果になりました。
pandas 1.5.3が1.77ミリ秒であるのに対し、pandas 2.0.0では474マイクロ秒となっており、3倍以上高速化していることがわかります。

pandas 1.5.3で実行した結果:

1.77 ms ± 9.08 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

pandas 2.0.0 で実行した結果:

474 µs ± 1.44 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)

isna() 以外でも、pandas 2.0.0ではグループ化やマルチインデックスなど、さまざまな機能のパフォーマンスが改善しました。
詳しく知りたい方は、リリースノートの「パフォーマンス改善(Performance improvements)」の節を参照してください。

まとめ

本記事では、pandas 2.0.0の更新内容をピックアップして紹介しました。
特にパフォーマンスの改善は、大規模データセットが扱いやすくなって嬉しい改善ですね。
ぜひ、実際に試してみてください。

なおPyQでは、今後コンテンツをpandas 2.0.0に対応させる予定です。
楽しみにしてお待ちください。

Copyright ©2017- BeProud Inc. All rights reserved.