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.
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
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.
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
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']])