
CSV/JSON/Excelの読み書き、どこから気をつければいい?
文字化け・先頭ゼロ欠落・壊れたExcel……もう事故らせたくない!
結論:データ仕事の9割はI/O(入出力)。ここを整えるだけで、桁落ち・文字化け・先頭ゼロ欠落・Excel破損・巨大CSVでフリーズといった“現場の事故”を大幅に減らせます。本稿はpythonbunseki.comの実務テイストで、CSV/JSON/Excelの安全な読み方・壊さない書き方、型/エンコーディング/改行の落とし穴回避、そして大きなデータを速く回すコツを、コピペで使えるコード付きで解説します。
この記事で身に付く力
- CSV/JSON/Excelを“正しく読む・壊さず書く”実務レシピ
- 型・エンコーディング・改行の罠回避と再発防止テンプレ
- 巨大ファイルを速く回す:チャンク/Parquet/原子置換
関連記事:
>>【保存版】pandas基礎:データフレームの作成・整形・結合・集計を“実務の型”で身につける
>>【実務で差がつく】pandas実践:欠損処理・結合・ウィンドウ関数・時系列・品質保証まで“読みやすく速い”型を習得
>>【保存版】Jupyter Notebookの基本:環境構築・使い方・再現性・“読みやすいノート”設計まで完全ガイド
>>Python実務の型:例外処理と構造化ログでエラーに強いコードを書く
>>【保存版】SQLite×Pythonで作る“ローカルDWH”——ETL・集計・レポート自動化の最短手順
>>データ可視化レポート納品の型:Tableau/Matplotlib|“図3点+結論1行+運用”で伝わる・続く・刺さる
よくある5つの事故(まずここを潰す)
- 文字化け:UTF-8/BOM/CP932(Shift_JIS)の誤判定。濁点や絵文字が崩れる。
- 先頭ゼロ欠落:郵便番号や商品コードが00123 → 123になる。
- 日付が文字列のまま:
object
型で集計できない。 - Excelが壊れる:エンジン不一致、数式/書式の消失、巨大ファイルでクラッシュ。
- 重すぎて落ちる:数百万行CSVを一気読みしてNotebookが固まる。
ふみとの現場メモ
データサイエンティストとして10年・100件超の伴走で、事故が減る順序は明確でした。①pathlib
でパス統一 → ②読み込みでdtype/usecols/parse_dates
を明示 → ③書き込みは一時ファイル→原子置換 → ④中間はParquet → ⑤pytest×tmp_path
でラウンドトリップ検証。この記事はこの型に沿って進めます。
共通レシピ:パス・原子置換・ログ
まずは「壊れない書き込み」の土台から。書き途中の中断や電源断で破損しないよう、一時ファイル→os.replace
で原子的に置換します。
from pathlib import Path
import tempfile, os, logging
log = logging.getLogger(__name__)
def atomic\_write\_text(path: Path, text: str, encoding: str = "utf-8") -> None:
path.parent.mkdir(parents=True, exist\_ok=True)
with tempfile.NamedTemporaryFile("w", delete=False, dir=path.parent, encoding=encoding, newline="") as tmp:
tmp.write(text)
tmp\_path = Path(tmp.name)
os.replace(tmp\_path, path)
CSV:エンコーディング/改行/型の三点セット
先頭ゼロ保護と日付の即時datetime
化、そして列絞りで軽く読みます。文字化けしたらまずcp932
を試すのが日本の業務鉄板。
import pandas as pd
from pathlib import Path
p = Path("data/sales.csv")
usecols = \["date","store","sku","qty","price"]
dtypes = {"store":"category","sku":"string","qty":"int32","price":"float32"}
na\_vals = \["","NA","N/A","null","-","--"]
df = pd.read\_csv(
p,
encoding="utf-8", # ダメなら 'cp932'
usecols=usecols, # メモリ節約
dtype=dtypes, # 先頭ゼロ保護=string
na\_values=na\_vals,
parse\_dates=\["date"], # 日付→datetime
thousands=",", # 3桁区切り除去
skip\_blank\_lines=True
)
巨大CSVはチャンク読みで集計だけ溜めるのが安全で速いです。
agg = {}
for chunk in pd.read_csv(p, usecols=usecols, dtype=dtypes, parse_dates=["date"], chunksize=200_000):
g = chunk.groupby("store").qty.sum()
for k, v in g.items():
agg[k] = agg.get(k, 0) + int(v)
書き出し時は改行とエンコーディングを明示。Windowsで余計な空行が入る問題はnewline=""
で回避できます。
# pandas → CSV
from pathlib import Path
out = Path("out/sales_clean.csv")
df.to_csv(out, index=False, encoding="utf-8", line_terminator="\n")
# 標準csvで厳密制御(空行問題の回避)
import csv
rows = \[\["sku","qty"],\["00123",10],\["A-9",5]]
with Path("out/items.csv").open("w", encoding="utf-8", newline="") as f:
w = csv.writer(f, quoting=csv.QUOTE\_MINIMAL)
w\.writerows(rows)
JSON:構造化・NDJSON・精度の罠
json.dumps()
はensure_ascii=False
で日本語をそのまま、indent
/sort_keys
でレビューしやすく。大量ログは行ごとJSON(NDJSON)が相性◎。
import json
from pathlib import Path
p = Path("data/config.json")
config = json.loads(p.read\_text(encoding="utf-8"))
# or:
with p.open("r", encoding="utf-8") as f:
config = json.load(f)
text = json.dumps(config, ensure\_ascii=False, indent=2, sort\_keys=True)
Path("out/config.json").write\_text(text, encoding="utf-8")
# NDJSON(1行=1JSON)
import pandas as pd
df = pd.read\_json("data/events.ndjson", lines=True)
Path("out/events.ndjson").write\_text(
"\n".join(df.to\_json(orient="records", lines=True).splitlines()),
encoding="utf-8"
)
Excel:エンジン/書式/複数シートの基本
.xlsxは読みopenpyxl
、書きopenpyxl/xlsxwriter
が基本。コード列はstring
で先頭ゼロ保護を。
import pandas as pd
from pathlib import Path
# 読む
p = Path("data/report.xlsx")
df = pd.read\_excel(p, sheet\_name="売上", usecols="A\:F",
dtype={"店舗":"category","商品コード":"string"})
# 書く:複数シート+書式
with pd.ExcelWriter(Path("out/report.xlsx"), engine="xlsxwriter") as w:
df\_summary.to\_excel(w, sheet\_name="サマリ", index=False)
df\_detail.to\_excel(w, sheet\_name="明細", index=False)
wb = w\.book
ff = wb.add\_format({"bold": True})
num = wb.add\_format({"num\_format": "#,##0"})
ws1 = w\.sheets\["サマリ"]
ws1.set\_row(0, None, ff)
ws1.set\_column("D\:D", None, num)
高速化:Parquet/型詰め/チャンク
中間はCSVよりParquetが速くて軽い。ついでにdowncast
やcategory
化でメモリ1/2〜1/4へ。
import pandas as pd
df.to\_parquet("work/data.parquet", compression="zstd")
df2 = pd.read\_parquet("work/data.parquet")
def optimize\_dtypes(df: pd.DataFrame) -> pd.DataFrame:
for c in df.select\_dtypes(include="int64").columns:
df\[c] = pd.to\_numeric(df\[c], downcast="integer")
for c in df.select\_dtypes(include="float64").columns:
df\[c] = pd.to\_numeric(df\[c], downcast="float")
for c in df.select\_dtypes(include="object").columns:
if df\[c].nunique() / max(len(df),1) < 0.5:
df\[c] = df\[c].astype("category")
return df
フォルダ一括処理:日次CSVの連結
from pathlib import Path
import pandas as pd
root = Path("data/daily")
files = sorted(root.glob("sales\_2025-\*.csv"))
frames = \[]
for p in files:
df = pd.read\_csv(p, usecols=\["date","store","sales"], parse\_dates=\["date"], dtype={"store":"string"})
df\["source"] = p.name
frames.append(df)
all\_df = pd.concat(frames, ignore\_index=True)
エラーハンドリング:境界ごとに例外ラップ
from pathlib import Path
import pandas as pd
class DataError(Exception): ...
class ExternalError(Exception): ...
def load\_csv\_safe(path: Path) -> pd.DataFrame:
try:
return pd.read\_csv(path, encoding="utf-8")
except UnicodeDecodeError as e:
raise DataError(f"encoding error: {path}") from e
except OSError as e:
raise ExternalError(f"file io error: {path}") from e
ふみとの体験談:SJISの落とし穴
昔、社内システムがCP932(SJIS)固定で、外部ベンダはUTF-8。濁点が消える事故が連発し、顧客名マッチングが壊滅しました。以降は「まずUTF-8、無理ならCP932で仮読み→持ち主に確認」「先頭ゼロ列はstring」を徹底し、納品時は原子置換で壊れないようにしています。
よくある罠と対処(早見表)
症状 | 原因 | 対処 |
---|---|---|
文字化け | エンコーディング違い | encoding="cp932" やerrors="ignore" で仮読み→持ち主確認 |
先頭ゼロ欠落 | 自動型変換 | 読み:dtype="string" 、書き:Excelの表示形式=文字列 |
カンマ付き数値 | 区切り記号 | thousands="," /書きは{:,} で整形 |
巨大CSVで落ちる | 一括読み | chunksize ・usecols ・Parquet化 |
read_excel が遅い | エンジン/列過多 | usecols で範囲指定、必要ならengine="openpyxl" 明示 |
JSON桁落ち | float誤差 | decimal.Decimal 、または文字列保持 |
改行が倍増 | Windowsの改行制御 | open(..., newline="") で回避 |
ユースケース別の第一手(絞り込み)
- 社内定例のCSV→Excel配布:
pandas→ExcelWriter(xlsxwriter)
でサマリ/明細の2枚出力 → [内部リンク:データ可視化レポート納品の型] - APIログのNDJSON集計:
read_json(lines=True)
→groupby
→Parquet保存 - レガシーSJIS CSV:
encoding="cp932"
+dtype="string"
で先頭ゼロ保護 - 巨大CSV連結:
chunksize
+列絞り+集計のみ保持(生データはParquetへ)
今日やること(45分)
- あなたの実データ1本を、
usecols/dtype/parse_dates
を明示して読み込む - 先頭ゼロ列を
string
化、中間Parquetで保存して速度を体感 ExcelWriter(xlsxwriter)
でサマリ/明細の2シートを出力pytest×tmp_path
でCSVラウンドトリップのテストを1本作る
テスト:pytest×tmp_path(コピペ)
import pandas as pd
def test\_csv\_roundtrip(tmp\_path):
p = tmp\_path/"a.csv"
df = pd.DataFrame({"sku":\["001","002"],"qty":\[1,2]})
df.to\_csv(p, index=False, encoding="utf-8")
back = pd.read\_csv(p, dtype={"sku":"string"})
assert list(back.sku) == \["001","002"]
伴走のご案内:I/Oの型を整えて“壊れない納品”へ
I/Oは設計で9割決まる。無料カウンセリング/体験で、あなたのデータ仕様に合わせたエンコーディング/型/Parquet/Excel体裁を設計し、原子置換まで組み込んだ納品テンプレを作ります。
TechAcademy データサイエンスコース(受講料:174,600円~ ※更に割引あり)

