За дві попередні теми ми навчилися дані тримати. У темі 33 зʼявився масив, який рахує суму мільйона чисел одним рядком. У темі 34 — таблиця, яка знає імена своїх колонок і вміє групувати. Обидві охоче видають середнє, медіану, кореляцію. І саме тут ховається пастка, з якої почнеться ця тема: число — це вирок без пояснення. Воно каже «середній виторг 4289 гривень» і жодним чином не каже, що половина цієї суми припадає на два дні тижня.
Наскрізний приклад тут один — кавʼярня «Дві ложки»: шістдесят днів роботи, щоденний виторг, продані напої, середній чек по кварталах. Той самий набір ми пронесемо через усі одинадцять розділів, щоб порівнювати не стилі, а те, що видно на екрані.
01 / НавіщоЧому таблиця чисел мовчить
Почнемо з досліду, після якого сперечатись уже нема про що. Візьмемо чотири
маленькі набори даних — у кожному по одинадцять пар чисел. Уяви, що це чотири міста,
у кожному ми одинадцять місяців поспіль записували, скільки витратили на рекламу
(x) і скільки отримали виторгу (y).
Порахуємо для кожного набору те, що зазвичай і рахують: середнє по кожній величині, стандартне відхилення, коефіцієнт кореляції й пряму, яка найкраще описує звʼязок. Виходить ось що:
Чотири однакові рядки. Якби ми ухвалювали рішення за таблицею, ми б сказали: міста поводяться однаково, скрізь помірний позитивний звʼязок, вкладай усюди однаково. А тепер подивись, що це насправді за дані.
Схема 1 · Однакові числа, різні форми
Чотири набори по 11 точок. Усі п'ять показників збігаються до другого знака після коми. Масштаб осей однаковий у всіх чотирьох, щоб порівняння було чесним.
x, і всю кореляцію в поодинці створює єдина точка праворуч. Цей набір
відомий як квартет Анскомба, і він тут не заради краси: це найкоротший
можливий доказ, що зведення даних до кількох чисел втрачає саме те, заради чого
дані збирали.Статистика — це стиснення. Одинадцять пар чисел стиснулись у пʼять показників, і, як у будь-якому стисненні з втратами, щось зникло. Графік теж не показує всього, але він втрачає інше: він не дасть тобі точного значення кореляції, зате миттєво покаже дугу, викид або порожнечу.
02 / БудоваFigure і Axes
matplotlib — це бібліотека, яку ставлять окремо, як ми вчилися в
темі 17:
Найперший її графік виглядає так — три рядки, і на екрані вікно з кривою:
Розберемо перший рядок, бо в ньому вся будова бібліотеки. Функція
plt.subplots() повертає дві речі, і плутати їх не варто.
Figure — це аркуш паперу. Він має розмір у дюймах,
колір тла, роздільність і вміє зберігатися у файл. Сам він нічого не малює. Змінну
для нього прийнято називати fig.
Axes — це накреслена на аркуші система координат.
Саме вона має дві осі, підписи, сітку, легенду й усе намальоване всередині. Малює
вона: ax.plot, ax.bar, ax.set_title.
На одному аркуші може бути одна така область, а може бути дванадцять.
Axes —
це не «осі» в множині. Осі — це Axis (їх дві: ax.xaxis і
ax.yaxis), а Axes — уся область побудови разом з ними.
Різниця в одну літеру, і саме через неї новачки шукають метод не там, де він живе.Схема 2 · Анатомія одного графіка
Десять частин, кожна названа й показана на тому самому прикладі: виторг кавʼярні за два тижні. Праворуч — метод, яким ця частина створюється.
fig. Усе, що
стосується конкретної картинки — дані, підписи, легенда, межі, — робить
ax. Дев'ять із десяти методів, які ти писатимеш, починаються з
ax., і саме тому змінну ax треба мати під рукою явно.Чому fig, ax = plt.subplots(), а не просто plt.plot()
Той самий графік можна намалювати коротше:
Так пишуть у більшості коротких прикладів, і для одного графіка в зошиті це цілком
нормально. Але тут працює механіка, яку варто розуміти з самого початку:
plt.plot малює не «десь», а в поточній області. Якщо
поточної немає — вона мовчки створюється. Якщо є — малюється туди, хай навіть це
залишок від клітинки, яку ти запускав десять хвилин тому.
Наслідки видно на трьох типових ситуаціях:
- Два графіки в одній клітинці. Другий
plt.plotдомалює лінію на той самий аркуш, хоч ти хотів окремий. З явнимиax1іax2ця плутанина неможлива. - Функція, яка щось малює. Якщо вона всередині кличе
plt.plot, то псує «поточну» область того, хто її викликав. Функція, яка приймаєaxаргументом, малює рівно там, куди її просять. - Сітка з чотирьох графіків. Через
pltдоводиться перемикати поточну область викликамиplt.subplot(2, 2, 3)і памʼятати номери. Черезaxце просто чотири різні змінні.
Тому далі в лекції — тільки явний варіант. Це не питання смаку: явна змінна перетворює «намалюй мені щось» на «намалюй ось сюди», а різниця між цими двома формулюваннями — та сама, що між глобальною змінною й аргументом функції з теми 15.
03 / ТипиЧотири типи й що кожен показує
Тепер до наших даних. Кавʼярня працювала шістдесят днів; для кожного дня ми знаємо
виторг у гривнях. Це звичайний масив numpy завдовжки 60 — той самий, що
з теми 33.
Чотири базові типи графіка малюються чотирма методами. Кожен потребує рівно одного рядка, і кожен відповідає на своє питання:
Різниця не косметична. Лінія сполучає сусідні точки відрізком і
цим стверджує: між ними є неперервний перехід. Для днів це правда, для назв напоїв —
ні, тому лінія по категоріях завжди неправильна. Точки нічого не
стверджують про порядок і тому годяться там, де порядку немає: зріст і вага,
реклама і виторг. Стовпчики порівнюють висоти, і око робить це
точно — але тільки поки стовпчиків небагато. Гістограма взагалі не
має осі «що це за спостереження»: вона ріже діапазон значень на смуги
(bins) і рахує, скільки спостережень потрапило в кожну.
Інтерактив 1 · Ті самі 60 днів чотирма способами
Дані не змінюються — змінюється лише один виклик. Дивись, що зʼявляється й що зникає при кожному перемиканні.
04 / ПідписиПідписи, заголовок, легенда
Графік без підписів осей — це орнамент. Він може бути красивим, але сказати за ним нічого не можна: невідомо ні що по горизонталі, ні в чому виміряне те, що по вертикалі. Виправляється це трьома рядками:
Три речі тут варті окремої уваги.
Одиниці виміру — частина підпису. «Виторг» — це не підпис, «виторг, грн» — підпис. Через півроку саме ти й будеш тією людиною, яка дивиться на свій графік і не памʼятає, гривні там чи тисячі гривень.
Легенда бере назви з label=. Виклик
ax.legend() без аргументів обходить усе намальоване й збирає підписи
звідти. Якщо жодного label= не було, matplotlib надрукує попередження
й намалює порожню рамку. Легенда потрібна тоді й тільки тоді, коли ліній більше
однієї: підписувати єдину лінію — це витрачати місце на те, що вже написано в
заголовку.
Сітка має бути тлом, а не візерунком. alpha=0.3
робить її напівпрозорою. Сітка допомагає зчитати значення, тож вона мусить бути
видимою, але не мусить конкурувати з даними: якщо ти помічаєш сітку раніше за лінію,
вона зайва.
05 / СіткаКілька графіків у сітці
Часто одного графіка мало: хочеться поруч бачити виторг у часі, розподіл виторгу,
продані напої й середній чек. Для цього в plt.subplots є два перші
аргументи — скільки рядків і скільки стовпців:
Коли рядків або стовпців більше одного, plt.subplots віддає не одну
область, а масив numpy — той самий двовимірний масив
із теми 33. Тому й індексується він так само:
axes[рядок, стовпець]. Для однієї смуги
(plt.subplots(1, 3)) масив одновимірний: axes[0],
axes[1], axes[2]. Це часте джерело помилки
IndexError: too many indices — коду написали axes[0, 1],
а масив плаский.
Два аргументи роблять сітку придатною для читання:
constrained_layout=True— саме розсуває графіки так, щоб підписи не налазили один на одного. Без нього нижній заголовок регулярно накриває верхні поділки. Старша й досі поширена альтернатива — викликатиfig.tight_layout()наприкінці.sharex=True/sharey=True— робить осі спільними. Це важливіше, ніж здається: два графіки поруч із різними шкалами виглядають порівнюваними, хоч насправді ні. Якщо ти ставиш картинки поруч, щоб їх порівняли, шкала має бути одна.
06 / ФайлЗберегти у файл
Графік у вікні живе, поки програма не закрилась. У звіт, у листа чи в репозиторій його треба покласти файлом — це вміє сам аркуш:
dpi — крапок на дюйм. Розмір аркуша задано в дюймах
(figsize=(8, 4)), тож фактичний розмір картинки в пікселях — це добуток:
8 × 150 = 1200 пікселів завширшки. Значення за замовчуванням, 100, дає 800 пікселів —
на екрані нормально, а в друкованому звіті шрифт підписів розсиплеться на квадратики.
Робочі орієнтири: 100 — чернетка й нотатник, 150 — презентація й
екран високої роздільності, 300 — друк.
bbox_inches="tight" — обрізати білі поля. Без нього
matplotlib збереже аркуш цілком, разом із порожнечею навколо, і довгий підпис осі
може навіть не поміститись і виявитись обрізаним.
plt.show() у багатьох
середовищах звільняє аркуш після показу — і savefig після нього
запише порожню картинку. Правило просте: спершу зберігай, потім показуй. А в
скриптах, які працюють на сервері без екрана, show() не потрібен зовсім:
достатньо savefig.Формат вибирається за розширенням імені: .png — точковий, годиться
всюди; .svg — векторний, не втрачає якості при збільшенні, зручний для
вебу; .pdf — векторний, для документів і друку. Векторні формати не
мають dpi взагалі: у них зберігаються не пікселі, а вказівки, як
намалювати.
07 / ВибірЯкий тип під які дані
Тип графіка не обирають за красою — його обирають за питанням, на яке треба відповісти. Ось коротка відповідність, яка покриває майже всі щоденні випадки.
Схема 3 · Дані → графік → що видно і що ні
Кожен тип щось показує ціною того, що приховує інше. Останню колонку варто читати уважніше за другу.
| які в тебе дані | виклик | що покаже | що приховає |
|---|---|---|---|
| Одна величина в часі: виторг по днях, температура по годинах | ax.plot | напрям і форму зміни, ритм, різкі стрибки | розподіл: скільки взагалі було високих і низьких днів |
| Розподіл однієї величини: усі чеки за місяць | ax.hist | де скупчення, чи є два горби, чи довгий хвіст | порядок у часі та кожне окреме спостереження |
| Дві величини разом: реклама і виторг, зріст і вага | ax.scatter | форму звʼязку, силу розкиду, викиди | скільки спостережень злилось в одну точку |
| Порівняння категорій: продано лате, чаю, какао | ax.bar | хто більший і приблизно на скільки | розкид усередині категорії й кількість спостережень |
| Розкид усередині кількох категорій: чеки по днях тижня | ax.boxplot | медіану, розмах і викиди в кожній групі одразу | форму розподілу: два горби виглядатимуть як один |
| Кілька рядів у часі: цього року й торік | ax.plot ×2 | де ряди сходяться й розходяться | усе, якщо ліній більше пʼяти — вони зіллються |
08 / ЧесністьЯк збрехати ненавмисно
Найпоширеніший спосіб зробити оманливий графік — не злий намір, а налаштування, яке здавалося розумним. Найпоширеніше з них — обрізана вісь.
Кавʼярня рахує середній чек по кварталах: 96, 98, 101 і 104 гривні. Зростання
за рік — з 96 до 104, тобто на 8.3 %. Якщо почати вісь y не з нуля, а,
скажімо, з 95, стовпчики стануть заввишки 1, 3, 6 і 9 одиниць — і останній
виявиться вдевʼятеро вищим за перший. Дані ті самі. Висновок, який зробить читач, —
різний.
Інтерактив 2 · Де починається вісь
Повзунок рухає лише нижню межу осі. Числа не змінюються жодного разу — праворуч завжди той самий графік із нулем для порівняння.
Обрізана вісь — найвідоміший спосіб, але не єдиний. Ще два трапляються не рідше.
Нерівний крок по горизонталі. Якщо виміри зроблено 1, 2, 3, 10 і
30 числа, а на осі вони стоять на однаковій відстані, то графік показує неіснуючу
рівномірність: три дні перетворюються на такий самий проміжок, як двадцять. Ліки —
передавати справжні значення по x, а не порядкові номери: тоді
matplotlib сам розставить точки на правильній відстані.
Забагато кольорів. Колір — це такий самий канал інформації, як довжина. Якщо він нічого не кодує, він додає шум: пʼять різних кольорів для пʼятьох стовпчиків змушують око шукати сенс там, де його немає. Колір виправданий тоді, коли щось означає — рік, категорію, «наш» і «конкурент», — і тоді кольорів рівно стільки, скільки значень. Окремо варто перевірити, чи розрізняється твоя пара кольорів у відтінках сірого: приблизно кожен дванадцятий чоловік не розрізняє червоне й зелене.
Схема 4 · Один графік до і після
Ті самі пʼять чисел — продані порції за тиждень. Ліворуч звичайні помилки, праворуч виправлено, унизу перелік того, що саме змінили.
09 / pandasЗвʼязок із pandas
Таблиця з теми 34 вміє малювати себе сама.
Метод plot є і в DataFrame, і в Series, і
всередині він кличе той самий matplotlib:
Виграш тут не в довжині рядка, а в тому, що pandas сам бере підписи з таблиці: назва колонки стає підписом осі, індекс — значеннями по горизонталі, назви колонок — легендою. Для швидкого погляду на дані під час роботи це найкоротший шлях, і саме ним варто користуватись у зошиті, коли ти щось досліджуєш.
Важлива деталь: df.plot() повертає Axes —
той самий обʼєкт, про який була вся ця лекція. Тому доробити результат можна
звичайними методами, а можна навпаки — попросити pandas малювати в уже готову
область:
Практичний поділ виходить такий. Досліджуєш — пиши
df.plot(): він короткий і сам підписує. Готуєш картинку, яку
хтось побачить, — бери fig, ax: тобі знадобиться контроль над
розміром, шрифтами, межами й збереженням, а через df.plot() усе це
доводиться передавати домовленостями про іменовані аргументи.
10 / ГлибшеДля тих, кому мало
Чому plt взагалі влаштований так дивно. Модуль
pyplot — це шар сумісності з MATLAB, написаний як
машина станів. Усередині нього живе список аркушів і вказівник на поточний;
plt.plot насправді робить приблизно
plt.gcf().gca().plot(...), де gcf — «дай поточний аркуш»,
а gca — «дай поточну область» (обидві створюють обʼєкт, якщо його ще
немає). Це глобальний змінюваний стан з усіма його властивостями: зручний в
інтерактивній сесії, де ти — єдиний, хто його чіпає, і небезпечний у скрипті,
бібліотеці чи тесті, де порядок викликів не гарантований.
Витік памʼяті, який ловлять усі. Кожен
plt.subplots() реєструє аркуш у глобальному списку pyplot,
і збирач сміття його не забере, бо посилання є. Скрипт, який у циклі малює тисячу
графіків і зберігає їх у файли, спокійно зʼїдає всю памʼять. Ліки — закривати аркуш
явно після збереження:
Бекенди. matplotlib відділяє «що намалювати» від «куди
намалювати». Пристрій виводу називається бекендом: TkAgg малює у вікно,
module://matplotlib_inline.backend_inline — у клітинку зошита,
Agg — просто в памʼять, без жодного вікна. На сервері без графічного
середовища треба саме Agg, інакше програма впаде на спробі відкрити
вікно:
Стилі й шрифти. Вигляд за замовчуванням задається словником
plt.rcParams — розмір шрифту, товщина ліній, палітра, розмір аркуша.
Змінювати кожен параметр окремо рідко потрібно: є готові набори,
plt.style.use("seaborn-v0_8-whitegrid") або
plt.style.use("ggplot"). Окрема поширена морока — кирилиця: якщо
підписи виводяться квадратиками, потрібен шрифт із кириличними літерами,
наприклад plt.rcParams["font.family"] = "DejaVu Sans" (він іде в
комплекті з matplotlib і кирилицю має).
А що таке seaborn. Це надбудова над matplotlib, яка вміє в один
рядок те, що тут вимагає пʼяти — статистичні графіки з групуванням прямо з
DataFrame; вона повертає ті самі Axes, тож усе, що ти
вивчив, лишається чинним.
11 / ПідсумокЩо забрати з теми — і що далі
Уся тема тримається на одній думці: графік — це не ілюстрація до аналізу, це його перший крок. Числа стискають дані й тим самим ховають форму; око бачить форму миттєво й безкоштовно.
- Однакова статистика не означає однакові дані. Чотири набори
з
x̄ = 9.00,ȳ = 7.50,r = 0.82й тією самою прямою виявились прямою, дугою, прямою з викидом і взагалі відсутністю звʼязку. fig, ax = plt.subplots().fig— аркуш (розмір, збереження),ax— система координат (дані, підписи, легенда, межі). Явна змінна замість «поточної області» рятує від цілого класу мовчазних помилок.- Чотири типи — чотири питання. Лінія — як змінювалось у часі; точки — як повʼязані дві величини; стовпчики — яка категорія більша; гістограма — як розподілені значення. Кожен щось показує ціною того, що приховує інше.
- Без
set_xlabel,set_ylabelі заголовка графіка не існує. Одиниці виміру — частина підпису. Легенда потрібна від другої лінії. - Сітка — це
plt.subplots(2, 2)і масивnumpyу відповідь.constrained_layout=Trueпроти накладених підписів,sharey=True— щоб сусідні графіки справді можна було порівнювати. fig.savefig(..., dpi=150, bbox_inches="tight"), і саме доshow(). Розмір у пікселях — цеfigsize, помножений наdpi.- Обрізана вісь — найтихіша брехня. Зростання на 8.3 % при нижній межі 95 виглядає як зростання вдевʼятеро. Разом із нею — нерівний крок по горизонталі й кольори, які нічого не кодують.
df.plot()для розвідки,fig, axдля результату. Метод pandas повертаєAxes, тому обидва підходи зчіплюються черезax=ax.
На цьому курс Python закінчується. Ти пройшов шлях від першого запуску інтерпретатора до масивів, таблиць і графіків — тобто до повного набору інструментів, якими щодня працюють з даними. Далі починається інша розмова.
Досі ми відповідали на питання «що сталося»: скільки продано, як змінювався виторг, які дні вибиваються з ряду. Наступний курс відповідає на питання «що станеться» — і починається він рівно там, де закінчився цей: з даних, які треба спершу зрозуміти. Перша ж тема машинного навчання спирається на все, що ти щойно вивчив: масив ознак із теми 33, таблиця з теми 34 і графік із цієї — це не підготовка до аналізу, це і є перші три його кроки. Модель, побудована без погляду на дані, помиляється не тому, що алгоритм поганий, а тому, що ніхто не помітив дуги, викиду й порожнечі — рівно того, що ми знайшли в першому розділі.
Курс машинного навчання → Від «що сталося» до «що станеться»: як із тих самих таблиць і графіків народжується модель, яка робить передбачення, і як зрозуміти, чи можна їй вірити. перейти до курсуpractice.ipynb ти
власноруч порахуєш статистики чотирьох наборів і доведеш assert-ом,
що вони збігаються до другого знака, а потім намалюєш їх і побачиш різницю;
побудуєш усі чотири типи графіка на тому самому виторзі кавʼярні; підпишеш осі,
заголовок і легенду; зіб'єш чотири графіки в сітку 2×2; збережеш
картинку у файл із двома різними dpi й порівняєш розміри в байтах;
намалюєш те саме через df.plot(); і зробиш пару «обрізана вісь проти
чесної», щоб на своїх очах побачити, у скільки разів змінюється враження від
тих самих чисел.Далі в темі
Теорію прочитано. Тепер закріпи її на практиці.