Задача для аналитиков: повысить эффективность поиска

С объявлением итогов конкурса.

Материал подготовлен при поддержке сервиса Юла

«Юла» — это сервис, где пользователи выкладывают свои объявления. На нём продаются квартиры и самодельные украшения, готовый бизнес или услуги — практически что угодно.

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

Большинство запросов делается без дополнительных фильтров, но существенная часть пользователей их использует — отсеивают выдачу по цене или расстоянию, например.

Взглянуть, как это работает, можно на сайте или приложениях iOS и Android.

В чём задача

Предложить изменения в поисковой выдаче, чтобы увеличить количество контактов по объявлениям. Контакт — это нажатие на кнопку «Позвонить» или отправка сообщения продавцу в чат.

В архиве — три CSV-таблицы. Это сырые логи запросов пользователей: как они искали объявления, какими фильтрами пользовались и связались ли с продавцами.

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

Расшифровка столбцов в таблицах

Условия и призы

На решение есть две недели: закончим принимать работы 28 октября в 23:59. Оценивать работы будет Егор Данилов, директор по продукту «Юлы». Он определит три лучших решения, прокомментирует свой выбор, а их авторы разделят призовой фонд следующим образом:

  • Первое место — 300 тысяч рублей.
  • Второе место — 200 тысяч рублей.
  • Третье место — 100 тысяч рублей.

Что конкретно делать

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

или
Приём работ завершён
(function(d, w, ver) { var s = d.createElement('script'); s.src = 'https://www.google.com/recaptcha/api.js?' + ver; s.async = true; var container = d.getElementById('ula-form-wrapper'); if (container) { s.onload = function() { var ulaForm = d.querySelector('[data-ula-form]'), ulaFile = d.querySelector('[data-ula-file]'); if (!ulaForm) return false; var ulaInputs = [].slice.call(ulaForm.querySelectorAll('input')).slice(0, 3), ulaSubmitBtn = ulaForm.querySelector('[type="submit"]'); ulaInputs.forEach(function(ulaInput) { ulaInput.addEventListener('change', function(e) { ulaInput.parentElement.classList.remove('is-error'); }); }); ulaFile.addEventListener('change', function(e) { var textSpan = ulaFile.parentElement.children[1].children[0], svgIcon = ulaFile.parentElement.querySelector('svg'); textSpan.innerText = getFileName(ulaFile); if (svgIcon) svgIcon.remove(); }); ulaForm.addEventListener('submit', function(e) { e.preventDefault(); var response = (grecaptcha) ? grecaptcha.getResponse() : ''; if (!response.length) return false; if (ulaSubmitBtn.className.indexOf('is-loading') !== -1) { return false; } if (ulaSubmitBtn.innerText === 'Готово!') { ulaSubmitBtn.innerText = 'Отправить'; return false; } for (let i = 0; i < ulaInputs.length; i++) { ulaInputs[i].parentElement.classList.remove('is-error'); if (ulaInputs[i].value.length === 0) { ulaInputs[i].parentElement.classList.add('is-error'); ulaInputs[i].focus(); return false; } } ulaSubmitBtn.classList.add('is-loading'); var formData = new FormData(ulaForm); Promise.all([wait(), ajax(ulaForm.getAttribute('action'), formData)]).then(function(data) { var res = data[1]; ulaSubmitBtn.classList.remove('is-loading'); if (res && res.rm === 'Success') { ulaSubmitBtn.innerText = 'Готово!'; } else { } }).catch(function() { ulaSubmitBtn.classList.remove('is-loading'); }); }); function ajax(url, data) { return new Promise(function(resolve, reject) { var request = new XMLHttpRequest(); request.open('POST', url, true); request.setRequestHeader('X-This-Is-CSRF', 'THIS IS SPARTA!'); request.onload = function() { if (this.status >= 200 && this.status < 400) { var resp = this.response; resolve(JSON.parse(resp)); } else { reject(); } }; request.onerror = function() { reject(); }; request.send(data); }); } function getFileName(input) { var fullPath = input.value; if (fullPath) { var startIndex = (fullPath.indexOf('\\') >= 0 ? fullPath.lastIndexOf('\\') : fullPath.lastIndexOf('/')); var filename = fullPath.substring(startIndex); if (filename.indexOf('\\') === 0 || filename.indexOf('/') === 0) { filename = filename.substring(1); } return filename.slice(0, ); } } function wait() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(); }, 1000); }); } }; } d.body.appendChild(s); })(document, window, 5);

