Add markov chain text generator.

This commit is contained in:
Alexandr Bogomyakov
2020-02-07 17:24:10 +03:00
parent 7f1f542be2
commit 92686ed13d
6 changed files with 317 additions and 1 deletions

View File

@ -162,7 +162,11 @@ class DataBase:
def get_random_word(self, count=1, like="%"): def get_random_word(self, count=1, like="%"):
sql = "SELECT word FROM word WHERE word LIKE '%s' ORDER BY random() LIMIT %s" % (like, count) sql = "SELECT word FROM word WHERE word LIKE '%s' ORDER BY random() LIMIT %s" % (like, count)
print(sql) result = self.execute(sql)
return(result)
def get_random_message(self):
sql = "SELECT text FROM xxx_message ORDER BY RANDOM() LIMIT 1"
result = self.execute(sql) result = self.execute(sql)
return(result) return(result)

7
markov/__init__.py Normal file
View File

@ -0,0 +1,7 @@
"""Markov chain text generator"""
from .gen import MarkovChain
from .simple import get
__all__ = ["MarkovChain", "get"]

74
markov/gen.py Normal file
View File

@ -0,0 +1,74 @@
from .histograms import Dictogram
import random
from collections import deque
import re
class MarkovChain:
def __init__(self, text=str()):
self.length = 25
self.text = text
self.text_list = None
self._prepare_list()
self.model = self._gen_model()
def _prepare_list(self):
if isinstance(self.text, str):
self._clean_text()
self.text_list = self.text.split()
else:
raise TypeError
def _clean_text(self):
text = self.text.replace('','')
text = self.text.replace('«','')
text = self.text.replace('»','')
text = self.text.replace('(','')
text = self.text.replace(')','')
text = self.text.replace('.','')
text = self.text.replace(',','')
self.text = text
print(self.text)
def _gen_model(self):
data = self.text_list
if data is None:
raise TypeError
markov_model = dict()
for i in range(0, len(data)-1):
if data[i] in markov_model:
markov_model[data[i]].update([data[i+1]])
else:
markov_model[data[i]] = Dictogram([data[i+1]])
return markov_model
def _generate_random_start(self):
model = self.model
# Чтобы сгенерировать любое начальное слово, раскомментируйте строку:
return random.choice(list(model.keys()))
# Чтобы сгенерировать "правильное" начальное слово, используйте код ниже:
# Правильные начальные слова - это те, что являлись началом предложений в корпусе
if 'END' in model:
seed_word = 'END'
while seed_word == 'END':
seed_word = model['END'].return_weighted_random_word()
return seed_word
return random.choice(list(model.keys()))
def generate_random_sentence(self):
markov_model = self.model
length = self.length if len(self.text_list) > self.length else len(self.text_list) - 1
current_word = self._generate_random_start()
sentence = [current_word]
for i in range(0, length):
try:
current_dictogram = markov_model[current_word]
random_weighted_word = current_dictogram.return_weighted_random_word()
current_word = random_weighted_word
sentence.append(current_word)
except KeyError:
pass
sentence[0] = sentence[0].capitalize()
return ' '.join(sentence) + '.'

49
markov/histograms.py Normal file
View File

@ -0,0 +1,49 @@
import random
class Dictogram(dict):
def __init__(self, iterable=None):
# Инициализируем наше распределение как новый объект класса,
# добавляем имеющиеся элементы
super(Dictogram, self).__init__()
self.types = 0 # число уникальных ключей в распределении
self.tokens = 0 # общее количество всех слов в распределении
if iterable:
self.update(iterable)
def update(self, iterable):
# Обновляем распределение элементами из имеющегося
# итерируемого набора данных
for item in iterable:
if item in self:
self[item] += 1
self.tokens += 1
else:
self[item] = 1
self.types += 1
self.tokens += 1
def count(self, item):
# Возвращаем значение счетчика элемента, или 0
if item in self:
return self[item]
return 0
def return_random_word(self):
random_key = random.sample(self, 1)
# Другой способ:
# random.choice(histogram.keys())
return random_key[0]
def return_weighted_random_word(self):
# Сгенерировать псевдослучайное число между 0 и (n-1),
# где n - общее число слов
random_int = random.randint(0, self.tokens-1)
index = 0
list_of_keys = list(self.keys())
# вывести 'случайный индекс:', random_int
for i in range(0, self.types):
index += self[list_of_keys[i]]
# вывести индекс
if(index > random_int):
# вывести list_of_keys[i]
return list_of_keys[i]

95
markov/simple.py Normal file
View File

