Новое Распределяем каналы MAX по категориям и территориям. Читать новость
Открытый канал ID 72343601022986

Аналитика, машинное обучение и наука о данных для начинающих / Analytics, machine learning, and data science for beginners. Просто о сложных технологиях

🐍 Python для аналитики | 👨‍💻 SQL | 📊 Визуализация | ➕ Математика | 🦾 Машинное обучение (machine learning, ML) | Наука о данных (Data Science)

Канал носит исключительно информационный и ознакомительный характер. Автор делится личным опытом и субъективным мнением о программировании. Материалы не являются официальной образовательной программой

Каналов в рубрике
101 367
Место этого канала
№ 26 700
Открыть рубрику →
Каналов в рубрике
2 093
Место этого канала
№ 251
Открыть рубрику →
Каналов в рубрике
465
Место этого канала
№ 465
Открыть рубрику →
Каналов в рубрике
932
Место этого канала
№ 932
Открыть рубрику →
Открыть в MAX

Публикации за период

По реакциям

Поиск и вся лента — после бесплатной регистрации. Свои даты — по подписке; владелец канала видит их бесплатно. Сортировка по реакциям — по подписке. Зарегистрироваться

Загрузка…

🤗 Сводная таблица pivot_table — альтернатива groupby для матричных отчётов

🧭 Что такое pivot_table

Это группировка в матрицу: строки × столбцы = значение.
Результат — всегда DataFrame (в отличие от df.groupby(), который может вернуть Series).

📄 Аналоги

- Excel: сводная таблица (Pivot Table)
- SQL: оператор PIVOT (кросстабуляция)

📐 Когда что использовать?

- по одному признаку → groupby
- перекрёстно (2+ признака) → pivot_table
- несколько метрик в одной сводной → pivot_table(values=[...])

🔧 Синтаксис

pd.pivot_table(df,
index='строки',
columns='столбцы', # опционально
values='данные',
aggfunc='mean', # по умолчанию mean
fill_value=0, # замена всех NaN
margins=True, # итоги
sort=True)


📊 Исходные данные

df = pd.DataFrame({
'ch': ['ya','ya','vk','ok'],
'city': ['МСК','СПб','МСК','СПб'],
'cost': [15000,15000,5000,5000],
'revenue': [30000,40000,10000,None]
})


✅ Простая сводная — только index (аналог groupby)

pd.pivot_table(df, index='ch',
values=['cost','revenue'],
aggfunc='sum', fill_value=0)

cost revenue
ch
ok 5000 0
vk 5000 10000
ya 30000 70000


*Списки в values, но строка в aggfunc → две колонки, без лишнего уровня.*

🦾 Матрица (index + columns) — сила pivot_table

pd.pivot_table(df, index='ch',
columns='city',
# 🦾
values='cost',
aggfunc='sum')

city МСК СПб
ch
ok NaN 5000
vk 5000 NaN
ya 15000 15000


📌 fill_value=0 заменяет любые пропуски (NaN):

- если отсутствует комбинация строк и столбцов (пример выше);
- если агрегатная функция вернула NaN для пустой группы.

pd.pivot_table(df, index='ch',
columns='city',
values='cost',
aggfunc='sum',
fill_value=0)

city МСК СПб
ch
ok 0 5000 # нет пары → был бы NaN
vk 5000 0 # также
ya 15000 15000


Без fill_value=0 в таких ячейках будет NaN. С ним — чистая матрица без пропусков, удобна для анализа.

💡 aggfunc и values: строки vs списки (MultiIndex)

aggfunc и values задают, сколько уровней (level) будет в колонках результата (в шапке).

Правило:
- Строка (aggfunc='sum', values='cost') → плоские колонки.
- Список (даже из одного: aggfunc=['sum'], values=['cost']) → дополнительный уровень (MultiIndex).

pd.pivot_table(df, index='ch', values=['cost'], aggfunc=['sum'])

sum
cost
ch
ok 5000
vk 5000
ya 30000


Дополнение: `columns` всегда создаёт уровень — это сами столбцы матрицы, у него есть имя:

result = pd.pivot_table(df, index='ch',
columns='city',
# ← вот уровень
values='cost',
aggfunc='sum',
fill_value=0)

result.columns
# Index(['МСК', 'СПб'], dtype='object', name='city')

name='city' — это и есть уровень, который создал columns.


📊 Тип результата

type(df.groupby('ch')['revenue'].sum()) # Series
type(df.groupby('ch')[['revenue']].sum()) # DF
type(pd.pivot_table(df, index='ch', values='revenue', aggfunc='sum')) # DF

☝️ Про None → NaN (кратко)

- Числовой столбец None → NaN (единый формат для вычислений).
- Строковый столбец None остаётся None (NaN не существует для строк).
- Смешанный → None (без чисел) / NaN (с числами).
- Тип столбца определяется автоматически.
- fill_value=0 заменяет любые NaN в итоговой таблице (включая mean, median).
- Для гарантии замены в pandas < 1.3.0 (особенно при нескольких агрегациях) можно явно дописать .fillna(0):

result = pd.pivot_table(...).fillna(0)

🆚 pivot_table vs groupby

groupby:
- не строит матрицу ❌
- длинная таблица
- только вниз
- возвращает Series или DF

pivot_table:
- строит матрицу ✅
- широкая таблица
- вниз и вправо
- возвращает всегда DF

✅ Чек-лист

- groupby('col') — по одному столбцу
- groupby(['col1','col2']) — по двум
- .groupby(...)['col'].sum() → Series
- .groupby(...)[['col']].sum() → DF
- pivot_table() — перекрёстная группировка
- groupby(..., dropna=False) — не исключать пропуски

💬 Далее:
Объединение таблиц — merge, concat, join.
Просмотры: 52
Перейти в пост Открыть в MAX Сообщить о неточности
🐼 Pandas. Группировки и агрегации

🧐 Логика

Pandas, как и SQL, реализует парадигму Разделение‑Применение‑Объединение / Split‑Apply‑Combine:
- разбиваем данные на группы
- применяем к каждой группе функцию (медиану и т.д.)
- собираем результаты в новый DataFrame

