TowardsDataScience 博客中文翻译 2020(二百一十三)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

聚类:国际象棋开局分类器(第一部分)

原文:https://towardsdatascience.com/clustering-chess-openings-classifier-part-i-6299fbc9c291?source=collection_archive---------71-----------------------

聚类方法

第一部分:下载象棋游戏数据集

在我的库中有完整的代码

聚类使我们能够在数据样本中发现模式。聚类应用有很多,我们选择的算法取决于我们需要解决的问题类型。在这个系列中,我将解释如何在成千上万的样本(象棋比赛)中找到最受专业人士欢迎的空缺。

为什么要下象棋?

对于初级分析师来说,最令人兴奋的事情之一就是处理任何类型的数据。为什么不处理象棋比赛数据?这些数据是开源的,可以很容易地大量找到,并且可以从多个来源获得。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

菲利克斯·米特迈尔Unsplash 上拍摄的照片

什么是棋局开局?

在国际象棋理论中,比赛的初始阶段已经被研究和概念化了。冠军被用来研究以开局开始的游戏发展,而不是即兴发挥:大师们已经给数千个预定义的国际象棋游戏序列命名。职业棋手研究这些序列,以获得对敌人的竞争优势。

lichess.org

玩家玩免费象棋和提高技术的最好网站之一叫做 lichess.org。这个网站允许开发者免费下载他们想要的所有数据,只有很小的数据流限制。

我通过标签找到了lichess.org 排名前 50 的玩家,并将他们的名字保存到一个 python 列表中。这是我开始从服务器下载数据所需的少数信息之一。在本教程中,我将简单地使用 3 个姓名标签,但是在我的代码中(在我的库中)你会找到所有 50 个顶级棋手的姓名标签。

存储变量

我将开始设置主要变量,这些变量将通过我们的算法存储所有主要信息。

#list of nametages
player_list = ['Konevlad','Liem_Chess','Azzaro25']#list of nametags that have been downloading successfully
player_completed = list()#all the downloaded matches
game_dataset = list()

当我们运行我们的算法时,如果查询成功,player_completed 和 game_dataset 将填充我们可以导出的信息。

下载功能

现在我需要创建一个可以执行单个请求的函数。因为要下载信息,我需要访问一个特定的链接,所以我将使用一个函数来创建允许我执行 GET 请求的正确字符串。我将多次运行这个函数,这样我将为我列表中的每个玩家创建一个链接,并且我将一次为每个玩家下载 200 个游戏。

import requests
import time
#get request of single user
def get_request(user):
  print('trying ' + user)
  if user in player_completed:
    print('already existing')
    return 0
  else:
    print(user)
    response = requests.get('[https://lichess.org/api/games/user/'](https://lichess.org/api/games/user/') + user, params={'max':200, 'pgnInJson':False})
    return response

不幸的是,数据是 ndjson 格式的。由于一个错误,我无法用任何已知的库来解码它:我需要即兴创作一个算法,可以提取每一个游戏并将其添加到一个列表中。

#conversion one user response to all games returned as a list
def games_to_list(response):
  a = str(response.content)lines = a.split("\\n");game_list = list()
  for r in lines:
    if "[" in r:
      pass
    else:
      if r == '':
        pass
      else:
        game_list.append(r)
  game_list.remove("'")
  return game_list

开始下载

现在是运行算法的时候了。像往常一样,我们不能过分强调在线数据库,否则,它们可能会让我们在几分钟内无法进行查询。如果每次出现问题时我们都必须手动重启下载算法,那么使用我们的浏览器手动下载这些数据会更舒服(这是我们想要避免的)。

#for each player download a single request
for _ in player_list:response = get_request(_)
  time.sleep(140)
  if response == 0:
    pass
  else:
    player_completed.append(_)
    #download games into a list
    game_list = games_to_list(response)
    #add all games of a user to the final list: game_dataset
    for _ in game_list:
      game_dataset.append(_)

为了解决这个问题,我将尝试每隔 140 秒向数据库发送一次查询(GET 请求)。

    • *如果我们不是第一次运行该算法,并且一些匹配(但不是标签列表中的所有匹配)已经存储在我们的变量中(换句话说,我们之前的尝试已经部分完成),我添加了一个解决方法。该函数将发现它正在尝试下载列表中已经存在的标签:它将返回 0 作为输出,算法将传递它作为避免进行相同查询的措施。您可以为错误添加退出案例,以使其更加平滑。

输出

正如我们所看到的,在为每个请求运行并等待必要的时间之后,算法应该输出每个请求的状态。

trying Konevlad 
Konevlad 
trying Liem_Chess 
Liem_Chess 
trying Azzaro25 
Azzaro25

我已经将成功下载的标签存储在一个特定的列表中。

player_completed
['Konevlad', 'Liem_Chess', 'Azzaro25']

所有匹配都存储在一个名为 final_dataset 的数据帧中。

import pandas as pd
game_dataset = pd.DataFrame(game_dataset)
final_dataset = game_dataset.copy()
final_dataset

解决异常

要提取比赛中的每一步棋,每一行都必须是相同的格式。每一行(至少表面上)都有以下模式:

1\. c4 c5 2\. Nc3 g6 3\. g3 Bg7 4\. Bg2 e6...

但是,我需要检查有问题的行,这些行可能会在我们的数据分析中产生错误。因此,我将向所有不以 1 开头的行发送信号。

    • *这似乎是一种非常近似的发现异常的方法,但是在用聚类算法测试数据集之后,结果证明这已经足够了
#non valid rows
for _ in range(len(final_dataset.values)):
  if final_dataset.values[_][0][0] != '1':
    print(_)
106 
107

行 106 和 107 没有相同的模式,让我们更仔细地看看:

final_dataset[105:110]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因为行 106 和 107 有异常,我们将删除它们。如果您要下载成千上万的匹配,包含异常的行可能会更多。

#drop non valid rows
final_dataset = final_dataset.drop([106, 107], axis=0)

导出数据帧

我们准备好导出数据集以用于分析目的:

#export dataset
final_dataset.to_csv('chess.csv')

- >进行第二部分

聚类:国际象棋开局分类器(第二部分)

原文:https://towardsdatascience.com/clustering-chess-openings-classifier-part-ii-7a4f0ce9dcc9?source=collection_archive---------53-----------------------

聚类方法

第二部分:国际象棋开局分类

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Unsplash 上由 A. Yobi Blumberg 拍摄的照片

在本文的前一部分,我已经编写了一个 API,允许我从 lichess.org 下载大量的顶级国际象棋比赛。我已经收集了 7000 多个匹配项,今天我将使用聚类技术对它们进行分析。我的目标是:列出这些比赛中最受欢迎的空缺。

我的仓库里有完整的代码。

导入数据集

像往常一样,第一步是使用导入下载的匹配数据集,这是我上一篇文章(第一部分)中的说明。

import pandas as pd
#importing dataset
X = pd.read_csv('/content/drive/My Drive/Colab Notebooks/Projects/20200526_Chess Openings/chess1.csv')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不幸的是,数据根本没有结构化。我需要预处理所有的数据来创建一个数据集,为每一列保存一个单独的棋步。

n_moves = X.shape[0]#break every game in individual moves
moves = [[] for x in range(n_moves-1)]
for _ in range(n_moves-1):
  game = X['0'][_].split(".")
  game.remove('1')
  #print(game)
  for move in game:
    try:
      player_move = move.split(" ")
      #print(player_move)
      moves[_].append(player_move[1]) #add white move
      moves[_].append(player_move[2]) #add black move
    except:
      #if error occurs
      print(_, move)

使用这个算法,我获取了第 0 列的所有内容,并分解了每个字符串(因此每个匹配)。比如我分解了“1。d4 Nf6 2。Nf3 g6 3。Bf4 Bg7 4。e3 O-O 5。H3……”,分成单个动作。它将创建一个名为 moves 的数据集,将每个单独的移动作为一个字符串放在不同的列中。

输出

该函数还将返回将遇到错误的行,并避免将它们添加到数据集中。

3302  
3303  
3304  
3305  
3306   
3307   
3308   
3309  
3310  
5280  
5284  
5285 
    • *对于一个微小的错误,数字会在代码中重复一次,但对我们的结果没有任何影响

现在,让我们看看最终的结果:

moves = pd.DataFrame(moves)
moves

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的结构化数据集

空缺

如果我将聚类算法应用于 moves,即结构化数据集,我将不只是对空缺进行分类,而是对整个匹配进行分类。我需要设置一个限制,集群将在数据中找到模式。

#only conserve opening columns
list1 = [x for x in range(0, 10)]
list1moves.columns
tot = moves[list1]
tot

我选择选择最多 10 步棋(相当于 5 回合)。这些数据足以为我提供一组常用的组合,但也不会有太多的数据模糊组合的频率。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按名称分组

通过这个简单的 groupby 算法,我可以通过组合对所有 7674 个匹配进行排序。

final = tot.groupby([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], group_keys=True, squeeze=True, sort=True).count()
final = pd.DataFrame(final)
final

这将是我们的输出。如果您查看前 4 行,您会看到它们都包含变体:Nc3、d5、Nf3。现在的问题是我们按字母顺序排列它们。当我们的数据集超过 7000 行时,我们不知道这些开口有多受欢迎。这些只是没有明显重量的随意搭配。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

按频率分组

我要用的是另一种形式的 groupby。在 7000 个匹配中,我可以通过统计它们的频率立即看出哪些是最受欢迎的空缺。

final = tot.groupby([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], group_keys=True, squeeze=True, sort=True).size().sort_values(ascending=False)
final = pd.DataFrame(final)
final

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我要做的是将这个数据集转换成熊猫数据帧(我找到的唯一方法是将其导出为。csv,然后再次导入)。

#in order to reset it, we need to export it
final.to_csv('chess.csv')#import of already reset dataset
final_ = pd.read_csv('/content/chess.csv')
final_.columns = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'frequency']
final_ = final_[0:20]
final_

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最后,我可以按字母顺序对空缺进行分组,以使用第一种分组算法来识别聚类:

final_ = final_.groupby([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], group_keys=True, squeeze=True, sort=True).count()
final_[0:20]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如您所见,这些是按频率选择的前 20 个空缺职位,然后按类别分组。虽然每个聚类的频率都为 1,但它只是随着数据集格式的变化而重置,因此我们可以忽略它。

使用 K 均值机器学习算法对客户进行聚类

原文:https://towardsdatascience.com/clustering-customers-using-k-means-machine-learning-algorithm-fb0a28948975?source=collection_archive---------12-----------------------

这个故事是关于使用 K-means 算法对网上购物者进行聚类的。一种有趣的方法,使用机器学习来细分您的客户,或者检查您的细分是否正确。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拉胡尔·潘迪特来自 Pexels.com 的照片

为什么要细分?

营销人员的工作是找到客户的模式和共同特征,对他们进行细分。这是你想出正确的信息和渠道与客户沟通并建立持久关系的第一步。

对具有共同特征的客户进行细分并不容易,需要大量的人力和智慧。

基于机器学习算法的聚类

机器学习算法的最新发展给了我们一个新的工具集,使用机器在数据集中寻找模式并创建聚类。无监督学习 K-means 算法搜索数据集中的隐藏模式(人类不可见),并将每个观察值分配到相关的聚类。我们将使用 R 进行 K 均值聚类。

关于数据集

数据集是从 Kaggle 中获取的。它包含零售购物网站的客户信息。数据集有 10 个变量和 1000 条记录(在数据清理之前)。让我们看看数据集的详细信息:

数据集中的前几条记录

数据集有 8 个分类变量和 2 个连续变量(薪水花费金额)。由于缺少很多值,我们将删除历史变量,剩下 7 个分类变量。此外,删除缺少值的行会留下 994 条记录。我们还是好好的:)

让我们把分类变量的因子级别变成整数。例如,对于年龄设置年轻=0,中间=1,年老=2。对于性别,设置女性=0,男性=1,其他分类变量也是如此。

变量及其分布:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个分类变量的数据分布

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

连续变量的分布

相互关系

cor.matrix <- cor(data, method = "pearson", use = "complete.obs")corrplot(cor.matrix)

分析 9 个变量(7 个分类变量和 2 个连续变量)的相关矩阵,揭示了一些直观的见解,其中一些令人惊讶:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相互关系

结婚工资,以及花费工资都呈高度正相关。

子女(子女数量)与年龄呈负相关,在数据集中,老年人有少量子女——要么是 0,要么是 1。

来自关联矩阵的另一个令人惊讶的发现是结婚了有了孩子——他们没有关联。经过调查,我们发现已婚和单身的孩子数量差不多。

不那么令人惊讶和更直观的是,性别年龄之间没有明显的相关性。

我们有非常有趣的顾客在网站上购物:)

聚类数

我们需要多少个集群?为了确定这一点,让我们使用肘法,轮廓评分和 Calinski-Harabasz 指数。一种方法可能无法给我们一个明确的答案。这就是为什么我们需要尝试另一种方法来决定正确的集群数量。

这些方法在 R 中有现成的公式,我们只需要应用到我们的数据集。

E lbow 方法:

此方法的 r 代码:

上述 R 代码的结果显示在下图中。从图中可以看出,很难决定最佳的集群数量。2 个 3 个还是 4 个?嗯…很难说…我们用另一种方法吧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

肘法

剪影分数:

r 代码:

opt.k.sil <- Optimal_Clusters_KMeans(data, max_clusters=10, plot_clusters = TRUE, criterion = "silhouette")

结果如下图所示。较高的轮廓分数为我们提供了最佳聚类数的指示。在我们的例子中,两个和四个集群都有最高的分数,但是问题是两个都是一样的,我们不能决定选择哪一个:(。让我们转到卡林斯基-哈拉巴斯指数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

剪影配乐

卡林斯基-哈拉巴斯指数:

r 代码:

较高的 Calinski-Harabasz 指数决定了要选择的聚类数。现在很明显,4 个集群是最好的:)

聚类结果

K 均值算法的 r 代码:

KMC <- kmeans(data, centers = 4, iter.max = 999, nstart=50)

应用算法后,让我们看看每个集群中有多少客户:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个集群中的客户数量

集群 1、2、4 几乎均匀分布,分别有 269、285 和 283 个客户,而集群 3 有 157 个客户。

每个集群中的数据:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

聚类结果

C 光泽 1——大多是年轻的单身女性,没有孩子,租房居住。该集群具有最低的平均工资和最低的平均支出额。

C 第二低的平均工资和花费。

C光泽 3——多为中年男性,自有住房。这个群体中的每个人都结婚了。相对于其他群体,他们有 2 到 3 个孩子的比例最高。他们收入最高,花费也最多。

C 买高端产品,花第二高的金额。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

米盖尔·Á的照片。来自 Pexels.com 的帕德里纳

