自動化プロジェクトのデータパイプラインで、長らくCSVを中間形式として使っていました。しかし、411カラム x 10データソース x 8集計期間という規模になると、CSVの限界が顕著になりました。
- 型の曖昧さ: 数値カラムが文字列として読み込まれる
- 読み込み速度: 全カラムを毎回パースする無駄
- ファイルサイズ: 圧縮なしのテキストで数GB
本記事では、CSVからParquetへの移行で得られた具体的な改善と、移行時に踏んだ落とし穴を記録します。
CSVの何が問題だったか
型の消失
CSVはプレーンテキストなので、全ての値が文字列として保存されます。
1import pandas as pd2
3# CSVに書き出し4df = pd.DataFrame({"value": [145.50, 146.20], "count": [1000, 2000]})5df.to_csv("data.csv", index=False)6
7# CSVから読み込み → 型が変わることがある8df_loaded = pd.read_csv("data.csv")9print(df_loaded.dtypes)10# value float64 ← たまたま正しい11# count int64 ← たまたま正しい12
13# しかし、欠損値があると...14df2 = pd.DataFrame({"value": [145.50, None], "count": [1000, None]})15df2.to_csv("data2.csv", index=False)4 collapsed lines
16df2_loaded = pd.read_csv("data2.csv")17print(df2_loaded.dtypes)18# value float6419# count float64 ← intがfloatに変わった411カラムのうち、どのカラムの型が変わったかを追跡するのは現実的ではありません。
全カラム読み込みの無駄
分析パイプラインでは「411カラムのうち10カラムだけ使う」ケースが大半です。CSVでは全カラムをパースしてからフィルタします。
1# CSV: 411カラム全部読んでからフィルタ2df = pd.read_csv("analysis.csv") # 全カラムパース(遅い)3df = df[["timestamp", "metric_a", "metric_b"]] # 使うのは3カラムだけParquetを選んだ4つの理由
1. 列指向フォーマット
Parquetは列指向(columnar)フォーマットです。必要な列だけを物理的に読み込めます。
1import pandas as pd2
3# Parquet: 必要な列だけ読み込み(高速)4df = pd.read_parquet(5 "analysis_results.parquet",6 columns=["timestamp", "metric_a", "metric_b"]7)411カラムから10カラムだけ読む場合、理論上は約40分の1のI/Oで済みます。実測でもCSVの3〜5倍高速でした。
2. スキーマ保存
型情報がファイル自体に埋め込まれます。「数値が文字列に化ける」問題が構造的に発生しません。
1import pyarrow.parquet as pq2
3# 書き込み(型情報付き)4df.to_parquet("analysis_results.parquet", engine="pyarrow")5
6# スキーマ確認7schema = pq.read_schema("analysis_results.parquet")8print(schema)9# timestamp: double10# metric_a: double11# metric_b: double12# result_label: string13# ... 型情報が保存されているスキーマがファイルに埋め込まれているため、読み込み側でdtypeを指定する必要がありません。パイプラインの下流で「この列は本当にfloatか?」と心配しなくて済みます。
3. 圧縮効率
Parquetはsnappy圧縮をデフォルトで使用します。テキストベースのCSVと比較して、1/5〜1/10のファイルサイズになります。
1import os2
3# 同じデータをCSVとParquetで保存して比較4df.to_csv("data.csv", index=False)5df.to_parquet("data.parquet", engine="pyarrow", compression="snappy")6
7csv_size = os.path.getsize("data.csv")8parquet_size = os.path.getsize("data.parquet")9print(f"CSV: {csv_size / 1024 / 1024:.1f} MB")10print(f"Parquet: {parquet_size / 1024 / 1024:.1f} MB")11print(f"圧縮率: {parquet_size / csv_size:.1%}")12# 実測: CSV 230MB → Parquet 28MB(約12%)4. pandas互換
移行コストが極めて低いです。to_csv()をto_parquet()に変えるだけで基本的に動きます。
1# Before2df.to_csv("output.csv", index=False)3df = pd.read_csv("output.csv")4
5# After6df.to_parquet("output.parquet", engine="pyarrow")7df = pd.read_parquet("output.parquet")実データの規模感
本番システムで扱うデータの規模は以下の通りです。
1411カラム(分析指標)2 x 10データソース3 x 8集計期間(1H, 2H, 4H, 6H, 8H, 12H, D, W)4 = 約32,880カラム相当のデータ量このスケールでCSVを使い続けることは、I/O・型安全性・ストレージの全ての面で非合理でした。
BIツール対応:Version 8.4の改善
Parquetへの移行に合わせて、BIツール(Metabase等)との連携を考慮したカラム設計も行いました。
ReferenceDate列の追加
1from datetime import datetime2
3
4def add_reference_columns(df: pd.DataFrame, source: str, period: str) -> pd.DataFrame:5 """BIツール連携用のメタデータカラムを追加する。"""6 df = df.copy()7 df["ReferenceDate"] = datetime.now().strftime("%Y-%m-%d")8 df["Source"] = source9 df["Period"] = period10 return df複合IDカラム
各レコードを一意に識別するための複合IDを追加しました。
1from datetime import datetime2
3
4def generate_composite_id(5 reference_date: str,6 source: str,7 period: str,8 sequence: int,9) -> str:10 """複合IDを生成する。11
12 形式: {YYYYMMDD}_{Source}_{Period}_{YYYYMMDDHHMM}_{00001-99999}13 例: 20260327_SRC001_4H_202603271500_0000114 """15 now = datetime.now().strftime("%Y%m%d%H%M")1 collapsed line
16 return f"{reference_date}_{source}_{period}_{now}_{sequence:05d}"落とし穴:ファイル命名規則変更によるインシデント
Parquet移行時に最大の問題を引き起こしたのは、ファイル命名規則の変更でした。
何が起きたか
1Before: analysis_SRC001_4H_20260301.csv2After: analysis_results_SRC001_4H_20260301.parquet拡張子だけでなく、ファイル名のプレフィックスも変更しました。その結果、下流パイプラインの20以上のスクリプトでファイルが見つからなくなりました。
対策:IMPACT_ANALYSISの必須化
この経験から、データパイプラインのファイル形式変更には必ずIMPACT_ANALYSISを実施するルールを追加しました。
1## IMPACT_ANALYSIS 必須チェック項目(データ形式変更時)2
31. このファイルを読み込んでいるスクリプトの一覧42. ファイル名パターンをハードコードしている箇所53. 拡張子でフィルタしている箇所(*.csv → *.parquet)64. バックアップスクリプトの対象パターン75. CI/CDパイプラインの成果物パスファイル命名規則の変更は「コード変更」ではなく「インフラ変更」として扱うべきです。
学んだこと
1. CSVからParquetへの移行は型安全性の向上が最大の利点
パフォーマンスや圧縮率も重要ですが、最大の利点は「型が勝手に変わらない」安心感です。411カラムの型を毎回確認する必要がなくなりました。
2. 列指向は分析パイプラインに最適
「全行を読むが、一部の列だけ使う」という分析パイプラインの典型的なアクセスパターンに、列指向フォーマットは理想的にマッチします。
3. ファイル命名規則はIMPACT_ANALYSISの対象
データ形式の変更は、コードの変更よりも広範囲に影響します。ファイル名・パス・拡張子の変更は、必ず下流の全スクリプトを洗い出してから実施すべきです。
まとめ
ParquetをCSVの代替として採用する判断で重要なのは以下の3点です。
- 型安全性が最大の利点: スキーマがファイルに埋め込まれ、読み込み時の型変化が構造的に防がれます
- 列指向の読み込み効率: 411カラム中10カラムだけ読む場合、CSVの3〜5倍高速です
- 移行時はファイル命名規則に注意: 拡張子やプレフィックスの変更は下流パイプライン全体に影響します。IMPACT_ANALYSISを必ず実施してください