Рассуждение о последовательных точках данных без использования итерации

Я делаю анализ SPC, используя numpy/ pandas.

Частично это проверяет ряд данных на соответствие правилам Нельсона и правилам Western Electric.

Например (правило 2 из правил Нельсона): Проверьте, находятся ли девять (или более) точек в одной стороне от среднего значения.

Теперь я мог бы просто реализовать проверку правила, как это, перебирая массив.

  • Но прежде чем сделать это, я проверяю здесь на SO, есть ли у numpy/pandas способ сделать это без итераций?
  • В любом случае: каков "тупой" способ реализовать проверку, подобную описанной выше?

4 ответа

Решение

Как я уже упоминал в комментарии, вы можете попробовать использовать некоторые хитрости.

  • Во-первых, давайте создадим массив с размером ваших аномалий: мы можем поставить его как np.int8 чтобы сэкономить место

    anomalies = x - x.mean()
    signs = np.sign(anomalies).astype(np.int8)
    
  • Теперь о шагах. Если вы хотите рассмотреть N подряд очки, вы будете использовать

    from np.lib.stride_tricks import as_strided
    strided = as_strided(signs, 
                         strides=(signs.itemsize,signs.itemsize), 
                         shape=(signs.shape,N))
    

    Это дает нам (x.size, N) Rollin массив: первый ряд x[0:N], второй x[1:N+1]... конечно, последний N-1 строки будут бессмысленными, так что теперь мы будем использовать

    strided = strided[:-N+1]
    
  • Подведем итоги по строкам

    consecutives = strided.sum(axis=-1)
    

    Это дает нам массив размеров (x.size-N+1) значений между -N а также +N: нам просто нужно найти абсолютные значения N:

    (indices,) = np.nonzero(consecutives == N)
    

    indices это массив индексов i вашего массива x для которого значения x[i:i+N] находятся на одной стороне от среднего...

Пример с x=np.random.rand(10) а также N=3

>>> x = array([ 0.57016436,  0.79360943,  0.89535982,  0.83632245,  0.31046202,
            0.91398363,  0.62358298,  0.72148491,  0.99311681,  0.94852957])
>>> signs = np.sign(x-x.mean()).astype(np.int8)
array([-1,  1,  1,  1, -1,  1, -1, -1,  1,  1], dtype=int8)
>>> strided = as_strided(signs,strides=(1,1),shape=(signs.size,3))
array([[  -1,    1,    1],
       [   1,    1,    1],
       [   1,    1,   -1],
       [   1,   -1,    1],
       [  -1,    1,   -1],
       [   1,   -1,   -1],
       [  -1,   -1,    1],
       [  -1,    1,    1],
       [   1,    1, -106],
       [   1, -106,  -44]], dtype=int8)
>>> consecutive=strided[:-N+1].sum(axis=-1)
array([ 1,  3,  1,  1, -1, -1, -1,  1])
>>> np.nonzero(np.abs(consecutive)==N)
(array([1]),)
import numpy as np
x = np.random.rand(100)
f = np.sign(x - x.mean())
c = np.cumsum(f)
d = c[9:] - c[:-9]
print np.max(d), np.min(d)

если np.max(d) == 9 или np.min(d) == -9, то в строке находятся девять (или более) точек с одной и той же стороны от среднего значения.

Или вы можете использовать следующий код для расчета длины каждой строки:

np.diff(np.where(np.diff(np.r_[-2,f,-2]))[0])

Другая возможность: использовать коррелировать или свертывать

>>> a = np.random.randn(50)
>>> b = (a - a.mean()) > 0
>>> b.astype(int)
array([0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1,
       1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0,
       1, 1, 1, 1])

>>> c = np.correlate(b, np.ones(3), mode='valid')
>>> c
array([ 2.,  2.,  1.,  1.,  1.,  1.,  0.,  0.,  1.,  2.,  3.,  2.,  2.,
        1.,  1.,  0.,  0.,  1.,  2.,  3.,  3.,  3.,  3.,  3.,  2.,  2.,
        2.,  2.,  2.,  1.,  1.,  1.,  1.,  2.,  1.,  2.,  2.,  2.,  1.,
        0.,  0.,  1.,  2.,  2.,  2.,  2.,  3.,  3.])

>>> c.max() == 3
True
>>> c.min() == 0
True

Это будет медленнее, чем версия HYRY cumsum.

в сторону: в statsmodels есть тесты для тестирования подобных прогонов

Дано data и минимальный length, вы можете проверить, есть ли массив

np.diff(np.cumsum(np.sign(data - np.mean(data))), length)

содержит ноль.

Другие вопросы по тегам