#python #python-3.x
#python #python-3.x
Вопрос:
Это принимает a numpy.array
и возвращает a pandas.Series
.
Есть ли более питонический способ написания этого, а for
не цикл с if
s?
def channel_memory(elements, lower_bound, upper_bound):
signal = 0;
signals = [];
for element in elements:
if element is np.nan:
pass
elif signal != 1 and element >= upper_bound:
signal = 1
elif signal != -1 and element <= lower_bound:
signal = -1
signals.append(signal)
return pd.Series(signals)
Ответ №1:
Следует отметить, что некоторые проверки не так уж необходимы. Если элемент np.nan
— ни одно из сравнений не будет истинным, поэтому предыдущее значение будет сохранено. То же самое касается проверок signal != /-1
— если signal
is 1
и новый элемент больше, чем граница, ничего не изменится. Итак, одним из способов «улучшить» код может быть:
def channel_memory(elements, lower_bound, upper_bound):
signal = 0
signals = []
for element in elements:
if element >= upper_bound:
signal = 1
elif element <= lower_bound:
signal = -1
signals.append(signal)
return pd.Series(signals)
Следующий альтернативный способ может быть длиннее по длине кода, но я чувствую, что он более четко передает, что происходит в коде с использованием итераторов. Сначала мы добавляем 0
s до тех пор, пока число находится между границами. Как только он покинул границы, мы продолжаем добавлять одно и то же значение до тех пор, пока не произойдет изменение в привязке, пока итератор в списке не будет исчерпан:
def channel_memory(elements, lower_bound, upper_bound):
signals = []
elements = iter(elements)
element = next(elements)
while lower_bound < element < upper_bound:
signals.append(0)
element = next(elements)
while True:
try:
while element > lower_bound:
signals.append(1)
element = next(elements)
while element < upper_bound:
signals.append(-1)
element = next(elements)
except StopIteration:
break
return pd.Series(signals)
Ответ №2:
В дополнение к упрощениям, которые были предложены Tomerikoo, я бы предложил использовать функцию генератора для yield
значений сигналов.
Тогда вам не нужен временный список, и, более того, я бы рассмотрел этот Pythonic 🙂
def channel_memory(elements, lower_bound, upper_bound):
def signals():
signal = 0
for element in elements:
if element >= upper_bound:
signal = 1
elif element <= lower_bound:
signal = -1
yield signal
return pd.Series(signals())
Комментарии:
1. Как вы думаете, это будет быстрее, чем первый ответ Tomerikoo? Первый ответ Tomerikoo кажется (мне) наиболее читаемым.
2. Я не проверял, какой подход быстрее. Решение генератора не нуждается во временном списке и, таким образом, экономит выделение памяти, что также требует времени. С другой стороны, управление генератором может добавить некоторые накладные расходы. Вам нужно будет попробовать его с подходящим эталоном 🙂
3. Кстати, я вижу, что решение на основе списков может выглядеть более читабельным, если вы не знакомы с генераторами и
yield
ключевым словом. Я сам был в такой же ситуации несколько лет назад. Но генераторы часто используются в Python и позволяют вам делать много вещей, таких как создание итераций, которые не помещаются в память в виде временного списка, или даже бесконечных итераций. Поэтому я бы рекомендовал каждому программисту на Python ознакомиться сyield
ключевым словом 🙂
Ответ №3:
Если вы уже используете pandas, почему бы не использовать его в полной мере?
def channel_memory(elements, lower_bound, upper_bound):
return pd.Series(elements).fillna(method = 'ffill').fillna(0).clip(lower = lower_bound, upper = upper_bound)
PS Мне это тоже кажется наиболее питоническим.
Как это работает?
fillna(method = 'ffill')
продолжает заполнять значения nan последним значением, отличным от nan, с которым он столкнулся. Так что обслуживает часть памяти вашей логики. fillna(0)
затем заполняет любое количество начальных nan
значений нулями (то, что вы использовали в качестве начального значения сигнала. Это, конечно, может быть любое значение). .clip()
затем обрезает все значения до диапазона (1,-1)
. Который эффективно эмулирует:
Запускайте сигнал с 0
, пока не встретится значение, отличное от nan. затем начинается заполнение значениями в диапазоне (-1, 1) на основе порога. В конечном итоге перевод на вашу логику.
Комментарии:
1. Это просто «обрезает» верхние и нижние значения? Как это сохраняет память о предыдущем значении сигнала?
2. Обновлено!
fillna()
в сочетании с этим вы ищете, чтобы сохранить память о предыдущем значении сигнала.3. Я думаю, что fillna() просто заполняет значения NaN, я не думаю, что это совсем то, что делает моя логика. Пожалуйста, объясните подробнее, если вы считаете, что это так