我正在关注此博客,以在我的时间序列数据中识别季节性客户: https://www.kristenkehrer.com/seasonality-code
我的代码几乎与博客完全相同,但有一些小的调整,下面是代码。我能够完全为2000位客户运行代码。几个小时后,我的搜索结果将0位客户标记为季节性客户。
我会随着时间的推移手动查看客户数据,我相信我有很多应该被挑选出来的季节性客户的例子。以下是我正在使用的数据示例。
我想念一些愚蠢的东西吗?我对Python还是很陌生,甚至想尝试一下吗?
请注意,我在数据源中添加了“ 0 months”,但是我认为再次检查该函数不会有任何损害。我也不包括数据源凭证步骤。
谢谢
import pandas as pa
import numpy as np
import pyodbc as py
cnxn = py.connect('DRIVER='+driver+';SERVER='+server+';PORT=1433;DATABASE='+database+';UID='+username+';PWD='+ password)
original = pa.read_sql_query('SELECT s.customer_id, s.yr, s.mnth, Case when s.usage<0 then 0 else s.usage end as usage FROM dbo.Seasonal s Join ( Select Top 2000 customer_id, SUM(usage) as usage From dbo.Seasonal where Yr!=2018 Group by customer_id ) t ON s.customer_id = t.customer_id Where yr!= 2018 Order by customer_id, yr, mnth', cnxn)
grouped = original.groupby(by='customer_id')
def yearmonth_to_justmonth(year, month):
return year * 12 + month - 1
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()))
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')
outfile = 'results.csv'
df_list = []
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
customerTS = stats.ts(dataForCustomer.usage.astype(int),
start=base.c(startYear,startMonth),
end=base.c(endYear, endMonth),
frequency=12)
r.assign('customerTS', customerTS)
try:
seasonal = r('''
fit<-tbats(customerTS, seasonal.periods = 12,
use.parallel = TRUE)
fit$seasonal
''')
except:
seasonal = 1
df_list.append({'customer_id': customerid, 'seasonal': seasonal})
print(f' {customerid} | {seasonal} ')
seasonal_output = pa.DataFrame(df_list)
print(seasonal_output)
seasonal_output.to_csv(outfile)
答案 0 :(得分:0)
克里斯汀(这里是我的代码)。 1实际上意味着客户不是季节性的(或者无法接受),而NULL也意味着不是季节性的。如果他们有季节性使用模式(12个月的期限,这就是代码所要查找的),则会输出[12]。
您始终可以通过检查单个客户行为的图表然后通过算法对其进行确认。我还喜欢用Python或R中的季节性分解算法进行交叉检查。
这是一些R代码,用于查看时间序列的分解。如果图中没有季节性窗口,则结果不是季节性的:
library(forecast)
myts<-ts(mydata$SENDS, start=c(2013,1),end=c(2018,2),frequency = 12)
plot(decompose(myts))
此外,您提到(由于您的Twitter对话)某些0的问题没有填写,我没有遇到这个问题,但是我的客户的任期从2年到13年不等。不知道这里出了什么问题。
让我知道我是否可以提供其他帮助:)
答案 1 :(得分:0)
回圈回答我是如何工作的,只是将“原始”数据帧传递到for循环中。我的数据已经有空的$ 0个月,所以我不需要运行那部分代码。谢谢大家的帮助