Come sbarazzarsi della punteggiatura usando il tokenizer NLTK?


125

Sto appena iniziando a usare NLTK e non capisco come ottenere un elenco di parole dal testo. Se uso nltk.word_tokenize(), ottengo un elenco di parole e punteggiatura. Mi servono invece solo le parole. Come posso eliminare la punteggiatura? Inoltre word_tokenizenon funziona con più frasi: i punti vengono aggiunti all'ultima parola.


12
Perché non rimuovi tu stesso la punteggiatura? nltk.word_tokenize(the_text.translate(None, string.punctuation))dovrebbe funzionare in python2 mentre in python3 puoi farlo nltk.work_tokenize(the_text.translate(dict.fromkeys(string.punctuation))).
Bakuriu

3
Questo non funziona. Non succede nulla con il testo.
lizarisk

Il flusso di lavoro assunto da NLTK è che prima tokenizzate in frasi e poi ogni frase in parole. Ecco perché word_tokenize()non funziona con più frasi. Per eliminare la punteggiatura, puoi utilizzare un'espressione regolare o la isalnum()funzione di Python .
Suzana

2
Si fa il lavoro: >>> 'with dot.'.translate(None, string.punctuation) 'with dot'(nota n punto alla fine del risultato) Può causare problemi se avete le cose come 'end of sentence.No space', in questo caso fare questo, invece: the_text.translate(string.maketrans(string.punctuation, ' '*len(string.punctuation)))che sostituisce tutta la punteggiatura con spazi bianchi.
Bakuriu

Oops, funziona davvero, ma non con le stringhe Unicode.
lizarisk

Risposte:


162

Dai un'occhiata alle altre opzioni di tokenizzazione fornite da nltk qui . Ad esempio, puoi definire un tokenizer che seleziona sequenze di caratteri alfanumerici come token e rilascia tutto il resto:

from nltk.tokenize import RegexpTokenizer

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

Produzione:

['Eighty', 'seven', 'miles', 'to', 'go', 'yet', 'Onward']

55
Nota che se usi questa opzione, perdi le caratteristiche del linguaggio naturale speciali word_tokenizecome la divisione delle contrazioni. Puoi ingenuamente dividere sulla regex \w+senza bisogno di NLTK.
sffc

3
Per illustrare il commento @sffc, potresti perdere parole come "Mr."
geekazoid

sta sostituendo 'n't' a 't' come sbarazzarsi di questo?
Md. Ashikur Rahman

46

Non hai davvero bisogno di NLTK per rimuovere la punteggiatura. Puoi rimuoverlo con semplice python. Per archi:

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

O per Unicode:

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

e quindi usa questa stringa nel tuo tokenizer.

Il modulo stringa PS ha altri set di elementi che possono essere rimossi (come le cifre).


3
Rimuovi tutta la punteggiatura usando l'espressione dell'elenco che funziona anche. a = "*fa,fd.1lk#$" print("".join([w for w in a if w not in string.punctuation]))
Johnny Zhang

32

Il codice seguente rimuoverà tutti i segni di punteggiatura e i caratteri non alfabetici. Copiato dal loro 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)

produzione

['i', 'ca', 'do', 'this', 'now', 'because', 'i', 'so', 'tired', 'please', 'give', 'me', 'some', 'time', 'sd']

17
Basta essere consapevoli del fatto che utilizzando questo metodo perderai la parola "non" in casi come "non posso" o "non", che possono essere molto importanti per la comprensione e la classificazione della frase. È meglio usare phrase.translate (string.maketrans ("", "",), chars_to_remove), dove chars_to_remove può essere "., ':;!?"
MikeL

3
@MikeL Non puoi aggirare parole come "non posso" e "non" importando contrazioni e contrazioni.fix (frase_qui) prima di tokanizzare. Trasformerà "non posso" in "non posso" e "non" in "non".
zipline86

16

Come notato nei commenti, inizia con sent_tokenize (), perché word_tokenize () funziona solo su una singola frase. Puoi filtrare la punteggiatura con filter (). E se hai stringhe Unicode assicurati che sia un oggetto Unicode (non un "str" ​​codificato con una codifica come "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)

14
La maggior parte della complessità coinvolta nel tokenizer Penn Treebank ha a che fare con la corretta gestione della punteggiatura. Perché usare un tokenizer costoso che gestisca bene la punteggiatura se hai intenzione di eliminare solo la punteggiatura?
rmalouf

3
word_tokenizeè una funzione che restituisce [token for sent in sent_tokenize(text, language) for token in _treebank_word_tokenize(sent)]. Quindi penso che la tua risposta stia facendo quello che già fa nltk: usare sent_tokenize()prima di usare word_tokenize(). Almeno questo è per nltk3.
Kurt Bourbaki

2
@rmalouf perché non hai bisogno di token di sola punteggiatura? Quindi vuoi dide n'tma non.
Ciprian Tomoiagă

11

Ho appena usato il seguente codice, che ha rimosso tutta la punteggiatura:

tokens = nltk.wordpunct_tokenize(raw)

type(tokens)

text = nltk.Text(tokens)

type(text)  

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

2
perché convertire i token in testo?
Sadik

6

Penso che tu abbia bisogno di una sorta di corrispondenza delle espressioni regolari (il seguente codice è in 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)

Produzione:

['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']

Dovrebbe funzionare bene nella maggior parte dei casi poiché rimuove la punteggiatura preservando i token come "n't", che non possono essere ottenuti da tokenizer di regex come wordpunct_tokenize.


Questo rimuoverà anche cose come ...e --preservando le contrazioni, cosa s.translate(None, string.punctuation)che non farà
CJ Jackson

5

Chiedendo sinceramente, cos'è una parola? Se la tua ipotesi è che una parola sia composta solo da caratteri alfabetici, ti sbagli poiché parole come can'tverranno distrutte in pezzi (come cane t) se rimuovi la punteggiatura prima della tokenizzazione , il che molto probabilmente influirà negativamente sul tuo programma.

Quindi la soluzione è tokenizzare e quindi rimuovere i token di punteggiatura .

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']

... e poi, se lo desideri, puoi sostituire alcuni token come 'mcon am.


4

Uso questo codice per rimuovere la punteggiatura:

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 ")

E se vuoi controllare se un token è una parola inglese valida o meno, potresti aver bisogno di PyEnchant

Tutorial:

 import enchant
 d = enchant.Dict("en_US")
 d.check("Hello")
 d.check("Helo")
 d.suggest("Helo")

2
Attenzione che questa soluzione uccide le contrazioni. Questo perché word_tokenizeusa il tokenizer standard,, TreebankWordTokenizerche divide le contrazioni (ad esempio can'ta ( ca, n't). Tuttavia n'tnon è alfanumerico e si perde nel processo.
Diego Ferri

1

Rimuovi la punteggiatura (rimuoverà. Così come parte della gestione della punteggiatura utilizzando il codice sottostante)

        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 

Ingresso / uscita campione:

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']


Grazie mille

1

Aggiungendo semplicemente alla soluzione di @rmalouf, questo non includerà alcun numero perché \ w + è 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!')

Questo crea un gettone per ogni lettera.
Rishabh Gupta

1

Puoi farlo in una riga senza nltk (python 3.x).

import string
string_text= string_text.translate(str.maketrans('','',string.punctuation))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.