pandas: filtro complejo en filas de DataFrame


85

Me gustaría filtrar filas por una función de cada fila, por ejemplo

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

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

O para otro ejemplo más complejo y artificial,

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

¿Como lo puedo hacer?

Respuestas:


121

Puede hacer esto usando DataFrame.apply, que aplica una función a lo largo de un eje dado,

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

15
No es necesario applyen esta situación. Un índice booleano normal funcionará bien. df[df['b] > df['c']]. Hay muy pocas situaciones que realmente lo requieran applye incluso pocas que lo necesiten conaxis=1
Ted Petrou

@TedPetrou ¿Qué sucede si no está seguro de que todos los elementos de su marco de datos sean del tipo correcto? ¿Un índice booleano regular admite el manejo de excepciones?
D. Ror.

13

Supongamos que tengo un DataFrame de la siguiente manera:

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

Puedo usar sin y DataFrame.prod para crear una máscara 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

Luego use la máscara para seleccionar del 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
en realidad, este fue probablemente un mal ejemplo: np.sinse transmite automáticamente a todos los elementos. ¿Qué pasa si lo reemplazo con una función menos inteligente que solo pueda manejar una entrada a la vez?
Dudoso


5

No puedo comentar sobre la respuesta de Duckworthd , pero no funciona perfectamente. Se bloquea cuando el marco de datos está vacío:

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

Salidas:

ValueError: Must pass DataFrame with boolean values only

Para mí, parece un error en pandas, ya que {} es definitivamente un conjunto válido de valores booleanos. Para obtener una solución, consulte la respuesta de Roy Hyunjin Han .


3

El mejor enfoque que he encontrado es, en lugar de usarlo reduce=Truepara evitar errores para df vacío (ya que este argumento está obsoleto de todos modos), simplemente verifique que df size> 0 antes de aplicar el 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

Puede usar la locpropiedad para dividir su marco de datos.

Según documentación , locpuede tener callable functioncomo argumento.

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]

si desea combinar su función de filtro fifcon otros criterios de filtro

df1 = df.loc[fif].loc[(df.b >= 0.5)]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.