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

Прежде чем вы попытаетесь вникнуть в это объяснение, позвольте мне показать вам результаты, чтобы вы увидели ценность прочтения оставшейся части. На следующей диаграмме показано время выполнения для обработки данных за X дней (я объясню, как выглядят данные в следующем разделе), и какой метод мы использовали для обработки данных. Если вы не знакомы с набором инструментов Pandas Python, я бы порекомендовал изучить его. На данный момент, скажем так, дает нам возможность легко манипулировать большими наборами данных в Python.

Чтобы дать вам общее представление о проблеме, которую мы решаем, позвольте мне сначала показать вам базовую форму набора данных:

Из этого вы можете видеть, что элемент может принадлежать многим разделам, а раздел может содержать много элементов. Что-то, чего нет на этом графике, но также важно иметь в виду, это то, что в данный день есть много предметов.

Допустим, в течение определенного дня было продано 10 экземпляров Item 1. Наша цель — попытаться спрогнозировать распределение продаж по разделам, к которым он относится. Например, мы можем предположить, что 6 экземпляров были проданы в разделе A, 3 — в разделе B и 1 — в разделе C.

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

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

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

Теперь о его реализации. Поскольку наша команда хорошо разбирается в Python, а Pandas — наш хлеб с маслом, мы решили использовать его. (Ха-ха каламбуры с маслом)

Реализация выглядела следующим образом:

Потрясающий. У нас есть исходный рабочий код. Чтобы дать представление о размере данных, с которыми мы имеем дело, мы обрабатываем около 60 млн строк за последние 15 дней. Кроме того, кадр данных веса устарел, поскольку конкретный вес для секции может (и, вероятно, должен) немного меняться каждый день.

Мы запустили это и измерили одну метрику — время обработки 10 000 строк. Эта реализация обработала 10 тыс. строк за 30 минут или чуть более 3 месяцев для всего набора данных.

Теперь, учитывая SLA с нашим клиентом и время получения данных в системе, у нас было максимум 4 часа. Это явно не сработает.

После некоторого построчного анализа времени выполнения мы увидели, что поиск df_row занял 0,5 с. Немного покопавшись в выборке Pandas, мы пришли к выводу, что он выполняет полное сканирование фрейма данных по 60 миллионам строк. Таким образом, время поиска имело смысл и явно было узким местом.

Следующим планом действий было устранение этого узкого места. Итак, мы добавили столбец в фреймворк данных через

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

Это приводит к теоретическому ускорению времени выполнения с O(# строк) до O(log(# строк)).

Первоначальный запуск дал нам скорость 1 секунду/10 тыс. строк или около 1,5 часов для всех 60 млн строк. В этот момент мы могли бы собраться и пойти домой. Технически мы уложились в сроки SLA.

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

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

Отлично, теперь мы теоретически можем развернуть один из этих причудливых 16-ядерных серверов с 122 ГБ ОЗУ на AWS и обработать все это менее чем за 15 минут!

Мы так и сделали. Мы выделили ядро ​​для каждой даты и сказали ей бежать так быстро, как она может. Результаты: 3 минуты/10 тыс. строк или 10 часов для всего набора данных. Первоначально это не имело смысла, учитывая, что одно ядро ​​работало за 1,5 часа, а также тот факт, что в нашем локальном тестировании мы достигли 1 с / 10 000 строк с распараллеливанием. Как мы вскоре выяснили, нам нужна пара свободных ядер только для целей ввода-вывода и управления остальной частью системы.

Когда мы освободили 2 ядра, мир снова воцарился. Затем мы достигли 1 с/10 тыс. строк, на которые рассчитывали, и смогли обработать все строки за 10 минут.

Тем не менее, помните эти веса? Да, этим парням по-прежнему требовался час на обработку, и мы не нашли способа ускорить это. Таким образом, время обработки составило 1 час 10 минут. Еще не самый большой. У нас было предчувствие, что из-за простоты того, как мы рассчитывали веса, это должно быть намного быстрее. Мы также думали, что достигли максимальной скорости, которую могут достичь Панды.

Итак, после некоторого мозгового штурма, мы решили попробовать перенести его на SQL. Казалось, что это соответствует инструментам, которые предоставил нам SQL, и не выглядело так уж много работы, чтобы сделать это. В течение следующих 3 дней мы портировали его, активно используя оконные функции. Время выполнения вычисления весов с помощью SQL составило около 2 секунд, а вычисление распределения продаж заняло около 37 мс/10 тыс. строк или 1 минуту для 60 млн строк. С этим мы определенно могли бы жить.

В целом, вот некоторые преимущества, которые мы получили от переноса на SQL:

  • Повышенная читаемость кода
  • Удалено около 100 строк кода
  • Снижение сложности за счет устранения побочных процессов
  • Сэкономлено 120 долл. США в месяц на серверных расходах.
  • Гарантия того, что мы всегда можем выполнить наш SLA

А вот несколько отличных графиков и таблиц, иллюстрирующих преимущества:

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

Если вам понравилось и вы заинтересованы в расширении возможностей предприятий с помощью прогнозной аналитики, напишите мне по адресу [email protected].