Дмитрий Кузьмин | Инженерия данных


Гео и язык канала: Россия, Русский
Категория: Технологии


Путь Data engineer от Junior до Lead.
Делюсь мыслями, рабочими кейсами, обучением. Блог для junior - middle DE.
Мой профиль: @dim4eg91
Сайт: https://kuzmin-dmitry.ru
Практикум Data engineer: https://kuzmin-dmitry.ru/de_practicum

Связанные каналы  |  Похожие каналы

Гео и язык канала
Россия, Русский
Категория
Технологии
Статистика
Фильтр публикаций


Немного личного: теперь я КМС

Друзья, хочу поделиться новостью. Помимо любимой айтишной работы у меня есть ещё одно серьёзное увлечение: тяжёлое железо 🏋️

Вчера мне официально присвоили разряд кандидата в мастера спорта по становой тяге. Теперь всё по-настоящему: со значком и разрядной книжкой.

Шёл к этому не быстро, но последовательно. Большие цели, будь то спорт, учёба или работа, обычно складываются из маленьких действий, которые повторяешь достаточно долго.

Вчера все эти шаги сложились в результат. Очень рад 🖤

P.S. Ребят, всем кто скидывал мне домашки по практикуму, в ближайшее время обязательно отвечу, был занят.


За последний год у меня собралась линейка курсов по SQL и Python.

Эту линейку я весь год дорабатывал: пересматривал задачи, добавлял практику и менял то, что по обратной связи оказывалось слабым. По обратной связи от тех, кто уже прошёл курсы, я продолжаю обновлять задачи и добавлять то, что чаще встречается в работе и на собеседованиях.

Здесь не обязательно брать самый большой курс или сразу всю программу. Полезнее выбрать один навык, которого не хватает в работе или на собеседованиях прямо сейчас.

⭕️ Если пока путаются SELECT, WHERE, GROUP BY и первые соединения, лучше начать с основ.

⭕️ Если ты пока неуверенно работаешь с JOIN, GROUP BY, NULL и базовыми бизнес-условиями, начни с Junior.

⭕️ Если обычные запросы уже даются нормально, но есть сложности с CTE, окнами и ранжированием, тебе подойдёт Middle.

⭕️ Middle+ рассчитан на тех, кому база и окна уже знакомы, но не хватает плотной практики со сложными задачами и объяснением решений на собеседованиях.

⭕️ Python стоит выбрать, когда SQL уже держится, а работа с файлами, функциями и небольшими пайплайнами всё ещё идёт медленно.

⚠️ Если текущие задачи и так решаются уверенно, покупать ещё один курс только из-за скидки смысла нет.

🛒 До 31 августа включительно действуют специальные цены:

️SQL основы: 3190 → 2600 ₽
SQL Junior: 2690 → 2300 ₽
SQL Middle: 3490 → 3000 ₽
SQL Middle+ : 4290 → 3700 ₽
Python для DE: 2500 → 2200 ₽

Если нужен последовательный маршрут, есть два комплекта:

Junior + Middle: 5390 → 4500 ₽
SQL Pro pack: 8690 → 7300 ₽

Перед покупкой можно посмотреть примеры задач и пройти диагностику. Она поможет выбрать уровень: Выбрать свой маршрут

⚠️ Промокод TG_AUGUST_2026 действует до 31 августа включительно.

Доступ к курсам остаётся бессрочным. Можно купить сейчас, а пройти позже в удобном темпе.

#курсы


Как посчитать несколько метрик без четырёх подзапросов

Задача не выглядит эффектно, зато постоянно встречается при разработке витрин.

🟫 Допустим, в одной строке за каждый месяц нужно получить:

• общее число заказов;
• число оплаченных заказов;
• долю оплаченных;
• выручку по оплатам.

Похожая задача есть и в моём курсе SQL Middle.

Можно написать несколько подзапросов, посчитать в каждом свою метрику, а потом соединить результаты. Но для такой задачи это лишняя конструкция.

☕️ Я обычно использую условную агрегацию через SUM(CASE WHEN ...):

