Как да извлечем знак ngram от изречения? - питон

Следната функция word2ngrams извлича знак 3 грама от дума:

>>> x = 'foobar'
>>> n = 3
>>> [x[i:i+n] for i in range(len(x)-n+1)]
['foo', 'oob', 'oba', 'bar']

Тази публикация показва извличането на символни ngrams за една дума, Бързо внедряване на знак n-грами с помощта на python.

Но какво, ако имам изречения и искам да извлека знака ngrams, има ли по-бърз метод, различен от итеративно извикване на word2ngram()?

Каква ще бъде версията на регулярния израз за постигане на същия изход word2ngram и sent2ngram? ще бъде ли по-бързо?

Опитах:

import string, random, time
from itertools import chain

def word2ngrams(text, n=3):
  """ Convert word into character ngrams. """
  return [text[i:i+n] for i in range(len(text)-n+1)]

def sent2ngrams(text, n=3):
    return list(chain(*[word2ngrams(i,n) for i in text.lower().split()]))

def sent2ngrams_simple(text, n=3):
    text = text.lower()
    return [text[i:i+n] for i in range(len(text)-n+1) if not " " in text[i:i+n]]

# Generate 10000 random strings of length 100.
sents = [" ".join([''.join(random.choice(string.ascii_uppercase) for j in range(10)) for i in range(100)]) for k in range(100)]

start = time.time()
x = [sent2ngrams(i) for i in sents]
print time.time() - start        

start = time.time()
y = [sent2ngrams_simple(i) for i in sents]
print time.time() - start        

print x==y

[вън]:

0.0205280780792
0.0271739959717
True

РЕДАКТИРАНО

Методът regex изглежда елегантен, но работи по-бавно от итеративното извикване на word2ngram():

import string, random, time, re
from itertools import chain

def word2ngrams(text, n=3):
  """ Convert word into character ngrams. """
  return [text[i:i+n] for i in range(len(text)-n+1)]

def sent2ngrams(text, n=3):
    return list(chain(*[word2ngrams(i,n) for i in text.lower().split()]))

def sent2ngrams_simple(text, n=3):
    text = text.lower()
    return [text[i:i+n] for i in range(len(text)-n+1) if not " " in text[i:i+n]]

def sent2ngrams_regex(text, n=3):
    rgx = '(?=('+'\S'*n+'))'
    return re.findall(rgx,text)

# Generate 10000 random strings of length 100.
sents = [" ".join([''.join(random.choice(string.ascii_uppercase) for j in range(10)) for i in range(100)]) for k in range(100)]

start = time.time()
x = [sent2ngrams(i) for i in sents]
print time.time() - start        

start = time.time()
y = [sent2ngrams_simple(i) for i in sents]
print time.time() - start        

start = time.time()
z = [sent2ngrams_regex(i) for i in sents]
print time.time() - start  

print x==y==z

[вън]:

0.0211708545685
0.0284190177917
0.0303599834442
True

person alvas    schedule 15.03.2014    source източник


Отговори (1)


Защо не просто (?=(...))

редактиране Същото нещо, но не празно пространство (?=(\S\S\S))
редактиране2 Можете също да използвате каквото искате. Пр. използва само буква (?=([^\W_]{3}))

Използва поглед напред за улавяне на 3 знака. След това двигателят повишава позицията 1 път на всяко
съвпадение. След това заснема следващите 3.

Резултатът от foobar е
foo
oob
oba
лента

 # Compressed regex
 #  (?=(...))

 # Expanded regex
 (?=                   # Start Lookahead assertion
      (                     # Capture group 1 start
           .                     # dot - metachar, matches any character except newline
           .                     # dot - metachar
           .                     # dot - metachar
      )                     # Capture group 1 end
 )                     # End Lookahead assertion
person Community    schedule 15.03.2014
comment
извинете за глупостта ми, какво е (?=(...))? можете ли да дадете работещ пример? Опитах: (?=('foobar')), но получих синтактична грешка. - person alvas; 15.03.2014
comment
Добавени са някои коментари, (?=(...)) е регулярният израз. Не познавам Python, но трябва да се използва като регулярен израз в контекста на съвпадение на всички (получаване на изходен масив). - person ; 15.03.2014
comment
re.findall(r'(?=(...))','foobar'), излязъл: ['foo', 'oob', 'oba', 'bar']. - person alvas; 15.03.2014
comment
Cool ... Това ли търсите? - person ; 15.03.2014
comment
да, нека опитам да го профилирам и да видя колко време ще спестя =). Благодаря за точния трик с регулярен израз. Има ли начин да не проверявате за " "? - person alvas; 15.03.2014
comment
Така че, не празно пространство тогава. Чакай малко. - person ; 15.03.2014
comment
Да, същото нещо, без интервали (?=(\S\S\S)) - person ; 15.03.2014
comment
@sin, това е хубав регулярен израз, но работи много по-бавно от повторението през отделни думи =( - person alvas; 16.03.2014
comment
@alvas - Забавянето може да бъде rgx = трябва да се компилира само веднъж, а не за всяко изречение. Той трябва да бъде предварително компилиран преди итерацията. Можете също така да подобрите скоростта на регулярния израз с %10-15, ако активно местите позицията на съвпадение. т.е. /(?=(\S\S\S))./ Добавете модификатора Dot-All (същото като /(?=(\S\S\S))[\S\s]/ или /(?s)(?=(\S\S\S))./). - person ; 17.03.2014
comment
Това реши моя проблем; без дори да се доближава до вашия подход! Благодаря! - person Akbar Hussein; 05.05.2020