経理のExcel作業をpandasで置き換える|CSV一括取込・帳票整形・Excel出力
/ 10 min read
Table of Contents
はじめに
経理や会計の仕事をしていると、毎月まったく同じExcel作業を繰り返すことになります。部門ごとに分かれたCSVを開いて1つに貼り付け、桁区切りが文字列になっているのを直し、全角の混ざった摘要を揃えて、最後にシートを分けて資料として出す。手順は決まっているのに、毎月2〜3時間が消えていきます。
この記事は、私が公認会計士として実務で実際に使っているpandasのコードを、月次決算の作業順にまとめたものです。1つ1つは短いコードですが、通しで組むと上記の作業がほぼ全自動になります。
1. 部門別に分かれたCSVを一気に取り込む
月次でいちばん多いのが「同じ形式のファイルが部門の数だけある」パターンです。1つずつ開いて貼り付ける必要はありません。
from pathlib import Pathimport pandas as pd
data_dir = Path("data/2026-08")
# 同じ形式のCSVをまとめて1つのDataFrameにするdfs = [pd.read_csv(f) for f in sorted(data_dir.glob("*.csv"))]df = pd.concat(dfs, ignore_index=True)sorted() を挟んでいるのは、取り込み順を安定させるためです。glob() の返す順序はOSに依存するので、並び順に意味がある場合(部門コード順など)は必ずソートします。
どのファイル由来の行なのかを残しておくと、後で差異を追うときに効きます。
dfs = []for f in sorted(data_dir.glob("*.csv")): tmp = pd.read_csv(f) tmp["source"] = f.stem # ファイル名を列として残す dfs.append(tmp)
df = pd.concat(dfs, ignore_index=True)2. 会計データ特有の「型崩れ」を取り込み時に潰す
会計システムから出したCSVは、そのまま読むとほぼ確実に型が崩れます。ここを取り込み時点で潰しておくのが、後工程を楽にする最大のコツです。
3桁区切りの数字が文字列になる
1,234,567 のようなカンマ区切りの金額は、そのまま読むと数値ではなく object 型になります。合計しようとしても文字列連結になってしまうやつです。
# read_csv の thousands で区切り記号を指定するだけdf = pd.read_csv("shikin.csv", thousands=",")すでに読み込んでしまった後なら、文字列としてカンマを消してから型変換します。
df["金額"] = df["金額"].str.replace(",", "").astype("int64")列名とデータ型を自分で決める
ヘッダーのないCSVや、勘定科目コードの先頭ゼロが消えると困る場合は、読み込み時に列名と型を指定します。
names = ["勘定科目コード", "勘定科目名", "金額"]dtype = {"勘定科目コード": str, "勘定科目名": str, "金額": float}
df = pd.read_csv("moto.csv", names=names, dtype=dtype)ポイントは、列名の指定が columns ではなく names であることです。ここは間違えやすいところです。
3. 摘要欄の表記ゆれをならす
手入力の摘要欄は、全角と半角が容赦なく混ざります。集計キーに使う列は先に揃えておきます。
$ pip install mojimojiimport mojimoji
# 全角を半角に(英数字・記号が対象、日本語はそのまま)df["摘要"] = df["摘要"].apply(mojimoji.zen_to_han)カナだけは全角に揃えたい、というケースも実務では多いので、その場合は引数で制御します。
# 英数字は半角に、カナは全角のままにするdf["摘要"] = df["摘要"].apply(lambda x: mojimoji.zen_to_han(x, kana=False))4. 数値列だけを合計する
科目名などの文字列列が混ざったDataFrameで df.sum() をすると、文字列まで連結されてしまいます。numeric_only=True を付けるだけで解決します。
import pandas as pd
df = pd.DataFrame({ "科目": ["現金", "売掛金", "商品"], "当期": [1000, 2000, 3000], "前期": [900, 1800, "―"], # 文字列が混ざっている})
df.sum(numeric_only=True)# 当期 6000# dtype: int64前期列が集計から落ちていることに注目してください。― のような記号が1つ混ざるだけで列全体が object 型になるため、合計が合わないときはまず df.dtypes を疑うのが鉄則です。
df.dtypes # 型を確認df["前期"] = pd.to_numeric(df["前期"], errors="coerce") # 変換できない値はNaNに5. 複数の表を1つのExcelにシート分けして出力する
最後の成果物はやはりExcelです。ExcelWriter を使うと、複数のDataFrameをシートに分けて1ファイルに書き出せます。
import pandas as pd
sheets = { "貸借対照表": bs_df, "損益計算書": pl_df, "部門別内訳": bumon_df,}
with pd.ExcelWriter("gessou_2026-08.xlsx") as writer: for name, sheet_df in sheets.items(): sheet_df.to_excel(writer, sheet_name=name, index=False)HTMLの財務諸表など、1つのファイルに表が複数入っている場合は read_html と組み合わせると一気に処理できます。
def export_xlsx(input_path, output_path): dfs = pd.read_html(input_path) with pd.ExcelWriter(output_path) as writer: for i, d in enumerate(dfs, start=1): d.to_excel(writer, sheet_name=f"table{i}", index=False)
export_xlsx("kessan.html", "kessan.xlsx")CSVに追記していく
日次で溜めていくログのような使い方なら、mode="a" で最終行に追記できます。
df.to_csv("shiwake_log.csv", mode="a", index=False, header=False)header=False を忘れると、追記のたびにヘッダー行が混ざります。
通して組むとこうなる
ここまでを1つのスクリプトにすると、月次の前処理はこれだけで終わります。
from pathlib import Pathimport mojimojiimport pandas as pd
data_dir = Path("data/2026-08")dtype = {"勘定科目コード": str, "勘定科目名": str}
# 1. 部門別CSVを一括取込(桁区切りも同時に解決)dfs = []for f in sorted(data_dir.glob("*.csv")): tmp = pd.read_csv(f, thousands=",", dtype=dtype) tmp["部門"] = f.stem dfs.append(tmp)
df = pd.concat(dfs, ignore_index=True)
# 2. 表記ゆれをならすdf["摘要"] = df["摘要"].apply(lambda x: mojimoji.zen_to_han(x, kana=False))
# 3. 部門別に集計bumon_df = df.groupby("部門").sum(numeric_only=True).reset_index()
# 4. Excelに出力with pd.ExcelWriter("gessou_2026-08.xlsx") as writer: df.to_excel(writer, sheet_name="明細", index=False) bumon_df.to_excel(writer, sheet_name="部門別集計", index=False)おわりに
毎月2〜3時間かかっていた作業が、実行して待つだけの数十秒になります。最初にスクリプトを組む手間はかかりますが、月次は12回、四半期レビューまで含めればそれ以上繰り返す作業なので、1年で確実に回収できます。
慣れてきたら、取り込んだ時点で件数と合計金額をチェックする処理を足しておくと安心です。手作業のコピペと違って、ファイルの取り込み漏れは目視では気づけないので、そこだけは仕組みで担保しておく価値があります。