Это логика мышления, а не жёсткий порядок операций.

🆚️ Агрегации в SQL и Pandas

SQL:
- Быстрая агрегация внутри базы без перелива данных
- GROUP BY + COUNT, SUM, AVG, MIN, MAX, STRING_AGG, ARRAY_AGG
- Оконные функции (ROW_NUMBER, LAG, SUM OVER) — агрегация с сохранением строк

Pandas:
- Гибкая агрегация с произвольными функциями: .agg(['mean', 'std', custom_func])
- Трансформация с сохранением размера: .transform()
- Фильтрация групп: .filter(lambda x: len(x) > 5)
- Мультииндексные утилиты: unstack(), pivot_table()

👥 Агрегации по ролям

Аналитик
SQL — метрики по группам
Pandas — аномалии

Дата-сайентист (Data Scientist)
SQL — примеры (sample)
Pandas — генерация признаков (feature engineering)

Инженер данных (Data Engineer)
SQL — витрины
Pandas — прототипы

BI‑аналитик
SQL — вся агрегация в DWH
Pandas — ❌ не нужно

Продуктовый аналитик
SQL — 80% когорт
Pandas — 20% кастом

ML-инженер (Machine Learning Engineer)
SQL — выгрузка признаков (features, фичей)
Pandas — пайплайны

📊 Пример

import pandas as pd

df = pd.DataFrame({
'ch': ['ya', 'vk', 'ok', None, 'tg'],
'cost': [5000, 5000, 500, 500, None],
'revenue': [30000, 10000, None, 40000, 30000]
})


🌟 Синтаксис groupby

df.groupby('ch')['cost'].sum()

df — объект DataFrame
.groupby() — метод группировки
['cost'] — выбор данных
.sum() — метод агрегации

🐍 Типы скобок в Python

( ) — вызов функции/метода
[ ] — доступ к элементу

👥 Группировка

groupby() только группирует данные, не считает их.
Результат появляется при добавлении агрегации: sum(), mean() и т.д.

df.groupby('ch')[['cost', 'revenue']].sum()

Результат — DataFrame: потеряли 40k выручки 🤯

cost revenue
ch
ok 500.0 0.0
tg 0.0 30000.0
vk 5000.0 10000.0
ya 5000.0 30000.0


Почему?
`0.0`, а не `0`: если есть None → Pandas автоматически приводит всю колонку к типу float64

нет строки с NaN: по умолчанию строки с None в ключе группировки исключаются из результата groupby(dropna=True)
dropna=False — чтобы вернуть, см. ниже.

0️⃣ NaN или 0?

Зависит от:
1) метода агрегации
2) объекта агрегации

1️⃣ Методы sum и count возвращают число, все остальные — NaN:

• .sum() → пустая сумма = 0
• .mean() → нечего усреднять = NaN
• .count() → ноль штук = 0
• .min() → не из чего выбирать = NaN
• .max() → NaN
• .std() → NaN

2️⃣ NaN в ключе группировки — другие правила

- NaN в значениях (revenue, cost) → правила агрегации (см. таб. выше)
- NaN в ключе группировки (ch) → зависит от dropna:
`dropna=True` (по умолчанию): → строки с None в ключе исключаются
`dropna=False`: → None становится отдельной группой, внутри — те же правила агрегации

df.groupby('ch', dropna=False)[['cost', 'revenue']].sum()

Результат:

cost revenue
ch
NaN 500.0 40000.0
ok 500.0 0.0
tg 0.0 30000.0
vk 5000.0 10000.0
ya 5000.0 30000.0

# появилась группа NaN и 40k 🥳

💡 Альтернатива: fillna(0)

Чтобы цифры сходились, меняем None на 0 (везде):

df.fillna(0).groupby('ch')['revenue'].mean()

🤔 Series или DataFrame?

Pandas определяет автоматически по результату:
df['cost'] → Series (одинарные скобки)
df[['cost']] → DataFrame (двойные скобки)


.groupby() аналогично:
- одна колонка (агрегация) → Series
- две+ колонок (агрегации) → DataFrame

➕ Несколько агрегаций через agg

df.groupby('ch')['revenue'].agg(['sum', 'mean'])

💡 transform — среднее внутри каждой группы

df['mean_ch'] = df.groupby('ch')['revenue'].transform('mean')

Результат:

ch cost revenue mean_ch
0 ya 5000.0 30000.0 30000.0
1 vk 5000.0 10000.0 10000.0
2 ok 500.0 NaN NaN
3 None 500.0 40000.0 NaN
4 tg NaN 30000.0 30000.0


🎯 transform полезен для:
- нормализации
- расчёта долей
- заполнения пропусков

💬 Далее:
Сводные таблицы — pivot_table
Просмотры: 195
Перейти в пост Открыть в MAX Сообщить о неточности
🧹 Чистка и преобразование данных в Pandas

Разберём основные приёмы.

📄 Копия или оригинал?

В Pandas большинство методов по умолчанию не изменяют исходный DataFrame, а возвращают новый изменённый объект.

Если нужно изменить исходный, используется параметр inplace=True.

☝️ inplace=True не используется в цепочках методов (method chaining), потому что он возвращает None, а не DataFrame, и цепочка обрывается. Поэтому в стиле df.method1().method2() всегда работают методы без inplace, а результат присваивается переменной или передаётся дальше.

import pandas as pd

# ✅ Цепочки (без inplace)
df_clean = (df
.fillna(0)
.dropna()
.rename(columns={'A': 'B'}))

# ❌ Так не работает – inplace вернёт None
df.fillna(0, inplace=True).dropna() # Ошибка!


🍩 Что делать с пропусками?

.dropna() и .fillna() — два основных метода для работы с пропущенными значениями (NaN, None, NaT).

Для замены других некорректных значений (например, 0 или пустая строка) используйте .replace() (см.ниже).

Выбор зависит от количества пропусков:
• Удалить (.dropna()) — когда данные с пропусками не нужны для анализа (теряем объём, но избавляемся от проблем)
• Заполнить (.fillna()) — когда пропусков мало и разумная замена (среднее, медиана, 0 и т.п.) не сильно исказит данные

