¿Cómo deshacerse de la puntuación usando el tokenizer NLTK?

125

Estoy empezando a usar NLTK y no entiendo cómo obtener una lista de palabras del texto. Si lo uso nltk.word_tokenize(), obtengo una lista de palabras y puntuación. Solo necesito las palabras en su lugar. ¿Cómo puedo deshacerme de la puntuación? Tampoco word_tokenizefunciona con varias oraciones: se agregan puntos a la última palabra.

lizarisk
fuente
12
¿Por qué no eliminas la puntuación tú mismo? nltk.word_tokenize(the_text.translate(None, string.punctuation))debería funcionar en python2 mientras que en python3 puedes hacerlo nltk.work_tokenize(the_text.translate(dict.fromkeys(string.punctuation))).
Bakuriu
3
Esto no funciona No pasa nada con el texto.
lizarisk
El flujo de trabajo asumido por NLTK es que primero se tokeniza en oraciones y luego cada oración en palabras. Por eso word_tokenize()no funciona con múltiples oraciones. Para deshacerse de la puntuación, puede usar una expresión regular o la isalnum()función de Python .
Suzana
2
Se hace el trabajo: >>> 'with dot.'.translate(None, string.punctuation) 'with dot'(nota sin punto al final del resultado) que puede causar problemas si tiene cosas como 'end of sentence.No space', en cuyo caso debe hacerse lo siguiente: the_text.translate(string.maketrans(string.punctuation, ' '*len(string.punctuation)))que sustituye a todos los puntuacion con espacios en blanco.
Bakuriu
Vaya, funciona de hecho, pero no con cadenas Unicode.
lizarisk

Respuestas:

162

Eche un vistazo a las otras opciones de tokenización que nltk proporciona aquí . Por ejemplo, puede definir un tokenizador que seleccione secuencias de caracteres alfanuméricos como tokens y suelte todo lo demás:

from nltk.tokenize import RegexpTokenizer

tokenizer = RegexpTokenizer(r'\w+')
tokenizer.tokenize('Eighty-seven miles to go, yet.  Onward!')

Salida:

['Eighty', 'seven', 'miles', 'to', 'go', 'yet', 'Onward']
rmalouf
fuente
55
Tenga en cuenta que si usa esta opción, perderá las características del lenguaje natural especiales, word_tokenizecomo separar las contracciones. Puede dividirse ingenuamente en la expresión regular \w+sin necesidad de NLTK.
sffc
3
Para ilustrar el comentario de @sffc, puede perder palabras como "Sr."
geekazoid
está reemplazando 'n't' a 't' ¿cómo deshacerse de esto?
Md. Ashikur Rahman
46

Realmente no necesita NLTK para eliminar la puntuación. Puede eliminarlo con python simple. Para cuerdas:

import string
s = '... some string with punctuation ...'
s = s.translate(None, string.punctuation)

O para unicode:

import string
translate_table = dict((ord(char), None) for char in string.punctuation)   
s.translate(translate_table)

y luego usa esta cadena en tu tokenizer.

El módulo de cadena PS tiene algunos otros conjuntos de elementos que se pueden eliminar (como dígitos).

Salvador Dalí
fuente
3
Elimine todos los signos de puntuación utilizando la expresión de lista que también funciona. a = "*fa,fd.1lk#$" print("".join([w for w in a if w not in string.punctuation]))
Johnny Zhang
32

El siguiente código eliminará todos los signos de puntuación, así como los caracteres no alfabéticos. Copiado de su libro.

http://www.nltk.org/book/ch01.html

import nltk

s = "I can't do this now, because I'm so tired.  Please give me some time. @ sd  4 232"

words = nltk.word_tokenize(s)

words=[word.lower() for word in words if word.isalpha()]

print(words)

salida

['i', 'ca', 'do', 'this', 'now', 'because', 'i', 'so', 'tired', 'please', 'give', 'me', 'some', 'time', 'sd']
Madura Pradeep
fuente
17
Solo tenga en cuenta que al usar este método perderá la palabra "no" en casos como "no se puede" o "no", que puede ser muy importante para comprender y clasificar la oración. Es mejor usar oración.translate (string.maketrans ("", "",), chars_to_remove), donde chars_to_remove puede ser "., ':;!?"
MikeL
3
@MikeL No puedes evitar palabras como "no puedo" y "no" importando contracciones y contracciones.fix (oración_aquí) antes de tokanizar. Convierte "no se puede" en "no se puede" y "no" en "no se debe".
zipline86
16

Como se observó en los comentarios, comience con sent_tokenize (), porque word_tokenize () solo funciona en una sola oración. Puede filtrar la puntuación con filter (). Y si tiene cadenas unicode, asegúrese de que sea un objeto unicode (no una 'str' codificada con alguna codificación como 'utf-8').

from nltk.tokenize import word_tokenize, sent_tokenize

text = '''It is a blue, small, and extraordinary ball. Like no other'''
tokens = [word for sent in sent_tokenize(text) for word in word_tokenize(sent)]
print filter(lambda word: word not in ',-', tokens)
palooh
fuente
14
La mayor parte de la complejidad involucrada en el tokenizer de Penn Treebank tiene que ver con el manejo adecuado de la puntuación. ¿Por qué utilizar un tokenizador costoso que maneja bien la puntuación si solo va a eliminar la puntuación?
rmalouf
3
word_tokenizeEs una función que regresa [token for sent in sent_tokenize(text, language) for token in _treebank_word_tokenize(sent)]. Así que creo que su respuesta es hacer lo que nltk ya hace: usar sent_tokenize()antes de usar word_tokenize(). Al menos esto es para nltk3.
Kurt Bourbaki
2
@rmalouf porque no necesitas tokens de puntuación solamente? Así que quieres didy n'tno.
Ciprian Tomoiagă
11

