Ela.kpi.ua



НАЦІОНАЛЬНИЙ ТЕХНІЧНИЙ УНІВЕРСИТЕТ УКРАЇНИ

«КИЇВСЬКИЙ ПОЛІТЕХНІЧНИЙ ІНСТИТУТ

імені ІГОРЯ СІКОРСЬКОГО»

Інститут прикладного системного аналізу

Кафедра математичних методів системного аналізу

«До захисту допущено»

В.о.завідувача кафедри

__________ О.Л. Тимощук

ДИПЛОМНА РОБОТА

на здобуття ступеня бакалавра

з напряму підготовки 6.040303 Системний аналіз

на тему: «Програмний комплекс автоматизованого формування розкладу сеансів для кінотеатрів на основі статистичних даних»

Виконала:

студентка IV курсу, групи КА-53

Зінченко Анастасія Олександрівна __________

Керівник:

професор кафедри ММСА, д.т.н.

Мухін В.Є. __________

Консультант з економічного розділу:

доцент, к.е.н.

Шевчук О.А. __________

Консультант з нормоконтролю:

доцент, к.т.н.

Коваленко А.Є. __________

Рецензент: __________

Засвідчую, що у цій дипломній роботі немає запозичень з праць інших авторів без відповідних посилань.

Студентка _____________

Київ – 2019 року

РЕФЕРАТ

Дипломна робота містить: .. с., 11 табл., 24 рис., 3 дод. та 22 джерел.

ПРОГРАМНИЙ КОМПЛЕКС, РОЗКЛАД СЕАНСІВ, ПРОГНОЗНА МОДЕЛЬ

В даній роботі було розроблено програмний комплекс автоматизованого формування розкладу сеансів кінотеатрів на основі статистичних даних, що складається з трьох модулів: модуль збору даних, модуль прогнозної моделі та модуль генерування розкладу.

Під час аналізу предметної області були виявлені її слабкі місця, а дослідження аналогів показало, що жодна з програм не має в собі потрібного функціоналу.

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

В результаті роботи створено програму що відповідає поставленим вимогам, а через її модульність всі її частини можуть бути використані окремо і для інших проектів в цій області.

Ця програма має допомогти власникам кінотеатрів спростити процес формування розкладу та забезпечити підвищення прибутку за рахунок аналізу статистичних даних схожих фільмів, що були в прокаті раніше.

ABSTRACT

The bachelors work : __ p., 11 tabl. 24 fig., 3 additions, 22 references.

SOFTWARE COMPLEX, CALCULATION SCHEDULE, FOREIGN MODEL

The theme: software complex of automated scheduling of sessions for cinemas on the basis of statistical data

In this work had created a software complex for the automated formation of cinema session schedules was developed based on statistical data, consisting of three modules: a data acquisition module, a prediction model module, and a scheduling module.

During the analysis of the subject area, we had identified bottlenecks of existing system, and revealed that none of analogues has the necessary functional.

To develop this complex, we analyzed the history of the emergence of mathematical modeling, the main varieties of mathematical models and methods, the principles of constructing ensembles, considered begging and boosting, as well as a detailed analysis of the field of cinematography.

As a result, we created a complex system for sheduling, and all its parts can be used separately for other projects in this area.

This program should help cinema owners to simplify the process of scheduling and provide increased revenue by analyzing the statistics of similar films that were hired earlier

ЗМІСТ

ВСТУП 7

РОЗДІЛ 1 АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ 9

1.1 Модельні представлення світу 9

1.2 Вимоги до обчислювальних алгоритмів 14

1.4 Властивості та характеристики узагальнених моделей 16

1.5 Проблема в кінематографії 17

1.6 Моделі та методи прогнозування 20

1.7 Модель Random forest 22

1.8 Градієнтний бустинг 24

1.9 Цілочисельне програмування 26

1.10 Постановка задачі 28

1.11 Вимоги до програмного забезпечення 28

1.12 Висновки до розділу 1 29

РОЗДІЛ 2 ОПИС ПОБУДОВАНОЇ МАТЕМАТИЧНОЇ МОДЕЛІ ТА ПРОГРАМНИХ ЗАСОБІВ ДЛЯ ВИРІШЕННЯ ПОСТАВЛЕНОЇ ЗАДАЧІ 30

2.1 Первинний аналіз вхідних даних 30

2.2 Структура програмного комплексу 35

2.3 Використання методів оптимізації 49

2.4 Двох-етапне формування розкладу 50

2.4.1 Формування базового розкладу по обмеженням дистриб’юторів 51

2.4.2 Формування повного розкладу 51

2.5 Висновки до розділу 2 52

РОЗДІЛ 3 ОПИС ВИПРОБУВАНЬ ПРОДУКТУ 53

3.1 Сервіс для підбору схожих фільмів 53

3.2 Програмний комплекс для формування розкладу 55

РОЗДІЛ 4 ЕКОНОМІЧНА ЧАСТИНА 59

4.1 Постановка задачі техніко-економічного аналізу 60

4.2 Варіанти реалізації основних функцій 61

4.3 Обґрунтування системи параметрів ПП 63

4.3.1 Опис параметрів 63

4.3.2 Кількісна оцінка параметрів 63

4.3.3 Аналіз експертного оцінювання параметрів 66

4.4 Аналіз рівня якості варіантів реалізації функцій 70

4.5 Економічний аналіз варіантів розробки ПП 72

4.6 Вибір кращого варіанта ПП техніко-економічного рівня 77

ПЕРЕЛІК ПОСИЛАНЬ 81

Додаток А. 83

Додаток Б 84

Додаток В…………………………………………………………………………………....89

ВСТУП

У наш час важливою є тема оптимізації виробничого процесу, економії часу на планування та розподіл робіт, тощо. Крім того, бажання збільшити прибуток та мінімізувати витрати є невід’ємною частиною будь якого підприємства.

Маючи доступ до бази даних підприємства можна врахувати багато факторів, провести візуальний аналіз даних. Але цього недостатньо для того щоб побудувати план, і тим більше представити його у вигляді функції прибутку.

Для цього вже треба використовувати методи більш глибокого аналізу даних.

Зазвичай під час такого аналізу з’являються додаткові фактори впливу, інформацію про які потрібно шукати окремо. Це призводить до значного розширення даних аналізу, і обійтись без програмного забезпечення вже не можливо.

Завдяки спеціалізованому програмному забезпеченню деякі проблеми що не мали можливості бути вирішеними раніше, зараз займають секунди машинного часу, і простір для реалізації нових ідей вже не так залежить від цього фактору.

Наразі в Україні є багато підприємств, виробничі процеси яких так чи інакше підпорядковані програмам оптимізації хоча б частково. А деякі і досі роблять все руками, незважаючи на те, що крізь них з кожним роком проходять все більші об’єми даних.

Це пов’язано з тим, що або необхідне програмне забезпечення відсутнє на українському ринку, або ж воно коштує занадто дорого та не вирішує поставлених проблем в повній мірі.

Актуальність роботи полягає в тому що в сфері кінематографії жодне існуюче програмне забезпечення в повній мірі не допомагає вирішити проблеми розподілу кількості сеансів між фільмами.

На разі відсутня єдина база фільмів із якісним контентом, для якої був би відкритий доступ по API, в кожного кінотеатру своя цільова аудиторія клієнтів яка пов’язана з його розташуванням, наявними технологіями, тощо, і це не дозволяє використовувати єдиний алгоритм для всіх випадків навіть в одній мережі.

Кожен власник намагається вигадати щось своє і хоч якось випередити суперників, та програми яка б яка могла охопити поставлені задачі в повному обсязі відсутня на ринку України.

В той же час за кордоном математичні моделі поведінки користувачів, попиту значно спрощують формування виробничих планів, розкладів, тощо. Завдяки моделям можна тестувати нововведення і відслідковувати небажані впливи у тестовому оточенні, а не у реальному житті.

В умовах жорсткої конкуренції на ринку, потреба в подібних програмах зростає з кожним роком, а через відсутність рішень виявлена проблема є актуальною.

РОЗДІЛ 1 АНАЛІЗ ПРЕДМЕТНОЇ ОБЛАСТІ

Мета роботи: створити програмний комплекс автоматизованого формування розкладу сеансів для кінотеатрів на основі статистичних даних.

1 Модельні представлення світу

Одна з перших згадок про моделі та моделювання датується періодом бронзового віку, і вона пов’язана саме з технологією виробництва. Ще в ІІІ ст. до н.е. існували вироби з бронзи та ювелірних металів, які не могли бути виконані інакше , а ніж шляхом виплавки з форм, що були виготовлені по існуючим зразкам [1].

Функції ливарних моделей частково збігаються з функціями аналогічних сучасних об’єктів, хоча термін «модель» тут можна використовувати лише умовно, так як він виник набагато пізніше [1].

Наступною гілкою розвитку є згадки в «Своде Механики» Філона Візантійського, де він згадує модель в сенсі загальновідомого предмета, що не потребує пояснень. В аналогічному сенсі про це каже і Вітрувій, та його підхід більш глибокий і базується на тому що «не все можливо повторити одним і тим же способом, та деякі речі зроблені по зразку невеликої моделі діють однаково і більшому розмірі, а деякі в малому розмірі здаються робочими та правдоподібними, але будучи збільшені, розвалюються на очах».

Із моделей, що дійшли до сучасності ще з періоду античності, варто відзначити дві основних – модель поведінки твердого тіла в рідині Архімеда та модель планетного руху Птоломея [1].

Однак, якщо розглядати ці моделі з точки зору сучасних модельних уявлень, то модель Птоломея носить чисто феноменологічний характер, цілком базується на спостереженнях, і є прототипом моделей що описують властивості об’єкта як чорний ящик – безвідносно до реальних механізмів [ 2].

Модель Архімеда, в свою чергу, можна розглядати як результат комбінування теоретичних та аналітичних моделей, що розкривали внутрішній механізм явищ, які вивчалися [ 2].

Далі є сенс звернути увагу на моделі-зразки епохи Відродження, де як раз використовувався принцип перенесення властивостей об’єкта з малого на більший. Зрозуміло, що всі моделі подобного роду мали лише якісний характер. Питання міцності, навантажень на конструкцію, геометричних розмірів елементів, тощо вирішувались на основі вивчення прототипів, якісних суджень о характері і взаємодії частин моделі між собою без використовування наукових методів та розрахунків.

Що до випробувань нововведень у фіктивному світі, то в історії збереглися поняття модельного експерименту. Яскравим прикладом є записки Уатта про результати проведення ряду модельних експериментів, наслідком яких стала ідея вирішення проблеми машини Ньюкомена [ 2].

Надалі в сфері науки та техніки виникла перепона: формулювання більшості досліджень в формі математичних моделей не викликало труднощів, але вирішення великої кількості подібних задач дуже часто стикалося з серйозними проблемами.

Відомо що з десяти математичних задач що описують реальні об’єкти, дев’ять не мають чіткого аналітичного розв’язку. Ця проблема частково вирішувалась за рахунок наближених обчислень, апроксимації, тощо. Та ці наближення були джерелом грубих помилок, особливо у задачах дослідження стійкості систем [3,4].

Тоді були створені перші електронні моделюючі установки. Вирішити задачу на основі деякої базової моделі було набагато простіше аніж шукати розв’язок математичним шляхом та у чисельній формі.

Подальший розвиток машинного алгоритмічного моделювання став ключем до вирішення задач подібного роду.

Модель – це спрощене представлення що до реального об’єкту, процесу чи явища, іншими словами це деякий спрощений об’єкт, що відображає особливості реального явища чи процесу [3,4].

Моделювання – дослідження об’єктів пізнання на моделях, побудова моделей існуючих предметів та явищ.

Моделювання виконується на основі обраної мети:

− Пізнання оточуючого світу

− Створення об’єкта з встановленими наперед властивостями

− Виявлення наслідків взаємодії на об’єкт та прийняття рішень

− Ефективність управління об’єктом

Моделювати можна об’єкти, явища та процеси, серед яких:

− Об’єктами прийнято вважати живі організми, сонячну систему, будівельні конструкції, художні твори, тощо

− Явищами – землетруси, магнітні поля, електричні сили, паводки, грози, тощо

− Процесами – економічні, політичні, соціальні , фізичні тощо.

Поняття комп’ютерного моделювання було ведемо для того щоб відобразити використання в процесі моделювання новий потужний засіб обробки інформації – комп’ютер [4,5,6].

Методологія математичного моделювання сформульована А. А. Самарським, засновником математичного моделювання, у вигляді відомої тріади «модель-алгоритм-програма» [7].

Основні етапи моделювання можна представити наступною схемою (рисунок 1.1):

[pic]

Рисунок 1.1 – основні етапи моделювання

На етапі постановки задачі потрібно виявити проблему, яку потрібно вирішити. Починається дослідження об’єкта з виявленням його основних властивостей та характеристик. Будується структура об’єкта, досліджується вкладеність та підпорядкованість [4,6,7].

Для підготовки задачі до реалізації на комп’ютері її представляють у формальному вигляді, або ж, інакшими словами, у вигляді математичної моделі.

Великі комп’ютерні проекти (демографічні, кліматичні) потребують великих зусиль на ідентифікацію, перевірку, та виявлення великої кількості параметрів. Великою спокусою є бажання підвищити точність моделі за рахунок введення нових параметрів, виявити які неможливо. В реальності абсолютна точність досліджень на основі моделювання не є досяжною [3,5].

При вивченні поведінки моделі робиться висновок що до можливості використовувати отриману модель для дослідження об’єктів реального світу, або ж приймається рішення що до додаткових експериментів та коригування задачі, тоді весь цикл повторюється.

Інформаційні технології що підтримують обчислювальний експеримент, включають в себе методи побудови математичних моделей кінцевих користувачів інформаційних систем (спеціалістів у своїй предметній області), інформаційну підтримку діяльності для пошуку та вибору алгоритмів і програм чисельного вирішення задач, методи та засоби контролю точності підрахунків і коректності роботи програм, що використовуються [6,7].

При проведенні обчислювального експерименту дослідник має можливість за допомогою інтерфейсу користувача «гратися» з моделлю, змінюючи параметри що його цікавлять та отримувати належні результати.

Таким чином, користувач отримує потужний інструмент для аналізу та прогнозу поведінки складних багатопараметричних об’єктів і явищ, вивчення яких традиційними методами викликає ускладнення, або ж взагалі не є можливим.

Розробка програмного забезпечення для обчислювального експерименту в конкретній області призводить до створення великого програмного комплексу. Він складається з зв’язаних між собою прикладних програм та системних засобів, які включають в себе інструменти для керування обчислювальним експериментом, обробки та представлення результатів [3,6,7].

Такий комплекс ще називають проблемно-орієнтованим пакетом прикладних програм.

Але все ж таки модель не може замінити об’єкт. При вирішенні конкретної задачі модель часто стає єдиним інструментом для дослідження обраного об’єкта. В реальному часті оригінал вже може не існувати (минуле), або його ще не існує (майбутнє).

На основі відомих фактів методом гіпотез можна побудувати модель подій минулого. Таким же чином можна зазирнути в майбутнє.

Обчислювальний експеримент ні в якому разі не може замінити реальний. Однак, методи аналізу реального світу можуть бути використані у світі комп’ютерних моделей [4,7].

1. Вимоги до обчислювальних алгоритмів

На основі побудованих математичних моделей реального об’єкта виконується наступний етап математичного моделювання – побудова обчислювального алгоритму, що включає в себе його розробку та аналіз необхідних властивостей [5,7,9].

Після переходу від математичної моделі до дискретної скінченновимірної задачі потрібно побудувати сам обчислювальний алгоритм, тобто вказати послідовність дій що буде виконуватися комп’ютером [5].

Сюди ж відноситься і створення глобальної системи функціонування проекту, детальний опис всіх модулів, функцій, та змінних. Для однієї і тієї ж задачі може бути багато різних рішень, але це не означає що результати будуть однаковими.