株式会社キカガク AI人材長期育成コース(受講料:237,600円~)

この記事から次に読む(内部リンク)
-
-
【保存版】pandas基礎:データフレームの作成・整形・結合・集計を“実務の型”で身につける
データ分析の9割は前処理と整形です。ここをpandasで素早く正確にこなせるかが、成果物の質と速度を決めます。本記事は未経験〜初学者が週10時間×2〜3週で、pandasの基礎(読み込み/選択/整形/ ...
-
-
【実務で差がつく】pandas実践:欠損処理・結合・ウィンドウ関数・時系列・品質保証まで“読みやすく速い”型を習得
リード(結論)基礎を終えたら次は実務の現場で頻出する処理を“型”で覚える段階です。本記事は、pandas 2.x を前提に、欠損・外れ値・結合・ウィンドウ関数・時系列・カテゴリ処理・集計の自動化・大規 ...
-
-
【保存版】SQLite×Pythonで作る“ローカルDWH”——ETL・集計・レポート自動化の最短手順
ローカルでゼロ構築、ファイル1つで完結、サーバ不要。本記事はSQLite×Pythonで“毎日回る”ETL・集計・レポート自動化を最短で作るための完全ガイドです。データ設計→DB作成→ETL(取り込み ...
-
-
【保存版】可視化入門:Matplotlib/Plotlyの使い分けと“伝わるグラフ設計”10ステップ
結論:可視化は「きれいに描く」ことではなく、意思決定を動かすための設計です。本稿では、未経験〜初学者が 週10時間×1〜2週 で、Matplotlib/Plotlyを軸に “伝わるグラフ”の設計と実装 ...
-
-
データ可視化レポート納品の型:Tableau/Matplotlib|“図3点+結論1行+運用”で伝わる・続く・刺さる
結論:レポートは「データ→図」ではなく「意思決定→図」の順で設計します。最短で伝わり、運用で続く“型”は、(1) 結論1行、(2) 図3点(推移・分解・構成)、(3) 打ち手(閾値/費用対効果)、(4 ...
最近のコメント