Python数据分析之用户留存

  • 前言
  • 概览
  • 数据预处理和数据集探究
    • 规范列名
    • 删除多余的列
    • dummy化类别型特征
    • 探究通话时间与费用的关系
    • 探究流失率
    • 添加平均通话时长
    • 可视化缺失值分布
  • 训练模型
    • 模型对比
    • 训练决策树
    • 网格搜索-优化决策树
    • 评判模型效果-混淆矩阵
  • 进阶分析-模型再优化
    • 网格搜索2
    • 参数选择
    • 决策树模型可视化
  • 后记

前言

在产品生产力过剩的年代,由于转换成本越来越低,消费者也越来越容易放弃原来所依赖的产品而转去体验别的企业的产品,所以企业处理用户留存问题的能力对企业的持续盈利及发展有着至关重要的作用。在互联网产品的用户留存问题中,已然形成一系列成熟的方法论,包括但不局限于同期群分析、用户生命周期分析等。而这些方法论的核心思想,同样适用于金融行业、餐饮行业、通讯行业,因为企业只要有完善的数据平台采集、记录、实时更新用户的关键行为数据,就能通过数据分析的方式给用户留存问题一个定量的视角去赋能于实际业务。

本文就以通讯行业的用户行为数据和企业提供的CLV(客户终身价值)、CRC(客户留存成本)等信息,通过数据分析和数学建模的视角去给通讯行业的用户留存问题提出一种解决方案。值得说明的是,这种解决方案同样适用于互联网商业产品、金融服务产品甚至于餐饮行业的会员制管理的用户留存问题。

要想直接看解决方案可直接跳转到留存方案APP。

概览

本文处理的数据集原地址为:https://www.kaggle.com/datasets/sagnikpatra/edadata。该数据集记录了3000+用户的行为数据,包括每个时段的通话时长、是否开通长途电话套餐、拨打了多少次运营商服务电话等信息,并且标记出每个用户最终是否流失掉。(下图为部分数据)

本文目标是通过机器学习中的二分类算法模型训练该数据集以预测用户是否将要流失掉从而及时得采取行动留存用户。

数据预处理和数据集探究

规范列名

import pandas as pd
pd.options.display.encoding = 'GBK'
df=pd.read_csv('telecom_churn.csv')#列重命名-将列名所有小写,并将空格替换成下划线
df.columns=[col.lower().replace(' ','_') for col in df]

删除多余的列

#查看state的元素
len(df['state'].unique()) #51个
#state元素太多了,即类别太多了,最好删掉
state=df.pop('state')
#drop掉无用的列-account length呈现
df.drop(['account_length'],axis=1,inplace=True)

dummy化类别型特征

#查看area code的元素
df['area_code'].unique()
#仅有3个,可以转成字符型数值以便于后面dummy
df['area_code']=df['area_code'].astype('str')
mapping={'415':'A','408':'B','510':'C'}
df['area_code']=df['area_code'].map(mapping)
df=pd.get_dummies(df,drop_first=True)

探究通话时间与费用的关系

'''data manipulation'''
#可视化通话时间和收费的关系
import matplotlib.pyplot as plt
fig , axes=plt.subplots()
axes.scatter(df['total_night_minutes'],df['total_night_charge'])
axes.set(ylabel='total_night_charge', xlabel='total_night_minutes',title='通话时间和收费的关系')
plt.savefig('image/通话时间和收费的关系.jpg',dpi=400)


不出所料地,他们是呈绝对的线性关系,所以之后为了避免共线性的影响需要删除其中之一的特征列

探究流失率

labels = 'churn', 'stay'
sizes = [len(df[df['churn']==1]), len(df[df['churn']==0])]
explode = (0.1, 0) fig1, ax1 = plt.subplots()
ax1.pie(sizes, explode=explode ,labels=labels, autopct='%1.1f%%',shadow=True, startangle=90)
ax1.axis('equal')  # Equal aspect ratio ensures that pie is drawn as a circle.
ax1.set_title('churn rate')
plt.savefig('image/churn_rate.jpg',dpi=400)