Acabo de usar el siguiente código, que eliminó toda la puntuación:

tokens = nltk.wordpunct_tokenize(raw)

type(tokens)

text = nltk.Text(tokens)

type(text)  

words = [w.lower() for w in text if w.isalpha()]
vish
fuente
2
¿Por qué convertir tokens a texto?
Sadik
6

Creo que necesita algún tipo de coincidencia de expresiones regulares (el siguiente código está en Python 3):

import string
import re
import nltk

s = "I can't do this now, because I'm so tired.  Please give me some time."
l = nltk.word_tokenize(s)
ll = [x for x in l if not re.fullmatch('[' + string.punctuation + ']+', x)]
print(l)
print(ll)

Salida:

['I', 'ca', "n't", 'do', 'this', 'now', ',', 'because', 'I', "'m", 'so', 'tired', '.', 'Please', 'give', 'me', 'some', 'time', '.']
['I', 'ca', "n't", 'do', 'this', 'now', 'because', 'I', "'m", 'so', 'tired', 'Please', 'give', 'me', 'some', 'time']

Debería funcionar bien en la mayoría de los casos, ya que elimina la puntuación al tiempo que conserva los tokens como "n't", que no se pueden obtener de los tokenizadores regex como wordpunct_tokenize.

Quan Gan
fuente
Esto también eliminará cosas como ...y --al mismo tiempo preservará las contracciones, que s.translate(None, string.punctuation)no lo harán
CJ Jackson
5

Sinceramente preguntando, ¿qué es una palabra? Si su suposición es que una palabra consiste solo en caracteres alfabéticos, está equivocado ya que las palabras como can'tse destruirán en pedazos (como cany t) si elimina la puntuación antes de la tokenización , lo que es muy probable que afecte negativamente a su programa.

Por lo tanto, la solución es tokenizar y luego eliminar los tokens de puntuación .

import string

from nltk.tokenize import word_tokenize

tokens = word_tokenize("I'm a southern salesman.")
# ['I', "'m", 'a', 'southern', 'salesman', '.']

tokens = list(filter(lambda token: token not in string.punctuation, tokens))
# ['I', "'m", 'a', 'southern', 'salesman']

... y luego, si lo desea, puede reemplazar ciertas fichas como 'mcon am.

Bora M. Alper
fuente
4

Yo uso este código para eliminar la puntuación:

import nltk
def getTerms(sentences):
    tokens = nltk.word_tokenize(sentences)
    words = [w.lower() for w in tokens if w.isalnum()]
    print tokens
    print words

getTerms("hh, hh3h. wo shi 2 4 A . fdffdf. A&&B ")

Y si desea verificar si un token es una palabra inglesa válida o no, es posible que necesite PyEnchant

Tutorial:

 import enchant
 d = enchant.Dict("en_US")
 d.check("Hello")
 d.check("Helo")
 d.suggest("Helo")
zhenv5
fuente
2
Tenga en cuenta que esta solución mata las contracciones. Esto se debe a que word_tokenizeusa el tokenizador estándar TreebankWordTokenizer, que divide las contracciones (por ejemplo, can'ta ( ca, n't). Sin embargo, n'tno es alfanumérico y se pierde en el proceso.
Diego Ferri
1

Eliminar puntuación (eliminará, así como parte del manejo de puntuación usando el código a continuación)

        tbl = dict.fromkeys(i for i in range(sys.maxunicode) if unicodedata.category(chr(i)).startswith('P'))
        text_string = text_string.translate(tbl) #text_string don't have punctuation
        w = word_tokenize(text_string)  #now tokenize the string 

Muestra de entrada / salida:

direct flat in oberoi esquire. 3 bhk 2195 saleable 1330 carpet. rate of 14500 final plus 1% floor rise. tax approx 9% only. flat cost with parking 3.89 cr plus taxes plus possession charger. middle floor. north door. arey and oberoi woods facing. 53% paymemt due. 1% transfer charge with buyer. total cost around 4.20 cr approx plus possession charges. rahul soni

['direct', 'flat', 'oberoi', 'esquire', '3', 'bhk', '2195', 'saleable', '1330', 'carpet', 'rate', '14500', 'final', 'plus', '1', 'floor', 'rise', 'tax', 'approx', '9', 'flat', 'cost', 'parking', '389', 'cr', 'plus', 'taxes', 'plus', 'possession', 'charger', 'middle', 'floor', 'north', 'door', 'arey', 'oberoi', 'woods', 'facing', '53', 'paymemt', 'due', '1', 'transfer', 'charge', 'buyer', 'total', 'cost', 'around', '420', 'cr', 'approx', 'plus', 'possession', 'charges', 'rahul', 'soni']

ascii_walker
fuente
Muchas gracias
1

Simplemente agregando a la solución por @rmalouf, esto no incluirá ningún número porque \ w + es equivalente a [a-zA-Z0-9_]

from nltk.tokenize import RegexpTokenizer
tokenizer = RegexpTokenizer(r'[a-zA-Z]')
tokenizer.tokenize('Eighty-seven miles to go, yet.  Onward!')
Himanshu Aggarwal
fuente
Este crea una ficha para cada letra.
Rishabh Gupta
1

Puede hacerlo en una línea sin nltk (python 3.x).

import string
string_text= string_text.translate(str.maketrans('','',string.punctuation))
Nishān Wickramarathna
fuente