select
date_trunc('month', created_at)::date as month_start,
count(*) as total_orders,
sum(
case
when status = 'paid' then 1
else 0
end
) as paid_orders,
round(
100.0 * sum(
case
when status = 'paid' then 1
else 0
end
) / count(*), 1
) as paid_share,
sum(
case
when status = 'paid' then amount
else 0
end
) as paid_revenue
from orders
group by 1
order by 1;

Каждый CASE решает, какие строки попадут в конкретную метрику. Все метрики считаются в рамках одной агрегации, без отдельных подзапросов и JOIN между их результатами.

Главное, не выносить status = 'paid' в общий WHERE. Иначе неоплаченные заказы исчезнут ещё до агрегации, а доля оплат получится бессмысленной.

✅ В PostgreSQL ту же логику можно записать короче через FILTER:

count(*) filter (where status = 'paid')

В конкретной задаче это аналог:

sum(case when status = 'paid' then 1 else 0 end)

Мне чаще удобнее SUM(CASE WHEN ...): конструкция читается буквально и поддерживается разными СУБД. Но для PostgreSQL вариант с FILTER тоже вполне рабочий.

Такой шаблон подходит не только для заказов. Тем же способом считаются этапы воронки, статусы событий, категории ошибок и результаты проверок качества данных.

Ставь реакцию:

🔥 - знаю и применяю конструкцию
🤔 - узнал новое

#база_знаний


Начал ковыряться в n8n ⌨️

Вокруг него много разговоров про ИИ-агентов и автоматизацию всего подряд. Я очень долго смотрел на этот сервис и все никак не мог подступиться. Сейчас захотелось собрать несколько схем руками и понять, как это работает.

🔛 Пока попробовал загрузку текста через webhook, разбиение на части, простое векторное хранилище и поиск по нему. Затем добавил Telegram: отправляешь вопрос, workflow находит подходящие фрагменты и возвращает ответ.

Визуально всё выглядит понятно, но внутри очень много деталей: структура items, связи между узлами, циклы, преобразования и передача контекста. Как раз это и интересно поковырять самостоятельно, и с первого раза сложно. Сервис напоминает конструктор Lego.

Дальше хочу посмотреть, насколько n8n удобен для обычных задач с данными: API, проверки, небольшие загрузки и уведомления об ошибках. Интересно также собрать своего помощника для простых повторяющихся действий. Кажется, что это может упростить отдельные задачи, как в свое время я написал скрипт по сборке файлов для релизов 🎧

Статьи, с которых начал:
n8n: всё, что нужно знать о сервисе
n8n: реальные возможности и ограничения
Плюсы и минусы n8n

Если уже что-то собирали в n8n, накидайте интересных сценариев. Особенно интересны сценарии вокруг данных и обучения.

#материалы

568 0 11 3 17

📌 Сегодня хочу дать шпаргалку. SQL-запрос легко прочитать и неправильно представить, как он выполняется.

Мы пишем SELECT первым, хотя движок добирается до него далеко не сразу. Понимание этого порядка часто проверяют на собеседованиях по SQL, аналитике и Data Engineering.

Упрощённый логический порядок выглядит так:

FROM → JOIN → WHERE → GROUP BY → HAVING → оконные функции → SELECT → DISTINCT → ORDER BY → LIMIT


⬇️Какие отсюда следствия:

• WHERE фильтрует исходные строки до группировки;
• HAVING работает уже с результатами группировки;
• оконные функции считаются после WHERE, GROUP BY и HAVING, поэтому обратиться к результату ROW_NUMBER() в WHERE того же запроса нельзя (важно);
• ORDER BY видит алиасы из SELECT, а WHERE обычно ещё не видит.

Например, если нужно оставить только первую строку внутри каждой группы, придётся использовать подзапрос, CTE или QUALIFY, если СУБД его поддерживает.

Но логический порядок ещё не означает, что база физически выполнит операции именно так.

⬆️Оптимизатор может:

• протолкнуть фильтр ближе к чтению данных;
• не читать лишние столбцы и партиции;
• поменять порядок JOIN;
• выбрать Hash Join, Merge Join или Broadcast Join;
• добавить сортировку, Exchange или Shuffle.

По плану выполнения можно заметить полное чтение таблицы вместо нужных партиций, лишний Shuffle, перекос данных, неудачную стратегию JOIN или большую разницу между ожидаемым и фактическим количеством строк.

Поэтому к запросу стоит задавать не только вопрос «правильный ли получился результат?», но и «что движку пришлось сделать, чтобы его получить?».

В PostgreSQL для этого есть EXPLAIN ANALYZE, в Spark — explain() и Spark UI.

Сохраняй и ставь 🔥, если было полезно.

#материалы


🆙 В понедельник, 10 августа, стартует пятый поток DE-практикума.

🧩 Если совсем простыми словами, мы берём сырые CSV-файлы и постепенно превращаем их в нормальный работающий проект: принимаем данные, чистим ошибки, раскладываем по слоям, запускаем обработку и доводим всё до отчёта для бизнеса.

По пути поднимаем Docker-стенд, работаем с PostgreSQL, Spark, Airflow, MinIO и BI. Хочется, чтобы в конце ты действительно понимал, как движутся данные, зачем нужен каждый слой, где искать ошибку и как перезапустить пайплайн, если что-то пошло не так.

Я собирал этот практикум примерно так, как сам хотел бы изучать Data Engineering: на одном цельном проекте, руками и с возможностью задать вопрос, когда застрял. Для меня это давно уже не просто записанный курс. Я постоянно что-то дополняю, переписываю и стараюсь проще объяснять места, на которых участники спотыкаются.

🆕 Буквально вчера полностью обновил модуль по основам Spark. Добавил Spark UI, карту архитектуры и задания по чтению Jobs, Stages, Tasks и Executors.

Теперь можно легко посмотреть, что происходило внутри, как распределилась работа и где искать проблему, если обработка начала тормозить.

В пятом потоке этот модуль уже будет. Участникам предыдущих потоков тоже добавлю обновление в течение недели.

🔎 Заранее знать Spark и Airflow не нужно. Достаточно базы SQL, немного Python и желания разобраться. При этом нужно быть готовым выделять на практикум примерно 6–8 часов в неделю и действительно работать руками.

Если что-то непонятно, будем разбираться вместе. Я много отвечаю участникам и остаюсь на связи до полного прохождения, а не только до формального окончания потока. Мне правда важно, чтобы человек не просто получил доступ, а смог собрать проект до конца.

Старт 10 августа.

Программа и короткая диагностика

💬 Если интересно, но остались сомнения или вопросы, просто напиши мне: @dim4eg91. Посмотрим на твою текущую базу и решим, комфортно ли тебе заходить сейчас.

#путь_DE


(Кейс из сториз)


🥳 Наконец-то обновил сайт

Как многие, наверное, уже заметили, за последние пару недель я почти полностью пересобрал сайт.

Я менял не только оформление. Главное, что теперь по сайту можно пройти от своей текущей точки до подходящего курса или практикума:
посмотреть программу, требования к старту, примеры задач и ожидаемый результат.

🔍 Что изменилось:

1️⃣ Разделил направления

Теперь на сайте три отдельные траектории:
→ SQL
→ Python для работы с данными
→ Data Engineering

Все направления и точки входа собраны на главной странице.

2️⃣ Выстроил понятный маршрут по SQL

SQL-линейка теперь разделена по уровням:
→ база: JOIN, GROUP BY, NULL, даты и CTE
→ Junior: подготовка к собеседованиям
→ Middle: оконные функции и сложные запросы
→ Upper-Middle: очень плотная практика

Добавил примеры задач, чтобы можно было заранее оценить сложность, а не ориентироваться только на описание.

Там же появилась короткая SQL-диагностика. Она помогает определить текущую точку и предлагает следующий курс или маршрут по всей линейке.

3️⃣ Отдельно оформил Python для работы с данными

