无论在工作还是参加数据科学竞赛时,当我们拿到一份新的数据集,首先需要对数据进行探索性分析(EDA)。本次用户购买预测的数据集是典型的时间序列数据,往往用于处理一些有日期的业务流水。类似的场景有预测未来商品的销量、预测设备是否故障等。
对于时间序列数据的EDA,一般可以从以下几方面着手:
import numpy as np # linear algebra
import pandas as pd
from datetime import datetime, date, timedelta
from scipy.stats import skew # for some statistics
from scipy.special import boxcox1p
from scipy.stats import boxcox_normmax
from sklearn.linear_model import ElasticNetCV, LassoCV, RidgeCV, Ridge, Lasso, ElasticNet
from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor, RandomForestClassifier
from sklearn.feature_selection import mutual_info_regression
from sklearn.svm import SVR, LinearSVC
from sklearn.pipeline import make_pipeline, Pipeline
from sklearn.preprocessing import RobustScaler, LabelEncoder
from sklearn.model_selection import KFold, cross_val_score, train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
import os
import re
import seaborn as sns
import matplotlib.pyplot as plt
import time
from itertools import product
import datetime as dt
import calendar
import gc
RANDOM_SEED = 42
# 引入中文字体
from matplotlib.font_manager import FontProperties
myfont = FontProperties(fname="/home/aistudio/NotoSansCJKsc-Light.otf", size=12)
PATH = './data/data19383/'
train = pd.read_csv(PATH + 'train.csv')
test = pd.read_csv(PATH + 'submission.csv').set_index('customer_id')
对于特别大的文件,我们需要做一些内存检查:
# 训练集文件
mem_train = train.memory_usage(index=True).sum()
mem_test = test.memory_usage(index=True).sum()
print(u"训练集使用内容 "+ str(mem_train/ 1024**2)+" MB")
print(u"测试集使用内存 "+ str(mem_test/ 1024**2)+" MB")
下面是对数据集的基本信息分析:
train.info()
summary_stats_table(train)
接下来我们对一些数据类型进行处理,以优化内存使用:
# 处理id字段
train['order_detail_id'] = train['order_detail_id'].astype(np.uint32)
train['order_id'] = train['order_id'].astype(np.uint32)
train['customer_id'] = train['customer_id'].astype(np.uint32)
train['goods_id'] = train['goods_id'].astype(np.uint32)
train['goods_class_id'] = train['goods_class_id'].astype(np.uint32)
train['member_id'] = train['member_id'].astype(np.uint32)
# 处理状态字段,同时处理空值,将空值置为0
train['order_status'] = train['order_status'].astype(np.uint8)
train['goods_has_discount'] = train['goods_has_discount'].astype(np.uint8)
train["is_member_actived"].fillna(0, inplace=True)
train["is_member_actived"] = train["is_member_actived"].astype(np.int8)
train["member_status"].fillna(0, inplace=True)
train["member_status"] = train["member_status"].astype(np.int8)
train["customer_gender"].fillna(0, inplace=True)
train["customer_gender"] = train["customer_gender"].astype(np.int8)
train['is_customer_rate'] = train['is_customer_rate'].astype(np.uint8)
train['order_detail_status'] = train['order_detail_status'].astype(np.uint8)
# 处理日期
train['goods_list_time'] = pd.to_datetime(train['goods_list_time'], format="%Y-%m-%d")
train['order_pay_time'] = pd.to_datetime(train['order_pay_time'], format="%Y-%m-%d")
train['goods_delist_time'] = pd.to_datetime(train['goods_delist_time'], format="%Y-%m-%d")
然后我们再次检查内存使用情况:
# 检查内存使用
mem_train = train.memory_usage(index=True).sum()
mem_test = test.memory_usage(index=True).sum()
print(u"训练集使用内容 "+ str(mem_train/ 1024**2)+" MB")
print(u"测试集使用内存 "+ str(mem_test/ 1024**2)+" MB")
接下来我们进行数据集的探索性分析(EDA),从用户、商品、异常交易等多个方面进行分析。这里只展示部分分析结果:
# 年度销售情况
train_2012 = train.loc[train['Year'] == 2012]
train_2013 = train.loc[train['Year'] == 2013]
# 用户性别分布
plt.figure(figsize=(8,4))
plt.title('会员性别分布', fontproperties=myfont)
train.customer_gender.value_counts().plot.bar()
# 用户城市分布
plt.figure(figsize=(24,8))
plt.title('用户城市分布',fontproperties=myfont)
x = train.customer_city.value_counts().head(30).index
plt.bar(train.customer_city.value_counts().head(30).index, train.customer_city.value_counts().head(30).values)
plt.xticks(x, fontproperties=myfont, rotation=45)
plt.show()
# 商品价格分布
plt.figure(figsize=(12,8))
plt.title('商品价格分布', FontProperties=myfont)
sns.distplot(train.goods_price)
# 异常交易订单付款金额
plt.figure(figsize=(12,8))
plt.title('异常交易订单付款金额', fontproperties=myfont)
sns.distplot(tmp.order_total_payment)
# 异常交易用户分布
plt.figure(figsize=(12,8))
plt.title('异常交易用户分布', fontproperties=myfont)
sns.distplot(tmp.customer_id)
# 商品价格分布
plt.figure(figsize=(12,8))
plt.title('商品价格分布', FontProperties=myfont)
sns.distplot(tmp.goods_price)
这一阶段主要目的是构建合适的特征集,为模型提供有效的输入。特征工程包括从用户行为和商品两个角度提取了一系列特征,如用户购买频率、购买金额统计、评价情况统计、商品销售数量、平均价格、折扣情况等。同时进行了日期特征的处理、类别特征的编码以及缺失值的处理等。最后,通过数据合并,将构建好的特征集合并到原始数据集中。
我们可以从用户行为的角度提取一些特征,例如:
# 用户购买频率
user_purchase_frequency = train.groupby('customer_id')['order_id'].nunique()
user_purchase_frequency = user_purchase_frequency.reset_index()
user_purchase_frequency.columns = ['customer_id', 'purchase_frequency']
# 用户购买金额统计
user_total_purchase = train.groupby('customer_id')['order_total_payment'].sum()
user_total_purchase = user_total_purchase.reset_index()
user_total_purchase.columns = ['customer_id', 'total_purchase']
# 用户评价情况统计
user_rate_count = train.groupby('customer_id')['is_customer_rate'].sum()
user_rate_count = user_rate_count.reset_index()
user_rate_count.columns = ['customer_id', 'rate_count']
# 合并用户行为特征
user_behavior_features = pd.merge(user_purchase_frequency, user_total_purchase, on='customer_id', how='left')
user_behavior_features = pd.merge(user_behavior_features, user_rate_count, on='customer_id', how='left')
从商品的角度提取一些特征,例如:
# 商品销售数量
goods_sales_count = train.groupby('goods_id')['order_total_num'].sum()
goods_sales_count = goods_sales_count.reset_index()
goods_sales_count.columns = ['goods_id', 'sales_count']
# 商品平均价格
goods_avg_price = train.groupby('goods_id')['goods_price'].mean()
goods_avg_price = goods_avg_price.reset_index()
goods_avg_price.columns = ['goods_id', 'avg_price']
# 商品折扣情况
goods_discount = train.groupby('goods_id')['goods_has_discount'].mean()
goods_discount = goods_discount.reset_index()
goods_discount.columns = ['goods_id', 'discount']
# 合并商品特征
goods_features = pd.merge(goods_sales_count, goods_avg_price, on='goods_id', how='left')
goods_features = pd.merge(goods_features, goods_discount, on='goods_id', how='left')
将日期特征进行拆分,提取年、月、日、小时等信息:
# 处理日期
train['Year'] = pd.DatetimeIndex(train['order_pay_time']).year
train['Month'] = pd.DatetimeIndex(train['order_pay_time']).month
train['Day'] = pd.DatetimeIndex(train['order_pay_time']).day.astype(np.uint8)
train['Hour'] = pd.DatetimeIndex(train['order_pay_time']).hour
对于类别特征,使用独热编码或者Label Encoding进行处理:
# 类别特征处理
train['customer_city_id'] = LabelEncoder().fit_transform(train['customer_city'].astype(str))
train['customer_province_id'] = LabelEncoder().fit_transform(train['customer_province'].astype(str))
对于缺失值,可以采用填充、删除等策略:
# 缺失值处理
train.fillna(0, inplace=True) # 以0填充缺失值
将构建好的特征合并到原始数据集中:
# 合并特征到原始数据集
train = pd.merge(train, user_behavior_features, on='customer_id', how='left')
train = pd.merge(train, goods_features, on='goods_id', how='left')
以上仅为特征工程和数据预处理的简单示例,实际情况可能需要根据数据集的特点进行更复杂的处理。
在进行机器学习模型训练之前,还需要进行数据切分,将数据分为训练集和验证集:
# 数据切分
features = ['feature1', 'feature2', ...] # 替换为实际的特征列
target = 'target' # 替换为实际的目标列
X_train, X_val, y_train, y_val = train_test_split(train[features], train[target], test_size=0.2, random_state=RANDOM_SEED)
接下来可以使用机器学习模型对数据进行训练。常见的模型包括线性回归、决策树、随机森林、梯度提升等。在实际应用中,可以根据问题的特点选择合适的模型。
在这一阶段,导入了必要的模型库,并使用随机森林模型进行了训练。模型评估通过计算准确性(accuracy)和生成分类报告(classification report)来评估模型性能。接着,通过调整模型参数和进行特征选择,进一步提升模型性能。具体包括使用网格搜索调优模型参数,获取特征重要性并选择重要性较高的特征。
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, classification_report
# 定义特征和目标列
features = ['feature1', 'feature2', ...] # 替换为实际的特征列
target = 'target' # 替换为实际的目标列
X_train, X_val, y_train, y_val = train_test_split(train[features], train[target], test_size=0.2, random_state=RANDOM_SEED)
# 使用随机森林模型进行训练
model = RandomForestClassifier(random_state=RANDOM_SEED)
model.fit(X_train, y_train)
# 模型评估
y_val_pred = model.predict(X_val)
accuracy = accuracy_score(y_val, y_val_pred)
print(f'Accuracy: {accuracy}')
classification_rep = classification_report(y_val, y_val_pred)
print('Classification Report:\n', classification_rep)
根据模型的评估结果,可以进行模型的调优,包括调整模型参数、特征选择、交叉验证等。
from sklearn.model_selection import GridSearchCV
# 定义参数网格
param_grid = {
'n_estimators': [50, 100, 200],
'max_depth': [None, 10, 20],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 2, 4]
}
# 使用网格搜索进行调优
grid_search = GridSearchCV(estimator=RandomForestClassifier(random_state=RANDOM_SEED),
param_grid=param_grid,
scoring='accuracy',
cv=5,
n_jobs=-1)
grid_search.fit(X_train, y_train)
# 输出最佳参数
best_params = grid_search.best_params_
print('Best Parameters:', best_params)
# 使用最佳参数的模型进行训练
best_model = grid_search.best_estimator_
best_model.fit(X_train, y_train)
可以根据特征的重要性进行选择:
# 获取特征重要性
feature_importances = best_model.feature_importances_
# 根据重要性进行排序
feature_importance_df = pd.DataFrame({'Feature': features, 'Importance': feature_importances})
feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False)
# 选择重要性较高的特征
selected_features = feature_importance_df.loc[feature_importance_df['Importance'] > threshold]['Feature'].tolist()
通过以上步骤,我们可以得到一个在验证集上表现较好的模型,并根据需要进行模型的调优,以提高预测性能。最终,我们可以使用这个模型对测试集或未来数据进行预测。
注意: 上述代码中的参数、模型选择等内容需要根据实际情况进行调整和优化。模型训练与调优是一个迭代的过程,需要不断尝试不同的方法以达到更好的效果。
最终阶段是使用最终调优的模型对测试集或未来数据进行预测,并生成提交文件。这部分代码包括使用最终模型进行预测,将预测结果添加到测试集中,然后生成提交文件。
整个过程形成了一个完整的用户购买预测的流程,从数据的初步了解到模型的训练和优化,再到最终的预测。
# 使用最终模型进行预测
test_predictions = best_model.predict(test[features])
# 将预测结果添加到测试集中
test['result'] = test_predictions
# 生成提交文件
submission = test[['customer_id', 'result']].reset_index(drop=True)
submission.to_csv('submission.csv', index=False)
以上代码将生成一个名为submission.csv
的提交文件,其中包含用户ID和预测结果。
通过以上步骤,我们完成了整个用户购买预测的流程,从数据探索性分析(EDA)到特征工程、模型训练与调优,最终到模型预测与提交。希望这个方案能够对你在实际应用中的机器学习项目有所帮助。