Деякі алгоритми працюють краще на деякому класі задач, і гірше – на всіх інших. Деякі навпаки підходять під більшість загальних умов, але показують гірші результати на вузькопрофільних задачах з додатковими властивостями [8].

Для цього мають бути сформульовані критерії оцінки алгоритмів.

Основна вимога до обчислювальних алгоритмів- вимога точності. Воно означає що обчислювальний алгоритм має давати наближений розв’язок побудованої або обраної математичної моделі з заданою точністю β>0 за скінченну кількість дій Q(β).

Алгоритм має бути реальним в реалізації, тобто давати розв’язок задачі за допустимий машинний час. Для майже всіх алгоритмів час розв’язку задачі росте із збільшенням точності [7,8,11].

Також дуже важливою є вимога економічності. Під цим мається на увазі мінімальна кількість дій Q(β), і тим самим мінімальний машинний час [7,8,11].

При можливості сюди додається умова розпаралелювання алгоритму: можливість сегментації обчислень на прості підпроцеси, що періодично обмінюються даними [11].

Що до всієї системи, то її проектування має включати в себе аналіз всіх алгоритмів, побудова їх попередньої структури, шляхів сполучення, тощо.

1. Програмування модулів системи

Модульність є основним принципом побудови комп’ютерних систем.

Програмний модуль – відокремлена функціонально завершена програмна одиниця, що деяким чином ідентифікується та використовується в програмі. Модульний аналіз дозволяє виконувати відокремлене програмування окремих частин проекту програми та полегшує тестування проекту в цілому [12, 13].

Процес програмування будується за ітеративним принципом: спочатку кодуються найбільш важливі елементи алгоритму, а доповнення деталями та сервісними можливостями відбувається потім.

Програмні модулі мають задовольняти мінімальним вимогам:

− Розширюваність;

− Адаптивність;

− Переносимість.

Під розширюваністю варто розуміти те, що при створенні нових доповнень не потрібно переписувати старі.

Адаптивність програмного модулю має гарантувати продуктивність програмного комплексу в цілому [13,14].

В свою чергу¸ переносимість - здібність модуля до функціонування в програмному та апаратному середовищу з варіаціями його властивостей. Іншими словами, модуль не має залежати від комп’ютера та конкретної операційної системи [10,12].

2 Властивості та характеристики узагальнених моделей

В вивченні поняття моделі в його узагальненому вигляді точкою відправлення є положення матеріалістичної теорії пізнання:

− Кожен матеріальний об’єкт нескінченновимірний: в абсолютному сенсі він може бути охарактеризований лише нескінченною кількістю ознак, властивостей та характеристик;

− Знання що до оточення та об’єктів оточення на кожній ланці розвитку є відносними та скінченними: кожен реальний об’єкт містить недосліджені ознаки та властивості;

− Результатом пізнання є формування деякого ідеального образу що об’єктивно відображає деякі властивості та характеристики досліджуваного об’єкта. В формуванні цього образу беруть участь усі накоплені теоретичні представлення, досвід та навички суб’єкта що займається пізнанням;

− Пізнання носить цілеспрямований характер. В його основі в усіх випадках лежить задача яку вирішує суб’єкт заради досягнення поставлених цілей.

Наступним важливим уточненням є усвідомлення того що часто порівняно прості математичні залежності описують явища з великим ступенем правдоподібності.

Звісно, якщо спробувати описати геометричними кривими форму деякого складного об’єкту, то можна прийти до висновку що математика не підходить для опису подібних об’єктів. Однак деякі окремі властивості цього об’єкту можна виразити через більш прості функції з достатньою точністю [8, 15].

3 Проблема в кінематографії

Моделі використовуються всюди, на будь-яких підприємствах та у будь-яких організаціях. Десь вони вже пройшли стадію реалізації в готові програмні продукти, а десь використовуються неявно, лише за допомогою працівників закладів.

Попит породжує пропозицію, і різноманітність програмних продуктів з кожним роком зростає. Але далеко не всюди є можливість включити готове рішення, іноді для його впровадження необхідна деяка реорганізація підприємства.

В кінематографії наразі як раз той випадок, де готові рішення не мають змоги бути реалізованими «з коробки» через специфічність внутрішньої організації роботи підприємства.

Кожна мережа кінотеатрів має свої технології, обмеження на прокат, та часто навіть різні назви фільмів, що катаються. Отже використання єдиної системи для усіх стає просто неможливим. По друге, через різні інтерфейси та різні типи баз даних інтеграція будь якого стороннього продукту потребує додаткових грошових вкладень, та робочих часів для навчання.

1. Огляд існуючих програмних продуктів

Серед існуючих реалізацій програмного забезпечення які хоча б частково вирішували поставлену проблему є програми «Прем’єра» від R-keeper та КіноПлан.

Ці програми створені для того щоб полегшити проектування плану сеансів, однак доля роботи оператора все ще залишається дуже великою – програми скоректують заповнений вручну розклад, перевіривши обмеження на зали та технології, але аж ніяк не сформують хоча б базову версію розкладу самостійно.

Програма «Прем’єра» є готовим сервісом, в якому реалізовані модулі бронювання білетів, відслідковування наповненості залів, інтеграції з смс-розсилками, контролю прибутку, тощо.

Серед переваг цього сервісу слід відзначити наступні:

− Готова система, що може розширюватись, так як має модульну структуру;

− При формуванні розкладу оператором система перевіряє наявні обмеження;

− Внутрішня база прокату фільмів, в якій відображаються дані про стрічку.

Серед недоліків основним є те що Оператор сам складає розклад, і це займає дуже багато часу. Розклад не є оптимальним, деякі зали пустують, тощо.

Програма Кіноплан є більш цікавим інструментом для планування. Окрім функцій, вказаних у Прем’єрі, в Кіноплані реалізована можливість авто-розкладу згідно з обмеженнями, багато можливостей по налаштуванню показів, тощо.

Переваги цього сервісу більш вагомі:

− Можливість попереднього формування розкладу;

− Велика кількість налаштувань;

− Автоматично оновлювана інформація по кількості обмежень від дистриб’юторів на фільми;

− Візуально приємний інтерфейс;

− Багато інформації що до користування програмою;

− Модульність системи.

Однак формування розкладу поверх обмежень все одно потребує великої участі оператора.

Оцінка самого фільму, пошук попередніх рейтингів, намагання подумати за глядача та передбачити куди саме додати сеанси щоб прибуток був більший – на такі запити немає відповіді в жодному з аналогів.

Саме тому метою роботи стало створення програмного продукту, який відповів би на поставлені питання.

2. Особливості поставленої задачі

Принцип розподілу сеансів зараз доволі простий – вгадати на який час і в який зал прийде більше людей на конкретний фільм, та поставити туди його сеанс. Це доволі просто зробити коли фільмів близько 5-6, але в реальному оточенні це перетворюється на затратну по часу та ресурсам проблему.

Для того щоб мати уявлення про кількість глядачів на сеансі – потрібно знати хоча б частково інформацію про фільм. Ідеально було б знати скільки людей прийшло на нього вчора або позавчора, які були враження від перегляду, в який час було більше людей, тощо. Але є декілька нюансів. По-перше, такі дані відсутні для прем’єрних фільмів, та й кількість глядачів по вихідним та робочим дням буде сильно відрізнятися.

А по друге, які об’єми інформації потрібно кожного разу аналізувати людині-оператору, та скільки робочого часу на це витрачається не раціонально.

Відсутність єдиної бази фільмів також сильно підводить – у різних кінотеатрах фільм може називатися по різному, може відрізнятись прокатна інформація, дозволений вік для перегляду, тощо.

Також що до вікових обмежень (таблиця 1.1), то вони значно відрізняються від міжнародного стандарту, їх більше та вони часто пересікаються між собою, що не дає змоги однозначно класифікувати фільм.

Таблиця 1.1 – порівняння систем обмежень

|Американська система |Наша система |

|G |0+, 1+, 2+, 3+ |

|PG |6+, 7+ |

|PG-13 |12+, 14+ |

|R |18+, 21+ |

Крім того, ще даний перелік необхідних умов від кінотеатру, а саме :

− Встановлений час роботи залів;

− Різноманіття технологій в залах, обмеження на них;

− Часові рамки від дистриб’юторів – ранок, день, вечір.

Наразі всі ці обмеження перевіряються вручну.

4 Моделі та методи прогнозування

Система прогнозування – впорядкована сукупність методик, технічних засобів, що призначена для прогнозування складних явищ та процесів [9, 16].

Математична модель це або фізичне представлення математичних понять, або математичне представлення реальності [17].

Математична модель як фізичне представлення включає в себе репродукції твердих тіл з картону, дерева, пластику, чи інших матеріалів, і не є об’єктом нашого дослідження.

Набагато ширшим є поняття математичної моделі як способу представлення реальності через формули і математичні поняття.

В цілому, будь що в фізичному чи біологічному світі, не важливо, природне воно чи створене з впливом технологій та людини, може бути проаналізоване за допомогою математичних моделей, якщо його можна описати в математичних термінах [10, 18].

Моделі предметної області – математичні моделі прогнозування, для створення яких використовуються деякі відомі законі обраної предметної області. Для розробки таких моделей потрібен індивідуальний підхід та спеціаліст з обраної області [11].

Моделі часових рядів – моделі, за допомогою яких можна знайти залежність майбутніх значень від минулих в середині процесу, і через цю залежність обрахувати прогноз. Такі моделі є універсальними, їх зовнішній вигляд не змінюється від природи часового ряду [11].

[pic]

Рисунок 1.3- класифікація методів і моделей прогнозування

Що до класифікації моделей (рисунок 1.3), то в моделях предметної області таке представлення не є можливим – скільки областей стільки і моделей. На відміну від них, моделі часових рядів можна розділити на статистичні та структурні моделі [11, 19].

В статистичних моделях залежність минулого від майбутнього задається у вигляді рівняння [11]. До них відносяться такі як :

− Регресійні моделі;

− Авторегресійні моделі;

− Моделі єкспоненційного згладжування (модель Холта-Вінтерса, модель Брауна).

В структурних моделях залежність майбутнього значення від минулого задається у вигляді певної структури та правил переходу по ній [11, 21]. Наприклад:

− Моделі на базі нейронних мереж;

− Моделі на базі ланцюгів Маркова;

− Моделі на базі класифікаційно-регресійних дерев.

В роботі використано модель на базі класифікаційно-регресійних дерев, а саме модель Random forest.

5 Модель Random forest

Модель Random forest є ансамблем множини дерев рішень, що дозволяє знизити проблему перенавчання і підняти точність в порівнянні з одним деревом. Прогнозом є результат агрегування відповідей всіх дерев.

Тренування дерев проходить на різних підмножинах, що вирішує проблему побудови однакових дерев на сталому наборі даних.

Ансамбль — це набір простих моделей, які разом дають деяку відповідь. Одна з переваг їх використання – декілька моделей, які намагаються спрогнозувати одну й ту ж змінну дадуть кращий результат, ніж одна модель. Техніки ансамблювання надалі розділяються на беггінг та бустинг.

Для беггінгу є сенс використовувати велику кількість дерев рішень із достатньою глибиною. В результаті класифікації фінальним результатом буде той клас, за який проголосувала більшість дерев ансамблю.

Random forest потребує багато ресурсів, але обмеження на глибину\кількість дерев значно понижує точність, тому важливим етапом підготовки моделі є вибір оптимальних значень її гіперпараметрів.

До основних параметрів моделі відносяться:

− Кількість дерев;

− Кількість ознак для відокремлення;

− Мінімальна кількість об’єктів для яких виконується відокремлення;

− Обмеження на кількість об’єктів в листах;

− Максимальна глибина дерева;

− Критерій відокремлення.

Що до кількості дерев, то чим їх більше, тим вище якість алгоритму на тренувальній підмножині, а на тестовій якість виходить на деяку асимптоту. Це дозволяє підібрати необхідну кількість дерев для кожної задачі окремо.

Кількість ознак для відокремлення - чим більша кількість цих ознак, тим більш однакові дерева отримуємо в результаті. За замовчанням він рівний √n в задачах класифікації та n/3 в задачах регресії.

Мінімальна кількість об’єктів для яких виконується відокремлення – один з параметрів який можна залишати сталим – відповідає за рішення поділу отриманого листа.

Обмеження на кількість об’єктів в листах – схоже на попередній параметр, є обмеженням зверху на об’єм листа.

Максимальна глибина дерева – параметр що відповідає за якість моделі. Чим більша глибина, тим повільніше працює алгоритм. При використанні неглибоких дерев зміна параметрів об’єму листа не призводить до видимих змін. Через це рекомендується проводити тестування моделі на предмет виявлення найкращих параметрів.

Критерій відокремлення реалізований двома функціями MSE та MAE. Для класифікації реалізовані критерії gini та entropy.

Для підбору параметрів зазвичай використовується алгоритм перехресної перевірки по k блокам (k-fold Cross-Validation). Принцип його роботи полягає в тому що модель навчається на різних частинах тренувальної вибірки окремо і порівнюється результат.

Запускаючи кросс-валідацію ми отримуємо багато запусків моделі з різними параметрами, серед яких обирається та що показала найкращий результат.

В роботі використовувалась кросс-валідація для підбору найкращих параметрів, а потім виконувалось навчання і власне прогноз по кращій моделі.

6 Градієнтний бустинг

Коли ми намагаємося спрогнозувати цільову змінну за допомогою будь-якого алгоритма машинного навчання, головні причини відмінностей між реальними даними та прогнозом це шум, відхилення та зміщення. Ансамбль допомагає зменшити останні два фактори.

Бустинг — це техніка побудови ансамблів, в якій пре диктори будуються не окремо один від одного , а один за одним. Таким чином наступна модель буде навчатися на помилках попередньої. В них не рівноймовірна можливість появи, частіше з’являються більш грубі помилки.

Через те що предиктори вчаться на помилках попередників, алгоритм потребує набагато менше часу для того щоб отримати правильну відповідь.

Однак потрібно обирати правильний критерій зупинки – інакше можна стикнутися з проблемою перенавчання.

В роботі було використано градієнтний бустинг.

Градієнтним бустингом називають техніку машинного навчання для задач класифікації та регресії, яка будує модель прогнозу в формі ансамблю слабких моделей, зазвичай дерев рішень.

Мета будь якого алгоритму навчання з учителем – визначити функцію витрат та мінімізувати її.

Якщо в якості функції втрат взяти середньоквадратичну помилку (MSE) (1.1):

[pic] (1.1 )

де [pic] -реальне значення;

[pic] - прогнозоване;

[pic].

Прогноз має будуватися таким чином щоб MSE була мінімальною. Використовуючи градієнтний спуск та оновлюючи прогнози, проводиться пошук значень на яких показник буде мінімальним (1.2 – 1.3).

[pic] (1.2)

[pic] (1.3)

де α – швидкість навчання.

В цілому, весь механізм градієнтного бустингу полягає в наступному:

− Побудова простої моделі та аналіз помилок;

− Відбір точок що не вписуються в просту модель;

− Додавання моделей які обробляють точки з минулого пункту;

− Сбір усіх побудованих моделей та визначення ваги кожного предиктора.

7 Цілочисельне програмування

Математичними моделями дискретного програмування вирішується багато задач, пов’язаних з розподілом ресурсів, мережевим та календарним плануванням [22]. Загальна постановка задачі виглядає наступним чином (1.4 -1.8):

[pic] (1.4)

[pic] (1.5)

[pic] (1.6)

[pic] (1.7)

[pic], (1.8)

де D – скінченна або зліченна;

х – цілі числа [22].

Область допустимих розв’язків таких задач є не опуклою та незв’язною, що спричиняє багато труднощів при пошуку розв’язків. Зокрема, є неможливим перехід до неперервного аналогу дискретної задачі, та заокруглення розв’язку до найближчого цілого [22].

Вирішення поставленої задачі зводиться до розв’язку задачі про рюкзак.

