时间序列聚类的直观方法

一、介绍

        我们将使用轮廓分数和一些距离度量来执行时间序列聚类实验,同时利用直观的可视化,让我们看看下面的时间序列:

        这些可以被视为具有正弦、余弦、方波和锯齿波的四种不同的周期性时间序列

        如果我们添加随机噪声和距原点的距离来沿 y 轴移动序列并将它们随机化以使它们几乎难以辨别,则如下所示 - 现在很难将时间序列列分组为簇:

        上面的图表是使用以下脚本创建的:

# Import necessary libraries
import os
import pandas as pd
import numpy as np

# Import random module with an alias 'rand'
import random as rand
from scipy import signal

# Import the matplotlib library for plotting
import matplotlib.pyplot as plt

# Generate an array 'x' ranging from 0 to 5*pi with a step of 0.1
x = np.arange(0, 5*np.pi, 0.1)

# Generate square, sawtooth, sin, and cos waves based on 'x'
y_square = signal.square(np.pi * x)
y_sawtooth = signal.sawtooth(np.pi * x)
y_sin = np.sin(x)
y_cos = np.cos(x)

# Create a DataFrame 'df_waves' to store the waveforms
df_waves = pd.DataFrame([x, y_sawtooth, y_square, y_sin, y_cos]).transpose()

# Rename the columns of the DataFrame for clarity
df_waves = df_waves.rename(columns={0: 'time',
                                    1: 'sawtooth',
                                    2: 'square',
                                    3: 'sin',
                                    4: 'cos'})

# Plot the original waveforms against time
df_waves.plot(x='time', legend=False)
plt.show()

# Add noise to the waveforms and plot them again
for col in df_waves.columns:
    if col != 'time':
        for i in range(1, 10):
            # Add noise to each waveform based on 'i' and a random value
            df_waves['{}_{}'.format(col, i)] = df_waves[col].apply(lambda x: x + i + rand.random() * 0.25 * i)

# Plot the waveforms with added noise against time
df_waves.plot(x='time', legend=False)
plt.show()

二、问题陈述

现在我们需要决定聚类的基础。可能有两种方法:

  1. 我们希望将更接近一组的波形分组——欧氏距离较低的波形将被组合在一起。
  2. 我们想要对看起来相似的波形进行分组 - 它们具有相似的形状,但欧氏距离可能不低

2.1 距离度量

一般来说,我们希望根据形状 (2) 对时间序列进行分组,对于这样的聚类,我们可能希望使用距离度量,例如相关性,它们或多或少独立于波形的线性移位。

让我们检查一下具有上述定义的噪声的波形对之间的欧氏距离和相关性的热图:

        使用欧几里德距离对波形进行分组很困难,因为我们可以看到任何波形对组中的模式都保持相似,例如平方和余弦之间的相关形状与平方和平方非常相似,除了对角线元素之外

        我们可以看到,使用相关热图可以轻松地将所有形状组合在一起 - 因为相似的波形具有非常高的相关性(sin-sin 对),而 sin 和 cos 等对的相关性几乎为零。

2.2 剪影分数

        分析上面显示的热图并根据高相关性分配组看起来是一个好主意,但是我们如何定义相关性阈值,高于该阈值我们应该对时间序列进行分组。看起来像是一个迭代过程,容易出现错误并且需要大量的手动工作。

        在这种情况下,我们可以利用 Silhouette Score 为执行的聚类分配一个分数。我们的目标是最大化剪影得分。Silhouette 分数是如何工作的——尽管这可能需要单独讨论——让我们回顾一下高级定义

  1. 轮廓得分计算:单个数据点的轮廓得分是通过将其与其自身簇中的点的相似度(称为“a”的度量)与其与该点不属于的最近簇中的点的相似度进行比较来计算的(称为“b”的措施)。该点的轮廓得分由 (b — a) / max(a, b) 给出。
  • a(内聚性):衡量该点与其自身簇中其他点的相似程度。较高的“a”表示该点在其簇内的位置很好。
  • b(分离):测量点与最近邻簇中的点的不同程度。较低的“b”表示该点远离最近簇中的点。
  • 轮廓分数的范围从 -1 到 1,其中高值(接近 1)表示该点聚类良好,低值(接近 -1)表示该点可能位于错误的聚类中。

