Excel・文書業務

Excelの重複行を見つけ、確認用ファイルに分ける

四列が完全に一致する行を探し、元の行番号とともに新しいExcelファイルへまとめます。最初に出現した行も含めて、重複グループを確認します。

目次を表示

この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 한국어

対象読者Excelデータを集約した後に重複入力を確認する事務担当者

準備するもの
  • Python 3.12とターミナルを用意します。python --versionでバージョンを確認します。
  • ダウンロードしたZIPをすべて展開し、example.pyがあるフォルダーでターミナルを開きます。
  • 最初は同梱の練習データで実行し、結果と元のファイルを比較します。
  • orders.xlsxのOrdersシートとopenpyxlを使います。コードの実行にMicrosoft Excelアプリは不要です。

01何を重複と見なすか決める

この例では、order_id、customer、item、quantityの四つの値がすべて一致する行を重複と見なします。注文番号だけが同じ行は重複とは見なしません。文字列の空白や大文字・小文字も自動では変更しません。

重複を削除する前に、同じ内容を持つすべての行をDuplicatesシートに集めます。最初に出現した行も含めます。一回だけ出現した行はUniqueシートに残し、全8行を漏れなく確認できるようにします。

02入力ファイルの構成を確認する

orders.xlsxのシート名はOrdersとし、先頭行の列名と順序を以下に合わせる必要があります。列を追加してもヘッダーエラーになるため、業務ファイルで直接置き換えるのではなく、必要な四列をコピーした練習用ファイルを作ってください。

役割
order_id文字列の注文番号O-1001
customer文字列の顧客名Alpha
item文字列の品目Sensor
quantity正の整数の数量2

完全に空の行は読み飛ばします。一部の値が空、または数式を含む場合は停止します。注文番号・顧客名・品目は空でない文字列である必要があり、数値の注文番号は自動で文字列に変換しません。

03ファイルを準備して実行する

  1. ZIPを展開し、README.txtに記載された入力ファイルがexample.pyと同じ場所にあるか確認します。ZIPの中から直接実行しないでください。
  2. example.pyがあるフォルダーで、以下のコマンドを一行ずつ実行します。Windowsでpythonコマンドがなくpyがある場合は、各コマンドのpythonをpy -3.12に置き換えます。
  3. 完了メッセージが表示されたら、新しいoutputsフォルダーを開きます。既存のoutputsがある場合は、先に別名で保管してから再実行します。
bash
python --version
python -m pip install -r requirements.txt
python example.py

インストールにはインターネット接続が必要です。コードのBASEは、現在のターミナル位置ではなくexample.pyの位置を基準に入力と結果のパスを決めます。

04コード全体と処理の順序

load_workbookの読み取り専用モードで元のファイルを開き、Counterで各行の出現回数を数えます。元のExcelの行番号は、enumerateのstart=2で記録します。結果は新しいWorkbookに保存するため、元のファイルを保存し直しません。

example.py
"""Find repeated business rows. Keep the source workbook unchanged."""
from collections import Counter
from pathlib import Path

from openpyxl import Workbook, load_workbook
from openpyxl.styles import Font, PatternFill

BASE = Path(__file__).resolve().parent
HEADERS = ("order_id", "customer", "item", "quantity")


def main():
    destination = BASE / "outputs"
    if destination.exists():
        raise ValueError("outputs already exists; rename it before running again.")
    source = load_workbook(BASE / "orders.xlsx", read_only=True, data_only=False)
    try:
        if "Orders" not in source.sheetnames:
            raise ValueError("Orders sheet is missing.")
        worksheet = source["Orders"]
        rows = list(worksheet.iter_rows(values_only=True))
        if not rows or tuple(rows[0]) != HEADERS:
            raise ValueError(f"Expected headers: {HEADERS}")
        records = []
        for row_number, row in enumerate(rows[1:], start=2):
            if all(value is None for value in row):
                continue
            if any(value is None for value in row):
                raise ValueError(f"Row {row_number}: missing value.")
            if any(isinstance(value, str) and value.startswith("=") for value in row):
                raise ValueError(f"Row {row_number}: formulas are not supported.")
            if not all(isinstance(value, str) and value.strip() for value in row[:3]):
                raise ValueError(f"Row {row_number}: first three fields must be text.")
            if type(row[3]) is not int or row[3] <= 0:
                raise ValueError(f"Row {row_number}: quantity must be a positive integer.")
            records.append((row_number, tuple(row)))
        if not records:
            raise ValueError("No data rows were found.")
    finally:
        source.close()

    counts = Counter(row for _, row in records)
    repeated = [(number, row) for number, row in records if counts[row] > 1]
    unique = [(number, row) for number, row in records if counts[row] == 1]
    book = Workbook()
    book.remove(book.active)
    for name, selected in (("Duplicates", repeated), ("Unique", unique)):
        sheet = book.create_sheet(name)
        sheet.append(("source_row", *HEADERS, "occurrences"))
        for number, row in selected:
            sheet.append((number, *row, counts[row]))
        sheet.freeze_panes = "A2"
        sheet.auto_filter.ref = sheet.dimensions
        sheet.sheet_view.showGridLines = False
        for cell in sheet[1]:
            cell.font = Font(name="Arial", bold=True, color="FFFFFF")
            cell.fill = PatternFill("solid", fgColor="243B53")
        for column, width in zip("ABCDEF", (14, 16, 18, 20, 14, 16)):
            sheet.column_dimensions[column].width = width
    destination.mkdir(exist_ok=False)
    book.save(destination / "duplicate_review.xlsx")
    book.close()
    groups = sum(count > 1 for count in counts.values())
    print(f"Input rows: {len(records)}; duplicate groups: {groups}")
    print(f"Duplicate rows: {len(repeated)}; unique rows: {len(unique)}")
    print("Created outputs/duplicate_review.xlsx (source unchanged).")


