Reemplazar valores en blanco (espacio en blanco) con NaN en pandas

152

Quiero encontrar todos los valores en un marco de datos de Pandas que contengan espacios en blanco (cualquier cantidad arbitraria) y reemplazar esos valores con NaN.

¿Alguna idea de cómo se puede mejorar esto?

Básicamente quiero convertir esto:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

Dentro de esto:

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

Me las arreglé para hacerlo con el código a continuación, pero el hombre es feo. No es Pythonic y estoy seguro de que tampoco es el uso más eficiente de los pandas. Recorro cada columna y hago un reemplazo booleano contra una máscara de columna generada al aplicar una función que realiza una búsqueda de expresiones regulares de cada valor, que coincide en el espacio en blanco.

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None

Se podría optimizar un poco solo iterando a través de campos que podrían contener cadenas vacías:

if df[i].dtype == np.dtype('object')

Pero eso no es una gran mejora

Y finalmente, este código establece las cadenas de destino en None, que funciona con las funciones de Pandas fillna(), pero sería bueno para completar si pudiera insertar un NaNdirectamente en lugar de None.

Chris Clark
fuente
2
Lo que realmente desea es poder usar replacecon una expresión regular ... (tal vez esto debería solicitarse como una característica).
Andy Hayden
3
Hice un problema de github para esta función: github.com/pydata/pandas/issues/2285 . Estaría agradecido por las relaciones públicas! :)
Chang Ella
Para aquellos que desean convertir exactamente un solo carácter en blanco en perdido, vea esta solución simple a continuación
Ted Petrou el

Respuestas:

200

Creo que df.replace()hace el trabajo, ya que los pandas 0.13 :

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

Produce:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN

Como lo señaló Temak, úselodf.replace(r'^\s+$', np.nan, regex=True) en caso de que sus datos válidos contengan espacios en blanco.

patricksurry
fuente
1
regex es una bandera booleana. Tal vez te refieres a lo pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).valuesque da['1', 'X', '9', 'X', None]
patricksurry
2
2 años después, he cambiado la respuesta aceptada a esto, ahora que los pandas lo admiten. ¡Gracias!
Chris Clark
35
NOTA : si no desea que un elemento que contiene espacio en el medio se reemplace con el uso de NaNdf.replace(r'^\s+$', np.nan, regex=True)
Temak
77
Traté de usar esto, pero descubrí que r '^ \ s * $' debería ser la expresión a usar. sin ^ y $ coincidirá con cualquier cadena con dos espacios en blanco consecutivos. También cambió + a * para incluir la cadena vacía "" en la lista de cosas para convertir a NaN
Master Yogurt
1
Estoy probando su solución en mi código, pero no tiene ningún efecto. Estoy intentando "energy [" Energy Supply "]. Replace (to_replace =" ... ", value = np.NaN)". Queriendo cambiar la cadena "..." a valores NaN, pero no hace nada y devuelve el mismo marco de datos.
Archan Joshi el
50

Si desea reemplazar una cadena vacía y registros con solo espacios, la respuesta correcta es :

df = df.replace(r'^\s*$', np.nan, regex=True)

La respuesta aceptada

df.replace(r'\s+', np.nan, regex=True)

¡No reemplaza una cadena vacía !, puede probarlo usted mismo con el ejemplo dado ligeramente actualizado:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

Tenga en cuenta también que 'fo o' no se reemplaza con Nan, aunque contiene un espacio. Nota adicional, que un simple:

df.replace(r'', np.NaN)

Tampoco funciona, pruébalo.

Philipp Schwarz
fuente
33

Qué tal si:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

La applymapfunción aplica una función a cada celda del marco de datos.