2.解读剪影分数:

  • 所有点的平均轮廓得分较高(接近 1)表明聚类定义明确且不同。
  • 较低或负的平均轮廓分数(接近 -1)表明重叠或形成不良的簇。
  • 0 左右的分数表示该点位于两个簇之间的边界上。

2.3 聚类

        现在让我们利用上面计算的两个版本的距离度量,并尝试在利用 Silhouette 分数的同时对时间序列进行分组。测试结果更容易,因为我们已经知道存在四种不同的波形,因此理想情况下应该有四个簇。

欧氏距离

############ reduing components on eucl distance metrics for visualisation #######
pca = decomposition.PCA(n_components=2)
pca.fit(df_man_dist_euc)
df_fc_cleaned_reduced_euc = pd.DataFrame(pca.transform(df_man_dist_euc).transpose(), 
                                              index = ['PC_1','PC_2'],
                                              columns = df_man_dist_euc.transpose().columns)

index = 0
range_n_clusters = [2, 3, 4, 5, 6, 7, 8]

# Iterate over different cluster numbers
for n_clusters in range_n_clusters:
    # Create a subplot with silhouette plot and cluster visualization
    fig, (ax1, ax2) = plt.subplots(1, 2)
    fig.set_size_inches(15, 7)

    # Set the x and y axis limits for the silhouette plot
    ax1.set_xlim([-0.1, 1])
    ax1.set_ylim([0, len(df_man_dist_euc) + (n_clusters + 1) * 10])

    # Initialize the KMeans clusterer with n_clusters and random seed
    clusterer = KMeans(n_clusters=n_clusters, n_init="auto", random_state=10)
    cluster_labels = clusterer.fit_predict(df_man_dist_euc)

    # Calculate silhouette score for the current cluster configuration
    silhouette_avg = silhouette_score(df_man_dist_euc, cluster_labels)
    print("For n_clusters =", n_clusters, "The average silhouette_score is :", silhouette_avg)
    sil_score_results.loc[index, ['number_of_clusters', 'Euclidean']] = [n_clusters, silhouette_avg]
    index += 1
    
    # Calculate silhouette values for each sample
    sample_silhouette_values = silhouette_samples(df_man_dist_euc, cluster_labels)
    
    y_lower = 10

    # Plot the silhouette plot
    for i in range(n_clusters):
        # Aggregate silhouette scores for samples in the cluster and sort them
        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]
        ith_cluster_silhouette_values.sort()

        # Set the y_upper value for the silhouette plot
        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i

        color = cm.nipy_spectral(float(i) / n_clusters)

        # Fill silhouette plot for the current cluster
        ax1.fill_betweenx(np.arange(y_lower, y_upper), 0, ith_cluster_silhouette_values, facecolor=color, edgecolor=color, alpha=0.7)

        # Label the silhouette plot with cluster numbers
        ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
        y_lower = y_upper + 10  # Update y_lower for the next plot

    # Set labels and title for the silhouette plot
    ax1.set_title("The silhouette plot for the various clusters.")
    ax1.set_xlabel("The silhouette coefficient values")
    ax1.set_ylabel("Cluster label")

    # Add vertical line for the average silhouette score
    ax1.axvline(x=silhouette_avg, color="red", linestyle="--")
    ax1.set_yticks([])  # Clear the yaxis labels / ticks
    ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])

    # Plot the actual clusters
    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
    ax2.scatter(df_fc_cleaned_reduced_euc.transpose().iloc[:, 0], df_fc_cleaned_reduced_euc.transpose().iloc[:, 1],
                marker=".", s=30, lw=0, alpha=0.7, c=colors, edgecolor="k")

    # Label the clusters and cluster centers
    centers = clusterer.cluster_centers_
    ax2.scatter(centers[:, 0], centers[:, 1], marker="o", c="white", alpha=1, s=200, edgecolor="k")

    for i, c in enumerate(centers):
        ax2.scatter(c[0], c[1], marker="$%d$" % i, alpha=1, s=50, edgecolor="k")

    # Set labels and title for the cluster visualization
    ax2.set_title("The visualization of the clustered data.")
    ax2.set_xlabel("Feature space for the 1st feature")
    ax2.set_ylabel("Feature space for the 2nd feature")

    # Set the super title for the whole plot
    plt.suptitle("Silhouette analysis for KMeans clustering on sample data with n_clusters = %d" % n_clusters,
                 fontsize=14, fontweight="bold")