#探究该运营商的收费标准
#可视化每个时间段收费标准的分布
fig,axes=plt.subplots(nrows=2,ncols=2)
fig.suptitle(u'每个时间段收费标准的分布')
ax0, ax1, ax2, ax3 = axes.flatten()
ax0.hist(df['total_day_charge']/df['total_day_minutes'])
ax0.set(title='日间收费标准',ylabel='记录数')
ax1.hist(df['total_eve_charge']/df['total_eve_minutes'])
ax1.set(title='傍晚收费标准',ylabel='记录数')
ax2.hist(df['total_night_charge']/df['total_night_minutes'])
ax2.set(title='夜晚收费标准',ylabel='记录数')
ax3.hist(df['total_intl_charge']/df['total_intl_minutes'])
ax3.set(title='国际通话收费标准',ylabel='记录数')
fig.tight_layout()
plt.savefig('image/每个时间段收费标准的分布.jpg',dpi=400)
#发现收费标准呈类似正态分布


由上图可知,不同客户的不同时间段的收费标准都有可能不尽相同。其中,国际长途话费平均(0.27)>日间话费平均(0.17)>傍晚话费平均(0.085)>国际长途话费平均(0.045)。

添加平均通话时长

#因为运营商收取的费用与通话时长线性相关,所以需要去掉通话时长--避免共线性
df.drop(['total_day_charge','total_eve_charge','total_night_charge','total_intl_charge'],axis=1,inplace=True)#添加用户在每个时间段的平均通话时长
df['avg_day_minutes']=df.apply(lambda x: x.total_day_minutes/x.total_day_callsif x.total_day_calls != 0 else 0, axis=1)df['avg_eve_minutes']=df.apply(lambda x: x.total_eve_minutes/x.total_eve_callsif x.total_eve_calls != 0 else 0, axis=1)df['avg_night_minutes']=df.apply(lambda x: x.total_night_minutes/x.total_night_callsif x.total_night_calls != 0 else 0, axis=1)df['avg_intl_minutes']=df.apply(lambda x: x.total_intl_minutes/x.total_intl_callsif x.total_intl_calls != 0 else 0, axis=1)

可视化缺失值分布

#缺失值数可视化
import seaborn as sns
sns.heatmap(df.isnull()) #可视图看出没有缺失值


由上图可知,数据集中已无缺失值。至此,数据预处理完毕。

训练模型

现在有许多成熟且有科学的数学理论支撑的二分类算法模型,比如支持向量机、逻辑回归和决策树等。

模型对比

训练各算法模型并比较分类效果

x=df.drop('churn',axis=1) #特征集
y=df['churn'] #标签from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.ensemble import VotingClassifier
from sklearn.neighbors import KNeighborsClassifier
import numpy as npmodel1=LogisticRegression(solver='liblinear')
model2=DecisionTreeClassifier()
model3=SVC()
model4=KNeighborsClassifier()
model_combo=VotingClassifier(estimators=[('lrp',model1),('dt',model2),('sv',model3),('knn',model4)])from sklearn.model_selection import cross_val_score
model1_score=cross_val_score(model1,x,y,scoring='f1',cv=10,verbose=0) #verbose=0 为不在标准输出流输出日志信息
model2_score=cross_val_score(model2,x,y,scoring='f1',cv=10,verbose=0)
model3_score=cross_val_score(model3,x,y,scoring='f1',cv=10,verbose=0)
model4_score=cross_val_score(model4,x,y,scoring='f1',cv=10,verbose=0)
model_combo_score=cross_val_score(model_combo,x,y,scoring='f1',cv=10,verbose=0)print('LogisticRegression',model1_score.mean()) #0.29
print('DecisionTreeClassifier',model2_score.mean()) #0.70
print('SVC',model3_score.mean()) #0.01
print('KNeighborsClassifier',model4_score.mean()) #0.45
print('model combo',model_combo_score.mean()) #0.11method_name=np.array(['LogisticRegression','DecisionTreeClassifier','SVC','KNeighborsClassifier','model combo'])
mult_method_score=np.array([0.29,0.70,0.01,0.45,0.11])fig, ax = plt.subplots(figsize=(30,10))
method_name_sort = method_name[np.argsort(mult_method_score)]
mult_method_score_sort = mult_method_score[np.argsort(mult_method_score)]
ax.set_yticks(np.arange(len(method_name_sort)), method_name_sort, fontsize =18)
rect=ax.barh(np.arange(len(method_name_sort)), mult_method_score_sort)
ax.bar_label(rect, padding=3)
ax.set_xlabel('f1_score')
plt.title('模型效果对比')
plt.savefig('image/模型效果对比.jpg',dpi=400)
plt.show()


