2012-09-11 11 views
59

I tienen un conjunto de datos de precio OHLC, que he analizado desde CSV en una trama de datos pandas y resampled 15 bar min:Adición de la columna (s) calculado a una trama de datos en pandas

<class 'pandas.core.frame.DataFrame'> 
DatetimeIndex: 500047 entries, 1998-05-04 04:45:00 to 2012-08-07 00:15:00 
Freq: 15T 
Data columns: 
Close 363152 non-null values 
High  363152 non-null values 
Low  363152 non-null values 
Open  363152 non-null values 
dtypes: float64(4) 

me gustaría agregue varias columnas calculadas, empezando por simples, como el Periodo de rango (HL) y luego booleanos para indicar la aparición de patrones de precios que definiré, por ej. un patrón de vela de martillo, para los que una definición de ejemplo:

def closed_in_top_half_of_range(h,l,c): 
    return c > l + (h-1)/2 

def lower_wick(o,l,c): 
    return min(o,c)-l 

def real_body(o,c): 
    return abs(c-o) 

def lower_wick_at_least_twice_real_body(o,l,c): 
    return lower_wick(o,l,c) >= 2 * real_body(o,c) 

def is_hammer(row): 
    return lower_wick_at_least_twice_real_body(row["Open"],row["Low"],row["Close"]) \ 
    and closed_in_top_half_of_range(row["High"],row["Low"],row["Close"]) 

problema básico: ¿Cómo puedo asignar la función a la columna, específicamente donde me gustaría hacer referencia a más de una otra columna o la fila entera o lo que sea?

This post se trata de agregar dos columnas calculadas fuera de una sola columna fuente, que está cerca, pero no del todo.

Y un poco más avanzado: para patrones de precios que se determinan con referencia a más de una barra (T), ¿cómo puedo hacer referencia a diferentes filas (por ejemplo, T-1, T-2, etc.) desde la definición de la función ?

Muchas gracias de antemano.

Respuesta

57

El código exacto variará para cada una de las columnas que desea hacer , pero es probable que desee utilizar las funciones map y apply. En algunos casos, puede calcular utilizando directamente las columnas existentes, ya que las columnas son objetos de la serie Pandas, que también funcionan como matrices Numpy, que funcionan automáticamente como elemento para las operaciones matemáticas habituales.

>>> d 
    A B C 
0 11 13 5 
1 6 7 4 
2 8 3 6 
3 4 8 7 
4 0 1 7 
>>> (d.A + d.B)/d.C 
0 4.800000 
1 3.250000 
2 1.833333 
3 1.714286 
4 0.142857 
>>> d.A > d.C 
0  True 
1  True 
2  True 
3 False 
4 False 

Si es necesario utilizar operaciones como máximo y mínimo dentro de una fila, puede utilizar apply con axis=1 aplicar cualquier función le gusta a cada fila. He aquí un ejemplo que calcula min(A, B)-C, que parece ser como tu "mecha inferior":

>>> d.apply(lambda row: min([row['A'], row['B']])-row['C'], axis=1) 
0 6 
1 2 
2 -3 
3 -3 
4 -7 

suerte que le da una idea de cómo proceder.

Editar: para comparar filas con filas vecinas, el enfoque más simple es dividir las columnas que desea comparar, dejando el principio/final, y luego comparar las divisiones resultantes. Por ejemplo, esto le dirá a qué filas del elemento en la columna A es menor que el elemento de la fila siguiente en la columna C:

d['A'][:-1] < d['C'][1:] 

y esto lo hace a la inversa, que las filas que tienen un menor que el contar C precedente de fila:

d['A'][1:] < d['C'][:-1] 

haciendo ['A"][:-1] rebanadas fuera el último elemento de la columna A, y haciendo ['C'][1:] rebanadas fuera el primer elemento de la columna C, por lo que cuando se alinean estos dos y compararlos, que está comparando cada elemento en A con la C de la siguiente fila.

35

Usted podría tener is_hammer en términos de row["Open"] etc. de la siguiente manera

def is_hammer(rOpen,rLow,rClose,rHigh): 
    return lower_wick_at_least_twice_real_body(rOpen,rLow,rClose) \ 
     and closed_in_top_half_of_range(rHigh,rLow,rClose) 

continuación, puede utilizar el mapa:

df["isHammer"] = map(is_hammer, df["Open"], df["Low"], df["Close"], df["High"]) 
+2

También es útil, muchas gracias. Muchas formas de despellejar un gato y todo. Te daría un voto positivo, pero esta es mi primera pregunta sobre StackOverflow y lamentablemente no tengo suficientes representantes. Supongo que no tiene ninguna idea sobre la segunda parte, es decir, hace referencia a filas vecinas en el marco de datos desde la función de mapa/aplicación. Saludos de nuevo. – ultra909

+0

Gran ejemplo. Código muy legible –

+0

Por alguna razón, el método que utiliza 'map (f, col1, col2)' es mucho más rápido que 'df.apply (..., axis = 1)'. el mapa toma 0.35s vs df.apply tomando 26s para un dataframe de 1M fila. ¿Alguna idea de por qué? (Python 2.7 y pandas 0.18.1) – MohamedEzz

1

Las cuatro primeras funciones que lista trabajará en vectores, así, con la excepción de que lower_wick necesita ser adaptada. Algo como esto,

def lower_wick_vec(o, l, c): 
    min_oc = numpy.where(o > c, c, o) 
    return min_oc - l 

donde o, l y c son vectores. Se podía hacerlo de esta manera en lugar, que sólo se necesita el DF como entrada y evitar el uso de numpy, aunque será mucho más lento:

def lower_wick_df(df): 
    min_oc = df[['Open', 'Close']].min(axis=1) 
    return min_oc - l 

Los otros tres trabajar en columnas o vectores tal como son. A continuación, puede terminar con

def is_hammer(df): 
    lw = lower_wick_at_least_twice_real_body(df["Open"], df["Low"], df["Close"]) 
    cl = closed_in_top_half_of_range(df["High"], df["Low"], df["Close"]) 
    return cl & lw 

operadores bit puede realizar la lógica conjunto de vectores booleanos, & para and, | para or etc. Esto es suficiente para vectorizar por completo los cálculos de muestra que diste y debe ser relativamente rápido. Probablemente puedas acelerar aún más si trabajas temporalmente con las matrices numpy subyacentes a los datos mientras realizas estos cálculos.

Para la segunda parte, recomendaría introducir una columna que indique el patrón para cada fila y escribir una familia de funciones que se ocupen de cada patrón. Luego agrupe el patrón y aplique la función apropiada a cada grupo.

Cuestiones relacionadas