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.