Data Driven / Bogdan Ivanov
890 subscribers
68 photos
47 videos
2 files
54 links
Данные, карьера, саморазвитие

https://datadrivenbogdan.com/

Помогаю вырасти в работе с данными и зарабатывать больше.
За менторством и сотрудничеством — @b_ivanov_kz
Download Telegram
This media is not supported in your browser
VIEW IN TELEGRAM
Хорошего окончания рабочей недели!
И немного юморка вам в придачу — чтобы никто такое безобразие вам в mr не предлагал!

@data_driven_bogdan
❤29🤣20🔥13🥰7👍4👏4
Обзор собеседования (Ч.1)

Компания: Dodo brands
Вилка: +-350 net
Должность: Дата аналитик
Формат: Удаленный
Этапы собеседования:
HR – Тех. собес (внутренний) – Хуево кукуево созвонов с кем только можно – Offer
Грейд: Middle + / Senior
Дата собеседования: декабрь 2025

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

SQL, Python и немного статистического мышления

Технический блок: SQL + Python

1️⃣ 60-day retention по когортам (SQL)

Необходимо посчитать 60-day retention по факту покупок пользователей
orders (order_id, user_id, order_date)

В ответе должна быть выведена след. таблица:
- cohort_month (Первый день месяца первой покупки, YYYY-MM-01)
- cohort_size (Общее количество новых покупателей в этом месяце)
- retention_60d (Процент повторно купивших в течение 60 дней)

Решение:
-- определяем дату первой покупки каждого юзера
WITH first_order AS (
SELECT
user_id,
MIN(order_date) AS first_order_date
FROM orders
GROUP BY 1
),
-- находим юзеров, которые сделали хотя бы 1 покупку после первой в теч. 60 дней

returns AS (
SELECT DISTINCT
f.user_id
FROM first_order f
JOIN orders o
ON f.user_id = o.user_id
AND o.order_date > f.first_order_date
AND o.order_date <= f.first_order_date + INTERVAL '60 day'
)

-- считаем стату, left'ом оставляем юзеров без повторной покупки

SELECT
DATE_TRUNC('month', f.first_order_date)::date AS cohort_month,
COUNT(f.user_id) AS cohort_size,
ROUND(
COUNT(r.user_id) * 1.0 / COUNT(f.user_id),
2
) AS retention_60d
FROM first_order f
LEFT JOIN returns r USING (user_id)
GROUP BY 1;


2️⃣ Клиенты с суммарным весом покупок > 2 кг (Python)

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

SALES: client_id, date, sku, cnt
DICT: sku (товар), weight (вес в кг)

Решение:
-- ласт месяц берем
last_month = sales[
sales["date"] >= sales["date"].max() - pd.DateOffset(months=1)
]
-- дикс мерджим с фактом
df = last_month.merge(dict_, on="sku", how="inner")

-- считаем тотал вес (штуки на справочный вес)
df["total_weight"] = df["cnt"] * df["weight"]

-- агрегация сум. веса по каждому клиенту и отбор по общему весу >2 кг
result = (
df.groupby("client_id", as_index=False)["total_weight"]
.sum()
.query("total_weight > 2")
)
-- список client_id
clients = result["client_id"].tolist()


3️⃣ Реализация дисперсии (Python)

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

Решение:

def var(a: list):
mean = sum(a) / len(a)
return sum((x - mean) ** 2 for x in a) / (len(a) - 1)


Важно не перепутать population и sample variance (ну и помнить конечно, что такое дисперсия в целом — мера разброса значений относительно среднего, если на человеческом)
Несмещенная выборочная дисперсия отличается от обычной тем, что в знаменателе не n, а на n − 1 это корректирует смещение оценки дисперсии генеральной совокупности

4️⃣ Анализ распределения (статистика)

Дана выборка клиентов с тратами и нужно было проанализировать распределение
60 клиентов — 0 ₽
38 клиентов — 1000–1500 ₽
2 клиента — 10 000 ₽

