2017-05-01 2 views
2

J'ai une liste de valeurs flottantes qui représentent l'heure d'une observation. (Chaque valeur flottante peut effectivement être représentée comme un entier, mais j'espère pouvoir généraliser pour d'éventuelles circonstances futures).Comment un pad peut-il mettre un zéros entre des éléments flottants non consécutifs d'une liste triée contenant des doublons?

list_hrs = [4,6,8,8,10] # actual list is thousands of floats 

Je suis en train de rembourrer les valeurs qui ne correspondent pas à leurs indices avec zéro tout en ne comptant que une seule occurrence d'entrées en double. Par exemple la liste, je veux

list_hrs = [0,0,0,0,4,0,6,0,8,8,0,10] 

Les quatre premières entrées sont 0 parce qu'il ya quatre numéros inclusivement 0-3. Le 0 entre 4 et 6 est voulu là parce que 5 est manquant; de même pour 0 entre 6 et 8. Le 0 entre 8 et 10 est recherché ici parce que la valeur 9 est manquante. De plus, les doublons 8 ne sont pas touchés, car ils seront traités plus tard dans mon code; une seule occurrence de la copie 8 doit être prise en compte avant le remplissage 0.

Ma première tentative a été d'essayer ceci:

for index in range(len(list_hrs)): 
    if list_hrs != index: 
     list_hrs.insert(index, 0) 

>> [0, 0, 0, 0, 0, 4, 6, 8, 8, 10] 

J'ai lu alors différents SO messages et sont repartis avec l'impression qu'il vaut mieux d'abord faire une liste des 0 « s, dont la longueur doit être égal au nombre de points de données considérés. Ensuite, les entrées non nulles peuvent remplacer les entrées 0. Donc, j'ai essayé le texte suivant:

def make_zeros(hrs=list_hrs): # make list of 0's 
    num_zer = int(max(hrs)) 
    list_zer = [0 for index in range(num_zer+1)] 
    return list_zer 

