TowardsDataScience 博客中文翻译 2020(五百九十九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

Keras 的长短期记忆(LSTM)

原文:https://towardsdatascience.com/long-short-term-memory-lstm-in-keras-2b5749e953ac?source=collection_archive---------13-----------------------

在这篇文章中,你将学习如何在 Keras 建立一个 LSTM 网络。在这里,我将解释所有的小细节,这将有助于您立即开始使用 LSTMs。

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

娜塔莎·康奈尔在 Unsplash 上拍摄的照片

在本文中,我们将首先关注单向和双向 LSTMs。我将解释从为训练准备数据到定义 LSTM 模型的所有步骤。在本文中,我将只解释顺序模型。在本文中,我将使用 LSTMs 对亚马逊美食评论进行情感分析。所以让我们开始吧。

矢量化和输入格式:

在本节中,您将学习如何对数据进行矢量化,并将其作为输入传递给架构。我将举一个例子,这样你就能学会根据你的数据集给出输入。在这里,我将使用亚马逊评论数据集来展示如何向量化您的数据。您可以在此 下载 的数据集。

在继续之前,首先让我们看看数据,并做一些数据清理。首先,我们将加载。csv 文件使用熊猫图书馆。我们将把那些评级不等于 3 星的评论视为 3 星评级的评论是中性的。下面是做同样事情的代码。

过滤掉中立评论的代码。

现在,我们将通过删除 HTML 标签、停用词和重复项来进行标准的数据预处理。你可以在这里看到细节。清理数据集后,我们将有两列。一个包含评论,另一个包含标签 1 或 0。1 表示正面评价,0 表示负面评价。

现在下一步是对数据进行矢量化。我们将使用 Keras 模块的 tokenizer 类对数据进行矢量化。这将为每个唯一的单词分配一个整数。所以现在每个单词都将由一个整数来标识。下面是具体实现这一点的代码。

向量化文本数据的代码。

每个单词都应该用一个向量来表示,以便模型能够理解它们。有两种方法:一种是使用预先训练的单词嵌入,如 word2vec 和 glove,另一种是为数据集训练自己的单词嵌入。在训练我们的深度学习模型时,我们可以通过使用 Keras 模块的嵌入层来实现这一点。嵌入层采用词汇量、词向量维数和每次评论的输入长度。这里我们将维度保持为 32。那么在训练完成后,每个单词将由长度为 32 的向量表示。我们已经将评论的最大长度固定为 150。下面是定义嵌入层的代码。

定义嵌入层。

你可以看到嵌入层是模型的第一层。我们必须指定以下三个参数。

  1. vocab size :这是训练集中唯一单词的数量。如果字被编码为从 0 到 100,那么 vocab 的大小将是 101。
  2. 输出维度:这是训练集中每个单词将被编码的维度。可以选择任意维度。例:8、32、100 等。这里我们选择了 32 个。
  3. 输入长度:输入序列的长度。如果你的数据的所有输入序列都是 100,那么这个值将是 100。对于我们的例子,我们选择了 150。

模型架构:

在本节中,我们将定义模型。在我们的建筑中,我们将使用两层 LSTM,每层 128 个单元,一个堆叠在另一个上。普通 LSTM 比 CuDNNLSTM 慢 3 到 4 倍。因此,如果你是在 GPU 上训练,那么使用 CuDNNLSTM 训练。你会发现训练速度会神奇地提高 3 到 4 倍。下面是定义架构的代码。

定义模型的代码。

如果要使用 LSTM 的堆叠图层,请在将输入传递到下一个 LSTM 图层之前使用 return_sequences=True。对于最后一个 LSTM 层,不需要使用 return_sequences=True。您可以更改使用的单位数。这里我们用了 128,你可以用适合你的情况。您可以改变这些值并保持最佳值。你也可以尝试其他激活功能,看看哪五个性能最好,然后你可以选择最好的一个。你也可以尝试堆叠更多的层,并检查是否有所改善。

模型培训:

在本节中,我们将训练上面定义的模型。我们可以使用“模型.拟合”方法来训练模型。这需要以下参数:

  1. x_train :这是用于训练的矢量化数据。在这里,我们对评论进行了矢量化,并将其存储在 x_train 中。
  2. y_train :它包含了被矢量化的评论的标签。
  3. batch_size :这是模型看到的数据点的数量,之后模型将更新权重。不要保持这么低,因为训练时间会增加。尝试一堆值,找到最适合你的情况的值。这里我们取批量= 1024。
  4. 时期:在训练结束之前,模型将在训练期间看到数据的次数。
  5. validation_split :这是在训练过程中将进行验证的数据的百分比。
  6. 回调:这些是在训练过程中调用的函数,用来监控模型在训练过程中的状态。训练时,我们不知道什么时候停止,因为模型只会在达到提到的次数后停止。有时,模型可能会在这些次数之前达到最优,但它仍然继续训练并过度拟合。为了防止这种情况,我们将使用回调来监控训练期间模型的丢失。如果损失在一些时期内没有减少,那么我们停止模型的训练。对于这种情况,我们已经决定,如果损失在 4 个时期后仍未减少,那么我们将停止。

下面的代码做了上面提到的事情。

开始训练的代码。

使用这种架构,我们在测试数据集上获得了 83%的准确率。使用双向 lstm 代替单向 lstm 给了我们 92%的准确率。下面是使用双向 LSTMs 的代码。

双向 LSTM 模型代码。

现在的问题是使用双向 LSTMs 背后的直觉是什么。在单向 LSTM 中,我们通过查看单词左边的单词来对单词进行编码。在双向 LSTM 中,我们通过查看单词左侧和右侧的单词来对单词进行编码。很明显,如果我们也观察单词的左右两边,我们可以更好地对单词进行编码。对吗?

我们可以从已经取得的结果中看到这一点。使用双向模型将我们模型的准确率从 83%提高到 92%,这是一个显著的飞跃。

我希望这将有助于您开始使用 LSTMs。我将提供 jupyter 笔记本的链接以供进一步参考。可以在这里 查看笔记本

如果你有任何疑问,请告诉我。

参考资料:

  1. https://keras.io/getting-started/sequential-model-guide/
  2. http://www.bioinf.jku.at/publications/older/2604.pdf
  3. https://keras.io/callbacks/
  4. https://keras.io/layers/recurrent/

前瞻优化器:向前 k 步,向后 1 步

原文:https://towardsdatascience.com/lookahead-optimizer-k-steps-forward-1-step-back-b18ffbae1bdd?source=collection_archive---------66-----------------------

活动讲座

迈克尔·张| TMLS2019

在多伦多机器学习峰会上的演讲

关于演讲者

Michael Zhang 是多伦多大学和 Vector Institute 的博士生,导师是 Jimmy Ba。他目前的研究重点是优化和深度学习。他之前在加州大学伯克利分校,在 Pieter Abbeel 的小组中从事强化学习和机器人方面的研究。

关于谈话

绝大多数成功的深度神经网络是使用随机梯度下降(SGD)算法的变体来训练的。最近改进 SGD 的尝试可以大致分为两类:(1)自适应学习速率方案,如 AdaGrad 和 Adam,以及(2)加速方案,如重球和内斯特罗夫动量。在本文中,我们提出了一种新的优化算法,Lookahead,它与以前的方法正交,并迭代更新两组权重。直观地说,该算法通过预测由另一个优化器生成的“快速权重”序列来选择搜索方向。我将讨论如何分析神经网络算法,并展示前视提高了学习稳定性,降低了内部优化器的方差,而计算和内存开销可以忽略不计。然后,我将展示经验结果,证明前瞻可以显著提高 SGD 和 Adam 的性能,即使在 ImageNet、CIFAR-10/100、神经机器翻译和 Penn Treebank 上使用它们的默认超参数设置。

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

前瞻优化器:前进 k 步,后退 1 步 | Michael Zhang

超越特性的重要性

原文:https://towardsdatascience.com/looking-beyond-feature-importance-37d2807aaaa7?source=collection_archive---------22-----------------------

如何在 Python 中使用部分依赖图

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

约翰·塔在 Unsplash 上拍摄的照片

许多 文章 讨论如何使用特征重要性来选择特征和分析你的机器学习模型。当您选择了您的重要特征并重新运行您的模型时会发生什么?特征重要性对于理解是什么在驱动我们的模型非常有用,但是它没有告诉我们那个特征如何与模型预测相关。本文介绍了如何超越要素重要性,并使用绘图方法来更深入地了解模型中的要素是如何驱动模型预测的。

特征重要性

在我们讨论特性重要性之前,我们需要定义特性重要性,并讨论何时使用它。

在最高级别上,要素重要性是对特定预测变量(要素)对模型预测准确性的影响程度的度量。您可以使用特征重要性来修剪模型,并通过删除低性能特征来减少过度拟合。

如果您使用 scikit-learn (sklearn),计算特性重要性的默认方法取决于您正在构建的模型的类型。例如,我将在本文中使用随机森林回归模型。sklearn 对随机森林模型的默认特征重要性是通过归一化每个特征通过分割该特征的“杂质减少”帮助预测的样本分数来计算的。然而,要获得线性模型(线性回归、逻辑回归)的特征重要性,您可以查看由特征的标准偏差缩放的参数系数值。

就个人而言,我更喜欢特性重要性的模型不可知方法。默认的 sklearn 随机森林特性重要性对我来说很难掌握,所以我使用了一种排列重要性方法。Sklearn 实现了一种排列重要性方法,其中通过随机排列每个特征中的数据并计算相对于基线的 MSE(或您选择的分数)的平均差异来确定特征的重要性。这种类型的特征重要性具有更大的计算负荷,但是可以跨不同类型的模型来完成,这对于标准化您的建模方法是很好的。

通常,一旦我们找到了一个合理的特征集,可以准确预测我们的响应变量,我们就可以停下来。然而,有时深入了解我们的模型和系统如何工作是有用的。为此,我们需要一个单独的方法来提取特性和响应变量之间的关系。

现在的问题是,我们将何去何从?

部分依赖情节:下一代

你可以从我之前的文章中看出,我非常喜欢使用图表和可视化来理解数据中的关系。在大多数情况下,目视检查方法适用于各种数据分布和方法。对于机器学习,确定特征与响应变量的关系的最直接的方法之一是使用部分相关图(PDP)。

构建 PDP 非常直观:

  1. 选择您感兴趣的功能(FOI)
  2. 对于连续 foi:
    -为分类 FOIs 创建一个从特征
    的最小值到最大值的序列:
    -为分类特征中的每个级别(n-1)创建一个虚拟变量
  3. 用序列中的值替换 FOI 中的每个值。
  4. 从这个新的特征集中获取预测,并对所有预测进行平均。将平均值存储在向量中。
  5. 对特征序列中的每个值重复步骤 3 和 4
  6. 根据特征序列本身绘制平均预测

这个算法有数学描述。本质上,这个算法显示了我们的 FOI 对模型预测的边际效应。

在本文的其余部分,我将向您展示如何构建 PDP 以及如何解释它们。我将向你展示如何使用 sklearn 创建这些情节,以及如何自己直接构造这些情节。最后,我将讨论使用 PDP 时需要注意的潜在缺陷以及如何解决这些问题。

一维部分相关图

读入并分割数据

对于这个分析,我将使用 scikit-learn 包中的波士顿住房数据集进行随机森林回归。

波士顿住房数据集中有 13 个特征,你可以在这里了解它们。在我们做了一些初步的特性选择后,我将分解更重要的特性代表什么。该数据集中的目标变量是“以 1000 美元为单位的自有住房的中值”。所以本质上,这里的任务是根据一组特征来预测房价。

建造初始模型

读入数据后,我创建了一个随机森林回归。我选择了最大树深度和给出良好模型性能的估计器数量,并且没有进行任何超参数调整。

一旦我创建了模型,我就提取了特征的重要性。上面的要点显示了如何用两种不同的方法来实现这一点,或者是默认的。来自 sklearn 的 feature_importances_ method 或使用 sklearn 中的 permutation_importance 函数。我在下面绘制了 permutation_importance 函数的结果。

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

条形的高度表示平均特征重要性,误差条形是每个特征平均值的一个标准偏差。

根据置换特性的重要性,RM、DIS 和 LSTAT 特性比其他特性几乎高出一个数量级!置换特征重要性也给我们一个特征重要性方差的估计。根据对误差线的快速查看,RM 和 LSTAT 可能对最终模型有统计上不可区分的影响,DIS 显然是第二重要的。这些特征代表什么?

  • RM:每个住宅的平均房间数
  • DIS:到五个波士顿就业中心的加权距离
  • LSTAT: %人口的较低地位

请记住,仅凭特征重要性,我们无法了解这些特征与我们的响应变量(中值住房价值)之间的关系。你可能会对这些关系的未来做出一些有根据的猜测。

用 reduce 特征集构建模型

现在,让我们用一组性能最好的特性来重做这个模型。

对于我们的分析,简化模型预测测试集足够好,测试集上的 R 为 0.82。我还在上面的代码片段中包含了从训练集和测试集中获取 MSE 的代码,以了解该模型中过度拟合有多糟糕。

如果您在这里检查特性的重要性,您会看到一个类似的模式,RM 最高,LSTAT 次之,DIS 最低。同样,RM 和 LSTAT 的特征重要性的差异看起来好像这两个特征的影响在统计上并不明显。

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

条形的高度表示平均特征重要性,误差条形是每个特征平均值的一个标准偏差。

构建部分相关图

Sklearn 有一个快速的脏函数,它会为您绘制所有的特征,或者您可以执行 run a 函数,只获取部分依赖项而不绘制它们。在下面的代码片段中,我既有 sklearn 方法,也有一个 quick 函数,它说明了幕后发生的事情。

关于 sklearn 函数有两点需要注意。sklearn 函数中的默认网格从数据的 5%到 95%边界,我在绘图中使用了完整的数据范围。此外,如果您正在构建许多功能的 PDP,plot _ partial _ dependence 函数允许您使用“n_jobs”参数并行进行计算。

让我们看看我们的部分依赖模式在我们的模型中是什么样子的。每个图都有一条代表部分相关性的线(当所有特征值都设置为一个值时模型的平均响应)和一个沿着底部的凹凸图

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

随着家中房间数量的增加,预测的家庭价值会增加,直到某一点,然后开始减少。一般来说,随着房间数量的增加,房屋价值也会增加。但是,有了 PDP,我们可以更进一步了解这一点。有趣的是,对房间数量的反应是相当非线性的,当超过 6 个房间时,房屋价值迅速增加。我们可以推测这种情况发生的一些原因,也许超过 7 个房间的房子有其他豪华的住宿设施(也许有一个大厨房?).我还想指出的是,我们需要小心解读这个图表的右边。观察到的中值住宅值的降低发生在非常大的值上,这些值在训练集中不经常出现。

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

到波士顿就业中心的距离只在距离很近的情况下对房屋价值有影响。再次,我们可以推测原因。对这种模式的一种可能的解释是,只有当员工可以步行、骑自行车或乘坐公共交通工具去工作场所时,靠近就业中心才是有价值的。在很短的距离之外,汽车可以让所有的距离都同样吸引人。换句话说,在更高的距离上缺乏关系可能是由于其他因素淹没了距离对价格的任何影响。

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

随着较低地位百分比的增加,住房价值下降,直到达到 20%左右。这种效应可能表明波士顿房地产市场已经触底,在其他因素的作用下,房地产价格不太可能下降超过某个值。

多维部分相关图

到目前为止,我们已经分别研究了每个特性的部分相关性。我们也可以使用上述相同的算法来构建部分相关的二维图。二维图将允许我们研究变量组合如何影响模型输出。

一维 PDP 假定特征之间是独立的;因此,相关的特征可能导致 PDP 中的伪图案。如果两个特征相关,那么我们可以在我们的 PDP 算法中创建非常不可能的数据点。看看下面的情节。您可以看到,RM 和 LSTAT 特征与-0.61 的皮尔逊相关系数呈负相关。这将如何影响我们的 PDP?

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

让我们以 FOI 的 RM 特性为例。当我们构建 RM PDP 时,我们用 min(RM)和 max(RM)之间的序列中的值替换 RM 的每个值。在 LSTAT 的高值时,没有观察到 RM 的高值,这意味着随着我们在 RM 序列中的进展,我们最终将创建 RM 和 LSTAT 的组合,这些组合对于特征集来说是不符合逻辑的,从而对我们的训练数据中没有出现的值进行预测。这本书的第部分以身高和体重的相关性为例,清楚地解释了这个问题。简而言之,你不会期望一个 6 英尺高的人有 50 磅重,但是 PDP 算法没有做出这样的区分。

我们怎样才能解决这个问题呢?在这种情况下,您可以使用二维 PDP 图,只检查与相关性重叠的值。下面是使用 sci kit-learn plot _ partial _ dependency()函数构建的 LSTAT 和 RM 的 2D PDP 图。如果我们不批判性地思考我们的数据,我们可以假设 RM 比 LSTAT 有更大的影响,如图表右侧所示。

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

等高线描绘了预测中值家庭价值分布的中断。暖色对应着较高的中值房价。

然而,如果我们覆盖 LSTAT 和 RM 数据点之间的散布,我们可以看到,在我们的训练集中,图的右手侧的接近垂直的等高线没有被表示。在高 RM 和高 LSTAT 没有数据点。我们应该只考虑与数据点重叠部分的模型部分响应。从这里,我们可以确定,当房间数量增加时,当较低地位人口的百分比下降时,住房价格增加,非线性模式仍然表现良好。如果您有兴趣更正式地执行此操作,您可以解包 plot _ partial _ dependence 函数的输出,并且只绘制出现在二维特征分布的 95%范围内的值。

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

部分依赖陷阱

PDP 很难在非常大的特征集中进行解释。通常,我只检查我的特性集中最重要的特性的 PDP。一旦超过 3 或 4 个特征,立即可视化多个特征的 PDP 几乎是不可能的。

我想重申,你的特征之间的相关性使得 PDP 难以解释。高度相关的特征会产生不准确的部分相关性预测,因为相关的特征可能不是独立的。通过设置多维特征分布之外的值(例如,高 RM 和高 LSTAT)来计算预期模型响应实际上是在训练数据之外进行外推。我们可以通过构建多维部分相关图并只关注多维特征分布内的区域来解决这个问题。你也可以使用一个累积的局部效果图,在这个 python 库中实现。

最后,重要的是要记住,PDP 是模型对相关特性的平均响应。在随机森林模型中,该功能可用于预测决策树中多种类型的响应。根据数据集的其余部分,特征和响应变量可能在某些情况下正相关,而在其他情况下负相关(参见此处的示例)。PDP 将是一条水平线,不会反映响应的异质性。如果您认为这发生在您的数据集中,您可以为每个数据点绘制单独的线,而不是这些线的平均值(这种类型的绘图称为单独条件期望绘图)。

就这样结束了!

像许多数据科学方法一样,PDP 应该小心使用,并与其他测试和数据检查结合使用。然而,我认为它们是理解黑盒模型中正在发生的事情的一种非常有用的方式,并且是一种超越特性重要性的方式。

如果你想要这篇文章中的任何代码,都可以在 Github 上找到。

延伸阅读

透过宣传看:模块化整体软件架构真的死了吗?

原文:https://towardsdatascience.com/looking-beyond-the-hype-is-modular-monolithic-software-architecture-really-dead-e386191610f8?source=collection_archive---------4-----------------------

现代软件开发中模块化整体软件体系结构的真实性检验

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

来源: Shutterstock

在 2010 年,许多网络规模的公司,如网飞、亚马逊、Spotify、优步都有特殊的要求:应用规模、开发规模、更短的上市时间。他们还发现,现有的模块化整体架构或面向服务的架构(SOA)无法解决他们的需求。于是,2012 年诞生了一种新的软件架构风格:微服务软件架构

从那以后,微服务的受欢迎程度一路飙升,声势浩大,令人乐观。会议充满了微服务讲座和研讨会。正如我们经常看到的,炒作和神话一起出现。

微服务架构是许多用例的正确选择。但是像任何其他软件架构一样,它也有甜蜜点(它擅长的地方)和极限情况(它失败的地方)。

可惜很多人以为微服务是银弹,解决了软件开发的所有问题。他们也抛弃了其他的建筑风格,如模块化整体建筑。还有,就像任何被炒作的技术一样,有些人把微服务想成了“金锤”,并试图在各种软件开发中使用它,而不考虑上下文。

人们被闪亮的新微服务迷住了,他们把模块化单片软件架构说成是一种可怕的架构风格,这种风格很快就会消亡,在现代软件开发中没有立足之地。同样,如果你是一名软件开发人员或软件架构师,并且支持模块化整体软件架构,那么你将会在你的公司中被抛弃。你的同行会把你看作是一个阻碍软件栈现代化的老派人物。

另一方面,如果你**在设计会议上提到“微服务架构”**这个术语,那么你的同事会对你充满敬意和敬畏。

所有这些关于模块化整体软件架构的批评都是合理的吗?在 Docker、Kubernetes、云、大数据和更快的发布周期的时代,它是否已经死亡,在现代软件开发中没有一席之地?

在本文中,我将更深入地研究模块化整体软件架构及其在当今软件开发环境中的相关性。

模块化整体架构

自从软件开发的早期(20 世纪 50 年代),软件系统被作为一个单一的系统开发,并作为一个单一的过程部署。这种软件系统被称为单片软件系统。下面是一个典型的整体式 Web 应用程序的示例:

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

整体 Web 应用程序

在上面的设计中,整个设备被分成多个层(表示层、业务层、持久层),整个应用程序被部署在应用服务器/Web 服务器上。

随着软件系统开始变得越来越复杂(从 20 世纪 70 年代开始),软件工程师通过将整个系统分解成“松散耦合、高度内聚的”模块来解决复杂性。这个系统被称为模块化单片软件架构。以下是我对模块化整体软件架构的定义:

一个软件系统可能由多个层或六边形组件组成,每个层或六边形组件被分解成“松散耦合、高度内聚的模块”,但整个系统作为一个整体部署,这被称为模块化整体软件架构。

下面是一个大型复杂 Web 应用程序中模块化整体架构的示例:

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

由 Md Kamaruzzaman开发的模块化单片网络应用

在上面的例子中,每一层被分成多个松散耦合、高度内聚的模块(例如 Java 库),这些模块内部连接(通过方法调用或函数调用)是语言相关的。

以下是模块化整体架构的特征:

  • 整个软件系统作为一个整体进行部署(要么全部部署,要么全部不部署)
  • 模块化的边界是内部的,很容易被跨越,这可能导致意大利面条式的代码(如上面的黄线所示)
  • 应用程序作为单个进程运行
  • 这是一种通用的解决方案,即一种解决方案适用于所有规模的应用
  • 模块之间没有严格的数据所有权

像所有的架构风格一样,模块化整体架构有优点也有缺点,我将简单讨论一下。

优点:

  • Monolith 有几个移动部分(例如,一个进程、一个应用服务器、一个数据库)。因此,设计、部署和测试(系统测试、e2e 测试)一个单一的应用程序变得更加容易。
  • 由于运动部件的数量较少,它具有更小的表面积来攻击。因此,保护单片应用程序更加容易。
  • 低操作复杂性
  • 单一应用程序只有一个 OLTP 数据库。因此,管理交易和数据共享变得更加容易。

缺点:

  • 由于共享的代码库(通常是意大利面条式的代码)和共享的数据源,在多个团队中并行化工作是困难的。所以,开发规模是可怕的。
  • 大型整体代码库(通常是意大利面条式的代码)给开发人员带来了巨大的认知复杂性。结果就是发展速度差。
  • 粒度扩展(即扩展应用程序的一部分)是不可能的。
  • 多语言编程或多语言数据库具有挑战性。
  • 由于单片应用程序“全有或全无”的特性,现代化是复杂的

微服务架构

在 2010 年代,网络规模的公司发现,对于极其大型的应用程序,模块化的单片软件架构并不合适,并创建了微服务软件架构。以下是我对微服务的定义:

微服务架构还将大型复杂系统垂直(根据功能或业务需求)划分为更小的子系统,这些子系统是流程(因此可独立部署),这些子系统通过轻量级、语言无关的网络调用(例如 REST、gRPC)相互通信

如果我们考虑以前的大型复杂 Web 应用程序,那么这就是该应用程序基于微服务的架构:

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

Md Kamaruzzaman 的微服务架构

以下是微服务架构的特征:

  • 整个应用程序被分割成独立的进程,每个进程可以包含多个模块
  • 与模块化单片或 SOA 相反,微服务应用是垂直分割的(根据功能或领域)
  • 微服务边界在外部。因此,微服务通过网络调用相互通信。
  • 每个微服务都有自己的数据库,而不是一个单独的数据库。
  • 由于“每个微服务的数据库”,需要额外的数据同步

在之前的帖子中,我已经详细讨论了微服务架构:

[## 微服务架构:简要概述以及为什么您应该在下一个项目中使用它

微服务架构的背景以及微服务架构相对于整体架构的优势

towardsdatascience.com](/microservice-architecture-a-brief-overview-and-why-you-should-use-it-in-your-next-project-a17b6e19adfd)

与炒作和神话相反,微服务有许多优点,也有相当多的缺点,如下所示:

优点:

  • 更好的开发伸缩性因为团队可以自主地在不同的微服务上并行工作,几乎没有外部依赖性
  • 微服务的规模相对较小。它将低认知复杂性放在开发人员的头上,开发人员会更有效率。
  • 由于每个微服务都是一个独立的进程,因此可以独立部署。因此,微服务架构提供了更快的发布周期**。**
  • 粒度扩展,即扩展应用的一部分,是可能的。
  • 粒度数据所有权,因为每个微服务都有自己的数据库
  • 只要维持外部契约,一个微服务就能像乐高积木一样迅速被替代。所以,微服务应用更容易现代化

缺点:

  • 垂直分割整个系统是艺术,而不是科学,需要极端的技巧。此外,将一个数据库分成多个数据库,然后在它们之间共享数据是一项艰巨的任务。所以,设计整个系统比较复杂
  • 代码复杂度经常被替换为操作复杂度
  • 由于多个数据库(通常是分布式的),数据共享和事务管理极具挑战性。
  • 由于众多移动部件(许多进程、数据库、网络调用、容器、虚拟机),完整的应用程序更加难以保护
  • 由于外部网络调用,整个应用程序的整体延迟要高得多

模块化整体架构死了吗?

总之,答案是:没有。在最近接受 Go Time 播客 采访时,云和 Kubernetes 大师Kelsey Hightower已经预示了未来几年模块化整体架构的回归。此外,在之前的一篇文章“关于 2020 年软件发展趋势的 20 个预测”中,我预测了使用模块化单片集成电路的增长趋势。也有太多的文章描述了公司迁移微服务架构的尝试是如何失败的,他们转向了单片软件架构。

**当微服务第一次出现时,许多人只是被冲昏了头脑,认为这是统治所有人的“**一种架构。”他们认为微服务架构是解决软件系统的所有组织限制和技术复杂性的灵丹妙药,并试图在任何地方使用它。在某种意义上,这让我想起了 SQL/NoSQL 之争

在 2010 年代,当 NoSQL 出现时,许多人都在讨论 SQL 作为一种技术是如何过时的,并且在行业中没有一席之地。毕竟,NoSQL 提供横向扩展,并被谷歌、脸书、亚马逊等公司所使用。因此,他们采用了 NoSQL 的数据库,没有考虑到他们的用例与谷歌或脸书的用例不同。很快,各公司艰难地认识到,他们不能用非 ACID NoSQL 数据库替换他们的跨国 SQL 数据库。随着炒作的平息,我们现在知道行业需要 OLTP 数据库(SQL)和 OLAP 数据库(NoSQL)** 。**

在这里,我列出了模块化整体软件架构在现代软件开发中仍然适用并且不会很快消亡的原因:

  • ****应用多样性:有许多大公司和网络规模的公司需要微服务架构。但是也有很多公司的微服务架构是错误的选择。对于他们来说,模块化整体架构是最佳选择。现代软件应用前景相当多样化。它为模块化单片和微服务软件架构提供了空间。
  • 统一解决方案:模块化单片软件架构的最大优势之一就是它给出了一个经过时间考验的解决方案。如果它适合应用程序的规模,那么 Ruby on Rails 或 Spring Boot 提供了一组定义良好的标准模式来开发应用程序。相反,微服务架构就像狂野的西部,它总是取决于许多因素(应用程序大小、上下文)。如果不仔细设计,微服务可以很快变成“分布式的 Monolith ”,具有 Monolith 的所有缺点和微服务的所有复杂性。
  • 诀窍:模块化单片软件架构从 20 世纪 70 年代就有了,大多数开发人员都知道如何开发模块化单片软件架构。微服务相对较新,缺乏专家。此外,垂直分解应用程序是艺术而不是科学,需要大量的技巧和实践。许多公司可以使用成熟的模块化整体软件架构来更快地开发软件,而不是新的微服务软件架构。最终用户不关心底层架构或技术;他只关心功能性。

模块化整体架构用例

在这里,我列出了一些模块化单片软件架构将在 2020 年及以后使用的用例:

小而简单的应用

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

来源:马丁福勒

正如先锋微服务专家 Martin Fowler 所展示的那样,模块化单片软件架构是特定规模和复杂性应用的更好选择。就开发人员的工作效率而言,达到一定规模的 Ruby on Rails 或 Spring Boot 应用程序可以轻而易举地击败复杂的微服务架构。如果一个公司有一个开发团队(6-8 个开发人员),那么他们应该使用模块化整体软件架构。

现代模块化整体式建筑

不幸的是,在过去的十年中,模块化独石几乎没有创新,因为它可能正在等待死亡。但随着模块化的 Monoliths 经受住了微服务的冲击,我们可以期待一些创新。微服务有许多新概念,Monoliths 可以采用。

一个这样的现代模块化整体框架是**inertia . js。它结合了简单、高效的服务器端 Web 开发框架,如 Ruby On Rails、Django 和基于 JavaScript 的 SPA,如 React、Vue。诀窍是他们将两个世界的精华与任何 API 结合在一起。**

这样的框架将极大地推动模块化整体软件开发。

棕色地带应用

微服务非常适合绿地应用。但通常情况下,公司已经有了现有的设计不佳的单一应用程序。处理现有单片应用程序的一种方法是将其退役,并开发新的微服务。我们都知道让一个正在运行的应用程序退役几乎是不可能的。此外,用新的微服务替换该应用程序(这将受到初期问题的困扰)将令人望而生畏。更好的方法可能是将设计糟糕的整体应用程序重构为模块化的整体应用程序,并保持原样。

复杂领域的应用

微服务都是垂直拆分一个复杂的系统,也就是按照领域逻辑切割。有时候,有些应用的域是纠缠在一起的,要解开这些域是很复杂的。如果把纠结的域拆分成微服务,那么就导致了分布式的 Monolith,带有 Monolith 和微服务的所有不好的东西。在这种情况下,模块化整体可能是更好的邪恶。

非商业、特殊用途的应用

并非所有应用程序都是企业应用程序。在许多应用中(例如电信、汽车),延迟和更快的响应更为关键。此外,在许多特定领域(例如,机器学习、深度学习、数据存储),CPU 吞吐量或网络吞吐量是最重要的标准。在这些领域,模块化单片软件架构将比微服务更具优势。

内部工具

每个公司都有一些内部工具。通常,这些工具不会遭受不受监管的增长。对于内部工具开发,模块化整体软件架构将是比微服务架构更好的选择。

类似文章:

** [## 微服务架构:简要概述以及为什么您应该在下一个项目中使用它

微服务架构的背景以及微服务架构相对于整体架构的优势

towardsdatascience.com](/microservice-architecture-a-brief-overview-and-why-you-should-use-it-in-your-next-project-a17b6e19adfd) [## 有效的微服务:10 个最佳实践

正确实施微服务架构的 10 个技巧

towardsdatascience.com](/effective-microservices-10-best-practices-c6e4ba0c6ee2) [## 微服务架构及其 10 个最重要的设计模式

微服务架构、每个微服务的数据库、事件源、CQRS、Saga、BFF、API 网关、扼杀者、电路…

towardsdatascience.com](/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41)**

寻找数据科学家的工作-这里是你应该知道的!

原文:https://towardsdatascience.com/looking-for-data-scientist-jobs-here-is-what-you-should-know-758d28df4010?source=collection_archive---------71-----------------------

基于对 100 多个数据科学职位发布的审查的有用见解

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

datasciencia.com

注:这项研究是基于印度列出的数据科学家招聘信息进行的。

学习数据科学可能很难,如果你是初学者,在数据科学领域找工作可能同样很难。作为一个初学者,了解公司的机会和期望将有助于你做出更好的决定。

我也有其他问题。

  • 我至少需要什么样的相关经验才能接到面试电话?
  • 我能全面了解数据科学家所需的所有技能吗?我应该专注于哪些技术?
  • 深度学习知识对于找工作有必要吗?
  • 领域知识是必需的吗?公司需要吗?
  • 我应该专注于哪个领域/垂直行业?

正如我们开始的任何与数据相关的项目一样,我开始挖掘数据。阅读并分析了印度四个不同城市的两个工作门户网站上的 100 多个招聘信息,工作经验为 0 至 5 年。

在这篇文章的最后,如果你有和我一样的问题,你将会得到很好的信息来做出正确的决定。这篇文章的目的是帮助你在简历中加入所需的技能和经验水平。如果你刚刚开始学习数据科学,这将有助于初学者对数据科学的就业市场有一个现实的看法。

研究范围

  • 这项研究仅限于印度就业市场。就业市场因国家而异。这在一定程度上可以推广到其他国家。
  • 对过去 30 天(23/6/20 至 23/7/20)发布的职位进行了分析
  • 主要城市的招聘信息被纳入调查范围——钦奈、班加罗尔、孟买、海德拉巴,以及一些二线城市的招聘信息,如古尔冈和诺伊达。
  • 总共分析了 118 份招聘信息
  • 【naukri.com】【indeed.com】中的招聘启事拿来学习。
  • 用于搜索工作的关键字—初级数据科学家、数据科学家
  • 经验限于 0 至 5 年 —即,这是求职所需的最低经验要求。

一些公司用多个名称称呼数据科学家,如机器学习工程师、数据科学顾问、决策科学家等。这些不同标题下列出的所有与数据科学相关的工作都在范围内。

极端值

我不得不过滤掉符合上述标准的工作,因为它们太具体或太模糊。我称之为离群值。以下是标准

  • 工作描述不完整的工作
  • 像计算机视觉这样的小众工作
  • 只需要 IIT/IIM/NIT 毕业生的工作
  • 需要博士学位的工作
  • 兼职、实习或自由职业的工作
  • 避免数据工程师职位——过多投入开发运营、构建管道和协助数据科学家等工作。
  • 需要丰富软件工程经验的工作,即明显不太关注机器学习、寻找核心开发人员的工作。

**注意:**我已经看了每一份工作描述,并准备了一份资料供你参考。你可以订阅我的时事通讯,并获得一份数据集的副本,用于进一步的分析和公司链接发布的实际工作。

让我们开始分析吧!

哪个城市的数据科学家职位空缺最多?

如果你猜的是班加罗尔,那就对了!。班加罗尔或孟加拉印度的硅谷拥有最多的数据科学家职位,如亚马逊、花旗、Genpact 和霍尼韦尔等公司。在所分析的工作岗位中,班加罗尔约占 35%

如果你刚开始从事数据科学,并且只有几年的相关经验,班加罗尔是一个不错的选择。钦奈以 25%的职位排名第二。

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

作者图片

两个平台中的哪一个——事实上/ Naukri 是最适合初学者的?

最好这个词是主观的。对我来说,一个不用四处寻找重要信息就能帮助快速轻松找到工作的网站是最好的。在过去的 30 天里,纳克里和实际上有几乎相同数量的招聘信息——分别是 61 条和 57 条。使用 Naukri,工作搜索范围很广,可以搜索多个职位,如数据科学家、数据分析师等。你可以一次选择不同的城市。事实上,我能找到的最好的方法是选择一个城市或一个职位。我觉得这很压抑。绝对不是最好的体验。

我也觉得这的确是一份松散的工作清单。Naukri 有一个专门的工作地点、最低工资、最高工资、兼职/全职信息,这些信息与工作描述的其余部分分开。事实上,它无处不在,很难快速找到信息。

当心!我发现不到 10%的招聘信息是重复的——既有在 Naukri 发布的,也有在 entire 发布的。这意味着当你申请工作时,在两个网站上发布。

我没有在 Linked-in 上找工作,这是另一个找工作的绝佳资源。你可能需要探索其他的网站。

发布的数据科学职位是否必须有数学背景(正规教育)?

几乎所有发布的职位都要求对数学有足够的了解,在统计、概率和其他定量方法方面有很强的技能。尽管如此,只有 11%的职位空缺明确要求具备出色的数学技能。否则,如果你是数据科学的自学者或者有不错的数学技能,你还是应该找份工作。

搜索数据科学家职位用什么关键词最好?

这是显而易见的。数据科学家关键词搜索将涵盖数据科学家、首席数据科学家、高级数据科学家、机器学习工程师,并应涵盖 80% 的职位空缺。如果招聘人员没有正确标记职位空缺,那么你可能会考虑其他关键词,如人工智能工程师、数据科学顾问、应用科学家、决策科学家等。

用于该搜索的数据集中存在全部的关键字。请参见数据集中的“角色”列。

哪个垂直/领域的数据科学家职位最多?

首先,这里的域指的是公司的行业类型。例如银行业、零售业、制造业等。

大多数软件服务公司现在让客户能够根据数据做出决策,我看到这些公司中有 25%在多个领域工作。这是一个好消息,因为你可以申请这些职位中的任何一个,而不考虑专业领域。没有特定领域知识的要求。

但是,如果你想通过竞争或者瞄准一个感兴趣的特定领域,这三个领域有最多的职位发布。银行和金融服务(BFSI) 16% ,医疗保健 8% 和电信 5%

如果你想瞄准媒体、房地产或游戏等利基领域,你有职位,但不多。请参考数据集中的准确数字和公司列表。

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

作者图片

作为数据科学家,公司是否特别要求特定领域的知识?

你当然可以用你的领域知识来击败你的竞争对手。此外,如果你有领域知识,数据会更有意义,你可以交付更多的商业价值。

它是强制性的吗?只有 12%的公司在寻找特定领域的知识。你可以使用这两种方式——剩下的 88%的公司你可以申请,你被选中的机会更大,或者你可以专门瞄准 12%的公司,因为你在那里有优势,可以在竞争中胜出。

数据科学家起步需要深度学习知识吗?

37%的公司正在寻找某种深度学习的体验——尤其是像 Tensorflow 这样的软件包。

除了核心 ML 之外,公司对 NLP 这样的专门 ML 领域感兴趣吗?

简短的回答是是的**。大约 30% 的公司要求像 NLP(自然语言处理)这样的文本分析技能。**

公司热衷于云体验吗?如果是,他们最需要的技术体验是什么?

大约 20%的招聘信息需要在云上部署模型的经验。其余 80%不热衷于云体验。如果你没有云经验,申请大公司是合乎逻辑的,大公司可能有单独的团队来照顾这一要求。规模较小或拥有小型数据科学团队的公司将期待这种体验,因为他们依赖云来获得计算能力和可扩展性。

这些 20% 的公司期待什么样的云技术?— 暴露于 Azure/AWS 将使您被覆盖在 99% 的案例中。

****实际数据:23118公司需要云体验。其中,2223公司需要 AWS/Azure 方面的经验。

这些公司大部分用于数据科学的编程语言是什么?

Python 是这里无可争议的领导者,将近 95%的公司使用它进行数据争论、分析和机器学习。

其次是 R,52%的公司将其列为他们使用的统计工具之一。

大多数情况下,组织都在寻找这两种工具。拥有 R 知识可以给你一个优势。

除了这两种语言, SAS 和 Java 也很受欢迎,有 18% 的公司希望获得这些语言方面的经验。

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

作者图片

我需要接触大数据技术吗?

Hadoop 和 Spark 这样的大数据技术受到 25% 招聘公司的追捧。

除此之外,Hive、Pig、Sqoop 等相关技术也受到追捧——尽管我没有在数据集中捕捉到这些数字,因为它们属于大数据领域。

数据科学家经常忽视的最被低估的技术,但公司会问!?

大多数数据科学家的志向和爱好者专注于机器学习算法和编程语言,他们错过了公司习惯的基础技术。几乎所有的公司都有某种关系数据库来存储结构化数据,并期待有人能够利用这些结构化数据。这使得 SQL 专业知识成为一项有价值的技能。

58%的公司期望数据科学家角色编写 SQL 语句来处理结构化数据。也就是说,公司也在寻找没有 SQL 数据库技能的人——有 15%的公司要求像 MongoDB 这样的非结构化数据库的专业知识。

数据科学家最期待的可视化工具技能?

Tableau 是业内最受欢迎的工具,有 27% 要求这个技能。力量 BI 第二15%Qlikview 第三 10% 求这些技能。

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

作者图片

还有其他公司感兴趣的技术吗?

如果你让自己接触以下工具和框架,这将增加你被选中的几率。按重要性顺序排列

  • ETL 工具的工作知识
  • 使用 Excel 的专业知识
  • API 知识(例如 REST API)
  • 接触敏捷方法
  • 一些 DevOps 的经验不会有坏处!

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

作者图片

最重要的问题—入门级数据科学家的平均最低经验是什么?

如果你是一个有着零到两年经验的新手,得到面试电话的可能性大约为 20%

如果你有三年的经验,得到面试电话可能性增加 45%。

根据分析,我认为入门级数据科学家的平均最低经验是三年。****

摘要

这是总结一切的信息图。

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

datasciencia.com

获取数据集的访问权限

通过在此 注册简讯 获取数据集。您将收到一封电子邮件,其中包含下载数据集的链接。数据集以.xlsx格式提供。

通过数据集,您可以获得进一步的见解。

  • 访问寻找本研究中使用的数据科学技能的公司。我把每家公司都链接到了自己的网站上,这样可以节省你亲自查看这些公司的时间。
  • 获取在制造、零售、电子商务等领域工作的公司列表。
  • 列出的每个工作的工作描述的链接。
  • 对只从事机器学习技术的创业公司做进一步的研究。
  • 你应该联系招聘/人才管理机构,以加快你的求职速度。

如果你喜欢阅读,考虑在你的社交网络上分享这篇文章。您还可以在社交媒体上关注我们 【脸书】推特链接在****

这篇文章最初贴在这里感谢阅读!

了解 Mahalanobis 度量匹配

原文:https://towardsdatascience.com/looking-inside-mahalanobis-metric-matching-4e43cca46a6?source=collection_archive---------55-----------------------

它实际上是如何工作的?

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

安妮·尼加德在 Unsplash 上拍摄的照片

我之前的帖子展示了如何使用 13 行 R 代码实现倾向评分匹配。通过这些代码,您可以看到匹配更多的是关于数据预处理。我们的想法是找到与治疗单位相当的控制单位,这样我们就可以更有把握地将治疗组之间的结果差异归因于治疗。此外,我想说匹配实际上很简单,所以你不应该被它吓到。

今天,我研究了一个流行的替代倾向评分匹配的方法:Mahalanobis 度量匹配。这两种匹配方法的不同之处主要在于 MM 匹配使用 Mahalanobis 度量而不是倾向得分来查找匹配。我将一步一步向你展示在实践中如何使用最简单的 R 代码完成 MM 匹配。这些步骤嵌入在进行匹配的程序中,但它们通常是不可见的。

进行马氏度量匹配的 r 码

与我之前的帖子类似,我为数据练习模拟了一个小数据集。从这里可以找到生成模拟数据的 R 代码。模拟数据有两个连续协变量(W1,W2),一个二元处理(A),和一个连续结果(Y)。为了简单起见,我只生成 10 个数据点,其中 4 个显示为处理过的单元。

生成数据的代码是:

library(Matching)
library(dplyr)
library(tidyr) # call libraries 
set.seed(123) # seed for reproducibility
data <- generateData(10) # generate 10 data points (need to download # the function provided in the main text)

模拟数据值为:

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

图一。模拟数据值

第一步。定义一个函数来计算马氏距离

计算马氏距离的数学公式为:MD =(X1-X2)’S(X1-X2),其中 X1、X2 分别为处理单元和对照单元的协变量(本例中为 W1 和 W2)的向量。 S 是数据的样本协方差的倒数。注意,我们可以计算每一对的距离(处理对对照)。我们不计算治疗单位之间的距离,因为我们是在治疗组之间匹配,而不是在同一治疗组内。

让我们定义一个函数,它可以计算一对观察单元的 MD。然后我们可以循环遍历所有的线对。该函数如下所示。每一行代码都是不言自明的,所以我在这里不再提供更多的讨论。

mahalDist <- function(Tr, Cn, inv.cov, dataset) {
 covars <- dimnames(inv.cov)[[1]] # get covariate names
 xtreat <- as.matrix(dataset[Tr, covars]) # get X1 (treated)
 xcntrl <- as.matrix(dataset[Cn, covars]) # get X2 (control)
 xdiffs <- as.matrix(xtreat — xcntrl) # calculate differences
 out <- as.numeric(xdiffs %*% inv.cov %*% t(xdiffs)) # MD
 names(out) <- paste0(Tr, ‘-’, Cn) # Set a name for the output
 return(out)
}

第二步。计算每个治疗/对照对的马氏距离

现在,我们使用以下代码计算每个处理/对照对的 MD:

icv <- solve(cov(data[, c(‘W1’, ‘W2’)])) # Inverse of the variance
treat.rows <- which(data$A == 1) # treated rows
cntrl.rows <- which(data$A == 0) # control rowsmdist <- mapply(function(x,y) mahalDist(x, y, inv.cov = icv, data = data), rep(treat.rows, each = length(cntrl.rows)),
       rep(cntrl.rows, times = length(treat.rows)))

