环形周期客流量数据的Python聚类实现方案问询
环形周期数据的Python聚类方案
针对你提到的餐厅夜店客流量的时间周期数据(23点与0点连续),以下是几个可落地的Python聚类方案:
方案1:映射到二维单位圆 + 常规聚类算法
把一维环形时间数据转换成二维平面的单位圆坐标,让周期连续性被常规算法识别,之后用GMM/K-Means都可实现,支持指定聚类数。
实现步骤:
- 将时间(0-23)转换为弧度:
theta = (time / 24) * 2 * np.pi(用24确保0和24/23点对应圆上同一位置) - 计算二维坐标:
x = np.cos(theta), y = np.sin(theta) - 用sklearn的
GaussianMixture或KMeans执行聚类 - 将聚类中心的二维坐标转换回原时间维度
代码示例:
import numpy as np from sklearn.mixture import GaussianMixture # 模拟围绕0和12.5分布的环形数据 np.random.seed(42) data = np.concatenate([np.random.normal(0, 1, 200), np.random.normal(12.5, 1, 200)]) data = np.mod(data, 24) # 处理超出0-23的数值 # 转换为单位圆坐标 theta = (data / 24) * 2 * np.pi x = np.cos(theta) y = np.sin(theta) X = np.column_stack([x, y]) # 指定2个聚类,用GMM拟合 gmm = GaussianMixture(n_components=2, random_state=42) labels = gmm.fit_predict(X) # 将聚类中心转回时间维度 centers_theta = np.arctan2(gmm.means_[:, 1], gmm.means_[:, 0]) centers_time = (centers_theta % (2 * np.pi)) * 24 / (2 * np.pi) print("聚类中心时间:", centers_time) # 输出接近0和12.5
方案2:环形高斯混合模型(基于冯·米塞斯分布)
冯·米塞斯(Von Mises)分布是正态分布在环形数据上的等价形式,直接用该分布构建混合模型,更贴合周期数据特性。支持通过BIC准则自动选择聚类数。
代码示例(手动拟合双组件Von Mises混合模型):
import numpy as np from scipy.stats import vonmises from scipy.optimize import minimize # 将数据转换为0-2π的弧度 data_rad = (data / 24) * 2 * np.pi # 定义混合模型的负对数似然函数 def neg_log_likelihood(params): mu1, kappa1, mu2, kappa2, weight = params # 约束权重在0-1区间 if weight < 0 or weight > 1: return np.inf ll = np.log(weight * vonmises.pdf(data_rad, kappa1, loc=mu1) + (1 - weight) * vonmises.pdf(data_rad, kappa2, loc=mu2)) return -np.sum(ll) # 初始参数:mu对应0和12.5的弧度,kappa为浓度参数(类似正态分布方差的倒数) init_params = [0, 5, (12.5/24)*2*np.pi, 5, 0.5] result = minimize(neg_log_likelihood, init_params, method='L-BFGS-B') # 提取结果并转回时间 mu1_time = (result.x[0] % (2*np.pi)) *24/(2*np.pi) mu2_time = (result.x[2] % (2*np.pi)) *24/(2*np.pi) print("聚类中心时间:", mu1_time, mu2_time)
方案3:自定义环形距离的DBSCAN(无需指定聚类数)
DBSCAN是基于密度的聚类算法,无需预先指定聚类数。核心是将距离度量替换为环形距离:即两个时间点的最小间隔(比如23和0的距离为1,而非23)。
代码示例:
import numpy as np from sklearn.cluster import DBSCAN # 自定义环形距离函数 def circular_distance(a, b): return np.minimum(np.abs(a - b), 24 - np.abs(a - b)) # 用DBSCAN聚类,调整eps和min_samples控制聚类效果 dbscan = DBSCAN(eps=1.5, min_samples=5, metric=circular_distance) labels = dbscan.fit_predict(data.reshape(-1, 1)) # 计算每个聚类的环形均值作为中心 unique_labels = np.unique(labels[labels != -1]) cluster_centers = [] for label in unique_labels: cluster_data = data[labels == label] rad = (cluster_data /24)*2*np.pi mean_rad = np.arctan2(np.mean(np.sin(rad)), np.mean(np.cos(rad))) mean_time = (mean_rad % (2*np.pi)) *24/(2*np.pi) cluster_centers.append(mean_time) print("聚类中心时间:", cluster_centers)
内容的提问来源于stack exchange,提问作者masteryoda436
相关产品推荐
相关产品推荐