Коротко:
*️⃣распределение с heavy tail
*️⃣среднее будет сильно смещено выбросами
*️⃣медиана и перцентили устойчивее

В некст части больше упора будет на статистику и потом будет 3ая очень интересная часть про переговоры

🍌 Подпишись и поддержи канал бустом

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥45🦄2513❤8👏55👍4
Channel photo updated
Работаем на дефицит 🤝💼

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
😁10🔥7❤64👍1
🍌 Апдейт

Я наконец-то пересобрал всю информацию с телетайпа и вынес ее на сайт — теперь все про менторство и другие виды моих услуг по data компактно собрано в одностраничнике.

👉 https://datadrivenbogdan.com

На сайте описал форматы работы со мной:
— разовые консультации
— мок-собеседования
— работа с резюме/легендой
— менторство с доведением до оффера

👨‍💻Коротко о себе:

В сфере data уже больше 3х лет. Работал в финтехе, FMCG и ритейле — Альфа-Банк, ВТБ, Philip Morris, Магнит, а также стартапах. Постоянно выхожу на рынок, много собеседуюсь и коммуницирую с кандидатами, поэтому хорошо понимаю актуальные требования рынка и ожидания работодателей.

Работа со мной — это про системную работу, психологию и понимание рынка, без громких обещаний и иллюзий быстрых результатов при отсутствии должных усилий.

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

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

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥33👍21❤16👏84🦄3🥰2🤯2
Data Driven / Bogdan Ivanov pinned «🍌 Апдейт Я наконец-то пересобрал всю информацию с телетайпа и вынес ее на сайт — теперь все про менторство и другие виды моих услуг по data компактно собрано в одностраничнике. 👉 https://datadrivenbogdan.com На сайте описал форматы работы со мной: — разовые…»
🏠Trino + Apache Iceberg — стандарт современной Data Lakehouse архитектуры (Ч. 1)

С развитием аналитической архитектуры крупные компании перестают укладываться в модель «одна большая СУБД на всё» (Greenplum, Oracle, Teradata). По мере зрелости появляются доменные команды, новые источники данных, стриминг, ML-пайплайны, разные типы нагрузок — и архитектура естественным образом начинает распределяться. Часть данных хранится в S3, часть — в CH, часть — в постгре, что-то приходит через Kafka, где-то до сих пор живёт Hive или старый DWH. Всё это — нормальная эволюция сложной системы.

Проблема в другом: бизнесу по-прежнему нужен единый SQL-доступ ко всем данным, дешёвое масштабируемое хранение, контроль над инфраструктурой и отсутствие жёсткой привязки к одному вендору или облаку. И вот в этот момент модель «центральная СУБД» перестаёт быть устойчивой.

Возникает архитектурный разрыв: данные уже распределены, а инструменты доступа к ним — нет. Нужен слой, который объединит всё это без миграции в одну гигантскую систему, и при этом даст управляемость уровня DWH поверх Data Lake.

И именно здесь появляется связка Trino + Apache Iceberg.

Что это за связка и почему именно она
❓

Trino
— это распределённый SQL движок. Он не хранит данные и не является хранилищем в классическом смысле. Это слой выполнения запросов, который может ходить в разные источники (СУБД) через коннекторы и исполнять один SQL поверх разных систем одновременно.

Ключевая идея Trino — federation.
Один SQL может одновременно читать таблицу в S3, сделать join с CH, подтянуть данные из GP и агрегировать всё в одном запросе. Без физической миграции данных в единое DWH. Trino становится универсальным SQL-слоем компании.

Apache Iceberg
— это не движок, а формат таблиц для Data Lake. Он добавляет к object storage (S3 / GCS / ADLS) управляемость уровня DWH. Iceberg хранит данные в columnar-форматах (Parquet / ORC / Avro), а поверх них — слой метаданных и snapshot-модель.

В чём его разница от обычного S3
❓

Коротко — в управлении. Iceberg даёт:
➖ ACID-транзакции поверх S3
➖snapshot-based commits
➖ schema evolution без поломки старых запросов
➖time travel (чтение таблицы на конкретный snapshot)
➖ manifest-файлы и metadata pruning

