雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

『滑らか』なセンサデータの極大・極小を取得してみる

最近は Python でセンサデータを確認したり、特徴を取ったりな仕事をしています。
その中で最近「データの極大値・極小値を取る」ことをやりました。

Python で行う方法を調べると、よく出てくるのは scipy の signal モジュールで、

import numpy as np
from scipy import signal

a = np.array([1, 2, 3, 4, 5, 4, 3, 2, 1])
b = signal.argrelmax(a, order = 1)[0]

print(b)
# [4]

と、確かに極大値となっているところのインデックスを取得できるのですが、

最大値が連続しているような、以下の形式だと極大値を取ってくることができませんでした。

c = np.array([1, 2, 3, 4, 5, 5, 4, 3, 2, 1])
d = signal.argrelmax(c, order = 1)[0]

print(d)
# []

おそらく、signal は信号処理で argrelmax が極大値ではなくピークを取ってくる処理なので、
このような『なめらか』なのは極大値とみなされないのかと。


――あんまりやっている人が見つからなかったので、書いてみました。
やってることは大したことではなく、結局は傾き = 変化量が 0 を含めて符号変わるところを見るだけなので。。
後々の処理の都合で『後優先』になっています。

def get_peaks(data) :
    """ 
    データの変化量を順にみて、
    極大 (変化量が + から - に変わる地点)、極小 (- から +) のインデックスをそれぞれ取得 
    """

    if data.size < 2 :
        raise Exception("Data size is too few.")

    # 変化量算出
    diffs = np.zeros((data.size - 1))
    for i in range(data.size - 1) :
        idx = i + 1
        diffs[i] = data[idx] - data[idx - 1]

    # 符号
    def get_flag(diff) :
        if diff > 0 : return 1
        if diff < 0 : return -1
        return 0

    peak_max = []
    peak_min = []

    pre_flag = get_flag(diffs[0])

    for i in range(1, diffs.size) :

        flag = get_flag(diffs[i])

        # 今の変化量が 0 なら無視
        if flag == 0 : continue

        # 前の変化量が 0 or 今の変化量が 0 以外 → 設定
        # NOTE: 0 番目の変化量から 0 で続いていたときのみ
        if pre_flag == 0 :
            if flag > 0 : 
                peak_min.append(i)
            else :
                peak_max.append(i)
            pre_flag = flag
            continue

        # 変化量が前と違う場合は設定
        if pre_flag != flag :
            if flag > 0 :
                peak_min.append(i)
            else :
                peak_max.append(i)
            pre_flag = flag

    return peak_max, peak_min

面倒なので極大極小まとめて作っちゃいましたが、先ほどの配列 c に対してかけると

pmax, pmin = get_peaks(c)

print(pmax, pmin)
# [5] []

と、欲しかった結果を得ることができました。


…実は signal じゃないモジュールで取得してくれる処理があったりするのかも。と思いつつ。。。