【机器学习与实战】分类与聚类算法:K-Means聚类分析-02实战演练

105 阅读8分钟

【机器学习与实战】分类与聚类算法:K-Means聚类分析-02实战演练

配套视频课程:www.bilibili.com/video/BV1iS…

一、问题的提出
1、数据集情况

本案例中的业务场景为,通过各类广告渠道90天内额日均UV,平均注册率、平均搜索率、访问深度、平均停留时长、订单转化率、投放时间、素材类型、广告类型、合作方式、广告尺寸和广告卖点等特征,将渠道分类,找出每类渠道的重点特征,为接下来的业务讨论和数据分析提供支持。

假如你们公司投放广告的渠道很多,每个渠道的客户性质也可能不同,比如在优酷视频投广告和今日头条投放广告,效果可能会有差异。现在需要对广告效果分析实现有针对性的广告效果测量和优化工作。

数据集基本信息如下:

1.png

利用Python获取更多数据集信息:

data_raw = pd.read_csv("./ad_performance.csv")
print(data_raw.head())                    # 前5行数据和列的分布
print(pd.DataFrame(data_raw.dtypes).T)  # 打印数据类型分布
print(data_raw.describe().round(2).T)      # 打印原始数据基本描述性信息
print(data_raw.isnull().sum())          # 查看具有缺失值的列

发现列 平均停留时间 有2个缺失值,所以需要进行缺失值填充,比较常用的方案就是要么删除这2行,或者利用平均值进行填充:

# data_raw.dropna(inplace=True)
data_raw.fillna({'平均停留时间': data['平均停留时间'].mean()}, inplace=True)
print(data_raw.isnull().sum())
2、相关性分析

由于数据集中,某些列并非数值,所以先提取已有的数值列进行相关性分析

data = data_raw.iloc[:, 2:9]
print(data.corr().round(2))

结果为:

        日均UV  平均注册率  平均搜索量  访问深度  平均停留时间  订单转化率  投放总时间
日均UV    1.00  -0.05  -0.07 -0.02    0.04  -0.05  -0.04
平均注册率  -0.05   1.00   0.24  0.11    0.22   0.32  -0.01
平均搜索量  -0.07   0.24   1.00  0.06    0.17   0.13  -0.03
访问深度   -0.02   0.11   0.06  1.00    0.72   0.16   0.06
平均停留时间  0.04   0.22   0.17  0.72    1.00   0.25   0.05
订单转化率  -0.05   0.32   0.13  0.16    0.25   1.00  -0.00
投放总时间  -0.04  -0.01  -0.03  0.06    0.05  -0.00   1.

基本可以看出,访问深度和平均停留时间的相关性最高,相关性高说明两个变量在建立模型的时候,作用是一样或者效果是一样的,可以考虑组合或者删除其一。

也可以使用Seaborn进行热力图绘制,这样看起来更加直观:

data = data_raw.iloc[:, 2:9]
from pylab import mpl
mpl.rcParams["font.sans-serif"] = ["SimHei"]
mpl.rcParams["axes.unicode_minus"] = False
import seaborn as sns
corr=data.corr().round(2)
sns.heatmap(corr,cmap="Reds",annot=True)
plt.show()

2.png

二、数据预处理

1、独热编码构造新数据集

# 独热编码,先了解一下各个列的基本情况
cols=["素材类型","广告类型","合作方式","广告尺寸","广告卖点"]
for x in cols:
    uniq = data_raw[x].unique()
    print(f"列 {x} 的取值有:{uniq}")
# 使用OneHotEncoder进行独热编码
cols = ['素材类型','广告类型','合作方式','广告尺寸','广告卖点']
ohe_matrix=pd.get_dummies(data_raw[cols])
print(ohe_matrix.head(5))
data = pd.concat((data_raw.iloc[:, 2:9], ohe_matrix), axis=1)
print(data.head())

上述代码可以通过concat合并DataFrame数据,但是数值类数据还未进行标准化,所以还需要重新处理。

2、数据标准化

data = data_raw.iloc[:, 2:9]
scaler = MinMaxScaler()
data = scaler.fit_transform(data)
data = pandas.DataFrame(data)
# 重新设置列名
data.rename(columns={0:"日均UV", 1:"平均注册率",2:"平均搜索量",3:"访问深度",
                     4:"平均停留时间",5:"订单转化率",6:"投放总时间"}, inplace=True)
data = pd.concat((data, ohe_matrix), axis=1)
print(data.head())

由于数据标准化使用了SKLearn,所以将data转换成了numpy数组,进而失去了列名称,所以此时要么利用DataFrame强转并重新设置列名,当然,在进行实际运算过程中,列名并不重要,所以也可以直接使用Numpy的hstack进行合并:

data = data_raw.iloc[:, 2:9]
scaler = MinMaxScaler()
data = scaler.fit_transform(data)
data = np.hstack((data, ohe_matrix))
三、模型训练与评估

1、先快速实现一段KMeans代码:

model = KMeans(n_clusters=5)
y_pred = model.fit_predict(data)
plt.scatter(data[:, 0], data[:, 1], c=y_pred)
plt.show()

3.png

上述代码存在什么问题?

(1)设置 n_clusters=5 是否是最好的分类数量?我们并不清楚,所以我们需要有一套评价标准。

(2)基于前面的数据预处理阶段发现,目前的数据集有34列,那么 data[:, 0], data[:, 1] 作为图中的x和y轴,只是其中2列数据的展示而已,不具备任何实际意义。

2、引入评价标准

