Excel の重複行を見つけて確認用に分離する
4列すべての値が完全一致する行を見つけ、元の行番号とともに新しい Excel ファイルへまとめます。最初に出現した行も含め、重複グループごとに確認します。
同じ列構成を持つ複数のワークシートを1つの Excel 表に統合し、各行がどのシートから来たかを記録します。小さな合成ワークブックを使うことで、行順、件数、合計値を手作業でも確認できます。
この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English
対象読者同じような構造の Excel シートを複数受け取り、元のワークブックを変更せずに1つの統合データセットを作成したい人向けのガイドです。
このワークフローでは、1つのワークブックから選択したワークシートを新しいワークブックに統合します。各元シートのヘッダー行は、列順と大文字・小文字を含めて完全に一致している必要があります。出力では最初の列として source_sheet を追加し、各レコードの出所を保持します。
空白行は無視されます。空白でない行は、ワークシートの順序、その後に各シート内の元の行順でコピーされます。元のワークブックは読み取り専用で開かれ、結果は outputs の下にある別フォルダへ書き込まれます。
以下のワークブックは合成データであり、この記事のために作成したものです。セットアップスクリプトを create_excel_merge_demo.py として保存してください。East, West, Online という3つのシートを作成し、それぞれ order_id, customer, amount の列を使用します。
from pathlib import Path
from openpyxl import Workbook
SOURCE_DIR = Path("outputs") / "excel_merge_demo"
SOURCE = SOURCE_DIR / "regional_orders.xlsx"
HEADERS = ["order_id", "customer", "amount"]
DATA = {
"East": [
["E001", "Ana", 120],
["E002", "Ben", 80],
],
"West": [
["W001", "Cara", 150],
["W002", "Dan", 70],
],
"Online": [
["O001", "Emi", 90],
[None, None, None],
["O002", "Finn", 110],
],
}
SOURCE_DIR.parent.mkdir(parents=True, exist_ok=True)
SOURCE_DIR.mkdir() # Stop if the synthetic source folder already exists.
workbook = Workbook()
for index, (sheet_name, rows) in enumerate(DATA.items()):
if index == 0:
sheet = workbook.active
sheet.title = sheet_name
else:
sheet = workbook.create_sheet(sheet_name)
sheet.append(HEADERS)
for row in rows:
sheet.append(row)
workbook.save(SOURCE)
python create_excel_merge_demo.py| シート | 空白でないデータ行 | amount 合計 |
|---|---|---|
| East | 2 | 200 |
| West | 2 | 220 |
| Online | 2 | 200 |
空白でないデータレコードは合計6件です。amount の合計は 620 です: 120 + 80 + 150 + 70 + 90 + 110。Online には2件のレコードの間に完全な空白行を1行意図的に入れており、統合処理で空白行がスキップされることを確認できます。
スクリプトではシートを East, West, Online の順で明示的に指定しています。この順序が統合後のレコード順を決定します。各シート内では、元の上から下への順序が維持されます。
| source_sheet | order_id | customer | amount |
|---|---|---|---|
| East | E001 | Ana | 120 |
| East | E002 | Ben | 80 |
| West | W001 | Cara | 150 |
| West | W002 | Dan | 70 |
| Online | O001 | Emi | 90 |
| Online | O002 | Finn | 110 |
したがって出力は4列となり、ヘッダーを含めるとワークシート上では7行になります: ヘッダー1行 + データ6行。source_sheet の値は手作業での件数確認にも使えます。East は2回、West は2回、Online は2回出現します。
次のスクリプトを excel_merge_sheets.py として保存してください。元ワークブックを読み取り専用で開き、選択したシート名とヘッダーを検証し、完全な空白行を無視します。また、数式を元のワークブックの文脈から切り離した状態でコピーしないよう、数式セルを検出した場合は処理を停止します。
from pathlib import Path
from openpyxl import Workbook, load_workbook
SOURCE = Path("outputs") / "excel_merge_demo" / "regional_orders.xlsx"
OUTPUT_DIR = Path("outputs") / "excel_merge_result"
OUTPUT = OUTPUT_DIR / "merged_orders.xlsx"
SHEETS = ("East", "West", "Online")
OUTPUT_SHEET = "Combined"
SOURCE_COLUMN = "source_sheet"
def read_sheet_rows(sheet, expected_header=None):
first_row = next(
sheet.iter_rows(min_row=1, max_row=1, values_only=True),
None,
)
if first_row is None:
raise ValueError(f"Sheet is empty: {sheet.title}")
header = tuple(first_row)
if any(not isinstance(name, str) or not name.strip() for name in header):
raise ValueError(f"Invalid header in sheet: {sheet.title}")
if len(set(header)) != len(header):
raise ValueError(f"Duplicate header name in sheet: {sheet.title}")
if SOURCE_COLUMN in header:
raise ValueError(f"Reserved column already exists: {SOURCE_COLUMN}")
if expected_header is not None and header != expected_header:
raise ValueError(f"Header mismatch in sheet: {sheet.title}")
rows = []
for excel_row, values in enumerate(
sheet.iter_rows(min_row=2, max_col=len(header), values_only=True),
start=2,
):
if all(value is None for value in values):
continue
if any(isinstance(value, str) and value.startswith("=") for value in values):
raise ValueError(
f"Formula found in {sheet.title} row {excel_row}; "
"this example merges literal values only."
)
rows.append(tuple(values))
return header, rows
def main() -> None:
source_path = SOURCE.resolve(strict=True)
if not source_path.is_file():
raise ValueError("SOURCE must be an Excel file.")
if len(set(SHEETS)) != len(SHEETS):
raise ValueError("SHEETS contains a duplicate sheet name.")
source_workbook = load_workbook(
source_path,
read_only=True,
data_only=False,
)
try:
missing = [name for name in SHEETS if name not in source_workbook.sheetnames]
if missing:
raise ValueError(f"Missing sheets: {missing}")
expected_header = None
combined_rows = []
for sheet_name in SHEETS:
sheet = source_workbook[sheet_name]
header, rows = read_sheet_rows(sheet, expected_header)
if expected_header is None:
expected_header = header
for row in rows:
combined_rows.append((sheet_name, *row))
finally:
source_workbook.close()
OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
OUTPUT_DIR.mkdir() # Refuse to reuse an existing output folder.
output_workbook = Workbook()
output_sheet = output_workbook.active
output_sheet.title = OUTPUT_SHEET
output_sheet.append([SOURCE_COLUMN, *expected_header])
for row in combined_rows:
output_sheet.append(row)
output_workbook.save(OUTPUT)
check_workbook = load_workbook(OUTPUT, read_only=True, data_only=False)
try:
if check_workbook.sheetnames != [OUTPUT_SHEET]:
raise RuntimeError("Unexpected output worksheet structure.")
check_sheet = check_workbook[OUTPUT_SHEET]
written = list(check_sheet.iter_rows(values_only=True))
finally:
check_workbook.close()
expected = [(SOURCE_COLUMN, *expected_header), *combined_rows]
if written != expected:
raise RuntimeError("Saved workbook does not match the expected rows.")
print(f"Merged {len(SHEETS)} sheets into {len(combined_rows)} data rows.")
print(f"Verified {len(expected_header) + 1} columns and {len(combined_rows)} data rows.")
print(f"Output: {OUTPUT.as_posix()}")
if __name__ == "__main__":
main()
python excel_merge_sheets.py合成ワークブックでは、出力に Combined という名前のワークシートが正確に1つ含まれるはずです。1行目は source_sheet, order_id, customer, amount となり、その後に前述の6件のレコードが続きます。
以下の想定コンソール出力は、コードと合成データから手作業で導出したものです。実際に取得した実行ログではありません。
Merged 3 sheets into 6 data rows.
Verified 4 columns and 6 data rows.
Output: outputs/excel_merge_result/merged_orders.xlsx| 確認項目 | 想定結果 |
|---|---|
| 選択した元シート | 3 |
| 統合データ行 | 6 |
| 出力列 | 4 |
| East 行 | 2 |
| West 行 | 2 |
| Online 行 | 2 |
| amount 合計 | 620 |
実務ワークブックでは、すべてのワークシートを自動統合するのではなく、SHEETS を明示的に変更してください。こうすることで、同じファイル内にある参照表、メモ、非表示シート、集計シートなどを誤って含める可能性を減らせます。
| 症状 | 確認すること |
|---|---|
| ModuleNotFoundError: No module named openpyxl | python -m pip install openpyxl を使い、同じ Python 環境に openpyxl をインストールします。 |
| FileNotFoundError | SOURCE とターミナルの作業ディレクトリを確認します。この例では、先に合成ワークブックのセットアップを実行してください。 |
| Missing sheets | SHEETS の名前を、空白や大文字・小文字も含めて正確に確認します。 |
| Header mismatch | 選択したすべてのシートで、同一の列名が同一の順序で使われていることを確認します。 |
| Reserved column already exists | source_sheet という元列の名前を変更するか、SOURCE_COLUMN を衝突しない名前に変更します。 |
| Formula found | 統合前に数式の扱い方を決めてください。このチュートリアルでは、文脈のない数式式をコピーする代わりに意図的に停止します。 |
| FileExistsError | 出力フォルダがすでに存在します。以前の結果を確認し、上書きせず新しい保存先を使用します。 |
OUTPUT_DIR の作成後に保存や検証が失敗すると、フォルダまたはワークブックが部分的な結果として残る場合があります。未検証の結果として扱い、問題を修正した後は新しい保存先を使用してください。
このスクリプトは、ヘッダー行が1行の矩形データシートを想定しています。複数行ヘッダー、結合されたヘッダーセル、ピボットテーブル、グラフ、画像、複数の無関係な表が同じシート内にあるワークシートには対応していません。
統合されるのはセル値だけです。数値書式、フォント、塗りつぶし、条件付き書式、入力規則、ハイパーリンク、コメント、数式、行の高さ、列幅、ワークシートレベルの設定は再現されません。日付値や数値そのものは値として保持できますが、元の表示書式はコピーされません。
また、このスクリプトは出力ワークブックを作成する前に、統合したすべての行タプルを Python のメモリへ読み込みます。この小規模な検証済みワークフローでは扱いやすい方法ですが、非常に大きなワークブックには適さない可能性があります。大規模ファイルでは、write-only 出力と段階的な別検証を組み合わせたストリーミング設計にすると、メモリ使用量を減らせます。
2026-09-20 · 手作業で確認した例 · 対象: Python 3.12 · openpyxl 必須 · 未実行
説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。