n- กรัมในหลามสี่ห้าหกกรัม?


142

ฉันกำลังหาวิธีแบ่งข้อความเป็น n-grams โดยปกติฉันจะทำสิ่งที่ชอบ:

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

ฉันทราบว่า nltk ให้บริการเฉพาะ bigrams และ trigrams แต่มีวิธีแบ่งข้อความของฉันเป็นสี่กรัมห้ากรัมหรือร้อยกรัมหรือไม่

ขอบคุณ!


คุณต้องการให้ข้อความแบ่งออกเป็นกลุ่มขนาด n ตามคำหรืออักขระ? คุณช่วยยกตัวอย่างได้ไหมว่าผลลัพธ์ข้างต้นควรมีลักษณะอย่างไร
ChrisProsser

4
ไม่เคยทำ nltk แต่ดูเหมือนว่าจะมีฟังก์ชันingramsที่มีพารามิเตอร์ที่สองคือระดับของ ngrams ที่คุณต้องการ คือนี้รุ่นของ nltk คุณกำลังใช้? แม้ว่าจะไม่ก็ตามนี่คือแหล่งที่มา EDIT: มีngramsและingramsอยู่ในนั้นingramsเป็นเครื่องกำเนิดไฟฟ้า
Brian

นอกจากนี้ยังมีคำตอบในหัวข้อนี้ที่อาจเป็นประโยชน์: stackoverflow.com/questions/7591258/fast-n-gram-calculation
ChrisProsser

คำตอบ:


220

คำตอบจาก Python ดั้งเดิมที่ยอดเยี่ยมจากผู้ใช้รายอื่น แต่นี่คือnltkแนวทาง (ในกรณีนี้ OP จะถูกลงโทษสำหรับการคิดค้นสิ่งที่มีอยู่แล้วในnltkห้องสมุดใหม่)

มีความเป็นโมดูล ngramnltkที่คนไม่ค่อยใช้ในการ ไม่ใช่เพราะว่ามันยากที่จะอ่าน ngrams แต่การฝึก model base บน ngrams โดยที่ n> 3 จะทำให้ข้อมูลกระจัดกระจายมาก

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 ฉันไม่คุ้นเคยกับ Python ดังนั้นฉันจึงไม่รู้ว่ามันสามารถเปิดไฟล์ txt จากนั้นใช้การวิเคราะห์ N-gram เพื่อตรวจสอบได้หรือไม่?
maoyi

1
ใครสามารถแสดงความคิดเห็นเกี่ยวกับวิธีทดสอบความถูกต้องของsixgrams?
LYu

65

ฉันแปลกใจที่สิ่งนี้ยังไม่ปรากฏขึ้น:

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

17

ใช้เครื่องมือ 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']

เพื่อให้ ngrams อยู่ในรูปแบบอาร์เรย์เพียงแค่ลบ ' '.join


15

นี่เป็นอีกวิธีง่ายๆในการทำ n-grams

>>> 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.download ('punkt') เพื่อใช้ฟังก์ชัน nltk.word_tokenize () นอกจากนี้ในการพิมพ์ผลลัพธ์ต้องแปลงวัตถุเครื่องกำเนิดไฟฟ้าเช่น bigrams, trigrams และ fourgrams เพื่อแสดงรายการโดยใช้ list (<genrator_object>)
im_bhatman

13

มีคนตอบค่อนข้างดีสำหรับสถานการณ์ที่คุณต้องการ 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')]

ในกรณีที่คุณมีขีด จำกัด เช่นในกรณีของ Trigrams ซึ่งความยาวสูงสุดควรเป็น 3 คุณสามารถใช้ max_len param เพื่อระบุได้

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

คุณสามารถแก้ไข max_len param เพื่อให้ได้กรัมใด ๆ เช่นสี่กรัมห้ากรัมหกหรือร้อยกรัม

โซลูชันที่กล่าวถึงก่อนหน้านี้สามารถแก้ไขได้เพื่อใช้โซลูชันที่กล่าวถึงข้างต้น แต่โซลูชันนี้ตรงไปตรงมากว่านั้นมาก

อ่านเพิ่มเติมคลิกที่นี่

และเมื่อคุณต้องการกรัมเฉพาะเช่น bigram หรือ trigram เป็นต้นคุณสามารถใช้nltk.util.ngramsตามที่กล่าวไว้ในคำตอบของ MAHassan


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

4

วิธีที่สง่างามมากขึ้นเพื่อสร้าง bigrams กับ zip()builtin เพียงแค่แปลงสตริงเดิมเป็นรายการโดย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-G ทั้งหมดที่เกิดขึ้นในสตริงนี่คือวิธีการ 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เป็นจำนวนเต็มบวกใดก็ได้ กล่าวคือคุณสามารถแบ่งข้อความออกเป็นสี่กรัมห้ากรัมหรือร้อยกรัม


2

หากประสิทธิภาพเป็นปัญหาและคุณต้องสร้าง n-g หลาย ๆ ตัว (ไม่เกินหนึ่งร้อยตามที่คุณพูด) แต่คุณต้องการใช้ python บริสุทธิ์ฉันจะทำ:

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)

repost จากฉันคำตอบก่อนหน้า


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-6gram ทั้งหมดโดยใช้รหัสโดยไม่มีแพ็คเกจอื่นด้านล่าง:

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

หลังจากผ่านไปประมาณเจ็ดปีนี่คือคำตอบที่สวยงามยิ่งขึ้นโดยใช้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.