【Python開発物語】CSV列抽出スクリプトが「賢いツール」に進化するまで(Pandasと設定ファイル)

ALL

こんにちは、Tech Samuraiです!
今回の記事は、一本のPythonスクリプトが、試行錯誤を経て、より賢く、より使いやすいツールへと「進化」していく物語です。

物語は、「手元にあるCSVファイルから、特定の列だけを抽出して、新しいCSVファイルとして保存したい」という、非常にシンプルな一つの要望から始まりました。この目的を達成するため、私はPythonの力を借りることにしました。


第一章:二つの道の提示

目の前には、二つの道が示されました。

  • 道1:標準ライブラリ「csv」
    Pythonが生まれながらに持つ、堅実で信頼性の高い道。追加の準備は不要で、Pythonの基本的な知識があれば誰でも歩むことができます。
  • 道2:強力なライブラリ「pandas」
    データ分析の世界で絶大な支持を得る、強力なライブラリ。まるで魔法のようにデータを操り、複雑な処理も驚くほど短いコードで実現できます。

第二章:選択と決断 – なぜPandasか

二つの道を比較検討した結果、その優位性は明白でした。

csvモジュールは確かに確実ですが、処理が少し冗長になりがちです。一方、pandasは、

  • コードの簡潔さ: データの読み込み、列の抽出、ファイルへの保存が、それぞれほぼ1行で完結する。
  • 可読性の高さ: 何をしているかが直感的に理解しやすい。
  • 将来の拡張性: 今回は列抽出だけですが、将来的にデータの並べ替えや集計など、より高度な処理をしたくなった場合にも簡単対応できる。

「これは明らかにpandasの方がスッキリしていて、未来的だ!」

私は迷わずpandasの道を選択しました。コードはより洗練され、目的はスマートに達成されたのです。


第三章:新たなる課題 – ハードコーディングの壁

しかし、物語はここで終わりませんでした。完成したスクリプトには、一つの見過ごせない問題が潜んでいたのです。

# スクリプトの初期段階
input_csv_file = 'source_data.csv'
output_csv_file = 'extracted_data.csv'
target_headers = ['氏名', '年齢', '部署']

これらの設定が、スクリプトの中に直接書き込まれていました(ハードコーディング)。もし、別のCSVファイルでこのスクリプトを使いたくなったら?もし、抽出したい列を変更したくなったら?そのたびにコードを直接編集しなければなりません。これは非常に手間がかかるし、誤ってコードの他の部分を壊してしまう危険性もはらんでいました。


第四章:賢者の選択 – 設定ファイルの導入

この課題を解決するため、私はプログラミングの重要な原則に立ち返りました。
「プログラムのロジック(どう動くか)」と「設定(何を使うか)」を分離するのです。

解決策として「設定ファイル」を導入しました。

  1. config.iniの作成:
    ユーザーが編集するための、シンプルな設定ファイルを用意しました。Pythonのコードを触ることなく、入力ファイル名、出力ファイル名、抽出したい列を自由に変更できます。 [SETTINGS] input_csv_file = csv_column_extractor/source_data.csv output_csv_file = csv_column_extractor/extracted_data_pandas.csv target_headers = 氏名,年齢,部署
  2. configparserの活用:
    Pythonスクリプト側では、標準ライブラリconfigparserを使い、このconfig.iniの内容を読み込むように改良しました。

これにより、私のスクリプトは最終形態へと進化しました。もはや、ユーザーはPythonのコードを一行も触る必要はありません。config.iniという名の設計図を書き換えるだけで、あらゆるCSVファイルに対応できる、柔軟で再利用性の高いツールが誕生したのです。


後日談:新たなる敵 – WindowsとExcelの「文字化け」

完成したと思ったツールでしたが、Windows環境のユーザーから「出力されたCSVをExcelで開くと、日本語が文字化けする」という報告が。これは、Python開発者(特にMacやLinuxユーザー)がよく遭遇する、根深い問題です。

原因:

  • Python (Pandas) の出力: 私のスクリプトは、国際標準であるUTF-8という文字コードでCSVファイルを出力していました。これは正しい挙動です。
  • Windows版Excelの挙動: 一方、Windows版のExcelは、CSVファイルをダブルクリックで開く際、古い日本語標準であるShift_JISという文字コードだと想定して開こうとします。

UTF-8で書かれた手紙を、Shift_JISのルールで無理やり読もうとするため、文字化けが発生していたのです。

対策:
解決策は、Excelに「このファイルはUTF-8で書かれていますよ」と正しく教えてあげることです。そのための最も簡単な方法は、BOM (Byte Order Mark) 付きのUTF-8でファイルを出力することです。Pandasでは、encoding`utf-8-sig` を指定するだけで、このBOM付きUTF-8ファイルを簡単に作成できます。


完成したツールとコード(改良版)

こうして、単なる目的達成のためのコードは、クロスプラットフォームの問題も解決した、誰でも安全かつ簡単に使える「賢いスクリプト」へと昇華しました。

この開発の物語を経て完成したツールの全コードは、以下のGitHubリポジトリで公開しています。

# 最終的なスクリプト (extract_csv.py)
import pandas as pd
import configparser
import sys

def load_config(filename='csv_column_extractor/config.ini'):
    """設定ファイル(config.ini)を読み込む"""
    config = configparser.ConfigParser()

    if not config.read(filename, encoding='utf-8'):
        print(f"エラー: 設定ファイル '{filename}' が見つからないか、空です。")
        sys.exit(1)

    settings = config['SETTINGS']

    headers_str = settings.get('target_headers', '')
    headers_list = [header.strip() for header in headers_str.split(',') if header.strip()]

    return {
        'input': settings.get('input_csv_file', ''),
        'output': settings.get('output_csv_file', ''),
        'headers': headers_list
    }

def extract_columns_with_pandas(input_filename, output_filename, headers_to_keep):
    """
    pandasを使用して、CSVファイルから指定された列を抽出します。
    """
    if not input_filename or not output_filename or not headers_to_keep:
        print("エラー: 設定ファイルの値が不足しています。")
        return

    try:
        df = pd.read_csv(input_filename, encoding='utf-8')

        existing_headers = [h for h in headers_to_keep if h in df.columns]
        missing = set(headers_to_keep) - set(existing_headers)
        if missing:
            print(f"⚠️ 警告: 次のヘッダーはCSVファイルに存在しませんでした: {', '.join(missing)}")

        if not existing_headers:
            print("エラー: 抽出対象のヘッダーがCSVファイルに一つも存在しません。")
            return

        df_extracted = df[existing_headers]

        # ▼▼▼ ここを修正しました ▼▼▼
        # Excelで日本語が文字化けしないように、BOM付きUTF-8(utf-8-sig)で出力
        df_extracted.to_csv(output_filename, index=False, encoding='utf-8-sig')
        # ▲▲▲ 変更ここまで ▲▲▲

        print(f"✅ 処理が完了しました。'{output_filename}' に結果を保存しました。")

    except FileNotFoundError:
        print(f"エラー: 入力ファイル '{input_filename}' が見つかりません。")
    except Exception as e:
        print(f"予期せぬエラーが発生しました: {e}")

if __name__ == '__main__':
    config = load_config()
    extract_columns_with_pandas(
        input_filename=config['input'],
        output_filename=config['output'],
        headers_to_keep=config['headers']
    )

コメント

タイトルとURLをコピーしました