You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨时间窗口K-means聚类匹配:基于企业重叠度的编号对齐

实现跨月度滚动窗口的K-means聚类匹配与编号调整

我来给你梳理一下具体的实现思路和代码,完全贴合你提出的跨窗口聚类匹配需求——核心就是先量化聚类间的企业重叠度,再根据规则建立对应关系,最后调整聚类编号让跨窗口的同类聚类保持一致编号。

第一步:数据预处理,提取每个聚类的企业集合

首先我们需要把每个(窗口, 聚类)对应的企业列表转成集合,这样后续计算交集会非常高效。假设你的原始DataFrame名为df,代码如下:

import pandas as pd
import numpy as np
from itertools import product

# 把窗口转成可排序的月度周期格式(比如'2023-01'转成Period类型)
df['Window'] = pd.to_datetime(df['Window']).dt.to_period('M')

# 按窗口和聚类分组,提取每个聚类的企业集合
cluster_firm_sets = df.groupby(['Window', 'Cluster'])['Company'].apply(set).reset_index()
# 按窗口时间排序,确保后续处理是按滚动顺序来的
cluster_firm_sets = cluster_firm_sets.sort_values('Window').reset_index(drop=True)

第二步:定义匹配度计算函数

我们需要一个函数来计算窗口t的聚类和窗口t+1的聚类之间的匹配分数,也就是t聚类中同时出现在t+1聚类里的企业占比:

def calculate_match_ratio(firms_t, firms_t1):
    # 避免空聚类导致的除以0错误
    if len(firms_t) == 0:
        return 0.0
    # 计算t聚类企业在t1聚类中的占比
    return len(firms_t & firms_t1) / len(firms_t)

第三步:遍历窗口对,建立聚类对应关系

接下来我们要逐个处理连续的窗口对(t和t+1),计算所有聚类间的匹配分数,然后根据≥60%的规则找到对应关系,同时处理可能的冲突(比如多个t聚类匹配同一个t+1聚类):

# 获取所有按时间排序的唯一窗口
sorted_windows = sorted(cluster_firm_sets['Window'].unique())

# 存储最终的聚类映射:键是(窗口, 原聚类编号),值是调整后的统一聚类编号
cluster_mapping = {}

# 初始化第一个窗口的映射:聚类编号保持不变
first_window = sorted_windows[0]
first_clusters = cluster_firm_sets[cluster_firm_sets['Window'] == first_window]['Cluster'].unique()
for clus in first_clusters:
    cluster_mapping[(first_window, clus)] = clus

# 从第二个窗口开始,逐个和前一个窗口匹配
for idx in range(1, len(sorted_windows)):
    curr_window = sorted_windows[idx]
    prev_window = sorted_windows[idx-1]
    
    # 提取前窗口和当前窗口的聚类数据
    prev_cluster_data = cluster_firm_sets[cluster_firm_sets['Window'] == prev_window]
    curr_cluster_data = cluster_firm_sets[cluster_firm_sets['Window'] == curr_window]
    
    # 构建匹配矩阵:行=前窗口聚类,列=当前窗口聚类,值=匹配分数
    match_matrix = pd.DataFrame(
        index=prev_cluster_data['Cluster'],
        columns=curr_cluster_data['Cluster'],
        dtype=np.float64
    )
    
    # 填充匹配矩阵的所有元素
    for prev_clus, curr_clus in product(prev_cluster_data['Cluster'], curr_cluster_data['Cluster']):
        prev_firms = prev_cluster_data[prev_cluster_data['Cluster'] == prev_clus]['Company'].iloc[0]
        curr_firms = curr_cluster_data[curr_cluster_data['Cluster'] == curr_clus]['Company'].iloc[0]
        match_matrix.loc[prev_clus, curr_clus] = calculate_match_ratio(prev_firms, curr_firms)
    
    # 按匹配分数从高到低排序,优先处理分数高的匹配,避免冲突
    sorted_matches = match_matrix.unstack().reset_index(name='score').sort_values('score', ascending=False)
    
    # 存储前窗口聚类到当前窗口聚类的对应关系,同时记录已被使用的当前聚类
    prev_to_curr = {}
    used_curr_clusters = set()
    
    for _, row in sorted_matches.iterrows():
        prev_clus = row['Cluster']
        curr_clus = row['level_0']
        score = row['score']
        
        # 只保留满足≥60%、未被匹配过的对应关系
        if prev_clus not in prev_to_curr and curr_clus not in used_curr_clusters and score >= 0.6:
            prev_to_curr[prev_clus] = curr_clus
            used_curr_clusters.add(curr_clus)
    
    # 构建当前窗口的聚类编号映射:原编号 → 调整后的统一编号(对应前窗口的编号)
    curr_cluster_map = {}
    unused_prev_clusters = set(prev_cluster_data['Cluster']) - set(prev_to_curr.keys())
    
    for curr_clus in curr_cluster_data['Cluster']:
        # 找到当前聚类对应的前窗口聚类编号
        matched_prev_clus = next((k for k, v in prev_to_curr.items() if v == curr_clus), None)
        if matched_prev_clus is not None:
            curr_cluster_map[curr_clus] = matched_prev_clus
        else:
            # 如果没有匹配到,分配一个未被使用的前窗口编号,避免重复
            if unused_prev_clusters:
                curr_cluster_map[curr_clus] = unused_prev_clusters.pop()
            else:
                # 极端情况:所有前窗口编号都被用了,保留原编号
                curr_cluster_map[curr_clus] = curr_clus
    
    # 把当前窗口的映射存入总字典
    for curr_clus, new_clus in curr_cluster_map.items():
        cluster_mapping[(curr_window, curr_clus)] = new_clus

第四步:将调整后的聚类编号应用到原始数据

最后我们把调整后的聚类编号添加到原始DataFrame中,同时可以生成新的Adjusted_WindClus字段:

# 添加调整后的聚类编号
df['Adjusted_Cluster'] = df.apply(
    lambda row: cluster_mapping[(row['Window'], row['Cluster'])],
    axis=1
)

# 生成新的窗口-聚类拼接字段
df['Adjusted_WindClus'] = df['Window'].astype(str) + '_' + df['Adjusted_Cluster'].astype(str)

关键细节说明

  • 冲突处理:通过按匹配分数从高到低排序,优先分配分数最高的对应关系,避免一个当前聚类被多个前聚类抢占的情况。
  • 未匹配聚类:对于没有满足≥60%重叠度的聚类,我们分配一个未被使用的前窗口编号,确保跨窗口的聚类编号始终是1-10的连续值(如果你的原始聚类编号是1-10的话)。
  • 效率优化:用集合来计算企业交集,比列表遍历快得多,适合处理大量企业数据的场景。

内容的提问来源于stack exchange,提问作者Pierrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:54:08