Рассуждение о последовательных точках данных без использования итерации
Я делаю анализ 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)
содержит ноль.