Python 1ファイルに全ステップ分が記述されたcsvから散布図を作成する「時間に関する列がある場合」

 '21/02/26更新:本文更新。
 下図のように行方向に全ステップの時系列データがあるとします。この例では、一番左の列に時間に関するデータがあります。

f:id:HK29:20191230202525p:plain

 本プログラムを実行すると、下図のように散布図を作成します。この時、指定した2つには赤と青の色を付けて凡例も付ける。それ以外は黒点線とする仕様。ちなみに、列名「material」の「Au」を指定してその行データだけを抽出している。グラフタイトルには、処理したファイルがわかるようにファイルパスを表示するようにした。

f:id:HK29:20200125140914j:plain

ちなみに、下記のような複数のフォルダとファイル構成においても、再帰的にフォルダを探索し、フォルダ別」csvファイル別に「グラフ化」処理する仕様にしました。

カレントフォルダ
├─testA
│ ├─test_data_1.csv
│ ├─test_data_11.csv
│ ├─test_data_2.csv
│ └─test_data_3.csv
│ …
├─testB
│ ├─test_data_21.csv
│ ├─test_data_22.csv
│ └─test_data_23.csv
│ …
├─testC
│ ├─test_data_1.csv
│ ├─test_data_10.csv
│ ├─test_data_20.csv
│ ├─test_data_30.csv
│ ├─test_data_50.csv
│ └─test_data_100.csv

そうすることで、例えば右記リンク先、VBA サブフォルダ内の全ての画像ファイルをExcelに貼り付ける - HK29’s blog を利用した場合に、下図のようにフォルダ別にグラフを比較することができるようになったりする。ちなみに、下図は例のため実施したものであり、データは全て同じでフォルダ名とファイル名だけ変更して動作テストした結果です。

f:id:HK29:20200125142015p:plain

■本プログラム
複数のcsvファイルに対して、それぞれの画像ファイルを作成する仕様にしています。### parameter 以下で、ファイルパスや、抽出する列名やグラフレンジなどを指定出来るようにしている。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import datetime
now = datetime.datetime.now()
now = now.strftime("%y%m%d")