df.dropna() # удалить строки с любым NaN
df.dropna(how='all') # удалить строки, где все значения NaN

df.fillna(0)
# заполнить константой
df.fillna(df['cost'].mean()) # заполнить средним по столбцу
df.fillna(df.median()) # каждый столбец заполняет свои пропуски собственной медианой этого же столбца


🔄 Преобразование типов данных

Изменить тип столбца можно с помощью .astype():

df['price'] = df['price'].astype('float32')
df['date'] = pd.to_datetime(df['date']) # работа с датами


🆕 Перезапись существующих столбцов

➡️ Простое добавление новых столбцов — описано ранее.

Перезапись происходит через прямое переприсваивание:

# Повышаем цены на 10%
df['price'] = df['price'] * 1.1 # без методов .update() или .replace()


📌 Замена значений: .apply(), .map(), .replace()

Метод .apply() — для функции любой сложности (своей, lambda, встроенной) на Series и DataFrame

Лямбда — компактная функция в одну строку (одно выражение). Для простых действий это удобнее, чем создавать отдельную функцию:
lambda x: x * 1.1 # возьми x, верни x * 1.1

# лямбда-функция: пересчёт цены в доллары
df['price_usd'] = df['price_eur'].apply(lambda x: x * 1.1)

# своя функция (когда логика сложнее)
def discount(price):
return price * 0.9 if price > 1000 else price

df['price_disc'] = df['price'].apply(discount)

# применение к строке (axis=1) — для больших данных может быть медленнее векторизованных операций
df['full_info'] = df.apply(lambda row: f"{row['name']} - {row['price']}", axis=1)

Метод .map() — простое преобразование элементов или замена значений по словарю (только Series)

# Замена кодов на названия городов
df['city'] = df['city_code'].map({1: 'Москва', 2: 'СПб', 3: 'Казань'})
# Если кода нет в словаре → NaN


Метод .replace() — глобальная замена конкретных значений (Series и DataFrame)

# Замена 'Msk' на 'Москва', 'Spb' на 'СПб' (во всём DataFrame)
df['city'] = df['city'].replace({'Msk': 'Москва', 'Spb': 'СПб'})


Короткое правило:
• одна колонка → .map()
• весь DataFrame → .replace()

➗ Разделение строк

# Исходные данные
df = pd.DataFrame({'full_name': ['Иванов Иван', 'Петров Петр', 'Сидоров Сидор']})

# Разделяем фамилию и имя
df[['last_name', 'first_name']] = df['full_name'].str.split(' ', expand=True)


✅ Что нужно запомнить

• Копии vs оригинала (inplace и method chaining)
• Работа с пропусками: .dropna(), .fillna()
• Преобразование типов: .astype(), pd.to_datetime()
• Перезапись существующих столбцов:
.apply() с кастомными функциями любой сложности
.map() — замена по словарю
.replace() — глобальная замена значений
• Разделение строк .str.split()

💬 Далее:
Pandas. Группировки и агрегации
Просмотры: 275
Перейти в пост Открыть в MAX Сообщить о неточности
Сортировка и фильтрация в Pandas

🃏 Сортировка по значениям столбцов: sort_values()

Обязательная база:
• by (столбец / список столбцов) — порядок задаёт приоритет
• ascending (направление)
- True — по возрастанию
- False — по убыванию

# ❌ краткая форма (для нескольких столбцов без by= — ошибка)
df.sort_values('cost', ascending=False)

# ✅ явная форма — рекомендуется
df.sort_values(by='cost', ascending=False)


Опционально:
• inplace — изменить исходный DataFrame (True) или вернуть копию (False, по умолч.)
• ignore_index — сбросить индекс и создать новый последовательный (0…n-1)
• key — векторизованная функция, применяемая к значениям перед сортировкой
• na_position — куда помещать NaN, None: 'first' (в начало) или 'last' (в конец, по умолч.)

❗️Частая ловушка: inplace и присваивание

`inplace=True` возвращает `None`, не пытайся поймать его в переменную.

# ❌ df_sorted = None
df_sorted = df.sort_values(by='cost', inplace=True)


# ✅ Два правильных варианта:
# 1. Меняем исходник — без присваивания
df.sort_values(by='cost', inplace=True)

# 2. Создаём копию — inplace=False
df_sorted = df.sort_values(by='cost')


9️⃣ Сортировка по индексу: sort_index()

df_sorted = df.sort_index(ascending=False)
• Полезно, если индекс — временная метка (быстрый просмотр последних записей)
• Сортировка колонок по именам: df.sort_index(axis=1)

🔥 Быстрая сортировка топ‑N

• nlargest() — N наибольших значений (от большего)
• nsmallest() — N наименьших значений (от меньшего)

На больших данных работает быстрее, чем sort_values().head(n):
- sort_values() сортирует все строки
- nlargest() использует алгоритм — O(n log N), где N — малое число

top_3 = df.nlargest(3, 'cost')
bottom_3 = df.nsmallest(3, 'cost')


🦾 Продвинутые приёмы (кратко)

• `key` — для преобразования значений перед сортировкой (регистронезависимая сортировка и т.д.):

df.sort_values(by='name', key=lambda col: col.str.lower())

• Категории — для своего порядка сортировки (не лексикографического):
- преобразовать столбец к типу CategoricalDtype с ordered=True
- указать порядок categories (например: 'low' < 'medium' < 'high')
- далее обычный sort_values()

from pandas.api.types import CategoricalDtype
df['priority'] = df['priority'].astype(
CategoricalDtype(categories=['low', 'medium', 'high'], ordered=True)
)
df.sort_values('priority') # low → medium → high


✅ После сортировки: reset_index()

Если не использовать ignore_index=True, после сортировки старый индекс «едет». Чтобы сохранить данные (например, ID пользователя) — применить reset_index():

df_sorted = df.sort_values('cost').reset_index()
# старый индекс стал колонкой index, добавили новый индекс

⚠️ ignore_index=True отбрасывает старый индекс, восстановить его нельзя. Не смешивайте оба подхода.

👩‍🎓 Выбор алгоритма сортировки: kind