每个聚类的特征都是非常独特的,并且产生几乎相同的部分。很容易注意到,集群 1 拥有“最没有价值”的客户,但是,我们没有关于该细分市场购买频率的数据。但是这个片段描绘的是一个年轻的女学生,她没有赚很多钱,也不花钱。

集群 2 是收入不高、消费也不多的中老年客户,几乎类似于集群 4 但是这些中年人确实工资高,确实消费多,大多是高端产品。

集群 3 则是中年男性,收入稳定高,有孩子,消费金额最高。

现在就看我们针对每个集群的策略了。

这个故事是基于我和我的同事诺姆·施穆尔在华沙大学教授亚采克·卢科维兹博士的“无监督学习”课上共同完成的项目

完整的 R 代码和数据库在我的 GitHub 上。

参考资料:

[1] A. Rezaeian,S. Shokouhyar 和 F. Dehghan,使用聚类技术测量电子商务网站的客户满意度:Nyazco 网站的案例研究。国际管理、会计和经济学杂志,(2016),3(1),61–74。

[2] A. Sagar,利用 K 均值聚类进行客户细分 (2019),towardsdatascience.com

[3] Dataflair 团队,数据科学项目— 在 R 使用机器学习进行客户细分(2019)

[4]cran . R-project . org 上的 R 包描述:corr plot 包介绍和 r-blogger 上 R 中的 RFM 分析

[5] 数据集取自 Kaggle.com

具有数字和分类变量的聚类数据集

原文:https://towardsdatascience.com/clustering-datasets-having-both-numerical-and-categorical-variables-ed91cdca0677?source=collection_archive---------1-----------------------

随着现代机器学习的出现,企业已经看到了他们做出决策和推动利润的方式的转变。一个成功公司最重要的属性之一是他们能多好地理解他们的客户,以及他们能多好地迎合他们的个性化需求。对于公司来说,理解“一刀切”的模式不再有效变得越来越重要。

这就把我们带到了集群的话题上。聚类只不过是对实体的分割,它允许我们理解数据集中不同的子群。虽然许多文章回顾了使用具有简单连续变量的数据的聚类算法,但是对具有数字和分类变量的数据进行聚类在现实问题中是常见的情况。本文讨论了一种使用高尔距离、PAM(Medoids 周围划分)方法和剪影宽度的聚类方法,并用 r。

我们使用的’german credit数据取自 UCI 的机器学习知识库:(https://archive . ics . UCI . edu/ml/Machine-Learning-databases/statlog/german/)。为了导入 R 中的数据,我们使用’ read.table’ 命令,如下所示:

*set.seed(2020)
german_credit = read.table("[http://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data](http://archive.ics.uci.edu/ml/machine-learning-databases/statlog/german/german.data)")*

我们注意到这个数据集的列名不是不言自明的,因此我们在文档的帮助下重命名这些列。

*colnames(german_credit) = c("chk_acct", "duration", "credit_his", "purpose", "amount", "saving_acct", "present_emp", "installment_rate", "sex", "other_debtor",                   "present_resid", "property", "age", "other_install", "housing", "n_credits", "job", "n_people", "telephone", "foreign", "response")*

此外,为了使事情更容易,我们只选择几个相对更重要的变量。请注意,我选择这些特定的列只是为了举例说明,读者可以自由尝试不同的列。

*library(dplyr)
german_credit_clean = german_credit %>% select(duration, amount, installment_rate, present_resid, age, n_credits, n_people, chk_acct, credit_his, sex, property, housing, present_emp)*

我们看到这个数据集既包含数字变量(连续变量)也包含分类变量。

连续变量:

期限:期限(月)
金额:贷款的信用额度
分期付款率:分期付款率占可支配收入的百分比
现在 _ 剩余:在现居住地居住的时间
年龄:年龄(年)
n _ 信用:在该银行的现有信用数量
n _ 人数:有责任为
信用提供维护的人数 _his:信用历史
财产:财产
住房:住房
现在 _ 雇员:目前就业自

分类变量:

chk_account:现有支票账户的状态
性别:个人状态和性别
credit_his:信用记录
财产:财产
住房:住房
present_emp:目前就业自

既然我们已经理解并预处理了数据,我们将注意力转向聚类。聚类的基本前提是数据点之间的相似/相异。正如我们所知,K-means 算法反复迭代,直到它达到一种状态,其中一个聚类的所有点彼此相似,而属于不同聚类的点彼此不相似。这种相似性/不相似性由点之间的距离来定义。

度量这个距离的一个流行的选择是欧几里德距离。一些研究人员还提倡对特定类型的问题使用曼哈顿距离。然而,这两种距离度量仅适用于连续数据。在包含混合数据类型的数据中,欧几里德距离和曼哈顿距离不适用,因此,K-means 和层次聚类等算法将无法工作。

因此,我们使用高尔距离,这是一种可用于计算两个实体之间距离的度量,这两个实体的属性是分类值和数量值的混合。该距离的数值范围为 0(相同)和 1(最大差异)。高尔距离的数学细节相当复杂,将在另一篇文章中讨论。

在 R 中,高尔距离可以使用’雏菊 包来计算。在我们使用’ daisy '函数之前,我们观察数字变量的分布,以了解它们是否需要转换。我们发现变量的数量需要进行对数变换,因为它的分布存在正偏斜。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传**外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面的代码显示了高尔距离计算的实现。请注意,我们已经将“2”指定为类型参数中的参数。这确保了在执行高尔计算之前,第二个变量 amount 经过对数转换。

*library(cluster)
gower_df <- daisy(german_credit_clean,
                    metric = "gower" ,
                    type = list(logratio = 2))*

接下来,我们检查*‘gower _ df’*数据帧的摘要。类型“I”和“N”告诉我们相应的列是区间标度的(数值)还是标称的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

K-medoids 和围绕 medoids 的划分

现在我们已经有了一个距离矩阵,我们需要选择一个聚类算法来从这些距离中推断相似性/不相似性。就像 K-means 和分层算法与欧几里德距离密切相关一样,Medoids 周围划分(PAM)算法也与高尔距离密切相关。PAM 是一个迭代聚类过程,就像 K-means 一样,但有一些细微的区别。PAM 不是 K-means 聚类中的质心,而是反复迭代,直到质心不再改变它们的位置。分类的中间值是分类的一个成员,它代表所有考虑中的属性的中间值。

剪影宽度选择最佳聚类数

在选择最佳聚类数时,轮廓宽度是非常受欢迎的选择之一。它测量每个点与其聚类的相似性,并将其与该点与最近的相邻聚类的相似性进行比较。该度量的范围在-1 到 1 之间,其中较高的值意味着点与其聚类的相似性较好。因此,较高的轮廓宽度值是可取的。我们计算一系列集群数量的这个度量,并找到它最大化的地方。以下代码显示了 R 中的实现:

*silhouette <- c()
silhouette = c(silhouette, NA)for(i in 2:10){
  pam_clusters = pam(as.matrix(gower_df),
                 diss = TRUE,
                 k = i)
  silhouette = c(silhouette ,pam_clusters$silinfo$avg.width)
}plot(1:10, silhouette,
     xlab = "Clusters",
     ylab = "Silhouette Width")
lines(1:10, silhouette)*

轮廓宽度与聚类数的关系如图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看出,该值在 5 处最大。因此,我们可以得出结论,将数据聚类成 5 个簇可以提供最佳的分段。说到这里,我们构建了一个包含 5 个集群的 PAM 模型,并尝试在 medoids 的帮助下解释这些集群的行为。

*pam_german = pam(gower_df, diss = TRUE, k = 5)
german_credit_clean[pam_german$medoids, ]*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上图显示了 medoids 表,其中每一行代表一个聚类。使用该表,我们可以推断出属于聚类 1 的客户具有以下特征:持续时间为 15 个月,信用金额为 1829 美元,分期付款率为 4,他们从 4 个月起就一直住在现居住地,他们的平均年龄为 46 岁,他们有 2 笔贷款未决,他们有 1 个受抚养人,他们没有支票账户(A14),他们的信用历史很重要(A34),他们是男性单身(A93),他们有一辆汽车作为他们的财产(A123),住在他们自己的住房中(A152),并且已经被雇用更多请注意,不是所有的客户都会完全这样;并且 medoids 仅仅是中间值的表示。

对其他集群也可以作出类似的解释。为了更深入地了解每个集群的特征,我们找到了摘要统计信息。下面显示了相同的 R 代码,以及第一个集群的统计数据。

*pam_summary <- german_credit_clean %>%
  mutate(cluster = pam_german$clustering) %>%
  group_by(cluster) %>%
  do(cluster_summary = summary(.))pam_summary$cluster_summary[[1]]*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可视化

最后,让我们用一些奇特的视觉效果来结束这篇文章。为此,我们使用 t-SNE 或 t-分布随机邻居嵌入技术。这项技术提供了一种很好的方式来可视化多维数据集,比如我们正在处理的数据集。

*library(Rtsne)
library(ggplot2)tsne_object <- Rtsne(gower_df, is_distance = TRUE)tsne_df <- tsne_object$Y %>%
  data.frame() %>%
  setNames(c("X", "Y")) %>%
  mutate(cluster = factor(pam_german$clustering))ggplot(aes(x = X, y = Y), data = tsne_df) +
  geom_point(aes(color = cluster))*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如我们所见,t-SNE 帮助我们将多维数据可视化为一个简单的二维图形。虽然这些集群看起来有一些重叠,但它们总体上非常独特。

这就把我们带到了本文的结尾。总之,我们讨论了高尔距离、PAM 聚类和侧影宽度的概念,以聚类具有数字和分类特征的数据。希望这能帮助周围所有遇到这种数据集的数据人。

快乐聚类!

用 Python 聚类文档

原文:https://towardsdatascience.com/clustering-documents-with-python-97314ad6a78d?source=collection_archive---------2-----------------------

自然语言处理在过去的几年里取得了巨大的进步。目前,神经网络的各种实现是最前沿的,似乎每个人都在谈论它们。但是,有时更简单的解决方案可能更好。毕竟,一个人应该在跑之前先试着走一走。在这篇短文中,我将展示一种用 Python 对文档进行聚类的简单方法。所有代码都可以在 GitHub 获得(请注意,在 nbviewer 中查看代码可能会更好)。

我们将使用 k-means 算法对维基百科文章进行聚类。执行此操作的步骤如下:

  1. 获取一些维基百科的文章,

2.将每篇文章表示为一个向量,

3.执行 k 均值聚类,

4.评估结果。

1.获取维基百科文章

使用维基百科包可以很容易地从维基百科下载内容。对于本例,我们将文章的内容用于:

  • 数据科学
  • 人工智能
  • 机器学习
  • 欧洲中央银行
  • 银行
  • 金融技术
  • 国际货币基金组织
  • 篮球
  • 游泳
  • 网球

每篇维基百科文章的内容存储在 wiki_list 中,而每篇文章的标题存储在变量 title 中。

import pandas as pd
import wikipediaarticles=['Data Science','Artificial intelligence','Machine Learning','European Central Bank','Bank','Financial technology','International Monetary Fund','Basketball','Swimming','Tennis']wiki_lst=[]
title=[]
for article in articles:
   print("loading content: ",article)
   wiki_lst.append(wikipedia.page(article).content)
   title.append(article)print("examine content")
wiki_lstAt the top of the github page there is a button that will allow you to execute the notebook in Google’s Colab. Feel free to experiment!

2.将每篇文章表示为一个向量

因为我们要使用 k-means,所以我们需要将每篇文章表示为一个数字向量。一种流行的方法是使用词频/逆词频(tf-idf) 。简单地说,用这种方法对每个单词 w 和文档 d 我们计算:

tf(w,d)😗*dw 的出现次数除以d中总字数的比值

idf(w): 文档总数除以包含 w. 的文档数的分数的对数

tfidf(w,d)=tf(w,d) x idf(w)

建议排除常见的停用词。所有的计算都可以通过 sklearn 的 tfidf 矢量器轻松完成。

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words={'english'})
X = vectorizer.fit_transform(wiki_lst)

(说实话,sklearn 的计算略有不同。你可以在文档中读到。)

3.执行 k 均值聚类

变量 X 的每一行都是维基百科文章的向量表示。因此,我们可以使用 X 作为 k-means 算法的输入。

首先,我们必须决定集群的数量。这里我们就用肘法

