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

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

Python で複雑な波形データを作る



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


この記事の目次



背景・目的


Python で分析をするにあたり、今のところ波形データの分析が取り組みやすく、簡単だなぁ…と思っているところです。

分析対象の波形作成を簡単かつスピーディにするためのプログラムを作りました。

Microsoft Word で作成した波形の仕様書もついでに出力するプログラムにしました。

Microsoft Word の操作は下の記事でまとめています。
Python で docx を使って Microsoft Word を操作する - 解析エンジニアの自動化 blog



動作環境


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(wave_number, data_number, dt):
   
    ampl        = np.random.rand(wave_number) # 0.0 以上 1.0 未満の乱数
    freq        = np.random.rand(wave_number) # 0.0 以上 1.0 未満の乱数
    t           = np.arange(0, data_number*dt, dt)
   
    for i in range(0, wave_number):
        if i==0:
            f  = ampl[i] * np.sin(2 * np.pi * freq[i] * t)
        else:
            f += ampl[i] * np.sin(2 * np.pi * freq[i] * t)
   
    # グラフ表示
    plt.plot(t, f)
    plt.grid(True)
    plt.title('Wave')
    plt.xlabel('time[sec]')
    plt.ylabel('amplitude')
   
    # グラフ出力
    file_name = 'wave.jpg'
    plt.savefig(file_name)
   
    return ampl, freq, t, f, file_name
 
#==============================================================================
# 試験用波形を作成する関数 Wave の実行結果報告書作成関数
#==============================================================================
def DocumentReport(wave_number, data_number, dt, ampl, freq, t, f, img_name):
   
    # 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に示し、試験用波形のデジタルデータをまとめる。')
    doc.add_picture(img_name) # 作成した波形グラフの挿入
    p = doc.add_paragraph('図1 試験用波形\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\\python-docx.docx')
 
#==============================================================================
# 試験用波形を作成する関数 Wave で作成した試験用波形デジタルデータの csv 出力関数
#==============================================================================
def WriteWaveCsv(t, f):
    w = np.stack([t, f])
    np.savetxt('C:\\Users\\UseName\\Desktop\\python-csv.csv', w.T, delimiter=',')
 
#==============================================================================
# 実用例
#==============================================================================
wave_number = 5     # 合成する波形の数
data_number = 2**12 # データ数
dt = 0.01           # サンプリング周期 [sec]
 
# 波形作成
ampl, freq, t, f, img_name = Wave(wave_number, data_number, dt)
 
# Misrosoft Word での波形作成レポート作成
DocumentReport(wave_number, data_number, dt, ampl, freq, t, f, img_name)
 
# 波形データの csv 出力
WriteWaveCsv(t, f)



結果

このプログラムから出力される波形のグラフ、 Word ファイル、 csv ファイルをそれぞれ図1 〜 図3 に示します。

図2 と図3 の Word ファイルと csv ファイルは内容が1枚の画像には収まらないので、冒頭の部分のみの画像にしました。

図1 作成した波形


図2 作成した Word ファイル(冒頭)


図3 作成した csv ファイル(冒頭)



コメント

こういったプログラムを量産していくと仕事の自動化に効果的な気がしてます。

さらに、 GUI 化や exe 化などで汎用的に出来れば効果はもっと大きいものとなると思います。

GUI の作り方と exe 化については下の記事でまとめています。

GUI の作り方】
Python で ファイル選択ダイアログを使う - 解析エンジニアの自動化 blog

【 exe 化】
Python を exe ファイルに変換する - 解析エンジニアの自動化 blog



以上

Python で docx を使って Microsoft Word を操作する



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


この記事の目次



背景・目的


Python に仕事をさせて最後に待ち受けているのが報告書の作成です。

自動化している事を悟られない様に Microsoft Word で報告書を書きました。

docx というモジュールの入門みたいな記事です。



動作環境


Windows 7
・winpython 64bit 3.4.4



インストール

pip コマンドからインストールしました。

pip install python-docx



プログラム

ソースコード