• 'quicksort' (по умолчанию) — быстрый, но нестабильный (равные элементы могут поменять порядок)
• 'mergesort' / 'stable' — стабильный (сохраняет исходный порядок равных ключей)
• 'heapsort' — нестабильный

🌪 Фильтрация

Логика: оставляем строки, где условие истинно.

Базовое условие

df[df['cost'] > 1000] # сравнение со скаляром

Сложное условие (И / ИЛИ)

- & — оба условия истинны (И)
- | — хотя бы одно истинно (ИЛИ)
- Каждое условие обязательно в своих круглых скобках – иначе Pandas не поймёт порядок операций

df[(df['cost'] > 1000) & (df['channel'] != 'ok')] # И
df[(df['channel'] == 'ya') | (df['channel'] == 'vk')] # ИЛИ


Отрицание условия — тильда ~

df[~(df['cost'] > 1000)] # строки, где cost <= 1000
df[~df['channel'].isin(['ya', 'vk'])] # все каналы, кроме ya и vk


isin() — фильтр по списку значений

Заменяет цепочку условий == через | (работает как «ИЛИ»). Короткая, удобная запись, особенно когда элементов много.

df[df['channel'].isin(['ya', 'vk'])] # оставить ya или vk

query() — альтернатива для читаемых сложных условий

df.query('cost > 1000 and channel != "ok"')
df.query('channel in ["ya", "vk"]')


💬 Далее:
Преобразование данных в Pandas
Просмотры: 230
Перейти в пост Открыть в MAX Сообщить о неточности
📏 Pandas: меняем размер таблицы — добавляем и удаляем данные

Сегодня разбираем, как менять размер таблицы: добавлять и удалять строки и столбцы.

🤝 Фиксированная длина

Series и DataFrame в Pandas имеют фиксированную длину. Нельзя просто взять и добавить новую строку к существующему объекту — нужно создавать новый объект, объединяя старый с новыми данными.

Почему?
Pandas внутри использует неизменяемые массивы NumPy — менять размер без копирования нельзя. Это жертва ради скорости и памяти.

➕ Добавление строк

Чтобы добавить одну или несколько строк, используют функцию pd.concat(). Старый метод .append() устарел.

import pandas as pd

# Исходный DF
df = pd.DataFrame({'город': ['Москва', 'СПб'],
'нас.млн': [13.27, 5.65]})

# Создаём новые строки как DF (любое кол-во)
new_rows = pd.DataFrame({'город': ['Саратов', 'Нск'],
'нас.млн': [0.89, 1.63]})

# Объединяем (новая копия!)
df_new = pd.concat([df, new_rows], ignore_index=True)


- ignore_index=True переиндексирует результат (0,1,2…), иначе индекс останется старым.

➕ Добавление столбцов

Добавить новый столбец проще — он не меняет длину таблицы. Просто добавляем столбец с новым именем.

Новый столбец всегда будет той же длины, что и DataFrame. ⚠️ Длина списка должна совпадать с числом строк (иначе ошибка).

На практике такая задача бывает редко, чаще создаём столбцы с константой, векторизованными вычислениями и сложной функцией .apply() — Pandas автоматически выдаст результат той же длины.

# + столбец с константой
df['порог_миллионника'] = 1.0

# + столбец, вычисленный из существующих
df['миллионник'] = df['нас.млн'] >= 1.0 # True для городов-миллионников (Москва, СПб, Нск), False для Саратова


⚠️ При добавлении столбца Pandas автоматически выравнивает данные по индексу, если присваивается Series.

❌ Три способа удалить данные и один — отфильтровать

1. Метод drop — гибкое удаление

- имеет параметр axis: удаляет строки (axis=0) или столбцы (axis=1)
- по умолчанию возвращает новый DataFrame (новую копию), не меняя исходный
- чтобы изменить исходный DataFrame → используйте inplace=True
- чтобы сохранить новый DataFrame → присвойте результат переменной

⚠️ inplace=True изменяет исходный объект и возвращает None 🤯

# без присваивания — результат пропадёт
df.drop('город', axis=1) # ⚠️ вернул новый DF, но никуда не записали → потеряли

# с присваиванием — когда исходные данные ещё пригодятся
df2 = df.drop('город', axis=1) # исходный df остаётся нетронутым

# перезаписываем ту же переменную — когда исходный больше не нужен
df = df.drop('город', axis=1)


2. Метод pop — вырезать столбец и вернуть его

- меняет исходный DataFrame сразу (без inplace)
- возвращает удалённый столбец как Series (не DataFrame!)
- удобен для отделения целевой переменной (y) от признаков (X)

y = df.pop('target') # target удаляется из df и сохраняется в y
X = df # то, что осталось


Пример:

df = pd.DataFrame({'a': [1, 2, 3],
'b': [4, 5, 6],
'target': [7, 8, 9]})

y = df.pop('target') # вырезаем колонку 'target'

print(df)
# a b
# 0 1 4
# 1 2 5
# 2 3 6

print(y)
# 0 7
# 1 8
# 2 9
# Name: target, dtype: int64


y — это Series. Если нужен DataFrame из одного столбца, используйте .to_frame().

3. Оператор del — встроенный в Python (не метод Pandas)

del df['column'] # удаляет столбец, меняет исходный DataFrame

В аналитике del используют редко — .drop() и .pop() удобнее и нагляднее.

4. Удаление через фильтрацию

Создаётся новый DataFrame только с нужными строками (исходный не меняется).

# оставляем строки, где численность > 1 млн
df_filtered = df[df['нас.млн'] > 1]
print(df_filtered)


❗️ Производительность

Создание новых объектов (через concat, drop и т.д.) занимает время и память. Для больших данных лучше минимизировать.

💬 Далее:
Pandas. Сортировка и фильтрация
Просмотры: 228
Перейти в пост Открыть в MAX Сообщить о неточности
🔍 Первичный осмотр данных в Pandas

После загрузки данных в DataFrame важно убедиться, что файл отображается правильно (не съехали столбцы, правильный разделитель, верная кодировка). Стандартное превью — достаточно неудобное и затратное по ресурсам. Здесь опишу базовые практики.