if __name__ == "__main__":
    try:
        main()
    except (OSError, ValueError) as error:
        raise SystemExit(f"Stopped: {error}") from error

05実行で作成される結果

outputs/duplicate_review.xlsxにDuplicatesとUniqueの二つのシートが作られます。source_rowは元のExcelの行番号、occurrencesは同じ内容が入力全体に現れた回数です。

項目同梱データの結果
入力データ行数8
重複グループ数2
Duplicatesの行数4(元の2, 3, 5, 8行)
Uniqueの行数4(元の4, 6, 7, 9行)

Duplicatesに4行あるからといって、削除すべき行が4行あるわけではありません。各グループから一行ずつ残すなら余分に入力された行は2行ですが、何を残すかは元のファイルを確認してから決める必要があります。

06結果を自分で確認する

  1. Duplicatesで、source_rowが2と5の行がO-1001で、occurrencesがどちらも2になっているか確認します。
  2. source_rowが3と8の行がO-1002であるか確認します。
  3. Duplicatesの4行とUniqueの4行を足し、入力の8行と一致するか確認します。
  4. orders.xlsxを開き直し、8行のデータがそのまま残っているか確認します。

07よくあるエラーと解決方法

メッセージまたは症状確認すること
Orders sheet is missing元のシート名をOrdersに合わせます。
Expected headers列名、順序、余分な列を確認します。
missing value / formulas are not supported表示された元の行を確認し、数式ではなく値で用意します。
quantity must be a positive integer数量は1以上の整数で入力します。
outputs already exists前回の結果フォルダーを別名にして保管します。
ModuleNotFoundError実行に使うpythonで、requirements.txtのパッケージをインストールします。

08この例で扱わないこと

  • 書式、グラフ、マクロ、接続、行の非表示状態は複製しません。結果には、確認に必要な値と基本的な書式だけを含めます。
  • 空白の除去、大文字・小文字の統一、似た文字列の判定は行いません。重複の定義が変わる場合は、比較基準を別途設計する必要があります。
  • ファイル全体をメモリに集めるため、大容量データでの性能は保証しません。暗号化されたファイルや破損したファイルはサポートしません。
  • 結果ファイルの値を変更しても、元のファイルや結果が自動で再計算されることはありません。新しい結果が必要なら、元ファイルのコピーを修正して再実行します。

実行・検証の記録

Windows 11 (10.0.26200)、CPython 3.12.14(64ビット)。openpyxl 3.1.5、Matplotlib 3.10.8。各例で使用するライブラリのバージョンはrequirements.txtで固定しています。

  • 正常なデータの行数、元の行番号、重複回数を検証
  • 元ファイルのSHA-256が不変で、再実行時の上書きを拒否することを確認
  • ヘッダー不一致、欠損値、数式、不正な数量のエラーを検査
検証範囲の限界
  • Excelデスクトップアプリでの表示や、大容量データでの性能は検証していません。

サイト全体の執筆・検証方針

自分で実行するためのサンプルファイル

コード、入力データ、実行手順が含まれています。展開して、まずREADME.txtを読んでください。

サンプルZIPをダウンロード

サンプルコード、ファイル名、入力キーは原文のままです。翻訳本文のコマンドと確認手順も併せて参照してください。

独自に作成した練習用資料 · 元のファイルを別に保管してから実行してください。

参考資料

説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。