import docx
from docx.enum.text import WD_ALIGN_PARAGRAPH

# ワードドキュメント作成
doc = docx.Document()

# 段落に見出し追加
doc.add_heading('報告書', 0)

# 段落に文章追加
doc.add_heading('はじめに', 1)
doc.add_paragraph('はじめに〜')

doc.add_heading('対象', 1)
doc.add_paragraph('対象〜')
p = doc.add_paragraph('図1 対象')
p.alignment = WD_ALIGN_PARAGRAPH.CENTER

doc.add_heading('方法', 1)
doc.add_paragraph('方法〜')

doc.add_heading('結果', 1)
doc.add_paragraph('結果〜')
p = doc.add_paragraph('図2 結果')
p.alignment = WD_ALIGN_PARAGRAPH.CENTER

# ファイル名を指定して保存
doc.save('C:\\Users\\UserName\\Desktop\\test-docx.docx')



結果

デスクトップに "test-docx.docx" というファイルが出来たと思います。

図1 に作成した Word ファイルを示します。

図1 作成した Word ファイル



コメント

Python から Microsoft-Word の操作は簡単です!

目次やヘッダー・フッターなど、 Microsoft-Word で使う機能は言うほど多くないので、もう少し調べれば普段の仕事で行う Microsoft-Word のタスクは自動化出来そうです。

以外と、インターネットに情報が少なくて驚きました。

ソースコードの分割が出来れば Word ファイルを操作する Python 関数を量産したソースコードをひとまとめに出来ます。

ソースコードの分割は下記の記事にサンプルをまとめています。
Python のソースコードを分割する - 解析エンジニアの自動化 blog



以上

Python のソースコードを分割する



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


この記事の目次



背景・目的


Python でコードを書いているときに、クラス毎や関数毎にファイルが分割出来たら良いなと思いました。
クラス毎や関数毎に分担する事も出来そうですし…

ファイルが分割出来ると1度作ったソースコードの流用がとても簡単になり、ソースコードを資産的に管理する事が出来る様になります。

なので、ファイル分割の方法をまとめます。



動作環境


Windows 7
・winpython 64bit 3.4.4



プログラム

ソースコードは以下の2つを用意します。
関数とクラスのファイル:split_files_child.py
関数とクラスの実行ファイル:split_files.py

ソースコード 〜関数とクラスのファイル〜


###############################################################################
# サンプル関数・クラスファイル
###############################################################################
 
# 関数 1
def sample_function1():
    print('split_files_child.py の sample_function1 が実行されました。')
 
# 関数 2
def sample_function2():
    print('split_files_child.py の sample_function2 が実行されました。')
 
# クラス
class samplclass():
   
    # コンストラクタ
    def __init__(self, arg1):
        self.value = arg1
   
    # メソッド
    def method1(self):
        self.value = '\n' + self.value + '\nsampleclass の method1 によって追加された文章!!'



ソースコード 〜別ファイルの関数とクラスの実行ファイル〜


###############################################################################
# 分割されているソースコードファイルの実行プログラム
###############################################################################
 
# ファイルインポート
import split_files_child as sfc # .py は不要
 
# split_files_child.py の sample_function1 を実行
sfc.sample_function1()
 
# split_files_child.py の sample_function1 を実行
sfc.sample_function2()
 
# split_files_child.py の sampleclass のインスタンス生成
# 'split_files.py' で初期化
myclass = sfc.samplclass('split_files.py')
 
# value の表示
print(myclass.value)
 
# split_files_child.py の sampleclass のメソッド実行
myclass.method1()
 
# value の表示
print(myclass.value)



結果

かなり簡単に分割出来ました。

関数とクラスのファイルを実行ファイルと同じディレクトリに置いて、 import しただけです。


In [1]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/split_files.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
Reloaded modules: split_files_child
split_files_child.py の sample_function1 が実行されました。
split_files_child.py の sample_function2 が実行されました。
split_files.py
 
split_files.py
sampleclass の method1 によって追加された文章!!



コメント