☄️ Методы для быстрого анализа

df.head() # первые 5 строк
df.tail(3) # последние 3 строки
df.shape # размер (строки, столбцы)
df.info() # типы столбцов, количество не‑NaN, память
df.columns # имена колонок (фичей)


🚩 Заголовки / header

Важно правильно указать, где находятся заголовки. Говорим системе:

- header=0 (по умолчанию) — первая строка становится именами колонок
- header=None — в файле нет строки-заголовка (колонки получат имена 0,1,2...)
- header=1 — заголовки лежат во второй строке, первая — служебная/мусорная
- names=['a','b','c'] — передаём явно имена колонок
- skiprows=[0,2,5] — пропустить строки с номерами 0,2,5 (мусорные)
- skipfooter=2 — пропустить последние 2 строки (требует engine='python', потому что стандартный C-движок не умеет читать файл с конца):

pd.read_csv('file.csv', skipfooter=2, engine='python')

📊 Какие столбцы загружать

- usecols=['id','price'] — читать только указанные столбцы (экономит память)
- nrows=1000 — прочитать только первые 1000 строк (удобно для теста)

# Прочитай только столбцы 'id' и 'price'
pd.read_csv('file.csv', usecols=['id', 'price'])

# Только первые 1000 строк
pd.read_csv('file.csv', nrows=1000)

# Комбинация
pd.read_csv('file.csv', usecols=['id', 'price'], nrows=1000)


☝️ Проверка пропущенных значений (NaN)

NaN — «дырка» в данных, если не проверить:
- базовые агрегаты выдадут NaN
- модели машинного обучения выдадут ошибку
- графики могут исказиться

df.isna().sum() # сколько NaN в каждом столбце (часто используют вместе/вместо с info)
df.isna().sum().sum() # общее число NaN во всей таблице


🧷 Типы данных

Pandas различает только три основных типа:
- Числовые (int64, float64)
- Булевы (bool)
- Объекты (object) — строки, смешанные типы, даты*, списки и т.д.

*Для работы с датами лучше использовать datetime64, но при первичном осмотре они тоже попадут в object, если не преобразовать.

Типы столбцов можно задать сразу при чтении (через `dtype`) или поменять после (через `astype()`):
• dtype экономит память и время при загрузке больших файлов
• astype даёт гибкость при постобработке

# Задать типы столбцов сразу при чтении — ускоряет чтение
df = pd.read_csv('file.csv', dtype={'id': int, 'price': float})

# Посмотреть типы всех столбцов
df.dtypes

# Поменять тип после загрузки (пример)
df = pd.read_csv('file.csv')
df['price'] = df['price'].astype(float)


📦 Доступ к столбцу

Предпочтительно через квадратные скобки.

df['cost'] # ✅ работает всегда
df.cost # ⚠️ не работает с пробелами, спецсимволами, может совпасть с методом DataFrame


📊 Описательные статистики / descriptive statistics

Один из ключевых методов разведочного анализа данных (EDA).

1. Функция для числовых данных — describe():

- count – сколько непустых значений
- mean – среднее арифметическое
- std – стандартное отклонение (разброс данных)
- min – минимум
- 25%, 50%, 75% – квантили (границы, ниже которых лежат 25%, 50%, 75% данных)
- max – максимум

df.describe()

2. Для текстовых (категориальных) столбцов — include=['object']:

df.describe(include=['object'])

Покажет: количество уникальных значений, самую частую категорию (top), её частоту (freq).

9️⃣ Подсчёт частот значений / frequency counts

Применяется к одному столбцу (Series), считает, сколько раз встречается каждое значение.

df['channel'].value_counts() # абсолютные значения
# ya 99
# vk 1


`normalize=True` — доли (относительные частоты), помогает выявить дисбаланс данных:

df['channel'].value_counts(normalize=True) # доли
# ya 0.99
# vk 0.01


Если одна категория встречается гораздо чаще других, модель будет плохо предсказывать редкие классы.

💬 Далее:
Pandas. Меняем размер таблицы — добавляем и удаляем данные
Просмотры: 240
Перейти в пост Открыть в MAX Сообщить о неточности
📥 Как загрузить и сохранить данные в Pandas

Pandas работает с файлами самых разных форматов. Таблицу можно создать прямо в коде.

🆕 Создание DataFrame с нуля

import pandas as pd

df = pd.DataFrame({
'город': ['Москва', 'СПб'],
'продажи': [15000, 12000]
})
print(df)


Таблица готова.

⌛ Загрузить / сохранить файл

• Загрузить (файл → DataFrame): pd.read_*()
• Сохранить (DataFrame → файл): df.to_*()

pd.read_csv('data.csv')
↑ ↑ ↑
│ │ путь к файлу
│ функция чтения (возвращает DataFrame)
библиотека pandas
# ( ) — вызов функции


📖 Распространённые форматы

📄 CSV (популярный формат)
pd.read_csv() / df.to_csv()
Универсальный, человекочитаемый, но медленный и большой

📊 Excel
pd.read_excel() / df.to_excel()
Много листов; для обмена с нетехническими специалистами

🌐 JSON
pd.read_json() / df.to_json()
API, логи

🥒 Pickle (только для временных файлов на своей машине)
pd.read_pickle() / df.to_pickle()
Бинарный, быстрый, сохраняет типы, но небезопасен (может выполнить код). Не открывать из непроверенных источников!

📦 Parquet (лучший выбор для больших данных)
pd.read_parquet() / df.to_parquet()
Современный столбцовый формат, сжатие до 10x, быстрый, безопасный; лучший для больших данных

🧑‍🎓 Пример CSV

CSV — всегда один «лист» (плоская таблица). Не поддерживает множественные листы.

# Чтение
df = pd.read_csv('file.csv', sep=';', encoding='utf-8')

# Сохранение
df.to_csv('file.csv', index=False, sep=';', encoding='utf-8-sig')


⚠️ Проблемы:
- sep — разделитель (, или ;), иначе всё склеится в один столбец
- encoding — для кириллицы 'cp1251' или 'utf-8', иначе кракозябры
- header=None — если в файле нет заголовка (столбцы назовутся по умолчанию: 0,1,2…)
- index_col — какой столбец сделать индексом при открытии (обычно не трогаем)

