Код Python для идентификации сезонных клиентов

Ранее я писал о том, как я использовал временные ряды для выявления клиентов в нашей базе данных с сезонными моделями использования. Если вы хотите, чтобы он был полностью исчерпан, вы можете найти статью здесь.

Если вы хотите, чтобы история была короткой: чтобы выявить клиентов с сезонными моделями использования, я взял данные о customer_id, month, year, и данные об использовании, агрегированные по месяцам, отсортированные по id, году и месяцу. Мы можем только оценить сезонность клиентов, которые работают с нами не менее 2 лет, чтобы алгоритм мог определить закономерность. Итак, данные (полностью составленные, просто для иллюстрации) выглядели так:

Вы заметите, что в этих данных отсутствуют май, август, сентябрь и октябрь. Это означает, что этот клиент не пользовался услугами в те месяцы. Итак, первой задачей было заполнить недостающие нули в наборе данных. Я извлек данные из базы данных и назвал их «оригинальными». Я не привел здесь код для подключения к базе данных, потому что мы используем Netezza SQL.

Код ниже (некоторые отступы неверны для длинных строк, извинения!) 🙂

import pandas as pa 
import numpy as np 
## Outfile is just because we're going to export our results as a .csv to save them. 
outfile = '[put your file path here].csv' 
## Headings in the .csv that I'm going to output 
filledIn = pa.DataFrame(columns=['customer_id','yr','mnth','usage']) 
##original was just the name of my dataframe with data 
grouped = original.groupby(by='customer_id') 
def yearmonth_to_justmonth(year, month): 
     return year * 12 + month - 1 
##Defining a function to fill in the zeros. 
def fillInForOwner(group): 
     min = group.head(1).iloc[0] 
     max = group.tail(1).iloc[0]       
     minMonths = yearmonth_to_justmonth(min.yr, min.mnth) 
     maxMonths = yearmonth_to_justmonth(max.yr, max.mnth)
     filled_index = pa.Index(np.arange(minMonths, maxMonths, 1),
       name="filled_months") 
     group['months'] = group.yr * 12 + group.mnth - 1 
     group = group.set_index('months') 
     group = group.reindex(filled_index) 
     group.customer_id = min.customer_id 
     group.yr = group.index // 12 group.mnth = group.index % 12 + 1
     group.usage = np.where(group.usage.isnull(), 0,
       group.usage).astype(int) 
     return group 
filledIn = grouped.apply(fillInForOwner) 
newIndex = pa.Index(np.arange(filledIn.customer_id.count())) 
## Printing out the results to a .csv 
filledIn = filledIn.set_index(newIndex) 
filledIn.to_csv(outfile)  
## I also print results on the screen 
print(filledIn)

Затем я решил запустить R на Python для фрагмента временного ряда. Я действительно столкнулся с небольшой икотой, о которой стоит упомянуть. На моем компьютере были установлены 64- и 32-разрядные версии R, и я указывал на версию R (из Python), в которой еще не были установлены все мои пакеты. Смущающая правда: я вручную скопировал и вставил файлы из одной папки в другую. Я уверен, что есть более «правильный» способ сделать это, но копирование и вставка их в правильную папку в каталоге работает.

Еще одна проблема, также стоит отметить, что запуск кода R на Python был не первым методом, который я попробовал. На самом деле вы можете использовать язык Python для запуска кода R, но я изо всех сил пытался заставить его работать, поэтому я пошел по маршруту rpy2.

Здесь я импортирую rpy2, чтобы использовать код R в Python. Это также все необходимые мне библиотеки.

import rpy2 as r 
     from rpy2.robjects.packages import importr 
     from rpy2.robjects import r, pandas2ri, globalenv
     pandas2ri.activate() 
base = importr('base') 
colorspace = importr('colorspace') 
forecast = importr('forecast') 
times = importr('timeSeries') 
stats = importr('stats')

Затем я просмотрел каждого покупателя. Я нашел их месяц / год начала и месяц / год окончания и создал объект временного ряда для каждого клиента. Я включил оператор try / except, потому что изначально я пытался запустить его без оператора try / except, и он подавился клиентом после того, как он проработал пару часов, что не идеально.

После создания объекта временного ряда я просто спрашиваю R, есть ли в модели сезонный компонент, и распечатываю идентификатор клиента и были ли они сезонными.

##Again, this is just for saving your results to a .csv 
outfile = '[put your file path here].csv' 
seasonal_output = pa.DataFrame(columns=['customer_id', 'seasonal']) 
### Now we're going to loop through our customers 
for customerid, dataForCustomer in filledIn.groupby(by=['customer_id']): 
     startYear = dataForCustomer.head(1).iloc[0].yr 
     startMonth = dataForCustomer.head(1).iloc[0].mnth 
     endYear = dataForCustomer.tail(1).iloc[0].yr 
     endMonth = dataForCustomer.tail(1).iloc[0].mnth 
#Creating a time series object 
     customerTS = stats.ts(dataForOwner.SENDS.astype(int),
          start=base.c(startYear,startMonth), 
          end=base.c(endYear, endMonth), frequency=12)
     r.assign('customerTS', customerTS) 
##Here comes the R code piece 
     try:  
          seasonal = r(''' 
          fit<-tbats(customerTS, seasonal.periods = 12, use.parallel
          = TRUE) 
          fit$seasonal 
          ''') 
     except: 
          seasonal = 1 
     seasonal_output = seasonal_output.append({'customer_id':
          customerid, 'seasonal': seasonal}, ignore_index=True) 
     print(f' {customerid} | {seasonal} ') 
print(seasonal_output) 
seasonal_output.to_csv(outfile)

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

Если вы хотите подписаться на мои будущие статьи, вы можете подписаться здесь.

Первоначально опубликовано на сайте datamovesme.com 1 июля 2018 г.