Pythonスクリプトをパラメトリックに計算させるためのドライバースクリプト


これはなに

自作のTimerライブラリーの誤差を測定するために作成した、ドライバースクリプトです。
あるpythonスクリプトを、異なる条件で計算させるためのドライバーツールです。どのように表現してよいかわかりませんでしたが、WebDriverをイメージして、ドライバーと表現しました。
Pythonスクリプトをドライブするための、ドライバースクリプトという感じです。

ドライバースクリプト

python
from timer import Timer

def main():
    seconds = 5
    interval_set = [0.01,0.025,0.05,0.1,0.15,0.2,0.25,0.5,0.75,1,2,2.5,3,3.5,4,4.5]

    num = len(interval_set) + 1

    for case in range(1,num):
        interval = interval_set[case-1]
        timer = Timer(seconds, interval)
        timer.test(seconds, interval, case)

if __name__ == '__main__':
    main()

これを、実行するとtimer.pyにあるtestinterval_setに指定された値ごとに実行し結果を記録します。
timer.pyの前半部分は再掲になりますが、以下のとおりです。

python
import time
import csv
import platform

class Timer:
    def __init__(self, seconds, interval):
        self.interval = interval
        self.seconds = seconds
        self.n = int((seconds - (seconds % interval))/interval)

    def timer(self, seconds,interval):
        time_start = time.perf_counter()
        time.sleep(self.interval*self.n)
        time_stop = time.perf_counter()
        self.remain = seconds - (time_stop - time_start)
        if self.remain > 0: # 補正を入れるとするとココかな
            time.sleep(self.remain)
        else:
            pass
        self.real = time.perf_counter() - time_start
        self.error = self.real - seconds
        return False

    def test(self,seconds, interval, case):
        filename = f"./error_data{case}"+"_"+str(platform.system())+".csv" # フォーマット済み文字列リテラル
        with open(filename, 'w') as f: # wは新規作成
            f.write('seconds(sec),interval(sec),real(sec),error(msec)\n')
            f.close()
        for i in range(1000): # 計測回の設定
            while self.timer(seconds, interval):
                pass
            else:
                print('ループ'+str(case)+' '+str(i+1)+'回目 '+'interval=' + str(interval) + 'sec Error:'+str(self.error*1000)+'msec')
                save_data = [self.seconds,self.interval,self.real, self.error*1000]
                with open(filename, 'a', newline='') as f:
                    writer = csv.writer(f, lineterminator='\r\n')
                    writer.writerow(save_data)

この例では16のintervalの値ごと、1000回ほど誤差の測定を行っています。
なかなかの待ち時間なので、一回づつ実行するのは修行に近いですね。

こういう仕事こそコンピューターの出番です。

main.pyを実行することによりerror_data1_Darwin.csvに、結果が記録されます。
Macで実行しているので、Darwinになっています。ラズパイで実行すると、Linuxになります。

$ python main.py

可視化

大量のグラフをいちいち表示していたら心が折れそうになるので、以下のスクリプトでグラフ化しています。

python
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

num_figure = 9    # 描画したいcsvファイルの数

fig = plt.figure(figsize=(10.0, 15.0))


for i in range(1, num_figure+1):
    df = pd.read_csv('./data/error_data{}_Darwin.csv'.format(i))    # csvファイル読み込み
    df_error = df['error(sec)']   # Error列の読み込み
    df_param = df['interval(sec)'] # Interval列の読み込み
    df_sec = df['seconds(sec)'] # Seconds列の読み込み

    ax = fig.add_subplot(5, 2, i)  # 5行2列の配置で順に描画してくれます。
    sns.distplot(df_error, kde=False, rug=False, bins=100) 
#    plt.hist(df_error,bins=100, alpha=0.4, histtype='stepfilled', color='g')
    ax.set_title("Case{}".format(i)+' ('+str(df_sec[1])+'sec Timer, Int.='+str(df_param[1])+'sec)')  # グラフにタイトルをつける
    ax.set_ylim(0,25) # y軸の範囲を設定
    ax.set_xlim(-0.001,0.011) # x軸の範囲を設定

plt.tight_layout() # グラフの間隔を調整
plt.savefig('Error.png')
plt.show()

あっという間に、グラフが描画されます。
グラフの並べ方を変えただけで、再掲になります。

「こうしたらもっと良いよ」など、コメントいただけると嬉しく思います。