def plot_graph(my_file, my_save_dir):
    # ファイルパスから名前を取得
    file_name = os.path.basename(my_file)
    
    # ファイル名から、文字列「_」で分割してリスト化する
    file_name_list = file_name.split('_')
    
    # csvファイルを読み込む
    df = pd.read_csv(my_file, sep=',', header=(my_header-1), encoding="Shift-JIS" ) # "utf-8" "Shift-JIS"
    #print("df -> " + str(df))
    
    # 特定の列で指定した値(文字)が含まれる行を残す
    df2 = df[df[my_check_column].str.contains(my_check_column_target, regex=False)]
    print("df2 -> " + str(df2))
    
    # ダブりを省き、x軸とする値をリスト化
    df3 = df2.drop_duplicates(my_legend, keep='first')
    # 数値型へ変換
    df4 = df3[my_legend].astype(str)
    legend_list = list(df4)
    print("legend_list -> " + str(legend_list))

    y_name=""
    x_value_list = []
    y_value_list = []
    for i, legend in enumerate(legend_list):
        # 型は文字列である必要がる
        #df5 = df2[df2[my_legend].str.endswith(str(legend), na=False)]
        df5 = df2[df2[my_legend].astype(str).str.contains(str(legend), regex=False)]
        print("df5 -> " + str(df5))
        x_df = df5[my_x_column].astype(float)
        y_df = df5[my_y_column].astype(float)
  
        plt.figure(num=1, figsize=(8,6), dpi=100, facecolor='w', edgecolor='k')
        if legend == my_legend_target[0]:
            if my_calc == 'mean':
                my_comment = 'mean of '
                my_y_value = y_df.mean()
            elif my_calc == 'min':
                my_comment = 'min of '
                my_y_value = y_df.min()
            else:
                my_comment = 'max of '
                my_y_value = y_df.max() 
            y_name = my_y_column + "_" + my_comment
            plt.scatter(x_df, y_df, s=60, c='r',
                        label=my_legend + " " + str("{:}".format(legend)) + " " + my_legend_unit + '\n'
                        + y_name + " " + str("{:.1f}".format(my_y_value)) + " " + my_y_unit)
            plt.plot(x_df, y_df, c="r")
            plt.legend(loc='best', numpoints=1, fontsize=18)
            x_value_list.append(legend)
            y_value_list.append(my_y_value)
            
        elif legend == my_legend_target[1]:
            if my_calc == 'mean':
                my_comment = 'mean of '
                my_y_value = y_df.mean()
            elif my_calc == 'min':
                my_comment = 'min of '
                my_y_value = y_df.min()
            else:
                my_comment = 'max of '
                my_y_value = y_df.max() 
            y_name = my_y_column + "_" + my_comment
            plt.scatter(x_df, y_df, s=60, c='b',
                        label=my_legend + " " + str("{:}".format(legend)) + " " + my_legend_unit + '\n'
                        + y_name + " " + str("{:.1f}".format(my_y_value)) + " " + my_y_unit)
            plt.plot(x_df, y_df, c="b")
            plt.legend(loc='best', numpoints=1, fontsize=18)
        elif i % my_out_step == 0:
            plt.scatter(x_df, y_df, s=20, facecolor='none', edgecolor='k')
            plt.plot(x_df, y_df, c="k", linewidth=1, linestyle="dotted") # "dashed")
        else:
            pass
    plt.title(my_file, fontsize=12)
    plt.xlabel(my_x_column, fontsize=20)
    plt.ylabel(my_y_column, fontsize=20)
    if my_y_range:
        plt.ylim(my_y_range[0], my_y_range[1])
    plt.grid()
    plt.tick_params(labelsize=18)
    plt.tight_layout()
    my_save_file_path = my_save_dir + '/' + now + "_" + file_name \
                        + "_" + my_x_column + "_" + my_y_column + ".jpg"
    plt.savefig(my_save_file_path)
    plt.close()
    
    return [file_name, my_legend, y_name, x_value_list, y_value_list]

def create_csv(my_save_dir, num, file_name, x_name, y_name, x, y):
    with open(my_save_dir + '/' + now + x_name + '_' + str(x) + '.csv', 'a', encoding="utf-8") as f:
        if num == 0:
            row_list = ["fileNo", my_legend, y_name]
            row_str = ','.join(row_list)
            f.write(row_str + '\n')
        row_list = [file_name, str(x), str(y)]
        row_str = ','.join(row_list)
        f.write(row_str + '\n')

def main():
    for dirpath, dirnames, fnames in os.walk('./'):
        my_save_dir = ''
        for num, my_file in enumerate(fnames):
            print(num, my_file)
            if '.csv' in my_file:
                print(my_file)
                if num==0:
                    my_save_dir = dirpath + '_jpg'
                    os.mkdir(my_save_dir)
                filepath = dirpath + '/' + my_file        
                # 散布図を作成する関数呼び出し
                file_No, my_legend, y_name, x_value_list, y_value_list = plot_graph(filepath, my_save_dir)
                for x, y in zip(x_value_list, y_value_list):
                    # csvファイルを作成する関数呼び出し
                    create_csv(my_save_dir, num, file_No, my_legend, y_name, x, y)

if __name__ == '__main__':
    ### parameter
    my_header = 1
    my_check_column = "material"
    my_check_column_target = "Au"
    my_legend = "Time"
    my_legend_target = ('3', '5')
    my_legend_unit = "min"
    my_x_column = "x"
    my_y_column = "stress"
    my_y_unit = "MPa"
    my_y_range = (50, 900) # レンジの範囲を指定しない場合は空()
    my_calc = "max" # mean or min or max 
    my_out_step = 1
    
    ### call function
    main()
    print('finished')

●類似コード

hk29.hatenablog.jp

以上

<広告>