インターネットを調べているとクラスだけ書かれているサイトを見かけたりします。

こうやって使えば良かったんですね。

これで、コーディング時間が削減できたら良いですね。

あまり視覚的では無いので読みづらい記事になってしまった…



以上

Python で逆高速フーリエ変換(IFFT)をする



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


この記事の目次



背景・目的


フーリエ変換をやったので、逆フーリエ変換をやります。



動作環境


Windows 7
・winpython 64bit 3.4.4



プログラム

ソースコード


###############################################################################
# 逆高速フーリエ変換( IFFT )を計算するプログラム
###############################################################################
# インポート
import matplotlib.pyplot as plt
import numpy as np
 
# 簡単な信号の作成
N     = 2**6 # サンプル数
dt    = 0.025 # サンプリング周期( sec )
freq1 = 10 # 周波数( Hz )
ampl1 = 1 # 振幅
freq2 = 15 # 周波数( Hz )
ampl2 = 1 # 振幅
print("■■■ 入力条件 ■■■")
print("サンプル数 : " + str(N))
print("サンプリング周期 : " + str(dt) + ' sec')
print("サンプリング周波数 : " + str(1/dt) + ' Hz')
print("入力波1")
print("  周波数 : " + str(freq1) + ' Hz')
print("  振 幅 : " + str(ampl1))
print("入力波2")
print("  周波数 : " + str(freq2) + ' Hz')
print("  振 幅 : " + str(ampl2))
 
# 時間軸
t = np.arange(0, N*dt, dt)
print("サンプリング時間 : " + str(N*dt-dt) + ' sec')
print("サンプリング時刻 : " + str(t))
 
# {周波数 freq1, 振幅 amp1 の正弦入力波} + {周波数 freq2, 振幅 amp2 の正弦入力波}
f = ampl1*np.sin(2*np.pi*freq1*t) + ampl2*np.sin(2*np.pi*freq2*t)
# 周波数 freq1, 振幅 amp1 の正弦入力波
#f = amp1 * np.sin(2*np.pi*freq1*t)
 
# 高速フーリエ変換( FFT )
F = np.fft.fft(f)
 
# FFT の複素数結果を絶対に変換
absf = np.abs(F)
 
# 振幅をもとの信号に揃える
absf_amp = absf / N * 2
absf_amp[0] = absf_amp[0] / 2
 
# 周波数軸のデータ作成
fq = np.linspace(0, 1.0/dt, N) # 周波数軸 linspace(開始, 終了, 分割数)
 
idx = np.argmax(f)
print("\n■■■ 入力信号特性 ■■■")
print("入力信号の最大idx : " + str(idx))
print("入力信号の最大振幅 : " + str(f[idx]))
print("入力信号の最大時刻 : " + str(t[idx]))
 
idx = np.array(absf_amp[:int(N/2)+1]) # コピー
idx = idx.argsort()[::-1] # 降順に並べ替えた時のインデックスを取得する
print("\n■■■ フーリエ変換信号特性 ■■■")
print("フーリエ変換信号の最大idx : " + str(idx[0]))
print("フーリエ変換信号の最大振幅 : " + str(absf_amp[idx[0]]))
print("フーリエ変換信号の最大周波数 : " + str(fq[idx[0]]))
 
print("\nフーリエ変換信号の次点idx : " + str(idx[1]))
print("フーリエ変換信号の次点振幅 : " + str(absf_amp[idx[1]]))
print("フーリエ変換信号の次点周波数 : " + str(fq[idx[1]]))
 
# 逆フーリエ変換( IFFT )
F_ifft = np.fft.ifft(F)
 
# 実数部の取得
F_ifft_real = F_ifft.real
 
# スライス
F_ifft_real[:10]
 
idx = np.argmax(F_ifft_real)
print("\n■■■ 逆フーリエ変換信号特性 ■■■")
print("逆フーリエ変換信号の最大idx : " + str(idx))
print("逆フーリエ変換信号の最大振幅 : " + str(F_ifft_real[idx]))
print("逆フーリエ変換信号の最大時刻 : " + str(t[idx]))
 
