Cela prend un numpy.array et renvoie un pandas.Series .
Existe-t-il une manière plus pythonique d'écrire ceci plutôt qu'une boucle for avec 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)
3 Réponses :
Une chose à noter est que certaines vérifications ne sont pas si nécessaires. Si l'élément est np.nan - aucune des comparaisons ne sera vraie, donc la valeur précédente sera conservée. Il en va de même pour les vérifications du signal != +/-1 - si le signal est 1 et que le nouvel élément est supérieur à la borne, rien ne changera. Donc, une façon d '"améliorer" le code peut être:
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)
La méthode alternative suivante peut être plus longue en longueur de code, mais je pense qu'elle exprime plus clairement ce qui se passe dans le code avec l'utilisation d'itérateurs. Nous ajoutons d'abord 0 s tant que le nombre est entre les bornes. Une fois qu'il a quitté les limites, nous continuons à ajouter la même valeur jusqu'à ce qu'il y ait un changement de borne, jusqu'à ce que l'itérateur de la liste soit épuisé:
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)
En plus des simplifications proposées par Tomerikoo, je suggérerais d'utiliser une fonction générateur pour yield les valeurs du signal.
Ensuite, vous n'avez pas besoin d'une liste temporaire, et de plus, je considérerais ce 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())
Pensez-vous que ce sera plus rapide que la première réponse de Tomerikoo? La première réponse de Tomerikoo me semble la plus lisible.
Je n'ai pas testé quelle approche est la plus rapide. La solution de générateur n'a pas besoin d'une liste temporaire et économise ainsi les allocations de mémoire, qui nécessitent également du temps. D'autre part, la gestion du générateur peut ajouter des frais généraux. Il faudrait l'essayer avec un benchmark approprié :-)
BTW, je vois que la solution basée sur une liste peut sembler plus lisible si vous n'êtes pas familier avec les générateurs et le mot-clé yield . J'étais moi-même dans la même situation il y a quelques années. Mais les générateurs sont beaucoup utilisés en Python et vous permettent de faire beaucoup de choses, comme construire des itérables qui ne rentreraient pas en mémoire comme une liste temporaire, ou même des itérables infinis. Par conséquent, je recommanderais à chaque programmeur Python de se familiariser avec le mot-clé yield :-)
Lorsque vous utilisez déjà des pandas, pourquoi ne pas l'utiliser au maximum?
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 Cela me semble aussi le plus pythonique.
fillna(method = 'ffill') continue de remplir les valeurs nan avec la dernière valeur non nan rencontrée. Donc répond à la partie mémoire de votre logique. fillna(0) remplit alors n'importe quel nombre de valeurs nan significatives avec des zéros (ce que vous avez utilisé comme valeur de signal initiale. Cela peut bien sûr être n'importe quelle valeur). .clip() alors toutes les valeurs dans la plage (1,-1) . Qui émule efficacement:
Démarrez un signal avec 0 , jusqu'à ce qu'une valeur non nanométrique se rencontre. puis commence le remplissage avec des valeurs comprises entre (-1, 1) en fonction du seuil. Finalement, traduire dans votre logique.
Est-ce que cela «coupe» simplement les valeurs supérieures et inférieures? Comment cela conserve-t-il une mémoire de la valeur du signal précédent?
Mis à jour! fillna() associé à ceci est ce que vous recherchez pour conserver une mémoire de la valeur du signal précédent.
Je pense que fillna () remplit juste les valeurs NaN, je ne pense pas que ce soit tout à fait ce que fait ma logique. Veuillez expliquer davantage si vous pensez que oui