n-gram trong trăn, bốn, năm, sáu gram?


136

Tôi đang tìm cách chia văn bản thành n-gram. Thông thường tôi sẽ làm một cái gì đó như:

import nltk
from nltk import bigrams
string = "I really like python, it's pretty awesome."
string_bigrams = bigrams(string)
print string_bigrams

Tôi biết rằng nltk chỉ cung cấp bigram và trigram, nhưng có cách nào để chia văn bản của tôi thành bốn gram, năm gram hoặc thậm chí trăm gram không?

Cảm ơn!


Bạn có muốn văn bản chia thành các nhóm có kích thước n theo từ hoặc ký tự không? Bạn có thể đưa ra một ví dụ về đầu ra sẽ như thế nào cho ở trên?
ChrisProsser

4
Không bao giờ thực hiện nltk nhưng có vẻ như có một hàm ingramscó tham số thứ hai là mức độ của ngrams bạn muốn. Là NÀY phiên bản của NLTK bạn đang sử dụng? Thậm chí nếu không, đây là EDIT nguồn : Có ngramsingramstrong đó, ingramslà một máy phát điện.
Brian

Ngoài ra còn có một câu trả lời theo chủ đề này có thể hữu ích: stackoverflow.com/questions/7591258/fast-n-gram-calculation
ChrisProsser

Câu trả lời:


210

Câu trả lời tuyệt vời dựa trên python được đưa ra bởi người dùng khác. Nhưng đây là nltkcách tiếp cận (chỉ trong trường hợp, OP bị phạt vì phát minh lại những gì đã có trong nltkthư viện).

Có một mô-đun ngram mà mọi người hiếm khi sử dụng nltk. Không phải vì khó đọc ngrams, nhưng đào tạo một mô hình dựa trên ngrams trong đó n> 3 sẽ dẫn đến nhiều dữ liệu thưa thớt.

from nltk import ngrams

sentence = 'this is a foo bar sentences and i want to ngramize it'

n = 6
sixgrams = ngrams(sentence.split(), n)

for grams in sixgrams:
  print grams

4
Đối với ngrams nhân vật, vui lòng xem tại: stackoverflow.com/questions/22428020/NH
alvas

Có cách nào để sử dụng N-gram để kiểm tra toàn bộ tài liệu như txt không? Tôi không quen thuộc với Python vì vậy tôi không biết liệu nó có thể mở tệp txt không và sau đó sử dụng phân tích N-gram để kiểm tra?
maoyi

1
Ai đó có thể nhận xét về cách kiểm tra độ chính xác của sixgrams?
LYu

64

Tôi ngạc nhiên khi điều này chưa xuất hiện:

In [34]: sentence = "I really like python, it's pretty awesome.".split()

In [35]: N = 4

In [36]: grams = [sentence[i:i+N] for i in xrange(len(sentence)-N+1)]

In [37]: for gram in grams: print gram
['I', 'really', 'like', 'python,']
['really', 'like', 'python,', "it's"]
['like', 'python,', "it's", 'pretty']
['python,', "it's", 'pretty', 'awesome.']

Đó chính xác là những gì câu trả lời đầu tiên trừ đi việc đếm tần số và chuyển đổi tuple.
Brian

Nó là tốt hơn để xem nó viết lại như là một sự hiểu biết mặc dù.
Brian

@amirouche: bắt tốt. Cảm ơn các báo cáo lỗi. Nó đã được sửa ngay bây giờ
thanh

16

Chỉ sử dụng các công cụ nltk

from nltk.tokenize import word_tokenize
from nltk.util import ngrams

def get_ngrams(text, n ):
    n_grams = ngrams(word_tokenize(text), n)
    return [ ' '.join(grams) for grams in n_grams]

Ví dụ đầu ra

get_ngrams('This is the simplest text i could think of', 3 )

['This is the', 'is the simplest', 'the simplest text', 'simplest text i', 'text i could', 'i could think', 'could think of']

Để giữ ngrams ở định dạng mảng, chỉ cần xóa ' '.join


15

Đây là một cách đơn giản khác để làm n-gram

