XXШ. - А что, так можно было? - Каждый прозаик жел

[ Все книги с иллюстрациями - на авторском сайте: y-books.neocities.org ]
[ подробнее см. proza.ru/avtor/yarmosh ]


2. КАЖДЫЙ ПРОЗАИК ЖЕЛАЕТ ЗНАТЬ, ЧТО ВЫБИРАЮТ ЧИТАТЕЛИ

Спешим уточнить, что речь пойдет не о прозаиках как классе. В контексте этой истории прозаиками будем называть авторов, активно пользующихся сайтом proza.ru для поиска и расширения своей читательской ауди­тории.

Платформа явно заслуживает внимания многих из тех, кому есть что сказать в литератур­ном творчестве, выходя­щем за рамки блогерства. Несмотря на аскетичный интерфейс, ею легко пользоваться читателям, да и самому автору удобно ра­ботать в личном каби­нете. Можно ставить как отдельные про­изведения, так и сбор­ники, по сути имитируя формат кни­ги. Не будем останавливаться на том, почему это важ­но, ибо во­прос уже подробно разобран нами в учебнике "Талан­ты явля­ются в книгах". А вот, пожалуй, единственное, чего не хватает платфор­ме - это как-раз инструмен­тов для анализа читатель­ского интереса. Данные, хоть и огра­ниченные рамками двух недель, есть, но обра­ботать, сде­лать из них вы­воды бывает трудновато. Хотя это дело поправи­мое. Им и займемся.

Для начала посмотрим, как накопленную за две неде­ли информацию о читательских обращениях к творчеству ав­тора взять и, если надо, присоединить к предыдущей, а затем компактно пред­ставить в числовом виде, удобном для постро­ения графиков.

Воспользуемся условным примером, поскольку реальные данные обновляются с каждым днем, и увидеть их "завтра" при всем желании невозмож­но. Зато, с другой стороны, заду­манный нами инструмент способен делать анализ всех работ - как своих, так и, к слову, любого из трехсот с лишним тысяч авторов на платфор­ме, бла­го сбор данных не требует регистрации и постоянно доступен.

Вначале выходим на страницу автора по адресу: proza.ru/avtor/имя_автора . Копируем оттуда полный список про­изведений вместе с датой постановки и приводим его к форма­ту, показанному ниже. Записываем в файле all.js . Эти данные нужны лишь для того, чтобы правильно группировать публи­кации, разбитые на главы, а также оценивать интерес к сбор­никам и книгам в целом.

В нашем примере соберем пять известных книг зна­менито­го авто­ра, включа­ющие соответ­ст­венно 12, 11, 13, 16 (плюс Предисловие), 4 рассказа (либо главы), и добавим к ним рас­сказ "Полосатый сундук", не входя­щий в сборники:

all=[
['',  'Полосатый сундук', '01.01.2023'],
['Приключения Шерлока Холмса (12 рассказов)',
  'Скандал в Богемии', '01.02.2023',
  'Союз рыжих', '02.02.2023',
   . . .
  'Медные буки', '12.02.2023'],
['Записки о Шерлоке Холмсе (11 рассказов)',
  'Серебряный', '01.03.2023',
  'Жёлтое лицо', '02.03.2023',
   . . .
  'Последнее дело Холмса', '11.03.2023'],
['Возвращение Шерлока Холмса (13 рассказов)',
  'Пустой дом', '01.04.2023',
  'Подрядчик из Норвуда', '02.04.2023',
   . . .
  'Второе пятно', '13.04.2023'],
['Затерянный мир',
  'Затерянный мир. Предисловие', '01.05.2023',
  'Затерянный мир. Гл. I Всегда есть возможность совершить подвиг',
   . . .                '02.05.2023',
  'Затерянный мир. Гл. XVI Процессия! Процессия!', '17.05.2023'],
['Капитан Шарки',
  'Как губернатор Сент-Китта вернулся на родину', '01.06.2023',
  'Как капитан Шарки и Стивен Крэддок перехитрили друг друга', '02.06.2023',
  'Как Копли Бэнкс прикончил капитана Шарки', '03.06.2023',
  'Ошибка капитана Шарки', '04.06.2023']
]

