У тебе 1200 оголошень і 4 числові ознаки. Яку форму має матриця ознак X, яку чекає fit(X, y)?
Чому так. Домовленість, якої дотримуються всі бібліотеки: рядок — це обʼєкт, стовпець — ознака. Тому перше число у формі — кількість оголошень, друге — кількість ознак. Варіант (4, 1200) — це транспонована матриця; вона теж прямокутна й теж «виглядає правильно», але fit прочитає її як 4 обʼєкти з 1200 ознаками кожен.
Питання 2 / 8
X має форму (1200, 4). Що поверне X.mean(axis=0) і що це означає?
Чому так.axis=k — це вісь, уздовж якої рухається підсумовування, і саме вона зникає з форми. Вісь 0 — це рядки, тобто оголошення; рухаючись по них згори вниз, ми отримуємо по одному числу на стовпець, тобто на ознаку: (1200, 4) → (4,). Найпоширеніша помилка — читати «axis=0» як «по рядках, тобто результат на кожен рядок»: результат навпаки, на кожен стовпець.
Питання 3 / 8
Чому векторна операція над масивом швидша за цикл Python приблизно в десятки разів?
Чому так. Звичайна операція над масивом виконується в одному потоці, на одному ядрі. Прискорення дають три речі разом: цикл написаний на C, тип питають один раз (він однаковий для всього масиву), і результат пишеться в готовий блок памʼяті замість створення мільйона обʼєктів. Кілька ядер NumPy справді задіює, але лише у важких операціях на кшталт множення матриць.
Питання 4 / 8
тренувальні = X[:900], далі тренувальні[0, 3] = 0. Що станеться з X?
Чому так. Зріз масиву не копіює жодного байта — він створює подання (view) на ту саму памʼять, тому запис у зріз є записом у вихідний масив. Це протилежність тому, як поводиться список Python, і саме тому пастка така тиха: помилки немає, попередження немає, просто зіпсовані дані. Перевірка — np.shares_memory(X, тренувальні), ліки — .copy().
Питання 5 / 8
X має форму (1200, 4). Чому вираз X - X.mean(axis=1) падає з ValueError, а X - X.mean(axis=0) працює?
Чому так. Форми звіряються з кінця, справа наліво, а коротшу доповнюють одиницями зліва. (1200,) стає (1, 1200), і на останній осі 4 стикається з 1200 — ні збігу, ні одиниці, отже ValueError. А (4,) стає (1, 4), четвірки збігаються, одиниця розтягується на 1200 рядків. Ліки для першого випадку — keepdims=True: тоді форма буде (1200, 1) і все спрацює.
Питання 6 / 8
Чим np.random.default_rng(42) кращий за np.random.seed(42)?
Чому так.np.random.seed налаштовує один глобальний стан, спільний для всієї програми: будь-яка бібліотека, яка теж смикне np.random, зсуне твою послідовність — і відтворюваність зникне без жодної зміни у твоєму коді. default_rng(42) повертає окремий обʼєкт-генератор: скільки їх створиш, стільки й буде, і кожен живе своїм життям.
Питання 7 / 8
У колонці «вік акаунта» три значення з 1200 замінено на np.nan. Що поверне вік.mean() і як знайти ці дірки?
Чому так.nan заражає все, чого торкнеться: одна дірка робить nan із суми, а отже й із середнього. Версія, яка дірки пропускає, — np.nanmean. Шукати їх порівнянням марно: nan не дорівнює навіть сам собі, тому вік == np.nan дає суцільні False. Правильний спосіб — np.isnan(вік).sum().
Питання 8 / 8
маска = y < np.median(y) дала 600 значень True. Що поверне X[маска] і скільки памʼяті це коштує?
Чому так. Довжина маски дорівнює кількості рядків X, тому NumPy читає її як «залиш рядки, навпроти яких True» — виходить (600, 4). Те, що маску побудували по таргету, а застосували до ознак, — звичайна й правильна практика: обидві вибірки лишаються узгодженими. Але, на відміну від звичайного зрізу, індексація маскою чи списком номерів завжди дає копію: відібрані рядки розкидані по памʼяті, і подання з рівним кроком із них не складеш.