Машинне навчання · Блок 2 · Тема 08

Розвідка даних

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

Тобі передали файл із дошки оголошень про вживані телефони: 1 212 рядків, вісім стовпців, супровідний лист на три речення. «Ціна в гривнях, памʼять у гігабайтах, остання колонка — чи виявилось оголошення шахрайським». Файл відкривається, помилок немає, усе на місці. Здається, можна працювати.

Не можна. У цій таблиці зараз лежать: 101 порожня ціна, причому порожня не випадково; 12 рядків, які просто продубльовані; стовпець із памʼяттю, який pandas прочитав як текст; шість оголошень дорожчих за 70 000 грн, з яких частина справжня, а частина — одруки; і один стовпець, звʼязаний із відповіддю настільки добре, що ним хочеться одразу скористатись — і саме тому не можна.

Нічого з цього не видно з опису файлу. Усе це видно за пʼятнадцять хвилин, якщо знати, куди дивитись. Це і є розвідувальний аналіз даних (exploratory data analysis, EDA) — і ця тема саме про нього.

01 / МотивДані не такі, як їх описали

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

Дані збирали не для тебе. Колонку «стан телефона» заповнює продавець у вікні з випадним списком — і половина вибирає перший пункт, бо швидше. Колонку «скарг» проставляє служба підтримки. Ніхто з них не думав про майбутню модель.

Дані зберігали через кілька систем. Числа проходять через експорт у таблицю, вивантаження у файл, читання назад — і на кожному кроці «128» може перетворитись на «128 ГБ», а порожнє поле — на текст «немає».

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

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

Уся тема — на одній таблиці. Ось вона.

стовпецьщо цетип
модельназва телефона, шість різних моделейтекст
рікрік випуску, від 2017 до 2024ціле
станнове, дуже добре, добре, задовільнетекст
памʼять_гбобсяг памʼяті: 64, 128, 256 або 512?
вік_акаунтаскільки днів існує акаунт продавцяціле
скаргскільки скарг надійшло на це оголошенняціле
цінаціна в оголошенні, гривеньдробове
шахрайськетаргет: 1 — оголошення виявилось шахрайським0 або 1

Знак питання в третьому рядку не помилка верстки. Це те, що ми зараз побачимо.

Що треба знати заздалегідь. Механіку pandas — DataFrame, Series, читання файлів, loc і iloc, фільтр умовою, groupby, fillna — ми розібрали в темі 34 курсу Python. Тут ми жодного разу не пояснюємо, як викликати метод. Ми говоримо лише про те, що читати в його відповіді. Графіки — на matplotlib з теми 35.

02 / ОглядПерший погляд: пʼять команд

Перше знайомство з будь-якою таблицею — це пʼять викликів. Вони завжди ті самі, і робити їх треба навіть тоді (особливо тоді), коли ти впевнений, що знаєш, що всередині. Цінність не в командах, а в питаннях, які до кожної з них поставлені.

shapeчи стільки рядків, скільки я очікував. Наша таблиця дає (1212, 8). Якщо в листі було сказано «вивантаження за рік, близько тисячі оголошень» — усе гаразд. Якщо було сказано «40 тисяч» — далі можна не дивитись, спершу треба розібратись, куди поділись 39 тисяч рядків. Це найдешевша перевірка з усіх і найчастіше пропущена.

head()чи значення схожі на те, що обіцяє назва стовпця. Не «які тут числа», а саме це. У нашій таблиці другий рядок одразу показує NaN у стані, а четвертий — памʼять, записану як 256 ГБ. Обидві дрібниці видно неозброєним оком у перших пʼятьох рядках, і обидві коштуватимуть годину, якщо їх не помітити.

dtypesчи всі числові стовпці справді числові. Ось що віддає наша таблиця:

модель object рік int64 стан object памʼять_гб object ← має бути число вік_акаунта int64 скарг int64 ціна float64 шахрайське int64

object у pandas означає «щось, що я не зміг звести до числа» — зазвичай текст. Для «моделі» й «стану» це нормально, для «памʼять_гб» — тривожний знак. Тип не буває «майже правильним»: досить одного нечислового значення, щоб уся колонка стала текстом. Тобто object у числовій колонці — це не властивість даних, а слід події: десь у файлі є рядок, який випадає із загального правила.

