Сравнение корпусов¶
ruts.corpus.compare_corpora(), ruts.corpus.corpus_features(), ruts.corpus.text_features(), ruts.corpus.split_windows(), ruts.corpus.sentence_rhythm()
Описание¶
Сравнение двух корпусов по всем признакам текста сразу: какие статистики различают авторов, жанры, переводы, человеческие и сгенерированные тексты - и насколько. Для отдельных слов то же делает keyness, для расстояний между текстами - delta.
Тексты обоих корпусов режутся на окна одинакового размера (split_windows: число окон - округленное отношение числа слов к размеру окна, не меньше одного, части равные; граница проходит перед открывающими знаками первого слова окна - тире, кавычками, скобками, - чтобы пунктуация не терялась), чтобы убрать зависимость признаков от длины текста. Для каждого окна считаются признаки (text_features или своя функция), для каждого признака сравниваются два набора значений. Результат - DataFrame признак × статистики, отсортированный по убыванию модуля дельты Клиффа.
Признаки¶
text_features(text) возвращает 130 признаков с префиксом по источнику:
| Префикс | Признаки | Источник |
|---|---|---|
basic_ |
доли уникальных, длинных, сложных, простых, одно- и многосложных слов, букв, пробелов и знаков препинания; буквы и слоги на слово, слова на предложение | BasicStats |
readability_ |
все формулы удобочитаемости, сводный класс, время чтения | ReadabilityStats |
diversity_ |
все меры лексического разнообразия | DiversityStats |
morph_ |
доли частей речи от числа слов (morph_pos_NOUN) и доли значений внутри каждого признака (morph_case_Gen, morph_tense_Past) |
MorphStats по pymorphy3 |
sents_ |
средняя длина предложения в словах, стандартное отклонение, коэффициент вариации, автокорреляция соседних длин (sentence_rhythm) - ритм текста |
sentence_lengths |
punct_ |
частоты знаков по типам на 1000 слов и доля буквы ё | punctuation_profile |
На окно в 1000 слов уходит около 0.1 с. Признаки по разбору spaCy (SyntaxStats, CohesionStats) в набор по умолчанию не входят - функция работает по строкам; их можно добавить своей функцией признаков через features, см. пример ниже. corpus_features(texts, window, features) отдает матрицу признаков окон с индексом (номер текста, номер окна) - для своих классификаторов.
Статистики¶
Для признака со значениями \(x_1 \dots x_{n_A}\) в корпусе A и \(y_1 \dots y_{n_B}\) в корпусе B (неопределенные и бесконечные значения отброшены; при менее чем двух значениях на стороне - nan):
| Столбец | Описание |
|---|---|
mean_A, mean_B, median_A, median_B |
средние и медианы |
median_diff, ci_low, ci_high |
разность медиан и ее перцентильный бутстрэп-интервал 95%: оба набора пересэмплируются n_bootstrap раз (bootstrap_median_diff) |
cohen_d |
\(d = (\bar{x} - \bar{y}) / s\), \(s\) - объединенное стандартное отклонение; 0.2 - малый эффект, 0.5 - средний, 0.8 - большой (calc_cohen_d) |
cliff_delta |
\(\delta = P(x > y) - P(x < y)\) от −1 до 1; \(\lvert\delta\rvert\) < 0.147 - пренебрежимый эффект, < 0.33 - малый, < 0.474 - средний, иначе большой (Romano и др. 2006; calc_cliff_delta) |
auc |
признак как одиночный классификатор: доля пар окон, где значение в A больше, чем в B, ничьи за половину; \(\delta = 2 \cdot AUC - 1\), 0.5 - признак не различает корпуса |
u, p_value |
U-критерий Манна-Уитни и двустороннее p-значение (scipy.stats.mannwhitneyu) |
p_holm |
p-значение с поправкой Холма на число признаков (holm_correction): таблица из сотни строк без поправки приглашает к ложным открытиям |
n_A, n_B |
число окон с определенным значением |
Дельта Клиффа и AUC считаются из той же U-статистики, поэтому согласованы между собой; d Коэна чувствителен к выбросам и ненормальности, его стоит смотреть рядом с дельтой.
Параметры¶
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
a |
list[str] | - |
Тексты первого корпуса |
b |
list[str] | - |
Тексты второго корпуса |
window |
int | 1000 |
Размер окна в словах; None - тексты целиком |
features |
callable | None |
Функция признаков текста; None - text_features |
labels |
tuple[str, str] | ("A", "B") |
Имена корпусов для столбцов |
n_bootstrap |
int | 1000 |
Число выборок бутстрэпа |
seed |
int | 0 |
Зерно генератора случайных чисел; None - случайное |
Пример использования¶
Чехов против Толстого по прозе из набора RussianLiterature: 77 и 42 произведения, 292 и 1444 окна по 1000 слов, около двух минут.
Пример
Код:
from ruts.corpus import compare_corpora
from ruts.datasets import RussianLiterature
rl = RussianLiterature()
chekhov = list(rl.get_texts(genre="prose", author="Чехов"))
tolstoy = list(rl.get_texts(genre="prose", author="Толстой"))
result = compare_corpora(chekhov, tolstoy, window=1000, labels=("Чехов", "Толстой"))
columns = [
"median_Чехов",
"median_Толстой",
"ci_low",
"ci_high",
"cohen_d",
"cliff_delta",
"auc",
"p_holm",
]
result[columns].head(10).round(3)
Результат:
median_Чехов median_Толстой ci_low ci_high cohen_d cliff_delta auc p_holm
punct_ellipsis 15.842 2.000 10.940 18.013 1.861 0.719 0.860 0.0
punct_exclamation 14.881 3.996 9.145 12.378 1.704 0.670 0.835 0.0
morph_verb_form_Fin 0.761 0.695 0.056 0.076 1.161 0.594 0.797 0.0
punct_yo_share 0.007 0.000 0.007 0.008 1.065 0.557 0.778 0.0
readability_gunning_fog_index 5.995 7.970 -2.283 -1.681 -0.961 -0.540 0.230 0.0
readability_matskovsky_index 8.600 11.232 -3.148 -2.324 -0.947 -0.531 0.235 0.0
basic_words_per_sent 11.122 15.136 -4.595 -3.400 -0.943 -0.531 0.235 0.0
sents_mean 11.122 15.136 -4.636 -3.417 -0.943 -0.531 0.235 0.0
readability_dale_chall_index 5.187 6.676 -1.776 -1.229 -0.930 -0.527 0.237 0.0
readability_smog_index 5.711 7.294 -1.903 -1.322 -0.895 -0.516 0.242 0.0
У Чехова в разы больше многоточий и восклицаний на 1000 слов, короче предложения и выше доля личных форм глагола; Толстой сложнее по всем формулам удобочитаемости. Из 130 признаков у 100 поправленное p-значение ниже 0.01, но большой эффект по дельте Клиффа только у 14 - на тысячах окон значимость дешева, размер эффекта важнее.
Свои признаки, например синтаксические по spaCy, передаются функцией:
Пример
import spacy
from ruts import SyntaxStats
from ruts.corpus import compare_corpora, text_features
nlp = spacy.load("ru_core_news_sm")
def features(text):
stats = SyntaxStats(nlp(text)).get_stats()
return {**text_features(text), **{f"syntax_{key}": value for key, value in stats.items()}}
compare_corpora(chekhov, tolstoy, window=1000, features=features, labels=("Чехов", "Толстой"))