import matplotlib.pyplot as plt
from sklearn.cluster import KMeansSum_of_squared_distances = []
K = range(2,10)for k in K:
   km = KMeans(n_clusters=k, max_iter=200, n_init=10)
   km = km.fit(X)
   Sum_of_squared_distances.append(km.inertia_)plt.plot(K, Sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('Sum_of_squared_distances')
plt.title('Elbow Method For Optimal k')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每组数量的距离平方和图

情节几乎是一条直线,可能是因为我们要的文章不多。但是在更近的观察中,当 k=4 或 k=6 时,出现了一个凹痕。我们将尝试分成 6 组。

true_k = 6
model = KMeans(n_clusters=true_k, init='k-means++', max_iter=200, n_init=10)
model.fit(X)
labels=model.labels_
wiki_cl=pd.DataFrame(list(zip(title,labels)),columns=['title','cluster'])
print(wiki_cl.sort_values(by=['cluster']))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

聚类结果

4.评估结果

因为我们只使用了 10 篇文章,所以通过检查每个集群中包含哪些文章来评估集群是相当容易的。这对于大型语料库来说是很困难的。一个很好的方法是从每个集群的文章中创建一个词云。

from wordcloud import WordCloud
result={'cluster':labels,'wiki':wiki_lst}
result=pd.DataFrame(result)
for k in range(0,true_k):
   s=result[result.cluster==k]
   text=s['wiki'].str.cat(sep=' ')
   text=text.lower()
   text=' '.join([word for word in text.split()])
   wordcloud = WordCloud(max_font_size=50, max_words=100, background_color="white").generate(text)
   print('Cluster: {}'.format(k))
   print('Titles')
   titles=wiki_cl[wiki_cl.cluster==k]['title']         
   print(titles.to_string(index=False))
   plt.figure()
   plt.imshow(wordcloud, interpolation="bilinear")
   plt.axis("off")
   plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

星团的文字云

  • 集群 0 由关于欧洲中央银行、银行和国际货币基金组织的文章组成
  • 集群 1 由关于人工智能和机器学习的文章组成
  • 第二组有一篇关于游泳的文章
  • 集群 3 有一篇关于数据科学的文章
  • 集群 4 有关于篮球和网球的文章
  • 第五组有一篇关于金融科技的文章

游泳不属于篮球和网球这一类似乎有点奇怪。或者说 AI 和机器学习跟数据科学不是一个群体。这是因为我们选择创建 6 个集群。但是通过查看单词 clouds,我们可以看到关于篮球和网球的文章中有一些单词是相同的,比如游戏、运动员、团队、球,而关于游泳的文章则没有。

在 GitHub 页面的顶部,有一个按钮,可以让你在谷歌的 Colab 中执行笔记本。您可以很容易地尝试不同数量的集群。结果可能会让你大吃一惊!

悉尼的美食聚集地

原文:https://towardsdatascience.com/clustering-food-venues-in-sydney-bf48877650d5?source=collection_archive---------49-----------------------

使用 Foursquare API 和悉尼美食聚集地。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

雅各布·卡普斯纳克在 Unsplash 上的照片

背景信息

对一些人来说,很难确定他们想和朋友去哪里吃饭。有这么多不同的食物和菜肴可供选择。一般来说,如果一个地区因某种特定的美食而闻名,那么游客更有可能在那个地区吃那种美食。澳大利亚是一个多元文化的国家,在共同的地方不难找到不同的民族社区。

在这个项目中,我们的目标是帮助人们根据自己的兴趣找到在悉尼就餐的地点。例如,如果你想吃泰国菜,你可以去你周围的郊区。我们将使用 Foursquare APIK-means 聚类来发现地区中最常见的美食,并在地图上可视化聚类。

数据

悉尼火车站附近有很多餐馆。为此,我编制了一个数据集,其中包含了悉尼所有的火车站和地铁站,以及它们各自的经度和纬度坐标。这将使我们能够在悉尼的地图上看到这些地点。车站位置的选择是因为大多数悉尼工人都乘坐公共交通工具。靠近车站的餐馆为居民提供了方便的选择。这里可以查看数据集,共 181 个站。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

。悉尼站数据帧的 head()

方法和分析

在将悉尼站读入一个熊猫数据框后,我们现在将使用 Foursquare API 来查找我们站附近的美食地点。然后,我们将使用 K-means 聚类来创建具有相似特征的站点的聚类。

首先,让我们看看我们的站在悉尼地图上是什么样子,以检查我们的坐标是否正确。我们可以使用 geopy叶子库来创建一张悉尼地图,上面叠加我们的站坐标。

悉尼火车/地铁网络——使用笔获得交互式地图

现在,我们使用 four square API T1,这是一种社交定位服务,允许用户发现商业和景点。由于我们试图找到与食物相关的地点,我添加了类别 ID '4d 4b 7105d 754 a 06374d 81259’作为 API 请求 URL 的一部分。我将返回的场地限制为 300 个,半径为 1 公里。

url = 'https://api.foursquare.com/v2/venues/search?&categoryId=4d4b7105d754a06374d81259&client_id=**{}**&client_secret=**{}**&v=**{}**&ll=**{}**,**{}**&radius=**{}**&limit=**{}**'.format(
            CLIENT_ID, 
            CLIENT_SECRET, 
            VERSION, 
            lat, 
            lng, 
            radius, 
            LIMIT)

利用我们提取的附近美食场所的信息,我们可以将其合并到一个包含我们的站点信息的新数据帧中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

。head()提取的悉尼美食场馆数据框

四方共退回 5565 家美食场所。然而,当查看一些站点的场馆数量时,一些站点的数量很少或者根本没有。为了使这些站点不影响聚类,我删除了所有少于 9 个美食场所的站点。这导致了总共 5458 个美食场所和 175 个独特的类别。从 181 个站点,我们的数据帧现在包含 142 个站点,即删除了 39 个站点。

在使用一种热编码并对每个食品场所类别的频率取平均值后,我们可以使用 K-means 聚类,这是一种无监督学习算法,用于根据特征相似性创建数据点的 K 聚类。

sydney_onehot = pd.get_dummies(sydney_venues[['Venue Category']], prefix="", prefix_sep="")

*# add neighborhood column back to dataframe*
sydney_onehot['Station'] = sydney_venues['Station'] 

*# move neighborhood column to the first column*
fixed_columns = [sydney_onehot.columns[-1]] + list(sydney_onehot.columns[:-1])
sydney_onehot = sydney_onehot[fixed_columns]sydney_grouped = sydney_onehot.groupby('Station').mean().reset_index()
sydney_grouped.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

悉尼分组数据框显示了前 7 个类别的频率平均值

使用这个数据框,我们可以删除第一列并将其用于 K-means 聚类。观察肘方法和剪影评分,我们发现要使用的最佳聚类数是 K = 5

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

最佳 K 的肘方法和轮廓分数

生成聚类标签后,我们可以将其添加到新的数据框中,合并原始数据框和它们各自的前 10 个场馆。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于之前已经删除了一些站点,我们获得了一些站点的 NaN 值,因此我们使用 dropna() 方法删除了这些行。
我将每个车站的第一个最常见的场馆可视化,按照它们的集群进行分组,让我们清楚地了解这些集群可能具有的特征。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过查看条形图,我归纳了每个集群并给它们贴上标签。
我不得不包括几个类别作为标签,因为顶级场所有相似之处,例如咖啡馆:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据框中每个聚类的标签

我还添加了每个站点的前三个地点,这样我们可以在地图上查看它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后,通过合并先前的数据帧,创建包含所有新信息的最终数据帧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果

我们的最终地图显示的信息集群创建。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

悉尼车站集群地图

对总共 142 个站(最初来自 181 个)进行聚类的结果展示了 5 个聚类。我们将集群归纳如下:

  • 集群 0(黄色)-23 个车站、面包店/咖啡馆/印度餐馆场馆
  • 聚类 1(青色)-2 个车站,韩国餐馆
  • 第 2 组(紫色)——35 个车站、咖啡厅
  • 集群 3(蓝色)-63 个车站、咖啡馆/咖啡/泰国餐馆场馆
  • 第 4 组(红色)-19 个车站,中国/越南/披萨餐厅场馆

悉尼车站周围的美食场所-使用笔来获得交互式地图版本

虽然有些车站可能与标签描述不同,但它很好地概述了有哪些类型的餐馆或美食场所。

结论

总之,我们使用了 K-means 聚类,为悉尼火车站和地铁站创建了 5 个不同的聚类,这些火车站和地铁站周围至少有 9 个美食场所。下次你出去吃饭的时候(希望快点!),你可以看看这张地图,了解一下悉尼车站和周围的美食广场都提供什么样的食物。

感谢阅读!

这是我的“探索悉尼的地区和场所”文章的第 2 部分,在这里我重点介绍了悉尼的餐馆和其他与食物相关的场所。
你可以在这里查看这个项目的全部代码/笔记本

欢迎在 LinkedIn 上联系我,或者在 Medium 上关注我。

高级数据聚类教程

原文:https://towardsdatascience.com/clustering-for-data-nerds-ebbfb7ed4090?source=collection_archive---------9-----------------------

K 均值,基尼系数,熵的终极指南。如何用 R 和 Python 从相机图像中对人体手势进行分类?

在本文中,我们使用 R 和 Python 实现了数据分割和数据聚类,用于对相机图像中的手势和人体姿态进行分类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源

我们什么时候对数据进行聚类?

当没有明确定义的分类或回归任务时,我们面临着一种无监督学习的情况,在这种情况下,我们基本上探索观察组,并尝试对数据进行聚类,以便有意义。

聚类的目标是将一组对象组合在一起,使得同一聚类中的对象彼此之间比其他聚类中的对象更相似。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:加州大学 Jonathan Richard Shewchuk 教授的讲稿

相似度是反映两帧之间关系强度的量。相似性度量的经典方法是欧几里德距离和曼哈顿距离

K-Means 聚类是如何工作的?

K-Means 聚类是最简单也是最常用的聚类方法,用于将数据集分割成一组 K 个(预定义的)组。

  • 该算法从随机选择 K 个称为质心的起始点开始。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:马蒂亚·西内利的博客

  • 然后,它计算数据集中每个点与质心之间的距离。被认为最接近给定质心的点被认为是由该质心表示的聚类的一部分。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:马蒂亚·西内利的博客

  • 在最后一步中,在每个聚类内计算一个新的质心作为平均点。重复这些步骤,直到质心不再改变。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:马蒂亚·西内利的博客

手势识别的数据聚类

手势和手势识别旨在识别特定的人类手势,并使用它们来传递信息。对非语言交流进行正确的分类对于一个熟练的人机交互框架来说是必不可少的。数据聚类有助于解决这个问题。

聚类的工作原理是探索从一组用户记录的视频姿势,并对数据进行分区,使其有意义。

通过将视频帧分成簇,我们可以认识到每个用户都有一个独特的姿势集。我们还可以将姿势分组,显示所有用户以相似的方式执行相同的姿势。

我们可能会发现异常聚类,这些异常聚类识别出执行特定任务的用户与其他人有很大不同。这些发现可能有助于将这些簇与某种疾病状况联系起来,或者突出一些罕见的物理特性。

数据

我们将聚集从运动捕捉相机系统收集的数据,发表在这篇加德纳的论文。该系统被用于记录 14 个不同的用户进行 5 种不同的手部姿势,并在左手手套上附着标记。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:安德鲁·加德纳,加州大学

手套背面的一组 4 个标记用于建立手的局部坐标系。它们在手背上形成一个固定的图案,作为手的位置和方向的标识。他们还为附着在手套的拇指和手指上的剩余标记创建了一个局部坐标系。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:安德鲁·加德纳,加州大学

基于来自照相机系统的观察,总共收集了 24 个特征。数据集中的另外两个变量是用户的 ID 和用户做出的姿势。

完整的数据集可在这里获得。所有标记都被转换到包含它们的记录的本地坐标系。这些数据已经过预处理,包括转换到记录的本地坐标系统、去除异常值和去除缺失数据。

数据集postures_clean.csv包含 38,943 行和 26 列。每行对应于照相机系统捕获的单个帧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些列如下所述。

描述给定观察的手部姿势,具有以下可能值:

1 = Fist (with thumb out)
2 = Stop (hand flat)
3 = Point1 (point with index finger)
4 = Point2 (point with index and middle fingers)
5 = Grab (fingers curled as if to grab)

用户代表贡献记录的用户的 ID。

X0,Y0,Z0,X1,Y1,Z1,…,X7,Y7,Z7 代表八个未标记标记位置的 x 坐标,y 坐标,z 坐标。

下面我们绘制了每个用户和每种姿势记录的帧数。我们可以观察到一些用户缺少一些姿势。此外,为给定姿势记录的帧数因用户而异。我们还注意到,当用户 id 从 0 到 14 时,用户 id 3 丢失了,剩下 14 个用户。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们观察到,由于自遮挡,许多原始记录缺少标记并被从数据集中删除。由于记录中可见标记的数量总是 8,我们可以得出结论,一些用户的姿势被完全删除了。这将解释例如用户 4 和 7 仅具有少量记录且仅执行了一些姿势。

有些姿势记录的总帧数非常少。例如,类别 1(拇指向外的拳头)只为 3 个用户记录,总共 52 帧。大概是用户握拳的时候大部分标记都被遮挡了。

聚类需要距离的概念

查看数据,我们注意到平均值 X、Y、Z 大致来自同一分布。不存在可见的异常值。这表明坐标代表现实世界中的物理位置。我们还可以观察到,值的范围(最小值和最大值)与 X 的不同,而与 Y 或 z 的不同。如果我们在 3D 空间中绘制数据,它在空间中会显得受到挤压。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些发现表明,扩大这类数据可能不是一个好主意。缩放会将例如所有用户的 X0 值投影到同一坐标系上,从而失去校准的局部性。因此,我们将在不缩放坐标的情况下继续。

欧几里德距离对于我们数据集中的坐标非常有意义。然而,让 id 为 0 的用户和 id 为 14 的用户之间的欧几里德距离影响聚类是不太合适的。类似地,姿势类别 1 与姿势类别 5 的不同之处与姿势类别 2 的不同之处相同。因此,如果我们将用户和类特性包括在集群任务中,我们可能必须扩展它们。

主成分分析聚类

PCA 可用于从我们的 26 维特征集中导出 2 维特征集,同时努力保留尽可能多的信息(方差)。

我们设想下面的前两个主要部分。虽然有两个组件可以解释数据中的差异,但是它们不能对记录进行聚类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

k-表示 R 中的实现

在本文中,我们演示了如何在 Python 笔记本中使用 R 执行 K-Means 聚类。这多亏了 rpy2 ,一个到 R 语言的 Python 接口。

以下函数对输入数据帧df执行 K 均值聚类。cols_features是我们要考虑的数据帧中的列列表。如果为空,则考虑所有列。cols_to_scale是需要缩放以进行适当相似性测量的特征子集。nb_clusters是我们尝试构建的预定义集群数量。nb_config是这个过程重复的次数。cluster_col_name是用于在原始数据帧中存储聚类标签的列名。

我们首先仅使用坐标特征对数据进行聚类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,我们使用所有列进行聚类。我们感兴趣的是调查用户和类如何影响集群的学习。对于我们的任务来说,这一步没有多大意义,但是我们很好奇 K-Means 是否能够拾取标签。为了保持一致的距离度量,我们在聚类之前对用户和类列进行了标准化。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

集群数量的选择很重要

下面,我们使用 R 函数r_factoextra.fviz_cluster可视化 2000 个观察值的随机样本的 K 均值结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在主成分分析图上,聚类看起来非常重叠。第一个维度只能解释数据中 13%的差异,而第二个因素只能解释 8%。在那个空间中,一些质心彼此非常接近。我们可能想把一些集群合并在一起。对于我们的探索,使用 14 个集群并不是一个好的选择。

下面,我们通过将聚类的数量设置为 5 来执行用于姿势识别的 K 均值聚类。可视化显示了更好的集群分离。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

集群有多好?

重要的是评估我们的聚类有多好,换句话说,这些区域的分离程度有多好。

基尼系数聚类优度

基尼系数是评价集群集合异质性程度的常用方法。对于 K 个总标签,每个聚类的基尼系数如下所示,其中 p 是聚类中带有标签 i 的数据点的比例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在我们的例子中,基尼系数将测量任何用户是否以高比例出现在所创建的任何一个集群中。如果一个或多个用户以高比例出现在所考虑的集群中,则该集群的平方和将更接近 1,并且基尼系数将更接近 0。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通常,如果类别在聚类中完全混合,基尼系数最大。第 2 和第 4 簇最纯,系数分别为 0.405 和 0.285。第二类具有特定姿势的强比例(0.697),并且很可能是该姿势的强代表。

这为基于手边数据集的姿势识别目的的学习聚类提供了额外的证据。

熵聚类优度

熵也可以用来检验聚类质量。它利用了簇 i 中的记录被分类为类别 i 的概率。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然基尼系数旨在将错误分类的概率降至最低,但熵是一种衡量杂质的方法。

如果聚类的所有记录都属于同一姿态,则熵为 0,如果我们具有均匀的姿态分布,则熵最大。换句话说,因为概率是 1log(1) = 0 ,所以分离良好的聚类的熵为零。当集群中的所有姿态具有相等的概率时,熵达到最大值。下面我们可以看到第一、第三和第五簇是最不纯的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

结果表明,我们的数据集更有可能被分为 5 组,而不是 14 组。给定记录标记的位置,我们几乎没有机会识别记录是从哪个用户那里产生的。从同一用户获得的位置在不同的手姿势之间没有足够的相似性,因此它们可以被分组到不同的群中。

然而,用户以相似的方式执行姿势,因此不同用户记录的位置之间的距离最小,允许我们以可接受的纯度将它们分组为姿势群。

这些结果是有意义的,但我们可能需要寻找最佳的聚类数,这将使我们实现更好的分离,并可能了解更多的数据。使用肘方法间隙统计剪影技术我们将能够评估群集数量的最佳选择。

聚类地理空间数据

原文:https://towardsdatascience.com/clustering-geospatial-data-f0584f0b04ec?source=collection_archive---------1-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用交互式地图绘制机器学习和深度学习聚类

摘要

在本文中,我将使用数据科学和 Python,展示如何将不同的聚类算法应用于地理空间数据,以解决零售合理化业务案例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

门店合理化 是公司为了提高经营效率,降低成本而进行的重组。由于 T4 和新冠肺炎的危机,世界各地的几家零售企业正在关闭店铺。这并不仅仅是金融危机的症状,事实上许多公司已经将投资集中在使他们的业务更加数字化上。

聚类 是对一组对象进行分组的任务,使同一组中的观察值彼此之间比其他组中的观察值更相似。这是无监督学习(没有目标变量时的机器学习)最受欢迎的应用之一。

地理空间分析 是处理卫星图像、GPS 坐标和街道地址以应用于地理模型的数据科学领域。

在本文中,我将使用地理数据聚类来解决零售合理化问题。我将展示一些有用的 Python 代码,这些代码可以很容易地应用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,以便您可以复制这个示例(下面是完整代码的链接)。

[## mdipietro 09/data science _ 人工智能 _ 实用工具

permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…

github.com](https://github.com/mdipietro09/DataScience_ArtificialIntelligence_Utils/blob/master/machine_learning/example_clustering.ipynb)

我将使用" Starbucks Stores dataset "提供所有正在运营的商店的位置(下面的链接)。我将选择一个特定的地理区域,除了所提供的纬度和经度之外,我将模拟数据集中每个商店的一些业务信息(成本、容量、员工)。

[## 星巴克在全球的位置

运营中的每家星巴克店的名称、所有权类型和位置

www.kaggle.com](https://www.kaggle.com/starbucks/store-locations)

特别是,我将经历:

  • 设置:导入包,读取地理数据,创建业务功能。
  • 数据分析:用叶子geopy 在地图上展示商业案例。
  • 聚类:用 scikit-learn 的机器学习(K-Means / Affinity Propagation),用 minisom 的深度学习(自组织映射)。
  • 商店合理化:构建确定性算法来解决业务案例。

设置

首先,我需要导入以下包。

**## for data**
import **numpy** as np
import **pandas** as pd**## for plotting**
import **matplotlib**.pyplot as plt
import **seaborn** as sns**## for geospatial**
import **folium**
import **geopy****## for machine learning**
from **sklearn** import preprocessing, cluster
import **scipy****## for deep learning**
import **minisom**

然后我将把数据读入一个熊猫数据帧。

dtf = pd.read_csv('data_stores.csv')

原始数据集包含超过 5,000 个城市和 25,000 家商店,但是出于本教程的目的,我将只处理一个城市。

filter = **"Las Vegas"**dtf = dtf[dtf["City"]==filter][["City","Street Address","Longitude","Latitude"]].reset_index(drop=True)dtf = dtf.reset_index().rename(columns={"index":"id"})
dtf.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在那个地区,有 156 家商店。为了继续进行业务案例,我将模拟每个商店的一些信息:

  • 潜在能力:员工的总能力(例如,10 表示商店最多可容纳 10 名员工)
  • 员工:当前员工级别(如 7 表示该店目前有 7 名员工在运营)
  • 容量:当前剩余容量(如 10–7 = 3,商店仍可容纳 3 名员工)
  • 成本:公司维持店铺运营的年度成本()
dtf["**Potential**"] = np.random.randint(low=3, high=10+1, size=len(dtf))dtf["**Staff**"] = dtf["**Potential**"].apply(lambda x: int(np.random.rand()*x)+1)dtf["**Capacity**"] = dtf["**Potential**"] - dtf["**Staff**"]dtf["**Cost**"] = np.random.choice(["high","medium","low"], size=len(dtf), p=[0.4,0.5,0.1])dtf.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,这只是一个模拟,这些数字是随机产生的,并不真正反映星巴克(或任何其他公司)的业务。

现在一切都准备好了,我将从分析业务案例开始,然后构建一个聚类模型和一个合理化算法。

我们开始吧,好吗?

数据分析

假设我们拥有一家零售企业,我们不得不关闭一些商店。我们希望在不裁员的情况下实现利润最大化(通过成本最小化)。

费用分配如下:

**x = "Cost"**ax = dtf[x].value_counts().sort_values().plot(kind="barh")
totals = []
for i in ax.patches:
    totals.append(i.get_width())
total = sum(totals)
for i in ax.patches:
     ax.text(i.get_width()+.3, i.get_y()+.20, 
     str(round((i.get_width()/total)*100, 2))+'%', 
     fontsize=10, color='black')
ax.grid(axis="x")
plt.suptitle(x, fontsize=20)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

目前,只有一小部分商店在满负荷运转(剩余容量= 0),这意味着有些商店的员工数量很少(剩余容量很高):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们把这些信息在地图上形象化。首先,我需要获得地理区域的坐标来启动地图。我会用 geopy 来做:

city = **"Las Vegas"****## get location**
locator = geopy.**geocoders**.**Nominatim**(user_agent="MyCoder")
location = locator.geocode(city)
print(location)**## keep latitude and longitude only**
location = [location.latitude, location.longitude]
print("[lat, long]:", location)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我将使用创建地图,这是一个非常方便的包,允许我们绘制交互式地图,而无需加载 shapefile 。每个商店应通过一个点来识别,该点的大小与其当前的员工人数成比例,颜色基于其成本。我还将向默认地图添加一小段 HTML 代码来显示图例。

**x, y = **"Latitude", "Longitude"**
color = **"Cost"** size = **"Staff"** popup = **"Street Address"**
data = dtf.copy() **## create color column**
lst_colors=["red","green","orange"]
lst_elements = sorted(list(dtf[color].unique()))
data["color"] = data[color].apply(lambda x: 
                lst_colors[lst_elements.index(x)])**## create size column (scaled)**
scaler = preprocessing.MinMaxScaler(feature_range=(3,15))
data["size"] = scaler.fit_transform(
               data[size].values.reshape(-1,1)).reshape(-1) **## initialize the map with the starting location** map_ = folium.**Map**(location=location, tiles="cartodbpositron",
                  zoom_start=11)**## add points**
data.apply(lambda row: folium.**CircleMarker**(
           location=[row[x],row[y]], popup=row[popup],
           color=row["color"], fill=True,
           radius=row["size"]).add_to(map_), axis=1)**## add html legend** legend_html = """<div style="position:fixed; bottom:10px; left:10px; border:2px solid black; z-index:9999; font-size:14px;">&nbsp;<b>"""+color+""":</b><br>"""
for i in lst_elements:
     legend_html = legend_html+"""&nbsp;<i class="fa fa-circle 
     fa-1x" style="color:"""+**lst_colors[lst_elements.index(i)]**+"""">
     </i>&nbsp;"""+str(i)+"""<br>"""
legend_html = legend_html+"""</div>"""map_.get_root().**html.add_child**(folium.**Element**(legend_html)) **## plot the map**
map_**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的目标是尽可能多地关闭高成本商店(红点),将员工转移到位于同一社区的低成本商店(绿点)。因此,我们将最大化利润(通过关闭高成本商店)和效率(通过让低成本商店满负荷工作)。

我们如何在不选择距离阈值和地理边界的情况下定义邻域?嗯,答案是……集群。

使聚集

有几种算法可以使用,这里列出了主要的。我将尝试 K-Means、相似性传播、自组织映射。

K-Means 旨在将观察值划分为预定义数量的聚类( k ),其中每个点属于具有最近均值的聚类。它首先随机选择 k 个质心并将这些点分配给最近的聚类,然后用聚类中所有点的平均值更新每个质心。当您需要获得精确的组数时,这种算法很方便(例如,保持最小数量的运营商店),并且它更适合于少量的偶数聚类。

这里,为了定义正确的 k,我将使用肘方法:绘制方差作为集群数量的函数,并选择使曲线变平的 k

**X = dtf[[**"Latitude","Longitude"**]]max_k = **10****## iterations**
distortions = [] 
for i in range(1, max_k+1):
    if len(X) >= i:
       model = cluster.KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10, random_state=0)
       model.fit(X)
       distortions.append(model.inertia_)**## best k: the lowest derivative**
k = [i*100 for i in np.diff(distortions,2)].index(min([i*100 for i 
     in np.diff(distortions,2)]))**## plot**
fig, ax = plt.subplots()
ax.plot(range(1, len(distortions)+1), distortions)
ax.axvline(k, ls='--', color="red", label="k = "+str(k))
ax.set(title='The Elbow Method', xlabel='Number of clusters', 
       ylabel="Distortion")
ax.legend()
ax.grid(True)
plt.show()**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以尝试使用 k = 5 ,这样 K-Means 算法将找到 5 个理论质心。此外,我还将确定真正的质心(离聚类中心最近的观测值)。

**k = 5
model = cluster.**KMeans**(n_clusters=k, init='k-means++')
X = dtf[[**"Latitude","Longitude"**]]**## clustering**
dtf_X = X.copy()
dtf_X["cluster"] = model.fit_predict(X)**## find real centroids**
closest, distances = scipy.**cluster.vq.vq**(model.cluster_centers_, 
                     dtf_X.drop("cluster", axis=1).values)
dtf_X["centroids"] = 0
for i in closest:
    dtf_X["centroids"].iloc[i] = 1**## add clustering info to the original dataset** dtf[["**cluster**","**centroids**"]] = dtf_X[["**cluster**","**centroids**"]]
dtf.sample(5)**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我向数据集添加了两列:" cluster “表示观察值属于哪个簇,以及” centroids "如果观察值也是质心(最靠近中心),则为 1,否则为 0。让我们计划一下:

****## plot**
fig, ax = plt.subplots()
sns.**scatterplot**(x="Latitude", y="Longitude", data=dtf, 
                palette=sns.color_palette("bright",k),
                hue='cluster', size="centroids", size_order=[1,0],
                legend="brief", ax=ax).set_title('Clustering 
                (k='+str(k)+')')th_centroids = model.cluster_centers_
ax.scatter(th_centroids[:,0], th_centroids[:,1], s=50, c='black', 
           marker="x")**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相似性传播 是一种基于图形的算法,将每个观察值分配给其最近的样本。基本上,所有的观察“投票”给它们想要关联的其他观察,这导致将整个数据集划分成大量不均匀的聚类。当您不能指定簇的数量时,这是非常方便的,并且它适用于地理空间数据,因为它可以很好地处理非平面几何。

**model = cluster.**AffinityPropagation**()**

使用之前的相同代码,您可以拟合模型(找到 12 个簇),并且您可以使用下面的代码来绘图(不同之处在于 k 在开始时没有声明,并且没有理论上的质心):

**k = dtf["cluster"].nunique()sns.**scatterplot**(x="Latitude", y="Longitude", data=dtf, 
                palette=sns.color_palette("bright",k),
                hue='cluster', size="centroids", size_order=[1,0],
                legend="brief").set_title('Clustering 
                (k='+str(k)+')')**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自组织地图 (SOMs)由于使用深度学习而大不相同。事实上,SOM 是一种人工神经网络,它使用无监督学习进行训练,以产生输入空间的低维表示,称为“地图”(也称为 Kohonen 层)。基本上,输入连接到形成映射的 n x m 神经元,然后为每个观察计算“获胜”神经元(最近的),并使用横向距离将神经元聚集在一起。这里,我将尝试使用 4x4 SOM:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

**X = dtf[[**"Latitude","Longitude"**]]
map_shape = **(4,4)****## scale data**
scaler = preprocessing.**StandardScaler**()
X_preprocessed = scaler.fit_transform(X.values)**## clustering** model = minisom.**MiniSom**(x=map_shape[0], y=map_shape[1], 
                        input_len=X.shape[1])
model.train_batch(X_preprocessed, num_iteration=100, verbose=False)**## build output dataframe**
dtf_X = X.copy()
dtf_X["cluster"] = np.**ravel_multi_index**(np.array(
      [model.winner(x) for x in X_preprocessed]).T, dims=map_shape)**## find real centroids** cluster_centers = np.array([vec for center in model.get_weights() 
                            for vec in center])closest, distances = scipy.**cluster.vq.vq**(model.cluster_centers_, 
                                         X_preprocessed)