У нас таких рядків 215 із 1212: у них памʼять записана як 128 ГБ замість 128. І тут ховається справжня пастка. Найочевидніша реакція — «зведімо колонку до чисел» — виглядає так:

pd.to_numeric(дошка["памʼять_гб"], errors="coerce") # 215 нових NaN

errors="coerce" означає «те, що не зміг прочитати, зроби пропуском». Ця команда не падає, не попереджає й нічого не пише в консоль. Вона просто мовчки перетворює 215 цілком справжніх значень на порожнечу — сьома частина таблиці. Правильна дія — спершу прибрати суфікс, і лише потім переводити в число:

памʼять = дошка["памʼять_гб"].str.replace(" ГБ", "") памʼять = pd.to_numeric(памʼять) # 0 нових NaN

info()де дірки. Він друкує для кожного стовпця кількість непорожніх значень, і її треба порівнювати з першим рядком виводу. У нас 1212 entries, а в колонці «ціна» стоїть 1111 non-null — отже, 101 порожня. У колонці «стан» — 1164, отже 48 порожніх. Решта стовпців повні.

describe()чи числа взагалі бувають такими. Його читають не зверху вниз, а трьома парами рядків. Ось вивід для ціни:

count 1111.0 ← менше за 1212: тут дірки mean 6508.8 ← середнє std 8674.0 ← розкид більший за середнє min 130.0 ← 130 грн за телефон? 25 % 2180.0 50 % 4010.0 ← медіана в півтора раза менша за середнє 75 % 7700.0 max 95000.0 ← 95 000 грн за вживаний телефон?

Пара перша: count проти кількості рядків. Різниця — це пропуски, і вона тут є.

Пара друга: min і max проти здорового глузду. Не «чи це можливо в принципі», а «чи буває таке на дошці вживаних телефонів». 130 грн — буває, це майже брухт. 95 000 грн теж буває, але на ці рядки доведеться подивитись окремо.

Пара третя: mean проти 50 %. Середнє 6 509, медіана 4 010. Коли ці два числа розходяться в півтора раза, у даних або довгий хвіст, або кілька дуже великих значень, або помилки введення. У нас, як зʼясується, всі три.

І шоста команда, яку варто дописати до цих пʼятьох: duplicated().sum(). Вона дає 12. Дванадцять рядків у таблиці повторюються повністю, до останнього символу. Найчастіша причина — оголошення подали двічі або вивантаження злилося з двох файлів. Дублікати не ламають нічого одразу, але вони тихо збільшують вагу тих самих обʼєктів у будь-яких підрахунках.

Таблиця 1 · Що бачу у виводі → про що це говорить

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

бачупро що це говоритьщо зробити
Числовий стовпець має тип objectдесь є нечислове значення — суфікс, пробіл між тисячами, кома замість крапки, слово «немає»подивитись на унікальні значення, розібрати текст, лише потім переводити в число
count менший за кількість рядківу стовпці є пропускизʼясувати, у яких саме рядках, і чи це випадково
Середнє помітно більше за медіанурозподіл скошений праворуч, є довгий хвістдивитись на медіану, а не на середнє; побудувати гістограму
std більше за meanрозкид величезний, у стовпці змішані різні за природою обʼєктирозділити за категорією і порахувати окремо
min дорівнює нулю там, де нуль неможливийнулем позначили відсутність значеннязамінити нулі на NaN перед будь-якими підрахунками
max у сотні разів більший за медіануабо справжній рідкісний обʼєкт, або зайвий нуль при введенніподивитись на ці рядки цілком, з усіма стовпцями
Кількість унікальних значень дорівнює кількості рядківце ідентифікатор, а не ознакаприбрати зі списку ознак
duplicated().sum() більше нулярядки повторюються повністюзрозуміти, звідки повтори, перш ніж їх прибирати
Останній рядок — найпідступніший. Прибрати дублікати одним викликом легко, але спершу треба знати, чому вони зʼявились. Якщо це збій вивантаження — їх видаляють. Якщо продавець справді подав те саме оголошення двічі — це поведінка, і вона може бути ознакою.

03 / ПропускиПропуски: скільки, де і чому

