2012-12-18 5 views
6

ich ein 1 dimensionalen Datensatz mit einigen ohne Datenwerte haben, die hier als 9999. gesetzt werden, ist ein Extrakt, wie es ist ziemlich lang:Python: Werte in einem Array austauschen

this_array = [ 4, 4, 1, 9999, 9999, 9999, -5, -4, ... ] 

Ich möchte ersetzen die keine Datenwerte mit dem Durchschnitt der nächsten Werte auf beiden Seiten, da jedoch einige Datenwerte keine engsten Werte haben, wie auch keine Datenwerte, ist es etwas schwieriger, sie zu ersetzen. , d. H. Ich möchte, dass die drei Nicht-Datenwerte durch -2 ersetzt werden. Ich habe eine Schleife geschaffen durch jede der Skalare in dem Array und Test für keine Daten zu gehen:

for k in this_array: 
    if k == 9999: 
     temp = np.where(k == 9999, (abs(this_array[k-1]-this_array[k+1])/2), this_array[k]) 
    else: 
     pass 
this_array[k] = temp 

Jedoch muss ich in eine hinzuzufügen, wenn Funktion oder Weg, um den Wert vor der k-1 oder nach dem k zu nehmen +1, wenn diese auch gleich 9999 zB:

if np.logical_or(k+1 == 9999, k-1 == 9999): 
    temp = np.where(k == 9999, (abs(this_array[k-2]-this_array[k+2])/2), this_array[k]) 

wie man sagen kann, dieser Code wird chaotisch wie man den falschen Wert unter oder enden mit vielen verschachtelten wenn Funktionen können am Ende. Kennt jemand eine sauberere Möglichkeit, dies zu implementieren, da es im gesamten Datensatz ziemlich variabel ist?

Wie angefordert: Wenn die ersten und/oder letzten Punkte keine Daten sind, werden sie vorzugsweise durch den nächsten Datenpunkt ersetzt.

+3

Was ist, wenn das letzte Element in der Liste '9999' ist? Um welchen Wert soll es ersetzt werden? – Cameron

+0

@Cameron Entschuldigung, wenn das letzte Element '9999' ist, kann es durch das zweitletzte Element ersetzt werden. Vielen Dank. – AJEnvMap

Antwort

3

Es kann eine weitere efficeint Weg, dies mit numpy Funktionen zu tun, aber hier ist eine Lösung, mit der itertools module:

from itertools import groupby 

for k, g in groupby(range(len(this_array)), lambda i: this_array[i] == 9999): 
    if k: 
     indices = list(g) 
     new_v = (this_array[indices[0]-1] + this_array[indices[-1]+1])/2 
     this_array[indices[0]:indices[-1]+1].fill(new_v) 

Wenn das letzte Element oder das erste Element 9999 sein können, verwenden Sie die folgenden Schritte aus:

from itertools import groupby 

for k, g in groupby(range(len(this_array)), lambda i: this_array[i] == 9999): 
    if k: 
     indices = list(g) 
     prev_i, next_i = indices[0]-1, indices[-1]+1 
     before = this_array[prev_i] if prev_i != -1 else this_array[next_i] 
     after = this_array[next_i] if next_i != len(this_array) else before 
     this_array[indices[0]:next_i].fill((before + after)/2) 

Beispiel für die Verwendung zweite Version:

>>> from itertools import groupby 
>>> this_array = np.array([9999, 4, 1, 9999, 9999, 9999, -5, -4, 9999]) 
>>> for k, g in groupby(range(len(this_array)), lambda i: this_array[i] == 9999): 
...  if k: 
...   indices = list(g) 
...   prev_i, next_i = indices[0]-1, indices[-1]+1 
...   before = this_array[prev_i] if prev_i != -1 else this_array[next_i] 
...   after = this_array[next_i] if next_i != len(this_array) else before 
...   this_array[indices[0]:next_i].fill((before + after)/2) 
... 
>>> this_array 
array([ 4, 4, 1, -2, -2, -2, -5, -4, -4]) 
1

Ich würde etwas in den folgenden Zeilen tun:

import numpy as np 

def fill(arr, fwd_fill): 
    out = arr.copy() 
    if fwd_fill: 
    start, end, step = 0, len(out), 1 
    else: 
    start, end, step = len(out)-1, -1, -1 
    cur = out[start] 
    for i in range(start, end, step): 
    if np.isnan(out[i]): 
     out[i] = cur 
    else: 
     cur = out[i] 
    return out 

def avg(arr): 
    fwd = fill(arr, True) 
    back = fill(arr, False) 
    return (fwd[:-2] + back[2:])/2. 

arr = np.array([ 4, 4, 1, np.nan, np.nan, np.nan, -5, -4]) 
print arr 
print avg(arr) 

Die erste Funktion tun kann entweder eine Vorwärts- oder ein Rückwärts füllen, jede NaN mit dem nächsten nicht-NaN ersetzen.

Sobald Sie das haben, ist die Berechnung des Durchschnitts trivial und erfolgt durch die zweite Funktion.

Sie sagen nicht, wie Sie wollen, dass das erste und das letzte Element behandelt werden, also hackt der Code sie einfach ab.

Schließlich ist es erwähnenswert, dass die Funktion NaNs zurückgeben kann, wenn entweder das erste oder das letzte Element des Eingabearrays fehlt (in diesem Fall gibt es keine Daten zur Berechnung einiger der Mittelwerte).

0

Hier ist eine rekursive Lösung, wo die erste und die letzte nicht 9999 sind. Sie könnten es wahrscheinlich mit einem Generator aufräumen, da die Rekursion irgendwie tief werden könnte. Es ist eine vernünftige Start

def a(list, first, depth):  
    if ([] == list): 
    return [] 
    car = list[0] 
    cdr = list[1:] 
    if (9999 == car):   
     return a(cdr, first, depth+1) 
    if (depth != 0): 
     avg = [((first + car) /2)] * depth 
     return avg + [car] + a(cdr, car, 0) 
    else: 
     return [car] + a(cdr, car, 0) 



print a([1,2,9999, 4, 9999,9999, 12],0,0) 
# => [1, 2, 3, 4, 8, 8, 12] 
0

Ok, ich fürchte, ich habe es selbst zu schreiben, können Sie np.interp oder gleichwertig (vielleicht etwas schöner und viel mehr vorge) scipy Funktionen nutzen Sie in scipy.interpolate finden.

Ok, erneut lesen ... Ich denke, Sie wollen keine lineare Interpolation?In diesem Fall funktioniert das natürlich nicht ganz ... Obwohl ich sicher bin, dass es einige vektorisierte Methoden gibt.

imort numpy as np 
# data is the given array. 
data = data.astype(float) # I cast to float, if you don't want that badly... 
valid = data != 9999 
x = np.nonzero(valid)[0] 
replace = np.nonzero(~valid)[0] 
valid_data = data[x] 

# using np.interp, but I think you will find better things in scipy.interpolate 
# if you don't mind using scipy. 
data[replace] = np.interp(replace, x, valid_data, 
            left=valid_data[0], right=valid_data[-1])