>>> from nltk.util import ngrams
>>> text = "I am aware that nltk only offers bigrams and trigrams, but is there a way to split my text in four-grams, five-grams or even hundred-grams"
>>> tokenize = nltk.word_tokenize(text)
>>> tokenize
['I', 'am', 'aware', 'that', 'nltk', 'only', 'offers', 'bigrams', 'and', 'trigrams', ',', 'but', 'is', 'there', 'a', 'way', 'to', 'split', 'my', 'text', 'in', 'four-grams', ',', 'five-grams', 'or', 'even', 'hundred-grams']
>>> bigrams = ngrams(tokenize,2)
>>> bigrams
[('I', 'am'), ('am', 'aware'), ('aware', 'that'), ('that', 'nltk'), ('nltk', 'only'), ('only', 'offers'), ('offers', 'bigrams'), ('bigrams', 'and'), ('and', 'trigrams'), ('trigrams', ','), (',', 'but'), ('but', 'is'), ('is', 'there'), ('there', 'a'), ('a', 'way'), ('way', 'to'), ('to', 'split'), ('split', 'my'), ('my', 'text'), ('text', 'in'), ('in', 'four-grams'), ('four-grams', ','), (',', 'five-grams'), ('five-grams', 'or'), ('or', 'even'), ('even', 'hundred-grams')]
>>> trigrams=ngrams(tokenize,3)
>>> trigrams
[('I', 'am', 'aware'), ('am', 'aware', 'that'), ('aware', 'that', 'nltk'), ('that', 'nltk', 'only'), ('nltk', 'only', 'offers'), ('only', 'offers', 'bigrams'), ('offers', 'bigrams', 'and'), ('bigrams', 'and', 'trigrams'), ('and', 'trigrams', ','), ('trigrams', ',', 'but'), (',', 'but', 'is'), ('but', 'is', 'there'), ('is', 'there', 'a'), ('there', 'a', 'way'), ('a', 'way', 'to'), ('way', 'to', 'split'), ('to', 'split', 'my'), ('split', 'my', 'text'), ('my', 'text', 'in'), ('text', 'in', 'four-grams'), ('in', 'four-grams', ','), ('four-grams', ',', 'five-grams'), (',', 'five-grams', 'or'), ('five-grams', 'or', 'even'), ('or', 'even', 'hundred-grams')]
>>> fourgrams=ngrams(tokenize,4)
>>> fourgrams
[('I', 'am', 'aware', 'that'), ('am', 'aware', 'that', 'nltk'), ('aware', 'that', 'nltk', 'only'), ('that', 'nltk', 'only', 'offers'), ('nltk', 'only', 'offers', 'bigrams'), ('only', 'offers', 'bigrams', 'and'), ('offers', 'bigrams', 'and', 'trigrams'), ('bigrams', 'and', 'trigrams', ','), ('and', 'trigrams', ',', 'but'), ('trigrams', ',', 'but', 'is'), (',', 'but', 'is', 'there'), ('but', 'is', 'there', 'a'), ('is', 'there', 'a', 'way'), ('there', 'a', 'way', 'to'), ('a', 'way', 'to', 'split'), ('way', 'to', 'split', 'my'), ('to', 'split', 'my', 'text'), ('split', 'my', 'text', 'in'), ('my', 'text', 'in', 'four-grams'), ('text', 'in', 'four-grams', ','), ('in', 'four-grams', ',', 'five-grams'), ('four-grams', ',', 'five-grams', 'or'), (',', 'five-grams', 'or', 'even'), ('five-grams', 'or', 'even', 'hundred-grams')]

1
Phải làm nltk.doad ('punkt') để sử dụng hàm nltk.word_tokenize (). Ngoài ra, để in kết quả phải chuyển đổi đối tượng trình tạo như bigram, trigram và bốn gram thành danh sách bằng cách sử dụng danh sách (<genrator_object>).
bhatman

11

Mọi người đã trả lời khá độc đáo cho kịch bản mà bạn cần bigram hoặc trigram nhưng nếu bạn cần mọi sơ đồ cho câu trong trường hợp đó bạn có thể sử dụngnltk.util.everygrams

>>> from nltk.util import everygrams

>>> message = "who let the dogs out"

>>> msg_split = message.split()