dtf_X["centroids"] = 0
for i in closest:
    dtf_X["centroids"].iloc[i] = 1**## add clustering info to the original dataset** dtf[["cluster","centroids"]] = dtf_X[["cluster","centroids"]]**## plot** k = dtf["cluster"].nunique()fig, ax = plt.subplots()
sns.**scatterplot**(x="Latitude", y="Longitude", data=dtf, 
                palette=sns.color_palette("bright",k),
                hue='cluster', size="centroids", size_order=[1,0],
                legend="brief", ax=ax).set_title('Clustering 
                (k='+str(k)+')')th_centroids = scaler.inverse_transform(cluster_centers)
ax.scatter(th_centroids[:,0], th_centroids[:,1], s=50, c='black', 
           marker="x")**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

独立于您用来对数据进行聚类的算法,现在您有了一个多了两列的数据集(" cluster “,” centroids ")。我们可以用它来可视化地图上的星团,这次我也要用一个标记来显示质心。

**x, y = **"Latitude", "Longitude"**
color = **"cluster"** size = **"Staff"** popup = **"Street Address"** marker = "**centroids**"
data = dtf.copy()**## create color column**
lst_elements = sorted(list(dtf[color].unique()))
lst_colors = ['#%06X' % np.random.randint(0, 0xFFFFFF) for i in 
              range(len(lst_elements))]
data["color"] = data[color].apply(lambda x: 
                lst_colors[lst_elements.index(x)])**## create size column (scaled)**
scaler = preprocessing.MinMaxScaler(feature_range=(3,15))
data["size"] = scaler.fit_transform(
               data[size].values.reshape(-1,1)).reshape(-1)**## initialize the map with the starting location** map_ = folium.**Map**(location=location, tiles="cartodbpositron",
                  zoom_start=11)**## add points**