BrenBarn
fuente
¡Qué buena mejora! Debería haber pensado en esto en retrospectiva, pero me obsesioné con hacer reemplazos booleanos por alguna razón. Una pregunta: ¿hay una ventaja en hacer la verificación de la cadena base en comparación con solo str (x) .isspace ()?
Chris Clark
1
@ChrisClark: Cualquiera de los dos está bien, aunque supongo que isinstanceserá un poco más rápido.
BrenBarn
13
La referencia a "basetring" en el código anterior no funcionará en Python 3 ... en ese caso, intente usar "str" ​​en su lugar.
Spike Williams
44
Tenga en cuenta que esta solución no reemplaza cadenas vacías ''. Para considerar también cadenas vacías, use:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x)
tuomastik
18

Haré esto:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)

o

df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)

Puede quitar todos los str, luego reemplazar str vacío con np.nan.

Xiaorong Liao
fuente
lambda x: x.str.strip () debería ser lambda x: x.strip ()? sugerencia menor: agregue .astype (str) al frente, esto resuelve otros problemas de datos para mí. Esto funciona para mí: df = df.apply ['column']. Astype (str) .apply (lambda x: x.strip ()). Replace ('', np.nan)
Wouter
La segunda línea de código maneja las columnas int / float y string. Agradable. Tks!
Kate Stohr
6

La más simple de todas las soluciones:

df = df.replace(r'^\s+$', np.nan, regex=True)
Gil Baggio
fuente
5

Si está exportando los datos del archivo CSV, puede ser tan simple como esto:

df = pd.read_csv(file_csv, na_values=' ')

Esto creará el marco de datos y reemplazará los valores en blanco como Na

ibrahim rupawala
fuente
2
Otra opción ... el uso skipinitialspace=Truetambién elimina cualquier espacio en blanco después del delimitador que causaría cualquier longitud de espacio en blanco, las cadenas vacías se leerán como nan. Sin embargo, si desea conservar los espacios iniciales por cualquier razón, esta opción no es una buena opción.
Rajshekar Reddy
1
@RajshekarReddy, ¿puedes poner esto como respuesta en alguna parte, fue genial!
Usuario2321
2

Para una solución muy rápida y simple en la que verifique la igualdad con un solo valor, puede usar el maskmétodo.

df.mask(df == ' ')
Ted Petrou
fuente
1

Todos estos están cerca de la respuesta correcta, pero no diría que ninguno resuelva el problema mientras sigue siendo más legible para otros que leen su código. Yo diría que la respuesta es una combinación de la Respuesta de BrenBarn y el comentario de tuomasttik debajo de esa respuesta . La respuesta de BrenBarn utilizaisspace incorporado, pero no admite la eliminación de cadenas vacías, como lo solicitó OP, y tendería a atribuirlo como el caso de uso estándar de reemplazar cadenas con valores nulos.

Lo reescribí con .apply, para que pueda llamarlo en un pd.Serieso pd.DataFrame.


Python 3:

Para reemplazar cadenas vacías o cadenas de espacios enteramente:

df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

Para reemplazar cadenas de espacios enteramente:

df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

Para usar esto en Python 2, deberá reemplazarlo strpor basestring.

Python 2:

Para reemplazar cadenas vacías o cadenas de espacios enteramente:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

Para reemplazar cadenas de espacios enteramente:

df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)
spen.smith
fuente
1

Esto funcionó para mí. Cuando importo mi archivo csv agregué na_values ​​= ''. Los espacios no están incluidos en los valores predeterminados de NaN.

df = pd.read_csv (filepath, na_values ​​= '')

Sambrowne
fuente
0

También puedes usar un filtro para hacerlo.

df = PD.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '])
    df[df=='']='nan'
    df=df.astype(float)
ERIC
fuente
Cada línea de este código (sin incluir datos) es defectuosa.
Julius
0
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column
Jayantha
fuente
0

Esta no es una solución elegante, pero lo que parece funcionar es guardar en XLSX y luego volver a importarlo. Las otras soluciones en esta página no funcionaron para mí, sin saber por qué.

data.to_excel(filepath, index=False)
data = pd.read_excel(filepath)
David Kong
fuente