python – perplejo por pandas condicionales y / o indexación booleana

Estoy teniendo problemas con la indexación condicional / booleana. Estoy tratando de llenar un dataframe (dfp) con lógica que está condicionada a los datos de un dataframe de forma similar (dfs) más la fila anterior de sí mismo (dfp). Este es mi último fallo …

import pandas as pd dfs = pd.DataFrame({'a':[1,0,-1,0,1,0,0,-1,0,0],'b':[0,1,0,0,-1,0,1,0,-1,0]}) In [171]: dfs Out[171]: ab 0 1 0 1 0 1 2 -1 0 3 0 0 4 1 -1 5 0 0 6 0 1 7 -1 0 8 0 -1 9 0 0 dfp = pd.DataFrame(index=dfs.index,columns=dfs.columns) dfp[(dfs==1)|((dfp.shift(1)==1)&(dfs!=-1))] = 1 In [166]: dfp.fillna(0) Out[166]: ab 0 1.0 0.0 1 0.0 1.0 2 0.0 0.0 3 0.0 0.0 4 1.0 0.0 5 0.0 0.0 6 0.0 1.0 7 0.0 0.0 8 0.0 0.0 9 0.0 0.0 

Así que me gustaría que dfp tenga un 1 en la fila n si se cumple alguna de las dos condiciones:

 1) dfs same row = 1 or 2) both dfp previous row = 1 and dfs same row  -1 

Me gustaría que mi salida final se vea así:

  ab 0 1 0 1 1 1 2 0 1 3 0 1 4 1 0 5 1 0 6 1 1 7 0 1 8 0 0 9 0 0 

ACTUALIZACIÓN / EDICIÓN: A veces, lo visual es más útil; a continuación se muestra cómo se mapearía en Excel.

introduzca la descripción de la imagen aquí

Gracias de antemano, muy agradecido por su tiempo.

Resummos las invariantes:

  • Si el valor 1 dfp es 1 entonces el valor de dfp es 1 .
  • Si el valor de dfp es -1 entonces el valor de dfp es 0 .
  • Si el valor de dfp es 0 entonces el valor de dfp es 1 si el valor de dfp anterior es 1 contrario es 0 .

O formular de otra manera:

  • El dfp comienza con 1 si el primer valor es 1 , de lo contrario 0
  • Los valores de dfp son 0 hasta que hay un 1 en dfs .
  • Los valores de dfp son 1 hasta que hay un -1 en dfs .

Esto es muy fácil de formular en python:

 def create_new_column(dfs_col): newcol = np.zeros_like(dfs_col) if dfs_col[0] == 1: last = 1 else: last = 0 for idx, val in enumerate(dfs_col): if last == 1 and val == -1: last = 0 if last == 0 and val == 1: last = 1 newcol[idx] = last return newcol 

Y la prueba:

 >>> create_new_column(dfs.a) array([1, 1, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64) >>> create_new_column(dfs.b) array([0, 1, 1, 1, 0, 0, 1, 1, 0, 0], dtype=int64) 

Sin embargo, esto es muy ineficiente en Python porque la iteración sobre varios arrays (y pandas Series / DataFrames) es lenta y los bucles for en python también son ineficientes.

Sin embargo, si tiene numba o Cython , puede comstackr esto y será (probablemente) más rápido de lo que podría ser cualquier solución NumPy, ya que NumPy requeriría varias operaciones de balanceo y / o acumulación.

Por ejemplo con numba:

 >>> import numba >>> numba_version = numba.njit(create_new_column) # comstacktion step >>> numba_version(np.asarray(dfs.a)) # need cast to np.array array([1, 1, 0, 0, 1, 1, 1, 0, 0, 0], dtype=int64) >>> numba_version(np.asarray(dfs.b)) # need cast to np.array array([0, 1, 1, 1, 0, 0, 1, 1, 0, 0], dtype=int64) 

Incluso si dfs tiene millones de filas, la solución numba tomará solo milisegundos:

 >>> dfs = pd.DataFrame({'a':np.random.randint(-1, 2, 1000000),'b':np.random.randint(-1, 2, 1000000)}) >>> %timeit numba_version(np.asarray(dfs.b)) 100 loops, best of 3: 9.37 ms per loop 

No es la mejor manera de hacerlo sino algo que funciona.

  dfs = pd.DataFrame({'a':[1,0,-1,0,1,0,0,-1,0,0],'b':[0,1,0,0,-1,0,1,0,-1,0]}) dfp = dfs.copy() 

Defina la función de la siguiente manera. El uso de ‘last’ aquí es un poco hacky.

  last = [0] def f( x ): if x == 1: x = 1 elif x != -1 and last[0] == 1: x = 1 else: x = 0 last[0] = x return x 

Simplemente aplique la función f en cada columna.

  dfp.a = dfp.a.apply( f ) dfp ab 0 1 0 1 1 1 2 0 0 3 0 0 4 1 -1 5 1 0 6 1 1 7 0 0 8 0 -1 9 0 0 

Del mismo modo para col b. No te olvides de volver a inicializar ‘último’.

  last[0] = 0 dfp.b = dfp.b.apply( f ) dfp ab 0 1 0 1 1 1 2 0 1 3 0 1 4 1 0 5 1 0 6 1 1 7 0 1 8 0 0 9 0 0