➕ Полезно:
- index=False – не создает лишний столбец с номерами строк при сохранении файла
- encoding='utf-8-sig' – для совместимости с Excel

📊 Пример Excel (много листов / worksheets)

При загрузке указываем sheet_name (имя или номер). По умолчанию загрузится только первый лист. Загрузить все листы — sheet_name=None (получится словарь DataFrame'ов)

# Загрузить конкретный лист
df = pd.read_excel('file.xlsx', sheet_name='Sheet1')

# Загрузить все листы в словарь
all_sheets = pd.read_excel('file.xlsx', sheet_name=None)

# Сохранить
df.to_excel('out.xlsx', index=False)

📥 Другие форматы (кратко)

# JSON
df = pd.read_json('file.json')
df.to_json('out.json', orient='records') # orient='records' — превращает таблицу в JSON-список, где каждый элемент — словарь: ключи — названия столбцов, значения — данные строки


# Pickle (небезопасно)
df = pd.read_pickle('file.pkl')
df.to_pickle('out.pkl')


# Parquet
df = pd.read_parquet('file.parquet')
df.to_parquet('out.parquet', compression='snappy') # compression='snappy' — алгоритм сжатия для Parquet


🔥 Чтение из буфера обмена — для быстрых тестов

# скопировать таблицу в буфер (Ctrl+C), затем выполнить:
df = pd.read_clipboard()
# Pandas сам считает данные из буфера и создаст DataFrame


⚡️ Чтение больших файлов кусками / чанками

Если файл > 1 ГБ и не влезает в память — его читают по кускам.

chunks = pd.read_csv('big_file.csv', chunksize=10000)
for chunk in chunks: # цикл, который перебирает все куски (chunks) один за другим
print(len(chunk)) # обрабатываем кусок


- chunksize=N – возвращает итератор, выдающий следующий кусок
- Каждый кусок – обычный DataFrame, после обработки удаляется из памяти

Пример: накопить сумму без загрузки всех данных

total = 0
for chunk in pd.read_csv('big.csv', chunksize=100000):
total += chunk['sales'].sum()
print(total)


❌ Что нельзя с чанками?

Операции вроде merge или groupby без агрегации требуют всех данных сразу. Для них нужны Dask / Spark.

✅ Чек-лист

• Загрузить через pd.read_*()
• Сохранить через df.to_*() с index=False
• Чтение из буфера pd.read_clipboard()
• Parquet — лучший формат
• Большой файл читать чанками (chunksize)

💬 Далее:
Первичный осмотр данных в Pandas
Просмотры: 304
Перейти в пост Открыть в MAX Сообщить о неточности
🆚 Pandas против SQL

Pandas и SQL сравнивают, т.к. они решают одну задачу — агрегацию данных. Хотя сам вопрос некорректный и возникает из-за непонимания экосистем и ролей:

• SQL — пром-агрегации (быстро, масштабируемо, вычисления прямо на стороне сервера БД (In-Database Processing))
• Pandas — исследования (удобно, гибко, но память ограничена (необходимо скачать данные на клиентскую машину))

🏡 Где живут данные

SQL: в БД (PostgreSQL, ClickHouse, etc.)
Pandas: в оперативной памяти (на ноутбуке / сервере)

🥇 Кто что умеет лучше

SQL: считать колоссальные объемы
Pandas: гибко трансформировать, писать свои функции

⏳ Когда использовать

SQL: финальные метрики, витрины, отчёты
Pandas: разведка, генерация признаков (feature engineering), аномалии

❌ Что нельзя сделать?

SQL: применить сложную python-функцию к каждой группе
Pandas: обработать 100 млрд строк

🤔 Почему?
- SQL не умеет выполнять произвольный python-код внутри GROUP BY
- Pandas держит все данные в RAM

👔 Что делать?

SQL: выгрузить данные в Pandas → применить функцию → вернуть обратно
Pandas: использовать SQL или изучить Spark

🧩 Что в голове у новичка?

• Каша из инструментов — зачем мне два похожих инструмента
• Непонимание ролей — где заканчивается аналитика и начинается инженерия данных

Ниже — краткая карта.

⚔️ Pandas против SQL: не «или», а «когда»

Запрос к базе данных
SQL: ✅ основной инструмент
Pandas: ⚠️ можно, но нет pushdown (фильтрация в Python)

Работа с таблицами 5–50 млн строк
SQL: ✅ да
Pandas: ⚠️ тяжело (зависит от памяти и числа колонок)

Разведочный анализ (EDA)
SQL: ❌ многословно
Pandas: ✅ быстро, гибко

Машинное обучение (ML)
SQL: ❌ нет (ограничен реляционной алгеброй (работа с таблицами); не приспособлен для математики ML)
Pandas: ✅ да (единая экосистема для математики ML: PyTorch/TensorFlow (градиенты и тензоры), NumPy/Scikit-Learn (матрицы и линейная алгебра)

Автоматизация отчётов
SQL: ⚠️ хранимые процедуры
Pandas: ✅ скрипты, Jupyter

Неструктурированные данные (текст, JSON, логи)
SQL: ❌ нет
Pandas: ✅ да

Работа с файлами (CSV, Excel)
SQL: ❌ нет
Pandas: ✅ отлично

Производительность на терабайтах
SQL: ✅ (BigQuery, Redshift)
Pandas: ❌ нет (нужен Spark/Dask)

Коротко:
- SQL — для хранения, надёжности, быстрых запросов к большим данным
- Pandas — для гибкого анализа, ML, прототипирования

🧭 Роли в данных: кому нужен SQL, кому — Pandas

Аналитик
SQL: ✅
Pandas: ✅
Почему: SQL — взять данные из базы, Pandas — исследовать, группировать, визуализировать

Дата-сайентист (Data Scientist)
SQL: ⚠️
Pandas: ✅
Почему: SQL — выгрузить примеры (сэмпл), остальное в Pandas/sklearn

Инженер данных (Data Engineer)
SQL: ✅
Pandas: ⚠️
Почему: SQL — основа ETL, Pandas — прототипы трансформаций

Разработчик систем бизнес-анализа (BI-разработчик)
SQL: ✅
Pandas: ❌
Почему: дашборды → SQL → хранилище (DWH)

Продуктовый аналитик
SQL: ✅
Pandas: ⚠️
Почему: SQL — 80%, Pandas — сложные преобразования или join не в базе

ML-инженер
SQL: ⚠️
Pandas: ✅
Почему: SQL — выгрузка признаков (фичей), Pandas — конвейер (pipeline), но чаще Spark/Polars

🎯 Главный вывод

Оба инструмента делают одно и то же, но по-разному и в разных условиях. Аналитик и Data Scientist должны знать оба инструмента. Но на практике:
- если данные в базе и нужна скорость → SQL
- если данные в CSV/Excel или нужно ML → Pandas

В ML выбор инструмента диктуют:
- объём данных
- нужна ли кастомная функция на группе
- где лежат данные (в SQL базе или в csv/parquet)

🐼 Первые шаги в Pandas: порядок действий

1. Загрузка данных (pd.read_csv, pd.read_excel)
2. Первичный осмотр (head, info, describe, isna)
3. Сортировка и фильтрация (sort_values, [])
4. Чистка данных (astype, rename, dropna, fillna, str-методы)
5. Агрегация и группировки (groupby, agg, transform)
6. Сводные таблицы (pivot_table, crosstab)
7. Объединение таблиц (merge, concat, join)
8. Визуализация (plot, matplotlib, seaborn)

💬 Далее:
Как загрузить данные в Pandas
Просмотры: 432
Перейти в пост Открыть в MAX Сообщить о неточности
📚 Pandas. Индексы и доступ к данным

Учимся ориентироваться в данных: выбирать строки и столбцы, понимать индексы и правильно к ним обращаться.

🔢 Индекс в Pandas

Индекс — это метки (label) строк, отдельная сущность, не входящая в состав колонок. Он служит для идентификации строк и быстрого доступа к ним.

Если индекс не задан явно, Pandas создаёт RangeIndex — стандартный числовой индекс от 0 до N-1 с шагом 1. Он работает по принципу range(), хранит только параметры start, stop, step и поэтому не занимает память. RangeIndex неизменяемый, но его можно переопределить, задав свой индекс.

import pandas as pd

df = pd.DataFrame({'город': ['Москва', 'Сочи']})
print(df.index) # RangeIndex(start=0, stop=2, step=1)


Типы индексов могут быть:
- Числовые (0, 1, 2...)
- Строковые ('a', 'b', 'c')
- Временные (2023-01-01, 2023-01-02)

Индекс может быть неуникальным (содержать повторяющиеся метки)

Альтернативы RangeIndex:
- DatetimeIndex (для временных рядов)
- MultiIndex (иерархический)
- CategoricalIndex (для категориальных меток)

🎯 Польза индекса

1. Быстрый доступ по метке — df.loc['a'] вместо запоминания номера строки
2. Автоматическое выравнивание — при объединении данных Pandas сопоставляет строки по индексам
3. Срез по меткам — удобная выборка диапазонов (например, df.loc['2020':'2022'])
4. Смысловая нагрузка — индекс может быть id, датой и т.д.

📍 Доступ к данным: .loc и .iloc

В Pandas два основных метода выбора данных:

- `.loc[]` — выбор по меткам (именам) строк и столбцов
Включает правую границу среза (закрытый интервал). Это удобно, когда метки — не числа (например, даты или буквы): хочется получить все строки от 'a' до 'c' включительно.

- `.iloc[]` — выбор по позициям (целым числам)
Исключает правую границу (как в Python-срезах). Это естественно для позиционной индексации, где привычно, что [0:2] берёт элементы с индексами 0 и 1.

df = pd.DataFrame({'город': ['Москва', 'Казань', 'Сочи'],
'год': [2020, 2021, 2022]},
index=['a', 'b', 'c'])
print(df)
# город год
# a Москва 2020
# b Казань 2021
# c Сочи 2022


Выбор по меткам — `.loc`

# Одна строка по метке
print(df.loc['b'])
# город Казань
# год 2021

# Несколько строк по меткам
print(df.loc[['a', 'c']])
# город год
# a Москва 2020
# c Сочи 2022

# Срез по меткам (включая правую границу!)
print(df.loc['a':'b'])
# город год
# a Москва 2020
# b Казань 2021


Выбор по позициям — `.iloc`

# Первая строка (позиция 0)
print(df.iloc[0])
# город Москва
# год 2020

# Несколько строк по позициям
print(df.iloc[[0, 2]])
# город год
# a Москва 2020
# c Сочи 2022

# Срез по позициям (правая граница не включается)
print(df.iloc[0:2])
# город год
# a Москва 2020
# b Казань 2021

Выбор конкретных ячеек

Можно указать и строку, и столбец:

# .loc[метка_строки, метка_столбца]
print(df.loc['b', 'город']) # Казань

# .iloc[позиция_строки, позиция_столбца]
print(df.iloc[1, 0]) # Казань (строка 1, столбец 0)


Все строки, конкретный столбец:

print(df.loc[:, 'город']) # все города
print(df.iloc[:, 0]) # первый столбец

Конкретные строки и столбцы:

print(df.loc[['a', 'c'], ['город']])
# город
# a Москва
# c Сочи


🔄 Установка и сброс индекса

Сделать столбец индексом — `.set_index()`

df = pd.DataFrame({'id': [101, 102, 103],
'город': ['Москва', 'Казань', 'Сочи']})
print(df)
# id город
# 0 101 Москва
# 1 102 Казань
# 2 103 Сочи


df_indexed = df.set_index('id')
print(df_indexed)
# город
# id
# 101 Москва
# 102 Казань
# 103 Сочи


Вернуть индекс обратно в столбец — `.reset_index()`

df_reset = df_indexed.reset_index()
print(df_reset)
# id город
# 0 101 Москва
# 1 102 Казань
# 2 103 Сочи


Параметр drop=True удаляет старый индекс без сохранения в столбец:

df_reset = df_indexed.reset_index(drop=True)
print(df_reset)
# город
# 0 Москва
# 1 Казань
# 2 Сочи

💬 Далее:
Pandas vs SQL
Просмотры: 335
Перейти в пост Открыть в MAX Сообщить о неточности
Pandas для начинающих: Series и DataFrame — видео, канал «Аналитика, машинное обучение и наука о данных для начинающих / Analytics, machine learning, and data science for beginners. Просто о сложных технологиях» ▶ видео
Pandas для начинающих: Series и DataFrame — фото, канал «Аналитика, машинное обучение и наука о данных для начинающих / Analytics, machine learning, and data science for beginners. Просто о сложных технологиях»
📚 Pandas для начинающих: Series и DataFrame

🎯 В чём суть?

Pandas — это надстройка над NumPy. Зачем? NumPy отлично работает с многомерными массивами, но имеет ограничения:
- Массивы «безликие»: нет подписей у строк и столбцов
- Плохо работает с разнотипными данными (числа + текст + даты)
- Для анализа (группировки, сводных) нужно много кода

В аналитике данные имеют структуру (названия столбцов, метки строк) и, как правило, неоднородную: пропуски, разные типы данных и т.д.

🚀 Pandas добавляет главное — именованную структуру данных:

- У столбцов есть имена (например, «город»)
- У строк есть индексы (метки, например, id пользователя), не обязательно уникальные
- Все операции (объединение, группировка, фильтрация) работают по именам, а не по позициям

Коротко: NumPy — про быстрые вычисления с числами. Pandas — про работу с поименованными данными.

🔄 ETL-конвейер на Pandas — основной сценарий работы аналитика

В своей работе аналитик постоянно делает микро-ETL:

1. Извлечь (Extract) — загрузить данные (CSV, Excel, SQL, API)
2. Преобразовать (Transform) — обработать: очистить, отфильтровать, сгруппировать, посчитать метрики
3. Загрузить (Load) — сохранить результат (в новый файл, базу данных, отчёт)

Pandas закрывает все три этапа в одном инструменте — быстрый «ручной» ETL. Большие промышленные ETL-пайплайны инженеры данных пишут на оркестраторах вроде Airflow, но внутри них всё равно часто вызывают Pandas-скрипты.

🧱 Две главные структуры Pandas

• Series — одномерный массив с метками (столбец)
• DataFrame — двумерная таблица с именами строк и столбцов (таблица)

📈 Series

`pd.Series` — одномерный индексируемый массив. Индексы могут быть неуникальными (например, 'a', 'b', 'b'). Series можно создать из любого итерируемого объекта.

import pandas as pd
import numpy as np

Из списка (индексы по умолчанию):
pd.Series([10, 20, 30, 40])
# 0 10
# 1 20
# 2 30
# 3 40


Из кортежа:
pd.Series((1, 2, 3))

Из генератора:
pd.Series(range(3))

Из строки (итерируется по символам):
pd.Series('abc')
# 0 a
# 1 b
# 2 c


Из множества:
pd.Series({1, 2, 3})

Из словаря (ключи → индексы, значения → данные):
pd.Series({'яблоко': 5, 'банан': 7, 'апельсин': 3})

Из NumPy-массива:
arr = np.array([100, 200, 300])
pd.Series(arr, index=['x', 'y', 'z'])


📊 DataFrame

`pd.DataFrame` — двумерная таблица: и строки, и столбцы имеют имена. Создаётся из любого итерируемого объекта, но требующего двумерную структуру (список списков, словарь списков и т.д.).

Из списка списков (явно задаём названия столбцов):
pd.DataFrame([[1, 2], [3, 4]], columns=['A', 'B'])
# A B
# 0 1 2
# 1 3 4


Из словаря списков (ключи → названия столбцов):
data = {'имя': ['Аня', 'Боря'], 'возраст': [25, 30]}
df = pd.DataFrame(data)
print(df)
# имя возраст
# 0 Аня 25
# 1 Боря 30


print(df['имя']) # доступ по имени столбца
print(df.index) # RangeIndex(start=0, stop=2)


Из списка словарей (каждый словарь — строка):
rows = [{'город': 'Москва', 'год': 2020},
{'город': 'Казань', 'год': 2021}]
pd.DataFrame(rows)
# город год
# 0 Москва 2020
# 1 Казань 2021


Из NumPy-массива:
arr = np.array([[1, 2], [3, 4], [5, 6]])
pd.DataFrame(arr, columns=['A', 'B'], index=['x', 'y', 'z'])
# A B
# x 1 2
# y 3 4
# z 5 6


📐 Оси в Pandas

Оси в Pandas работают так же, как в NumPy:

- `axis=0` — операции вдоль строк (индексы)
- `axis=1` — операции вдоль столбцов (названия)

Тут: про Оси (axis) в NumPy. Важно для понимания, как Pandas соединит данные.

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})

