Назад к материалам

Видео + статья / Собеседования

Собеседование Senior Data Scientist в Райффайзен Банке: вопросы и ответы

Разбираем собеседование Senior Data Scientist: разреженные векторы, сложность алгоритма, SQL, инкассация, временные ряды и проверка модели.

Компания
Райффайзен Банк
Опубликовано
Обновлено
Видео вышло
Видео
10:41
Текст
7 минут

Если ролик не загружается, выберите другую площадку.

Смотреть на

Перед нами техническое собеседование на позицию Senior Data Scientist в Райффайзен Банке. Вопросы охватывают Python, алгоритмы, SQL, временные ряды и бизнес-мышление. Личные данные участников мы не используем: нас интересуют сами задачи и то, как на них отвечать.

Что проверяли на собеседовании?

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

Такой набор заданий хорошо показывает ожидания от Senior Data Scientist. Интервьюеру важны код и теория, но ещё важнее ход мысли: какие данные нужны, сколько стоит ошибка, как проверить модель и что должно произойти после ноутбука с экспериментом.

Как посчитать скалярное произведение разреженных векторов?

Короткий ответ: хранить только пары «индекс — значение», отсортировать их по индексу и пройти по двум последовательностям двумя указателями.

Разреженный вектор содержит много нулей. Хранить каждый ноль и перемножать все позиции невыгодно. Если в первом векторе n ненулевых элементов, а во втором m, двойной цикл потребует до n × m сравнений. Два указателя уменьшают сложность до O(n + m).

Пример кодаPython
def sparse_dot(
    left: list[tuple[int, float]],
    right: list[tuple[int, float]],
) -> float:
    # Оба списка содержат пары (индекс, значение)
    # и заранее отсортированы по индексу.
    left_pos = 0
    right_pos = 0
    result = 0.0

    while left_pos < len(left) and right_pos < len(right):
        left_index, left_value = left[left_pos]
        right_index, right_value = right[right_pos]

        if left_index == right_index:
            # Вклад в произведение есть только у одинаковых индексов.
            result += left_value * right_value
            left_pos += 1
            right_pos += 1
        elif left_index < right_index:
            # Текущий индекс слева уже не встретится справа.
            left_pos += 1
        else:
            # Текущий индекс справа уже не встретится слева.
            right_pos += 1

    return result


# Нулевые позиции не храним: 3 × 4 + 2 × 5 = 22.
print(sparse_dot([(1, 3), (7, 2)], [(1, 4), (7, 5)]))

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

Как улучшить решение при очень разных размерах векторов?

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

Если в одном векторе два ненулевых элемента, а в другом тысяча, полный проход по обоим всё ещё работает, но может быть избыточным. Для короткого списка длины n и длинного длины m бинарный поиск даст сложность около O(n log m). На собеседовании полезно сравнить оба варианта и сказать, при каком распределении данных каждый из них выгоднее.

Что нужно добавить перед выпуском кода в продакшен?

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

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

На Senior-собеседовании стоит упомянуть профилирование. «Быстрее по асимптотике» ещё не означает «быстрее на наших данных»: сравните реализации на типичных и предельных размерах.

Как правильно рассуждать об оптимизации SQL?

Короткий ответ: сначала проверить корректность запроса, затем посмотреть EXPLAIN, объём промежуточных данных, условия соединения и подходящие индексы.

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

Также проверяем тип соединения. Для INNER JOIN перенос условия обычно не меняет смысл, а с LEFT JOIN неосторожный фильтр может изменить результат. После этого смотрим индексы по ключам соединения и селективным условиям.

Как решить бизнес-кейс с инкассацией?

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

Цель нельзя свести к минимальной MAE. Банку важны деньги. Мы бы построили симулятор процесса на историческом периоде и сравнили новую стратегию с текущей: сколько рейсов потребовалось, сколько стоила перевозка, сколько денег лежало без движения и сколько раз возникал дефицит.

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

Как учесть разную цену ошибок и выбросы?

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

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

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

Как валидировать модель и провести эксперимент?

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

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

Схема TimeSeriesSplit: обучающая выборка расширяется, а тестовый период остаётся в будущем

Синим показано обучение, красным — проверка на следующем периоде. Источник: пример scikit-learn, лицензия BSD-3-Clause.

Перед экспериментом фиксируем основную метрику, допустимый риск дефицита, минимальный полезный эффект, уровень значимости и мощность теста. Затем по историческим данным оцениваем необходимое число отделений и длительность проверки. Важен не сам факт «A/B-теста», а возможность принять решение по заранее заданным правилам.

Получил ли кандидат оффер?

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

Мы бы оценили выступление как сильное для Middle и недостаточно самостоятельное для Senior. Это не провал знаний, а точный список зон роста: проговаривать сложность сразу, связывать метрики с деньгами и доводить решение до плана внедрения.

Источники

частые вопросы

Короткие ответы

Что спрашивают на собеседовании Senior Data Scientist?

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

Как посчитать скалярное произведение разреженных векторов?

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

Как проверять модель на временных данных?

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

Получил ли кандидат оффер в Райффайзен Банк?

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

следующий шаг

Отработайте вопросы Data Science до реального интервью

Тренажёр собеседований ЮНИКОД помогает повторить теорию, найти пробелы и научиться давать короткие точные ответы.

Перейти в тренажёр →