Modificar un subconjunto de filas en un dataframe de pandas

Supongamos 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 sea siempre NaN siempre que A sea 0. ¿Cómo lograría eso?

Probé lo siguiente

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

y

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

sin éxito.

Utilice .loc para la indexación basada en tags:

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

La expresión df.A==0 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 pandas internals para saber exactamente por qué 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. De acuerdo con la documentación aquí , este comportamiento depende del comportamiento subyacente. Descubrí que es más probable que el acceso a todo en una operación (en lugar de [uno] [dos]) funcione para la configuración.

Aquí es de pandas documentos en indexación avanzada:

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

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

Así que la respuesta de Bren es: ‘búscame todos los lugares donde df.A == 0 , selecciona la columna B y np.nan en np.nan

A partir de pandas 0.20 ix está en desuso . La forma correcta es usar 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 AB 0 0 NaN 1 1 0 2 0 NaN >>> 

Explicación:

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

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

  • Aprovechando el hecho de que loc puede tomar una matriz booleana como una máscara que le dice a los pandas qué subconjunto de filas queremos cambiar en row_index
  • La explotación del hecho loc también se basa en la etiqueta para seleccionar la columna usando la etiqueta 'B' en el column_index

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

 >>> df = pd.DataFrame({"A":[0,1,0], "B":[2,0,5]}, columns=list('AB')) >>> df AB 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 anterior de valores booleanos para seleccionar y modificar las filas necesarias:

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

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

Para un aumento masivo de la velocidad, use la función Where de NumPy’s.

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 where es 4 veces más rápido

Para reemplazar columnas múltiples, conviértalos a una matriz .values utilizando .values :

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