2022年MathorCup数学建模竞赛:基站聚类思路

辐射归并算法和聚类实践

  • 算法运作基本原理
  • 算法的 Python 实现
  • 聚类结果

算法运作基本原理

题中给出的弱覆盖点聚类原理是一种“传染机制”:距离不大于 20 的两点属于同一类别,因此通过点和点之间的“传染标记”得到一个完整的类别,算法原理如同病毒扩散。因此,我们将“类别传染者”命名为“辐射点”,未归类点命名为“待归类点”。

因此,如果想得到一类,应当从一个辐射点开始在所有待归类点中寻找与其距离不超过 20 的点后将它们标记为相同类别,并将每次迭代后类中的新增点作为下一次迭代的辐射点,将每次迭代后剩下的为归类点作为下一次迭代的待归类点……在某一次迭代后如果没有出现新增点(即下一次迭代无辐射点),则本轮迭代结束。依照这种迭代规则,每一轮迭代停止后可以得到一类;下一轮迭代将重新从所有剩下的“待归类”点中进行。

算法的 Python 实现

首先,撰写用于分出聚类单类别的函数SingleClass(*args, **kwargs)

'''
df 代表用 DataFrame 格式存放的点集
classtype 为人工输入的类编号
dis_square 为距离阈值的平方(题中为 20 ** 2 即 400)
'''
def SingleClass(df, classtype, dis_square):
    # 初始迭代,“class”字段存放点被归入的类别编号,因此没有被归类的点该字段存放空值
    newdf = df.loc[df['class'].isnull()].copy()
    startindex = newdf.index[0]
    # “dis”字段存放两点间欧氏距离的平方,而并非欧氏距离
    df.loc[:, 'dis'] = (df.loc[startindex, 'x'] - df['x']) ** 2 + (df.loc[startindex, 'y'] - df['y']) ** 2
    df.loc[df['dis'] <= dis_square, 'class'] = classtype
    index_update = df.loc[df['dis'] <= dis_square].index
    # 完成了从空到有类的步骤,然后进入循环
    while True:
        newdf = df.loc[df['class'].isnull()].copy()
        last_increased_index = []
        for i in index_update:
            newdf.loc[:, 'dis'] = (newdf.loc[:, 'x'] - df.loc[i, 'x']) ** 2 + (newdf.loc[:, 'y'] - df.loc[i, 'y']) ** 2
            last_increased_index.extend(newdf[newdf['dis'] <= dis_square].index)
        # 去除重复的点索引
        last_increased_index = list(set(last_increased_index))
        
        if last_increased_index == []: # 如果没有找到满足要求的点则跳出循环
            break
        else: # 更改类值添加分类
            df.loc[last_increased_index, 'class'] = classtype
            index_update = last_increased_index
    return df

第二步,撰写利用SingleClass(*args, **kwargs)函数进行迭代的总聚类函数Cluster(*args, **kwargs)

'''
df 为输入的点集
dis_square 为距离阈值的平方(本题中即为400)
'''
def Cluster(df, dis_square):
    count = 1
    while True:
        df_null = df[df['class'].isnull()]
        if df_null.shape[0] == 0:
            return df
        else:
            df = SingleClass(df, f'Classed-{count}', dis_square)
            print(f'类别{count}, 完成!')
            count += 1

使用两个函数进行聚类的前提是预处理数据集,使其具有“class”字段和“dis_square”字段。因此,在调用函数Cluster(*args, **kwargs)之前,要对原数据集进行预处理。

if __name__ == '__main__':
	# 使用文件路径读取数据集
    df = pd.read_csv('*****.csv')
    df[['dis', 'class']] = np.nan
    df = Cluster(df, dis_square = 400)
    df.drop(['dis'], axis = 1, inplace = True)
    # df.to_csv('聚类结果.csv', index=False)

聚类结果

最后,通过作图,可以直观看出聚类结果。下方两张图分别为聚类前和聚类后的点簇图。经过聚类,一共得到898类,所有弱覆盖栅格点均被归类

你可能感兴趣的:(聚类,机器学习,算法,python)