Creo que lo que quiero hacer es una tarea bastante común, pero no he encontrado ninguna referencia en la web. Tengo texto con puntuación y quiero una lista de las palabras.
"Hey, you - what are you doing here!?"
debiera ser
['hey', 'you', 'what', 'are', 'you', 'doing', 'here']
Pero Python str.split()
solo funciona con un argumento, así que tengo todas las palabras con la puntuación después de dividirme con espacios en blanco. ¿Algunas ideas?
str.split()
también funciona sin argumentos en absolutoRespuestas:
Un caso donde las expresiones regulares están justificadas:
fuente
re
, pero nofindall
. La respuesta a continuaciónre.split()
es superior.don't
se trata como una sola palabra, en lugar de dividirse endon
yt
.re.split ()
fuente
\w
,\W
,\s
, y\S
. Quien haya pensado que la capitalización de una bandera debería invertir su significado debe recibir un disparo en la cabeza.shift
tecla para hacer lo contrario de algo.ctrl+z
deshacer vs.ctrl+shift+z
para rehacer. Entoncesshift w
, oW
, sería lo contrario dew
.Otra forma rápida de hacer esto sin una expresión regular es reemplazar primero los caracteres, como se muestra a continuación:
fuente
Tantas respuestas, sin embargo, no puedo encontrar ninguna solución que haga eficientemente lo que literalmente pide el título de las preguntas (división en múltiples separadores posibles; en cambio, muchas respuestas se dividen en cualquier cosa que no sea una palabra, que es diferente). Así que aquí hay una respuesta a la pregunta en el título, que se basa en el
re
módulo estándar y eficiente de Python :dónde:
[…]
partidos uno de los separadores enumerados en el interior,\-
en la expresión regular está aquí para evitar la interpretación especial de-
como un indicador de rango de caracteres (como enA-Z
),+
saltos de uno o más delimitadores (que podrían omitirse gracias a lafilter()
, pero esto produciría innecesariamente cadenas vacías entre separadores emparejados), yfilter(None, …)
elimina las cadenas vacías posiblemente creadas por los separadores iniciales y finales (ya que las cadenas vacías tienen un valor booleano falso).Esto
re.split()
precisamente "se divide con separadores múltiples", como se solicitó en el título de la pregunta.Además, esta solución es inmune a los problemas con caracteres que no son ASCII en las palabras que se encuentran en otras soluciones (vea el primer comentario a la respuesta de ghostdog74 ).
¡El
re
módulo es mucho más eficiente (en velocidad y concisión) que hacer bucles y pruebas de Python "a mano"!fuente
Otra forma, sin expresiones regulares
fuente
"Hey, you - what are you doing here María!?"
. La solución aceptada no funcionará con el ejemplo anterior.''.join([o if not o in string.punctuation else ' ' for o in s]).split()
o for o in s if (o in not string.punctuation or o == "'")
, pero luego se está volviendo demasiado complicado para una sola línea si agregamos también el parche de cedbeu."First Name,Last Name,Street Address,City,State,Zip Code"
y queremos dividir solo en una coma,
. La salida deseada sería:['First Name', 'Last Name', 'Street Address', 'City', 'State', 'Zip Code']
Lo que obtenemos en su lugar:['First', 'Name', 'Last', 'Name', 'Street', 'Address', 'City', 'State', 'Zip', 'Code']
re
módulo es estándar y ofrece legibilidad y velocidad, no veo por qué debería evitarse.Pro-Tip: uso
string.translate
para las operaciones de cadena más rápidas que tiene Python.Alguna prueba ...
Primero, el camino lento (lo siento pprzemek):
A continuación, usamos
re.findall()
(como se indica en la respuesta sugerida). Mucho mas rápido:Finalmente, usamos
translate
:Explicación:
string.translate
se implementa en C y, a diferencia de muchas funciones de manipulación de cadenas en Python,string.translate
no produce una nueva cadena. Por lo tanto, es lo más rápido posible para la sustitución de cadenas.Sin embargo, es un poco incómodo, ya que necesita una tabla de traducción para hacer esta magia. Puede hacer una tabla de traducción con la
maketrans()
función de conveniencia. El objetivo aquí es traducir todos los caracteres no deseados a espacios. Un sustituto uno por uno. Nuevamente, no se producen nuevos datos. Entonces esto es rápido !A continuación, usamos buenos viejos
split()
.split()
de manera predeterminada, funcionará en todos los caracteres de espacios en blanco, agrupándolos para la división. El resultado será la lista de palabras que desea. ¡Y este enfoque es casi 4 veces más rápido quere.findall()
!fuente
patt = re.compile(ur'\w+', re.UNICODE); patt.findall(S)
es más rápido que traducir, porque debe codificar la cadena antes de aplicar la transformación y decodificar cada elemento de la lista después de la división para volver a Unicode.s.translate(''.join([(chr(i) if chr(i) not in seps else seps[0]) for i in range(256)])).split(seps[0])
Tenía un dilema similar y no quería usar el módulo 're'.
fuente
re
módulo, que es más rápido y más claro (no es que las expresiones regulares sean especialmente claras, sino porque es mucho más corto y directo)?Primero, quiero estar de acuerdo con los demás en que la expresión regular o las
str.translate(...)
soluciones basadas son más eficaces. Para mi caso de uso, el rendimiento de esta función no fue significativo, por lo que quería agregar ideas que consideraba con ese criterio.Mi objetivo principal era generalizar las ideas de algunas de las otras respuestas en una solución que pudiera funcionar para cadenas que contengan más que palabras de expresión regular (es decir, poner en una lista negra el subconjunto explícito de caracteres de puntuación frente a caracteres de palabras en la lista blanca).
Tenga en cuenta que, en cualquier enfoque, uno también podría considerar el uso
string.punctuation
en lugar de una lista definida manualmente.Opción 1 - re.sub
Me sorprendió ver que no hay respuesta hasta ahora utiliza re.sub (...) . Me parece un enfoque simple y natural para este problema.
En esta solución, anidé la llamada al
re.sub(...)
interiorre.split(...)
, pero si el rendimiento es crítico, compilar la expresión regular en el exterior podría ser beneficioso, para mi caso de uso, la diferencia no fue significativa, por lo que prefiero la simplicidad y la legibilidad.Opción 2 - reemplazo de str.
Estas son algunas líneas más, pero tiene la ventaja de ser expansible sin tener que verificar si necesita escapar de cierto carácter en expresiones regulares.
Hubiera sido agradable poder asignar el str.replace a la cadena en su lugar, pero no creo que se pueda hacer con cadenas inmutables, y aunque el mapeo contra una lista de caracteres funcionaría, ejecutar cada reemplazo contra cada carácter Suena excesivo. (Editar: consulte la siguiente opción para ver un ejemplo funcional).
Opción 3 - functools.reduce
(En Python 2,
reduce
está disponible en el espacio de nombres global sin importarlo desde functools).fuente
str.translate
: no es compatible con Unicode, pero es más rápido que otros métodos y, como tal, podría ser bueno en algunos casos:replacements=',-!?'; import string; my_str = my_str.translate(string.maketrans(replacements, ' ' * len(replacements)))
también aquí es obligatorio tener reemplazos como una cadena de caracteres, no tuplas o lista.Entonces esto se convierte en un trazador de líneas:
Explicación
Esto es lo que en Haskell se conoce como la mónada Lista. La idea detrás de la mónada es que una vez "en la mónada" usted "permanece en la mónada" hasta que algo lo saque. Por ejemplo, en Haskell, supongamos que asigna la
range(n) -> [1,2,...,n]
función de Python sobre una Lista. Si el resultado es una Lista, se agregará a la Lista en el lugar, por lo que obtendría algo asímap(range, [3,4,1]) -> [0,1,2,0,1,2,3,0]
. Esto se conoce como map-append (o mappend, o tal vez algo así). La idea aquí es que tienes esta operación que estás aplicando (dividiendo en un token), y cada vez que haces eso, unes el resultado en la lista.Puede abstraer esto en una función y tener
tokens=string.punctuation
por defecto.Ventajas de este enfoque:
fuente
map_then_append
se puede usar para hacer que un problema sea de 2 líneas, así como muchos otros problemas mucho más fáciles de escribir. La mayoría de las otras soluciones usan elre
módulo de expresión regular , que no es python. Pero he estado descontento con la forma en que hago mi respuesta parece poco elegante y bloaty cuando está muy concisa ... voy a editarlo ...fragments
resultado es solo una lista de los caracteres de la cadena (incluidos los tokens).fragments = ['the,string']
,fragments = 'the,string'
ofragments = list('the,string')
ninguno de ellos están produciendo la salida derecha.prueba esto:
esto imprimirá
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
fuente
Use reemplazar dos veces:
resultados en:
fuente
Me gusta re , pero aquí está mi solución sin ella:
sep .__ contiene__ es un método utilizado por el operador 'in'. Básicamente es lo mismo que
Pero es más conveniente aquí.
groupby obtiene nuestra cadena y función. Divide la cadena en grupos usando esa función: cada vez que cambia un valor de función, se genera un nuevo grupo. Entonces, sep .__ contiene__ es exactamente lo que necesitamos.
groupby devuelve una secuencia de pares, donde pair [0] es el resultado de nuestra función y pair [1] es un grupo. Usando 'si no k' filtramos los grupos con separadores (porque un resultado de sep .__ contiene__ es verdadero en los separadores). Bueno, eso es todo: ahora tenemos una secuencia de grupos donde cada uno es una palabra (el grupo es en realidad un iterable, por lo que usamos join para convertirlo en cadena).
Esta solución es bastante general, ya que utiliza una función para separar cadenas (puede dividirlas por cualquier condición que necesite). Además, no crea cadenas / listas intermedias (puede eliminar la unión y la expresión se volverá perezosa, ya que cada grupo es un iterador)
fuente
En lugar de utilizar una función re module re.split, puede lograr el mismo resultado utilizando el método de pandas series.str.split.
Primero, cree una serie con la cadena anterior y luego aplique el método a la serie.
thestring = pd.Series("Hey, you - what are you doing here!?") thestring.str.split(pat = ',|-')
El parámetro pat toma los delimitadores y devuelve la cadena dividida como una matriz. Aquí los dos delimitadores se pasan usando un | (u operador). La salida es la siguiente:
[Hey, you , what are you doing here!?]
fuente
Me estoy reencontrando con Python y necesitaba lo mismo. La solución Findall puede ser mejor, pero se me ocurrió esto:
fuente
usando maketrans y traducir puedes hacerlo de manera fácil y ordenada
fuente
En Python 3, puede usar el método de PY4E - Python para todos .
your_string.translate(your_string.maketrans(fromstr, tostr, deletestr))
Puedes ver la "puntuación":
Por su ejemplo:
Para más información, puede consultar:
fuente
Otra forma de lograr esto es usar el Kit de herramientas de lenguaje natural ( nltk ).
Esto imprime:
['Hey', 'you', 'what', 'are', 'you', 'doing', 'here']
El mayor inconveniente de este método es que necesita instalar el paquete nltk .
Los beneficios son que puedes hacer muchas cosas divertidas con el resto del paquete nltk una vez que obtengas tus tokens.
fuente
En primer lugar, no creo que su intención sea utilizar la puntuación como delimitadores en las funciones divididas. Su descripción sugiere que simplemente desea eliminar la puntuación de las cadenas resultantes.
Me encuentro con bastante frecuencia, y mi solución habitual no requiere re.
Función lambda de una línea con comprensión de lista:
(requiere
import string
):Función (tradicional)
Como función tradicional, esto sigue siendo solo dos líneas con una comprensión de la lista (además de
import string
):Naturalmente, también dejará intactas las contracciones y las palabras con guiones. Siempre puede usar
text.replace("-", " ")
para convertir guiones en espacios antes de la división.Función general sin Lambda o comprensión de lista
Para una solución más general (donde puede especificar los caracteres a eliminar), y sin una comprensión de la lista, obtiene:
Por supuesto, siempre puede generalizar la función lambda a cualquier cadena de caracteres especificada también.
fuente
En primer lugar, use siempre re.compile () antes de realizar cualquier operación RegEx en un bucle porque funciona más rápido que la operación normal.
entonces, para su problema, primero compile el patrón y luego realice una acción sobre él.
fuente
Aquí está la respuesta con alguna explicación.
o en una línea, podemos hacer así:
respuesta actualizada
fuente
Cree una función que tome como entrada dos cadenas (la cadena fuente que se dividirá y la cadena de delimitadores de la lista dividida) y generará una lista de palabras divididas:
fuente
Me gusta la solución de pprzemek porque no asume que los delimitadores son caracteres individuales y no trata de aprovechar una expresión regular (que no funcionaría bien si el número de separadores llegara a ser muy largo).
Aquí hay una versión más legible de la solución anterior para mayor claridad:
fuente
tengo el mismo problema que @ooboo y encuentro este tema @ ghostdog74 me inspiró, tal vez alguien encuentre útil mi solución
ingrese algo en el lugar del espacio y divídalo con el mismo carácter si no desea dividirlo en los espacios.
fuente
Aquí está mi ir a una división con múltiples deliminadores:
fuente
Creo que la siguiente es la mejor respuesta para satisfacer sus necesidades:
\W+
puede ser adecuado para este caso, pero puede no serlo para otros casos.fuente
\w
y\W
no son una respuesta a (el título de) la pregunta. Tenga en cuenta que en su respuesta,|
debe eliminarse (está pensando enexpr0|expr1
lugar de[char0 char1…]
). Además, no hay necesidad decompile()
la expresión regular.Aquí está mi opinión sobre esto ...
fuente
Me gusta la
replace()
forma lo mejor. El siguiente procedimiento cambia todos los separadores definidos en una cadenasplitlist
al primer separadorsplitlist
y luego divide el texto en ese separador. También tiene en cuenta sisplitlist
resulta ser una cadena vacía. Devuelve una lista de palabras, sin cadenas vacías.fuente
Aquí está el uso:
fuente
Si desea una operación reversible (preservar los delimitadores), puede usar esta función:
fuente
Hace poco necesitaba hacer esto, pero quería una función que coincidiera con la
str.split
función de biblioteca estándar , esta función se comporta igual que la biblioteca estándar cuando se llama con 0 o 1 argumentos.NOTA : Esta función solo es útil cuando los separadores consisten en un solo carácter (como fue mi caso de uso).
fuente