Порахувати пропуски вміє один рядок: дошка.isna().sum(). У нас 101 у ціні й 48 у стані. Це відповідь на питання «скільки» — і саме тут більшість зупиняється. А найважливіше питання інше: чому саме ці рядки.

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

  1. Випадковий. Значення зникло без звʼязку з чим завгодно — збій запису, людина проґавила поле. Такі пропуски найбезпечніші: заповнення їх майже не спотворює.
  2. Повʼязаний з іншими стовпцями. Наприклад, стан не вказують частіше для старих телефонів. Пропуск не випадковий, але його можна пояснити тим, що в таблиці вже є.
  3. Повʼязаний із самим значенням, якого немає. Ціну не вказують саме тоді, коли вона незручна. Це найгірший сорт: те, що зникло, зникло через себе саме, і в даних не лишилось нічого, чим це відновити.

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

рядкискількичеснихшахрайськихчастка шахрайських
ціна вказана1 11197213912.5 %
ціни немає101445756.4 %

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

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

дошка["ціни_немає"] = дошка["ціна"].isna().astype(int)

Порівняй це зі стовпцем «стан». Там пропусків 48, і частка шахрайських серед них 12.5 % проти 16.3 % у решті таблиці — різниця в межах випадковості на такій кількості рядків. Пропуск у стані ні про що не говорить; пропуск у ціні говорить майже все. Один рядок коду розрізняє ці випадки, і зробити його треба до того, як заповнювати дірки.

Схема 1 · Пропуск як ознака

Ті самі 1 212 рядків, поділені двічі: за пропуском ціни й за пропуском стану. Висота стовпчика — частка шахрайських оголошень у групі.

Ліва пара розходиться, права — ні. Це і є вся перевірка: якщо стовпчики в парі різної висоти, пропуск несе інформацію й заслуговує на власну колонку. Якщо однакової — пропуск випадковий, і його можна спокійно заповнювати. Пунктирна лінія — середня частка шахрайських по всій таблиці, 16.2 %.

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

04 / ФормаФорма розподілу

describe() віддає сім чисел, і цього мало. Сім чисел не розрізняють розподіл з одним піком і розподіл з двома, а це різні світи. Тому наступний крок після таблиці — гістограма: вісь ціни ріжуть на однакові відрізки (кошики) і рахують, скільки оголошень потрапило в кожен.

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

Інтерактив 1 · Скільки кошиків — стільки й висновків

Ціни з нашої таблиці, 1 111 оголошень із вказаною ціною. Вісь обрізана на 25 000 грн — вище лежать 33 оголошення, і про них окрема розмова в наступному розділі. Рожева лінія — середнє, бірюзова — медіана.

середнє, ₴
медіана, ₴
у піку
ширина кошика
Прожени повзунок в обидва краї. На трьох кошиках усе зліва — один стовпчик на 864 оголошення, і висновок «найтиповіша ціна — біля нуля»: це неправда, її створила ширина кошика у вісім тисяч гривень. Пік відліплюється від нуля лише на одинадцятому кошику. На двадцяти він стоїть там, де він і є насправді — між 1 250 і 2 500 грн, — і добре видно довгий хвіст праворуч. На вісімдесяти сусідні стовпчики стрибають на десятки відсотків, а місцями й удвічі: це вже не форма розподілу, а кількість оголошень, яка випадково потрапила в конкретний відрізок. Робочий діапазон широкий, але не безмежний: приблизно від 15 до 40 кошиків на тисячу значень.

Форма, яку ми бачимо, зветься скошеною праворуч (right-skewed): пік зліва, довгий хвіст справа. Так виглядає майже все, що не буває відʼємним і має верхню межу лише теоретично — ціни, зарплати, час очікування, розмір міста. Протилежна форма, симетричний горбик, у природних даних трапляється рідше, ніж здається з підручників.

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

крок 1 · що таке кожне з чисел

Середнє — сума всіх цін, поділена на кількість. Кожне оголошення вносить свою суму цілком: телефон за 95 000 грн важить у сумі стільки ж, скільки двадцять телефонів по 4 750 грн.

Медіана — значення, яке стоїть посередині відсортованого списку. Їй байдуже, наскільки велике найбільше число: важливо лише, що воно більше за середину.

крок 2 · що вийшло на наших даних
середнє = 6 509   ·   медіана = 4 010