请注意,最后一个 mapply 函数只是简单地循环计算 MD(由 mahalDist 定义),基于所有可能的处理/控制对的行号。下面报告了 mdist 的输出,例如,位于第 1 行的处理单元和位于第 3 行的控制单元之间的 MD 为 12.12。该值似乎是与位于第 1 行的处理单元相关的所有 MDs 中最大的。与位于第 6 行的控制单元相关的最小 MD 是 1.67。

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

图二。mdist 的输出

第三步。寻找距离最短的最佳匹配

由于样本量很小,我们可以很容易地找到每个处理单元的最佳匹配(最低 MD)。但是下面是一些自动找到最佳匹配的代码:

mdist_data <- 
 data.frame(pairs = names(mdist), maha_dist = unlist(mdist),
 stringsAsFactors = F) %>% # Create a data frame to save pair names #and MD values. 
 separate(pairs, into = c(‘treat’, ‘cntrl’), sep = ‘-’) %>% # get #separate names for treated and control units.
 group_by(treat) %>% # Search for the lowest MD by each treated unit
 filter(maha_dist == min(maha_dist)) # return the lowest MD 

mdist_data 的输出为:

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

图 3。mdist_data 的输出

您可以看到,四个 MD 最低的对照单位与四个治疗单位相匹配。相对而言,最后一对(即第 7 行的处理单位-第 9 行的对照单位)是最接近的一对,而第一部分(即第 1 行的处理单位-第 6 行的对照单位)是最不同的一对。事实上,如图 1 所示,第 1 行的处理单元恰好具有最低的 w 1 和最高的 W2。因此,很难为它找到一个好的匹配(与其他人太不同)。