data.apply(lambda row: folium.**CircleMarker**(
           location=[row[x],row[y]], popup=row[popup],
           color=row["color"], fill=True,
           radius=row["size"]).add_to(map_), axis=1)**## add html legend** legend_html = """<div style="position:fixed; bottom:10px; left:10px; border:2px solid black; z-index:9999; font-size:14px;">&nbsp;<b>"""+color+""":</b><br>"""
for i in lst_elements:
     legend_html = legend_html+"""&nbsp;<i class="fa fa-circle 
     fa-1x" style="color:"""+**lst_colors[lst_elements.index(i)]**+"""">
     </i>&nbsp;"""+str(i)+"""<br>"""
legend_html = legend_html+"""</div>"""map_.get_root().**html.add_child**(folium.**Element**(legend_html))**## add centroids marker**
lst_elements = sorted(list(dtf[marker].unique()))
data[data[marker]==1].apply(lambda row: 
           folium.**Marker**(location=[row[x],row[y]], 
           popup=row[marker], draggable=False,          
           icon=folium.**Icon**(color="black")).add_to(map_), axis=1)**## plot the map**
map_**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们有了集群,我们可以在每个集群内部开始商店合理化。

商店合理化

因为本文的主要焦点是地理空间数据的聚类,所以我将保持这一部分非常简单。在每个集群中,我将选择潜在目标(高成本商店)和中心(低成本商店),并将目标的员工重新安置在中心,直到后者达到满负荷。当一个目标的所有员工都被转移时,商店可以关闭。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

集群内的迭代

**dtf_new = pd.DataFrame()for c in sorted(dtf["cluster"].unique()):
    dtf_cluster = dtf[dtf["cluster"]==c]

   ** ## hubs and targets**
    lst_hubs = dtf_cluster[dtf_cluster["Cost"]=="low"
               ].sort_values("Capacity").to_dict("records")
    lst_targets = dtf_cluster[dtf_cluster["Cost"]=="high"
               ].sort_values("Staff").to_dict("records") **## move targets**
    for target in lst_targets:
         for hub in lst_hubs:
             **### if hub has space**
             if hub["Capacity"] > 0:
                residuals = hub["Capacity"] - target["Staff"] **#### case of hub has still capacity: do next target**
                if residuals >= 0:
                   hub["Staff"] += target["Staff"]
                   hub["Capacity"] = hub["Potential"] - hub["Staff"]
                   target["Capacity"] = target["Potential"]
                   target["Staff"] = 0
                   break **#### case of hub is full: do next hub**
                else:
                   hub["Capacity"] = 0
                   hub["Staff"] = hub["Potential"]
                   target["Staff"] = -residuals
                   target["Capacity"] = target["Potential"] - 
                                          target["Staff"] dtf_new = dtf_new.append(pd.DataFrame(lst_hubs)
                 ).append(pd.DataFrame(lst_targets))dtf_new = dtf_new.append(dtf[dtf["Cost"]=="medium"]
                 ).reset_index(drop=True).sort_values(
                 ["cluster","Staff"])
dtf_new.head()**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是一个非常简单的算法,可以通过多种方式进行改进:例如,将中等成本的商店纳入等式,并在低成本商店全部满员时重复这一过程。

让我们看看我们用这个基本流程关闭了多少家高成本商店:

**dtf_new["**closed**"] = dtf_new["**Staff**"].apply(lambda x: 1 
                                           if x==0 else 0)
print("closed:", dtf_new["**closed**"].sum())**

我们成功关闭了 19 家商店,但我们是否也保持了该地区的均匀覆盖,这样顾客就不需要去另一个社区逛商店了?让我们在地图上标出关闭的商店( marker = “closed” )来想象一下后果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论

这篇文章是一篇关于如何在零售商业案例中使用聚类和地理空间分析的教程。我使用模拟数据集来比较流行的机器学习和深度学习方法,并展示了如何在交互式地图上绘制输出。我还展示了一个简单的确定性算法来提供业务案例的解决方案。

我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。

👉我们来连线👈

本文是用 Python 进行机器学习系列的一部分**,参见:**

** [## 用 Python 进行机器学习:分类(完整教程)

数据分析和可视化、特征工程和选择、模型设计和测试、评估和解释

towardsdatascience.com](/machine-learning-with-python-classification-complete-tutorial-d2c99dc524ec) [## Python 机器学习:回归(完整教程)

数据分析和可视化、特征工程和选择、模型设计和测试、评估和解释

towardsdatascience.com](/machine-learning-with-python-regression-complete-tutorial-47268e546cea) [## Python 深度学习:神经网络(完整教程)

用 TensorFlow 建立、绘制和解释人工神经网络

towardsdatascience.com](/deep-learning-with-python-neural-networks-complete-tutorial-6b53c0b06af0) [## 基于神经网络的现代推荐系统

使用 Python 和 TensorFlow 构建混合模型

towardsdatascience.com](/modern-recommendation-systems-with-neural-networks-3cc06a6ded2c)**

聚集不稳定性

原文:https://towardsdatascience.com/clustering-instability-486643bb686e?source=collection_archive---------73-----------------------

S 选举集群数量

聚类是一种无监督的学习技术,用于创建数据点的聚类。营销中的客户细分就是一个例子。

有几种聚类算法可用。然而,它们需要给定聚类数(k)作为输入。选择聚类的数量可能是困难的,因为这是一个无监督的问题,没有标签。

给定 k,可以测量聚类不稳定性以确定聚类算法的性能。我们更希望聚类是稳定的,但这意味着什么呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

假设你有一个观察样本(X),我们可以打乱这个数据集,取三个子样本。这三个子样本来自同一个样本 X,因此它们应该遵循相同的结构。

我们选择聚类算法并训练两个不同版本的模型。首先,在数据集 z1 上拟合模型。第二,在数据集 z2 上拟合不同的模型。在这两种情况下,给定特定的 k 值,即聚类数,应用相同的算法。

型号 1.fit(z1)

模型 2 .拟合(z2)

在两个模型都被训练之后,我们预测第三个(验证)子样本 z3 的标签。

预测模型 1(z3)

预测模型 2(z3)

为了测量该聚类映射的不稳定性,我们计算如下:

  • 对于每一个观察点 i 和它的邻居 j = i+1
  • 检查模型 1 是否同意它们具有相同的标签:标签 1(xi) =标签 1(xj)
  • 检查模型 2 是否同意它们具有相同的标签:标签 2(xi) =标签 2(xj)

如果模型 1 将观察值 I 和 j 标记为相等,但是另一方面,模型 2 将两者标记为不同,这将导致聚类映射的不稳定性。

如果同意 1!=同意 2:

不稳定性+= 1

这就引出了这个 k 和这些子样本的不稳定性分数。我们希望对 k 的范围重复这一过程,以评估所有的聚类不稳定性。

最后,我们选择使不稳定性最小化的聚类数(k)。

如有任何关于理论或如何编码的问题,请随时回复。

我目前正在开发一个应用程序,通过潜在的狄利克雷分配来选择主题建模中的主题数量,如果您想看后续报道,请告诉我!

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

聚类移动对象轨迹

原文:https://towardsdatascience.com/clustering-moving-object-trajectories-216c372d37e2?source=collection_archive---------22-----------------------

两个端点之间有多少不同的轨迹?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

亚历克斯·霍利奥克在 Unsplash 上拍摄的照片

你可能会从家里到办公室走一条你从经验中学到的已知路线,在某种意义上这是最好的。也许它最大限度地减少了你早上的通勤时间或旅行成本,或者也许它是最方便的。每天早上上班的时候,我把车开到公共停车场,然后坐地铁。回家的通勤从地铁开始,然后是汽车,但这次我总是使用不同的路线。使用早上的路线开车回家将意味着严重的交通堵塞,所以我绕道缩短我的通勤时间。我的路线选择反映了我的一些条件,了解这些可能有利于交通部门更好地规划。但是他们怎么能仅仅通过观察交通模式就知道这些呢?

轨迹的研究是理解运动物体行为的基础。我们不仅关心开始和结束位置,而且特别关心对象在端点之间移动的路径。一旦我们知道了轨迹,我们就可以收集关于移动物体行为的统计数据,并将其用于未来的推断。例如,在给定一般路径的情况下,我们确定车辆在两个已知位置之间行驶时的典型燃料消耗。我们可以稍后决定在相同位置之间的新行程是属于已知分布还是过于激进。

轨道