比较常使用的评估标准有:轮廓系数法(Silhouette Coefficient) 和 CH系数法(Calinski-Harabasz Index),无论采用哪一个均可,用于更好的确定K值。我们采用轮廓系数法来评估一下K值取多少时效果最好:

scores = list()  # 用来存储每个K下模型的平局轮廓系数
silhouette = -1  # 初始化的平均轮廓系数阀值
for n in range(2, 8):
    model = KMeans(n_clusters=n)
    y_pred = model.fit_predict(data)
    tmp = silhouette_score(data, y_pred)  # 得到每个K下的平均轮廓系数
    if tmp > silhouette:
        best_k = n
        silhouette = tmp
    scores.append([n, tmp])      # 将每次K及其得分追加到列表
print(scores)
print(f'最优的K值是:{best_k} \n对应的轮廓系数是:{silhouette}')

输出的结果可能如下:

[[2, 0.38609733207697233], [3, 0.45693419777401045], [4, 0.5013910017581341], [5, 0.4957553420627367], [6, 0.500401436910274], [7, 0.4818033786151394]]
最优的K值是:4 
对应的轮廓系数是:0.5013910017581341

如果结果有所区别,可以多尝试几次,找到一个最优解。为什么会不同的结果,这是由于算法的随机性导致的。K-Means算法是一种迭代聚类算法,其结果取决于初始聚类中心的选择和迭代过程中的随机性。如果需要固定聚类结果,可以通过设置随机种子(random_state参数)来控制算法的随机性。通过固定随机种子,可以确保在相同的数据集和参数设置下,算法的运行结果是确定性的。

model = KMeans(n_clusters=4, random_state=1)

3、合并标签并写入

model = KMeans(n_clusters=4)
y_pred = model.fit_predict(data)
label_data = pd.concat((data_raw, pd.DataFrame(y_pred, columns=['clusters'])), axis=1)
label_data.to_excel("./ad-label.xlsx")

写入文件后,相当于给每一行进行了分类,这样就可以分析各个渠道的一些相关性了,比如查看一下各个类别的占比情况等:

count = pd.DataFrame(label_data['渠道代号'].groupby(label_data['clusters']).count()) # 计算每个聚类类别的样本量
ratio = (count / len(label_data))  # 计算每个聚类类别的样本量占比
print(count)
print(ratio)

4、查看各类别显著特征

# 计算各个聚类类别内部最显著特征值
features = []       # 空列表,用于存储最终合并后的所有特征信息
for n in range(4):  # 读取每个类索引
    label = label_data[label_data['clusters'] == n]  # 获得特定类的数据
    number = label.iloc[:, 2:9]            # 获得数值型数据特征
    number_desc = number.describe().round(3)    # 得到数值型特征的描述性统计信息
    number_feature = number_desc.iloc[1, :]     # 得到数值型特征的均值
    string = label.iloc[:, 9:-1]               # 获得字符串型数据特征
    string_desc = string.describe(include='all')    # 获得字符串型数据特征的描述性统计信息
    string_feature = string_desc.iloc[2, :]         # 获得字符串型数据特征的最频繁值
    merge_line = pd.concat((number_feature, string_feature), axis=0)  # 将数值型和字符串型典型特征沿行合并
    features.append(merge_line)                     # 将每个类别下的数据特征追加到列表
#  输出完整的类别特征信息
features = pd.DataFrame(features).T
# 也可以再附加上count和ratio,进行整体分析
result = pd.concat((count.T, ratio.T, features),axis=0)  # 将每个聚类类别的所有信息合并
print(result)

最终输出结果可能为:

               0         1         2         3
渠道代号         142       345       158       244
渠道代号     0.15973  0.388076  0.177728  0.274466
日均UV    1184.601   303.675   598.319   464.332
平均注册率      0.002     0.001     0.003     0.001
平均搜索量      0.045     0.016      0.02      0.05
访问深度       2.092     2.273     2.184     2.051
平均停留时间   393.595   239.855   241.558   232.404
订单转化率      0.005     0.002     0.003     0.003
投放总时间     15.549    15.365    15.639    17.586
素材类型         swf       jpg       jpg       swf
广告类型        tips        横幅    banner       不确定
合作方式         cpc       cpc       cpc       roi
广告尺寸      600*90    600*90   308*388    600*90
广告卖点          打折        直降        满减        打折
四、特征降维

降维的算法有很多,比如 奇异值分解(SVD)、主成分分析(PCA)、因子分析(FA)、独立成分分析(ICA)等。具体算法可以参考:

zhuanlan.zhihu.com/p/37777074,…

from sklearn.decomposition import PCA
data = [[2,8,4,5], [6,3,0,8], [5,4,9,1]]
# 1、实例化PCA, 小数——保留多少信息
transfer = PCA(n_components=0.9)
# 2、调用fit_transform
data1 = transfer.fit_transform(data)
print("保留90%的信息,降维结果为:\n", data1)
# 1、实例化PCA, 整数——指定降维到的维数
transfer2 = PCA(n_components=3)
# 2、调用fit_transform
data2 = transfer2.fit_transform(data)
print("降维到3维的结果:\n", data2)

有了特征降维以后,我们就可以将上述数据进行降维处理后再进行聚类分析,比如降至2维并进行绘图:

pca = PCA(n_components=2)
data = pca.fit_transform(data)
model = KMeans(n_clusters=4, random_state=1)
y_pred = model.fit_predict(data)
label_data = pd.concat((data_raw, pd.DataFrame(y_pred, columns=['clusters'])), axis=1)
centers = model.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=150)
plt.scatter(data[:, 0], data[:, 1], c=y_pred)
plt.show()

4.png

不妨再试试,进行降维之后,分类和占比,以及数据关键特征等变化是否明显。