经过三个步骤和少量代码,我们完成了基于马氏距离匹配的数据预处理。人们可以用匹配的数据做进一步的分析。我希望你喜欢这本书!

向自然寻求优化

原文:https://towardsdatascience.com/looking-to-nature-for-optimization-359bc327229c?source=collection_archive---------53-----------------------

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

弗朗西斯科·费尔南德斯在 Unsplash 上退火的照片

思想与理论

利用宇宙一直知道的东西

优化问题在现代计算和经济学中无处不在。他们致力于在具有多种可能性的复杂空间中寻找“最佳”解决方案。大自然对此感到不可思议!自从事物存在以来,大自然就一直在寻找尽可能好的做事方式。让我们看看如何利用自然中发现的技术来解决现代问题。

为了形式化一个优化问题,一个解空间被认为是解决问题的可能方法。每个解决方案都有一个我们寻求最大化的价值。然而,当解决方案空间如此之大,以至于要花太长时间来检查每个解决方案时,这可能是一个难以置信的挑战。

优化的一个常见例子是旅行推销员问题。在最基本的版本中,地图上有许多随机的点。你必须找到连接它们的总距离最小的路径。在这个微小的变化中,我们寻求最小化 K (或者我们可以最大化 -K)。

让我们看一个简单的解空间来帮助我们形象化!

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

