Excel・文書業務

Excel の重複行を見つけて確認用に分離する

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

目次を表示

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

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

準備するもの
  • Python 3.12 とターミナルを用意します。python --version でバージョンを確認してください。
  • ダウンロードした ZIP 全体を展開し、example.py があるフォルダでターミナルを開きます。
  • まず付属の練習データから始め、その後で結果を元ファイルと比較します。
  • この例では orders.xlsx の Orders シートと openpyxl を使用します。コード実行に Microsoft Excel アプリは必要ありません。

01何を重複とみなすか定義する

この例では、order_id, customer, item, quantity の4つの値がすべて一致した場合にのみ重複とみなします。注文番号だけが一致していても重複にはなりません。文字列内の空白や大文字・小文字は自動変更しません。

何かを削除する前に、最初に出現した行も含め、内容が一致するすべての行を Duplicates シートに集めます。1回しか出現しない行は Unique シートに入れるため、入力された8行すべてを漏れなく確認できます。

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

orders.xlsx のシート名は Orders でなければならず、1行目には以下の列名が同じ順序で入っている必要があります。列を追加した場合もヘッダーエラーになります。実務ファイルをすぐに使うのではなく、まず必要な4列だけをコピーして練習用ファイルを作成してください。

列用途例
order_id文字列としての注文番号O-1001
customer文字列としての顧客名Alpha
item文字列としての品目Sensor
quantity正の整数としての数量2

完全に空の行はスキップされます。一部の値だけが欠けている場合や、行に数式が含まれている場合はスクリプトが停止します。注文番号、顧客名、品目は空でない文字列でなければなりません。数値の注文番号は自動変換されないため、入力ファイルでは文字列として用意してください。

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

  1. ZIP を展開し、README.txt に記載された入力ファイルが example.py と同じフォルダにあることを確認します。ZIP アーカイブ内から直接実行しないでください。
  2. example.py があるフォルダで、以下のコマンドを1行ずつ実行します。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 で各行が何回出現するか数えます。enumerate と start=2 を使って元の Excel 行番号を追跡します。結果は新しい 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 の2つのシートを作成します。source_row は元ファイルでの Excel 行番号、occurrences は入力全体で同じ内容が出現した回数です。

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

Duplicates に4行あるからといって、4行削除すべきという意味ではありません。各グループで1行だけ残す場合、余分な入力は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 integer1 以上の整数を数量として入力します。
outputs already exists以前の結果フォルダの名前を変更して保持します。
ModuleNotFoundErrorexample.py を実行するのと同じ python を使って requirements.txt のパッケージをインストールします。

08この例で扱わないこと

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

実行・検証の記録

Windows 11 (10.0.26200), CPython 3.12.14 (64-bit). openpyxl 3.1.5, Matplotlib 3.10.8. 各例で使用するライブラリは requirements.txt に固定されています。

  • 有効データについて、行数、元の行番号、出現回数を検証しました
  • 元ファイルの SHA-256 ハッシュが変更されていないことと、再実行時の上書きが拒否されることを確認しました
  • ヘッダー不一致、欠損値、数式、不正な数量に対するエラーを確認しました
検証範囲の限界
  • Excel デスクトップアプリでの表示と大規模ファイルでの性能は検証していません。

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

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

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

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

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

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

参考資料

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