やってみたこと
PythonライブラリのPickleを用いて、ビッグデータを扱うプログラミングを快適に進める方法を考えてみました。
ビッグデータを扱うPythonプログラムでは、データの取得や前処理に時間がかかり、開発効率を低下しがちです。Pickleを活用すると、プログラムで生成したデータを効率的に一時保存し、後続の処理に再利用できます。
これにより、デバッグや開発の生産性を向上させることができます。
Pickleを使ったデータのファイル保管 → 復元の流れ
以下の感じで、プログラムで生成されたデータが簡単にファイルに出力できます。
import pickle
data = [1, 2, 3, 4, 5] #リスト型(整数)
with open("./stored_data.pkl", "wb") as f:
pickle.dump((data), f)
また、Pickle形式で保管されたデータを復元して、別のプログラムで使用するときは以下のとおり。
with open("./stored_data.pkl", "rb") as f:
loaded_data = pickle.load(f)
print(loaded_data)
ファイルへの書き込み(dump)で保存、ファイルの読み取り(load)で復元、という感じ。とにかくコードが短いので、これなら何回か書くうちに自然と覚えられそうです。
Pickleの便利な点
「プログラムで生成したデータをファイル保管する」点で、用途はjsonやcsvと同様です。しかし、Pickle形式でのデータ保管には以下のメリットがあります。
list型、辞書型、タプル型など、複雑なデータ構造もOK
要はPythonオブジェクトであれば、クラスから生成したインスタンスや、関数などなんでも保管できるということです。
複雑な変換作業がない
データ型も保持してくれ、ファイル読み込み時に改めてデータ型を再度指定する必要もないため、圧倒的にシンプルです。
軽くて早い
Python特化の技術であるため、ほかのプログラミング言語をサポートしていないものの、csvやjsonよりも圧倒的にファイルサイズが小さく、また読み書きも高速なようです。ビッグデータを扱う作業だと、この点はたしかに助かります。
pythonオブジェクトならなんでも、いくつでも保管可能
また(やりたければ)、複数のPythonオブジェクトをひとつのPickleファイルにまとめて保管(梱包?)することもできます。
たとえばタプル型で、複数のPythonオブジェクトを保管する場合であれば以下の感じ。
import pickle
var_data = 123 #変数
list_data = [10, 20, 30, 40, 50] #リスト型
dict_data = {"name": "Charlie", "age": 35, "city": "Osaka"} #辞書型
set_data = {100, 200, 300, 400, 500} # セット型
def func(name): #関数
return f"Hello, {name}!"
file_path = "./five_objects.pkl"
with open(file_path, "wb") as f:
pickle.dump((var_data,list_data, dict_data, set_data,func), f) # タプルにまとめて保存
中身を取り出す際も、普段通りpickle.loadでOK。
with open(file_path, "rb") as f:
var_data,loaded_list, loaded_dict, loaded_set,func = pickle.load(f)
#結果確認
print(var_data)
print(loaded_list)
print(loaded_dict)
print(loaded_set)
print(func("World!"))
※関数をあえてpickle形式で保管するのは、役に立つ場面はほとんどない気がします。しかし、技術的にはこんなもの(関数)まで保管可能とは驚きです。
どんなとき役に立つのか
データ取得・前処理に時間がかかるプログラムを毎回実行し直すのが面倒なとき。
Pythonでデータ分析基盤を構築する際、最初のステップで、データを収集・取得をしなければならない場合がよくあります。この際、とくに、
・大量のファイルの読み込み
・大量のHTTPリクエストの実施(Webスクレイピングなど)
などは時間がかかりやすく、時間を浪費しがちです。また、データの件数の増加につれて、プログラムの実行時間も長くなるので、ビッグデータの実務で開発効率・生産性の低下は深刻になりやすいです。
「後続のソースコードを一行デバッグしただけなのに、前段までのデータ取得に一時間かかるので、結果の確認に一時間かかる」といった状態では、作業が停滞しがちです。
こうした軽微なコード修正ではPickleを活用し、前段のデータ取得の結果を用意することで、作業が円滑に進みます。たとえば、特定のフォルダ階層のエクセルファイルの一覧を作成し、Pickle形式で保管する流れは以下のとおり。
from pathlib import Path
import pickle
#エクセルファイルの帳票一覧データソース(ここにパスを指定する)
EXCEL_DATA_SOURCE = "./data_source"
#フォルダ内の全ファイルのパス一覧取得
all_file_paths = [str(path) for path in Path(EXCEL_DATA_SOURCE).rglob('*') if path.is_file()]
#うち、エクセルファイルに限定
paths_xl = [path for path in all_file_paths if Path(path).suffix in [".xlsx", ".xls"]]
#パスの一覧をPickleファイル化
with open("./xl_path_list.pkl", "wb") as f:
pickle.dump((paths_xl), f)
「対象ファイルの特定はすでに終わったもの」という前提で、そのデータの加工・変換・集計のプログラム作成に取り組めるため、目の前の作業に集中しやすくなるのです。
また、上記プログラムには含めていませんが、エクセルファイルの解析結果をクラス変数に格納するところまで一気にやってしまい、そこまでをpickleにまとめるのも良さそうです。これなら多数のファイルアクセスを何度も繰り返さずに済み、さらなる時間短縮・効率化につながりそうです。
データを共有しながらのチーム開発を効率化したいとき
一例として、
- データ取り込み
- エラーチェック
- 前処理
- データ集計
- レポート・ダッシュボードの作成
といった一連のデータパイプラインをPythonで実装する場合に、一旦はダッシュボード仮作成を進める場合を考えてみます。
この場合の進め方として、以下のような役割分担は十分あり得る気がします。
- 前処理(3)まで : データエンジニアの担当。暫定での前処理の結果をPickle形式にし、データを提供
- 前処理(3)の後 : データアナリストの担当。前処理の暫定結果を受領し、データ集計・ダッシュボード作成に着手
全工程の動作確認を後回しにして、一旦1~5の各ステップをひとつずつ完成させていくイメージだと思います。
- 1~5の各ステップを、ひとつずつ作成 → それぞれ単体テスト
- 各ステップが完成したら、全体を通しで実行 → 結合テスト
という感じで、チームとしてのスケジュール管理も簡単になりそうです。
Pickleの注意点
Python以外での使用はできない
Pickle形式のデータは、Pythonに特化しており、別のプログラミング言語での使用はできないようです(2025年1月時点)。
悪意のあるデータが埋め込まれる可能性に注意
Pickleの公式ドキュメントで、信頼できない提供元からPickle形式のデータを受け取るリスクが警告されています。
警告 pickle モジュールは 安全ではありません 。信頼できるデータのみを非 pickle 化してください。
非 pickle 化の過程で任意のコードを実行する ような、悪意ある pickle オブジェクトを生成することが可能です。信頼できない提供元からのデータや、改竄された可能性のあるデータの非 pickle 化は絶対に行わないでください。データが改竄されていないことを保証したい場合は、 hmac による鍵付きハッシュ化を検討してください。
信頼できないデータを処理する場合 json のようなより安全な直列化形式の方が適切でしょう。
公式リファレンス
技術的仕様の詳細は、公式リファレンスで確認してください。
英語:https://docs.python.org/3/library/pickle.html
日本語:https://docs.python.org/ja/3.13/library/pickle.html
まとめ
「シンプルなコードが書けて、すぐに実行結果を試せる」点はPythonの魅力であるものの、データ量の増加に備えることも大事だと思います。
プログラムの実行時間の短縮・分業体制づくりの促進など、Pickleを用いた開発チームの生産性改善の可能性は、まだまだいろいろ眠っていそうな気がします。
たとえばpandasのデータフレームをpickle形式で、テーブルの代用(?)にするといった開発手法も試してみたいです。
筆者からひと事
・pickle形式で関数や、クラス(インスタンスではない)といったオブジェクトを保管することが適したユースケースを思いついた人がいたら、教えてください。