Mais je ne suis pas sûr de la façon de mettre en œuvre la condition pour obtenir le résultat désiré après ce point. Je pense qu'il existe un moyen d'utiliser enumerate pour vérifier si l'index correspond à la valeur de cet index, mais je ne sais pas comment procéder en raison d'entrées en double (comme les 8 dans l'exemple ci-dessus).

Cette méthode est-elle une bonne direction pour continuer à fonctionner, ou existe-t-il un moyen plus efficace/plus simple d'atteindre le résultat souhaité? Toute aide ou conseil serait apprécié.

+0

Pourquoi sont-ils flottants, attendez-vous des nombres décimaux là-dedans? Comme pourrait-il être dit «4.2»? Si oui, comment devrait ressembler la sortie? – Divakar

+0

En outre, l'entrée serait-elle toujours triée? – Divakar

+0

Chaque flottant peut effectivement être interprété comme un nombre entier. La liste d'exemples devrait en fait être '[4.0.6.0,8.0,8.0,10.0]'. J'espérais juste généraliser le code. Mais s'il y a une solution pour le cas des entiers, cela fonctionnera bien pour ce cas. – mikey

Répondre

2

Voici une approche vectorisé -

def make_zeros_vectorized(A, dtype=float): 
    a = np.asarray(A).astype(int) 
    idx = a + np.r_[0, (a[1:] == a[:-1]).cumsum()] 
    out = np.zeros(idx[-1]+1,dtype=dtype) 
    out[idx] = A 
    return out 

runs exemples -

In [95]: A 
Out[95]: [4.0, 6.0, 8.0, 8.0, 10.0, 10.0, 10.0, 14.0, 16.0] 

In [96]: make_zeros_vectorized(A) 
Out[96]: 
array([ 0., 0., 0., 0., 4., 0., 6., 0., 8., 8., 0., 
     10., 10., 10., 0., 0., 0., 14., 0., 16.]) 

In [100]: A 
Out[100]: [4.0, 4.0, 4.0, 4.0, 6.0, 8.0, 8.0, 10.0, 10.0, 10.0, 14.0, 16.0] 

In [101]: make_zeros_vectorized(A) 
Out[101]: 
array([ 0., 0., 0., 0., 4., 4., 4., 4., 0., 6., 0., 
     8., 8., 0., 10., 10., 10., 0., 0., 0., 14., 0., 
     16.]) 

étapes

Liste d'entrée

In [71]: A = [4.0,6.0,8.0,8.0,10.0,10.0,10.0,14.0,16.0] 

Convertir en tableau

In [72]: a = np.asarray(A).astype(int) 

In [73]: a 
Out[73]: array([ 4, 6, 8, 8, 10, 10, 10, 14, 16]) 

Créez un masque de doublons. C'est le point central de cette approche, car nous prévoyons d'utiliser la sommation cumulative plus tard.Avec les doublons étant représentés comme vrai, lorsque cumulativement résumé se traduirait par des valeurs incrémentielles, pour être utilisés comme indices différentiels pour placer les valeurs de tableau d'entrée dans le tableau de sortie

In [74]: a[1:] == a[:-1] 
Out[74]: array([False, False, True, False, True, True, False, False], dtype=bool) 

In [75]: (a[1:] == a[:-1]).cumsum() 
Out[75]: array([0, 0, 1, 1, 2, 3, 3, 3]) 

Ajoute un zéro au début, en tant que précédemment "a [1:] == a [- 1]" aurait résulté en un élément inférieur matrice

In [76]: np.r_[0, (a[1:] == a[:-1]).cumsum()] 
Out[76]: array([0, 0, 0, 1, 1, 2, 3, 3, 3]) 

Enfin, ajouter à la matrice d'entrée de sorte que les doublons sont déplacés/ajoutés one-up et nous donnant ainsi les indices auxquels le tableau de sortie doit être affecté

In [77]: a + np.r_[0, (a[1:] == a[:-1]).cumsum()] 
Out[77]: array([ 4, 6, 8, 9, 11, 12, 13, 17, 19]) 

Les étapes ultérieures créent fondamentalement un tableau de sortie et lui affectent des valeurs de a en utilisant les indices obtenus précédemment.


Si vous avez besoin du masque de zéros ou les indices, voici une version modifiée -

def get_zeros_mask(A): 
    a = np.asarray(A).astype(int) 
    idx = a + np.r_[0, (a[1:] == a[:-1]).cumsum()] 
    mask = np.ones(idx[-1]+1,dtype=bool) 
    mask[idx] = 0 
    return mask 

run échantillon -

In [93]: A 
Out[93]: [4.0, 6.0, 8.0, 8.0, 10.0, 10.0, 10.0, 14.0, 16.0] 

In [94]: make_zeros_vectorized(A) 
Out[94]: 
array([ 0., 0., 0., 0., 4., 0., 6., 0., 8., 8., 0., 
     10., 10., 10., 0., 0., 0., 14., 0., 16.]) 

In [95]: get_zeros_mask(A) 
Out[95]: 
array([ True, True, True, True, False, True, False, True, False, 
     False, True, False, False, False, True, True, True, False, 
     True, False], dtype=bool) 

In [96]: np.flatnonzero(get_zeros_mask(A)) 
Out[96]: array([ 0, 1, 2, 3, 5, 7, 10, 14, 15, 16, 18]) 
+0

Le code fonctionne parfaitement! Si vous comprenez ce que vous avez fait, 'np.r_' concatène les entrées dans une seule liste. Je comprends que cumsum est une somme cumulative, mais que fait cette ligne de code? – mikey

+0

Merci pour l'explication. Alors que l'autre code est plus flexible en termes de gestion des flottants, ce code est beaucoup plus rapide, ce qui est utile pour des milliers de points de données. – mikey

+0

Cela servirait mieux à mes fins de trouver les indices pour lesquels les zéros devraient être complétés. De cette façon, je peux tamponner des zéros pour les temps d'observation et les valeurs observées à ces moments (puisqu'une heure pour laquelle aucune observation n'a eu lieu correspond à une valeur observée de zéro). J'ai été incapable d'adapter ce code à cette fin. Vous avez été généreux de me donner un code de travail complet pour ma question originale; Pouvez-vous s'il vous plaît m'aider à trouver un moyen de le faire? Ai-je raison d'essayer de changer 'out [idx] = A' en quelque chose comme' out [not idx] = A'? (Je ne connais pas la syntaxe correcte). – mikey

1

Juste un autre exemple:

list_hrs = [4,6,8,8,10] 
lh = iter(list_hrs) 
fit = range(int(max(list_hrs))+1) 

result = [0 if i not in list_hrs else next(lh) for i in fit for _ in range(list_hrs.count(i)) or [1]] 
+0

Je n'ai jamais utilisé 'iter' avant, mais cela a du sens pour moi. Merci pour l'approche alternative. – mikey

+0

Vous êtes les bienvenus :) – zipa

+0

Ce code est également plus polyvalent car il peut également gérer les flotteurs. – mikey