Тільки замість цінності предмету в рюкзаку є орієнтовна кількість людей на сеансі, замість ваги рюкзака – максимальний час роботи залу, вага кожного предмету замінюється на час прокату фільму, і вводяться додаткові обмеження на кількість фільмів кожної технології. Отже цільовою функцією є максимізація прибутку кінотеатру.

Задача про рюкзак є однією з найбільш популярних задач цілочисельного програмування.

Турист готується до тривалого переходу в горах. Йому потрібно спакувати рюкзак з n речей, при чому у кожної речі є своя цінність [pic] та вага [pic]. Максимальна вага рюкзака не має перевищувати W кг. Предметів кожного виду необхідно взяти щоб сумарна цінність обраних предметів була максимальною та виконувались обмеження на масу рюкзака [22].

Така задача (1.9 - 1.11) є прикладом задачі комбінаторної оптимізації, та вирішується методом гілок та меж.

[pic], (1.9)

[pic] (1.10)

[pic] (1.11)

Принцип роботи методу - спрямований перебір варіантів розв’язків оптимізаційних задач зазначеного типу [22].

Ідея методу полягає в наступному:

1) Обчислюють деяку нижню (для задачі мінімізації) чи верхню (для задачі максимізації) оцінку цільової функції [pic]на допустимій множині розв’язків[pic](наприклад, її нижню чи верхню межу [22];

2) Множину [pic]певним способом розбивають на дві неперетинні підмножини, на кожній з яких рахують оцінки цільової функції[pic]. Підмножина з кращою оцінкою перспективніша для пошуку, тому її вибирають для подальшого галуження. Другу підмножину вважають кінцевою на цьому етапі. Якщо вона має строго більшу (меншу) оцінку, ніж вибрана для галуження, то надалі її вже не розглядають. Якщо ж відмінність в оцінках для обох підмножин нестрога, то на наступних кроках можливе повернення до якоїсь кінцевої підмножини [22];

3) Якщо з кожним новим галуженням оцінка цільової функції “не погіршується”, то отриманий на певному кроці цілочисловий (дискретний) розв’язок – оптимальний розв’язок відповідної початкової задачі. Інакше виконується повернення до однієї з попередніх кінцевих підмножин, що має кращу оцінку, ніж отримана на цьому етапі. Тоді процес поділу повторюється для іншої підмножини [22].

2. Постановка задачі

Для досягнення поставленої мети були вирішені наступні завдання:

1) Дослідити предметну область формування розкладу сеансів для кінотеатрів, виявити слабкі місця системи розподілу сеансів;

2) Провести пошук повних та часткових аналогів готових програмних рішень автоматизованого формування розкладу сеансів для кінотеатрів;

3) Побудувати математичну модель системи автоматизованого формування розкладу сеансів для кінотеатрів, враховуючи потреби користувача;

4) Протестувати створену модель автоматизованого формування розкладу сеансів для кінотеатрів на реальних даних.

8 Вимоги до програмного забезпечення

В роботі використано мову програмування Python, бібліотеки numpy, pandas, pymysql, json, requests, sklearn.

Програмний продукт розроблено в операційній системі Windows 10, але його використання не залежить від встановленої ОС.

Програмний код завантажено на сервер з доступом до візуального інтерфейсу.

Тестування проводилось в операційній системі Windows 10.

3. Висновки до розділу 1

В даному розділі було розглянуто поняття математичної моделі, моделювання, наведено визначення понять беггінгу та бустингу моделей. Приведено класифікацію математичних моделей і методів, визначено до якого класу методів відноситься вибраний для роботи алгоритм.

Розглянуто декілька способів вирішення задачі формування розкладу, а саме використання методів оптимізації на прикладі відомої задачі пакування рюкзака.

Проаналізовано зовнішнє оточення проекту, виявлено відсутність аналогів продукту.

Розглянуто основні вимоги до програмної реалізації модульних систем.

Окрім того, наведено стислу історію виникнення математичного моделювання.

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

РОЗДІЛ 2 ОПИС ПОБУДОВАНОЇ МАТЕМАТИЧНОЇ МОДЕЛІ ТА ПРОГРАМНИХ ЗАСОБІВ ДЛЯ ВИРІШЕННЯ ПОСТАВЛЕНОЇ ЗАДАЧІ

2.1 Первинний аналіз вхідних даних

Після отримання даних та їх очистки було вирішено дослідити структуру самих даних, виявити періодичність та основні фічі для подальшого моделювання.

Перша гіпотеза – кількість відвідувань залежить від дня тижня. Проілюструємо це на гістограмі (рисунок 2.1).

Рисунок 2.1 - залежність кількості відвідувань від дня тижня

Як видно з гістограми, в робочі дні (0-4) кількість відвідувань приблизно однакова, а у вихідні (5, 6) значно зростає. Різниця між вихідними не є значною, отож можна ввести ознаку для даних – вихідний чи робочий день. Це може допомогти пояснити моделі різницю в кількості людей для побудови більш точного прогнозу відвідувачів на майбутні сеанси.

Друга гіпотеза що до отриманих даних – в них наявна циклічність. Щоб виявити природу циклічності вирішено було візуалізувати частину даних (обраний період – 6 місяців) (рисунок 2.2).

Рисунок 2.2 - візуалізація даних, згрупованих по ознаці місяць - день тижня

Як можна побачити з наведеною вище гістограми (рисунок 2.2) в даних наявна місячна циклічність. Однак дані відносно місяців майже не змінні, тобто кількість відвідувань не залежить від місяця.

Розглянемо дані за 1 рік в розрізі сезонів (рис 2.3).

[pic]

Рисунок 2.3 - дані за 1 рік по сезонам

Як видно на круговій діаграмі, сезони зима весна та літо майже однакові, в межах 30%, а осінь лише 10%. Отже можна спробувати ввести в модель параметр сезонності для уточнення прогнозу кількості людей в залежності від дати прокату фільму.

Наступною ідеєю було перевірити вплив технології на кількість відвідувань (рисунок 2.4).

[pic]

Рисунок 2.4 - вплив технології прокату на к-ть відвідувань

Як видно з гістограми (рис 2.4) к-ть відвідувань залежить від виду технології, отже можна ввести їх як ознаку для майбутньої моделі.

Для вирішення проблеми отримання базової інформації по фільму було запропоновано створити веб-сервіс який би скрапив необхідну інформацію та оновлював її по запиту.

Наступним кроком став аналіз контенту(рисунок 2.5) – чи є якісь популярні жанри, які можна було б виділити в окрему ознаку. Дані по жанрам було взято з IMDB.

[pic]

Рисунок 2.5 - Аналіз контенту. Жанри

Найбільш часто зустрічалися жанри Драма Трилер та Экшн, але після експертного аналізу прийшли до висновку що цих даних замало для характеристики фільму.

Крім жанрів було проаналізовано теги та експертним шляхом виділено основні теги що відповідають певним групам фільмів.

Ще була сформульована гіпотеза що до залежності к-ті відвідувань від часу сеансу (рисунок 2.6).

Рисунок 2.6 - залежність відвідувань від часу сеансу

На діаграмі (рисунок 2.6) взято двох-годинні проміжки для кожного сеансу наступним чином (2.1):

[pic] (2.1 )

Оскільки колонки достатньо відрізняються одна від одної, то можна спробувати ставити сеанси саме в ці проміжки, що значно спростить розстановку сеансів по кожному залу окремо.

2.2 Структура програмного комплексу

Для побудови математичної моделі необхідно було проаналізувати недоліки існуючих систем та включити в модель варіанти їх вирішення.

Основними недоліками існуючих систем є ручне формування розкладу та відсутність бази фільмів з обновлюваною інформацією.

Щоб вирішити проблему нестачі інформації по прокату фільму для створення розкладу, було вирішено розробити програму яка б шукала схожі фільми по наданій інформації з IMDB.

Враховуючи отримані дані, першою спробою стало спробувати розбити фільми на категорії за основними жанрами. Але після тесту було виявлено наступні недоліки:

− Багато фільмів мають три та більше жанрів;

− На мультики рекомендує дорослі фільми та навпаки.

Через це було вирішено спробувати поділити фільми за віковими обмеженнями.

Так як дані бралися з IMDB, то вікові обмеження були наступними ( таблиця 2.1):

Таблиця 2.1 – Шифр вікових обмежень

|Значення |Сутність |

|G |Документальні фільми, дитячі мультики |

|PG |дитячі мультики з рейтингом 3+ і більше |

|PG-13 |Підліткові фільми, комедії |

|R |Доросле кіно |

Після детального аналізу фільмів в кожній категорії було прийнято рішення поділити їх на дві частини: [G, PG] [PG-13, R].

Маючи інформацію по країні виробнику, був виконаний поділ на категорії:

− Українські та російські фільми;

− Французьке кіно;

− Інші.

Окрім жанрів, у фільмів були наявні теги. Їх було вирішено використати разом з жанрами, методика поділу обиралась вручну.

Такий підхід після значної кількості змін в структурі категорій врешті решт привів до непоганого результату.

Для реалізації формування базового розкладу було вирішено використати градієнтний бустинг на основі Random Forest для прогнозу кількості людей та вибір сеансів з найбільшою кількістю людей в межах встановлених мінімальних обмежень для формування остаточного розкладу.

Модель системи було поділено на три частини (рисунок 2.7).

Перша частина відповідає за збір даних, їх обробку, та підбір схожих фільмів.

Друга частина працює напряму з даними користувача кожного конкретного кінотеатру, завантажує та обробляє їх, та на їх основі робить передбачення для можливої кількості людей на сеанс. Цим вирішується проблема недостатньої кількості інформації про фільм що виходить – підбираються схожі фільми про які багато історії, та на їх основі навчається модель.

Третя частина розроблена для того щоб врахувати обмеження дистриб’юторів, а також обмеження на кількість сеансів в кожному залі, враховуючи години роботи кожного кінотеатру.

В цілому ці три частини склали програмний комплекс, який було вирішено реалізувати мовою програмування Python, та випробувати на реальних даних одної з мереж кінотеатрів України.

Рисунок 2.7– Модель створеної математичної моделі

На першому етапі було виконано збір даних. За основу взяли базу IMDB, та його дзеркало - api TMDB (таблиця 2.2).

Таблиця 2.2 – Приклад отриманих даних

|Назва |Приклад значення |Формат |

|MovieName |Alita: Battle Angel |string |

|MovieRating |7.6 |float |

|Genres |Action, Adventure, Sci-Fi |Array of string |

|Rate |PG-13 |string |

|Tags |robot, future, anime |array of string |

|Country |USA, Japan |array of string |

|Director |Robert Rodriguez |array of string |

|Actors | Rosa Salazar, Christoph Waltz, Jennifer Connelly |array of string |

Надалі цей механізм отримання даних було автоматизовано до стану онлайн отримання даних через мікросервіс.

Механізм його роботи описується наступним алгоритмом (рисунок 2.8) :

[pic]

Рисунок 2.8 - Механізм роботи мікросервісу для отримання даних

Користувач подає запит у вигляді POST запиту наступного формату (2.2 – 2.3):

http://[host]:[port]/statistics/current (2.2)

body: {"imdbId":”tt0437086”} (2.3)

Запит надходить до сервера, зчитується інформація з поля imdbId. Виконується перевірка, чи є фільм в локальній базі сервера, якщо так то інформація по фільму повертається з локальної бази без підключення до бази IMDB.

Якщо ж фільм відсутній у базі, то запускається виконання скрапінгу по сторінці на сайті IMDB, інформація надходить у локальну базу, зберігається там і віддає отриману інформацію користувачу.

Також там є окремі команди що відповідають за оновлення бази, отримання усіх фільмів, тощо. Автоматично оновлення завантажуються в момент найнижчої активності системи – вночі.

Надалі інформація по новому фільму завантажується в другу частину алгоритму, яка здійснює підбір схожих фільмів.

Принцип її роботи наступний (рисунок 2.9):

[pic]

Рисунок 2.9 - Механізм підбору схожих фільмів

Сама модель підбору була перевірена на багатьох фільмах та експертна оцінка що до результатів її роботи є хорошою.

Спершу ми відбираємо фільми по віковим обмеженням на 2 категорії: [G, PG] та [PG-13, R] , відділяючи таким чином дитячі мультики від фільмів.

Далі сортуємо за країною-виробником, відділяючи в окрему категорію українські та російські фільми (рисунок 2.10). Крім того, в деяких країнах є своє, авторське кіно, цей підбір вирішує проблему таких рекомендацій.

[pic]

Рисунок 2.10 – сортування за країною-виробником

Третій пункт – сортування у звязці «Жанр +Тег». Теги, які вирізняють цілі розділи фільмів, такі як Space, Postapokalypsis, Alien, Timeloop, тощо. В об’єднанні з тегами вони дають хороший результат (рисунок 2.11 – 2.12).

[pic]

Рисунок 2.11 – сортування у звязці жанр+тег

Четвертим пунктом є градація у отриманих списках фільмів. Також за допомогою цієї частини знаходимо сіквели та приквели до фільмів. Зазвичай в них однакові режисери та здебільшого співпадає акторський склад. Ця градація присвоює оцінки (в залежності від кількості збігів), та список сортується вже за цією оцінкою.

Останнім етапом є використання моделі Word2Vec, що являє собою інструмент для аналізу семантики природніх мов. Його технологія базується на векторному представленні слів. Модель вчиться в режимі CBOW, намагаючись передбачити слово, виходячи з його контексту. На виході отримуємо координатні представлення векторів-слів, для яких обчислюємо семантичну відстань між словами.

За допомогою цього показника відбувається сортування всередині списку отриманих фільмів – щоб виловити найбільш схожі за тегами фільми в обраній категорії.

Надалі з кожної відсортованої категорії обирається топ-10 найкращих фільмів. Ця частина програми також виконана як окремий мікросервіс (Додаток А рисунок 1, рисунок2).

Наступна частина програми – прогноз кількості сеансів фільмів по статистичним даним (рисунок 2.13).

[pic]

Рисунок 2.13 - прогноз кількості сеансів фільмів по статистичним даним

Спершу дані прокату фільмів формуються користувачем в візуальному інтерфейсі.

Після введення коду ІMDB та натискання кнопки «Заполнить» формується запит до мікросервісу отримання даних, та інші поля заповнюються автоматично (Додаток А рисунок 3).

У випадку якщо щось введено некоректно або не відповідає прокатній картці фільму, користувач може внести зміни та зберегти інформацію в програмі.

Далі з прокатної картки переноситься інформація що до обмежень прокату – в яких технологіях, в який період часу та скільки мінімально разів має кататися фільм щоденно (Додаток А рисунок 4).

Надалі користувач вибирає необхідні фільми, кінотеатр, та кількість днів для формування розкладу. Ці дані записуються в файл json, і подаються на вхід програми.

Далі з бази даних користувача завантажується існуюча інформація про фільм. Це має бути дані про прем’єру фільму, та якщо фільм вже є в прокаті – то статистичні дані минулих днів прокату.

Використовуючи попередній алгоритм підбору схожих фільмів, отримуємо топ-3 схожих фільми. Якщо отриманої інформації по ним не достатньо, то береться більша кількість фільмів.

За списком внутрішніх ID фільмів завантажуємо статистичні дані з Бази продажів. Інформація що отримується, виглядає наступним чином (таблиця 2.3):

Таблиця 2.3 – Інформація з «Бази продажів»

|Змінна |Значення |

|CountID |К-ть куплених білетів |

|Movie_code |Внутрішній код фільму |

|Data |Дата сеансу |

Продовження таблиці 2.3

|Змінна |Значення |

|Time |Час сеансу |

|TehCode |Технологія |

|Cinema |Кінотеатр |

|Hall |Зал |

|Seats |К-ть місць |

|sessionID |Номер сеансу |

Після цього проводиться обробка даних.

Час сеансу розбивається відповідно (2.4):

[pic] (2.4)

