# Python для Data Science: NumPy, Pandas та Scikit-Learn у 2026 > Практичний посібник з NumPy 2.1, Pandas 2.2 та Scikit-Learn 1.6 у Python 3.12. Від очищення даних та feature engineering до побудови продуктивного ML-конвеєра з повними прикладами коду. - Published: 2026-04-07 - Updated: 2026-04-07 - Author: Anthony Fillion-Maillet - Tags: data-science, python, numpy, pandas, scikit-learn, machine-learning, tutorial - Reading time: 10 min --- Python залишається беззаперечним лідером серед мов програмування для Data Science у 2026 році. Це зумовлено не модою, а зрілою екосистемою бібліотек, що покривають повний цикл роботи з даними: від завантаження та очищення до побудови продуктивних моделей машинного навчання. Три бібліотеки формують ядро цієї екосистеми: NumPy забезпечує ефективні операції над масивами та лінійну алгебру, Pandas надає гнучкі інструменти для маніпуляції табличними даними, а Scikit-Learn пропонує готові алгоритми машинного навчання з єдиним API. У цій статті розглядається наскрізний практичний приклад — аналіз та прогнозування підвищення співробітників — з використанням усіх трьох бібліотек. > **Версії, що використовуються** > > Усі приклади коду розроблені та протестовані з Python 3.12+, NumPy 2.1, Pandas 2.2 та Scikit-Learn 1.6. Старіші версії можуть відрізнятися в окремих деталях API, зокрема у поведінці Copy-on-Write у Pandas 2.2 та нових можливостях Array API у NumPy 2.1. ## Операції з масивами NumPy: основи та булева індексація NumPy замінює стандартні списки Python та класичні цикли на векторизовані операції, що виконуються на оптимізованому C-коді. Замість ітерації через `for`-цикл функції NumPy обробляють увесь масив одночасно, що забезпечує прискорення у 10-100 разів для типових задач Data Science. Наступний приклад демонструє базові операції з масивами: створення з різних джерел, арифметичну векторизацію та булеву індексацію — механізм фільтрації без циклів. ```python # numpy_basics.py import numpy as np # Create arrays from different sources prices = np.array([29.99, 49.99, 19.99, 99.99, 39.99]) quantities = np.arange(1, 6) # [1, 2, 3, 4, 5] # Vectorized arithmetic — no loops needed revenue = prices * quantities print(revenue) # [29.99, 99.98, 59.97, 399.96, 199.95] # Statistical aggregations print(f"Total revenue: ${revenue.sum():.2f}") # $789.85 print(f"Mean price: ${prices.mean():.2f}") # $47.99 print(f"Std deviation: ${prices.std():.2f}") # $27.64 # Boolean indexing — filter without loops premium_mask = prices > 40 premium_items = prices[premium_mask] # [49.99, 99.99] ``` Булева індексація є фундаментальним патерном у NumPy: вираз `prices > 40` створює масив значень `True`/`False`, що безпосередньо використовується як маска. Цей самий концепт працює і в Pandas DataFrame, оскільки Pandas побудований поверх NumPy. Статистичні агрегатні функції — `sum()`, `mean()`, `std()` — за замовчуванням обчислюються по всіх елементах, але параметр `axis` дозволяє застосовувати їх до окремих вимірів багатовимірних масивів. ## Зміна форми масивів та broadcasting у NumPy Багатовимірні масиви зустрічаються повсюдно на практиці: дані продажів по продуктах і місяцях, піксельні значення зображень або матриці ознак для моделей машинного навчання. NumPy забезпечує гнучку зміну форми масивів через `reshape()`, а також автоматичний broadcasting при обчисленнях з масивами різних розмірностей. ```python # numpy_reshape.py import numpy as np # Monthly sales data: 4 products x 3 months sales = np.array([ [120, 150, 130], # Product A [200, 180, 220], # Product B [90, 110, 95], # Product C [300, 280, 310], # Product D ]) # Column-wise mean (average per month) monthly_avg = sales.mean(axis=0) # [177.5, 180.0, 188.75] # Row-wise sum (total per product) product_totals = sales.sum(axis=1) # [400, 600, 295, 890] # Normalize each product relative to its own max normalized = sales / sales.max(axis=1, keepdims=True) # keepdims=True preserves the shape for broadcasting print(normalized[0]) # [0.8, 1.0, 0.867] — Product A relative to its peak # Reshape for Scikit-Learn (requires 2D input) flat_sales = sales.flatten() # 1D array of 12 values reshaped = flat_sales.reshape(-1, 1) # 12x1 column vector ``` Параметр `axis` є ключовим: `axis=0` агрегує по стовпцях (вздовж рядків), `axis=1` — по рядках (вздовж стовпців). При нормалізації з `keepdims=True` зберігається форма `(4, 1)`, що забезпечує коректний broadcasting під час ділення. Конструкція `reshape(-1, 1)` в кінці є поширеним патерном: Scikit-Learn завжди вимагає щонайменше двовимірний масив на вході. ## Маніпуляція та очищення даних у Pandas DataFrame Pandas є основним інструментом для роботи з табличними даними в Python. DataFrame поводиться подібно до таблиці бази даних або електронної таблиці, але надає значно потужніші можливості трансформації. На практиці очищення даних є найбільш часозатратним етапом будь-якого проекту Data Science — пропущені значення, викиди, дублікати та неузгоджені типи даних потребують систематичної обробки. Рекомендований підхід полягає у створенні відтворюваного ланцюжка трансформацій, що залишає вихідний DataFrame незмінним і зберігає результат у нову змінну. У прикладі використовується набір даних з 1500 профілів кандидатів. ```python # pandas_cleaning.py import pandas as pd import numpy as np # Load and inspect raw data df = pd.read_csv("candidates.csv") print(df.shape) # (1500, 8) print(df.dtypes) # Check column types print(df.isna().sum()) # Count missing values per column # Clean in a reproducible chain df_clean = ( df .dropna(subset=["salary", "experience_years"]) # Drop rows missing critical fields .assign( salary=lambda x: x["salary"].clip(lower=20000, upper=500000), # Cap outliers experience_years=lambda x: x["experience_years"].astype(int), hired_date=lambda x: pd.to_datetime(x["hired_date"], errors="coerce"), ) .drop_duplicates(subset=["email"]) # Remove duplicate candidates .query("experience_years >= 0") # Filter invalid entries .reset_index(drop=True) ) print(f"Cleaned: {len(df)} -> {len(df_clean)} rows") ``` Метод `.assign()` додає нові стовпці або перезаписує існуючі без зміни оригінального DataFrame. Аргумент `clip()` обмежує зарплати в межах реалістичного діапазону та запобігає спотворенню майбутньої моделі екстремальними значеннями. Функція `pd.to_datetime(..., errors="coerce")` перетворює некоректні дати на `NaT` (Not a Time) замість генерації помилки — надійний патерн для роботи з реальними даними. > **Pandas 2.2: Copy-on-Write** > > Починаючи з Pandas 2.2, режим Copy-on-Write (CoW) активований за замовчуванням. Це означає, що операції над підмножинами DataFrame (slices) завжди створюють копії, а не представлення (views). Поширене попередження `SettingWithCopyWarning` більше не виникає. Якщо потрібен прямий доступ in-place, слід явно викликати `.copy()` або використовувати нові CoW-сумісні патерни. ## GroupBy-агрегації та feature engineering у Pandas Подача необроблених даних безпосередньо в модель рідко дає якісні результати. Feature engineering — цілеспрямована трансформація та комбінування вихідних даних у змістовні ознаки — часто має найбільший вплив на якість моделі. Операції GroupBy у Pandas є незамінними для цього: вони дозволяють обчислювати агреговані статистики по групах, які потім слугують новими ознаками. Наступний приклад обчислює статистики по відділах та створює на їх основі нові ознаки, що допомагають моделі зрозуміти, як кандидат співвідноситься з середніми показниками свого відділу. ```python # pandas_groupby.py import pandas as pd # Aggregate candidate stats by department dept_stats = ( df_clean .groupby("department") .agg( avg_salary=("salary", "mean"), median_experience=("experience_years", "median"), headcount=("email", "count"), max_salary=("salary", "max"), ) .sort_values("avg_salary", ascending=False) ) print(dept_stats.head()) # Create features for ML: encode categorical + add aggregated stats df_features = ( df_clean .assign( # Ratio of individual salary to department average salary_ratio=lambda x: x["salary"] / x.groupby("department")["salary"].transform("mean"), # Time since hire in days tenure_days=lambda x: (pd.Timestamp.now() - x["hired_date"]).dt.days, # Binary encoding is_senior=lambda x: (x["experience_years"] >= 5).astype(int), ) ) ``` Патерн `transform("mean")` є особливо корисним: на відміну від `agg()`, що повертає агреговану таблицю, `transform()` зберігає вихідну кількість рядків і повертає для кожного рядка відповідне групове значення — ідеально для обчислення співвідношень на кшталт `salary_ratio`. Ознака `tenure_days` перетворює дату на числове значення, яке моделі можуть безпосередньо обробляти. ## Побудова конвеєра Scikit-Learn Конвеєри (Pipelines) Scikit-Learn є центральним механізмом для створення відтворюваних та придатних для продакшену робочих процесів машинного навчання. Конвеєр об'єднує етапи попередньої обробки та модель в єдиний об'єкт, що запобігає витоку даних (data leakage), спрощує налаштування гіперпараметрів та забезпечує коректну серіалізацію для розгортання. Критичний момент: розділення на тренувальну та тестову вибірки має відбутися до будь-якої попередньої обробки. Параметри масштабування (середнє, стандартне відхилення) повинні обчислюватися виключно на тренувальних даних, а не на тестових. Конвеєр забезпечує це розділення автоматично. ```python # sklearn_pipeline.py from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import classification_report import pandas as pd # Define column groups numeric_features = ["salary", "experience_years", "salary_ratio", "tenure_days"] categorical_features = ["department", "role_level"] target = "promoted" # Split before any preprocessing X = df_features[numeric_features + categorical_features] y = df_features[target] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # Build the preprocessing + model pipeline preprocessor = ColumnTransformer( transformers=[ ("num", StandardScaler(), numeric_features), # Scale numeric columns ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features), # Encode categories ] ) pipeline = Pipeline([ ("preprocessor", preprocessor), ("classifier", GradientBoostingClassifier( n_estimators=200, learning_rate=0.1, max_depth=4, random_state=42, )), ]) # Train and evaluate pipeline.fit(X_train, y_train) y_pred = pipeline.predict(X_test) print(classification_report(y_test, y_pred)) ``` `ColumnTransformer` дозволяє визначити різні етапи попередньої обробки для числових і категоріальних стовпців. `StandardScaler` нормалізує числові ознаки до середнього 0 та стандартного відхилення 1. `OneHotEncoder(handle_unknown="ignore")` перетворює категоріальні значення на бінарні стовпці та коректно обробляє невідомі категорії в тестовому наборі. Хоча `GradientBoostingClassifier` стійкий до немасштабованих ознак, наявність `StandardScaler` у конвеєрі дозволяє за потреби легко замінити модель на лінійну або SVM. ## Крос-валідація та налаштування гіперпараметрів Одне розділення на тренувальну та тестову вибірки дає занадто нестабільну оцінку якості моделі. Крос-валідація розбиває дані на кілька фолдів і усереднює результати, що забезпечує більш надійну оцінку здатності моделі до узагальнення. Після цього `GridSearchCV` систематично оптимізує гіперпараметри моделі через той самий механізм крос-валідації. ```python # sklearn_tuning.py from sklearn.model_selection import cross_val_score, GridSearchCV import numpy as np # 5-fold cross-validation on the full pipeline scores = cross_val_score(pipeline, X_train, y_train, cv=5, scoring="f1") print(f"F1 scores: {scores}") print(f"Mean F1: {scores.mean():.3f} (+/- {scores.std() * 2:.3f})") # Grid search over hyperparameters param_grid = { "classifier__n_estimators": [100, 200, 300], "classifier__max_depth": [3, 4, 5], "classifier__learning_rate": [0.05, 0.1, 0.2], } grid_search = GridSearchCV( pipeline, param_grid, cv=5, scoring="f1", n_jobs=-1, # Use all CPU cores verbose=1, ) grid_search.fit(X_train, y_train) print(f"Best params: {grid_search.best_params_}") print(f"Best F1: {grid_search.best_score_:.3f}") # Evaluate the best model on held-out test set best_model = grid_search.best_estimator_ print(classification_report(y_test, best_model.predict(X_test))) ``` Подвійне підкреслення у `"classifier__n_estimators"` є конвенцією Scikit-Learn для доступу до параметрів вкладених компонентів конвеєра: `classifier` — це назва кроку конвеєра, `n_estimators` — параметр відповідного оцінювача. Параметр `n_jobs=-1` використовує всі доступні ядра процесора та значно прискорює пошук: при сітці 3x3x3 комбінацій і 5-fold крос-валідації тренується 135 моделей. > **Запобігання витоку даних при крос-валідації** > > Найпоширеніша помилка при крос-валідації з попередньою обробкою — обчислення параметрів масштабування на всьому тренувальному наборі до розбиття на фолди. Це призводить до надмірно оптимістичних результатів, оскільки валідаційні дані опосередковано впливають на масштабування. Конвеєри Scikit-Learn розв'язують цю проблему автоматично: у межах кожного фолду весь етап попередньої обробки навчається виключно на тренувальному фолді. ## Збереження та завантаження моделей Навчена модель має практичну цінність лише тоді, коли її можна зберегти та завантажити в інших середовищах — веб-серверах, пакетних процесах, мікросервісах. Конвеєри Scikit-Learn повністю серіалізуються: параметри попередньої обробки (середні значення скейлера, категорії енкодера) та ваги моделі зберігаються в одному файлі. Для глибшого ознайомлення з [технологіями Data Science](/technologies/data-science) варто переглянути доступні навчальні матеріали. ```python # sklearn_export.py import joblib from pathlib import Path # Save the complete pipeline (preprocessor + model) model_dir = Path("models") model_dir.mkdir(exist_ok=True) joblib.dump(best_model, model_dir / "promotion_model_v1.joblib") # Load and predict in a different process loaded_model = joblib.load(model_dir / "promotion_model_v1.joblib") new_data = pd.DataFrame({ "salary": [75000], "experience_years": [4], "salary_ratio": [1.05], "tenure_days": [730], "department": ["Engineering"], "role_level": ["Mid"], }) prediction = loaded_model.predict(new_data) probability = loaded_model.predict_proba(new_data)[:, 1] print(f"Promoted: {bool(prediction[0])}, Confidence: {probability[0]:.2%}") ``` Бібліотека `joblib` оптимізована для числових масивів і працює значно швидше за `pickle` з великими моделями. Файл `.joblib` містить усі параметри скейлера та словники енкодера — при завантаженні в іншому процесі не потрібно повторно обробляти тренувальні дані. Метод `predict_proba()` повертає ймовірності класів; зріз `[:, 1]` витягує ймовірність позитивного класу (підвищення), що на практиці є інформативнішим за бінарне передбачення. ## Висновок NumPy, Pandas та Scikit-Learn доповнюють одна одну: NumPy забезпечує матричну математику та операції над масивами, Pandas надбудовується над ним і надає гнучку маніпуляцію даними з іменованими стовпцями та індексами, а Scikit-Learn використовує обидві бібліотеки як формат вхідних даних для своїх алгоритмів. Продемонстрований робочий процес є стандартом у професійних командах Data Science: - **NumPy** — векторизовані операції, статистичні агрегації та булева індексація для ефективної роботи з числовими даними - **Pandas** — завантаження, очищення, трансформація табличних даних та feature engineering через ланцюжки методів і GroupBy-операції - **Scikit-Learn** — побудова конвеєрів, крос-валідація, налаштування гіперпараметрів та серіалізація моделей - **Конвеєри (Pipelines)** — єдиний об'єкт, що інкапсулює попередню обробку та модель, запобігаючи витоку даних - **Крос-валідація** — надійна оцінка якості моделі через усереднення по кількох фолдах замість одного розділення Опанування цих трьох бібліотек відкриває шлях до складніших фреймворків, таких як PyTorch або XGBoost, адже їхні API-конвенції базуються на тих самих принципах. Поглибити знання можна на сторінці [технологій Data Science](/technologies/data-science). --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/data-science/python-data-science-numpy-pandas-scikit-learn-2026