Comment supprimer les parties indésirables des chaînes d'une colonne?
6 ans après la publication de la question initiale, les pandas ont maintenant un bon nombre de fonctions de chaîne "vectorisées" qui peuvent exécuter succinctement ces opérations de manipulation de chaînes.
Cette réponse explorera certaines de ces fonctions de chaîne, suggérera des alternatives plus rapides et passera à une comparaison des temps à la fin.
Spécifiez la sous-chaîne / modèle à faire correspondre et la sous-chaîne par laquelle le remplacer.
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 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 souhaitez pas modifier df
sur place, utilisez 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
renverra une série avec les éléments capturés du premier groupe de capture.
Le fractionnement fonctionne en supposant que toutes vos chaînes 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 satisfait des str
solutions succinctes et lisibles basées sur les accesseurs ci-dessus, vous pouvez vous arrêter ici. Cependant, si vous êtes intéressé par des alternatives plus rapides et plus performantes, continuez à lire.
Optimisation: compréhension de listes
Dans certaines circonstances, les compréhensions de liste devraient être privilégiées par rapport aux fonctions de chaîne de pandas. La raison en est que les fonctions de chaîne sont intrinsèquement difficiles à vectoriser (dans le vrai sens du terme), de sorte que la plupart des fonctions de chaîne et de regex ne sont que des enveloppes autour des boucles avec plus de surcharge.
Mon article, les boucles for dans les pandas sont-elles vraiment mauvaises? Quand devrais-je m'en soucier? , entre plus en détail.
L' str.replace
option peut être réécrite en utilisantre.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
L' str.extract
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 des NaN ou des non-correspondances 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 liste:
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 pour la gestion des NaN, 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 répertoriées ci-dessous.
Certaines de ces comparaisons sont injustes car elles tirent parti de la structure des données d'OP, mais en tirent 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.
Les 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']])