🐈
[Pandas] シーケンス番号の差分計算
シーケンス番号など固定のフィールド長で最大値に到達したら 0 になるデータの差分を計算
概略
Pandas では DataFrame の diff を使って差分計算ができる。
しかし、シーケンス番号などデータでは最大値に到達した後次のデータは 0 に戻る。
単純に diff を計算するとマイナスの値になつてしまう。
このため、最大値に到達した後 0 に戻ることを前提にシーケンス番号の差分を計算する方法を説明する。
参考にしたサイト
- https://info.drobe.co.jp/blog/engineering/pandas-3-like
- https://qiita.com/stokes/items/157f7ab737c5ded26832
ソースコード
※ コードの解説は後程
import pandas as pd
import numpy as np
def calc_seqnumber_diff(df, M, fieldName, fieldNameforDiff):
df_diff = df[ [fieldName] ].diff()
print("Diff:")
print(df_diff)
# 最大値を含む行に True の印をつける
df["max"] = (df[fieldName] == (M-1) )
# 最小値を含む行に True の印をつける
df["min"] = (df[fieldName] == 0 )
# 最小値を含む行で、かつ前の行が最大値の行を見つけるため、
# 最大値の行の印を下に1行シフトする
df["max_shift"] = df["max"].shift(1)
# 最小値を含む行で、かつ前の行が最大値の行に True の印をつける
df["point"] = (df["max_shift"] == True) & (df["min"] == True)
# 最小値を含む行で、かつ前の行が最大値の行の場合差分は 1 とする。
# それ以外の場合は前の行との差分を出力する。
df[fieldNameforDiff] = np.where(df["point"] == True, 1, df_diff[fieldName])
print("Working:")
print(df)
# 一時的に使用した列を削除する
del df["max"]
del df["max_shift"]
del df["min"]
del df["point"]
return df
N = 10
M = 4
df = pd.DataFrame(
{
'sequence_number': np.arange(0, N) % M,
}
)
print("Original:")
print(df)
df = calc_seqnumber_diff(df, M, "sequence_number", "diff")
print("Result:")
print(df)
実行結果
Original:
sequence_number
0 0
1 1
2 2
3 3
4 0
5 1
6 2
7 3
8 0
9 1
Diff:
sequence_number
0 NaN
1 1.0
2 1.0
3 1.0
4 -3.0
5 1.0
6 1.0
7 1.0
8 -3.0
9 1.0
Working:
sequence_number max min max_shift point diff
0 0 False True NaN False NaN
1 1 False False False False 1.0
2 2 False False False False 1.0
3 3 True False False False 1.0
4 0 False True True True 1.0
5 1 False False False False 1.0
6 2 False False False False 1.0
7 3 True False False False 1.0
8 0 False True True True 1.0
9 1 False False False False 1.0
Result:
sequence_number diff
0 0 NaN
1 1 1.0
2 2 1.0
3 3 1.0
4 0 1.0
5 1 1.0
6 2 1.0
7 3 1.0
8 0 1.0
9 1 1.0
解説
通常の diff と比べて注意する点は、前のデータが最大値で、その次のデータが 0 になっているデータに関して差分計算の結果が マイナスの最大ではなく、1 になるようにしたい。
このため、最小値のデータを持つ行でかつ、直前の行が、最大値のデータを特定してその場合だけ差分として 1 を採用するような仕組みを導入する。
最大値の行
以下のようにして、最大値を含む行が True となるようにする。
# 最大値を含む行に True の印をつける
df["max"] = (df[fieldName] == (M-1) )
最小値の行
以下のようにして、最小値を含む行が True となるようにする。
# 最小値を含む行に True の印をつける
df["min"] = (df[fieldName] == 0 )
最小値を含む行の前の行が最大値の行
最小値の前の行が最大値であるものを確認するため、最大値の行を下に1行シフトする。
シフトした結果と、最小値の行の両方が True となる行を探す。
# 最小値を含む行で、かつ前の行が最大値の行を見つけるため、
# 最大値の行の印を下に1行シフトする
df["max_shift"] = df["max"].shift(1)
# 最小値を含む行で、かつ前の行が最大値の行に True の印をつける
df["point"] = (df["max_shift"] == True) & (df["min"] == True)
実行例
sequence_number max min max_shift point diff
0 0 False True NaN False NaN
1 1 False False False False 1.0
2 2 False False False False 1.0
3 3 True False False False 1.0
4 0 False True True True 1.0
5 1 False False False False 1.0
6 2 False False False False 1.0
7 3 True False False False 1.0
8 0 False True True True 1.0
9 1 False False False False 1.0
シーケンス番号の差分の計算
最小値の前の行が最大値であるものは差分として 1 を採用して、それ以外の行に関しては通常の差分を返す。
# 最小値を含む行で、かつ前の行が最大値の行の場合差分は 1 とする。
# それ以外の場合は前の行との差分を出力する。
df[fieldNameforDiff] = np.where(df["point"] == True, 1, df_diff[fieldName])
Discussion