>>> list(everygrams(msg_split))
[('who',), ('let',), ('the',), ('dogs',), ('out',), ('who', 'let'), ('let', 'the'), ('the', 'dogs'), ('dogs', 'out'), ('who', 'let', 'the'), ('let', 'the', 'dogs'), ('the', 'dogs', 'out'), ('who', 'let', 'the', 'dogs'), ('let', 'the', 'dogs', 'out'), ('who', 'let', 'the', 'dogs', 'out')]

Trong trường hợp bạn có giới hạn như trong trường hợp bát quái trong đó độ dài tối đa phải là 3 thì bạn có thể sử dụng max_len param để chỉ định nó.

>>> list(everygrams(msg_split, max_len=2))
[('who',), ('let',), ('the',), ('dogs',), ('out',), ('who', 'let'), ('let', 'the'), ('the', 'dogs'), ('dogs', 'out')]

Bạn chỉ có thể sửa đổi thông số max_len để đạt được bất kỳ gram nào, ví dụ bốn gram, năm gram, sáu hoặc thậm chí trăm gram.

Các giải pháp được đề cập trước đây có thể được sửa đổi để thực hiện các giải pháp được đề cập ở trên nhưng giải pháp này còn đơn giản hơn thế nhiều.

Để đọc thêm bấm vào đây

Và khi bạn chỉ cần một gram cụ thể như bigram hoặc trigram, v.v., bạn có thể sử dụng nltk.util.ngrams như được đề cập trong câu trả lời của MAHassan.


6

Bạn có thể dễ dàng thực hiện chức năng của mình để thực hiện việc này bằng cách sử dụng itertools:

from itertools import izip, islice, tee
s = 'spam and eggs'
N = 3
trigrams = izip(*(islice(seq, index, None) for index, seq in enumerate(tee(s, N))))
list(trigrams)
# [('s', 'p', 'a'), ('p', 'a', 'm'), ('a', 'm', ' '),
# ('m', ' ', 'a'), (' ', 'a', 'n'), ('a', 'n', 'd'),
# ('n', 'd', ' '), ('d', ' ', 'e'), (' ', 'e', 'g'),
# ('e', 'g', 'g'), ('g', 'g', 's')]

1
Bạn có thể vui lòng giải thích izip(*(islice(seq, index, None) for index, seq in enumerate(tee(s, N))))tôi không hiểu lắm.
TomazStoiljkovic

4

Một cách tiếp cận thanh lịch hơn để xây dựng các bigram với nội dung của python zip(). Chỉ cần chuyển đổi chuỗi gốc thành một danh sách theo split(), sau đó chuyển danh sách một lần bình thường và một lần được bù bởi một phần tử.

string = "I really like python, it's pretty awesome."

def find_bigrams(s):
    input_list = s.split(" ")
    return zip(input_list, input_list[1:])

def find_ngrams(s, n):
  input_list = s.split(" ")
  return zip(*[input_list[i:] for i in range(n)])

find_bigrams(string)

[('I', 'really'), ('really', 'like'), ('like', 'python,'), ('python,', "it's"), ("it's", 'pretty'), ('pretty', 'awesome.')]

2

Tôi chưa bao giờ giao dịch với nltk nhưng đã thực hiện N-gram như một phần của dự án lớp nhỏ. Nếu bạn muốn tìm tần số của tất cả N-gram xảy ra trong chuỗi, đây là một cách để làm điều đó. Dsẽ cung cấp cho bạn biểu đồ của các từ N của bạn.

D = dict()
string = 'whatever string...'
strparts = string.split()
for i in range(len(strparts)-N): # N-grams
    try:
        D[tuple(strparts[i:i+N])] += 1
    except:
        D[tuple(strparts[i:i+N])] = 1

collections.Counter(tuple(strparts[i:i+N]) for i in xrange(len(strparts)-N))sẽ hoạt động nhanh hơn so với thử ngoại trừ
thanh

2

Đối với bốn_gram đã có trong NLTK , đây là một đoạn mã có thể giúp bạn hướng tới điều này:

 from nltk.collocations import *
 import nltk
 #You should tokenize your text
 text = "I do not like green eggs and ham, I do not like them Sam I am!"
 tokens = nltk.wordpunct_tokenize(text)
 fourgrams=nltk.collocations.QuadgramCollocationFinder.from_words(tokens)
 for fourgram, freq in fourgrams.ngram_fd.items():  
       print fourgram, freq

