Ai Python

Робота з даними з використанням NumPy і pandas

Чому стандартних списків Python недостатньо для Data Science, як NumPy прискорює обчислення у 100 разів, що таке Series і DataFrame у pandas, як читати CSV і Excel та які методи потрібні для першого знайомства з датасетом

Робота з даними з використанням NumPy і pandas

Завдання, яке розкриє правду про чистий Python

Уявіть: вам надіслали CSV-файл із 10 000 рядків — дані про продажі магазину за рік. Кожен рядок — одна транзакція: дата, категорія товару, ціна, кількість.

Ваш менеджер хоче знати:

  1. Яка середня ціна товарів у категорії «Електроніка»?
  2. Який товар продали найбільше разів?
  3. Скільки продажів було у грудні?

Здається, нічого складного. Спробуємо зробити це «чистим Python» — тобто тільки вбудованими засобами мови без жодних бібліотек:

import csv

# Читаємо CSV вручну
data = []
with open("sales.csv", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        data.append(row)

# Завдання 1: середня ціна в категорії "Електроніка"
total = 0
count = 0
for row in data:
    if row["category"] == "Електроніка":
        total += float(row["price"])
        count += 1
average = total / count if count > 0 else 0
print(f"Середня ціна: {average:.2f}")

# Завдання 2: найпопулярніший товар
product_counts = {}
for row in data:
    name = row["product"]
    product_counts[name] = product_counts.get(name, 0) + int(row["quantity"])
best_product = max(product_counts, key=product_counts.get)
print(f"Лідер продажів: {best_product}")

# Завдання 3: продажі у грудні
december_sales = 0
for row in data:
    if row["date"].startswith("2024-12"):
        december_sales += 1
print(f"Продажів у грудні: {december_sales}")

Ось правда: цей код працює, але він:

  • займає ~25 рядків для трьох простих запитань
  • виконується повільно (цикл for по 10 000 рядків — три рази!)
  • важко читати і підтримувати
  • не масштабується — для 1 000 000 рядків буде ще гірше

А тепер подивіться на те саме з pandas:

import pandas as pd

df = pd.read_csv("sales.csv")

# Завдання 1: середня ціна в "Електроніці"
average = df[df["category"] == "Електроніка"]["price"].mean()

# Завдання 2: найпопулярніший товар
best_product = df.groupby("product")["quantity"].sum().idxmax()

# Завдання 3: продажі у грудні
december_sales = df[df["date"].str.startswith("2024-12")].shape[0]

print(f"Середня ціна: {average:.2f}")
print(f"Лідер продажів: {best_product}")
print(f"Продажів у грудні: {december_sales}")

Три рядки замість 25. Швидше у 10–100 разів. Читається як англійська мова.

Аналогія: чистий Python для роботи з даними — це як рахувати тисячі транзакцій на звичайному калькуляторі. NumPy та pandas — це як Excel із формулами, але на стероїдах: не тільки швидше та потужніше, а й у тисячу разів зручніше для складних запитань.

У цій статті ми побудуємо розуміння обох бібліотек з нуля: спочатку NumPy — бо саме він є «двигуном» усіх обчислень, а потім pandas — інструмент для роботи з таблицями.


Частина 1. NumPy — фундамент числових обчислень

Чому стандартного списку Python недостатньо

Ви вже знаєте списки Python — це один із найбільш гнучких типів даних:

numbers = [1, 2, 3, 4, 5]
mixed   = [1, "два", 3.0, True, None]  # Список може містити будь-що

Саме ця гнучкість і є проблемою. Розберемо, що відбувається в пам'яті комп'ютера, коли ви створюєте список [1, 2, 3, 4, 5]:

Список Python в пам'яті:
┌─────────────────────────────────────────────────────┐
│ Список (list object)                                │
│  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐     │
│  │ ptr │  │ ptr │  │ ptr │  │ ptr │  │ ptr │ ...  │
│  └──┬──┘  └──┬──┘  └──┬──┘  └──┬──┘  └──┬──┘     │
│     │        │        │        │        │          │
│     ▼        ▼        ▼        ▼        ▼          │
│ ┌───────┐ ┌───────┐ ┌───────┐ ...                 │
│ │ тип   │ │ тип   │ │ тип   │                     │
│ │ лічил.│ │ лічил.│ │ лічил.│                     │
│ │ знач=1│ │ знач=2│ │ знач=3│                     │
│ └───────┘ └───────┘ └───────┘                     │
└─────────────────────────────────────────────────────┘

Кожен елемент списку — це вказівник (pointer) на окремий об'єкт у пам'яті. Кожен об'єкт містить:

  • тип даних (int? str? float?)
  • лічильник посилань (скільки змінних вказують на цей об'єкт)
  • саме значення

Це надає гнучкість (можна мати елементи різних типів), але коштує дорого — і пам'яттю, і часом.

Що відбувається при for циклі по списку?

total = 0
for x in numbers:  # Python: знайти вказівник → зайти в пам'ять →
    total += x     # прочитати тип → прочитати значення → виконати операцію
                   # → повернути вказівник → перейти до наступного...

Для кожного елемента — серія окремих операцій із перевіркою типу. На 10 000 елементів це помітно. На 10 мільйонах — катастрофа.

NumPy ndarray: однорідний масив у пам'яті

NumPy (Numerical Python) — бібліотека для ефективних математичних обчислень. Її основний тип — ndarray (N-dimensional array, N-вимірний масив).

Ось як той самий масив [1, 2, 3, 4, 5] виглядає в NumPy:

NumPy масив в пам'яті:
┌─────────────────────────────┐
│ ndarray (масив об'єкт)      │
│ dtype: int64                │
│ shape: (5,)                 │
│ ┌────┬────┬────┬────┬────┐  │
│ │ 1  │ 2  │ 3  │ 4  │ 5  │  │
│ └────┴────┴────┴────┴────┘  │
│  Суцільний блок пам'яті!    │
└─────────────────────────────┘

Всі елементи одного типу (int64), лежать впритул у пам'яті. Не потрібні вказівники, не потрібна перевірка типів для кожного елемента.

Аналогія: список Python — це шафа, де кожна річ загорнута в окрему коробку з підписом (що це, кому належить, скільки місця займає). NumPy масив — це монолітна полиця, де однакові предмети лежать рівно, один біля одного. Дістати будь-який — миттєво, бо ви знаєте точну відстань від початку.

Встановлення та перший запуск

pip install numpy
import numpy as np  # np — загальноприйнятий псевдонім
Чому import numpy as np, а не просто import numpy? Суто зручність — np.array(...) коротше, ніж numpy.array(...). Це загальноприйнятий стандарт — весь світ пише np.

Створення масивів

import numpy as np

# 1. З Python-списку
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)        # [1 2 3 4 5]
print(type(arr1))  # <class 'numpy.ndarray'>

# 2. Масив нулів
zeros = np.zeros(5)          # [0. 0. 0. 0. 0.]
zeros_2d = np.zeros((3, 4))  # Матриця 3×4 з нулів

# 3. Масив одиниць
ones = np.ones(5)          # [1. 1. 1. 1. 1.]

# 4. Послідовність чисел (аналог range())
seq = np.arange(0, 10, 2)  # [0 2 4 6 8]

# 5. Рівномірно розподілені числа в діапазоні
lin = np.linspace(0, 1, 5)  # [0.   0.25 0.5  0.75 1.  ]

# 6. Випадкові числа
rand = np.random.rand(5)    # 5 випадкових числа від 0 до 1
randn = np.random.randn(5)  # 5 чисел з нормального розподілу

Атрибути масиву

Кожен ndarray «знає» про себе важливу інформацію:

arr = np.array([[1, 2, 3],
                [4, 5, 6]])

print(arr.shape)   # (2, 3)  — 2 рядки, 3 стовпці
print(arr.ndim)    # 2       — кількість вимірів
print(arr.size)    # 6       — загальна кількість елементів
print(arr.dtype)   # int64   — тип елементів
shape — один із найважливіших атрибутів у ML. Форма масиву (наприклад, (1000, 28, 28)) говорить вам: 1000 зображень розміром 28×28 пікселів. Ви будете бачити arr.shape у кожному ML-проєкті.

Векторизовані операції — головна сила NumPy

Ось де починається магія. Замість циклів — прямі математичні операції над усім масивом:

arr = np.array([1, 2, 3, 4, 5])

# Операції зі скалярами — застосовуються до КОЖНОГО елемента
print(arr + 10)     # [11 12 13 14 15]
print(arr * 2)      # [ 2  4  6  8 10]
print(arr ** 2)     # [ 1  4  9 16 25]
print(arr > 3)      # [False False False  True  True]

# Операції між масивами — поелементно
a = np.array([1, 2, 3])
b = np.array([10, 20, 30])
print(a + b)   # [11 22 33]
print(a * b)   # [10 40 90]

Порівняємо швидкість — наочний тест:

import time

# Зі списком Python
python_list = list(range(1_000_000))
start = time.time()
python_result = [x * 2 for x in python_list]
print(f"Python: {time.time() - start:.3f}с")

# З NumPy масивом
numpy_arr = np.arange(1_000_000)
start = time.time()
numpy_result = numpy_arr * 2
print(f"NumPy:  {time.time() - start:.3f}с")
Порівняння швидкості
In [1]: Запуск тесту...
Python: 0.124с
NumPy: 0.002с
NumPy швидше у ~62 рази

Індексація та зрізи

Синтаксис схожий на списки Python, але потужніший:

arr = np.array([10, 20, 30, 40, 50])

# Індексація (як у списках)
print(arr[0])    # 10  — перший елемент
print(arr[-1])   # 50  — останній елемент

# Зрізи [start:stop:step]
print(arr[1:4])  # [20 30 40]
print(arr[::2])  # [10 30 50]  — кожен другий

# 2D масив (матриця)
matrix = np.array([[1, 2, 3],
                   [4, 5, 6],
                   [7, 8, 9]])

print(matrix[1, 2])      # 6   — рядок 1, стовпець 2
print(matrix[:, 1])      # [2 5 8]  — весь стовпець 1
print(matrix[0, :])      # [1 2 3]  — весь рядок 0
print(matrix[1:, 1:])    # [[5 6] [8 9]]  — підматриця

Булева індексація — фільтрація без циклів

Одна з найпотужніших можливостей NumPy:

scores = np.array([85, 42, 91, 67, 78, 55, 96, 33])

# Знайти всі оцінки вище 70
mask = scores > 70           # [True False True False True False True False]
good_scores = scores[mask]   # [85 91 78 96]

# Або коротше — в одну дію
good_scores = scores[scores > 70]

print(f"Хороших оцінок: {len(good_scores)}")  # 4
print(f"Середня серед хороших: {good_scores.mean():.1f}")  # 87.5

Аналогія: булева індексація — це як фільтр у Excel. Ви задаєте умову («ціна > 1000»), і Excel автоматично показує лише відповідні рядки. Тільки NumPy робить це для масивів з мільйонами елементів — миттєво.

Агрегатні функції

data = np.array([23, 45, 12, 67, 89, 34, 56, 78, 90, 11])

print(f"Сума:             {data.sum()}")        # 505
print(f"Середнє:          {data.mean():.1f}")   # 50.5
print(f"Мінімум:          {data.min()}")        # 11
print(f"Максимум:         {data.max()}")        # 90
print(f"Стд. відхилення:  {data.std():.1f}")    # 27.7
print(f"Індекс мінімуму:  {data.argmin()}")     # 9
print(f"Індекс максимуму: {data.argmax()}")     # 8

Для 2D масивів можна вказати вісь (axis):

matrix = np.array([[1, 2, 3],
                   [4, 5, 6]])

print(matrix.sum())           # 21  — сума всіх елементів
print(matrix.sum(axis=0))     # [5 7 9]  — сума по стовпцях
print(matrix.sum(axis=1))     # [6 15]   — сума по рядках

Зміна форми: reshape

arr = np.arange(12)          # [ 0  1  2  3  4  5  6  7  8  9 10 11]
matrix = arr.reshape(3, 4)   # Перетворити у матрицю 3×4

# [[0  1  2  3]
#  [4  5  6  7]
#  [8  9 10 11]]

flat = matrix.flatten()      # Знову в 1D: [0 1 2 3 4 5 6 7 8 9 10 11]
reshape — фундаментальна операція в ML. Зображення 28×28 пікселів потрібно перетворити у вектор із 784 елементів для подачі в нейронну мережу? image.reshape(784). Або навпаки — вектор у 2D зображення? flat.reshape(28, 28). Ви будете використовувати це постійно.

Порівняльна таблиця: список Python vs NumPy

ОпераціяСписок PythonNumPy ndarray
Тип елементівБудь-які (гнучко, повільно)Однаковий (компактно, швидко)
Пам'ятьВказівники + обгортки об'єктівСуцільний блок пам'яті
Швидкість +Конкатенація списківПоелементне додавання
ЦиклиПотрібні для кожної операціїНе потрібні — векторизація
БагатовимірністьСписок списків (незручно)Вбудована підтримка (ndarray)
Булева індексаціяНемаєЄ — фільтрація без циклів
Математичні функціїПотрібні циклиnp.sin(), np.exp() і т.д.
Відносна швидкість10–100×

Частина 2. pandas — робота з таблицями даних

Що таке pandas і чому він скрізь

pandas — бібліотека для роботи з табличними даними. Назва походить від "Panel Data" (панельні дані) — термін з економетрики. Саме pandas перетворив Python на мову №1 для Data Science.

Pandas побудований поверх NumPy — усередині кожного pandas-об'єкту лежить NumPy-масив. Тому pandas поєднує зручність роботи з таблицями (індекси, назви стовпців, перемішані типи) зі швидкістю NumPy.

pip install pandas
import pandas as pd  # pd — загальноприйнятий псевдонім

Series: одновимірна структура даних

Series — це одновимірна структура даних pandas. Уявіть її як один стовпець Excel-таблиці: зліва — мітка рядка (індекс), справа — значення.

import pandas as pd

# Найпростіший Series: зі списку Python
temperatures = pd.Series([22, 25, 19, 27, 23])

print(temperatures)
# 0    22
# 1    25
# 2    19
# 3    27
# 4    23
# dtype: int64

За замовчуванням індекс — це числа 0, 1, 2... Але можна задати власний:

# Series з іменованим індексом
grades = pd.Series(
    [85, 92, 78, 96, 71],
    index=["Іван", "Олена", "Петро", "Марія", "Андрій"]
)

print(grades)
# Іван      85
# Олена     92
# Петро     78
# Марія     96
# Андрій    71
# dtype: int64

Аналогія: Series — це як стовпець «Оцінки» у класному журналі. Зліва — прізвища учнів (індекс), справа — їхні бали (значення). Кожен рядок — пара «ім'я: оцінка».

Доступ до елементів Series:

print(grades["Олена"])    # 92  — за ім'ям індексу
print(grades.iloc[1])     # 92  — за числовою позицією (iloc = integer location)
print(grades[1])          # 92  — теж за позицією (якщо індекс числовий)

# Зрізи
print(grades["Іван":"Петро"])  # Іван, Олена, Петро
print(grades.iloc[0:3])        # Перші три елементи

Векторизовані операції над Series:

# Операції зі скалярами — застосовуються до кожного елемента
print(grades + 5)      # Додати 5 балів усім
print(grades * 1.1)    # Підвищити всі оцінки на 10%
print(grades > 80)     # Хто пройшов поріг у 80 балів?

# Відбір лише тих, хто має оцінку > 80
excellent = grades[grades > 80]
print(excellent)
# Іван     85
# Олена    92
# Марія    96
# dtype: int64

# Агрегатні функції
print(f"Середня оцінка: {grades.mean():.1f}")   # 84.4
print(f"Найвища:        {grades.max()}")          # 96
print(f"Найнижча:       {grades.min()}")          # 71

Series зі словника Python:

# Словник автоматично перетворюється на Series
city_populations = pd.Series({
    "Київ":    2952301,
    "Харків":  1421125,
    "Одеса":   1015826,
    "Дніпро":   980775
})

print(city_populations.sort_values(ascending=False))
# Київ      2952301
# Харків    1421125
# Одеса     1015826
# Дніпро     980775

DataFrame: двовимірна таблиця даних

DataFrame — це двовимірна структура даних pandas, аналог Excel-таблиці або SQL-таблиці бази даних. Складається зі стовпців (кожен стовпець — це Series) та рядків (кожен рядок — одне спостереження).

Аналогія: якщо Series — це один стовпець класного журналу (скажімо, оцінки з математики), то DataFrame — це весь журнал з усіма предметами. У кожному рядку — один учень, у кожному стовпці — один предмет.

Створення DataFrame зі словника:

data = {
    "Ім'я":     ["Іван",  "Олена", "Петро", "Марія"],
    "Вік":      [20,      22,      21,      23],
    "Математика":[85,     92,      78,      96],
    "Фізика":   [78,      88,      82,      91],
    "Python":   [91,      95,      73,      89]
}

df = pd.DataFrame(data)
print(df)
DataFrame вивід
In [1]: print(df)
Ім'я Вік Математика Фізика Python
0 Іван 20 85 78 91
1 Олена 22 92 88 95
2 Петро 21 78 82 73
3 Марія 23 96 91 89

Структура DataFrame:

  • Рядки (rows): кожен рядок — один студент (спостереження)
  • Стовпці (columns): кожен стовпець — одна характеристика (ознака)
  • Індекс (index): автоматичний (0, 1, 2...) або кастомний — мітки рядків

Доступ до даних у DataFrame

Це одна з найважливіших тем — є кілька різних способів, і кожен для свого випадку:

1. Доступ до стовпця — як до ключа словника:

# Один стовпець → Series
ages = df["Вік"]
print(type(ages))   # <class 'pandas.core.series.Series'>
print(ages)
# 0    20
# 1    22
# 2    21
# 3    23
# Name: Вік, dtype: int64

# Кілька стовпців → DataFrame (передайте список)
subjects = df[["Математика", "Фізика", "Python"]]

2. Доступ до рядків через .iloc — за числовою позицією:

# iloc = integer location (позиція, як у списку)
first_row = df.iloc[0]     # Перший рядок (Іван)
last_row  = df.iloc[-1]    # Останній рядок (Марія)
slice_df  = df.iloc[1:3]   # Рядки 1 та 2 (Олена і Петро)

# Конкретна комірка: рядок 1, стовпець 2
value = df.iloc[1, 2]  # 92 — оцінка Олени з математики

3. Доступ до рядків через .loc — за міткою індексу:

# loc = label-based location (за міткою)
# Якщо індекс числовий, він збігається з iloc:
row = df.loc[0]    # Рядок з індексом 0 (Іван)

# Але якщо задати кастомний індекс:
df_named = df.set_index("Ім'я")  # Тепер індекс — імена студентів
ivan_data = df_named.loc["Іван"]  # Доступ за ім'ям
Різниця між iloc та loc:
  • df.iloc[0] — завжди перший рядок (за позицією)
  • df.loc[0] — рядок з міткою 0 (якщо індекс числовий, збігається; якщо ні — інша поведінка)
Правило: якщо ви думаєте «мені потрібен перший/другий/N-й рядок» — використовуйте iloc. Якщо «мені потрібен рядок з конкретним ім'ям або ID» — loc.

4. Фільтрація рядків за умовою:

# Студенти, яким більше 21 року
older = df[df["Вік"] > 21]

# Студенти з оцінкою з Python вище 85
python_good = df[df["Python"] > 85]

# Кілька умов (& = AND, | = OR)
top_students = df[(df["Математика"] > 85) & (df["Python"] > 85)]

print(top_students[["Ім'я", "Математика", "Python"]])
#    Ім'я  Математика  Python
# 1  Олена          92      95

5. Додавання нових стовпців:

# Обчислити середню оцінку кожного студента
df["Середня"] = df[["Математика", "Фізика", "Python"]].mean(axis=1)

print(df[["Ім'я", "Середня"]])
#    Ім'я     Середня
# 0  Іван   84.666667
# 1  Олена  91.666667
# 2  Петро  77.666667
# 3  Марія  92.000000

6. Сортування:

# Відсортувати за середньою оцінкою (від найвищої)
df_sorted = df.sort_values("Середня", ascending=False)

print(df_sorted[["Ім'я", "Середня"]])
#    Ім'я     Середня
# 3  Марія  92.000000
# 1  Олена  91.666667
# 0  Іван   84.666667
# 2  Петро  77.666667

Частина 3. Читання та запис даних

У реальному житті дані рідко вводять вручну. Зазвичай це CSV-файли з бази даних, Excel-звіти від відділу продажів, або JSON із API сервісу.

Читання CSV файлів

CSV (Comma-Separated Values) — найпоширеніший формат обміну табличними даними:

df = pd.read_csv("sales.csv")

Але в реальних файлах часто є нюанси. read_csv має десятки параметрів — ось найважливіші:

# Основні параметри read_csv
df = pd.read_csv(
    "sales.csv",
    sep=";",                   # Роздільник (за замовчуванням ",")
    header=0,                  # Рядок із заголовками (0 = перший)
    encoding="utf-8",          # Кодування файлу
    na_values=["N/A", "-"],    # Значення, які трактувати як NaN (пропуск)
    usecols=["date", "price"], # Читати лише певні стовпці
    dtype={"price": float},    # Примусово задати тип стовпця
    parse_dates=["date"],      # Автоматично розпарсити дати
    nrows=1000                 # Читати лише перші 1000 рядків
)
Проблема кодування — дуже поширена. Якщо у файлі є кирилиця і Python видає UnicodeDecodeError, спробуйте encoding="utf-8-sig" (Excel часто додає BOM-маркер), або encoding="cp1251" (Windows-кодування для кирилиці).

Приклад реального читання:

# Уявіть файл sales.csv:
# date,product,category,price,quantity
# 2024-01-15,Телефон,Електроніка,12000,3
# 2024-01-16,Ноутбук,Електроніка,28000,1
# 2024-01-17,Футболка,Одяг,450,10

df = pd.read_csv("sales.csv", parse_dates=["date"])
print(df.dtypes)
# date        datetime64[ns]  ← автоматично розпарсена дата!
# product     object          ← рядки
# category    object
# price       int64
# quantity    int64

Читання Excel файлів

# Один аркуш (перший за замовчуванням)
df = pd.read_excel("report.xlsx")

# Конкретний аркуш за ім'ям або номером
df_sheet2 = pd.read_excel("report.xlsx", sheet_name="Продажі")
df_third   = pd.read_excel("report.xlsx", sheet_name=2)

# Усі аркуші — отримаємо словник
all_sheets = pd.read_excel("report.xlsx", sheet_name=None)
# all_sheets["Продажі"] → DataFrame
# all_sheets["Витрати"] → DataFrame
Для читання .xlsx файлів pandas потребує додаткової бібліотеки — openpyxl. Встановіть її: pip install openpyxl. Для старого формату .xlsxlrd.

Запис даних у файли

# Запис у CSV
df.to_csv("output.csv", index=False)  # index=False — не писати стовпець індексу

# Запис у CSV із іншим роздільником
df.to_csv("output_semicolon.csv", sep=";", index=False)

# Запис у Excel
df.to_excel("output.xlsx", sheet_name="Результати", index=False)

# Запис у JSON
df.to_json("output.json", orient="records", force_ascii=False)
index=False при записі в CSV — майже завжди потрібний параметр. Без нього pandas додає зайвий стовпець із числами 0, 1, 2... ліворуч від даних, що робить файл «незрозумілим» для інших програм.

Частина 4. Інформаційні методи — перше знайомство з датасетом

Щоразу коли ви отримуєте новий датасет — перший крок однаковий: дізнатися, що взагалі в цьому файлі. Для цього є кілька незамінних методів.

Будемо працювати з одним прикладом — уявний датасет продажів інтернет-магазину:

import pandas as pd

# Завантажуємо датасет
df = pd.read_csv("sales_data.csv")

df.head(n) та df.tail(n) — погляд на дані

head() показує перші рядки (за замовчуванням — 5), tail() — останні:

df.head()    # Перші 5 рядків
df.head(10)  # Перші 10 рядків
df.tail(3)   # Останні 3 рядки
df.head(3)
In [2]: df.head(3)
order_id date product category price quantity
0 1001 2024-01-05 Телефон Електроніка 12000 2
1 1002 2024-01-06 Ноутбук Електроніка 28000 1
2 1003 2024-01-07 Футболка Одяг 450 10

Навіщо: одразу після pd.read_csv() ви ще не знаєте, що у файлі. head() дає перший «погляд» — чи ті стовпці? Чи правильно розпарсилися дати? Чи немає зайвих символів?

tail() перевіряє кінець файлу — іноді в Excel-файлах є рядки «Разом:» або «Примітки:» в кінці, які не є даними. tail() це виявить.

df.sample(n) — випадкова вибірка

df.sample(5)              # 5 випадкових рядків
df.sample(frac=0.01)     # 1% випадкових рядків від усього датасету
df.sample(5, random_state=42)  # Фіксований seed для відтворюваності

Навіщо: head() показує лише початок — а раптом перші 1000 рядків усі з однієї категорії? sample() дає репрезентативний погляд на різноманітність даних.

df.info() — технічна довідка про датасет

Це один із найважливіших методів для першого знайомства:

df.info()
df.info()
In [3]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000 entries, 0 to 9999
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 order_id 10000 non-null int64
1 date 10000 non-null object
2 product 10000 non-null object
3 category 9850 non-null object ← 150 пропусків!
4 price 10000 non-null float64
5 quantity 9920 non-null int64 ← 80 пропусків!
dtypes: float64(1), int64(2), object(3)
memory usage: 468.8 KB

df.info() розповідає вам відразу:

  • Скільки рядків у датасеті (10 000)
  • Скільки стовпців (6)
  • Тип кожного стовпцяint64, float64, object (рядки)
  • Кількість не-пустих значень — якщо Non-Null Count менше загальної кількості рядків, є пропуски!
  • Скільки пам'яті займає датасет

Аналогія: df.info() — це як технічний паспорт товару. Не розгортаючи упаковку, ви вже знаєте: що всередині, скільки важить, які матеріали. У випадку датасету — що за стовпці, якого типу, де є «діри».

df.describe() — статистичний портрет даних

df.describe()
df.describe()
In [4]: df.describe()
price quantity
count 10000.000 9920.000 ← кількість непустих значень
mean 5234.120 4.832 ← середнє
std 8901.445 3.217 ← стандартне відхилення
min 99.000 1.000 ← мінімум
25% 450.000 2.000 ← перший квартиль
50% 1200.000 4.000 ← медіана
75% 5500.000 7.000 ← третій квартиль
max 120000.000 50.000 ← максимум

df.describe() обчислює статистику для числових стовпців:

  • count — кількість непустих значень
  • mean — середнє
  • std — стандартне відхилення (наскільки «розкидані» дані)
  • min, max — екстремальні значення
  • 25%, 50%, 75% — квартилі (медіана = 50%)

Що можна дізнатися з одного погляду:

  • max = 120 000 при mean = 5 234 — є дуже дорогі товари (аутлайери!)
  • min = 1 для quantity — але max = 50? Реалістично для магазину
  • std > mean для price — дані дуже «розкидані»
# Для текстових стовпців — окремо:
df.describe(include="object")

# Або включити все:
df.describe(include="all")

Швидка перевірка пропусків

Після df.info() ми вже знаємо, де є пропуски. Але для детальної картини:

# Кількість пропусків у кожному стовпці
print(df.isnull().sum())
# order_id      0
# date          0
# product       0
# category    150
# price         0
# quantity     80

# Відсоток пропусків
print(df.isnull().sum() / len(df) * 100)
# category    1.5%
# quantity    0.8%
Пропуски в даних (NaN — Not a Number) — одна з головних проблем реальних датасетів. Якщо у стовпці 5% пропусків — це нормально. Якщо 50% — такий стовпець майже непридатний для аналізу. В Модулі 3 ми вивчимо, як з ними боротися.

Підсумкова таблиця методів

Аналогія: уявіть, що вам принесли товсту папку з незнайомими документами (датасет). Ось що ви робите в першу чергу:

МетодАналогіяЩо дізнаєтеся
df.head()Переглянути першу сторінкуСтруктура, назви стовпців, перші дані
df.tail()Перегорнути до кінцяЧи немає зайвих рядків у кінці
df.sample()Відкрити навманняРізноманітність та типовість даних
df.info()Прочитати змістТипи, розмір, де є пропуски
df.describe()Прочитати резюмеСтатистичний портрет числових даних
df.isnull().sum()Знайти порожні аркушіТочна кількість пропусків

Практика


Резюме

NumPy ndarray

N-вимірний масив однорідного типу. Зберігається суцільним блоком у пам'яті. Операції виконуються векторизовано — без явних циклів Python. У 10–100 разів швидше за списки для числових обчислень.

Векторизація

Виконання операції відразу над усіма елементами масиву без циклу for. arr * 2 замість [x * 2 for x in arr]. NumPy передає роботу оптимізованому C-коду всередині.

pandas Series

Одновимірна структура: індекс + значення. Аналог одного стовпця таблиці. Підтримує іменовані мітки, векторизовані операції, булеву індексацію.

pandas DataFrame

Двовимірна таблиця: рядки (спостереження) × стовпці (ознаки). Кожен стовпець — Series. Аналог Excel-таблиці або SQL-таблиці. Основна структура даних у Data Science.

iloc vs loc

df.iloc[0] — рядок за числовою позицією (0, 1, 2...). df.loc["Іван"] — рядок за міткою індексу. Якщо індекс числовий — збігаються. Якщо ні — поведінка різна.

read_csv / to_csv

pd.read_csv("file.csv") — читання CSV. Ключові параметри: sep, encoding, parse_dates, na_values. df.to_csv("out.csv", index=False) — запис. index=False майже завжди потрібний.

head / tail / sample

Перші N рядків, останні N рядків, N випадкових рядків. Перший крок при роботі з будь-яким новим датасетом.

info / describe

df.info() — технічна довідка: типи, кількість non-null, пам'ять. df.describe() — статистичний портрет числових стовпців: mean, std, min, max, квартилі.
Наступна стаття — Основи статистики: середнє, медіана, мода, дисперсія, стандартне відхилення, розподіли та кореляція. Ці поняття пояснять, чому цифри у df.describe() мають саме такі значення та що за ними стоїть.
Copyright © 2026