J'ai une série de pandas comme pd.Series ([- 1, -1, -1, 0, 0, 0, -5, -5, 0, 0, 0, -1, -1, -1, -1]) . Comment puis-je le convertir en pd.Series ([- 1, 0, 0, 0, -5, -5, 0, 0, 0, -1]) .
La condition à filtrer est que si les -1 sont supérieurs ou égaux à 3 dans une séquence, alors gardez la première occurrence et supprimez le reste.
Puisque la première séquence de -1 est 3 , nous gardons -1 et supprimons le reste. Après les premières valeurs 3 , la séquence se brise (puisque la valeur est maintenant 0 ). De même, la dernière séquence de -1 est 4 , donc nous gardons le -1 et supprimons le reste.
Le filtre s'applique uniquement à -1 et -5 doit être laissé tel quel
Merci
PS: J'ai pensé à groupby, mais je pense que cela ne respecte pas la séquence que j'ai décrite ci-dessus
4 Réponses :
IIUC, masquage des pandas et groupby:
>>> remove_streaks(4) [-1, -1, -1, 0, 0, 0, -5, -5, 0, 0, 0, -1] >>> remove_streaks(3) [-1, 0, 0, 0, -5, -5, 0, 0, 0, -1]
def remove_streaks(T):
'''T is the threshold
'''
g = s.groupby(s.diff().ne(0).cumsum() + s.ne(-1).cumsum())
mask = g.transform('size').lt(T).cumsum() + s.diff().ne(0).cumsum()
return s.groupby(mask).first()
Avec masque conditionnel :
In [43]: s = pd.Series([-1, -1, -1, 0, 0, 0, -5, -5, 0, 0, 0, -1, -1, -1 , -1]) In [44]: m = (s.diff() == 0) & (s.eq(-1)) In [45]: s[~m] Out[45]: 0 -1 3 0 4 0 5 0 6 -5 7 -5 8 0 9 0 10 0 11 -1 dtype: int64
Cela supprime les stries de 2. Il ne devrait y en avoir que 3 ou plus.
Avec certains outils SciPy -
# Using .tolist() simply for better visualization In [47]: s.tolist() Out[47]: [-1, -1, -1, 0, 0, 0, -5, -5, 0, 0, 0, -1, -1, -1, -1] In [48]: keep_first_neg1s(s,W=3).tolist() Out[48]: [-1, 0, 0, 0, -5, -5, 0, 0, 0, -1] In [49]: keep_first_neg1s(s,W=4).tolist() Out[49]: [-1, -1, -1, 0, 0, 0, -5, -5, 0, 0, 0, -1]
Un plus simple et, espérons-le, plus performant aussi -
def keep_first_neg1s_v2(s, W=3):
m1 = binary_opening(a==-1, np.ones(W,dtype=bool))
return s[np.r_[True,~m1[:-1]]]
S'exécute sur un échantillon donné s -
from scipy.ndimage.morphology import binary_opening,binary_erosion
def keep_first_neg1s(s, W=3):
k1 = np.ones(W,dtype=bool)
k2 = np.ones(2,dtype=bool)
m = s==-1
return s[~binary_erosion(binary_opening(m,k1),k2) | ~m]
Créez un masque booléen m pour identifier les positions où les valeurs changent. Groupby s sur m.cumsum () avec transformation pour identifier les groupes ayant un numéro de -1 m1 . Booléen m ou m1 et cumsum pour séparer uniquement les groupes avec numéro -1 > = 3 dans le même nombre. Enfin, utilisez dupliqué pour découper.
s Out[148]: 0 -1 1 -1 2 -1 3 0 4 -1 5 -1 6 0 7 0 8 -5 9 -5 10 0 11 0 12 0 13 -1 14 -1 15 -1 16 -1 dtype: int64 m = s.diff().ne(0) Out[150]: 0 True 1 False 2 False 3 True 4 True 5 False 6 True 7 False 8 True 9 False 10 True 11 False 12 False 13 True 14 False 15 False 16 False dtype: bool m1 = s.groupby(m.cumsum()).transform(lambda x: x.eq(-1).sum() < 3) Out[152]: 0 False 1 False 2 False 3 True 4 True 5 True 6 True 7 True 8 True 9 True 10 True 11 True 12 True 13 False 14 False 15 False 16 False dtype: bool m2 = ~((m | m1).cumsum().duplicated()) Out[159]: 0 True 1 False 2 False 3 True 4 True 5 True 6 True 7 True 8 True 9 True 10 True 11 True 12 True 13 True 14 False 15 False 16 False dtype: bool In [168]: s[m2] Out[168]: 0 -1 3 0 4 -1 5 -1 6 0 7 0 8 -5 9 -5 10 0 11 0 12 0 13 -1 dtype: int64
Pas à pas :
Je modifie votre échantillon pour inclure le cas -1 ayant 2 lignes consécutives que nous devrions conserver.
m = s.diff().ne(0) m1 = s.groupby(m.cumsum()).transform(lambda x: x.eq(-1).sum() < 3) m2 = ~((m | m1).cumsum().duplicated()) s[m2]
si -1s est supérieur à 3 dans une séquence - vous voulez dire plus de 2 ?
Désolé pour la confusion. Il aurait dû être supérieur ou égal à 3. Ajout de la modification à la question. Merci de l'avoir signalé
L'une des solutions affichées a-t-elle fonctionné pour vous?
Merci pour votre réponse Divakar. Je n'ai pas pu commenter plus tôt. Pardon