196 votes

Suppression des parties non désirées des chaînes de caractères dans une colonne

Je cherche un moyen efficace de supprimer les parties non désirées des chaînes de caractères dans une colonne DataFrame.

Les données ressemblent :

    time    result
1    09:00   +52A
2    10:00   +62B
3    11:00   +44a
4    12:00   +30b
5    13:00   -110a

J'ai besoin de réduire ces données à :

    time    result
1    09:00   52
2    10:00   62
3    11:00   44
4    12:00   30
5    13:00   110

J'ai essayé .str.lstrip('+-') et . str.rstrip('aAbBcC') mais j'ai eu une erreur :

TypeError: wrapper() takes exactly 1 argument (2 given)

Tout conseil serait grandement apprécié !

239voto

eumiro Points 56644
data['result'] = data['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC'))

201voto

coldspeed Points 111053

Comment supprimer les parties non désirées des chaînes de caractères dans une colonne ?

Six ans après la publication de la question originale, pandas dispose désormais d'un bon nombre de fonctions de chaînes "vectorisées" qui peuvent effectuer succinctement ces opérations de manipulation de chaînes.

Cette réponse explorera certaines de ces fonctions de chaîne de caractères, suggérera des alternatives plus rapides et se terminera par une comparaison de temps.


.str.replace

Spécifiez la sous-chaîne/motif à faire correspondre, et la sous-chaîne à remplacer par celle-ci.

pd.__version__
# '0.24.1'

df    
    time result
1  09:00   +52A
2  10:00   +62B
3  11:00   +44a
4  12:00   +30b
5  13:00  -110a

df['result'] = df['result'].str.replace(r'\D', '')
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

Si vous avez besoin que le résultat soit converti en un nombre entier, vous pouvez utiliser Series.astype ,

df['result'] = df['result'].str.replace(r'\D', '').astype(int)

df.dtypes
time      object
result     int64
dtype: object

Si vous ne voulez pas modifier df en place, utiliser DataFrame.assign :

df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged

.str.extract

Utile pour extraire la ou les sous-chaînes que vous souhaitez conserver.

df['result'] = df['result'].str.extract(r'(\d+)', expand=False)
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

Avec extract il est nécessaire de spécifier au moins un groupe de capture. expand=False retournera une série contenant les éléments capturés du premier groupe de capture.


.str.split y .str.get

Le fractionnement fonctionne en supposant que toutes vos chaînes de caractères suivent cette structure cohérente.

# df['result'] = df['result'].str.split(r'\D').str[1]
df['result'] = df['result'].str.split(r'\D').str.get(1)
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

À ne pas recommander si vous recherchez une solution générale.


Si vous êtes satisfaits de la version succincte et lisible de l'ouvrage str solutions basées sur les accesseurs ci-dessus, vous pouvez vous arrêter là. Cependant, si vous êtes intéressés par des alternatives plus rapides et plus performantes, poursuivez votre lecture.


Optimisation : Compréhensions de listes

Dans certaines circonstances, il convient de privilégier les compteurs de listes par rapport aux fonctions de chaînes de caractères de pandas. La raison en est que les fonctions de chaîne de caractères sont par nature difficiles à vectoriser (au sens propre du terme), de sorte que la plupart des fonctions de chaîne de caractères et de regex ne sont que des enveloppes autour de boucles avec plus de surcharge.

Mon rapport, Les boucles for dans les pandas sont-elles vraiment mauvaises ? Quand faut-il s'en préoccuper ? est plus détaillée.

Le site str.replace peut être réécrite en utilisant re.sub

import re

# Pre-compile your regex pattern for more performance.
p = re.compile(r'\D')
df['result'] = [p.sub('', x) for x in df['result']]
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

Le site str.extract L'exemple peut être réécrit en utilisant une compréhension de liste avec re.search ,

p = re.compile(r'\d+')
df['result'] = [p.search(x)[0] for x in df['result']]
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