空间轨迹是运动物体在地理空间中产生的轨迹,通常由一系列按时间顺序排列的点表示,例如p1→p2→pn,其中每个点都由一个地理空间坐标集和一个时间戳组成,例如 p = ( xyt [1]

移动的物体产生轨迹,即定义空间曲线的时间位置序列。我们通常使用采样过程收集轨迹信息,以离散的时间间隔收集位置。当你允许你的智能手机收集你的位置信息时,这个过程就会发生。该设备正在收集形成折线的数据,而不是一条连续的线。通过给每个顶点分配一个时间戳,折线呈现一个明确的方向,从较小的时间值到较大的时间值。

聚类轨迹

为什么我们需要对轨迹进行聚类?让我们以轻型车辆在现代城市中行驶为例。理解汽车沿着特定轨迹行驶时的驾驶行为是令人感兴趣的。人们可以简单地把可比较的路线想象成在相同的大致位置开始和结束的路线。但是这种假设是误导性的,因为驾驶员可能会选择不同的路径从 A 到达 b。一天中不同的交通状况可能会影响可用的选项,并且相同的行程可能会使用不同的轨迹。如果我们想以某种方式研究两个端点之间的驾驶性能,我们还必须确保我们沿着相似的路径进行比较。我们需要确保轨迹在相同的地方开始和结束,并遵循相同的一般路径。然而,我们需要给轨迹的实际地理匹配留有余地。轨迹记录存在自然的可变性,我们不想强迫车辆精确地通过相同的位置。我们还必须确保考虑到通过 GPS 传感器测量车辆位置时不可避免的误差。

我们如何让轨迹聚类发挥作用?我们使用聚类算法和距离度量。您可以选择任何一种聚类算法,但是距离度量仅限于一小组选项。我们希望确保相似的轨迹比不相似的轨迹产生更低的距离度量值。幸运的是,我们可以为此使用几个距离度量,例如豪斯多夫距离弗雷歇距离。在本文中,我们将使用后者和 HDBSCAN 作为聚类算法。

例子

我将说明如何使用车辆能量数据集【2】数据和代码库对车辆轨迹进行聚类,我一直在构建代码库来探索它。我邀请你克隆储存库并打开 Python 笔记本 9 号。请注意,如果这是您第一次使用此代码,您必须按顺序运行所有笔记本,以准备使用数据库。

笔记本从声明几个支持函数开始,主要是读取和显示轨迹和聚类 geofences。真正的行动开始于名为“轨迹显示”的部分我们将查看一组特定的路径,即从第九组开始并在第六组结束的路径。

下图显示了所选簇之间的所有轨迹。从九点到六点有两种不同的方式,左边的阶梯路径和右边的平滑路径。我们还可以看到一些外围轨迹。聚类过程会对它们产生什么影响?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的地图显示了从第九个星团到第六个星团的所有轨迹。有两个清晰的集群,左边的阶梯路径和右边的弯曲路径。向左的长轨迹似乎是一个异常值。作者创造的形象。

在我们开始聚类过程之前,我们必须首先计算所有轨迹之间的距离矩阵。HDBSCAN 算法不支持轨迹距离函数,因此我们提供了一个距离矩阵。计算非常简单,有两个嵌套循环,并确保所需的对称性。出于性能原因,我们只计算矩阵的上半部分。

上述函数使用离散弗雷歇距离计算对称距离矩阵。

现在,我们可以继续将数据提供给 HDBSCAN,并收集计算出的集群标识符。通过将这些提供给地图显示函数,我们得到了下面显示的最终结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面的地图显示了先前轨迹集的聚类结果。聚类过程正确地识别了两个主要的聚类和一些用灰色表示的外围路径。作者创造的形象。

我们现在可以确定驾驶员在两个终点之间使用的两个主要轨迹,并使用其特定的数据分布来表征它们。这些信息可能有助于我们回答相关的问题,例如为什么司机选择一个轨迹而不是另一个。

结论

本文使用 HDBSCAN 算法和离散弗雷歇距离作为度量,对轨迹进行了聚类。使用这一对算法,首先要计算所有路径之间的距离矩阵。轨迹聚类是移动对象分析的基本工具,因为它可以帮助揭示数据中隐藏的行为。

笔记

1 —由 Scikit-Learn 包实现的 KMeans 聚类算法由于缺乏对距离矩阵的支持而无法使用。显然,这是有充分理由的。

资源

GitHub 库

相关文章

[## 快速离散弗雷歇距离

如何提高离散弗雷歇距离计算性能?

towardsdatascience.com](/fast-discrete-fréchet-distance-d6b422a8fb77) [## 使用 HDBSCAN 进行地理聚类

如何使用 HDBSCAN、H3、图论和 OSM 探索地理数据。

towardsdatascience.com](/geographic-clustering-with-hdbscan-ef8cb0ed6051)

参考

[1]郑,杨,周,谢.(编者).用空间轨迹计算。斯普林格,2011 年。

[2] 汽车能源数据集(VED),用于汽车能源消耗研究的大规模数据集

使用 Python 和 r 对音乐进行聚类,在 Spotify 上创建您的播放列表。

原文:https://towardsdatascience.com/clustering-music-to-create-your-personal-playlists-on-spotify-using-python-and-k-means-a39c4158589a?source=collection_archive---------26-----------------------

使用 K-Means 在 Spotify 播放列表上按相似性对歌曲进行分组的简单方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由海蒂·芬Unsplash 拍摄

S potify 是发现新音乐的最著名的音乐平台之一。该公司使用许多不同的算法,根据用户的音乐偏好向他们推荐新音乐,这些推荐大多位于播放列表中。这些播放列表是根据各种音乐流派为不同用户创建的,甚至 Spotify 也能够根据心情推荐新音乐。

在我的一生中,音乐一直在我的日常生活中,它是一种药物,当我做家务、在办公室工作、遛狗、锻炼等等时,我都需要它。我在 Spotify 上有很多音乐,我一直想根据歌曲的相似性将其分开,并保存到不同的播放列表中。幸运的是,凭借一点机器学习算法和 Python 的知识,我可以实现这个目标!!!。

为此,首先我会列出构建聚类模型所需的工具和 Spotify 音频功能的一些定义。

工具:

  • 熊猫和 Numpy 进行数据分析。
  • Sklearn 构建机器学习模型。
  • Spotipy Python 库(点击此处了解更多信息)。
  • Spotify 凭证访问 Api 数据库和播放列表修改(点击此处了解更多信息)。

Spotify 音频功能:

Spotify 使用一系列不同的功能对歌曲进行分类。我从 Spotify 网页复制/粘贴信息。

  • 声音:一种置信度,从 0.0 到 1.0,表示音轨是否是声音的。1.0 表示音轨是声学的高置信度。
  • 可跳舞性:可跳舞性描述了一个曲目在音乐元素组合的基础上适合跳舞的程度,包括速度、节奏稳定性、节拍强度和整体规律性。值 0.0 最不适合跳舞,1.0 最适合跳舞。
  • 能量:能量是一个从 0.0 到 1.0 的度量,代表强度和活动的感知度量。通常,高能轨道感觉起来很快,很响,很嘈杂。例如,死亡金属具有高能量,而巴赫前奏曲在音阶上得分较低。对该属性有贡献的感知特征包括动态范围、感知响度、音色、开始速率和一般熵。
  • 乐器性:预测音轨是否不包含人声。“Ooh”和“aah”在这种情况下被视为乐器。Rap 或口语词轨道明显是“有声的”。乐器度值越接近 1.0,轨道不包含人声内容的可能性就越大。高于 0.5 的值旨在表示乐器轨道,但随着该值接近 1.0,置信度会更高。
  • 活跃度:检测录音中是否有观众。较高的活跃度值表示音轨被现场执行的概率增加。高于 0.8 的值很有可能表示该音轨是实时的。
  • 响度:轨道的整体响度,以分贝(dB)为单位。响度值是整个轨道的平均值,可用于比较轨道的相对响度。响度是声音的质量,是与体力(振幅)相关的主要心理因素。值通常在-60 和 0 db 之间。
  • 语速:语速检测音轨中是否存在口语单词。越是类似语音的录音(例如脱口秀、有声读物、诗歌),属性值就越接近 1.0。高于 0.66 的值描述可能完全由口语单词组成的轨道。介于 0.33 和 0.66 之间的值描述可能包含音乐和语音的轨道,可以是分段的,也可以是分层的,包括说唱音乐。低于 0.33 的值很可能代表音乐和其他非语音类轨道。
  • 配价:从 0.0 到 1.0 的一种量度,描述音轨所传达的音乐积极性。高价曲目听起来更积极(例如,快乐、愉快、欣快),而低价曲目听起来更消极(例如,悲伤、沮丧、愤怒)。
  • 速度:轨道的整体估计速度,单位为每分钟节拍数(BPM)。在音乐术语中,速度是给定作品的速度或步调,直接来源于平均节拍持续时间。

出于减少信息的目的,我决定使用响度化合价能量、可跳舞性的特征,因为它们对区分精力充沛的歌曲和放松的歌曲更有影响。

1.获取和分析数据:

我最喜欢的乐队是电台司令,所以我决定获得他们的唱片目录以及他们在个人音乐生涯中创作的所有音乐。

我用 Spotipy 库创建了一些函数来下载电台司令汤姆·约克和平原子约翰尼·格林伍德艾德·奥布莱恩科林·格林伍德菲尔·塞尔维的所有歌曲(是的,我痴迷于他们的音乐呵呵)。你可以在我的 Github 库上访问这些功能(点击这里)。

我获得了以下数据:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

具有 423 行 10 列形状的数据框。(图片由作者提供)

我一直想知道为什么我喜欢很多电台司令的音乐,我意识到他们的大多数歌曲都倾向于忧郁。描述了上述特征,数据显示我的化合价和能量小于 0.5,Danceability 倾向于低值,所以我喜欢低能量和负面声音的轨道(仍然有一些我 2000 年的 Emo 边看 MTV 视频)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据框的主要统计数据(图片由作者提供)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

歌曲特征直方图(图片由作者提供)

2.构建模型:

由于我的数据(423 首曲目)的形状,并考虑到我想创建 2 个播放列表,将放松的曲目与充满活力的曲目分开(K=2),我决定使用 K-means 聚类进行无监督的机器学习。

重要提示 :我没有使用训练和测试数据,因为在这种情况下,我只想将所有的曲目分成 2 个不同的组,以创建包含全部数据的播放列表。

所以让我们开始吧!。我首先导入库:

from sklearn.cluster import KMeansfrom sklearn.preprocessing import MinMaxScaler

然后,我需要定义特性并规范化模型的值。我将使用最小最大缩放器来保持原始分布的形状,并在 0 到 1 的范围内缩放特征。一旦我有了正确格式的值,我只需简单地创建 K-Means 模型,然后将标签保存到名为“df”的主数据框中。

col_features = ['danceability', 'energy', 'valence', 'loudness']
X = MinMaxScaler().fit_transform(df[col_features])kmeans = KMeans(init="kmeans++",
                n_clusters=2,
                random_state=15).fit(X)df['kmeans'] = kmeans.labels_

就这样,我把音乐分成了 2 组!!!

但现在我需要研究这些标注的特征,所以我绘制了 3D 散点图中的轨迹,然后我分析了通过 K 均值结果标注对数据框进行分组的每个特征的各自均值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用“能量”、“可跳舞性”和“响度”特征的音轨 3D 散点图(图片由作者提供)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

每个 K 均值标签的均值特征(图片由作者提供)

正如我在图上注意到的,这些值被很好地分组,蓝色值位于标签 0 中,红色值位于标签 1 中。查看手段表,标签 0 将具有较少可跳舞性、能量、效价、响度的轨道分组,因此这个对应于放松歌曲,同样地,标签 1 具有能量歌曲

3.带 R 的模型的精度:

我知道,试图评估聚类算法的最佳结果时,聚类准确性有点主观,但同样,我想观察我的模型是否能很好地分离轨迹。所以在 Rstudio 的一点帮助下,我使用了轮廓分析。来衡量我的模型的准确性。

在 Rstudio 中,我使用库“cluster”和“factoextra”来可视化和计算使用欧几里德距离的轮廓分析。完整的代码在我的 Github 存储库中(点击这里):

#Calculate The euclidean distance of my dataframe values.
dd <- dist(df,method="euclidean")#Silhouette Analysis using the K-means model(km) and distance(dd)
sil.km <- silhouette(km$cluster,dd)
fviz_silhouette(sil.km)

结果是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

轮廓分析(图片由作者提供)

轮廓分析是一种测量聚类中的每个点与其相邻聚类中的点的接近程度的方法。轮廓值在[-1,1]范围内。值+1 表示样本远离其相邻聚类,而非常接近其所分配的聚类。类似地,值为-1 表示该点与其相邻的簇的距离比与其所分配的簇的距离更近。因此,在我的例子中,值在 0.25 和 0.60 之间,这意味着大多数值被很好地分组。

4.在 Spotify 上创建播放列表:

为了创建播放列表并添加集群曲目,我使用了本文第一部分中介绍的库 Spotipy。你只需要获得一个客户端 id、客户端密码和用户名代码,就可以使用 Spotify 的 API 并操作你的音乐库。我把信息的链接给你(点击这里)。

我必须将曲目分成 2 个不同的变量,然后有了曲目的 id,我只需创建 2 个新的播放列表,并向它们传递曲目的 id。代码如下所示:

#Separating the clusters into new variables
cluster_0 = df[df['kmeans']==0]
cluster_1  = df[df['kmeans']==1]#Obtaining the ids of the songs and conver the id dataframe column to a list.
ids_0 = cluster_0['id'].tolist()
ids_1 = cluster_1['id'].tolist()#Creating 2 new playlists on my Spotify User
pl_energy = sp.user_playlist_create(username=username,
                                           name="Radiohead :)")pl_relaxed = sp.user_playlist_create(user=username,
                                            name="Radiohead :(")#Adding the tracks into the playlists
#For energetic Playlist
sp.user_playlist_add_tracks(user=username,
                            playlist_id = pl_energy['id'],
                            tracks=ids_1)#For relaxed Playlist
sp.user_playlist_add_tracks(user=username,
                            playlist_id = pl_relaxed['id'],
                            tracks=ids_0)

最后,我有两个电台司令歌曲的播放列表,使用 K-means 聚类算法将充满活力的歌曲从放松的歌曲中分离出来!!!!。

如果你想听这两个播放列表,你可以在下面访问它们。

最有活力的电台司令歌曲(209 首)

作者创建的播放列表

最轻松的电台司令歌曲(214 首)

作者创建的播放列表

结论

机器学习算法在实现与你喜欢的事情相关的想法或项目方面有很多乐趣。就我而言,我非常喜欢音乐,所以我可以用这些知识创造一些很酷的方法来帮助我自动完成一项可能需要很长时间才能完成的任务。我还可以更多地了解这个神奇的数据科学世界,以及我对音乐品味的倾向。

我的文章:

参考资料:

聚类技术

原文:https://towardsdatascience.com/clustering-techniques-hierarchical-and-non-hierarchical-b520b5d6a022?source=collection_archive---------13-----------------------

聚类属于 u 监督学习技术。在这种技术中,数据没有标记,也没有定义因变量。这种类型的学习通常是为了识别数据中的模式和/或对相似的数据进行分组。

在这篇文章中,详细解释了集群技术的类型,并提供了一个代码遍历。

什么是集群?

聚类是一种对相似对象进行分组的方法。聚类的目的是从异类的观察中创建同类的组。假设数据来自多个群体,例如,可能有不同行业的人出于不同目的向银行申请贷款。如果这个人是学生,他/她可以申请教育贷款,想买房的人可以申请住房贷款等等。聚类有助于识别相似的群体,更好地满足需求。

为什么要集群?

聚类是一种基于距离的算法。聚类的目的是最小化类内距离和最大化类间距离。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

未分类的数据(图片由作者提供)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

聚类数据(按作者分类的图片)

聚类作为一种工具可以用来深入了解数据。通过可视化数据可以获得大量的信息。聚类的输出也可以用作其他算法的预处理步骤。这项技术有几个广泛使用的用例,其中一些重要的是市场细分、客户细分和图像处理。

在继续之前,让我们了解一下集群的核心。

距离的度量

聚类完全是关于两点之间的距离和两个聚类之间的距离。距离不能为负。对于聚类问题,该算法使用一些常见的距离度量。

欧氏距离

这是算法使用的默认距离。最好解释为两点之间的距离。如果要测量两点 p 和 q 之间的距离,则欧几里德距离为

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

欧几里德距离(图片由作者提供)

曼哈顿距离

它是沿轴以垂直角度计算的两点之间的距离。它也被称为出租车距离,因为这代表了曼哈顿市的车辆如何在街道以直角相交的地方行驶。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

曼哈顿距离(图片作者提供)

闵可夫斯基距离

在 n 维空间中,两点之间的距离称为闵可夫斯基距离。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

闵可夫斯基距离(图片作者提供)

欧几里德距离和曼哈顿距离的推广是,如果 p 的值是 2,则它成为欧几里德距离,如果 p 的值是 1,则它成为曼哈顿距离。

聚类的类型

有两种主要类型的聚类技术

  1. 等级或聚集
  2. k 均值

让我们看看每种类型以及代码走查

分层聚类

这是一种自下而上的方法。基于记录之间的距离以及聚类之间的距离,数据集中的记录被顺序分组以形成聚类。这是一个逐步实现这个方法的方法

  1. 从 n 个群集开始,其中每行被视为一个群集
  2. 使用基于距离的方法,将彼此最接近的两个记录合并到一个聚类中。在图 3 中,对于给定的五个记录,假设 A 和 C 在距离上最接近,它们形成一个聚类,同样 B 和 E 形成另一个聚类,等等

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

两个最接近记录的聚类(按作者分类的图片)

3.在每一步,两个最接近的聚类将被合并。要么将单个记录(singleton)添加到现有分类中,要么合并两个分类。在至少一个多元素集群形成后,需要为单个元素和一组观察值计算距离的场景,这就是链接的概念出现的地方。有五种主要类型的链接。通过使用下面的一个概念,聚类发生了-

  • 单联动: 它是两个集群中任意两点之间的最短距离
  • 完全联动: 与单联动相反。这是两个集群中任意两点之间的最长距离
  • 平均连锁度: 一个聚类中的每一点到另一个聚类中的每一点的平均距离
  • 质心联动: 一个簇的中心点到另一个簇的中心点的距离
  • 沃德连锁: 平均法和质心法的结合。通过确定分类的中心点和观察值与中心的距离来计算分类内方差。当试图合并两个聚类时,在聚类之间找到方差,并且与另一个组合相比方差较小的聚类被合并。

需要注意的一点是,每种链接方法都会产生一个独特的结果。当这些方法中的每一种应用于相同的数据集时,它可能被不同地聚类。

4.重复这些步骤,直到出现一个包含所有记录的聚类

形象化

为了形象化聚类,有一个叫做**树状图的概念。**树状图是总结聚类过程的树形图。记录在 x 轴上。相似的记录由直线连接,直线的垂直长度反映了记录之间的距离。身高差距越大,差异越大。展示了一个样本树状图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

树状图(图片由作者提供)

分层聚类代码演练

分层聚类的代码是使用 jupyter notebook 用 Python 3x 编写的。让我们从导入必要的库开始。

#Import the necessary librariesimport numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster

接下来,加载数据集。这里使用了星巴克食物菜单上的一个数据集。

#Read the datasetdf = pd.read_csv('starbucks_menu.csv')#Look at the top 5 rows df.head()

进行必要的探索性数据分析,如查看描述性统计数据,检查空值和重复值。进行单变量和双变量分析,进行异常值处理(如果有的话)。由于这是一种基于距离的算法,因此有必要在适用的情况下执行标准化,以便所有变量都没有任何测量单位。这使得模型能够以最佳状态运行。

from scipy.stats import zscore
df.iloc[:,1:6] = df.iloc[:,1:6].apply(zscore)#Check the head after scalingdf.head()

一旦数据准备好了,让我们开始构建模型。需要分配一个标签列表,它是分类变量的唯一值的列表。这里,标签列表是从食物变量中创建的。

#Before clustering, setup label list from the food variablelabelList = list(df.Food.unique())
labelList

下一步是形成一个链接,将单例集群和另一个集群连接起来。在这种情况下,沃德的法是首选。

#Create linkage method using Ward's methodlink_method = linkage(df.iloc[:,1:6], method = 'ward')

借助树状图可视化聚类。在这种情况下,通过指定显示倒数第二个和倒数第二个聚类的 p 值,得到一个截断的树状图。

#Generate the dendrogramdend = dendrogram(link_method,
                  labels = labelList,
                  truncate_mode='lastp', 
                  p=10)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

截断的树状图(图片由作者提供)

一旦创建了树状图,就需要切割树来确定最佳的聚类数。有两种方法可以做到这一点(如图所示)。在这种情况下,选择 3 个集群。可将聚类作为新列附加到数据框中,以获得更多信息。

