連番なファイルをループで読み込んで処理し、グラフ化する方法

file_1.dat,file_2.dat ...... file_100.datのような数字だけが連続で変化するdatファイルをfor文を用いて読み込んで処理し、グラフ化する方法です。
まとまった説明が見つからなかったので、備忘録として残します。

#何がしたいか
![コメント 2020-08-11 164540.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/668653/eedb9b1d-3779-f2b1-9ebf-47a53f4decd7.jpeg)

こんな感じのn行2列のdatファイルが数十個あり、2列目の値のみを取り出して計算し、グラフ化したいと思っています。
仮にExcelに1ファイルずつコピペして貼り付けて～となると時間も労力もかなりかかってしまいます。

当初はファイル一つ一つを読み込み、処理して、x,yの値に代入して…といったプログラムだった、つまり

```
data_number_1,data_intensity_1 = np.loadtxt("file_1.dat",unpack = True)
s1 = su(data_intensity_1)
m1 = mean(data_intensity_1)
x=[s1]
y=[m1]
```

これをファイルの個数分繰り返すというとんでもなく行数も分量も多いプログラムだったのですが、冒頭で述べたようにファイル名は数字の箇所だけが異なるため、Pythonでfor文を用いることで簡単に処理できるのではないか？と考えたことがプログラムを考えた動機です。

<用いた環境>
spyder(python3.7)
計算：numpy (import numpy as np)
描画：matplotrib (import matplotlib.pyplot as plt)

#datファイルの読み込みとfor文

datファイルの読み込みにはnp.loadtextを用います。
参考：https://deepage.net/features/numpy-loadsavetxt.html

読み込みに用いたコードのサンプルは以下の通りです。
ここでは、file_1からfile_20までの連番ファイルを読み込むとします。

```
data = []
for i in range(1,21):
    data.append(np.loadtxt("file_{}.dat".format(i),usecols=(1),unpack=True))
```

以下、順を追ってコードを説明します。

##for文について

for文のiの範囲を設定するとき、**最大値-1**の範囲が適用されるため、今回はファイルの番号である20+1として(1,21)と範囲を設定します。

ファイル名は、file_**数字**.datなので、formatメゾットを使用し、**file_{}.dat".format(i)**としてやります。
こうすることで、file_1.dat,file_2.dat......といったループ処理ができます。

そしてこれらを作成したdataというリストに**data.append**で繰り返される処理の値をリストにどんどん追加していく、といった具合です。



##loadtextについて

datファイルのうち、2行目の値だけを数値として扱いたいので、そのための処理を書き加えてやる必要があります。

そのままloadtextを用いて数値を読み込むと、リストには先の画像の例で言うと[0.0 72925],[1.0 70740]...[10.0 73343]といったように行ごとに入れられてしまいます。

そこで**unpack**を用いて転置してやり、列ごとに別の変数に入れてやります。
Trueにしてやることで転置が起こり、リストには[0.0 1.0 ... 10.0],[72925 70740 ... 73343]と列ごとに異なる変数にすることが可能です。

また、1列目は使わず、2列目のみを数値として扱いたいので**usecols**によって読み取る行を指定します。
0を最初の行として読み込む行を決めているので、2列目を読み込む際には(**1**)としてやる必要があります。
そうすることでdataリストには[72925 70740 ... 73343]のみが入ることになります。

詳しくは上記に掲載した参考サイトをご覧ください。
このようにunpackとusecolsを用いることで、各ファイルごとに2列目だけをある変数に入れてやることができました。

data = [file_1の2列目,file_2の2列目,...,file_20の2列目]　といった感じです。

<br>

当初のプログラムではファイル分行を打っていたのがなんとたったあれだけで済みました！行数も文字量も驚きの減少です。
もちろんファイルの量が増えれば増えるほど楽になります。




#処理

こうして2行目の値だけを全ファイル文取り込むことができたので、次はそれぞれについて処理をしていきます。
リストに入った値を一括処理するため、関数を定義してやり、順に処理させます。

今回は横軸を各ファイルごとの値を合計した値、縦軸はその平均値としてグラフを書くとします。

関数の定義はそれぞれ次のように書くことができます。

```
def su(a):
    return np.sum(a)
```
```
def mean(a):
    return np.mean(a)
```

これらの処理をリストdataの個々に適用してやります。

```
for i in range(1,21):
    si = su(data[i-1])
    mi = mean(data[i-1]) 
```

ここで注意しなければならないのは範囲の値です。
ファイル名は1から始まっているのですが、リストの値は0から始まります。
よって関数でiの値を指定するときに**(i-1)**としてやる必要があります。

1～20だと思っている値を0~19の範囲にしてやる必要があるということです。


 

#グラフの作成

今回は細かい説明については省きます。matplotribで検索すれば色々説明がでてくるかと思います。

先の処理方法で計算した値をそれぞれx,yとしてやる必要があります。
そのため、ファイルを読み込む時と同様にリストを作り、処理した値を順にx,yリストに入れるようにします。

```
x=[]
y=[]
for i in range(1,21):
    si = sum(data[i-1])
    mi = mean(data[i-1]) 
    x.append(si)
    y.append(mi)
```

こうすることでx軸y軸の値を用意することができたので、後はplotを用いてグラフにすれば完成です。


#全体のコードサンプル

以上を踏まえた全体のコードは以下のようになります。（値、ファイル、グラフはサンプルとして適当に用意したものであり、何の意味も持ちません）

```
import numpy as np
import matplotlib.pyplot as plt

def su(a):
    return np.sum(a)

def mean(a):
    return np.mean(a)

data = []
for i in range(1,21):
    data.append(np.loadtxt("file_{}.dat".format(i),usecols=(1),unpack=True))

x=[]
y=[]
for i in range(1,21):
    si = su(data[i-1])
    mi = mean(data[i-1]) 
    x.append(si)
    y.append(mi)

plt.plot(x,y,ls="",marker="o");

plt.xlim(xmin=0)
plt.ylim(ymin=0)

plt.show()
```

![Figure_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/668653/5389b74d-5a7e-5400-8d73-8c664bedb68c.png)


このようにループ処理を用いることで100行以上の簡略化に成功し、効率は格段にあがりました！
同じような問題で悩んでいる方の参考になれば幸いです。

閲覧ありがとうございました。