Допустим, у меня есть массив:
a = [1,4,3,6,4]
Я хочу получить массив, в котором для каждого элемента есть наименьшее значение в a
справа (включительно). То есть для a
я бы хотел создать массив:
[1,3,3,4,4]
Есть ли быстрый и лаконичный способ сделать это?
@BigBen Да, извини, исправил опечатку
Ваш вопрос на самом деле неоднозначный. Хотите ли вы рассмотреть следующее значение или все следующие значения?
вычислите накопленный минимум в перевернутом массиве с помощью минимум.аккумулировать:
a = np.array([1,4,3,6,4])
out = np.minimum.accumulate(a[::-1])[::-1]
Выход:
array([1, 3, 3, 4, 4])
С чистым Python и itertools.accumulate:
from itertools import accumulate
a = [1,4,3,6,4]
out = list(accumulate(a[::-1], min))[::-1]
# [1, 3, 3, 4, 4]
Вы можете изменить значения и использовать np.minimum:
a = np.array([1,4,3,6,4])
out = np.minimum(a, np.r_[a[1:], a[-1]])
Выход:
array([1, 3, 3, 4, 4])
или сравните последовательные значения и создайте маску для изменения a
на месте:
a = np.array([1,4,3,6,4])
m = a[:-1] > a[1:]
# or
# m = np.diff(a) < 0
a[np.r_[m, False]] = a[1:][m]
Изменено a
:
array([1, 3, 3, 4, 4])
a = np.array([1,4,3,6,5,6,4])
np.minimum.accumulate(a[::-1])[::-1]
# array([1, 3, 3, 4, 4, 4, 4])
np.minimum(a, np.r_[a[1:], a[-1]])
# array([1, 3, 3, 5, 5, 4, 4])
Я думаю, вы имеете в виду, что вам нужен наименьший из элементов справа от списка (включительно). Именно так я интерпретирую ваш пример. Если да, то это решение:
b = [np.min(a[i:]) for i in range(len(a))]
Это довольно кратко. Если вы хотите, чтобы это был еще один массив np:
b = np.array([np.min(a[i:]) for i in range(len(a))])
Обновлено:
Это простой способ сделать это, но он не такой быстрый, как способ Mozway. Конкретно:
out = np.minimum.accumulate(a[::-1])[::-1]
что он и написал в своем ответе. Выполнение этих двух вычислений 100_000 раз занимает 2,2487 с для моего решения и 0,0972 с для решения Mozway, что делает его решение примерно в 23 раза быстрее. Я бы порекомендовал его решение.
Мое решение на самом деле медленнее, чем создание его на чистом Python без numpy. Вот решение на Python без numpy:
result = [min(b) for i in range(len(a))]
Это занимает 0,1890 с. Это все еще примерно в 2 раза медленнее, чем чистый способ, но ок. В 12 раз быстрее, чем смесь, которую я предоставил.
Решение с использованием pandas, предоставленное PaulS, очень интересное, но медленное со временем 7,7083 с.
Чтобы внести ясность, измеренное время относится к длинному списку/массиву из 6 элементов, и, как пишет Mozway, для более длинных списков, вероятно, разница еще больше.
Это был бы крайне неэффективный способ. Вам не нужно перезапускать все, чтобы вычислить текущий минимум. np.minimum.accumulate(a[::-1])[::-1]
делает то же самое эффективно (в 2000 раз быстрее для 10 тысяч элементов, в 7000 раз быстрее для 100 тысяч, для большего количества это может даже не работать)
Я надеюсь, что itertools.accumulate
сможет дать вам желаемый результат
from itertools import accumulate
list(accumulate(a[::-1], lambda x, y: [x,y][y <= x]))[::-1]
# [1, 3, 3, 4, 4]
Я только что добавил вариант accumulate
к своему ответу, но list(accumulate(a[::-1], min))[::-1]
достаточно;)
@mozway ага, только что увидел это в твоем ответе, удалю свое решение
Вы можете оставить это!
@mozway Хорошо, спасибо, но у тебя очень кратко, ура!
Другое возможное решение — использовать панду тмин:
pd.Series(a[::-1]).cummin().iloc[::-1].values
Выход:
array([1, 3, 3, 4, 4])
Какой должен быть результат для
a = np.array([1,4,3,6,5,6,4])
?