はじめに
これは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型にキャスト
といった感じで解決しました。そのソースコードがこちら。
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
を見事ぶちのめしました。
ちなみにできたグラフはこちら。
(このグラフはあるtopicを抽出しているので上のCSVとは若干異なるものになってます)
おわりに
今回はtimedelta
型を超絶無理やり変形してグラフにプロットする方法を書きました。
ここまでやって言うのもアレですが、自分でもアホなやり方だな~って関心しちゃうくらい無理やりな手法なので、ホントに困ったときに最終奥義くらいのつもりでいてくれると嬉しいです。
ワイ「こんな無理やりなやり方マジで酷いよなぁ!!?!ハム○郎!!お前もそう思うよなァ!!」
ワイの中の全肯定ハム○郎「そうなのだ!!!もっといいやり方が絶対あるはずなのだ!!!!」
ネタが古いとか言わない
そんなわけで、「もっといいやり方知ってるぜ!」という方がいらっしゃいましたら、是非コメントでご教授ください…