发现决策树模型在这个数据集和实际业务问题中表现最佳,所以决定采用决策树模型。

f1_score为模型的综合得分

训练决策树

from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=50)
model2.fit(x_train,y_train)model2_pred_train= model2.predict(x_train)
f1_score(y_train,model2_pred_train)   #1.0## testing data
model2_pred_test = model2.predict(x_test)
f1_score(y_test,model2_pred_test)   #0.69model2.tree_.max_depth  #max_depth=21

在没有进行任何调参数的情况下,这个决策树模型应用在训练集上的f1_score为1,而在测试集上的f1_score仅为0.69,且该决策树树深为21,初步判断该决策树过拟合了。一般采用剪枝解决决策树的过拟合问题。

过拟合:当模型过度拟合训练集的数据而导致模型在训练集上的表现极佳,但是在测试集上表现欠佳(也称模型的泛化能力差)。
剪枝:减少树的深度

网格搜索-优化决策树

调用sklearn中的网格搜索以训练不同最大树深的决策树,并对比他们K层交叉检验后的f1_score,找出泛化能力强的决策树深度。

K层交叉检验就是把原始的数据随机分成K个部分。在这K个部分中,选择一个作为测试数据,剩下的K-1个作为训练数据。
交叉检验的过程实际上是把实验重复做K次,每次实验都从K个部分中选择一个不同的部分作为测试数据(保证K个部分的数据都分别做过测试数据),剩下的K-1个当作训练数据进行实验,最后把得到的K个实验结果平均。

parameter_grid = {'max_depth':range(1,50)}import numpy as np
from sklearn.model_selection import GridSearchCV # import
gird = GridSearchCV(model2,parameter_grid,verbose=2,scoring='f1',return_train_score=True,cv=10) #initialize
gird.fit(x,y)## verbose shows the output
## best parameters
gird.best_params_
gird.best_score_results=gird.cv_results_
ddf_result=pd.DataFrame(results)
results['mean_train_score']save_grid_best_balance=DecisionTreeClassifier(max_depth=9)#visualizing the process of training
plt.figure(figsize=(13, 13))
plt.title("GridSearchCV evaluating using different max_depth", fontsize=16)plt.xlabel("max_depth")
plt.ylabel("f1_score")ax = plt.gca()
ax.set_xlim(1,51)
ax.set_ylim(0.6, 1)# Get the regular numpy array from the MaskedArray
X_axis = np.array(results["param_max_depth"].data, dtype=float)for sample, style in (("train", "--"), ("test", "-")):sample_score_mean = results["mean_%s_score" % sample]sample_score_std = results["std_%s_score" % sample]ax.plot(X_axis,sample_score_mean,style,alpha=1 if sample == "test" else 0.7,label="f1 (%s)" %  sample,)best_index = np.nonzero(results["rank_test_score" ] == 1)[0][0]
best_score = results["mean_test_score"][best_index]# Plot a dotted vertical line at the best score for that scorer marked by x
ax.plot([X_axis[best_index],]* 2,[0, best_score],linestyle="-.",marker="x",markeredgewidth=3,ms=8,
)best_score=round(best_score,2)# Annotate the best score for that scorer
ax.annotate('['+str(best_index)+','+str(best_score)+']', (X_axis[best_index], best_score + 0.005))plt.legend(loc="best")
plt.grid(False)
plt.savefig('image/训练过程可视化.jpg',dpi=400)
plt.show()