На месте троеточий следует добавить пропущенные ради компактности строки. Обращаем особое внимание, чтобы все кавыч­ки, запятые и квадратные скобки были на местах.

Последний подготовительный шаг - собрать статистику об­ращений за неделю. Находим ее там же, по ссылке "Чита­тели". Список разбит на страницы по 20 строк такого вида:

  неизвестный читатель 1    Союз рыжих    27.08.2025    15:56    не определен

Просто копи­руем их блоками по порядку и без изменений в один файл. Добавляем вверху короткую строчку s=` , внизу вторую, с одним только значком ` , и записываем всё это под именем readers.js .

А мы, продолжая свой пример, возьмем некие реальные данные за две недели и просто подставим в них перечисленные выше 58 произведе­ний по дням, для последующего анализа. [...]

Теперь можем приступать собственно к программе. Нач­нем с того, что приведем всё обилие информации из readers.js к компактной форме, используя данные из файла all.js, чтобы дальше с ними можно было удобно работать. За­дача на этом шаге - собрать по каждому дню перечень всех читательских запросов на книги и конкретно рассказы или главы в них, в виде ссылок на их порядковые номера, разделенные точкой. Например, рассказ "Последнее дело Холмса" получает ссылку 2.11, так как это 11-й рассказ из второй книги. Для рас­сказов, не входящих в сборники, номером книги служит 0, как в случае с "Полосатым сундуком", ссылка на который - 0.1.

Решение задачи, длиной по сути в десяток строк, запишем в файле proza.html:

      <script src='readers.js'></script>
      <script src='all.js'></script>
      <script>
      let a=s.split('\n').filter(Boolean), d=[];
      for (let i=0; i<a.length; i++) a[i]=[a[i].split(' \t')[1],a[i].split(' \t')[2]];
      for (let i=0; i<a.length; i++) if (d.indexOf(a[i][1])<0) d.push(a[i][1]);
      for (let day=0; day<d.length; day++) {
         let dd=d[day].slice(-2)+d[day].slice(3,5)+d[day].slice(0,2);
         let dy=new Date(dd); dd=dy.getDay()+' '+dd;
         for (let i=0; i<a.length; i++)
            for (let n=0; n<all.length; n++)
               if ((all[n]).indexOf(a[i][0].slice(0,-1))>-1 && a[i][1]==d[day])
                dd+=' '+n+'.'+((all[n].indexOf(a[i][0].slice(0,-1)))/2+0.5);
         d[day]=dd;
      } console.log(d);
      </script>

Открыв файл в браузере, вызываем консоль клавишей F12: [...].

Легко убедиться, что здесь плотно упакованы все данные, которые нужны для изучения. По каждой дате собран перечень произведений и книг, к которым обращались, и даже поряд­ко­вый день недели, который приходится на эту дату.

А дальше начинается самое интересное. Пусть теперь про­грамма ответит на те вопросы, ради которых всё это и затева­лось. Мы хотим знать для начала:
- распределение частоты обращений ко всем произведени­ям по дням недели;
- рейтинг произведений по количеству просмотров;
- график изменений читательских интересов со временем.

Воспользуемся пакетом chart.js, который предназначен как раз для построения всевозможных графиков. Загрузим его с адреса: cdnjs.com/libraries/chart.js, и подключим к программе на самом верху одной строкой:

      <script src='chart.js'></script>

Сразу под ней разместим три холста для намеченных гра­фиков:

      <div style="width: 100%">
        <p><canvas id="chart1"></canvas><hr>
        <p><canvas id="chart2"></canvas><hr>
        <p><canvas id="chart3" height="200"></canvas><br>
      </div>

Построение каждого графика состоит из двух шагов - под­готовки данных и затем их отображения на холсте. Всё это просто добавляем последовательно к программе.

Не станем уделять внимания оптимизации, посколь­ку нас сейчас интересует не компактность и эффектив­ность ко­да (об этом можно позаботиться позже), а стремление ско­рее уви­деть, как будет выглядеть первый результат на экране. На­бираем текст:

<script>
//--- Данные первого графика
let wk=[[],[],[],[],[],[],[]], ww=[[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]];
for (let i=0; i<d.length; i++) {
   [a,b, ...rest]=d[i].split(' ');
   wk[a]=wk[a].concat(rest);
   ww[0][a]+=rest.length;
   ww[1][a]+=1;
} for (let i=0; i<7; i++) ww[0][i]=ww[0][i]/ww[1][i];
//--- Отображение первого графика
lbl=['Вс','Пн','Вт','Ср','Чт','Пт','Сб'];
let r=[lbl[d[0].split(' ')[0]],lbl[d[d.length-1].split(' ')[0]]];
new Chart(document.getElementById('chart1'), {
    type: 'bar',
    data: { labels: lbl, datasets: [{ data: ww[0] }] },
    options: {
      plugins: {
        legend: {display: false},
        title: {
          display: true,
          text: "Прочтений в неделю, с "+
r[1]+' '+d[d.length-1].split(' ')[1]+' по '+
r[0]+' '+d[0].split(' ')[1]+' (мм/дд/гг):',
          font: {size:16}
        }
      }
    }
});
</script>

Открываем программу в браузере. Вот и график. Анализ оставим на потом, чтобы пока перейти к следующему:

<script>
//--- Данные второго графикa
let v0=[],v1,v2=[],titles=[];
for (let i=0; i<d.length; i++) {
  [,, ...rest2]=d[i].split(' ');
  v0.push(rest2);
} v0=v0.flat(); v1=[...new Set(v0)].sort();
for (let i=0; i<v1.length; i++) {
  v2.push(0); for (let j=0; j<v0.length; j++) if (v1[i]==v0[j]) v2[i]+=1;
}
for (let i=0; i<v1.length; i++) {
  let t=v1[i].split('.'); titles.push(all[t[0]][t[1]*2-1]);
}
//--- Отображение второго графикa
new Chart(document.getElementById('chart2'), {
    type: 'doughnut',
    data: {
      labels: titles,
      datasets: [{label: 'Прочтений', data: v2, borderWidth: 1}]
    },
  options: {
    cutout: '30%',
    plugins: {
      legend: {display:true},
      title: {
        display: true,
        text: "Доля каждого произведения из всех прочтений:",
        font: {size:16}
      }
    }
  }
});
</script>

Новый яркий и красочный график совсем не похож на предыдущий. Если, взглянув на тот, узнаешь что-то новое о читателях, а именно, какие дни недели они чаще выбирают для знаком­ства с твоими произведениями, то этот показывает, насколько сами произведения привлекают внимание чита­тельской ауди­тории. Потому и формат графика другой. Круго­вая диаграмма отражает долю каждого произведения в сум­марном количестве читательских обращений. Чем шире сек­тор, тем больше про­чтений. Притом, несмотря на кажущуюся сложность уловить, где кто и какова их доля, график предель­но легко читать. До­статочно навести мышь на любой сектор, чтобы увидеть на­звание работы и сколько прочтений за рас­сматриваемый пери­од она собрала. Одновременно по размеру сектора сразу по­ни­маешь, как он выглядит на фоне других. Кроме того, щелкая мышью на любом произведении из полного списка над графи­ком, можно временно убирать его из рассмотрения, оставляя только ин­тересующие, или возвращать обратно. Словом, есть что иссле­до­вать.

Третий график - чуть сложнее по структуре данных, и тоже совсем не по­хож на предыдущие. Здесь меньше обобщений и больше информации, которую нужно подать. Суть в том, что­бы показать, как накапливалось количество просмотров по каждому произведению день за днем. Строим график так:

<script>
//--- Данные третьего графикa
let v3=[], v4=[], v5=[], v6=[], data3=[];
for (let i=0; i<d.length; i++) {
  [,dx, ...rest3]=d[i].split(' ');
  v3.push(dx); v4.push(rest3);
}
let v3r=v3.reverse(), v4r=v4.reverse();
for (let i=0; i<v4r.length; i++) {
  v5.push([]); for (let k=0; k<v1.length; k++) v5[i].push(0);
  for (let k=0; k<v4r[i].length; k++) v5[i][v1.indexOf(v4[i][k])]+=1;
  if (i>0) for (let k=0; k<v1.length; k++) v5[i][k]+=v5[i-1][k];
}
for (let i=0; i<v1.length; i++) {
  v6.push([]); for (let k=0; k<v5.length; k++) v6[i].push(v5[k][i])
}
for (let i=0; i<v1.length; i++)
  data3.push({label:titles[i], data:v6[i], borderWidth:1});
//--- Отображение третьего графика
new Chart(document.getElementById('chart3'), {
    type: 'line', data: {labels: v3r, datasets: data3},
    options: {
      plugins: {
        legend: {display:true},
        title: {
          display: true,
          text: "Рост числа просмотров произведений по дням:",
          font: {size:16}
        }
      }
    }
});
</script>

Тут в нашем примере - пока только 52 произведения из 58, и 14 дней их пре­бы­вания на чита­тель­ской полке. Это 728 то­чек, которые нужно показать и еще снабдить ярлыками, чтобы при наведении на любую указателя мыши видеть заглавие и число­вое значение просмо­тров. Потому и блок подготовки данных в программе оказался на несколько строк длиннее. Хотя, конечно, его можно существенно оптимизировать.

Как и в случае с предыдущим, на этом графике лю­бое из произве­дений тоже можно по желанию вычеркивать, то есть уби­рать из рас­смотрения, упрощая таким образом картину для большей на­гляд­ности.

Программа на этом закончена, но впереди еще один этап - проверки ее в условиях практического использования.

Спустя почти неделю, мы сняли свежие данные в продол­жение статистики прочтений. Специально не стали дожидать­ся полной недели, а остановились на шести днях, дабы убе­диться, что первый график будет верно реагировать на непол­ные сведения при расчете средних значений. Причем напом­ним, что всё это не случайные числа, а реальные данные, про­сто с другими за­главиями и существенно сокращенным коли­чеством произведений, а также со сдвинутой датой начала.

В целом, предыдущие данные были дополнены новым перечнем, тоже в формате, представленном выше, сразу после слов "по 20 строк такого вида" [...].

Результат оказался ожидаемо информативным. Уже первый график заставил присмотреться к изменениям:

- они никак не коснулись Среды; разной величины рост по­каза­ли ПН (+100%), ВТ (+65%), ВС (+40%), СБ (+25%) и ЧТ (+10%), а вот у Пятницы - даже небольшой спад (-6%)

- в итоге Понедельник стал продуктивней Вторника, а Суб­бота оказалась с лучшей активностью, чем Пятница;

- хотя Воскресенье сохранило лидерство, его отрыв от бли­жайших конкурентов заметно сократился.

Особенно наглядно проявляются эти тенденции, если по­ставить старые и новые данные для сравнения рядом.

Следующий график тоже приготовил сюрпризы. Во-пер­вых, нашлись читатели, которые обратили наконец внимание на оставшиеся шесть произведе­ний, к кото­рым в первые две недели совсем никто не интересовался [...]. Вот почему их не было на пре­дыдущих графиках, но появились сейчас, доведя количе­ство произведений до полных 58.

Во-вторых, сменилась тройка лидеров. Если неделю назад за "Одинокой велосипедисткой" (17 прочтений) с большим отрывом следовали "Случай в интернате" (9) и "Скандал в Бо­гемии" (7), то теперь ее 19 прочтений догоняет "Подрядчик из Норвуда" (11), оставляя позади "Случай в интернате" (10). Эту динамику наглядно отражает третий из графиков, который состоит теперь уже из 58 произведений по 20 датам, то есть имеет 1160 информационных точек.

Конечно, это всего лишь пример. Но взят он из реальности. И такой инструмент любому прозаику (в том значении слова, которое сформулиро­вано в нача­ле рассказа) может оказаться полезным для анализа собственных работ.

Как будут ме­нять складывающуюся картину приходящие свежие данные, какие выводы всякий раз делать дальше, и какие действия пред­принимать - каждый будет решать сам.


Рецензии