Reemplazar valores no válidos con Ninguno en Pandas DataFrame

80

¿Hay algún método para reemplazar valores Noneen Pandas en Python?

Puede usar df.replace('pre', 'post')y puede reemplazar un valor con otro, pero esto no se puede hacer si desea reemplazarlo con un Nonevalor, que si lo intenta, obtendrá un resultado extraño.

Así que aquí tienes un ejemplo:

df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)

que devuelve un resultado exitoso.

Pero,

df.replace('-', None)

que devuelve el siguiente resultado:

0
0   - // this isn't replaced
1   3
2   2
3   5
4   1
5  -5
6  -1
7  -1 // this is changed to `-1`...
8   9

¿Por qué se devuelve un resultado tan extraño?

Como quiero verter este marco de datos en la base de datos MySQL, no puedo poner NaNvalores en ningún elemento en mi marco de datos y en su lugar quiero poner None. Seguramente, primero puede cambiar '-'a NaNy luego convertir NaNa None, pero quiero saber por qué la trama de datos actúa de una manera tan terrible.

Probado en pandas 0.12.0 dev en Python 2.7 y OS X 10.8. Python es una versión preinstalada en OS X e instalé pandas usando el script SciPy Superpack, para su información.

Blaszard
fuente
¿ write_frameNo analiza NaNs a nones?
Andy Hayden
Sip. Encuentra un InternalError: (1054, u"Unknown column 'nan' in 'field list'")error. No sé acerca de cualquier soluciones en él distintos de la conversión NaNa Noneantes de ejecutar write_frameel método.
Blaszard
¿Qué versión de pandas estás usando?
Andy Hayden
Scipy super pack entrega dev? Bien, definitivamente creo que deberías plantear esto como un problema en github , no debería ser demasiado difícil de solucionar.
Andy Hayden
Si está leyendo estos datos de CSV / Excel , puede leer estos valores como NaN usando el na_valuesargumento. Más información en esta respuesta.
cs95

Respuestas:

111

En realidad, en versiones posteriores de pandas, esto dará un TypeError:

df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping

Puede hacerlo pasando una lista o un diccionario:

In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', {0: None})
Out[11]:
      0
0  None
1     3
2     2
3     5
4     1
5    -5
6    -1
7  None
8     9

Pero recomiendo usar NaN en lugar de Ninguno:

In [12]: df.replace('-', np.nan)
Out[12]:
     0
0  NaN
1    3
2    2
3    5
4    1
5   -5
6   -1
7  NaN
8    9
Andy Hayden
fuente
15
O simplemente una lista, por ejemplo df.replace(['-'], [None]), o df.replace({'-': None}), creo. El uso de Nonecomo centinela también excluye su uso como valor ..
DSM
@ user2360798 replace es en realidad una función muy rica en funciones (lectura complicada), aunque la cadena de documentación (dev) es realmente buena.
Andy Hayden
4
No sé si es obvio, pero tuvo que asignarse de dfnuevo a sí mismo como:df = df.replace({'?': np.nan})
luckyging3r
3
@AndyHayden se df.replace('-', df.replace(['-'], [None])ve raro , ¿es un error tipográfico?
lin_bug
2
@lin_bug Aunque parece que ya no funciona en las versiones recientes de pandas. df.where (df! = '-', None) works
Andy Hayden
17

Prefiero la solución que usa replacecon a dictpor su sencillez y elegancia:

df.replace({'-': None})

También puede tener más reemplazos:

df.replace({'-': None, 'None': None})

E incluso para reemplazos más grandes, siempre es obvio y claro qué se reemplaza por qué, lo que es mucho más difícil para listas largas, en mi opinión.

Michael Dorner
fuente
1
Vale la pena señalar que parte de la razón por la que esta técnica funciona es que el uso del dicttipo in to_replacehace que el methodparámetro no se evalúe y, por lo tanto, el method='pad'valor predeterminado no tenga efectos nocivos.
bsplosion
15

wherees probablemente lo que estás buscando. Entonces

data=data.where(data=='-', None) 

De los documentos de panda :

where [devuelve] un objeto de la misma forma que self y cuyas entradas correspondientes son de self donde cond es Verdadero y de lo contrario son de otro).