Это не общий курс по языку, а практика с файлами и данными: CSV, JSON, даты, Decimal, дубли, некорректные строки, группировки и небольшие batch-пайплайны.

На странице можно посмотреть программу и примеры задач, а заодно понять, достаточно ли текущей базы Python для такого формата.

4️⃣ Полностью пересобрал страницу DE-практикума

На странице практикума теперь видно, что именно делает участник и что остаётся после прохождения.

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

Там же работает диагностика готовности. Шесть вопросов проверяют SQL, Python, чтение кода, окружение и доступное время, а затем предлагают следующий шаг.

5️⃣ Добавил бесплатную точку входа

Перед практикумом можно поднять демо-проект: локально запустить PostgreSQL и Airflow, пройти путь данных от CSV до витрины и выполнить несколько проверок.

Это небольшой, но настоящий фрагмент работы внутри полного проекта.

📆 Пятый поток DE-практикума стартует 10 августа.

Если маршрут уже понятен, перейти к подходящему курсу можно прямо со страницы направления.

На DE-практикум сначала лучше пройти диагностику или оставить заявку. После заявки я сверю входную точку и пришлю ссылку на оплату. После оплаты отправлю чек-лист по инструментальной подготовке, чтобы до старта спокойно проверить окружение.

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

P.S. Пока сайт работает на Tilda. Следующим техническим шагом рассматриваю переезд на отдельный сервер: хочу ускорить загрузку и сделать переходы между страницами плавнее.


Один файл, четыре слоя и сумма, которая легко уезжает

В пятницу показал дерево репозитория практикума и обещал показать что-нибудь интересное 😡

На скрине было много папок: data, dags, db, scripts, spark, checks. Без контекста это выглядит просто как большой технический проект, и сложно понять, что к чему.

Допустим, источник прислал orders.csv.
Внутри идентификатор заказа, дата, статус и сумма. Одна сумма записана как 1299.90, другая как "1 299,90". Где-то нет даты, часть заказов повторилась, а один и тот же файл вообще могли прислать второй раз.

😮 Путь этого файлика будет примерно таким:
orders.csv → RAW → STG → CORE → MARTS → BI


1️⃣ В RAW сохраняем файл таким, каким он приехал. Вместе с ним полезно зафиксировать имя, дату загрузки, источник, количество строк и хеш.
Если дальше что-то сломалось, можно вернуться к исходному файлу и понять, что именно было на входе. Также хеш помогает проверить, не загружали ли мы этот файл раньше.

2️⃣ В STG начинаем приводить данные в порядок. Нормализуем названия колонок, разбираем даты, превращаем сумму в число, ищем пустые обязательные поля. Строки с ошибками лучше складывать отдельно, чтобы они не исчезали бесследно.
Здесь же возникает важный вопрос: какие дубли технические, а какие действительно пришли из источника? Просто сделать DISTINCT обычно недостаточно, потому что он уберёт одинаковые строки, но не объяснит, почему они появились.

3️⃣ В CORE собираем нормальную модель данных. Определяем ключ заказа, связываем его с клиентом и платежом, разбираемся со статусами, отменами и возвратами.
Именно здесь сумма начинает получать бизнес-смысл. Заказ создан, но не оплачен. Оплачен, но потом возвращён. Частично оплачен. Все эти случаи могут выглядеть одинаково в исходном CSV, но по-разному влиять на выручку.

4️⃣ В MARTS данные уже собираются под конкретный вопрос: продажи по дням, заказы по регионам, средний чек или показатели для дашборда.
На этом шаге особенно важно понимать гранулярность таблицы. Одна строка здесь означает заказ, товар в заказе, клиента или день? Если ошибиться, обычный JOIN легко размножит сумму, а итоговая цифра при этом будет выглядеть вполне правдоподобно.

😎 В практикуме этот путь собирается на локальном стенде. MinIO хранит файлы, Spark их обрабатывает, CORE и MARTS лежат в Postgres, Airflow запускает шаги в нужном порядке, а проверки помогают поймать расхождение до BI.

