Como faço para remover partes indesejadas de seqüências de caracteres em uma coluna?
Seis anos após a publicação da pergunta original, o pandas agora possui um bom número de funções de string "vetorizadas" que podem executar sucintamente essas operações de manipulação de strings.
Esta resposta irá explorar algumas dessas funções de string, sugerir alternativas mais rápidas e entrar em uma comparação de tempos no final.
Especifique a substring / padrão para corresponder e a substring para substituí-lo.
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
Se você precisar converter o resultado em um número inteiro, poderá usar Series.astype
,
df['result'] = df['result'].str.replace(r'\D', '').astype(int)
df.dtypes
time object
result int64
dtype: object
Se você não deseja modificar df
no local, use DataFrame.assign
:
df2 = df.assign(result=df['result'].str.replace(r'\D', ''))
df
# Unchanged
Útil para extrair a substring (s) que você deseja manter.
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
Com extract
, é necessário especificar pelo menos um grupo de captura. expand=False
retornará uma série com os itens capturados do primeiro grupo de capturas.
A divisão funciona assumindo que todas as suas strings seguem essa estrutura consistente.
# 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
Não recomende se você estiver procurando uma solução geral.
Se você estiver satisfeito com as str
soluções baseadas em acessor sucintas e legíveis acima, você pode parar por aqui. No entanto, se você estiver interessado em alternativas mais rápidas e com melhor desempenho, continue lendo.
Otimizando: compreensões da lista
Em algumas circunstâncias, a compreensão da lista deve ser preferida às funções de string dos pandas. O motivo é que as funções de string são inerentemente difíceis de vetorizar (no verdadeiro sentido da palavra), portanto, a maioria das funções de string e regex são apenas wrappers em torno de loops com mais sobrecarga.
Meu artigo: Os loops de pandas são realmente ruins? Quando devo me importar? , entra em maiores detalhes.
A str.replace
opção pode ser reescrita usandore.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
O str.extract
exemplo pode ser reescrito usando uma compreensão de lista com 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
Se NaNs ou não correspondências forem possíveis, você precisará reescrever o item acima para incluir alguma verificação de erro. Eu faço isso usando uma função.
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
Também podemos reescrever as respostas de @ eumiro e @ MonkeyButter usando a compreensão de lista:
df['result'] = [x.lstrip('+-').rstrip('aAbBcC') for x in df['result']]
E,
df['result'] = [x[1:-1] for x in df['result']]
Aplicam-se as mesmas regras para o tratamento de NaNs, etc.
Comparação de desempenho
Gráficos gerados usando perfplot . Listagem de código completo, para sua referência. As funções relevantes estão listadas abaixo.
Algumas dessas comparações são injustas porque tiram vantagem da estrutura dos dados do OP, mas tiram dela o que você deseja. Uma coisa a notar é que toda função de compreensão de lista é mais rápida ou comparável do que sua variante equivalente de pandas.
Funções
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']])