Код 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 г.