Tôi hy vọng nó sẽ giúp.


2

Bạn có thể sử dụng sklearn.feature_extraction.text.CountVectorizer :

import sklearn.feature_extraction.text # FYI http://scikit-learn.org/stable/install.html
ngram_size = 4
string = ["I really like python, it's pretty awesome."]
vect = sklearn.feature_extraction.text.CountVectorizer(ngram_range=(ngram_size,ngram_size))
vect.fit(string)
print('{1}-grams: {0}'.format(vect.get_feature_names(), ngram_size))

đầu ra:

4-grams: [u'like python it pretty', u'python it pretty awesome', u'really like python it']

Bạn có thể đặt thành ngram_sizebất kỳ số nguyên dương. Tức là bạn có thể chia một văn bản trong bốn gram, năm gram hoặc thậm chí trăm gram.


2

Nếu hiệu quả là một vấn đề và bạn phải xây dựng nhiều n-gram khác nhau (lên đến một trăm như bạn nói), nhưng bạn muốn sử dụng python thuần tôi sẽ làm:

from itertools import chain

def n_grams(seq, n=1):
    """Returns an itirator over the n-grams given a listTokens"""
    shiftToken = lambda i: (el for j,el in enumerate(seq) if j>=i)
    shiftedTokens = (shiftToken(i) for i in range(n))
    tupleNGrams = zip(*shiftedTokens)
    return tupleNGrams # if join in generator : (" ".join(i) for i in tupleNGrams)

def range_ngrams(listTokens, ngramRange=(1,2)):
    """Returns an itirator over all n-grams for n in range(ngramRange) given a listTokens."""
    return chain(*(n_grams(listTokens, i) for i in range(*ngramRange)))

Sử dụng :

>>> input_list = input_list = 'test the ngrams generator'.split()
>>> list(range_ngrams(input_list, ngramRange=(1,3)))
[('test',), ('the',), ('ngrams',), ('generator',), ('test', 'the'), ('the', 'ngrams'), ('ngrams', 'generator'), ('test', 'the', 'ngrams'), ('the', 'ngrams', 'generator')]

~ Cùng tốc độ với NLTK:

import nltk
%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
nltk.ngrams(input_list,n=5)
# 7.02 ms ± 79 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
n_grams(input_list,n=5)
# 7.01 ms ± 103 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
nltk.ngrams(input_list,n=1)
nltk.ngrams(input_list,n=2)
nltk.ngrams(input_list,n=3)
nltk.ngrams(input_list,n=4)
nltk.ngrams(input_list,n=5)
# 7.32 ms ± 241 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
range_ngrams(input_list, ngramRange=(1,6))
# 7.13 ms ± 165 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Đăng lại từ câu trả lời trước của tôi .


0

Nltk là tuyệt vời, nhưng đôi khi là một chi phí cho một số dự án:

import re
def tokenize(text, ngrams=1):
    text = re.sub(r'[\b\(\)\\\"\'\/\[\]\s+\,\.:\?;]', ' ', text)
    text = re.sub(r'\s+', ' ', text)
    tokens = text.split()
    return [tuple(tokens[i:i+ngrams]) for i in xrange(len(tokens)-ngrams+1)]

Ví dụ sử dụng:

>> text = "This is an example text"
>> tokenize(text, 2)
[('This', 'is'), ('is', 'an'), ('an', 'example'), ('example', 'text')]
>> tokenize(text, 3)
[('This', 'is', 'an'), ('is', 'an', 'example'), ('an', 'example', 'text')]

0

Bạn có thể nhận được tất cả 4-6 gram bằng cách sử dụng mã mà không cần gói khác bên dưới:

from itertools import chain

def get_m_2_ngrams(input_list, min, max):
    for s in chain(*[get_ngrams(input_list, k) for k in range(min, max+1)]):
        yield ' '.join(s)

def get_ngrams(input_list, n):
    return zip(*[input_list[i:] for i in range(n)])