Кожна цифра – двогодинний проміжок, куди можна поставити фільм. Таких проміжків 7, в подальшому проміжки від 1 до 4 – це ранок, а від 5 до 7 - вечір. Це необхідно для розбиття часового інтервалу трансляції на денні та вечірні сеанси.

Далі програмно створюються нові змінні (таблиця 2.4):

− Кількість днів з релізу фільму;

− День тижня;

− Вихідний чи робочий день;

− Час сеансу.

Таблиця 2.4 – нові змінні для моделі

|Змінна |Формат даних |

|Кількість днів з релізу фільму |[1,2,3,…] |

|День тижня |[0,1,2,…,6] |

|Вихідний чи робочий день |[0,1] |

|Час сеансу |[1,2,3,4,5,6,7] |

На цьому підготовка даних завершується та починається налаштування моделі.

Надалі дані завантажуються в модель, проводиться підбір гіперпараметрів за допомогою кросс-валідації. Для кожного випадку обирається найкраща модель, яка надалі використовується для прогнозу.

Коли модель навчена, ми формуємо базову таблицю для прогнозу, в яку вводимо всі можливі варіанти розташування сеансу в кожному залі (рисунок 2.14).

[pic]

Рисунок 2.14 – приклад можливого розташування сеансів по трьом залам в трьох технологіях

Ця таблиця передається на вхід навченої моделі, яка повертає прогноз для кожного залу кінотеатру кількості людей що прийдуть на сеанс.

Наступний крок – відсів варіантів які не задовольняють умовам прокату фільму.

Завантажуємо інформацію що до прокату фільму з бази (рисунок 2.15)

[pic]

Рисунок 2.15 – приклад інформації прокату фільмів

Після цього проводимо відсів варіантів які не задовольняють технічним обмеженням кінотеатру.

На цьому етапі з бази завантажується інформація про зали та технології (рисунок 2.16).

Рисунок 2.16 – приклад інформації про зали та технології

В формуванні вибірки враховано що різні технології катаються у різних залах.

Далі необхідно скоригувати кількість сеансів згідно з обмеженнями дистриб’ютора. Мається на увазі що користувач може ввести мінімально необхідну кількість сеансів за ранок, вечір, або в цілому за день по кожній технології.

Було розглянуто два варіанти розв’язку цієї задачі.

2.3 Використання методів оптимізації

Ця задача в загальному вигляді є NP-повною задачею комбінаторної оптимізації. Її відомий аналог – задача про рюкзак.

Якби кількість сеансів не була цілим числом, то потрібно було б вирішувати задачу лінійного програмування за допомогою симплекс-методу. Але внаслідок додаткового обмеження на цілочисельність задача зводиться до задачі цілочисельного лінійного програмування та вирішується методом гілок та меж.

Постановка задачі виглядає наступним чином (2.5-2.8):

[pic] (2.5)

[pic], (2.6)

[pic], (2.7)

[pic]. (2.8)

де 2.5 – цільова функція максимізації кількості людей в день,

2.6 - обмеження дистриб’юторів,

2.7- обмеження на кількість сеансів у залі,

[pic] – кількість сеансів i-того фільму в j-тому залі,

n – кількість фільмів,

m – кількість сеансів,

В – обмеження дистриб’юторів, t- час прокату фільмів,

А – прогнозована кількість людей з попереднього алгоритму.

Після вирішення цієї задачі отримуємо розклад для обраного кінотеатру з урахуванням обмежень як дистриб’юторів так і обмежень залів.

Однак проблема виявилась в тому що при введенні обмежень на кожен фільм утворювалось дуже багато умов виду (2.6) і (2.7), і сам алгоритм відпрацьовував дуже довго через велику кількість змінних та умов.

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

Тому вирішено було спростити алгоритм формування розкладу.

2.4 Двох-етапне формування розкладу

В другому алгоритмі є дві частини: за допомогою першої формується базовий розклад лише по обмеженням, а за допомогою другої додаються сеанси на порожні місця розкладу.

Це зручно тим що окрім сеансів фільмів, користувач може додавати внутрішні події, які не можна ввести через загальну форму створення фільму. Це можуть бути допрем’єрні покази, фестивалі кіно, фільми поза зоною прокату, тощо.

2.4.1 Формування базового розкладу по обмеженням дистриб’юторів

Після того як було отримано прогнозну кількість відвідувачів, с бази завантажуються обмеження дистриб’юторів на прокат кожного фільму.

Вони бувають трьох типів:

− Обмеження ранкових сеансів;

− Обмеження вечірніх сеансів;

− Обмеження на день.

На кожен фільм кожної технології є такі обмеження. У випадку якщо нічого не вказано, то фільм можна не включати в розклад.

В цих обмеженнях також вказано розбиття періодів на ранок та вечір, в нашому випадку це з 10:00 до 17:59 та з 18:00 до 00:00.

Після отримання даних, відбувається сортування за тижнем прокату (більш нові вище), та за кількістю сеансів на день.

З отриманих даних що до прогнозу в порядку черги обираються сеанси з найбільшою кількістю людей вказаного фільму та технології відповідно, після чого вони додаються в розклад. Сеанси на цей час в обраному залі видаляються з базової таблиці прогнозу. Таким чином заповнюється базовий розклад, та починається другий етап.

2.4.2 Формування повного розкладу

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

При додаванні нового сеансу в таблицю розкладу, з таблиці прогнозів видаляються всі сеанси на цей час в цьому залі, таким чином запобігаючи нашаруванню сеансів один на одного.

Крім того встановлено лічильник на кількість однакових фільмів у розкладі. Експертна оцінка цієї цифри - показник не має перевищувати обмежень дистриб’юторів на п’ять позицій.

Далі таблиця оформлюється в файл з розширенням json, та заливається на сервер з візуальною частиною.

2.5 Висновки до розділу 2

В другому розділі було виконано детальний огляд предметної області кінематографії, попередній аналіз можливих ознак для побудови математичної моделі прогнозу очікуваної кількості людей на сеанс та розроблено загальну модель системи для проектування програмного комплексу формування розкладу на основі передбачень кількості людей на сеанси.

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

РОЗДІЛ 3 ОПИС ВИПРОБУВАНЬ ПРОДУКТУ

3.1 Сервіс для підбору схожих фільмів

Тестування сервісу для підбору схожих фільмів проводилось за рахунок кінцевих користувачів. Продукт розміщено на сервері замовника (рисунок 3.1-3.2).

Рисунок 3.2 – Вхідна сторінка сервісу для підбору схожих фільмів

Рисунок 3.3 – вікно підбору схожих фільмів

В сервісі є можливість редагувати теги, жанри, продивлятись коротку інформацію по фільму, також користувач може додавати схожі, на його думку, фільми. Всі зміни зберігаються в базі, звідки відбуваеться завантаження інфомації на тестовий та справжній сайти.

Крім того при наведенні курсору на стручку схожих фільмів, знизу зявляється пояснення, за якими критеріями була збіжність.

3.2 Програмний комплекс для формування розкладу

Тестування створеного програмного продукту проводилось в три етапи.

Першим етапом була перевірка точності роботи алгоритму підбору схожих фільмів. Тестування виконувалось експертами зі сторони кінцевого користувача. Результат цієї перевірки підтвердив достатню коректність результатів підбору.

Алгоритм користування цим модулем доволі простий:

а) Перейти на вкладку з вікном вибору фільмів для аналізу (Додаток А рисунок 1);

б) натиснути на фільм з зони «зараз в прокаті», або з «випадково обрані фільми», або зі списку пошуку.

Після цього внизу буде топ-10 фільмів, схожих на обраний фільм.

Для того щоб подивитися який відсоток їх схожості, потрібно натиснути на іконку ока в верхньому правому куті сторінки.

Ще реалізована можливість подивитися за якими параметрами вони схожі – для цього потрібно навести курсор на схожий фільм.

Надалі потрібно було перевірити коректність роботи моделі що прогнозувала кількість людей, які відвідають сеанс.

Для цього з історії було взято розклад одного з тижнів прокату фільмів.

Для тестування було завантажено:

1. Реальну кількість відвідувачів (рисунок 3.3);

2. Прогноз моделі що до кількості відвідувачів на існуючий розклад (рисунок 3.4);

3. Запропонований розклад розміщення з очікуваною кількістю людей (рис .3.5).

[pic]

Рисунок 3.3- Реальна кількість відвідувачів кінотеатру на сеанс

Рисунок 3.4 - Кількість відвідувачів кінотеатру на сеанс за прогнозом

[pic]

Рисунок 3.5 – Розклад моделі на той самий день

Перевірка виявила що модель непогано вловлює тренди, але сама точність цифр є відносною.

Після створення прототипу програми необхідно було спробувати ввести в неї реальні дані та порівняти їх з реальними розкладами сеансів (рисунок 3.4-3.5).

Було виявлено що створений програмний продукт ефективно проектує план сеансів на день, враховуючи всі необхідні нам параметри по фільмах.

Висновки до розділу 3

Тестування створеного програмного продукту було проведено в три етапи.

За результатами експертної оцінки першого етапу модель підбору схожих фільмів була визнана робочою.

За результатами другого етапу було з’ясовано що модель непогано вловлює тренди, але сама точність цифр є відносною.

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

РОЗДІЛ 4 ЕКОНОМІЧНА ЧАСТИНА

У даному розділі проводиться оцінка основних характеристик програмного продукту, призначеного для оцінки та прогнозування багатофакторних ризиків. Інтерфейс користувача був розроблений за допомогою мови програмування Python у середовищі розробки Python Jupyter.

Програмний продукт призначено для використання на персональних комп’ютерах під управлінням операційної системи Windows, Linux.

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

Функціонально-вартісний аналіз (ФВА) – це технологія, яка дозволяє оцінити реальну вартість продукту або послуги незалежно від організаційної структури компанії. Як прямі, так і побічні витрати розподіляються по продуктам та послугам у залежності від потрібних на кожному етапі виробництва обсягів ресурсів. Виконані на цих етапах дії у контексті метода ФВА називаються функціями.

Мета ФВА полягає у забезпеченні правильного розподілу ресурсів, виділених на виробництво продукції або надання послуг, на прямі та непрямі витрати. У даному випадку – аналізу функцій програмного продукту й виявлення усіх витрат на реалізацію цих функцій.

Фактично цей метод працює за таким алгоритмом:

• визначається послідовність функцій, необхідних для виробництва продукту. Спочатку – всі можливі, потім вони розподіляються по двом групам: ті, що впливають на вартість продукту і ті, що не впливають. На цьому ж етапі оптимізується сама послідовність скороченням кроків, що не впливають на цінність і відповідно витрат.

• для кожної функції визначаються повні річні витрати й кількість робочих часів.

• для кожної функції на основі оцінок попереднього пункту визначається кількісна характеристика джерел витрат.

• після того, як для кожної функції будуть визначені їх джерела витрат, проводиться кінцевий розрахунок витрат на виробництво продукту.

4.1 Постановка задачі техніко-економічного аналізу

У роботі застосовується метод ФВА для проведення техніко-економічного аналізу розробки. Відповідно до цього необхідно обирати систему показників якості програмного продукту.

Технічні вимоги до продукту наступні:

– програмний продукт повинен функціонувати на персональних комп’ютерах із стандартним набором компонент;

– забезпечувати високу швидкість обробки великих об’ємів даних у реальному часі

– забезпечувати зручність і простоту взаємодії з користувачем або з розробником програмного забезпечення у випадку використовування його як модуля;

– передбачати мінімальні витрати на впровадження програмного продукту.

Головна функція [pic] – розробка програмного комплексу, який, використовуючи прогнозну модель відбудовує розклад.

Виходячи з конкретної мети, можна виділити основні функції ПП:

[pic] – вибір мови програмування

[pic] – вибір інструментів для прогнозу

[pic] – обробка даних для представлення в розкладі.

Кожна з основних функцій може мати декілька варіантів реалізації.

Функція [pic]:

а) мова програмування С++;

б) мова програмування Python;

Функція [pic]:

а) вбудовані бібліотеки та методи;

б) власна реалізація бібліотек та методів

Функція [pic]:

а) використання вбудованих функцій для формування розкладу;

б) власна реалізація пакету потрібних функцій.

4.2 Варіанти реалізації основних функцій

Морфологічна карта (Рисунок 4.1) відображає всі можливі поєднання варіантів реалізації функцій, які складають повну множину варіантів ПП. На основі карти було побудовано позитивно-негативну матрицю варіантів основних функцій (таблиця 4.1).

Рисунок 4.1 Морфологічна карта

Таблиця 4.1 - позитивно-негативна матриця варіантів основних функцій

|Основні функції |Варіанти реалізації |Переваги |Недоліки |

|F1 |А |Простота реалізації |Проблеми з версіями бібліотек |

| |Б |Стабільні класи та потоки |Складна структура коду |

|F2 |А |Широкий набір бібліотек |Потребує більших знать для використання |

| |Б |Простий у виконанні |Потребує дописування основних функцій |

|F3 |А |Наявність явного розпаралелювання |Написання власних методів |

| |Б |Готові бібліотеки |Відсутність явного розподілу навантаження |

| | | |між ядрами |

На основі аналізу позитивно-негативної матриці робимо висновок, що при обрані програмного продукту деякі варіанти реалізації функцій варто відкинути, тому, що вони не відповідають поставленим перед програмним продуктом задачам. Ці варіанти відзначені у морфологічній карті.

Функція F1: Оскільки потрібно буде підстроювати програму під кінцевого користувача, то структура ПО повинна бути якомога простішою, тому варіант б) має бути відкинутий.

Функція F2: Реалізовані функціїї кращі за написані власноруч, тож відкинемо варіант б).

Функція F3: Виведення результату роботи програми у варіантах а) та б) гідне розгляду. Таким чином, будемо розглядати такі варіанти реалізації ПП:

1. F1а – F2а – F3а

2. F1а – F2a – F3б

Для оцінювання якості розглянутих функцій обрана система параметрів, описана нижче.

4.3 Обґрунтування системи параметрів ПП

4.3.1 Опис параметрів

На підставі даних про основні функції, що повинен мати програмний

продукт, вимог до нього, визначаються основні параметри виробу, що будуть

використані для розрахунку коефіцієнта технічного рівня.

Для того, щоб охарактеризувати програмний продукт, будемо використовувати

наступні параметри:

X1 – швидкодія мови програмування;

X2 – об’єм пам’яті для збереження даних;

X3 – швидкість взаємодії з користувачем;

X4 – потенційний об’єм програмного коду.

4.3.2 Кількісна оцінка параметрів

Гірші, середні і кращі значення параметрів вибираються на основі вимог

замовника й умов, що характеризують експлуатацію ПП як показано у таблиця 4.2

Таблиця 4.2

|Назва |Умовні позначення |Одиниці виміру |Значення параметра |

|Параметра | | | |

| | | |гірші |середні |кращі |

|Швидкодія мови програмування |X1 |нс/Оп |200 |50 |1 |

|Об’єм пам’яті для коректної роботи |X2 |Мб |32 |16 |8 |

|Час виводу результату |X3 |мс |2000 |840 |120 |

|Потенційний об’єм програмного коду |X4 |кількість рядків коду |1800 |1200 |900 |

За даними таблиці 4.2 будуються графічні характеристики параметрів (рисунок 4.2 – рисунок 4.5).

[pic]

Рисунок 4.2 – [pic], швидкодія мови програмування

[pic]

Рисунок 4.3 – [pic], об’єм пам’яті для коректної роботи

[pic]

Рисунок 4.4 – [pic], час виведення результату

[pic]

Рисунок 4.5 – [pic], потенційний об’єм програмного коду

4.3.3 Аналіз експертного оцінювання параметрів

Після детального обговорення й аналізу кожний експерт оцінює ступінь важливості кожного параметру для конкретно поставленої цілі – розробка програмного продукту, який дає найбільш точні результати при знаходженні параметрів моделей адаптивного прогнозування і обчислення прогнозних значень.

