2012-05-07 6 views
5

tengo datos periódicas con el índice es un número de coma flotante de este modo:Cálculo de cruce (en el origen) puntos de una serie o trama de datos

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

Quiero crear una función cross(df, y_val, direction='rise' | 'fall' | 'cross') que devuelve una matriz de tiempos (índices) con todos los puntos interpolados donde los valores de voltaje son iguales a y_val. Para 'aumento', solo se devuelven los valores donde la pendiente es positiva; para 'caer', solo se retienen los valores con una pendiente negativa; para 'cruz' ambos son devueltos. Entonces, si y_val = 0 y direction = 'cross', se devolverá una matriz con 10 valores con los valores X de los puntos de cruce (siendo el primero aproximadamente 0.025).

Estaba pensando que esto podría hacerse con un iterador, pero me preguntaba si habría una mejor manera de hacerlo.

Gracias. Me encantan los pandas y la comunidad de los pandas.

+0

Por cierto, es posible que haya tropezado con un error en el trazado de pandas. Creo que el primer cruce debería ser alrededor de 0.05, basado en los datos, pero las etiquetas no se alinean, lo que parece cruzar a 0.025. (pandas 0.7.3) – Garrett

Respuesta

13

Para hacer esto terminé con lo siguiente. Es una versión vectorizada que es 150 veces más rápida que una que usa un bucle.

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) 

Fue bastante satisfactorio cuando funcionó. Cualquier mejora que se puede hacer?

+0

Cool. Creé un problema sobre esto aquí: https://github.com/pydata/pandas/issues/1256. Tendré que echarle un vistazo más de cerca en el futuro –

+0

¿Alguna otra técnica de interpolación que pueda usarse para obtener una mayor precisión en la búsqueda de los puntos de cruce? – Chetan

Cuestiones relacionadas