# グラフ表示
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 12))
 
# 信号のグラフ(時間軸)
axes[0, 0].plot(t, f)
axes[0, 0].set_title('Input Wave')
axes[0, 0].set_xlabel('time[sec]')
axes[0, 0].set_ylabel('amplitude')
axes[0, 0].grid(True)
 
# FFTのグラフ(周波数軸)
axes[0, 1].plot(fq[:int(N/2)+1], absf_amp[:int(N/2)+1])
axes[0, 1].set_title('Fast Fourier Transform')
axes[0, 1].set_xlabel('freqency[Hz]')
axes[0, 1].set_ylabel('amplitude')
axes[0, 1].grid(True)
 
# IFFTのグラフ(時間軸)
axes[1, 0].plot(t, F_ifft_real, c="g")
axes[1, 0].set_title('Inverse Fast Fourier Transform')
axes[1, 0].set_xlabel('time[sec]')
axes[1, 0].set_ylabel('amplitude')
axes[1, 0].grid(True)
 
# Input Wave と IFFT Wave の重ね合わせ
axes[1, 1].plot(t, f, c="g")
axes[1, 1].plot(t, F_ifft_real, c="b")
axes[1, 1].set_title('Input Wave and IFFT')
axes[1, 1].set_xlabel('time[sec]')
axes[1, 1].set_ylabel('amplitude')
axes[1, 1].grid(True)
 
# グラフの出力
file_dir  = 'C:\\Users\\UserName\\Desktop\\'
file_name = 'ifft'
fig.savefig(file_dir + file_name + '0.0.jpg', bbox_unches="tight")



結果

図1 の 4 つのグラフの説明をします。

Input Wave は入力波のグラフです。

Fast Fourier Transform は Input Wave を高速フーリエ変換したグラフです。

Inverse Fast Fourier Transform は Fast Fourier Transform を逆高速フーリエ変換したグラフです。

Input Wave and IFFT は Input Wave と Inverse Fast Fourier Transform のグラフを重ね合わせたグラフです。

図1 変換結果


Input Wave and IFFT の図から Fast Fourier Transform の波形を逆高速フーリエ変換して求めた波形が入力した Input Wave とピッタリ重なっています。

そのため、正しく逆高速フーリエ変換が出来た事がわかります。

Python の 出力結果を示します。

In [1]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/fft-0.0.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
■■■ 入力条件 ■■■
サンプル数 : 64
サンプリング周期 : 0.025 sec
サンプリング周波数 : 40.0 Hz
入力波1
  周波数 : 10 Hz
  振 幅 : 1
入力波2
  周波数 : 15 Hz
  振 幅 : 1
サンプリング時間 : 1.5750000000000002 sec
サンプリング時刻 : [ 0. 0.025 0.05 ..., 1.525 1.55 1.575]
 
■■■ 入力信号特性 ■■■
入力信号の最大idx : 57
入力信号の最大振幅 : 1.70710678119
入力信号の最大時刻 : 1.425
 
■■■ フーリエ変換信号特性 ■■■
フーリエ変換信号の最大idx : 24
フーリエ変換信号の最大振幅 : 1.0
フーリエ変換信号の最大周波数 : 15.2380952381
 
フーリエ変換信号の次点idx : 16
フーリエ変換信号の次点振幅 : 1.0
フーリエ変換信号の次点周波数 : 10.1587301587
 
■■■ 逆フーリエ変換信号特性 ■■■
逆フーリエ変換信号の最大idx : 57
逆フーリエ変換信号の最大振幅 : 1.70710678119
逆フーリエ変換信号の最大時刻 : 1.425



コメント

Input Wave を FFT した後、ノイズを除去して、 IFFT する事があります。

Input Wave は横軸が時間のグラフで FFT したグラフは横軸が周波数です。

横軸が周波数になるとノイズなどが見やすくなることが多いので、波形をキレイにするときは、『入力波形 → FFT → ノイズ除去 → IFFT → キレイな波形』という手法は王道です。



