PrepBro
Профессии
PrepBro
Профессия:

Подготовка

  • Вопросы181
  • Задачи19

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Профессия:

Подготовка

  • Вопросы181
  • Задачи19

Аналитика

  • hh статистика
  • Анализ резюме

Практика

  • Тестовое собеседование
  • Mock-собеседование
  • Менторы

Поддержка / отзывы

Telegram админа
Все 24 профессии
Android DeveloperData AnalystSystem Analyst1С DeveloperiOS DeveloperBusiness AnalystJava DeveloperData ScientistQA EngineerQA AutomationPHP BackendC/C++ BackendDevOps EngineerIT Project ManagerFrontend DeveloperNode.js BackendUnity DeveloperC# BackendProduct AnalystFlutter DeveloperPython DeveloperIT Product ManagerGo DeveloperData Engineer

© 2026 PrepBro. Все права защищены.

Telegram-бот

Задачи по Data Analyst

Соединение таблиц сотрудников и должностей
1.2 Junior🔥 23💬 1

Решение: SQL JOIN для соединения таблиц

Основной запрос

SELECT p.id, p.name, pos.title AS pos_title FROM persons p INNER JOIN positions pos ON p.pos_id = pos.id;

Объяснение

SELECT столбцы:

  • p.id — идентификатор сотрудника из persons
  • p.name — имя сотрудника
  • pos.title AS pos_title — название должности с переименованием

FROM persons p: основная таблица с сокращением p

INNER JOIN positions pos: соединяем positions, выводим только совпадения

ON p.pos_id = pos.id: условие — id должности в persons совпадает с id в positions

Как работает запрос

  1. Берем таблицу persons (Иван pos_id=1, Мария pos_id=2)
  2. Присоединяем positions (1=Аналитик, 2=Менеджер)
  3. Ищем совпадения по pos_id
  4. Выводим нужные столбцы

Результат: id | name | pos_title 1 | Иван | Аналитик 2 | Мария | Менеджер

Другие типы JOIN

LEFT JOIN: все сотрудники, даже без должности (результат NULL)

RIGHT JOIN: все должности, даже без сотрудников (результат NULL)

Читать полностью ->
Кейс: Анализ снижения продаж
3.0 Senior🔥 23💬 1

Решение

Пошаговый план анализа снижения продаж на 15%

Снижение продаж на 15% может быть вызвано множеством факторов. Проведу систематический анализ для выявления корневых причин.


1. Требуемые данные

Основные таблицы:

  • orders — id, date, amount, status, customer_id, product_id
  • products — id, name, category, price, supplier_id
  • customers — id, region, acquisition_date, segment
  • order_items — order_id, product_id, quantity, price
  • traffic — date, source, sessions, orders
  • inventory — product_id, stock_quantity, date
  • marketing — date, channel, budget, impressions, clicks

Дополнительные источники:

  • Внешние события (конкурентная активность, маркетинг)
  • Сезонные индексы
  • Рыночные данные

2. Основные гипотезы для проверки

Гипотеза 1: Снижение трафика и конверсии

Читать полностью ->
Средняя зарплата по отделу
1.0 Junior🔥 20💬 1

Решение

Задача требует добавления столбца с средней зарплатой по отделу используя оконные функции.

SQL-запрос

SELECT 
  depname,
  empno,
  salary,
  ROUND(
    AVG(salary) OVER (PARTITION BY depname)::NUMERIC,
    2
  ) AS avg_salary_dept
FROM salaries
ORDER BY depname, empno;

Пошаговое объяснение

1. SELECT столбцы

  • depname — название отдела
  • empno — номер сотрудника
  • salary — зарплата
  • ROUND(..., 2) — округляем среднее до 2 знаков

2. AVG(salary) OVER (PARTITION BY depname)

  • Оконная функция AVG()
  • PARTITION BY depname — считаем среднее отдельно для каждого отдела
  • Результат: для каждой строки выводим среднюю зарплату её отдела

