Modificar un subconjunto de filas en un marco de datos de pandas

143

Suponga que tengo un DataFrame de pandas con dos columnas, A y B. Me gustaría modificar este DataFrame (o crear una copia) para que B siempre sea NaN siempre que A sea 0. ¿Cómo lo lograría?

Probé lo siguiente

df['A'==0]['B'] = np.nan

y

df['A'==0]['B'].values.fill(np.nan)

sin éxito.

Arthur B.
fuente
Si está buscando una solución muy rápida, use NumPy's wherecomo se ve en esta solución a continuación
Ted Petrou

Respuestas:

243

Uso .locpara indexación basada en etiquetas:

df.loc[df.A==0, 'B'] = np.nan

La df.A==0expresión crea una serie booleana que indexa las filas, 'B'selecciona la columna. También puede usar esto para transformar un subconjunto de una columna, por ejemplo:

df.loc[df.A==0, 'B'] = df.loc[df.A==0, 'B'] / 2

No sé lo suficiente sobre los elementos internos de los pandas para saber exactamente por qué eso funciona, pero el problema básico es que a veces la indexación en un DataFrame devuelve una copia del resultado, y a veces devuelve una vista del objeto original. Según la documentación aquí , este comportamiento depende del comportamiento numpy subyacente. He descubierto que acceder a todo en una operación (en lugar de [uno] [dos]) es más probable que funcione para la configuración.

BrenBarn
fuente
La segunda parte de esto es una buena respuesta a una pregunta que ni siquiera se hizo ;-) Me pregunto si esta sigue siendo la respuesta canónica de los pandas, en particular b / c es una violación SECA obvia, aunque supongo que está en hecho necesario para violar DRY dadas las limitaciones de los pandas internos? (Puedo publicar exactamente este tipo de pregunta, con más detalle, pero quería ver si tenía una respuesta rápida antes que yo)
JohnE
¿Cómo subdividir un Dataframe que no tiene nombres de columna, cómo subdividir df solo por índice? df.loc [df [0] == 0] no funciona ... ¿Cuál es la alternativa? Gracias
amipro
89

Aquí hay documentos de pandas sobre indexación avanzada:

¡La sección explicará exactamente lo que necesita! Resulta que df.loc(como .ix ha quedado en desuso, como muchos han señalado a continuación) se puede usar para cortar / cortar en frío un marco de datos. Y. También se puede usar para configurar cosas.

df.loc[selection criteria, columns I want] = value

Así que la respuesta de Bren está diciendo 'yo encontrar todos los lugares donde df.A == 0, seleccione la columna By la puso a np.nan'

badgley
fuente
2
Me has alegrado el día. Explicación clara.
TwinPenguins
1
Sí, de alguna manera loc[selection criteria, columns I want]se te pega perfectamente a la mente ...
EmEs
29

A partir de pandas 0.20 ix está en desuso . La forma correcta es usar df.loc

aquí hay un ejemplo de trabajo

>>> import pandas as pd 
>>> import numpy as np 
>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN
>>> 

Explicación:

Como se explica en el documento aquí , .loc se basa principalmente en etiquetas, pero también se puede usar con una matriz booleana .

Entonces, lo que estamos haciendo arriba es aplicar df.loc[row_index, column_index]por:

  • Explotando el hecho de que locpuede tomar una matriz booleana como una máscara que le dice a los pandas en qué subconjunto de filas queremos cambiarrow_index
  • La explotación del hecho loctambién se basa en la etiqueta para seleccionar la columna utilizando la etiqueta 'B'en elcolumn_index

Podemos usar lógica, condición o cualquier operación que devuelva una serie de booleanos para construir la matriz de booleanos. En el ejemplo anterior, queremos cualquiera rowsque contenga un 0, para eso podemos usar df.A == 0, como puede ver en el ejemplo a continuación, esto devuelve una serie de booleanos.

>>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB'))
>>> df 
   A  B
0  0  2
1  1  0
2  0  5
>>> df.A == 0 
0     True
1    False
2     True
Name: A, dtype: bool
>>> 

Luego, usamos la matriz de booleanos anterior para seleccionar y modificar las filas necesarias:

>>> df.loc[df.A == 0, 'B'] = np.nan
>>> df
   A   B
0  0 NaN
1  1   0
2  0 NaN

Para obtener más información, consulte la documentación de indexación avanzada aquí .

Mohamed Ali JAMAOUI
fuente
11

Para un aumento masivo de la velocidad, use la función where de NumPy.

Preparar

Cree un DataFrame de dos columnas con 100,000 filas con algunos ceros.

df = pd.DataFrame(np.random.randint(0,3, (100000,2)), columns=list('ab'))

Solución rápida con numpy.where

df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)

Tiempos

%timeit df['b'] = np.where(df.a.values == 0, np.nan, df.b.values)
685 µs ± 6.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df.loc[df['a'] == 0, 'b'] = np.nan
3.11 ms ± 17.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Numpy's wherees aproximadamente 4 veces más rápido

Ted Petrou
fuente
Tenía curiosidad sobre esto, así que lo probé yo mismo y la diferencia fue aún mayor usando otros parámetros. Numpy fue casi 10 veces más rápido al reemplazar 0s con un número entero en lugar de np.nan. Me pregunto qué lleva el tiempo extra.
Alexander
¿Es necesario usar .valuesen np.where(df.a.values == 0, np.nan, df.b.values)? Parece que np.where(df.a == 0, np.nan, df.b)también funciona?
hsl
4

Para reemplazar las columnas múltiples, conviértalas en una matriz numpy usando .values:

df.loc[df.A==0, ['B', 'C']] = df.loc[df.A==0, ['B', 'C']].values / 2
Adrien Renaud
fuente