Si les NaN ou les non-concordances sont une possibilité, vous devrez réécrire ce qui précède pour inclure une vérification des erreurs. Je fais cela en utilisant une fonction.

def try_extract(pattern, string):
    try:
        m = pattern.search(string)
        return m.group(0)
    except (TypeError, ValueError, AttributeError):
        return np.nan

p = re.compile(r'\d+')
df['result'] = [try_extract(p, x) for x in df['result']]
df

    time result
1  09:00     52
2  10:00     62
3  11:00     44
4  12:00     30
5  13:00    110

Nous pouvons également réécrire les réponses de @eumiro et @MonkeyButter en utilisant des compréhensions de listes :

df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]

Et,

df['result'] = [x[1:-1] for x in df['result']]

Les mêmes règles de traitement des NaNs, etc. s'appliquent.


Comparaison des performances

enter image description here

Graphiques générés à l'aide de perfplot . Liste complète des codes, pour votre référence. Les fonctions pertinentes sont énumérées ci-dessous.

Certaines de ces comparaisons sont injustes car elles tirent parti de la structure des données de l'OP, mais prenez-en ce que vous voulez. Une chose à noter est que chaque fonction de compréhension de liste est soit plus rapide, soit comparable à sa variante pandas équivalente.

Fonctions

def eumiro(df):
    return df.assign(
        result=df['result'].map(lambda x: x.lstrip('+-').rstrip('aAbBcC')))

def coder375(df):
    return df.assign(
        result=df['result'].replace(r'\D', r'', regex=True))

def monkeybutter(df):
    return df.assign(result=df['result'].map(lambda x: x[1:-1]))

def wes(df):
    return df.assign(result=df['result'].str.lstrip('+-').str.rstrip('aAbBcC'))

def cs1(df):
    return df.assign(result=df['result'].str.replace(r'\D', ''))

def cs2_ted(df):
    # `str.extract` based solution, similar to @Ted Petrou's. so timing together.
    return df.assign(result=df['result'].str.extract(r'(\d+)', expand=False))

def cs1_listcomp(df):
    return df.assign(result=[p1.sub('', x) for x in df['result']])

def cs2_listcomp(df):
    return df.assign(result=[p2.search(x)[0] for x in df['result']])

def cs_eumiro_listcomp(df):
    return df.assign(
        result=[x.lstrip('+-').rstrip('aAbBcC') for x in df['result']])

def cs_mb_listcomp(df):
    return df.assign(result=[x[1:-1] for x in df['result']])

61voto

Coder375 Points 1355

J'utiliserais la fonction pandas replace, très simple et puissante puisque vous pouvez utiliser une expression rationnelle. Ci-dessous, j'utilise l'expression rationnelle \D pour supprimer tous les caractères non numériques, mais il est évident que vous pouvez être très créatif avec les expressions rationnelles.

data['result'].replace(regex=True,inplace=True,to_replace=r'\D',value=r'')

39voto

MonkeyButter Points 229

Dans le cas particulier où vous connaissez le nombre de positions que vous souhaitez supprimer de la colonne du cadre de données, vous pouvez utiliser l'indexation des chaînes à l'intérieur d'une fonction lambda pour vous débarrasser de ces parties :

Dernier personnage :

data['result'] = data['result'].map(lambda x: str(x)[:-1])

Les deux premiers personnages :

data['result'] = data['result'].map(lambda x: str(x)[2:])

18voto

Wes McKinney Points 17545

Il y a un bogue ici : actuellement, il n'est pas possible de passer des arguments à str.lstrip y str.rstrip :

http://github.com/pydata/pandas/issues/2411

EDIT : 2012-12-07 cela fonctionne maintenant sur la branche dev :

In [8]: df['result'].str.lstrip('+-').str.rstrip('aAbBcC')
Out[8]: 
1     52
2     62
3     44
4     30
5    110
Name: result

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X