#Method 1: criterion = 'maxclust' where a cut is defined based on the number of clustersclusters = fcluster(link_method, 3, criterion='maxclust') 
clusters#Method 2: criterion='distance' where a cut is defined based on distance in the y-axis#clusters = fcluster(link_method, 800, criterion='distance')#Apply the clusters back to the datasetdf['HCluster'] = clusters
df.head()

最后一步是进行聚类分析,从算法中提取信息和见解,以帮助做出有效的决策。聚类分析是通过对聚类的平均值进行分组并基于频率进行排序来完成的。

aggdata=df.iloc[:,1:8].groupby('HCluster').mean()
aggdata['Frequency']=df.HCluster.value_counts().sort_index()
aggdata

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

聚类分析(作者图片)

快速了解一下,第一类食物通常热量较低,因此宏量营养素含量较低。第二类食物的卡路里含量最高,因此宏量营养素含量也较高,第一类和第二类食物的卡路里含量居中,第三类食物的卡路里含量和宏量营养素含量较高。总的来说,简而言之,这种模式聚集得很好。

让我们继续下一个方法

k 均值聚类

K-Means 是一种无层次的方法。这个想法是事先指定集群的数量。根据分类的数量,每个记录根据与每个分类的距离被分配到分类中。当数据集很大时,这种方法是首选的。单词表示 k-means 中的是指数据的平均,也称为寻找质心。这是一个循序渐进的方法

  1. 事先指定 k 值
  2. 将每个记录分配给到质心的距离最小的聚类。默认情况下,K 均值使用欧几里德距离
  3. 重新计算新形成的簇的质心。基于距离,一些数据点可能会移动。
  4. 重新分配可以在迭代的基础上发生,并且形成新的质心。这个过程将停止,直到没有从一个集群到另一个集群的观察跳跃。
  5. 如果有任何重新分配,请返回步骤 3 并继续这些步骤。如果没有,则集群被最终确定。

尽管我们已经事先确定了聚类的数量,但这并不总是正确的,因此有必要确定最优的聚类数量。没有确定集群数量的可靠解决方案,但是有一个通用的方法。对于 k 的每个值,可以识别平方和(WSS)值。不能选择单个聚类,因此重要是找到 k 值,在该 k 值之后 WSS 值没有显著差异。为了提高效率,可以绘制一个肘形图,y 轴为 WSS 分数,x 轴为聚类数,以直观显示最优聚类数。

有一种方法可以了解模型的表现。这是通过检查两个度量来完成的,即轮廓宽度和**轮廓分数。**这有助于我们根据距离标准分析映射到聚类的每个观察值是否正确。轮廓宽度计算如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

轮廓宽度公式(图片由作者提供)

其中 b 是观测值和相邻聚类的质心之间的距离,a 是观测值和自身聚类的质心之间的距离。

轮廓宽度的值可以在-1 到 1 的范围内。如果轮廓宽度的值为正,则观测值到当前聚类的映射是正确的。当 a > b 时,轮廓宽度将返回负值。所有轮廓宽度的平均值称为轮廓得分。如果最终得分是正值,并且接近+1,则平均来说,聚类被很好地分开。如果接近 0,说明分离得不够好。如果是负值,则该模型在聚类中犯了一个错误。

k-均值聚类代码遍历

让我们从导入必要的库开始

#Import the necessary librariesimport numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

接下来,加载数据集。这里使用了用于分层聚类的相同数据集。

进行必要的探索性数据分析,如查看描述性统计数据,检查空值和重复值。进行单变量和双变量分析,进行异常值处理(如果有的话)。K-means 聚类需要扩展。这样做是为了使所有的变量都没有任何测量单位。这使得模型能够以最佳状态运行。在这种情况下,使用 StandardScaler 方法。

#Importing the standard scaler module and applying it on continuous variablesfrom sklearn.preprocessing import StandardScaler 
X = StandardScaler()
scaled_df = X.fit_transform(df.iloc[:,1:6])
scaled_df

下一步是调用 KMeans 方法,预先定义集群的数量。然后将缩放后的数据集拟合到模型中。

# Create K Means cluster and store the result in the object k_meansk_means = KMeans(n_clusters=2)# Fit K means on the scaled_dfk_means.fit(scaled_df)# Get the labelsk_means.labels_

现在是时候通过分析给定 k 范围内的平方和(WSS)值来找到最佳聚类数了

#To determine the optimum number of clusters, check the wss score for a given range of kwss =[] 
for i in range(1,11):
    KM = KMeans(n_clusters=i)
    KM.fit(scaled_df)
    wss.append(KM.inertia_)

wss

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

WSS 得分(图片由作者提供)

可以看出,在 k=2 之后,WSS 分数有所下降,因此让我们关注 k=3。同样的情况也可以用肘图来显示

#Draw the elbow plotplt.plot(range(1,11), wss, marker = '*')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

肘图(图片由作者提供)

决定聚类数量的另一个帮助可以是轮廓分数的值。如前所述,分数越高,聚类越好。让我们检查分数。

#Checking for n-clusters=3k_means_three = KMeans(n_clusters = 3)
k_means_three.fit(scaled_df)
print('WSS for K=3:', k_means_three.inertia_)
labels_three = k_means_three.labels_
print(labels_three)#Calculating silhouette_score for k=3print(silhouette_score(scaled_df, labels_three))

k=3 的 WSS 是 261.67,并且这些标签的轮廓分数是 0.3054。因为分数是正的,所以这是发生了良好聚类的标志。

最后一步是进行聚类分析,以了解聚类是如何发生的,并获得更多的见解。

clust_profile=df.iloc[:,1:8].groupby('KMCluster').mean()
clust_profile['KMFrequency']=df.KMCluster.value_counts().sort_index()
clust_profile

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

KMeans 的聚类分析(图片由作者提供)

就像分层聚类一样,这三个聚类表示三个级别的食物,它们具有不同的热量和宏观营养范围。

使用凸包的聚类

原文:https://towardsdatascience.com/clustering-using-convex-hulls-fddafeaa963c?source=collection_archive---------17-----------------------

如何在数据聚类中使用凸包

最近在模式识别快报偶然看到哈坎·切维卡普的文章《利用局部仿射/凸包 进行高维数据聚类》。提出了一种利用局部仿射/凸包对高维数据进行聚类的新算法。他们使用凸包进行聚类的方法启发了我。我想尝试使用凸包实现我自己的简单聚类方法。因此,在本文中,我将向您介绍我使用凸包实现聚类方法的过程。在我们进入编码之前,让我们看看什么是凸包。

凸包

根据维基百科,凸包定义如下。

在几何学中,一个形状的凸包或凸包络或凸闭包是包含它的最小凸集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图一。一组钉子的凸包(图片由作者提供)

让我们考虑一个简单类比的例子。如图 1 所示,假设有几个钉子被钉在木板的中间。你拿一根橡皮筋,把它拉长包住钉子,然后放开它。它将适合最外面的指甲(显示为蓝色),并采取最小化其长度的形状。被橡皮筋围起来的区域叫做这组钉子的凸包

这个二维空间中的凸包(如图 1 所示)将是一个凸多边形,其所有内角都小于 180°。如果是在 3 维或者更高维的空间,那么凸包会是一个多面体

有几种算法可以确定给定点集的凸包。一些著名的算法是礼品包装算法格雷厄姆扫描算法

由于凸包包围了一组点,所以它可以充当聚类边界,允许我们确定聚类中的点。因此,我们可以利用凸包和执行聚类。让我们进入代码。

简单的例子

在这个例子中,我将使用 Python。在开始之前,我们需要以下 Python 库。

sklearn
numpy
matplotlib
mpl_toolkits
itertools
scipy
quadprog

资料组

为了创建我们的样本数据集,我将使用 sci-kit learn 库的 make blobs 函数。我将制作 3 个集群。

import numpy as np
from sklearn.datasets import make_blobscenters = [[0, 1, 0], [1.5, 1.5, 1], [1, 1, 1]]
stds = [0.13, 0.12, 0.12]X, labels_true = make_blobs(n_samples=1000, centers=centers, cluster_std=stds, random_state=0)
point_indices = np.arange(1000)

由于这是一个三维点数据集,我将绘制一个 3D 图来显示我们的地面真实集群。图 2 显示了带有彩色聚类的数据集的散点图。

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3Dx = X[:,0]
y = X[:,1] 
z = X[:,2]

# Creating figure 
fig = plt.figure(figsize = (15, 10)) 
ax = plt.axes(projection ="3d") 

# Add gridlines  
ax.grid(b = True, color ='grey',  
        linestyle ='-.', linewidth = 0.3,  
        alpha = 0.2)  

mycolours = ["red", "green", "blue"]# Creating color map 
col = [mycolours[i] for i in labels_true]# Creating plot 
sctt = ax.scatter3D(x, y, z, c = col, marker ='o')plt.title("3D scatter plot of the data\n") 
ax.set_xlabel('X-axis', fontweight ='bold')  
ax.set_ylabel('Y-axis', fontweight ='bold')  
ax.set_zlabel('Z-axis', fontweight ='bold')

# show plot 
plt.draw()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图二。数据集的初始散点图(图片由作者提供)

获得初始聚类

首先,我们需要将数据集分成两部分。一部分将被用作种子,以使用 K-means 获得初始聚类。另一部分中的点将被分配给基于初始聚类的聚类。

from sklearn.model_selection import train_test_splitX_seeds, X_rest, y_seeds, y_rest, id_seeds, id_rest = train_test_split(X, labels_true, point_indices, test_size=0.33, random_state=42)

现在,我们对种子点执行 K 均值聚类。

from sklearn.cluster import KMeanskmeans = KMeans(n_clusters=3, random_state=9).fit(X_seeds)
initial_result = kmeans.labels_

由于生成的标签可能与地面真实标签不同,我们必须映射这两组标签。为此,我们可以使用下面的函数。

from itertools import permutations# Source: [https://stackoverflow.com/questions/11683785/how-can-i-match-up-cluster-labels-to-my-ground-truth-labels-in-matlab](https://stackoverflow.com/questions/11683785/how-can-i-match-up-cluster-labels-to-my-ground-truth-labels-in-matlab)def remap_labels(pred_labels, true_labels): pred_labels, true_labels = np.array(pred_labels), np.array(true_labels)
    assert pred_labels.ndim == 1 == true_labels.ndim
    assert len(pred_labels) == len(true_labels)
    cluster_names = np.unique(pred_labels)
    accuracy = 0 perms = np.array(list(permutations(np.unique(true_labels)))) remapped_labels = true_labels for perm in perms: flipped_labels = np.zeros(len(true_labels))
        for label_index, label in enumerate(cluster_names):
            flipped_labels[pred_labels == label] = perm[label_index] testAcc = np.sum(flipped_labels == true_labels) / len(true_labels) if testAcc > accuracy:
            accuracy = testAcc
            remapped_labels = flipped_labels return accuracy, remapped_labels

我们可以从上面的函数中得到精度和映射的初始标签。

intial_accuracy, remapped_initial_result = remap_labels(initial_result, y_seeds)

图 3 表示种子点的初始聚类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图三。使用 K-means 对种子点进行初始聚类(图片由作者提供)

得到初始聚类的凸包

一旦我们获得了初始聚类,我们就可以得到每个聚类的凸包。首先,我们必须获得聚类中每个数据点的索引。

# Get the idices of the data points belonging to each cluster
indices = {}for i in range(len(id_seeds)):
    if int(remapped_initial_result[i]) not in indices:
        indices[int(remapped_initial_result[i])] = [i]
    else:
        indices[int(remapped_initial_result[i])].append(i)

现在我们可以从每个集群中获得凸包。

from scipy.spatial import ConvexHull# Get convex hulls for each cluster
hulls = {}for i in indices:
    hull = ConvexHull(X_seeds[indices[i]])
    hulls[i] = hull

图 4 表示代表 3 个聚类中的每一个的凸包。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。每个聚类的凸包(图片由作者提供)

将剩余的点分配给最近的凸包簇

现在我们有了初始聚类的凸包,我们可以将剩余的点分配给最近的凸包的聚类。首先,我们必须将数据点投影到一个凸包上。为此,我们可以使用下面的函数。

from quadprog import solve_qp# Source: [https://stackoverflow.com/questions/42248202/find-the-projection-of-a-point-on-the-convex-hull-with-scipy](https://stackoverflow.com/questions/42248202/find-the-projection-of-a-point-on-the-convex-hull-with-scipy)def proj2hull(z, equations): G = np.eye(len(z), dtype=float)
    a = np.array(z, dtype=float)
    C = np.array(-equations[:, :-1], dtype=float)
    b = np.array(equations[:, -1], dtype=float) x, f, xu, itr, lag, act = solve_qp(G, a, C.T, b, meq=0, factorized=True) return x

