ログアウト中 ログイン

【Python】Pandas版 売上データ集計ツール開発講座 第3章3.2節「コマンドライン・ログ機能の構築」




現在の見出し:3.2節:コマンドライン・ログ機能の構築

見出し一覧

  • 3.2節:コマンドライン・ログ機能の構築
  • - argparse.ArgumentParser()によるコマンドライン引数処理機能の実装
  • - logging.basicConfig()によるログ出力機能の追加

【PR】『Python』を学べる企業・個人向けのプログラミングコース

argparse.ArgumentParser()によるコマンドライン引数処理機能の実装

前節(第3章 3.1節)で作成したコードを基盤として、argparse.ArgumentParser()によるコマンドライン引数の処理機能を実装します。現在の実装では、入力ファイルパスファイルパスはコンピュータ上でファイルを特定するための指示書のようなものです。と出力ファイルパスがソースコードソースコードはプログラミング言語で書かれたテキストファイルで、コンピューターが理解して実行できる命令や手順が含まれています。内に固定値として記述されているため、異なるファイルを処理する際にプログラムプログラムはコンピュータに対して何をすべきかを指示する一連の命令です。を修正する必要があり、実用性が制限されています。

argparse.ArgumentParser()を使用することで、コマンドライン実行時にファイルパスを動的に指定できるようになります。この機能により、同一プログラムで複数の異なるExcelファイルを処理でき、出力先も自由に変更可能になります。引数の設定には「必須引数」と「オプション引数」の2種類があり、それぞれ異なる用途で使用します。

引数タイプ 記述方法 特徴 用途例
必須引数 引数名のみ 実行時に必ず指定が必要 入力ファイルパス
オプション引数 -o, --output 省略可能、デフォルト値設定可能 出力ファイルパス

以下が前節のコードに、コマンドライン引数の処理機能を追加したプログラムです。parse_arguments関数関数は一定の処理をまとめたプログラムの塊です。を新規追加することで引数処理を独立管理し、main関数内の固定パスパスはファイルやディレクトリの場所を示すために使われる言葉です。を動的取得方式に変更しています。また、helpパラメータの設定により、ユーザーが「python プログラム名.py --help」を実行した際に適切な使用方法が表示され、プログラムの操作性が大幅に向上します。

# コード
import pandas as pd
import os
from openpyxl import load_workbook
from openpyxl.chart import LineChart, Reference
import argparse

def load_sales_data(filepath):
    """売上データの読み込み"""
    if not os.path.exists(filepath):
        print(f"エラー: ファイルが見つかりません - {filepath}")
        return None
    
    df = pd.read_excel(filepath)
    print(f"データ読み込み完了: {len(df)}行, {len(df.columns)}列")
    print("データ構造:")
    print(df.head())
    return df

def preprocess_data(df):
    """データ前処理"""
    if df is None:
        return None
    
    print(f"元データ: {len(df)}行")
    
    # 日付変換
    df['Date'] = pd.to_datetime(df['Date']).dt.strftime("%Y-%m")
    print("日付変換完了")
    
    # 欠損値除去
    original_rows = len(df)
    df = df.dropna()
    print(f"欠損値除去: {original_rows - len(df)}行削除")
    
    print("処理済みデータ:")
    print(df.head())
    return df

def analyze_basic_sales(df):
    """基本売上分析"""
    if df is None:
        return None
    
    # 商品・地域リスト抽出
    products = sorted(df['Product'].unique())
    areas = sorted(df['Area'].unique())
    
    print(f"商品数: {len(products)}")
    print(f"地域数: {len(areas)}")
    print(f"商品リスト: {products}")
    print(f"地域リスト: {areas}")
    
    # 月別商品別集計
    monthly_product_sales = df.groupby(['Date', 'Product'])['Price'].sum().reset_index()
    print("\n月別商品別売上:")
    print(monthly_product_sales.head(10))
    
    return {
        'products': products,
        'areas': areas,
        'monthly_product': monthly_product_sales
    }

def create_pivot_analysis(df, basic_results):
    """ピボット分析"""
    if df is None or basic_results is None:
        return basic_results
    
    # ピボットテーブル作成(月×地域)
    pivot_table = pd.pivot_table(df, 
                                index='Date', 
                                columns='Area', 
                                values='Price', 
                                aggfunc='sum', 
                                fill_value=0)
    print("\n地域別売上ピボットテーブル:")
    print(pivot_table)
    
    # 商品別売上推移
    product_trend = df.groupby(['Date', 'Product'])['Price'].sum().unstack(fill_value=0)
    print("\n商品別売上推移:")
    print(product_trend)
    
    # 既存結果に新しい分析結果を追加
    basic_results['pivot_table'] = pivot_table
    basic_results['product_trend'] = product_trend
    
    return basic_results

def create_excel_report(analysis_results, output_path):
    """Excel形式の分析レポート作成"""
    if analysis_results is None:
        print("分析結果がありません")
        return False
    
    os.makedirs(os.path.dirname(output_path), exist_ok=True)
    
    # 基本出力
    with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
        analysis_results['pivot_table'].to_excel(writer, sheet_name='地域別売上')
        analysis_results['product_trend'].to_excel(writer, sheet_name='商品別推移')
    
    print(f"Excel基本出力完了: {output_path}")
    return True

def add_sales_chart(output_path, product_trend_df):
    """売上推移グラフの追加"""
    wb = load_workbook(output_path)
    ws = wb['商品別推移']
    
    chart = LineChart()
    chart.title = "商品別売上推移"
    chart.x_axis.title = "月"
    chart.y_axis.title = "売上額"
    
    data_rows = len(product_trend_df) + 1
    data_cols = len(product_trend_df.columns) + 1
    
    data = Reference(ws, min_col=2, min_row=1, max_row=data_rows, max_col=data_cols)
    cats = Reference(ws, min_col=1, min_row=2, max_row=data_rows)
    
    chart.add_data(data, titles_from_data=True)
    chart.set_categories(cats)
    ws.add_chart(chart, "G2")
    
    wb.save(output_path)
    print(f"グラフ追加完了: {output_path}")

