Python下K-Means聚类应用:出租车运营区域判定及预处理疑问
数据集代码与样例
import pandas as pd import random random.seed(901) rand_list1= [] rand_list2= [] rand_list3= [] rand_list4= [] rand_list5= [] for i in range(20): x = random.randint(80,1000) rand_list1.append(x/100) y1 = random.randint(-200,200) rand_list2.append(y1/10) y2 = random.randint(-200,200) rand_list3.append(y2/10) y3 = random.randint(-200,200) rand_list4.append(y3/10) y4 = random.randint(-200,200) rand_list5.append(y4/10) df = pd.DataFrame({'Rainfall Recorded':rand_list1, 'TAXI A':rand_list2, 'TAXI B':rand_list3, 'TAXI C':rand_list4, 'TAXI D':rand_list5}) df.head()
数据集前5行预览:
| Rainfall Recorded | TAXI A | TAXI B | TAXI C | TAXI D |
|---|---|---|---|---|
| 5.21 | 13.7 | -5.0 | -14.2 | 9.8 |
| 2.39 | -0.3 | 18.8 | 4.8 | -6.4 |
| 8.09 | 15.0 | -3.6 | 18.6 | 12.7 |
| 5.79 | -0.2 | 14.6 | 0.9 | 3.8 |
| 7.48 | 10.9 | 9.0 | 15.4 | -16.5 |
问题描述
给定区域降雨量(单位:厘米)及受访出租车上报的营收变化百分比,假设降雨量与营收变化存在关联,请问能否使用K-Means Clustering判定这些出租车是否在本地运营?我从网络获取了一段简单代码:
km = KMeans(n_clusters=2) y_predicted = km.fit_predict(df[['TAXI','Rainfall Recorded']]) y_predicted但不确定使用该代码前需执行哪些数据转换操作。
可行性分析
可以尝试用K-Means聚类实现,但核心前提是:本地运营的出租车,营收变化与降雨量的关联模式会高度相似;而非本地运营的出租车,营收变化和本地降雨量的关联会很弱,甚至模式完全不同。如果这个前提成立,K-Means会把关联模式一致的出租车归为同一簇,从而区分本地/非本地车辆。但如果所有车辆的营收-降雨关联都无明显规律,或规律趋同,聚类就起不到区分作用。
数据转换与预处理步骤
你拿到的代码有明显问题:原数据集没有名为TAXI的列,且数据结构不符合K-Means的样本-特征要求,需按以下步骤调整:
1. 重构数据集结构
K-Means需要「单样本对应一组特征」的格式,这里要把宽表转成长表,让每一行对应「某辆出租车某一天的降雨量+营收变化」:
# 宽表转长表,提取出租车ID、对应日期的营收变化 df_long = df.melt(id_vars='Rainfall Recorded', var_name='Taxi_ID', value_name='Revenue_Change')
转换后的数据结构示例:
| Rainfall Recorded | Taxi_ID | Revenue_Change |
|---|---|---|
| 5.21 | TAXI A | 13.7 |
| 2.39 | TAXI A | -0.3 |
2. 特征标准化
K-Means是基于距离计算的算法,降雨量(数值范围0.8-10)和营收变化百分比(数值范围-20-20)的量纲差异会干扰距离计算,必须做标准化处理:
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() # 对降雨量和营收变化做标准化,生成新特征列 df_long[['Rainfall_Scaled', 'Revenue_Scaled']] = scaler.fit_transform( df_long[['Rainfall Recorded', 'Revenue_Change']] )
3. 调整聚类输入(两种可选思路)
思路一:基于「营收-降雨相关性」聚类(更直接)
本地出租车的营收与降雨的相关性应该趋同,非本地的相关性则接近0或完全不同。先计算每辆出租车的营收-降雨相关系数,再用该系数做聚类:
from sklearn.cluster import KMeans import pandas as pd # 计算每辆出租车的营收变化与降雨量的皮尔逊相关系数 taxi_corr = df_long.groupby('Taxi_ID').apply( lambda x: x['Revenue_Change'].corr(x['Rainfall Recorded']) ).reset_index(name='Rainfall_Revenue_Corr') # 执行K-Means聚类(设定2类:本地/非本地) km = KMeans(n_clusters=2, random_state=901) taxi_corr['Cluster'] = km.fit_predict(taxi_corr[['Rainfall_Revenue_Corr']])
思路二:基于「每日数据特征」聚类
用标准化后的降雨量和营收变化作为特征,对每日数据聚类,再统计每辆出租车落在不同簇的比例——比例集中在单个簇的车辆,营收-降雨模式一致(大概率本地):
km = KMeans(n_clusters=2, random_state=901) df_long['Cluster'] = km.fit_predict(df_long[['Rainfall_Scaled', 'Revenue_Scaled']]) # 统计每辆出租车的簇分布 taxi_cluster_dist = df_long.groupby('Taxi_ID')['Cluster'].value_counts(normalize=True)
4. 可选:验证聚类合理性
用肘部法则验证设定的聚类数(比如n=2)是否合理:
import matplotlib.pyplot as plt inertias = [] # 尝试1-5个聚类数 for k in range(1,6): km = KMeans(n_clusters=k, random_state=901) km.fit(df_long[['Rainfall_Scaled', 'Revenue_Scaled']]) inertias.append(km.inertia_) plt.plot(range(1,6), inertias, marker='o') plt.xlabel('聚类数量') plt.ylabel('簇内平方和') plt.show()
如果曲线在k=2处出现明显拐点,说明设定2类是合理的。
内容的提问来源于stack exchange,提问作者prashanth manohar

