Uber打车用户留存情况预测
1. 项目概况
1.1 项目背景与定义
为了辨别打车用户特征对于留存情况的影响以进一步提高用户留存率,Uber公开了50,000名于2014年1月注册Uber账户的用户部分信息,希望基于该信息得到具有相对可靠性及参考意义的预测模型。
为解决该二分类问题,本项目将实际运用Python相关库(Pandas, Numpy,Matplotlib, Scikit-learn等)进行分类模型的筛选、细化与评估,在得到可靠模型的同时,输出客户各特征对于Uber留存率的重要度。
1.2 项目流程
- 数据前处理与特征工程
- 数据探索分析
- 数值型特征缺失值处理
- 数值型特征异常点检测
- 留存用户标签定义
- 数据可视化与特征关联性分析
- 类别型特征指示变量转换
- 日期型特征离散
- 分类模型筛选
- 定义模型评分依据
- 尝试不同分类模型并筛选表现较好模型
- 模型交叉验证与调优
- 利用GridSearchCV调优得到max_depth, max_features, n_estimators参数较优值
- 模型验证与评估
- 通过学习曲线评估模型随复杂度变化的表现情况
- 结论
- 给出特征重要度排名
2. 数据前处理与特征工程
2.1 数据集概述
city
: 用户注册所在城市phone
: 用户使用的手机系统signup_date
: 账户注册日期,格式为‘YYYY-MM-DD’last_trip_date
: 用户最近一次打车日期,格式为‘YYYY-MM-DD’avg_dist
: 用户注册后前30天内平均打车距离(英里)avg_rating_by_driver
: 司机对该用户打分的平均值avg_rating_of_driver
: 该用户对所有司机打分的平均值surge_pct
: 用户紧急叫车次数占总乘车次数的百分比avg_surge
: 用户紧急叫车的平均加价倍率trips_in_first_30_days
: 用户注册后前30天内的总乘车次数uber_black_user
: 是否为UberBlack级别车用户weekday_pct
: 用户工作日打车占比
2.2 数据探索分析
import pandas as pd
import numpy as np
import json
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# 加载数据
with open('train.json', 'r') as f:
data = json.load(f)
df = pd.DataFrame(data)
df.head()
avg_dist | avg_rating_by_driver | avg_rating_of_driver | avg_surge | city | last_trip_date | phone | signup_date | surge_pct | trips_in_first_30_days | uber_black_user | weekday_pct | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3.67 | 5.0 | 4.7 | 1.10 | King’s Landing | 2014-06-17 | iPhone | 2014-01-25 | 15.4 | 4 | True | 46.2 |
1 | 8.26 | 5.0 | 5.0 | 1.00 | Astapor | 2014-05-05 | Android | 2014-01-29 | 0.0 | 0 | False | 50.0 |
2 | 0.77 | 5.0 | 4.3 | 1.00 | Astapor | 2014-01-07 | iPhone | 2014-01-06 | 0.0 | 3 | False | 100.0 |
3 | 2.36 | 4.9 | 4.6 | 1.14 | King’s Landing | 2014-06-29 | iPhone | 2014-01-10 | 20.0 | 9 | True | 80.0 |
4 | 3.13 | 4.9 | 4.4 | 1.19 | Winterfell | 2014-03-15 | Android | 2014-01-27 | 11.8 | 14 | False | 82.4 |
# 各字段统计值
df.describe()
avg_dist | avg_rating_by_driver | avg_rating_of_driver | avg_surge | surge_pct | trips_in_first_30_days | weekday_pct | |
---|---|---|---|---|---|---|---|
count | 50000.000000 | 49799.000000 | 41878.000000 | 50000.000000 | 50000.000000 | 50000.000000 | 50000.000000 |
mean | 5.796827 | 4.778158 | 4.601559 | 1.074764 | 8.849536 | 2.278200 | 60.926084 |
std | 5.707357 | 0.446652 | 0.617338 | 0.222336 | 19.958811 | 3.792684 | 37.081503 |
min | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 2.420000 | 4.700000 | 4.300000 | 1.000000 | 0.000000 | 0.000000 | 33.300000 |
50% | 3.880000 | 5.000000 | 4.900000 | 1.000000 | 0.000000 | 1.000000 | 66.700000 |
75% | 6.940000 | 5.000000 | 5.000000 | 1.050000 | 8.600000 | 3.000000 | 100.000000 |
max | 160.960000 | 5.000000 | 5.000000 | 8.000000 | 100.000000 | 125.000000 | 100.000000 |
df1 = df.copy()
2.3 数值型特征缺失值处理
# 查看各字段缺失值情况
df1.isnull().sum()
avg_dist 0
avg_rating_by_driver 201
avg_rating_of_driver 8122
avg_surge 0
city 0
last_trip_date 0
phone 396
signup_date 0
surge_pct 0
trips_in_first_30_days 0
uber_black_user 0
weekday_pct 0
dtype: int64
avg_rating_by_driver, avg_rating_of_driver和phone三个字段存在缺失值,处理方法:
* avg_rating_by_driver、avg_rating_of_driver字段:用各自统计的中位数填充
* phone字段:直接删除对应的行
# avg_rating_by_driver、avg_rating_of_driver字段:用各自统计的中位数填充
df1['avg_rating_by_driver'].fillna(df1['avg_rating_by_driver'].median(), inplace=True)
df1['avg_rating_of_driver'].fillna(df1['avg_rating_of_driver'].median(), inplace=True)
# phone字段:直接删除对应的行
df1 = df1.drop(index=df[df.phone.isnull()].index).reset_index(drop=True)
df1.isnull().sum()
avg_dist 0
avg_rating_by_driver 0
avg_rating_of_driver 0
avg_surge 0
city 0
last_trip_date 0
phone 0
signup_date 0
surge_pct 0
trips_in_first_30_days 0
uber_black_user 0
weekday_pct 0
dtype: int64
2.4 数值型特征异常点检测
# 利用箱体图描述各字段分布情况
fig, ax = plt.subplots(figsize=(10,6))
sns.boxplot(data=df1[['avg_rating_by_driver', 'avg_rating_of_driver', 'avg_surge']])
fig, ax = plt.subplots(figsize=(10,6))
sns.boxplot(data=df1[['avg_dist', 'surge_pct', 'trips_in_first_30_days', 'weekday_pct']])
统计各记录异常点情况
# 统计各记录异常点情况
df1['outlier_counts'] = 0
def