➡️ Небольшая проверка для своего проекта: возьмите одно поле, например amount, и попробуйте провести его от источника до отчёта.

Как оно выглядело в исходном файле?
Где поменялся тип?
На каком шаге добавилась бизнес-логика?
Когда появилась агрегация? Как проверить итоговую сумму?


🔦 Если на эти вопросы есть ответы, путь данных уже читается намного понятнее.

Ставь 🔥 если было полезно и показало картину немного сверху.

#путь_DE


🐻 Сегодня моему блогу про Data Engineering ровно 2 года! ✨

Спасибо всем, кто читает, отвечает, спорит, задаёт вопросы и иногда просто молча остаётся рядом!

Очень ценю, что всё это время вы здесь! ❤️❤️❤️


Docker и Airflow: небольшая практика руками

🙂А помните, я недавно делал опрос, что сильнее всего тормозит при изучении DE. Больше всего ответов набрали Docker и Airflow.

Поэтому собрал небольшое упражнение, которое можно пройти на готовом проекте 🎧

Поднимите стенд и попробуйте разобраться:
🟢 Какие контейнеры запущены и за что отвечает каждый?
🟢 Где Airflow берет DAG и из каких задач он состоит?
🟢 В каком порядке выполняются задачи?
🟢 В какие таблицы записываются данные после каждого шага?
🟢 Как проверить, что загрузка прошла правильно?

После запуска DAG откройте Postgres и посмотрите данные по слоям. Проверьте количество строк, дубли, пустые ключи и итоговую витрину. Потом специально сломайте один шаг: поменяйте название таблицы или файла, снова запустите DAG и найдите ошибку в логах.

Это открытое демо DE-практикума. Внутри Docker, Postgres, Airflow, несколько слоев данных, готовый DAG и задачи руками. Проект небольшой, поэтому можно пройти весь маршрут за небольшое время и посмотреть, как компоненты работают вместе.

📁 Открыть Демо на GitHub (также ссылка в закрепе)

🩵 Если попробуете, напишите, на чем споткнулись: Docker, Airflow, DAG или проверка данных.

#путь_DE


Ребят, кто еще делает такие ошибки время от времени?

Бывает, я могу забыть запятую между CTE. Поменять дату в одном месте и забыть поменять во втором. Два раза заджойнить одну и ту же таблицу. Добавить агрегацию и забыть GROUP BY.

Последнее, конечно, жестко. 🫠
Но когда торопишься, бывает.

Еще классика: поставить фильтр после LEFT JOIN, а потом смотреть, почему строк стало меньше, данных нет, но вы держитесь 🧐

И самое обидное, что это не сложные ошибки. Наоборот, они слишком простые. Из-за этого их и сложно ловить.

В простых вещах такое встречается чаще всего. Не видишь, не видишь, а потом как увидишь.

Если у тебя такое бывает, ставь 🔥

#рабочее


➡️ Хочу сегодня посоветовать канал Саши

Мы познакомились еще в 2024 году, когда оба только активно развивали свои блоги про данные. Я больше писал про SQL, DWH и Data Engineering, Саша - про аналитику, A/B-тесты, статистику и продуктовые решения.

С тех пор я периодически читаю его канал: Саша пишет живо, с иронией, но всегда по делу. Видно, что он очень хорош в настоящих продуктовых ситуациях, где данные есть, но готового ответа из учебника нет.

Да и мне самому полезно читать такой контент. В DE легко закопаться в пайплайны, витрины, Airflow, SQL-проверки и забыть, что дальше эти данные кто-то использует для продуктовых решений. А у Саши как раз хорошо видно, что происходит на следующем слое.

Несколько постов, с которых я бы начал:

1️⃣ Забыл A/B тест - про то, почему эксперименты забываются, зачем нужна нормальная документация и как не потерять пользу от работы, которую команда уже один раз сделала.

2️⃣ P-value = 0.051? - про пограничные результаты, p-hacking и неприятный выбор между статистической строгостью, сроками и деньгами.

