Дані для всіх трьох рівнів генеруй сам, у тимчасовій теці через
tempfile.mkdtemp(), і прибирай її наприкінці через shutil.rmtree().
Нічого не завантажуй з мережі: усе, що потрібно, ти вмієш записати у файл
із теми 20.
рівень 1Рівень 1 — База
Придумай свою таблицю на 12-15 рядків: щось, що ти справді бачив у житті — витрати за тиждень, оцінки з предметів, тренування, покупки. Обовʼязково мусять бути щонайменше один текстовий стовпець (для групування) і два числові.
Далі:
- збери її як
DataFrameзі словника й запиши у CSV ізto_csv(..., index=False); - прочитай назад через
read_csvі покажиhead(),shape,info(),describe(); - додай новий стовпець, порахований з двох наявних;
- відфільтруй рядки однією умовою, потім — двома через
&; - згрупуй за текстовим стовпцем і порахуй
sumтаmeanчислового.
Зроблено, якщо: зошит виконується наскрізь без правок; є assert, який
перевіряє, що таблиця з файлу збігається з початковою
(прочитана.equals(початкова)); є assert, який перевіряє, що сума значень
по групах дорівнює сумі по всій таблиці; тимчасова тека наприкінці видалена
(assert not тека.exists()).
рівень 2Рівень 2 — Плюс
Візьми свою таблицю з рівня 1 і зіпсуй її так, як псують дані в житті:
- вибий 3-4 значення в числових стовпцях (постав
np.nan); - в одному рядку напиши категорію з великої літери, в іншому — з малої, у третьому — з пробілом у кінці («Кава», «кава», «кава ») ;
- запиши файл із роздільником
;і десятковою комою, як це робить український Excel.
Тепер полагодь:
- прочитай файл із правильним діалектом і покажи, що станеться, якщо про
діалект не сказати (порівняй
shape); - знайди всі пропуски через
isna().sum(); - порахуй середнє числового стовпця трьома способами — як є,
після
dropna()і післяfillna(0)— і поясни словами, чому числа різні і яке з них ти вважаєш чесним; - почисть категорії через
.str.strip().str.capitalize()і покажиvalue_counts()до й після.
Зроблено, якщо: у зошиті є три різні числа середнього з підписом, звідки
кожне взялося; value_counts() до чищення дає більше категорій, ніж після,
і це підтверджено assert-ом; є речення про те, чому fillna(0) у твоїх
даних доречний або недоречний — з посиланням на зміст стовпця, а не на код.
рівень 3Рівень 3 — Виклик
Напиши власну функцію моє_groupby(таблиця, ключ, стовпець, дія), яка
повторює таблиця.groupby(ключ)[стовпець].agg(дія), але не користується
groupby: тільки цикл, словник і список. Підтримай три дії:
"sum", "mean", "count".
Умови:
- функція повертає
pd.Seriesз тим самим індексом і в тому самому порядку, що й pandas (нагадаємо:groupbyсортує ключі); - пропуски вона має ігнорувати так само, як згортки pandas:
NaNне входить ані в суму, ані в лічильник дляmean; - порівняння з бібліотечним результатом робиться через
pd.testing.assert_series_equal, а не через==.
Потім заміряй час обох варіантів на таблиці з 200 000 рядків
(np.random.default_rng(42) для генерації) і запиши висновок одним реченням:
у скільки разів твоя версія повільніша й чому — що саме робить pandas,
чого не робить твій цикл.
Зроблено, якщо: assert_series_equal проходить для всіх трьох дій і на
чистій таблиці, і на таблиці з пропусками; заміряний час обох варіантів
надрукований; у висновку названа конкретна причина різниці (не «pandas
швидший», а «згортка виконується всередині C над суцільним масивом, а мій
цикл створює пітонівський обʼєкт на кожне значення»).
Підказки
dropna()без аргументів іdropna(subset=[...])— це два різні рішення. Перше викидає рядок, де порожня будь-яка клітинка; друге — лише там, де бракує саме потрібного стовпця. На рівні 2 різниця між ними і є половиною відповіді.- Індекс після фільтра перестає збігатися з позиціями. Якщо в рівні 3 твоя
функція збирає результат у список, а потім будує
Series, — не забудь передатиindex=явно, інакше отримаєшRangeIndexіassert_series_equalскаже про це першим. .str.strip()і.str.capitalize()працюють одразу над усім стовпцем, циклу не треба. Це те саме, що методи рядків із теми 05, тільки застосовані векторно.- Для заміру часу вистачить
time.perf_counter()навколо виклику. Одного прогону досить: різниця там така, що її не сплутаєш із шумом.