파이썬에서 n 그램, 4, 5, 6 그램?


137

텍스트를 n 그램으로 나누는 방법을 찾고 있습니다. 일반적으로 다음과 같은 작업을 수행합니다.

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

nltk는 bigram과 trigram 만 제공하지만 텍스트를 4 그램, 5 그램 또는 심지어 100 그램으로 분할하는 방법이 있습니까?

감사!


텍스트를 단어 또는 문자별로 n 크기의 그룹으로 나누시겠습니까? 위의 출력 결과를 보여줄 수 있습니까?
ChrisProsser

4
nltk를 수행하지는 않았지만 ingrams두 번째 매개 변수가 원하는 ngram의 정도 인 함수가있는 것처럼 보입니다 . 인가 이건 당신이 사용하는 NLTK의 버전? 심지어하지 않을 경우, 여기입니다 소스 편집 :이 ngrams하고 ingrams, 거기에 ingrams발전기 것.
Brian

이 스레드에는 다음과 같은 답변이 있습니다. stackoverflow.com/questions/7591258/fast-n-gram-calculation
ChrisProsser

답변:


212

다른 사용자가 제공 한 훌륭한 파이썬 기반 답변. 그러나 여기에 nltk접근 방식이 있습니다 (이 경우 nltk라이브러리에 이미 존재하는 것을 재발 명하기 위해 OP가 처벌됩니다 ).

있습니다 N- 그램 모듈 사람들이 거의에서 사용하지 그게 nltk. ngram을 읽기가 어렵 기 때문이 아니라 n> 3으로 ngram을 기반으로 모델 기반을 학습하면 데이터가 많이 희박 해집니다.

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
: 문자 ngrams를 들어,도 봐주세요 stackoverflow.com/questions/22428020/...
alvas

N-gram을 사용하여 txt와 같은 전체 문서를 검사하는 방법이 있습니까? 파이썬에 익숙하지 않으므로 txt 파일을 열고 N-gram 분석을 사용하여 확인할 수 있는지 여부를 알 수 없습니다.
maoyi

1
누군가의 정확성을 테스트하는 방법에 대해 언급 할 수 있습니까 sixgrams?
LYu

64

아직 표시되지 않은 것에 놀랐습니다.

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

이것이 정확히 첫 번째 답에서 주파수 카운팅과 튜플 변환을 뺀 것입니다.
Brian

그래도 이해력으로 다시 작성되는 것이 더 좋습니다.
Brian

@amirouche : 잘 잡았습니다. 버그 보고서에 감사드립니다. 이제 수정되었습니다
inspectorG4dget

16

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]

출력 예

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

배열 형식으로 ngram을 유지하려면 제거하십시오. ' '.join


15

여기에 n 그램을 수행하는 또 다른 간단한 방법이 있습니다.

>>> 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
nltk.word_tokenize () 함수를 사용하려면 nltk.download ( 'punkt')를 수행해야했습니다. 또한 결과를 인쇄하려면 bigrams, trigrams 및 fourgrams와 같은 생성기 객체를 list (<genrator_object>)를 사용하여 목록으로 변환해야했습니다.
bhatman

11

사람들은 이미 bigrams 또는 trigrams가 필요한 시나리오에 대해 꽤 잘 대답했지만이 경우 문장에 대한 everygram 이 필요한 경우 사용할 수 있습니다nltk.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')]

최대 길이가 3이어야하는 트라이 그램의 경우와 같이 제한이있는 경우 max_len 매개 변수를 사용하여 지정할 수 있습니다.

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

max_len 매개 변수를 수정하여 4 그램, 5 그램, 6 그램 또는 심지어 100 그램을 달성 할 수 있습니다.

위에서 언급 한 솔루션은 위에서 언급 한 솔루션을 구현하도록 수정 될 수 있지만이 솔루션은 그보다 훨씬 간단합니다.

자세한 내용은 여기를 클릭 하십시오

그리고 bigram 또는 trigram 등과 같은 특정 그램이 필요할 때 MAHassan의 답변에 언급 된 것처럼 nltk.util.ngrams 를 사용할 수 있습니다 .


6

다음을 사용하여 자신의 기능을 쉽게 채울 수 있습니다 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
izip(*(islice(seq, index, None) for index, seq in enumerate(tee(s, N))))내가 잘 이해하지 못한다고 설명해 주시겠습니까 ?
TomazStoiljkovic 2014

4

python 's builtin으로 bigram을 빌드하는보다 우아한 접근 방식 zip(). 로 원래 문자열을 목록으로 변환 한 split()다음 목록을 정상적으로 한 번 전달하고 한 요소 만 오프셋하면됩니다.

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

나는 nltk를 다루지 않았지만 소규모 프로젝트의 일부로 N-gram을 수행했습니다. 문자열에서 발생하는 모든 N 그램의 빈도를 찾으려면 다음과 같이하십시오. D당신에게 당신의 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))try-except보다 빠르게 작동합니다
inspectorG4dget

2

four_grams는 이미 NLTK에 있으며, 여기에 도움이되는 코드가 있습니다.

 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

도움이 되길 바랍니다.


2

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

출력 :

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

ngram_size양의 정수로 설정할 수 있습니다 . 즉, 텍스트를 4 그램, 5 그램 또는 심지어 100 그램으로 나눌 수 있습니다.


2

효율성이 문제이고 여러 다른 n 그램 (최대 100 개)을 작성해야하지만 순수한 파이썬을 사용하려면 다음을 수행하십시오.

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

사용법 :

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

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

이전 답변 에서 다시 게시하십시오 .


0

Nltk는 훌륭하지만 때로는 일부 프로젝트의 오버 헤드입니다.

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

사용 예 :

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

아래의 다른 패키지없이 코드를 사용하여 4-6 그램을 모두 얻을 수 있습니다.

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)

출력은 아래와 같습니다 :

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

블로그 에서 자세한 내용을 확인할 수 있습니다


0

약 7 년 후 다음을 사용하여보다 우아한 답변을 얻을 수 있습니다 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']

산출:

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

일정한 메모리 사용으로 큰 문자열을위한 순수한 반복자 솔루션을 원한다면 :

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

테스트:

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

산출:

['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']
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.