plt.savefig('sil_score_eucl.png')
plt.show()

        很明显,簇都是混合在一起的,并且不能为任何数量的簇提供良好的轮廓分数。这符合我们基于欧几里得距离热图的初步评估的预期

三、相关性

############ reduing components on eucl distance metrics for visualisation #######
pca = decomposition.PCA(n_components=2)
pca.fit(df_man_dist_corr)
df_fc_cleaned_reduced_corr = pd.DataFrame(pca.transform(df_man_dist_corr).transpose(), 
                                              index = ['PC_1','PC_2'],
                                              columns = df_man_dist_corr.transpose().columns)

index=0
range_n_clusters = [2,3,4,5,6,7,8]
for n_clusters in range_n_clusters:
    # Create a subplot with 1 row and 2 columns
    fig, (ax1, ax2) = plt.subplots(1, 2)
    fig.set_size_inches(15, 7)

    # The 1st subplot is the silhouette plot
    # The silhouette coefficient can range from -1, 1 but in this example all
    # lie within [-0.1, 1]
    ax1.set_xlim([-0.1, 1])
    # The (n_clusters+1)*10 is for inserting blank space between silhouette
    # plots of individual clusters, to demarcate them clearly.
    ax1.set_ylim([0, len(df_man_dist_corr) + (n_clusters + 1) * 10])

    # Initialize the clusterer with n_clusters value and a random generator
    # seed of 10 for reproducibility.
    clusterer = KMeans(n_clusters=n_clusters, n_init="auto", random_state=10)
    cluster_labels = clusterer.fit_predict(df_man_dist_corr)

    # The silhouette_score gives the average value for all the samples.
    # This gives a perspective into the density and separation of the formed
    # clusters
    silhouette_avg = silhouette_score(df_man_dist_corr, cluster_labels)
    print(
        "For n_clusters =",
        n_clusters,
        "The average silhouette_score is :",
        silhouette_avg,
    )
    sil_score_results.loc[index,['number_of_clusters','corrlidean']] = [n_clusters,silhouette_avg]
    index=index+1
    
    sample_silhouette_values = silhouette_samples(df_man_dist_corr, cluster_labels)
    
    y_lower = 10
    for i in range(n_clusters):
        # Aggregate the silhouette scores for samples belonging to
        # cluster i, and sort them
        ith_cluster_silhouette_values = sample_silhouette_values[cluster_labels == i]

        ith_cluster_silhouette_values.sort()

        size_cluster_i = ith_cluster_silhouette_values.shape[0]
        y_upper = y_lower + size_cluster_i

        color = cm.nipy_spectral(float(i) / n_clusters)
        ax1.fill_betweenx(
            np.arange(y_lower, y_upper),
            0,
            ith_cluster_silhouette_values,
            facecolor=color,
            edgecolor=color,
            alpha=0.7,
        )

        # Label the silhouette plots with their cluster numbers at the middle
        ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))

        # Compute the new y_lower for next plot
        y_lower = y_upper + 10  # 10 for the 0 samples

    ax1.set_title("The silhouette plot for the various clusters.")
    ax1.set_xlabel("The silhouette coefficient values")
    ax1.set_ylabel("Cluster label")

    # The vertical line for average silhouette score of all the values
    ax1.axvline(x=silhouette_avg, color="red", linestyle="--")

    ax1.set_yticks([])  # Clear the yaxis labels / ticks
    ax1.set_xticks([-0.1, 0, 0.2, 0.4, 0.6, 0.8, 1])

    # 2nd Plot showing the actual clusters formed
    colors = cm.nipy_spectral(cluster_labels.astype(float) / n_clusters)
    
    ax2.scatter(
        df_fc_cleaned_reduced_corr.transpose().iloc[:, 0], 
        df_fc_cleaned_reduced_corr.transpose().iloc[:, 1], marker=".", s=30, lw=0, alpha=0.7, c=colors, edgecolor="k"
    )
    
