この記事の目的
時系列データの分析をするとき、「ルーティン作業から出てくる時系列データを、各ルーティン毎のデータに分けたいな…」いう悩みが職場で声が上がったので、pythonを使ってちょちょいのちょいって感じで解決したので記録として残します(すぐ忘れそうなので)。
具体的にはどのようなデータ?
例えば、製造業では製品を作る際、プラントが稼働しています。超ざっくりですが、1回分の製造過程のことを1バッチといい、1バッチの製造ではいろんな工程が存在します(反応工程、濾過工程など)。
この各工程には工程Noといって番号が振り分けられています。この番号を使って、大量の時系列データからバッチ毎のデータを横並びにしよう!というのが今回です。
じゃあ、どのようなデータなのか?ということで今回もサンプルデータを作成してみましょう。
import pandas as pd
import numpy as np
# カラム名を決める
column_names = ['date', 'AA', 'BB', '工程No']
# 時系列データの期間を設定
dates = pd.date_range(start='2020-05-23 00:00:01', end='2020-05-25 00:00:01', freq='S')
# 'AA'と'BB'にはランダムなfloat型の数字を生成
np.random.seed(0) # for reproducibility
AA = np.random.rand(len(dates))
BB = np.random.rand(len(dates))
# 工程Noには1から15の数字を連続で入力し、それを繰り返す
工程No = np.tile(np.arange(1, 16), len(dates) // 15 + 1)[:len(dates)]
# データフレームを作成
df = pd.DataFrame(data=zip(dates, AA, BB, CC), columns=column_names)
df
今回は自力で作成しました。
どのようなデータか見てみましょう。
このように'工程No'というカラムは1から15までの数字が連続して入っており、15が入力されたあとは再び1から番号がついているというデータです。
本当はもっと複雑ですが、今回はイメージしやすいようにこのようなデータとしました。
この工程Noが15→1に切り替わる瞬間がバッチが切り替わる瞬間と捉えていただければと思います。
では、このデータをバッチ毎に横方向に並べていきましょう。
コードを書いていく
まずは必要なライブラリをインポートして、データを読み込みましょう。
import pandas as pd
data = pd.read_csv('ファイルのパス')
今回はバッチ処理を行うため、データセットを小さなチャンクに分割し、各チャンクに対して処理を行い、最後に結果を結合します。
def shift_rows_optimized(df):
dfs = []
while len(df) > 0:
df_diff = data['工程No'].diff().abs()
shift_index = df_diff[df_diff >= 4].index
if len(shift_index) == 0:
dfs.append(df)
break
first_shift_index = shift_index[0]
remaining_df = df.loc[first_shift_index:]
df = df.loc[:first_shift_index - 1] # -1を入れてエラーが出たら消してください。
dfs.append(df)
df = remaining_df
return dfs
dfs_batches = shift_rows_optimized(data)
dfs_batches
ここでは、工程Noのデータの各行で差分を取り、その絶対値が4以上のとき1バッチ分としてdfsというリストに追加しています。
絶対値が4以上とした理由は、工程Noが連番でない場合を想定して、大きめに設定しているだけです。
実行することで、各バッチのデータが得られます。
次は、これを横方向に結合します。
dfs_dropped = [df.dropna().reset_index(drop=True) for df in dfs_batches]
df_concat_dropped = pd.concat(dfs_dropped, axis=1)
df_concat_dropped
各バッチに分割しましたが、インデックスが維持されたまま、分割されるので欠損値を含むデータになってしまいます。
例えば、2バッチ目のデータは、元データのインデックスを維持したまま、2バッチ目の部分のみ抽出してくるので、1バッチ目と3バッチ目以降のデータは欠損値となります。
そのため、欠損値を削除したうえで結合する必要があります。
完成
ということで完成しました。
バッチ毎に分割できていますね。
これで、問題が発生したバッチデータを見つけやすくなりますし、バッチ毎の特定の工程のデータをフィルタリングしやすくなりました。
カラム名とかは適宜変える必要がありますけどね…
お疲れさまでした!!!
まとめ
ここまでご覧いただきありがとうございます。
今回書いたコードですが、以前はopenpyxlを用いて処理していました。
そのコードもほとんどは同僚が完成させたものだったので、ほかの方法を見つけたいといったのがきっかけです。
私自身pandasしかやってないので、なんとしてもpandasを使いたかったってのもあります。
結果が同じであれば、どっちでもいいとは思いますが、コードを管理していく上では理解があるものを手元に置きたかったのです。
ということで今回は時系列データの分割についてでした!
これからも、データ処理の記録をつけていこうと思います。
それでは!!!