Значимість кожного параметра визначається методом попарного порівняння. Оцінку проводить експертна комісія із 7 людей. Визначення коефіцієнтів значимості передбачає:

- визначення рівня значимості параметра шляхом присвоєння різних рангів;

- перевірку придатності експертних оцінок для подальшого використання;

- визначення оцінки попарного пріоритету параметрів;

- обробку результатів та визначення коефіцієнту значимості.

Результати експертного ранжування наведені у таблиці 4.3.

Таблиця 4.3 – Результати ранжування параметрів

|Познач. параметра |Назва параметра |Одиниці виміру |Ранг параметра за оцінкою експерта|

| |1 |2 |3 |4 |

| |[pic] |[pic] |[pic] |[pic] |[pic] |

|[pic] ([pic]) |А |11000 |1,8 |0,158 |0,2844 |

|[pic]([pic], [pic]) |А |19 |3,7 |0,208 |0,7696 |

|F3([pic]) |А |760 |3,8 |0,274 |1,0412 |

| |Б |60 |5,4 |0,361 |1,9494 |

За даними з таблиці 4.6 за формулою (4.10)

[pic] (4.10)

визначаємо рівень якості кожного з варіантів (4.11-4.12):

[pic] = 0,2844 + 0,7696 + 1,0412 = 2,0952 (4.11)

[pic] = 0,2844 + 0,7696 + 1,9494 = 3,0034 (4.12)

Як видно з розрахунків, кращим є другий варіант, для якого коефіцієнт технічного рівня має найбільше значення.

4.5 Економічний аналіз варіантів розробки ПП

Для визначення вартості розробки ПП спочатку проведемо розрахунок трудомісткості.

Всі варіанти включають в себе два окремих завдання:

1. Розробка проекту програмного продукту;

2. Розробка програмної оболонки;

Завдання 1 за ступенем новизни відноситься до групи А, завдання 2 – до групи Б. За складністю алгоритми, які використовуються в завданні 1 належать до групи 1; а в завданні 2 – до групи 3.

Для реалізації завдання 1 використовується довідкова інформація, а завдання 2 використовує інформацію у вигляді даних.

Проведемо розрахунок норм часу на розробку та програмування для кожного з завдань. Загальна трудомісткість обчислюється як (4.13):

[pic] (4.13)

де [pic] – трудомісткість розробки ПП;

[pic] – поправочний коефіцієнт;

[pic] – коефіцієнт на складність вхідної інформації;

[pic] – коефіцієнт рівня мови програмування;

[pic] – коефіцієнт використання стандартних модулів і прикладних програм;

[pic] – коефіцієнт стандартного математичного забезпечення.

Для першого завдання, виходячи із норм часу для завдань розрахункового характеру степеню новизни А та групи складності алгоритму 1, трудомісткість дорівнює: [pic] = 120 людино-днів. Поправочний коефіцієнт, який враховує вид нормативно-довідкової інформації для першого завдання: [pic] = 1.7. Поправочний коефіцієнт, який враховує складність контролю вхідної та вихідної інформації для всіх семи завдань рівний 1: [pic] = 1. Оскільки при розробці першого завдання використовуються стандартні модулі, врахуємо це за допомогою коефіцієнта [pic] = 0.8. Тоді, за формулою 4.1, загальна трудомісткість програмування першого завдання дорівнює (4.14):

[pic] = 120⋅1.7⋅0.8 = 163.2 людино-днів. (4.14)

Проведемо аналогічні розрахунки для подальших завдань.

Для другого завдання (використовується алгоритм третьої групи складності, степінь новизни Б), тобто [pic] = 30 людино-днів, [pic] = 0.9, [pic] = 1, [pic] = 0.8 (4.15):

[pic] = 30 ⋅ 0.9 ⋅ 0.8 = 21.6 людино-днів.(4.15)

Найбільш високу трудомісткість має варіант IІ.

В розробці беруть участь два програмісти з окладом 14000 грн., один фінансовий аналітик з окладом 12000 грн. Визначимо зарплату за годину за формулою (4.16):

[pic] (4.16)

де [pic] – місячний оклад працівників;

[pic] – кількість робочих днів тиждень;

[pic] – кількість робочих годин в день.

[pic] (4.17)

Тоді розрахуємо заробітну плату за формулою (4.18)

[pic], (4.18)

де [pic]– величина погодинної оплати праці програміста;

[pic] – трудомісткість відповідного завдання;

[pic] – норматив, який враховує додаткову заробітну плату.

Зарплата розробників за варіантами становить (4.19 -4.20):

I.    [pic] = 79,37⋅ 1689.6 ⋅ 1.2 = 160924, 27 грн. (4.19)

II.    [pic] = 79,37⋅ 1706.48 ⋅ 1.2 = 162531, 99 грн. (4.20)

Відрахування на єдиний соціальний внесок в залежності від групи професійного ризику (II клас) становить 22%(4.21 -4.22):

I.    [pic] = [pic] ⋅ 0.22 = 160924,77*0.22 = 35403,45 грн. (4.21)

II.    [pic] = [pic] ⋅ 0.22 = 162531,99*0.22 = 35757,04 грн. (4.22)

Тепер визначимо витрати на оплату однієї машино-години. ([pic])

Так як одна ЕОМ обслуговує одного програміста з окладом 14000 грн., з коефіцієнтом зайнятості 0,2 то для однієї машини отримаємо (4.23) :

[pic] = 12 ⋅ [pic] ⋅ [pic] = 12 ⋅ 14000⋅ 0,2 = 33600 грн. (4.23)

З урахуванням додаткової заробітної плати (4.24):

[pic] = [pic]⋅ (1+ [pic]) = 33600⋅ (1 + 0.2) = 40320 грн. (4.24)

Відрахування на єдиний соціальний внесок (4.25):

[pic] = [pic] ⋅ 0.22 = 40320 * 0,22 = 8870,4 грн. (4.25)

Амортизаційні відрахування розраховуємо при амортизації 25% та вартості ЕОМ – 25000 грн.

[pic] = [pic] ⋅ [pic]⋅ [pic] = 1.15 ⋅ 0.25 ⋅ 25000 = 7187,5 грн., (4.26)

де [pic] – коефіцієнт, який враховує витрати на транспортування та монтаж приладу у користувача;

[pic] – річна норма амортизації;

[pic] – договірна ціна приладу.

Витрати на ремонт та профілактику розраховуємо як (4.27):

[pic] = [pic] ⋅ [pic] ⋅ [pic] = 1.15 ⋅ 25000 ⋅ 0.05 = 1437,5 грн., (4.27)

де [pic] – відсоток витрат на поточні ремонти.

Ефективний годинний фонд часу ПК за рік розраховуємо за формулою (4.28):

[pic]

[pic] годин, (4.28)

де [pic] – календарна кількість днів у році;

[pic] – відповідно кількість вихідних та святкових днів;

[pic] – кількість днів планових ремонтів устаткування;

[pic] –кількість робочих годин в день;

[pic]– коефіцієнт використання приладу у часі протягом зміни.

Витрати на оплату електроенергії розраховуємо за формулою (4.29):

[pic] = [pic]⋅ [pic]⋅ [pic]⋅ [pic] =1706,4 ⋅ 0,156 ⋅ 0,85 ⋅ 2,7515= 622,58 грн. (4.29)

де [pic] – середньо-споживча потужність приладу;

[pic] – коефіцієнтом зайнятості приладу;

[pic] – тариф за 1 КВт-годин електроенергії.

Накладні витрати розраховуємо за формулою (4.30):

[pic] = [pic] ⋅ 0.67 = 25000⋅ 0,67 =16750 грн. (4.30)

Тоді річні експлуатаційні витрати будуть (4.31 -4.32):

[pic] (4.31)

[pic] = 40320 + 8870,4 + 7187,5 + 1437,5+ 622,58 + 16750 = 75187,98 грн. (4.32)

Собівартість однієї машино-години ЕОМ дорівнюватиме (4.33):

[pic] = [pic]/ ТЕФ = 75083,41 /1706,4 = 44,01 грн/год. (4.33):

Оскільки в даному випадку всі роботи, які пов‘язані з розробкою програмного продукту ведуться на ЕОМ, витрати на оплату машинного часу, в залежності від обраного варіанта реалізації, складає (4.34-4.36):

[pic] (4.34)

І.     [pic] (4.35)

ІІ.     [pic] (4.36)

Накладні витрати складають 67% від заробітної плати (4.37-3.39):

[pic] = [pic] ⋅ 0,67 (4.37)

І.     [pic] = 160924,77 * 0,67 = 107819,6 грн.; (4.38)

ІІ.     [pic] = 162531,99 * 0,67 = 108896,44 грн.; (4.39)

Отже, вартість розробки ПП за варіантами становить (4.40-4.42):

[pic] = [pic]+ [pic]+ [pic] + [pic] (4.40)

І.     [pic] = 160924, 27 + 35403,45 + 74359,3 + 107819,6 = 378506,6 грн.; (4.41)

ІІ.     [pic] = 162531,99 + 35757,04 + 75102,19 + 108896,44 = 382287,66 грн.;(4.42)

4.6 Вибір кращого варіанта ПП техніко-економічного рівня

Розрахуємо коефіцієнт техніко-економічного рівня за формулою (4.43-4.45):

[pic] = [pic]⁄[pic], (4.43)

[pic] = 2,0952 / 378506,6 = 0,56⋅10-7, (4.44)

[pic] = 3,0034 / 382287,66 = 0,79⋅10-7. (4.45)

Як бачимо, найбільш ефективним є другий варіант реалізації програми з коефіцієнтом техніко-економічного рівня [pic]= 0,79⋅10-7.

Цей варіант реалізації програмного продукту має такі параметри:

- мова програмування – Python

- вбудовані бібліотеки для прогнозування;

- власна реалізація пакету потрібних для відображення результату функцій.

Даний варіант виконання програмного комплексу дає користувачу можливість використовувати програму для автоматичного розподілу сеансів з якомога більшою швидкодією та найменшим завантаженням пам’яті ПК.

ВИСНОВКИ

Було створено програмний комплекс автоматизованого формування розкладу сеансів для кінотеатрів на основі статистичних даних.

1) Досліджено предметну область формування розкладу сеансів для кінотеатрів.

Розглянуто декілька способів вирішення задачі формування розкладу, а саме використання методів оптимізації на прикладі відомої задачі пакування рюкзака.

Та замість цінності предмету в рюкзаку є орієнтовна кількість людей на сеансі, замість ваги рюкзака – максимальний час роботи залу, вага кожного предмету замінюється на час прокату фільму, і вводяться додаткові обмеження на кількість фільмів кожної технології. І цільовою функцією стала максимізація прибутку кінотеатру.

Також було проаналізовано зовнішнє оточення проекту, розглянуто основні вимоги користувача що до програмної реалізації продукту.

2) Проведено пошук повних та часткових аналогів готових програмних рішень автоматизованого формування розкладу сеансів для кінотеатрів.

Виявлено слабкі місця в інших програмних продуктах Повних аналогів не виявлено, а серед недоліків часткових аналогів основним є те що Оператор сам складає розклад, і це займає дуже багато часу. Розклад не є оптимальним, деякі зали пустують, тощо.

3) Побудовано математичну модель системи автоматизованого формування розкладу сеансів для кінотеатрів.

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

4) Введено в тестування створену модель автоматизованого формування розкладу сеансів для кінотеатрів.

Тестування створеного програмного продукту було проведено в три етапи.

За результатами експертної оцінки першого етапу модель підбору схожих фільмів була визнана робочою.

За результатами другого етапу було з’ясовано що модель непогано вловлює тренди, але сама точність цифр є відносною.

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

Таким чином розроблений програмний продукт є найбільш сучасним, ефективним інструментом для планування та розподілу сеансів фільмів на основі статистичних даних, який не має аналогів в Україні.

ПЕРЕЛІК ПОСИЛАНЬ

1. Неуймин Я. Г. Модели в науке и технике. История, теория, практика, Ленинград: Наука, Ленинградское отделение, 1984. 190 с.

2. Rolt L. T. C., Allen J. S. The Steam engine of Thomas Newcomen. Landmark, Ashbourne, 1997. 44 р.

3. Павловский Ю. Н. Имитационные модели и системы. ФАЗИС, ВЦ РАН. 2000. 144 с.

4. Петров А. А. Экономика. Модели. Вычислительный эксперимент. М.: Наука, 1996. 251 с.

5. Плохотников К. Э. Математическое моделирование и вычислительный эксперимент. Едиториал УРСС, 2003. 280 с.

6. Попов Ю. П., Самарский А. А. Вычислительный эксперимент. М.: Знание, 1983. 64 с.

7. Самарский А. А., Михайлов А. П. Математическое моделирование. М.: ФИЗМАТЛИТ, 2002. 320 с.

8. Бейли Н. Математика в биологии и медицине. М.: Мир, 1970. 328 с.

9. Обзор методов прогнозирования.

URL: (дата звернення: 03.05.2019).

10. Mathematical model.

URL: (дата звернення 03.05.2019)

11. Чучуева И. А. Модель прогнозирования временных рядов по выборке максимального подобия. Московский государственный технический университет им. Н. Э. Баумана. Москва, 2012.

12. Тихонов Э. Е. Прогнозирование в условиях рынка. Невинномысск, 2006. 221 с.

13. Мерков А. Б. Распознавание образов: Введение в методы статистического обучения. М.: Едиториал УРСС, 2011. 254 с.

14. Сидоров С. Г., Никологорская A. B. Анализ временных рядов как метод построения потребления электроэнергии . Вестник ИГЭУ. 2010, Вып. 3. С. 81–83.

15. Draper N., Smith H. Applied regression analysis. New York: Wiley, In press, 1981. 693 p.

16. Armstrong J.S. Forecasting for Marketing. Quantitative Methods in Marketing. London: International Thompson Business Press, 1999. P. 92 –119.

17. Бокс Дж., Дженкинс Г. М. Анализ временных рядов, прогноз и управление. М.: Мир, 1974. 406 с.

18. Фейгин Л. И. Задачи теории расписаний при нечетких длительностях операций . Л.И. Фейгин. Доклады АН СССР, 1983, т. 272, №4, с. 812–815.

19. Шеннон Р. Имитационное моделирование систем. Искусство и наука: пер. с англ. М.: Мир, 1978. 418 с.

20. Хаббард Дж. Автоматизированное проектирование баз данных. Дж. Хаббард М.: Мир, 1983. 295 с.

21. Щербакова И. В. Математическое моделирование информационных систем центров ситуационного управления в интересах обеспечения безопасности: автореф. дис.канд. техн. наук. Воронеж: ВИ МВД России, 2009. 16 с.

22. Зайченко Ю. П. Исследование операций (задачник). Киев, 2003. 250 с.

ДОДАТОК А

Рисунок А.1 – Поле вибору фільму для аналізу

Рисунок А.2 – Вікно аналізу фільмів

Рисунок А.3 – Вікно вводу обмежень дистрибюторів

Рисунок А.4 – вікно збору даних по фільму

[pic]

Рисунок А.5 – Приклад розкладу сеансів

ДОДАТОК Б

Додаток В

import requests

import pandas as pd

import numpy as np

from stemming.porter2 import stem

import json

import time

import pymssql

import pymysql

from pymysql.cursors import DictCursor

connect_to_theSameMovie = ''

movie_db_host = '10.128.150.49'

#movie_db_host = '10.128.150.49'

#thesamemovie_server = ''

thesamemovie_server = ''

connection_MySQL = pymysql.connect(

host='10.128.150.113',

port=3306,

user='root',

password='F26AvPjG',

db='thesamemovie',

charset='utf8mb4',

cursorclass=DictCursor )

path_to_your_dir = ''

from keras.layers import Input, Embedding, Dot, Reshape, Dense

from keras.models import Model

from keras.models import load_model

#from tqdm import tqdm

from tqdm import tqdm_notebook as tqdm

import random

import itertools as it

np.set_printoptions(suppress=True)

def separator(x):

x=x.split(',')