3. ::NUMERIC для корректного деления

  • Гарантирует правильное вычисление среднего

4. ORDER BY depname, empno

  • Упорядочиваем результат по отделу и номеру сотрудника

Пример выполнения

Читать полностью ->
Потерянные пользователи (Churned Users)
2.0 Middle🔥 20💬 1

Решение

Задача требует подсчета пользователей, которые ушли из сервиса (churn) — были активны в предыдущем месяце, но перестали логиниться в текущем месяце.

Подход к решению

Логика:

  1. Определить уникальных пользователей, активных в каждом месяце
  2. Для каждого месяца найти пользователей, которые были в предыдущем месяце
  3. Проверить, отсутствуют ли они в текущем месяце
  4. Подсчитать таких пользователей

Вариант 1: С использованием самообъединения (Self-Join)

Читать полностью ->
Анализ A/B теста
3.0 Senior🔥 20💬 1

Решение

A/B тест: Анализ статистической значимости

Провожу полный статистический анализ А/В теста с использованием z-теста для сравнения пропорций.


1. Вычисление конверсии для каждой группы

Контрольная группа (A):

  • Пользователей: n₁ = 10,000
  • Конверсий: x₁ = 320
  • Конверсия: p₁ = 320 / 10,000 = 0.032 = 3.2%

Тестовая группа (B):

  • Пользователей: n₂ = 10,000
  • Конверсий: x₂ = 368
  • Конверсия: p₂ = 368 / 10,000 = 0.0368 = 3.68%

Абсолютная разница (Lift):

  • Δp = p₂ - p₁ = 0.0368 - 0.032 = 0.0048 = 0.48 процентных пункта

Относительное улучшение:

  • Relative Lift = (p₂ - p₁) / p₁ × 100% = 0.0048 / 0.032 × 100% = 15%

Тестовая группа показывает на 15% лучше конверсию, чем контрольная.


2. Проверка статистической значимости (Z-тест)

Гипотезы:

  • H₀ (нулевая): p₁ = p₂ (между группами нет разницы)
  • H₁ (альтернативная): p₁ ≠ p₂ (между группами есть разница)
  • Уровень значимости: α = 0.05 (двусторонний тест)
Читать полностью ->
Удержание пользователей по месяцам (Retention)
2.0 Middle🔥 20💬 1

Решение

Задача требует подсчета удержанных пользователей (retained users) — тех, кто авторизовался и в текущем, и в предыдущем месяце. Это классический метрический расчёт для оценки retention rate.

Подход к решению

Этап 1: Определяем уникальных пользователей по месяцам.

Этап 2: Для каждого месяца находим пересечение пользователей с предыдущим месяцем.

Этап 3: Подсчитываем количество удержанных пользователей.

Вариант 1: С использованием self-join (самообъединение)

WITH monthly_users AS (
  SELECT 
    DATE_TRUNC('month', login_date) AS month,
    user_id
  FROM logins
  GROUP BY DATE_TRUNC('month', login_date), user_id
)
SELECT 
  curr.month,
  COUNT(DISTINCT curr.user_id) AS retained_users
FROM monthly_users curr
INNER JOIN monthly_users prev 
  ON curr.user_id = prev.user_id 
  AND curr.month = prev.month + INTERVAL '1 month'
GROUP BY curr.month
ORDER BY curr.month;

Вариант 2: С использованием оконных функций (более эффективный)

Читать полностью ->
Процентное изменение MAU месяц к месяцу
2.2 Middle🔥 19💬 1

Решение

Задача требует расчёта ежемесячного процентного изменения MAU (Monthly Active Users). Это ключевая метрика для отслеживания роста пользовательской базы.

Подход к решению

Логика:

  1. Вычислить MAU для каждого месяца (количество уникальных пользователей)
  2. Получить MAU из предыдущего месяца с помощью LAG()
  3. Рассчитать процентное изменение: ((текущий - предыдущий) / предыдущий) * 100
  4. Для первого месяца результат будет NULL

