python – perplexo por condicionais de pandas e / ou indexação booleana

Estou com problemas com condicionais / indexação booleana. Eu estou tentando preencher um dataframe (dfp) com lógica que é condicional em dados de um dataframe (dfs) de formato semelhante, mais a linha anterior do próprio (dfp). Esta é minha última falha …

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 

Então, eu gostaria que o dfp tivesse um 1 na linha n se qualquer uma das duas condições forem atendidas:

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

Eu gostaria que minha saída final fosse assim:

  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 

ATUALIZAR / EDITAR: Às vezes, o visual é mais útil – abaixo está como ele seria mapeado no Excel.

insira a descrição da imagem aqui

Agradecemos antecipadamente, muito grato pelo seu tempo.

Vamos resumir os invariantes:

  • Se o valor de dfs for 1 , o valor de dfp será 1 .
  • Se o valor de dfs for -1 , o valor de dfp será 0 .
  • Se o valor dfs for 0 , o valor dfp será 1 se o valor dfp anterior for 1 caso contrário, será 0 .

Ou formular de outra maneira:

  • O dfp começa com 1 se o primeiro valor for 1 , caso contrário, 0
  • Os valores de dfp são 0 até que haja um 1 em dfs .
  • Os valores de dfp são 1 até que haja -1 em dfs .

Isso é muito fácil de formular em 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 

E o teste:

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

No entanto, isso é muito ineficiente em Python, pois a iteração de matrizes numpy (e pandas Series / DataFrames) é lenta e os for -loops em python também são ineficientes.

No entanto, se você tiver numba ou Cython poderá compilar isso e será (provavelmente) mais rápido do que qualquer solução NumPy, porque o NumPy exigiria várias operações de rolagem e / ou acumulação.

Por exemplo, com numba:

 >>> import numba >>> numba_version = numba.njit(create_new_column) # compilation 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) 

Mesmo se o dfs tiver milhões de linhas, a solução numba levará apenas milissegundos:

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

Não é a melhor maneira de fazer isso, mas 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 a function da seguinte maneira. O uso de ‘last’ aqui é um pouco 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 

Basta aplicar a func f em cada coluna.

  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 

Similarmente para col b. Não se esqueça de reinicializar o ú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 
Intereting Posts