图 1:示例解决方案空间,在 Desmos 中制作

图 1 为我们的问题提供了不同的解决方案。我没有包括标度或单位,因为在这个例子中它们是任意的。每个点代表不同的配置。一个解附近的点代表相似解的一个邻域。这样做是为了从一个点移动到下一个点只需要对解决方案做最小的改变。对于旅行推销员问题,一个解决方案与另一个解决方案相邻意味着它们是相同的,除了路线上两个相邻的“站”交换了顺序。我们希望相邻的解决方案只有微小的差异,这样它们的 K 就相似了。

在图 1 的 y 轴上,我们有 K 。这是我们试图最大化的变量。快速浏览一下图表,就会发现点 B 显然是最佳答案。在点 D 还有一个局部极大值,我们必须考虑。记住,这是大大简化了的!很多优化问题会有万亿个解,解空间会不止一个维度。

现在我们有了一个解空间,让我们看看两种不同的算法如何处理这个问题。

病爬

我们的第一个方法不是在自然界中发现的。然而,这是迄今为止最容易理解的算法,并将作为解决这个问题的起点。我将用伪代码展示它,并附上书面解释。这个代码将简称为 HillClimb

point = RandomPoint
while True
   newpoint = Blankfor p in point.Neighborhood
      if point.k < p.k and newpoint.k < p.k
         newpoint = pif newpoint != Blank
      point = newpoint
   else
      break

要开始爬山,选择一个随机的解决方案 S 。然后,检查 S 邻域内每个点的 K (根据问题不同,邻域可以有多种不同的定义方式)。如果 S 的邻域中的任何点的 KS 高,则将该点设置为新的 S 。如果多个点的 KS 高,则选择最高的一个。如果出现平局,从给出的最高分中随机选择一分。继续这样做,直到 S 的邻域没有比 S 本身具有更高 K 的点。

爬山有一个非常简单的优点。它也适用于只有一个最大值的解空间。但是,它无法处理具有许多不同最大值的空间,例如图 1 中的两个最大值 BD

让我们看看爬山如何处理图 1 中的不同点。

假设我们碰巧从最佳方案开始:点 B爬山将检查它旁边的两个点,并查看两个点都没有更高的 K 并退出。我们完全凭运气找到了正确答案!

但是如果爬山从 **D 点开始呢?**紧挨着 D 的两个点都没有更高的 K ,所以它会停下来说 D 是最优解,基于解空间这显然是不成立的。这说明了爬山的问题:它没有办法处理局部最大值。相反,爬山必须依靠运气从一个随机点开始,这将导致它达到真正的最大值,例如点 A

基于此爬山演示,如果从图 1 中的点 C 开始,会给出什么“最佳”解决方案?答案在本页末给出!

这个问题的严重程度取决于问题的性质。如果找到最高的 K 不是绝对关键,而是只要找到一个相对较高的 K 的解决方案,爬山就可以了。该算法还可以重复运行,以增加找到更好解决方案的可能性,因为它非常依赖于起始点。 HillClimb 确实与非常“锯齿状”的解决方案空间作斗争,如下图 2 所示。这是因为它通常会很快找到一个“最佳”解决方案,而不是因为大量的局部最大值而搜索更好的解决方案。

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

图 2:参差不齐的解决方案空间

爬山很好,但肯定有一些问题。主要是它不能处理局部最大值,并且严重依赖于初始起点。我们如何解决这个问题?一种可能的方法是在我们的算法中引入一些随机性。如果有时候,我们的道路没有把我们带到附近更好的地方,而是一个 K 值更低的地方呢?我们需要小心这一点,我们不能随机地从一个点到另一个点,然后选择一个。我们的新算法应该具有随机的能力,但也仍然以一个合理的答案结束。让我们看看这是如何工作的!

模拟退火

我们可以从一个自然过程中寻找一种方式来改变爬山退火是金属从受热状态冷却时发生的过程。当加热时,金属中的原子可以自由移动,当金属冷却时,会寻找更节能的排列方式。高温度 ( T )意味着原子更有可能四处移动,暂时变得能量效率更低,以可能找到更好的排列。随着 T 降低,原子变得不太可能进行这种赌博。这种最终状态将比金属最初被加热时更节能。大自然有一个很好的方法来优化金属中原子的排列。

我们如何将此添加到爬山中?让我们有一个变量, T ,它表示我们的算法选择一个具有较低 K 的邻近解的可能性。它还应该取决于我们当前点和新点之间的差异。换句话说,如果一个点比我们现在拥有的差很多,我们的算法就不太可能选择一个更差的点。我将在下面展示算法退火的伪代码。别急,后面会有文字解释!

point = RandomPoint
T = StartingTemperature
delT = ChangeInTemperaturewhile True
   newpoint = Blank
   T = T - delTfor p in point.Neighborhood
      if point.k > p.k
         if random[0,1] < exp((p.k - point.k) / T)
            newpoint = p
            break
      elseif point.k < p.k and newpoint.k < p.k
         newpoint = pif newpoint != Blank
      point = newpoint
   else
      break

让我们慢慢地浏览一下退火的伪代码。你首先应该注意的是开头的两个新变量。我们有 T ,起始温度,和 delT ,它告诉我们每一步减少 T 多少。另一个主要的区别发生在循环当前点 S 附近的点时。如果我们正在检查的点的 KS 低,我们运行另一个检查。在 0 和 1 之间选择一个随机数,然后检查它是否在下面的表达式中:

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

我将此称为 prob 。记住 prob 指的是我们选择一个不太有利的解决方案(较低的 K )的概率。如果我们选择了一个不太有利的点,我们停止寻找新的点,移动到那个点,然后重新开始。剩下的退火爬山 一样。 最终,附近将不再有具有较高 K 的点,并且 prob 将如此之低,以至于不会移动到具有较低 K 的点。

prob 将总是小于 1,因为 p.k < point.k 使得指数为负。 p.kp . k之间的巨大差异导致更负的指数,使得 prob 更小。大的 T 导致较小的负指数,这使得 prob 更大。随着 T 变小(但保持正值),指数值将趋向于越来越负,使得 prob 变小。

这很复杂,所以我鼓励你花点时间,确保你理解这个算法。下面有个例子。

我们对退火的行为有很多控制。这里有很多随机性,但我们有一些空间来调整它。主要是,我们可以改变 T21 如何变化。退火每次迭代减少 TdelT 。我们可以调整 TdelT 的尺寸。这将影响退火的持续时间,以及移动到不太有利的解决方案的自由度。另一种可能是让 T 以指数函数递减,只要它在递减。记住 T 必须始终保持正值。我没有将它包含在退火的伪代码中,但是如果它变得太小,应该有检查来保持 T 为正。

让我们重新检查一下图 1,我把它复制到这里,这样您就不用来回滚动了。

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

图 1 的副本

首先,我们看看从点 B 开始时会发生什么。早在退火的时候,的概率就很高。我们可能会远离 B ,然后随着 prob 减少,移回B,然而,我们总是有可能过于靠近 D ,一旦 prob 变得非常低, Anneal 将卡在该区域,并在 D 处结束。这是退火的权衡。虽然我们更有可能获得具有更高 K 的最终点,但我们也更有可能跳过具有真正高值 K 的点。从某种意义上来说,退火使我们的最终 K 相对于爬坡居中。我们很少得到很低和很高的 K 值。有趣的是,我们的算法有更多的随机性,退火,导致最终点的随机性减少。

A 开始通常会导致 B 的最终答案,但是仍然有可能会向右移动太多,并在 D 处结束。

让我们来看看 C ,这将揭示出我给出的退火pseuodcode 中的一个缺陷。退火C总是**右移,不管 prob 是什么。这是因为退火仅在相邻点的 K 低于我们当前点时检查 prob 。如果所有相邻的点都有一个更高的 K ,那么退火也会选择最高的一个来移动。这也意味着从点 D 开始永远不会导致在 B 结束。

看看能不能写点伪代码解决这个问题!下面是一个潜在解决方案的简要描述。如果我们生成一个小于概率的随机数,总是移动到比另一个相邻点具有更低 K 的相邻点。如果是一条领带,随便挑一条。那里!现在我们的算法不会卡在 C 上,如果它从 D 开始,可能会移动到 B

有许多其他方法可以解决这个问题。他们中的许多人也从其他自然过程中获得灵感!例如,量子退火使用概率函数,如下所示:

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

探针用于量子退火

该函数还考虑了分隔这两点的最大宽度。量子退火更有可能穿过非常高但也非常薄的局部极大值。这种算法还有其他变体!

我个人最喜欢的是那套遗传算法。这些算法从物种的自然进化中获得灵感。这个过程将从大量的随机解开始,而不是从一个随机解开始。这叫第一代。每个解决方案都有一个 K 值,我们称之为“适应性”有一个概率函数再现K 增加,随时间减少。对于每个“步骤”,对照检查每个解决方案,在当前世代中再现。因此,更“适合”的解决方案有更高的重现机会。将通过的解决方案随机配对。对于每一对,以某种方式组合解决方案,产生一个具有双亲特征的后代。此外,检查每一个后代是否有突变的概率,如果通过了,就会对其进行随机改变。现在对新一代的后代重复这个过程!

遗传算法有很多种。虽然它可能非常有效,但有许多步骤,可能比攀爬或退火需要更长的时间。还有如何组合解决方案的问题。有些问题有明显的组合,有些问题就模糊得多。

其他一些有趣方法包括蚁群优化粒子群集以及更多!尽情探索吧!

感谢阅读!如果您对本文有任何想法或问题,请发表评论。

如果你喜欢我的作品,那就考虑用这个链接报名成为一个中等会员吧!每月只需 5 美元,使用该链接直接支持我。你也可以给我买杯咖啡!如果有的话,先谢谢你了!

解决爬山问题的方法是点 D 。尽管事实上与 C 相邻的两个点都较高,但它总是向右移动,因为那是最高的。该算法将最终停留在 D.

查看引擎盖下的仪表板框架

原文:https://towardsdatascience.com/looking-under-the-hood-at-the-dashboarding-frameworks-f87d8b4f3f7a?source=collection_archive---------51-----------------------

编写简单的实验代码,突出流行的仪表板框架 Voila、Streamlit 和 Plotly Dash 背后的不同执行模型

如今,数据科学家拥有广泛的开源工具,允许他们为自己的模型构建用户界面,而无需学习任何 Javascript,甚至无需考虑 UI,只需声明用户可以选择哪些变量。

一旦他们相信这是可以实现的承诺,接下来的问题就围绕着性能和编程细节:

  • 我的仪表板上的每个用户每次在初始数据处理期间都需要等待吗?
  • 如果我想在未来超越简单的 UI,我选择的框架有多大的可扩展性?还是我会碰壁,试图让它做得更多?
  • 当两个用户同时访问应用程序时,他们会开始看到彼此的数据,计算会不同步吗?

有时你需要做的只是运行一些简单的代码测试来理解框架是如何运作的。本文将展示三个流行框架的执行模型:Voila、Streamlit 和 Plotly Dash。我们将在每个框架中编写一个版本的基本 UI,让两个不同的用户同时访问它,然后我们将观察这会如何影响我们的模拟数据。

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

内森·范·埃格蒙德在 Unsplash 上拍摄的照片

测试

我们将模拟仪表板的两个主要组件:启动计算和用户交互。

通常,仪表板在首次启动时需要读入或计算预测模型。当数据科学家开始构建仪表板时,一个常见的抱怨是“每次新用户访问仪表板时,他们都必须等待模型重新计算”。我们想了解我们的代码为每个新用户缓存模型有多容易。

我们要做的不是实际计算一个模型,而是将当前时间存储在一个变量中。如果每次运行都显示相同的时间,我们就知道“模型”没有被重新计算——我们使用的是在初始服务器启动时缓存的时间变量。如果它在每次按下按钮时都发生变化,我们就会知道一切都是从零开始重新计算的。

用户交互将是真实的(不需要模拟)。我们将提供的只是一个“增量”按钮,它将增加计数器的值并再次显示。我们可以观察这一点,以了解多个用户是否看到彼此相同的值,或者他们是否正在运行隔离的环境。计数器是否为每个用户成功递增,或者每次按下按钮都会重置,因为框架正在从头开始重新计算整个脚本?

