解析エンジニアの自動化 blog

コツコツと自動化した方法を残す blog

Python で曲線近似(フィッティング)テスト用データの作成



こんにちは。
仕事の自動化にやりがいと達成感を感じるガッくんです。


この記事の目次



背景・目的


曲線近似のプログラムを作っていく上で、テストは欠かせません。

もちろん、どんなプログラムを作るにしてもテストは必要ですが…

そこで、テスト用のある程度複雑な曲線を作り、 その曲線上の点をランダムに抽出して、誤差を与えます。

そして、 Microsoft Word ファイルと csv ファイルにそれぞれ曲線仕様、抽出点仕様および抽出点の座標データを出力するプログラムを作ります。



動作環境


Windows 7
・winpython 64bit 3.4.4



プログラム

ソースコード


###############################################################################
# 曲線近似(フィッティング)テスト用データの作成プログラム
###############################################################################
#==============================================================================
# ライブラリインポート
#==============================================================================
import numpy as np
import matplotlib.pyplot as plt
import docx
from docx.enum.text import WD_ALIGN_PARAGRAPH
 
#==============================================================================
# グラフ描画関数
#==============================================================================
def wave_graph(x, y, x1, y1, xmin, xmax, ymin, ymax):
    plt.figure(figsize=(5.5, 3.5))
   
    # 元々の値の出力
    plt.plot(x, y, 'ro', label="original data point")
    plt.plot(x, y, 'r')
   
    # 誤差を含んだ値の出力
    plt.plot(x1, y1, 'bo', label="data point for test")
    plt.plot(x1, y1, 'b')
   
    # グラフのタイトル・目盛設定
    plt.title("wave for test")
    plt.xlim([xmin, xmax])
    plt.ylim([ymin, ymax])
   
    # 凡例の位置設定
    plt.legend(loc='upper right')
   
    # 格子の設定
    plt.grid(which='major', color='black', linestyle='-')
    plt.grid(which='minor', color='black', linestyle='-')
   
    # グラフ出力
    file_name = 'wave for test.jpg'
    plt.savefig(file_name)
   
    # 表示
    plt.show()
   
    return file_name
 
def err_graph(x, y, xmin, xmax, ymin, ymax):
    plt.figure(figsize=(5.5, 3.5))
   
    # 各 x 値における誤差の出力
    plt.bar(x, y, linewidth = 0, width = 0.0007)
    plt.plot(x, y, 'ro', label="error of data point")
    plt.plot(x, y, 'r')
   
    # グラフのタイトル・目盛設定
    plt.title("error of wave for test")
    plt.xlim([xmin, xmax])
    plt.ylim([ymin, ymax])
   
    # 凡例の位置設定
    plt.legend(loc='upper right')
   
    # 誤差の表示
    max_err = max(y)
    min_err = min(y)
    plt.text(xmin+np.abs((xmax-xmin)*0.01), ymin+np.abs((ymax-ymin)*0.07), 'upper error = ' + str(max_err))
    plt.text(xmin+np.abs((xmax-xmin)*0.01), ymin+np.abs((ymax-ymin)*0.01), 'lower error = ' + str(min_err))
   
    # 格子の設定
    plt.grid(which='major', color='black', linestyle='-')
    plt.grid(which='minor', color='black', linestyle='-')
   
    # グラフ出力
    file_name = 'error of wave for test.jpg'
    plt.savefig(file_name)
   
    # 表示
    plt.show()
   
    return file_name
 
