День 7. Обогащаем данные. Сегментируем клиентскую базу по активности.

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

Как производится обогащение данных

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

Особенность таких полей в том, что их нет в исходных данных, и часто их создание требует манипуляций с данными нескольких таблиц. Поэтому для этих задач используются так называемые ETL-инструменты (Extract - Transform - Load). Они позволяют взять данные из нескольких источников, трансформировать их, и предоставить BI-системе для загрузки.
Visiology полагается на использование внешних ETL-инструментов. Вы можете использовать любую систему, которая позволяет загружать и преобразовывать данные, а потом сохранять их в месте, откуда их сможет прочитать Visiology.

Однако, сегодня мы рассмотрим пример с применением ViXtract - это библиотека преднастроенных компонентов и библиотек обработки данных на Python в формате Open Source.

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

Например, есть отличный вариант с российской low-code платформой Loginom. Кроме того, вскоре мы анонсируем эфир о том, как использовать ChatGPT для упрощения написания кода на Python для пользователей с нулевым опытом. Так что не пропустите анонс в чате!

Задача по обогащению

Итак, что же мы будем обогащать? У нас есть аналитика продаж по клиентам. Мы, конечно же, можем посмотреть статистику по каждому клиенту, какая у него динамика роста/падения. Но вообще-то, работа с данными должна побуждать нас к совершению проактивных действий, а не просто к осознанию “как оно было”.

Одним из сценариев работы клиентской базы является отслеживание ее “живости”. А именно - мониторинг, кто из клиентов перестал покупать, с целью установления менеджером по продажам своевременного контакта, реанимации клиента и сокращению оттока.
РАЗРАБОТКА СКРИПТА ПО ОБОГАЩЕНИЮ ДАННЫХ
Вам откроется чистый лист проекта. Сюда мы будем писать команды для Python.

✅Можете переименовать проект, например, в Sprint 2023 + ваши инициалы.


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

✅Вначале нам нужно импортировать библиотеки, которые будут выполнять необходимые преобразования данных.
Код для их загрузки:

import pandas as pd #преобразование данных

import sqlalchemy as db #соединение с БД

import numpy as np #поиск максимальной даты

import datetime as dt #экспорт необходимых библиотек, преобразование дат

import pickle #глобальная переменная
✅Вставьте его в текстовую область, вот так:

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

✅Нажмите на кнопку Play, чтобы выполнить блок.


Пока блок выполняется, в нем будет находиться символ “*”. Когда блок выполнится, в нем появится число. Это значит, что можно создавать новый блок, и задействовать результаты выполнения предыдущего блока.

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

with open('connection.pkl', 'rb') as f:
    connection = pickle.load(f) # чтение переменной
✅Используя переменную connection подключимся к базе данных:

connection = db.create_engine(connection) #подключение к БД
Наша задача на обогащение данных такова:
Создать для идентификаторов клиентов следующие аналитические признаки: сколько дней прошло с последней продажи, классифицировать это по риску оттока, а также понять, когда мы последний раз звонили клиенту.

✅Для этого нам потребуется работать с таблицей продаж и таблицей звонков. Создадим еще 2 переменных, которые будут содержать запросы к таблицам продаж и звонков.

query_leads = "SELECT \"Leads\".* FROM \"Leads\"" #создаем переменные с запросом формирование запроса к таблице Leads, слэши - экранирование кавычек
query_calls = "SELECT \"Calls\".* FROM \"Calls\"" #формирование запроса к таблице Calls

✅Теперь, когда у нас есть переменная с настройками подключения к БД, и переменные с SQL-запросами, мы можем выполнить запросы к этим таблицам, получив переменные с соответствующими дата-фреймами (иначе говоря, переменные, содержащие структурированные данные - таблицы).

Leads=pd.DataFrame(connection.execute(query_leads).fetchall()) #выполнение запроса к таблице Leads, переменная содержит DF таблицы
Calls=pd.DataFrame(connection.execute(query_calls).fetchall()) #выполнение запроса к таблице Calls

На текущий момент ваш скрипт должен выглядеть примерно так:




Кстати, после выполнения запросов вы можете написать название переменной, например Leads, и увидеть, какие данные в ней содержатся.