@ -0,0 +1,95 @@
import random
from collections import deque
import re
class Dictogram(dict):
def __init__(self, iterable=None):
super(Dictogram, self).__init__()
self.types = 0
self.tokens = 0
if iterable:
self.update(iterable)
def update(self, iterable):
for item in iterable:
if item in self:
self[item] += 1
self.tokens += 1
else:
self[item] = 1
self.types += 1
self.tokens += 1
def count(self, item):
if item in self:
return self[item]
return 0
def return_random_word(self):
random_key = random.sample(self, 1)
return random_key[0]
def return_weighted_random_word(self):
random_int = random.randint(0, self.tokens-1)
index = 0
list_of_keys = list(self.keys())
for i in range(0, self.types):
index += self[list_of_keys[i]]
if(index > random_int):
return list_of_keys[i]
def get():
def generate_random_start(model):
if 'END' in model:
seed_word = 'END'
while seed_word == 'END':
seed_word = model['END'].return_weighted_random_word()
return seed_word
return random.choice(list(model.keys()))
def generate_random_sentence(length, markov_model):
current_word = generate_random_start(markov_model)
sentence = [current_word]
for i in range(0, length):
current_dictogram = markov_model[current_word]
random_weighted_word = current_dictogram.return_weighted_random_word()
current_word = random_weighted_word
sentence.append(current_word)
sentence[0] = sentence[0].capitalize()
return ' '.join(sentence) + '.'
return sentence
def make_markov_model(data):
markov_model = dict()
for i in range(0, len(data)-1):
if data[i] in markov_model:
markov_model[data[i]].update([data[i+1]])
else:
markov_model[data[i]] = Dictogram([data[i+1]])
return markov_model
text = """
Олег Соколов, преподававший в СПбГУ, в ноябре был задержан в Петербурге,
в его рюкзаке обнаружили две отпиленные женские руки. Соколов признался,
что убил и расчленил свою бывшую студентку Анастасию Ещенко, с которой
его связывали близкие отношения. Адвокат Соколова Александр Почуев заявлял,
что не исключает «и версию самооговора» его подзащитного и иные версии
преступления, «вплоть до мистических»."""
# simple cleanup
text = text.replace('','')
text = text.replace('«','')
text = text.replace('»','')
text = text.replace('(','')
text = text.replace(')','')
text = "START " + text
text = text.replace('.', ' END')
text_list = text.split()
model = make_markov_model(text_list)
generated = generate_random_sentence(50, model)
generated = generated.replace(' END', '.')
print(generated)

87
simple.py Normal file
View File

@ -0,0 +1,87 @@
import random
from collections import deque
import re
class Dictogram(dict):
def __init__(self, iterable=None):
super(Dictogram, self).__init__()
self.types = 0
self.tokens = 0
if iterable:
self.update(iterable)
def update(self, iterable):
for item in iterable:
if item in self:
self[item] += 1
self.tokens += 1
else:
self[item] = 1
self.types += 1
self.tokens += 1
def count(self, item):
if item in self:
return self[item]
return 0
def return_random_word(self):
random_key = random.sample(self, 1)
return random_key[0]
def return_weighted_random_word(self):
random_int = random.randint(0, self.tokens-1)
index = 0
list_of_keys = list(self.keys())
for i in range(0, self.types):
index += self[list_of_keys[i]]
if(index > random_int):
return list_of_keys[i]
def get(text):
def generate_random_start(model):
if 'END' in model:
seed_word = 'END'
while seed_word == 'END':
seed_word = model['END'].return_weighted_random_word()
return seed_word
return random.choice(list(model.keys()))
def generate_random_sentence(length, markov_model):
current_word = generate_random_start(markov_model)
sentence = [current_word]
for i in range(0, length):
current_dictogram = markov_model[current_word]
random_weighted_word = current_dictogram.return_weighted_random_word()
current_word = random_weighted_word
sentence.append(current_word)
sentence[0] = sentence[0].capitalize()
return ' '.join(sentence) + '.'
return sentence
def make_markov_model(data):
markov_model = dict()
for i in range(0, len(data)-1):
if data[i] in markov_model:
markov_model[data[i]].update([data[i+1]])
else:
markov_model[data[i]] = Dictogram([data[i+1]])
return markov_model
# simple cleanup
text = text.replace('','')
text = text.replace('«','')
text = text.replace('»','')
text = text.replace('(','')
text = text.replace(')','')
text = "START " + text
text = text.replace('.', ' END')
text_list = text.split()
model = make_markov_model(text_list)
generated = generate_random_sentence(50, model)
generated = generated.replace(' END', '.')
print(generated)