毎月、支店や部署から届く複数のExcelを開いて、1つのシートに貼り付けている。届く表にはセル結合や小計の行があり、そのままでは集計しにくい。Pythonで自動化したいが、どこから書けばよいか迷っている。
この記事でわかること
- pandasで複数のExcelをまとめ、表を整える手順
- 作った表の確かめ方と、見た目と中身が違う値(小数・日付・「1,000」)の直し方
- 手作業を処理に置き換えると、転記のミスと時間が減ること
結論:まとめる・整える・確かめるの順に、Pythonで書く
PythonでのExcelの自動化は、「まとめる→整える→確かめる」の順に書きます。
- まとめる:フォルダー内のExcelを読み込み、縦につなぐ
- 整える:セル結合や小計のある表を、1行に1件の形にする
- 確かめる:元のExcelと見比べ、見た目と中身が違う値を直す
先にまとめるのは、1つにつないでから整えれば、整える処理を1回書くだけで済むからです。毎月同じ形で届く表なら、その処理を翌月もそのまま使えます。
最後に確かめるのは、Excelの画面で見える値と、Pythonが読む値が違うことがあるからです。
手順1 まとめる:フォルダー内のExcelを読み込み、縦に1つにつなぐ
複数のExcelは、1つのフォルダーに置いて順に読み込み、pandasのconcatで縦につなぎます。
準備:pandasとopenpyxlを入れる
ターミナル(Windowsではコマンドプロンプト)で次を実行します。
pip install pandas openpyxl
pandasは表の読み込みや加工に、openpyxlはExcelファイルの読み書きに使うライブラリです。pandasが.xlsxを読むときにも、openpyxlが使われます。
3つのファイルを読み込み、縦につなぐ
自分は、複数ファイルを1つにまとめる処理をPythonで書いています。以下は架空の例です。東京・大阪・福岡の3支店から、毎月同じ形の売上表が届くとします。
ファイル名は売上_東京.xlsx・売上_大阪.xlsx・売上_福岡.xlsxです。3つとも「売上_2026-10」フォルダーに入れ、そのフォルダーを.pyファイルやノートブックと同じ場所に置きます。
from pathlib import Path
import pandas as pd
# 架空の例: 3支店のファイルを置いたフォルダー
folder = Path("売上_2026-10")
frames = []
for path in sorted(folder.glob("*.xlsx")):
# Excelで開いている間にできる一時ファイルは飛ばす
if path.name.startswith("~$"):
continue
# 1行目はまとまりの見出しなので、2行目を列名にする
df = pd.read_excel(path, header=1)
# どのファイルの行かを残す
df["ファイル名"] = path.name
frames.append(df)
data = pd.concat(frames, ignore_index=True)
header=1は、2行目を列名にする指定です(pandasは行を0から数えます)。
つないだ後は、列名とファイルごとの件数を確かめます。
print(data.columns.tolist())
print(data["ファイル名"].value_counts())
concatは、列名が同じ列どうしをつなぎます。列が想定より多いときは、列名が1文字でも違っていないかを見直します。
手順2 整える:見た目用の表を、1行に1件の形にする
見た目用の表は、空欄を埋めて小計と空白の行を除き、月を縦に並べると集計しやすい形になります。
見た目用の表は、人が読みやすいように作られた表のことです。セル結合や2段の見出し、横に並んだ日付や月、途中の小計や空白の行があります。
自分は、見た目用の表を集計しやすい形に整える作業を、Pythonで行いました。
支店から届くExcelの表は、架空の例で次のような形をしています。「文具」は、3行目と4行目のセルを結合しています。
1行目: 売上(個)
2行目: 区分 商品 4月 5月 6月
3行目: 文具 ペン 120 130 110
4行目: ノート 80 90 85
5行目: 小計 200 220 195
6行目: (空白の行)
7行目: 家具 椅子 15 12 18
| 見た目用の表の工夫 | 集計で困ること | pandasでの直し方 |
|---|---|---|
| 見出しが2段 | どの段を列名にするか決まらない | header=1で2段目を列名にする |
| セル結合 | 「ノート」の行の「区分」が空欄になる | ffillで上の値を埋める |
| 小計・空白の行 | 合計に小計が二重に入る | dropnaで除く |
| 月が横に並ぶ | 月ごとの集計や、月の追加がしにくい | meltで縦にする |
この表では、工夫ごとの困ることと直し方を並べています。2段の見出し以外の3つは、次のコードで直します。
# セル結合で空欄になった「区分」を、上の値で埋める
data["区分"] = data["区分"].ffill()
# この例では、小計と空白の行は「商品」が空欄なので、それで除く
data = data.dropna(subset=["商品"])
# 横に並んだ月を、「月」と「売上」の2列にする
tidy = data.melt(
id_vars=["ファイル名", "区分", "商品"],
var_name="月",
value_name="売上",
)
tidy.to_excel("売上_2026-10_まとめ.xlsx", index=False)
ffillは、「区分」のように、セル結合で空欄になった列だけに使います。売上の列に使うと、本当に空欄だった所まで上の数字で埋まります。
meltのid_varsにそのまま残したい列を書くと、それ以外の列(この例では4月〜6月)が縦に並びます。
整えた後の表は、次のようになります(先頭の3行。売上の数字は省いています)。
ファイル名 区分 商品 月 売上
売上_大阪.xlsx 文具 ペン 4月 ...
売上_大阪.xlsx 文具 ノート 4月 ...
売上_大阪.xlsx 家具 椅子 4月 ...
1行に1件ずつ入ったので、支店別や月別の合計をgroupby(グループごとに集計する処理)で出せます。
手順3 確かめる:元のExcelと、Pythonで作った表を見比べる
確かめる基本は、元のExcelとPythonで作った表を見比べ、同じ行の値と合計が合うかを見ることです。
Pythonで作った表は、手順2で書き出した「売上_2026-10_まとめ.xlsx」です。たとえば、東京のペンの4月の売上が、元のファイルと同じ数字かを見ます。支店ごとの合計が、元のファイルの小計を足した数と合うかも見ます。
ここまでは、openpyxlでセルの書式を調べなくても、2つのファイルを見比べるだけでできます。合わないときは、次の「見た目と中身の違い」を疑います。
現場目線の注意点:Excelの見た目と、pandasが読む中身は違うことがある
自分が気をつけているのは、データの型(数値・文字・日付といった値の種類)が変わるパターンです。Excelの画面では使いたい値になっているのに、書式設定を見ると実は違う、ということがあります。
特に注意しているのは、見た目は小数3桁だが、中身は小数5桁や0桁(整数)である場合です。見た目は日付だが中身は文字や数字、見た目は「1,000」だが中身は文字、ということもあります。
架空の値で並べると、次のとおりです。
| Excelの見た目 | セルの中身 | pandasで読んだ値 |
|---|---|---|
| 1.235 | 数値の1.23456 | 1.23456 |
| 2.000 | 数値の2 | 2 |
| 2026/10/01 | 文字の「2026/10/01」 | 文字のまま |
| 2026/10/01 | 数値の20261001(書式で日付の形に見せている) | 20261001 |
| 1,000 | 文字の「1,000」 | 文字のまま |
この表では、見た目が同じでも、中身によって読んだ値が変わっています。なお、Excelに日付として入力したセルは、日付として読まれます。
小数は丸める前の値で読まれるので、画面の数字を書き写した報告書とは、下の桁で合わないことがあります。見た目の桁で集計するか、中身の値で集計するかは、集計結果を使う人と決めておきます。丸めるなら、四捨五入などの丸め方も決めます。
型を見る:数値のはずの列が「object」なら、文字が混ざっている
Pythonで作った表の型は、dtypesで列ごとに見ます。
print(tidy.dtypes)
数値の列は「int64」や「float64」と出ます。数値のはずの列が「object」と出たら、文字の値が混ざっています。
列がすべて文字の場合は、pandasのバージョンによって「str」と出ることがあります。
もっと詳しく確かめたいとき:openpyxlで書式を見る
見比べても原因が分からないときは、openpyxlでセルの中身と書式を並べて見られます。
from openpyxl import load_workbook
# data_only=True: 数式のセルは、最後に保存された計算結果を読む
wb = load_workbook("売上_2026-10/売上_東京.xlsx", data_only=True)
cell = wb.active["C3"]
print(cell.value, cell.number_format)
"C3" はペンの4月の売上のセルです。valueが中身、number_formatが書式です。
たとえば「1.23456 0.000」と出たら、中身は小数5桁で、書式で3桁に見せています。
直し方:文字の数字は、カンマを外して数値にする
「1,000」のような文字は、カンマを外してから数値にします。
s = tidy["売上"].astype(str).str.replace(",", "")
tidy["売上"] = pd.to_numeric(s, errors="coerce")
# 数値にできなかった行(空欄を含む)を確かめる
print(tidy[tidy["売上"].isna()])
errors="coerce" は、数値にできない値を空欄(NaN)にする指定です。
文字の日付は、pd.to_datetimeで日付にします。20261001のような数値は、文字にしてからformat="%Y%m%d" を指定して日付にします。
列に空欄があると、20261001.0のような小数で読まれるので、先に整数にしてから文字にします。
# 例: 「日付」の列に、20261001のような数値が入っている場合
s = tidy["日付"].astype("Int64").astype(str)
tidy["日付"] = pd.to_datetime(s, format="%Y%m%d", errors="coerce")
astype("Int64") は、空欄を残したまま整数にする指定です。
業務への着地:手作業を処理に置き換えると、転記のミスと時間が減る
まとめる・整える・確かめるを処理で書くと、毎月の貼り付けと書き写しがなくなり、転記のミスと時間が減ります。
処理は毎回同じ手順で動くので、貼り付ける範囲のずれや、打ち間違いが入りにくくなります。
架空の例で、Before/Afterを比べると次のとおりです。
| 作業 | Before(手作業) | After(Pythonの処理) |
|---|---|---|
| まとめる | 3つのファイルを開いて貼り付ける | フォルダーに置いて処理を動かす |
| 整える | 小計を消し、月ごとに貼り直す | 同じ処理で1行に1件にする |
| 確かめる | 書き写した数字を1つずつ見直す | 件数と合計を、元のExcelと見比べる |
この表では、手で書き写していた作業が、処理を動かす作業に変わっています。
自分は、手作業をやめて処理で書くようにしたので、単純にミスがなくなり、転記の時間も減りました。
自動化した後も、件数と合計は毎月確かめます。動かせる人が1人にならないよう、動かし方は手順書に残します。
自動化しないほうがいいとき:フォーマットが毎回違うなら、まず業務を見直す
自動化しないほうがいいのは、Excelへ転記するときのフォーマットが、毎回異なる場合です。
ExcelからPythonに変えるのではなく、業務のプロセスや、そのプロセスにかかるアウトプットの定義を見直したほうがいいです。
アウトプットの定義は、その作業で何を、どんな形で作るかの決まりです。支店から届く表の列や、作る資料の項目がこれにあたります。
形がそろっているかは、次の3つで確かめます。
- 列の名前・順番が毎回同じか
- 見出しの行の位置が毎回同じか
- ファイルの置き場所と名前が決まっているか
どれかが毎回変わるなら、まず業務のほうをそろえます。Pythonにするのは形がそろったあとでよく、将来的でもかまいません。
そろえ方は、次の記事の「なくす・分ける・そろえる」で整理しています。
まとめ
Excelの手作業の自動化は、次の3つを順に進めます。
- まとめる:フォルダー内のExcelを読み込み、concatで縦につなぐ
- 整える:空欄を埋め、小計と空白の行を除き、meltで月を縦にする
- 確かめる:元のExcelと見比べ、見た目と中身が違う値を直す
手作業を処理に置き換えると、転記のミスと時間が減ります。ただし、フォーマットが毎回違う作業は、まず業務を見直して形をそろえます。Pythonにするのはそのあとでよく、将来的でもかまいません。