for i in range(len(x)):

x[i]=x[i].strip()

return x

def SQL_BASE_user(connection_MySQL):

connection = connection_MySQL

query='''

select

MovieList.movieUid as 'long',

MovieList.movieNameUkr as ukr,

MovieList.imdb as imdb,

GROUP_CONCAT(DISTINCT RateRelations.rate SEPARATOR ', ') as 'rated',

GROUP_CONCAT(DISTINCT GenreRelations.genre SEPARATOR ', ') as 'genre',

GROUP_CONCAT(DISTINCT TagRelation.tag SEPARATOR ', ') as 'keywords'

from

MovieList

left join RateRelations on RateRelations.movieId=MovieList.movieUid and RateRelations.flag=1

left join GenreRelations on GenreRelations.movieId=MovieList.movieUid and GenreRelations.flag=1

left join TagRelation on TagRelation.movieId=MovieList.movieUid and TagRelation.flag=1 and TagRelation.keyTag=1

group by MovieList.movieUid, MovieList.movieNameUkr, MovieList.imdb

'''

df = pd.read_sql(query,connection)

df=df.fillna('')

df.genre=df.genre.apply(lambda x: separator(x))

df.keywords=df.keywords.apply(lambda x: separator(x))

df.rates=df.rated.apply(lambda x: separator(x))

TOKEN = 'YnJvd3Nlcjo='

params = {

'scope': 'ui',

'username': 'zao1',

'password': 'qwerty1234',

'grant_type': 'password'

}

r = requests.post(thesamemovie_server + '/uaa/oauth/token', params=params, headers={'Authorization': 'Basic %s' % TOKEN})

response = requests.get(thesamemovie_server + '/statistics/current', headers={'Authorization':'Bearer ' + r.json()['access_token']})

all_films_postman = pd.DataFrame(response.json())

all_films_postman.drop(columns=['rated', 'genre','keywords', 'title' ],inplace=True )

df.rename(columns={'imdb':'imdbID'}, inplace=True )

df=pd.merge(df,all_films_postman, on='imdbID')

df.fillna('', inplace=True)

# print(df.shape)

df['actors'] = df['actors'].apply(lambda x: ' '.join([i.replace(' ', '_') for i in x]))

df['country'] = df['country'].apply(lambda x: " ".join(x))

df['director'] = df['director'].apply(lambda x: ' '.join([i.replace(' ', '_') for i in x]))

df['genre'] = df['genre'].apply(lambda x: ["genre_" + i for i in x])

df['genre'] = df['genre'].apply(lambda x: " ".join(x))

df['keywords_whole'] = df['keywords'].apply(lambda x: ' '.join([i.replace(' ', '_') for i in x]))

df['keywords_by_space'] = df['keywords'].apply(lambda x: ' '.join([i for i in x]))

df['soup'] = df['genre'] + ' ' + df['keywords_whole']

df['keywords_stemmed'] = df['keywords'].apply(lambda x: [' '.join([stem(word) for word in sentence.split(" ")]) for sentence in x])

df['keywords_whole_for_nn'] = df['keywords_stemmed'].apply(lambda x: ' '.join([i.replace(' ', '_') for i in x]))

df['bag_of_words_for_train_nn'] = df['genre'] + ' ' + df['keywords_whole_for_nn']

df['actors'] = df['actors'].str.lower()

df['country'] = df['country'].str.lower()

df['director'] = df['director'].str.lower()

df['genre'] = df['genre'].str.lower()

df['keywords_whole'] = df['keywords_whole'].str.lower()

df['keywords_by_space'] = df['keywords_by_space'].str.lower()

df['soup'] = df['soup'].str.lower()

df['keywords_stemmed'] = df['keywords_stemmed'].str.lower()

df['keywords_whole_for_nn'] = df['keywords_whole_for_nn'].str.lower()

df['bag_of_words_for_train_nn'] = df['bag_of_words_for_train_nn'].str.lower()

return df[['long', 'imdbID', 'ukr','originalTitle', 'year','rated',

'genre', 'country', 'soup','keywords_whole_for_nn','bag_of_words_for_train_nn',

'keywords_by_space', 'keywords_whole', 'director', 'actors']]

def get_mannual_changes(connection_MySQL):

query='''

select * from ManualChangesRecommendations

'''

connection =connection_MySQL

df = pd.read_sql(query,connection)

connection.close()

return df

def make_pairs_for_train_NN(df):

st_wrds_not_stemmed = ["mightn't", 'you', 'theirs', 'aren', 'has', 'she', 'they', 'over',

'below', 'how', 'yours', "hasn't", 'here', 'where', 'nor',

'couldn', "you've", 'wouldn', 'yourselves', 'if', 'shouldn', 's',

'before', 'as', 'between', 'mustn', 'doesn', 'hadn', 'too', 'won',

'their', 'which', 'few', "you're", 'what', 'with', 'and', 'itself',

'we', 'do', 'until', 'up', 'then', 'y', "couldn't", 'when', 'into',

'themselves', "don't", 'hasn', 'her', 'its', 're', 'does', 'an',

'mightn', "weren't", 'had', 'haven', 'doing', 'shan', 'i', "you'd",

'ours', 'there', 'was', 'some', 'needn', 'he', 'hers', 'on', 'why',

"should've", "she's", 'own', "hadn't", 'weren', 'of', 'me',

'while', "won't", "you'll", 'these', "needn't", 'now', 'once',

"mustn't", 'll', 'only', 'that', "it's", 'didn', 'no', 'ain',

'him', 'each', 'to', 'during', 'same', 'other', 'are', 'from', 'a',

'don', 'all', 'who', "shouldn't", "wasn't", "wouldn't", 'his',

'have', 't', 'wasn', 'under', 'about', 'be', 'or', 'for', 'my',

'so', 'through', 'those', 'not', 'isn', 'in', 'herself', 'any',

'yourself', "isn't", "aren't", 'very', 'against', 'again',

"didn't", 'after', 'them', 'whom', 'the', 'down', "that'll", 'out',

'ourselves', "doesn't", "shan't", 'but', 'should', 'just', 'ma',

'being', 'because', 'at', 'myself', 'himself', 'above', 'more',

'been', 'by', 'further', 've', 'm', 'most', 'off', 'it', 'than',

'were', 'having', 'such', 'your', 'will', 'o', 'our', 'can',

'this', 'd', 'is', 'did', 'am', "haven't", 'both', '']

st_wrds = ["mightn't", 'you', 'their', 'aren', 'has', 'she', 'they', 'over', 'below', 'how', 'your', "hasn't",

'here', 'where', 'nor', 'couldn', "you'v", 'wouldn', 'yourselv', 'if', 'shouldn', 's', 'befor', 'as',

'between', 'mustn', 'doesn', 'hadn', 'too', 'won', 'their', 'which', 'few', "you'r", 'what', 'with', 'and',

'itself', 'we', 'do', 'until', 'up', 'then', 'y', "couldn't", 'when', 'into', 'themselv', "don't", 'hasn', 'her',

'it', 're', 'doe', 'an', 'mightn', "weren't", 'had', 'haven', 'do', 'shan', 'i', "you'd", 'our', 'there', 'was', 'some',

'needn', 'he', 'her', 'on', 'whi', "should'v", 'she', 'own', "hadn't", 'weren', 'of', 'me', 'while', "won't", "you'll",

'these', "needn't", 'now', 'onc', "mustn't", 'll', 'onli', 'that', 'it', 'didn', 'no', 'ain', 'him', 'each', 'to',

'dure', 'same', 'other', 'are', 'from', 'a', 'don', 'all', 'who', "shouldn't", "wasn't", "wouldn't", 'his', 'have', 't',

'wasn', 'under', 'about', 'be', 'or', 'for', 'my', 'so', 'through', 'those', 'not', 'isn', 'in', 'herself', 'ani', 'yourself',

"isn't", "aren't", 'veri', 'against', 'again', "didn't", 'after', 'them', 'whom', 'the', 'down', "that'll", 'out', 'ourselv',

"doesn't", "shan't", 'but', 'should', 'just', 'ma', 'be', 'becaus', 'at', 'myself', 'himself', 'abov', 'more', 'been', 'by',

'further', 've', 'm', 'most', 'off', 'it', 'than', 'were', 'have', 'such', 'your', 'will', 'o',

'our', 'can', 'this', 'd', 'is', 'did', 'am', "haven't", 'both', '']

flat_list_all_tags = [item for sublist in df.bag_of_words_for_train_nn.apply(lambda x: x.split(' ')) for item in sublist]

st_wrds.extend(pd.Series(flat_list_all_tags).value_counts()[pd.Series(flat_list_all_tags).value_counts() == 1].index.tolist())

df.bag_of_words_for_train_nn = df.bag_of_words_for_train_nn.apply(lambda x: [i for i in x.split(' ') if i not in st_wrds])

film_index = {film: idx for idx, film in enumerate(df.imdbID.values)}

index_film = {idx: film for film, idx in film_index.items()}

list_of_lists = df.bag_of_words_for_train_nn.values

flat_list = [item for sublist in list_of_lists for item in sublist]

tags = pd.Series(flat_list).value_counts().to_dict()

tag_index = {tag: idx for idx, tag in enumerate(tags)}

index_tag = {idx: tag for tag, idx in tag_index.items()}

pairs = []

films = df.imdbID.values

# Iterate through each book

for film in films:

# Iterate through the links in the book

pairs.extend((film_index[film], tag_index[tag.lower()]) for tag in df[df.imdbID == film].bag_of_words_for_train_nn.values[0] if tag.lower() in tags)

pairs_set = set(pairs)

return pairs, pairs_set, tag_index, film_index, index_film, tags, films

def generate_batch(pairs, pairs_set, films, tags, n_positive = 50, negative_ratio = 1.0):

"""Generate batches of samples for training"""

batch_size = n_positive * (1 + negative_ratio)

batch = np.zeros((batch_size, 3))

neg_label = -1

# This creates a generator

while True:

# randomly choose positive examples

for idx, (film_id, tag_id) in enumerate(random.sample(pairs, n_positive)):

batch[idx, :] = (film_id, tag_id, 1)

# Increment idx by 1

idx += 1

# Add negative examples until reach batch size

while idx < batch_size:

# random selection

random_film = random.randrange(len(films))

random_tag = random.randrange(len(tags))

# Check to make sure this is not a positive example

if (random_film, random_tag) not in pairs_set:

# Add to batch and increment index

batch[idx, :] = (random_film, random_tag, neg_label)

idx += 1

# Make sure to shuffle order

np.random.shuffle(batch)

yield {'film': batch[:, 0], 'tag': batch[:, 1]}, batch[:, 2]

def film_embedding_model(film_index, tag_index, embedding_size = 50):

"""Model to embed books and wikilinks using the functional API.

Trained to discern if a link is present in a article"""

# Both inputs are 1-dimensional

film = Input(name = 'film', shape = [1])

tag = Input(name = 'tag', shape = [1])

# Embedding the book (shape will be (None, 1, 50))

film_embedding = Embedding(name = 'film_embedding',

input_dim = len(film_index),

output_dim = embedding_size)(film)

# Embedding the link (shape will be (None, 1, 50))

tag_embedding = Embedding(name = 'tag_embedding',

input_dim = len(tag_index),

output_dim = embedding_size)(tag)

# Merge the layers with a dot product along the second axis (shape will be (None, 1, 1))

merged = Dot(name = 'dot_product', normalize = True, axes = 2)([film_embedding, tag_embedding])

# Reshape to be a single number (shape will be (None, 1))

merged = Reshape(target_shape = [1])(merged)

model = Model(inputs = [film, tag], outputs = merged)

pile(optimizer = 'Adam', loss = 'mse')

return model

def get_similar_film_by_nn(name, model, film_index, index_film):

film_layer = model.get_layer('film_embedding')

weights = film_layer.get_weights()[0]

weights = weights / np.linalg.norm(weights, axis = 1).reshape((-1, 1))

index = film_index

rindex = index_film

# Check to make sure `name` is in index

try:

# Calculate dot product between book and all others

dists = np.dot(weights, weights[index[name]])

except KeyError:

print(f'{name} Not Found.')

return

# Sort distance indexes from smallest to largest

sorted_dists = np.argsort(dists)

# Take the last n sorted distances

closest = sorted_dists[::-1][1:]

# closest = sorted_dists[:]

return closest, dists[sorted_dists[::-1][1:]]

def train_model(df, pairs, pairs_set, tag_index, film_index, index_film, tags, films, epoch, batch_size):

model = film_embedding_model(film_index, tag_index)

gen = generate_batch(pairs, pairs_set, films, tags, batch_size, negative_ratio = 2)

model.fit_generator(gen, epochs = epoch,

steps_per_epoch = len(pairs) // batch_size,

verbose = 2)

return model

def get_chunk(df, IMDB_ID, closest_films, film_index):

film_for_reccomendations = df[df.imdbID == IMDB_ID]

df = df[df.imdbID != IMDB_ID]

current_rated = film_for_reccomendations.rated.values[0]

current_country = film_for_reccomendations.country.values[0]

current_directors = film_for_reccomendations.director.values[0]

current_genre = film_for_reccomendations.genre.values[0].split(' ')

current_keywords_whole = film_for_reccomendations.keywords_whole.values[0]

current_keywords_by_space = film_for_reccomendations.keywords_by_space.values[0].split(' ')

current_bag_of_words_for_train_nn = film_for_reccomendations.soup.values[0].split(' ')

chunk = []

######################### DIRECTOR SORT #########################

reccomendation_by_director = []

director_list = ['christopher_nolan', 'wes_anderson', 'quentin_tarantino', 'woody_allen', 'james_cameron', 'david_fincher', 'guy_ritchie',

'george_lucas', 'lana_wachowski', 'david_lynch', 'hayao_miyazaki', 'luc_besson', 'pedro_almodóvar',

'martin_scorsese', 'francis_ford_coppola', 'clint_eastwood', 'peter_jackson', 'tim_burton', 'joel_coen',

'steven_soderbergh', 'kar-wai_wong', 'robert_zemeckis', 'steven_spielberg', 'roman_polanski', 'aamir_khan', 'jean-pierre_jeunet',

'gus_van_sant', 'thomas_vinterberg', 'asghar_farhadi', 'denis_villeneuve', 'guillermo_del_toro', 'alfonso_cuarón',

'darren_aronofsky', 'makoto_shinkai', 'chan-wook_park', 'richard_linklater', 'martin_mcdonagh', 'gaspar_noé',

'lars_von_trier', 'sofia_coppola', 'alejandro_g._iñárritu']

for director in director_list:

if director in current_directors:

director_df = df[df.director.str.contains(director)]

if not director_df.empty:

reccomendation_by_director.extend(director_df.imdbID.tolist())

reccomendation_by_director_index = [film_index[i] for i in reccomendation_by_director]

for i in closest_films:

if i in reccomendation_by_director_index:

chunk.append([i, ' director ' + director])

######################### AGE SORT #########################

# rated_pg_13 = df[df.rated == 'PG-13']

rated_pg_13 = df[df.rated.apply(lambda x: 'PG-13' in x)]

if 'G' in current_rated or 'PG' in current_rated:

rated_df = df[df.rated.apply(lambda x: 'G' in x or 'PG' in x)]

# rated_df = df[np.logical_or(df.rated == 'G', df.rated == 'PG')]

age_limit = 'G or PG'

# print('G and PG')

else:

rated_df = df[~df.rated.apply(lambda x: 'G' in x or 'PG' in x)]

# rated_df = df[~np.logical_or(df.rated == 'G', df.rated == 'PG')]

age_limit = 'PG-13 or R'

# print('PG-13 or R')

country_ukraine_russia_df = rated_df[np.logical_or(rated_df.country.str.contains('ukraine'), rated_df.country.str.contains('russia'))]

country_usa_df = rated_df[rated_df.country.str.contains('usa')]

country_others_df = rated_df[~np.logical_or(np.logical_or(rated_df.country.str.contains('ukraine'), rated_df.country.str.contains('russia')),

rated_df.country.str.contains('usa') )]