PS архитектуру Iceberg'а разберем в следующем посте подробнее.

Фактически Iceberg превращает папку с файлами в полноценную аналитическую таблицу.

Почему именно они в связке
❓

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

Iceberg гарантирует, что таблица атомарна и согласована. Trino умеет эффективно читать manifest-файлы, отсекать лишние файлы на этапе планирования, делать file pruning и выполнять запросы распределённо. Это архитектурно совместимые компоненты.

Если упростить архитектуру:
Object Storage → Iceberg (табличный слой) → Trino (SQL-слой) → BI / аналитика / ML

❤️‍🔥 Подписывайтесь и поддержите канал бустом
↗️ Менторю и помогаю выйти на оффер
❓ Уже работаете с Trino / Iceberg или пока на классическом DWH?

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥19👍9❤76🦄2
This media is not supported in your browser
VIEW IN TELEGRAM
Видимо теперь переходим на спортивный контент…

@data_driven_bogdan
1❤13🔥9👍43😁1😍1
Causal Inference: как оценивать эффект, когда A/B — не вариант (Ч. 2)

В первой части разобрали DiD, сегодня разберем еще один популярный инструмент — Propensity Score Matching (далее PSM).

Идея PSM'a - мы не сравниваем всех подряд из теста и контроля, а сначала стараемся сделать группы максимально сопоставимыми по характеристикам,
и только потом считаем эффект.

Если DiD — это про сравнение трендов, то PSM — это про подбор группы.

В чем проблема, которую решает PSM❓

Представим ситуацию:
➖ акция показана только лояльным клиентам
➖новая функция доступна только premium подписчикам
➖пользователи сами приняли решение участвовать

В этом случае назначение treatment не случайно, то есть попадание в тест зависит от характеристик X (доход, активность, средний чек и тд).
P(T=1 | X) ≠ P(T=1)

Значит, если просто сравнить разницу средних, то мы получим смещенную оценку, где разница может быть вызвана не воздействием, а тем, что группы изначально разные.
E[Y | T=1] − E[Y | T=0]

Формальная логика

Для каждого объекта i существуют два потенциальных исхода:

Yᵢ(1) — если получил воздействие
Yᵢ(0) — если не получил воздействие

Мы наблюдаем только один из них. Второй — контрфакт.

Нас интересует: ATT = E[Y(1) − Y(0) | T=1]

Средний эффект на тех, кто получил treatment.

Проблема в том, что E[Y(0) | T=1] мы не наблюдаем.
Мы не знаем, что было бы с тестовой группой без воздействия.

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

Как это работает❓

Вместо того чтобы матчить по десяткам признаков X напрямую, мы считаем:

p(X) = P(T=1 | X) - это вероятность попадания в treatment при данных характеристиках.

Теорема Розенбаума и Рубина говорит, что если выполняется предпосылка unconfoundedness, то достаточно сбалансировать группы по ps, а не по всему вектору признаков X.

По сути, мы схлопываем многомерную проблему в одно число.

Как строится PSM на практике

1️⃣ Выбираем две группы
Собираем датасет из тестовой группы (получила воздействие) и контроль (не получила воздействие)
ps ниже в комментах напишу пример

Контроль желательно существенно больше теста (часто в 3–5 раз), чтобы алгоритму было из чего подбирать пары.

2️⃣ Формируем признаки X. (aov, arppu, freq, трафик, регион, также, например, метрики на товар X и так далее)
Критично: все признаки считаются только по данным ДО момента воздействия.

3️⃣ Строим модель для оценки propensity score (PS).
Это обычная бинарная классификация: таргет — T (0/1), признаки — X,
только нам нужно не само значение таргета, а скор по клиенту для попадания в таргет

Для этого используются ML модели для проблемы классификации: лог. регрессия, град бустинг, рандом форест и тд.

Важно: нам нужна хорошая калибровка вероятностей, если модель идеально различает тест и контроль — это тревожный сигнал => группы могут быть слишком разными, те нужно подбирать другую КГ.

