机器学习中的向量范数
原文:https://towardsdatascience.com/vector-norms-in-machine-learning-5b8381a5130c
p-规范指南。
马库斯·温克勒在 Unsplash 上的照片
如果你正在阅读这篇文章,很可能你已经知道向量是什么,以及它们在机器学习中不可或缺的位置。概括地说,向量是特定长度的数字的一维数组。如下所示:
包含 n 个元素的向量。(图片由作者提供)
向量的元素以特定的顺序排列,元素的位置通常具有固有的含义。我们可以使用位置(或索引)来访问单个元素。
使用索引访问向量的元素。(图片由作者提供)
我们也可以认为向量代表空间中的一个点。如果向量的长度是 n ,则称该点在一个 n 维空间中。例如,如果向量的大小为 2,这可以表示二维空间中相对于原点的点,如下所示:
显示点(4,2)的向量的 2D 图。(图片由作者提供)
向量规范
向量范数被定义为一组函数,这些函数以一个向量作为输入,输出一个正值与之相对。这被称为矢量的大小。根据我们用来计算大小的函数的类型,我们可以得到同一个向量的不同长度。
显示向量范数函数族及其输出的图。(图片由作者提供)
虽然经常被忽视,但规范是训练机器学习模型的核心。实际上,就在每次反向传播迭代之前,您要计算一个标量损失值(正值),它是预测值和地面真实值的平方之差的平均值。这个定标器损失值只不过是一个范数函数的输出。我们计算损失的方法如下所示:
显示使用预测值和真实值计算损失的图表。(图片由作者提供)
标准范数方程—P-范数
所有的范数函数都来源于一个标准的范数方程,称为 p-范数。对于参数 p 的不同值(p 应该是大于或等于 1 的实数),我们得到不同的范数函数。然而,通用的等式如下所示:
p-范数方程。(图片由作者提供)
这需要一个 n 维向量 x 并计算每个元素的 p 次方次幂。然后,我们将得到的所有元素求和,取 p 次根,得到向量的 p 范数,也就是它的大小。现在,通过参数 p 的不同值,我们将获得不同的范数函数。下面我们就来一一讨论一下。
L0 标准:
虽然 p=0 位于 p-范数函数的域之外,但是在上面的等式中代入 p=0 会得到各个向量元素的 0 次幂,也就是 1(假设数字不为零)。此外,我们在方程中还有一个 p 次根,它不是为 p=0 定义的。为了解决这个问题,定义 L0 范数的标准方法是计算给定向量中非零元素的数量。下图显示了给定向量的 L-0 范数函数的输出:
该图显示了 L0 范数的值。(图片由作者提供)
L1 标准:
在 p-范数的标准方程中代入 p=1 ,我们得到如下:
L1 范数的方程。((图片作者提供)
- 当用于计算损失时,L1 范数也被称为平均绝对误差*。*
- 无论是远离还是靠近原点,L1 范数在所有位置都呈线性变化。
下图显示了给定向量的 L1 范数函数的输出:
显示 L1 范数值的图像。(图片由作者提供)
L2 标准:
在所有的范数函数中,最常见和最重要的是 L2 范数。在我们上面讨论的 p 范数的标准方程中代入 p=2 ,我们得到 L2 范数的以下方程:
L2 范数的方程。((图片作者提供)
- 当用于计算误差时,上述等式通常被称为均方根误差。
- L2 范数度量从原点的距离,也称为欧几里德距离。
下图显示了给定向量的 L2 范数函数的输出:
显示 L2 范数值的图像。(图片由作者提供)
平方 L2 范数:
顾名思义,L2 范数的平方与 L2 范数相同,只是平方不同。
平方 L2 范数的方程。((图片作者提供)
- 当用于计算机器学习中的误差时,上述等式通常被称为均方误差。
与 L2 范数相比,平方 L2 范数在计算上相对便宜。这是因为:
- 它缺少平方根。
- 在机器学习应用中,平方 L2 范数的导数更容易计算和存储。平方 L2 范数中元素的导数需要元素本身。然而,在 L2 范数的情况下,需要整个向量。
最大范数(或 L-∞范数):
由于无穷是数学中的一个抽象概念,我们不能仅仅在标准的 p-范数方程中代入 p=∞。然而,当 p 接近无穷大时,我们可以用极限来研究函数的行为。最大范数方程的简单推导可以在这里找到。
最大范数方程。(图片由作者提供)
Max norm 返回最大幅度元素的绝对值。下图显示了给定向量的最大范数函数的输出:
显示最大范数值的图像。(图片由作者提供)
结论说明:
- 向量范数是以一个向量为输入,输出一个正值的函数。
- 所有的范数函数都可以从一个方程中导出。范数函数族被称为 p-范数。
- L1 范数也被称为平均绝对误差。
- L2 范数也被称为均方根误差。
- 平方 L2 范数也被称为均方误差。
产品的矢量表示 Prod2Vec:如何去除大量嵌入
你好!我叫 Alex,在 Ozon 的产品匹配团队工作。Ozon 是一家电子商务公司,为客户提供从不同卖家购买商品的服务。我们每天都要处理数十万件产品,我们的任务是识别和比较我们网站上的类似报价(查找匹配),以便将不同卖家的报价收集到一个产品卡中。
每个产品都有以下信息:图片、标题、描述和附加属性。我们希望检索和处理所有这些信息,以处理不同的任务,而这对产品匹配团队尤为重要。
为了从产品中提取特征,我们使用不同的文本模型(fastText,transformers)为描述和标题创建向量表示(嵌入),并使用大量的卷积神经网络(ResNet,Effnet,NFNet)为图像创建向量表示。这些向量进一步用于特征生成和产品匹配。
Ozon 上每天有数百万的更新,这就是为什么统计所有模型的嵌入变得具有挑战性。通常每个向量描述产品的不同部分。如果我们不这样做,而是一次只获得一个产品范围的向量,会怎么样?听起来不错,但是如何正确实施呢…
图片由作者提供,灵感来自电影《宿醉》
要创建产品的矢量表示,我们可以使用:
1.内容—图像信息、文本、产品名称和属性。
2.用户会话—用户查看/购买产品的历史记录。
让我们谈谈我们是如何应用第一种方法(使用内容)解决这个问题的。
作者图片
方案
除了这种架构可以用于推荐、搜索和匹配的事实之外,它允许将关于产品的所有信息(图像、标题、描述和属性)统一到单个向量中,因此简化了一些管道(排序、搜索和候选产品的选择)。
建筑
使用度量学习方法来完成这项任务是合乎逻辑的:最小化相似产品之间的距离,并使用例如三重损失来迫使不相似的产品彼此分开。有许多有趣的问题(负采样,这里什么被认为是正例,如何正确地建立数据集)。由于我们已经有了一些这种类型的模型,我们决定使用监督学习方法来解决这个问题——预测类别树中最低级别的类别。
每个产品都属于一个特定的类别树,从高层次(衣服、书籍、电子产品)到低层次(短裤、杯子、智能手机外壳)。我们有几千个这样的低级类别。
例如,电子产品(第一类)→手机、平板电脑(第二类)→苹果智能手机(第三类)。
作者图片
为了对如此大量的类别进行分类,而不是通常的 Softmax(它没有显示出令人满意的结果),我们决定尝试使用另一种方法,这种方法最初是为人脸识别任务提出的— ArcFace 。
经典的 Softmax 并不直接影响一个类内的学习嵌入的接近度和不同类中的远离度。ArcFace 就是为此而专门设计的:通过选择边缘惩罚 m 参数,我们可以调整相同或不同类的嵌入闭合/距离。
作者图片
模型架构的第一个版本如下所示:
作者图片
对于模型来说,立即区分类别 3 被证明是太困难了:我们试图在每次迭代中根据类别 3 的最终交叉熵损失来训练图像、文本和属性模型。这导致他们的体重会聚不良和缓慢。因此,我们决定从以下方面改进该模型:
1.从每个编码器中,我们获得中间层输出— cat1(高级类别)预测。
2.总损失是所有损失的加权总和,首先,我们给予第 1 类损失更大的权重,然后逐渐向第 3 类损失转移。
因此,我们有以下架构:
作者图片
我们采用通常的指数函数作为加权系数:
作者图片
在推断过程中,我们不再对 cat3 预测感兴趣,而是对产品的矢量表示感兴趣,因此我们将图层的输出带到 ArcFace 这是我们需要的嵌入。
准备数据
如果我们只统计所有产品的类别,我们会得到大约 6000 个,其中一些非常相似(维生素和矿物质复合物和膳食补充剂),而另一些则相互嵌入(咖啡和咖啡胶囊),还有一些包含的产品示例太少(理疗机)。
这就是为什么将原始类别作为目标不是一个选项的原因——我们必须执行相当多的预处理来合并相似的类别。结果,我们得到了一个数据集,包括大约 500 万个项目,1300 个类别,每个类别至少有 500 个样本。
数据本身处理如下:
1.文本被转换成小写,停用词被删除。
2.使用标准方法(水平、垂直翻转、亮度和对比度变化)增强图像。
3.那些没有太大意义并且在几乎所有产品中都存在的属性(例如序列号)被删除了。我们尝试了不同的选项来处理属性:将“键-值”对分别添加到每个属性中,并将它们组合成一个字符串。最终没有太大的区别,但第二个选项在学习过程中看起来更优雅,所以我们选定了它。
学习过程
我们决定看看更轻的架构,因为我们有太多的数据,需要将两个文本和一个图像模型放入学习管道。我们使用 ResNet34 作为 CNN,使用两个 Rubert-Tiny 作为文本——作为标题和属性。
我们有文本和图像模型,所以我们为它们分别设置了一个优化器:AdamW——用于 BERTs 和 SGD——用于 ResNet 和 model head。总而言之,我们训练了 60 个历元:最初是 15 个学习率较高的历元,然后继续进行较小的历元,并使用 horovod 在 GPU 上并行化它们。
验证的结果是 85% Acc@1 和 94% Acc@5。相比之下:在标题上训练的 fastText 给出了 60%的 Acc@1 的准确率。
作者图片
当我们渴望了解我们是否成功地为产品生成了良好的嵌入时,类别预测的准确性是不够的。此外,我们使用了带有 3D 矢量可视化的投影仪:在那里你可以选择不同的降维方式,看看我们的矢量是如何投影到球体上的。
例如,这里有 t-SNE 和 UMAP 的可视化:
作者 gif
作者 gif
如果我们仔细观察,我们会发现每个聚类都包含相同类别的产品:
作者图片
当你观察生产管道中产品的最近邻居时,会发生这样的情况:
作者图片
最重要的是,排名模型的推理时间大大减少了:使用 Prod2Vec 嵌入而不是图像和文本嵌入,我们获得了超过三倍的加速:
作者图片
结果和观点
我们对结果很满意,所以我们将完成的架构投入生产,现在每天通过 Spark 结构化流计算数百万个这样的嵌入。它们可以被进一步放入排名模型中,从而产生匹配的良好候选。
此外,嵌入可以用于我们团队中出现的许多其他任务或相关任务。
产品匹配的结果看起来是这样的:在一个产品卡片上可以看到不同的卖家。
作者图片
然而,有趣的是,如果我们用度量学习来训练它,检查这个架构是否会工作得很好。这一切都有待于将来去发现。
如果你做过类似的事情,或者知道解决类似问题的其他方法,请在下面留下评论。希望你觉得这篇文章有趣,有帮助:)
矢量化:将运算速度提高 100 倍的必备技术
任何现代数据科学或机器学习应用程序都需要
乔伊·凯伯在 Unsplash 上的照片
介绍
在当前的数据科学或机器学习应用中,通常会涉及到庞大的数据集和复杂的网络。因此,在处理计算工作量时,代码效率变得非常重要。
例如,在经典的多层感知器(又名前馈神经网络)中,网络通常包含多个线性层。假设输入层包含 64 个神经元,而第一个隐藏层包含 128 个隐藏神经元。然后,为了在给定输入的情况下计算隐藏层的输出,直接的方法是使用由 Numpy 库提供的[np.dot](https://numpy.org/doc/stable/reference/generated/numpy.dot.html)
方法:
import numpy as npfeat = np.ones(64)
w = np.random.rand(64,128)feat.dot(w)
如果我们想计算执行这个方法的时间,我们可以使用[%timeit](https://docs.python.org/3/library/timeit.html)
:
%timeit feat.dot(w)
我们可以看到,这种方法平均需要 1.4 微秒。
然而,如果我们不使用np.dot
,而是在仍然使用 Numpy 数组的同时,恢复到传统的 for 循环,那么您应该这样写:
def loop(feat, w):
res = np.zeros(128)
for i in range(w.shape[1]):
s = 0
for j in range(len(feat)):
s += feat[j]*w[j][i]
res[i] = s
return resloop(feat, w)%timeit loop(feat, w)
正如你所看到的,像 for 循环这样直观的东西平均需要 4.94 毫秒,比使用之前的方法慢了 3000 倍以上。虽然这些都是很小的时间单位,但想象一下,当神经网络非常深入,数据集达到数百万或数十亿时,那么 3000 倍的差异很可能会产生影响,并决定应用程序的可行性。
顺便提一下,我们可以检查两种方法计算出的结果是否相同(打印前十个元素来验证):
feat.dot(w)[:10]
## result: array([30.80125191, 34.84418376, 29.5061941 , 33.00162298, 31.77854219,
31.93457429, 30.25155328, 35.71776325, 35.45861881, 33.58667301])loop(feat, w)[:10]
## result: array([30.80125191, 34.84418376, 29.5061941 , 33.00162298, 31.77854219,
31.93457429, 30.25155328, 35.71776325, 35.45861881, 33.58667301])
所以,让我们回到今天的主题——矢量化。什么是矢量化? 矢量化 是一种处理整个数组而不是单个元素的编程风格。这通常会加快计算速度。使用np.dot
是矢量化的一个例子;这个函数由 Numpy 优化,它直接使用两个输入向量执行内积,而不需要使用 for 循环(通常是嵌套的 for 循环)手动进行乘法和求和。
为什么矢量化速度更快
矢量化比 for 循环更快的一个主要原因是 Numpy 操作的底层实现。
正如你们许多人所知(如果你熟悉 Python ),Python 是一种动态类型语言。当你声明一个变量时,不需要指定数据类型,因为 Python 会在运行时为你推断出来。这不同于静态类型语言,静态类型语言需要指定数据类型以便编译器检查,这些语言包括 Java 和 c。
在 Python 中,假设一个列表存储异构元素,在运行时,当执行某个操作时,必须检查列表中的每个单独元素(类型检查)。这将给常规列表增加一定的开销。另一方面,Numpy 使用的是[ndarray](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html)
,它是存储同质元素的固定大小项目的多维数组。ndarray
被优化,因为底层操作是使用高效的 C 操作完成的,这允许向量化。
矢量化的另一个例子
为了让您更好地理解矢量化,另一个例子是常见的损失函数线性回归 — 均方误差(MSE) 。回想一下,线性回归的 MSE 定义为数据集中预测值和实际标注值之间的均方误差,预测值是当前权重集与要素的点积。这可以使用矢量化实现,如下所示:
def MSE(theta, X, y):
return ((X @ theta - y)**2).sum() / len(X)
注意,除了使代码执行方式更快之外,代码也直观简单。不需要混乱的嵌套 for 循环。
矢量化的条件
现在,您可能知道矢量化速度很快。但是我们可以对任何类型的函数使用矢量化吗?矢量化的条件是什么?
要使用矢量化,一个关键要求是底层的元素操作应该相互独立。例如,在 x 和 y 的点积中,x_1 * y_1
的结果与x_2 * y_2
无关。当不满足这个独立性标准时,我们只能回到使用 for 循环。以下给出了无法应用矢量化时的 3 种常见情况:
- 循环依赖性
for i in range(1, len(x1)):
x1[i] = x1[i-1] * x2[i-1]
- 间接存储器访问
for i in range(len(x2)):
x1[x2[i]] = x3[i] * x4[i]
- 代码分支
for i in range(len(x1)):
if i < 8:
i += func()
x1[i] = x2[i] * x3[i]
np .矢量化方法
如果一个方法是可向量化的(不是上面三个条件中的任何一个),那么我们可以通过[np.vectorize](https://numpy.org/doc/stable/reference/generated/numpy.vectorize.html)
函数注册它。举个例子,
def func(a, b):
return a+1 if a >= b else a-1func_vec = np.vectorize(func)
a = np.array([1, 2, 3, 4, 5, 6])
b = 3
func_vec(a, b)
# result: array([0, 1, 4, 5, 6, 7])
我们可以定义自己的定制函数并将其矢量化,这很好。然而,在注册自己的矢量化函数之前,请考虑是否存在执行相同操作的多种方法。上述功能可以通过以下方式实现:
np.where(a>=b, a+1, a-1)
# result: array([0, 1, 4, 5, 6, 7])
这两种方法给出了相同的结果。哪个更快?当我们使用%timeit
时,我们得到如下结果:
%timeit func_vec(a, b)
%timeit np.where(a>=b, a+1, a-1)
答案似乎是使用np.where
比定义我们自己的矢量化函数快 4 倍。原因是使用预定义的 Numpy 方法利用了 Numpy 使用的底层优化 C 代码。
因此,每当您想要定义一个定制的矢量化函数时,首先要考虑是否有任何预定义的方法可以做同样的工作;否则,您可以安全地注册自己的矢量化函数。
矢量化思维过程
现在您已经知道了什么是矢量化,为什么要使用矢量化,以及何时使用矢量化,我想给您提供一个关于矢量化思维过程的额外提示。
有些公式可能无法直观地进行矢量化,为了帮助解决这个问题,这里有一个大纲,告诉你如何考虑如何对特定表达式进行矢量化。
矢量化的步骤:
- 给定一个迭代表达式,考虑等价的向量/矩阵表达式形式
- 将这个向量/矩阵分解成更小的部分
- 向量化每个组件,例如元素式操作、
XX^T
、X^TX
等。 - 识别 Numpy 函数,例如
np.dot
、np.sum
、np.outer
、您自己的np.vectorize
函数,以执行矢量化
一个有趣的注意事项是,向量化不限于 Numpy 事实上,它也可以应用于其他数据科学和深度学习库,包括 Pandas、scikit-learn、Pytorch 等。
摘要
如果你能走到这一步,恭喜你!
再次重申,矢量化是一种一次处理整个数组/矩阵而不是单个元素的编程方式。向量化的主要优势是(方式)更快的代码执行和更干净的代码。注意,并不是所有的公式或方法都可以向量化,如果可以的话,我们可以通过np.vectorize
注册它,假设现有的 Numpy 函数没有其他的方法(例如np.dot
、np.where
、np.sum
等)。
这就是这篇文章的全部内容,希望你在这篇文章中学到了一些东西!下一集再见。
请随意浏览我以前的帖子,如果有任何问题,请告诉我:
https://medium.datadriveninvestor.com/beginners-guide-to-web-scraping-using-beautifulsoup-and-python-999a927f20e6 https://medium.datadriveninvestor.com/top-20-masters-programs-in-ai-data-science-2021-98aadb7db409
参考
使用 RStudio 进行版本控制
原文:https://towardsdatascience.com/version-control-with-rstudio-d20c54bd5b49
对于 Git 和 GitHub
RStudio 用于版本控制的典型界面。作者出于教育目的创建的图像
1.一般概念
科学最重要的一个方面是可重复性。每个实验都必须可以被其他人和团队复制。在计算机科学的背景下,科学计算、模拟和数据管理必须像任何其他实验一样是可复制的。为了确保结果是可重复的,必须能够将代码和分析分解为以前的版本,并复制图。这些过程和步骤是通过版本控制实现的。
版本控制是一个重要的过程,用于备份变更文件、存储和访问带注释的历史,以及管理不同变更集之间的变更合并。版本控制广泛应用于学术界、研究实验室和工业界。版本控制最重要的目标是简化和最大化软件开发人员的产出。
在过去的几年中,分布式版本控制如 Git、bazaar 等。,变得非常受欢迎。具体来说,Git 和 GitHub 几乎已经成为处理代码和文件的团队的默认选择,因为它们更加强大和用户友好。
Git 是一个跟踪变化的软件,GitHub 是一个使用 Git 进行操作的在线仓库。它们相互关联,但不是一回事。Git 用于本地版本控制(你的电脑),GitHub 用于远程版本控制(互联网和云)。
在大多数情况下,Git 直接通过用户终端与命令行一起使用。然而,也有可能将 Git 和 GitHub 与不同的图形用户界面一起使用,以简化事情。Git 和 GitHub 最重要的 GUI 是 GitHub 桌面和 RStudio 。
在本文中,我将讨论如何使用 RStudio 对 Git 和 GitHub 进行版本控制。这里我假设读者知道如何使用 Git 和 GitHub,也知道什么是 RStudio。我还假设读者已经在其计算机中安装了 Git、RStudio,并且还拥有一个 GitHub 帐户。
2.用于版本控制的 RStudio 配置
一旦您安装了 Git 和 RStudio,您必须在计算机上有一个本地 Git 存储库。要做到这一点,首先在您的计算机中的所需位置创建一个文件夹(例如“Git_examples”)。创建文件夹后,打开终端(在我的例子中是 MacBook pro)并运行以下代码:
$ **cd** .../Git_examples
$ **git init**
在上面的代码中,我首先移动到空文件夹“Git_examples ”,然后初始化一个 Git 存储库。我假设读者熟悉这些简单的概念。当初始化 Git 储存库时,文件夹不一定需要为空。它可以是空的,也可以是包含多种不同类型文件的文件夹。 git init 命令在“Git_examples”目录中创建一个隐藏文件夹,目的是跟踪目录中的所有文件。如果您的计算机上已经有了一个初始化的 Git 存储库,那么就没有必要继续上面描述的步骤,原因将在下面解释。
现在打开 RStudio IDE。一旦你打开它,你将看到 RStudio 的默认外观。第一步是在 RStudio 中激活版本控制。进入:工具 → 全局选项,然后点击 Git/SVN ,如下图 1 所示。如果你已经安装了 Git,很有可能在 Git 可执行文件中会出现 Git 在你电脑中的安装路径。您可以保留默认路径,也可以用另一个路径来更改它。如果您的计算机中也安装了 SVN,那么 SVN 可执行文件字段也将被填充。在我的例子中,没有安装 SVN,因此默认情况下 RStudio 会写入(未找到)。也可以创建一个 SSH RSA 密钥,但是在本文中,我将使事情更简单。
完成这些步骤后,选择复选框:启用 RStudio 项目的版本控制接口,然后点击对话框底部的“应用”按钮。现在 RStudio 中启用了版本控制。此时,建议重启 RStudio。
图一。作者创造的形象。
重启 RStudio 后,点击按钮“创建一个项目”,如图 2 中箭头所示。一旦你点击了“创建一个项目”按钮,一个对话窗口“新项目向导”将出现在主窗口的顶部。在这个对话框中,“创建项目”下通常有三个默认选项:新目录、现有目录和版本控制。
图二。作者出于教育目的创建的图像。
项目向导中的每个目录都有特定的用途。第一个(新目录)与新的 R 项目目录相关,第二个(现有目录)使得将新项目与现有工作目录相关联成为可能,第三个(版本控制)本质上与现有 GitHub 目录相关。
在本文中,我将向您展示如何将一个项目与一个现有的工作目录相关联。正如您所记得的,我已经向您展示了如何在一个给定的目录中启动一个 Git 存储库,因此我将使用该目录继续进行。点击现有目录,你将看到如图 3 所示的窗口。然后点击“Browse”按钮,选择已经启动 Git 存储库的文件夹(在我的例子中是“Documents/Git_examples”),然后点击“Create Project”。之后,项目被创建为 Git 存储库。
图 3。作者创造的形象
3.准备并提交 RStudio
使用 RStudio 创建项目后,您的默认窗口应该如图 4 所示。花点时间探索这个窗口,你通常会观察到许多不同的选项。在左侧窗口的一半,您将看到 R 控制台外壳、终端外壳和作业外壳。在右边的一半,在上面的面板中你会看到不同的面板,其中有一个“Git”面板。在下半部分,您将看到 Git 存储库所在的目录和文件。
你可以看到在文件区有*。gitignore* 和 Git_examples。Rproj 文件。当一个人像我上面所做的那样创建一个新的项目时,这些文件是自动创建的。我创建的目录没有其他文件,除了在我们开始项目时由 RStudio 自动创建的文件。有两种方法可以将文件添加到我们的目录中,要么直接使用 RStudio 创建文件,要么像处理其他文件夹一样将文件添加到我们的存储库文件夹中。
要创建一个包含 RStudio 的文件并将其添加到我们的存储库中,请单击绿色的加号按钮,如图 4 中的箭头所示。
图 4。作者创造的形象。
单击图 4 所示的绿色按钮后,您将看到一个类似于图 5 所示的窗口。正如你在对话窗口中看到的,RStudio 为用户提供了创建不同格式文件的可能性,如文本文件、Python 脚本、R 脚本等。让我们通过选择如图 5 中箭头所示的字段来创建一个文本文件。
图 5。作者创造的形象。
在文本文件字段中单击一次后,RStudio IDE 中将出现一个新的节窗口,其中文件窗口位于左侧的上部窗口中。可以向文本文件添加一些文本(例如:“我的第一个版本控制文件”),并且在点击“另存为…”图标之后,如图 6 中的箭头所示。将出现一个提示窗口,询问保存文件的位置、名称等。
默认情况下,保存目录是存储库目录。例如,通过单击“另存为…”图标,以“First_file.tex”的名称保存文件。之后,已经创建并保存的文件将出现在右半部分窗口下面板的 Git 存储库“Git_examples”文件夹中。它还会出现在右半部分窗口上面板的“Staged/Commit”Git 面板中。还可以看到终端外壳,如果需要,可以方便地通过终端直接编写命令。
图 6。作者创造的形象。
Git 跟踪了我在存储库文件夹中创建的“First_file.tex ”,但是还没有提交。首先要做的是在 Git 窗口的左上角选择“Staged”框,如图 6 所示。之后,文件的状态将从"?到“A ”,其中后者意味着它已经被添加到登台区,并且该文件准备好被提交。对于其他 Git 操作,还可以注意到“Diff”、“Log”和“Revert”图标。
图 6。
为了提交文件,有两个选项,如图 6 中的箭头所示。无论哪种方式,选择提交更改,然后通过点击“提交”按钮,在主窗口的顶部将出现如图 7 所示的对话窗口。此后,用户在“提交消息”文本框中写入提交消息,然后单击“提交”按钮保存消息。然后关闭对话窗口。
图 7。
在您执行了图 7 之前的所有步骤之后,您将看到一个窗口,如下图 8 所示。您可能会注意到,“First_file.tex”现在已经从 Git 面板中消失了,因为我们已经提交了这个文件。在此面板中,仅显示暂存和未跟踪的文件。另一件需要注意的事情是,在 Git 面板中,在我们提交“First_file.tex”文件之后,RStudio 将分支的状态从“no branch”更改为“master”。
图 8。作者创造的形象。
4.的”。gitignore”文件
当我们第一次创建 RStudio 项目时,会自动创建两个文件,其中一个是“”。gitignore" 文件。这个文件非常有用,因为它允许用户忽略并因此不在 Git 面板中显示用户不想包含在项目中的所有文件。因此,你需要考虑什么类型的文件不希望包含在项目中,并且在点击之后。gitignore 文件来修改它。我假设读者知道此时该做什么。将不需要的文件包含在中后。gitignore 文件,保存,大功告成。
5.远程版本控制(拉/推)
到目前为止,我已经向您展示了如何处理本地版本控制,即通过使用 RStudio 进行标准的 Git 操作。正如您在上面的图 8 中看到的,在 Git 面板中,绿色的 push 和 pull 数组是灰色的。原因是我们没有告诉 RStudio 使用任何像 GitHub 中那样的远程存储库。
要激活拉/推按钮,首先需要使用终端通过 push 命令添加一个远程源,或者从 GitHub 拉一个存储库。这里我展示了如何将本地存储库推送到 GitHub。
进入 RStudio IDE 中的终端,如上图 8 所示。默认情况下,终端设置为项目存储库文件夹。在终端中键入以下命令行:
$ **git remote add origin** URL
这里的 URL 指的是 GitHub 上资源库的完整链接。这实质上意味着在编写上面的命令行之前,用户已经在 GitHub 上有了一个存储库!
在上面的命令行中按下 enter 键后,很可能会出现一个对话框,要求您输入一些 GitHub 凭证,如用户名和/或密码。从 2021 年 8 月 13 日起,GitHub 改变了 Git 操作的认证规则,现在人们需要一个令牌来代替密码。
将本地存储库推送到 GitHub 需要在终端中编写的下一个命令行如下:
$ **git push --set-upstream** **origin master**or$ **git push -u origin master**
在您输入上面的 push 命令之后,您的本地存储库已经被推送到 GitHub,您可以通过查看适当的 GitHub 帐户来直接检查它。如果你做了上述操作,RStudio 上的灰色推/拉按钮将被激活,然后你就可以直接使用这些按钮与 GitHub 进行交互,参见本文顶部的图。
6.结论
在本文中,我向您展示了如何使用 RStudio 进行 Git/GitHub 操作。正如我上面提到的,Git 有几个图形用户界面,比如 GitHub Desktop,但是在我看来,RStudio 优于这些图形用户界面中的大多数。
RStudio IDE 非常用户友好,在主窗口中,可以有终端、Git 面板、文件夹目录以及创建和修改文件的空间。事实上,使用 RStudio,您可以轻松创建不同类型的文件,如 Python 脚本、R 脚本、Tex 文件,甚至编译 LaTeX 文件。
RStudio 有许多选项可以简化存储库中的 Git 操作。事实上,我相信每个在终端中使用 Git 命令行的人很快就会意识到,对于非常大的项目来说,这些命令行会变得非常麻烦和难以使用。RStudio 大大简化了这些 Git 操作。如果您需要使用终端进行一些特定的 Git 操作,您可以在主 RStudio 窗口中使用终端 shell,如图 8 所示。
虽然在本文中,我试图涵盖 RStudio 版本控制最重要的方面,但读者也可以在这里寻找一些额外的信息。
如果你喜欢我的文章,请与你可能对这个话题感兴趣的朋友分享,并在你的研究中引用/参考我的文章。不要忘记订阅将来会发布的其他相关主题。
视图:SQL 中被低估的表的替代品
原文:https://towardsdatascience.com/views-an-underrated-alternative-to-tables-in-sql-26cd6d1d8660
SQL 用户将从早期养成使用视图的习惯中受益匪浅
由保罗·斯科鲁普斯卡斯在 Unsplash 上拍摄
视图通常被称为“虚拟表”,是许多 SQL 脚本中不可或缺的组件。
尽管如此,大多数钻研 SQL 的资源很少关注视图,而是选择更重视更高级的查询概念,如连接和 cte。
这可能是因为视图在语法方面更容易掌握,并且在编码访谈中不常见。
不管怎样,视图有巨大的效用。SQL 用户将从早期养成使用它们的习惯中受益匪浅。
什么是视图?
视图只是在给定数据库上运行的存储查询的输出。
它们经常被比作表格,因为它们也以行和列的形式报告数据。
然而,与表不同,视图实际上不是数据库模式的一部分。使用视图只需要运行它存储的查询。因此,它们需要内存,但不需要额外的存储空间。
视图的好处
视图在 SQL 中非常有用,因为它们能够简化操作和管理访问控制。
- 简化复杂查询
虽然有些 SQL 查询简单易懂,但其他查询可能很复杂,需要许多行代码。
考虑已经被规范化的数据库,其结果是包含多个表。不断编写使用多个连接或 cte 来获取相同信息的查询会很麻烦。这不仅费时,而且让用户容易出错。
幸运的是,当试图重复访问相同的信息时,没有必要经历这么多麻烦。视图可以让用户以最小的努力重复查询,从而消除所有不必要的复杂性。
2。管理访问控制
安全性是数据库管理中的一个关键因素。理想情况下,人们应该只被授权访问符合其角色或地位的数据。
您不希望存储在表格或列中的任何个人信息或机密信息被错误的人看到。
数据库管理员可以通过创建视图并授予其他人访问这些视图的权限来规避这个问题。通过这种安排,任何外部方(例如客户)都无法看到他们不应该知道的信息。
在 SQL 中使用视图
由于 SQL 的用户友好语法,视图易于创建和管理。
作为视图的演示,我们有两个表,名为People
和Job
,我们将使用 PostgreSQL 查询它们(语法可能会因所选的 RDMBS 而异)。
代码输出(由作者创建)
代码输出(由作者创建)
假设您想要定期跟踪“高收入者”的记录,这些人的工资至少为 20 万英镑。您可以通过以下查询获得这些记录。
代码输出(由作者创建)
该查询将返回期望的输出,但是重复编写多行代码来获得相同的输出会令人厌烦。
此外,向外部客户机显示这些信息的用户可能不希望他们也能访问数据库中的first_name
列。对于这样的场景,创建一个只存储所需列的视图,然后将这些列提供给客户机,这将是非常有益的。
- 创建视图
创建视图的语法类似于创建表的语法。
上述查询可以存储在具有以下内容的视图中:
现在,我们可以像查询任何其他表一样查询视图。
代码输出(由作者创建)
2。更新视图
可以用CREATE OR REPLACE
命令更新视图。
假设定义高收入者的起征点应该是 15 万,而不是 20 万。
可以修改视图来解决这一变化。
现在查询同一个视图会产生稍微不同的输出。
代码输出(由作者创建)
虽然CREATE OR REPLACE
命令能够修改视图,但是它有自己的局限性。也就是说,它希望查询输出保留相同的列数、列名和列数据类型。因此,任何导致数据结构变化的修改都会产生错误。
3。查看所有视图
在 SQL 脚本上花费大量时间后,很容易失去对所有创建视图的跟踪。此外,记住存储在这些视图中的复杂查询可能会很麻烦。
对于这种情况,可以使用下面的查询找到所有创建的视图。
代码输出预览(由作者创建)
注意,包含了WHERE
命令是为了省略 PostgreSQL 中的内置视图。
在输出中,table_name
列显示了视图的名称,而view_definition
列显示了视图中存储的查询。
4。删除视图
最后,可以用简单的一行程序删除视图。
结论
照片由 Prateek Katyal 在 Unsplash 上拍摄
视图不仅使用户能够简化复杂的查询,还允许他们限制其他人对个人或机密信息的访问。
总而言之,对于那些希望在与同事或外部客户协作的同时高效地执行 SQL 数据分析的人来说,它们非常有用。
我祝你在数据科学的努力中好运!
Python 中的视图和副本
原文:https://towardsdatascience.com/views-and-copies-in-python-1e46b5af4728
“我学得越多,就越意识到自己有多少不知道。”
本文解释了在使用切片、花式索引和布尔索引等操作时,如何复制或引用 Python 列表、NumPy 数组和 pandas 数据框。这些操作在数据分析中非常常见,不能掉以轻心,因为错误的假设可能会导致性能损失甚至意外的结果。Python 看起来很简单,但是每次回到基础,总会有新的东西需要探索和学习。副标题中阿尔伯特·爱因斯坦的名言适用于一切,Python 也不例外。
介绍
我经常怀疑自己什么时候真正了解一门学科。完成了博士学位,并在过去做了一段时间的研究员,我可以自信地说,答案是绝对不会。我使用 Python 已经有一段时间了,它是一个非常棒的数据分析工具。我为现实生活中的问题提供了非常需要的解决方案,并产生了影响。尽管如此,每次我回到基础,都有新的东西要学,或者有新的角度来看待事物。当阅读一本书的介绍部分时,这种想法经常被触发,这些部分在书的本质真正开始之前被认为是容易阅读的。阿尔伯特·爱因斯坦的这句话在我的脑海中回响,不久之后,我使用 Python 解释器来尝试一些东西,想知道我到底是如何发现如此基础的东西的。我也想知道如果有这么多问题我什么时候能看完这本书,但是时间管理是另一个痛苦的故事…
这篇文章就跟随了这样一个时刻。它旨在深入解释 Python 列表、NumPy 数组和 pandas 数据框在使用切片、花式索引和布尔索引等操作时如何创建视图或副本。有一些混淆,因为像浅拷贝和深拷贝这样的术语并不总是指同一件事,同时也不清楚什么时候像 NumPy 数组元数据和 Pandas 索引这样的辅助数据被拷贝或引用。这篇文章可能不会提供所有的答案,但是希望它除了提供文档参考之外,还提供了一个在有疑问时运行快速计算实验的框架。所有示例都是用 Python v3.8.10、pandas v1.5.1 和 NumPy v1.23.4 编写的。
Python 列表
在这一节中,我们将运行一些计算实验来理解如何创建 Python 列表的副本。如果您运行类似的实验,请记住 Python 在内存中缓存小整数和字符串,以便它可以引用预先制作的对象,而不是创建新的对象。这个所谓的 interning 是本文中使用的标准 Python 实现 CPython 的优化之一。使用不同的字符串和整数是明智的,这样可以避免在查找对象地址时产生混淆。
Python 列表是人们可能认为容易的部分。让我们创建一个包含整数、Python 列表和嵌套 Python 列表作为元素的 Python 列表。我们还创建了一个实用函数来打印各种 Python 列表元素的地址,为了简洁起见,只显示了地址的最后四位数字。
上面的代码打印出来
a : 4160 | 7728 | 9888 3376 | 3232 0848 2480
注意,地址在每次执行中当然是不同的。因此,我们将确保数组a
从现在开始不会被改变。让我们尝试不同的方法来复制数组a
,从简单的(重新)绑定到另一个变量到深度复制
那会打印
new binding : 4160 | 7728 | 9888 3376 | 3232 0848 2480
shallow copy I : 7072 | 7728 | 9888 3376 | 3232 0848 2480
shallow copy II : 9312 | 7728 | 9888 3376 | 3232 0848 2480
shallow copy III : 1488 | 7728 | 9888 3376 | 3232 0848 2480
shallow copy IV : 8128 | 7728 | 9888 3376 | 3232 0848 2480
deep copy : 0528 | 7728 | 6848 3376 | 0816 2960 2480
首先要观察的是,除了新绑定(第一行)之外,列表的地址(第一列有地址)在所有其他尝试中都发生了变化。这意味着进行了复制。代码提供了四种不同的方法来创建浅层副本,这意味着列表的元素是相同的对象,尽管列表本身不是。如果我们试图改变列表的浅层副本的不可变元素,原始列表将不会被修改,但是改变可变元素会改变原始列表。举个例子,
印刷品
a_demo (before) -> ['d1', ['d2', 'd3']]
a_demo (after) -> ['d1', ['**D2**', 'd3']]
a_demo_shallow_copy -> ['**D1**', ['**D2**', 'd3']]
这意味着在嵌套列表和使用其他可变列表元素的情况下,浅拷贝会导致副作用。在深层拷贝的情况下,我们是安全的,如下面的代码所示
那会打印
a_demo (before) -> ['d1', ['d2', 'd3']]
a_demo (after) -> ['d1', ['d2', 'd3']]
a_demo_deep_copy -> ['**D1**', ['**D2**', 'd3']]
以上是相当简单的概括。任何类型的 Python 列表切片,如a[:]
、a[1:4]
、a[:5]
或a[::-1]
,都会创建列表保留部分的浅层副本。但是当我们连接或相乘列表时会发生什么呢?你能预测下面的操作会发生什么吗?
上面的版画
a : 4160 | 7728 | 9888 3376 | 3232 0848 2480
b (first part) : 5712 | 7728 | 9888 3376 | 3232 0848 2480
b (second part) : 5712 | 7728 | 9888 3376 | 3232 0848 2480a : 4160 | 7728 | 9888 3376 | 3232 0848 2480
b (first part) : 5648 | 7728 | 9888 3376 | 3232 0848 2480
b (second part) : 5648 | 7728 | 9888 3376 | 3232 0848 2480
b (third part) : 5648 | 7728 | 9888 3376 | 3232 0848 2480
这意味着我们创建了列表元素的更多引用(绑定),也就是说,这就像创建了一个浅层拷贝。这可能会导致意想不到的副作用,如下面的实验所示
那会打印
a_demo (before) -> ['d1', ['d2', 'd3']]
b -> ['**D1**', ['**D2**', 'd3'], 'd1', ['**D2**', 'd3']]
a_demo (after) -> ['d1', ['**D2**', 'd3']]
再说一次,请随意进行如上所述的简单计算实验。Python 是一种很好的实验语言,因为它的语法简单、简洁。
NumPy 数组
与 Python 列表类似,NumPy 数组也可以通过视图复制或公开。为了说明该功能,我们将通过绘制 0 到 9 范围内的随机整数来创建一个数组
我们还定义了一个实用函数来显示数组内容、数组元素消耗的总字节数、数组在内存中的总大小、一个布尔值来显示数组是拥有它使用的内存还是从另一个对象借用的内存,如果内存来自其他对象,则显示基本对象。上面的版画
[[8 2 8 8 1]
[7 4 2 8 8]
[3 3 2 3 3]
[0 0 7 6 8]
[2 7 3 4 6]]datatype is int64
number of bytes is 200 bytes (25 x 8 bytes)
size is 328 bytes
owndata is True
base is None
该数组将其数据类型显式设置为int64
,因此每个数组元素消耗 8 个字节。总共 25 个数组元素消耗 200 个字节,但是由于数组元数据,例如数据类型、步长和其他有助于轻松操作数组的重要信息,内存大小为 328。我们可以看到数组保存自己的数据,因此它的基数是None
。
让我们看看如果我们创建一个视图会发生什么
那会打印
[[8 2 8 8 1]
[7 4 2 8 8]
[3 3 2 3 3]
[0 0 7 6 8]
[2 7 3 4 6]]datatype is int64
number of bytes is 200 bytes (25 x 8 bytes)
size is 128 bytes
owndata is False
base is [[8 2 8 8 1]
[7 4 2 8 8]
[3 3 2 3 3]
[0 0 7 6 8]
[2 7 3 4 6]]
数组的内容保持不变。类似地,数组元素的数据类型和字节数保持不变。其余的数组属性现在不同了。大小已经减少到 128 字节(即 238–200 字节),因为数组视图为 NumPy 数组属性消耗内存。数组元素没有被复制而是被引用。这从不再是None
的基本属性中可以明显看出。在 NumPy 术语中,视图有相同的数据缓冲区(实际数据),但有自己的元数据。修改视图的元素也会修改原始数组。
让我们看看创建拷贝时会发生什么
那会打印
[[8 2 8 8 1]
[7 4 2 8 8]
[3 3 2 3 3]
[0 0 7 6 8]
[2 7 3 4 6]]datatype is int64
number of bytes is 200 bytes (25 x 8 bytes)
size is 328 bytes
owndata is True
base is None
输出看起来与原始数组相同。修改副本的元素不会修改原始数组。
我们可以很容易地试验各种整形、切片和索引功能,以检查是否创建了视图或副本
那会打印
reshape produces a view
transpose/reshape produces a view
ravel produces a view
transpose/ravel produces a copy
transpose/ravel (F-order) produces a view
flatten produces a copy
transpose/flatten produces a copy
slicing produces a view
advanced indexing produces a copy
combined indexing and slicing produces a copy
Boolean indexing produces a copy
对于某些功能,行为并不总是相同的。例如,[numpy.ravel](https://numpy.org/doc/stable/reference/generated/numpy.ravel.html)
返回一个连续的扁平数组,该数组仅在需要时才是副本。另一方面,[numpy.ndarray.flatten](http://numpy.ndarray.flatten)
总是返回折叠成一维的数组的副本。[numpy.reshape](https://numpy.org/doc/stable/reference/generated/numpy.reshape.html)
的行为有点复杂,感兴趣的读者可以参考官方文档。
需要说明的是,NumPy 在原始数组中用偏移量和跨距寻址元素时,即在使用基本索引和切片时,会创建视图。这与 Python 列表的行为相矛盾!另一方面,高级索引总是创建副本。整形操作更复杂,是否返回副本或视图取决于上下文。
使用高级索引创建的副本,以及使用[numpy.copy](https://numpy.org/doc/stable/reference/generated/numpy.copy.html)
创建的副本不会深入复制数组中的可变元素。与 Python 列表的浅层副本一样,NumPy 数组副本包含相同的对象,如果该对象可以被修改(可变),这可能会导致意外:
那会打印
Numpy shallow copy
a_np_demo (before) -> [1 2 list([3, 4])]
b -> [**-1** 2 list([**-3**, 4])]
a_np_demo (after) -> [1 2 list([**-3**, 4])]Python deep copy
a_np_demo (before) -> [1 2 list([3, 4])]
b2 -> [**-1** 2 list([**-3**, 4])]
a_np_demo (after) -> [1 2 list([3, 4])]
这可能是理论上的兴趣,因为 NumPy 数组通常不用于存储可变对象。不过,很高兴知道copy.deepcopy()
起作用了。
熊猫数据框
现在让我们把注意力转向熊猫数据框。按照通常的方式,我们将定义一个熊猫数据框和一个效用函数来显示它的细节
该数据与早期的 NumPy 数组具有相同的数据结构,即它具有 5x5 int64 个元素,但是我们另外定义了索引和列名。效用函数已被修改。pandas 数据帧的不同列可以有不同的数据类型,因此我们用a_df.dtypes.unique()
返回唯一的数据类型。为了查看底层数据何时被复制或引用,我们首先使用a_df.to_numpy()
获取底层 NumPy 数组,然后使用数组接口获取指向数据第一个元素的指针。上面的版画
dataframe is
c0 c1 c2 c3 c4
r0 5 2 8 6 6
r1 1 9 1 1 1
r2 0 7 6 3 7
r3 7 4 9 5 2
r4 5 8 3 7 1datatypes are [dtype('int64')]
number of bytes is 200 bytes (25 x 8 bytes)
size is 511 bytes
pointer to data area 2893487649296
我们现在有足够的设备来试验拷贝和视图。
查看 pandas API 参考,我们可以找到一个数据帧复制函数,它接受一个deep
布尔参数:当 True(默认)时,一个新对象被创建,带有调用对象的数据和索引的副本(这不是标准库的copy.deepcopy()
意义上的深度副本;见下文!).我们可以修改数据和索引,而原始数据框将保持不变。如果为 False,将创建一个新对象,而不复制调用对象的数据或索引,也就是说,我们只创建对数据和索引的引用。这意味着对原始数据框数据的任何更改都将反映在副本中。
让我们用一种观点做实验
那会打印
dataframe is
c0 c1 c2 c3 c4
r0 5 2 8 6 6
r1 1 9 1 1 1
r2 0 7 6 3 7
r3 7 4 9 5 2
r4 5 8 3 7 1datatypes are [dtype('int64')]
number of bytes is 200 bytes (25 x 8 bytes)
size is 511 bytes
pointer to data area 2893487649296
Same base: True
Same row index: True
Same column index: True
我们可以看到数据区指向同一个内存地址,NumPy 数组基是同一个对象,两个索引也是同一个对象。
让我们创建一个副本(deep=True
是默认的,但为了清楚起见,我们包括它)
那会打印
dataframe is
c0 c1 c2 c3 c4
r0 5 2 8 6 6
r1 1 9 1 1 1
r2 0 7 6 3 7
r3 7 4 9 5 2
r4 5 8 3 7 1datatypes are [dtype('int64')]
number of bytes is 200 bytes (25 x 8 bytes)
size is 511 bytes
pointer to data area 2893487655536
Same base: False
Same row index: False
Same column index: False
我们可以看到,副本与原始数据帧具有不同的基础,这也反映在不同的数据区指针中。我们还为这两个索引创建了新的对象。同样,类似于 NumPy 数组的情况,如果数据帧包含可变元素,那么改变副本的这些可变对象会修改原始数据帧,如下面的数值实验所示
那会打印
a_df_demo (before) -> c1 c2 0 1 3 1 2 {'key1': '✓', 'key2': '✓'}
b -> c1 c2 0 1 3 1 2 {'key1': '✓'}
a_df_demo (after) -> c1 c2 0 1 3 1 2 {'key1': '✓'}
对于熊猫数据框来说,这并不是一个非常常见的用例,但是记住这一点还是很有用的。对熊猫来说不幸的是,似乎不可能通过使用 Python 的copy.deepcopy()
函数从标准库中获得真正的深度副本,因为熊猫开发者已经将实现为 pd.DataFrame.__deepcopy__()
为pd.DataFrame.copy(deep=True)
。不确定这在将来是否会改变,但无论如何它都被认为是一个反模式。熊猫在这方面与 NumPy 不同。
我们现在可以看看用熊猫选择行和列的各种方法
那会打印
select one column uses the same base
select one column using [] does not use the same base
select one column with loc uses the same base
select columns with loc and slicing uses the same base
select columns with loc and fancy indexing does not use the same base
select rows using loc and a Boolean mask does not use the same base
select rows with loc and slicing uses the same base
chained indexing uses the same base
基本的索引和切片,比如使用(单个)方括号或.loc[]
访问器的简单列索引使用相同的基,而所有其他操作不使用。当有疑问时,上述计算实验框架可以给出快速答案。不幸的是,检查基数是否保持不变并不总是足以预测使用链式索引时会发生什么(见下文),但它提供了一个起点。在最后一次尝试中,基数保持不变,但是如果我们使用这种链式索引来设置值,原始数据帧仍然保持不变。然而,反过来似乎是正确的:如果基数改变了,那么我们就在拷贝上操作。我将欢迎对此的评论,因为我开始如履薄冰。请继续阅读。
现在让我们转向与熊猫有关的最后一个话题,著名的链式索引和相关的SettingWithCopyWarning
。使用先前定义的a_df
数据框架,我们可以通过使用布尔索引来尝试改变列中某些元素的值。假设我们使用链式索引,有两种方法可以想到
那会打印
attempt 1
c0 c1 c2 c3 c4
r0 5 2 8 6 6
r1 1 9 1 1 1
r2 0 7 6 3 7
r3 7 4 9 5 2
r4 5 8 3 7 1attempt 2
c0 c1 c2 c3 c4
r0 5 2 8 6 6
r1 1 9 1 -1 1
r2 0 7 6 -1 7
r3 7 4 9 5 2
r4 5 8 3 -1 1<ipython-input-789-06440868e65b>:5: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead
See the caveats in the documentation: [https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy](https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy)
a_df_demo.loc[msk]['c3'] = -1
评估顺序事项。第一次尝试给出了一个SettingWithCopyWarning
,这并不奇怪。使用带有布尔掩码的.loc[]
访问器创建一个副本。为副本的元素分配新值不会更改原始数据框。这是预期的行为,但是 pandas 更进一步,通知用户。NumPy 就没那么善良了。但是即使是熊猫,也不要总是依赖警告,因为它可能不会发布。例如
不给出任何警告,数据框也不会像上面打印的那样被修改
c0 c1 c2 c3 c4
r0 5 2 8 6 6
r1 1 9 1 1 1
r2 0 7 6 3 7
r3 7 4 9 5 2
r4 5 8 3 7 1
这些我们都要记住吗?答案是否定的,不仅因为事实上不可能列举所有不同的链式索引的可能性,还因为当涉及到发出SettingWithCopyWarning
时,不能确定不同的 pandas 版本中的行为是否保持相同。更糟糕的是,数据框可能会随着一个熊猫版本而改变,而不是另一个版本(我个人对此没有证据,但这是我的担心)。使用一个虚拟环境并建立一个需求文件不仅可以防止依赖性地狱,还可以防止这样的问题,尽管最佳实践是知道哪些熊猫任务是有风险的并避免它们。当数据帧具有层次索引和不同的数据类型时,情况甚至更加复杂。预测链式索引的结果是不安全的。
避免链式索引的正确方法是使用单个访问器来设置值,例如
打印修改后的数据帧,并且不发出警告。
这一切都是关于使用单个访问器吗?
使用单个访问器和避免使用赋值的链式索引绝对是可靠的建议,但是还有更多令人惊讶的地方。让我们创建一个数据帧的切片,并检查原始数据帧被修改后会发生什么情况。
第一次尝试做了三个这样的实验
实验的不同之处仅在于原始数据帧的修改是如何发生的。第一个实验只修改了列a
中的一个元素,第二个实验使用df.loc[0:,'a']
修改了整个列a
,第三个实验也修改了整个列a
,但是如果我们看看我们得到的结果,这次使用的是df.loc[:,'a'].
experiment 1
data buffer pointer (before) -> 2893435341184
data types (before) -> [dtype('int32')]
my_slice (before) -> [**1, 2, 3, 1, 2, 3]**
data buffer pointer (after) -> 2893435341184
data types (after) -> [dtype('int32')]
my_slice (after) -> **[-10, 2, 3, 1, 2, 3]**experiment 2
data buffer pointer (before) -> 2893490708496
data types (before) -> [dtype('int32')]
my_slice (before) -> **[1, 2, 3, 1, 2, 3]**
data buffer pointer (after) -> 2893490708496
data types (after) -> [dtype('int32')]
my_slice (after) -> **[-10, -10, -10, 1, 2, 3]**experiment 3
data buffer pointer (before) -> 2893435341184
data types (before) -> [dtype('int32')]
my_slice (before) -> **[1, 2, 3, 1, 2, 3]**
data buffer pointer (after) -> 2893491528672
data types (after) -> [dtype('int64'), dtype('int32')]
my_slice (after) -> **[1, 2, 3, 1, 2, 3]**
该切片以粗体显示,可以很容易地看到发生了什么。在成功修改数据帧之后,切片在前两个实验中被修改,而在第三个实验中没有被修改。如果您仔细观察,就会发现数据框的一个列类型被更改为int64
,并且它的数据缓冲区在内存中被重新定位。我假设这是因为我们改变了整个列a
的值,使其成为int64
。如果我们在创建数据框时显式设置数据类型,这似乎可以得到证实
那会打印
experiment 1
data buffer pointer (before) -> 2893491528672
data types (before) -> [dtype('int64')]
my_slice (before) -> [**1, 2, 3, 1, 2, 3]**
data buffer pointer (after) -> 2893491528672
data types (after) -> [dtype('int64')]
my_slice (after) -> **[-10, 2, 3, 1, 2, 3]**experiment 2
data buffer pointer (before) -> 2893486517968
data types (before) -> [dtype('int64')]
my_slice (before) -> **[1, 2, 3, 1, 2, 3]**
data buffer pointer (after) -> 2893486517968
data types (after) -> [dtype('int64')]
my_slice (after) -> **[-10, -10, -10, 1, 2, 3]**experiment 3
data buffer pointer (before) -> 2893491528672
data types (before) -> [dtype('int64')]
my_slice (before) -> **[1, 2, 3, 1, 2, 3]**
data buffer pointer (after) -> 2893491528672
data types (after) -> [dtype('int64')]
my_slice (after) -> **[-10, -10, -10, 1, 2, 3]**
我想留下一个挥之不去的片段是没有意义的,除非它是一个带有df.loc[1:3].copy()
的显式副本。否则,人们总是可以在需要时对数据帧进行切片,并且总是有新的数据。但是,这仍然是一个有效的计算实验,可以学习更多关于视图和副本的知识。
结论
理解 Python 何时创建副本和视图需要一些实践。Python 列表、NumPy 数组和 pandas 数据框提供了创建副本和视图的功能,如下表所示(作为 GitHub gist 为中等作者创建)
然而,最重要的带回家的信息与使用 NumPy 数组和 Pandas 数据帧时基于索引的赋值行为有关:
- NumPy 链式索引通常很容易理解:基本索引产生视图,而高级索引返回副本,防止在赋值时修改原始数组;当使用整形操作时,行为更加复杂
- 应该避免熊猫链式索引,而应该为所有赋值使用单个访问器;即使人们认为链式索引的行为是可以预测的,这也是事实
理解视图和副本至关重要,尤其是在处理大型数组和数据框时。希望这篇文章能为进一步阅读提供一个起点。我确信我错过了一些方面,很可能我误解了一些细微的差别。在你的评论中指出这些将再次证明阿尔伯特·爱因斯坦的引用是多么正确!
推荐进一步阅读
- 优秀的文章讲述了大熊猫无处不在的复制警告
- 关于副本和视图的官方数字文档
- 关于副本和视图的官方熊猫文档
视觉问答挑战
原文:https://towardsdatascience.com/visual-question-answering-challenge-51c4623ddfa5
一个稍微不同的 ML 竞赛和你为什么应该参加
视觉问题:你用什么击球?—图片来自 COCO 女士数据集(知识共享公共许可 )
简介
这篇文章介绍了一个有趣的 ML 比赛,现在可以在 CodaLab 上看到,你可以参加到今年年底(2022)。
起初,它看起来像一个标准的“Kaggle”风格的比赛,但让我告诉你为什么我觉得它特别有趣。显然,除了赢得一些奖金,你还可以在一个著名的关于搜索和数据挖掘的国际会议上展示成果( WSDM 2023 )。
什么是视觉问答(VQA)挑战赛?
这是一个结合了视觉图像和文字的挑战。对于每幅图像,给你一个自然语言问题,你的任务是检查图像的特征,并在物体周围画一个边界框,这是问题的答案。
看看下面的一些例子。第一张图片有一个自然语言问题*“人们用什么来切割”?图片中这个问题的正确答案是“剪刀”*。因此,带有剪刀的图像区域用边界框高亮显示。
图片来自可可女士数据集(知识共享公共许可 )
同样,在第二张图中,我们有一个问题*“我们用什么来支持免疫系统和获取维生素 C?”。*在这种情况下,通过粘合盒选择含有维生素 C 补充剂的包装。
为什么这次比赛与众不同
视觉问答挑战赛结合了视觉和自然语言输入数据,并要求理解和正确解释这两者。这种类型的任务对人类来说很容易,因为我们的大脑自然会这样做,但对于机器学习模型来说,这可能是一项非常具有挑战性的任务。
通常,这类任务的评估是通过使用交集/并集(IoU) 来完成的。在这个数据集上,平均而言,机器学习方法的评估 IoU 得分为 0.20,而人类的表现得分为 0.87。这是一个巨大的差距,尤其是在过去几年涉及计算机视觉和自然语言的多模态深度学习模型取得巨大进展之后,这是一个有趣的现象。
VQA 数据集
视觉问答挑战的数据集有 45,199 个图像实例,分为三个子集:
- 训练(38,990 个图像实例),
- 公开测试(1,705 个图像实例),
- 和私有测试(4,504 个图像实例)。
VQA 数据集中包含的所有图像都来自经许可在 CC 下发布的 MS COCO 数据集,额外的地面真相答案是使用 Toloka 众包平台获得的。
如何赢得视觉问答(VQA)挑战赛
我可能无法完全回答这个问题,但至少让我提出一个你可以采取的方向。
查看与竞赛一同发布的这一视觉问答挑战的基准模型。它使用两个基本模型。第一个,YOLOR 是一个计算机视觉模型,它进行对象检测并在图像中生成候选矩形。第二个是 CLIP,它是一个多模态模型,结合了计算机视觉和自然语言特征,用于测量视觉问题和每个候选矩形所包围的图像部分之间的相似性。basline 的代码可以在这个 GitHub 页面上找到。
以上是一个很好的起点,但在评估指标中仅存档了 0.2 IoU 分数。如前所述,人类在这个数据集上取得了很高的精确度(0.87 IoU),因此还有很大的改进空间。
总结
我希望这篇文章解释了为什么视觉问答挑战是一个有趣的比赛,激励你参加它,并给你一个如何开始的想法。
可以直接在 CodaLab 网站参与。我还感兴趣的是,你将如何使用不同于基线建议的方法来解决这个计算机视觉和自然语言的挑战。欢迎在评论中分享你的想法。
PS:我正在 Medium 和https://www.aboutdatablog.com/上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的 邮件列表 每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入。**
下面是一些你可能会喜欢的帖子
** **
基于神经符号人工智能的 DeepProbLog 视觉问答
基于视觉问答的神经符号人工智能与纯神经网络方法的比较
本文重点关注视觉问题回答,其中将具有知识库的神经符号人工智能方法与纯粹基于神经网络的方法进行了比较。从实验中可以看出,用于神经符号人工智能方法的框架 DeepProbLog 能够实现与基于纯神经网络的方法相同的准确性,而迭代次数几乎减少了 200 倍。显然,这种训练更有针对性,但也是有代价的。DeepProbLog 内部的代数运算符非常昂贵,因此实际的训练时间要慢得多。DeepProbLog 的另一个缺点是无法轻松实现加速,因为代数运算符只在 CPU 上工作(至少目前是这样),因此无法从 GPU 等加速器中受益。
图片由 Philipp Marquetand 来自 Pixabay
1.介绍
神经符号人工智能领域感兴趣的是在概率知识的鲁棒性和深度神经网络的众所周知的普及性和已证实的优势之间建立一座桥梁。DeepProbLog [1]通过利用神经网络(即系统 1,典型的潜意识任务,如视觉识别、语言处理……)的优势,以及基于规则的概率系统(即系统 2,缓慢、有序的思维,如证明的推导)[2]的优势,提供了这种能力。
本文详细阐述了一个需要使用这两个系统的应用程序,即视觉问答。需要系统 1 来理解被研究的图像,特别是它们的形状和颜色。另一方面,系统 2 将使用该提取的信息来导出对象的某些属性(例如,找到绿色对象的形状),或者甚至用于捕捉对象之间的关系(例如,计算图像中的圆的数量)。
2.文学
该应用程序侧重于视觉问题回答(VQA),其中存在巨大的数据集,以及非常复杂的方法。VQA 最著名的数据集是 CLEVR [3],它包含 10 万张图片和 100 万个问题。下面给出了一个示例图像,而示例问题是:
- 大型物体和金属球体的数量相等吗?
- 大球体左边的棕色金属物体的左边的圆柱体有多大?
- 有多少物体不是小圆柱体就是金属的东西?
图 CLEVR 数据集的样本图像[3]
显然,在回答这些问题时,系统 1 和系统 2 都被积极地使用。人们可能会想,在没有明确的系统 2 编码(即,基于规则的知识库)的情况下,神经网络是否能够独自回答这些问题。直觉上,如果知道世界上的某些事实,学习会进行得更快。从优化的角度来看,在这种设置中,预测期间产生的误差可以被精确地瞄准,这使得优化过程也更有针对性,因此更有效。最后,本文还为这些说法提供了证据,因为在第 4.1 小节中,VQA 实现与 DeepProbLog 之间的比较是用一种纯粹基于神经网络的方法进行的。
本文受 CLEVR 数据集的启发,但是使用了一个更加简化的版本。本质上,它几乎就像是一个分类数据集[4]。CLEVR 分类数据集包含如图 2 所示的图像,同时提出如下问题:
- 非关系型问题:物体的形状、水平或垂直位置。
- 关系问题:离被调查物体最近/最远的物体的形状,或者具有相同形状的物体的数量。
图 2:来自分类数据集的样本图像[4]
如前所述,这些类型的 VQA 需要系统 1 和系统 2。
A.桑托罗等人用本文中的 Sort-of-CLEVR 数据集进行了类似的实验,他们能够在 CNN 的关系问题上达到 63%的准确率。相比之下,CNN 的关系型和非关系型问题的准确率都达到了 94%,并辅以 RN。对他们来说,用一个关系模块,比如一个 RN,来扩充模型,结果足以克服解决关系问题的障碍[4]。他们是唯一在与本文相似的数据集上进行实验的研究人员,因此是唯一的参考点。
最后,由于这个应用程序使用 DeepProbLog,所以花了相当多的时间来消化 DeepProbLog 文章[1],以及理解代码库中提供的示例[5]。
3 方法
实施过程包括三个主要部分:
- 数据的生成
- 用纯 Python 代码链接数据和控制训练过程。
- 用 DeepProbLog 语句创建逻辑部分。
3.1 数据的生成
如第 2 节所述,本应用程序中使用的数据基于 Sort-of-CLEVR 数据集,并进行了额外的简化。假设逻辑部分将必须决定对象是否例如位于图像的左侧,则神经网络将必须向逻辑部分传送位置信息。因此,每个离散的位置必须由神经网络的可能结果编码。因此,对象可能只位于网格中的特定位置。在本文中,将讨论 2x2 和 6x6 网格上的结果。
用于创建分类数据集的数据生成器已经过修改,以便将对象定位在上述网格位置[4]。图 3 给出了一个生成图像的例子,与图 2 的区别在于网格布局。
图 3:这个应用程序使用的数据集的样本图像
每种指定的颜色都有一个位于网格中某处的对象,其形状可以是正方形或圆形。这些图像附有一个关于随机对象的问题,可以是以下内容之一:
- 非相关—这个对象的形状是什么(正方形或圆形)?
- 非相关—该对象是否位于图像的左侧
? - 非相关—该对象是否位于图像的底部
侧? - 相关——有多少物体与
这个物体具有相同的形状?
这些问题以矢量编码方式进行编码,然后与预期答案一起存储在 CSV 文件中。为了使训练过程更有效,已经预先生成了训练和测试数据集。
3.2 控制培训过程
整个训练过程是通过 DeepProbLog 的 Python API 以及 CNN 的一般 PyTorch 实现来控制的。首先,CNN 是用 PyTorch 定义的。使用相对简单的网络,其中输入作为 100 像素宽的正方形 RGB 图像给出,由 CNN 转换成 6x6 网格的 72 个输出特征(对于 2x2 网格的例子,需要 8 个输出特征)。图像中出现的每种颜色都有其伴随的 CNN 网络,因此 72 个输出特征用该颜色编码了物体的可能位置,以及它们的形状,可以是正方形或圆形(6 6 2 = 72)。
在开始训练过程之前,最后一件事(除了逻辑规则编码之外)是数据加载器。这里最具挑战性的部分是从生成的数据到特定查询映射及其结果的转换。
3.3 逻辑规则编码
一旦属于特定颜色的 CNN 已经确定了该对象的位置和形状,逻辑规则就可以推断出该对象是否位于图像的左侧、底部,以及有多少对象具有相同的形状。逻辑规则程序可以在相关的 Github 中找到,链接在本文的底部。显示了一个示例片段:
4 次实验
本文的主要重点是概述使用神经符号人工智能方法(由 DeepProbLog 框架提供)的优势,而不是纯粹的基于神经网络的方法。因此,必须实施基于神经网络的方法。这里不会列出所有细节,但主要思想是图像必须与问题融合,然后才能做出预测。该网络的一般结构如图 4 所示。
图 4:纯神经网络架构的抽象表示[6]
4.1 与纯系统 1 方法的比较
- 实验— 2x2 网格:
图 5 和图 6 分别显示了 DeepProbLog 方法和纯神经网络方法的损耗曲线。
图 5: DeepProbLog 2x2:损耗曲线
图 6:基于纯神经网络的方法 2x2:损耗曲线
一个非常重要的注释是“迭代次数”和“纪元次数”之间的区别。迭代次数意味着一批(大小为 32)的向前和向后传递的次数,而时期数表示训练集的所有图像向前和向后传递的次数。在该应用中,使用了 10 000 的训练大小,因此一个时期由 312.5 次迭代组成。
从损失曲线可以清楚地看出,这两种方法似乎都达到了 100%的精度。然而,DeepProbLog 只需要大约 40 次迭代,而基于纯神经网络的方法至少需要 7 800 次迭代。这再次证明了神经符号人工智能的价值。
另一方面,我们必须考虑这些迭代的实际运行时间。DeepProbLog 需要大约 10 分钟来完成它的 160 次迭代,而纯粹基于神经网络的方法只需要大约 5 分钟来完成 7 800 次迭代。考虑到纯粹基于神经网络的方法可以被大规模加速(通过使用 GPU),而 DeepProbLog 不能,很明显 DeepProbLog 训练更有针对性,但计算量极大(至少目前如此)。
请注意,DeepProbLog 提供了将 CNN 发送到 GPU 以进行更快推断的能力,但是,DeepProbLog 的算术运算符(即半环)在 CPU 上工作。这些算术运算符拥有最高的计算成本。
- 实验— 6x6 网格:
DeepProbLog 的 6x6 实验和基于神经网络的方法的损耗曲线分别如图 7 和图 8 所示。
图 7: DeepProbLog 6x6:损耗曲线
图 8:基于纯神经网络的方法 6x6:损耗曲线
在这些实验中,基于纯神经网络的方法的训练时间为 20 分钟(为了公平的速度估计,已经在 CPU 上进行了训练),而 DeepProbLog 方法的训练时间略低于八小时。
然而,应该提到的是,每次大约有一半的时间花费在计算准确性上,因为整个测试集必须通过网络转发,而对于训练迭代来说,这只是一个批次。
这里最重要的观察是,纯神经网络方法很快过拟合,并且也实现了相当低的准确度(DeepProbLog 的 68%而不是 75%)。另一个重要的注意事项是,这两种方法显然不再能够达到 100%的准确度。然而,如果 DeepProbLog 网络可以训练更长时间,它将能够进一步收敛。
在图 9 和图 10 中,描述了混淆矩阵,以显示典型错误发生的位置。
图 9:来自 DeepProbLog 的 6x6 数据集的混淆矩阵
图 10:来自纯神经网络方法的 6x6 数据集的混淆矩阵
DeepProbLog 自然不会犯任何“无意义”的错误,比如对“正在研究的物体是什么形状?”这个问题回答“是”,因为可能的正确答案已编码在程序中。然而,基于纯神经网络的方法已经很好地学会了将问题的可能答案联系起来。
另一个观察结果是,DeepProbLog 在回答“正在研究的物体是什么形状?”这个问题时要好得多以及“物体是位于图像的左侧(还是底部)?”比纯粹的神经网络方法更有效。这是有意义的,因为一旦给定对象的位置(和形状)被确定,DeepProbLog 可以使用其知识库来导出这些属性。a .桑托罗等人[4]也观察到,基于纯神经网络的方法很难区分这些情况,在这些问题上,他们采用基于纯神经网络的方法实现了 63%的准确率。DeepProbLog 无法实现这些研究人员通过添加 RN 模块在所有问题上实现的 94%的准确性,这可能是由于代数运算符的固有培训成本,以及由于资源较少、超参数调整较少和缺少 RN 模块等许多其他未知变量。
关于关系问题:“有多少物体与被研究的物体具有相同的形状?”这两种方法都存在更多的混乱。DeepProbLog 通常能够接近正确的答案,但可能会犯一些**“一个接一个”的错误**(即一个 CNN 错误地预测了其对象的形状)。从这个角度来看,基于纯神经网络的方法也稍差一些。还有一种可能是,在这种方法中,神经网络可能已经观察到,由于概率原因,可能有三个或四个相同的对象(包括正在研究的对象),因此更喜欢这样的答案。
5 个结论
神经符号人工智能领域的优势已经在视觉问题回答的背景下得到证明。通过使用 DeepProbLog,为神经符号人工智能任务选择的框架,很明显,几乎需要 200 倍的迭代来实现与纯粹基于神经网络的方法相同的准确性。然而,由于昂贵的代数运算符,与纯粹基于神经网络的方法相比,DeepProbLog 方法的总训练时间要慢得多**。**
值得注意的是,DeepProbLog 在非关系问题上表现得更好。这是因为知识库可以更准确地推导出这些属性,而基于纯神经网络的方法在这种推导上有更多的困难。
因此,尽管代数运算代价高昂,但神经符号人工智能方法仍有很多价值。特别是对于知识库非常大的任务,这些方法可以决定是否能够学习某项任务。随着时间的推移,这些代数运算符的加速可能会得到发展,这为更多的应用开辟了道路。
参考
- [1] R. Manhaeve,A. Kimmig,s . duman ci,T. Demeester,L. De Raedt,“Deepproblog: 神经概率逻辑编程”,载于《神经信息处理系统进展,2018-Decem 卷,第 3749-3759 页,2018 年 7 月,doi:10.48550/arxiv.1907.08194,URL:https://arxiv.org/abs/1907.08194v2【T10
- [2] D .卡尼曼,《思考,快与慢》,企鹅出版社,伦敦,2012 年。
- [3] J. Johnson,l .飞飞,B. Hariharan,C. L. Zitnick,L. Van Der Maaten,R. Girshick,“CLEVR:一个用于组合语言和基本视觉推理的诊断数据集”,会议录-第 30 届 IEEE 计算机视觉和模式识别会议,CVPR 2017 年,第 2017 卷-Janua,第 1988-1997 页,2017 年,doi:10.1109/cvpr . 2017 . 2017 . 2017 . 2017
- [4] K. Heecheol,“kimhc 6028/Relational-Networks:py torch 实现“用于关系推理的简单神经网络模块”(Relational Networks) ,URL:【https://github.com/kimhc6028/relational-networks】,许可证:BSD 3-Clause
- [5] R. Manhaeve,“ML-KULeuven/Deepproblog:DeepProbLog 是 ProbLog 的扩展,通过引入神经谓词将概率逻辑编程与深度学习相结合。”,网址:https://github.com/ML-KULeuven/deepproblog,许可证:Apache 2.0
【6】c . Theodoropoulos,“信息检索与搜索引擎[H02C8b]项目视觉问答”,托莱多,第 1–5 页,2022。
除非另有说明,所有图片均为作者所有。
属于这篇文章的代码可以在这里找到。
SQL 连接的直观解释
原文:https://towardsdatascience.com/visual-sql-joins-4e3899d9d46c
用维恩图和实际例子理解 SQL 连接
考虑到单个表可以被视为一个集合,维恩图是可视化 SQL 连接如何工作的一个很好的方式。尽管可能有许多不同的方式来描述连接,但我坚信维恩图有助于读者以清晰一致的方式理解在两个表之间执行特定类型的连接时,哪些记录将包含在返回的结果行集中。
在本文中,我们将展示以下连接类型的可视化表示
- 左连接(也称为左外连接)
- 右联接(也称为右外联接)
- 内部连接
- 完全外部连接
- 左反连接(也称为左排除连接)
- 右反联接(也称为右排除联接)
- 完全反联接
- 交叉连接
此外,我们还将提供用于在 SQL 中执行上述连接的实际语法。我们开始吧!
首先,让我们创建两个表,我们将在随后的部分中引用这两个表,以便使用实际的例子来演示各种 SQL 连接类型。
我们第一个名为users
的表有 3 列和 4 条记录:
+----+--------+-----+
| id | name | age |
+----+--------+-----+
| 1 | John | 21 |
| 2 | Helen | 22 |
| 3 | Andrew | 31 |
| 4 | Bob | 19 |
+----+--------+-----+
名为orders
的第二个表有 3 列和 5 条记录:
+----+---------+--------+
| id | user_id | amount |
+----+---------+--------+
| 1 | 1 | 7.99 |
| 2 | 4 | 10.49 |
| 3 | 2 | 45.89 |
| 4 | 2 | 19.99 |
| 5 | 1 | 54.89 |
+----+------------------+
左连接(也称为左外连接)
我们将展示的第一种连接类型是左连接(或左外连接)。这种类型的联接将导致从左表中提取的所有记录,以及从右表中提取的那些在指定的联接列上与左表具有匹配值的记录。结果中包含的右表记录中的任何缺失值都将被替换为null
。对于左表和右表之间的每个匹配值,都将返回一条新记录。例如,如果左边表中的一个键与右边表中的两个记录匹配,那么两个记录将作为结果的一部分返回。
维恩图,说明在 SQL 中的两个表之间执行左连接(或左外连接)时选择的记录—来源:作者
在 SQL 中,可以使用下面列出的语法启动左连接:
SELECT *
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id;
结果记录将包括:
+----+--------+-----+----+---------+--------+
| id | name | age | id | user_id | amount |
+----+--------+-----+----+---------+--------+
| 1 | John | 21 | 1 | 1 | 7.99 |
| 4 | Bob | 19 | 2 | 4 | 10.49 |
| 2 | Helen | 22 | 3 | 2 | 45.89 |
| 2 | Helen | 22 | 4 | 2 | 19.99 |
| 1 | John | 21 | 5 | 1 | 54.89 |
| 3 | Andrew | 31 |null| null | null |
+----+--------+-----+----+---------+--------+
右连接(也称为右外连接)
我们将展示的第二种连接类型是右连接(或右外连接)。这种类型的联接将导致从右表中提取的所有记录,以及从左表中提取的那些在指定的联接列上与右表具有匹配值的记录。
维恩图,说明在 SQL 中的两个表之间执行右连接(或右外连接)时选择的记录—来源:作者
右连接的 SQL 语法概述如下:
SELECT *
FROM users u
RIGHT JOIN orders o
ON u.id = o.user_id;
在我们的示例表中,由上述查询推断出的记录共享如下:
+----+--------+-----+----+---------+--------+
| id | name | age | id | user_id | amount |
+----+--------+-----+----+---------+--------+
| 1 | John | 21 | 1 | 1 | 7.99 |
| 4 | Bob | 19 | 2 | 4 | 10.49 |
| 2 | Helen | 22 | 3 | 2 | 45.89 |
| 2 | Helen | 22 | 4 | 2 | 19.99 |
| 1 | John | 21 | 5 | 1 | 54.89 |
+----+--------+-----+----+---------+--------+
内部连接
两个表之间的内部联接将产生一组在指定联接列中具有共同值的记录。
维恩图说明了在 SQL 中的两个表之间执行内部连接时选择的记录—来源:作者
要执行内部联接,只需运行以下查询:
SELECT *
FROM users u
INNER JOIN orders o
ON u.id = o.user_id;
注意,您甚至可以省略INNER
关键字,换句话说,上面的查询等价于下面列出的查询:
SELECT *
FROM users u
JOIN orders o
ON u.id = o.user_id;
对于我们的示例表,从上述查询返回的记录共享如下:
+----+--------+-----+----+---------+--------+
| id | name | age | id | user_id | amount |
+----+--------+-----+----+---------+--------+
| 1 | John | 21 | 1 | 1 | 7.99 |
| 4 | Bob | 19 | 2 | 4 | 10.49 |
| 2 | Helen | 22 | 3 | 2 | 45.89 |
| 2 | Helen | 22 | 4 | 2 | 19.99 |
| 1 | John | 21 | 5 | 1 | 54.89 |
+----+--------+-----+----+---------+--------+
完全外部连接
SQL 中的一个完整的外部连接基本上包括左表和右表中的所有记录。这种类型的连接将使用两个表中的键—对于任何缺失的行,将插入null
值。
维恩图说明了在 SQL 中的两个表之间执行完全外部连接时选择的记录—来源:作者
完整外部联接的语法概述如下:
SELECT *
FROM users u
FULL OUTER JOIN orders o
ON u.id = o.user_id;
而结果记录将是
+----+--------+-----+----+---------+--------+
| id | name | age | id | user_id | amount |
+----+--------+-----+----+---------+--------+
| 1 | John | 21 | 1 | 1 | 7.99 |
| 4 | Bob | 19 | 2 | 4 | 10.49 |
| 2 | Helen | 22 | 3 | 2 | 45.89 |
| 2 | Helen | 22 | 4 | 2 | 19.99 |
| 1 | John | 21 | 5 | 1 | 54.89 |
| 3 | Andrew | 31 |null| null | null |
+----+--------+-----+----+---------+--------+
左反连接(又名左排除连接)
左反连接将包含左表中连接键没有出现在右表中的所有记录。
维恩图说明了在 SQL 中的两个表之间执行左反连接(或左排除连接)时选择的记录—来源:作者
换句话说,左反联接将返回所有(还)没有下单的客户。在 SQL 中,这被翻译为:
SELECT *
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id
WHERE o.user_id is null;
结果记录概述如下:
+----+--------+-----+----+---------+--------+
| id | name | age | id | user_id | amount |
+----+--------+-----+----+---------+--------+
| 3 | Andrew | 31 |null| null | null |
+----+--------+-----+----+---------+--------+
右反联接(又名右排除联接)
类似地,右反连接将包含右表中键不出现在左框架中的所有记录。
维恩图说明了在 SQL 中的两个表之间执行右反联接(或右排除联接)时选择的记录—来源:作者
SELECT *
FROM users u
RIGHT JOIN orders o
ON u.id = o.user_id
WHERE u.id is null;
注意,在我们的示例表中,这个查询不会产生任何记录,因为所有下订单的用户(即包含在orders
表中的用户)都出现在左边的表中(即users
)。
完全反联接
完整的反连接将包含左表和右表中没有任何公共键的所有记录。
维恩图说明了在 SQL 中的两个表之间执行完全反连接时选择的记录—来源:作者
SELECT *
FROM users u
JOIN orders o
ON u.id = o.user_id
WHERE u.id is null and o.user_id is null;
交叉连接
现在,交叉连接不能用维恩图来可视化,因为它本质上是要在所涉及的每个表的记录之间创建所有可能的组合。因此,甚至不需要提供连接键(即 SQL 中的ON
关键字)。
SELECT *
FROM users u
CROSS JOIN orders o;
假设左边的表包含 4 条记录,而右边的表包含 5 条记录,上面的查询将返回 20 条结果记录。
最后的想法
维恩图是一种构造,当在两个不同的表之间执行特定的连接时,它通常用于可视化结果记录集。在今天的教程中,我们介绍了 SQL 中可用的大多数(如果不是全部)连接类型,用适当的图表显示了它们,并分享了相应的查询语法。
掌握 SQL 连接对于处理数据的专业人员来说非常重要,因为它使他们能够将来自许多不同来源的记录汇集在一起,从而带来商业价值并支持决策制定。
成为会员 阅读媒体上的每一个故事。你的会员费直接支持我和你看的其他作家。你也可以在媒体上看到所有的故事。
https://gmyrianthous.medium.com/membership
相关文章你可能也喜欢
</2-rules-groupby-sql-6ff20b22fd2c>
用 Python 可视化世界二氧化碳排放量
用 Python 展示全球二氧化碳排放的严峻现实
作者图片
介绍
出于某种原因,气候变化最近经常出现在新闻中。特别是我们的汽车、工厂、船只、飞机(仅举几例)排放的二氧化碳与通过温室效应导致的地球变暖之间的联系。上图是 2018 年全球短周期二氧化碳排放量。除了看起来很棒,几乎是艺术化的,它还提供了有用的背景信息,让我们了解世界上的碳排放到底来自哪里。这张地图清楚地显示,北美、欧洲、中国和印度是全球主要的排放源。放大不同的区域会显示大量有趣的特征,在北美和欧洲,高亮显示主要城市的亮区,所有亮区都与对应于主要道路的亮区相关联。在海上,可以找到主要的航线,例如中国-新加坡-马六甲海峡-苏伊士运河是一条特别明亮的线。还有一系列与主要航线相对应的曲线,特别是在北美和欧洲之间。人口密度可以用来解释这张地图,但是也有一些明显的例外。例如,南美洲的部分地区比预期的要亮,而西非可能比预期的要暗一些。相比之下,居住着埃及 95%人口的尼罗河却像圣诞树一样被点亮。也就是说,需要注意的是,这些地图纯粹是定性的,而不是定量的,所以要小心从中得出什么结论。
那么我们如何着手绘制地图呢?第一步是获取数据。数据可从 EDGARv6.0 网站(链接)获得,引用克里帕等人(2021) ( 链接),只要使用了适当的引用,就可以下载和使用。有许多数据集可供选择,我将在以后的文章中探讨其中的一些。在本文中,我们将研究 2018 年的短周期二氧化碳排放(遗憾的是,2018 年是最近的数据集)。值得注意的一点是,该数据集包括所有化石二氧化碳来源的排放,如化石燃料燃烧、非金属矿物加工(如水泥生产)、金属(黑色和有色金属)生产过程、尿素生产、农业石灰处理和溶剂使用。不包括大规模生物质燃烧,包括稀树草原燃烧、森林火灾以及土地利用、土地利用的变化和林业的源和汇。他们方法的完整描述可以在这里找到。
所以下载数据,并把它存储在你想执行代码的任何地方。我已经将数据读入到一个pandas.DataFrame
中,并打印出结果DataFrame
来看看我们在处理什么。数据非常简单,包括一个特定纬度/经度对的排放值,单位为吨/年。
数据探索
调查污染最严重的地区也很有趣。
也许并不令人惊讶的是,虽然也有一些来自印度、俄罗斯和台湾的显著贡献,但大多数都在中国。
绘制数据显示,数据没有覆盖整个世界,太平洋和南大洋的部分数据缺失。这并不奇怪,因为这些海洋非常广阔,而且通常没有人类的影响。我刚刚在这里使用了一个散点图来表示纬度和经度值。关于接下来的剧情需要注意一些事情。这些点被设置为 0.05,因为如果保留默认值 1,它们会重叠。分散点的边缘独立于点本身,不能小于 1。所以这些必须关掉。同样需要注意的是,在绘制纬度和经度值时,纬度是 y,经度是 x。
数据中纬度和经度值的分布。作者图片
根据发射值给点着色是一种很好的方式来了解数据的实际情况。不幸的是,该图以低排放值为主。
由发射值着色的纬度和经度值。作者图片
研究数据(为简洁起见,我在很大程度上省略了这些数据)显示,主要是每年 0-1000 吨二氧化碳的数值,还有一小部分数值高出几个数量级。
考虑到这一点,我们将绘制对数标度的值。如果可能的话,我尽量避免使用对数图,因为由此产生的图可能很难解释,而且数据通常没有意义。但是,当一小部分值的数量级大于大多数值时,以及在试图显示乘法因子的图中,对数图是很好的选择。我们的数据符合这两个类别,因此在这种情况下是合适的。对数图还有一个上下文原因。我们想知道二氧化碳的排放来自哪里,如果我们使用上面的图表,我们会得出这样的结论:排放量在世界范围内是均匀分布的。我们知道伦敦的排放量可能高于大西洋中部,所以我们需要一种方法来区分少量的高值和大量的低值。
绘制数据
Matplotlib
有一个内置的对数标尺,用于下面的区块。
以对数标度绘制的排放数据。作者图片
我们所知的世界现在开始展现自己,所以现在是时候让它看起来更漂亮了。虽然绿色在科学上是完美的色彩图(【https://www.youtube.com/watch?v=xAoljeRJ3lU】),但我想要一些火热的东西来展示二氧化碳的排放,因为它们导致了全球变暖,这是潜在的火热。因此,我们将它切换到 AFM hot _ r(r 表示颜色映射反转,将低值映射到浅色,将大值映射到深色)。我把它反过来了,因为背景是白色的,我们想突出大的排放值。
使用 afmhot_r 颜色图以对数标度绘制的排放数据。作者图片
地图开始成形了,现在开始投影。有许多地理投影,在开幕图像中显示的一个被称为罗宾逊投影,虽然有争议,但它通常被认为是最现实的。到目前为止,我们一直依赖于用标准散点图(模糊地对应于墨卡托投影)来绘制值。这在技术上是好的,但是如果我们想要将排放数据正确地映射到地理投影上,这就不合适了。因此,纬度/经度值需要转换成shapely
点,然后可以转换成我们想要的投影。
然后可以用subplots
函数中的一个名为cartopy
的库重新投影数据。随意应用你想要的任何样式改变,例如我喜欢黑暗模式,所以我把背景改为黑色,并翻转颜色图,这样大的值会更亮。
黑暗模式下的二氧化碳排放!作者图片
与原始研究相比如何?
最初绘制这张地图时,我对上面显示的地图非常满意,但我认为最好通过查看原始出版商的地图来检查它应该是什么样子。下图是由原始出版商制作的。出于我无法确定的原因,他们选择了一个奇怪的色阶值系列,即 0.0、0.06、6、60、600、3000、6000、24000、45000、120000。除了缺乏解释,我认为复制这个图会很有趣,因为这些是气候专家,可能比我知道得更多,因此可能有一个很好的理由。
EDGARv6.0 网站(链接)到克里帕等人(2021) ( 链接)。作者图片
像以前一样,我们将使用 afmhot 作为我们的颜色图,但这次我们将它映射到 10 种颜色,并将这 10 种颜色归一化为上图中的 10 个值(0.0,0.06,6,60,600,3000,6000,24000,45000,120000)。默认情况下,在matplotlib
中使用色图的对象将色图中的颜色从数据集中的最小值线性映射到数据集中的最大值,间隔由色图中的颜色数量决定。BoundaryNorm
类允许你将颜色图中的颜色映射到一组自定义值。在下面的代码中,我们创建了一个有 10 种颜色的颜色图和一个BoundaryNorm
对象,它将根据预定义的级别把数据中的值映射到颜色图。
图解说明如何将发射映射到颜色。图片作者。
上面的图像给出了正在发生的事情的大致形象。颜色图由 10 种颜色生成,我们的排放数据集中的值将根据上图中的值进行着色。例如,等于或大于每年 120000 吨二氧化碳的数值将被涂成白色。
现在,有了新的色图和标准化的边界,我们可以重新绘制地图,这次以一种更能让人想起原作者所做的方式将值映射到颜色。
世界二氧化碳排放量!作者图片
最后要做的是提供一个 colourbar,这样我们的读者就可以理解他们实际上在看什么。我有时并不担心这一步,但思考一下还是很有用的。
瞧啊。作者图片
结论
我们看到了,一张漂亮的地图显示了世界二氧化碳排放的来源。这是计划展示如何使地理空间数据看起来很棒的许多文章中的第一篇,请订阅以便您不会错过它们。我也喜欢反馈,所以请让我知道你会如何做不同或建议改变,使它看起来更棒。我每周都会在我的推特账户上发布数据可视化,看看地理空间数据可视化是不是你的菜https://twitter.com/PythonMaps
参考文献
m .克里帕、d .吉扎尔迪、Schaaf、e .索拉索、e .芒特恩、m .蒙弗蒂-费拉里奥、f .奥利维尔、J.G.J .、维尼亚蒂:世界所有国家的化石二氧化碳和 GHG 排放量——2021 年报告,编写中。
m .、Solazzo、e .、Huang、Guizzardi、d .、Koffi、e .、Muntean、m .、Schieberle、c .、Friedrich、r .和-Maenhout、g .:全球大气研究排放数据库中的高分辨率时间廓线。Sci 数据 7,121 (2020)。doi:10.1038/s 41597–020–0462–2。
国际能源机构(2019)世界能源平衡,www.iea.org/data-and-statistics,版权所有,由欧盟委员会联合研究中心修改。
Jalkanen,J. P .,Johansson,l .,Kukkonen,j .,Brink,a .,Kalli,j .,& Stipa,T. (2012 年)。颗粒物和一氧化碳船舶交通废气排放评估模式的扩展。大气化学和物理学,12(5),2641–2659。doi:10.5194/ACP-12–2641–2012
Johansson,l .,Jalkanen,J.-P .,& Kukkonen,J. (2017)。基于高时空分辨率的 2015 年全球航运排放评估。大气环境,167,403–415。doi:10.1016/j . Atmos env . 2017 . 08 . 042
用螺旋线可视化时间序列数据
原文:https://towardsdatascience.com/visualising-time-series-data-with-spirals-efe0cd9f4c5
使用 Matplotlib 和 Python 绘制气候螺旋图
全球气温变化-作者图片
作为数据科学家,我工作的一部分是处理不同种类的时间序列数据。试图以对用户有意义的方式可视化数据中的模式和变化可能是一个相当大的挑战。因此,当我最近看到一个显示 1850 年至 2021 年全球气温变化的动画螺旋时,我认为这是一个以有影响力的方式显示时间序列数据的好方法:
https://www.climate-lab-book.ac.uk/spirals/
虽然它展示的东西很吓人,但我真的很喜欢这个动画本身。当我跟踪所有链接时,我发现它是用 Javascript 实现的。我想我会尝试做同样的事情,但是使用 Python 和 Matplotlib。好消息是我能够做到,我会告诉你怎么做。我还在我的 GitHub 上提供了源代码——查看文章末尾的链接。
画一个圆
在这个解决方案的不同部分,我们需要能够画一个圆。特别是,这个圆圈需要分成 12 个部分——一年中的每个月一个部分。为了做到这一点,我们将使用一些标准的三角。
单位圆有一些我们将要用到的很好的性质。它被定义为半径为 1 的圆。这意味着一些三角方程变得更容易计算,因为半径部分实际上消失了。
单位圆—作者图片
如果我们想知道圆上一点的 x 和 y 的值,这将由 x =半径 cos(θ)* 和 y =半径 sin(θ)* 给出。因为这是单位圆,所以变成了 x = cos(θ) 和 y = sin(θ)。
还有一些我们应该注意的事情。θ为零时,它将指向 3 月(如果这是一个钟面,则指向 3:00)。随着角度的增加,月份名称会向后移动。为此,我创建了一个查找表(因为只有 12 个值)来将月份索引映射到相应的段索引。
创建 12 个分段
为了创建 12 个分段,我创建了一个函数, segment_circle :
气候数据
使用的数据是由英国气象局哈德利中心提供的数据集。这是相对于 1961-1990 参考期(从 1850 年到 2022 年)的全球历史地表温度异常的网格化数据集。它有大量的数据。
HadCRUT.5.0.1.0 数据于 2022 年 3 月 28 日从http://www.metoffice.gov.uk/hadobs/hadcrut5获得,由英国皇家版权所有,Met Office 2022,在开放政府许可下提供,http://www . national archives . gov . uk/doc/Open-Government-License/version/3/
气候螺旋图
以下是 Matplotlib 中所有数据的螺旋图:
全球气温变化-作者图片
制作视频
为了创建一个绘制所有点的视频,我使用了 ffmpeg 。将它下载并安装到您的计算机上,并确保它在您的路径上(这样您就可以在命令行上执行它)。FFmpeg 可以接受来自 stdin 的输入,这意味着您可以通过管道将视频帧传输给它,它们将在视频中累积。这些年来,我发现这是一种用 matplotlib 创建视频的有用方法。
为了创建我们的视频,我们首先用 matplotlib 创建一个时间点的帧,然后保存到 stdin。按照这个过程,我们循环所有的时间点,创造了气候螺旋。
气候螺旋视频——作者视频
后续步骤
- 实现螺旋的 3D 版本。这应该很容易做到,因为您只需在 3D 中绘制点,并使用时间作为 Z 轴。
- 实现了这个螺旋之后,我认为这实际上是一个可视化某些时间序列数据的好方法。我将寻找机会在我的时间序列数据工作中使用它。
资源
- 单位圈百科文章:https://en.wikipedia.org/wiki/Unit_circle
- FFMPEG:https://ffmpeg.org/
- 源代码:https://github.com/ianormy/ClimateSpiralMatplotlib
- HadCRUT.5.0.1.0 数据于 2022 年 3 月 28 日从http://www.metoffice.gov.uk/hadobs/hadcrut5获得,由英国皇家版权所有,Met Office 2022,在开放政府许可下提供,http://www . national archives . gov . uk/doc/Open-Government-License/version/3/
- 气候螺旋:【https://www.climate-lab-book.ac.uk/spirals/
PyTorch 中神经网络的可视化
原文:https://towardsdatascience.com/visualization-for-neural-network-in-pytorch-7658cfd32149
Python 可视化工具包
照片由 Unsplash 上的 Aziz Acharki 拍摄
PyTorch 是一个基于 Python 的 Torch 库的开源 ML 框架。基本用于 NLP、计算机视觉等应用。它是由脸书开发的,并且是开源的。它是继 Tensorflow 和 Keras 之后使用最多的框架之一。PyTorch 和其他框架的主要区别在于 PyTorch 以更 Pythonic 化的方式工作,即面向对象的方法。
一旦使用 PyTorch 创建了一个模型,我们就可以使用 FlashTorch 创建不同的可视化效果。是为了解决理解神经网络如何工作的问题。它有助于使用不同的特征可视化技术(如显著图和激活最大化)来理解模型行为。
在本文中,我们将探索使用 FlashTorch 的不同类型的可视化,我们还将探索如何使用 FlashTorch 工具进行图像转换。
让我们开始吧…
安装所需的库
我们将从使用 pip 安装闪光灯开始。下面给出的命令可以做到这一点。
pip install flashtorch
导入所需的库
在这一步中,我们将导入所需的库来加载预训练的 PyTorch 模型并创建可视化。
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
from flashtorch.utils import(load_image, apply_transforms, denormalize, format_for_plotting)
正在加载图像
接下来,我们将加载将在本文中使用的图像。你可以使用任何你想要的图像
image = load_image('Image.jpg')
print(type(image))
print(image**.**mode)
图像属性(来源:作者)
可视化原始图像
在这一步中,我们将形象化我们将在本文中使用的图像。
plt.imshow(image)
plt.title('Original image');
原始图片(来源:作者)
转换图像
在这一步中,我们将加载预训练的模型来变换图像。我们正在使用 alexnet 模型,我们将使用 flash torch 的应用变换功能。
model = models.alexnet(pretrained=True)
backprop = Backprop(model)
input_ = apply_transforms(image)
target_class = 24
backprop.visualize(input_, target_class, guided=True, use_gpu=True)
转换(来源:作者)
在这里你可以清楚地看到转换后的图像,我们有最大梯度,RGB 通道和叠加。我们可以清楚地分析不同的图像及其差异。
继续尝试不同的图像,并转换它们。如果您发现任何困难,请在回复部分告诉我。
本文是与皮尤什·英加尔合作完成的。
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github简介针对不同的数据科学项目和包教程。还有,随意探索 我的简介 ,阅读我写过的与数据科学相关的不同文章。
Python 中的可视化:寻找点与点之间的路径
原文:https://towardsdatascience.com/visualization-in-python-finding-routes-between-points-2d97d4881996
了解如何对您的位置进行地理编码,并根据距离、出行模式和时间计算两点之间的最短路径
蒂莫·维林克在 Unsplash 上的照片
在我的上一篇文章中,我谈到了使用 leav 库可视化您的地理空间数据。
开发人员经常必须使用地理空间数据执行的另一项任务是绘制各种兴趣点之间的路由路径。因此,在这篇文章中,我将向您展示如何:
- 对您的位置进行地理编码
- 查找两个位置之间的最短距离
安装 OSMnx
布线的第一步是安装 OSMnx 包。
OSMnx 是一个 Python 包,允许您从 OpenStreetMap 下载地理空间数据,并对现实世界的街道网络和任何其他地理空间几何图形进行建模、投影、可视化和分析。
安装 OSMnx 包有点棘手——在执行通常的pip/conda install
之前,您必须使用以下步骤:
$ **conda config --prepend channels conda-forge**
$ conda install osmnx
注意:如果
conda install osmnx
失效,使用pip install osmnx
一旦执行了上述步骤,OSMnx 现在应该可以使用了。
寻找最短的路线
您现在可以使用 OSMnx 包和 NetworkX 包来查找两点之间的路线。
NetworkX 是一个 Python 包,用于创建、操作和研究复杂网络的结构、动态和功能。
以下代码片段查找旧金山两个地点之间的最短步行距离:
以下代码已更新,可使用最新版本的 OSMnx (1.3.0)
import osmnx as ox
import networkx as nxox.settings.log_console=True
ox.settings.use_cache=True**# define the start and end locations in latlng**
start_latlng = (37.78497,-122.43327)
end_latlng = (37.78071,-122.41445)**# location where you want to find your route**
place = 'San Francisco, California, United States'**# find shortest route based on the mode of travel**
mode = 'walk' # 'drive', 'bike', 'walk'**# find shortest path based on distance or time**
optimizer = 'time' # 'length','time'**# create graph from OSM within the boundaries of some
# geocodable place(s)**
graph = ox.graph_from_place(place, network_type = mode)**# find the nearest node to the start location**
orig_node = ox.distance.nearest_nodes(graph, start_latlng[1],
start_latlng[0])**# find the nearest node to the end location**
dest_node = ox.distance.nearest_nodes(graph, end_latlng[1],
end_latlng[0])**# find the shortest path**
shortest_route = nx.shortest_path(graph,
orig_node,
dest_node,
weight=optimizer)
寻找最短路径的默认方法是“
dijkstra
”。您可以通过将shortest_path()
功能的method
参数设置为“bellman-ford
”来进行更改。
shortest_route
变量现在保存了在最短的时间内从一点走到另一点的路径的集合:
[5287124093,
65314192,
258759765,
65314189,
5429032435,
65303568,
65292734,
65303566,
2220968863,
4014319583,
65303561,
65303560,
4759501665,
65303559,
258758548,
4759501667,
65303556,
65303554,
65281835,
65303553,
65303552,
65314163,
65334128,
65317951,
65333826,
65362158,
65362154,
5429620634,
65308268,
4064226224,
7240837048,
65352325,
7240837026,
7240837027]
绘制路径
显然,拥有一个路径列表本身并没有多大用处。解释结果的更有意义的方法是使用plot_route_folium()
函数绘制路径:
shortest_route_map = ox.**plot_route_folium**(graph, shortest_route)
shortest_route_map
plot_route_folium()
函数返回一个树叶地图(folium.folium.Map
)。当显示在 Jupyter 笔记本上时,它看起来是这样的:
作者图片
使用的默认 tileset 是cartodbpositron
。如果您想要更改它,您可以将tiles
参数设置为您想要使用的 tileset。以下代码片段显示了使用openstreetmap
tileset 显示的地图:
shortest_route_map = ox.plot_route_folium(graph, shortest_route,
**tiles='openstreetmap'**)
shortest_route_map
以下是使用openstreetmap
tileset 显示的地图:
作者图片
如果您想让用户在运行时选择他们的首选 tileset,请使用下面的代码片段:
import foliumfolium.TileLayer('openstreetmap').add_to(shortest_route_map)
folium.TileLayer('Stamen Terrain').add_to(shortest_route_map)
folium.TileLayer('Stamen Toner').add_to(shortest_route_map)
folium.TileLayer('Stamen Water Color').add_to(shortest_route_map)
folium.TileLayer('cartodbpositron').add_to(shortest_route_map)
folium.TileLayer('cartodbdark_matter').add_to(shortest_route_map)folium.LayerControl().add_to(shortest_route_map)shortest_route_map
用户现在可以选择自己的 tileset 来显示地图:
作者图片
改变出行模式和优化器
除了寻找步行的最短路径,您还可以绘制驾驶的最短路径:
# find shortest route based on the mode of travel
mode = '**drive**' # 'drive', 'bike', 'walk'# find shortest path based on distance or time
optimizer = 'time' # 'length','time'
以下是驾驶路径:
作者图片
骑自行车怎么样?
# find shortest route based on the mode of travel
mode = '**bike**' # 'drive', 'bike', 'walk'# find shortest path based on distance or time
optimizer = 'time' # 'length','time'
这里是骑自行车的最短路径:
作者图片
您还可以根据距离而不是时间来查找最短路径:
# find shortest route based on the mode of travel
mode = 'bike' # 'drive', 'bike', 'walk'# find shortest path based on distance or time
optimizer = '**length**' # 'length','time'
这是骑自行车的最短距离:
作者图片
我会把剩下的组合留给你去尝试。
地理编码您的位置
在两点之间查找路径时,指定位置的纬度和经度并不方便(除非您的数据集中已经有了坐标)。相反,指定它们的友好名称要容易得多。您实际上可以使用 geopy 模块来执行这个步骤(称为地理编码)。
地理编码是将地址转换成其坐标的过程。反向地理编码则是将一对坐标变成一个友好的地址。
要安装 geopy 模块,在终端中键入以下命令:
$ **pip install geopy**
以下代码片段为 OpenStreetMap 数据创建了一个Nominatim
地理编码器类的实例。然后它调用geocode()
方法对金门大桥的位置进行地理编码。使用地理编码位置,您现在可以提取位置的纬度和经度:
from geopy.geocoders import Nominatimlocator = Nominatim(user_agent = "myapp")
location = locator.geocode("Golden Gate Bridge")print(location.latitude, location.longitude)
# 37.8303213 -122.4797496print(location.point)
# 37 49m 49.1567s N, 122 28m 47.0986s Wprint(type(location.point))
# <class 'geopy.point.Point'>
你可以到https://www.google.com/maps验证结果,并将经纬度粘贴到搜索框中:
作者图片
现在让我们修改我们的原始代码,以便我们可以对起点和终点进行地理编码:
以下代码已更新,可使用最新版本的 OSMnx (1.3.0)
import osmnx as ox
import networkx as nxox.settings.log_console=True
ox.settings.use_cache=True**from geopy.geocoders import Nominatim
locator = Nominatim(user_agent = "myapp")**# define the start and end locations in latlng
**#** start_latlng = (37.78497,-122.43327)
**#** end_latlng = (37.78071,-122.41445)**start_location = "Hilton San Francisco Union Square"
end_location = "Golden Gateway Tennis & Swim Club"****# stores the start and end points as geopy.point.Point objects
start_latlng = locator.geocode(start_location).point
end_latlng = locator.geocode(end_location).point**# location where you want to find your route
place = 'San Francisco, California, United States'# find shortest route based on the mode of travel
mode = 'bike' # 'drive', 'bike', 'walk'# find shortest path based on distance or time
optimizer = 'length' # 'length','time'# create graph from OSM within the boundaries of some
# geocodable place(s)
graph = ox.graph_from_place(place, network_type = mode)# find the nearest node to the start location
orig_node = ox.distance.nearest_nodes(graph, **start_latlng[1],
start_latlng[0]**)# find the nearest node to the end location
dest_node = ox.distance.nearest_nodes(graph, **end_latlng[1],
end_latlng[0]**)...
注意,
get_nearest_node()
函数可以接受位置坐标,或者是包含纬度和经度的元组,或者是一个geopy.point.Point
对象。
这里是从联合广场希尔顿酒店**到旧金山黄金门户网球&游泳俱乐部的最短骑行距离:
作者图片
显示起点和终点的标记
如果你能显示指示起点和终点的标记,那就更清楚了。正如我在上一篇文章中所描述的,您可以使用 follow 中的Marker
类来显示带有弹出窗口的标记。
以下代码片段显示了两个标记,绿色表示起点,红色表示终点:
*import folium**# Marker class only accepts coordinates in tuple form**
start_latlng = (start_latlng[0],start_latlng[1])
end_latlng = (end_latlng[0],end_latlng[1])start_marker = folium.Marker(
location = start_latlng,
popup = start_location,
icon = folium.Icon(color='green'))end_marker = folium.Marker(
location = end_latlng,
popup = end_location,
icon = folium.Icon(color='red'))# add the circle marker to the map
start_marker.add_to(shortest_route_map)
end_marker.add_to(shortest_route_map)shortest_route_map*
注意
Marker
类只接受元组形式的坐标。因此,您需要修改start_latlng
和end_latlng
成为包含纬度和经度的元组。
这是指示路径起点和终点的两个标记:
作者图片
绘制静态图
在前面的部分中,您使用了plot_route_folium()
函数在一张树叶地图上绘制两点的最短路径:
*shortest_route_map = ox.**plot_route_folium**(graph, shortest_route)
shortest_route_map*
还有一个你可能会感兴趣的功能——plot_graph_route()
。它不是输出交互式地图,而是生成静态图形。如果你想制作一个海报/图像显示两点的路径,这是很有用的。
以下代码片段使用上一节中使用的点生成了一个静态图:
以下代码已更新,可使用最新版本的 OSMnx (1.3.0)
*import osmnx as ox
import networkx as nxox.settings.log_console=True
ox.settings.use_cache=Truegraph = ox.graph_from_place(place, network_type = mode)orig_node = ox.distance.nearest_nodes(graph, start_latlng[1],
start_latlng[0])
dest_node = ox.distance.nearest_nodes(graph, end_latlng[1],
end_latlng[0])shortest_route = nx.shortest_path(graph,
orig_node,
dest_node,
weight=optimizer)fig, ax = **ox.plot_graph_route**(graph,
shortest_route,
save=True)*
您将看到以下输出:
作者图片
摘要
我希望你从这篇文章中找到灵感,并开始使用它来创建兴趣点的路线。您可能希望让用户输入他们的当前位置,然后绘制路线,向他们显示如何到达目的地。无论如何,玩得开心!
*https://weimenglee.medium.com/membership
参考文献:波音,G. 2017。 OSMnx:获取、构建、分析和可视化复杂街道网络的新方法。计算机、环境和城市系统 65,126–139。doi:10.1016/j . compenurbsys . 2017 . 05 . 004*
用 NASA 数据和 Python 可视化不可见的 SO2
原文:https://towardsdatascience.com/visualize-the-invisible-so2-with-nasa-data-and-python-2619f8ed4ea1
绘制火山爆发后二氧化硫的移动。
第一张图:2022 年 1 月 15 日洪加汤加-洪加 Haʻapai 火山爆发的卫星动画截图。图片来自维基百科。第二张图片:热图显示了 1 月 17 日,也就是火山爆发几天后,SO2 的含量。使用由叶子制成的 GES 光盘中的数据。图片由作者提供。
2022 年 1 月中旬,汤加群岛的火山岛亨加汤加-亨加 Haʻapai 发生火山爆发。一个卫星动画展示了一次巨大的爆炸,伴随着大量的烟雾。我们看到的烟雾自然包括水蒸气、二氧化碳(CO2)、二氧化硫(SO2)和灰烬。
2022 年 1 月 15 日洪加汤加-洪加 Haʻapai 火山爆发的卫星动画。图片来自维基百科
再来说说 SO2。二氧化硫被释放后,在高空转化为硫酸盐气溶胶。气溶胶能够反射阳光,使地球气候变冷,它们也是臭氧损耗的一个原因。随着大规模喷发,二氧化硫会被喷射到 10 公里以外的大气中。
然而,由于其的不可见性,释放的 SO2 很难被观察到。
本文将展示如何用散点图和热图来可视化火山爆发后 SO2 的移动。
检索数据
大气成分数据,包括二氧化硫,可以从美国宇航局戈达德地球科学数据和信息服务中心( GES 光盘)下载。注册后数据是公开的。
GES 碟网站
SO2 数据可通过 OMPS_NPP_NMSO2_PCA_L2 数据产品获取。这是 NASA/NOAA Suomi 国家极地轨道伙伴关系(SNPP)卫星上的臭氧测绘和剖面仪套件(OMPS)天底成像仪(NM)的 2 级轨道火山和人为二氧化硫产品。
请考虑到这不是原始数据。2 级数据产品被定义为与 1 级源数据具有相同分辨率和位置的衍生地球物理变量。关于关卡的更多信息,你可以点击查看。
基于 PCA 算法的 OMPS 核电厂最低点映射 SO2 二级产品页
为了从网站上获取 SO2 数据,我按照中的有用步骤为你的下一个地理项目获取 NASA 数据,这是一篇关于获取 GES 光盘数据文件的深刻文章。
由于洪加汤加-洪加 Haʻapai 火山爆发于 2022 年 1 月 15 日达到高潮,日期范围从 1 月 12 日细化到 1 月 23 日。
除了获取数据,还要下载一个文本文件(。txt),点击下载链接列表。这个文本文件将帮助我们在下一步中获得文件名。
下载链接列表和数据 URL。图片来自 OMPS NPP NMSO2 PCA L2 页面。
输入数据
从库开始,除了 NumPy 和 Pandas,H5py 库是用来读取下载的 HDF5 文件的。
读取获得的文本文件(。txt)来获取文件名。
文本文件中的文件名示例
下面的代码展示了如何用 H5py 从文件名中读取文件。读取后,数据将保存在一个列表中。
选择键
HDF5 格式内部包含许多密钥。我们需要浏览数据来选择我们要使用的键。本文使用的两个主要关键字是 GEOLOCATION_DATA 和 SCIENCE_DATA。
list(dataset[0].keys())### Output:
# ['ANCILLARY_DATA',
# 'GEOLOCATION_DATA',
# 'SCIENCE_DATA',
# ...
GEOLOCATION_DATA:纬度、经度和 UTC_CCSDS_A。
list(dataset[0]['GEOLOCATION_DATA'].keys()) ### Output:
#['Latitude',
# 'LatitudeCorner',
# 'Longitude',
# ...
# 'UTC_CCSDS_A',
# ...
SCIENCE_DATA: ColumnAmountSO2,以 Dobson 单位表示的垂直列 SO2 总量。
list(dataset[0]['SCIENCE_DATA'].keys())### Output:
# ...
# 'ColumnAmountSO2',
# ...
数据集内部还包含其他有趣的键。更多信息,官网提供了解释其他键的 PDF 文件这里。
创建数据框架
现在我们已经选择了键,让我们定义一个函数来提取数据。
创建纬度、经度、二氧化硫含量和日期的列表。
在绘制散点图之前,我们必须转换经度范围。这是因为典型地图的经度范围,包括来自 GES 圆盘的数据,在-180°和 180°之间。
如果我们直接绘制位于经度-175.4°的亨加汤加-亨加 Haʻapai 的位置,SO2 的移动将由于靠近范围的极限(-180°)而被切断。下面的代码用于修改经度范围。
从列表中创建一个熊猫数据框。
描述性分析
开始的一个重要步骤是做描述性分析。这是帮助我们理解数据的有用的一步。
要查看火山周围每天的 SO2 量,我们将创建一个过滤数据框,仅选择距离喷发点 15 度经纬度的正方形区域内的 SO2 量。我们还将只选择 SO2 值为正值的行。
按日期对过滤后的数据帧进行分组,并对 SO2 的日总量进行求和。
上表显示,SO2 日排放量在 1 月 16 日达到最大值。
从数据帧中绘制时间序列折线图,以查看时间线中的进度。
匈牙利汤加-匈牙利 Haʻapai 周围的 SO2 日排放量
图表显示,爆炸发生时,从 15 日到 1 月 16 日,该岛周围的二氧化硫急剧增加。
形象化
1.散点图
利用纬度和经度,SO2 的位置被绘制为点,根据位置和密度分别用颜色表示。
首先创建一个日期列表来过滤数据框架。仅选择 SO2 含量大于 0.5 DU 的行,以避免绘制时数据过多。
现在我们已经做了所有的事情,作为一个例子,我将展示 1 月 17 日,也就是火山爆发几天后,SO2 含量的散点图。
散点图显示了 1 月 17 日的二氧化硫含量。数据来自 GES 光盘。用 Seaborn 做的。图片由作者提供。
制作动画是为了让可视化看起来有趣。我们可以把这些日常的情节组合成一个 GIF 文件来看进度。为此,我们将绘制每个日散点图,并将它们存储在一个列表中。
组合这些图,并把它们转换成 GIF 文件。
洪加汤加-洪加 Haʻapai 火山爆发后 SO2 的移动。数据来自 GES 光盘。图片由作者提供。
2.热图
yellow 是一个 Python 库,易于使用,功能强大,可以处理地理空间数据。从导入库开始。
例如,我将展示如何绘制 1 月 17 日 SO2 的热图。
为了使用叶子,我们需要从数据帧中创建一个纬度、经度和二氧化硫含量的列表。然后,使用热图功能创建热图。
这张热图显示了 2022 年 1 月 17 日的二氧化硫含量。用叶子做的。数据来自 GES 光盘。图片由作者提供。
随时间变化的热图
Heat 有一个名为 HeatMapWithTime 的功能,用于制作时间序列热图。这对于创建 SO2 运动的动画非常有用。
瞧啊。!…
洪加汤加-洪加 Haʻapai.火山爆发后 SO2 的移动用叶子做的。数据来自 GES 光盘。图片由作者提供。
讨论
据消息2022 年 1 月 15 日喷发达到高潮,之后趋于平静。然而,用 OMPS_NPP_NMSO2_PCA_L2 数据(这是一个 2 级产品)进行可视化显示,释放的 SO2 仍然在空气中保留了一个多星期。
结果与有毒物质和疾病登记机构(ATSDR)的信息一致,即 SO2 的大气寿命约为 10 天。
摘要
尽管用肉眼很难观察到二氧化硫,但我们可以使用 Python 和美国宇航局的数据来绘制二氧化硫释放后的移动情况。本文展示了数据的来源以及如何下载和可视化它们。
正如我在探索数据步骤中提到的,HDF5 文件中还有其他键。你可以回去和其他人一起玩。如果任何人有问题或建议,请随时留下评论。
这些是关于数据可视化的其他文章,您可能会感兴趣。
参考
- 李灿、Nickolay A. Krotkov、Peter Leonard、Joanna Joiner (2020 年)、OMPS/NPP 五氯苯甲醚 SO2 总列 1-轨道 L2 条带 50x50km V2,美国马里兰州格林贝尔特,戈达德地球科学数据和信息服务中心(GES 光盘),访问日期:2022 年 3 月 19 日,10.5067/MEASURES/SO2/DATA205
- Bhanot,K. (2020 年 8 月 12 日)。为你的下一个地理项目获取 NASA 数据。中等。2022 年 4 月 12 日检索,来自https://towards data science . com/getting-NASA-data-for-your-next-geo-project-9d 621243 b8 F3
- Python 可视化。(2020).叶子。从 https://python-visualization.github.io/folium取回
- 有毒物质和疾病登记处。1998.二氧化硫的毒理学简介。佐治亚州亚特兰大:美国卫生与公众服务部,公共健康服务。
在分类问题中可视化数字特征的预测能力
如何可视化数字特征的预测能力
作者图片
在有监督的机器学习问题中,测量某些特征的预测能力总是一项难以完成的任务。在使用任何相关性度量标准之前,可视化一个特性是否能提供信息是很重要的。在本文中,我们将把数据可视化应用于分类问题。
数字特征如何影响分类目标?
有几种方法可以预测数字特征与分类目标之间的关系。例如,在血液分析中,如果某个值超过了某个阈值,生物学家可以假设存在某种疾病或健康状况。这是一个简单的例子,但它让我们清楚地了解了这个问题。在数字特征域中,我们必须找到一些子区间,在这些子区间中,分类目标显示一个比其他值更频繁的值。这是数字特征对分类目标的预测能力的定义。
这可能发生在我们想要的任意多个子区间上(实际上,这就是决策树实际上对我们的数据集建模的方式)。
数据可视化
在深入研究这种相关性的数值估计之前,以某种不偏不倚的方式将其可视化总是有用的。
在我关于 Python 中的探索性数据分析的免费课程中,我谈到了一种特殊的可视化,即堆叠直方图。
实际上,我们执行一个变量的直方图,并且根据与直方图的那个柱相关的目标变量的值,将每个柱分割成堆叠的子柱。这里有一个例子:
作者图片
正如我们所看到的,在 0.3 之前,多数类是 1(条形几乎是黄色的),而对于更高的值,条形变得更蓝。这在数字特征和分类变量之间有很强的相关性,因为我们已经能够发现两个区间,在这两个区间中,目标变量的一个值明显比其他值更频繁。
这个直方图的想法是,寻找一种颜色比其他颜色出现得更频繁的范围。使用这种方法,我们可以说有一个阈值根据特征值决定我们的目标变量的行为。这正是模特需要的学习能力。
通过逻辑回归和二元决策树可以很容易地发现这种相关性,因此这种分析可以为我们提供关于实际可行的模型类型的信息。
现在让我们看一个 Python 中的例子。
Python 中的一个例子
对于这个例子,我将使用 scikit-learn 库中包含的乳腺癌数据集。
首先,让我们导入一些库。
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
然后,让我们导入数据集。
dat = load_breast_cancer(as_frame=True)
df = dat['data']
df['target'] = dat['target']
让我们假设我们想要可视化“平均半径”特征的预测能力。我们可以只用一行代码来绘制堆积直方图:
sns.histplot(df,x="mean radius", hue="target", multiple="stack")
这是结果。
作者图片
正如我们所看到的,在 16 左右有一个阈值,低于这个阈值,目标变量的多数值为 1,高于这个阈值,目标变量的多数值为 0。所以,我们可以说这个变量是有预测性和信息性的。
我们现在可以对每个数值变量进行探索性分析,以显示它们的预测能力。
结论
在本文中,我展示了我最喜欢的一种方法,即可视化数字特征对分类目标的预测能力。这可能不是唯一可能的方法。例如,100%堆积直方图可能更具可读性。总的想法是,在计算任何数字之前,我们总是需要可视化信息,使用这种类型的图表可以提高我们项目的质量,同时产生良好的可交付成果。
原载于 2022 年 3 月 30 日【https://www.yourdatateacher.com】。