以上

Python で高速フーリエ変換(FFT)をする



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


この記事の目次



背景・目的


とにかく何か分析めいたものがやりたくて、特に理由はないけれどフーリエ変換をやりました。

波形の分析の初歩なので、まとめておいて損はないと思いまして…



動作環境


Windows 7
・winpython 64bit 3.4.4



プログラム

ソースコード


###############################################################################
# 高速フーリエ変換( FFT )を計算するプログラム
###############################################################################
# インポート
import matplotlib.pyplot as plt
import numpy as np
 
# 簡単な入力波の作成
N     = 2**6 # サンプル数
dt    = 0.025 # サンプリング周期( sec )
freq1 = 10 # 周波数( Hz )
ampl1 = 1 # 振幅
freq2 = 15 # 周波数( Hz )
ampl2 = 1 # 振幅
print("■■■ 入力条件 ■■■")
print("サンプル数 : " + str(N))
print("サンプリング周期 : " + str(dt) + ' sec')
print("サンプリング周波数 : " + str(1/dt) + ' Hz')
print("入力波1")
print("  周波数 : " + str(freq1) + ' Hz')
print("  振 幅 : " + str(ampl1))
print("入力波2")
print("  周波数 : " + str(freq2) + ' Hz')
print("  振 幅 : " + str(ampl2))
 
# 時間軸
t = np.arange(0, N*dt, dt)
print("サンプリング時間 : " + str(N*dt-dt) + ' sec')
print("サンプリング時刻 : " + str(t))
 
# {周波数 freq1, 振幅 amp1 の正弦入力波} + {周波数 freq2, 振幅 amp2 の正弦入力波}
f = ampl1*np.sin(2*np.pi*freq1*t) + ampl2*np.sin(2*np.pi*freq2*t)
# 周波数 freq1, 振幅 amp1 の正弦入力波
#f = amp1 * np.sin(2*np.pi*freq1*t)
 
# 高速フーリエ変換( FFT )
F = np.fft.fft(f)
 
# FFT の複素数結果を絶対に変換
absf = np.abs(F)
 
# 振幅をもとの信号に揃える
absf_amp = absf / N * 2
absf_amp[0] = absf_amp[0] / 2
 
# 周波数軸のデータ作成
fq = np.linspace(0, 1.0/dt, N)
 
idx = np.argmax(f)
print("\n■■■ 入力信号特性 ■■■")
print("入力信号の最大idx : " + str(idx))
print("入力信号の最大振幅 : " + str(f[idx]))
print("入力信号の最大時刻 : " + str(t[idx]))
 
idx = np.array(absf_amp[:int(N/2)+1]) # コピー
idx = idx.argsort()[::-1] # 降順に並べ替えた時のインデックスを取得する
print("\n■■■ フーリエ変換信号特性 ■■■")
print("フーリエ変換信号の最大idx : " + str(idx[0]))
print("フーリエ変換信号の最大振幅 : " + str(absf_amp[idx[0]]))
print("フーリエ変換信号の最大周波数 : " + str(fq[idx[0]]))
 
print("\nフーリエ変換信号の次点idx : " + str(idx[1]))
print("フーリエ変換信号の次点振幅 : " + str(absf_amp[idx[1]]))
print("フーリエ変換信号の次点周波数 : " + str(fq[idx[1]]))
 
# グラフ表示
fig, (axL, axR) = plt.subplots(ncols=2, figsize=(10,4))
 
# 信号のグラフ(時間軸)
axL.plot(t, f)
axL.set_title('Input Wave')
axL.set_xlabel('time[sec]')
axL.set_ylabel('amplitude')
axL.grid(True)
 
# FFTのグラフ(周波数軸)
axR.plot(fq[:int(N/2)+1], absf_amp[:int(N/2)+1])
axR.set_title('Fast ')
axR.set_xlabel('freqency[Hz]')
axR.set_ylabel('amplitude')
axR.grid(True)
 
