Выполнили Ковригин Александр и Панюхин Никита
Импортируем библиотеки и небольшая настройка
import os
import re
from typing import List, Tuple
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from dataclasses import dataclass, field
from IPython.display import display
from IPython.display import set_matplotlib_formats
from tqdm import tqdm_notebook as tqdm
%matplotlib inline
set_matplotlib_formats('svg')
Константы
NORM = 100000
DATA_DIRECTORY = "./data/"
FONT_SIZE = 6
DOT_SIZE = 10
COLOR_SCHEME = [
"#0000ff",
"#ff0000",
"#00ff00"
]
PHRASES_FILE = "_phrases.txt"
Функция поиска фразы в тексте
def find(phrase: str, text: str) -> int:
pattern = r"(\s|^)" + phrase + r"([\;\,\.\s\!\?\:\-]|$)"
return len(re.findall(pattern, text))
Считываем файл с фразами
with open(os.path.join(DATA_DIRECTORY, PHRASES_FILE), "r", encoding='utf-8') as file:
phrases = list(filter(lambda x: x, list(map(lambda x: x.strip().lower(), file.readlines()))))
phrases_count = len(phrases)
Класс результатов анализа с методами визуализации
@dataclass
class WordAnalysisResult:
total_values: np.array
target_values: np.array
filename: str = ''
delta: np.array = field(init=False)
def __post_init__(self):
self.delta = [abs(self.target_values[i] - self.total_values[i]) for i in range(phrases_count)]
def view_as_table(self):
nums = list(range(phrases_count))
indexes = [y for x, y in sorted(zip(self.delta, nums), reverse=True)]
data = np.array(np.array([(phrases[i],
self.total_values[i],
self.target_values[i],
self.delta[i]) for i in indexes]))
df = pd.DataFrame(data=data,
columns=['Word',
'Average frequency of word occurrence (occurrences/' + str(NORM) + ' words)',
'Target average frequency of word occurrence (occurrences/' + str(NORM) + ' words)',
'Delta (occurrences/' + str(NORM) + ' words)'])
df.name = 'Word occurrences difference in ' + self.filename
display(df)
return self
def view_as_plot(self):
def f(o):
return [y for x, y in sorted(zip(self.delta, o))]
sorted_total_values = f(self.total_values)
sorted_target_values = f(self.target_values)
sorted_phrases = f(phrases)
sorted_delta = sorted(self.delta)
plt.rc('font', size=FONT_SIZE) # controls default text sizes
plt.rc('axes', titlesize=FONT_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=8) # fontsize of the x and y labels
plt.rc('xtick', labelsize=FONT_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=FONT_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=FONT_SIZE) # legend fontsize
plt.rc('figure', titlesize=FONT_SIZE) # fontsize of the figure title
plt.xticks(rotation=-90)
plt.scatter(sorted_phrases, sorted_total_values, c=COLOR_SCHEME[0], s=DOT_SIZE).set_label('All works')
plt.scatter(sorted_phrases, sorted_target_values, c=COLOR_SCHEME[1], s=DOT_SIZE).set_label('Target work')
plt.scatter(sorted_phrases, sorted_delta, c=COLOR_SCHEME[2], s=DOT_SIZE).set_label('Delta')
plt.legend(loc="center left")
plt.show()
return self
Обработка текстов
def analyze(target_file: str, exclude: List[str] = None) -> WordAnalysisResult:
if exclude is None:
exclude = []
total_text_length = 0
target_text_length = 0
total_values = np.full(phrases_count, 0, float)
target_values = np.full(phrases_count, 0, float)
pbar = tqdm(os.listdir(DATA_DIRECTORY))
for filename in pbar:
if filename == "_phrases.txt":
continue
pbar.set_description("Processing %s" % filename)
with open(os.path.join(DATA_DIRECTORY, filename), "r", encoding='utf-8') as file:
text = file.read().strip().lower()
if filename in exclude:
continue
elif filename == target_file:
target_text_length += len(text.split())
for i in range(phrases_count):
if text.count(phrases[i]) != 0:
target_values[i] += find(phrases[i], text)
else:
total_text_length += len(text.split())
for i in range(phrases_count):
if text.count(phrases[i]) != 0:
total_values[i] += find(phrases[i], text)
for i in range(phrases_count):
total_values[i] *= len(phrases[i]) * NORM / total_text_length
target_values[i] *= len(phrases[i]) * NORM / target_text_length
return WordAnalysisResult(total_values, target_values, target_file)
r1 = analyze("don.txt", exclude=["dostoevsky.txt"]).view_as_table().view_as_plot()
Посмотрим на график. Заметим, что чуть с большей/меньшей частотой встречаются слова и фразы "может быть", "должно", "наверное" и т.д.
Для уверенности также проверим на отличность произведение "Поднятая Целина":
r2 = analyze("tselina.txt", exclude=["don.txt", "dostoevsky.txt"]).view_as_table().view_as_plot()
Видно, что распределение $\Delta$ для обоих произведений довольно близко. Хотя для "Тихого Дона" $\Delta_{max}$ всё-таки чуть больше.
Теперь для сравнения посмотрим на совсем другого автора, например, Достоевского. Проанализируем "Преступление и Наказание".
r3 = analyze("dostoevsky.txt", exclude=["don.txt"]).view_as_table().view_as_plot()
Если раньше $\Delta$ не превышало и 60, то теперь она бывает и под 800. Конечно, трудно попорить, что Фёдор Михайлович - совсем другой автор, но это всё равно даёт хорошее представление о работе нашего метода.
Из всего этого можно сделать вывод, что скорее всего Михаил Шолохов действительно является автором "Тихого Дона".