def parse_arguments():
    """コマンドライン引数の解析"""
    parser = argparse.ArgumentParser(description='売上データ集計ツール')
    parser.add_argument('input_file', help='入力Excelファイルパス')
    parser.add_argument('-o', '--output', default='output/sales_report.xlsx', 
                       help='出力ファイルパス(デフォルト: output/sales_report.xlsx)')
    
    return parser.parse_args()

# メイン処理
def main():
    # コマンドライン引数処理
    args = parse_arguments()
    
    filepath = args.input_file
    output_path = args.output
    
    df = load_sales_data(filepath)
    
    if df is not None:
        print("読み込み成功!")
        processed_df = preprocess_data(df)
        if processed_df is not None:
            print(f"最終データ: {len(processed_df)}行")
            print("データ型:")
            print(processed_df.dtypes)
            
            analysis_results = analyze_basic_sales(processed_df)
            if analysis_results:
                print(f"\n基本分析完了 - 商品数: {len(analysis_results['products'])}")
                
                full_results = create_pivot_analysis(processed_df, analysis_results)
                print(f"ピボット分析完了 - 地域数: {len(full_results['areas'])}")
                
                if create_excel_report(full_results, output_path):
                    add_sales_chart(output_path, full_results['product_trend'])
                    print("レポート作成完了!")
    else:
        print("読み込み失敗")

if __name__ == "__main__":
    main()
# 実行結果
python プログラム名.py Excel名.xlsx -o フォルダ名/出力Excel名.xlsx
データ読み込み完了: 5行, 4列
データ構造:
    Product    Area       Date  Price
0  ProductA   Tokyo 2024-01-15   1000
1  ProductB   Osaka 2024-01-16   1500
2  ProductC   Tokyo 2024-01-17   2000
3  ProductA  Nagoya 2024-01-18   1200
4  ProductB   Tokyo 2024-01-19   1800
読み込み成功!
元データ: 5行
日付変換完了
欠損値除去: 0行削除
処理済みデータ:
    Product    Area     Date  Price
0  ProductA   Tokyo  2024-01   1000
1  ProductB   Osaka  2024-01   1500
2  ProductC   Tokyo  2024-01   2000
3  ProductA  Nagoya  2024-01   1200
4  ProductB   Tokyo  2024-01   1800
最終データ: 5行
データ型:
Product    object
Area       object
Date       object
Price       int64
dtype: object
商品数: 3
地域数: 3
商品リスト: ['ProductA', 'ProductB', 'ProductC']
地域リスト: ['Nagoya', 'Osaka', 'Tokyo']

月別商品別売上:
      Date   Product  Price
0  2024-01  ProductA   2200
1  2024-01  ProductB   3300
2  2024-01  ProductC   2000

基本分析完了 - 商品数: 3

地域別売上ピボットテーブル:
Area     Nagoya  Osaka  Tokyo
Date
2024-01    1200   1500   4800

商品別売上推移:
Product  ProductA  ProductB  ProductC
Date
2024-01      2200      3300      2000
ピボット分析完了 - 地域数: 3
Excel基本出力完了: reports/monthly_report.xlsx
グラフ追加完了: reports/monthly_report.xlsx
レポート作成完了!
行数 種別 コード 解説
6行目 新規 import argparse コマンドライン引数解析のためのargparseモジュールモジュールはプログラミングで機能や処理をまとめた再利用可能な単位で、コードの管理や保守を容易にします。をインポートします。動的なファイルパス指定機能を実現するための重要なライブラリライブラリは再利用可能なソフトウェアの部品です。です。
130行目 新規 def parse_arguments(): コマンドライン引数解析専用の関数を定義します。引数の定義と解析処理を独立して管理し、メイン処理から分離します。
132行目 新規 parser = argparse.ArgumentParser(description='売上データ集計ツール') ArgumentParserオブジェクトオブジェクトはプログラムの要素の一つで、データとその操作手段を一つにまとめたものです。を作成してプログラムの説明文を設定します。ヘルプ表示時にユーザーに分かりやすい情報を提供します。
133行目 新規 parser.add_argument('input_file', help='入力Excelファイルパス') 必須の位置引数として入力ファイルパスを定義します。実行時に必ずExcelファイルパスの指定が求められるようになります。
134行目 新規 parser.add_argument('-o', '--output', default='output/sales_report.xlsx', オプショナル引数として出力ファイルパスを定義します。短縮形と完全形の両方を提供し、デフォルト値も設定します。
142行目 新規 args = parse_arguments() 引数解析関数を呼び出してコマンドライン引数を取得します。実行時に指定された引数値を変数変数はデータを一時的に記憶しておく場所です。として利用可能にします。
144行目 変更 filepath = args.input_file 前のセクションの固定値「sample.xlsx」を、コマンドライン引数で取得した動的な値に変更します。柔軟なファイル処理を実現します。
145行目 変更 output_path = args.output 前のセクションの固定出力パスを、引数で指定された動的な値に変更します。ユーザーが任意の出力先を選択できるようになります。
logging.basicConfig()によるログ出力機能の追加

第3章3.2節「logging.basicConfig()によるログ出力機能の追加」の続きを見る


すべてのカリキュラムを無料登録で学ぶ

プログラミング・マーケティング・AI活用方法を
今すぐ無料登録すると、全てのコンテンツが視聴可能になります。