Как посчитать в векторе чисел количество убывающих или возрастающих элементов в сравнении с предыдущим?

Есть вектор с числами.

import numpy as np

arr = np.array([1, 3, 6, 5, 3, 2, 1, 2, 4, 8, 5, 4, 3, 1], float)

Задача посчитать количество убывающих или возрастающих элементов в сравнении с предыдущим. Сбрасывать до 1 или -1 если изменилось направление, т.е., если убывало и стало возрастать, или наоборот. В результате должен быть вектор:

[ 1.  2. -1. -2. -3. -4.  1.  2.  3. -1. -2. -3. -4.]

Ответы (4 шт):

Автор решения: nick
import numpy as np

arr = np.array([1, 3, 6, 5, 3, 2, 1, 2, 4, 8, 5, 4, 3, 1], float)

current = arr[1:]
previous = arr[:-1]

res = np.empty(current.size)
upcount = 0
dncount = 0

for i in range(current.size):
    if current[i] > previous[i]:
        res[i] = upcount = upcount + 1
        dncount = 0

    elif current[i] < previous[i]:
        res[i] = dncount = dncount - 1
        upcount = 0
        
print(res)
→ Ссылка
Автор решения: MaxU

Воспользуйтесь Pandas:

import pandas as pd

s = pd.Series(arr)
pos = s.groupby((s.shift() > s).cumsum()).cumcount()
neg = s.groupby((s.shift() < s).cumsum()).cumcount()

res = np.where(s.diff() > 0, pos, -neg)[1:]

In [46]: res
Out[46]: array([ 1,  2, -1, -2, -3, -4,  1,  2,  3, -1, -2, -3, -4], dtype=int64)

слегка измененное решение:

sd = s.diff()
pos = s.groupby(sd.lt(0).cumsum()).cumcount()
neg = s.groupby(sd.gt(0).cumsum()).cumcount()
res = np.where(sd > 0, pos, -neg)[1:]
→ Ссылка
Автор решения: strawdog

Я бы советовал использовать pandas, и, наверняка вам покажут, как. Мне вот было интересно средствами numpy:

import numpy as np

arr = np.array([1, 3, 6, 5, 3, 2, 1, 2, 4, 8, 5, 4, 3, 1], float)
signs = np.sign(np.diff(arr))
res = np.concatenate([x.cumsum() for x in 
                      np.split(signs, np.where(signs!=np.roll(signs, 1))[0])[1:]])

res:

array([ 1.,  2., -1., -2., -3., -4.,  1.,  2.,  3., -1., -2., -3., -4.])​
→ Ссылка
Автор решения: nick

Мне подсказали еще один вариант с NumPy, который по замерам быстрее решения с Pandas.

arr = np.diff(arr)
pos = np.clip(arr, 0, 1).astype(bool).cumsum()
neg = np.clip(arr, -1, 0).astype(bool).cumsum()
res = np.where(arr >= 0, pos - np.maximum.accumulate(np.where(arr <= 0, pos, 0)),
                  -neg + np.maximum.accumulate(np.where(arr >= 0, neg, 0)))

Замер скорости

import numpy as np
import pandas as pd
import timeit

arr = np.array([1, 3, 6, 5, 3, 2, 1, 2, 4, 8, 5, 4, 3, 1], float)
arr = np.concatenate([arr] * 10 ** 4)

# 1 Pandas

starttime = timeit.default_timer()
s = pd.Series(arr)
sd = s.diff()
pos = s.groupby(sd.lt(0).cumsum()).cumcount()
neg = s.groupby(sd.gt(0).cumsum()).cumcount()
streak = np.where(sd > 0, pos, -neg)[1:]
print("Execution time #1:", timeit.default_timer() - starttime)

# 2 Numpy

starttime = timeit.default_timer()
arr = np.diff(arr)
pos = np.clip(arr, 0, 1).astype(bool).cumsum()
neg = np.clip(arr, -1, 0).astype(bool).cumsum()
streak = np.where(arr >= 0, pos - np.maximum.accumulate(np.where(arr <= 0, pos, 0)),
                  -neg + np.maximum.accumulate(np.where(arr >= 0, neg, 0)))
print("Execution time #2:", timeit.default_timer() - starttime)
Execution time #1: 0.026871045999999954
Execution time #2: 0.0042138660000000105
→ Ссылка