SQL-запрос

WITH monthly_mau AS (
  SELECT 
    DATE_TRUNC('month', date) AS month,
    COUNT(DISTINCT user_id) AS mau
  FROM logins
  GROUP BY DATE_TRUNC('month', date)
)
SELECT 
  TO_CHAR(month, 'YYYY-MM') AS month,
  mau,
  ROUND(
    (
      (mau - LAG(mau) OVER (ORDER BY month)) / 
      LAG(mau) OVER (ORDER BY month) * 100
    )::NUMERIC,
    2
  ) AS mau_change_pct
FROM monthly_mau
ORDER BY month ASC;

Пошаговое объяснение

Читать полностью ->
Процент дозвона по дням
2.0 Middle🔥 19💬 1

Решение

Задача требует расчета процента успешных звонков (дозвонов) для каждого дня в указанном периоде. Необходимо вычислить долю звонков с флагом dozv_flg = 1 от общего количества звонков в день.

Подход к решению

Этап 1: Группируем звонки по дате.

Этап 2: Подсчитываем общее количество звонков в день.

Этап 3: Подсчитываем количество успешных звонков (dozv_flg = 1).

Этап 4: Рассчитываем процент успешности.

SQL-запрос

SELECT 
  call_date,
  COUNT(*) AS call_count,
  SUM(dozv_flg) AS success_count,
  ROUND(
    CAST(SUM(dozv_flg) AS NUMERIC) / COUNT(*) * 100, 
    2
  ) AS success_rate
FROM calls
WHERE call_date >= '2024-10-01'
GROUP BY call_date
ORDER BY call_date ASC;

Пошаговое объяснение

Читать полностью ->
Расчёт MAU (Monthly Active Users)
2.2 Middle🔥 19💬 1

Решение

MAU (Monthly Active Users) — это ключевая метрика для анализа активности приложения. Задача состоит из двух частей: сначала найти уникальных пользователей за каждый месяц, а затем вычислить среднее значение по всем месяцам.

Подход к решению

Этап 1: Группируем данные по месяцам и считаем уникальных пользователей в каждом месяце.

Этап 2: Вычисляем среднее значение MAU по всем месяцам.

SQL-запрос

WITH monthly_users AS (
  SELECT 
    DATE_TRUNC(month, activity_date) AS month,
    COUNT(DISTINCT client_id) AS mau
  FROM clients
  GROUP BY DATE_TRUNC(month, activity_date)
)
SELECT 
  ROUND(AVG(mau)::NUMERIC, 2) AS average_mau
FROM monthly_users;

Альтернативный вариант (для MySQL/SQLite)

Если используется MySQL или SQLite, можно применить функцию DATE_FORMAT:

Читать полностью ->
Клиенты, покупающие один продукт, но не другой
2.2 Middle🔥 18💬 1

Решение

Задача требует нахождения клиентов, которые совершили определённую покупку (Gel), но не совершили другую (Soap). Это классическая задача на использование подзапросов и логики исключения. Существует несколько эффективных подходов.

Вариант 1: NOT EXISTS (рекомендуемый)

SELECT DISTINCT
  c.customer_id,
  c.name
FROM customers c
WHERE EXISTS (
  SELECT 1 FROM purchases p
  WHERE p.customer_id = c.customer_id AND p.product = 'Gel'
)
AND NOT EXISTS (
  SELECT 1 FROM purchases p
  WHERE p.customer_id = c.customer_id AND p.product = 'Soap'
);

Вариант 2: С использованием подзапросов IN/NOT IN

SELECT 
  c.customer_id,
  c.name
FROM customers c
WHERE c.customer_id IN (
  SELECT DISTINCT customer_id FROM purchases WHERE product = 'Gel'
)
AND c.customer_id NOT IN (
  SELECT DISTINCT customer_id FROM purchases WHERE product = 'Soap'
);