3️⃣ A/B без A/B? - про Diff in Diff, Matching, Synthetic Control и почему эти методы не волшебная кнопка, а набор допущений, которые надо понимать.

Если вам интересны продуктовая аналитика, эксперименты, метрики и нормальный живой взгляд на работу аналитика, загляните к Саше.


Пока в сториз идет опрос про главный стопор в DE, Docker и Airflow уверенно забрали пятницу себе.

SQL держится бодро, а вот вокруг локального запуска и DAG начинается борьба 😄

🌀 Если Docker не поднимается, я бы шел так:

1️⃣Проверить, что Docker Desktop вообще запущен.

2️⃣Выполнить:

docker --version
docker compose version
docker info

3️⃣Если проект уже есть, посмотреть контейнеры:

docker compose ps

4️⃣Если контейнер упал, идти в логи:

docker compose logs --tail=100 имя_контейнера

5️⃣Если не стартует база или Airflow, проверить порты. Часто нужный порт уже занят другим процессом.

🟢 С Airflow похожая история.

Если DAG не работает, сначала не надо переписывать код вслепую. Лучше проверить по шагам:

1️⃣ Видит ли Airflow сам DAG.
2️⃣ Нет ли import error.
3️⃣ Появились ли нужные task внутри DAG.
4️⃣ В каком task упал запуск.
5️⃣ Что написано в логах именно этого task.
6️⃣ Был ли результат в данных после успешного запуска.

Зеленый DAG сам по себе еще не доказывает, что данные правильные. Он только говорит, что шаги технически выполнились.

Поэтому после запуска все равно нужны проверки: строки, даты, дубли, null в ключах, сверка сумм между слоями.

🟢 Если совсем коротко: Docker и Airflow становятся проще, когда перестаешь воспринимать их как страшные инструменты и начинаешь смотреть на них как на систему диагностики.
Что запущено?
Где упало?
Что в логах?
Появились ли данные?
Прошли ли проверки?


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

Сохраняй. Пятничный чек-лист на случай, если контейнер решил жить своей жизнью. 🤣

#база_знаний


Не весь Python. А тот, который чистит данные

На неделе я писал про python задачи, которые часто встречаются рядом с DE: привести колонки к одному виду, проверить обязательные поля, поймать изменение схемы, посчитать результат загрузки, замаскировать личные данные.

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

Такие задачи я как раз вынес в отдельный курс на Stepik: Python для DE: фундамент и обработка данных

Курс для тех, кто уже знает базовый Python, но хочет увереннее работать с данными руками: CSV, JSON, даты, суммы, дубли, пустые поля, проверки перед загрузкой и маленькие batch-задачи.

Есть бесплатный демо-модуль. Можно открыть, посмотреть формат и решить первые задачи без покупки.

Для канала сделал отдельный промокод TG300.
До 1 июля курс можно взять за дешевле.

➡️ Ссылка на курс:
https://stepik.org/a/290179

#курсы


5 практичных Python-задач для DE

В SQL-проверках мы часто смотрим уже готовую витрину: сколько строк, какой период, нет ли дублей, не поехали ли суммы.

Но часть проблем появляется раньше. Еще до витрины.
Пришел файл. В нем странные названия колонок, пустые обязательные поля, лишние столбцы, дубли, email в открытом виде, непонятно сколько строк реально загрузилось. Часто нужно взять чей-то сэмпл данных и поработать с ним, например использовать его для загрузки таблицы на тестовом стенде.

И тут Python хорошо ложится на такие задачи.

Можно начать с простого.

1️⃣ Сначала привести названия колонок к одному виду. Внешние файлы часто приезжают как попало:

name = " Order--Total "

parts = name.strip().lower()
parts = parts.replace("-", " ").replace("_", " ").split()

"_".join(parts)

# "order_total"

Кажется ерундой, пока потом не начинается join по Customer ID, customer_id и CUSTOMER-ID.

2️⃣ Дальше проверить обязательные поля перед загрузкой.
Например, в записи есть amount = 0 и это нормальное значение. Его нельзя считать пустым просто потому, что if not value сработает как ложь.