usuario2966041
fuente
5
Esto es realmente inexacto. data = data.where (data == '-', None) reemplazará cualquier cosa que NO sea IGUAL a '-' con None. La versión de Pandas de where mantiene el valor del primer argumento (en este caso, data == '-') y reemplaza cualquier otra cosa con el segundo argumento (en este caso, Ninguno). Es un poco confuso ya que np. Donde es más explícito porque pregunta el condicional en el primer argumento, luego el si es verdadero en el segundo argumento, luego el si es falso en el tercer argumento.
clg4
8

Antes de continuar con esta publicación, es importante comprender la diferencia entre NaN y None . Uno es un tipo flotante, el otro es un tipo de objeto. Pandas es más adecuado para trabajar con tipos escalares, ya que se pueden vectorizar muchos métodos en estos tipos. Pandas intenta manejar None y NaN de manera consistente, pero NumPy no puede.

Mi sugerencia ( y la de Andy ) es seguir con NaN.

Pero para responder a tu pregunta ...

pandas> = 0.18: Use na_values=['-']argumento conread_csv

Si cargó estos datos desde CSV / Excel, tengo buenas noticias para usted. Puede anular esto en la raíz durante la carga de datos en lugar de tener que escribir una solución con código como paso posterior.

La mayoría de las pd.read_*funciones (como read_csvy read_excel) aceptan un na_valuesatributo.

file.csv

A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0

Ahora, para convertir los -caracteres en NaN, haga,

import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df

     A    B
0  NaN  1.0
1  3.0  NaN
2  2.0  NaN
3  5.0  3.0
4  1.0 -2.0
5 -5.0  4.0
6 -1.0 -1.0
7  NaN  0.0
8  9.0  0.0

Y similar para otras funciones / formatos de archivo.

PD: en v0.24 +, puede conservar el tipo de entero incluso si su columna tiene NaN (sí, hable de tener el pastel y comérselo también). Puede especificardtype='Int32'

df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

df.dtypes

A    Int32
B    Int32
dtype: object

El dtype no es un tipo int convencional ... sino más bien, un tipo entero que acepta valores NULL. Hay otras opciones.


Manejo de datos numéricos: pd.to_numericconerrors='coerce

Si está tratando con datos numéricos, una solución más rápida es usar pd.to_numericcon el errors='coerce'argumento, que coacciona valores no válidos (valores que no se pueden convertir en numéricos) a NaN.

pd.to_numeric(df['A'], errors='coerce')

0    NaN
1    3.0
2    2.0
3    5.0
4    1.0
5   -5.0
6   -1.0
7    NaN
8    9.0
Name: A, dtype: float64

Para retener (nullable) dtype entero, use

pd.to_numeric(df['A'], errors='coerce').astype('Int32')

0    NaN
1      3
2      2
3      5
4      1
5     -5
6     -1
7    NaN
8      9
Name: A, dtype: Int32 

Para coaccionar varias columnas, use apply:

df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')

     A    B
0  NaN    1
1    3  NaN
2    2  NaN
3    5    3
4    1   -2
5   -5    4
6   -1   -1
7  NaN    0
8    9    0

... y asigne el resultado de nuevo después.

Puede encontrar más información en esta respuesta .

cs95
fuente
3
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)
Shravan kp
fuente
0

La configuración de valores nulos se puede hacer con np.nan:

import numpy as np
df.replace('-', np.nan)

La ventaja es que las df.last_valid_index()reconoce como inválidas.

Freek Wiekmeijer
fuente
0

Usando reemplazar y asignando un nuevo df:

import pandas as pd
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
dfnew = df.replace('-', 0)
print(dfnew)


(venv) D:\assets>py teste2.py
   0
0  0
1  3
2  2
3  5
4  1
5 -5
daniel rocha
fuente
0
df.replace('-', np.nan).astype("object")

Esto asegurará que pueda usar isnull()más adelante en su marco de datos

Keng Chan
fuente
0

Con la versión de Pandas ≥1.0.0, usaría DataFrame.replaceo Series.replace:

df.replace(old_val, pd.NA, inplace=True)

Esto es mejor por dos razones:

  1. Utiliza en pd.NAlugar de Noneo np.nan.
  2. Reemplaza el valor en el lugar que podría ser más eficiente en memoria.
Acumenus
fuente