У попередній темі ми пройшли дошку оголошень наскрізь і жодного разу нічого в ній не змінили. Вийшов список знахідок: дванадцять повних дублікатів; стовпець памʼяті, який pandas прочитав як текст; сто одна порожня ціна, причому порожня не випадково; сорок вісім порожніх значень стану, а ці якраз випадкові; сімдесят вісім оголошень за межею міжквартильного розмаху, серед яких є і колекційні флагмани, і одруки; і колонка «скарг», яка виявилась витоком.
Список є. Тепер його треба виконати — і саме тут починається найцікавіше. Бо жодна з цих дірок не має правильної відповіді в підручнику.
01 / МотивРішення, а не рецепти
Передобробка (preprocessing) — це перетворення таблиці, якою вона приїхала, у таблицю, з якою можна працювати: без дірок, без тексту там, де потрібні числа, з ознаками, що вимірюються в порівнянних одиницях. Звучить як технічна робота. Насправді це найзмістовніша частина всієї задачі, і ось чому.
Візьмімо одну дірку — порожню ціну в оголошенні. Що з нею робити?
- Викинути рядок. Ти кажеш: «оголошень без ціни для мене не існує». Але вони існують, і, як ми зʼясували, серед них шахрайських більше половини.
- Поставити середню ціну. Ти кажеш: «це звичайне оголошення, просто ціну загубили при вивантаженні». Для нашої дошки це неправда.
- Поставити нуль. Ти кажеш: «телефон роздають безкоштовно». Це не неправда навіть — це просто інша неправда, і найгірша з трьох.
Три однорядкові команди, три різні твердження про світ. Код не покаже різниці: усі три відпрацюють без попереджень і повернуть охайну таблицю. Різниця виявиться пізніше — у відповіді, яку ти отримаєш.
Тому головна думка теми проста й неприємна: у передобробці немає операцій,
є рішення. Кожен виклик fillna, кожне видалення рядка, кожне
кодування категорії — це припущення, яке ти вносиш у дані власноруч. Далі все, що ти
дізнаєшся, буде дізнане разом із цим припущенням, і відрізнити одне від одного
вже не вийде.
Звідси й правило, яке проходить через усю тему: перш ніж застосувати перетворення, скажи вголос, що саме воно стверджує. Якщо твердження звучить безглуздо — перетворення теж безглузде, хоч би скільки бібліотек його підтримували.
Почнімо з двох кроків, у яких рішень майже немає. Дублікати: дванадцять
рядків повторюються повністю, вони прийшли зі злиття двох вивантажень, і ми їх прибираємо —
лишається 1 200 рядків. Тип стовпця: памʼять записана як
128 ГБ, ми відрізаємо суфікс і переводимо в число. Після цих двох кроків
пропусків у ціні стає 100, а не 101: один із дублікатів був рядком без ціни. Далі кожен
крок уже вимагатиме вибору.
fillna,
dropna, get_dummies, groupby — ми розібрали в
темі 34 курсу Python, а векторні
операції над матрицею ознак — у темі 07.
Тут ми говоримо не про те, як викликати метод, а про те, що кожен виклик
стверджує про дані.02 / ПропускиПропуски: три шляхи
Із дірками в таблиці можна зробити рівно три речі: викинути рядок, викинути стовпець або чимось заповнити. Розберімо всі три на наших числах.
Шлях перший: викинути рядки
Найчесніший на вигляд варіант: не вигадувати нічого, працювати лише з повними рядками. Порахуймо, скільки їх у нас.
Втрачено 145 рядків — восьма частина таблиці. Але подивімось не на кількість, а на те, які це рядки:
| рядків | шахрайських | частка | |
|---|---|---|---|
| було | 1 200 | 195 | 16.3 % |
| лишилось | 1 055 | 135 | 12.8 % |
Разом із 145 рядками ми викинули 60 шахрайських оголошень із 195 — майже третину всього, заради чого будується задача. І не випадкову третину: саме ту, де шахрай не назвав ціни. Дірки в цій колонці не рівномірні по таблиці, тому й видалення рядків б'є не рівномірно.
Видалення рядків годиться, коли пропуски рідкісні (одиниці відсотків) і випадкові. У нас другої умови немає.
Шлях другий: викинути стовпець
Якщо в колонці порожньо в половині рядків, заповнювати там нічого — ти вигадуєш більше, ніж знаєш. Тоді колонку прибирають цілком, і всі рядки лишаються на місці. Орієнтир простий: понад половина пропусків — колонка радше шкодить; менше десятої частини — заповнюй спокійно; між тим і тим — дивись, що ця колонка взагалі означає.
У нас ціна порожня у 8.3 % рядків, стан — у 4.0 %. Обидві колонки лишаємо.
Шлях третій: заповнити
Найпоширеніший вибір — і найбільше можливостей помилитись. Для числової колонки беруть середнє або медіану; для текстової — найчастіше значення (моду) або окрему категорію «невідомо».
Між середнім і медіаною вибір не косметичний. У темі 08 ми бачили, що ціни розподілені скошено: середнє 6 498 грн, медіана 4 000 грн, і дешевших за середнє оголошень аж 68.6 %. Тобто «середнє оголошення» — це обʼєкт, якого майже не буває. Заповнивши сотню дірок числом 6 498, ти вставляєш у дані сто копій неіснуючого телефона, та ще й у ту частину шкали, де реальних оголошень мало. Медіана менш вигадлива: 4 000 грн — це справді типова ціна на цій дошці.
Правило, яке варто запамʼятати: чим сильніше розподіл скошений, тим надійніша медіана. Середнє тягнуть за собою кілька великих значень — у нас це чотири колекційні Gamma X і два одруки; медіана про них навіть не знає.
А для стану заповнення модою («добре», 483 рядки з 1 152) підіймає частку цього значення
з 41.9 % до 44.2 % — тобто робить найчастішу категорію ще частішою. Іноді це прийнятно.
Часто чесніше створити окрему категорію невідомо: вона нічого не вигадує й
чесно каже, що значення не було.
Інтерактив 1 · Чотири способи закрити дірку
Розподіл цін на нашій дошці, кошик 1 250 грн, вісь обрізана на 25 000 грн. Рожеве — справжні оголошення; яскравий стовпчик — сто значень, яких у даних не було і які ми щойно вигадали.
03 / ПрапорецьПропуск як ознака
Тепер найважливіші двісті слів теми. Поверніться до знахідки теми 08: серед оголошень без ціни шахрайських 56.0 %, серед оголошень із ціною — 12.6 %. Різниця в чотири з половиною рази.
Дивись, що це означає. Ціни немає — значення втрачене, відновити його нічим. Але сам факт відсутності — не втрачений. Він лежить у таблиці, доступний, і він відрізняє шахрая від чесного продавця краще, ніж багато справжніх колонок. Треба лише його записати:
Один рядок — і в таблиці зʼявилась нова ознака. Її звʼязок із таргетом можна виміряти
одразу: кореляція ціни_немає з міткою шахрайства — 0.325.
Для порівняння, та сама кореляція для стану_немає — −0.021,
тобто нічого. Один пропуск інформативний, другий ні, і розрізняє їх одне число.
Чому це так цінно. Ознака не коштувала нічого: ні збору даних, ні домовленостей із іншим відділом, ні розмітки. Вона вже була в таблиці — просто у вигляді порожнечі, яку всі звикли вважати проблемою. Заповнення дірки цю ознаку знищує: щойно ти поставив на місце пропуску 4 000, відрізнити цей рядок від справжніх чотиритисячних оголошень стає неможливо.
І узагальнення, ширше за нашу дошку. Пропуск інформативний скрізь, де його створює
поведінка людини, а не збій техніки. Не вказаний дохід у заявці на кредит; не заповнене
поле «попереднє місце роботи»; порожній результат аналізу, який лікар не призначив, бо
вважав зайвим. У всіх цих випадках прапорець значення_немає — безкоштовна
ознака. Робити його завжди не варто (для випадкових пропусків це шум), але
перевірити, чи він щось несе, коштує один рядок.
04 / КатегоріїТекст у числа
Наступна дірка в наших даних не порожня, а текстова. Колонки «модель» і «стан» — слова, а майже все, що ми будемо з даними робити, вимагає чисел: додавання, множення, відстані. Значить, слова треба перевести в числа. Способів два, і вони стверджують дуже різні речі.
Порядкове кодування
Порядкове кодування (ordinal encoding) — це «пронумеруймо значення». Alfa A5 → 0, Alfa A7 → 1, Beta 12 → 2, і так далі. Колонка була одна — колонка й лишилась, дешево та компактно.
Тепер скажи вголос, що це стверджує. Що Beta 12 (2) удвічі більша за Alfa A7 (1). Що відстань від Alfa A5 до Alfa A7 така сама, як від Gamma X до Gamma X Ultra. Що Beta 12 лежить рівно посередині між Alfa A5 і Gamma X. Останнє легко перевірити: медіанні ціни цих трьох моделей — 1 845, 4 215 і 9 350 грн, а середина між крайніми — 5 598. Тобто код каже «посередині», а насправді Beta 12 на третину дешевша за середину.
І найгірше: числа тут беруться з назв, а не зі світу. Додай на дошку дешеву модель «Delta Z» — за алфавітом вона дістане код 6, найбільший, тобто опиниться на місці найдорожчого телефона. Дані не змінились — змінилась лише вигадана нами шкала.
Але для колонки «стан» порядкове кодування — правильний вибір, і це важливо не переплутати. «Задовільне → 0, добре → 1, дуже добре → 2, нове → 3» — тут порядок є насправді, він у самих значеннях, і числа його не вигадують, а записують. Ознаки з внутрішнім порядком звуться порядковими (ordinal), і саме для них цей спосіб і придуманий.
Пряме кодування
Пряме кодування, воно ж one-hot — це «колонка на кожне значення». Замість однієї колонки «модель» зʼявляються шість, у кожному рядку рівно одна одиниця, решта нулі. Ніякого порядку не стверджується: усі шість моделей рівновіддалені одна від одної, і це чесно.
Схема 1 · Одна колонка проти шести
Ліворуч — те, що робить пряме кодування: одна одиниця в рядку, і жодних тверджень про порядок. Праворуч — те, що стверджує порядкове кодування, поруч із тим, як воно є насправді.
За пряме кодування платять шириною таблиці. Шість моделей — шість колонок; чотири значення стану плюс «невідомо» — пʼять колонок. Разом одинадцять колонок замість двох. Поки що дрібниця. Але уяви колонку «місто» з чотирма сотнями значень: чотириста колонок, у кожній майже самі нулі, і в більшості з них — одиниця в трьох-чотирьох рядках з тисячі. Це називають високою кардинальністю (high cardinality), і пряме кодування на ній розсипається: таблиця роздувається, а користі майже немає.
Виходи є. Найпростіший — залишити найчастіші двадцять значень, а решту зсипати в категорію «інше». Другий, спокусливіший — цільове кодування (target encoding): замінити кожне місто середнім значенням таргета в цьому місті. Виглядає це чудово: одна колонка замість чотирьохсот, та ще й одразу «корисна». Пастка теж є, і велика — саме тому цільове кодування ми розберемо окремо, у наступній темі, разом із захистом від неї. Поки що досить знати, що воно існує і що вмикати його не думаючи не можна.
05 / МасштабГривні, дні й гігабайти
Числа в нашій таблиці міряються в різних одиницях, і розкид у них різний на порядки: ціна — тисячі гривень, вік акаунта — сотні днів, памʼять — десятки гігабайтів, рік — чотиризначне число, яке взагалі майже не змінюється. Поки кожну колонку розглядають окремо, це не заважає. Щойно їх починають порівнювати між собою — заважає одразу.
Найпростіший приклад такого порівняння — схожість двох оголошень. Візьмімо три справжні рядки з нашої дошки.
| модель | ціна | вік акаунта | |
|---|---|---|---|
| базове | Beta 12 Pro | 4 620 грн | 459 днів |
| А | Alfa A7 | 4 650 грн | 273 дні |
| Б | Gamma X | 5 480 грн | 466 днів |
Яке з двох ближче до базового? Порахуймо різниці, як їх рахують у школі — по теоремі Піфагора, ніби це дві координати точки:
Для А різниці такі: 30 гривень і 186 днів. Для Б: 860 гривень і 7 днів.
Виходить, що А ближче. Але подивись, що ми щойно зробили: оголосили, що 860 гривень — це «більше», ніж 186 днів. Гривні порівняли з днями, ніби це однакові величини.
Виміряймо кожну різницю не в її одиницях, а в тому, наскільки вона велика для своєї колонки. Розкид цін на дошці — 8 685 грн, розкид віку акаунтів — 407 днів.
Тепер картина перевертається. 860 гривень — це дрібниця на тлі того, як ціни взагалі гуляють. А 186 днів — майже пів розкиду віку акаунтів, тобто справді інший продавець. Ближчим виявляється Б.
Те, що ми щойно зробили руками, і зветься масштабуванням. У NumPy воно займає один рядок на всю матрицю ознак одразу — рівно той broadcasting, який ми розбирали в темі 07. Двох способів вистачає на майже все.
Стандартизація (standardization): від кожного значення відняти середнє колонки й поділити на її стандартне відхилення. Результат каже «на скільки розкидів це значення вище або нижче за типове». Для ціни 4 620 грн при середньому 6 498 і відхиленні 8 685 виходить (4 620 − 6 498) / 8 685 = −0.22: трохи дешевше за середнє. Нуль означає «рівно середнє», одиниця — «на один розкид вище».
Мінімакс (min-max): відняти мінімум і поділити на розмах, тобто втиснути колонку рівно у відрізок від 0 до 1. Для тієї самої ціни: (4 620 − 130) / (95 000 − 130) = 0.047.
Другий спосіб виглядає охайнішим, і саме тут ховається його вада. Розмах ціни на нашій
дошці задають два значення: найдешевший телефон за 130 грн і колекційна Gamma X за
95 000. Одне-єдине оголошення розтягує шкалу так, що 81.7 % усіх цін лягають
нижче 0.1 — 95 % таблиці тиснеться в пʼятій частині відрізка. Стандартизація
теж не любить викидів, але переживає їх спокійніше: під |z| < 1 потрапляє
92.3 % оголошень, і шкала лишається читабельною.
Інтерактив 2 · Хто ближчий до базового оголошення
Базове оголошення — у центрі. Точки А і Б показані як зсув від нього по двох осях. Кола проведені через А і через Б: чиє коло менше, той і ближчий. Масштаб обох осей однаковий, тому кола можна порівнювати очима.
Коли масштабування потрібне, а коли ні? Правило таке: воно потрібне скрізь, де колонки складають, множать або порівнюють між собою — у відстанях, у сумах із коефіцієнтами, у будь-якому геометричному міркуванні. Воно не потрібне там, де кожну колонку розглядають окремо й лише порівнюють значення всередині неї.
06 / ВикидиЩо робити з викидами
У темі 08 ми знайшли 78 оголошень за межею 1.5·IQR і зʼясували головне: викид — не синонім помилки. Чотири колекційні Gamma X за 82–95 тисяч гривень справжні, два оголошення з зайвим нулем — ні, а правило міжквартильного розмаху їх не розрізняє. Тепер треба вирішити, що з ними робити. Варіантів чотири.
Лишити як є. Правильно завжди, коли значення справжнє й ти вмієш із ним працювати. Колекційний телефон за 95 000 грн — частина ринку, і викидати його означає брехати про ринок.
Обрізати (winsorize): усе, що вище межі, замінити самою межею. Значення лишається великим, але перестає тягнути на себе арифметику. На наших цінах обрізання по верхній межі 16 061 грн опускає середнє з 6 498 до 5 557 грн, а максимум — до тієї самої межі. Рядки цілі, розподіл цілий, крайні значення приборкані.
Прибрати рядок. Годиться лише тоді, коли ти впевнений, що це помилка запису. Два оголошення з зайвим нулем — саме такий випадок; краще за видалення тут хіба що виправлення на 8 350 і 7 600 грн. Автоматичне видалення всього за межею коштувало б нам 75 рядків, серед них — увесь дорогий кінець ринку.
Логарифмувати. Найелегантніший варіант, коли викиди справжні, а розподіл скошений. Замість самої ціни беруть її логарифм, і величезні значення підтягуються до решти самі, без жодного правила й жодної межі.
Десятковий логарифм відповідає на питання «скільки нулів». Ціна 1 000 грн дає 3, ціна 10 000 — 4, ціна 95 000 — 4.98. Тобто розрив у сто разів перетворюється на розрив у дві одиниці.
Скошеність (skewness) — одне число, що міряє, наскільки хвіст витягнутий у правий бік. Нуль означає симетрію.
Було сильно скошено — стало практично симетрично. І ще одна перевірка: середнє логарифмів, повернуте назад у гривні, дає 4 075 — тобто майже точно медіану 4 000, а не роздуте середнє 6 498.
Схема 2 · Та сама колонка до й після логарифма
Ліворуч — ціни як є: пік притиснутий до лівого краю, довгий хвіст, у який не влазить вісь. Праворуч — десятковий логарифм тих самих цін, уся колонка без обрізань.
log(1 + x). По-друге, усе, що ти далі
порахуєш, буде порахувано в логарифмах, і назад у гривні воно повертається не завжди
очевидно. Це чесна ціна, і платити її варто свідомо.07 / ВитікВитік через передобробку
А тепер найдорожча помилка в усій темі. Вона не в тому, що ти робиш із даними, а в тому, у якому порядку.
Ось як виглядає природний хід думки. «Спершу приведімо таблицю до ладу: заповнимо дірки медіаною, масштабуємо колонки, закодуємо категорії. Тепер дані чисті — можна ділити на навчальну й тестову частини й міряти якість». Звучить бездоганно. І це помилка.
Медіана, якою ти заповнив дірки, порахована по всій таблиці — включно з рядками, які за хвилину стануть тестовими. Середнє й відхилення для масштабування — теж. Тобто в момент, коли ти нібито «ще не бачив» тестову частину, вона вже встигла вплинути на кожне число в навчальній. Тема 03 називає це прямо: тест має грати роль майбутнього, якого ще немає. А тут майбутнє підглянуло саме в себе.
Покажімо, скільки це коштує. Візьмімо задачу, яку можна розвʼязати без жодної моделі: позначити підозрілі оголошення за ціною. З теми 08 ми знаємо, що звʼязок ціни з шахрайством U-подібний — підозрілі і найдешевші, і найдорожчі. Отже, правило таке: оголошення підозріле, якщо його ціна нижча за поріг П1 або вища за поріг П2. Пороги ми не вигадуємо — підбираємо за даними, окремо для кожної пари «модель + стан», бо для Alfa A5 і для Gamma X Ultra «дешево» означає різні суми. Груп виходить 30, порогів — 60 чисел, і всі вони вивчені з таблиці.
Далі ділимо дошку на 900 навчальних рядків і 300 тестових і міряємо якість на тестових через precision, recall і F1. Двома способами.
Інтерактив 3 · Порядок, у якому рахують
Ті самі 1 200 рядків, той самий поділ, те саме правило. Різниця лише в тому, звідки взято числа, які правило вивчило. Оцінка — на тих самих 300 тестових рядках.
Звідки береться саме така величина? Порогів 60, а груп, у яких вони підбираються, аж 30, і в найменшій із них усього пʼять рядків. На пʼятьох рядках «найкращий поріг» — це поріг, підігнаний під ці пʼять рядків. Коли вони всі були доступні під час підбору, правило їх просто запамʼятало; коли доступні лише навчальні, воно мусить вгадувати. Чим більше чисел ти вивчаєш із даних, тим дорожчий витік:
| що вивчаємо | неправильно | правильно | завищення |
|---|---|---|---|
| 2 числа: спільні пороги | 0.360 | 0.333 | +0.027 |
| 12 чисел: пороги на модель | 0.615 | 0.565 | +0.050 |
| 60 чисел: модель і стан | 0.631 | 0.483 | +0.147 |
Числа в таблиці — середні F1 по 60 різних поділах, щоб не залежати від щасливого випадку. Нижній рядок — той, що на картинці вище. Верхній показує головну підступність: коли вивчених чисел мало, завищення дрібне, його не помічають — і саме тому звикають робити неправильно.
08 / КонвеєрПорядок дій і конвеєр
Отже, порядок. Кроки бувають двох сортів, і сортувати їх треба саме за цією ознакою:
- Кроки, які нічого не вивчають. Прибрати дублікати, відрізати суфікс «ГБ», перевести стовпець у число, викинути колонку «скарг». Вони роблять із кожним рядком те саме незалежно від решти таблиці — тому їх можна робити до поділу.
- Кроки, які щось вивчають. Заповнення (треба медіана), масштабування (треба середнє й відхилення), кодування категорій (треба список значень), обрізання викидів (треба межі). Ці — тільки після поділу й тільки на train.
Схема 3 · Де проходить лінія поділу
Зверху — кроки, які роблять із кожним рядком те саме. Далі — поділ. Знизу — кроки, які спершу щось вивчають, і напрямок, у якому вивчене має право рухатись.
Тримати це в голові на десяти колонках і чотирьох перетвореннях — справа безнадійна.
Тому в scikit-learn є конвеєр (Pipeline) —
обʼєкт, який складає всі кроки передобробки в один ланцюжок і має рівно два методи:
fit — «вивчи все, що треба вивчити» — і transform — «застосуй
вивчене». Далі ти пишеш конвеєр.fit(train) — і підмішати туди тест уже
нічим: до тестової частини застосовується transform, а він нічого не вчить,
він лише підставляє вивчене.
Конвеєр — не турбота про красу коду. Це те, що фізично унеможливлює помилку з попереднього розділу: не існує способу випадково застосувати до тесту статистику, яку вивчили на тесті. У нашому зошиті конвеєр із чотирьох кроків перетворює сім вихідних колонок на 16 числових і, навчений на 900 навчальних рядках, запамʼятовує медіану ціни 3 975 грн — навчальну, а не загальну 4 000. Ця різниця в 25 гривень і є вся чесність вимірювання.
09 / ПідсумокТаблиця рішень
Зберімо тему в один аркуш. Третя колонка тут найважливіша: у передобробці не буває безкоштовних дій, і питання завжди не «що правильно», а «чим я готовий заплатити».
Таблиця · Проблема, способи і ціна кожного
Жоден рядок не має єдиної правильної відповіді. Правильність визначається тим, що означають дані, а не тим, що зручніше набрати.
| проблема | способи | чим платиш |
|---|---|---|
| Пропуски, яких мало й вони випадкові | видалити рядки; заповнити медіаною або модою | видалення — рядками; заповнення — штучним піком у розподілі |
| Пропуски, повʼязані з таргетом | прапорець «значення немає» плюс заповнення | зайва колонка; забудеш прапорець — втратиш ознаку назавжди |
| Пропусків понад половину стовпця | прибрати стовпець | усім, що в ньому було, разом із самим фактом пропуску |
| Категорія без порядку | пряме кодування | шириною таблиці: колонка на кожне значення |
| Категорія з порядком | порядкове кодування | твердженням, що кроки між значеннями однакові |
| Категорія з сотнями значень | «інше» для рідкісних; цільове кодування | злиттям різних речей в одну; ризиком витоку |
| Колонки в різних одиницях | стандартизація; мінімакс | читабельністю сирих чисел; мінімакс — беззахисністю перед викидом |
| Викид, який справжній | лишити; обрізати; логарифмувати | обрізання — величиною; логарифм — простотою тлумачення |
| Викид, який є помилкою | виправити; прибрати рядок | рядком і часом на розбір |
| Статистики для всіх цих кроків | рахувати лише на train, через конвеєр | нічим — це єдиний рядок таблиці без ціни |
10 / ДаліЩо далі
Таблиця, з якою ми почали, тепер придатна до вжитку: без дублікатів, з числами замість
тексту, без дірок, з ознаками в порівнянних одиницях і без колонки «скарг», яка була
витоком. Але зверни увагу на дещо: стовпців у ній стало більше, ніж було.
Ми додали ціни_немає, і ця вигадана з порожнечі колонка виявилась
інформативнішою за деякі справжні.
Це вже не прибирання. Це інженерія ознак (feature engineering) — тема наступної лекції. Там ми будемо не лагодити колонки, а робити нові: ціна відносно типової для цієї моделі; вік телефона замість року випуску; ознаки, зібрані групуванням; і цільове кодування разом із захистом від пастки, яку ми щойно пообіцяли розібрати. З того самого U-подібного звʼязку ціни з шахрайством, який зіпсував нам кореляцію в темі 08, там вийде одна колонка, у якій звʼязок стане прямим.
А головне з цієї теми — те, з чого вона почалась. Передобробка виглядає як підготовча робота, яку хочеться зробити швидко й забути. Насправді це десяток тверджень про світ, які ти вписуєш у дані власною рукою. Виписуй їх свідомо — і, будь ласка, рахуй усе, що вчиться, лише на навчальній частині.
practice.ipynb ти збереш ту саму
дошку, пройдеш кожне рішення з цієї теми по черзі й надрукуєш його наслідки, додаси
колонку-прапорець, закодуєш категорії обома способами й порівняєш, масштабуєш числа,
відтвориш витік із сьомого розділу до цифри — і збереш усе в конвеєр, який робить це
правильно сам.Далі в темі
Теорію прочитано. Тепер закріпи її на практиці.