Используйте вот эту конструкцию, чтобы получить список клиентов с максимальной датой покупки. Если помните, то у нас данные в Leads разбиты на события: дата создания и дата закрытия. Нам нужно взять дату закрытия у сделок, которые завершились успешно (имеют статус “Win”).

✅Вот такой запрос должен получиться. Определяем переменную, содержащую перечень фильтруемых значений для поля статус. Делаем фильтрацию, а после агрегируем максимальную дату по полю Client_ID.

options_status = ['Win'] 
Leads = Leads[Leads['Status'].isin(options_status)] #фильтрация поля Status по значению Win
Leads= Leads.groupby(['Client_ID']) #группировка по Client_ID
Leads = Leads.agg(Maximum_Date=('Close_date', np.max)) #поиск максимальной даты в группировке

✅Следующий блок посчитает нам, сколько дней прошло с последней продажи, и сгруппирует эти дни по уровню активности клиента.

Leads['current_date'] = pd.to_datetime(dt.datetime.now().date()) #добавление столбца с текущей датой
Leads['diff'] = Leads['current_date'] - Leads['Maximum_Date'] #вычисление разницы между текущей даты и максимальной даты
Leads['diff'] = Leads['diff'].astype(np.int64) // 10**9 // 86400 #преобразование типа данных для значения разницы
def klass_func(row):
    if row['diff'] < 30:
        return 'ОК'
    elif row['diff'] >= 30 and row['diff'] < 60:
        return 'Теряем'
    else:
        return 'Потеряли' #условие для параметра
Leads['klass']=Leads.apply(lambda row: klass_func(row), axis=1) #применения условия

Проверьте содержимое переменной Leads. Результат должен быть таким:


✅Повторяем аналогичный набор действий для переменной Calls

Calls = Calls.groupby(['Client_ID']) #группировка по Client_ID
Calls = Calls.agg(Maximum_Date=('Date', np.max)) #поиск максимальной даты в группировке
Calls['current_date'] = pd.to_datetime(dt.datetime.now().date()) #добавление столбца с текущей датой
Calls['diff'] = Calls['current_date'] - Calls['Maximum_Date'] #вычисление разницы между текущей даты и максимальной даты
Calls['diff'] = Calls['diff'].astype(np.int64) // 10**9 // 86400 #преобразование типа данных для значения разницы
Calls['klass'] = ['На связи' if x >=0 and x < 14 else 'Не контактируем' for x in Calls['diff']] #условие для параметра и его применения

Проверяем результат через просмотр переменной:

✅Теперь, нам надо соединить эти таблицы в одну, чтобы получить единый набор признаков.

Leads_calls = pd.merge(Leads, Calls, on='Client_ID', how='left') #объединение таблиц

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

Leads_calls.to_csv('Leads_calls.csv', index=True) #экспорт итоговой таблицы в CSV
Итоговый файл появится в боковой панели. ✅Скачайте его оттуда, он понадобится для выполнения домашнего задания.

ЗАКЛЮЧЕНИЕ

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

Евгений Стучалкин
BI2BUSINESS

Домашнее задание

  • 1
    Загрузите полученный CSV файл в ViQube через загрузчик с названием “Параметры клиентов” (настройки формата: разделитель - запятая, двойные кавычки).
  • 2
    Свяжите поле Client_ID с измерением Клиенты.
  • 3
    Переименуйте поля следующим образом:
    Maximum_Date_x - Дата последней продажи;
    current_date_x - не загружаем;
    diff_x - Дней с последней продажи;
    klass_x - Cтатус клиента;
    Maximum_Date_y - Дата последнего дозвона
    current_date_y - не загружаем;
    diff y - Дней с последнего дозвона;
    klass_y - Статус коммуникаций.
  • 4
    Все эти поля также добавьте в измерение Клиент. Сохраните изменения и загрузите данные.
  • 5
    Добавьте фильтр Статус клиента и Статус коммуникаций на лист Динамика по клиентам. Отфильтруйте клиентов в статусе Теряем, по которым нет коммуникации.

ЗАДАЧА СО ЗВёздочкой

✅Cоздайте лист с графиком, который наглядно покажет, какая доля выручки приходится на живых, теряемых и потерянных клиентов.

✅Напишите в чат, какие еще сценарии обогащения данных приходят вам в голову.

Помните, что для самых активных участников чата заготовлены призы от Visiology! ;)