panda: filtro complesso sulle righe di DataFrame


86

Vorrei filtrare le righe in base a una funzione di ciascuna riga, ad es

def f(row):
  return sin(row['velocity'])/np.prod(['masses']) > 5

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, f)]

O per un altro esempio più complesso e artificioso,

def g(row):
  if row['col1'].method1() == 1:
    val = row['col1'].method2() / row['col1'].method3(row['col3'], row['col4'])
  else:
    val = row['col2'].method5(row['col6'])
  return np.sin(val)

df = pandas.DataFrame(...)
filtered = df[apply_to_all_rows(df, g)]

Come posso farlo?

Risposte:


122

Puoi farlo usando DataFrame.apply, che applica una funzione lungo un dato asse,

In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [4]: df
Out[4]: 
          a         b         c
0 -0.001968 -1.877945 -1.515674
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

In [6]: df[df.apply(lambda x: x['b'] > x['c'], axis=1)]
Out[6]: 
          a         b         c
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

16
Non è necessario applyin questa situazione. Un normale indice booleano funzionerà perfettamente. df[df['b] > df['c']]. Ci sono pochissime situazioni che effettivamente richiedono applye anche poche che ne hanno bisognoaxis=1
Ted Petrou

@TedPetrou E se non sei sicuro che ogni elemento nel tuo dataframe sia del tipo giusto. Un normale indice booleano supporta la gestione delle eccezioni?
D. Ror.

13

Supponiamo che avessi un DataFrame come segue:

In [39]: df
Out[39]: 
      mass1     mass2  velocity
0  1.461711 -0.404452  0.722502
1 -2.169377  1.131037  0.232047
2  0.009450 -0.868753  0.598470
3  0.602463  0.299249  0.474564
4 -0.675339 -0.816702  0.799289

Posso usare sin e DataFrame.prod per creare una maschera booleana:

In [40]: mask = (np.sin(df.velocity) / df.ix[:, 0:2].prod(axis=1)) > 0

In [41]: mask
Out[41]: 
0    False
1    False
2    False
3     True
4     True

Quindi utilizzare la maschera per selezionare dal DataFrame:

In [42]: df[mask]
Out[42]: 
      mass1     mass2  velocity
3  0.602463  0.299249  0.474564
4 -0.675339 -0.816702  0.799289

2
in realtà, questo era probabilmente un cattivo esempio: np.sintrasmette automaticamente a tutti gli elementi. E se lo sostituissi con una funzione meno intelligente che potrebbe gestire solo un input alla volta?
duckworthd


5

Non posso commentare la risposta di Duckworthd , ma non funziona perfettamente. Si blocca quando il dataframe è vuoto:

df = pandas.DataFrame(columns=['a', 'b', 'c'])
df[df.apply(lambda x: x['b'] > x['c'], axis=1)]

Uscite:

ValueError: Must pass DataFrame with boolean values only

A me sembra un bug nei panda, poiché {} è definitivamente un insieme valido di valori booleani. Per una soluzione fare riferimento alla risposta di Roy Hyunjin Han .


3

L'approccio migliore che ho trovato è, invece di utilizzare reduce=Trueper evitare errori per df vuoto (poiché questo argomento è comunque deprecato), controlla semplicemente che df size> 0 prima di applicare il filtro:

def my_filter(row):
    if row.columnA == something:
        return True

    return False

if len(df.index) > 0:
    df[df.apply(my_filter, axis=1)]

0

È possibile utilizzare la locproprietà per suddividere il dataframe.

Secondo la documentazione , locpuò avere callable functioncome argomento.

In [3]: df = pandas.DataFrame(np.random.randn(5, 3), columns=['a', 'b', 'c'])

In [4]: df
Out[4]: 
          a         b         c
0 -0.001968 -1.877945 -1.515674
1 -0.540628  0.793913 -0.983315
2 -1.313574  1.946410  0.826350
3  0.015763 -0.267860 -2.228350
4  0.563111  1.195459  0.343168

# define lambda function
In [5]: myfilter = lambda x: x['b'] > x['c']

# use my lambda in loc
In [6]: df1 = df.loc[fif]

se desideri combinare la tua funzione di filtro fifcon altri criteri di filtro

df1 = df.loc[fif].loc[(df.b >= 0.5)]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.