一、项目背景
某超市希望通过历史优惠券核销数据,建立精准营销模型以识别高潜力客户。数据包含客户性别、年龄、消费水平、商品品类等特征,目标是通过逻辑回归模型预测客户核销优惠券的概率。
二、实现步骤详解
1. 数据加载与预览
import pandas as pd
# 加载数据
df = pd.read_csv("优惠券核销数据.csv")
# 数据概览
print("数据前5行:")
display(df.head())
print("\n数据基本信息:")
print(df.info())
print("\n数据统计描述:")
display(df.describe())
输出说明 :
- 包含5个特征:是否核销(Accepted)、性别(Sex)、年龄(Age)、液奶品类(Class)、平均消费额(AvgSpending)
- 数据类型验证(无缺失值)
2. 数据可视化分析
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示
plt.figure(figsize=(15, 10))
# 性别与核销关系
plt.subplot(2, 2, 1)
sns.countplot(x='Sex', hue='Accepted', data=df)
plt.title('不同性别优惠券核销情况')
# 年龄段与核销关系
plt.subplot(2, 2, 2)
sns.countplot(x='Age', hue='Accepted', data=df)
plt.title('不同年龄段优惠券核销情况')
# 商品品类与核销关系
plt.subplot(2, 2, 3)
sns.countplot(x='Class', hue='Accepted', data=df)
plt.title('不同液奶品类优惠券核销情况')
# 平均消费额分布
plt.subplot(2, 2, 4)
sns.histplot(data=df, x='AvgSpending', hue='Accepted', multiple='stack', kde=True)
plt.title('平均消费额分布与核销关系')
plt.tight_layout()
plt.show()
3. 数据预处理
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
# 特征工程
X = df.drop('Accepted', axis=1)
y = df['Accepted']
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 预处理管道
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), ['AvgSpending']), # 数值标准化
('cat', OneHotEncoder(drop='first'), ['Sex', 'Age', 'Class']) # 类别编码
])
处理要点 :
- 类别特征独热编码(避免顺序影响)
- 数值特征标准化(加速模型收敛)
- 保留20%数据用于最终验证
4. 模型构建与训练
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
# 构建流水线
model = Pipeline([
('preprocessor', preprocessor),
('classifier', LogisticRegression(max_iter=1000, solver='lbfgs'))
])
# 训练模型
model.fit(X_train, y_train)
1. 准确率(Accuracy):0.78
表示整体预测的准确程度。模型在测试集上正确预测了78%的样本。
2. 混淆矩阵
混淆矩阵展示了模型在不同类别上的预测结果,分为“实际未核销”和“实际已核销”两列,以及“预测未核销”和“预测已核销”两行。
预测未核销 | 预测已核销 | |
---|---|---|
实际未核销 | 41 | 20 |
实际已核销 | 20 | 98 |
-
实际未核销(61个样本):
-
41个被正确预测为未核销(True Negative)。
-
20个被错误预测为已核销(False Positive)。
-
-
实际已核销(118个样本):
-
98个被正确预测为已核销(True Positive)。
-
20个被错误预测为未核销(False Negative)。
-
3. 分类报告
分类报告提供了每个类别的详细性能指标,包括精确率(Precision)、召回率(Recall)、F1分数(F1-score)和支持(Support)。
类别 | Precision | Recall | F1-score | Support |
---|---|---|---|---|
未核销 | 0.67 | 0.67 | 0.67 | 61 |
已核销 | 0.83 | 0.83 | 0.83 | 118 |
未核销类别:
-
Precision(精确率):0.67
在预测为未核销的样本中,67%是实际未核销。 -
Recall(召回率):0.67
实际未核销的样本中,67%被正确识别。 -
F1-score:0.67
Precision和Recall的调和平均值,综合性能指标。
已核销类别:
-
Precision(精确率):0.83
在预测为已核销的样本中,83%是实际已核销。 -
Recall(召回率):0.83
实际已核销的样本中,83%被正确识别。 -
F1-score:0.83
综合性能指标。
宏观平均(Macro Avg)和加权平均(Weighted Avg):
-
Macro Avg:
-
Precision: 0.75
-
Recall: 0.75
-
F1-score: 0.75
-
对所有类别同等权重的平均值,不考虑样本数量。
-
-
Weighted Avg:
-
Precision: 0.78
-
Recall: 0.78
-
F1-score: 0.78
-
根据样本数量加权的平均值,更符合实际分布。
-
参数说明 :
max_iter=1000
:确保模型充分收敛solver='lbfgs'
:适合小规模数据集
5. 模型评估
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, roc_curve, roc_auc_score
# 预测与评估
y_pred = model.predict(X_test)
y_proba = model.predict_proba(X_test)[:, 1]
print(f"准确率:{accuracy_score(y_test, y_pred):.2f}")
print("混淆矩阵:")
print(pd.DataFrame(confusion_matrix(y_test, y_pred),
index=['实际未核销','实际已核销'],
columns=['预测未核销','预测已核销']))
print("\n分类报告:")
print(classification_report(y_test, y_pred, target_names=['未核销','已核销']))
# 绘制ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_proba)
plt.plot(fpr, tpr, label=f'AUC={roc_auc_score(y_test, y_proba):.2f}')
plt.plot([0,1],[0,1],'--')
plt.title('ROC曲线')
plt.xlabel('假阳性率')
plt.ylabel('真阳性率')
plt.legend()
plt.show()
6. 特征重要性分析
# 获取特征名称
ohe = model.named_steps['preprocessor'].transformers_[1][1]
feature_names = ['AvgSpending'] + list(ohe.get_feature_names_out(['Sex','Age','Class']))
# 可视化系数
coef = model.named_steps['classifier'].coef_[0]
pd.DataFrame({'特征': feature_names, '系数': coef}).sort_values('系数').plot(
x='特征', y='系数', kind='barh',
title='特征对核销概率的影响(正值促进核销)'
)
plt.show()
-
横轴 :表示逻辑回归系数,正值表示促进核销,负值表示抑制核销。
-
纵轴 :列出了各个特征的名称,包括 Age_2、AvgSpending、Class_2、Class_3 和 Sex_2。
-
数据解读 :
-
Age_2 :逻辑回归系数约为 0.434,在所列特征中对核销概率的促进作用最明显,意味着在 Age 这一特征的某种取值情况下(可能对应年龄的一个区间或类别),核销概率会增加。
-
AvgSpending :对核销概率有一定的抑制作用,系数约为 -0.089。
-
_Class2 :对核销概率有抑制作用,系数约为 -1.096。
-
Class_3 :对核销概率有较大的抑制作用,系数约为 -1.973。
-
Sex_2 :对核销概率的抑制作用最为显著,系数约为 -2.475,在所列特征中对核销概率的阻碍作用最大。
-
三、完整代码
# 导入必要库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, roc_curve, roc_auc_score, classification_report
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
# 设置中文显示(解决坐标轴中文乱码)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 使用黑体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 1. 数据加载与预览
print("数据预览:")
df = pd.read_csv(r'd:\Users\Administrator\Desktop\实验四\优惠券核销数据.csv')
display(df.head())
print("\n数据基本信息:")
print(df.info())
print("\n数据统计描述:")
display(df.describe())
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
# 2. 数据可视化分析
plt.figure(figsize=(15, 10))
# 性别与核销关系
plt.subplot(2, 2, 1)
sns.countplot(x='Sex', hue=df['Accepted'].astype(str), data=df)
plt.title('不同性别优惠券核销情况')
plt.xlabel('性别(1=女,2=男)')
plt.ylabel('人数')
plt.legend(['未核销', '已核销'])
# 年龄段与核销关系
plt.subplot(2, 2, 2)
sns.countplot(x='Age', hue=df['Accepted'].astype(str), data=df)
plt.title('不同年龄段优惠券核销情况')
plt.xlabel('年龄段(1=中青年,2=中老年)')
plt.ylabel('人数')
plt.legend(['未核销', '已核销'])
# 商品品类与核销关系
plt.subplot(2, 2, 3)
sns.countplot(x='Class', hue=df['Accepted'].astype(str), data=df)
plt.title('不同液奶品类优惠券核销情况')
plt.xlabel('液奶品类(1=低端,2=中档,3=高端)')
plt.ylabel('人数')
plt.legend(['未核销', '已核销'])
# 平均消费额分布
plt.subplot(2, 2, 4)
sns.histplot(data=df, x='AvgSpending', hue='Accepted', multiple='stack', kde=True)
plt.title('平均消费额分布与核销关系')
plt.xlabel('平均消费额(元)')
plt.ylabel('人数')
plt.tight_layout()
plt.show()
# 3. 数据预处理
# 定义特征和目标变量
X = df.drop('Accepted', axis=1)
y = df['Accepted']
# 划分训练测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 预处理管道
categorical_features = ['Sex', 'Age', 'Class']
numerical_features = ['AvgSpending']
preprocessor = ColumnTransformer(
transformers=[
('num', StandardScaler(), numerical_features),
('cat', OneHotEncoder(drop='first'), categorical_features)
])
# 4. 模型训练
model = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', LogisticRegression(max_iter=1000, solver='lbfgs', verbose=1))
])
model.fit(X_train, y_train)
# 5. 模型评估
y_pred = model.predict(X_test)
y_pred_proba = model.predict_proba(X_test)[:, 1]
# 输出评估指标
print("\n模型评估结果:")
print(f"准确率: {accuracy_score(y_test, y_pred):.2f}")
print("混淆矩阵:\n", pd.DataFrame(confusion_matrix(y_test, y_pred),
index=['实际未核销', '实际已核销'],
columns=['预测未核销', '预测已核销']))
print("\n分类报告:\n", classification_report(y_test, y_pred, target_names=['未核销', '已核销']))
# 绘制ROC曲线
fpr, tpr, _ = roc_curve(y_test, y_pred_proba)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr, label=f'ROC曲线 (AUC = {roc_auc_score(y_test, y_pred_proba):.2f})')
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('假阳性率')
plt.ylabel('真阳性率')
plt.title('ROC曲线与AUC值')
plt.legend()
plt.show()
# 6. 特征重要性分析
# 获取特征名称
ohe = model.named_steps['preprocessor'].transformers_[1][1]
feature_names = (numerical_features +
[f"{col}_{cat}" for col, cats in zip(categorical_features, ohe.categories_)
for cat in cats[1:]])
# 创建系数DataFrame
coef_df = pd.DataFrame({
'特征名称': feature_names,
'影响系数': model.named_steps['classifier'].coef_[0]
}).sort_values(by='影响系数', ascending=False)
print("\n特征影响系数排序:")
display(coef_df)
# 可视化特征重要性
plt.figure(figsize=(12, 6))
sns.barplot(x='影响系数', y='特征名称', data=coef_df)
plt.title('特征对核销概率的影响程度')
plt.xlabel('逻辑回归系数(正值促进核销,负值抑制核销)')
plt.show()
# 7. 模型参数输出
print("\n模型训练详情:")
print(f"最大迭代次数: {model.named_steps['classifier'].max_iter}")
print(f"实际迭代次数: {model.named_steps['classifier'].n_iter_[0]}")
print(f"是否收敛: {'是' if model.named_steps['classifier'].n_iter_ < model.named_steps['classifier'].max_iter else '否'}")