J'ai un tableau de 0 et de 1 en tant que tel
[0,0,3,0,0,0,0,0,0,2,0,0,0]
Je veux définir une fonction qui prendra ce tableau en entrée et sortira un tableau de même longueur, avec le nombre de adjacents 1 dans l'index où le premier 1 est apparu (et 0 sinon). Donc, la sortie serait
[0,0,1,1,1,0,0,0,0,1,1,0,0]
car 1 est apparu dans le 2ème index 3 fois consécutives et 1 est apparu dans le 9ème index 2 fois consécutives.
Y a-t-il un moyen de faire cela en utilisant numpy? Sinon, y a-t-il un moyen pythonique (efficace) de le faire?
8 Réponses :
Vous pouvez utiliser groupby pour regrouper les éléments consécutifs :
def groups(lst): for key, group in groupby(lst): count = sum(1 for _ in group) if key and count > 1: yield count yield from (0 for _ in range(count - key)) print(list(groups(a)))
Sortie
[0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
Un plus court (plus pythonique ?) est ce qui suit:
from itertools import groupby a = [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0] def groups(lst): result = [] for key, group in groupby(lst): if not key: # is a group of zeroes result.extend(list(group)) else: # is a group of ones count = sum(1 for _ in group) if count > 1: # if more than one result.append(count) result.extend(0 for _ in range(count - 1)) else: result.append(0) return result print(groups(a))
Voici une façon d'utiliser numpy et une compréhension de liste:
In [63]: def summ_cons(li): ...: for k,g in groupby(li) : ...: if k: ...: s = sum(g) ...: yield s ...: yield from (0 for _ in range(s-1)) ...: yield from g ...: In [65]: list(summ_cons(a)) Out[65]: [0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
La logique:
np.hstack
. Si vous voulez remplacer les restants par 0, procédez comme suit:
In [28]: np.hstack([[x.sum(), *[0]*(len(x) -1)] if x[0] == 1 else x for x in np.split(a, np.where(np.diff(a) != 0)[0]+1)]) Out[28]: array([0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0])
[0] * (len (x) - 1)
créera les 0 attendus pour vous et en utilisant un déballage sur place, vous pourrez les placer à côté de la sum(x)
.
Si vous avez toujours voulu une approche Python pure, voici une façon d'utiliser itertools.groupby
:
In [23]: a = np.array([0,0,1,1,1,0,0,0,0,1,1,0,0]) In [24]: np.hstack([x.sum() if x[0] == 1 else x for x in np.split(a, np.where(np.diff(a) != 0)[0]+1)]) Out[24]: array([0, 0, 3, 0, 0, 0, 0, 2, 0, 0])
Cela produit un tableau plus court que le tableau d'entrée.
@fuglede Je viens d'ajouter ce cas aussi
Votre deuxième exemple est parfait car il fonctionne pour les cas où le tableau commence et se termine par 0 ou 1 ... c'est une excellente solution .... np.hstack ([[x.sum (), * [0] * ( len (x) -1)] si x [0] == 1 sinon x pour x dans np.split (a, np.where (np.diff (a)! = 0) [0] +1)])
Utilisation du module itertools
:
from itertools import chain, groupby A = [0,0,1,1,1,0,0,0,0,1,1,0,0] def get_lst(x): values = list(x[1]) return [len(values)] + [0]*(len(values) - 1) if x[0] else values res = list(chain.from_iterable(map(get_lst, groupby(A)))) # [0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
Considérant qu'OP a utilisé numpy
et qu'il existe également de nombreuses réponses génératrices / itertools similaires et meilleures ici, celle-ci est très inefficace. Si vous souhaitez utiliser itertools
ou en général Python, il existe déjà des moyens meilleurs et plus rapides de le faire.
@ Kasrâmvd, tout à fait d'accord. Je conseillerais à OP de choisir une alternative à NumPy si celle-ci était proposée.
0 [0, 0] 1 [1, 1, 1] 0 [0, 0, 0, 0] 1 [1, 1] 0 [0, 0]
vous donnera:
for k, g in itertools.groupby(input): print(g, list(k))
itertools a la fonction groupby
pour diviser les exécutions de "le même"
[0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
vous donnera:
import itertools input = [0,0,1,1,1,0,0,0,0,1,1,0,0] result = [] for k, g in itertools.groupby(input): if k == 1: ll = len(list(g)) result.extend([ll,] + [0 for _ in range(ll-1)]) else: result.extend(list(g))
donc k
est la clé, l'élément dans la séquence d'entrée et g
est le groupe.
donc les conditions if ajoutent soit (dans le cas d'un 0) la série de 0 à partir de l'entrée, soit la longueur si la série de 1 plus les zéros à remplir pour la longueur de la 1-course.
La longueur du tableau de sortie ne correspond pas à la longueur du tableau d'entrée.
Voici une solution utilisant des opérations vectorisées pures et aucune itération de liste:
import numpy as np data = np.array([0,0,1,1,1,0,0,0,0,1,1,0,0]) output = np.zeros_like(data) where = np.where(np.diff(data))[0] vals = where[1::2] - where[::2] idx = where[::2] + 1 output[idx] = vals output # array([0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0])
Notez que cela échoue si data
se termine par un 1.
Il échoue également si data
commence par 1
. Je pense que cela fonctionne si data [0]
et data [-1]
sont identiques
Utilisez des pandas, profitez des pandas count
compte des valeurs non NaN. Créez des NaN en utilisant le masque
puis groupez les modifications des valeurs de s.
[0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
Sortie:
import pandas as pd l = [0,0,1,1,1,0,0,0,0,1,1,0,0] s = pd.Series(l) g = s.diff().ne(0).cumsum() s.mask(s==0).groupby(g).transform('count').mask(g.duplicated(), 0).tolist()
Autre option sans dépendances:
La bonne vieille boucle while
accédant par index (parfois plus rapide que numpy):
def count_same_adjacent_non_zeros(iterable): i, x, size = 0, 0, len(iterable) while i < size-1: if iterable[i] != iterable[i+1]: tmp = iterable[x:i+1] if not iterable[i] == 0: tmp = [len(tmp)] + [0 for _ in range(i-x)] for e in tmp: yield e x = i + 1 i += 1 for e in iterable[x:size]: yield e print(list(count_same_adjacent_non_zeros(array))) #=> [0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
pouvez-vous partager ce que vous avez essayé jusqu'à présent?
Comme ce que vous avez essayé n'est pas clair, regardez peut-être groupby a >?
Quelle est la sortie pour un '1' non adjacent, par ex.
[0, 1, 0]
? est-ce[0, 0, 0]
ou[0, 1, 0]
? Votre explication implique la première, mais seulement indirectement.