Розрив у 1.62 раза. Перевіримо, звідки він: скільки оголошень дешевші за середнє?

763 з 1 111  =  68.7 %
крок 3 · висновок

Понад дві третини оголошень дешевші за «середнє оголошення». Отже, речення «середня ціна на дошці — 6 509 грн» описує обʼєкт, якого майже не буває: воно правдиве арифметично й хибне по суті. Речення «половина оголошень дешевша за 4 010 грн» описує саме те, що є.

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

05 / ВикидиВикиди й межа

У хвості розподілу лежать викиди (outliers) — значення, які помітно відірвались від решти. Найпоширеніший спосіб їх позначити не потребує ніяких припущень про форму розподілу і зветься правилом міжквартильного розмаху. Порахуймо його руками на наших числах.

крок 1 · квартилі

Квартилі — це три значення, що ділять відсортований список на чотири рівні за кількістю частини. Нам потрібні перший і третій: нижче Q1 лежить чверть оголошень, нижче Q3 — три чверті. describe() уже їх надрукував:

Q1 = 2 180   ·   Q3 = 7 700
крок 2 · міжквартильний розмах

Це просто відстань між ними — ширина смуги, у якій живе середня половина оголошень. Позначають IQR (interquartile range).

IQR = 7 700 − 2 180 = 5 520

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

крок 3 · межі

Від квартилів відкладають півтора розмаху в кожен бік:

верхня = 7 700 + 1.5 · 5 520 = 15 980
нижня = 2 180 − 1.5 · 5 520 = −6 100

Нижня межа вийшла відʼємною, а ціна відʼємною не буває — отже, знизу викидів немає жодного. Усе цікаве вгорі: 78 оголошень дорожчі за 15 980 грн, це 7.0 % таблиці.

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

Інтерактив 2 · Де провести межу

Кожна риска внизу — одне оголошення. Зверху — «скринька»: прямокутник від Q₁ до Q₃, лінія посередині — медіана, вуса — до поточної межі. Оголошення за межею позначені рожевим.

верхня межа, ₴
викидів
частка таблиці
серед них шахрайських
Множник не має правильного значення — його має рішення. На 0.5 за межею опиняється кожне шосте оголошення, і слово «викид» втрачає сенс. На 3.0 лишається 35 рядків — це вже справді окремі випадки. Але жодне з цих чисел не каже, що з ними робити: правило вміє лише показати пальцем.

І тепер найважливіше в цьому розділі. Викид — не синонім помилки. Правило міжквартильного розмаху не знає нічого про телефони; воно знає лише, що число велике. Подивімось на шість найдорожчих оголошень цілком, з усіма стовпцями:

модельрікстанпамʼятьскаргціна
Gamma X2017нове512095 000
Gamma X2017нове512091 000
Gamma X2017нове512088 000
Beta 12 Pro2021добре256083 500
Gamma X2017нове512082 000
Gamma X Ultra2017добре256076 000

Чотири рядки з шести — це та сама комбінація: Gamma X 2017 року, стан «нове», 512 гігабайтів. Запакований флагман семирічної давності, який ніколи не вмикали. За такі речі колекціонери платять, і 90 000 грн тут не помилка, а ринок. Прибрати ці рядки означає викинути справжнє явище, яке в даних є.

Два інші рядки — Beta 12 Pro у стані «добре» за 83 500 і Gamma X Ultra 2017 року за 76 000 — на тій самій висоті, але сусідні стовпці їх не підтримують. Телефон у стані «добре» не коштує як чотири нових. Це майже напевно зайвий нуль при введенні: 8 350 стало 83 500, 7 600 стало 76 000. Дві однакові за величиною ціни, і два протилежні рішення — причому розрізняє їх не ціна, а решта рядка.

Правило, що економить дні. Рішення «видалити чи лишити» змістове, а не технічне. Число за межею — привід відкрити рядок цілком і спитати, чи буває таке насправді. Автоматичне видалення всього за межею 1.5·IQR прибрало б у нас 78 рядків — і разом з ними чотирьох колекційних, увесь дорогий кінець ринку і чималу частину шахрайських оголошень із завищеною ціною.

