「大量データを扱う」という前提がなかった

この話は、 pandasの使い方を最初から勉強して データ処理を設計した、 という話ではありません。

もともとあった移行ツールを 引き継いだところから始まりました。

そのツールは、 もともと 1件のデータを投入する ことを前提に作られていました。

ところが、 実際の移行では大量のデータを扱う必要がありました。

実際に分かったこと

大量データをそのまま処理すると、 約2年半かかる。

ここで初めて、 「この処理を大量データ向けに作り直さないといけない」 という問題がはっきりしました。

まず、データをまとめて扱う

大量データを扱う場合、 1件ずつ処理するだけでは 全体を把握しづらくなります。

そこで、 CSVなどから読み込んだデータを DataFrame として扱いました。

import pandas as pd


df = pd.read_csv("data.csv")

DataFrameを使うことで、 行と列を持ったデータを 一つのまとまりとして扱えます。

例えば、

ID    Longitude    Latitude


1     139.7000     35.6000
2     139.7100     35.6100
3     139.7200     35.6200

のようなデータを、 Pythonの中で表形式のデータとして扱えます。

「1件ずつ処理する」から、 「大量のデータを一つの集合として扱う」 という考え方に変えていきました。

pandasを使えば速くなる、という話ではない

ここは重要なところです。

pandasを使ったから、 それだけで処理が劇的に高速化した、 という話ではありません。

大量データを扱う上で、

読み込み CSV → DataFrame
加工 データの抽出・変換
分割 処理単位に分ける
並列処理 複数プロセスへ渡す

というように、 データ処理全体を分解して考える必要がありました。

pandasは、 その中で 大量データを扱うための土台 として使いました。

DataFrameをどうやって処理単位に分けるか

大量データを一つのDataFrameとして持っただけでは、 並列処理にはなりません。

そこで、 データをいくつかの処理単位に分割し、 それぞれを別の処理へ渡すという考え方にしました。

処理のイメージ

大量データ



DataFrame



処理単位に分割



複数の処理へ渡す

この考え方が、 後の ProcessPoolExecutor による並列処理につながっていきます。

つまり、 pandasを使うこと自体が目的ではなく、

大量データを「処理しやすい単位」に 扱えるようにする。

ということが重要でした。

データ処理を細かく見る

大量データを処理する場合、 「Pythonの処理」と一言でまとめてしまうと、 どこが遅いのか分かりません。

そこで、 処理をいくつかに分けて考えました。

STEP 1 データ読み込み
STEP 2 データ加工
STEP 3 座標などの変換
STEP 4 GISへの投入

こうして分けて見ることで、 「どこを改善すれば全体が速くなるのか」 を考えやすくなりました。

実際の性能改善では、 一番時間がかかっている場所を見つけ、 そこに対して改修を行うことが重要でした。

CSVの読み込みも無視できない

大量データを扱うようになると、 データを加工する処理だけではなく、 データを読み込む時間 も無視できなくなります。

そこで、 CSVの読み込みについても調べました。

df = pd.read_csv(
"data.csv"


)

さらに、 pandasのCSV読み込みについて PyArrow を利用する方法も調査しました。

df = pd.read_csv(
"data.csv",
engine="pyarrow"


)

大量データを扱う場合、 「計算処理を高速化すれば終わり」 ではありません。

読み込み、
加工、
分割、
並列処理、
出力・投入。

全体のどこで時間を使っているのかを 見る必要があります。

大量データでは「メモリ」も問題になる

データを大量に扱う場合、 CPUだけを見ていても問題は解決しません。

DataFrameに大量のデータを載せれば、 当然メモリも使用します。

さらに、 データを複数の処理に分割して 並列処理を行う場合、 それぞれの処理がメモリを使用します。

注意するポイント

CPUをもっと使いたい

並列数を増やす

メモリ使用量も増える可能性がある

そのため、 「CPU使用率が低いから、 とにかく並列数を増やそう」 という単純な話ではありません。

CPU・メモリ・I/Oをまとめて見る。

この考え方は、 後にLinuxの vmstattop を使った性能確認にもつながりました。

pandasから並列処理へ

DataFrameで大量データを扱えるようになると、 次に考えたのが、

「このデータをどうやって複数の処理に分けるか」

ということでした。

そこで、 Python標準ライブラリの concurrent.futures を使った並列処理を検討しました。

from concurrent.futures import ProcessPoolExecutor

DataFrameを処理単位に分割し、 複数のプロセスで処理することで、 CPUをより有効に使える可能性があります。

ここから先は、 pandasそのものというより、 「大量データをどう並列処理するか」 という話になります。

詳しい部分は、 別の記事で整理します。

次の記事

Pythonで大量データを並列処理する

concurrent.futures / ProcessPoolExecutor

最初から「pandasを使おう」と決めていたわけではない

ここも、 この案件を振り返る上では重要なところです。

最初から、

pandasを使って、 DataFrameにして、 ProcessPoolExecutorで並列処理しよう。

と計画していたわけではありません。

そもそも、 仕様書も十分になく、 知見者もいない状態でした。

大量データを流してみた。

遅かった。

何が遅いのか調べた。

データ処理を見直した。

pandasを使ってデータを扱った。

さらに処理を分割した。

並列処理を試した。

そして、 CPUやメモリの状態も確認した。

必要になったものを、 その都度調べて使っていった。

「pandasを使った」よりも重要だったこと

この案件を、 「pandasを使って大量データを処理した」 とだけ説明すると、 かなり簡単な話に見えます。

でも、 実際にはそうではありませんでした。

最初は、 何が問題なのかすら分かっていませんでした。

だから、

実際にやったこと

データを流す

処理時間を見る

コードを調べる

データの扱い方を変える

テストする

結果を見る

また問題を見つける

また直す

その繰り返しでした。

技術を使ったというより、 問題を見つけるたびに 必要な技術を覚えていった。

そして、 その結果として pandas、 並列処理、 PyArrow、 Linuxのリソース確認などが 一つの処理の中につながっていきました。

大量データ処理で学んだこと

この案件で、 pandasは大量データを扱うための 重要な道具になりました。

pandas 大量データをDataFrameで扱う
DataFrame データをまとまりとして処理する
PyArrow CSV読み込みの高速化を検討する
ProcessPoolExecutor 処理を複数プロセスへ分ける
Linux CPU・メモリ・I/Oを確認する

ただし、 これらを最初から全部知っていたわけではありません。

2年半という数字を見て、 「このままでは終わらない」と思った。

そこから、 データ処理を見直し、 pandasを使い、 処理を分割し、 並列化し、 CPUやメモリを確認していきました。

最終的には、 移行処理にリソースを集中できる場合で 約2週間、 運用と並行する場合でも 約2か月で移行できる見込みまで 持っていくことができました。

この案件でのポイント

pandasを使ったことではなく、

大量データをどう分解し、 どう処理し、 どこがボトルネックなのかを 見るようになったこと。

そして、 その先にあったのが Pythonの並列処理でした。