我们将在 Chrome 和 Firefox 中访问仪表板,以模拟两个完全不同的用户同时访问 web 应用程序。

这是一个将您的 Jupyter 笔记本显示为独立网络应用的解决方案。本质上,它默认隐藏代码单元,并简单地从上到下运行您的笔记本,然后最终允许用户与页面上定义的任何小部件进行交互。以下是我们测试笔记本中的代码:

from datetime import datetime
start_time = datetime.now()
print(start_time)from ipywidgets import Button, Output, VBox
count = 0button = Button(description='Increment')
out = Output()def on_click(_):
    global count
    with out:
        count += 1
        print('Clicked! Start Time {}; Count {}'.format(start_time, count))

button.on_click(on_click)VBox([button,out])

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

Jupyter 笔记本电脑,随时可供使用

运行 Jupyter 笔记本类似于使用 Voila 作为服务器,您可以在上面看到笔记本正在做我们预期的事情:start_time 变量在计算后保持不变,并且计数器在每次单击按钮时递增。

让我们试着在 Voila 服务器上运行。我们将分别在 Chrome 和 Firefox 中打开。我们将在每个浏览器中交替单击“Increment ”,以查看计数器是否按预期工作:

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

瞧,在 Chrome 和 Firefox 上运行的笔记本

计数器按预期工作——每个用户获得一个独立的值。在每台笔记本电脑的运行过程中,时间也保持不变。但是你可以看到每个浏览器的时间是不同的,这意味着“启动计算”是为每个用户独立运行的。这意味着每个用户在每次访问 web 应用程序时都必须耐心等待任何长时间运行的模型计算。

这使我们能够理解 Voila 执行模型,并思考它对于任何项目的局限性和优势。(有意地)与 Jupyter 笔记本模型有相似之处;就好像每个用户都将笔记本加载到一个新的内核中,然后按 shift+键进入所有的单元格。一旦到了底层,我们最终会得到一个 Python 内核状态——即内存中设置的一些变量。真的不会再发生什么了…除非我们已经连接了一些小部件来运行 Python 代码作为回调。这就是我们对按钮小部件所做的。但是此时会忽略笔记本代码单元——此时发生的所有事情都基于 Python 内存状态,包括现在只是从内存而不是笔记本单元运行的按钮单击回调函数。

如果您是这方面的新手,理解细节并不重要,但是希望与其他两个框架相比,结果会更有意义。

Voila 中“启动计算成本”问题的一些解决方法将在后面讨论。

细流

这是一个用 Streamlit 构建的类似仪表板:

这一次的结果非常不同:

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

Streamlit 并发测试 1

我们不仅最初会在每个浏览器中看到不同的时间,而且每次我们单击增量按钮时,都会生成一个新的时间,并且计数器不会超过 1!

这是因为 Streamlit 中的执行模型是不同的:每次用户与它交互时,整个脚本都从上到下重新运行。这听起来效率很低,确实如此,但是它允许简单的声明式脚本风格工作。在 Streamlit 代码中,您可以看到按钮的点击功能就在“if”语句中。与 Voila 窗口小部件相比,根本不需要真正的回调机制,在 Voila 窗口小部件中,我们连接了一个在单击按钮时执行的回调函数。

为了解决这个潜在的低效率问题,Streamlit 提供了一个缓存装饰器。下面是一个更新的 Streamlit 示例:

现在,我们发现获取当前时间的“启动计算”只运行一次。

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

Streamlit 并发测试 2

两个浏览器中的时间完全相同,当按下任何一个“增量”按钮时,时间都不会改变。虽然 cache decorator 简单而有用,但它仅限于缓存可以在 Python 中“腌制”的对象,即转换成可以从磁盘加载或保存到磁盘的格式。

和以前一样,计数从不增加。这种每用户状态的遗漏是 Streamlit 正在寻求解决的问题,并且有一个解决方法可用。但实际上真正需要它并不常见:在大多数仪表板中,状态通常保存在小部件中,这些小部件由 Streamlit 基于每个用户维护。例如,用户可以拖动滑块来改变模型的输入,它的新值不会在代码重新运行后失效。

阴谋破折号

下面是 Plotly Dash 框架的类似脚本:

这里的代码从一开始就有点复杂,但这确实导致了更好的控制。

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

Plotly Dash 并发示例

这里的“启动计算”(时间)在两个浏览器中保持不变,当点击增量按钮时也不会改变。此外,计数器确实成功地并且独立于每个用户递增。这使用了 Plotly Dash 的按钮组件的n_clicks属性,它将该组件绑定到所呈现的每个物理 HTML 按钮。

请注意,在 Plotly Dash 示例中,每次按下按钮时,输出都会被覆盖。瞧,每按一个按钮就会有新的输出。在任一框架中,这种行为都很容易改变。

结论

如果您再次查看 Plotly Dash 的代码,您会发现 web 服务器更明显地暴露在您自己的代码中。我们代码的app.run_server调用是服务器‘循环’实际运行的地方。相比之下,在 Voila 和 Streamlit 中,服务器在很大程度上是在我们自己的脚本之外——它是在外部“加载”我们的脚本来为自己处理它。

对于 Plotly Dash 来说,这凸显了我们正在构建一个传统的 web 应用,Dash 框架提供的真正好处是展示了无需编写任何 Javascript 或 HTML 就可以使用的用户界面组件。

一般来说,Voila 和 Streamlit 对于非程序员来说更容易开始,但是如果你想构建更传统的“web app”功能,扩展性可能会受到限制。

这并不是说这些框架中的任何一个天生就比其他任何一个更好。它们都有不同的设计,适合不同的场景。

最终,Plotly Dash 在处理每用户状态和维护“启动计算”方面的便利来自于它没有试图将我们从标准 Python 编程环境中移除。另一方面,对于来自纯 Jupyter 背景的人来说,编码有更高的学习曲线——瞧,Streamlit 允许他们在通常的自顶向下的过程代码中添加一些小部件。

事实是,掌握底层编程语言应该能够让您克服所讨论的任何问题。例如,Voila 将很容易让我们编写自己的代码来保存我们的“启动计算”到磁盘,并在下次运行时读取它们。但这在笔记本环境中感觉不太自然。

实际上,对于这些框架中的任何一个,基本的工作仪表板都是使用从文档或 web 上的类似示例中找到的复制粘贴代码片段构建的!要开始构建仪表板,您可能想知道它需要多长时间才能发展成一个更大的 web 应用程序,但是选择一个框架进行试验的最佳方式是了解您已经最熟悉的环境和编码风格。

本文的示例代码为 ,可在 GitHub 上获得。

丹·莱斯特是ContainDS的联合创始人,这是一个为从事离散项目的团队提供的数据科学平台。

Python 中的循环

原文:https://towardsdatascience.com/looping-in-python-5289a99a116e?source=collection_archive---------7-----------------------

如何在 Python 中使用 enumerate()函数

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

弗洛里安·奥利佛在 Unsplash 上拍摄的照片

介绍

假设我们有一个列表。我们想要遍历这个列表,打印出索引,后面跟着列表元素或索引处的值。让我们使用 for 循环来实现这一点:

num_list= [42, 56, 39, 59, 99]for i in range(len(num_list)): 
    print(i, num_list[i])# output: 
0 42
1 56
2 39
3 59
4 99

range() 是 python 中的内置函数,它允许我们遍历一系列数字。如上所述,我们使用 for 循环来循环遍历一个 range 对象(这是一种 iterable 类型),直到我们的列表的长度。换句话说,我们从 I 值 0 开始,一直到(但不包括)num_list 的长度,也就是 5。然后我们使用方括号在第 I 个索引处访问 num_list 的元素。

然而,重要的是要理解我们实际上并没有迭代 num_list 。换句话说, i 充当索引的代理,我们可以用它来访问来自 num_list 的元素。

[## 如何在 Python 中分割序列

了解如何在 Python 中分割列表和字符串

towardsdatascience.com](/a-skill-to-master-in-python-d6054394e073)

使用 enumerate()函数

不使用 range()函数,我们可以改用 python 中内置的 enumerate() 函数。 enumerate() 允许我们遍历一个序列,但是它同时跟踪索引和元素。

枚举(iterable,start=0)

enumerate()函数接受一个 iterable 作为参数,比如列表、字符串、元组或字典。此外,它还可以接受一个可选参数, start ,它指定了我们希望计数开始的数字(默认为 0)。

使用 enumerate() 函数,我们可以将 for 循环重写如下:

num_list= [42, 56, 39, 59, 99]for index, element in enumerate(num_list):
    print(index, element)# output: 
0 42
1 56
2 39
3 59
4 99

就是这样!我们不需要使用 range() 函数。代码看起来更干净,更 pythonic 化。

[## Python 中的词典理解

如何使用字典理解在 python 中创建字典

towardsdatascience.com](/dictionary-comprehensions-in-python-912a453da512)

enumerate()如何工作

enumerate() 函数返回一个枚举对象,它是一个迭代器。当从这个枚举对象访问每个元素时,返回一个元组,其中包含索引和该索引处的元素:(index,element)。因此,在上面的 for 循环中,在每次迭代中,它都将这个返回元组的元素分配给 index 和 element 变量。换句话说,返回的元组在 for 语句中被解包:

for **index, element** in enumerate(num_list):# similar to:
index, element = (index, element)

通过下面的例子可以更容易地看出这些元组:

name_list = ['Jane', 'John', 'Mike']list(enumerate(name_list))# [(0, 'Jane'), (1, 'John'), (2, 'Mike')]

一旦我们在枚举对象(迭代器)上调用 list()函数,它将返回一个元组列表,每个元组都包括索引及其对应的元素或值。

如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体会员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。