# グラフの出力
file_dir  = 'C:\\Users\\UserName\\Desktop\\'
file_name = 'fft'
fig.savefig(file_dir + file_name + '0.0.jpg', bbox_unches="tight")



結果

numpy には高速フーリエ変換のメソッドがあります。
numpy は便利ですね。

図1 の Input Wave は分析した波形で、周波数 10Hz, 振幅 1 の正弦波と周波数 15Hz, 振幅 1 の正弦波の合成波でした。

図1 の Fast Fourier Transform は分析結果で、周波数が 10Hz と 15Hz でところで波形がとんがっています。
さらに、振幅がそれぞれ 1 を示しています。

見事に入力波が周波数 10Hz, 振幅 1 の正弦波と周波数 15Hz, 振幅 1 の正弦波の合成波である事が分かりました。

図1 変換結果



In [1]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/fft0.0.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
■■■ 入力条件 ■■■
サンプル数 : 64
サンプリング周期 : 0.025 sec
サンプリング周波数 : 40.0 Hz
入力波1
  周波数 : 10 Hz
  振 幅 : 1
入力波2
  周波数 : 15 Hz
  振 幅 : 1
サンプリング時間 : 1.5750000000000002 sec
サンプリング時刻 : [ 0. 0.025 0.05 ..., 1.525 1.55 1.575]
 
■■■ 入力信号特性 ■■■
入力信号の最大idx : 57
入力信号の最大振幅 : 1.70710678119
入力信号の最大時刻 : 1.425
 
■■■ フーリエ変換信号特性 ■■■
フーリエ変換信号の最大idx : 24
フーリエ変換信号の最大振幅 : 1.0
フーリエ変換信号の最大周波数 : 15.2380952381
 
フーリエ変換信号の次点idx : 16
フーリエ変換信号の次点振幅 : 1.0
フーリエ変換信号の次点周波数 : 10.1587301587



コメント

入力波形がどんな単純波形の合成で出来ているのかが分かる様になりました。

自動車の振動の測定結果や観測した地震波がどんな単純波形の合成で出来ているのかがわかる様になるため、対策すれば良い単純な波形がわかる様になります。

ただし、振動の測定結果や観測地震波などは波形が複雑なため、構成される単純波形がものすごい数になる事でしょう…



以上

Python の matplotlib の描画速度を計測した



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


この記事の目次



背景・目的


Python で色々な分析結果をグラフにしたいのですが、 matplotlib の描画スピードがとても遅かったので、どんな使い方をすれば良いのか検証しました。



動作環境


Windows 7
・winpython 64bit 3.4.4



入力データ

読み込むデータは Excel で作成しました。

Excel の 10,48,576 行を全て使いました。

Data1 〜 Data5 は以下の関係性を持たせています。

Data1 = No. / 10,000
Data2 = cos(radians(Data1))
Data3 = sin(radians(Data1))
Data4 = 2*cos(radians(Data1))
Data5 = 2*sin(radians(Data1))

そして、 Excel から csv 出力したファイルを作成しました。

図1 入力データ



プログラム

2つのソースコードを準備しました。

ソースコード
dask で csv ファイルを読み込んで matplotlib でグラフを描画するプログラム

ソースコード
dask で csv ファイルを読み込んで dask.dataframe を numpy 配列に変換してから matplotlib でグラフを描画するプログラム

2つのソースコードは matplotlib に入れる値が dask.dataframe か numpy 配列かの違いがあります。

ソースコード

dask で csv ファイルを読み込んで matplotlib でグラフを描画するプログラム


###############################################################################
# グラフの描画時間を計測するプログラム
###############################################################################
# インポート
from time import time
import dask.dataframe as dd
import matplotlib.pyplot as plt
 
# 時間計測関数
def calc_time(func):
    start = time()
    r = func()
    return{'value':r, 'time':time()-start}
 
# 表題行 + 1,048,575 行のデータ
file_dir  = 'C:\\Users\\UserName\\Desktop\\'
file_name = 'driven_test'
 
