#!/usr/bin/env python
# coding: utf-8
import pandas as pd
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns # 导入seaborn库
from sklearn.preprocessing import MinMaxScaler, OneHotEncoder
from sklearn.metrics import silhouette_score # 导入轮廓系数指标
from sklearn.cluster import KMeans # KMeans模块
## 设置属性防止中文乱码
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False
# 加载数据
raw_data = pd.read_csv(r'./ad_performance.csv')
# 数据审查
print("数据框的前几行:")
print(raw_data.head(2))
print("\n数据类型分布:")
print(raw_data.info())
print("\n原始数据基本描述性信息:")
print(raw_data.describe().round(2).T)
# 查看缺失值情况
na_cols = raw_data.isnull().any(axis=0)
print("\n每一列是否具有缺失值:")
print(na_cols)
print("\n具有缺失值的行总记录数:")
print(raw_data.isnull().sum().sort_values(ascending=False))
# 变量之间的相关性分析
print("\n原始数据相关性信息:")
corr = raw_data.corr(numeric_only=True).round(2)
print(corr.T)
# 热力图
plt.figure(figsize=(10, 8))
sns.heatmap(corr, cmap='coolwarm', annot=True, fmt=".2f")
plt.title('变量相关性热力图')
plt.show()
# 删除平均停留时间列
raw_data2 = raw_data.drop(['平均停留时间'], axis=1)
# 类别变量取值
cols = ["素材类型", "广告类型", "合作方式", "广告尺寸", "广告卖点"]
for x in cols:
data = raw_data2[x].unique()
print(f"\n变量【{x}】的取值有:\n{data}")
# 字符串分类独热编码处理
ohe_matrix1 = pd.get_dummies(raw_data2[cols])
print("\n独热编码后的矩阵头5行:")
print(ohe_matrix1.head(5))
# 数据标准化
sacle_matrix = raw_data2.iloc[:, 1:7] # 获得要转换的矩阵
model_scaler = MinMaxScaler() # 建立MinMaxScaler模型对象
data_scaled = model_scaler.fit_transform(sacle_matrix) # MinMaxScaler标准化处理
print("\n标准化后的数据样本(前5行):")
print(pd.DataFrame(data_scaled.round(2), columns=sacle_matrix.columns).head(5))
# 合并所有维度
X = np.hstack((data_scaled, ohe_matrix1.values))
# 建立模型
score_list = list() # 用来存储每个K下模型的平局轮廓系数
silhouette_int = -1 # 初始化的平均轮廓系数阀值
best_kmeans = None
cluster_labels_k = None
for n_clusters in range(2, 8): # 遍历从2到7几个有限组
model_kmeans = KMeans(n_clusters=n_clusters, random_state=42) # 建立聚类模型对象
labels_tmp = model_kmeans.fit_predict(X) # 训练聚类模型
silhouette_tmp = silhouette_score(X, labels_tmp) # 得到每个K下的平均轮廓系数
if silhouette_tmp > silhouette_int: # 如果平均轮廓系数更高
best_k = n_clusters # 保存最好的K
silhouette_int = silhouette_tmp # 保存平均轮廓得分
best_kmeans = model_kmeans # 保存模型实例对象
cluster_labels_k = labels_tmp # 保存聚类标签
score_list.append([n_clusters, silhouette_tmp]) # 将每次K及其得分追加到列表
print('\n{:*^60}'.format('K值对应的轮廓系数:'))
print(np.array(score_list)) # 打印输出所有K下的详细得分
print(f'\n最优的K值是:{best_k} \n对应的轮廓系数是:{silhouette_int}')
# 将原始数据与聚类标签整合
cluster_labels = pd.DataFrame(cluster_labels_k, columns=['clusters']) # 获得训练集下的标签信息
merge_data = pd.concat((raw_data2, cluster_labels), axis=1) # 将原始处理过的数据跟聚类标签整合
print("\n合并后的数据框前几行:")
print(merge_data.head())
# 计算每个聚类类别下的样本量和样本占比
clustering_count = merge_data.groupby('clusters').size().reset_index(name='counts') # 计算每个聚类类别的样本量
clustering_ratio = clustering_count.copy()
clustering_ratio['percentage'] = (clustering_ratio['counts'] / len(merge_data)).round(2) # 计算每个聚类类别的样本量占比
print("\n每个聚类类别的样本量:")
print(clustering_count)
print("\n#" * 30)
print("\n每个聚类类别的样本量占比:")
print(clustering_ratio)
# 计算各个聚类类别内部最显著特征值
cluster_features = [] # 空列表,用于存储最终合并后的所有特征信息
for line in range(best_k): # 读取每个类索引
label_data = merge_data[merge_data['clusters'] == line] # 获得特定类的数据
part1_data = label_data.iloc[:, 1:7] # 获得数值型数据特征
part1_desc = part1_data.describe().round(3) # 得到数值型特征的描述性统计信息
merge_data1 = part1_desc.iloc[2, :] # 得到数值型特征的均值
part2_data = label_data.iloc[:, 7:-1] # 获得字符串型数据特征
part2_desc = part2_data.describe(include='all') # 获得字符串型数据特征的描述性统计信息
merge_data2 = part2_desc.iloc[2, :] # 获得字符串型数据特征的最频繁值
merge_line = pd.concat((merge_data1, merge_data2), axis=0) # 将数值型和字符串型典型特征沿行合并
cluster_features.append(merge_line) # 将每个类别下的数据特征追加到列表
# 输出完整的类别特征信息
cluster_pd = pd.DataFrame(cluster_features).T # 将列表转化为矩阵
print('\n{:*^60}'.format('每个类别主要的特征:'))
all_cluster_set = pd.concat((clustering_count, clustering_ratio[['percentage']], cluster_pd), axis=1) # 将每个聚类类别的所有信息合并
print(all_cluster_set)
# 曲线图 - 每个集群的日均UV变化趋势
plt.figure(figsize=(12, 6))
for i in range(best_k):
cluster_data = merge_data[merge_data['clusters'] == i]
plt.plot(cluster_data.index, cluster_data['日均UV'], label=f'Cluster {i+1}')
plt.xlabel('Index')
plt.ylabel('日均UV')
plt.title('各聚类类别日均UV变化趋势')
plt.legend()
plt.grid(True)
plt.show()
# 直方图 - 各集群的订单转化率分布
plt.figure(figsize=(12, 6))
for i in range(best_k):
cluster_data = merge_data[merge_data['clusters'] == i]
plt.hist(cluster_data['订单转化率'], bins=10, alpha=0.5, label=f'Cluster {i+1}')
plt.xlabel('订单转化率')
plt.ylabel('Frequency')
plt.title('各聚类类别订单转化率分布')
plt.legend()
plt.grid(True)
plt.show()
# 扇形图 - 各集群的样本占比
plt.figure(figsize=(8, 8))
plt.pie(clustering_ratio['percentage'], labels=[f'Cluster {i+1}' for i in range(best_k)], autopct='%1.1f%%', startangle=140)
plt.title('各聚类类别样本占比')
plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
plt.show()
# 雷达图 - 各集群的主要特征对比
num_sets = cluster_pd.iloc[:best_k, :].astype(np.float64) # 获取要展示的数据
labels = num_sets.index # 设置要展示的数据标签
colors = ['#FF9999','#66B3FF','#99FF99','#FFCC99', '#CCCCFF', '#FF99CC'] # 定义不同类别的颜色
angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False) # 计算各个区间的角度
angles = np.concatenate((angles, [angles[0]])) # 建立相同首尾字段以便于闭合
# 标准化数据
scaler = MinMaxScaler()
num_sets_max_min = scaler.fit_transform(num_sets.T).T # 转置后标准化再转置回来
print("\n每个类别主要的数值特征(标准化后):")
print(num_sets_max_min)
# 画图
fig = plt.figure(figsize=(10, 10)) # 建立画布
ax = fig.add_subplot(111, polar=True) # 增加子网格,注意polar参数
# 画雷达图
for i in range(len(num_sets_max_min)): # 循环每个类别
data_tmp = num_sets_max_min[i, :] # 获得对应类数据
data = np.concatenate((data_tmp, [data_tmp[0]])) # 建立相同首尾字段以便于闭合
ax.plot(angles, data, 'o-', c=colors[i], label=f"第{i+1}类渠道") # 画线
ax.fill(angles, data, alpha=0.25, color=colors[i])
# 设置图像显示格式
ax.set_thetagrids(angles[:-1] * 180 / np.pi, labels, fontproperties="SimHei") # 设置极坐标轴
ax.set_title("各聚类类别显著特征对比", pad=20, fontproperties="SimHei") # 设置标题放置
ax.set_rlim(0, 1.2) # 设置坐标轴尺度范围
ax.grid(True)
plt.legend(loc="upper right", bbox_to_anchor=(1.2, 1.0), prop={'family': 'SimHei'})
plt.tight_layout(rect=[0, 0, 0.8, 1])
plt.show()
# 数据结论
print("\n数据结论:")
print("所有的渠道被分为{}各类别,每个类别的样本量分别为:{}\n对应占比分别为:{}".format(
best_k, clustering_count['counts'].values.tolist(), clustering_ratio['percentage'].values.tolist()))
最新发布