timedelta型のデータをグラフにプロットするのめんどくせえよなぁ！ハム○郎！！お前もそう思うよなァ！！

# はじめに

これはNISE (南山大学 青山研究室) Advent Calendar 2019の12日目の記事です。

`datetime`型の計算、というか`timedelta`型の扱いがなかなかしんどかったのでそれを残しておこうかな、と思います。

# やりたいこと

`datetime`型のカラムが2列挿入されているCSVファイルからその差分を計算し、計算結果の`second`と`microsecond`の部分をmatplotlibで折れ線グラフにしてプロットします。

グラフの横軸はCSVのindex、縦軸はtimestampの差とします。

CSVの中身はこんな感じです。

|send| receive |topic|CPU usage|memory usage|
|:--:|:-------:|:---:|:-------:|:----------:|
|16:25:54.492262|16:25:54.545652|normal/DrivePhoto/0|0.1|1140277248|
|16:25:54.502742|16:25:54.549113|normal/Timestamp/0|25|	1140740096|
|16:25:54.620683|16:25:54.672700|normal/DrivePhoto/1|3.1|1140445184|
|16:25:54.630237|16:25:54.682169|normal/Timestamp/1|25|1140084736|
|16:25:54.749052|16:25:54.800217|normal/DrivePhoto/2|3.2|1140187136|

`send`と`receive`が`datetime`型ですね。

# 何がめんどくさいのか

上記のCSVの例のように、`datetime`型は`strftime`を利用することで通常の`datetime`型から任意の形式に変形することができます。
例えばこのように、

```
today=datetime.datetime.today().strftime("%H:%M:%S.%f")
```
こうすれば、上のCSVみたいな表示にできるわけですが…


`datetime`型を計算すると`timedelta`型に強制変換されます。
こいつがクセもんで、上記の変形ができなくなるんですよね。
例えば、
「`receive`-`send`の計算結果のsecondとmicrosecondの部分だけ欲しい！~~yearとかmonthとかどうでもええねんボケェ~~」
といったことをしたくても一筋縄にはいかないのです。
何をしようとも、
`0 days 00:00:00.000000`
の形から逃れられないのです。

これが分かった途端私は、

**「あああああああtimedeltaはク○じゃけえええええええええ」**

と発狂したわけです。

あ、そもそもなんでこの変換をしようとしているのかというと、グラフに`timedelta`型をそのままプロットすると、グラフが強烈に見にくいものになってしまうからです。

# どうやって解決したか

結論から言うと、
計算結果をリスト化→リストを文字列型に変換→文字列から余計な文字を省く→float型にキャスト

といった感じで解決しました。そのソースコードがこちら。

```python:log_graph.py
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import sys
import csv
import datetime

file = input("input filename : ") #ファイル名入力

#読み込んだCSVファイルをListに変換
df = pd.read_csv(file, parse_dates=['send','receive'])

df['difference'] = df['receive'] - df['send'] #差分を計算

date = list(map(str, df['difference'])) #差分をmap関数でリスト化

date_str1 = ",".join(date) #リストを文字列化
date_str2 = date_str1.replace('0 days 00:00:0', '') #余分な文字を取り除く
date = date_str2.split(",") #文字列をリスト化

diff_df = pd.DataFrame(date, columns=['diff']) #プロット用DataFrameの生成

diff_df['diff'] = diff_df['diff'].astype(float) #froat型にキャスト

#matplotlibで折れ線グラフを生成
plt.plot(diff_df.index.values, diff_df['diff'])
plt.title(file)
plt.xlabel('message number')
plt.ylabel('difference time')
plt.show()
```

以下詳しく説明します

## リスト化して文字列型に変換

まず、最終目標として、`second`と`microsecond`の部分のみ表示したいと上で書きました。
つまり、それ以外の要素を取っ払うということです。
ということは…

**「文字列に強制変換してそれ以外の要素を消せばええやん!」**

という**まるで偏差値3のような考え方**に至りました。

そんなわけで、リストに変換するのは文字列に変換するための準備です。
CSVファイルを読み込んでも、そのままではリストになっていません。
そのため、まずは計算結果が格納されている`df[difference]`をリストに変換します。

さらに、map関数を使って一気に`timedelta`型を文字列型に変換します。

## 文字列から余計な文字を省く

上でも述べたように、`timedelta`型は標準で
`0 days 00:00:00.000000`
のような形になっています。

ここから、`second`、`microsecond`以外の要素を取り除く…

```
date_str1.replace('0 days 00:00:0', '')
```

はい。言うまでもないですね。

## float型にキャスト

グラフにプロットするときに、データは原則数値でないといけませんよね。
文字列を要素とするなら、それはよほどレアなケースでしょう。

`second`と`microsecond`だけが残ったデータ、一体どのような形になっているか考えてみましょう。

| send | receive | difference |
|:-:|:-:|:-:|
|16:25:54.492262|16:25:54.545652|0.053390|
|16:25:54.502742|16:25:54.549113|0.046371|
|16:25:54.620683|16:25:54.672700|0.052017|
|16:25:54.630237|16:25:54.682169|0.051932|
|16:25:54.749052|16:25:54.800217|0.051165|

`difference`が残ったデータです。

…実質数値じゃん

ということで、`float`型にキャストして優勝。
`timedelta`を見事ぶちのめしました。

ちなみにできたグラフはこちら。
![a_photo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274322/2018f174-22ee-802a-2414-8c81022f2f65.png)
(このグラフはあるtopicを抽出しているので上のCSVとは若干異なるものになってます)



# おわりに

今回は`timedelta`型を超絶無理やり変形してグラフにプロットする方法を書きました。

ここまでやって言うのもアレですが、**自分でもアホなやり方だな～って関心しちゃう**くらい無理やりな手法なので、ホントに困ったときに最終奥義くらいのつもりでいてくれると嬉しいです。
<br>
<br>
**ワイ「こんな無理やりなやり方マジで酷いよなぁ！！？！ハム○郎！！お前もそう思うよなァ！！」
ワイの中の全肯定ハム○郎「そうなのだ！！！もっといいやり方が絶対あるはずなのだ！！！！」**
~~ネタが古いとか言わない~~
<br>
<br>
そんなわけで、「もっといいやり方知ってるぜ！」という方がいらっしゃいましたら、是非コメントでご教授ください…