From 92686ed13d13ecd49566183c71be7832f3d6dc7b Mon Sep 17 00:00:00 2001 From: Alexandr Bogomyakov Date: Fri, 7 Feb 2020 17:24:10 +0300 Subject: [PATCH] Add markov chain text generator. --- database.py | 6 ++- markov/__init__.py | 7 ++++ markov/gen.py | 74 ++++++++++++++++++++++++++++++++++ markov/histograms.py | 49 +++++++++++++++++++++++ markov/simple.py | 95 ++++++++++++++++++++++++++++++++++++++++++++ simple.py | 87 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 317 insertions(+), 1 deletion(-) create mode 100644 markov/__init__.py create mode 100644 markov/gen.py create mode 100644 markov/histograms.py create mode 100644 markov/simple.py create mode 100644 simple.py diff --git a/database.py b/database.py index d99920b..1f6ba46 100755 --- a/database.py +++ b/database.py @@ -162,7 +162,11 @@ class DataBase: def get_random_word(self, count=1, like="%"): 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) return(result) diff --git a/markov/__init__.py b/markov/__init__.py new file mode 100644 index 0000000..3d55048 --- /dev/null +++ b/markov/__init__.py @@ -0,0 +1,7 @@ +"""Markov chain text generator""" + +from .gen import MarkovChain +from .simple import get + +__all__ = ["MarkovChain", "get"] + diff --git a/markov/gen.py b/markov/gen.py new file mode 100644 index 0000000..38379f4 --- /dev/null +++ b/markov/gen.py @@ -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) + '.' diff --git a/markov/histograms.py b/markov/histograms.py new file mode 100644 index 0000000..c0b2470 --- /dev/null +++ b/markov/histograms.py @@ -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] diff --git a/markov/simple.py b/markov/simple.py new file mode 100644 index 0000000..42f73e5 --- /dev/null +++ b/markov/simple.py @@ -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) diff --git a/simple.py b/simple.py new file mode 100644 index 0000000..d7ae0ee --- /dev/null +++ b/simple.py @@ -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)