4️⃣ Выполняем matching (M).
Для каждого объекта из treatment ищем объект из control с максимально близким ps. (иногда допускается matching с повторениями)

Популярные методы:
— Nearest Neighbor
— k-ближайших соседей
— Caliper (ограничение на |Δp| ≤ ε) и др

5️⃣Считаем эффект
Далее мы получили подмножество test и control'а, которым подобралась пара друг другаи теперь мы можем сравнить эффект:

ATT ≈ E[Y | T=1] − E[Y | T=0, matched]

Тем самым, мы заменили ненаблюдаемый контрфакт на максимально похожий на контрфакт объекты.

Вот так вот работает PSM, надеюсь объяснил понятно, пару дополнений оставлю в комментах.

❤️‍🔥 Подписывайтесь и поддержите канал бустом
↗️ Менторю и помогаю выйти на оффер
❓ Сталкивались с PSM? Если да, расскажите про ваши кейсы

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍2417❤11🦄4
This media is not supported in your browser
VIEW IN TELEGRAM
Ноги качать не забываем!
140 уже…

@data_driven_bogdan
1🔥11👍44❤‍🔥1😍1
This media is not supported in your browser
VIEW IN TELEGRAM
Вчера в приседе 150 сел
Потихоньку растем!😎

На выходных напишу про causal impact

Всем хороших выходных и с окончанием рабочей недели!👨‍💻

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤5👍5🔥5😍1
Causal Inference: как оценивать эффект, когда A/B — не вариант (Ч. 3)

Сегодня разберем еще один популярный инструмент — Causal Impact (далее CI), он же синтетический контроль на основе прогноза

Идея метода

Мы строим прогноз того, как вела бы себя метрика, если бы изменения не произошло, и сравниваем этот прогноз с фактом (в факте изменения есть).
То есть CI — это способ построить контрфакт через модель временного ряда. => Эффект = Факт − Прогноз

В чем задача, которую решает CI❓

Представим ситуацию:

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

Контрольной группы нет, но нам нужно ответить на вопрос:

👉 «А что было бы с метрикой, если бы изменений не произошло?»

Как работает CI❓

1️⃣ Берём длинную историю данных до вмешательства

2️⃣ Обучаем модель, которая учится на: тренде, сезонности, внешних факторах (если есть ковариаты)

3️⃣ Строим прогноз на период изменения — пытаемся прогнозировать ситуацию в "мире", где вмешательства как будто бы не было

4️⃣ Сравниваем прогноз с фактическими значениями

Если факт стабильно выходит за ДИ прогноза — скорее всего, изменение действительно повлияло.

Что происходит “под капотом”❓

Классическая реализация CI основана на BSTS (Bayesian Structural Time Series) — это класс байесовских моделей временных рядов.
Модель строит распределение прогнозов и оценивает неопределенность, из которого затем считается эффект и доверительные интервалы.

Реализация в R — пакет CausalImpact (Google), в Python есть — pycausalimpact.

Но можно применять любые модели временных рядов, например:

➖ARIMA / SARIMA (statsmodels)
➖Prophet (prophet)
➖модели из darts, gluonts, pmdarima
➖ML с лагами (XGBoost, LightGBM)

Также можно реализовать логику вручную через statsmodels, sklearn или любые TS-фреймворки.

Принцип один: обучаем модель на pre-period → прогнозируем period → сравниваем прогноз и факт.

Если в модели используются ковариаты (внешние факторы), они должны:

➖ хорошо коррелировать с целевой метрикой
➖ не быть напрямую затронуты вмешательством

Иначе модель может “встроить” эффект внутрь прогноза
и занизить итоговую оценку.

Когда метод применим❓

➖Есть длинная история наблюдений ДО вмешательства
➖ Модель хорошо прогнозирует pre-period (нет систематического смещения, разумный ДИ)
ps По хорошему ошибка прогноза на pre-period должна быть существенно меньше предполагаемого эффекта.
➖ Можно подобрать адекватные ковариаты
➖ Вмешательство произошло в конкретный момент времени