І ще один наслідок, який видно тільки на числах. Викиди спотворюють не лише середнє — вони ламають підрахунки звʼязків. Для моделі Gamma X кореляція між роком випуску й ціною дорівнює 0.06: наче зв'язку немає взагалі. Прибираємо чотири колекційні рядки зі 122 — і та сама кореляція стає 0.80. Чотири рядки з півтори сотні перевернули висновок.

06 / ЗвʼязкиЗвʼязки між ознаками

Кожен стовпець ми подивились окремо. Тепер попарно: чи повʼязані вони між собою і чи повʼязані з таргетом. Стандартний інструмент — кореляція Пірсона, число від −1 до +1.

Словами вона означає ось що: наскільки добре хмару точок можна замінити однією прямою. +1 — точки лежать на прямій, що йде вгору. −1 — на прямій, що йде вниз. 0 — прямої, яка щось описує, немає. Одна команда рахує всі пари одразу:

дошка[числові_стовпці].corr()
пара стовпцівкореляціяяк це читати
скарг ↔ шахрайське0.887майже ідеальний звʼязок — і саме тому підозрілий
рік ↔ ціна0.249новіші дорожчі, але слабко
памʼять ↔ ціна0.209більше памʼяті — трохи дорожче
вік акаунта ↔ шахрайське−0.101у шахраїв акаунти молодші
вік акаунта ↔ ціна−0.030звʼязку немає
ціна ↔ шахрайське−0.012звʼязку немає?

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

Рік і ціна: 0.249. Здається, вік телефона майже не впливає на ціну, що безглуздо. Причина в тому, що ми змішали шість різних моделей: Alfa A5 2024 року коштує дешевше за Gamma X Ultra 2017-го, і ця різниця забиває залежність від року. Візьмімо одну модель і лише чесні оголошення — Beta 12, 171 рядок — і порахуймо ту саму кореляцію: 0.840. Звʼязок був увесь час; кореляція по всій таблиці його не показала, бо всередині таблиці лежать шість різних ринків.

Ціна і шахрайство: −0.012. Практично нуль. Спокуса тут очевидна — викреслити ціну зі списку корисних ознак і піти далі. Це була б помилка, і ось чому. Порахуймо частку шахрайських окремо в кожній десятій частині цін, від найдешевших до найдорожчих:

десята частина за ціноюмежі, грнчастка шахрайських
найдешевшідо 1 23042.9 %
друга1 230 – 1 82018.9 %
третя1 820 – 2 51012.6 %
середина4 010 – 5 0602.7 %
девʼята9 040 – 13 5507.2 %
найдорожчіпонад 13 55018.0 %

Від 42.9 % до 2.7 % і назад до 18.0 %. Звʼязок величезний — просто він не прямий, а U-подібний: підозрілі й дуже дешеві оголошення (приманка «неймовірна знижка»), і дуже дорогі (виманити велику передоплату). Пряма, проведена крізь таку хмару, лежить горизонтально, і кореляція чесно повідомляє нуль. Вона не помилилась — вона відповіла на своє питання, а ми поставили інше.

Інтерактив 3 · Кореляція проти звʼязку

Пʼять пар стовпців із нашої таблиці. Точки — оголошення; вісь цін обрізана, найдорожчі оголошення в кадр не потрапляють. Жирна ламана — типове значення по вертикальній осі всередині кожної десятої частини за горизонтальною: медіана ціни, а для колонки з нулів і одиниць — частка одиниць. Там, де вертикальна вісь двійкова, точки трохи розсунуті, щоб не злипались.

кореляція
рядків
низ ламаної
верх ламаної
Порівняй третю кнопку з четвертою. В обох кореляція практично нульова: −0.030 і −0.012. Але в третій ламана справді пласка — звʼязку немає, і кореляція права. У четвертій ламана падає з 42.9 % до 2.7 % і повертається до 18.0 % — звʼязок є, величезний, а кореляція його не бачить. Одне й те саме число означає в цих двох випадках протилежні речі, і розрізняє їх лише картинка. Перші дві кнопки показують інший бік того самого: ламана там майже однакова, а кореляція відрізняється втричі, бо міряє вона не силу залежності, а те, наскільки тісно точки тиснуться до прямої.

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

Скарги і шахрайство: 0.887. Третій рядок, і найважчий. Почнімо з загального: кореляція не означає причину. Це фраза, яку всі чули й майже ніхто не застосовує, тож ось вона на нашій дошці.

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