Вариант 3: С GROUP BY и HAVING

Читать полностью ->
Поиск сотрудника с максимальной зарплатой в отделе
1.6 Junior🔥 18💬 1

Решение

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

Подход к решению

Оконные функции RANK() или DENSE_RANK() позволяют ранжировать зарплаты внутри каждого отдела и выбрать те, которые имеют ранг 1 (максимальные).

SQL-запрос (рекомендуемый вариант)

WITH ranked_salaries AS (
  SELECT 
    depname,
    empno,
    salary,
    RANK() OVER (PARTITION BY depname ORDER BY salary DESC) AS rank
  FROM salaries
)
SELECT 
  depname,
  empno,
  salary
FROM ranked_salaries
WHERE rank = 1
ORDER BY depname, empno;

Пошаговое объяснение

1. CTE ranked_salaries

  • PARTITION BY depname — окно считается отдельно для каждого отдела
  • ORDER BY salary DESC — сортируем по зарплате в убывающем порядке
  • RANK() — присваивает ранг (при одинаковых значениях ранг одинаковый, следующий скачет)
Читать полностью ->
Нарастающий итог денежного потока
2.2 Middle🔥 15💬 1

Решение

Задача требует расчета нарастающей суммы (cumulative sum) денежного потока с использованием оконных функций SQL. Это классический пример применения функции SUM() в окне с рамкой UNBOUNDED PRECEDING.

Подход к решению

Оконная функция SUM() OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) вычисляет сумму от первой строки до текущей для каждого дня.

SQL-запрос

SELECT 
  date,
  cash_flow,
  SUM(cash_flow) OVER (
    ORDER BY date 
    ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
  ) AS cumulative_cf
FROM transactions
ORDER BY date ASC;

Пошаговое объяснение

1. Оконная функция SUM()

  • Считает сумму всех значений cash_flow от начала до текущей строки
  • OVER (ORDER BY date ...) — определяет окно (partition) для функции
Читать полностью ->
7-дневное скользящее среднее регистраций
2.0 Middle🔥 14💬 1

Решение

Задача требует вычисления 7-дневного скользящего среднего (moving average) для ежедневных регистраций с использованием оконных функций SQL. Скользящее среднее — это среднее значение за последние N дней, включая текущий день.

Подход к решению

Оконная функция AVG() OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) вычисляет среднее за 7 дней (текущий + 6 предыдущих).

SQL-запрос

SELECT 
  date,
  sign_ups,
  ROUND(
    AVG(sign_ups) OVER (
      ORDER BY date 
      ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    )::NUMERIC,
    2
  ) AS moving_avg_7d
FROM signups
ORDER BY date ASC;

Пошаговое объяснение

1. Оконная функция AVG()

  • Считает среднее значение sign_ups за последние 7 дней
  • OVER (ORDER BY date ...) — определяет окно функции
Читать полностью ->
Пары штатов с похожими потоками
2.0 Middle🔥 13💬 1

Решение

Задача требует нахождения пар штатов с похожей популярностью потоков. Ключевой момент — исключить дубликаты пар и пары с самим собой.

Подход к решению

Логика:

  1. Использовать CROSS JOIN для создания всех возможных комбинаций штатов
  2. Отфильтровать пары, где разница потоков <= 1000
  3. Исключить пары штата с самим собой
  4. Исключить дубликаты пар (использовать упорядочение по названию)

SQL-запрос (основной вариант)

SELECT 
  s1.state AS state1,
  s2.state AS state2,
  s1.total_streams AS streams1,
  s2.total_streams AS streams2,
  ABS(s1.total_streams - s2.total_streams) AS streams_diff
FROM state_streams s1
CROSS JOIN state_streams s2
WHERE s1.state < s2.state  -- Исключает дубликаты и пары с самим собой
  AND ABS(s1.total_streams - s2.total_streams) <= 1000
ORDER BY s1.state, s2.state;