#     for i in range(len(df_fc_cleaned_cleaned_reduced.transpose().iloc[:, 0])):
#                         ax2.annotate(list(df_fc_cleaned_cleaned_reduced.transpose().index)[i], 
#                                      (df_fc_cleaned_cleaned_reduced.transpose().iloc[:, 0][i], 
#                                       df_fc_cleaned_cleaned_reduced.transpose().iloc[:, 1][i] + 0.2))
        
    # Labeling the clusters
    centers = clusterer.cluster_centers_
    # Draw white circles at cluster centers
    ax2.scatter(
        centers[:, 0],
        centers[:, 1],
        marker="o",
        c="white",
        alpha=1,
        s=200,
        edgecolor="k",
    )

    for i, c in enumerate(centers):
        ax2.scatter(c[0], c[1], marker="$%d$" % i, alpha=1, s=50, edgecolor="k")

    ax2.set_title("The visualization of the clustered data.")
    ax2.set_xlabel("Feature space for the 1st feature")
    ax2.set_ylabel("Feature space for the 2nd feature")

    plt.suptitle(
        "Silhouette analysis for KMeans clustering on sample data with n_clusters = %d"
        % n_clusters,
        fontsize=14,
        fontweight="bold",
    )

plt.show()

        当选择的簇数为 4 时,我们可以看到清晰分离的簇,并且结果通常比欧几里德距离好得多。

欧氏距离和相关轮廓分数之间的比较

        Silhouette 分数表示,当簇数为 4 时,基于相关性的距离矩阵提供最佳结果,而在欧几里德距离的情况下,其效果并不那么清晰

四、结论

        在本文中,我们研究了如何使用欧几里德距离和相关性度量来执行时间序列聚类,并且我们还观察了这两种情况下结果的变化。如果我们在评估聚类时结合 Silhouette,我们可以使聚类步骤更加客观,因为它提供了一种很好的直观方法来查看聚类的分离程度。

参考

  1. https://scikit-learn.org [KMeans 和 Silhouette 分数]
  2. Plotly: Low-Code Data App Development [可视化库]
  3. https://scipy.org [用于创建信号数据]
  4. 吉里什·戴夫·库马尔·乔拉西亚·

        如果您觉得我的讲解对您有帮助,请关注我以获取更多内容!如果您有任何问题或建议,请随时发表评论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/114980.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 组件集录 | InheritedNotifier 内置状态管理组件

theme: cyanosis 1. 前言 在上一篇 《Flutter 知识集锦 | 监听与通知 ChangeNotifier》 中,我们介绍了 ChangeNotifier 对象通知监听者的能力。并通过一个简单的模拟下载进度案例,介绍了它的使用方式: | 案例演示 | 监听-通知关系 | | --- | …

多用户商城系统对比 多用户商城系统哪个好

大环境越来越好,企业纷纷将消费者引入自己建设的独立商城,如零食行业的良品铺子、三只松鼠,从而打造属于自己的IP形象。此时,挑选一款优秀的商城源码是企业的不二之选。这里将国内三大优秀的多用户商城系统进行对比,以…

Elasticsearch 8.X 如何生成 TB 级的测试数据 ?

1、实战问题 我只想插入大量的测试数据,不是想测试性能,有没有自动办法生成TB级别的测试数据?有工具?还是说有测试数据集之类的东西?——问题来源于 Elasticsearch 中文社区https://elasticsearch.cn/question/13129 2…

解决VSCode使用SSH远程连接时无法指定用户名的问题

Windows 11自带OpenSSH客户端,和VSCode配合得很好,没有这个问题。 今天要说的是旧版本Windows 7/8/10系统遇到的问题。 PS: Windows 7可以运行的最后版本是VSCode 1.80.2 由于Windows 7/8/10没有自带的OpenSSH客户端,但可以调用MSYS环境下的…

python图像处理 ——几种图像增强技术

图像处理 ——几种图像增强技术 前言一、几种图像增强技术1.直方图均衡化2.直方图适应均衡化3.灰度变换4.同态滤波5.对比拉伸6.对数变换7.幂律变换(伽马变换) 前言 图像增强是指通过各种算法和技术,改善或提高数字图像的质量、清晰度、对比度…

3.22每日一题(二重积分求平面区域面积)

先复习求平面积分的公式 注:面对平面积分直接使用二重积分对1求积分即可;所以只需要背二重积分的两个公式: 1、直角坐标下对1积分 2、极坐标下对1积分 xy-1是等轴双曲线!! 1、先画图定区域 2、选择先对x积分还是先对…

