2012-12-18 30 views
6

Ho un 1 dati dimensionali stabiliti con alcuni senza valori di dati che sono impostati come 9999. Ecco un estratto in quanto è piuttosto lungo:Python: Sostituzione di valori in una matrice

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

vorrei sostituire i valori senza dati con la media dei valori più vicini su entrambi i lati, tuttavia, poiché alcuni valori di nessun dato hanno valori più vicini come nessun valore di dati, sostituirli è un po 'più difficile. Ad esempio, vorrei che i tre valori senza dati fossero sostituiti con -2. Ho creato un ciclo per passare attraverso ciascuno dei scalari nella matrice e test senza dati:

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 

Tuttavia devo aggiungere in se funzione o un modo per prendere il valore prima k-1 o dopo k +1 se questo è anche uguale a 9999 es:

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]) 

come si può dire, questo codice diventa disordinato come si può finire per prendere il valore errato o finire con un sacco di funzioni sE nidificate. Qualcuno sa di un modo più pulito per implementare questo dato che è piuttosto variabile in tutto il set di dati?

Come richiesto: Se il primo e/o l'ultimo punto non sono dati, preferibilmente saranno sostituiti con il punto dati più vicino.

+3

Cosa succede se l'ultimo elemento nell'elenco è '9999'? Con che valore vuoi sostituirlo? – Cameron

+0

@Cameron Scuse, se l'ultimo elemento è '9999', allora può essere sostituito con il penultimo elemento. Grazie. – AJEnvMap

risposta

3

Ci può essere un modo più efficeint per fare questo con funzioni NumPy, ma qui è una soluzione utilizzando il 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) 

Se l'ultimo elemento o primo elemento può essere 9999, si utilizza il seguente:

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) 

Esempio con seconda versione:

>>> 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

farei qualcosa lungo le seguenti linee:

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) 

La prima funzione può fare sia un avanti o un riempimento a ritroso, sostituendo ogni NaN con la più vicina non NaN.

Una volta ottenuto questo, calcolare la media è banale e viene eseguito dalla seconda funzione.

Non si dice come si desidera gestire il primo e l'ultimo elemento, quindi il codice li interrompe semplicemente.

Infine, vale la pena notare che la funzione può restituire NaN se manca il primo o l'ultimo elemento dell'array di input (nel qual caso non ci sono dati per calcolare alcune medie).

0

Ecco una soluzione ricorsiva in cui il primo e l'ultimo non sono 9999. Probabilmente è possibile pulirlo con un generatore in quanto la ricorsione potrebbe diventare più profonda. E 'un inizio ragionevole

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, ho paura devo scrivere io stesso, è possibile utilizzare np.interp o equivalente (forse un po' più bello e molto più in vetrina) funzioni SciPy si può trovare in scipy.interpolate.

Ok, rileggendo ... Immagino tu non voglia l'interpolazione lineare?Nel qual caso, ovviamente, questo non funziona ... Anche se sono sicuro che ci sono alcuni metodi vettorizzati.

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]) 
Problemi correlati