🤖

pythonをチャッピー(ChatGPT)に教えてもらう (Excel自動化ツール) #3

2025/01/09に公開

はじめに

しばらく実務でコーディングの機会がなかったので、定期的にChatGPTから課題をもらって、それを解くことでコーディングの習慣を取り戻したいと思います。

チャッピー(ChatGPT)から与えられた課題

課題: Excel自動化ツール
課題概要: 売上データが格納されたExcelファイルを読み込み、月ごとに集計を行う

ステップ:
1. openpyxlライブラリを使ってExcelファイルを読み込み。
2. 月ごとにデータを集計し、売上額を計算。
3. 加工したデータを新しいExcelファイルとして保存。
難易度: 中級
使用するライブラリ: openpyxl, pandas
追加のヒント:

データの集計には、pandasを併用すると効率的に処理できます。

自分で書いたソースコード

import openpyxl
import pandas as pd
from pandas import DataFrame
from openpyxl.workbook import Workbook

def read_excel(file_path: str) -> DataFrame:
    # Excelファイルを読み込む
    return pd.read_excel(file_path)

def aggregate_sales(data: DataFrame) -> DataFrame:
    # 月ごとに売上を集計
    data['月'] = data['Date'].dt.to_period('M')
    monthly_sales = data.groupby(['月'])['Total Sales'].sum().reset_index()
    return monthly_sales

def save_to_excel(workbook: Workbook, output_path: str) -> None:
    # 新しいExcelファイルとして保存
    workbook.save(output_path)

def main(input_file: str, output_file: str) -> None:
    # メイン処理
    data = read_excel(input_file)
    monthly_sales = aggregate_sales(data)

    # 新しいExcelワークブックを作成
    workbook = openpyxl.Workbook()
    sheet = workbook.active
    sheet.title = "月ごとの売上"

    # ヘッダーを追加
    sheet.append(['月', '売上額'])
    for index, row in monthly_sales.iterrows():
        sheet.append([row['月'].strftime('%Y-%m'), row['Total Sales']])

    save_to_excel(workbook, output_file)

if __name__ == "__main__":
    input_file = 'daily_sales_with_products.xlsx'  # 入力ファイル名
    output_file = '集計結果.xlsx'    # 出力ファイル名
    main(input_file, output_file)

チャッピー(ChatGPT)の評価

90点程度/100点

このコードをさらに改善して実務レベルでの利用に近づけるには、上記の改善案を取り入れるとより高い完成度が期待できます。また、エラーハンドリングやデータフォーマットの強化を行うことで、利用者に優しいツールになります。

リファクタリング

リファクタリングの主なポイント

  • エラー処理
    ファイルの存在確認や列名のチェックを追加。
    日付変換に失敗した場合に対処。

  • 柔軟性
    列名やフォーマットエラーに対応。

  • フォーマット調整
    列幅自動調整やセルの中央揃え。
    ヘッダー行を太字に設定。

  • 再利用性の向上
    Excelフォーマット部分を関数化。

修正後のソースコード

import pandas as pd
from pandas import DataFrame
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font

def read_excel(file_path: str) -> DataFrame:
    """
    Excelファイルを読み込む
    """
    try:
        data = pd.read_excel(file_path)
        if 'Date' not in data.columns or 'Total Sales' not in data.columns:
            raise KeyError("必要な列('Date', 'Total Sales')が存在しません。")
        if not pd.api.types.is_datetime64_any_dtype(data['Date']):
            data['Date'] = pd.to_datetime(data['Date'], errors='coerce')
        return data
    except FileNotFoundError:
        raise FileNotFoundError(f"ファイルが見つかりません: {file_path}")
    except Exception as e:
        raise Exception(f"エラーが発生しました: {e}")

def aggregate_sales(data: DataFrame) -> DataFrame:
    """
    月ごとに売上を集計
    """
    data['月'] = data['Date'].dt.to_period('M')
    monthly_sales = data.groupby(['月'])['Total Sales'].sum().reset_index()
    return monthly_sales

def format_excel_sheet(sheet) -> None:
    """
    Excelシートのフォーマットを調整
    """
    for col in sheet.columns:
        max_length = max(len(str(cell.value)) if cell.value else 0 for cell in col)
        sheet.column_dimensions[col[0].column_letter].width = max_length + 2

    for row in sheet.iter_rows():
        for cell in row:
            cell.alignment = Alignment(horizontal='center', vertical='center')
            if row[0] == sheet[1]:
                cell.font = Font(bold=True)

def save_to_excel(data: DataFrame, output_path: str) -> None:
    """
    集計データをExcelファイルとして保存
    """
    workbook = Workbook()
    sheet = workbook.active
    sheet.title = "月ごとの売上"

    # ヘッダーとデータを追加
    sheet.append(['月', '売上額'])
    for _, row in data.iterrows():
        sheet.append([row['月'].strftime('%Y-%m'), row['Total Sales']])

    # フォーマット調整
    format_excel_sheet(sheet)

    workbook.save(output_path)

def main(input_file: str, output_file: str) -> None:
    """
    メイン処理
    """
    try:
        data = read_excel(input_file)
        monthly_sales = aggregate_sales(data)
        save_to_excel(monthly_sales, output_file)
        print(f"集計結果を保存しました: {output_file}")
    except Exception as e:
        print(f"エラー: {e}")

if __name__ == "__main__":
    input_file = 'daily_sales_with_products.xlsx'  # 入力ファイル名
    output_file = 'monthly_sales_summary.xlsx'     # 出力ファイル名
    main(input_file, output_file)

学んだポイント

  • 忘れがちなエラーハンドリングを意識しよう
  • excelのフォーマット調整など、人に親切な成果物を意識して書く習慣を身に着けたい

Discussion