Помимо изменений, опишите, как будете оценивать результат эксперимента — на какие метрики будете смотреть и почему. Ещё нужно рассказать, что вы предпримите, если эксперимент уменьшит количество контактов по объявлениям.

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

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

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

  • Анализ данных.
  • Умение построить гипотезы на основе данных.
  • Аргументация решения, исходя из данных.
  • Визуализация предложенного решения.
  • Решение на случай, если предложенный эксперимент уменьшит количество контактов.

В результате в конкурсе победили:

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

Несколько комментариев по присланным работам:

  • Задача была для аналитиков. Отличных идей было много, но предпочтение отдавалось решениям, где данные стали основой. Неважно, это анализ датасета или опрос знакомых — приоритет таких работ был выше, чем у построенных на личном опыте.
  • Не все предложили сценарий на случай просадки метрик: часто озвучивался простой откат эксперимента. Но анализ неудачного эксперимента может дать хорошую пищу для дальнейших размышлений.
  • Многие справедливо писали, что ключевая метрика успеха – сделки, а не контакты. Но для анализа влияния на сделки участникам нужно было бы потратить значительно больше времени, поэтому мы упростили задание.
  • Многие уделили внимание технической стороне, но не менее важно понятно защитить свое решение: визуализировать его или логично вывести из анализа данных.

Общий срок проведения конкурса: с 14.10.19 по 18.11.2019 (регистрация участников до 28.10.19). Информация об организаторе, правилах, количестве призов, сроках, месте и порядке их получения — на этой странице.

0
239 комментариев
Написать комментарий...
Anton Tolkachev

Поле search_id - это вроде id сессии?
Если я набирают в поиске "диваны". Получаю запись в таблице searches с search_id. Если я потом уточняю поиск "с доставкой" при сохранении исходного поискового запроса, то запись в таблице searches будет с тем же search_id?

Ответить
Развернуть ветку
Daria Yakovleva

search_id – это уникальный идентификатор запроса, в таблице filter_explore_searches он может выступать как первичный ключ.

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

Ответить
Развернуть ветку
Валерия Смирнова

но почему тогда у одного search_id может быть несколько category_id?
только у 80% search_id 1 категория. у остальных от 2 до 8 (смотрю по таблице views)

Ответить
Развернуть ветку
Александр Казанский

Потому что там есть 2 типа поиска. По дефолту и по категории/тексту.

Вот когда человек ищет по дефолту, тогда в данных это main и в данном типе подразумевается, что пользователь не выбирает категорию.
У одного search_id может быть несколько разных категорий, потому что по дефолту ему выпадает список ближайших и "самых интересных" позиций из разных категорий. И вот когда пользователь сначала открыл и посмотрел iphone, вернулся назад и в том же списке открыл и посмотрел стиральную машину, тогда мы получаем в рамках одного seach_id две разные категории. 

Ответить
Развернуть ветку
Stas Kostenkov

правильно ли мне понятно что default == main - это когда пользователь расматривает предложения с главного экрана, а ввод любого запроса - это default == other

Ответить
Развернуть ветку
Александр Казанский

Я предполагаю что так, там еще есть рубрика "все объявления" я думаю, что она тоже относится к main. Меня смущает другое. @Daria Yakovleva  Я вижу сортировку "по умолчанию", что не означает сортировку по дистанции, т.к. дистанции все таки там идут в разнобой. Почему в выборке только 3 типа сортировки и нет "по умолчанию"?

Ответить
Развернуть ветку
Валерия Смирнова

я подумала что по умолчанию это null
но если сгруппировать запросы по типу сортировки, то 96% идут с сортировкой по дистанции, еще 3% сортировка по дате публикации. очень странно выглядит.

кажется что поиски с главной страницы создаются автоматически при заходе на сайт и там всегда стоит фильтр по умолчанию ( то есть не сортировки)

Ответить
Развернуть ветку
Александр Казанский

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

Ответить
Развернуть ветку
236 комментариев
Раскрывать всегда