if __name__ == '__main__':
    input_list = ['I', 'am', 'aware', 'that', 'nltk', 'only', 'offers', 'bigrams', 'and', 'trigrams', ',', 'but', 'is', 'there', 'a', 'way', 'to', 'split', 'my', 'text', 'in', 'four-grams', ',', 'five-grams', 'or', 'even', 'hundred-grams']
    for s in get_m_2_ngrams(input_list, 4, 6):
        print(s)

đầu ra dưới đây:

I am aware that
am aware that nltk
aware that nltk only
that nltk only offers
nltk only offers bigrams
only offers bigrams and
offers bigrams and trigrams
bigrams and trigrams ,
and trigrams , but
trigrams , but is
, but is there
but is there a
is there a way
there a way to
a way to split
way to split my
to split my text
split my text in
my text in four-grams
text in four-grams ,
in four-grams , five-grams
four-grams , five-grams or
, five-grams or even
five-grams or even hundred-grams
I am aware that nltk
am aware that nltk only
aware that nltk only offers
that nltk only offers bigrams
nltk only offers bigrams and
only offers bigrams and trigrams
offers bigrams and trigrams ,
bigrams and trigrams , but
and trigrams , but is
trigrams , but is there
, but is there a
but is there a way
is there a way to
there a way to split
a way to split my
way to split my text
to split my text in
split my text in four-grams
my text in four-grams ,
text in four-grams , five-grams
in four-grams , five-grams or
four-grams , five-grams or even
, five-grams or even hundred-grams
I am aware that nltk only
am aware that nltk only offers
aware that nltk only offers bigrams
that nltk only offers bigrams and
nltk only offers bigrams and trigrams
only offers bigrams and trigrams ,
offers bigrams and trigrams , but
bigrams and trigrams , but is
and trigrams , but is there
trigrams , but is there a
, but is there a way
but is there a way to
is there a way to split
there a way to split my
a way to split my text
way to split my text in
to split my text in four-grams
split my text in four-grams ,
my text in four-grams , five-grams
text in four-grams , five-grams or
in four-grams , five-grams or even
four-grams , five-grams or even hundred-grams

bạn có thể tìm thêm chi tiết trên blog này


0

Sau khoảng bảy năm, đây là một câu trả lời thanh lịch hơn bằng cách sử dụng collections.deque:

def ngrams(words, n):
    d = collections.deque(maxlen=n)
    d.extend(words[:n])
    words = words[n:]
    for window, word in zip(itertools.cycle((d,)), words):
        print(' '.join(window))
        d.append(word)

words = ['I', 'am', 'become', 'death,', 'the', 'destroyer', 'of', 'worlds']

Đầu ra:

In [15]: ngrams(words, 3)                                                                                                                                                                                                                     
I am become
am become death,
become death, the
death, the destroyer
the destroyer of

In [16]: ngrams(words, 4)                                                                                                                                                                                                                     
I am become death,
am become death, the
become death, the destroyer
death, the destroyer of

In [17]: ngrams(words, 1)                                                                                                                                                                                                                     
I
am
become
death,
the
destroyer
of

In [18]: ngrams(words, 2)                                                                                                                                                                                                                     
I am
am become
become death,
death, the
the destroyer
destroyer of

0

Nếu bạn muốn một giải pháp lặp thuần cho các chuỗi lớn với mức sử dụng bộ nhớ không đổi:

from typing import Iterable  
import itertools

def ngrams_iter(input: str, ngram_size: int, token_regex=r"[^\s]+") -> Iterable[str]:
    input_iters = [ 
        map(lambda m: m.group(0), re.finditer(token_regex, input)) 
        for n in range(ngram_size) 
    ]
    # Skip first words
    for n in range(1, ngram_size): list(map(next, input_iters[n:]))  

    output_iter = itertools.starmap( 
        lambda *args: " ".join(args),  
        zip(*input_iters) 
    ) 
    return output_iter

Kiểm tra:

input = "If you want a pure iterator solution for large strings with constant memory usage"
list(ngrams_iter(input, 5))

Đầu ra:

['If you want a pure',
 'you want a pure iterator',
 'want a pure iterator solution',
 'a pure iterator solution for',
 'pure iterator solution for large',
 'iterator solution for large strings',
 'solution for large strings with',
 'for large strings with constant',
 'large strings with constant memory',
 'strings with constant memory usage']
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.