[## 通过我的推荐链接加入媒体——卢艾·马塔尔卡

阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…

lmatalka90.medium.com](https://lmatalka90.medium.com/membership)

结论

在本教程中,我们学习了如何使用 enumerate()函数对 iterable 进行计数。

通过梦幻英超 API“元素-概要”端点循环下载 Python 和 Pandas 的球员历史

原文:https://towardsdatascience.com/looping-through-the-fantasy-premier-league-api-element-summary-endpoint-to-download-player-df4ab5151fcb?source=collection_archive---------18-----------------------

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

图片由免费提供-照片来自 Pixabay

用于创建当前赛季所有玩家游戏周历史和所有过去赛季历史的主数据框架的文档

大家好。看起来我的第一个 FPL API 教程很成功,所以我又为你们带来了另一个 Python/Pandas Fantasy Premier League API 教程。

这一次,我们将创建两个不同的数据帧:(1)包含每个玩家当前赛季游戏周历史的数据帧,以及(2)包含每个玩家所有过去赛季历史的数据帧。

这将是对使用 Requests 包访问 API 端点并构建数据帧的极好介绍,也是对使用 Python“For 循环”迭代一系列玩家 id 并访问每个玩家的/api/element-summary/{element_id}的极好介绍。

在赛季结束时,你将能够将这第一个数据帧(每个球员的 FPL 个人资料的“本赛季”部分中可用的相同数据)保存为 CSV 或 Pickle,允许你建立自己的 FPL 数据库,明年将无法通过 FPL API 获得。

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

第二个数据框架将允许转换和分析在每个球员的 FPL 档案的“前几个赛季”部分中可用的完全相同的数据。将这些数据与其他玩家放在一起进行对比将会很有趣,而且可能会有所启发。

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

让我们开始吧!

目录

  1. 入门指南
  2. 进口
  3. 点击 API 并创建带有请求的响应对象
  4. 用 Json()方法将响应对象转换成 JSON
  5. 使用 JSON Keys()创建数据帧
  6. 构建并测试 For 循环
  7. 遍历所有元素,用 f 字符串格式构建主数据帧
  8. 保存到 CSV 并保存

步骤 0:开始

如果您是 Python、Pandas 和 Jupyter 笔记本的新手,请阅读下面我的文章“Python、Pandas 和 Jupyter 笔记本入门”,然后回到这里继续学习本教程。

[## Python、Pandas 和 Jupyter 笔记本入门

用 Jupyter 笔记本设置您的机器并开始用 Python 编程所需的所有文档…

medium.com](https://medium.com/dev-genius/getting-started-with-python-pandas-and-jupyter-notebooks-bd08c963914)

步骤 1:导入和设置

对于这个任务,我们需要pandasrequests。启动新的 Jupyter 笔记本,然后在第一个单元格中,导入您的包:

import pandas as pd
import requests

如果你喜欢键盘快捷键,你会喜欢按住“shift”键的同时按下“enter”键来运行每个单元格。

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

我还喜欢更新笔记本中的设置,这样我就可以左右滚动,看到任何给定数据帧中的所有列。如果您以前在 Jupyter 笔记本上玩过,您可能会经历过这样一个恼人的时刻:由于数据框包含“太多”列,所以您看不到部分数据框。运行下一个单元格以更新显示设置:

pd.options.display.max_columns = None

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

附言:如果你想知道我是如何在这些截图中创建这些部分标题的,请尝试选择一个单元格,然后将该单元格从“Code”转换为“Markdown”,方法是选择该单元格并点击“m”键,或者选择该单元格,然后从菜单的下拉菜单中选择“Markdown”。当您使用键盘快捷键“m”时,单元格高亮显示应为蓝色,而不是绿色:

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

步骤 2:点击 API 并创建带有请求的响应对象

接下来,我们将目标 url 设置为 FPL API 的主端点:

url = '[https://fantasy.premierleague.com/api/bootstrap-static/'](https://fantasy.premierleague.com/api/bootstrap-static/')

然后,我们需要使用GET方法创建一个带有请求包的响应对象:

r = requests.get(url)

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

步骤 3:用 Json()方法将响应对象转换成 JSON

接下来,我们将使用json()方法将响应对象的正文解析成 JSON。

如果您尝试在一个单元格中运行响应对象r,您将看到的只是响应:<Response [200]>

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

响应对象有各种不同的属性和方法!点击查看他们的列表

对于我们当前的目的,我们真的只关心使用json()方法,但是知道这些其他方法是可用的也很好。

现在让我们继续将响应体转换为 JSON 对象:

json = r.json()

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

步骤 4:使用 JSON Keys()创建数据帧

接下来,我们将使用keys()方法来查看 JSON 字典中有哪些类型的键可用:

json.keys()

这将返回一个键列表:

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

因为我已经研究了这些键中的每一个都提供了什么,所以我已经确切地知道我想要从哪个键构建我的第一个数据帧:

elements_df = pd.DataFrame(json['elements'])

创建新的数据帧后,我将使用head()方法检查前 4 行:

elements_df.head()

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

这个数据帧对于在下一节循环游戏中的所有玩家元素是必要的。

步骤 5:构建并测试 For 循环

现在我们有了要迭代的元素列表,我们可以构建 For 循环并点击element-summary端点,在每次迭代中注入玩家的element_id :

url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')

当我们解决这个问题时,请记住,我不一定要分享如何进行 For 循环的“最佳实践”。我确信有更有效的做事方法。这就是我做这件事的方式。

警告结束后,让我们首先构建一个 For 循环的微型示例,并确保我们的迭代器将为我们工作:

for x in elements_df.index[:5] :
    element_id = elements_df.id[x]
    element_name = elements_df.first_name[x]
    print(x)
    print(element_id)
    print(element_name)

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

注意这里的一些事情。首先,这个拼图的for x in elements_df.index[:5]:部分。

我已经要求计算机使用 DataFrame 索引作为迭代器,遍历elements_df DataFrame 的前 5 个元素。

这里的[:x]语法有助于将循环限制在前 5 个元素。

[:x]语法是一种非常常见的 Pandas 操作,用于按行分割数据帧。例如,elements_df[:5]实际上和elements_df.head()是一回事。

我们可以使用这个切片操作来帮助测试我们的 For 循环,因为我们绝对不想遍历整个数据帧来测试我们的循环。

这里的x变量表示我们正在循环的每一行的索引。如果您检查elements_df.head(),您会在一个未命名的列中看到数据帧最左边的数据帧索引:

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

我们可以使用索引来访问数据帧中的任何值。例如,让我们使用索引获取第一行的first_name:

elements_df.first_name[0]

这将返回‘Mesut’,DataFrame 中第一个元素的名字。

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

我们可以批量执行这个操作,使用一个索引列表来单独遍历每一行,并给一个变量赋值。现在让我们再做一次,但是这次得到前 10 个元素:

for x in elements_df.index[:10] :
    element_id = elements_df.id[x]
    element_name = elements_df.first_name[x]
    print(x)
    print(element_id)
    print(element_name)

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

太好了!我们的 For 循环运行良好。

步骤 6:遍历所有元素,用 f 字符串格式构建主数据帧

你会注意到我们在上面设置的一个变量是element_df.id。这个id对于我们的下一个 API 调用至关重要。

我们将使用id传递到/api/element-summary/{element_id}端点,为每个玩家获取element-summary

这是通过 f 字符串格式化完成的。让我们用一个例子来快速回顾一下 f-string 格式:有 f-string 格式的和没有的*😗

不带 f 字符串格式:

your_name = "David"
print("Hi, my name is {your_name}"

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

嗯,不是我们想要的。

现在,让我们尝试将变量your_name传递到我的字符串语句中:

your_name = "David"
print(f"Hi, my name is {your_name}"

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

啊,是的!f-string 允许我们将自己的变量注入到字符串中。这很重要,因为/api/element-summary/{element_id}端点将被设置为字符串,但我们需要在每次点击 url 时将element_id注入 URL。像这样:

url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')

让我们看一个例子:

url = f'[https://fantasy.premierleague.com/api/element-summary/1/'](https://fantasy.premierleague.com/api/element-summary/1/')
r = requests.get(url)
json = r.json()
json.keys()

这将返回JSON blob 的密钥:

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

和以前一样,我们将使用这些键来创建数据帧:

json_fixtures_df = pd.DataFrame(json['fixtures'])
json_history_df = pd.DataFrame(json['history'])
json_history_past_df = pd.DataFrame(json['history_past'])

然后,让我们使用head()方法查看每个数据帧:

json_fixtures_df.head()

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

json_history_df.head()

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

json_history_past_df.head()

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

看完这些数据帧后,我对为所有玩家保存json_history_dfjson_history_past_df感兴趣,但我现在对json_fixtures_df不太感兴趣。

我们有了所有的部分,让我们继续把它们放在我们的 For 循环中:

for x in elements_df.index[:5] :
    print(x)
    element_id = elements_df.id[x]
    url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
    r = requests.get(url)
    json = r.json()
    json_history_df = pd.DataFrame(json['history'])
    json_history_past_df = pd.DataFrame(json['history_past'])

print(x)通过查看屏幕,可以帮助我了解操作位置。它充当一种“进度条”

其余的手术我们已经经历过了。

不过这还不够好,因为每次我们循环一个新的element_id,我们都会覆盖保存在json_history_dfjson_history_past_df数据帧中的数据。

我们需要另一个数据框架来存储所有这些数据!

我们将通过创建两个名为all_history_dfall_history_past_df的新数据帧来处理这个问题,然后将每个新的json_history_df数据帧和每个新的json_history_past_df数据帧附加到这些主all_history_dfall_history_past_df数据帧中:

for x in elements_df.index[:5] :
    print(x)
    element_id = elements_df.id[x]
    url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
    r = requests.get(url)
    json = r.json()
    json_history_df = pd.DataFrame(json['history'])
    json_history_past_df = pd.DataFrame(json['history_past'])

    if x == 0 :
        all_history_df = json_history_df
        all_history_past_df = json_history_past_df
    else : 
        all_history_df = all_history_df.append(json_history_df)
        all_history_past_df = all_history_past_df.append(json_history_past_df)

如果这是我们第一次运行这个循环——我们通过索引号x==0知道它——将json_history_dfjson_history_past_df数据帧复制为all_history_dfall_history_past_df

但是,如果这是第一个索引之后的任何内容*,则将新数据帧附加到主数据帧。这只是将每个玩家的个人历史数据帧一个接一个地堆叠到一个包含所有玩家的核心数据帧中。*

让我们用前 5 个元素来测试一下:

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

查看all_history_df数据帧的前 10 行,我们可以看到我们的循环成功了!

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

这个all_history_past_df也好看!

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

现在,让我们通过移除[:5]切片,让她撕开整个 FPL 球员数据库:

for x in elements_df.index :
    print(x)
    element_id = elements_df.id[x]
    url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
    r = requests.get(url)
    json = r.json()
    json_history_df = pd.DataFrame(json['history'])
    json_history_past_df = pd.DataFrame(json['history_past'])

    if x == 0 :
        all_history_df = json_history_df
        all_history_past_df = json_history_past_df
    else : 
        all_history_df = all_history_df.append(json_history_df)
        all_history_past_df = all_history_past_df.append(json_history_past_df)

第七步:保存到 CSV 和泡菜

这一步是可选的,但当 2020/21 赛季结束时,你会希望将all_history_df数据帧保存到你的本地计算机上,以确保你仍然可以访问那些辉煌的数据。您可以将此数据帧保存为 CSV 或 Pickle 文件。

不确定 Python 中的泡菜是什么?

来自 geeksforgeeks.org:

Python pickle 模块用于序列化和反序列化 Python 对象结构。Python 中的任何对象都可以被腌制,以便保存在磁盘上。pickle 所做的是在将对象写入文件之前先“序列化”对象。Pickling 是一种转换 python 对象(list、dict 等)的方法。)转换成字符流。这个想法是,这个字符流包含在另一个 python 脚本中重建对象所需的所有信息。

Pickle 是一种更好的存储 python 对象的格式,因为它“包含了在另一个 Python 脚本中重建对象所需的所有信息”

CSV 通常也是一种可靠的方式,但泡菜是一种更安全、更有效的方式。

我们将在下面介绍这两种方法:

保存

all_history_df.to_csv('/Users/davidallen/fpl_python/all_history_df_1920.csv')all_history_past_df.to_csv('/Users/davidallen/fpl_python/all_history_past_df_1920.csv')

保存

all_history_df.to_pickle('/Users/davidallen/fpl_python/all_history_df_2021_09252020.pickle')all_history_past_df.to_pickle('/Users/davidallen/fpl_python/all_history_past_df_2021_09252020.pickle')

当您想读取您的 pickle 或 CSV 文件时,您需要做的就是将文件分配给一个变量,如下所示:

#READING A CSVall_history_df = pd.read_csv('/Users/davidallen/fpl_python/all_history_df_2021_09252020.csv')all_history_past_df = pd.read_csv('/Users/davidallen/fpl_python/all_history_past_df_2021_09252020.csv')#READING A PICKLEall_history_df = pd.read_pickle('/Users/davidallen/fpl_python/all_history_df_2021_09252020.pickle')all_history_past_df = pd.read_pickle('/Users/davidallen/fpl_python/all_history_past_df_2021_09252020.pickle')

唷。我们做到了。

下次见…干杯。

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

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。

[## 通过我的推荐链接加入媒体-大卫艾伦

阅读大卫·艾伦(以及媒体上成千上万的其他作家)的每一个故事。您的会员费直接支持…

deallen7.medium.com](https://deallen7.medium.com/membership)

BigQuery 中的循环

原文:https://towardsdatascience.com/loops-in-bigquery-db137e128d2d?source=collection_archive---------5-----------------------

了解如何使用 BigQuery 脚本通过 for 循环计算斐波那契数

E ver 想在 SQL 中循环?嗯,你可以用脚本。让我们看看如何用循环在 BigQuery 中计算斐波那契数。

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

在 BigQuery 中执行循环!—Claire SateraUnsplash 上拍摄的照片

我之前展示了如何用 JavaScript UDF(用户定义的函数)在 BigQuery 中实现 Fibonacci,也讨论了 BigQuery 中的数组,所以如果你是 BigQuery 的新手,先看看这些。

脚本基础

在我们开始计算斐波那契数列之前,让我们先讨论一下 SQL 脚本的构建模块。在编程语言(比如 Python)中,用一些值设置变量,然后处理这些变量是很自然的,而在 SQL 中,通常是从表中的一些数据开始。

要获得所有媒体文章的完整信息——包括我的——请点击这里订阅

使用 SQL 脚本,你可以声明变量,然后在计算中使用它们。问题是(对于那些来自动态类型语言的人来说)你需要在开始使用它们之前声明每个变量的类型,这些类型永远不会改变。

使用 SQL 脚本,您可以声明变量,然后在计算中使用它们,执行 for 循环等。

让我们声明第一个变量,并将其打印到控制台:

我们在这里执行的步骤是:

  • 关键字DECLARE用名称uninteresting_number和类型INT64实例化我们的变量。
  • we SET中的号的值1729
  • 最后,我们只需选择数字,将其打印到控制台。

如果想把变量的声明和设置一气呵成,也可以使用DEFAULT实参:DECLARE my_number INT64 DEFAULT 1729;

如果我们想更新我们的变量,那么我们再次使用SET关键字来这样做:

这里,我们首先将c设置为等于a,然后将c增加b,得到如下结果:

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

看,妈妈,我会做数学!

斐波那契数列

现在我们知道了如何声明和设置变量,让我们开始循环。首先,我们需要弄清楚我们的算法将如何工作。这里有一个让它工作的方法:

  1. 设置一个数组[0,1]——斐波那契数列的前两个元素。
  2. 要求用户输入n —我们将产生序列的前 n 个数字。
  3. 在每次迭代(n次)中,将数组最后 2 个元素的和追加到数组中。

这使我们得到以下结果:

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

看看我们在做 WHILE 循环!— 来源

代码中的注释解释了大部分内容,但只是澄清一下:

  • 我们声明n来知道我们需要生成多少个数字。
  • 我们声明我们的斐波纳契数列为f=[0,1]——这是我们的第 0 和第 1 个斐波纳契数列的起点。仅供参考, *f* *代表斐波那契。*😃
  • 我们声明i,我们的计数器知道我们已经生成了多少个数字。我们将此设置为 1,因为我们在f中已经有了第 0 和第 1 个数字。
  • 然后,对于每次迭代,我们通过反转数组找到最后 2 个数字f——遗憾的是,BigQuery 中没有负索引——将它们相加并添加到数组中。对于你们这些聪明的笨蛋来说,你可以把新元素添加到开头,这样就没有必要在每一步都颠倒了。)
  • 哦,别忘了增加i!不要迷失在无限循环中。

没有数组

上面的是可行的,但是如果我们只需要第 n 个数而不是整个序列呢?那么我们就可以不用数组来完成整个过程。我们需要做的就是跟踪序列中的最后 2 个数字

我们从上面删除了i,取而代之的是,在每一步递减n,直到达到 1。在每个步骤中,我们执行以下操作:

  • b存储在一个临时变量中。
  • b设置为新值,即ab之和。
  • a设置为临时变量——之前的b

这为我们提供了带有a < b的序列的最后两个数字。

我将让您来弄清楚那个IF声明是怎么回事,以及我们为什么需要它。

摘要

阅读完本文后,您现在已经了解了以下内容:

  • SQL 脚本是为 BigQuery 中的循环做的事情
  • 如何用变量的名称和类型声明变量
  • 如何设置变量的值
  • 如何追加到数组中
  • 如何执行 WHILE 循环

今天学了新东西,给自己一点鼓励。哦,如果我在这篇文章中说了一些愚蠢的话,请在评论中指出来。😉

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

干得好,SQL 忍者!—Guillermo la TorreUnsplash 上拍摄的照片

关于斐波那契数列和数组的其他 BigQuery 文章,请参见这些:

[## BigQuery 中的斐波那契数列

在 BigQuery 中使用用户定义的 JavaScript 函数来计算 Fibonacci

towardsdatascience.com](/fibonacci-series-with-user-defined-functions-in-bigquery-f72e3e360ce6) [## BigQuery 中的 FizzBuzz

BigQuery 中的 FizzBuzz,而不是 BigQuery 中的 Java 或 Python。使用 SQL,为什么不呢?

towardsdatascience.com](/fizzbuzz-in-bigquery-e0c4fbc1d195)

如何利用 Python 中的循环

原文:https://towardsdatascience.com/loops-in-python-3ba17168de28?source=collection_archive---------18-----------------------

如今,ython 是一门如此强大的语言,使用它的工具只会给你带来好处。在马上要去之前,Python 的高级工具。我们来看看 Python 中可以作为基础知识进行排序的循环。

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

Tine ivaniUnsplash 上的原始照片

在我们进入不同的循环之前,这里有一个关于循环本身的概述。

一般循环

循环用于对代码块进行规定次数的迭代。您可以有不同的循环方法,我们现在就来看看。

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

这个循环用于迭代某种序列。这可以是列表、元组、字典、字符串等。

在其他编程语言中,for loop 用作关键字,而在 Python 和其他编程语言中,它用作迭代器方法。

for 语句中的所有内容都将被执行多次。

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

让我们看几个例子来弄清楚它实际上是如何工作的。

例子

列表迭代

在第一个例子中,我将创建一个列表,用 for 循环遍历它,并打印出列表中的每个成员。

list = ["loops", "in", "python"]
for element in list:
  print(element)

输出:

loops
in
python

值得一提的是,print 语句会自动换行。

  • 列出元素,这两个都只是变量的名称,因此可以随意更改它们以用于您自己的实现

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

字符串迭代

在这个例子中,我们获取一个字符串并打印该字符串中的每个字符。

string = "loops"
for character in string:
  print(character)

输出:

l
o
o
p
s

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

破裂

break 语句对于拦截 for 循环非常有用。让我们来看看这个例子:

list = ["loops", "in", "python"]
for element in list:  
  if element == "python":
    break 
  print(element)

输出:

loops
in

输出看起来是这样的,因为一旦列表中的元素与“python”匹配,它就会跳出 for 循环并立即结束。

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

继续

使用这个语句,您可以中断当前的迭代而不完成它,并继续下一个迭代。

list = ["loops", "in", "python"]
for element in list:  
  if element == "in":
    continue 
  print(element)

输出:

loops
python

它将跳过“in”的打印部分,直接打印“python”。

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

范围

您可以将 range()函数与 for 循环结合起来,以指定它应该经历多少次迭代。

for element in range(3):
  print(element)

输出:

0
1
2

range 函数从 0 开始,以 n-1 结束,因为有了 0,我们就有了 n 个数字。

您也可以像这样定义范围的开始:

for element in range(1,3):
  print(element)

输出:

1
2

您可以操作的另一件事是定义迭代之间的“步骤”。默认值为 1。

for element in range(2, 10, 4):
  print(element)

输出:

2
6

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

其他

即使不使用 If,也可以将 else 与 for 循环结合使用。让我告诉你怎么做:

for element in range(1,3):
  print(element)
else:
  print("Python is super fun!")

输出:

1
2
Python is super fun!

一旦循环中断,您将在 else 语句中输出该字符串。

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

及格

你不能让 for 循环为空,但是当你传递时,你不会得到一个编译错误。

for x in range(3):
  pass

输出:

在…期间

只要条件有效,这个循环就会运行。我们通常使用一个变量作为一个计数器,以便知道什么时候停止它。

让我们来看几个不同的例子!

cnt = 1
while cnt <= 3:
  print(cnt)
  cnt += 1

输出:

1
2
3

我们将计数器设置为 1,每次它迭代时,我们都将它增加 1,直到它达到 3 并结束。

我们还可以将 while 循环与其他语句和命令结合使用。

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

破裂

与休息的概念非常相似。

cnt = 1
while cnt < 3:
  print(cnt)
  if (cnt == 2):
    break
  cnt += 1

输出:

1
2

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

继续

与 for continue 的概念非常相似。

cnt = 1
while cnt < 3:
  print(cnt)
  if (cnt == 2):
    continue
  cnt += 1

输出:

1
3

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

其他

与 for continue 的概念非常相似。

cnt = 1
while cnt < 3:
  print(cnt)
  cnt += 1
else:
  print("Very funny")

输出:

1
2
Very funny

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

嵌套循环

这些只不过是另一个循环中的一个循环。

对于嵌套循环

list1 = ["loops", "in", "python"]
list2 = ["nested", "loops", "are", "fun"]for element1 in list1:
  for element2 in list2:
    print(element1, element2)

输出:

loops nested
loops loops
loops are
loops fun
in nested
in loops
in are
in fun
python nested
python loops
python are
python fun

它从第一个列表中取出一个单词,从第二个列表中取出一个单词,然后进行所有的组合。

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

感谢阅读!

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

查看我的其他文章并关注我的媒体

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

当我发布一篇新文章时,请在 Twitter 上关注我

维基指环王:将维基数据导入 Neo4j 并分析家谱

原文:https://towardsdatascience.com/lord-of-the-wiki-ring-importing-wikidata-into-neo4j-and-analyzing-family-trees-da27f64d675e?source=collection_archive---------36-----------------------

了解如何从维基数据中抓取 LOTR 世界,并使用 Neo4j for Graph Data Science 工具箱对其进行分析

在我之前关于结合 NLP 技术和图形的的长篇文章取得如此大的成功之后,我准备了另一个详尽的教程。我们将讨论几个话题。我们将通过维基数据 API 将数据导入 Neo4j 开始。当我们完成的时候,我们将会收集维基数据上的大部分 LOTR 信息。在下一步中,我们将准备一个探索性的数据分析,并展示如何根据一些假设填充缺失值。最重要的是,我们将运行一些图形算法,并准备一些漂亮的可视化。

确保你身边有一些爆米花,并准备好一些深入的图表分析。

议程

  • 将维基百科数据导入 Neo4j
  • 基本图形探索
  • 填充缺少的值
  • 更多的图形探索
  • 弱连通分量
  • 中间中心性
  • 用 Bloom 实现图形可视化(很酷的部分!)

图表模式

您可以通过db.schema.visualization()程序在 Neo4j 浏览器中可视化图形模式。这是一个方便的过程,可以自动捕获存储图的模式。

附注:只有在我们导入图表后才运行它

CALL db.schema.visualization()

结果

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

我们使用简单的图表模式已经有一段时间了。我很高兴地说,这一次有点复杂。我们有一个有家族关系的角色的社交网络,像配偶兄弟姐妹有父亲,甚至没有像敌人这样的家族关系。我们知道一些关于角色的附加信息,比如他们的国家、种族和他们所属的任何团体。

维基数据导入

如前所述,我们将在apoc.load.json过程的帮助下从 WikiData API 获取数据。如果您还不知道的话, APOC 为将数据导入 Neo4j 提供了出色的支持。除了从任何 REST API 获取数据的能力,它还具有通过 JDBC 驱动程序与其他数据库(如 MongoDB 或关系数据库)集成的功能。

如果你经常使用 RDF 数据,你应该去看看这个 新语义库

我们将从引入 LOTR 世界的所有种族开始。我不得不承认我对 SPARQL 一无所知,所以我不会深入解释语法。如果你需要关于如何查询维基数据的基本介绍,我推荐 Youtube 上的这个教程。基本上,LOTR 世界中的所有种族都是 id 为 Q989255 的中土种族实体的一个实例。为了获得特定条目的出现次数,我们使用下面的 SPARQL 子句:

?item wdt:P31 wd:Q989255

这可以翻译为“我们想要获取一个项目,它是一个 id 为 Q989255 的实体(wdt: P31 )的实例”。在我们用 APOC 下载了数据之后,我们将结果存储到 Neo4j 中。

// Prepare a SPARQL query
WITH 'SELECT ?item ?itemLabel
      WHERE{
      ?item wdt:P31 wd:Q989255 .
      SERVICE wikibase:label { bd:serviceParam wikibase:language
      "[AUTO_LANGUAGE],en" }
      }' AS sparql
// make a request to Wikidata
CALL apoc.load.jsonParams(
        "https://query.wikidata.org/sparql?query=" + 
            apoc.text.urlencode(sparql),
        { Accept: "application/sparql-results+json"}, null)
YIELD value
// Unwind results to row
UNWIND value['results']['bindings'] as row
// Prepare data
WITH row['itemLabel']['value'] as race, 
     row['item']['value'] as url, 
     split(row['item']['value'],'/')[-1] as id
// Store to Neo4j
CREATE (r:Race)
SET r.race = race,
    r.url = url,
    r.id = id

那很容易。下一步是获取给定中土世界种族的实例角色。SPARQL 语法与前面的查询几乎相同,只是这一次,我们对每个种族进行迭代,并找到属于它的字符。

// Iterate over each race in graph
MATCH (r:Race)
// Prepare a SparQL query
WITH 'SELECT ?item ?itemLabel
      WHERE {
        ?item wdt:P31 wd:' + r.id + ' .
        SERVICE wikibase:label { bd:serviceParam wikibase:language
        "[AUTO_LANGUAGE],en" }
      }' AS sparql, r
// make a request to Wikidata
CALL apoc.load.jsonParams(
    "https://query.wikidata.org/sparql?query=" + 
      apoc.text.urlencode(sparql),
    { Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
WITH row['itemLabel']['value'] as name, 
     row['item']['value'] as url, 
     split(row['item']['value'],'/')[-1] as id, 
     r
// Store to Neo4j
CREATE (c:Character)
SET c.name = name,
    c.url = url,
    c.id = id
CREATE (c)-[:BELONG_TO]->(r)

你知道中土世界至少有 700 个角色吗?我从来没有想到会有这么多记录在维基数据上。我们的第一个探索性的密码查询将是按种族统计他们。

MATCH (r:Race)
RETURN r.race as race, 
       size((r)<-[:BELONG_TO]-()) as members
ORDER BY members DESC 
LIMIT 10

结果

指环组的团队是中土世界种族的一个代表样本。大多数角色要么是人类,要么是霍比特人,有几个精灵和矮人漫步走过。不过,这是我第一次听说梵拉和梅尔的比赛。

现在是时候用关于人物性别、国家和死亡方式的信息来丰富图表了。SPARQL 查询将与以前略有不同。这一次,我们将直接通过惟一的 id 选择一个 WikiData 实体,并有选择地获取它的一些属性。我们可以使用下面的 SPARQL 子句按 id 过滤特定的项目:

filter (?item = wd:' + r.id + ')

与 cypher 查询语言类似,SPARQL 也区分了MATCHOPTIONAL MATCH。当我们想要返回一个实体的多个属性时,最好将每个属性包装成一个OPTIONAL MATCH。这样,如果任何属性存在,我们将得到结果。如果没有OPTIONAL MATCH,我们将只能得到所有三个属性都存在的实体的结果。这是与 cypher 相同的行为。

OPTIONAL{ ?item wdt:P21 [rdfs:label ?gender] . 
           filter (lang(?gender)="en") }

wdt:P21表示我们对性别属性感兴趣。我们还指定我们想要获得一个实体的英文标签,而不是它的 WikiData id。搜索所需属性 id 的最简单方法是检查 WikiData 网页上的项目,并将鼠标悬停在属性名称上。

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

另一种方法是使用 WikiData 查询编辑器,它通过使用 CTRL+T 命令具有强大的自动完成功能。

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

为了将图形存储回 Neo4j,我们将使用FOREACH技巧。因为我们的一些结果将包含空值,所以我们必须将MERGE语句包装到支持条件执行的FOREACH语句中。查看迈克尔·亨格的博客文章了解更多信息。

// Iterate over characters
MATCH (r:Character)
// Prepare a SparQL query
WITH 'SELECT *
      WHERE{
        ?item rdfs:label ?name .
        filter (?item = wd:' + r.id + ')
        filter (lang(?name) = "en" ) .
      OPTIONAL{
        ?item wdt:P21 [rdfs:label ?gender] .
        filter (lang(?gender)="en")
      }
      OPTIONAL{
        ?item wdt:P27 [rdfs:label ?country] .
        filter (lang(?country)="en")
      }
      OPTIONAL{
        ?item wdt:P1196 [rdfs:label ?death] .
        filter (lang(?death)="en")
      }}' AS sparql, r
// make a request to Wikidata
CALL apoc.load.jsonParams(
    "https://query.wikidata.org/sparql?query=" + 
    apoc.text.urlencode(sparql),
     { Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
SET r.gender = row['gender']['value'], 
    r.manner_of_death = row['death']['value']
// Execute FOREACH statement
FOREACH(ignoreme in case when row['country'] is not null then [1] else [] end | 
        MERGE (c:Country{name:row['country']['value']})
        MERGE (r)-[:IN_COUNTRY]->(c))

我们正在一点一点地将额外的信息连接到我们的图表,并慢慢地将它转化为知识图表。我们先来看死亡财产的方式。

MATCH (n:Character) 
WHERE exists (n.manner_of_death)
RETURN n.manner_of_death as manner_of_death, 
       count(*) as count

结果

没什么有趣的。这显然不是《权力的游戏》系列。我们也来考察一下国家财产的结果。

MATCH (c:Country)
RETURN c.name as country, 
       size((c)<-[:IN_COUNTRY]-()) as members
ORDER BY members 
DESC LIMIT 10

结果

我们有 236 个字符的国家信息。我们可以做一些假设,并尝试填充缺失的国家值。让我们假设如果两个人是兄弟姐妹,他们属于同一个国家。这很有道理。为了实现这一点,我们必须从维基数据中导入家族关系。具体来说,我们将获取父亲、母亲、亲戚、兄弟姐妹和配偶关系。

// Iterate over characters
MATCH (r:Character)
WITH 'SELECT *
      WHERE{
        ?item rdfs:label ?name .
        filter (?item = wd:' + r.id + ')
        filter (lang(?name) = "en" ) .
      OPTIONAL{
        ?item wdt:P22 ?father
      }
      OPTIONAL{
         ?item wdt:P25 ?mother
      }
      OPTIONAL{
         ?item wdt:P1038 ?relative
      }
      OPTIONAL{
         ?item wdt:P3373 ?sibling
      }
      OPTIONAL{
         ?item wdt:P26 ?spouse
      }}' AS sparql, r
// make a request to wikidata
CALL apoc.load.jsonParams(
    "https://query.wikidata.org/sparql?query=" + 
     apoc.text.urlencode(sparql),
     { Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
FOREACH(ignoreme in case when row['mother'] is not null then [1] else [] end | 
        MERGE (c:Character{url:row['mother']['value']}) 
        MERGE (r)-[:HAS_MOTHER]->(c))
FOREACH(ignoreme in case when row['father'] is not null then [1] else [] end | 
        MERGE (c:Character{url:row['father']['value']}) 
        MERGE (r)-[:HAS_FATHER]->(c))
FOREACH(ignoreme in case when row['relative'] is not null then [1] else [] end | 
        MERGE (c:Character{url:row['relative']['value']}) 
        MERGE (r)-[:HAS_RELATIVE]-(c))
FOREACH(ignoreme in case when row['sibling'] is not null then [1] else [] end | 
        MERGE (c:Character{url:row['sibling']['value']}) 
        MERGE (r)-[:SIBLING]-(c))
FOREACH(ignoreme in case when row['spouse'] is not null then [1] else [] end | 
        MERGE (c:Character{url:row['spouse']['value']}) 
        MERGE (r)-[:SPOUSE]-(c))

在我们开始填充缺失值之前,让我们检查一下中土世界的滥交情况。第一个查询将搜索有多个配偶的角色。

MATCH p=()-[:SPOUSE]-()-[:SPOUSE]-()
RETURN p LIMIT 10

结果

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

我们实际上发现了一个有两个配偶的角色。是芬威,诺尔多的第一任国王。我们也可以看看某人是否有多个伴侣的孩子

MATCH (c:Character)<-[:HAS_FATHER|HAS_MOTHER]-()-[:HAS_FATHER|HAS_MOTHER]->(other)
WITH c, collect(distinct other) as others
WHERE size(others) > 1
MATCH p=(c)<-[:HAS_FATHER|HAS_MOTHER]-()-[:HAS_FATHER|HAS_MOTHER]->()
RETURN p

结果

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

所以看起来芬威和茵迪丝有四个孩子,和米里埃尔有一个孩子。另一方面,贝伦有两个父亲是很奇怪的。我想阿达内尔需要解释一下。我们可能会在 GoT 世界发现更多的死亡和滥交。

填充缺少的值

现在我们知道了中土世界的人物不滥交,让我们填充缺失的国家价值观。记住,我们的假设是:

如果两个角色是兄弟姐妹,他们属于同一个国家。

在我们填充国家/地区的缺失值之前,让我们填充兄弟国家/地区的缺失值。我们将假设如果两个角色有相同的母亲或父亲,他们是兄弟姐妹。让我们看看一些同胞候选人。

MATCH p=(a:Character)-[:HAS_FATHER|:HAS_MOTHER]->()<-[:HAS_FATHER|:HAS_MOTHER]-(b:Character)
WHERE NOT (a)-[:SIBLING]-(b)
RETURN p
LIMIT 5

结果

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

阿轧曼塔·丘博至少有六个孩子。其中只有两个被标记为兄弟姐妹。因为根据定义,它们都是兄弟姐妹,所以我们将填充缺失的连接。

MATCH p=(a:Character)-[:HAS_FATHER|:HAS_MOTHER]->()<-[:HAS_FATHER|:HAS_MOTHER]-(b:Character)
WHERE NOT (a)-[:SIBLING]-(b)
MERGE (a)-[:SIBLING]-(b)

该查询添加了 118 个缺失的关系。我需要学习如何更新维基数据知识图,并批量添加缺失的连接。现在,我们可以为兄弟姐妹填写缺失的国家值。我们会将所有字符与已填写的国家信息进行匹配,并搜索他们没有国家信息的兄弟姐妹。我喜欢用 cypher 查询语言表达这种模式是如此简单。

MATCH (country)<-[:IN_COUNTRY]-(s:Character)-[:SIBLING]-(t:Character) WHERE NOT (t)-[:IN_COUNTRY]->()
MERGE (t)-[:IN_COUNTRY]->(country)

新增了 49 个缺失的国家。我们可以很快提出更多的假设来填补缺失的值。您可以尝试自己添加一些其他缺失的属性。

我们仍然需要在图表中添加一些信息。在这个查询中,我们将添加关于角色的职业、语言、群体和事件的信息。SPARQL 查询与之前的相同,我们遍历每个字符并获取额外的属性。

MATCH (r:Character)
WHERE exists (r.id)
WITH 'SELECT *
      WHERE{
         ?item rdfs:label ?name .
         filter (?item = wd:' + r.id + ')
         filter (lang(?name) = "en" ) .
      OPTIONAL {
        ?item wdt:P106 [rdfs:label ?occupation ] .
         filter (lang(?occupation) = "en" ).
       }
      OPTIONAL {
        ?item wdt:P103 [rdfs:label ?language ] .
        filter (lang(?language) = "en" ) .
       }
      OPTIONAL {
        ?item wdt:P463 [rdfs:label ?member_of ] .
        filter (lang(?member_of) = "en" ).
      }
      OPTIONAL {
        ?item wdt:P1344[rdfs:label ?participant ] .
        filter (lang(?participant) = "en") .
      }
     OPTIONAL {
       ?item wdt:P39[rdfs:label ?position ] .
       filter (lang(?position) = "en") .
     }}' AS sparql, r
CALL apoc.load.jsonParams(
    "https://query.wikidata.org/sparql?query=" + 
     apoc.text.urlencode(sparql),
    { Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
FOREACH(ignoreme in case when row['language'] is not null then [1] else [] end | 
    MERGE (c:Language{name:row['language']['value']}) 
    MERGE (r)-[:HAS_LANGUAGE]->(c))
FOREACH(ignoreme in case when row['occupation'] is not null then [1] else [] end | 
    MERGE (c:Occupation{name:row['occupation']['value']}) 
    MERGE (r)-[:HAS_OCCUPATION]->(c))
FOREACH(ignoreme in case when row['member_of'] is not null then [1] else [] end | 
    MERGE (c:Group{name:row['member_of']['value']}) 
    MERGE (r)-[:MEMBER_OF]->(c))
FOREACH(ignoreme in case when row['participant'] is not null then [1] else [] end | 
    MERGE (c:Event{name:row['participant']['value']}) 
    MERGE (r)-[:PARTICIPATED]->(c))
SET r.position = row['position']['value']

我们来调查一下各组的成绩和人物的职业。

MATCH (n:Group)<-[:MEMBER_OF]-(c)
OPTIONAL MATCH (c)-[:HAS_OCCUPATION]->(o)
RETURN n.name as group, 
       count(*) as size, 
       collect(c.name)[..3] as members, 
       collect(distinct o.name)[..3] as occupations
ORDER BY size DESC

结果

就在这时,我意识到整个霍比特人系列都包括在内。巴林是索林公司集团的日记作者。出于某种原因,我期待比尔博·巴金斯是日记作者。很明显,环组团契里只能有一个弓箭手,那就是勒苟拉斯。甘道夫似乎参与了几个组织。

我们将再执行一次 WikiData API 调用。这一次我们将取得敌人和角色拥有的物品。

MATCH (r:Character)
WHERE exists (r.id)
WITH 'SELECT *
      WHERE
      {
        ?item rdfs:label ?name .
        filter (?item = wd:' + r.id + ')
        filter (lang(?name) = "en" ) .
      OPTIONAL{
        ?item wdt:P1830 [rdfs:label ?owner ] .
        filter (lang(?owner) = "en" ).
      }
      OPTIONAL{
       ?item wdt:P7047 ?enemy 
      }}' AS sparql, r
CALL apoc.load.jsonParams(
    "https://query.wikidata.org/sparql?query=" + 
     apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
WITH value,r
WHERE value['results']['bindings'] <> []
UNWIND value['results']['bindings'] as row
FOREACH(ignoreme in case when row['owner'] is not null then [1] else [] end | 
    MERGE (c:Item{name:row['owner']['value']}) 
    MERGE (r)-[:OWNS_ITEM]->(c))
FOREACH(ignoreme in case when row['enemy'] is not null then [1] else [] end | 
    MERGE (c:Character{url:row['enemy']['value']}) 
    MERGE (r)-[:ENEMY]->(c))

最后,我们已经完成了图表的导入。我们来看看直系亲属之间有多少仇人。

MATCH p=(a)-[:SPOUSE|SIBLING|HAS_FATHER|HAS_MOTHER]-(b)
WHERE (a)-[:ENEMY]-(b)
RETURN p

结果

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

看起来 Morgoth 和 Manw 是兄弟也是敌人。这是我第一次听说这两个人,但是 LOTR 粉丝网站声称 Morgoth 是第一个黑魔王。我们来看看二级亲属内部有多少敌人。

MATCH p=(a)-[:SPOUSE|SIBLING|HAS_FATHER|HAS_MOTHER*..2]-(b) 
WHERE (a)-[:ENEMY]-(b) 
RETURN p

结果

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

第二代家庭内部的敌人并不多。我们可以观察到,瓦尔达已经采取了她丈夫的立场,也是与摩哥特为敌。这是一个稳定的三角形或三和弦的例子。三角形由一个积极的关系(配偶)和两个消极的关系(敌人)组成。在社会网络分析中,三角形用于衡量网络的凝聚力和结构稳定性。

图形数据科学

如果你读过我以前的博客文章,你会知道我必须包括一些来自图形数据科学图书馆的图形算法的用例。如果你需要快速复习一下 GDS 图书馆是如何运作的,幕后发生了什么,我建议你看看我之前的博客文章。

我们将从投影家庭网络开始。我们加载了所有的角色以及他们之间的家庭关系,比如配偶、兄弟姐妹、父母。

CALL gds.graph.create('family','Character',
    ['SPOUSE','SIBLING','HAS_FATHER','HAS_MOTHER'])

弱连通分量

弱连通分量算法用于在我们的网络中寻找孤岛或不连通分量。以下可视化包含两个连接的组件。第一部分由迈克尔、马克和道格组成,而第二部分由爱丽丝、查尔斯和布里奇特组成。

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

在我们的例子中,我们将使用弱连接组件算法来寻找家庭网络中的孤岛。同一家庭成员中的所有成员都以某种方式相互关联。可能是嫂子的祖母的表亲,或者更直接一点的兄弟姐妹。为了大致了解结果,我们将运行算法的stats模式。

CALL gds.wcc.stats('family')
YIELD componentCount, componentDistribution
RETURN componentCount as components, 
       componentDistribution.p75 as p75,
       componentDistribution.p90 as p90,
       apoc.math.round(componentDistribution.mean,2) as mean,
       componentDistribution.max as max

结果

在我们的图表中有 145 个岛屿。75%以上的组件只包含一个字符。这意味着大约 110 (75% * 145)个角色没有图中描述的任何家族联系。如果它们只有一个连接,那么组件的大小至少是两个。最大的组成部分有 328 名成员。那一定是一个幸福的家庭。让我们写回结果,进一步分析家庭组成。

CALL gds.wcc.write('family', {writeProperty:'familyComponent'})

我们将从五个最大的家庭组成部分开始。我们感兴趣的第一件事是哪些种族出现在家谱中。我们还将在结果中添加一些随机成员,以便更好地感受数据。

MATCH (c:Character)
OPTIONAL MATCH (c)-[:BELONG_TO]->(race)
WITH c.familyComponent as familyComponent, 
     count(*) as size, 
     collect(c.name) as members,
     collect(distinct race.race) as family_race
     ORDER BY size DESC LIMIT 5 
RETURN familyComponent, 
       size, 
       members[..3] as random_members,
       family_race

结果

如前所述,最大的家族有 328 名成员,他们来自不同的种族,从精灵到人类,甚至是玛雅人。在中土世界,精灵和人类的生活似乎纠缠在一起。还有他们的腿。半精灵种族的存在是有原因的。其他种族,像霍比特人和矮人,更忠于自己的同类。

让我们来看看最大的社区中的跨种族婚姻。

MATCH (c:Character)
WHERE c.familyComponent = 169 // fix the family component 
MATCH p=(race)<-[:BELONG_TO]-(c)-[:SPOUSE]-(other)-[:BELONG_TO]->(other_race)
WHERE race <> other_race AND id(c) > id(other)
RETURN c.name as spouse_1, 
       race.race as race_1, 
       other.name as spouse_2, 
       other_race.race as race_2

结果

首先,我不知道埃尔隆德是半精灵。看起来人类和精灵的“联盟”历史悠久。我主要是期待看到阿尔温和阿拉贡,因为我记得从电影中。了解半精灵可以追溯到多远会很有趣。我们来看看谁是拥有最多后代的半精灵。

MATCH (c:Character)
WHERE (c)-[:BELONG_TO]->(:Race{race:'half-elven'}) 
MATCH p=(c)<-[:HAS_FATHER|HAS_MOTHER*..20]-(end)
WHERE NOT (end)<-[:HAS_FATHER|:HAS_MOTHER]-()
WITH c, max(length(p)) as descendants
ORDER BY descendants DESC
LIMIT 5
RETURN c.name as character, 
       descendants

结果

迪奥·艾鲁奇似乎是记录在案的最古老的半精灵。我在 LOTR 粉丝网站检查了结果,看来我们是对的。迪奥·艾鲁奇尔诞生于公元 470 年的第一个纪元。还有几个半精灵出生在迪奥之后的 50 年内。

中间中心性

我们还将看一下介数中心算法。它用于查找不同社区之间的桥节点。如果我们看一下下面的可视化,我们可以观察到美国队长具有最高的中间中心性分数。这是因为他是网络中的主要桥梁,连接着图的左边和右边。网络中的第二座桥是野兽。我们可以很容易地看到,图表的中央和右侧之间交换的所有信息都必须通过他才能到达右侧。

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

我们会在最大的家庭网络中寻找桥段人物。我的猜测是,跨种族婚姻中的配偶将会胜出。这是因为所有种族之间的交流都通过他们进行。我们已经看到只有六种不同种族间的婚姻,所以很可能其中一些会胜出。

CALL gds.alpha.betweenness.stream({
    nodeQuery:"MATCH (n:Character) WHERE n.familyComponent = 169
               RETURN id(n) as id",
    relationshipQuery:"MATCH (s:Character)-[:HAS_FATHER|HAS_MOTHER|SPOUSE|SIBLING]-(t:Character)
                       RETURN id(s) as source, id(t) as target",
    validateRelationships:false})
YIELD nodeId, centrality
RETURN gds.util.asNode(nodeId).name as character,
       centrality
ORDER BY centrality DESC LIMIT 10

结果

有趣的是阿尔温和阿拉贡获得了冠军。不知道为什么,但我一直认为他们是现代的罗密欧和朱丽叶,他们通过婚姻结成了人类和半精灵之间的联盟。我不知道 JRR 托尔金系统是如何生成名字的,但它似乎有点偏向于以 a 开头的名字。

Neo4j Bloom

到目前为止,我们已经完成了数据分析,并获得了一些见解。现在是时候用图表的实际应用来给我们的同事留下深刻印象了。 Neo4j Bloom 是图形数据科学生态系统 Neo4j 的一部分。它是一个工具,主要用于研究图形,并允许用户在很少或没有密码知识的情况下进行研究。查看由 Lju Lazarevic 发布的绽放精彩的帖子,了解最新功能。

Neo4j Bloom 预装了 Neo4j 桌面包。我已经写了一篇关于如何开始使用它的博客文章。一旦你打开了 Neo4j Bloom,创建你的第一视角。

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

点击生成按钮,自动生成图形透视图。一旦创建了视图,将鼠标悬停在它上面并单击 Use 透视图。

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

近自然语言搜索

欢迎来到 Neo4j Bloom。没有 cypher 查询知识的用户可以使用近自然语言搜索来探索图表。我们先在搜索栏输入敌人开始。

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

Bloom 自动为我们提供了一个可能与我们的搜索查询相关的模式。如果我们点击它,我们将得到以下可视化。

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

是的,我知道。网络中的所有节点都是蓝色的。我们一会儿就会谈到这个问题。可视化清楚地显示了两个集群或社区。在图的左边,我们看到好人和索伦在战斗。这是 LOTR 系列的。在右边,我们有好人对抗魔哥特人。我猜这一定是霍比特人系列。

如果你想改变节点的颜色,按照这个图像。首先,点击字符标签,然后选择基于规则的选项卡,输入您的颜色规则。在我们的例子中,我们把所有的女性角色都涂成红色。

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

通过近自然语言搜索,我们还可以定义更精确的图形模式。例如,假设我们想调查佛罗多·巴金斯先生的敌人。

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

它会自动完成我们正在寻找的模式。如果我们点击它,我们会得到。

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

搜索短语:最短路径

搜索短语机制允许我们向 Neo4j Bloom 添加自定义搜索功能。我们首先定义搜索短语应该是什么样子。我们可以使用 $ 符号向搜索查询添加参数。对搜索短语参数的自动完成支持是现成的,这真的很可爱。然后,我们输入所需的 cypher 查询,就万事俱备了。我们将使用下面的 cypher 查询来查找任意两个角色之间家族关系的最短路径。

MATCH (s:Character)
WHERE s.name = $source
MATCH (t:Character)
WHERE t.name = $target
MATCH p=shortestPath((s)-[:HAS_FATHER|HAS_MOTHER|SIBLING|SPOUSE*..25]-(t))
RETURN p

填写好的搜索短语将如下所示:

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

现在我们可以在搜索框中执行新的搜索短语。如前所述,该应用程序帮助我们自动完成。

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

对于佛罗多·巴金斯和山姆卫斯·詹吉之间的最短家族关系路径,我们得到以下结果。他们有血缘关系,但是只有 9 步之遥。佛罗多的堂兄有一个儿子,是山姆女儿的丈夫的祖父。希望我没有搞砸。

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

搜索短语:祖先树

最后,我们将创建一个搜索短语来分析给定角色的家族祖先树。我为 cypher 查询准备了两种变体。第一个变体只遍历 HAS_FATHER 和 HAS_MOTHER 关系。

MATCH (c:Character)
WHERE c.name = $name
MATCH p=(c)-[:HAS_FATHER|HAS_MOTHER*..20]->()
RETURN p

第二个变体将之前用弱连通分量算法计算的整个家族分量可视化。

MATCH (c:Character)
WHERE c.name = $name
WITH c.familyComponent as family
MATCH p=(c1)--(c2)
WHERE c1.familyComponent = family AND c2.familyComponent = family
RETURN p

我们将使用第一种变体,因为它为博客文章产生了更漂亮的可视化效果,但是我鼓励您亲自尝试第二种变体。

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

我们添加了另一个搜索短语。我们现在可以在搜索栏中使用它。

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

结果

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

结论

我真的很喜欢写这篇博客和搜集维基数据知识图表。它包含了丰富的信息,我们可以在 Neo4j 中进行分析。我可以把这篇博文分成两部分甚至三部分。尽管如此,我还是喜欢将所有内容放在一个地方,向您展示完成图形分析的整个循环是多么容易,从导入和丰富图形到基本的数据探索和分析,我们以一些漂亮的图形可视化为基础。立即试用并下载 Neo4j 桌面。如果您有任何反馈或问题,可以在 Neo4j 社区网站上分享。

和往常一样,代码可以在 GitHub 上获得。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值