Дані тут інші, ніж у лекції, — навмисно: якщо та сама механіка працює на чужій задачі,
значить, справа не в завченому прикладі. Працюємо з показаннями метеостанцій:
таблиця «станція × година», у комірці температура в градусах. Усе роби в окремому файлі
pogoda.py або в новому зошиті — практику не редагуй, вона знадобиться для звірки.
Дані генеруй так і ніяк інакше, щоб числа збігались із критеріями нижче:
import numpy as np
генератор = np.random.default_rng(2024)
температури = генератор.normal(12.0, 6.0, size=(6, 24)).round(1) # 6 станцій × 24 години
станції = ["Лиман", "Гора", "Поле", "Ліс", "Берег", "Місто"]
рівень 1Рівень 1 — База
Роздивись масив і навчись діставати з нього шматки за координатами.
- Надрукуй паспорт масиву:
shape,ndim,size,dtype,itemsize,nbytes— кожне з підписом, що воно означає. - Порахуй, скільки памʼяті займав би той самий набір чисел у вигляді вкладеного
списку. Заміряй чесно через
tracemalloc, а не на око. - Дістань і надрукуй з підписами: температуру на станції «Поле» о 15-й годині; увесь день станції «Берег»; 8-му годину на всіх станціях; прямокутник «станції 1-3, години 6-12».
- Переведи весь масив у Фаренгейти однією векторною дією
(
градуси * 9 / 5 + 32) — без жодного циклу.
Зроблено, якщо:
- проходить
assert температури.shape == (6, 24) and температури.size == 144; - проходить
assert температури.nbytes == температури.size * температури.itemsize; - проходить
assert температури[2, 15].round(1) == температури[2][15].round(1)— і поруч коментар, чому обидва записи дають те саме, але перший кращий; - проходить
assert температури[1:4, 6:12].shape == (3, 6); - проходить
assert np.allclose(у_фаренгейтах, [[г * 9 / 5 + 32 for г in рядок] for рядок in температури])— тобто векторна дія дала те саме, що ручне включення; - у файлі надруковано два числа памʼяті (масив і список) і в скільки разів вони різняться.
рівень 2Рівень 2 — Плюс
Тепер підсумки, осі, транслювання й маски — і жодного for по елементах.
- Порахуй середню температуру по станціях і по годинах. Надрукуй обидва
масиви з підписами й поясни в коментарі, чому в одному випадку
axis=1, а в іншомуaxis=0. - Знайди для кожної станції її найтеплішу годину — через
argmaxз віссю, а не циклом. - Побудуй масив
аномалії= наскільки кожне вимірювання відхиляється від середнього по своїй станції. Скористайсяkeepdims=True. - Побудуй масив
холодні— маску «нижче за нуль». Порахуй, скільки таких вимірювань усього і скільки їх на кожній станції. - Одним рядком зроби
безпечні— копію масиву, у якій усе нижче за нуль замінено на 0, а решта лишилась як була. Оригінал при цьому має вціліти.
Зроблено, якщо:
- проходить
assert середнє_по_станціях.shape == (6,) and середнє_по_годинах.shape == (24,); - проходить
assert np.allclose(середнє_по_станціях.mean(), температури.mean())— і поруч коментар, чому це працює саме тут і зламалося б, якби рядки мали різну довжину; - проходить
assert np.allclose(аномалії.sum(axis=1), 0.0)— сума відхилень від власного середнього завжди нуль; - проходить
assert холодні.sum() == сума_вручну, десума_вручнупорахована звичайним вкладеним циклом; - проходить
assert безпечні.min() >= 0 and температури.min() < 0— тобто оригінал не постраждав; - у файлі немає жодного
forпо елементах масиву (цикли по назвах станцій для друку — можна).
рівень 3Рівень 3 — Виклик
Досліди межі: де вигляд кусає, де транслювання падає й де векторизація перестає вигравати.
- Напиши дві функції з однаковою логікою — «обнулити першу годину доби на всіх станціях»:
зіпсувати(масив), яка пише прямо у зріз переданого масиву, ібезпечно(масив), яка починає здані = масив.copy(). Доведиassert-ами: післябезпечнооригінал цілий, післязіпсувати— ні. Другийassertспіймай черезtry/except AssertionError, щоб файл усе одно дорахувався до кінця. - Збери таблицю поєднань форм: для пар
(6, 24)з(24,),(6,),(6, 1),(1, 24),(24, 1), а також окремо для пари(6, 1)з(1, 24)спробуй додавання й запиши в таблицю або форму результату, або текст помилки. Кожен рядок поясни одним реченням: яка вісь із якою зіткнулась. - Порівняй швидкість на трьох розмірах — 1 000, 100 000 і 5 000 000 елементів — для
трьох способів однієї дії
× 9 / 5 + 32: циклforзі списком, спискове включення, векторна дія. Виміряйtimeit.repeat(..., repeat=7)і бери мінімум. Побудуй табличку з трьох рядків і чотирьох стовпців. - Знайди в цій табличці розмір, на якому пришвидшення падає порівняно з попереднім
рядком, і поясни причину числом: порахуй
nbytesмасиву на кожному розмірі й назви, з якого моменту дані перестали вміщуватись у кеш процесора. - Порахуй ту саму таблицю аномалій із рівня 2 у трьох типах:
float64,float32іfloat16. Для кожного надрукуйnbytesі максимальне відхилення відfloat64-версії. Зроби висновок одним реченням: де економія памʼяті виправдана, а де вже ні.
Зроблено, якщо:
assert np.array_equal(до, після)післябезпечнопроходить, а такий самийassertпіслязіпсуватипадає й це падіння спіймано та надруковано;- у таблиці з пункту 2 є щонайменше одна форма, яка падає, і одна, де розтягуються обидва масиви;
- у табличці з пункту 3 всі дванадцять чисел заповнені, і найшвидший стовпець — векторний;
- названо конкретний розмір із пункту 4 і поруч стоїть його
nbytesу мегабайтах; - у пункті 5 надруковано три значення
nbytesі три максимальні відхилення, і відхиленняfloat16виявилось на кілька порядків більшим за відхиленняfloat32.
Підказки
- Рівень 1, пункт 2.
sys.getsizeofдля списку покаже лише масив посилань і збреше в рази. Чесну відповідь даєtracemalloc.start()/get_traced_memory()навколо самої побудови списку — і не забудьdelміж двома замірами, інакше другий побачить памʼять, яку тримає перший. - Рівень 2, пункт 1. Не намагайся запамʼятати «станції — це нуль». Дивись на
форму: тобі потрібні шість чисел, отже, з
(6, 24)має зникнути двадцять чотири. - Рівень 2, пункт 5. Потрібна функція, яка не викидає елементи, а замінює їх, зберігаючи форму. Вона згадана в розділі 09 лекції.
- Рівень 3, пункт 1. Різниця між функціями — рівно один рядок. Саме тому цю помилку так важко помітити в чужому коді: він виглядає бездоганно.
- Рівень 3, пункт 2. Форми порівнюються з кінця. Якщо здається, що
(6, 24)і(6,)мають поєднатись, бо шістка є в обох, — випиши обидві форми вирівняними праворуч і подивись, що з чим насправді стикається. - Рівень 3, пункт 4. Кеш останнього рівня у звичайного ноутбука — одиниці
мегабайтів. Шукай розмір, на якому
nbytesцю межу перетинає; далі час починає зʼїдати не арифметика, а перевезення байтів. - Рівень 3, пункт 5. У
float16на мантису лишається десять бітів. Це приблизно три десяткові знаки на все число — для температури 12.3 ще терпимо, для суми тисячі таких чисел уже ні.