07 / ТаргетПогляд на таргет

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

дошка["шахрайське"].value_counts() 0 1016 ← чесні, 83.8 % 1 196 ← шахрайські, 16.2 %

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

Згадай тему 05. Модель, яка не дивиться на ознаки взагалі й називає чесними геть усі оголошення, вгадає в 83.8 % випадків. Виглядає це як пристойний результат — і при цьому шахраїв упіймано рівно нуль. Саме тому дисбаланс треба знати до того, як обрана метрика: він визначає, з чим взагалі порівнювати будь-яке майбутнє число. Precision і recall ставлять правильні питання — скільки з упійманих справді шахраї і скільки шахраїв упіймано, — а частка правильних відповідей на нерівних класах просто бреше.

Другий погляд на таргет — розподіл ознак усередині кожного класу. Найкорисніше порівняння в нашій таблиці таке:

групаоголошеньмедіанна ціна
чесні1 0164 235 грн
шахрайські1961 940 грн

Медіанне шахрайське оголошення вдвічі дешевше за медіанне чесне. Але ми вже знаємо, що цим справа не вичерпується: серед найдорожчої десятої частини шахрайських теж 18 %. Тобто одне число («шахраї дешевші») правдиве й недостатнє — і зловити це вдалось лише тому, що ми подивились на розподіл, а не на середнє.

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

08 / ВитікВитік даних

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

чеснихшахрайських
скарг немає9940
скарг ≥ 122196

Правило «якщо є хоч одна скарга — оголошення шахрайське» ловить усі 196 шахрайських оголошень і помиляється лише на 22 чесних. Точність такого правила — 89.9 % серед позначених, і жодного пропущеного шахрая. Одна колонка, жодного навчання, майже ідеальний результат.

Це не удача. Це витік даних (data leakage) — ситуація, коли в ознаки потрапила інформація, якої в момент передбачення ще не існує.

Подумай, коли зʼявляється скарга. Оголошення публікують — скарг нуль. Хтось надсилає передоплату й не отримує телефон — надходить скарга. Служба підтримки розбирається й ставить мітку «шахрайське». Тобто скарга і мітка зʼявляються в один і той самий момент і з однієї й тієї самої події. Модель, навчена на скаргах, у робочому режимі отримає нове оголошення, у якого скарг нуль завжди — і назве чесними всі. Її чудова якість на історичних даних не переживе першого ж дня.

Схема 2 · Чому «скарг» не можна брати в ознаки

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

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

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

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

Розвідка ловить витік краще за будь-яку іншу перевірку — і ловить його рано, до навчання, коли виправлення коштує один рядок. Три сигнали, за якими його видно:

Що з цим робити? Викинути колонку «скарг» зі списку ознак — і не з таблиці. Вона чудово годиться для аналізу заднім числом («скільки шахрайських оголошень ми пропустили до першої скарги»), просто не годиться для передбачення. Це той самий принцип, що й у темі 03: усе, що модель бачить під час навчання, має бути доступним і в момент застосування — інакше вимірювання не має сенсу. І це продовження розмови з теми 02 про те, чим ознака відрізняється від відповіді, записаної іншими словами.

09 / ПідсумокЧекліст розвідки

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

Схема 3 · Чекліст розвідки даних

Пʼятнадцять пунктів, приблизно пʼятнадцять хвилин. Праворуч — те, на що дивитись у відповіді.

Пункт 11 — той, заради якого варто пройти решту. Він коштує один виклик corr(), а рятує від тижня роботи над моделлю, яка ніколи не запрацює. Пункти 1–4 роблять на будь-якій таблиці взагалі; 5–10 — на кожному числовому стовпці, який планується в ознаки.

Одна вимога, яка не вміщається в чекліст, але важливіша за половину його пунктів: записуй, що знайшов. Не в памʼять, а текстом — прямо в зошит, поруч із кодом. Через два тижні ти не згадаєш, чому в колонці ціни зʼявився стовпець «ціни_немає» і чому «скарг» немає серед ознак. А той, хто відкриє твій зошит замість тебе, не згадає й поготів.

10 / ДаліЩо далі

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

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

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

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

Далі в темі

Теорію прочитано. Тепер закріпи її на практиці.