由上图可知,当max_depth=7时,决策树的表现虽然在训练集上还没达到最好的表现,但是这时在测试集上表现最佳,所以应该选择max_depth=7作为训练决策树模型的参数。

x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=4)dt = DecisionTreeClassifier(max_depth=7) #initialize
dt.fit(x_train,y_train) #fit
dt_pred_test = dt.predict(x_test)

评判模型效果-混淆矩阵

二分类算法的预测结果的混淆矩阵通常最能说明模型的预测效果

def confusion_matrix_func(y_true,y_pred,labels,image_name):from sklearn.metrics import (confusion_matrix,precision_score,recall_score,accuracy_score,f1_score)import matplotlib.pyplot as pltfrom mlxtend.plotting import plot_confusion_matrixconf_matrix = confusion_matrix(y_true=y_true, y_pred=y_pred)fig, ax = plt.subplots(figsize=(7.5, 7.5))ax.matshow(conf_matrix, cmap=plt.cm.Blues, alpha=0.3)plot_confusion_matrix(conf_mat=conf_matrix, figsize=(6, 6), cmap=plt.cm.Greens)plt.xlabel('Predictions', fontsize=18)plt.ylabel('Actuals', fontsize=18)plt.title('Confusion Matrix', fontsize=18)textstr = '\n'.join((r'f1_score:%.2f' % f1_score(y_true,y_pred),r'accuracy:%.2f' % accuracy_score(y_true,y_pred),r'precision:%.2f' % precision_score(y_true,y_pred),r'recall:%.2f' % recall_score(y_true,y_pred)))props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)plt.text(3.1,4.2,textstr, transform=ax.transAxes, fontsize=12,verticalalignment='top', bbox=props)xlocations = np.array(range(len(class_names)))plt.xticks(xlocations, class_names)plt.yticks(xlocations, class_names)plt.savefig('image/'+image_name+'_confusion_matrix.jpg', dpi=300)confusion_matrix_func(y_test,dt_pred_test,class_names,image_name='decsion_tree_2')

TN(True Negative-真阴性):模型预测为不会流失的用户中,实际上确实没流失的用户
FN(False Negative-假阴性):模型预测为不会流失的用户中,但实际上其实是流失掉的用户
FP(False Positive-假阳性):模型预测为会流失的用户中,但实际上其实是没流失的用户
TP(True Positive-真阳性):模型预测为会流失的用户中,实际上确实流失掉的用户

在该预测结果中:
Accuracy=准确率=(TP+TN)/(TP+TN+FP+FN)=0.95,表示模型预测的结果有95%都是正确的。
Recall=召回率=真阳性率=TP/(TP+FN)=0.78,表示实际上流失掉的用户中,有78%被模型识别出来了。
Precision=精度=TP/(TP+FP)=0.84,表示模型预测为会流失的用户中,有84%是确实会流失的。
f1_score=2*Recall*Precision/(Recall+Precision)=0.71,表示召回率和精度的综合评分为81分(100分满分)

值得注意的是,优化决策树最后的结果表明交叉检验的f1_score平均值为0.77,但是模型混淆矩阵中的f1_score竟能高达0.81。这是由于,交叉检验的平均值就好比一个考生考了10次考试的平均值,而混淆矩阵仅能说明该考生一次考试中的具体情况。

