Pelar / recortar todas las cadenas de un marco de datos

80

Limpiando los valores de un marco de datos de varios tipos en python / pandas, quiero recortar las cadenas. Actualmente lo estoy haciendo en dos instrucciones:

import pandas as pd

df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])

df.replace('^\s+', '', regex=True, inplace=True) #front
df.replace('\s+$', '', regex=True, inplace=True) #end

df.values

Esto es bastante lento, ¿qué podría mejorar?

mxdbld
fuente
1
df.replace(r'\s*(.*?)\s*', r'\1', regex=True)
MaxU
1
Esta es la mejor respuesta, acaba de iniciar sesión para votar la respuesta por @MaxU
Linkon

Respuestas:

151

Puede utilizar DataFrame.select_dtypespara seleccionar stringcolumnas y luego applyfuncionar str.strip.

Aviso: los valores no pueden ser typescomo dictso lists, porque su dtypeses object.

df_obj = df.select_dtypes(['object'])
print (df_obj)
0    a  
1    c  

df[df_obj.columns] = df_obj.apply(lambda x: x.str.strip())
print (df)

   0   1
0  a  10
1  c   5

Pero si solo hay unas pocas columnas, use str.strip:

df[0] = df[0].str.strip()
Jezrael
fuente
1
Y SettingWithCopyWarning debe ignorarse en este caso como se explica stackoverflow.com/questions/20625582/…
Harvey
71

Tiro de dinero

Aquí hay una versión compacta del uso applymapcon una expresión lambda sencilla para llamar stripsolo cuando el valor es de un tipo de cadena:

df.applymap(lambda x: x.strip() if isinstance(x, str) else x)

Ejemplo completo

Un ejemplo más completo:

import pandas as pd


def trim_all_columns(df):
    """
    Trim whitespace from ends of each value across all series in dataframe
    """
    trim_strings = lambda x: x.strip() if isinstance(x, str) else x
    return df.applymap(trim_strings)


# simple example of trimming whitespace from data elements
df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])
df = trim_all_columns(df)
print(df)


>>>
   0   1
0  a  10
1  c   5

Ejemplo de trabajo

Aquí hay un ejemplo de trabajo alojado por trinket: https://trinket.io/python3/e6ab7fb4ab

Jonathan B.
fuente
1
Hola @DaleKube ... Acabo de probar esto nuevo en una nueva máquina solo como una verificación de cordura y obtengo los mismos resultados que los publicados en la respuesta. ¿Puede confirmar si está usando Python2 o Python3? Solo estoy usando Python3 en estos días, pero quizás ese sea un factor. Si es así, lo notaré en mi respuesta publicada si puede confirmar. ¡Gracias!
Jonathan B.
1
Borré mi comentario. Encontré un error en mi código y puedo confirmar que ahora funciona a las mil maravillas. Para su información, estoy usando Python 3. Perdón por las molestias.
Dale Kube
deberías usar type(x) == str, notype(x) is str
fjsj
@fjsj Gracias por el empujón. Actualicé el ejemplo utilizando la orientación de PEP8 favoreciendo isinstance(x, str).
Jonathan B.
10

Puedes probar:

df[0] = df[0].str.strip()

o más específicamente para todas las columnas de cadena

non_numeric_columns = list(set(df.columns)-set(df._get_numeric_data().columns))
df[non_numeric_columns] = df[non_numeric_columns].apply(lambda x : str(x).strip())
Aakash Makwana
fuente
9

Si realmente quieres usar expresiones regulares, entonces

>>> df.replace('(^\s+|\s+$)', '', regex=True, inplace=True)
>>> df
   0   1
0  a  10
1  c   5

Pero debería ser más rápido hacerlo así:

>>> df[0] = df[0].str.strip()
Roman Pekar
fuente
5

Puede utilizar la applyfunción del Seriesobjeto:

>>> df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])
>>> df[0][0]
'  a  '
>>> df[0] = df[0].apply(lambda x: x.strip())
>>> df[0][0]
'a'

Tenga en cuenta el uso de stripy no regexcuál es mucho más rápido

Otra opción: use la applyfunción del objeto DataFrame:

>>> df = pd.DataFrame([['  a  ', 10], ['  c  ', 5]])
>>> df.apply(lambda x: x.apply(lambda y: y.strip() if type(y) == type('') else y), axis=0)

   0   1
0  a  10
1  c   5
Dekel
fuente
1
df[0] = df[0].str.strip()- probablemente será más rápido en DF más grandes
MaxU
-1
def trim(x):
    if x.dtype == object:
        x = x.str.split(' ').str[0]
    return(x)

df = df.apply(trim)
hyunwoo jeong
fuente
1
¿Podría explicar qué hace la función, por favor?
CJ Dennis
por ejemplo, encuentro datos como este en mi trabajo diario: la 가나다 봻 parte izquierda del espacio en blanco es lo que quiero, la parte derecha es basura. La función de recorte extrae lo que quiero de los datos sin procesar.
hyunwoo jeong
Votado en contra porque esto no recorta la cadena, elimina todo lo que sigue al primer espacio. Este no es el comportamiento que se pide en la pregunta e introduce efectos secundarios que el lector puede no estar esperando. Además, los efectos secundarios pueden no ser evidentes de inmediato. Si está intentando recortar una columna de apellidos, podría pensar que esto funciona como se esperaba porque la mayoría de las personas no tienen varios apellidos y los espacios finales se eliminan. Luego, una persona portuguesa con dos apellidos se une a su sitio y el código elimina su apellido, dejando solo su primer apellido.
scottclowe