Как посчитать в векторе чисел количество убывающих или возрастающих элементов в сравнении с предыдущим?
Есть вектор с числами.
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