如何基于Pandas DataFrame的value_counts运行K-means聚类并可视化?
解决KMeans基于访问频次聚类IP的问题
你的问题核心在于KMeans只能处理数值型特征,而你的frq DataFrame里包含字符串类型的IP列,直接传入整个DataFrame会导致模型无法处理非数值数据。我们的目标是基于IP的访问频次(Frq列)来聚类,所以只需要用这一列数值数据进行聚类即可,之后再把聚类标签映射回每个IP。
步骤1:修正KMeans聚类代码
只传入Frq列的二维数值数组(sklearn要求输入是二维格式),代码如下:
from sklearn.cluster import KMeans import pandas as pd # 你的预处理代码(保持不变) frq = pd.DataFrame.from_dict(presult["ClientIP"].value_counts().to_dict(), orient="index").reset_index() frq.columns = ['IP', 'Frq'] # 仅基于Frq列进行聚类,注意用双括号[[ ]]保持二维结构 kmeans_model = KMeans(n_clusters=3, random_state=1).fit(frq[['Frq']]) # 添加聚类标签到原DataFrame frq['kmean'] = kmeans_model.labels_ # 查看各聚类的IP数量 print(frq['kmean'].value_counts()) # 查看带标签的完整数据 print(frq.head())
这样就能成功运行了,因为我们只给模型传入了数值型的访问频次数据,IP列只是用来标识样本,不参与聚类计算。
步骤2:聚类结果可视化
接下来可以用几种方式可视化聚类结果,帮助你理解不同聚类的特征:
方式1:箱线图展示各聚类的频次分布
用箱线图可以直观看到每个聚类的访问频次范围、中位数等统计特征:
import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize=(10,6)) sns.boxplot(x='kmean', y='Frq', data=frq) plt.title('IP访问频次的KMeans聚类分布') plt.xlabel('聚类标签') plt.ylabel('访问频次') plt.show()
方式2:柱状图展示各聚类的IP数量和平均频次
如果想对比不同聚类的IP数量和平均频次,可以组合柱状图:
cluster_stats = frq.groupby('kmean').agg( IP数量=('IP', 'count'), 平均频次=('Frq', 'mean') ).reset_index() fig, ax1 = plt.subplots(figsize=(10,6)) # 左侧柱状图:IP数量 ax1.bar(cluster_stats['kmean'], cluster_stats['IP数量'], color='skyblue') ax1.set_xlabel('聚类标签') ax1.set_ylabel('IP数量', color='skyblue') ax1.tick_params(axis='y', labelcolor='skyblue') # 右侧折线图:平均频次 ax2 = ax1.twinx() ax2.plot(cluster_stats['kmean'], cluster_stats['平均频次'], color='orange', marker='o', linewidth=2) ax2.set_ylabel('平均访问频次', color='orange') ax2.tick_params(axis='y', labelcolor='orange') plt.title('各聚类的IP数量与平均访问频次') plt.show()
方式3:散点图展示IP的聚类分布(按频次排序)
如果想看到每个IP的聚类归属,可以先按频次排序,再用散点图展示:
frq_sorted = frq.sort_values('Frq', ascending=False) plt.figure(figsize=(12,6)) sns.scatterplot(x=range(len(frq_sorted)), y='Frq', hue='kmean', data=frq_sorted, palette='viridis') plt.title('按访问频次排序的IP聚类分布') plt.xlabel('IP排序(从高到低频次)') plt.ylabel('访问频次') plt.legend(title='聚类标签') plt.show()
额外说明
- 如果之后你想基于IP本身的特征(比如IP段)进行聚类,需要先把IP地址转换成数值型特征(比如拆分四段转成整数,或者用One-Hot编码、哈希编码等方式),但从你的需求来看,基于访问频次聚类已经足够解决问题。
- 可以用肘部法则(Elbow Method)来选择最优的聚类数
n_clusters,而不是固定为3:
inertias = [] for k in range(1, 10): kmeans = KMeans(n_clusters=k, random_state=1) kmeans.fit(frq[['Frq']]) inertias.append(kmeans.inertia_) plt.figure(figsize=(10,6)) plt.plot(range(1,10), inertias, marker='o') plt.title('肘部法则选择最优聚类数') plt.xlabel('聚类数k') plt.ylabel('惯性值(Inertia)') plt.show()
找到曲线的“肘部”点(惯性值下降明显变慢的位置),就是合适的聚类数。
内容的提问来源于stack exchange,提问作者wishi
相关产品推荐
相关产品推荐