country_not_ukr_rus_df = rated_df[~np.logical_or(rated_df.country.str.contains('ukraine'), rated_df.country.str.contains('russia'))]

hard_hierarchy = {

'group_GENRE_animation_and_TAG_minion':{

'type':'Soup_just_1',

'soup':['genre_animation', 'minion'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_animation_and_TAG_racing':{

'type':'Soup_just_1',

'soup':['genre_animation', 'racing'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_animation_and_TAG_superhero':{

'type':'Soup_just_1',

'soup':['genre_animation', 'superhero'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_animation_and_TAG_disney':{

'type':'Soup_just_1',

'soup':['genre_animation', 'disney'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_action_AND_TAG_female_protagonist':{

'type':'Soup_just_1',

'soup':['genre_action', 'female_protagonist'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_horror_AND_TAG_shark':{

'type':'Soup_just_1_notEXACTLY_just_1',

'soup':['genre_horror'],

'not_exactly_tag':['shark'],

'times':3,

'skip_country': True,

'include_in_next': True

},

'group_GENRE_scifi_AND_horror':{

'type':'Soup_just_1',

'soup':['genre_sci-fi', 'genre_horror'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_sci-fi_AND_TAG_space_travel_OR_space':{

'type':'Soup_OR_2x2',

'soup':[['genre_sci-fi', 'space_travel'],

['genre_sci-fi', 'space']],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_sci-fi_AND_TAG_martial_arts':{

'type':'Soup_just_1',

'soup':['genre_sci-fi', 'martial_arts'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_sci-fi_AND_TAG_survivor':{

'type':'Soup_just_1',

'soup':['genre_sci-fi', 'survivor'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_thriller_AND_horror':{

'type':'Soup_just_1',

'soup':['genre_thriller', 'genre_horror'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_action_AND_thriller':{

'type':'Soup_just_1',

'soup':['genre_action', 'genre_thriller'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_adventure_AND_family_AND_fantasy':{

'type':'Soup_just_1',

'soup':['genre_adventure', 'genre_family', 'genre_fantasy'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_scifi':{

'type':'Soup_just_1',

'soup':['genre_sci-fi'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_biography':{

'type':'Soup_just_1',

'soup':['genre_biography'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_history':{

'type':'Soup_just_1',

'soup':['genre_history'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_documentary':{

'type':'Soup_just_1',

'soup':['genre_documentary'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_alien':{

'type':'Soup_just_1',

'soup':['alien'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_drama_and_TAGS_love_relationships':{

'type':'genre_tags_or_11',

'soup':[['genre_comedy', 'husband_wife_relationship'],

['genre_comedy', 'boyfriend_girlfriend_relationship'],

['genre_comedy', 'ex_husband_ex_wife_relationship'],

['genre_comedy', 'interracial_relationship'],

['genre_comedy', 'older_man_younger_woman_relationship'],

['genre_comedy', 'ex_boyfriend_ex_girlfriend_relationship'],

['genre_comedy', 'older_woman_younger_man_relationship'],

['genre_comedy', 'male_female_relationship'],

['genre_comedy', 'fiance_fiancee_relationship'],

['genre_comedy', 'ex_girlfriend_ex_boyfriend_relationship'],

['genre_comedy', 'girlfriend_boyfriend_relationship']],

'skip_country': False,

'include_in_next': True

},

'group_GENRE_drama_and_TAGS_family_relationships':{

'type':'genre_tags_or_21',

'soup':[['genre_drama', 'father_son_relationship'],

['genre_drama', 'father_daughter_relationship'],

['genre_drama', 'mother_son_relationship'],

['genre_drama', 'mother_daughter_relationship'],

['genre_drama', 'brother_sister_relationship'],

['genre_drama', 'brother_brother_relationship'],

['genre_drama', 'family_relationships'],

['genre_drama', 'sister_sister_relationship'],

['genre_drama', 'aunt_niece_relationship'],

['genre_drama', 'uncle_nephew_relationship'],

['genre_drama', 'grandfather_grandson_relationship'],

['genre_drama', 'cousin_cousin_relationship'],

['genre_drama', 'grandmother_granddaughter_relationship'],

['genre_drama', 'aunt_nephew_relationship'],

['genre_drama', 'uncle_niece_relationship'],

['genre_drama', 'grandfather_granddaughter_relationship'],

['genre_drama', 'grandmother_grandson_relationship'],

['genre_drama', 'stepfather_stepdaughter_relationship'],

['genre_drama', 'father_in_law_daughter_in_law_relationship'],

['genre_drama', 'mother_in_law_daughter_in_law_relationship'],

['genre_drama', 'father_in_law_son_in_law_relationship']],

'skip_country': False,

'include_in_next': True

},

'group_GENRE_drama_AND_romance':{

'type':'Soup_just_1',

'soup':['genre_drama', 'genre_romance'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_drama_AND_thriller':{

'type':'Soup_just_1',

'soup':['genre_drama', 'genre_thriller'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_fantasy_AND_adventure':{

'type':'Soup_just_1',

'soup':['genre_fantasy', 'genre_adventure'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_fantasy':{

'type':'Soup_just_1',

'soup':['genre_fantasy'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_action':{

'type':'Soup_just_1',

'soup':['genre_action'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_adventure':{

'type':'Soup_just_1',

'soup':['genre_adventure'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_horror':{

'type':'Soup_just_1',

'soup':['genre_horror'],

'skip_country': False,

'include_in_next': False

},

'group_GENRE_thriller':{

'type':'Soup_just_1',

'soup':['genre_thriller'],

'skip_country': False,

'include_in_next': False

},

elif hierarchy[header]['type'] == 'Soup_just_1_notEXACTLY_just_1':

if set(hierarchy[header]['soup']).issubset(current_film_soup) and current_keywords_by_space.count(hierarchy[header]['not_exactly_tag'][0]) >= hierarchy[header]['times']:

return header

elif hierarchy[header]['type'] == 'genre_tags_or_11':

if set(hierarchy[header]['soup'][0]).issubset(current_film_soup) or set(hierarchy[header]['soup'][1]).issubset(current_film_soup) or set(hierarchy[header]['soup'][2]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][3]).issubset(current_film_soup) or set(hierarchy[header]['soup'][4]).issubset(current_film_soup) or set(hierarchy[header]['soup'][5]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][6]).issubset(current_film_soup) or set(hierarchy[header]['soup'][7]).issubset(current_film_soup) or set(hierarchy[header]['soup'][8]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][9]).issubset(current_film_soup) or set(hierarchy[header]['soup'][10]).issubset(current_film_soup):

return header

elif hierarchy[header]['type'] == 'genre_tags_or_21':

if set(hierarchy[header]['soup'][0]).issubset(current_film_soup) or set(hierarchy[header]['soup'][1]).issubset(current_film_soup) or set(hierarchy[header]['soup'][2]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][3]).issubset(current_film_soup) or set(hierarchy[header]['soup'][4]).issubset(current_film_soup) or set(hierarchy[header]['soup'][5]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][6]).issubset(current_film_soup) or set(hierarchy[header]['soup'][7]).issubset(current_film_soup) or set(hierarchy[header]['soup'][8]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][9]).issubset(current_film_soup) or set(hierarchy[header]['soup'][10]).issubset(current_film_soup) or set(hierarchy[header]['soup'][11]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][12]).issubset(current_film_soup) or set(hierarchy[header]['soup'][13]).issubset(current_film_soup) or set(hierarchy[header]['soup'][14]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][15]).issubset(current_film_soup) or set(hierarchy[header]['soup'][16]).issubset(current_film_soup) or set(hierarchy[header]['soup'][17]).issubset(current_film_soup) \

or set(hierarchy[header]['soup'][18]).issubset(current_film_soup) or set(hierarchy[header]['soup'][19]).issubset(current_film_soup) or set(hierarchy[header]['soup'][20]).issubset(current_film_soup):

return header

return 'WRONG_FILM_PARAMETER'

if 'russia' in current_country or 'ukraine' in current_country :

header = __get_film_category(current_bag_of_words_for_train_nn, current_keywords_by_space, soft_hierarchy)

else:

header = __get_film_category(current_bag_of_words_for_train_nn, current_keywords_by_space, hard_hierarchy)

# print('Header')

# print(header, current_country)

def __get_film_batch_for_category(df, header, hierarchy):

batch = []

exclude_headers = list(hierarchy.keys())[:list(hierarchy.keys()).index(header)]

exclude_Soup_just_1 = []

exclude_Soup_OR_2x2 = []

exclude_Soup_OR_2x1 = []

exclude_Soup_OR_3x2 = []

exclude_Soup_OR_3x1 = []

exclude_genre_tags_or_21 = []

exclude_genre_tags_or_11 = []

exclude_Soup_just_1_notEXACTLY_just_1 = []

for excl_header in exclude_headers:

if hierarchy[excl_header]['type'] == 'Soup_just_1' and hierarchy[excl_header]['include_in_next'] == False:

exclude_Soup_just_1.append(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'Soup_OR_2x2' and hierarchy[excl_header]['include_in_next'] == False:

exclude_Soup_OR_2x2.extend(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'Soup_OR_2x1' and hierarchy[excl_header]['include_in_next'] == False:

exclude_Soup_OR_2x1.extend(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'Soup_OR_3x2' and hierarchy[excl_header]['include_in_next'] == False:

exclude_Soup_OR_3x2.extend(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'Soup_OR_3x1' and hierarchy[excl_header]['include_in_next'] == False:

exclude_Soup_OR_3x2.extend(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'genre_tags_or_11' and hierarchy[excl_header]['include_in_next'] == False:

exclude_genre_tags_or_11.extend(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'genre_tags_or_21' and hierarchy[excl_header]['include_in_next'] == False:

exclude_genre_tags_or_21.extend(hierarchy[excl_header]['soup'])

elif hierarchy[excl_header]['type'] == 'Soup_just_1_notEXACTLY_just_1' and hierarchy[excl_header]['include_in_next'] == False:

exclude_Soup_just_1_notEXACTLY_just_1.append([hierarchy[excl_header]['soup'], hierarchy[excl_header]['not_exactly_tag'], hierarchy[excl_header]['times']])

if not exclude_Soup_just_1:

exclude_Soup_just_1.append([None])

if not exclude_Soup_OR_2x2:

exclude_Soup_OR_2x2.extend([[None, None], [None, None]])

if not exclude_Soup_OR_2x1:

exclude_Soup_OR_2x1.extend([[None], [None]])

if not exclude_Soup_OR_3x2:

exclude_Soup_OR_3x2.extend([[None, None], [None, None], [None, None]])

if not exclude_Soup_OR_3x2:

exclude_Soup_OR_3x2.extend([[None], [None], [None]])

if not exclude_genre_tags_or_11:

exclude_genre_tags_or_11.extend([[None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None]])

if not exclude_genre_tags_or_21:

exclude_genre_tags_or_21.extend([[None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None], [None]])

if not exclude_Soup_just_1_notEXACTLY_just_1:

exclude_Soup_just_1_notEXACTLY_just_1.append([[None], [None], -9999999999999999])

for index, row in df.iterrows():

soup = row['soup'].split(' ')

imdb = row['imdbID']

tags_not_exactly = row['keywords_by_space'].split(' ')

flag = True

for _exclude_Soup_just_1 in exclude_Soup_just_1:

if set(_exclude_Soup_just_1).issubset(set(soup)):

flag = False

for _exclude_Soup_OR_2x2 in exclude_Soup_OR_2x2:

if set(_exclude_Soup_OR_2x2).issubset(set(soup)):

flag = False

for _exclude_Soup_OR_2x1 in exclude_Soup_OR_2x1:

if set(_exclude_Soup_OR_2x1).issubset(set(soup)):

flag = False

for _exclude_Soup_OR_3x2 in exclude_Soup_OR_3x2:

if set(_exclude_Soup_OR_3x2).issubset(set(soup)):

flag = False

for _exclude_genre_tags_or_11 in exclude_genre_tags_or_11:

if set(_exclude_genre_tags_or_11).issubset(set(soup)):

flag = False

for _exclude_genre_tags_or_21 in exclude_genre_tags_or_21:

if set(_exclude_genre_tags_or_21).issubset(set(soup)):

flag = False

for _exclude_Soup_OR_3x1 in exclude_Soup_OR_3x1:

if set(_exclude_Soup_OR_3x1).issubset(set(soup)):

flag = False

for _exclude_Soup_just_1_notEXACTLY_just_1 in exclude_Soup_just_1_notEXACTLY_just_1:

if set(_exclude_Soup_just_1_notEXACTLY_just_1[0]).issubset(set(soup)) and tags_not_exactly.count(_exclude_Soup_just_1_notEXACTLY_just_1[1][0]) >= _exclude_Soup_just_1_notEXACTLY_just_1[2]:

flag = False

if flag:

if hierarchy[header]['type'] == 'Soup_just_1':

if set(hierarchy[header]['soup']).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'Soup_OR_2x2':

if set(hierarchy[header]['soup'][0]).issubset(set(soup)) or set(hierarchy[header]['soup'][1]).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'Soup_OR_2x1':

if set(hierarchy[header]['soup'][0]).issubset(set(soup)) or set(hierarchy[header]['soup'][1]).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'Soup_OR_3x2':

if set(hierarchy[header]['soup'][0]).issubset(set(soup)) or set(hierarchy[header]['soup'][1]).issubset(set(soup)) or set(hierarchy[header]['soup'][2]).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'genre_tags_or_11':

if set(hierarchy[header]['soup'][0]).issubset(set(soup)) or set(hierarchy[header]['soup'][1]).issubset(set(soup)) or set(hierarchy[header]['soup'][2]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][3]).issubset(set(soup)) or set(hierarchy[header]['soup'][4]).issubset(set(soup)) or set(hierarchy[header]['soup'][5]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][6]).issubset(set(soup)) or set(hierarchy[header]['soup'][7]).issubset(set(soup)) or set(hierarchy[header]['soup'][8]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][9]).issubset(set(soup)) or set(hierarchy[header]['soup'][10]).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'genre_tags_or_21':

if set(hierarchy[header]['soup'][0]).issubset(set(soup)) or set(hierarchy[header]['soup'][1]).issubset(set(soup)) or set(hierarchy[header]['soup'][2]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][3]).issubset(set(soup)) or set(hierarchy[header]['soup'][4]).issubset(set(soup)) or set(hierarchy[header]['soup'][5]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][6]).issubset(set(soup)) or set(hierarchy[header]['soup'][7]).issubset(set(soup)) or set(hierarchy[header]['soup'][8]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][9]).issubset(set(soup)) or set(hierarchy[header]['soup'][10]).issubset(set(soup)) or set(hierarchy[header]['soup'][11]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][12]).issubset(set(soup)) or set(hierarchy[header]['soup'][13]).issubset(set(soup)) or set(hierarchy[header]['soup'][14]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][15]).issubset(set(soup)) or set(hierarchy[header]['soup'][16]).issubset(set(soup)) or set(hierarchy[header]['soup'][17]).issubset(set(soup)) \

or set(hierarchy[header]['soup'][18]).issubset(set(soup)) or set(hierarchy[header]['soup'][19]).issubset(set(soup)) or set(hierarchy[header]['soup'][20]).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'Soup_OR_3x1':

if set(hierarchy[header]['soup'][0]).issubset(set(soup)) or set(hierarchy[header]['soup'][1]).issubset(set(soup)) or set(hierarchy[header]['soup'][2]).issubset(set(soup)):

batch.append(imdb)

elif hierarchy[header]['type'] == 'Soup_just_1_notEXACTLY_just_1':

if set(hierarchy[header]['soup']).issubset(set(soup)) and tags_not_exactly.count(hierarchy[header]['not_exactly_tag'][0]) >= hierarchy[header]['times']:

batch.append(imdb)

batch = [film_index[i] for i in batch]

return list(set(batch))

if header != 'WRONG_FILM_PARAMETER':

if 'russia' in current_country or 'ukraine' in current_country:

country = 'Russia or Ukraine'

batch = __get_film_batch_for_category(country_ukraine_russia_df, header, soft_hierarchy)

else:

category(country_not_ukr_rus_df, 'group_GENRE_music_TAG_dancing', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_music_TAG_dancing')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_musical_and_TAG_dancing', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_musical_and_TAG_dancing')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_music', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_music')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_musical', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_musical')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_music_OR_history_music_OR_drama_music', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_music_OR_history_music_OR_drama_music')

elif header == 'group_GENRE_biography_music_OR_history_music_OR_drama_music':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_music_OR_history_music_OR_drama_music', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_biography_music_OR_history_music_OR_drama_music')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_music', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_music')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_music_TAG_dancing', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_music_TAG_dancing')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_musical', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_musical')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_musical_and_TAG_dancing', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_musical_and_TAG_dancing')

elif header == 'group_GENRE_music':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_music', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_music')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_music_TAG_dancing', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_music_TAG_dancing')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_musical', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_musical')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_musical_and_TAG_dancing', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_musical_and_TAG_dancing')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_music_OR_history_music_OR_drama_music', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_music_OR_history_music_OR_drama_music')

elif header == 'group_GENRE_documentary_war_OR_history_war_OR_biography_war':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary_war_OR_history_war_OR_biography_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary_war_OR_history_war_OR_biography_war')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_war')

elif header == 'group_GENRE_drama_AND_war':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_war')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary_war_OR_history_war_OR_biography_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary_war_OR_history_war_OR_biography_war')

elif header == 'group_GENRE_documentary_OR_history_OR_biography_AND_TAG_world_war_two':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary_OR_history_OR_biography_AND_TAG_world_war_two', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary_OR_history_OR_biography_AND_TAG_world_war_two')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_war')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary_war_OR_history_war_OR_biography_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary_war_OR_history_war_OR_biography_war')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_war')