Ограничения метода

➖Нет контроля за внешними событиями, если они совпали по времени
➖Сложно отделить эффект нескольких вмешательств

Пример

ЦБ резко поднял ключевую ставку — мы хотим понять, как это повлияло на объем кредитов. Контрольной страны нет, группы сравнивать не с кем.

Мы строим модель по данным до повышения ставки, прогнозируем объем кредитов без повышения
и сравниваем с фактом.

Разница — и есть оценка эффекта.

❤️‍🔥 Подписывайтесь и поддержите канал бустом
↗️ Менторю и помогаю выйти на оффер
❓ Все ли понятно? А если работали с Causal Impact — расскажите про ваши кейсы👇

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥7❤53
Дорогие девушки, с 8 марта! 🌸

Пусть в вашей жизни будет больше тепла, поддержки, уверенности в себе и людей, которые искренне ценят вас.
С праздником весны, красоты и внутренней силы!✨

@data_driven_bogdan
1❤195🔥4
Почему после JOIN появляются дубли строк❓

Часто замечаю, что многие, кто начинает свой путь в аналитике, не до конца понимают, как реально работают JOIN’ы в SQL.

Может ошибочно казаться, что условный LEFT JOIN только подтягивает поля из второй таблицы, соответственно кол-во строк остается таким же. (как ВПР в excеl'e)
Или INNER JOIN оставляет только строки из левой и правой таблиц, то есть количество строк останется таким же или становится меньше.

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

Разберем на примере:
tbl1 id - 10 строк
1
2
3
4
4
5
5
6
NULL
NULL

tbl2 id - 7 строк
1
3
4
4
5
6
NULL

Выполняем:
SELECT *
FROM table1 t1
LEFT JOIN table2 t2
ON t1.id = t2.id

Что получится:
➖1 → 1 совпадение
➖2 → нет совпадений, но в LEFT JOIN строка останется, а справа будут NULL
➖3 → 1 совпадение
➖4 → слева 2 строки, справа 2 строки → будет 4 строки
➖5 → слева 2 строки, справа 1 строка → будет 2 строки
➖6 → 1 совпадение
➖NULL с NULL через = не матчится, поэтому обе строки с NULL слева просто останутся без пары (заполнятся NULL'ами справа)
Итого: 12 строк


Почему так?
👉
каждая строка слева соединяется со всеми подходящими строками справа

То есть если ключ не уникален, строки начинают размножаться.

Например:
➖ 2 строки слева × 2 строки справа = 4 строки
➖ 2 строки слева × 3 строки справа = 6 строк

Именно поэтому я и говорю, что после JOIN количество строк может увеличиться.

И это касается не только LEFT JOIN.
➖В INNER JOIN строк тоже может стать больше, если есть множественные совпадения с обеих сторон
➖В RIGHT JOIN — та же логика, просто сохраняется уже правая таблица
➖В FULL JOIN — тоже могут появляться дополнительные строки за счет множественных совпадений и несовпавших записей с обеих сторон
➖В CROSS JOIN вообще сразу строится декартово произведение, поэтому там рост количества строк очевиден изначально

Еще раз закрепим: ключевая идея JOIN'ов не просто "подтянуть поля", а построить все пары строк, которые подходят под условие соединения

Еще одна задача на закрепление понимания

Если в левой таблице 100 строк и в правой таблице 100 строк,
то сколько строк может быть после JOIN в минимальном и максимальном случае? (разберем на примере INNER'а и LEFT'а)

Попробуйте сначала ответить сами и написать в комментах👇

Ответ:
➖для INNER JOIN минимум — 0, максимум — 10 000
➖для LEFT JOIN минимум — 100, максимум — 10 000

Разберем, почему так

Минимум:


➖INNER JOIN может дать 0 строк, если совпадений между таблицами нет вообще.
➖LEFT JOIN меньше 100 строк здесь дать не может, потому что он в любом случае сохраняет все строки из левой таблицы. Если совпадений справа нет — просто получим NULL в колонках правой таблицы.

Максимум:

➖В предельном случае каждая строка слева может соединиться с каждой строкой справа (в обоих случаях).
Тогда получаем: 100 × 100 = 10 000 строк

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


❤️‍🔥 Подписывайтесь и поддержите канал бустом
↗️ Менторю и помогаю выйти на оффер
❓ У кого после JOINа хоть раз ехали метрики из-за того, что выросло количество строк?👇

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤135🔥4
Пойти в лида или взять вторую работу❓

Две разные карьерные стратегии.
Первая — расти вверх: больше ответственности, управление, lead/head-трек и рост стоимости часа.
Вторая — расти вширь: оставить основную работу и добавить второй источник дохода.

Вокруг второго сценария обычно много путаницы, поэтому сначала стоит разобраться в базе: а как в РФ вообще можно оформить больше одной работы❓
Потому что многие либо не знают, либо смешивают в одну кучу совместительство, ГПХ, самозанятость и ИП, хотя это разные конструкции и разные риски.

Какие форматы оформления есть:

1️⃣ Трудовой договор + трудовой договор по совместительству
Это вполне легальный вариант, но с ограничениями по времени. По общему правилу совместительство — это не больше 4 часов в день и не больше половины месячной нормы рабочего времени. По сути, на совместительстве ты можешь работать не более 0.5 от фулл-ставки, поэтому две полноценные фултайм-работы по двум трудовым договорам — уже очень спорная история. На практике, конечно, встречаются и серые схемы обхода этого ограничения, но это уже отдельный риск для обеих сторон.

2️⃣ Трудовой договор + ГПХ
Обычно это вариант, когда вторая работа оформляется как подряд под конкретный результат: проект, аналитика, разработка, консультация. Но здесь важно, чтобы ГПХ не маскировал обычный найм. Если есть график, постоянная функция и фактически «вторая штатная работа», у налоговой и трудовой инспекции могут появиться вопросы о подмене трудовых отношений, поэтому в таком формате надо быть достаточно аккуратным.

3️⃣ Трудовой договор + самозанятость / ИП
Логика здесь похожа на подряд, но ИП — это уже отдельный предпринимательский статус, который обычно выбирают для более регулярной и системной деятельности. У ИП, как правило, больше операционной нагрузки: налоги и взносы нужно контролировать самостоятельно, а объем отчетности зависит от выбранного налогового режима. На практике эту рутину часто закрывают через онлайн-бухгалтерию или аутсорс.

Для самозанятости важно помнить два ограничения: нельзя оказывать услуги текущему работодателю и бывшему работодателю, если после увольнения прошло меньше двух лет, а еще есть лимит по доходу — не более 2,4 млн ₽ в год.

При этом у ГПХ, самозанятости и ИП есть важный плюс: в таком формате ты можешь брать сразу несколько проектов.

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

Главный вывод простой: в РФ оформить больше одной работы можно, но не все схемы одинаково удобны и безопасны — у каждой есть свои плюсы и минусы, поэтому при выборе нужно проанализировать свои вводные, оценить все риски согласно вводным и принимать решение.

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

❤️‍🔥 Подписывайтесь и поддержите канал бустом
↗️ Менторю и помогаю выйти на оффер
❓ Какая стратегия ближе вам: расти в сторону лида или добирать доход второй работой?👇

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤12👍5🔥41
This media is not supported in your browser
VIEW IN TELEGRAM
Спортивного контента в ленту 💪

160 кг в приседе — есть

Дальше больше…

@data_driven_bogdan
1🔥8❤6🤯1
👍👍👍👍Архитектура Apache Iceberg: как устроена таблица поверх S3 (Ч. 2)

В прошлом посте обсуждали связку Trino + Iceberg.
Теперь разберёмся, за счёт чего Iceberg вообще превращает “папку с файлами в S3” в полноценную аналитическую таблицу.

⚙️ Как устроен Iceberg

Если упрощать, у Iceberg несколько уровней:

1️⃣ Catalog
2️⃣ Metadata layer
3️⃣ Snapshot
4️⃣ Manifest list/manifest files
5️⃣ Data/Delete files

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

1️⃣ Catalog

Это слой, который отвечает за учёт таблиц и их текущего состояния.

Он хранит:
➖ список таблиц
➖ путь к актуальному metadata file каждой таблицы

То есть каталог — это не хранилище данных, а реестр актуальных версий таблиц.

Каталог может быть разным, Iceberg не привязан к одному хранилищу метаданных — можно выбрать, где хранить этот “реестр”: Hive Metastore (вариант из Hadoop-мира), JDBC Catalog (таблицы в обычной БД), Project Nessie (каталог с git-подобными ветками) или другие форматы. В них меняется реализация, но логика остается той же.

2️⃣ Metadata layer

Это основной уровень управления таблицей.

Главный metadata file хранит:
➖схему таблицы
➖ список snapshot’ов
➖ ссылки на manifest list

Каждое изменение таблицы создаёт новый metadata file (далее подробнее о нем).
За счёт этого Iceberg поддерживает версионирование, роллбэк, тайм тревел и безопасное изменение схемы

3️⃣Snapshot’ы

Это конкретная зафиксированная версия таблицы в момент времени.
Iceberg не “переписывает таблицу целиком” при каждом изменении.
Он создаёт новый snapshot, который ссылается на актуальный набор файлов через manifest list.

То есть: таблица = не просто набор файлов, а грубо говоря цепочка snapshot’ов, у каждого из которых своё состояние данных

4️⃣ Manifest list и manifest files

Вот здесь начинается самое важное для производительности.
Manifest list — это список manifest-файлов, относящихся к конкретному snapshot’у.
А сами manifest files уже содержат информацию о data files и delete files: какие файлы входят в таблицу, какие у них партиции и статистики (min/max, null count и т.д.).
За счёт этого движок ещё на этапе planning понимает, какие файлы можно вообще не читать. Именно так работают file/metadata pruning и data skipping.

5️⃣ Data files

Это уже сами файлы с данными в object storage: Parquet, ORC, Avro. Чаще всего в аналитике используют Parquet.
Важно, что сами data files не знают, что они часть таблицы — они просто хранят данные. Вся логика таблицы находится на уровне метаданных.

5️⃣ Delete files

Iceberg поддерживает не только append, но и DELETE / UPDATE / MERGE. Для этого используются delete files:
position deletes — удаление строк по позиции в файле,
equality deletes — удаление по значению ключа.
При чтении движок учитывает и data files, и delete files, чтобы собрать актуальное состояние таблицы.

Как происходит commit

Iceberg не переписывает таблицу целиком. При изменении он создаёт новые data/delete files, новые manifest’ы, собирает новый snapshot, формирует новый metadata file и обновляет указатель в catalog. По сути commit — это переключение таблицы на новую версию. За счёт этого Iceberg даёт atomic commit, snapshot isolation и optimistic concurrency control.

Как выполняется запрос


Если совсем упростить: catalog → metadata file → snapshot → manifest list → manifest files → нужные data files. То есть движок не читает все файлы в S3 подряд, а заранее отсекает лишние. Поэтому Iceberg — это не просто папка с parquet-файлами, а полноценный табличный формат поверх object storage.

Резюмируя, Iceberg — это слой метаданных и snapshot-логики, который превращает S3 в управляемую аналитическую таблицу.


❤️‍🔥 Подписывайтесь и поддержите канал бустом
↗️ Менторю и помогаю выйти на оффер
❓ Был ли у вас на проекте Iceberg или пока не доходили до него?

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤13🔥7👍6
Please open Telegram to view this post
VIEW IN TELEGRAM
1😁12❤8🔥5🤣3
К сожалению, Чемпионат Мира 2026 по футболу в Америке посетить не получится — визу не одобрили 😟

@data_driven_bogdan
Please open Telegram to view this post
VIEW IN TELEGRAM
1💔22😭843🎉1