Пошаговое объяснение

Читать полностью ->
Классификация пользователей по классам
2.0 Middle🔥 10💬 1

Решение

Задача требует условной классификации пользователей с приоритетом: если пользователь имеет записи обоих классов, его нужно отнести только к классу "b".

Подход к решению

Логика:

  1. Для каждого пользователя определить, какие классы у него есть
  2. Применить правило приоритета ("b" имеет приоритет над "a")
  3. Подсчитать пользователей по итоговому классу

SQL-запрос (основной вариант)

Читать полностью ->
Время отклика на письма
3.0 Senior🔥 9💬 1

Решение

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

Подход к решению

Логика:

  1. Найти все письма, полученные на support@company.com
  2. Для каждого письма найти первый ответ (письмо от support обратно отправителю)
  3. Вычислить разницу во времени в минутах
  4. Учитывать только первый ответ

SQL-запрос (рекомендуемый вариант)

Читать полностью ->
Анализ данных о звонках
3.0 Senior🔥 8💬 1

Решение

Анализ данных о длительности звонков в call-центре

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


1. План анализа

Этап 1: Описательная статистика

SELECT 
  COUNT(*) AS total_calls,
  MIN(duration_seconds) AS min_duration,
  MAX(duration_seconds) AS max_duration,
  AVG(duration_seconds)::INT AS avg_duration,
  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration_seconds) AS median_duration,
  PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY duration_seconds) AS q1,
  PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY duration_seconds) AS q3,
  STDDEV(duration_seconds)::INT AS std_dev,
  VARIANCE(duration_seconds)::INT AS variance,
  SKEWNESS(duration_seconds) AS skewness,
  KURTOSIS(duration_seconds) AS kurtosis
FROM calls;
Читать полностью ->
Гистограмма длительности сессий
2.3 Middle🔥 7💬 1

Решение

Задача требует создания гистограммы — распределения сессий по интервалам длительности. Это типичная задача для анализа пользовательского поведения и создания визуализаций.

Подход к решению

Логика:

  1. Определить нижнюю границу интервала для каждой сессии (floor to nearest 5)
  2. Создать метку интервала вида "X-Y"
  3. Сгруппировать по интервалам и подсчитать количество сессий
  4. Отсортировать по нижней границе

SQL-запрос (основной вариант)

WITH bucketed_sessions AS (
  SELECT 
    session_id,
    length_seconds,
    (FLOOR(length_seconds / 5.0) * 5)::INT AS bucket_start,
    (FLOOR(length_seconds / 5.0) * 5 + 5)::INT AS bucket_end
  FROM sessions
)
SELECT 
  bucket_start || '-' || bucket_end AS bucket,
  COUNT(*) AS session_count
FROM bucketed_sessions
GROUP BY bucket_start, bucket_end
ORDER BY bucket_start ASC;

Пошаговое объяснение

Читать полностью ->
Маркировка узлов древовидной структуры
2.0 Middle🔥 6💬 1

Решение

Задача требует классификации узлов дерева по их типам (Root, Leaf, Inner). Это типичная задача анализа иерархических структур, часто встречается при работе с каталогами, организационными структурами и ситемами меню.

Подход к решению

Логика классификации:

  • Root — узел без родителя (parent IS NULL)
  • Leaf — узел без потомков (отсутствует в столбце parent других строк)
  • Inner — узел с потомками и есть родитель (противоположность Root и Leaf)

SQL-запрос

SELECT 
  t.node,
  CASE 
    WHEN t.parent IS NULL THEN 'Root'
    WHEN NOT EXISTS (SELECT 1 FROM tree WHERE parent = t.node) THEN 'Leaf'
    ELSE 'Inner'
  END AS type
FROM tree t
ORDER BY t.node;

Пошаговое объяснение

1. Условие для Root

  • WHEN t.parent IS NULL — проверяем, есть ли родитель
  • Если родителя нет, это корневой узел
Читать полностью ->