#==============================================================================
# テスト用データを作成する関数 Wave の実行結果報告書作成関数
#==============================================================================
def DocumentReport(wave_number, data_number, dt, ampl, freq, t, f, img_name_wave, img_name_err):
   
    # add_heading の第2引数
    # 0 : 表題
    # 1 : 見出し1
    # 2 : 見出し2
    # 3 : 見出し3
    # 4 : 見出し4
   
    # Microsoft Word オブジェクト生成
    doc = docx.Document()
   
    # 見出し:表題の追加
    doc.add_heading('曲線近似(フィッティング)向けテスト用データの作成', 0)
   
    # 見出し:見出し1
    doc.add_heading('テスト用データの作成方法', 1)
    doc.add_paragraph(' テスト用データは正弦波の重ね合わせによって作成する。\n')
   
    # 見出し:見出し1
    doc.add_heading('テスト用データの作成仕様', 1)
    doc.add_paragraph(' テスト用データの作成仕様を列挙する。\n')
    doc.add_paragraph('  合成正弦波数:' + str(wave_number) + '波')
    doc.add_paragraph('  サンプリング点数:' + str(data_number) + '点')
    doc.add_paragraph('  サンプリング周期:' + str(dt) + '秒')
    doc.add_paragraph('  正弦波式: A × sin( 2 × π × f × t )\n')
   
    # 見出し:見出し2
    doc.add_heading('正弦波式', 2)
    doc.add_paragraph(' テスト用データの正弦波の重ね合わせに使用した正弦波式を列挙する。')
    for i in range(len(ampl)):
        doc.add_paragraph('  ' + str(ampl[i]) + ' × sin( 2 × π × ' + str(freq[i]) + ' × t )')
    doc.add_paragraph('')
   
    # 見出し:見出し1
    doc.add_heading('作成結果', 1)
    doc.add_paragraph(' 作成したテスト用データを図1に示す。また、付加した誤差を図2に示す。なお、テスト用データのデジタルデータをまとめる。')
   
    doc.add_picture(img_name_wave) # 作成したテスト用データグラフの挿入
    p = doc.add_paragraph('図1 テスト用データ\n')
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
   
    doc.add_picture(img_name_err) # 付加した誤差グラフの挿入
    p = doc.add_paragraph('図2 付加した誤差\n')
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
   
    doc.add_page_break() # 改ページ
   
    # 見出し:見出し1
    doc.add_heading('デジタルデータ', 0)
    doc.add_paragraph('time[sec],amplitude')
    for i in range(len(f)):
        doc.add_paragraph(str(t[i]) + ',' + str(f[i]))
   
    # 保存
    doc.save('C:\\Users\\UserName\\Desktop\\wave for test.docx')
 
#==============================================================================
# 作成したテスト用データのデジタルデータを csv 出力する関数
#==============================================================================
def WriteWaveCsv(t, f):
    w = np.stack([t, f])
    np.savetxt('C:\\Users\\UserName\\Desktop\\wave for test.csv', w.T, delimiter=',')
 
#==============================================================================
# テスト用データ作成
#==============================================================================
# 値の準備 =====================================================================
N = 2**5                           # サンプリング数
dt = 0.001                         # サンプリング周期
ampl = np.array([ 1,  1,  2])      # 振幅
freq = np.array([60, 45, 100])     # 周波数
peri_axis = np.arange(0, N*dt, dt) # 周期軸
error = 0.1                        # 誤差係数
for i in range(len(freq)):
    if i==0:
        origin_y = ampl[i] * np.sin(2*np.pi*freq[i]*peri_axis)                       # 入力波形
        y = ampl[i] * np.sin(2*np.pi*freq[i]*peri_axis) + error * np.random.randn(N) # 誤差を与えた入力波形
    else:
        origin_y += ampl[i] * np.sin(2*np.pi*freq[i]*peri_axis)                       # 入力波形
        y += ampl[i] * np.sin(2*np.pi*freq[i]*peri_axis) + error * np.random.randn(N) # 誤差を与えた入力波形
 
# 与えた誤差の計算 ==============================================================
err = (y) - (origin_y)
 
# グラフ出力設定 ================================================================
xmin = min(peri_axis) - (3*dt) # グラフの x 軸の最小値設定
xmax = max(peri_axis) + (3*dt) # グラフの x 軸の最大値設定
ymin = min(y) * 1.5            # グラフの y 軸の最小値設定
ymax = max(y) * 1.5            # グラフの y 軸の最大値設定
x_scale_interval = 1           # x 方向目盛間隔
y_scale_interval = 1           # y 方向目盛間隔
 
max_err = max(err)
min_err = min(err)
ymin_err = min_err * 1.5
ymax_err = max_err * 1.5
 
# グラフ出力 ====================================================================
img_name_wave = wave_graph(peri_axis, origin_y, peri_axis, y, xmin, xmax, ymin, ymax)
img_name_err  = err_graph(peri_axis, err, xmin, xmax, ymin_err, ymax_err)
 
# テスト用データ作成報告書の作成 ===================================================
DocumentReport(len(ampl), N, dt, ampl, freq, peri_axis, y, img_name_wave, img_name_err)
 
# テスト用データの csv 出力 =======================================================
WriteWaveCsv(peri_axis, y)



結果

このプログラムから出力される曲線近似(フィッティング)テスト用データのグラフ、テスト用データに付加した誤差のグラフ、 Word ファイル、 csv ファイルをそれぞれ図1 〜 図4 に示します。

図1 テスト用データグラフ


図2 誤差グラフ


図3 Word ファイル(冒頭)


図4 csv ファイル(冒頭)



コメント

曲線近似プログラムをテストするときはこのプログラムでテスト用データを大量に作成して、即座にテストに着手できるようになったかと思います。

以前の記事で波形のデータを作成するプログラムを作ったことがありました。

しかし、作成した波形データに誤差を与え忘れている事に気がつきました。



以上