row = {"order_id": 101, "email": " ", "amount": 0}
required = ["order_id", "email", "status", "amount"]

# missing: ["email", "status"]

Важно помнить, что None, пустая строка и 0 - это разные вещи.

3️⃣ Еще полезно собирать метрики загрузки файлов: сколько файлов пришло, сколько загрузилось, сколько отклонилось и сколько строк реально попало дальше.

loads = [
{"file": "a.csv", "status": "loaded", "rows": 120},
{"file": "b.csv", "status": "rejected", "rows": 30},
{"file": "c.csv", "status": "loaded", "rows": 80},
]

# total_files: 3
# loaded_files: 2
# rejected_files: 1
# loaded_rows: 200

4️⃣ Отдельная задача - изменение схемы.

Источник добавил колонку, убрал старую, поменял порядок. Если это не заметить сразу, как у меня было много раз, потом можно долго искать ошибки.

expected = ["order_id", "email", "amount"]
actual = ["order_id", "amount", "currency"]

# missing_columns: ["email"]
# unexpected_columns: ["currency"]

5️⃣ И еще одна практичная задача: не тащить личные данные в технические отчеты и сэмплы открытым текстом.

email = " Alice.Smith@Example.COM "

# "a***@example.com"

🟩 На разных проектах можно встретить разный стек и технологии, но такие задачи полезно уметь решать. Пусть не с первого раза и не самым оптимальным способом. В реальной работе обычно важнее не написать “самый красивый алгоритм”, а аккуратно привести данные в порядок и не сломать смысл.

Именно из таких маленьких функций часто собирается нормальная обработка данных и подготовка к дальнейшему ETL.

Один раз завести такую привычку, и она будет экономить вам часы работы.

➡️ Сохраняй. Это хороший короткий чек-лист для использования Python в задачах с данными.

#материалы


⚡️ Хочу оставить здесь несколько живых сообщений от участников потока. Для меня это хороший способ увидеть, где материал действительно начинает работать в реальных задачах и в понимании всего маршрута данных.

Если вам сейчас как раз этого и не хватает, посмотреть практикум можно здесь.
Программа курса на Stepik.

#путь_DE


💬 Что я проверяю в DE-практикуме

Когда речь заходит про проверку в учебном треке по Data Engineering, многие представляют следующее: сошлось или не сошлось, запустилось или упало, зелёный статус или красный. Плюс со стороны не всегда вообще понятно, как такие задачи проверяются, особенно когда внутри и SQL, и Python, и слои данных, и orchestration.

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

Первый слой технический. Студент должен руками собрать задание, получить ожидаемый результат, не потерять строки, не сломать логику связей между таблицами. Здесь я смотрю на конкретику: сколько получилось строк, что оказалось в таблице, где именно вылезла ошибка, что говорят логи, как человек собрал слой, как устроил загрузку, как связал сущности при построении хранилища. Это обычная техничка, и без неё никуда, потому что если у тебя внизу всё трещит, то разговор про мышление пока рано начинать.

💡 Но есть и второй слой, который для меня даже важнее. Это про то, насколько студент понимает, что именно он сделал и почему оно работает именно так. Потому что в DE проблема обычно не в том, что кто-то вообще ничего не сделал, а в том, что шаг выполнен, результат как будто есть, а понимание размыто. Человек поставил partition или repartition, но пока слабо чувствует, что именно после этого меняется. Добавил проверки data quality, но не до конца понимает, что именно они страхуют и почему их место именно после построения слоя, а не где попало. Выбрал один способ перезаливки, но пока не очень различает, где уместен replace by date, где нужен инкремент, а где полная перезагрузка просто ломает логику и делает систему хрупкой.