df.mean(axis=0) # ср. по строкам (для каждого столбца)
# A 1.5
# B 3.5

df.mean(axis=1) # ср. по столбцам (для каждой строки)
# 0 2.0
# 1 3.0


⚠️ Правило фиксированной длины

У Series / DataFrame нельзя просто добавить строку (как в списке Python). Внутри Pandas использует массивы NumPy фиксированного размера — это даёт высокую скорость, но требует создания нового объекта при изменении.

💬 Далее:
Pandas: индексы и доступ к данным
Просмотры: 314
Перейти в пост Открыть в MAX Сообщить о неточности
Вся лента — после бесплатной регистрации
ВКонтакте Телеграм Одноклассники
Избранное в datool.ru

Следите за каналом в личном кабинете

Зарегистрируйтесь в datool.ru, чтобы добавить канал «Аналитика, машинное обучение и наука о данных для начинающих / Analytics, machine learning, and data science for beginners. Просто о сложных технологиях» в избранное и отслеживать его показатели.

Зарегистрироваться
Для владельцев каналов

Подтвердите свой канал

Войдите или зарегистрируйтесь в datool.ru, затем добавьте нашего бота в канал по инструкции в личном кабинете. После проверки владения замок исчезнет и вам станет доступно управление каналом.

Войти или зарегистрироваться

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти