¿Manteniendo solo ciertos caracteres en una cadena usando Python?

En mi progtwig tengo una cadena como esta:

ag ct oso gcota

Usando python, mi objective es deshacerme del espacio en blanco y mantener solo los caracteres a, t, c y g. Entiendo cómo deshacerme del espacio en blanco (solo estoy usando line = line.replace (“”, “”)). Pero, ¿cómo puedo deshacerme de los caracteres que no necesito cuando podrían ser cualquier otra letra del alfabeto?

Una forma muy elegante y rápida es usar expresiones regulares:

import re str = 'ag ct oso gcota' str = re.sub('[^atcg]', '', str) """str is now 'agctgcta""" 

Yo podría hacer algo como:

 chars_i_want = set('atcg') final_string = ''.join(c for c in start_string if c in chars_i_want) 

Esta es probablemente la forma más fácil de hacer esto.


Otra opción sería usar str.translate para hacer el trabajo:

 import string chars_to_remove = string.printable.translate(None,'acgt') final_string = start_string.translate(None,chars_to_remove) 

No estoy seguro de cuál funcionaría mejor. Tendría que ser cronometrado a través del timeit para saberlo definitivamente.


actualización : tiempos!

 import re import string def test_re(s,regex=re.compile('[^atgc]')): return regex.sub(s,'') def test_join1(s,chars_keep=set('atgc')): return ''.join(c for c in s if c in chars_keep) def test_join2(s,chars_keep=set('atgc')): """ list-comp is faster, but less 'idiomatic' """ return ''.join([c for c in s if c in chars_keep]) def translate(s,chars_to_remove = string.printable.translate(None,'acgt')): return s.translate(None,chars_to_remove) import timeit s = 'ag ct oso gcota' for func in "test_re","test_join1","test_join2","translate": print func,timeit.timeit('{0}(s)'.format(func),'from __main__ import s,{0}'.format(func)) 

Lamentablemente (para mí), regex gana en mi máquina:

 test_re 0.901512145996 test_join1 6.00346088409 test_join2 3.66561293602 translate 1.0741918087 

¿Las personas probaron la función test_re () de mgilson antes de boost las votaciones? Los argumentos para re.sub () se invierten, por lo que estaba haciendo una sustitución en una cadena vacía, y siempre devuelve una cadena vacía.

Yo trabajo en python 3.4; string.translate () solo toma un argumento, un dict. Debido a que hay una sobrecarga en la construcción de este dictado, lo retiré de la función. Para ser justos, también moví la comstackción de expresiones regulares fuera de la función (esto no hizo una diferencia notable).

 import re import string regex=re.compile('[^atgc]') chars_to_remove = string.printable.translate({ ord('a'): None, ord('c'): None, ord('g'): None, ord('t'): None }) cmap = {} for c in chars_to_remove: cmap[ord(c)] = None def test_re(s): return regex.sub('',s) def test_join1(s,chars_keep=set('atgc')): return ''.join(c for c in s if c in chars_keep) def test_join2(s,chars_keep=set('atgc')): """ list-comp is faster, but less 'idiomatic' """ return ''.join([c for c in s if c in chars_keep]) def translate(s): return s.translate(cmap) import timeit s = 'ag ct oso gcota' for func in "test_re","test_join1","test_join2","translate": print(func,timeit.timeit('{0}(s)'.format(func),'from __main__ import s,{0}'.format(func))) 

Aquí están los horarios:

 test_re 3.3141989699797705 test_join1 2.4452173250028864 test_join2 2.081048655003542 translate 1.9390292020107154 

Es una pena que string.translate () no tenga una opción para controlar qué hacer con los personajes que no están en el mapa. La implementación actual es mantenerlos, pero también podríamos tener la opción de eliminarlos, en los casos en que los caracteres que queremos mantener son mucho menos que los que queremos eliminar (oh, hola, Unicode).