Вдохновившись статьёй для английских книг, решил проверсти аналогичный анализ для русских произведений.

Преподаватель английского языка автора оригинальной статьи утверждал, что прочитав 20 страниц любой книги можно узнать 90% всех слов произведения и далее читать книгу будет значительно проще.

В этой статье я хочу проверить аналогичное утверждение для русского языка: сколько страниц необходимо прочитать, чтобы узнать 90% книги.

Спойлер: прочитав 20 страниц книги вы практически наверняка не узнаете 90% всех слов. Далее будут графики, таблицы и немного кода.

Для анализа требуется стандартный для подобных задач набор бибилиотек Python: matplotlib, seaborn, pandas, numpy, stop_words и pymorphy2.

Что анализировать?

В качестве “подопытных” взял различные книги:

  1. Объёмную классику (Л. Толстой “Война и мир. Книга 1.")
  2. Нехудожественная литература (С. Галёнкин “Маркетинг игр”)
  3. Случайную бесплатную современную книгу на litres (С. Тармашев “Закат Тьмы”)
  4. Книгу, переведённую на русский (Ш. Стрейд “Дикая”)
  5. Небольшой рассказ Антона Чехова (А. Чехов “Человек в футляре”)

Что хотим узнать?

  1. Как быстро появляются новые слова в книге?
  2. Какой процент книги можно прочитать зная слова, которые были встречены к некоторой странице?

На графиках к книгам зелёный линия даёт ответ на первый вопрос: какой процент всех уникальных слов в книге были встречены к текущей странице. Синяя линия на графике отвечает на второй вопрос: какой объём от всей книги написан при помощи слов встреченных к текущей странице.

from matplotlib import pyplot

import seaborn as sns
from collections import Counter
from itertools import chain

import pandas as pd
import numpy as np 

import re

import pymorphy2
from stop_words import get_stop_words

page_size = 200

Подготовка данных

Для подготовки книг в виде текстовых файлов для анализа используем pymorhy2 для лемматизации и stop_words для удаления стоп-слов:

morph = pymorphy2.MorphAnalyzer()
sw = get_stop_words('ru')
pyplot.rcParams["figure.figsize"] = (10,5)

def lemmatize(text):
    """Функция удаляет из текста стоп-слова, 
    лемматизирует текст и берёт для каждого начальную форму.
    
    # Arguments
        text: исходный текст.
    
    # Returns
        fixed_text: список лемматизированых не-стоп-слов.
    """
    letters_only = re.sub('[^a-zA-Zа-яА-Я]', ' ', text)
    fixed_text = []
    letters_only = list(filter(None, letters_only.split(' ')))
    for word in letters_only:
        # Берём первое совпадения только для простоты.
        nf = morph.parse(word)[0].normal_form
        if nf in sw:
            continue
        
        fixed_text.append(nf)
        
    return fixed_text


def txt_to_pages(filename, encoding='utf8'):
    """Функция преобразует текст из текстового файла в "страницы"
    
    # Arguments
        filename: путь до текстового файла.
        encoding: кодировка исходного файла.
    
    # Returns
        book_pages: список списков лемматизированных слов ("страниц")    
    """
    book_text = open(filename, mode='r', encoding=encoding).read()
    book_text = lemmatize(book_text)
    
    book_pages = []
    for i in range(len(book_text) // page_size + 1):
        book_pages.append(book_text[i * page_size : i * page_size + page_size])
    
    return book_pages

Обработка книг

Следующие две функции содержат всю логику по анализу книг:

  • вычисляют “сложность” книги - сколько страниц текста необходимо прочитать, чтобы узнать 90% всех уникальных слов,
  • вычисляют основные метрики книги - какой процент книги возможно прочитать зная слова встреченные к текущей странице, какой процент уникальных слов уже встречен.
def calculate_hardness(coverage, percent = 0.9):
    """ Функция вычисляет "сложность" книги - 
            насколько большую часть книги необходимо прочтитать, 
            чтобы узнать percent всех используемых слов.
    
    # Arguments
        coverage: массив с информацией какой процент книги
            можем прочитать, зная слова полученные к i-ой странице.
        percent: сколько слов хотим знать.
    
    # Returns
        i: номер страницы.
        hardness: какой процент книги будет прочитан к этой странице.
    """
    for i in range(len(coverage)):
        if coverage[i] > percent:
            break
    
    hardness = (100 * i) / float(len(coverage))
    return i, hardness


def percentage(values, title):
    """ Функция вычисляет всё.
    
    # Arguments
        values: список "страниц" книги.
    """
    all_words = list(chain.from_iterable(values))
    result = {}
    current_text = []
    page_counter = 0
    file_name = title.lower().replace(' ', '_').replace('\'','') + '.png'
    
    counter = Counter()
    # "Счётчик" всех слов книги. 
    # Используется для подсчёта процента книги, 
    # который можно прочитать зная некоторый объём слов. 
    occurances = Counter(all_words)
    
    # i-ый элемент: какой процент книги можем прочитать зная слова полученные к i-ой странице.
    coverage = []
    
    # i-ый элемент: какой процент слов используемых в книге знаем к i-ой странице.
    uniqueness = []
    
    # i-ый элемент: сколько уникальных слов используемых в книге знаем к i-ой странице.
    total_unique_words_count = []
    
    total = float(len(all_words))
    total_unique = float(len(occurances.keys()))
    
    print('page\ttotal\tpercent of all\tpercent of book\tuniqueness')
    for page_text in values:
        # Обновим счётчик уникальных слов словами с новой страницы.
        counter.update(page_text)
        
        # Посчитаем, сколько вообще раз встречаются слова 
        # которые уже были встречены к текущей странице.
        occured_words_count = sum((occurances[w] for w in counter.keys()))

        # Какой процент от всей книги составляют слова, 
        # которые уже встретили к текущей странице.
        _coverage = occured_words_count / total
        coverage.append(_coverage)
        
        # Какой процент от всех уникальных слов встретили к текущей странице.
        _uniqueness = len(counter.keys()) / total_unique
        uniqueness.append(_uniqueness)
        
        # Какой процент от книги прошли к текущей странице.
        percent_book = page_counter / len(values)
        
        # Сколько уникальных слов будет известно к текущей странице.
        total_unique_words_count.append(len(counter.keys()))
        
        print('{0}\t{1}\t{2:2f}\t{3:2f}\t{4:2f}'.format(page_counter, 
        len(counter.keys()), 
        _coverage, 
        percent_book, 
        _uniqueness))
        
        result[page_counter] = { 
            'page': page_counter,
            'unique_words': _uniqueness, 
            'all_words': _coverage
        }
        page_counter += 1
    
    percent_df = pd.DataFrame.from_dict(result,orient='index')
    
    page, hardness = calculate_hardness(coverage)
    
    print('Всего страниц: {0}'.format(len(values)))
    print('Всего слов: {}'.format(len(all_words)))
    print('Всего уникальных слов: {}'.format(len(occurances.keys())))
    print('Прочитав {0} страниц книги ({1:.2f}% всей книги) вы узнаете {2} уникальных слов.\n
           К этой странице будут встречены 90% всех слов книги и {3:.2f}% уникальных.'.format(
          page,
          hardness,
          total_unique_words_count[page],
          uniqueness[page] * 100))
    
    pyplot.plot(percent_df['unique_words'], color='g', label='Уникальные слова')
    pyplot.plot(percent_df['all_words'], color='b', label='Все слова')
    pyplot.legend(loc=4)
    pyplot.title(title)
    pyplot.xlabel('Страница')
    pyplot.ylabel('Покрытие (%)')
    pyplot.show()

Результаты

Л. Толстой “Война и мир. Книга 1”

war_and_peace_pages = txt_to_pages('./books/Tolstoyi_L._Voyinaimir1._Voyina_I_Mir_Kniga_1.txt', 
    encoding='1251')
percentage(war_and_peace_pages, 'Война и мир')
page	total	percent of all	percent of book	uniqueness
0	141	0.087556	0.000000	0.009748
1	286	0.158995	0.001541	0.019773
2	404	0.201158	0.003082	0.027931
3	521	0.238750	0.004622	0.036020
4	630	0.271941	0.006163	0.043556
5	732	0.306381	0.007704	0.050608
6	832	0.341545	0.009245	0.057522
7	923	0.360869	0.010786	0.063814
8	1007	0.387377	0.012327	0.069621
9	1096	0.404189	0.013867	0.075774
10	1179	0.417508	0.015408	0.081513
...
639	14463	0.999992	0.984592	0.999931
640	14463	0.999992	0.986133	0.999931
641	14463	0.999992	0.987673	0.999931
642	14463	0.999992	0.989214	0.999931
643	14463	0.999992	0.990755	0.999931
644	14463	0.999992	0.992296	0.999931
645	14463	0.999992	0.993837	0.999931
646	14463	0.999992	0.995378	0.999931
647	14463	0.999992	0.996918	0.999931
648	14464	1.000000	0.998459	1.000000
Всего страниц: 649
Всего слов: 129734
Всего уникальных слов: 14464
Прочитав 197 страниц книги (30.35% всей книги) вы узнаете 8171 уникальных слов.
К этой стрнице будут встречены 90% всех слов книги и 56.49% уникальных.

png

Книга “Война и мир. Книга 1." Льва Тостого содержит 649 страниц без учёта стоп-слов. На этих страницах можно встретить 14464 уникальных слов, каждое из которых, в среднем, встречается 9 раз. К 197-ой странице будет встречено 8171 уникальное слово (56.49% от всех уникальных слов). При помощи этих слов написано 90% всей книги.

С. Галёнкин “Маркетинг игр”

game_marketing_pages = txt_to_pages('./books/Games-Marketing-by-Galyonkin-v1.txt')
percentage(game_marketing_pages, 'Маркетинг игр')
page	total	percent of all	percent of book	uniqueness
0	145	0.250361	0.000000	0.044602
1	274	0.334958	0.017857	0.084282
2	392	0.386150	0.035714	0.120578
3	514	0.434543	0.053571	0.158105
4	614	0.481040	0.071429	0.188865
5	715	0.511015	0.089286	0.219932
6	798	0.545323	0.107143	0.245463
7	879	0.581347	0.125000	0.270378
8	955	0.617822	0.142857	0.293756
9	1042	0.646443	0.160714	0.320517
10	1108	0.668021	0.178571	0.340818
...
46	2938	0.964608	0.821429	0.903722
47	2973	0.968039	0.839286	0.914488
48	3015	0.972553	0.857143	0.927407
49	3057	0.979415	0.875000	0.940326
50	3094	0.983026	0.892857	0.951707
51	3131	0.987450	0.910714	0.963088
52	3167	0.990791	0.928571	0.974162
53	3187	0.992597	0.946429	0.980314
54	3242	0.999097	0.964286	0.997232
55	3251	1.000000	0.982143	1.000000
Всего страниц: 56
Всего слов: 11076
Всего уникальных слов: 3251
Прочитав 34 страниц книги (60.71% всей книги) вы узнаете 2485 уникальных слов.
К этой стрнице будут встречены 90% всех слов книги и 76.44% уникальных.

png

“Маркетинг игр” Сергея Галёнкина менее объёмная и содержит лишь 56 страниц текста. Уникальных слов в книге 3251. Для того, чтобы иметь возможность прочитать 90% книги необходимо знать 2485 уникальных слов (76.44% всех уникальных слов). Этого можно достичь к 34 странице.

С. Тармашев “Закат Тьмы”

zakat_tmyi = txt_to_pages('./books/Tarmashev_S._Tma3._Zakat_Tmyi.txt',
    encoding='1251')
percentage(zakat_tmyi, 'Закат тьмы')
page	total	percent of all	percent of book	uniqueness
0	132	0.060600	0.000000	0.012445
1	251	0.073714	0.002611	0.023664
2	363	0.076843	0.005222	0.034223
3	446	0.079892	0.007833	0.042048
4	535	0.082471	0.010444	0.050438
5	621	0.084290	0.013055	0.058546
6	704	0.085690	0.015666	0.066371
7	779	0.086842	0.018277	0.073442
8	851	0.089970	0.020888	0.080230
9	951	0.123516	0.023499	0.089658
10	1097	0.193396	0.026110	0.103422
...
373	10482	0.998063	0.973890	0.988215
374	10502	0.998338	0.976501	0.990101
375	10522	0.998626	0.979112	0.991986
376	10541	0.998940	0.981723	0.993778
377	10560	0.999346	0.984334	0.995569
378	10578	0.999594	0.986945	0.997266
379	10586	0.999699	0.989556	0.998020
380	10598	0.999856	0.992167	0.999152
381	10607	1.000000	0.994778	1.000000
382	10607	1.000000	0.997389	1.000000
Всего страниц: 383
Всего слов: 76403
Всего уникальных слов: 10607
Прочитав 140 страниц книги (36.55% всей книги) вы узнаете 6585 уникальных слов.
К этой стрнице будут встречены 90% всех слов книги и 62.08% уникальных.

png

Книгу Тармашева “Закат Тьмы” решил взять как образец современной литературы (и потому, что она была бесплатной на litres). Книга среднего объёма - 383 страницы без стоп-слов. Уникальных слов 10607 и зная 6585 (можно встретить прочитав 140 страниц или 36.55% всей книги) можно прочитать 90% всей книги.

Ш. Стрэйд “Дикая”

dikaya_df = txt_to_pages('./books/Stryeyid_Sh._Proekttruesto._Dikaya_Opasnoe_Puteshestv.txt', 
    encoding='1251')
percentage(dikaya_df, 'Дикая')
page	total	percent of all	percent of book	uniqueness
0	156	0.083797	0.000000	0.014677
1	281	0.130858	0.003247	0.026437
2	421	0.157755	0.006494	0.039609
3	568	0.218298	0.009740	0.053439
4	711	0.269051	0.012987	0.066892
5	839	0.318194	0.016234	0.078935
6	948	0.352604	0.019481	0.089190
7	1052	0.372508	0.022727	0.098975
8	1158	0.407032	0.025974	0.108947
9	1258	0.430774	0.029221	0.118355
10	1342	0.448255	0.032468	0.126258
...
298	10423	0.996292	0.967532	0.980619
299	10430	0.996422	0.970779	0.981278
300	10456	0.996861	0.974026	0.983724
301	10469	0.997122	0.977273	0.984947
302	10484	0.997382	0.980519	0.986358
303	10515	0.997967	0.983766	0.989275
304	10603	0.999528	0.987013	0.997554
305	10629	1.000000	0.990260	1.000000
306	10629	1.000000	0.993506	1.000000
307	10629	1.000000	0.996753	1.000000
Всего страниц: 308
Всего слов: 61494
Всего уникальных слов: 10629
Прочитав 134 страниц книги (43.51% всей книги) вы узнаете 6997 уникальных слов.
К этой стрнице будут встречены 90% всех слов книги и 65.83% уникальных.

png

Ещё одна книга среднего размера - перевод книги Шерил Стрэйд “Дикая”. В книге 308 страниц со 10629 уникальными словами. Для того, чтобы прочитать 90% книги необходимо знать не менее 6997 уникальных слов. Этого можно добиться прочитав 134 первые страницы (43.51% всей книги).

А. Чехов “Человек в футляре”

man_in_the_case = txt_to_pages('./books/Chehov_A._Man_in_the_case.txt')
percentage(man_in_the_case, 'Человек в футляре')
page	total	percent of all	percent of book	uniqueness
0	169	0.266868	0.000000	0.153358
1	307	0.402820	0.100000	0.278584
2	426	0.507049	0.200000	0.386570
3	549	0.628902	0.300000	0.498185
4	652	0.717019	0.400000	0.591652
5	751	0.779960	0.500000	0.681488
6	858	0.854985	0.600000	0.778584
7	934	0.903323	0.700000	0.847550
8	1022	0.954683	0.800000	0.927405
9	1102	1.000000	0.900000	1.000000
Всего страниц: 10
Всего слов: 1986
Всего уникальных слов: 1102
Прочитав 7 страниц книги (70.00% всей книги) вы узнаете 934 уникальных слов.
К этой стрнице будут встречены 90% всех слов книги и 84.75% уникальных.

png

Последней книгой был короткий (всего 10 страниц) рассказ Антона Чехова “Человек в футляре”. Уникальных слов в книге немного - 1986. 90% книги написаны с использованием 934 уникальных слов (84.75% от всех уникальных слов), которые будете знать прочитав 7 страниц (но в это случае проще дочитать книгу, чем заниматься подобной ерундой, как подсчёт сложности книги).

Выводы

Во всех проанализированных книгах новые слова встречаются в течение всей книги (зелёная линия на графиках). Однако, если обратить внимание на синюю линию, то заметно, что б_о_льшую часть встречаемых слов в книге можно начать “понимать” довольно рано.

Возможно, это совпадение, но графики для художественной литературы (“Война и мир”, “Дикая”, “Закат Тьмы”) похожи. В это же время графики для книги “Маркетинг игр” идут значительно ближе друг к другу, т.е. книга “сложнее”.

Особняком стоит книга “Человек в футляре” Антона Чехова: она очень короткая и для того, чтобы встретить 90% уникальных слов потребуется прочитать всю книгу (см. таблицу).

На этом на сегодня всё.