深度学习之基于Yolov5闯红灯及红绿灯检测系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、闯红灯及红绿灯检测系统![请添加图片描述](https://img-blog.csdnimg.cn/8f260c2ed5ed4d8596e27d38abe42745.jpeg)四. 总结 一项目简介 基于Y…

力扣 upper_bound 和 lower_bound

👨‍🏫 34. 在排序数组中查找元素的第一个和最后一个位置 🌸 AC code 2023版 class Solution {public int[] searchRange(int[] nums, int target) {int[] res { -1, -1 };if(nums.length 0)return res;int l 0;int r nums.length - 1;…

QCC TX 音频输入切换+提示声音

QCC TX 音频输入切换提示声音 QCC蓝牙芯片(QCC3040 QCC3056 等等),AUX、I2S、USB输入 蓝牙音频输入,模拟输出是最常见的方式。 也可以再此基础上动态切换输入方式。 针对TX切换EQ,调节音量不能出提示声音问题,可以增…

Go 多版本管理

在日常开发工作过程中,很多时候我们都需要在自己的机器上安装多个go版本,像是go1.16引入的embed,go1.18引入了泛型;又或是自己本地使用的是最新版,但公司的项目中使用的go1.14、go1.13甚至是更早的版本。 那么有没有既…

QTreeView 常见节点操作

目录 1、节点遍历 2、设置当前选中项 3、树节点数据绑定 4、树节点自定义样式 5、数据检索 6、获取当前选中项 QTreeView作为项目最经常使用的空间,常用接口和操作必须熟悉熟悉在熟悉!!! 1、节点遍历 void ParamSettingDl…

存储器(详解)

概念 存储器(Memory)是计算机系统中用于存储和检索数据的硬件设备或组件。它在计算机中扮演着重要的角色,允许计算机暂时或永久地存储程序、数据和中间结果。 存储器是许多存储单元的集合,按单元号顺序排列。每个单元由若干二进制…

Flutter屏幕适配

文章目录 一、Flutter单位二、设备信息三、常见适配方案四、flutter_screenutil 一、Flutter单位 Flutter使用的是类似IOS中的点pt(point)。 iPhone6的尺寸是375x667,分辨率为750x1334。 iPhone6的dpr( devicePixelRatio ) 是2.0。 DPR 物…

Dev-C调试的基本方法2-1

在Dev-C中调试程序,首先需要在程序中设置断点,之后以调试的方式运行程序。 1 设置断点 当以调试的方式运行程序时,程序会在断点处停下来。点击要设置断点代码行号左侧部分,此时会有如图1所示的红点和绿色对勾,表示断…

服务号升级订阅号的流程

服务号和订阅号有什么区别?服务号转为订阅号有哪些作用?首先我们要知道服务号和订阅号有什么区别。服务号侧重于对用户进行服务,每月可推送4次,每次最多8篇文章,发送的消息直接显示在好友列表中。订阅号更侧重于信息传…

框架安全-CVE 复现Apache ShiroApache Solr漏洞复现

文章目录 服务攻防-框架安全&CVE 复现&Apache Shiro&Apache Solr漏洞复现中间件列表常见开发框架Apache Shiro-组件框架安全暴露的安全问题漏洞复现Apache Shiro认证绕过漏洞(CVE-2020-1957)CVE-2020-11989验证绕过漏洞CVE_2016_4437 Shiro-…

C++类和对象(七)const成员 及其初始化列表

1.const成员 将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。 成员函数定义的原则: 1.能定义成const的成员函数都应该定义…

ElasticSearch集群环境搭建

1、准备三台服务器 这里准备三台服务器如下: IP地址主机名节点名192.168.225.65linux1node-1192.168.225.66linux2node-2192.168.225.67linux3node-3 2、准备elasticsearch安装环境 (1)编辑/etc/hosts(三台服务器都执行) vim /etc/hosts 添加如下内…

NTT DATA利用相干伊辛机模拟基因组组装和疾病治疗的潜力

​(图片来源:网络) 7月20日,日本领先的IT服务提供商和行业咨询公司NTT DATA宣布完成了一个使用量子计算优化基因组组装过程的项目。这是量子计算应用于医疗保健和生命科学行业中的一个里程碑。 本项目通过比较量子和非量子计算方…

时间复杂度为 O(nlogn) 的排序算法

归并排序 归并排序遵循 分治 的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后合并这些子问题的解来建立原问题的解,归并排序的步骤如下: 划分:分解待排序的 n 个元素…