Вот в этих точках как раз и видно, начинает ли человек собирать инженерное мышление, или пока ещё просто двигается по инструкции. Мне вообще неинтересно использовать проверку как способ завалить кого-то на неточности. Это, на мой взгляд, тупиковый подход. Куда полезнее показать, где ответ пока сырой, где логика у человека ещё недокручена, где он уже сделал руками, но сам для себя ещё не распаковал, что именно произошло по пути. Иногда у человека задание формально собрано, но ответ остаётся слишком поверхностным. Иногда наоборот: мысль правильная, но техничка проседает. И вот задача проверки как раз в том, чтобы эти вещи развести и подсветить.

Хорошая проверка в таком треке для меня всегда отвечает на два вопроса. Первый: что у тебя получилось руками. Второй уже важнее: что ты по дороге реально понял, а что пока только повторил.

Именно поэтому часть проверки в практикуме сделана в формате свободного ответа с моей рецензией - многие важные вещи в DE невозможно нормально проверить одним автоматическим тестом. Тест покажет, что шаг формально выполнен. А вот насколько точно человек понимает логику, где у него слабое место и что он сам пока объясняет себе слишком поверхностно, это уже видно только в разборе ответа.

Мне как раз важно, чтобы по ходу практикума у человека постепенно собиралась связная картина: от источника до витрины, от загрузки до слоя, от SQL и Python до orchestration и дальше уже до спокойного понимания, где здесь слабое место и чем его проверять.

⤵️ Если у вас уже есть SQL-база, но цельная картина пока не складывается, страницу практикума можно посмотреть здесь.

⤴️ А если просто есть вопросы по курсу, пишите в личку, отвечу и подскажу.

#путь_DE


🤔 Проверки, которые ты делал, но через неделю уже не помнишь.

На проектах такое встречается часто - когда нужно сделать SQL проверку, ты написал select count(*), проверил даты, посмотрел дубли, всё увидел и пошёл дальше в разработку.

А потом этот же вопрос возвращается через пару дней. И ты вспоминаешь, что делал, какие таблицы проверял, ищешь нужный скрипт, вспоминаешь даты. Короче классика)

➡️ Мне нравится привычка: даже маленькая проверка должна оставлять след. Чтобы результат проверки можно было потом открыть в таблице.

Например, можно завести простую служебную таблицу:

create table if not exists dq_check_log (
checked_at timestamp default now(),
layer_name text,
table_name text,
check_name text,
status text,
check_value numeric,
details text
);

Допустим, после сборки витрины нужно проверить, что за нужную дату вообще появились строки:

insert into dq_check_log (
layer_name,
table_name,
check_name,
status,
check_value,
details
)
select
'marts',
'sales_daily',
'rows_for_date',
case when count(*) > 0 then 'PASS' else 'FAIL' end,
count(*)::numeric,
'rows in sales_daily for 2026-05-01'
from marts.sales_daily
where dt = date '2026-05-01';

Одна проверка и одна строка в журнале. Но потом она сильно помогает.

Можно открыть историю и увидеть, какие проверки проходили, когда они проходили и где впервые стало пусто:

select *
from dq_check_log
order by checked_at desc
limit 20;

Я бы начинал хотя бы с таких проверок:

есть ли строки за нужный период;
нет ли дублей на уровне витрины;
не пустые ли ключевые поля;
сходится ли сумма с предыдущим слоем;
не уехала ли дата.

Но если пайплайн повторяется, если к нему возвращаются, если результат смотрит кто-то ещё, проверка без следа теряется.

А уже дальше этот же подход можно привязывать к Airflow, Spark jobs и конкретным запускам пайплайна: добавлять run_id, название задачи, batch date и статус выполнения.

➡️ В практикуме я как раз прокачиваю у студентов эту привычку: не только собрать слой или витрину, но и оставить после запуска нормальный проверяемый след.

Потому что таблица может собраться. Запрос может выполниться. А вот доверять результату всё равно можно только после проверки.

🔛 Сохраняй и внедряй.

#база_знаний


Где у вас сейчас самый большой разрыв между аналитикой и DE?
Опрос
  •   💻 Хранилище данных и слои
  •   ⚙️ Spark / Airflow
  •   🔐 Проверки качества
  •   ☕️ Не вижу маршрут целиком
27 голосов

Показано 20 последних публикаций.