# ファイル読み込み
ddf = calc_time(lambda:dd.read_csv(file_dir + file_name + '.csv', header=0,
                                   names=('No.', 'Date', 'Data1', 'Data2', 'Data3', 'Data4', 'Data5')))
print('データ読込時間 : ' + str(ddf['time']) + ' sec')
all_time = ddf['time']
 
# グラフの描画
graph = calc_time(lambda:plt.plot(ddf['value']['No.'], ddf['value']['Data2']))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
graph = calc_time(lambda:plt.plot(ddf['value']['No.'], ddf['value']['Data3']))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
graph = calc_time(lambda:plt.plot(ddf['value']['No.'], ddf['value']['Data4']))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
graph = calc_time(lambda:plt.plot(ddf['value']['No.'], ddf['value']['Data5']))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
# 画像の出力
output = calc_time(lambda:plt.savefig(file_dir + file_name + '0.0.jpg', bbox_unches="tight"))
print('グラフ出力時間 : ' + str(output['time']) + ' sec')
all_time += output['time']
 
print('合計時間 : ' + str(all_time) + ' sec')



ソースコード

dask で csv ファイルを読み込んで dask.dataframe を numpy 配列に変換してから matplotlib でグラフを描画するプログラム


###############################################################################
# グラフの描画時間を計測するプログラム
###############################################################################
# インポート
from time import time
import dask.dataframe as dd
import numpy as np
import matplotlib.pyplot as plt
 
# 時間計測関数
def calc_time(func):
    start = time()
    r = func()
    return{'value':r, 'time':time()-start}
 
# 表題行 + 1,048,575 行のデータ
file_dir  = 'C:\\Users\\UserName\\Desktop\\'
file_name = 'driven_test'
 
# ファイル読み込み
ddf = calc_time(lambda:dd.read_csv(file_dir + file_name + '.csv', header=0,
                                   names=('No.', 'Date', 'Data1', 'Data2', 'Data3', 'Data4', 'Data5')))
print('データ読込時間 : ' + str(ddf['time']) + ' sec')
all_time = ddf['time']
 
ex = calc_time(lambda:np.array(ddf['value']))
print('データ変換時間 : ' + str(ex['time']) + ' sec')
all_time += ex['time']
 
graph = calc_time(lambda:plt.plot(ex['value'][0:,0], ex['value'][0:,3]))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
graph = calc_time(lambda:plt.plot(ex['value'][0:,0], ex['value'][0:,4]))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
graph = calc_time(lambda:plt.plot(ex['value'][0:,0], ex['value'][0:,5]))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
graph = calc_time(lambda:plt.plot(ex['value'][0:,0], ex['value'][0:,6]))
print('グラフ作成時間 : ' + str(graph['time']) + ' sec')
all_time += graph['time']
 
# 画像の出力
output = calc_time(lambda:plt.savefig(file_dir + file_name + '0.1.jpg', bbox_unches="tight"))
print('グラフ出力時間 : ' + str(output['time']) + ' sec')
all_time += output['time']
 
print('合計時間 : ' + str(all_time) + ' sec')



結果

numpy 配列に変換する処理に時間をさいても dask で csv ファイルを読み込んで dask.dataframe を numpy 配列に変換してから matplotlib でグラフを描画するプログラムのほうが速い結果となりました。

値の処理計算も numpy 配列が速いですし、当たり前といえば当たり前の結果でした。

作成したグラフももちろん同じモノが出来上がりました。

ソースコード①の結果


In [1]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/graph0.0.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
データ読込時間 : 0.01200103759765625 sec
グラフ作成時間 : 3.650207996368408 sec
グラフ作成時間 : 3.7332139015197754 sec
グラフ作成時間 : 3.2911880016326904 sec
グラフ作成時間 : 3.101177930831909 sec
グラフ出力時間 : 0.8210470676422119 sec
合計時間 : 14.608835935592651 sec


図2 ソースコード①のグラフ


ソースコード②の結果


In [1]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/graph0.1.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
データ読込時間 : 0.01100015640258789 sec
データ変換時間 : 1.9661130905151367 sec
グラフ作成時間 : 0.18301010131835938 sec
グラフ作成時間 : 0.13800787925720215 sec
グラフ作成時間 : 0.1370079517364502 sec
グラフ作成時間 : 0.14200782775878906 sec
グラフ出力時間 : 0.4520261287689209 sec
合計時間 : 3.0291731357574463 sec


図3 ソースコード②のグラフ



コメント

本来であればデータの読み込みからグラフを描画する間にデータの加工や分析処理が入ると思います。

なので、データの加工や分析処理を考えるとデータは読み込んだら numpy 配列に変換して持っておくのが良さそうだということが分かりました。



以上

Python で Excel ファイルと csv ファイルを読み込む



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


この記事の目次



背景・目的


Python で分析をするにあたって、データの読み込みは避けて通れません。
データの読み込みはやはり速いに越したことはありません。

そこで、 pandas と dask の使い方と読み込み速度を比べてみました。



動作環境


Windows 7
・winpython 64bit 3.4.4



入力データ

読み込むデータは Excel で作成しました。

Excel の 10,48,576 行を全て使いました。

Data1 〜 Data4 は乱数で Data5 は Data1 〜 Data4 の合計です。

また、 Excel から csv 出力したファイルも作成しました。

図1 作成した入力データ



プログラム

ソースコード


###############################################################################
# Excel ファイルと csv ファイルを読み込み時間を計測するプログラム
###############################################################################
# インポート
from time import time
import pandas as pd
import dask.dataframe as dd
 
# 時間計測関数
def calc_time(func):
    start = time()
    r = func()
    return{'value':r, 'time':time()-start}
 
# 表題行 + 1,048,575 行のデータ
file_dir  = 'C:\\Users\\UserName\\Desktop\\'
file_name = 'driven_test'
 
csv = calc_time(lambda:pd.read_csv(file_dir + file_name + '.csv'))
print( 'pd.read_csv で読込にかかった時間 : ' + str(csv['time']) + ' sec')
 
xls = calc_time(lambda:pd.ExcelFile(file_dir + file_name + '.xlsx', encoding='utf-8'))
print( 'pd.ExcelFile で読込にかかった時間 : ' + str(xls['time']) + ' sec')
 
csv = calc_time(lambda:dd.read_csv(file_dir + file_name + '.csv', header=0))
print( 'dd.read_csv で読込にかかった時間 : ' + str(csv['time']) + ' sec')



結果

3 回分の実行結果を表示します。

100万行程度であれば、 csv ファイルは pandas でも実用に耐えると思います。

Excel をそのまま読み込むのはやめたほうがいいですね。

dask が噂通りものすごく速いという結果になりました。

1,000 万行、 1 億行といったデータを扱いたいなら dask ですね!


In [1]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/split_file0.0.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
pd.read_csv で読込にかかった時間 : 1.8201038837432861 sec
pd.ExcelFile で読込にかかった時間 : 101.72981905937195 sec
dd.read_csv で読込にかかった時間 : 0.010999917984008789 sec
 
In [2]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/split_file0.0.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
pd.read_csv で読込にかかった時間 : 1.755100965499878 sec
pd.ExcelFile で読込にかかった時間 : 102.36885499954224 sec
dd.read_csv で読込にかかった時間 : 0.009999990463256836 sec
 
In [3]: runfile('C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3/split_file0.0.py', wdir='C:/WinPython-64bit-3.4.4.6Qt5/settings/.spyder-py3')
pd.read_csv で読込にかかった時間 : 1.801103115081787 sec
pd.ExcelFile で読込にかかった時間 : 100.0787239074707 sec
dd.read_csv で読込にかかった時間 : 0.009999990463256836 sec



コメント

dask の速さは異常ですね。

読み込みがこれだけ速いと、処理は値の計算とグラフ出力の時間で決まる様なものですね。

グラフの出力の高速化について調べてみたくなりました。



以上