寻找凸壳上的点的投影的问题可以使用二次规划来解决。上述功能利用了quadprog模块。您可以使用[conda](https://anaconda.org/omnia/quadprog)[pip](https://pypi.org/project/quadprog/)安装quadprog模块。

conda install -c omnia quadprog
OR
pip install quadprog

关于如何用二次规划解决这个问题,我就不赘述了。如果你有兴趣,你可以阅读更多来自这里这里

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图五。从一个点到它在凸包上的投影的距离(图片由作者提供)

一旦获得了凸包上的投影,就可以计算从该点到凸包的距离,如图 5 所示。基于这个距离,现在让我们将剩余的数据点分配给最近的凸包的簇。

我将考虑从数据点到它在凸包上的投影的欧几里德距离。那么该数据点将被分配到具有距离该数据点最短距离的凸包的聚类中。如果一个点位于凸包内,那么距离将为 0。

prediction = []for z1 in X_rest: min_cluster_distance = 100000
    min_distance_point = ""
    min_cluster_distance_hull = ""

    for i in indices: p = proj2hull(z1, hulls[i].equations) dist = np.linalg.norm(z1-p) if dist < min_cluster_distance: min_cluster_distance = dist
            min_distance_point = p
            min_cluster_distance_hull = i prediction.append(min_cluster_distance_hull)prediction = np.array(prediction)

图 6 显示了最终的聚类结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图六。凸包的最终结果(图片由作者提供)

评估最终结果

让我们评估一下我们的结果,看看它有多准确。

from sklearn.metrics import accuracy_scoreY_pred = np.concatenate((remapped_initial_result, prediction))
Y_real = np.concatenate((y_seeds, y_rest))
print(accuracy_score(Y_real, Y_pred))

我得到了 1.0 (100%)的准确率!棒极了,激动人心,对吧?😊

如果想了解更多关于评估聚类结果的内容,可以查看我之前的文章评估聚类结果

[## 评估聚类结果

用于评估聚类结果的标准

towardsdatascience.com](/evaluating-clustering-results-f13552ee7603)

我使用了一个非常简单的数据集。您可以在更复杂的数据集上尝试这种方法,看看会发生什么。

高维数据

我还尝试使用我的聚类外壳方法对一个具有 8 维数据点的数据集进行聚类。你可以找到显示代码和结果的 jupyter 笔记本。最终结果如下。

Accuracy of K-means method: 0.866
Accuracy of Convex Hull method: 0.867

我的凸包方法相对于 K-means 有一点改进。

最后的想法

HakanCevikalp 的题为 通过使用局部仿射/凸包 进行高维数据聚类的文章表明,他们提出的基于凸包的方法避免了“孔洞伪影”问题(高维空间中的稀疏和不规则分布会使最近邻距离不可靠),并且与其他最先进的子空间聚类方法相比,提高了高维数据集的准确性。

[## 利用局部仿射/凸包的高维数据聚类

我们提出了一种新的算法,使用局部仿射/凸包的高维数据聚类。*建议的…

www.sciencedirect.com](https://www.sciencedirect.com/science/article/pii/S0167865519302806?via%3Dihub)

您可以找到包含本文所用代码的 jupyter 笔记本

希望这篇文章是有趣和有用的。

干杯!😃

基于 k-Means 的聚类算法及其实现

原文:https://towardsdatascience.com/clustering-using-k-means-with-implementation-40988620a973?source=collection_archive---------42-----------------------

我们周围的物体来自自然群体

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:https://unsplash.com/@nauleyco

C 聚类是一种在数据中寻找自然群体的技术。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1:成群的动物(来源:作者)

如果我们给一个孩子看上面的图片,他可以识别出有四种动物。他可能不知道他们所有人的名字,但他仍然可以识别出有四种不同的类型,他可以独立完成这项工作,不需要成年人的帮助。由于我们不需要一个成年人来监督,聚类是一种无人监督的技术

集群的主要动机是什么?

这三个动机可以列举如下。

**底层结构:**我们可以理解有不同的组,组内的差异较小,组间的差异较小。

**自然分类:**我们知道动物有四种不同的类型或类群。

总结:如果我们现在为它们的住所或食物想出一些策略,我不需要为所有的动物考虑,因为一只羊的食物需求与熊猫、长颈鹿或 Rhyno 不同,但与另一只羊没有太大的不同。当任何公司试图提出一个客户群特定的策略时,这可能是非常重要的。

k-means 可以说是最流行的算法,它将对象分成 k 个组。这有许多应用,因为我们想在数据中找到结构。我们希望将学生、客户、患者、产品、电影、推文、图像等等分组。

现在让我们看看它是如何工作的。

k-means 如何工作

输入:将给出数据和“k”的值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据:输入到 K- Means(图片:作者)

步骤 1:从数据中初始化随机的“k”个点作为聚类中心,让我们假设 k 的值是 2,选择第 1 和第 4 个观察值作为中心。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

随机选择的 K (2)点(来源:作者)

步骤 2:对于所有的点,找出距离 k 个聚类中心的距离。可以使用欧几里德距离。下表用作说明。C1 和 C2 列分别表示离中心的距离。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

距质心的距离计算(来源:作者)

如果对于第二次观测,我们想要确定距质心 1 的距离,计算将如下所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

欧几里德距离的例子(来源:作者)

  • 请注意,第一个点距中心 1 的距离为 0,第四个点距第二个聚类的距离为 0。原因很简单,这些点被选为质心
  • 同样对于第三次观察,两个中心的距离是相同的。

步骤 3:将每个点分配给它最近的聚类中心,然后使用算术方法再次计算聚类中心

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分配给最近中心的点(来源:作者)

对于前两个点,它们被分配给聚类 1,第三个点被随机分配,最后四个点被分配给聚类 2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

集群中心(来源:作者)

重复步骤 2 和步骤 3,直到收敛

  • 每次迭代之间聚类中心没有变化
  • 集群分配的变化非常小

本质上,我们试图找到 k 个最佳代表点。这些是通过取平均值来选择的。因此,我们试图迭代地找到 k 个最佳代表点。因此得名 k-means。

k-means 中的一些问题是什么,如何绕过它们?

k-means 的一些问题是

  • 由于第一步是随机初始化,这关系到聚类的质量(其中一些可以通过 k-means ++来解决,k-means ++递增地选择随机点,确保下一个点仅在远离所选点时被选择)
  • 拾取圆形的圆(可以在一定程度上使用距离度量)
  • 受到异常值的影响(可以删除异常值,然后应用聚类)
  • k 的值需要由用户给定(讨论了找到相同值的方法)

如何求 k 的最优值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

WCSS 在平方和聚类内(来源:作者)

好的聚类是那些点靠近中心的聚类。怎样才能找到亲近?我们可以找到该簇中所有点离簇中心的欧几里德距离。现在我们需要将此扩展到所有集群。这种想法用上面的等式表示,称为平方的组内和。理想情况下,我们希望它很小。极端的情况是,我们可以对每个点有一个聚类,结果 WCSS 为 0。事实证明,随着 k 的增加,WCSS 继续减小。从技术上讲,这被称为单调递减。

我们想停下来,当我们在 y 轴上画 WCSS,在 x 轴上画星团数量的时候,有一个最陡的下降或形成一种肘形。

如何应用 k-means

标准化:所有基于距离的算法都需要对数据进行标准化,这样就不会因为值的范围更大而使某个属性变得过于重要。下面给出了代码片段。

# Importing the Standard Scaler
from sklearn.preprocessing import StandardScaler 
#Intializing the Stnadard Scaler
scaler = StandardScaler().fit(df) 
#Standardizing the data
df_scaled = scaler.transform(df)

集群:下面给出了代码片段

#Imporing the Library
from sklearn.cluster import KMeans
# Intialization
kmeans = KMeans(n_clusters = 3, init = 'random', max_iter = 300, n_init = 10, random_state = 0) 
#Applying Clustering
y_kmeans = kmeans.fit_predict(df_scaled)

一些重要参数:

  • n_clusters:簇的数量或 k
  • init: Random 或 kmeans++(我们已经讨论过 kmeans++如何提供更好的初始化)
  • n_init:算法是用这些不同的初始 k 点随机选择来运行的。最好的还回来了。
  • 最大迭代次数:算法将运行多少次迭代
  • tol:代表公差,也用于终止标准。这是一个有趣的概念。如果从一次迭代到另一次迭代,中心的变化很小,那么我们就趋向于收敛。中心是矩阵,我们可以通过检查元素间的欧几里德距离来找出两个矩阵之间的接近度。这被称为弗罗贝纽斯范数。

确定“k”的值

下面给出了代码片段。

# Applying k-means for diffrent value of k and storing the WCSS
from sklearn.cluster import KMeans
wcss = []
for i in range(1, 11):
    kmeans = KMeans(n_clusters = i, init = 'random', max_iter = 300, n_init = 10, random_state = 0)
    kmeans.fit(x_scaled)
    wcss.append(kmeans.inertia_)

用于绘制聚类数

plt.plot(range(1, 11), wcss)
plt.title('The elbow method')
plt.xlabel('Number of clusters')
plt.ylabel('SSE')      #within cluster sum of squares
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2:通过肘图得出的聚类数(来源:作者)

  • 你可以在这里查看我们的完整实现。
  • 你也可以在这里看一个关于集群的视频教程

结论:

k-means 因其简单、易于实现而一直存在。在任何调查中,它都很容易出现在 ML 的前 10 个算法中。有缺点,也有办法使 k-means 稳健。

参考:

[1]https://towardsdatascience . com/k-means-clustering-13430 ff 3461d

K-均值聚类

原文:https://towardsdatascience.com/clustering-with-k-means-1e07a8bfb7ca?source=collection_archive---------3-----------------------

使用无监督的机器学习在天气数据中寻找模式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片来源:unsplash-logoNicole Wilcox

无监督学习最常用的技术之一是聚类。顾名思义,聚类是对具有相似特征的数据进行分组的行为。在机器学习中,当没有预先指定的数据标签可用时,即我们不知道要创建哪种分组时,会使用聚类。目标是将数据分组到相似的类中,以便:

类内相似度高

类间相似度低

有两种主要的聚类类型— K 均值聚类层次凝聚聚类。在 K 均值聚类的情况下,我们试图找到 k 聚类中心作为属于这些聚类的数据点的均值。这里,预先指定了聚类的数量,并且该模型旨在为任何给定的聚类*,K*找到最优的聚类数量。

我们使用的是来自 Kaggle 的分钟天气数据集,其中包含与天气相关的测量值,如气压、最大风速、相对湿度等。这些数据是从 2011 年 9 月到 2014 年 9 月的三年时间里在圣地亚哥采集的,包含以一分钟为间隔采集的原始传感器测量值。

第一步是导入必要的库…

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
import sklearn
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn import metrics
from sklearn.cluster import AgglomerativeClustering

…和数据集

df = pd.read_csv('minute_weather.csv')
df.head()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

浏览我们的数据,我们发现有 1,587,257 行和 13 列!由于这个数据集非常大,我们需要随机抽取样本。此外,对于 K-means 方法,首先找到初始质心的位置是必要的,以便该算法可以找到收敛性。为了做到这一点,我们不使用整个数据集,而是起草一个样本,对随机初始化的质心进行短期运行,并跟踪度量的改进。这里给出了这种方法的一个很好的解释。

从每第 10 行抽取一个样本,我们创建一个新的样本数据帧

sample_df = df[(df['rowID'] % 10) == 0]
sample_df.shape

这产生了 158726 行和 13 列(好得多!)

检查空值,我们发现 rain_accumulation 和 rain_duration 可以被删除

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

df1 = sample_df.drop(columns =['rain_accumulation','rain_duration'])
print(df1.columns)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在最大风速/风向和最小风速/风向这两个值之间,出于聚类的目的,我只选择了最大值,因为我们已经有了这两个值的平均值。如果愿意,最小值也可以包括在分析中。最后,我们对聚类感兴趣的列可以排序到一个新的数据框架中,如下所示

cols_of_interest = ['air_pressure', 'air_temp', 'avg_wind_direction', 'avg_wind_speed','max_wind_direction',
                    'max_wind_speed', 'relative_humidity']

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下一步是衡量我们的价值观,给予它们同等的重要性。从聚类的角度来看,缩放也很重要,因为点之间的距离会影响聚类的形成方式。

使用 StandardScaler,我们将数据帧转换成以下 numpy 数组

X = StandardScaler().fit_transform(data)
X

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

K-均值聚类

如前所述,在 K-means 的情况下,在运行模型之前已经指定了聚类的数量。我们可以为 K 选择一个基准水平数,并迭代找到最佳值。为了评估哪个数量的聚类更适合我们的数据集,或者找到聚类适合度,我们使用两种评分方法— 剪影系数和 **Calinski Harabasz 评分。**实际上,根据模型中最重要的指标,有许多不同的评分方法。通常选择一种方法作为标准,但是为了这个分析的目的,我使用了两种方法。

使用每个样本的平均聚类内距离(a)平均最近聚类距离(b) 计算轮廓系数。样本的轮廓系数为 (b-a) / max(b-a)

Calinski Harabasz 得分或方差比率是类内离差类间离差之间的比率

让我们使用 sci-kit learn 实现 K-means 算法。

n 个簇= 12

*#Set number of clusters at initialisation time*k_means = KMeans(n_clusters=12)*#Run the clustering algorithm*model = k_means.fit(X)
model#Generate cluster predictions and store in y_haty_hat = k_means.predict(X)

计算轮廓系数…

from sklearn import metrics
labels = k_means.labels_metrics.silhouette_score(X, labels, metric = 'euclidean')

0.2405

…以及 CH 分数

metrics.calinski_harabasz_score(X, labels)

39078.93

让我们尝试另一个随机选择的值,即 n_clusters = 8

k_means_8 = KMeans(n_clusters=8)
model = k_means_8.fit(X)
y_hat_8 = k_means_8.predict(X)

再次计算轮廓系数和 CV 值

labels_8 = k_means_8.labels_
metrics.silhouette_score(X, labels_8, metric = 'euclidean')

轮廓系数= 0.244

metrics.calinski_harabasz_score(X, labels_8)

CV 值= 41105.01

我们可以看到,对于这两种类型的分数,8 个聚类给出了更好的值。然而,我们必须对不同数量的集群进行多次迭代,才能找到最优的集群。相反,我们可以使用一个叫做肘图的东西来找到这个最佳值。

肘形图显示了 k 值为多少时,一个聚类的平均值与该聚类中的其他数据点之间的距离最小。

这里有两个值很重要— 失真惯性。失真是离各个聚类的质心的欧几里德平方距离的平均值。惯性是样本到它们最近的聚类中心的平方距离之和。

*#for each value of k, we can initialise k_means and use inertia to identify the sum of squared distances of samples to the nearest cluster centre*sum_of_squared_distances = []
K = range(1,15)
for k in K:
    k_means = KMeans(n_clusters=k)
    model = k_means.fit(X)
    sum_of_squared_distances.append(k_means.inertia_)

请记住,我们关心 K-means 中的类内相似性,这是肘图有助于捕捉的。

plt.plot(K, sum_of_squared_distances, 'bx-')
plt.xlabel('k')
plt.ylabel('sum_of_squared_distances')
plt.title('elbow method for optimal k')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里我们可以看到,距离平方和的下降在 k=5 之后开始放缓。因此,5 是我们分析的最佳聚类数。

我们可以通过计算 k=5 时的轮廓系数和 CH 值来验证这一点。

k_means_5 = KMeans(n_clusters=5)
model = k_means_5.fit(X)
y_hat_5 = k_means_5.predict(X)labels_5 = k_means_5.labels_
metrics.silhouette_score(X, labels_5, metric = 'euclidean')metrics.calinski_harabasz_score(X, labels_5)

轮廓系数= 0.261

CV 值= 48068.32

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这两个值都高于我们之前的星团 12 和 8。我们可以得出结论,k=5 是我们的最佳聚类数。

最后,我们可以看到每个集群中包含的值。使用这个函数,我创建了一个效用图。

*#function that creates a dataframe with a column for cluster number*def pd_centers(cols_of_interest, centers):
        colNames = list(cols_of_interest)
        colNames.append('prediction')# Zip with a column called 'prediction' (index)
        Z = [np.append(A, index) for index, A in enumerate(centers)]# Convert to pandas data frame for plotting
        P = pd.DataFrame(Z, columns=colNames)
        P['prediction'] = P['prediction'].astype(int)
        return PP = pd_centers(cols_of_interest, centers)
P

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如需进一步阅读:

  1. https://sci kit-learn . org/stable/modules/generated/sk learn . cluster . k means . html
  2. https://www . ka ggle . com/prakharrathi 25/weather-data-clustering-using-k-means/notebook
  3. https://www . datascience central . com/profiles/blogs/python-implementing-a-k-means-algorithm-with-sk learn
  4. https://blog . Cambridge spark . com/how-to-determine-the-optimal-number-of-k-means-clustering-14f 27070048 f
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值