elif header == 'group_GENRE_crime_AND_TAG_gangster':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_crime_AND_TAG_gangster', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_crime_AND_TAG_gangster')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_crime', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_crime')

elif header == 'group_GENRE_sport_AND_comedy':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sport_AND_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport_AND_comedy')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sport', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport')

elif header == 'group_GENRE_sport':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sport', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_sport', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sport_AND_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport_AND_comedy')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_sport_AND_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport_AND_comedy')

elif header == 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_history', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_history')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_biography')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_documentary')

elif header == 'group_GENRE_western':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_western', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_western')

imdbids = country_not_ukr_rus_df[np.logical_and(np.logical_and(country_not_ukr_rus_df.soup.str.contains('shootout'), ~country_not_ukr_rus_df.soup.str.contains('genre_animation')),

np.logical_or(country_not_ukr_rus_df.soup.str.contains('genre_action'), country_not_ukr_rus_df.soup.str.contains('genre_thriller')))].imdbID.values

batch = [film_index[_index] for _index in imdbids]

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_action_OR_thriller_and_TAG_shootout')

elif header == 'group_GENRE_comedy_AND_horror':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy_AND_horror')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy')

elif header == 'group_GENRE_comedy_AND_TAG_zombie':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy_AND_TAG_zombie', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy_AND_TAG_zombie')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy_AND_horror')

elif header == 'group_GENRE_comedy_AND_TAG_kitchen':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy_AND_TAG_kitchen', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy_AND_TAG_kitchen')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy')

elif header == 'group_GENRE_comedy_AND_drama':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy_AND_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy_AND_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy')

elif header == 'group_GENRE_comedy_AND_TAG_sex':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy_AND_TAG_sex', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy_AND_TAG_sex')

elif header == 'group_GENRE_comedy':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_comedy')

elif header == 'group_GENRE_horror_OR_drama_AND_TAG_post_apocalypse':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror_OR_drama_AND_TAG_post_apocalypse', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_horror_OR_drama_AND_TAG_post_apocalypse')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_TAG_zombie', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_TAG_zombie')

elif header == 'group_TAG_zombie':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_TAG_zombie', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_TAG_zombie')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_horror')

elif header == 'group_GENRE_horror_AND_mystery_AND_thriller':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror_AND_mystery_AND_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_horror_AND_mystery_AND_thriller')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_TAG_dc_comic', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_TAG_dc_comic')

elif header == 'group_GENRE_drama_OR_thriller_AND_TAG_superhero':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_OR_thriller_AND_TAG_superhero', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_OR_thriller_AND_TAG_superhero')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_thriller')

elif header == 'group_GENRE_scifi_AND_TAG_female_protagonist':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_scifi_AND_TAG_female_protagonist', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_scifi_AND_TAG_female_protagonist')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sci-fi_AND_TAG_martial_arts', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sci-fi_AND_TAG_martial_arts')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_OR_thriller_AND_TAG_superhero', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_OR_thriller_AND_TAG_superhero')

elif header == 'group_GENRE_action_AND_TAG_female_protagonist':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_action_AND_TAG_female_protagonist', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_action_AND_TAG_female_protagonist')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sci-fi_AND_TAG_martial_arts', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_sci-fi_AND_TAG_martial_arts')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_OR_thriller_AND_TAG_superhero', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_OR_thriller_AND_TAG_superhero')

elif header == 'group_GENRE_horror_AND_TAG_shark':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror_AND_TAG_shark', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_horror_AND_TAG_shark')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller_AND_horror')

elif header == 'group_GENRE_horror_AND_TAG_time_loop':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror_AND_TAG_time_loop', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_horror_AND_TAG_time_loop')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller_AND_horror')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_horror')

elif header == 'group_GENRE_scifi_AND_horror':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_scifi_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_scifi_AND_horror')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller_AND_horror')

elif header == 'group_GENRE_sci-fi_AND_TAG_space_travel_OR_space':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sci-fi_AND_TAG_space_travel_OR_space', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sci-fi_AND_TAG_space_travel_OR_space')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_scifi', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_scifi')

elif header == 'group_GENRE_sci-fi_AND_TAG_martial_arts':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sci-fi_AND_TAG_martial_arts', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sci-fi_AND_TAG_martial_arts')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_scifi_AND_TAG_female_protagonist', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_scifi_AND_TAG_female_protagonist')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_scifi', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_scifi')

elif header == 'group_GENRE_sci-fi_AND_TAG_survivor':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_sci-fi_AND_TAG_survivor', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sci-fi_AND_TAG_survivor')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller')

elif header == 'group_GENRE_thriller_AND_horror':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller_AND_horror')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_thriller_AND_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_sport')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2' + age_limit + ' ' + 'group_GENRE_sport')

elif header == 'group_GENRE_action_AND_thriller':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_action_AND_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_action_AND_thriller')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_action', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_action')

elif header == 'group_GENRE_adventure_AND_family_AND_fantasy':

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_adventure_AND_family_AND_fantasy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_adventure_AND_family_AND_fantasy')

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_fantasy_AND_adventure', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_fantasy_AND_adventure')

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_fantasy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_fantasy')

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_family')

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_drama_AND_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_family')

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_adventure', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_adventure')

elif header == 'group_GENRE_scifi':

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_scifi', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_scifi')

elif header == 'group_GENRE_biography':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_biography')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_history', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_history')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

elif header == 'group_GENRE_history':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_history', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_history')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_biography')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

elif header == 'group_GENRE_documentary':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_documentary', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_documentary')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_history', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_history')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_biography')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

batch = __get_film_batch_for_category(rated_pg_13, 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_biography_drama_OR_history_drama_OR_documentary_drama')

elif header == 'group_GENRE_alien':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_alien', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_alien')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_scifi', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_scifi')

elif header == 'group_GENRE_drama_AND_romance':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_and_TAGS_love_relationships', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_and_TAGS_love_relationships')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_romance')

elif header == 'group_GENRE_drama_AND_thriller':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_thriller')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller')

elif header == 'group_GENRE_fantasy_AND_adventure':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_fantasy_AND_adventure', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_fantasy_AND_adventure')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_fantasy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_adventure')

elif header == 'group_GENRE_fantasy':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_fantasy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_fantasy')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_fantasy_AND_adventure', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_fantasy_AND_adventure')

elif header == 'group_GENRE_action':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_action', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_action')

elif header == 'group_GENRE_adventure':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_adventure', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_adventure')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_fantasy_AND_adventure', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_fantasy_AND_adventure')

elif header == 'group_GENRE_horror':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_horror', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_horror')

elif header == 'group_GENRE_thriller':

batch = __get_film_batch_for_category(rated_df, 'group_GENRE_thriller', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_thriller')

elif header == 'group_GENRE_drama_and_TAGS_love_relationships':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_and_TAGS_love_relationships', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_and_TAGS_love_relationships')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_family')

elif header == 'group_GENRE_drama_and_TAGS_family_relationships':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_and_TAGS_family_relationships', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_and_TAGS_family_relationships')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama_AND_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_family')

elif header == 'group_GENRE_drama':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_drama')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_family')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_and_TAGS_family_relationships', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_and_TAGS_family_relationships')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_romance')

elif header == 'group_GENRE_romance':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_romance', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_romance')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_and_TAGS_love_relationships', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_and_TAGS_love_relationships')

elif header == 'group_GENRE_family':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_family')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_drama_AND_family', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_drama_AND_family')

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_comedy', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#2 ' + age_limit + ' ' + 'group_GENRE_comedy')

elif header == 'group_GENRE_crime':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_crime', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_crime')

elif header == 'group_GENRE_mystery':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_mystery', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_mystery')

elif header == 'group_GENRE_war':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_war', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_war')

elif header == 'group_GENRE_short':

batch = __get_film_batch_for_category(country_not_ukr_rus_df, 'group_GENRE_short', hard_hierarchy)

chunk = __add_films(chunk, batch, closest_films, 'ADD_#1 ' + age_limit + ' ' + 'group_GENRE_short')

if header != 'WRONG_FILM_PARAMETER':

return chunk

else:

return 'WRONG_FILM_PARAMETER'

def main(model_name='default', save_model=True, path_to_model='your_path', debug = True, IMDB_ID='tt5884230'):

df = SQL_BASE_user(connection_MySQL)

pairs, pairs_set, tag_index, film_index, index_film, tags, films = make_pairs_for_train_NN(df)

if save_model:

model = train_model(df, pairs, pairs_set, tag_index, film_index, index_film, tags, films, epoch=120, batch_size=256)

model.save(path_to_your_dir + model_name + '.h5')

else:

model = load_model(path_to_your_dir + model_name + '.h5')

if debug:

closest_films, distances = get_similar_film_by_nn(df[df.imdbID == IMDB_ID].imdbID.values[0], model, film_index, index_film)

distances = np.interp(distances, (distances.min(), distances.max()), (0, +0.98))

zip_film_score = list(zip(closest_films, distances))

chunk = get_chunk(df, IMDB_ID, closest_films, film_index)

if chunk != 'WRONG_FILM_PARAMETER':

chunk = pd.DataFrame(chunk, columns=['indexx', 'Category'])

zip_film_score = pd.DataFrame(zip_film_score, columns=['indexx', 'Coef'])

result = pd.merge(chunk, zip_film_score, how='inner', on=['indexx'])

result['indexx'] = [index_film[i] for i in result['indexx'].tolist()]

res_title = [df[df.imdbID == i].originalTitle.values[0] for i in result['indexx'].tolist()]

result['res_title'] = res_title

result.loc[result[result.Category.str.contains('ADD_#1')]['Coef'].index, 'Coef'] = result[result.Category.str.contains('ADD_#1')]['Coef'] / 7

result.loc[result[result.Category.str.contains('ADD_#2')]['Coef'].index, 'Coef'] = result[result.Category.str.contains('ADD_#2')]['Coef'] / 14

result.loc[result[result.Category.str.contains('director')]['Coef'].index, 'Coef'] = 0.99

result['Coef'] = result['Coef'].round(2) * 100

result['Coef'] = result['Coef'].astype(int)

return result[['res_title', 'Category', 'Coef']]

else:

return 'WRONG_FILM_PARAMETER'

else:

result = pd.DataFrame()

for index, row in tqdm(df.iterrows()):

closest_films, distances = get_similar_film_by_nn(row['imdbID'], model, film_index, index_film)

# distances = np.interp(distances, (distances.min(), distances.max()), (0.02, +0.98))

zip_film_score = list(zip(closest_films, distances))

chunk = get_chunk(df, row['imdbID'], closest_films, film_index)

if chunk != 'WRONG_FILM_PARAMETER':

chunk = pd.DataFrame(chunk, columns=['indexx', 'Category'])

zip_film_score = pd.DataFrame(zip_film_score, columns=['indexx', 'Coef'])

temp = pd.merge(chunk, zip_film_score, how='inner', on=['indexx'])

temp['indexx'] = [index_film[i] for i in temp['indexx'].tolist()]

lon_rec = [df[df.imdbID == i].long.values[0] for i in temp['indexx'].tolist()]

temp['long_orig'] = df[df.imdbID == row['imdbID']].long.values[0]

temp['long_rec'] = lon_rec

temp = temp[:20]

add_2_coefs = temp[temp.Category.str.contains('ADD_#2')]['Coef'].values

add_1_coefs = temp[temp.Category.str.contains('ADD_#1')]['Coef'].values

main_coefs = temp[~temp.Category.str.contains('ADD_#')]['Coef'].values

try:

temp.loc[temp[~temp.Category.str.contains('ADD_#')]['Coef'].index, 'Coef'] = np.interp(main_coefs, (main_coefs.min(), main_coefs.max()), (0.6, 0.98))

except:

print('warning! while coef was tried change it was empty')

try:

temp.loc[temp[temp.Category.str.contains('ADD_#1')]['Coef'].index, 'Coef'] = np.interp(add_1_coefs, (add_1_coefs.min(), add_1_coefs.max()), (0.3, 0.59))

except:

print('warning! while coef was tried change it was empty')

try:

temp.loc[temp[temp.Category.str.contains('ADD_#2')]['Coef'].index, 'Coef'] = np.interp(add_2_coefs, (add_2_coefs.min(), add_2_coefs.max()), (0.02, 0.29))

except:

print('warning! while coef was tried change it was empty')

temp.loc[temp[temp.Category.str.contains('director')]['Coef'].index, 'Coef'] = 0.99

temp['Coef'] = temp['Coef'].round(2) * 100

temp['Coef'] = temp['Coef'].astype(int)

result = result.append(temp[['long_orig', 'long_rec', 'Coef', 'Category']])

else:

print(row)

print(row['imdbID'])

print('WRONG_FILM_PARAMETER')

mannual_changes = get_mannual_changes(connection_MySQL)

try:

for index, row in mannual_changes.iterrows():

if row['Type'] == 'Add':

result = result.append(row[['long_orig','long_rec', 'Coef', 'Category']])

else:

result = result[~((result['long_orig'] == row['long_orig']) & (result['long_rec'] == row['long_rec']))]

except:

print('Mannual changes table is empty')

connection = connection_MySQL

cur = connection.cursor()

cur.execute('truncate table RecMovies ')

sql = "INSERT into RecMovies ([MovieId-chosen], [MuvieId-suggested], [Percent], [reason]) values(%s, %s, %d, %s);"

data = [tuple(x) for x in result.values]

cur.executemany(sql, data)

mit()

connection.close()

#For update planeta PROD

# TOKEN='cEtpbm9BZG1pbjpteUtpbm9BZG1pbjIwMTlmdXBkcGxz'

# url_test = ''

# url_prod = connect_to_theSameMovie + '/smart-mailing/admin/forceUpdate?fullUpdate=false'

# headers={'Authorization': 'Basic %s' % TOKEN}

# requests.get(url_test, headers=headers, timeout=600)

# requests.get(url_prod, headers=headers, timeout=600)

main(model_name='epoch_201', save_model = True, debug = False)

-----------------------

Рисунок 2.12 – жанрова перевірка

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download