2012-05-07 5 views
5

Ho dati periodiche con l'indice essere un numero decimale in questo modo:Calcolo attraversamento (intercetta) punti di una serie o dataframe

time = [0, 0.1, 0.21, 0.31, 0.40, 0.49, 0.51, 0.6, 0.71, 0.82, 0.93] 
voltage = [1, -1, 1.1, -0.9, 1, -1, 0.9,-1.2, 0.95, -1.1, 1.11] 
df = DataFrame(data=voltage, index=time, columns=['voltage']) 
df.plot(marker='o') 

voglio creare una funzione cross(df, y_val, direction='rise' | 'fall' | 'cross') che restituisce un array di tempi (indici) con tutti i punti interpolati in cui i valori di tensione corrispondono a y_val. Per 'rise' vengono restituiti solo i valori in cui la pendenza è positiva; per 'fall' vengono ripristinati solo i valori con una pendenza negativa; per 'cross' vengono restituiti entrambi. Quindi se y_val = 0 e direction = 'cross', verrà restituito un array con 10 valori con i valori X dei punti di incrocio (il primo è circa 0,025).

Stavo pensando che questo potrebbe essere fatto con un iteratore, ma mi chiedevo se c'era un modo migliore per farlo.

Grazie. Sto amando la comunità di Pandas e Pandas.

+0

btw, potresti esserti imbattuto in un insetto nel placcaggio dei panda. Credo che il primo incrocio dovrebbe essere intorno a 0,05, in base ai dati, ma le etichette non si allineano, facendo sembrare che attraversi a 0.025. (panda 0.7.3) – Garrett

risposta

13

Per fare questo ho finito con il seguente. È una versione vettoriale che è 150 volte più veloce di una che usa un loop.

def cross(series, cross=0, direction='cross'): 
    """ 
    Given a Series returns all the index values where the data values equal 
    the 'cross' value. 

    Direction can be 'rising' (for rising edge), 'falling' (for only falling 
    edge), or 'cross' for both edges 
    """ 
    # Find if values are above or bellow yvalue crossing: 
    above=series.values > cross 
    below=np.logical_not(above) 
    left_shifted_above = above[1:] 
    left_shifted_below = below[1:] 
    x_crossings = [] 
    # Find indexes on left side of crossing point 
    if direction == 'rising': 
     idxs = (left_shifted_above & below[0:-1]).nonzero()[0] 
    elif direction == 'falling': 
     idxs = (left_shifted_below & above[0:-1]).nonzero()[0] 
    else: 
     rising = left_shifted_above & below[0:-1] 
     falling = left_shifted_below & above[0:-1] 
     idxs = (rising | falling).nonzero()[0] 

    # Calculate x crossings with interpolation using formula for a line: 
    x1 = series.index.values[idxs] 
    x2 = series.index.values[idxs+1] 
    y1 = series.values[idxs] 
    y2 = series.values[idxs+1] 
    x_crossings = (cross-y1)*(x2-x1)/(y2-y1) + x1 

    return x_crossings 

# Test it out: 
time = [0, 0.1, 0.21, 0.31, 0.40, 0.49, 0.51, 0.6, 0.71, 0.82, 0.93] 
voltage = [1, -1, 1.1, -0.9, 1, -1, 0.9,-1.2, 0.95, -1.1, 1.11] 
df = DataFrame(data=voltage, index=time, columns=['voltage']) 
x_crossings = cross(df['voltage']) 
y_crossings = np.zeros(x_crossings.shape) 
plt.plot(time, voltage, '-ob', x_crossings, y_crossings, 'or') 
plt.grid(True) 

E 'stato abbastanza soddisfacente quando ha funzionato. Eventuali miglioramenti che possono essere apportati?

+0

Freddo. Ho creato un problema al riguardo qui: https://github.com/pydata/pandas/issues/1256. Dovrò dargli un'occhiata più da vicino qualche volta in futuro –

+0

Eventuali altre tecniche di interpolazione che possono essere utilizzate per una maggiore precisione nel recupero dei punti di incrocio? – Chetan