在一般的二分类预测任务中,通常以f1_score为判断模型好坏的标准,因为f1_score代表了模型预测的综合表现。至此,经过了数据预处理、模型的选取、模型训练及模型优化、模型的评价,标准的解决二分类问题的流程就结束了。

但是在预测流失的业务场景中,由于客户终身价值一般会比留存成本高出好几倍,也就是说损失掉一个客户的代价往往比挽留一个客户的成本要高不少,所以这时Recall(召回率)就是最重要的指标,召回率越高模型就越不容易放过要流失掉的客户。在优先保证召回率的前提下,f1_score应为评判模型的第二指标。

进阶分析-模型再优化

要想提高模型的召回率,最直接的做法为增加流失掉客户的权重,即改变决策树参数中的class_weight参数。这是因为当给“流失”这一类别的权重越多,模型就越倾向于将客户预测成会流失,使得召回率会随之上升。而更多本不该被标记成“流失”的客户也会被标记成“流失”,使得精度下降。综合了召回率的上升和精度的下降,f1_score会时而上升时而下降,但总体的趋势一定是随着人为给的某一类别的权重越来越极端而下降。

score_list_class_weight_f1=[]
score_list_class_weight_recall=[]
for i in range(0,30,1):class_weight_dict = {0:1,1:i+1}dt = DecisionTreeClassifier(max_depth=7,class_weight=class_weight_dict)score_f1=cross_val_score(dt,x,y,scoring='f1',cv=10,verbose=1).mean()score_list_class_weight_f1.append(score_f1)score_recall=cross_val_score(dt,x,y,scoring='recall',cv=10,verbose=1).mean()score_list_class_weight_recall.append(score_recall)#plt.figure(figsize=[20,5])
fig, ax=plt.subplots(figsize=[20,5])
ax.plot(range(1,31,1),score_list_class_weight_recall,label='recall')
ax.plot(range(1,31,1),score_list_class_weight_f1,label='f1')
ax.legend(loc='lower right')
ax.set(xlabel='class_weight',ylabel='Score',title='f1 score and recall score of Decision tree in different class_weight')![请添加图片描述](https://img-blog.csdnimg.cn/a8e3622e5787427483541451ccfd81f5.jpeg)

网格搜索2

结合优化决策树中的max_depth和上述的class_weight这两参数训练模型,并记录不同参数的决策树模型的f1_score和recall

import numpy as np
from sklearn.model_selection import GridSearchCV # import
parameter_grid = {'max_depth':range(2,12),'class_weight':[{0:1,1:i} for i in range(1,30)]}
scoring=['f1','recall']
gird = GridSearchCV(model2,parameter_grid,verbose=2,scoring=scoring,refit='f1',return_train_score=True,cv=10) #initialize
gird.fit(x,y)
results_after=gird.cv_results_result_df_after=pd.DataFrame(results_after)
result_df_edited_after=result_df_after[['param_class_weight','param_max_depth','mean_test_recall','mean_test_f1','mean_train_recall','mean_train_f1','std_test_recall','std_test_f1']]fig,ax=plt.subplots()
ax.set_xlim(0.4,0.9)
ax.set_ylim(0.3, 0.8)
ax.scatter(result_df_edited_after['mean_test_recall'],result_df_edited_after['mean_test_f1'])
ax.set(xlabel='recall score',ylabel='f1 score', title='the trade off between recall score and f1 score')plt.show()


这时,我们可以画出该散点图的边界。

借用一下经济学中资产定价模型的概念,该边界顶点的右边的点形成的曲线可以称之为有效前沿,这是因为在相似的Recall score的情况下,有效前沿有着最高的f1 score; 在相似的f1 score的情况下,有效前沿有着最高的Recall score。

甚至,可以用多项式拟合边界的曲线。


result_df_edited_after['mean_test_recall']=round(result_df_edited_after['mean_test_recall'],2)
result_df_edited_after['mean_test_f1']=round(result_df_edited_after['mean_test_f1'],2)fig,ax=plt.subplots()
ax.set_xlim(0.4,0.9)
ax.set_ylim(0.3, 0.8)
ax.scatter(result_df_edited_after['mean_test_recall'],result_df_edited_after['mean_test_f1'])plt.show()df_groupby= result_df_edited_after[['mean_test_recall','mean_test_f1']].groupby(by='mean_test_recall',as_index=False).max()
df_merge=pd.merge(df_groupby,result_df_edited_after,on=['mean_test_recall','mean_test_f1'],how='left')df_merge_deleted=df_merge[~((0.55<df_merge['mean_test_recall']) & (df_merge['mean_test_recall']<0.7)& (0.5<df_merge['mean_test_f1'])& (df_merge['mean_test_f1']<0.6))]df_merge_deleted.to_csv('df_merge_deleted.csv',index=False)df_merge_deleted=pd.read_csv('df_merge_deleted.csv')## build a f1 score function of recall score
fig,ax=plt.subplots()
ax.set_xlim(0.4,0.9)
ax.set_ylim(0.3, 0.8)
ax.scatter(df_merge_deleted['mean_test_recall'],df_merge_deleted['mean_test_f1'])score_of_recall=df_merge_deleted['mean_test_recall']
score_of_f1_initial=df_merge_deleted['mean_test_f1']
para=np.polyfit(score_of_recall, df_merge_deleted['mean_test_f1'], deg=12)
score_f1_r=np.poly1d(para)score_f1_r_demical2=[]
for i in range(len(score_f1_r)):score_f1_r_demical2.append(round(score_f1_r[i],2))score_of_f1=score_f1_r(score_of_recall)
ax.plot(score_of_recall,score_of_f1,'g')
ax.set(xlabel='recall score',ylabel='f1 score', title='curve fitting')plt.show()

参数选择

现在我有了多个决策树模型,且这些决策树模型的参数和分类效果都各不相同,那么怎么根据实际的业务问题去选择具体的最佳的某一个决策树模型呢?本文采用定量的数学建模来辅助决策,并使用交互界面帮助企业根据自己的实际情况选择合适的模型。

假设这样一个情况,当该电信公司进行一次用户留存(比如每个月给可能要流失的用户发放coupon)时,假设该公司现有10000个用户,每次发放coupon的成本是5刀,而用户平均终身价值为31刀,且该月的用户流失率与数据集中的流失率一致,即实际上该月应该会有10000*流失率约等于1450个用户会流失。该公司的运营人员即可通过https://zhuozhiou.github.io/留存问题解决方案.html这个APP 将滑块①滑到step=客户终身价值/客户留存成本*10=31/5*10=62的位置,查看这种情况下预期损失最小的决策树模型的参数。

如图上所示,这时预期损失最小的决策树模型对应的召回率和f1 score分别为0.77、0.78,这表示在网格搜索的结果中,用②max_depth=6且class_weight={0:1,1:4}的决策树模型来预测用户流失能使预期的损失系数最小。可以算出精度=f1_score*Recall/(2*Recall-f1_score)=0.80。

这意味着在10000churn_ratio=1450个会流失的用户中,有77%(这表示TP=1117)的人被我们成功识别出来,剩下的333个(FN)没有被识别出来;而被标记成流失的用户中,有80%确实是会流失的,且有20%(这表示FP=279)人被误诊为会流失。所以电信公司预期损失为(279+1117)*5+279*31=15629(美元)。而损失系数的意义在于,它能更精准的预测电信公司的具体损失,能更快捷地计算出预计损失为损失系数客户总数*留存成本=17392.5(美元)。
应该说明的是,用损失系数计算出来的结果和用模型效果指标计算出来的结果不尽相同,这是因为上述的模型效果指标都是经过四舍五入的,所以真实的业务场景的损失更加接近17392.5。

总的来说,该APP实现了根据企业实情去选择合适的参数训练决策树模型的功能,并理论上能使企业的在用户流失方面的损失最小。

一个APP仅适用于一个提供了用户行为数据集的企业,也就是说对于某一企业而言,这套解决方案是定制性的。而搭建的思路和方法同样适用于要处理用户留存问题的、有收集用户行为数据能力的企业。

上述的损失系数搭建过程如下:
实际预期流失人数=流失率*客户数量
TP=实际预期流失人数*召回率
FN=实际预期流失人数-TP
FP=TP/精度-TP
预计损失=FN*客户终身价值+(TP+FP)*留存成本
损失系数=预计损失/(客户总数*留存成本)
所以预计损失也=损失系数*客户总数*留存成本

搭建交互界面的代码如下:

def f_loss_data(loss_to_cost):score_of_recall=df_merge_deleted['mean_test_recall']score_of_f1=df_merge_deleted['mean_test_f1']Retention_Cost=1people_num=1loss_to_cost=loss_to_costPotential_Loss=loss_to_cost*Retention_Costchurn_ratio=0.14491449144914492score_of_prec=score_of_f1*score_of_recall/(2*score_of_recall-score_of_f1)Actual_churn=churn_ratio*people_numTP=Actual_churn*score_of_recallFN=Actual_churn-TPFP=TP/score_of_prec-TPloss_r=FN*Potential_Loss+(TP+FP)*Retention_Costdic={'mean_test_recall':score_of_recall,'mean_test_f1':score_of_f1,'score_of_prec':score_of_prec,'loss_r':loss_r}df_loss_r=pd.DataFrame(dic)df_loss_r['mean_test_recall']=round(df_loss_r['mean_test_recall'],2)df_loss_r['mean_test_f1']=round(df_loss_r['mean_test_f1'],2)df_loss_r_merge=pd.merge(df_merge_deleted,df_loss_r,on=['mean_test_recall','mean_test_f1'],how='left')return df_loss_r_mergeimport plotly.graph_objects as go
import plotly
# Create figure
fig = go.Figure()# Add traces, one for each slider step
for step in np.arange(0, 50, 0.1):step=round(step,1)data=f_loss_data(step)loss_min_index=np.argmin(data['loss_r'])loss_min_score=round(data['loss_r'][loss_min_index], 5)loss_min_recall=data['mean_test_recall'][loss_min_index]loss_min_f1_score=data['mean_test_f1'][loss_min_index]loss_min_class_weight=data['param_class_weight'][loss_min_index]loss_min_max_depth=data['param_max_depth'][loss_min_index]f1_max_f1_score=data['mean_test_f1'].max()df_loss_r_max_f1=data[data['mean_test_f1']==f1_max_f1_score]f1_max_recall=df_loss_r_max_f1['mean_test_recall']f1_max_score=round(df_loss_r_max_f1['loss_r'], 2)fig.add_trace(go.Scatter(visible=False,line=dict(width=1),mode='lines+markers',name="												

Python数据分析之用户留存相关推荐

  1. python 电话呼叫_【Python数据分析】用户通话行为分析

    主要工作: 1.对从网上营业厅拿到的用户数据.xls文件,通过Python的xlrd进行解析,计算用户的主叫被叫次数,通话时间,通话时段. 2.使用matplotlib画图包,将分析的结果直观的绘制出 ...

  2. 【业务数据分析】—— 用户留存分析(以挖掘Aha时刻为例)

  3. python数据分析可视化实例_Python数据分析及可视化实例之基于Kmean分析RFM进行用户关怀...

    系列文章总目录:Python数据分析及可视化实例目录 数据集下载 Python数据分析及可视化实例之全国各城市房价分析(含数据采集) Python数据分析及可视化实例之帝都房价预测 Python数据分 ...

  4. Python数据分析高薪实战第十二天 网络服务用户流失预测分析和国产电视剧评分预测分析

    29 综合实战:网络服务用户流失预测与分析 绝大多数互联网公司都面临一个非常重要的问题:用户流失问题.随着互联网和移动互联网的充分发展,发展新用户(也就是一般所说的拉新)的成本越来越高,往往要几块或者 ...

  5. 用户运营:5步学会用户留存数据分析

    内容留人.功能留人.好友留人.物质激励.情感留人.个人品牌推荐.线下活动等只是常见的提升用户留存率的运营手段,为了更加有效和科学性的进行用户运营工作,运营需要做的是运营手段执行前的用户留存率数据分析, ...

  6. python数据分析实战之用户分析及RFM模型分析

    理论基础知识可以看我之前的博客: 1.python之Numpy知识点详细总结 2.python最最最重要的数据分析工具之pandas 3.pandas之表连接与高级查询 也可以进入我的专栏:欢迎订阅哦 ...

  7. 作业3(大作业):python数据分析与应用大作业,对用户用电量数据进行数据处理,包括数据转置、异常数据识别和处理、统计基本统计量(包括峰度、偏度)、按日/周(求和)差分、5%分位数等

    Python数据分析与应用大作业 使用学习过的知识(Numpy数值分析基础.Matplotlib数据可视化基础.Pandas统计分析基础),对data.csv用户用户用电量数据进行相关处理,其中数据中 ...

  8. (金融)线上消费贷风控优化(单变量分析)与用户留存(同期群分析)项目——数据分析

    (金融)线上消费贷风控优化(单变量分析)与用户留存项目(同期群分析)--数据分析 一.首逾率单变量分析 案例背景: 日常监控发现某款消费贷产品首逾率有逐渐升高的趋势,需要把首逾率降下来以减少产品带来的 ...

  9. python数据分析及可视化(十五)数据分析可视化实战篇(抖音用户数据分析、二手房数据分析)

    python数据分析的实战篇,围绕实例的数据展开分析,通过数据操作案例来了解数据分析中的频繁用到的知识内容. 抖音用户数据分析 1.理解数据 数据字段含义 了解数据内容,确保数据来源是正常的,安全合法 ...

最新文章

  1. Ubuntu下添加新分区并设置挂载点
  2. 邮件服务之Sendmail
  3. window中常用的命令
  4. java tomcat mysql_java+tomcat+mysql实现登录界面基本思路
  5. 转赋值表达式解析的流程
  6. 我国将于今年春季发射空间站核心舱 空间站进入全面实施阶段
  7. 如何保障一场千万级大型直播?
  8. Android多种View动画:EasyAndroidAnimations
  9. 优化--减少HTTP请求
  10. Python ValueError: IO operation on closed file
  11. openwrt dhcp 无法获取ip_电脑的 ip 是怎么来的呢?我又没有配置过
  12. Android访问php webservice
  13. 表格行上下移,置顶的js代码
  14. const与volatile
  15. 记一次YUV图像分析(二)
  16. pcre2 知:介绍
  17. 苹果为小学生推出编程指南
  18. Elemet-UI 中利用穿梭框对表格列进行动态设置
  19. 翻译:Firefox for Pentester: Privacy and Protection Configurations
  20. java基于微信小程序的公交线路查询系统 uniapp 小程序

热门文章

  1. AI 加持实时互动|ZegoAvatar 面部表情随动技术解析
  2. java苹果通知V2新版协议实现订阅退款
  3. Excel - 斜线表头制作
  4. S3C2440之IIC裸机驱动
  5. 淘宝直通车为什么要低价引流,低价引流的原理、低价引流的具体操作
  6. 股票量化自动交易软件下单原则条件
  7. element tree不刷新视图_00后都开始找工作了,这些都不懂还聊个啥?
  8. 计算机博弈与创新人才培养,计算机博弈竞赛和创新人才培养模式探究.pdf
  9. 腾讯云运维工程师认证(TCA)在线测试题
  10. Java的Lambda表达式实例