TowardsDataScience-博客中文翻译-2020-一百零六-

TowardsDataScience 博客中文翻译 2020(一百零六)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

Python 中的稀疏群组套索

原文:https://towardsdatascience.com/sparse-group-lasso-in-python-255e379ab892?source=collection_archive---------14-----------------------

如何在回归中使用最佳变量选择技术之一

准备使用套索,捕捉一些有意义的变量。普里西拉·杜·普里兹在 Unsplash 上的照片

所以昨天我为 python 发布了一个新的包: asgl (这个名字来源于自适应稀疏组套索),它增加了许多在 R 包中已经可用但在 python 中不可用的功能,比如求解稀疏组套索模型,并且超越了这一点,增加了额外的功能来改善稀疏组套索可以提供的结果。

这将是关于回归中最先进的正则化技术系列的第一篇文章,我想开始谈谈稀疏群套索:它是什么以及如何使用它。具体来说,在这里我们将看到:

  • 什么是稀疏群套索
  • 如何在 python 中使用稀疏群组套索
  • 如何进行 k 倍交叉验证
  • 如何使用网格搜索才能找到最优解。

什么是稀疏群套索

为了理解什么是稀疏群组套索,我们需要(简要地)谈谈两种技巧:套索群组套索。给定一个风险函数,例如线性回归风险,

线性回归模型的风险函数

套索: 定义了在β系数的绝对值上增加一个惩罚项,

套索惩罚公式

这个定义提供了稀疏解,因为它会将一些β系数(与响应变量最不相关)置零。这种惩罚的效果可以使用λ参数来控制。较大的λ值提供了惩罚更重要的解决方案,因此β系数中有更多的零。这主要在高维数据集中有用,这里的变量比观察值多,但我们只期望变量中的一小部分真正有意义。

但是,在某些情况下,X 中的预测变量具有自然的分组结构。例如,在生物统计学中,通常处理遗传数据集,其中预测因子被分组到遗传途径中。在股票市场分析中,人们可以将来自同一业务领域的公司分组。在气候数据中,人们可以对不同的地区进行分组 lasso 提供了单独的稀疏解决方案,而不是分组稀疏。

群拉索: 于是群拉索来救援了。组套索是建立为属于同一组的系数的平方和。

群体套索惩罚公式

通过这种方式,它考虑了预测器的可能分组结构,并且它将整组变量发送到零。如果所有组的大小都是 1(每个组只有一个预测值),我们将求解一个套索模型。让我们用图形来看套索和套索组,

套索、成组套索和山脊惩罚比较

在上图中,我们有一个简单的问题,有三个系数,β₁
β₁₁和β₁₂.最后两个系数组成一个组,正如我们看到的,lasso(左图)没有考虑这个分组信息,但 group lasso 考虑了。所以群套索可以看成是群与群之间的套索,群内的脊。如果一个群体有意义,我们选择整个群体。如果不是,我们把它发送到零。

稀疏群套索: 最后就是这里了,

稀疏群套索罚函数

稀疏组套索是套索和套索组之间的线性组合,因此它提供了稀疏组之间和之内的解决方案。

这种技术从最有意义的组中选择最有意义的预测值,是近年来最好的变量选择方法之一。然而,直到现在,python… 的稀疏组套索还没有实现。

迁移到 python:安装 asgl

让我们从安装asgl开始。这可以使用pip轻松完成

pip install asgl

或者,你可以提取 github 库并运行setup.py

git clone [https://github.com/alvaromc317/asgl](https://github.com/alvaromc317/asgl).git
cd asgl
python setup.py

导入库

一旦我们安装了这个包,我们就可以开始使用它了。首先,让我们导入我们将在本例中使用的库和BostonHousing数据集。

这里,除了导入数据集,我们还创建了一个名为group_index的变量。这个变量描述了数据的分组结构,所以如果我们有 13 个预测值,group_index应该是一个长度为 13 的变量,如果前三个预测值组成一个组,它们应该有相同的group_index值。然而,BostonHousing dataset 没有自然的分组结构,所以为了本文的目的,我们在这里定义一个假的。

sgl 模型的参数

如果我们看一下上面的稀疏群套索方程,我们可以看到有两个参数α和λ可以优化。 λ控制我们希望给予惩罚的权重,因此λ值越大,产生的解越稀疏。α控制套索和组合套索之间的权衡。α等于 1 提供套索,α等于 0 提供组合套索。现在,通常,我们可以为这两个参数定义一个可能值的网格,并尝试找到使误差最小的组合。

此外,我们指定要求解的模型类型(lm,因为我们正在求解一个线性模型)、惩罚(sgl,因为我们需要稀疏组 lasso)、要使用的错误类型(MSE,因为我们将使用均方误差)以及最后,我们可以指明是要顺序还是并行运行该代码。并行执行利用了multiprocess python 库,可以比顺序执行更快地解决问题。

交叉验证

我们已经定义了 23 个可能的λ值和 20 个可能的α值的网格。共有 460 种参数组合。我们将使用交叉验证(并行运行)找到最佳组合

所以首先,我们定义一个CV对象,我们插入上面定义的所有参数信息。然后,我们运行cross_validation()函数,该函数将返回 460 个模型中的每一个在 k 次折叠中达到的误差。

这样,我们搜索最小化均方误差的参数值,并将它们的值存储在optimal_lambdaoptimal_alpha中。

就这么简单,我们找到了我们的最优模型。

获得最终误差

然而,应该记住,交叉验证误差有点偏差,因此为了获得最终预测误差,我们将使用最佳参数运行最终训练/测试分割。

首先,我们定义一个ASGL对象,我们将使用它来拟合一个简单的稀疏组套索模型(没有交叉验证)。然后,我们使用函数train_test_split()将数据分成 70%训练 30%测试,我们拟合模型,以便获得β系数的最终估计值(存储在final_beta_solution

给定一个新的数据集,我们可以使用函数ASGL.predict()预测响应变量的值,并使用带有函数error_calculator的测试集计算最终预测误差。

以上就是如何用 python 实现稀疏群组套索。我希望你喜欢这篇文章,并发现它很有用,所以继续关注这个系列的未来文章,如果你有任何问题/建议,请不要犹豫,与我联系。

要更深入地了解asgl包提供了什么,我推荐阅读 github 库中提供的 jupyter 笔记本

祝你今天开心!玩的开心!

空间自相关:靠近的对象影响其他靠近的对象

原文:https://towardsdatascience.com/spatial-autocorrelation-close-objects-affecting-other-close-objects-90f3218e0ac8?source=collection_archive---------34-----------------------

深入探讨空间自相关及其行业使用案例

尼克·舒利亚欣在 Unsplash 上的照片

地理空间相关性

地理空间自相关是指一个对象与附近其他对象的相似程度。“Auto”表示自我,“correlation”表示关联。通俗地说,它衡量的是近距离物体与其他近距离物体的相似程度。

GIS 的第一条规则:任何事物都与其他事物相关。但是近处的事物比远处的事物更相关(瓦尔多·r·托布勒,1970)

要理解这个规律,我们来举个例子。

假设你从类似房产的房源网站上随机挑选了一栋房子,让我们假设房子的价格是 60 万美元。接下来,假设紧挨着它的房子也挂牌出售,你要预测它的价格。给你 65 万美元和 280 万美元两个选择,你会选哪个?

如果你选择了 60 万美元,那么你已经下意识地知道什么是空间自相关。它是两个相邻物体在一些共同特征上的相关性(例如房价)。

空间自相关的一个潜在应用是,它有助于分析生态和疾病的聚集和分散。诸如“该疾病是孤立病例”或“各地的降雨模式是聚集的还是相同的”等问题可以通过空间自相关分析得到很好的理解和回答。

技术定义

从技术上来说,空间自相关是指在空间尺度上相互接近的变量的观测值之间的关联性的度量。变量可以是:

1.在连续表面上的任何点(如一个地区的土地利用类型或年降水量)

2.在特定区域的一组固定地点(例如一组零售店)

3.跨越细分一个地区的一组区域(例如在划分一个城市地区的人口普查数据中拥有两辆或更多汽车的家庭的数量或比例)。

自相关违背了统计学的核心原则,即观测值相互独立。根据经典统计学中独立性的假设,组间的观测值和组内的观测值应该是独立的。因此,空间自相关显然违反了上述假设。

Patrick assaléonUnsplash摄影

空间相关性的概念是时间相关性的一种扩展。唯一的区别在于,时间相关性测量的是一个变量随时间的变化,而空间相关性测量的是两个变量的变化-观测值(如收入、降雨量等值)和位置。

空间相关性的类型

地理空间中最常见的空间关联形式是面片和梯度。

变量中的空间相关性可以是外源性(由另一个空间自相关变量如降雨量引起)或内源性(由某种起作用的过程如疾病传播引起)。

这里有一个视频详细讲述了空间自相关如何帮助提高空间自相关的重要性

来源

莫兰的我

空间自相关通过 Moran 的 I 进行测量。Moran 的 I 是一个相关系数,用于测量数据集中的整体空间相关性。莫兰 I 可分为正、负和无空间自相关:

1。正相关:

当相似值在地图上聚集在一起时,空间相关性为正。当 Moren I 接近+1 时,出现正自相关。下图显示了一个区域的土地覆盖情况,这是一个正相关的示例,因为相似的聚类就在附近。

描述正相关的土地覆盖图像

2.负相关:

当不同的值在地图上聚集在一起时,空间相关性为负。当莫兰的 I 值为-1 时,会出现负的空间自相关。棋盘是负自相关的一个很好的例子,因为不同的值彼此相邻。

不同的对象聚集在一起,因此负相关

2.零相关:

莫兰 I 值为 0 表示没有空间自相关。

空间相关性的应用

空间自相关的重要性在于,它有助于定义空间特征在影响空间中的给定对象时有多重要,以及对象与空间属性之间是否存在明确的关系。以下是空间自相关的一些有趣的工业用例:

  1. 衡量不平等:空间自相关有助于找出收入、人口或种族方面的不平等和多样性的衡量标准。它利用莫兰 I 系数来分析收入、人口等参数在某一地区是聚集分布还是均匀分布。[ 来源
  2. 环境:空间自相关有助于发现城市土壤中稀土元素的污染热点。[ 来源
  3. 兴趣点:自相关用于将不同参数映射为感兴趣变量的距离函数。例如,房价在离市中心多远的地方开始下降
  4. 生态学:空间自相关在海洋和珊瑚礁生态系统中被广泛用于重要的应用,如场地适宜性分析,以确定贻贝延绳钓养殖场或海洋水产养殖规划的区域。
  5. 人口统计:空间自相关用于绘制和分析选举期间的投票率,例如,空间自相关用于绘制法国总统选举和法国地方选举期间的缺席情况。[ 来源

照片由于切尔·莫兰Unsplash 上拍摄

案例研究:意大利人口的移民分析

自相关对迁移分析有很大影响。这项研究是论文在这里的复制。这里的案例考虑了对意大利外国人口迁移的分析。

移民是不同规模的人口动态演变的关键因素,对经济、文化和环境都有影响。利用空间自相关,我们确定了代表移民集中的空间集群。

从技术上讲,这里的莫兰 I 系数表示外来人口与本地居民人口比率的加权方差与广义方差之间的差异。通俗地说,它表达了给定地点的外来人口/人口比例与相邻空间单元的外来人口/人口比例之间的相关性。

使用相关指数 LISA (空间关联的局部指标),我们将场景分成五类:

1.现象值高且与其周围环境高度相似(高-高)的位置被定义为热点

2.现象值低且与周围环境相似度低(低-低)的地点被定义为冷点。

3.现象值高而相似度低的位置,反之亦然。这些被称为空间异常值。

来源

迁徙种群受到抑制的区域可分为以下三类:

  1. 第一类在地理上集中在东北部地区,代表正相关值(类型:高-高)。这些群体的特点是收入机会/福利不断增加,因此吸引了寻找工作的外国人。
  2. 在中心区域发现第二个聚类,其代表正相关的值(类型:高-高)。这些地区显示出福利更高的相似特征。
  3. 第三个集群是在意大利南部的城镇发现的(类型:低-低)。这些地区的典型特征是收入低,就业机会少。

结论

空间自相关不仅将相似的对象与其他相似的对象聚集在一起,而且还表示相关或相似的程度。它有助于发现隐藏的模式和关系。它在生态学和人口统计学中有很多应用。

原贴 此处

在现场,我们正在构建一个“ 运营 ”分析平台,使用按需公司供应和运营团队的位置数据。如果想深入钻研,可以查看我们的 网站 out 或者在LinkedIn或者Twitter上与 Aditi 取得联系。

空间自相关:邻居影响邻居

原文:https://towardsdatascience.com/spatial-autocorrelation-neighbors-affecting-neighbors-ed4fab8a4aac?source=collection_archive---------22-----------------------

了解值和事件的空间聚类

安德鲁·斯图特斯曼在 Unsplash上的照片

本文的目的是介绍地理空间分析中的一个重要概念——“空间自相关”,重点介绍以下内容:(1)自相关的关键概念;(二)行业应用和(三)在R中的实施

“一切都与其他一切相关。但是近处的东西比远处的东西更有联系”。瓦尔多·托布勒(1969)的地理第一定律

为了理解上面的托布勒定律,我们来做一个思维实验。

让我们从诸如 Zillow 的列表网站中随机挑选一所房子,假设该房子的列表价格为 40 万美元。接下来说,旁边的房子也挂牌出售。现在,如果你要预测第二套房子的价格,给你两个选择,45 万美元和 120 万美元,你会选哪个?

如果你选择了 45 万美元,那么你已经潜意识地知道什么是空间自相关。确实是两个邻居在一些共同特征上的相关性(比如房价)。“Auto”表示自我,“correlation”表示关联。因此,用一种更正式的说法来说,自相关是相邻观测值之间相似性的一种度量,它描述了在给定的空间尺度上某个变量(如人口密度)的观测值彼此相似的程度。或者简单地说,“莫兰 I 统计量是一个变量(比如收入)与其周围值之间关系的相关系数”(来源)。

请注意,有一个类似的概念称为时间自相关,其中观察(例如,身高,体重等。)与前一时间点的观察值相关。这种时间自相关在时间序列预测中是一个有用的概念。

在这些图中,几何图形在两个方面不同:形状和颜色。左边的块的位置是可预测的,因为相似的东西彼此邻近,因此空间自相关,而右边的形状是不可预测的,因为它们或多或少是随机分布的。

如何量化自相关(The Moran's I)

只需查看地图,我们就能对某些要素的空间自相关有所预感。然而,有一个正式的方法来定量测量它,莫兰的 I T8 就是一个用于这个目的的统计量。它取-1 和+1 之间的值(如正态相关系数, r, would),同时提供相关的p-值作为显著性测试(下面讨论)。正的 Moran's I 表示相似的观察值彼此更接近,而负值表示不相似的值聚集在一起。0 左右的值表示没有自相关,相反,观察值是随机分布的。

i ncome 与 i ncome 的滞后散点图。回归线的斜率代表莫兰 I 统计量。(来源)

衡量莫兰 I(蒙特卡罗模拟)的显著性

一旦对特定数据集测量了莫兰的 I,在观测值随机分布的零假设下,如何知道它是否显著?

测量显著性的一种方法是执行蒙特卡罗测试。该过程分三步完成:(I)首先,观察值(如房价)随机分布在不同的空间单位(如县多边形、栅格像元等),(ii)然后,计算该随机数据集的莫兰氏 I,(iii)最后,将模拟的莫兰氏 I 与观察到的莫兰氏 I 进行比较。这三步过程重复多次。运行所有模拟后,概率 p 计算如下:

((大于观察到的莫兰 I +1 的模拟莫兰 I 的数量)/(模拟 ran + 1 的数量)

比如说,

*number of simulations = 200*

*number of simulated Moran's I that are higher than observed value = 2*

然后根据上面的等式,零假设为真的概率:

*p = (1+1)/(200+1) = 0.009*

其中 p < 0.01 因此,可以拒绝观测值(如人口、房价)在空间上随机分布的零假设。换句话说,观察到的房价自相关性非常显著。

自相关的行业应用

自相关是空间分析中的一个关键概念,具有广泛的行业应用。其中一些如下:

  • 空间不平等/多样性的衡量标准:收入、人口、种族等。聚集或均匀分布在某些区域—可以使用 Moran 的 I。
  • 它已经被用于识别伦敦城市土壤中稀土元素的污染热点
  • 已用于演示感兴趣的变量的不同 距离衰减函数 (例如,房价作为距市中心距离的函数下降多远?).
  • 自相关经常用于许多其他机器学习算法(例如空间回归、分类、聚类分析)以及作为 EDA 一部分的空间数据可视化(例如热图、热点分析)

R 中的 5 步实现

步骤 1:安装依赖关系

library(sf)
library(spdep)
library(tigris)
library(acs)
library(tidyverse)
library(tmap)

第二步:获取数据

我在这个演示中使用的数据是从美国社区调查中检索的县/人口普查区级别的家庭收入中值数据集。要访问数据,用户必须发出一个针对 API 密钥的请求。在我的情况下,一旦提出要求,处理速度相当快。

api="MY API KEY"
api.key.install(key=api)
acs.tables.install()set.seed(1234) # because we are randomizing part of the process# Access the shapefile
s <- get_acs(geography = "tract", variables = "B19013_001", 
                state = c("VA"), geometry = TRUE)
# remove NA values is any
s <- na.omit(s)# select column to work with
s <- subset(s, select=c("estimate"))

第三步:探索性数据分析

# check data skewness
hist(s$estimate, main=NULL)# check for outliers
boxplot(s$estimate, horizontal = TRUE)# plot variable
tm_shape(s) + tm_fill(col="estimate", style="quantile", n=5, palette="Greens") +
tm_legend(outside=TRUE)

我感兴趣的变量不完全是我需要的形状,因为似乎有一些异常值。但是这应该适用于这个演示。

这显示了弗吉尼亚州人口普查区的家庭收入中位数的分布情况。收入差异非常明显,可以直观地识别出来。应该有很高的自相关性,但是我们会看到的!

第四步:建模

# define neighbor
nb <- poly2nb(s, queen=TRUE) # here nb list all ID numbers of neighbors;# assign weights to neighbors
lw <- nb2listw(nb, style="W", zero.policy=TRUE) # equal weights# compute neighbor average
inc.lag <- lag.listw(lw, s$estimate)# plot polygons vs lags
plot(inc.lag ~ s$estimate, pch=16, asp=1)
M1 <- lm(inc.lag ~ s$estimate)
abline(M1, col="blue")# access Moran's coeff
coef(M1)[2]# calculating Moran coeff with one line
I <- moran(s$estimate, lw, length(nb), Szero(lw))[1]

这只是一个有空间滞后的家庭收入散点图

第五步:假设检验

# hypothesis test with moran.test function
moran.test(s$estimate,lw, alternative="greater")# using Monte-Carlo simulation
MC<- moran.mc(s$estimate, lw, nsim=999, alternative="greater")# View results (including p-value)
MC# plot Null distribution
plot(MC)

正如所料,莫兰的 I 统计值(0.72)相当高,表明弗吉尼亚州中等家庭收入的空间自相关性显著(见 p 值)

结束注释

了解空间自相关是空间数据分析中的一个重要概念-不仅用于了解数据的空间模式和变化,还用于业务决策。它也是为高级空间显式统计和机器学习算法提供输入的关键统计数据。就实现而言,一旦有了数据,实现起来就相当容易了。我在R环境中实现,但是也有一些Python库(例如geopandas),然而,在我的经验中,R 更加用户友好,因为使用现有的包可以方便地访问县/人口普查区域规模的数据集。

在 ArcGIS Pro 中使用 Dask 进行空间数据工程

原文:https://towardsdatascience.com/spatial-data-engineering-using-dask-in-arcgis-pro-1298e67334ac?source=collection_archive---------49-----------------------

照片由 安德鲁·尼尔 发自 像素

世界上的数据环境正以指数级的速度增长,这使得执行适当的分析以获得指导和推动各种决策所需的见解和信息变得非常重要。的问题‘在哪里?’成为决策制定不可或缺的一部分,因为几乎所有数据点都有地理位置。

为了完成有效的数据分析,在“哪里”,我们需要进行适当的数据工程。数据工程是指对数据进行规划、准备和处理,使其对分析更有用。在本文中,我们将使用 ArcGIS Pro、ArcGIS notebook 并集成一个开源库— Dask 来执行数据工程。

为什么选择 Dask?

Pandas 是在 Python 编程语言中用于数据争论和分析的最流行和最受欢迎的数据科学工具之一。由于算法和本地内存限制,熊猫在大数据方面有自己的局限性。

然而,Dask 是一个开源的免费 Python 库。Dask 提供了以最少的重写更自然地扩展 Pandas、 Scikit-LearnNumpy 工作流的方法。Dask 的用户界面有三(3)种主要类型,即

  1. 数组,
  2. 包,还有
  3. 数据框。

在本文中,我们将主要关注 Dask 数据框架。就性能和可伸缩性而言,可以将 Dask 视为 Pandas 的扩展。更酷的是,你可以在 Dask dataframe 和 Pandas dataframe 之间切换,按需进行任何数据转换和操作。它是 Python 中一个灵活的分布式并行计算库。阅读更多

准备好 ArcGIS Pro

ArcGIS Pro 是来自 Esri 的最新专业桌面地理信息系统(GIS)应用。使用 ArcGIS Pro ,可以浏览、可视化、分析数据;创建 2D 地图和三维场景;分享你的作品。

要开始数据工程会话,我们需要在 ArcGIS Pro 上进行一些初步操作,包括

  • 在 ArcGIS Pro 中启动新项目和 jupyter 笔记本
  • 在 ArcGIS Pro 上安装 Dask 库

在 ArcGIS Pro 中启动新项目和 jupyter 笔记本:正确安装后,以下步骤将帮助您启动第一个项目。

ArcGIS 开始页面

创建 Jupyter 笔记本

打开 Jupyter 笔记本

笔记本已创建

步骤描述

在 ArcGIS Pro 上安装 Dask 库:python 体验已通过“Conda”包管理器整合到 ArcGIS 中。Conda 的包管理器自动使用 python 库和管理工作环境。让我们弄清楚一些术语的含义。

  • 环境:包含“Conda”包集合的文件夹或目录。
  • 包:包含 python 软件的压缩文件。
  • Channel:指向存储库的 URL。
  • 存储库:包的存储位置。

在 ArcGIS Pro 中安装 Dash 所需的步骤如下所示:

ArcGIS Pro 项目页面的元素:安装过程

ArcGIS Pro 项目页面的元素:安装过程继续

Dask 的数据工程

万花筒Unsplash 上拍摄的照片

本笔记本描述了下载和准备美国总统选举数据的过程。您将处理缺失值,重新格式化数据类型,并重新构建表的格式。这篇文章的参考资料可以在 这里 找到。

加载并准备数据

要下载和准备选举数据,您将使用 ArcPy、ArcGIS API for Python 以及 Pandas 和 Dask 数据框架。首先,您将导入这些模块来使用它们。然后,您将为美国县选举数据创建一个变量,并使用该变量将数据读入 Dask 数据帧。

清理数据

从上面数据集的预览中,可以观察到“state_po”是“state”功能的缩写。为了使数据更清晰,我们必须删除这个多余的特征。

选举数据包括在 FIPS、政党和候选人选票 字段中缺失数据的记录。这些丢失的数据被称为空值。在正确识别之后,我们必须设法处理带有缺失值的特性。

  • 用一个值填充它们
  • 移除数据集中的实例

我们在这里使用的处理缺失值的策略是用有效的有代表性的值替换缺失值。这可以通过 Dask 数据帧使用 fillna 方法实现。

【FIPS】【候选人投票】 特征都是数值。在这种情况下,由于数据是连续的,我们可以使用平均值或中值来很好地代表特征的集中趋势。在这种情况下,我们将使用这些特征的平均值来填充缺失值。

我们只剩下了中缺失的价值观中的特征。缺失值非常大,这使得我们必须做出正确的选择,用什么来填充它。让我们大致了解一下特性中的独特值。如下所示,这描绘了选举中的投票方。为了得到一个无偏的数据集,我们将用“未记录”来填充缺失值。

探索和处理数据类型

在查看数据时,您注意到 FIPS 字段被视为数值字段,而不是字符串。因此,FIPS 值中的前导零已被删除。得到的 FIPS 值只有四个字符,而不是五个。您将确定有多少记录缺少前导零,并添加或追加缺少的零。

同样,像 这样的字段应该是整数值而不是浮点数据类型。

重新格式化表格结构

目前,表中的每条记录对应于一个县的一名候选人和他们的选票。您需要重新格式化该表,以便每个记录对应于每个县,字段显示该选举年不同候选人的选票。使用数据透视表地理处理工具Excel 数据透视表可以做到这一点,但 Python 可能会使自动化和共享更容易。
下面的动画演示了重组表格的步骤:

下面的代码单元格执行这些步骤

计算附加列:特征工程

这里,我们将使用更新后的表中的值来添加额外的信息列,例如非主要政党的票数、每个政党的选民百分比等等。每一列都被称为数据集的一个属性。

地理启用数据

您最终将在空间分析中使用这些数据。这意味着数据需要包含位置信息,以确定数据在地图上的位置。您将使用现有的地理启用县数据对数据进行地理启用,或者向数据中添加位置。

加入数据

您有一个包含选举数据的数据框( df )和一个包含县几何数据的空间化数据框( counties_df )。您将把这些数据集合并成一个。

查询和计算属性

因为您有 2016 年的投票年龄人口,所以现在可以计算 2016 年的平均投票率(投票率)。该数据框架包括 2010 年至 2016 年的记录,但只有 2016 年的投票年龄人口。在计算投票率之前,您需要创建 2016 年的子集数据框架。

验证数据

在继续其他数据准备之前,您应该确认已经成功创建了输出数据。

首先,您将验证投票率的值。您将删除空值,因为这些值代表一个分数(总票数除以投票年龄人口),您将确认这些值的范围在 0 和 1 之间。

更新验证数据

在查看了人口普查局 2016 年的投票年龄人口数据后,您确定这些县的投票年龄人口较低,误差幅度相当大。这可能是这些县投票率高于 100%的原因。

您将使用这些县的误差范围上限来重新计算这些县的选民投票率字段:

  • 科罗拉多州圣胡安县:574 人
  • 新墨西哥州哈丁县:562 人
  • 德克萨斯州爱县:86
  • 德克萨斯州麦克马伦县:566 人

这些信息是从这里的中提取的。

将数据框转换为要素类以进行空间分析

您将使用本脚本开头导入的 ArcGIS API for Python 将空间化数据框导出到要素类。

注意:执行以下单元格可能需要几分钟时间

在页面顶部,单击“数据工程图”选项卡。拖动数据工程图选项卡以显示为其自己的窗口。查看添加到数据工程地图的要素类。

数据的颜色每次添加到地图时都会有所不同。

我希望本文能让您体会到数据工程对于 ArcGIS Pro 中空间分析的重要性。感谢您的阅读。

了解更多土星云

Kelly SikkemaUnsplash 上拍摄的照片

空间数据:图形-光谱作为特征

原文:https://towardsdatascience.com/spatial-data-graph-spectrum-as-features-da5d10ec2d68?source=collection_archive---------27-----------------------

一只小鸟在听吉米·亨德里克斯的红房子歌曲,还有一艘船。阿莫斯·霍奇曼的照片。经允许重新发布。

在我的第一篇中型文章中,我将展示一种用图论中的特性丰富空间数据的好方法。这些功能捕获数据中的重要信息,否则很难访问这些信息。

这个想法在 DataHack IL 2016 中获得了向风奖创新奖,它是 Mor Nitzan、Maria Dyshel、 Noa Weiss 和 Guy Keren 的联合作品。

TL;DR 空间数据(例如,一段时间内车辆的 GPS 位置)可以用图形表示。这些图形的复杂结构可以压缩成数学对象,即“特征值”。这些特征值易于计算,并且非常有利于区分空间行为(例如,驾驶员的行为)。你可以把那些特征值作为新的特征!

我喜欢这个正在讨论的魔术的一点是:

  1. 它可以很容易地应用于许多领域(下面我将给出几个例子)。
  2. 它捕捉到数据中真实的行为,否则很难处理。
  3. 人们不使用它是因为其中涉及的数学听起来很吓人。但是:
  4. 从 Python 和 R 到 Matlab——它在您使用的任何标准 ML/DS 平台上都非常容易使用!您可能甚至不需要导入/加载新的包/库。

让我们开始吧。

数据——什么是“空间”?

我们专注于空间数据,这些数据可以是地理空间数据,如来自一队汽车的 GPS 信号,或者当应用程序用户在智能手机上使用该应用程序时的地理位置。它也可以在更抽象的空间中是“空间的”,例如在线用户在网站之间移动,或者模因(或新冠肺炎)如何在社交网络中传播。

作为一个具体的例子,我将把重点放在我们最初想到这个想法的数据上: Windward 数据。

Windward 是一家专注于海事情报的初创公司。他们有很多关于船只的独特数据,但为了简单起见,我们将集中于船只的 GPS 位置,时间分辨率为,比方说,每小时。

想象一下,我们有 10 万艘船只的数据:过去 5 年中每小时的 GPS 定位。即使有如此简单的数据,你也可以设计出许多特征:船速、方向、领海、距海上兴趣点(如海峡、港口等)的距离。),可以添加领域知识和外部数据(河流、海峡、国家、港口、海洋深度、日光、天气等)。),但是空间数据有一个重要的方面可能会丢失。它隐藏得很好,很难直接进入。

但是我们一会儿会讲到。让我们先确定我们的目标。

目标

我们将专注于一个经典的监督分类问题,但我要介绍的特征也适用于其他任务:它们只是真实现象的非常好的描述符。它们封装了主要信息。

分类挑战:有不同类型的海上船只,从微型渔船到超级油轮和散装船。我们的目标是将 100K 艘船只分为 10 种类型。

挑战

假设数据是干净的、平衡的,并且有足够的训练数据。每个初学数据的科学家都会很容易地根据每小时的 GPS 位置设计出很酷的要素,但是空间行为中有一个方面很难编码为要素,即使对于专家来说也是如此(这种困难可能是固有的,我将在后面介绍)。

我们用一个直观的例子来解释这个。

想想夏威夷的一艘渔船和意大利的一艘渔船,将它们与一艘大型油轮相比,你会注意到不同之处。渔船往往有一个“母港”,进进出出。也许每天,也许每月——但运动“形状”保持相似。相反,离开港口的油轮通常会以几乎直线的方式驶往目的地并返回。在地图上,夏威夷和意大利的两艘渔船将画出一个星形,而油轮将画出巨大的之字形穿过海洋。

这显然是一个过度简化的问题,但是我希望现在已经清楚我之前提到的是什么类型的信息了:当你看着他们在地图上画出的形状时,很容易区分他们。但是我们怎样才能把这个图编码成一个特征呢?图论来拯救我们了。

数学部分

这里我做一个简短的 D 游,简单介绍一些你可能比较熟悉的术语:图,邻接矩阵,特征值。我将详细说明一个图的频谱及其特性。也就是说,我们不会进入精确、正式的定义(如果你感兴趣,我在下面添加了参考资料)。相反,我会试着复习一般的想法和相关的直觉。

我确实假设了一些关于矩阵特征值的先验知识。如果你不太关心数学,你可以跳到下一节。

图表。 是由一组顶点 V(又名节点)和一组 E(又名链接)组成的数学对象。每条边是两个顶点之间的链接。边可以是有向的,使得边 eᵢⱼ是从第个顶点到第个顶点的边(但不是反之亦然),但是为了简单起见,我们将假设一个无向图,其中对于每个边 eᵢⱼ还存在一个边 eⱼᵢ.

邻接矩阵。一种表示图形的方法是用方阵:如果有边 eᵢⱼ,矩阵中的(I,j)项为 1,否则为 0。这个矩阵被称为图的“邻接矩阵”,它是一个方阵(旁注:它也是拉普拉斯矩阵的平方)。

特征值。不用深入定义,我只是提醒你,对于一个正方形 n×n 矩阵,特征值是具有代数和几何意义的 n 数。每个特征值都有一个特征向量。图的特征值/向量是其邻接矩阵的特征值/向量。

图的特征值是图的邻接矩阵的特征值(在文献中存在替代定义,例如使用归一化的拉普拉斯算子,但是它们是相关的)。

图的特征值/向量反映了非常有趣的数学属性(如图的连通性、图的切割、扩展等),它们在其他领域也得到很好的利用,如物理学(扩散和逾渗理论)、社会网络(中心性测量、模因和病毒的传播)和机器学习(如特征向量中心性测量,如著名的 PageRank 算法)。

图谱及其性质。如果我们把特征值按降序排列,串联成一个向量——这个向量就是所谓的“图的谱”。

在图论的其他未决问题中,有一些美丽的现象已经被实验观察到(例如,通过计算机模拟),尽管它们还有待证明。一些最有趣的问题是相关图的谱 : 什么时候两个不同的图会有相似的谱?谱完全相同的两个图什么时候会不一样?两个(随机)图有相似谱的概率是多少?

数学家们对图形的相似性/距离和图形的频谱之间的联系知之甚少——其中的东西比看上去的要多。但是我们知道的一点点就足够了。

我们所知道的是

  1. 具有完全相同拓扑的两个图,即等于节点的排列(也称为同构),将具有完全相同的谱。
  2. 另一个方向并不总是正确的:有些图完全不同,但却有完全相同的频谱(又名“同频谱”)

但是我们也有以下两个猜想的经验证据:

  1. 高度相似的图形往往具有相似的光谱。
  2. 高度不同的图形往往有非常不同的光谱。

看到我如何使用“相似”、“不同”和“倾向于”而不解释我的确切意思了吗?这是因为确切的定义和实际的推测陈述有些技术性。但这是你应该采纳的主要直觉。

尽管上述猜想仍未被证实——它们被用于实际的算法。这个想法来自于这样一个事实,即图的谱被用于图同构的启发式解决方案[3,4],那么为什么不用于“几乎同构”呢?在社交网络中,它被成功地用于社交图中的模式检测[5]。

为了我们的目的,我们也要依靠这些推测!

关于图的光谱,我们还有很多不知道的。当我们逐渐在两个图形之间移动时,频谱究竟是如何变化的?它在某种意义上是单调的吗?随机图共谱的概率是多少(见[6])?漂亮的开放式问题,我甚至没有从特征向量的性质开始!

但是我们所知道的一点点足以帮助我们满足今天的需求。

这个技巧来自于图论和复杂性理论交汇处的另一个突出的公开问题:图同构。给定两个图——不知道验证它们是否同构(即,顶点排列的拓扑相同)有多难。图的相似性问题与图的同构问题密切相关,因此困难可能是固有的。在图同构中,技巧是使用图谱的近似:不同的图谱意味着不同的同构。您可以将频谱视为图形空间到低维向量空间的低冲突散列函数。

换句话说:为了测量图形之间的相似性,我们将图形嵌入到一个 n 维向量空间中。嵌入中的欧几里德距离定义了图形空间上的距离。如果您想更深入地研究,请注意谱的 l2- 范数是邻接矩阵的迹的根,它也是拉普拉斯矩阵的 Frobenius 范数 = sqrt(tr(A))

让我们进行最后一步并降低维数:如果您记得 PCA 中特征值用于降维的用法,这里我们可以做同样的技巧:我们可以只取前 k 个特征值(实际上,其余的通常非常接近零,无论如何都可以忽略不计)。

总结一下数学:

  • 我们通过获取邻接矩阵的特征值将图嵌入到低维空间,也就是谱空间。
  • 有(未证实的)现象表明,图之间的相似性在谱空间中也趋于保持(使用 l2 范数)。
  • 如同在 PCA 中一样,我们可以只考虑 k 个最大特征值,而不会丢失太多信息。

最后,我们可以回到海事数据(或任何您感兴趣的空间数据)。

诀窍是:嵌入船作为它的图谱

让我们来关注一艘在海洋中航行的船只。我们为每个这样的容器定义一个图形:

节点定义海洋上的节点(例如港口、海峡、海洋中预定义的多边形、交通分隔方案等。).请注意,我们对所有血管使用相同的节点,只有边缘会有所不同。

如果给定的船直接在两个节点之间航行,我们说这两个节点之间有边。(这个定义很好商量,见下文改进)。

现在,最后每艘船都有一个描述其空间行为的图表,我们可以谈论“船的光谱”。

船舶→图形→矩阵→光谱→距离。看哪!

我们之前问过:既然我们可以通过观察船只在地图上绘制的形状来在视觉上区分它们,那么我们可以将这种绘制编码为一种特征吗?

现在我们可以用图论来回答这个问题:

频谱是将海上船只嵌入到低维向量空间中,捕捉船只的空间行为。

显示魔术背后的逻辑的图表。如果两条血管的空间行为相似,那么它们的特征值也趋于相似。虽然另一个方向不总是正确的,但在实践中,特征值是相似性的一个很好的近似值。图片作者。

在现实生活中如何使用?

在现实生活中,你有更多关于每艘船的信息。因此,您实际想要做的是将新的图形特征连接到其他特征。

让我们看看整个过程:

  1. 为每个容器创建一个图表。
  2. 对于每个计算光谱。取顶- k 个特征值。
  3. 使用这些 k 特征值作为 k 特征(如果有的话,连接到其他特征)。
  4. 尽情享受吧!

可能的改进

为了简单起见,我描述了基本方法,但是您可以通过使用更微妙或更复杂的版本来改进您的结果:

  • 边缘的定义可以根据您的任务进行改进。为了鲁棒性,可以只考虑船只使用超过阈值的边缘。你可以选择无方向、无权重的边,或者尝试使用多边或超边。
  • 与其使用邻接矩阵的特征值,不如使用(归一化的)拉普拉斯矩阵的奇异值,甚至加权矩阵的奇异值。[1]
  • 为了提高效率,不要计算所有特征值然后取 top- k,考虑使用一个只计算 top- k 奇异值,例如在 python 中使用 scipy.sparse.linalg.svdssk learn . decomposition . truncated SVD
  • 真实的图通常是高度稀疏的。所以不要使用集中了这些特性的 SVD 库,因为这不会改变奇异值,而只会因为失去矩阵的稀疏性而增加计算量。

结论

现代数据的互连性让我们别无选择,只能接受图表视角。通过利用图论的力量,我们从复杂的数据中提取了更多的隐藏信息。

上面介绍的技术——使用特征值——应用于生产中真实数据的几个场景中。它改进了分类模型(如上所述),但在聚类任务、风险模型和其他方面也显示了良好的结果。

有两个“困难”的子问题必须被克服:首先,为每艘船创建一个合理的图表。第二,通过取特征值来嵌入图。

在处理您的数据时,我建议您将任务分成两部分:首先,您如何将您的数据描述为一个或多个图表?第二,由于图是“重”对象,您应该使用哪些图论工具将数据嵌入到低维空间中?

请,让我知道如果你使用了这个技巧并且在你的模型中看到了好的坏的结果。

参考

良好的起点

[1]朱,张平,&威尔逊,R. C. (2005 年 9 月).比较图的图谱研究。在 BMVC。

[2]谱图论,丹尼尔·a·斯皮尔曼——课堂讲稿。

图形匹配/同构问题

[3]梅山,S. (1988 年)。加权图匹配问题的特征分解方法。IEEE 模式分析和机器智能汇刊,10(5),695–703。

[4]徐,李,王,等(2001).快速检索属性图中结构模式的 PCA 方法。IEEE 系统、人和控制论汇刊,B 部分(控制论),31(5),812–817。

社交网络中的实际应用示例

[5] Leskovec,j .,Singh,a .,& Kleinberg,J. (2006 年 4 月)。推荐网络中的影响模式。亚太知识发现和数据挖掘会议(第 380-389 页)。斯普林格,柏林,海德堡。

共谱图

[6]h . hae mers 和 e . Spence(2004 年)。共谱图的计数。欧洲组合学杂志25 (2),199–211。

SAP HANA 支持的空间数据科学

原文:https://towardsdatascience.com/spatial-data-science-powered-by-sap-hana-9d1153afa577?source=collection_archive---------29-----------------------

了解如何在 SAP HANA 中加载、准备和分析空间数据

kepler.gl 可视化的滑行轨迹

在数据科学领域,空间数据变得越来越重要。因此,(地理)空间数据科学高级(地理)空间分析有望成为 2020 年的趋势话题之一。由于其将多模型数据嵌入式机器学习无缝集成,SAP HANA 是这一不断发展的学科的首选平台。

为了支持数据科学家的日常工作,SAP HANA 提供 Python 集成已经有一段时间了。用于机器学习算法的 SAP HANA Python 客户端 API甚至支持从 Python 调用数据库内 ML 算法。通过这种集成,数据科学家可以完全用 Python 构建机器学习模型,同时将算法应用于数据库,而无需将实际数据传输到客户端。

在这篇博客中,我想展示一些基本的地理空间分析以及与 SAP HANA 嵌入式机器学习的集成。我将从数据科学家的角度出发,使用 Jupyter 笔记本进行建模。

尽管“空间不仅仅是地图”这一事实,但可视化空间分析的结果始终是一个好主意。有各种各样的图书馆在 Jupyter 笔记本中提供地理空间可视化,其中follow是最著名的一个。尽管如此,在使用了其中几个库之后,我决定在这个博客中使用 kepler.gl 。kepler.gl 是用于地理空间分析可视化的开源应用程序,可用于 Jupyter 笔记本。它特别适用于大量数据,并且在编码时具有相当低的接触体验(这是我选择 kepler.gl 的主要原因)。

本例使用的技术

包括可再现性分析的完整 Jupyter 笔记本可以从 GitHubT5下载。它被设计为在普通 SAP HANA Express 实例上运行。如果你对 Jupyter 笔记本本身有信心,你也可以直接跳到它上面,跳过博客的其余部分。

如果您不喜欢 Python 或者喜欢更详细的描述,我将在下面向您展示最重要的步骤和想法。

数据集

在为地理空间分析寻找合适的数据集时,我在 Kaggle 上偶然发现了这个不错的数据集:https://www.kaggle.com/crailtap/taxi-trajectory

数据集包含波尔图市的出租车轨迹,格式为linestring。事实上,它不仅仅包含点数据,这使得它特别有趣。数据本身可以作为 CSV 文件下载,我们将通过 Jupyter 笔记本将其导入 SAP HANA。

为了更好地理解数据的背景,我们还将纳入来自 OpenStreetMap 的兴趣点(POI)数据,因为我们预计出租车的上客位置与高密度的 POI 相关。

用于此示例的数据

设置和配置

从 Jupyter 中的地理空间分析开始,我们需要设置并导入所需的库。这些是我们将使用的主要软件包:

  • Pandasgeo Pandas用于管理 Python 中的数据。虽然大多数人都熟悉熊猫,但 GeoPandas 只为处理空间数据的开发人员所知。可以理解为增强了地理空间能力的熊猫。****
  • SQLAlchemy 用于数据库通信。SQLAlchemy 有一个可用的 SAP HANA 方言。请按照这些说明进行初始设置。
  • OSMnx 是一个下载 OpenStreetMap 数据的舒适库。我们将使用这个库将 POI 数据加载到 SAP HANA,如这篇博客所述。
  • kepler.gl 用于可视化地理空间数据。KeplerGL 是一个用于地理空间分析可视化的开源应用程序,已经嵌入到 Jupyter 笔记本中。从数据帧中可视化几何图形本质上是一行程序,您将在后面看到。
  • hana_ml 用于调用数据库内机器学习算法。在这里,您必须小心地为您的 APL 安装选择正确的版本。这篇博客(尤其是 APL4 附录)帮助我为我的 HANA Express 实例设置了正确的版本。

所需进口的完整清单

值得一提的是,有一些基于 Jupyter 笔记本的 SQLAlchemy 的内联 SQL 单元格魔术,我已经使用过了。cell magic 支持将 SQL 语句或多或少地直接写入一个单元格,而无需 Python 开销。

SQL 单元格魔术

关于 SQL 魔术的更多细节请参考这篇博客

保存和增强轨迹数据

我们希望将 CSV 文件加载到 Python 中,并保存在 SAP HANA 中以供进一步处理。使用 Pandas 将 CSV 导入数据帧只需一行代码:

一旦数据被加载到数据帧中,我们需要直接获取一些数据类型(例如,将时间戳转换为适当的日期时间字段)并过滤掉不包含轨迹数据的行项目。有一定数量的出租车没有或只有一个 GPS 坐标。

在 SAP HANA 中保存数据是一个双重过程:

  • 首先,我们将持久化没有地理空间数据类型的平面数据

  • 其次,我们将从数据库中的文本表示生成地理空间列。

初始数据集只是提供一个具有一定数量的点的轨迹列。从数据描述我们知道每隔 15 秒就有一个 GPS 坐标。包含 10 个 GPS 坐标的行程将持续 150 秒。

我们可以从我们的数据集中导出该特征以及其他空间和非空间属性。以下语句将添加行程的起点、终点、持续时间、开始时间、结束时间、距离和平均速度。

我们上面使用的 HANA 空间函数如下:

我们可以使用增强的属性来检查数据的不一致性。例如,我们会发现一定数量的旅行带有非常(!)平均速度高。在假定无罪的情况下,我们会将此视为“数据质量问题”。

我们假设我们现在有一个相当干净的数据集等待我们分析!

选择正确的空间参考系统

你可能已经注意到,我把数据转换成了另一个空间参考系统 (SRS)。在我的代码中,我使用了变量' $srid ',它是我用平面 SRS 预先配置的。切换到平面投影的原因是与圆形地球计算相比提高了计算性能。

资料来源:SAP

如今,地图可视化最常用的 SRS 是所谓的 Web 墨卡托投影( SRID 3857 )。我们已经习惯了这样一个事实,格陵兰岛看起来和非洲一样大,而实际上,格陵兰岛的面积只是非洲面积的一小部分。

投影的失真随着到赤道的距离而增加。对于 web 地图来说不一定是问题的内容,在进行空间分析时可能会成为问题。对于我的分析,我选择了适用于葡萄牙的带有 SRID 5018 的 SRS(意味着失真在葡萄牙的区域最小化)。GPS 坐标使用 SRID 4326 ,这是一个绕地球模型,能够以计算能力为代价进行精确计算。

那么为什么不干脆选择 3857 呢?所有的网络地图都使用它,最终,葡萄牙比格陵兰岛更接近赤道。为了说明我们在计算中引入的误差,我比较了不同空间参考系统中出租车的平均行驶距离:

原来 SRS 3857 中的平均行驶距离要高出30%以上(!)比精确的绕地球计算。与适用于葡萄牙的 SRS 5018 进行同样的比较,误差仅为 0.01%,考虑到我们赢得的性能增益,这是可以维持的。

在为您的分析选择空间参考系统时,请始终牢记这一点!

包括兴趣点数据

最近,我发表了一篇关于如何使用 OSMnx 将兴趣点数据从 OpenStreetMap 加载到 SAP HANA 的博客:

在哪里(不)给我的车充电?SAP HANA 中的 OpenStreetMap 兴趣点

基本上,我做了同样的事情来检索波尔图的 POI 数据,但有一个有趣的不同点:我分析了该区域,而不是查询搜索字符串‘Porto’的 POI 数据,我的轨迹数据覆盖了。也有一些去里斯本的旅行,我也可能对这些目的地附近的兴趣点感兴趣。我应用了ST _ convegulhullaggr来计算所有轨迹上的凸包,并使用生成的多边形通过 OSMnx 查询 poi。

快速可视化揭示了凸包的大小。

用 kepler.gl 生成上面的可视化结果只需要一行代码,其中 DataFrame (df_poi_shape)被传递给地图。执行单元代码时会出现交互式 kepler.gl 客户端。

包含多边形的 WKT 表示的数据帧,覆盖了所有轨迹,现在可以传递给 OSMnx。

最后,我们将包含兴趣点的地理数据框架存储到一个 HANA 表中(在我的例子中命名为 OSM_POI)。我们可以利用空间连接将这些数据整合到我们的分析中。

添加参考网格

对于某些分析(即应用 ML 算法),可能有利的是去掉地理空间维度,代之以网格索引来引用和聚合某些数据点。一个六边形网格比其他网格有一定的优势(例如矩形)。我们保留了一个横跨 250 个六边形单元的参考网格供以后使用。

网格仅覆盖滑行轨迹的起点和终点,这对于我们正在进行的分析是足够的。我们还使用 ST_CENTROID 添加了每个网格单元的质心。

基础空间分析

完成所有准备工作后,我们现在准备好进行一些非常基本的空间分析。计算几个聚合量怎么样?

对你来说,这看起来不够“空间感”吗?请记住,所有这些测量值(距离、持续时间和速度)都是使用空间数据库功能从原始轨迹数据中获得的。我们刚刚从彻底的数据准备中获益。

此外,我们可以快速获取一些样本轨迹,并将其与 POI 数据一起可视化。请注意,kepler.gl 需要转换回 SRID 4326 以及表示为 WKT 或 GeoJSON。为此,我们使用 ST_TRANSFORMST_ASWKT (或者 ST_ASGEOJSON )。

可视化结果,我们还可以发现我们感兴趣的“点”不仅包含点数据,还包含多边形,如大型医院建筑

分析提货地点

在一个城市,当你看到出租车的上车地点时,你会期望看到一些模式。当然,你会预计到机场的高需求,以及夜间或凌晨的夜生活区的高需求。

提货地点并不总是相同的。为了以有意义的方式聚合它们,我们再次应用了一个六边形聚类,它以对数标度计算每个位置聚类的出租车搭载数量(以使可视化更有意义)。****

这一次,我们不仅可视化了聚类多边形,还根据拾取的数量给多边形着色。此外,我们还引入了第三维度,并根据拾音器的数量来缩放每个六边形区域的高度。

是的,这对于颜色编码来说是多余的。那么我们为什么要这样做呢?只因为我们能!****

我们可以立刻认出波尔图郊区的机场。左边是拾音密度高的区域。然而,接客率最高的地区就在波尔图的正中心。

我们可以通过使用带有 ST_INTERSECTS 的空间连接来检索前 3 个单元格和这些单元格中的 poi。

我们可以看到,排名靠前的地点是波尔图的历史中心(靠近圣本托站;左单元)以及主火车站(Campanhã右边的牢房),旁边有一个巨大的出租车站。

到目前为止,我们已经看到了皮卡的总数。当然,如前所述,这其中也有时间因素。某些地区在某些时候可能会有较高的流量。为了分析这种行为,我们引入了第二个宁滨维度。早些时候,我们使用六边形聚类构建了一个位置宁滨,现在我们通过聚合同一小时内的所有出租车乘坐次数来添加一个时间宁滨

这个数据集可以交给 kepler.gl,它有一个特性可以生成一个随时间变化的动画。该视频显示了预期的高峰时间以及圣诞节和新年期间的某些不规则性。

去机场的路线

我们认同我们之前的分析,机场在出租车乘坐方面扮演着特殊的角色。这一点也不奇怪。让我们再深入一点,看看接送地点之间的关系。去某个地方的旅行通常从哪里开始?****

****我们不是基于位置来回答这个问题,而是基于网格,使用我们之前创建的参考网格。因此,我们想回答的问题是:“当到达某个其他单元时,旅行通常从哪个六边形单元开始?”。以下语句计算参考网格中所有观察到的开始和结束单元格组合的行程数。

同样,我们需要更多的转换( ST_XST_Y )来适应可视化框架,这种可视化似乎需要单独的纬度和经度值。

这个结果可以用 3D 流可视化。我们可以很容易地发现,大多数前往机场的航班都是从圣本托站附近出发的。(第二多的是坎帕尼亚的波尔图总站)****

但是从圣便当站到机场的最佳方式是什么?要回答这个问题,我们可以查看从各个格网单元开始到包含机场的格网单元结束的所有行程的持续时间。就这么办吧!****

在数据中,我们可以观察到一些奇怪的行程,我们通过只允许短于车站和机场之间直线距离 2 倍的行程来过滤掉这些行程。在我们的抽象层次上,这对应于各个六边形单元的质心之间的 ST_DISTANCE

为了可视化,我们根据总行程持续时间对轨迹进行着色(绿色表示快;红色慢)。

我们可以看到有三条道路通往机场。大多数交通使用中间的主干道,而一些(大概是聪明的)司机使用较小的便道。当然,这些行程的距离更长。然而,许多使用较小通道的出行导致出行持续时间相当短。****

预测旅行的持续时间

到目前为止,我们已经对底层数据集有了很好的理解。我们主要处理地理空间功能和可视化。我们如何将这些知识整合到机器学习模型中?我们如何调整模型来理解地理空间数据?

阶梯一的答案是:给定正确的预处理,我们的机器学习模型不需要实际理解地理空间数据!使用我们的参考网格、空间连接和聚合,我们还可以将各个网格单元的 id 作为分类变量输入到我们的模型中。

只有我们知道这个 id 代表一个位置,我们可以将它映射回地图上——我们的 ML 模型不必关心那些繁琐的细节。

为了预测旅行的持续时间,我们使用 SAP HANA 的自动预测库 (APL)训练了一个回归模型。使用用于机器学习算法的 Python 客户端 API可以方便地调用 APL。使用这种方法,模型训练将被下推到数据库级别。我们将只向 Python 客户机传输数据,这是审查模型性能所必需的!

首先,我们将收集训练数据

HANA 数据帧‘hdf _ trajectories’的外观和的行为与 Pandas 数据帧相似,除了数据不会传输到客户端,除非‘collect()’被调用。

例如,可以用这条语句将前五个条目传输给客户机。

我们现在将这个 HANA 数据帧传递给模型训练函数,以训练一个以“持续时间”为因变量的回归模型

我们只允许 STARTTIME、HEXID_START 和 HEXID_END 作为输入变量。如前所述,我们的网格单元的 id 不是几何图形。APL 将自动处理时间戳,并生成一天中的小时、一周中的天、一月中的天等特征。

在博客的剩余部分,我将重点关注模型任务报告结果本身,而不是可视化它的代码。如果你对更多细节感兴趣,可以看看全 Jupyter 笔记本

模型性能的主要指标是预测力预测置信度。下图表明我们训练了一个稳定模型,该模型具有一定的预测能力(值为 0 表示猜测;1 将是有远见的)。

哪些因素会影响模型的结果?绘制贡献变量我们可以看到,空间维度的贡献最高。这是明显的,随着持续时间的增加,起点和目标之间的距离越来越远。

此外,我们还可以发现,一天中的某个小时贡献第二大,最后,一周中的某一天也是模型的一部分。请注意,我们启用了自动特征选择,这允许模型忽略无贡献的尺寸。

对于一天中的一个小时,我们预计会出现交通较慢、出行时间增加的高峰时段。我们可以通过绘制类别显著性来验证这一点。

上图告诉我们,我们预计下午 5 点到 6 点之间的最大延迟(对“持续时间”的积极贡献),我们预计凌晨 2 点到 3 点之间的最短行程持续时间(对“持续时间”的消极贡献)。

是啊,有道理!所以,我们的模型是有一定道理的!

最后,我们可以使用我们的模型来预测旅行时间。我们已经知道了索本托车站和机场,那么我们还应该选择什么呢?

我们的模型表明,2 月 10 日星期一早上 5 点乘坐出租车将持续 966 秒( ~16 分钟)。

同样的行程在下午 5 点将花费我们到 21 分钟

这听起来合理吗?让我们将它与一些可靠的来源进行比较,并让我们最喜欢的网络路由服务进行猜测(在撰写本文时,2 月 10 日是一个未来的日期)。

这实际上与我们的预测非常接近!请记住,我们的预测是基于 2013 年的一些出租车数据,而网络预测是基于数以亿计的智能手机用户提交他们的旅行地理位置和旅行速度。

预测出租车的实际目的地怎么样?你可以在我的后续博客中找到更多信息: 用 SAP HANA 预测出租车目的地

摘要

首先我得承认我没去过波尔图。在写这篇博客的过程中,我有一种感觉,我应该亲自尝试一下从机场到历史中心的出租车之旅。我肯定把这个加到我的遗愿清单上了。

埃维拉尔多·科埃略Unsplash 拍摄的照片

从技术面来看,我们看到了以下情况:

  1. 下载和保存地理空间数据集
  2. 使用基本计算准备和增强地理空间数据集
  3. 用空间谓词连接数据集
  4. 创建参考格网以提取地理空间细节
  5. 在 Jupyter 笔记本中可视化地理空间数据
  6. 应用机器学习模型来预测实际上不包含任何几何数据类型列的数据集的行驶时间

我们所做的事情背后有一个通用的模式,这适用于许多用例:

准备 增强 抽象 形象化 最后但并非最不重要的 生成洞察力

这个价值链不一定要由一个人或一个部门来执行。可能同时涉及 IT、GIS、数据科学、业务部门。将(地理)空间数据科学集成到 SAP HANA 中,使您能够打破孤岛,并打开数据的360°全方位视图,而不是在部门之间移动部分信息。

最后一件事:我最喜欢的网络路由服务也有一个自动完成的网络搜索功能。这是我为“波尔图骚 b”得到的第一个建议:

****

原载于 2020 年 2 月 24 日 https://blogs.sap.com。****

使用 PostgreSQL 的空间数据科学:几何

原文:https://towardsdatascience.com/spatial-data-science-with-postgresql-geometries-c00387755700?source=collection_archive---------20-----------------------

波斯特吉斯

PostGIS 几何类型和函数的初学者演练。

罗纳德·杨在 Unsplash 上的照片

T4:几何是将地理空间数据结合在一起的粘合剂。它们是任何空间数据处理不可或缺的一部分。在本教程中,我将介绍 Postgis 中一些不同类型的几何。我们还将通过真实世界的数据示例触及一些最常用的函数。

在我的上一篇文章中,我解释了如何安装 PostgreSQL 和激活 Postgis 扩展。我们还讲述了如何在 PgAdmin 中快速入门、创建空间数据库和加载空间数据。因此,如果您还没有设置 PostgreSQL 和 PostGIS,您可以以此为起点。

[## 使用 PostgreSQL/PostGIS 的空间数据科学

PostgreSQL 和 Python 空间数据入门实用指南。

towardsdatascience.com](/spatial-data-science-with-postgresql-postgis-2f941c8c367a)

几何

在 PostGIS 中,开放地理空间联盟(OGC)和“SQL 的简单特征”规范中指定的所有对象和函数都可用。Postgis 中支持的几何数据类型包括:

POINT()LINESTRING()POLYGON((),())MULTIPOINT((),())MULTILINESTRING((),())MULTIPOLYGON(((),()), (()))GEOMETRYCOLLECTION(POINT(),LINESTRING())

PostGIS 还扩展了栅格数据类型以及 3d(高度表示为 Z)和 4d(时间通常表示为 M)几何类型。

将空间数据转换为 PostGIS 几何可让您充分利用 PostGIS 中的空间功能。一个新的最受欢迎的特性是 PgAdmin 中内置的几何可视化,我们将在下一节中看到。在本教程中,我们只涉及基本的几何,点,多边形和直线。其他几何图形是这些基本几何图形类型的扩展版本。

我们从点几何开始,它通常指的是空间中没有大小和宽度的点。让我们看看现实世界数据中的点几何,并在 PostGIS 中探索它的一些方法。

传统的 SQL 查询包含 SELECT 和 From。在下一段 SQL 中,我们返回前面上传的 tfl_stations 数据集的前十行。

SELECT id, name, zone, geom 
FROM public.tfl_stations
LIMIT 10;

电台表

这个结果,但是,有几何列,如果你点击眼睛,你会看到你的几何渲染如下图。

几何可视化

要获得数据的几何类型,可以使用 ST_GeometryType(geom) 来找出。例如,以下 SQL 代码会返回车站数据集的几何数据类型。

SELECT ST_GeometryType(geom)
FROM tfl_stations
FETCH FIRST ROW ONLY

正如我们所料,这个查询返回 ST_Point。

“ST_Point”

如果您喜欢将几何图形作为带坐标的文本,也可以使用 ST_AsText(geom)。下面的查询返回每个点的坐标。

SELECT name, zone, ST_AsText(geom)
FROM tfl_stations
LIMIT 10;

ST_AsText

要分别访问坐标,我们可以使用 ST_X 和 ST_Y,如下例所示。

SELECT name, zone, ST_X(GEOM) AS Longitude, ST_Y(GEOM) AS Latitude
FROM tfl_stations
FETCH FIRST 10 ROW ONLY

圣 X(GEOM)和圣 Y(GEOM)

以下是 PostGIS 中点函数的一些亮点:

线串

线串是两点或多点之间连接的直线。在 PostGIS 中,您可以通过连接点构建线。

我们已经上传了街道数据集,所以让我们查询数据。

SELECT streetname, streetclas, streettype, ST_Length(geom) AS STREET_LENGTH
FROM public.streets
LIMIT 10;

使用 PostGIS 中的 ST_length,我们只需在数据集中创建一个附加列,使用 PostGIS 中的 ST_Length 计算每条街道的长度。

ST _ 长度

这是什么测量单位?您需要查看数据集的空间参考系统。要查找数据集使用的 SRID,您可以使用 Find_SRID 或圣 SRID 进行查找。我们这里用的是后一种

SELECT ST_SRID(geom)
FROM public.streets
LIMIT 1;

我们发现我们有 EPSG:26917,它使用米作为度量单位。让我们把街道长度改为公里,因为我们现在知道米是计量单位。

SELECT streetname, streetclas, streettype, ST_Length(geom)/1000 AS STREET_LENGTH_KM
FROM public.streets
LIMIT 10;

以下是 PostGIS 中线串函数的一些亮点:

  • ST_LinestringFromWKBST_MakeLine →构建线串几何图形。
  • ST _ shortest line-返回两个几何图形之间的二维最短直线。
  • ST_LongestLine —返回两个几何图形的二维最长线点。如果不止一行,函数将只返回找到的第一个最长的行。返回的行总是以 g1 开始,以 g2 结束。该函数返回的直线长度将始终与 st_maxdistance 为 g1 和 g2 返回的长度相同。

多边形

多边形由闭合的线串构成。多边形包含所有封闭的区域及其边界,而边界中封闭的线串称为外环

在下一个示例中,我们将使用 PostGIS 中的 ST_Area(geom)函数计算每个建筑物的面积。

SELECT theme, ST_Area(geom)
FROM public.buildings
LIMIT 10;

以下是 PostGIS 中多边形函数的一些亮点:

  • ST_MakePolygonST_PolygonST_PolygonFromText →创建一个多边形几何体。
  • ST_ExteriorRing →返回一个代表POLYGON几何体外环的字符串。如果几何图形不是多边形,则返回 NULL。不适用于多重多边形
  • ST_NRings —如果几何图形是多边形或多多边形,则返回环的数量。
  • ST _ Within-如果几何图形 A 完全在几何图形 b 内,则返回 true

结论

几何构成了空间数据分析的基本块。在本教程中,我们看到了 PostGIS 中不同类型的几何。通过一些例子,我们还介绍了每种几何类型的基本功能,包括点、多边形和线串。

使用 PostgreSQL/PostGIS 的空间数据科学

原文:https://towardsdatascience.com/spatial-data-science-with-postgresql-postgis-2f941c8c367a?source=collection_archive---------13-----------------------

波斯特吉斯

PostgreSQL 和 Python 空间数据入门实用指南。

丹尼斯·库默在 Unsplash 上拍摄的照片

空间性不特别!如果您曾经尝试过在普通 SQL 中使用空间数据,那么处理几何可能并不简单。然而,使用空间 SQL,感觉就像处理表中的另一列。

在本指南中,我们将介绍如何设置和开始使用空间 SQL。我们将安装 g PostgreSQL,激活 PostGIS 扩展并执行一些简单的空间 SQL 操作。在本文的后面,我们将了解如何将空间 SQL 整合到 Python 工作流中。

空间 SQL (PostGIS)

空间数据科学被邮政地理信息系统托起

空间 SQL 处理、利用和执行空间数据的空间操作。空间数据库针对存储、操作和查询具有已定义几何图形的地理数据进行了优化。

虽然有许多空间数据库提供商,但我通常更喜欢 PostgreSQL/PostGIS。PostGIS 在地理空间数据库领域也得到广泛应用——例如,Big Querry 就采用了它。

如果您是数据库领域的新手,您可能会问 PostgreSQL 和 PostGIS 之间的区别。我们可以这样想:PostgreSQL 是数据库 Database,而 PostGIS 是在 Postgresql 之上提供空间功能的扩展。

安装 PostgreSQL + PostGIS

安装 PostgreSQL 很简单。您需要为您的发行版选择二进制包:Windows、macOS 或 Linux。

[## 下载

PostgreSQL 对象关系数据库管理系统的核心有几个源代码和二进制…

www.postgresql.org](https://www.postgresql.org/download/)

一旦你下载了 EDB 的交互式安装程序,运行。或者按照发行版中给出的说明进行操作。我已经在这里包括(GIF 指令)如何添加 PostGIS 扩展到你的 PostgreSQL。我们还安装了 PgAdmin4 作为 PostgreSQL 的 GUI 界面。

确保在遵循交互式安装指南的同时检查 PgAdmin4 和 Stackbuilder。此步骤将允许我们稍后使用 PG Admin 4 用户界面。请务必检查 PostGIS 扩展,它使我们能够执行空间操作。

如果您想要操作系统的逐步指南,有一个交互式安装程序指南。

[## 简介— PostgreSQL 安装指南 12.0 文档

EnterpriseDB 创建的 PostgreSQL 安装程序旨在使在…上安装 PostgreSQL 变得快速而简单

www.enterprisedb.com](https://www.enterprisedb.com/edb-docs/d/postgresql/installation-getting-started/installation-guide-installers/12/index.html)

PgAdmin 4 GUI 界面

让我们熟悉一下我们将在本教程中使用的 PgAdmin GUI 界面。下面的 GIF 展示了界面。我们还创建了一个 PostGIS 扩展,它提供了我们稍后将使用的空间功能。

我们可以简单地创建一个 PostGIS 扩展,在 PgAdmin 查询工具中实现以下命令。

CREATE EXTENSION postgis;

运行该命令后,我们有大量新的空间函数可供使用。

让我们也使用 PgAdmin4 创建一个数据库。下面的 GIF 说明向您展示了如何创建一个新的数据库。

加载数据

加载地理空间数据不同于将 CSV 文件加载到 PostgreSQL 中。我们需要保留几何列。但是,有一种使用 Shapefile Loader 上传空间数据的简单方法。

以下 GIF 指令显示了如何将 shapefiles 加载到 PostgreSQL 数据库中。

在上传数据之前,您需要了解数据的坐标参考系统(CRS)是什么。在这种情况下,我已经在 QGIS 的帮助下查看了数据,并且我知道投影代码。但是如果你不确定,你可以输入 4326,这是常用的 WGS 84 码。

加载数据后,我们就可以使用 PostGIS 执行空间数据分析了。我也上传一些其他的文件到数据库:街道,边界和伦敦地铁站。我们将把它们作为

基本空间操作

空间 SQL 可以简化地理空间数据的处理和分析,还可以加快特定过程的速度。有数百种空间功能随时可供您使用。

让我们从一些简单的例子开始。在以后的文章中,我将更深入地研究空间 SQL 函数和过程。我已经上传了。

例 1:计算建筑物面积

SELECT objectid, 
       shapestare, 
       ROUND(ST_Area(geom)::numeric,2) AS AREA
FROM public.buildings

这个简单的 SQL 代码返回所有带有新面积列的建筑物,精度为两位小数。总的查询运行时间:659 毫秒。有 176023 行受到影响。

我们在这里使用的是ST_Area() PostGIS 函数。

例 2:计算每种街道类型的长度

SELECT streettype, Sum(ST_Length(geom)) AS length
FROM public.streets
GROUP BY 1
ORDER BY 2 DESC;

上述 SQL 代码对每种街道类型进行分组,并计算每种类型的长度。我们在这里使用的是ST_Length() PostGIS 函数。

用 Python 访问 PostGIS

好的一面是,您可以用 Python 轻松连接您的 PostGIS 数据库。Geopandas 是最常用的地理空间 python 库,可以从 PostGIS 中获取和读取 SQL 语句。以下是如何用 Python 连接数据库和运行空间 SQL 语句的方法。

import geopandas as gpd
import sqlalchemy as db# Create a database connection
engine = db.create_engine(‘postgresql+psycopg2://postgres:postgres@localhost:5432/postgis_database’)
con = engine.connect()# Read PostGIS database with Geopandas.
sql = “SELECT * FROM public.streets;”
data = gpd.read_postgis(sql=sql, con=con)

将您的 PostGIS 数据库引入 Python,为探索数据和可视化数据提供了其他可能性。让我们使用 Geopandas 和上下文底图来可视化我们所读取的街道数据集。

import geopandas as gpd
import matplotlib.pyplot as plt
import contextily as ctxfig, ax = plt.subplots(figsize=(18, 16))
data.to_crs(epsg=3857).plot(ax = ax,
 figsize=(20,18),
 color=”gray”,
 alpha=0.5,
 );
ctx.add_basemap(ax, url=ctx.providers.OpenStreetMap.Mapnik);

街道地图

结论

在本指南中,我们已经安装了 PostgreSQL 并激活了 PostGIS 扩展。我们已经了解了如何使用 PgAdmin 创建数据库,以及如何将空间数据上传到数据库 database 中。我们还看到了一些简单的空间 SQL 示例,最后,我们将空间数据库数据库集成到 Python 中。

我计划下一篇用空间 SQL 写空间数据处理的文章。敬请关注。

印度尼西亚城市规划的空间数据解决方案:了解地理数据框架

原文:https://towardsdatascience.com/spatial-data-solution-for-city-planning-in-indonesia-understanding-the-geodataframe-f50d58e6c9f2?source=collection_archive---------45-----------------------

爪哇岛西北部的人口(图片由作者提供,数据来源:印尼统计局

为了理解城市的行为和制定更好的政策,首先,我们必须理解城市内部的空间相互作用。如果城市没有包含空间要素的数据,这是不可能的。有些城市的数据集中有空间元素,但不幸的是,有些是不可计算的。这些空间元素必须是可计算的,因为可计算性降低了分析成本并提供了可靠的结果。GeoDataFrame 是一种简单的可计算且直观的“数据格式”,我建议将其作为解决方案,印度尼西亚的城市应该采购此类数据。

介绍

地理信息系统(GIS)是城市规划师必备的基本技能。利用 GIS,城市规划者可以管理、发布和分析空间数据。空间数据不同于普通数据,因为空间数据具有有时被人们忽略的空间元素。对“空间”的意识和直觉使城市规划师或地理学家变得强大。

作为一名印度尼西亚城市规划师,我每天都要处理空间数据,这篇文章的灵感来自印度尼西亚雅加达市公布的一个数据集。不得不说,雅加达市的空间数据仓库让我印象深刻!它是结构化的、可读的、可处理的,并且维护良好。你不需要数字化。这是一个很好的空间数据仓库的例子,许多行政区都必须查阅。但是有一个来自雅加达开放数据门户的“空间”数据提出了问题(至少对我来说):公共交通路线

雅加达公共交通路线(样本)(来源:雅加达公开数据)

路线是以人们可以理解的方式描述的,但在地理意义上却无法被计算机解析比如,在哪里“Jl。拉亚贝卡西"街?有多长?它是如何弯曲的?几何性质是什么?对于像我这样的数据分析师来说,这令人愤怒。我们必须找到这条街道,在地理信息系统软件中绘制出来,这样我们就可以用计算机做进一步的分析。这个(数字化)过程花费时间,时间就是金钱。毫无准备的数据耗费了分析师的成本,并产生了乏味的工作。

让我们来看另一个例子:一张地图,如下图所示。这应该是一张安格科特(“公共交通——小巴”)路线的地图。

安格科特 01 号公路。a(来源:万隆开放数据知识共享许可)

这不是一张地图本身,这是一张图表。“地图”中的几何特性只是一种图解表示。长度和距离不是真正的缩放距离,这就是为什么“地图”没有坐标系/参考。

应该理解包括如上所示的空间数据的尝试。它包含信息,但我们可以做得更多。

现在,我们应该如何通过计算来分析这些空间数据?如果你处理过很多数据,尤其是空间数据,我相信你一定很明白我在说什么。想象一下数字化和清理数据。这是没有效率的,只有一个人/实体应该做清理工作,而不是每个人都必须做。解决方案很简单,提供标准化、格式化和可解析的空间数据。

空间数据有如此多的格式。我猜最流行的是 Shapefile。shp)格式是由 Esri 在 90 年代开发的,非常古老!现在是 2020 年,距离 shapefile 时代已经过去了十年,肯定有许多数据和技术被开发出来。在使用 Python 的过程中,我注意到了地理数据框架和我遇到的空间数据问题之间的联系。因此,本文介绍了另一种地理空间数据——GeoDataFame,以及它如何解决印度尼西亚的空间数据属性问题。

本文由五部分组成:

  1. 什么是地理数据框架?(解释什么是地理数据框架以及它与传统电子表格有何不同)
  2. 为什么选择地理数据框架?(选择地理数据框架的原因,因为它对大多数人来说是直观的)
  3. 存储几何属性(显示几何数据的表现/形式。我们会在 Python 中使用 Shapely,但是不用担心 Python,你不用懂 Python)
  4. 我们如何存储和分发地理数据框架(以便熟悉和不熟悉空间数据的人仍能理解数据)
  5. 结束语(总结和建议我们应该如何开始)

什么是地理数据框架?

GeoDataFrame 是 GeoPandas 用作操作对象的空间数据框。当我们使用 GeoPandas 来分析和管理空间数据时,地理数据框架是目标对象。GeoPandas 中地理数据框架的官方解释是

"一个[**GeoDataFrame**](https://geopandas.org/reference/geopandas.GeoDataFrame.html#geopandas.GeoDataFrame)是包含一个[**GeoSeries**](https://geopandas.org/reference/geopandas.GeoSeries.html#geopandas.GeoSeries)的表格数据结构."地质公园

什么是地理系列? GeoSeries 是包含数据几何图形的向量。我们可以把一个向量想象成一个单列的电子表格。它在地理数据框中表示为一个名为“geometry”的列。我总是认为定义是令人困惑的,形象化总是有帮助的。以下是地理数据框架的外观:

地理数据框架(来源:作者,2020 年)

如您所见,地理数据框基本上是一个包含一列几何的电子表格。我想说的是,地理数据框架就像具有几何属性的行数据。每个要素(每行)都有值和几何属性。例如,如图所示,“Pulau Tidung”的人口为 11305,其几何在几何列中描述。

当我们绘图或进行空间分析时,这个“几何”列就是正在处理的列。剖析地理数据框架有 3 个组件:

  • 数据帧:这是数据的行和列。我们可以把它想象成一个 Ms. excel 电子表格。这里我们可以统计描述和分析这些特征。例如,哪个村庄的人口最多?每个村庄的人口密度如何?(但不是空间交互)
  • 几何图形:这是一个包含每行(特征)对应几何图形的列。这就是定义数据的空间元素
  • 坐标参考:几何图形的地理参考。例如,坐标 x=8,y=15 的点;这里每个 X 和 Y 可以引用的 0 在哪里?通常我们使用 WGS 1984(世界大地测量系统,EPSG 4326)或大多数人知道的经度和纬度。您可以看到,在第一张图中,在“geometry”列中,它保存了经度和纬度值)。

电子表格数据和地理数据框架(图片由作者提供;数据来源: Trafi, 开放数据 Jakarta)

绘制几何图形

我们还可以绘制几何线并制作地图。在这个例子中,雅加达市的人口。我们可以用 Choropleth 图来代替图表,这样我们可以概括空间关系。

雅加达人口(图片由作者提供,数据来源:印尼统计局)

为什么选择地理数据框架?

地理数据框架对普通人来说更具可读性。 GeoDataFrame 基本上就是一个表,大家都知道什么是表,所以很直观。不像 Shapefile,我想,不是每个人都知道。

如果您熟悉 python,它基本上是一个熊猫数据帧,带有一个保存几何属性的附加列。我们可以用熊猫的方法来建立地理数据框架。

存储几何数据

这个几何列中的值是什么?我们将使用一些 Python 术语,但这并不复杂,因为它非常简单。请容忍我一点。

有 3 种主要的空间数据类型:

  • 要点:这是非常直接的。点表示位置和地名。点是一维的,包含一个坐标。(除了多点,它有许多具有相同属性的坐标,但它们不构成线或区域)
  • 多段线:线或多段线是由一条线连接的坐标集合。我们可以使用折线来描述网络,如管道、道路或铁路网络。
  • 多边形:多边形是封闭的线,所以它有面积。用于描述行政边界、分区规则、地理分布图的基础。

所以空间数据基本上就是一堆坐标!

本质上,折线和多边形由顶点组成。这些顶点是几何列中保存的点的坐标。正如我们在下图中看到的,当一个几何值被展开时,它包含了很多经纬度的坐标,当我们可视化这些顶点时,它就画出了一个形状。

地理数据框架的几何(来源:作者,2020 年)

每个几何体都是 Shapely 的对象,是 Python 的库。Shapely 为了数学的目的研究几何。我们可以进行许多空间分析,例如相交、联合等等。这是一个非常简单的介绍:例如,如果我想创建一个几何对象,我需要做的是向 Python 编写一些命令,如下所示:

>> from shapely.geometry import Polygon
>> Polygon([(104.4, -6.2),(106.4, -7.0), (103.2, -8.9)]

这将创建一个矩形,因为有 3 个顶点(3 个坐标)。

几何输出(来源:作者,2020)

当然,要定义每个特征的几何形状,我们不能手动放置它。相反,我们可以集成 GPS 等传感器来自动执行任务,或者用一些脚本进行一些转换。

我们如何存储和分发地理数据框架?

虽然 shapefile 很受欢迎,但我认为 GeoJSON 更整洁。除了几何图形本身之外,Shapefile 由单独的文件组成,例如投影和属性;而 GeoJSON 只是一个“文本”文件。

满足OGC 标准的网络服务是必须的,这允许网站和算法远程连接,但有时用户需要普通和流行格式的可下载数据,如。xlsx。csv

我推荐几个选项:

  • 另存为 CSV: 由于空间数据必须可供来自不同背景的多人访问,因此最简单的存储方法是将地理数据框转换为 CSV(逗号分隔值)。通过这种方式,电子表格软件可以解析 CSV,使其成为一种琴谱格式。需要注意的是,“geometry”列将存储为文本,因此要将其转换为几何(shapely)对象,我们需要进一步解析。web 应用程序或一些简单的脚本应该可以完成这项工作。是一个选项,但我不认为这个选项是首选。

完整 CSV 文件的示例(来源:作者,2020)

  • 将数据帧保存为 CSV,将几何列保存为单独的 GeoJSON: 这将生成 2 个文件;CSV 和 GeoJSON。不用担心 GeoJSOn,它基本上就是一个文本文件。 GeoJSON 是系统或软件间互操作性的一种格式。使用 GeoJSON,许多软件或网站可以解析文本并绘制几何图形。将对 GeoJSON 对象进行索引,以便其可以与 CSV 连接。

CSV 和 GeoJSON(来源:作者,2020 年)

结束语

我认为应该对数据集中的空间属性有所了解。空间属性与数据框本身一样重要,不应分离。一个电子表格。xlsx 、 CSV、等等)必须附带 GeoJSON 格式的几何图形,甚至嵌入 CSV 本身。只要几何属性合适,表格数据格式必须有一个定义每个要素的几何的列。为什么选择地理数据框架、CSV 或 GeoJSON?所以对于那些不常见于空间数据的人来说,对于那些常见于空间数据的人来说,它是人眼可读的。几何图形必须由坐标组成。

我认为包含地理数据框架应该会增加许多人的直觉,即要素确实具有空间属性,并且在空间上是可交互的。地理数据框架还使我们能够以高效的方式计算空间数据。首先,我们可以简单地“只”在每个电子表格中添加一列,即几何列。

核空间插值

原文:https://towardsdatascience.com/spatial-interpolation-with-kernels-cf3616487714?source=collection_archive---------56-----------------------

估算地理区域空气质量的个例研究

马丁·桑切斯在 Unsplash 上的照片

加州最近火了。实际上是火。火产生烟。有很多。

显示不同地区空气质量的热图显然是有用的。构建这样一个热图的建模问题是什么?说真的,创业者都在琢磨这个问题[1]。

我们有空气质量传感器。但不是所有地方。我们希望通过附近传感器的测量来估计稀疏覆盖区域的空气质量。我们称之为空间插值

还有测量误差。传感器质量不一。昂贵的传感器更精确,但也更少。所以他们的报道很少。廉价的传感器要丰富得多。然而,不太准确。

考虑到这些权衡,我们应该结合所有数据(在足够接近的范围内),而不是选择一些数据。考虑到传感器位置及其变化的误差率。

也可能有异常读数。传感器有时会返回奇怪的结果。或者生病,甚至死亡。它们还会产生有偏差的结果,即那些始终不符合要求的结果。

异常需要检测,以便可以丢弃它们。偏差需要估计,这样才能被纠正。同一传感器频繁出现异常是故障的症状。

这些都很有趣,对吧?也很有用。空间插值也适用于温度、风速、降雨量、湿度等。

让我们开始吃吧。

问题陈述

我们的传感器位于 n 点( x 1、 y 1)、…、( xnyn )。我们将把任何这样的点称为 ij。位置 ij 的传感器发出读数 Sij 。我们将此读数视为该位置未知真值的讹误版本。我们试图从这些读数中估计该区域中每一点( xy )的 Txy

示例:假设我们在旧金山湾区有两个传感器。一个在旧金山,另一个在圣何塞。我们感兴趣的是估计两者之间的空气质量。(当然,我们只有两个传感器,所以我们的估计可能不会很大。)

可以把这想象成一维空间插值。估计连接旧金山和圣何塞的线路的空气质量。

内核:现在我们来讨论内核。在每个装有传感器的位置 ij ,我们附加一个内核 KijKij 衡量读数 Sijij 附近位置的影响。这种影响随着距离的增加而减弱。形式上,我们将这个权重表示为 K (d( xyij ))。这里 d ( xyij )表示 xyij 之间的距离。K( d )随 d 减小, K (0)为 1。对于 K ( d )的自然选择是 e^- ad ,其中 a 是控制影响随距离衰减的速率的参数。

示例:在我们的 SF-SJ 示例中,假设我们将内核 K ( d )设置为 1/(a* d +1),其中 d 的单位是英里。考虑 a = 1。SF 中读数的影响在离 SF 一英里处为=50%,在离 SF 两英里处为=25%。

为什么是这个内核函数,为什么 a =1 而不是其他选择?我们很快就会看到,插值对我们在这里做出的决定并不特别敏感。

第一插补器

考虑任何一点 xy 。考虑在位于 xy 附近的传感器处测量的 AQIs { Sij }。这些测量值的加权平均值是对 xy 处 AQI 的良好估计。越靠近 xy 的传感器对估计的影响越大。

更正式地说

s^xy=sum_ij pij(xy)** sij*

在哪里

pij(xy)= K(d(ijxy)/sum _ I ' j ' K(d(I ' j ',xy ))

这里的 S ^ xy 表示以这种方式在 xy 获得的强度估计值。

示例:考虑我们的 SF-SJ 示例,使用我们之前选择的内核函数。假设 SF 和 SJ 相距 50 英里。考虑两者之间的一个点,我们称之为圣马特奥(SM)。我们想从 SF 和 SJ 读数中估计 SM 的 AQI,我们将分别取 160 和 180 AQI。(AQI 是“空气质量指数”的简称。)

K(SM,SF) = K(SM,SJ) = 1/(25+1)=1/26

由此,我们得到

pSF(SM) = pSJ(SM) = ½

所以我们估计 SM 的 AQI 是 170,160 和 180 的平均值。

这个例子也揭示了插值对核函数的选择及其参数并不像我们想象的那样敏感。重要的是从核函数值中得到的概率 pij ,而不是这些值本身。只要核函数帮助我们估计相对对各个附近传感器的读数对插值的影响,我们就很好。

整合领域知识

假设我们对各种传感器有所了解。我们可能知道传感器的价格或品牌。根据这些信息,我们也许能够估计它的错误率。更好的是,错误率可能已经从测试中估计出来了。销售优质传感器的公司倾向于这样做,以突出他们的价值主张。

下面是一个简单的启发式扩展 pij 来利用这个知识。

pij = K(d(ijxy)*wij

这里的 wij 是一个正权重,表示位置 ij 处传感器的质量。更高的价值等同于更高的质量。

这样一来,低质量的传感器对^的影响要小于高质量的传感器。也就是说,在附近位置给出相似读数的多个低质量传感器的累积影响就好像有一个具有相似读数的高质量传感器。这是所希望的。

:假设我们设 wSF = 2,wSJ = 1。我们对旧金山传感器的信任度是圣何塞传感器的两倍。这将概率修改为 pSF(SM) = ⅔,pSJ(SM) = ⅓.因此,我们在 SM 的 AQI 估计值约为 166,更接近 SF 的读数。

应对异常读数

传感器有时会给出异常读数。根据定义,这样的读数可能“超出图表范围”如果不加检查,它们可能会严重扭曲其邻域内的插值。显然,我们应该检测异常读数,并忽略它们。

为此,我们将在 pij 中添加一个新术语:

pij = K(d(ijxy)wijAIJ

这里 aij 是一个异常折扣权重。如果传感器的读数被认为是正常的,其值接近 1,如果被认为是异常的,其值接近 0。

怎么才能估算出 aij ?基本想法很简单。如果大多数邻近的传感器具有相似的读数,并且与 ij 的读数相差很大,则 ij 的读数可能是异常的。

更详细地说,使用前面描述的公式,基于相邻传感器的读数,获得该传感器的 S ^ ij 。一定要排除 ij 本身对这个公式的贡献。比较 S ^ ijSij 。如果两者相距甚远,那么 ij 处的读数可能是异常的。

考虑到相邻传感器读数的可变性,这可以进一步增强。相邻传感器的读数越相似,该传感器(其读数显著不同)异常的可信度就越高。

有趣的是,这里的建模类似于[3]中描述的建模,特别是去噪图像。在这一点上,这里描述的两层结构很有意思。

示例:让我们在 SF-SJ 示例中添加一个新的传感器,位于 SM,两者之间。同样,让我们回到同样信任 SF 和 SJ 传感器。

假设 SM 的传感器读数为 5 AQI,而 SF 和 SJ 的读数分别为 180 和 160 AQI。根据 SF 和 SJ 读数,SM 的 AQI 估计值为 170。170 和 5 很不一样。这就对 SM 读数提出了质疑。

鉴于此,当从三个传感器估计 SF 和 SJ 之间某处的 AQI 时(比如在 Palo Alto ),我们可能想要丢弃 SM 传感器的读数。

检测时间变化

传感器可能在一段时间内一直报告相似的读数,然后突然报告非常不同的读数。出事了。如果附近的传感器没有同时报告突变,则无论发生什么都可能是该传感器本地发生的。

使用这样的时间信息和空间信息,我们通常可以增加读数异常的推断的可信度。

示例:考虑我们在 SF、SM 和 SJ 的三个传感器。假设我们在过去 5 个小时内每小时测量一次他们的读数,结果如下:

Hour         1   2   3   4   5
SF reading  159 163 161 166 157
SM reading  170 168 174 169   **5** SJ reading  181 178 172 178 182

显然,通过查看三个传感器在整个持续时间内的读数(不仅仅是在第 5 小时),我们可以更有把握地说,粗体显示的读数是异常的。

使用这种时间信息还可以帮助评估传感器是否有偏差。考虑上述例子的一个变体。

Hour         1   2   3   4   5
SF reading  159 163 161 166 157
**SM reading  129 121 125 122 124** SJ reading  181 178 172 178 182

要么是 SM 传感器有偏差,要么是 SM 中确实存在其他问题,导致 SM 中的读数始终低于 SF 和 SJ 中的读数。不管怎样,知道这一点很好。

延伸阅读

  1. air visual 的空气质量数据有多准确?|空中视觉支持中心
  2. 空间插值
  3. 马尔可夫随机场和图像处理|作者 Arun ja gota | 2020 年 8 月

用深度学习对死人说话

原文:https://towardsdatascience.com/speak-to-the-dead-with-deep-learning-a336ef88425d?source=collection_archive---------43-----------------------

如何训练聊天机器人听起来像任何有电话的人,包括你已故的亲人。

照片由马尼亚·卡塔林派克斯拍摄

介绍

我是看着科幻小说长大的,在科幻小说中,人们试图将他们的意识植入机器。我总是觉得这些故事很吸引人。有意识是什么意思?如果我把自己的一个完美副本放进一台机器里,哪一个是我?如果生物的我死了,而机械的复制我活了下来,那我死了吗?我仍然喜欢这样的故事,最近一直在阅读格雷格·伊根,如果你认为这些是有趣的问题,我强烈推荐他的书 D iaspora (只要 3 美元)。

但是我跑题了。以今天的技术,只用几行代码就可以粗略地模拟一个人的说话风格。随着新冠肺炎在世界各地被烧毁,我开始担心我生活中的老年人,并想知道是否有可能在某个地方保留他们的一部分。这篇教程是我微弱的尝试,试图捕捉和保持一个人死后的一些对话方面。

TLDR

你可以把你的文本带给任何人,用它们来训练一个简单的聊天机器人,只需要几行代码。本教程的所有代码都可以在这里找到。

获取数据

在当今世界,人们在网上发布了如此多的东西,你只需稍微搜索一下,就可以找到关于大多数人的海量数据,无论是好是坏。不幸的是,或者说幸运的是,收集老年人数据的现实是,他们没有强大的在线存在。我奶奶没有推特和脸书。如果他们这样做了,他们就不会经常发帖,以至于我无法提取任何有意义的信号。所以我求助于他们的电话。

每个人都有电话。我们一直用它们来交流,它们记录了我们过去的对话和互动。它们是捕捉某人谈话风格初始模型的完美工具。在这个项目中,我将创建一个我父亲的对话短信模型。他让我借他的手机一天,把里面所有的短信都擦掉。对于这些文本,我们将以一种深度对话模型可以理解的方式对其进行格式化,并根据他的短信模式对其进行微调。

提取文本

我爸爸和我都有 iPhone,所以本教程将围绕如何从 iPhone 中提取和处理文本。你可以用 iTunes 给你的设备做一个备份,但是查看和操作这个备份有点麻烦。我发现从 iPhone 中提取和处理文本的最好方法是图像化,本教程将带你了解如何使用这个工具。您不需要购买 iMazing 来执行此演练。他们有 30 天的免费试用期,应该可以让你通过这个项目没有问题,但如果你最终购买它,这是我的附属链接,所以如果你点击这个链接后购买,我会得到一点回扣:)。如果附属链接对你来说是奉承话,就点击这个。它去他们正常的网站,没有回扣。他们永远不会知道是我派你去的。

机器人

如果你运行的是 Android,看起来有一些软件选项可以帮助你提取和备份文本。这个免费的应用程序似乎能帮上忙。我还在这里找到了一个描述如何做的帖子。不幸的是,因为我没有安卓手机,所以我无法测试这些方法。不过,如果你在格式化数据时遇到困难,我很乐意和你聊天,帮你调试。

苹果手机

步骤 1:使用 iMazing 创建备份

插入你的 iPhone,打开 iMazing。你应该在右上角看到你的手机。右键单击并选择“备份”。这将像在 iTunes 中一样备份您的手机,但在这里您可以轻松地访问和导出您的数据。可能需要五到十分钟才能完成。

步骤 2:下载 CSV 格式的数据

备份完成后,我们可以通过以下方式访问短信:

  1. 点击信息图标
  2. 选择我们想要提取的对话
  3. 点击导出到 CSV 按钮

这将提取一个 CSV 格式的所有过去的文本保存在你的手机之间,你和每个人的对话。正如你在下面看到的,CSV 有许多字段,但我们只需要其中的三个用于这个项目消息日期文本发送者姓名

图 3

为培训准备数据

我们的任务是构建示例对话来训练我们的聊天机器人。第一件事是载入数据。下面的代码片段会解决这个问题。这段代码非常简单。我们加载 CSV,做一些重命名和数据格式化,以便于使用,瞧。需要注意的一点是,我在 speaker 列的空条目中填入了一个提供的receiver_name. iMazing 并不能很好地写下通话中谁是电话的主人。它会将该位置留空。所以无论哪里有丢失的值,我们都可以用拥有手机的人来填充。

随着数据的加载,我们现在需要将来自同一说话者的文本压缩成一行。请注意,在图 3 的数据集的前三行中,我是如何两次发送文本来完成一个想法的。我们需要将所有这些内容汇集在一起,以便我们的数据帧的每一行都是不同的发言人。这将使数据处理更容易。我们可以使用下面的代码来做到这一点:

这有点棘手,让我来解释一下。我们使用shift()来复制数据帧,但是偏移一行。然后,我们可以将每一行的发送方名称与其偏移发送方名称进行比较。如果它们不同,那么我们知道我们已经转换了扬声器。我们创建了一个group_key来代表这些不同的文本跨度。然后,我们可以根据这个键、日期和发件人姓名进行分组,以获得我们编译的文本范围。

我们需要做的最后一件事是拆分数据。在训练时,有一个小的验证集来获得一种概括的感觉是很有用的。

这里我们得到所有不同的日期,然后将它们分成训练集或验证集。然后,我们将这些分割合并回原始数据帧。这确保了在同一对话中出现的文本不会同时出现在训练集和验证集中。

格式化拥抱脸部模型的数据

我们将在这个项目中使用优秀的拥抱面部对话人工智能。脸书刚刚发布了 Blender ,如果你有一台超级计算机,这将是作为你的基本聊天机器人的另一个很酷的选择,但我没有,所以我将坚持使用我可以在有限时间内微调的模型:)。

拥抱脸变形模型的数据格式起初看起来有点混乱,但是它很容易生成。训练数据需要是具有以下签名的 JSON 文件:

{
  "train": [
    {
      "personality": [
        "sentence",
        "sentence"
      ],
      "utterances": [
        {
          "candidates": [
            "candidate 1",
            "candidate 2",
            "true response"
          ],
          "history": [
            "response 1",
            "response 2",
            "etc..."
          ]
        }
      ]
    }
  ],
  "valid": ...
}

让我们稍微分解一下。较大的 JSON 对象有两个主键。“训练”和“有效”。训练是训练数据,并且是个性、话语对的列表。除了验证集之外,Valid 是相同的。个性是定义说话者个性的句子列表。更多细节请看拥抱脸教程。对于我的模型,我把人格作为我父亲与之交谈的人的名字。候选项部分包含对输入的可能响应的列表。此列表包含对对话历史的一些非最佳响应,其中最后一句是基本事实响应。最后,我们必须定义对话的历史。这是一个字符串列表,其中每个位置包含一个新的话轮。如果你想要另一个如何格式化数据的例子,拥抱脸在资源库中有一个很好的例子,你可以在这里找到。

训练模型

拥抱脸的优秀员工为我们组装了一个 docker 容器,因此设置模型培训轻而易举!只需运行make build来构建 convai docker 容器,然后运行make run以交互模式进入容器。

从容器内部,我们可以训练我们的模型。我们只需从 docker 内部导航到我们的项目文件夹并运行:

python3 train.py --dataset_path {data-path}.json --gradient_accumulation_steps=4 --lm_coef=2.0 --max_history=4 --n_epochs=3 --num_candidates=4 --train_batch_size=2 --personality_permutations 2

这花了大约 10 个小时在我的 CPU 上训练。它应该在 GPU 上进行训练,但由于整夜运行它对我来说足够方便,我还没有在我的 GPU 上设置它。

与死者对话(或者在这种情况下不那么死)

最后一步是与我们的对话代理交互。convai 存储库再次为我们完成了大部分繁重的工作。我用 make 对它做了一个方便的包装,这样我们就可以简单地运行:

make interact CHECKPOINT_DIR={path-to-checkpoint} DATA={path-to-training-data}

这将打开一个交互式终端,您可以在这里与您新创建的机器人聊天。只需确保您的模型和数据位于 Makefile 所在的目录或子目录中。我所了解到的是,我和我爸爸并没有通过短信进行真正有意义的对话。看起来我们主要是分享一些信息,聊聊我们的一天,或者安排一些事情。模型超级擅长调度。

它不太擅长进行真诚的对话,但它确实准确地捕捉到了我父亲写短信的风格。如果谈话很短,而且是在我们通常会发短信谈论的领域,那感觉仍然像是在和他简短地聊天。

有时候它会让我吃惊。

后续步骤

在我们正常的短信交流中,这个聊天机器人有点像我父亲。我打算在接下来的一年里尝试通过文本进行一些更深入的对话,然后在明年秋天重复这个实验。我希望通过更长更复杂的基于文本的对话,我能捕捉到更多的信息。作为一个替代项目,你可以试着训练一个你自己的聊天机器人。有了 you-bot,使用 Twilio 之类的东西来自动化简单的文本对话可能会很有趣。

基于 RAVDESS 音频数据集的语音情感识别

原文:https://towardsdatascience.com/speech-emotion-recognition-using-ravdess-audio-dataset-ce19d162690?source=collection_archive---------5-----------------------

图片由腾雅特Unsplash 上拍摄

通过所有可用的感官,人类可以感觉到他们交流伙伴的情绪状态。这种情绪检测对人类来说是自然的,但对计算机来说是非常困难的任务;虽然他们可以很容易地理解基于内容的信息,但访问内容背后的深度是困难的,这就是语音情感识别(SER)要做的事情。它是通过计算机将各种音频语音文件分类为快乐、悲伤、愤怒、中性等不同情绪的系统。语音情感识别可用于医疗领域或客户呼叫中心等领域。我的目标是使用 Kaggle 上提供的 RAVDESS Audio 数据集来演示 SER。

# IMPORT NECESSARY LIBRARIES
import librosa
%matplotlib inline
import matplotlib.pyplot as plt
import librosa.display
from IPython.display import Audio
import numpy as np
import tensorflow as tf
from matplotlib.pyplot import specgram
import pandas as pd
from sklearn.metrics import confusion_matrix
import IPython.display as ipd  # To play sound in the notebook
import os # interface with underlying OS that python is running on
import sys
from sklearn.model_selection import StratifiedShuffleSplit
from sklearn.preprocessing import LabelEncoder
import keras
from keras.models import Sequential
from keras.layers import Conv1D, MaxPooling1D, AveragePooling1D
from keras.layers import Input, Flatten, Dropout, Activation, BatchNormalization, Dense
from sklearn.model_selection import GridSearchCV
from keras.wrappers.scikit_learn import KerasClassifier
from keras.optimizers import SGD
from keras.regularizers import l2
import seaborn as sns
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.utils import to_categorical
from sklearn.metrics import classification_report

电子设计自动化(Electronic Design Automation)

首先,让我们使用 IPython 从数据集中加载并播放一个样本音频文件。显示和 Python 的 librosa 库:

# LOAD IN FILE
x, sr = librosa.load('/Users/murielkosaka/Desktop/capstone_project/audio/audio_speech_actors_01-24/Actor_01/03-01-01-01-01-01-01.wav')# PLAY AUDIO FILE
librosa.output.write_wav('ipd.Audio Files/MaleNeutral.wav', x, sr)
Audio(data=x, rate=sr)

在这里,我们正在看一个男演员用中性语气说话的音频样本。

接下来让我们使用 librosa.display.waveplot 来看看这个音频文件的波形图:

# DISPLAY WAVEPLOT
plt.figure(figsize=(8, 4))
librosa.display.waveplot(x, sr=sr)
plt.title('Waveplot - Male Neutral')
plt.savefig('Waveplot_MaleNeutral.png')

作者图片

波形图描绘了一段时间内的信号幅度包络,看到情绪的整体形状可以帮助确定哪种特征提取方法(MFCC、STFT、对数-梅尔谱图、过零率、谱形心等)。)用于建模。特征提取在建模中很重要,因为它将音频文件转换成模型可以理解的格式。

在检查了每种情绪样本的波形图后,我决定使用 Log-Mel 频谱图作为特征提取的方法。我们可以使用librosa . display . spec show:显示相同样本音频的 Log-Mel 频谱图

# CREATE LOG MEL SPECTROGRAM
spectrogram = librosa.feature.melspectrogram(y=x, sr=sr, n_mels=128,fmax=8000) 
spectrogram = librosa.power_to_db(spectrogram)librosa.display.specshow(spectrogram, y_axis='mel', fmax=8000, x_axis='time');
plt.title('Mel Spectrogram - Male Neutral')
plt.savefig('MelSpec_MaleNeutral.png')
plt.colorbar(format='%+2.0f dB');

作者图片

准备数据

在建模之前,我通过创建音频文件的目录,然后创建一个函数来提取每个文件的情感标签和性别标签(虽然我只对情感分类感兴趣,但我也提取了性别标签,以防我也决定尝试分类性别),将数据结构化为 Pandas 数据帧,然后最后将提取的标签和相关文件路径放入数据帧 audio_df。

# CREATE FUNCTION TO EXTRACT EMOTION NUMBER, ACTOR AND GENDER LABEL
emotion = []
gender = []
actor = []
file_path = []
for i in actor_folders:
    filename = os.listdir(audio + i) #iterate over Actor folders
    for f in filename: # go through files in Actor folder
        part = f.split('.')[0].split('-')
        emotion.append(int(part[2]))
        actor.append(int(part[6]))
        bg = int(part[6])
        if bg%2 == 0:
            bg = "female"
        else:
            bg = "male"
        gender.append(bg)
        file_path.append(audio + i + '/' + f)# PUT EXTRACTED LABELS WITH FILEPATH INTO DATAFRAME
audio_df = pd.DataFrame(emotion)
audio_df = audio_df.replace({1:'neutral', 2:'calm', 3:'happy', 4:'sad', 5:'angry', 6:'fear', 7:'disgust', 8:'surprise'})
audio_df = pd.concat([pd.DataFrame(gender),audio_df,pd.DataFrame(actor)],axis=1)
audio_df.columns = ['gender','emotion','actor']
audio_df = pd.concat([audio_df,pd.DataFrame(file_path, columns = ['path'])],axis=1)

特征抽出

接下来,最重要的是,我使用了 librosa 的librosa . feature . Mel spectrogramlibrosa.power_to_db 来获得每个音频文件的 log-mel 频谱图值,然后对频谱图值进行平均,并将数据加载到标记为 df 的新数据帧中。

# ITERATE OVER ALL AUDIO FILES AND EXTRACT LOG MEL SPECTROGRAM MEAN VALUES INTO DF FOR MODELING 
df = pd.DataFrame(columns=['mel_spectrogram'])counter=0for index,path in enumerate(audio_df.path):
    X, sample_rate = librosa.load(path, res_type='kaiser_fast',duration=3,sr=44100,offset=0.5)

    #get the mel-scaled spectrogram (ransform both the y-axis (frequency) to log scale, and the “color” axis (amplitude) to Decibels, which is kinda the log scale of amplitudes.)
    spectrogram = librosa.feature.melspectrogram(y=X, sr=sample_rate, n_mels=128,fmax=8000) 
    db_spec = librosa.power_to_db(spectrogram)
    #temporally average spectrogram
    log_spectrogram = np.mean(db_spec, axis = 0)

    df.loc[counter] = [log_spectrogram]
    counter=counter+1print(len(df))
df.head()

因为这在一列下创建了一个值数组,所以我使用 pd.concat 将该数组转换成一个列表,并与我之前的数据帧 audio_df 连接,并删除必要的列以给出最终的数据帧。

作者图片

数据预处理

模型的数据预处理分五步进行:

1.训练,测试分割数据

train,test = train_test_split(df_combined, test_size=0.2, random_state=0,
                               stratify=df_combined[['emotion','gender','actor']]) X_train = train.iloc[:, 3:]
y_train = train.iloc[:,:2].drop(columns=['gender'])X_test = test.iloc[:,3:]
y_test = test.iloc[:,:2].drop(columns=['gender'])

2.标准化数据-提高模型稳定性和性能

# NORMALIZE DATA
mean = np.mean(X_train, axis=0)
std = np.std(X_train, axis=0)
X_train = (X_train - mean)/std
X_test = (X_test - mean)/std

3.转换为 Keras 的数组

X_train = np.array(X_train)
y_train = np.array(y_train)
X_test = np.array(X_test)
y_test = np.array(y_test)

4.目标变量的一键编码

# CNN REQUIRES INPUT AND OUTPUT ARE NUMBERS
lb = LabelEncoder()
y_train = to_categorical(lb.fit_transform(y_train))
y_test = to_categorical(lb.fit_transform(y_test))

5.重塑数据以包含 3D 张量

X_train = X_train[:,:,np.newaxis]
X_test = X_test[:,:,np.newaxis]

基础模型

对于基线模型,我决定从简单的虚拟分类器开始,该分类器通过尊重训练数据的类别分布来生成预测,并且具有 11.81%的低准确度分数。然后,我尝试了决策树,因为这是一个使用平均 log-mel 光谱图的多分类问题,并获得了 29.17%的准确率。

dummy_clf = DummyClassifier(strategy="stratified")
dummy_clf.fit(X_train, y_train)
DummyClassifier(strategy='stratified')
dummy_clf.predict(X_test)
dummy_clf.score(X_test, y_test)clf = tree.DecisionTreeClassifier()
clf = clf.fit(X_train, y_train)
clf.predict(X_test)
clf.score(X_test, y_test)

初始模型

对于我的初始模型,我训练了一个具有三个卷积层和一个输出层的 1D CNN,并在我的测试集上获得了 48%的准确率,这比我的基线决策树略好(在此插入悲伤表情)。

# BUILD 1D CNN LAYERS
model = Sequential()
model.add(Conv1D(64, kernel_size=(10), activation='relu', input_shape=(X_train.shape[1],1)))
model.add(Conv1D(128, kernel_size=(10),activation='relu',kernel_regularizer=l2(0.01), bias_regularizer=l2(0.01)))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Dropout(0.4))
model.add(Conv1D(128, kernel_size=(10),activation='relu'))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(8, activation='sigmoid'))
opt = keras.optimizers.Adam(lr=0.0001)
model.compile(loss='categorical_crossentropy', optimizer=opt,metrics=['accuracy'])
model.summary()

作者图片

数据扩充

为了提高我的初始模型的可推广性,我探索了数据增强,但是我没有增强音频文件的图像,而是增强了音频文件本身。利用余金乐在 Kaggle 上提供的自定义功能,我给原始音频文件添加了噪音、拉伸、速度和音高。音频文件的数据扩充也可以使用 Numpy 和 Librosa 来完成,本文将对此进行探讨。

从上面的代码中可以看出,将数据扩充应用于音频文件并使用特征提取方法(也使用 log-mel 频谱图),产生了四个数据帧。这四个数据帧以与数据准备步骤相似的方式组合,并遵循与上述相同的预处理步骤。与我的初始模型的 1,440 幅图像相比,数据扩充方法产生了 5,760 幅图像的大得多的训练集。

# BUILD 1D CNN LAYERS
model = Sequential()
model.add(Conv1D(64, kernel_size=(20), activation='relu', input_shape=(X_train.shape[1],1)))
model.add(Conv1D(128, kernel_size=(20),activation='relu',kernel_regularizer=l2(0.01), bias_regularizer=l2(0.01)))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Dropout(0.4))
model.add(Conv1D(128, kernel_size=(20),activation='relu'))
model.add(MaxPooling1D(pool_size=(8)))
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(8, activation='softmax'))
model.summary()
opt = keras.optimizers.Adam(lr=0.0001)

训练一个具有三个卷积层和一个输出层的 1D CNN 得到了略高的 58%的准确度分数(在此插入另一张悲伤的脸)。

作者图片

后续步骤

在接下来的步骤中,我将探索迁移学习来提高模型的性能。感谢您的阅读!:)完整代码可在我的 GitHub 上获得。

深度学习的语音增强

原文:https://towardsdatascience.com/speech-enhancement-with-deep-learning-36a1991d3d8d?source=collection_archive---------7-----------------------

这个项目旨在建立一个语音增强系统来衰减环境噪音。

音频有许多不同的表示方式,从原始时间序列到时频分解。表示的选择对于系统的性能至关重要。在时频分解中,频谱图已被证明是音频处理的一种有用的表示方法。它们由代表短时傅立叶变换(STFT)序列的 2D 图像组成,以时间和频率为轴,亮度代表每个时间帧的频率分量的强度。因此,它们似乎是将图像的 CNN 架构直接应用于声音的自然领域。在幅度谱和相位谱之间,幅度谱包含了信号的大部分结构。相位谱图似乎只显示出很少的时间和光谱规律。

在这个项目中,我将使用幅度谱图作为声音的表示(参见下图),以预测噪声模型,并将其减去噪声声谱图。

项目分解为三种模式:data creationtrainingprediction。该代码可从 Github 库获得:https://github.com/vbelz/Speech-enhancement

准备数据

为了创建用于训练的数据集,我从不同的来源收集了带有干净声音和环境噪音的英语语音示例。

干净的声音主要来自于 LibriSpeech :一个基于公共领域有声读物的 ASR 语料库。我也使用了来自 SiSec 的一些数据。环境噪声从 ESC-50 数据集https://www.ee.columbia.edu/~dpwe/sounds/中采集。

在这个项目中,我关注了 10 类环境噪音:滴答声、脚步声、铃声、手锯声、闹钟声、烟火声、昆虫声、刷牙声、吸尘器声和鼾声。下图展示了这些类(我用来自https://unsplash.com的图片创建了这张图片。)

为了创建用于训练/验证/测试的数据集,音频以 8kHz 进行采样,我提取了略高于 1 秒的窗口。我对环境噪声进行了一些数据扩充(在不同时间取窗口会产生不同的噪声窗口)。噪声已经混合到干净的声音中,并随机化噪声级别(在 20%和 80%之间)。最终,训练数据由 10h 的嘈杂语音和干净语音以及 1h 的声音的验证数据组成。

为了准备数据,我建议在与代码文件夹不同的位置创建 data/Train 和 data/Test 文件夹。然后创建下图所示的结构:

您可以将noise_dirvoice_dirpath_save_spectrogrampath_save_time_seriepath_save_sound路径名相应地修改到args.py文件中,该文件采用程序的默认参数。

将您的噪音音频文件放入noise_dir目录,将您的干净语音文件放入voice_dir

args.py中指定你想要创建多少帧作为nb_samples(或者从终端传递它作为一个参数)我让 nb_samples 默认为 50,但对于生产,我建议有 40 000 或更多。

然后运行python main.py --mode='data_creation'。这将随机混合来自voice_dir的一些干净声音和来自noise_dir的一些噪音,并将嘈杂声音、噪音和干净声音的频谱图以及复杂相位、时间序列和声音保存到磁盘(用于 QC 或测试其他网络)。它采用在args.py中定义的输入参数。STFT、帧长、hop_length 的参数可以在args.py中修改(或从终端作为参数传递),但在默认参数下,每个窗口将被转换为 128 x 128 大小的谱图矩阵。

用于训练的数据集将是嘈杂声音的幅度谱图和干净声音的幅度谱图。

培养

用于训练的模型是 U-Net,一种具有对称跳跃连接的深度卷积自动编码器。 U-Net 最初是为生物医学图像分割而开发的。这里,U-Net 已被用来对光谱图进行降噪处理。

作为网络的输入,噪声声音的幅度谱图。将噪声输出到模型(含噪语音幅度谱图—干净语音幅度谱图)。输入和输出矩阵都用全局缩放进行缩放,以映射到-1 和 1 之间的分布。

培训期间测试了许多配置。对于优选的配置,编码器由 10 个卷积层组成(具有 LeakyReLU、maxpooling 和 dropout)。解码器是具有跳跃连接的对称扩展路径。最后一个激活层是双曲正切(tanh ),其输出分布在-1 和 1 之间。对于从零开始的训练,初始随机权重用正常初始值设定。

模型用 Adam optimizer 编译,使用的损失函数是 Huber 损失,作为 L1 和 L2 损失之间的折衷。

在现代 GPU 上进行培训需要几个小时。

如果你本地电脑有深度学习计算的 GPU,可以用:python main.py --mode="training"进行训练。它将args.py中定义的参数作为输入。默认情况下,它将从头开始训练(你可以通过将training_from_scratch设为 false 来改变这一点)。您可以从weights_foldername_model中指定的预训练重量开始训练。我让model_unet.h5使用我在./weights训练中获得的重量。通过epochsbatch_size指定训练的时期数和批量大小。最佳重量在训练期间自动保存为model_best.h5。可以调用 fit_generator 在训练时只将部分数据加载到磁盘上。

就我个人而言,我在训练中使用了 Google Colab 提供的免费 GPU。我在./colab/Train_denoise.ipynb举了一个笔记本例子。如果您的驱动器上有大量可用空间,您可以将所有训练数据加载到您的驱动器上,并在训练时使用 tensorflow.keras 的 fit_generator 选项加载部分数据。就我个人而言,我的 Google drive 上的可用空间有限,所以我提前准备了 5Gb 的批量加载到驱动器上进行训练。重量会定期保存,并在下次训练时重新加载。

最终我获得了 0.002129 的训练损失和 0.002406 的验证损失。下面是在一次培训中制作的损失图。

预言;预测;预告

为了进行预测,嘈杂的语音音频被转换成略高于 1 秒的窗口的 NumPy 时间序列。每个时间序列通过 STFT 变换转换成幅度谱图和相位谱图。噪声声谱图被传入 U-Net 网络,该网络将预测每个窗口的噪声模型(参见下图)。使用传统的 CPU,一个窗口的预测时间一旦转换成幅度谱图大约是 80 ms。

然后,从有噪声的语音频谱图中减去该模型(这里,我应用直接减法,因为它对于我的任务来说是足够的,我们可以想象训练第二个网络来适应噪声模型,或者应用诸如在信号处理中执行的匹配滤波器)。“去噪”的幅度谱图与初始相位相结合,作为反短时傅立叶变换(ISTFT)的输入。我们去噪后的时间序列可以转换成音频(参见下图)。

让我们看看在验证数据上的表现!

下面我展示了一些警报/昆虫/真空吸尘器/铃声的验证结果。对于它们中的每一个,我都显示了初始的有噪语音谱图,网络预测的去噪谱图,以及真正干净的语音谱图。我们可以看到,该网络能够很好地概括噪声模型,并产生略微平滑版本的语音频谱图,非常接近真正干净的语音频谱图。

更多验证数据的声谱图去噪示例显示在存储库顶部的初始 gif 中。

让我们听听转换回声音的结果:

报警示例的音频:

输入示例报警

预测输出示例报警

真实输出示例报警

昆虫音频示例:

输入示例昆虫

预测产量示例昆虫

真实输出示例昆虫

吸尘器音频示例:

输入示例吸尘器

预测产量示例吸尘器

真实输出示例真空吸尘器

铃声示例:

输入示例铃声

预测输出示例铃声

真实输出示例铃声

下面我展示了转换回时间序列的相应显示:

你可以看看我在./demo_data文件夹中提供的 jupyter 笔记本demo_predictions.ipynb中的这些显示/音频。

下面,我展示的是时间序列域的声谱图去噪 gif(库顶)对应的 gif。

作为极端测试,我应用于一些混合了许多高水平噪音的声音。该网络在去噪方面表现得出奇地好。对 5 秒钟的音频进行降噪的总时间约为 4 秒钟(使用传统 CPU)。

下面是一些例子:

例一:

输入示例测试 1

预测输出示例测试 1

例二:

输入示例测试 2

预测输出示例测试 2

结论

提出了一种用于衰减环境噪声的深度学习语音增强系统。通过使用声音的幅度谱图表示,音频去噪问题被转化为图像处理问题,简化了其分辨率。要去除的噪声已经由 U-Net 建模,U-Net 是具有对称跳跃连接的深度卷积自动编码器。经过训练后,该网络能够模拟 10 类环境噪声。这种方法是不完美的,因为没有对噪声相位谱图进行建模,这在特定情况下可能会降低整体性能。此外,这种方法更适合离线去噪,因为去噪 5 秒的音频目前需要大约 4 秒(使用经典 CPU 进行预测)。尽管如此,这种方法是稳健的,能够推广到许多语音和噪声配置。此外,它可以转移学习到其他语言以外的英语和新类型的噪音。

参考

扬松、安德里亚斯、埃里克·汉弗莱、尼古拉·蒙特基奥、雷切尔·比特纳、阿帕娜·库马尔和蒂尔曼·韦德。基于深度 U 网卷积网络的歌唱声分离。伊斯米尔 (2017)。

https://ejhumphrey.com/assets/pdf/jansson2017singing.pdf】

格赖斯、艾玛德 m .和普拉姆布利、马克 d,《使用卷积去噪自动编码器的单通道音频源分离》( 2017 年)。

https://arxiv.org/abs/1703.08019

Ronneberger O .,Fischer P .,Brox T. (2015) U-Net:用于生物医学图像分割的卷积网络。载于:Navab N .,Hornegger J .,Wells W .,Frangi A. (eds) 医学图像计算和计算机辅助干预— MICCAI 2015 。MICCAI 2015。计算机科学讲义,第 9351 卷。施普林格·查姆

https://arxiv.org/abs/1505.04597

K. J .皮扎克。环境声音分类数据集。第 23 届 ACM 多媒体年会论文集,澳大利亚布里斯班,2015。**

【土井:http://dx.doi.org/10.1145/2733373.2806390

Python 中的语音识别——初学者完全指南

原文:https://towardsdatascience.com/speech-recognition-in-python-the-complete-beginners-guide-de1dd7f00726?source=collection_archive---------4-----------------------

简单的动手演练

Unsplash 上的 ConvertKit 拍摄

欢迎来到 Python 语音识别的完全初学者指南。

在这篇文章中,我将带你完成一些很棒的实践练习,帮助你对语音识别和机器学习的使用有一些了解。语音识别通过说话代替打字来帮助我们节省时间。它也给了我们与设备交流的能力,甚至不用写一行代码。这使得技术设备更容易接近和使用。语音识别是在现实生活中使用机器学习的一个很好的例子。

另一个语音识别的好例子:Google Meet web 应用程序,你知道你可以通过设置打开字幕吗?当你打开字幕时,后面的一个程序会识别你的讲话,并将其转换为现实生活中的文本。看到它发生得如此之快,真的令人印象深刻。这个 Google Meet 识别器的另一个很酷的功能是它还知道谁在说话。在本演练中,我们将使用 Google 的语音 API。我迫不及待地想向您展示如何构建自己的语音识别器。我们开始吧!

目录

  • 语音识别库
  • 识别器类
  • 语音识别功能
  • 音频预处理
  • 加成(不同场景)

语音识别视觉作者作者

语音识别库

  • CMU 狮身人面像
  • 卡尔迪
  • 演讲认知
  • wav2 字母++

“CMU 狮身人面像收集了 CMU 20 多年的研究成果。这个库的一些优势:CMUSphinx 工具是专门为低资源平台设计的,设计灵活,专注于实际应用开发而不是研究。”(参考)

" Kaldi 是一个语音识别工具包,供语音识别研究人员和专业人士使用。"(参考)

“语音识别是一个用于执行语音识别的库,支持多种引擎和 API,在线和离线。”(参考

“wav2 letter++是一款快速、开源的语音处理工具包,由脸书人工智能研究所的语音团队开发,旨在促进语音识别端到端模型的研究。”(参考)

从这些库中,我们将使用 speech recognition 库,因为它的入门门槛低,并且与许多可用的语音识别 API 兼容。我们可以通过在终端窗口中运行下面一行来安装speech recognition库:

pip install SpeechRecognition

识别器类

SpeechRecognition 库有许多类,但我们将重点关注一个名为 Recognizer 的类。这个类将帮助我们把音频文件转换成文本。要访问 Recognizer 类,首先,让我们导入库。

import speech_recognition as sr

现在,让我们定义一个变量,并通过调用它来分配 recognizer 类的一个实例。

recognizer = sr.Recognizer()

现在,让我们将能量阈值设置为 300。您可以将能量阈值视为音频文件的音量。低于阈值的值被认为是无声的,高于阈值的值被认为是语音。这将提高处理音频文件时语音的识别能力。

recognizer.energy_threshold = 300

SpeechRecognition 的文档推荐使用 300 作为阈值,它非常适合大多数音频文件。此外,请记住,能量阈值会随着识别器侦听音频文件而自动调整。

语音识别功能

在这一步中,我们将看到我们的识别器在工作,但是在我们让它工作之前,让我们看看这个实例的一些很酷的功能。语音识别有一个内置的功能,使它能够与现有的许多 API 一起工作:

  • 识别 _bing()
  • 识别 _ 谷歌()
  • 识别 _ 谷歌 _ 云()
  • 认可 _ 机智()

必应识别器功能使用微软的认知服务。

谷歌识别器功能使用谷歌免费的网络搜索 API。

谷歌云识别器功能使用谷歌的云语音 API。

Wit 识别器功能使用 wit.ai 平台。

我们将使用 Google Recognizer 函数,它是 recognize_google()。它是免费的,不需要 API 密钥就可以使用。这个识别器有一个缺点,当您想要处理较长的音频文件时,它会限制您。根据我的经验,我在处理 5 分钟以下的音频文件时没有遇到任何问题。我不建议将此识别器用于长音频文件。有不同的技术来处理较长的音频文件,我计划在另一篇文章中介绍它。

例子

import speech_recognition as srrecognizer = sr.Recognizer()recognizer.recognize_google(audio_data="my_audio.wav", language="en-US")

出错信息

音频预处理

前面的例子只是一个没有做任何音频预处理的尝试,正如你所看到的,它导致了一个错误。问题是我们音频文件的数据类型。为了避免这种问题,预处理步骤是必要的。你可以把它想象成我们通常在做数据分析之前做的数据预处理。对于这一步,我们将使用一个特殊的类,称为 AudioFile。

音频文件

import speech_recognition as srrecognizer = sr.Recognizer()audio_file_ = sr.AudioFile("data/my_audio.wav")type(audio_file)

当我们试图在 recognize_google()函数中传递 audio_file 变量时,它不会接受。原因是该函数接受 audiodata,但我们当前的变量类型是 audiofile。为了将其转换为音频数据类型,我们将使用 recognizer 类的内置方法 record。

记录方法

下面是如何使用记录方法:

with audio_file as source:
  audio_file = recognizer.record(source)
  recognizer.recognize_google(audio_data=audio_file)type(audio_file)

数据类型已更新

我们还可以使用记录方法的两个参数。

默认情况下,这两个参数都等于零。在默认模式下,record 方法将从文件的开头开始记录音频数据,直到没有音频为止。但是我们可以通过赋予它们浮点值来改变这一点。

  • 持续时间:假设我们只想要整个音频文件的前 7 秒,我们必须将持续时间参数设置为 7.0
  • 偏移量:用于在音频文件的开头切断或跳过指定的秒数。假设我们不想要音频文件的前 1 秒,我们必须将偏移参数设置为 1.0

持续时间

with audio_file_ as source:
  audio_file = recognizer.record(source, duration = 7.0)
  result = recognizer.recognize_google(audio_data=audio_file)

结果

偏移

with audio_file_ as source:
  audio_file = recognizer.record(source, offset = 1.0)
  result = recognizer.recognize_google(audio_data=audio_file)

结果

奖金(不同场景)

无声音频

# Import the silent audio file
silent_audio_file = sr.AudioFile("silent_audio.wav")# Convert the AudioFile to AudioData
with silent_audio_file as source:
  silent_audio = recognizer.record(source)# Recognize the AudioData with show_all turned on
recognizer.recognize_google(silent_audio, show_all=True)

多个扬声器

这种理解单个音频文件中不同说话者的过程称为说话者二进制化。这是一个很酷的功能,但是不幸的是,这个库中没有这个功能。一个解决方法是为不同的扬声器准备不同的音频文件,使用 for 循环遍历它们。

recognizer = sr.Recognizer()# Multiple speakers on different files
speakers = [sr.AudioFile("speaker_0.wav"), sr.AudioFile("speaker_1.wav"), sr.AudioFile("speaker_2.wav")]# Transcribe each speaker individually
for i, speaker in enumerate(speakers):
  with speaker as source:
    speaker_audio = recognizer.record(source)

 print(f"Text from speaker {i}:")
 print(recognizer.recognize_google(speaker_audio,language="en-US"))

背景噪声

为了处理背景噪声,recognizer 类有一个名为 adjust_for_ambient_noise 函数的内置函数,该函数也接受一个参数 duration。使用该函数,识别器类在从音频开始起的指定持续时间(秒)内监听音频,然后调整能量阈值,以便更容易识别整个音频。

# Import audio file with background nosie
noisy_audio_file = sr.AudioFile("noisy_audio.wav")# Adjust for ambient noise and record
with noisy_audio_file as source:
  recognizer.adjust_for_ambient_noise(source, duration=0.5)
  noisy_audio_file = recognizer.record(source)# Recognize the audio
recognizer.recognize_google(noisy_audio_file)

视频演示

我刚刚在 YouTube 上开始我的旅程,我正在做以下主题的视频演示:机器学习,数据科学,人工智能。尽情享受吧!

感谢你阅读这篇文章,我希望你今天喜欢并学到了一些新东西。欢迎联系我如果您在执行代码时有任何问题,我们将非常乐意提供帮助。

跟随 我的博客 走向数据科学 留下灵感。

相关内容

[## 使用 Python 从视频中提取语音

使用 Google 语音识别 API 的简单实用项目

towardsdatascience.com](/extracting-speech-from-video-using-python-f0ec7e312d38) [## 用 Python 构建语音翻译器

使用谷歌翻译 API 将你的演讲翻译成多种语言

towardsdatascience.com](/building-a-speech-translator-in-python-8ff190212b49)

使用 Tensorflow.js 进行语音识别

原文:https://towardsdatascience.com/speech-recognition-with-tensorflow-js-2f55c104e809?source=collection_archive---------35-----------------------

利用语音命令识别技术在浏览器中引入语音控制

图片来自 PixabayKaufdex

像亚马逊 Alexa 和谷歌 Home 这样的语音助手已经变得广泛流行,它们允许用户通过使用语音识别快速完成任务。

由于语音识别技术的改进,Tensorflow.js 发布了一个 JavaScript 模块,可以识别口头命令。

在本文中,我们将使用一个预先训练好的 Tensorflow.js 模型进行迁移学习。让我们建立一个可以识别你的语音命令的应用程序。

点击下面的链接亲自尝试一下:

[## 采用 TensorFlow.js - Benson 技术的语音识别

在本文中,我们将使用预训练的 TensorFlow.js 模型进行语音识别。让我们构建一个应用程序…

bensonruan.com](https://bensonruan.com/speech-recognition-with-tensorflow-js/)

我们现在可以用声音无接触地控制电脑,这难道不令人惊奇吗?Tensorflow.js 真正将人工智能带入浏览器。让我们深入代码,一步一步地向您展示如何用 tensorflow.js 构建这个语音识别应用程序。

履行

#步骤 1:包含 tensorflow.js

只需在 html 文件的<头>部分包含tfjsspeech-commands models的脚本。

<script src="[https://unpkg.com/@tensorflow/tfjs](https://unpkg.com/@tensorflow/tfjs)"></script><script src="[https://unpkg.com/@tensorflow-models/speech-commands](https://unpkg.com/@tensorflow-models/speech-commands)"></script>

#第二步:列出命令词

HTML—index.html

在 html 主体中添加一个

占位符

<div id="candidate-words"></div>

添加一个复选框来控制打开/关闭麦克风,我已经使用纯 css 定制了复选框,看起来像一个移动开关。

<label class="form-switch">
<input type="checkbox" id="audio-switch">
<i></i> Microphone</label>

Javascript—speech _ command . js

tfjs 语音命令库支持 20 项词汇,包括:“零”、“一”、“二”、“三”、“四”、“五”、“六”、“七”、“八”、“九”、“上”、“下”、“左”、“右”、“开始”、“停止”、“是”和“否”,此外还有“背景噪声”和“未知”。页面加载后,将单词追加到列表中。

wordList = ["zero","one","two","three","four","five","six","seven","eight","nine", "yes", "no", "up", "down", "left", "right", "stop", "go"];$.each(wordList, function( index, word ) {
 if (!word.startsWith('_')){
  $("#candidate-words").append('<span id='word-${word}'>${word}</span>');
 }
});

#步骤 3:加载模型

当麦克风开关(复选框)打开(选中)时,首先调用函数loadModel()加载预先训练好的模型,然后调用函数startListening()启动语音命令识别。

当麦克风开关(复选框)关闭(未选中)时,调用功能stopListening()断开麦克风。

if(this.checked){
 if(!modelLoaded){
  loadModel();
 }else{
  startListening();
 }
}
else {
 stopListening();
}

函数 loadModel()加载预先训练好的语音命令模型,调用speechCommands.createrecognizer.ensureModelLoaded的 API。调用 create 函数时,必须提供音频输入的类型。
两个可用选项是‘浏览器 _ 快速傅立叶变换’和‘软件 _ 快速傅立叶变换’。
— BROWSER_FFT 使用浏览器的原生傅立叶变换。
— SOFT_FFT 使用傅立叶变换的 JavaScript 实现(尚未实现)。

function loadModel(){
    recognizer = **speechCommands.create("BROWSER_FFT")**;  
    Promise.all([
        **recognizer.ensureModelLoaded()**
      ]).then(function(){
        **words = recognizer.wordLabels()**;
        modelLoaded = true;
        startListening();
      })
}

#步骤 4:启动语音识别

函数 startListening()调用recognizer.listen API 开始监听语音命令。listen()函数接受两个参数:
1。识别单词时调用的回调函数。
2。具有可调字段的配置对象,例如
—include spectrogram
—probability threshold(当且仅当所有单词的最大概率得分大于该阈值时,才会调用回调函数)
— includeEmbedding

分数的输出包含对应于 recognizer.wordLabels()的概率分数。下面的代码将分数转换成一个(分数,单词)对列表。

请注意,麦克风只能通过 https 协议访问

function startListening(){
    **recognizer.listen(({scores})** => {
        scores = Array.from(scores).map(**(s, i) => ({score: s, word: words[i]})**);
        scores.sort((s1, s2) => s2.score - s1.score);
        $("#word-"+scores[0].word).addClass('candidate-word-active');
    }, 
    {
        **probabilityThreshold**: 0.70
    });
}

最后,要关闭麦克风,调用 recognizer.stopListening API

function stopListening(){
    **recognizer.stopListening**();
}

GitHub 知识库

您可以通过下面的链接下载上述演示的完整代码:

[## 本森阮/言语指挥

使用 javascript 库 tensorflowjs 的浏览器中麦克风的语音命令识别器…

github.com](https://github.com/bensonruan/Speech-Command)

结论

阿尔瓦罗·雷耶斯在 Unsplash 上拍摄的照片

2020 年的 Tensorflow.js 由于其全面的线性代数核心和深度学习层,已经成为所有机器学习 JavaScript 项目的面包和黄油。从这篇文章中,我希望你能从中得到乐趣,并鼓励你更多地了解这个库。

感谢您的阅读。如果你喜欢这篇文章,请在脸书或推特上分享。如果你有任何问题,请在评论中告诉我。在 GitHubLinkedin 上关注我。

使用 TensorFlow.js 进行语音识别

原文:https://towardsdatascience.com/speech-recognition-with-tensorflow-js-66608355376e?source=collection_archive---------48-----------------------

语音识别

使用 TensorFlow.js 构建一个小型语音识别示例

马特·博茨福德在 Unsplash 上的照片

当我们通常谈论 AI、深度学习、机器学习时,我们会自动想到 Python、R 或 C++,但 JavaScript 呢?嗯……事实证明,Python 中最受欢迎的机器学习库之一也可用于 JavaScript,我们正在谈论 Tensorflow ,今天我们将对该库做一个简短的介绍,我们将一起构建一个有趣的项目。

Tensorflow.js 是什么,可以用来做什么?

TensorFlow.js 是一个由 Google 开发的 JavaScript 库,用于在浏览器和 Node.js 中训练和部署机器学习模型。它是 Python 的流行 ML 库 TensorFlow 的姊妹库。

TensorFlow.js 不仅仅是一个玩具库,它是严肃的商业,性能令人惊讶,尤其是在通过 WebGL 使用硬件加速时,但我们应该用它来训练模型吗?也许不是,即使你可以获得很好的性能,它的 Python 版本甚至更快,当使用 Python 时,你会发现更多的库来支持你的代码,如 Numpy 和 Pandas。除了学习资料,TensorFlow.js 的内容没有 TensorFlow 多。

现在,这并不意味着您不应该使用 TensorFlow.js,相反,我认为它是一个用于部署和运行 ML 模型的很好的库,这也是我们将在本文的剩余部分重点关注的内容。

使用 TensorFlow.js 部署示例模型

正如我们所说,TensorFlow.js 是一个强大的库,我们可以处理许多不同的事情,如图像分类、视频处理和语音识别等。今天,我决定研究一个基本的语音识别示例。

我们的代码将能够通过麦克风监听并识别用户在说什么,至少几个词,因为我们对我使用的示例模型有一些限制。但与其解释,我认为如果我们先看到它的实际应用会很酷:

不幸的是,我不能在介质上运行代码,但你可以在这里访问现场演示

很酷吗?我知道这可能有点不稳定,而且仅限于几个词,但如果您使用正确的模型,可能性是无穷的。说够了,让我们开始编码吧。

我们需要做的第一件事是安装库并获得我们的模型。为了安装 TensorFlow.js,有几个选项可以查看这里是,为了简单起见,我们将从 CDN 导入。

<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
<script src="https://unpkg.com/@tensorflow-models/speech-commands"></script>

然后我们将使用一些 HTML 来显示单词列表:

<div class="demo">
    <div>
        <label class="form-switch">
            <input type="checkbox" id="audio-switch">
            Microphone
        </label>
        <div id="demo-loading" class="hidden">Loading...</div>
    </div>
    <div id="sp-cmd-wrapper" class="grid"></div>
</div>

到目前为止,没有什么奇怪的,我们有我们的复选框,一个加载元素和一个包装元素,我们将使用它们来呈现单词列表,所以让我们接下来这样做:

const wrapperElement = document.getElementById('sp-cmd-wrapper');
for (let word of wordList) {
    wrapperElement.innerHTML += `<div id='word-${word}'>${word}</div>`;
}

为了让演示开始工作,我们需要单击麦克风复选框,让我们在那里设置一个事件监听器来触发加载和监听过程。

document.getElementById("audio-switch").addEventListener('change', (event) => {
    if(event.target.checked) {
        if(modelLoaded) {
            startListening();
        }else{
            loadModel();
        }
    } else {
        stopListening();
    }   
});

当复选框改变其值时,我们有 3 种不同的可能性,用户启用了复选框而模型未被加载,在这种情况下,我们使用loadModel()函数,但是如果模型已经被加载,我们触发监听过程。如果用户禁用了复选框,我们将停止访问麦克风。

让我们回顾一下每个函数的实现:

负载模型()

loadModel()负责创建识别器实例和加载模型。当模型被加载时,我们将能够通过recognizer.wordLabels()得到模型被训练的标签列表。这将有助于以后评估模型。

async function loadModel() { 
    // Show the loading element
    const loadingElement = document.getElementById('demo-loading');
    loadingElement.classList.remove('hidden');

    // When calling `create()`, you must provide the type of the audio input.
    // - BROWSER_FFT uses the browser's native Fourier transform.
    recognizer = speechCommands.create("BROWSER_FFT");  
    await recognizer.ensureModelLoaded()

    words = recognizer.wordLabels();
    modelLoaded = true;

    // Hide the loading element
    loadingElement.classList.add('hidden');
    startListening();
}

开始监听()

startListening()将在模型加载或用户启用麦克风后被调用,将负责访问麦克风 API 并评估模型以查看我们能够识别哪个单词。这听起来很复杂,但由于 TensorFlow 只有几行代码。

function startListening() {
    recognizer.listen(({scores}) => {

        // Everytime the model evaluates a result it will return the scores array
        // Based on this data we will build a new array with each word and it's corresponding score
        scores = Array.from(scores).map((s, i) => ({score: s, word: words[i]}));

        // After that we sort the array by scode descending
        scores.sort((s1, s2) => s2.score - s1.score);

        // And we highlight the word with the highest score
        const elementId = `word-${scores[0].word}`;
        document.getElementById(elementId).classList.add('active');

        // This is just for removing the highlight after 2.5 seconds
        setTimeout(() => {
            document.getElementById(elementId).classList.remove('active');
        }, 2500);
    }, 
    {
        probabilityThreshold: 0.70
    });
}

超级容易!现在是最后一个函数。

停止列表()

stopListening()将停止访问麦克风并停止评估。

function stopListening(){
    recognizer.stopListening();
}

就这样,这就是在 web 上构建第一个语音识别示例所需的全部内容。

把所有的放在一起

<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
<script src="https://unpkg.com/@tensorflow-models/speech-commands"></script><script type="text/javascript">
    let recognizer;
    let words;
    const wordList = ["zero","one","two","three","four","five","six","seven","eight","nine", "yes", "no", "up", "down", "left", "right", "stop", "go"];
    let modelLoaded = false;

    document.addEventListener('DOMContentLoaded', () => {
        const wrapperElement = document.getElementById('sp-cmd-wrapper');
        for (let word of wordList) {
            wrapperElement.innerHTML += `<div class='col-3 col-md-6'><div id='word-${word}' class='badge'>${word}</div></div>`;
        };

        document.getElementById("audio-switch").addEventListener('change', (event) => {
            if(event.target.checked) {
                if(modelLoaded) {
                    startListening();
                }else{
                    loadModel();
                }
            } else {
                stopListening();
            }   
        });
    });

    async function loadModel() { 
        // Show the loading element
        const loadingElement = document.getElementById('demo-loading');
        loadingElement.classList.remove('hidden');

        // When calling `create()`, you must provide the type of the audio input.
        // - BROWSER_FFT uses the browser's native Fourier transform.
        recognizer = speechCommands.create("BROWSER_FFT");  
        await recognizer.ensureModelLoaded()

        words = recognizer.wordLabels();
        modelLoaded = true;

        // Hide the loading element
        loadingElement.classList.add('hidden');
        startListening();
    }

    function startListening() {
        recognizer.listen(({scores}) => {

            # Everytime the model evaluates a result it will return the scores array
            # Based on this data we will build a new array with each word and it's corresponding score
            scores = Array.from(scores).map((s, i) => ({score: s, word: words[i]}));

            # After that we sort the array by scode descending
            scores.sort((s1, s2) => s2.score - s1.score);

            # And we highlight the word with the highest score
            const elementId = `word-${scores[0].word}`;
            document.getElementById(elementId).classList.add('active');

            # This is just for removing the highlight after 2.5 seconds
            setTimeout(() => {
                document.getElementById(elementId).classList.remove('active');
            }, 2500);
        }, 
        {
            probabilityThreshold: 0.70
        });
    }

    function stopListening(){
        recognizer.stopListening();
    }
</script><div class="demo">
    Please enable the microphone checkbox and authorize this site to access the microphone.
    <br />
    Once the process finished loading speak one of the word bellow and see the magic happen.
    <br /><br />
    <div>
        <label class="form-switch">
            <input type="checkbox" id="audio-switch">
            Microphone
        </label>
        <div id="demo-loading" class="hidden">Loading...</div>
    </div>
    <div id="sp-cmd-wrapper" class="grid"></div>
</div>

结论

TensorFlow.js 是一个强大的库,非常适合部署 ML 学习模型。今天我们了解到,只需几行代码,我们就能够加载一个模型并开始生成结果。与大多数 ML 解决方案一样,它与模型和数据一样好。

如果你有可以使用 TensorFlow.js 的好想法,或者如果你知道任何我可以用来构建下一个项目并在博客上展示的好模型,请在评论中告诉我。

一如既往的感谢阅读!

机器学习的速度立方

原文:https://towardsdatascience.com/speed-cubing-for-machine-learning-a5c6775fff0b?source=collection_archive---------52-----------------------

第 1 集:如何优化 Python 代码、CPU 和 I/O 利用率以及云中的部署。

莱纳斯·艾肯斯塔姆在 Unsplash 上的照片

介绍

在机器学习项目的某个阶段,您可能想要克服本地机器的限制(内核数量、内存等。),无论你是想生成大量数据来馈入深度学习网络,还是想尽快训练你的算法。在本文中,我们将带您踏上一段旅程,分享我们发现的困难和解决方案,从幼稚的实现到更高级的技术,利用云中可能存在的各种计算资源。我们希望这将帮助您有效地管理和提高您的机器学习项目的生产率。

生成大量数据(比如…很多!)

在我们的实验中,我们希望能够在几分钟内生成大量(比如说 1,000,000)的数据块,作为一些生成性对抗网络(GANs)的输入。如果你没听说过 GANs,可以在这里【1】阅读我的入门文章

我们正在讨论的块将是维度 n=100 的立方体(即1,000,000 个数据点)。这一特定数量的 1 兆体素(体积的数据点)被选择来匹配生成假脸的当前艺术状态,这可以在网站thispersondoesnotexist.com上看到。这里使用的技术被称为style gan2【2】(GANs 的一个变种),其中生成的图像大小为 1024 x 1024,因此数量级为 1 兆像素

为了反映来自真实世界的一些数据准备,额外的约束将应用于构建我们的立方体。它们将从数据帧中导出,并且将是堆叠 numpy 数组的结果。为了简单起见,这些数据帧将具有随机值。

这里的目标是尽可能快地创建立方体!

第一步将在我们的本地机器上完成,其规格在表 1 中描述。如你所见,这已经是一个相当不错的设置了。

表 1:我们本地机器的规格。

输入数据

我们首先生成大小为 1000 x 1000 的 100 个数据帧,由于使用了numpy.random.rand函数,这些数据帧包含随机浮点(四舍五入到 10 位小数)。然后使用pandas.DataFrame.to_csv功能将数据帧保存到磁盘。每个 csv 文件大约 13 Mo。

对于每个立方体,我们将不得不使用所有的数据帧来从中提取一个随机子集。使用pandas.read_csv函数,读取所有数据帧需要 20.87 秒,因此每秒 4.79 个文件。这是非常慢的。根据记录,如果我们想要构建 100 万个立方体,按照这个速度,需要超过 240 天

让我们考虑使用 parquet 文件格式来保存我们的数据帧,而不是使用 csv 格式。使用 fastparquet 引擎【3】,每个保存的拼花文件现在只有 8 Mo。如果你想了解更多的拼花文件格式,可以查看官网这里【4】。这一次,读取全部 100 个数据帧只需要 6.57 s (或 15.21 files/s)!这代表了 3.2 倍的加速。第一批结果汇总在表 2 中。

表 2:CSV 与 parquet 的读取速度比较(100 个文件)。

建造我们的第一个立方体

马克斯·亨克Unsplash 上拍摄的照片

使用一些约束,以下步骤将适用于构建一个立方体:

  1. 对于每个数据帧,我们提取包含前 100 行和 100 个随机列的子数据帧,
  2. 每个子数据帧然后被转换成尺寸为 100×100 的 numpy 数组,
  3. numpy 数组堆叠起来,形成一个立方体。
*# CREATING THE CUBES FROM FILES (READ IN A LOOP)***import** numpy **as** np
**import** pandas **as** pdncubes = 100
dim = 100**for** n **in** range(ncubes):
    cube = np.zeros(shape=(dim, dim, dim))
    *# 'files_list' is the list of the parquet file paths*
    **for** i, f **in** enumerate(files_list):
        df = pd.read_parquet(f)
        df = df.head(dim)
        rnd_cols = random.sample(range(1, df.shape[1]), dim)
        df = df.iloc[:, rnd_cols]
        layer = df.to_numpy()
        cube[i, :, :] = layer

获得一批 100 个立方体的总时间约为 661 秒(几乎 11 分钟),因此速率为 0.15 个立方体/秒

我相信你已经发现了这里的错误。事实上,对于每个立方体,我们每次都读取相同的 100 个拼花文件!实际上,您肯定不希望遍历这些文件。关于数据** 结构的下一个改进将会解决这个问题。**

提高速度—步骤 1:数据结构

照片由 Fezbot2000Unsplash 上拍摄

因为我们不想在循环中读取每个立方体的拼花文件,所以最好只提前执行一次这个任务。因此,我们可以构建一个字典df_dict,将文件名作为键,将数据帧作为值。这个操作非常快,而且字典只有 7.33 秒。

现在,我们将编写一个函数来创建一个立方体,利用这个字典已经读取了数据帧并将其存储为自己的值。

*# FUNCTION CREATING A CUBE FROM A DICTIONARY OF DATAFRAMES***def** create_cube(dimc, dict_df):
    cube = np.zeros(shape=(dimc, dimc, dimc))
    **for** i, df **in** enumerate(dict_df.values()):
        df = df.head(dimc)
        rnd_cols = random.sample(range(1, df.shape[1]), dimc)
        df = df.iloc[:, rnd_cols]
        layer = df.to_numpy()
        cube[i, :, :] = layer
    **return** cube

这一次,创建 100 个立方体只需要 6.61 秒,速率为 15.13 个立方体/秒。与不使用数据帧字典的先前版本相比,这代表了 100 倍的加速。创建我们这一批 100 万个立方体现在只需要将近 20 个小时,而不是最初的 240 天。

现在,我们仍然使用数据帧来构建我们的立方体,也许是时候使用fullNumPy 来提高我们的速度了。

提高速度——第二步:NumPy Rocks!

Hanson LuUnsplash 上的照片

以前使用数据帧字典的想法很有趣,但可以通过从一开始就构建一个从 parquet 文件派生的numpy.ndarray来改进,我们将沿着列对其进行子采样以创建我们的立方体。让我们首先创建这个大男孩:

*# CREATING THE RAW DATA (NUMPY FORMAT)*arr_data = np.zeros(shape=(100, 1000, 1000))
*# 'files_list' is the list of the parquet file paths*
**for** i, j **in** enumerate(files_list):
    df = pd.read_parquet(j)
    layer = df.to_numpy()
    arr_data[i, :, :] = layer

然后,我们必须相应地修改我们的create_cube函数,并实现一个完整的向量化:

*# FUNCTION CREATING A CUBE FROM RAW DATA (FULL NUMPY VERSION)***def** create_cube_np(dimc):
    rnd_cols = random.sample(range(1, 1000), dimc)
   * # First 100 rows, 100 random columns (vectorization)*
    cube = arr_data[:, :100, rnd_cols]
    **return** cube

使用这个新版本,我们能够在 1.31 秒内创建 100 个立方体,因此有一个不错的速率 76.26 立方体/秒

现在,我们可以进入下一步,以更快的速度前进。你猜对了,是时候进行并行化了!

提高速度—步骤 3:并行化

马克-奥利维尔·乔多因Unsplash 上拍摄的照片

在 Python【5】【6】中有几种方法可以执行并行化。这里,我们将使用原生的multiprocessing Python 包和imap_unordered函数来执行异步作业。我们计划利用本地机器上的 12 个内核。

*# PARALLELIZATION***from** multiprocessing.pool **import** ThreadPoolproc = 12  *# Number of workers*
ncubes = 100
dim = 100**def** work(none=None):
    **return** create_cube_np(dim)**with** ThreadPool(processes=proc) **as** pool:
    cubes = pool.imap_unordered(work, (None for i in range(ncubes)))
    **for** n **in** range(ncubes):
        c = next(cubes)  *# Cube is retrieved here*

这里导入了ThreadPool包(而不是通常的Pool包),因为我们希望确保以下:

  • 留在同一个进程中,
  • 避免在进程间传输数据,
  • 通过使用仅 numpy 操作绕过 Python 全局解释器锁(GIL)(大多数 numpy 计算不受 GIL 影响)。

在这篇漂亮的博客文章【7】中,你可以了解更多关于 Python 中多重处理和多线程的区别。

使用这种多线程方法,我们只需要 0.28 秒就可以创建一批 100 个立方体。我们达到了非常好的速率 355.50 立方/秒,因此与第一个版本相比,速度提高了 2370 倍(表 3 )。关于我们的 1,000,000 个立方体,生成时间已经下降了不到一个小时

表 3:立方体的本地生成,速度结果。

现在,是使用中的虚拟机实例飞行的时候了!

提高速度—步骤 4:云

SpaceXUnsplash 上拍摄的照片

如果说机器学习即服务( MLaaS ),排名前 4 的云解决方案分别是:微软 Azure、亚马逊 AWS、IBM Watson 和谷歌云平台(GCP)。在这项研究中,我们选择了 GCP,但任何其他供应商都可以完成这项工作。您可以在许多不同的虚拟机实例中选择或定制您自己的配置,您将能够在笔记本电脑中执行您的代码。

你想问自己的第一个问题如下:

"我想创建什么样的实例来满足我的计算需求?"

基本上,你可以找到三种类型的机器:通用、内存优化或计算优化(表 4 )。

表 4:不同工作负载的机器类型建议。

计算 numpyndarray步骤 2 开始,拼花文件首先被存储到云中的一个桶中。然后,在不同的 VM 实例上进行几次测试(表 5 ,保持与步骤 3 中相同的多线程代码,并逐步增加 vcpu(worker)的数量。一台虚拟机的结果示例在表 6 中给出。

表 5:一些虚拟机实例的选择。

表 6:针对“N2-high CPU-80”实例的速度结果。

在连接到虚拟机的终端中,您还可以使用htop linux 命令(图 1 )来可视化 vCPUs 的活动。

图 1:我们 160 个内核协同工作的情况(“m1-ultramem-160”实例)。

结论

图 2:所有测试的虚拟机实例的性能。

查看图 2 ,除了 m1-ultramem-160 实例(这是最昂贵的)之外,其他所有实例都执行得相当好,但是遵循相同的模式。该比率几乎随着工作人员数量的增加而线性增加,并在 60 个 vCPUs 时达到峰值。超过这个限制,速率会急剧下降,很可能是因为多线程的开销

在我们的选择中,胜出者n2-highcpu-80 实例(第二便宜),达到了 2026.62 cubes/s 的速率,几乎是每秒 20 亿个数据点。按照这个速度,我们可以在仅仅 8 分钟内生成 100 万个立方体。

我们最初的目标成功实现了!

整个实验表明,不仅代码很重要,硬件也很重要。我们在本地机器上以 0.15 立方/秒的速率开始,使用达到了非常快的速率 2027 立方/秒。这比13500 倍加速还要多!

这仅仅是开始…我们可以通过使用更先进的技术和基础设施来升级。这是为第二集准备的。

参考

[1] N. Morizet,生成式对抗网络导论(2020),Advestis Tech Report

[2] T. Karras 等人,StyleGAN (2019)的图像质量分析与改进,NVIDIA Research

[3]【fastparquet】正式文件(2020)

[4]阿帕奇拼花地板官方文档(2020)

[5] 多处理——基于进程的并行性(2020),Python 标准库

[6] " 雷"框架正式文件(2020)

[7]n . Grigg,阐释 Python 多线程与多处理(2015),来自 nathangrigg.com 的博客文章。

关于我们

Advestis 是一家欧洲合同研究组织(CRO),对统计学和可解释的机器学习技术有着深刻的理解和实践。Advestis 的专长包括复杂系统的建模和时间现象的预测分析。
领英:https://www.linkedin.com/company/advestis/

批量归一化折叠加速推理

原文:https://towardsdatascience.com/speed-up-inference-with-batch-normalization-folding-8a45a83a89d8?source=collection_archive---------25-----------------------

如何删除批处理规范化层,使您的神经网络更快。

介绍

批量标准化是一种技术,它负责标准化每一层的输入,以使训练过程更快、更稳定。实际上,这是一个额外的层,我们通常在计算层之后和非线性之前添加。

它包括 2 个步骤:

  • 通过先减去其平均值μ,然后除以其标准偏差σ来归一化该批次。
  • 进一步缩放因子γ并移位因子β。这些是批量归一化层的参数,在网络不需要数据的均值为 0 且标准差为 1 的情况下需要这些参数。

由于批处理规范化对训练神经网络的效率,它现在被广泛使用。但是在推理的时候有多大用处呢?

一旦训练结束,每个批次归一化层都拥有一组特定的γ和β,以及μ和σ,后者是在训练期间使用指数加权平均值计算的。这意味着在推断过程中,批量标准化是对前一层的结果进行简单的线性变换,通常是卷积。

由于卷积也是线性变换,这也意味着两种运算可以合并成一个线性变换!

这将删除一些不必要的参数,但也减少了推理时要执行的操作的数量。

在实践中如何做到这一点?

借助一点数学知识,我们可以很容易地重新排列卷积的项,以考虑批量标准化。

稍微提醒一下,对于输入 x ,批量归一化操作之后的卷积操作可以表示为:

因此,如果我们重新安排卷积的 Wb 来考虑批量归一化的参数,则:

我们可以删除批处理规范化层,但仍然有相同的结果!

注意:通常情况下,在批量标准化层之前的层中没有偏差。这是无用的,也是参数的浪费,因为任何常数都将被批量标准化所抵消。

效率如何?

我们将尝试两种常见的架构:

  • 带批次标准的 VGG16
  • ResNet50

仅仅为了演示,我们将使用 ImageNette 数据集和 PyTorch。两个网络都将被训练 5 个历元,以及在参数数目和推理时间方面有什么变化。

1.VGG16

先来训练 VGG16 个历元(最终精度无所谓):

然后显示其参数数量:

单幅图像的初始推断时间为:

现在,如果我们应用批量归一化折叠,我们有:

并且:

所以去掉 8448 参数甚至更好,几乎快了 0.4 ms 的推断!最重要的是,这是完全无损的,在性能方面完全没有变化:

让我们看看它在 Resnet50 的情况下是如何表现的!

2.Resnet50

同样,我们从训练它 5 个时期开始:

参数的初始数量是:

而推断时间是:

使用批量标准化折叠后,我们有:

并且:

现在,我们删除了 26,560 个参数,更令人印象深刻的是,推断时间减少了 1.5 毫秒!并且仍然没有任何性能下降。

因此,如果我们可以在不降低性能的情况下减少推理时间和模型的参数数量,为什么我们不能一直这样做呢?

希望这篇博文对你有所帮助!如果有不清楚的地方,请随时给我反馈或问我问题。

此处提供代码:

[## 纳坦胡本斯/法斯特赖

FasterAI:一个用 FastAI 库制作更小更快模型的库。-纳坦胡本斯/法斯特莱

github.com](https://github.com/nathanhubens/fasterai)

参考资料和进一步阅读材料:

加速训练并提高深度神经网络的性能

原文:https://towardsdatascience.com/speed-up-training-and-improve-performance-in-deep-neural-net-5732274d51a2?source=collection_archive---------44-----------------------

为 DNN 实现足够的初始化、激活功能和批量归一化/梯度剪裁

作者图片

T 训练一个大而深的神经网络是一项耗时耗力的任务,也是 20-30 年前 DNN 不受欢迎的主要原因。随着几种提高训练速度的技术被发现,深度学习重新回到了人们的视线中。那么使用哪种技术,如何以及何时使用哪种技术呢?在这里讨论一下吧!

时尚 MNIST 和加州住房数据集将分别用作分类&回归的示例。

1.应用初始化

初始化是用于加快神经元网络训练时间(以及提高性能)的第一批技术之一。在人工神经网络(ANN)中,不同神经元之间存在着大量的连接。当前层中的一个神经元连接到下一层中的几个神经元,并附着到前一层中的各个神经元。如果两个神经元比另一对更频繁地交互,它们的连接(即权重)将比另一对更强。

然而,人工神经网络的一个问题是,如果在训练开始时没有指定权重,连接可能会以太小或太大的值开始,从而使它们太小或太大,无法在网络中进一步使用。换句话说,网络会陷入消失梯度爆炸梯度的问题。

因此,如果从训练开始就将权重设置为合适的随机值,就可以避免这些问题。 Xavier 初始化Glorot 初始化技术是由 Glorot 和 Bengio 提出的,然后显著解除了这些不稳定问题。

在该策略中,神经元之间的连接权重将使用均值=0、方差σ= 2/(fan_in+fan_out)的正态分布随机初始化,其中 fan _ in 是输入神经元的数量,fan _ out 是输出神经元的数量。

除了 Glorot (在 Keras 中默认使用)之外,还有另外 2 种流行的初始化技术: HeLeCun

让我们用Fashion MNIST数据集检查不同的初始化技术对模型性能和训练时间的影响。

# Take a look at the dataset
plt.figure(figsize=(10, 10)) 
for row in range(5): 
    for col in range(5): 
        index = 5 * row + col 
        plt.subplot(5, 5, index + 1)
        plt.imshow(X_train_full[index], cmap="binary",
                   interpolation="nearest") 
        plt.axis('off') 
        plt.title(y_train_full[index], fontsize=12) 
plt.show()

以下是时尚 MNIST 的示例,其中预测值是一组代表图像的[28,28]形状的值;目标值是 10 种衣服和鞋子(用 0 到 9 表示)

首先,让我们从一个由 5 个隐藏层和 300,100,50,50,50 个神经元组成的网络上 Keras 的默认设置开始。

tf.random.set_seed(50) 
np.random.seed(50) 
model_default = keras.models.Sequential() model_default.add(keras.layers.Flatten(input_shape=[28, 28])) 
for n_layers in (300, 100, 50, 50, 50): 
    model_default.add(keras.layers.Dense(n_layers, 
                                         activation ='relu')) 
model_default.add(keras.layers.Dense(10, activation='softmax'))model_default.compile(loss="sparse_categorical_crossentropy",
                      optimizer=keras.optimizers.SGD(lr=1e-3), 
                      metrics=["accuracy"]) 
start_time = time.time() 
history = model_default.fit(X_train_full, y_train_full, epochs=20,
                            validation_split=0.1) 
print("--- %s seconds ---" % (time.time() - start_time))

结果

# Show the highest accuracy epoch 
Epoch 20/20 1688/1688 [==============================] - 5s 3ms/step - loss: 0.4185 - accuracy: 0.8526 - val_loss: 0.4256 - val_accuracy: 0.8518 
--- 99.03307843208313 seconds ---

训练集在 99.3 秒内达到 85.26%的准确率,Val 集达到 85.18%。如果没有设置activation ='relu'(即隐层没有激活函数),准确率分别为 85.32%和 84.95%,训练需要 104.5 秒。

将其与全 0 和全 1 的权重初始化进行比较:

# Zeros initialization Epoch 20/20 1688/1688 [==============================] - 3s 2ms/step - loss: 2.3026 - accuracy: 0.1008 - val_loss: 2.3028 - val_accuracy: 0.0925 
--- 69.43926930427551 seconds ---# Ones initialization Epoch 20/20 1688/1688 [==============================] - 3s 2ms/step - loss: 2.3026 - accuracy: 0.1008 - val_loss: 2.3028 - val_accuracy: 0.0925 
--- 67.2280786037445 seconds ---

两种情况下的性能都差得多,并且实际上,该模型从第 5 个时期开始就停止提高精度。

现在,让我们试试He Initialization,在 Keras 中通过向隐藏层添加kernel_initializer="he_normal"参数来启用。

结果

# Show the highest accuracy epoch 
Epoch 20/20 1688/1688 [==============================] - 5s 3ms/step - loss: 0.3780 - accuracy: 0.8672 - val_loss: 0.3924 - val_accuracy: 0.8637 
--- 99.76096153259277 seconds ---

精确度确实提高了,但是运行时间比 Glorot 初始化慢了半秒。

关于初始化技术中正态分布均匀分布的性能也有讨论,但确实没有一种技术表现出比另一种更好的性能。init = keras.initializers.VarianceScaling(scale=2.,mode='fan_avg',distribution='uniform')的结果对该数据集没有改善(训练集准确率:87.05%,值集:86.27%,运行时间 100.82 秒)

2.与正确的激活功能相处

选择不合适的激活函数是导致模型性能差的原因之一。sigmoid可能是个不错的选择,但我更喜欢用 SELU、ReLU 或 ReLU 变体来代替。

先说 ReLU 吧。简单来说,如果值大于 0,函数返回值本身;否则它返回 0。这种激活计算起来很快,但作为回报,将会出现停止输出除 0 以外的任何值的情况(即神经元死亡)。这个问题通常发生在学习率较大的情况下。

作者图片

这个问题的一些解决方案是使用 ReLU 的替代版本: LeakyReLU、随机化 LeakyReLU 或缩放 ReLU (SELU)

带有泄漏的路:

if x>0: 
   return x 
else: 
   return ax

其中 a 是给定 x <0. a is usually set at 0.01, serving as a small leak (that’s why this technique is called LeakyReLU). Using a helps to stop the dying problem (i.e. slope=0).

In the case of 随机泄漏率时 x 的斜率,a 是在给定范围内随机选择的。这种方法可以减少过拟合问题,但是由于额外的计算需要更多的运行时间。

DNN 的一个出色的激活函数是缩放的 ReLU (SELU)

if x>0: 
   return Lambda*x 
else: 
   return Lambda*(alpha*exp(x)-alpha)

在此函数中,每层输出的平均值为 0,标准差为 1。注意使用此激活功能时:

  • 必须和kernel_initializer="lecun_normal"一起使用
  • 输入要素必须标准化
  • 神经网络的结构必须是顺序的

让我们在fashion MNIST数据集上尝试不同的激活函数。

泄漏的结果

# Show the highest accuracy epoch 
Epoch 20/20 1688/1688 [==============================] - 5s 3ms/step - loss: 0.3791 - accuracy: 0.8670 - val_loss: 0.3910 - val_accuracy: 0.8615 
--- 101.87710905075073 seconds ---

随机化泄漏结果

# Show the highest accuracy epoch 
Epoch 20/20 1688/1688 [==============================] - 6s 3ms/step - loss: 0.3779 - accuracy: 0.8667 - val_loss: 0.3918 - val_accuracy: 0.8630 
--- 113.58738899230957 seconds ---

SELU的结果

# Show the highest accuracy epoch 
Epoch 19/20 1688/1688 [==============================] - 5s 3ms/step - loss: 0.3526 - accuracy: 0.8763 - val_loss: 0.3755 - val_accuracy: 0.8647 
--- 106.25733232498169 seconds ---

SELU 似乎比 ReLU 及其变种的性能稍好,但速度较慢(如预期)。

如果神经网络在低学习速率下表现相对较好,ReLU 是给定最快训练时间的最佳选择。在深度 NN 的情况下,SELU 是一个极好的尝试。

关于这些激活功能的详细说明可以在这里找到: ReLULeakyReLU、随机化 LeakyReLUSELU

3.批量标准化

为了确保消失/爆炸梯度问题不会在训练期间再次发生(因为初始化和激活功能有助于在训练开始时减少这些问题),实施批量标准化

批量归一化零点对每个输入进行居中和归一化,然后使用 1 个参数向量进行缩放和 1 个参数向量进行平移来缩放和平移结果。此技术评估当前小批量上输入的\(均值\)\(标准差\)并在训练集的所有小批量上重复此计算。均值和标准差是在训练期间估计的,但仅在训练后使用。

输入平均值的向量和输入标准偏差的向量将成为不可训练的参数(即反向传播不可触及的),并用于计算训练结束时的移动平均值。随后,这些最终参数将用于归一化新数据以进行预测。

如果使用批量标准化,输入数据在训练前不需要标准化。

tf.random.set_seed(50) 
np.random.seed(50)model_default = keras.models.Sequential() model_default.add(keras.layers.Flatten(input_shape=[28, 28])) 
for n_layers in (300, 100, 50, 50, 50):
    model_default.add(keras.layers.BatchNormalization())
    model_default.add(keras.layers.Dense(n_layers, 
                                         activation ='relu',
                                  kernel_initializer="he_normal")) 
model_default.add(keras.layers.Dense(10, activation='softmax'))model_default.compile(loss="sparse_categorical_crossentropy",                       
                      optimizer=keras.optimizers.SGD(lr=1e-3),                       
                      metrics=["accuracy"])  
start_time = time.time() 
history = model_default.fit(X_train_full, y_train_full, epochs=20, 
                            validation_split=0.1) 
print("--- %s seconds ---" % (time.time() - start_time))

结果

# Show the highest accuracy epoch 
Epoch 20/20 1688/1688 [==============================] - 8s 5ms/step - loss: 0.3799 - accuracy: 0.8645 - val_loss: 0.3571 - val_accuracy: 0.8685 
--- 167.6186249256134 seconds ---

显然,在批量标准化中训练较慢,因为在训练过程中需要更多的计算,但相比之下,模型收敛更快,因此达到相同的性能需要更少的时期。

4.渐变剪辑

由于批量归一化不建议与递归神经网络一起使用,梯度裁剪是 RNN 的替代选择。

关于渐变裁剪的细节

时尚 MNIST 数据集分类结果汇总

加州住房数据集回归任务的结果摘要

训练集和验证集的均方误差

关于这一部分的最后想法🔆

  • 对于大多数情况,Glorot 初始化是一个很好的起点。初始化技术有时比 Glorot 执行得更好(在上述分类示例中较慢,而在回归示例中较快)。
  • 如果优先考虑运行时间,ReLU 或 Leaky ReLU 是很好的选择。
  • 如果使用高学习率,应避免 ReLU。
  • 对于复杂的数据集和深度神经网络,SELU 是一个很好的选择,但可能会在运行时间上有所取舍。然而,如果神经网络的架构不允许自规范化,则使用 ELU 而不是 SELU。
  • SELU 和批处理规范化不能应用于 RNN。梯度裁剪是 RNN 批量标准化的替代策略。

源代码可以在这里获得

参考:

  1. x .格洛特和 y .本吉奥(2010 年)。理解训练深度前馈神经网络的困难。PMLR
  2. 何刚,张,徐,任,孙(2015)。深入研究整流器:在 ImageNet 分类上超越人类水平的性能。2015 年 IEEE 计算机视觉国际会议(ICCV)论文集
  3. Geron,A. (2019)。使用 Scikit-Learn、Keras 和 TensorFlow 进行机器学习。奥莱利媒体公司,
  4. 徐,王,陈,李,米(2015)。卷积网络中校正激活的经验评估。2020 年 5 月 5 日从https://arxiv.org/abs/1505.00853取回。
  5. Klambauer,g .,Unterthiner,t .,Mayr,a .,& Hochreiter,S. (2017 年)。自标准化神经网络。神经信息处理系统进展 30 (NIPS 2017)

使用 Python 的数据表包加速您的数据分析

原文:https://towardsdatascience.com/speed-up-your-data-analysis-with-pythons-datatable-package-56e071a909e9?source=collection_archive---------8-----------------------

Unsplash 上的 chuttersnap 拍摄

前阵子,我在 Python 的数据表库上写了一篇的文章。本文概述了 datatable 包,其重点是大数据支持和高性能。文章还在某些参数上比较了 datatable 和 pandas 库的性能。这是本系列的第二篇文章,具有双重目标:

  • 在本文中,我们将通过一个银行贷款场景,使用房利美数据集的子集,尝试理解与 datatable 的数据冲突。我们将学习如何管理贷款级别的数据,获得基本的洞察力,探索性的数据分析
  • 其次,我们将回顾开源数据科学中流行的各种类似数据库的工具的一些基准。

强烈建议在阅读本文之前先阅读第一部分。

数据表:快速概述

Python datatable是一个库,实现了广泛的(且不断增长的)操作二维数据框的操作符。它主要关注:大数据支持、高性能、内存和内存外数据集,以及多线程算法。Datatable 强大的 API 与 data.table 的类似,它努力提供更友好和直观的 API 体验,并提供有用的错误消息来加速问题的解决。

数据表的一些显著特征是:

  • 高效的多线程算法
  • 节约内存
  • 内存映射的磁盘数据集
  • 本机 C++实现
  • 完全开源

主数据表语法

在 datatable 中,所有这些操作的主要载体是受传统矩阵索引启发的方括号符号。

其中, i 是行选择器, j 是列选择器,...表示附加的修饰符。目前可用的修改器有by()join()sort()。这个工具包与熊猫非常相似,但更侧重于速度和大数据支持。

入门指南 是熟悉数据表的好地方。它包含关于如何开始使用 datable 的深入信息和示例。

让我们直接进入案例研究,让我们的手变脏。

案例研究:贷款违约预测

蒂埃拉·马洛卡在 Unsplash 上拍摄的照片

联邦国民抵押贷款协会 ( FNMA ),俗称 房利美 是一家政府支持的公司,成立于 1938 年声名狼藉的大萧条时期。房利美首先从一级贷款机构(富国银行、美国银行等)购买抵押贷款,然后在债券市场将其作为证券出售。然而,房利美购买的所有贷款都没有偿还,一些借款人实际上违约了。这是一个经典的例子,我们可以使用机器学习来预测房利美收购的贷款是否会丧失抵押品赎回权。

资料组

数据集来自房利美的单户家庭贷款表现数据 (SFLP),房利美保留所有权利。对于完整的原始数据集,你需要在房利美的网站上注册。在撰写本文时,可用的最新数据集来自2019.第三季度,然而,本文使用的是 2014 年第三季度的数据集,可从此处下载。

下载的数据集包含两个名为 Acquisition.txtPerformance.txt: 的文件

  • 获取数据:包含每个借款人的个人信息,包括个人的债务收入比、信用评分和贷款金额等。
  • 性能数据:包含有关贷款支付历史的信息,以及借款人最终是否拖欠贷款。

关于这两个文件内容的其他信息也可以在网站上以

目标

我们的目标是从这些数据中预测那些最有可能拖欠抵押贷款的借款人。为了开始分析,我们将使用 Python datatable 来获得从基本 EDA 和数据争论开始的基本见解。

整个代码可以从笔记本上访问: 用 Python 的数据表 加速你的数据管理

1.在数据集中读取

  • 导入数据表包
*import datatable as dt*
  • 加载数据集

接下来,我们将使用 datatable 的fread函数读取采集和性能文件。上面的fread()功能既强大又极快。它可以自动检测和解析大多数文本文件的参数,从。压缩档案或网址,阅读 Excel 文件,等等。

现有数据没有列标题,我们需要从文件中手动输入。

*col_acq = ['LoanID','Channel','SellerName','OrInterestRate','OrUnpaidPrinc','OrLoanTerm','OrDate','FirstPayment','OrLTV','OrCLTV','NumBorrow','DTIRat','CreditScore','FTHomeBuyer','LoanPurpose','PropertyType','NumUnits','OccStatus','PropertyState','Zip','MortInsPerc','ProductType','CoCreditScore','MortInsType','RelocationMort']col_per = ['LoanID','MonthRep','Servicer','CurrInterestRate','CAUPB','LoanAge','MonthsToMaturity','AdMonthsToMaturity','MaturityDate','MSA','CLDS','ModFlag','ZeroBalCode','ZeroBalDate','LastInstallDate','ForeclosureDate','DispositionDate','ForeclosureCosts','PPRC','AssetRecCost','MHRC','ATFHP','NetSaleProceeds','CreditEnhProceeds','RPMWP','OFP','NIBUPB','PFUPB','RMWPF',  'FPWA','SERVICING ACTIVITY INDICATOR'] df_acq = dt.fread('../input/Acquisition_2014Q3.txt',columns=col_acq)
df_per = dt.fread('../input/Performance_2014Q3.txt', columns=col_per)*

让我们检查两个框架的形状。

*print(df_acq.shape)
print(df_per.shape)
--------------------------------------------------------------------
(394356, 25)
(17247631, 31)*
  • 查看采集和性能数据框的前几行。

熊猫不同,.head()功能显示一帧的前 10 行,尽管您可以在括号中指定要显示的行数

*df_acq.head(5)*

*df_per.head(5))*

进度条的颜色表示数据类型,红色表示字符串,绿色表示int,蓝色表示float

2.数据预处理

性能数据集中,我们只对 LoanID止赎日期感兴趣,因为这将为我们提供借款人标识号以及他们最终是否违约。

  • 选择特定的列

因此,让我们只选择LoanIDForeclosureDate列,并丢弃其余的

*df_per = df_per[:,['LoanID','ForeclosureDate']]
df_per.head(5)*

  • 删除重复实体

贷款 ID 包含重复的实体。让我们也摆脱他们。

*dt.unique(df_per[:,"LoanID"]).head(5)*

  • 分组

让我们用独特的Loan IDs将画面分组。这将确保我们的数据集中只存在唯一的贷款 id。

*df_per = df_per[-1:,:, dt.by(dt.f.LoanID)]
df_per.head(5)*

f 表达式 支持算术运算以及各种数学和聚合函数。

  • 加入收购和业绩框架

现在让我们通过使用LoanID列执行内部连接来合并AcquisitionPerformance帧。让我们将产生的数据框架命名为df.,这个框架由ForeclosureDate列组成,我们将使用它作为我们的目标变量。让我们将该列重命名为Will_Default以避免任何混淆。

**df_per.names = ['LoanID','Will_Default']
df_per.key = 'LoanID'
df= df_acq[:,:,dt.join(df_per)]**
  • 格式化目标列

Will Default列由日期组成。例如,如果借款人已经偿还了贷款,那么就要提到偿还贷款的日期。但是,如果贷款尚未偿还,则该字段留空。让我们用“0”替换空白值,即贷款从未支付,并用一些值作为“1”填写。这意味着借款人没有违约,即在某个日期偿还了贷款。

***# Replacing the dates in the Will_Default column with '0' and null values with 1*
df[:,'Will_Default'] = df[:, {'Will_Default': dt.f['Will_Default']==""}]
df.head(5)**

最后,让我们看看处理过的数据集的形状。

**df.shape
-------------------------------------------------------
(394356, 26)**

dataframe 有 394356 行和 26 列,包含有关贷款利率、支付日期、财产状态和每个财产邮政编码的最后几个数字等信息。从这里开始,数据就可以输入到用于训练目的的模型中。人们也可以将其转换成熊猫数据帧、CSV 文件或二进制文件。jay 文件如下:

***df.to_pandas()*
*df.to_csv("out.csv")*
*df.to_jay("data.jay")***

类似数据库的运营基准

今天,数据科学生态系统中存在许多类似数据库的工具。为了比较它们的性能,已经创建了一个基准,它定期运行这些软件包的最新版本,并自动更新。这对包的开发者和用户都是有益的。你可以在 Matt Dowle 在 H2OWorld 2019 NYC conference 上发表的 数据处理效率幻灯片演讲中了解更多关于该项目的信息。

阅读基准

您可以单击选项卡,查看您拥有的数据的大小和正在执行的操作的类型。然后,您会看到各种工具的并排比较,以及任务所用的时间。例如,下面是在 5 GB 和 50GB 数据集上执行的' join '函数的基准测试,可以看出,datatable 运行得非常好。

  • 5GB 数据集

  • 50 GB 数据集

如需了解更多任务和其他详细信息,请随时查看该页面:

类似数据库的 ops 基准

** [## 类似数据库的运营基准

该页面旨在对开源数据科学中流行的各种类似数据库的工具进行基准测试。它定期与…运行

h2oai.github.io](https://h2oai.github.io/db-benchmark/)**

结论

在处理大数据时,datatable 包确实大放异彩。由于强调大数据支持,datatable 提供了许多好处,并且确实可以缩短在数据集上执行数据争论任务所需的时间。Datatable 是一个开源项目,因此它对贡献和合作开放,以改进它并使它变得更好。

参考资料和有用的资源

以下是一些有助于理解和了解更多关于 datatable 特性的资源:

  1. 官方文档

原发布于 H2O.ai

使用 klib 加速您的数据清理和预处理

原文:https://towardsdatascience.com/speed-up-your-data-cleaning-and-preprocessing-with-klib-97191d320f80?source=collection_archive---------10-----------------------

定制和非常容易应用的功能,具有合理的默认值

作者图片

TL;博士
klib软件包提供了许多非常容易应用的函数,具有合理的默认值,可以在几乎任何数据帧上使用,以评估数据质量,获得洞察力,执行清理操作和可视化,从而使 Pandas 数据帧的工作变得更加轻便和方便。

在过去的几个月里,我实现了一系列函数,这些函数我经常用于几乎任何数据分析和预处理任务,而不管数据集或最终目标是什么。

这些功能只需要任何大小和任何数据类型的 Pandas 数据框架,并且可以通过简单的一行调用来访问,以深入了解您的数据,清理您的数据框架并可视化要素之间的关系。这取决于你是否坚持明智的,但有时是保守的,默认参数或定制的经验,调整他们根据你的需要。

这个包并不意味着提供一个 Auto-ML 风格的 API。相反,它是一个函数的集合,每当您开始处理一个新的项目或数据集时,您都可以——并且可能应该——调用它。不仅仅是为了你自己对你正在处理的事情的理解,也是为了制作你可以展示给主管、客户或其他任何人的图表,以获得更高层次的数据表示和解释。

安装说明

使用 pip 安装 klib:

pip install --upgrade klib

或者,要安装 conda run:

conda install -c conda-forge klib

以下是我在面对新数据集时反复应用的工作流程和一套最佳实践。

快速大纲

  • 评估数据质量
  • 数据清理
  • 可视化关系

本指南中使用的数据是在 Kaggle 上找到的 NFL 数据集的略经删节的版本。你可以在这里下载它或者使用任何你想使用的数据。

评估数据质量

在开始处理数据集之前确定数据质量至关重要。实现这一点的一个快速方法是使用 klib 的缺失值可视化,调用起来很简单,如下所示:

缺失值的默认表示

这个单一的情节已经向我们展示了许多重要的东西。首先,我们可以识别所有或大部分值缺失的列。这些是要剔除的对象,而缺失值较少的对象可能会受益于插补。

其次,我们经常可以看到缺失行的模式跨越许多要素。在考虑放弃潜在的相关特性之前,我们可能想先消除它们。

最后,顶部和右侧的附加统计信息为我们提供了关于阈值的有价值的信息,我们可以使用这些信息来删除有许多缺失值的行或列。在我们的示例中,我们可以看到,如果我们删除超过 30 个缺失值的行,我们只会丢失几个条目。同时,如果我们排除缺失值大于 80%的列,那么四个受影响最大的列也将被删除。

关于性能的一个快速提示:尽管已经检查了大约 200 万个条目,每个条目有 66 个特征,但创建情节只需要几秒钟。

数据清理

有了这种认识,我们就可以开始清理数据了。使用 klib,这就像调用 klib.data_cleaning() 一样简单,它执行以下操作:

  • 清理列名: 这通过格式化列名,将 CamelCase 拆分为 camel_case,删除特殊字符以及前导和尾随空格,并将所有列名格式化为小写 _ 和 _ 下划线 _ 分隔的来统一列名。这还会检查并修复重复的列名,从文件中读取数据时有时会遇到这种情况。
  • 删除空列和虚拟空列: 您可以使用参数 drop_threshold_colsdrop_threshold_rows 来根据您的需要调整删除。默认情况下,删除 90%以上的值缺失的列和行。
  • 删除单值列: 顾名思义,这将删除每个单元格包含相同值的列。当您只查看单个年份时,如果包含“年份”等列,这将非常方便。其他的例子是“下载日期”或对所有条目都相同的指示符变量。
  • 删除重复的行: 这是一个直接删除完全重复的行的方法。如果您正在处理重复增加价值的数据,请考虑设置 drop_duplicates=False。
  • 最后,通常也是最重要的,特别是为了减少内存,从而加快工作流程中的后续步骤, klib.data_cleaning() 也优化了数据类型,如下图所示。
Shape of cleaned data: (183337, 62) - Remaining NAs: 1754608

Changes:
Dropped rows: 123
     of which 123 duplicates. (Rows: [22257, 25347, 26631, 30310, 33558, 35164, 35777, ..., 182935, 182942, 183058, 183368, 183369])
Dropped columns: 4
     of which 1 single valued. (Column: ['play_attempted'])
Dropped missing values: 523377
Reduced memory by at least: 63.69 MB (-68.94%)

您可以使用参数 show=Noneshow="changes "或 show="all" 来更改输出的详细程度。请注意,内存减少表示一个非常保守的值(即比实际实现的减少要少),因为它只执行浅层内存检查。深度内存分析会降低大型数据集的运行速度,但是如果您对大小的“真实”减少感兴趣,您可以使用如下所示的 df.info() 方法。

df.info(memory_usage='deep')dtypes: float64(25), int64(20), object(21)
memory usage: 256.7 MB

正如我们所看到的,pandas 为每个 float 和 int 分配了 64 位的存储空间。此外,有 21 列是“object”类型,这是一种非常低效的数据存储方式。在数据清理之后,的内存使用量下降到只有 58.4 MB,几乎减少了 80%!这是通过在可能的情况下将 float64 转换为 float32 以及将 int64 转换为 int8 来实现的。此外,还使用了 dtypes 字符串类别。可用参数如 convert_dtypes、类别cat_threshold 等允许您根据自己的需要调整功能。

df_cleaned.info(memory_usage='deep')dtypes: category(17), float32(25), int8(19), string(1)
memory usage: 58.4 MB

最后,我们看一下列名,它们在原始数据集中实际上已经被很好地格式化了。然而,在清理过程之后,你可以依靠小写和下划线连接的列名。虽然不建议避免歧义,但现在允许你使用 df.yards_gained 而不是 df["Yards。获得了“] ,这在进行快速查找或第一次探索数据时非常有用。

Some column name examples:
Yards.Gained --> yards_gained
PlayAttempted --> play_attempted
Challenge.Replay --> challenge_replay

最后,总结一下:我们发现,不仅列名被整齐地格式化和统一了,而且特性也被转换成了更有效的数据类型。使用相对温和的默认设置,只删除了 123 行和 4 列,其中一列是单值的。这给我们留下了一个轻量级数据帧的形状:(183337,62)和 58 MB 的内存使用。

相关图

一旦完成了初始数据清理,查看特征之间的关系是有意义的。为此,我们使用函数 klib.corr_plot() 。将 split 参数设置为【pos】【neg】【high】【low】,并可选地将每个设置与一个阈值相结合,允许我们更深入地挖掘并突出最重要的方面。

相关图

一眼看去,我们可以发现一些有趣的关系。类似地,我们可以很容易地放大任何给定阈值以上的相关性,比如说|0.5|。这不仅让我们能够在以后的分析中发现可能会引起麻烦的特征,还让我们看到在我们的数据中有很多高度负相关的特征。如果有足够的领域专业知识,这可能是一些特性工程的一个很好的起点!

高绝对相关图

此外,使用相同的函数,我们可以查看特征和所选目标之间的相关性。目标列可以作为当前数据帧的列名提供,作为一个单独的 pd。系列、名词短语或简单的列表。

与目标/标签的相关性图

就像以前一样,可以使用各种参数进行定制,如删除注释、更改关联方法或更改色彩映射表,以匹配您喜欢的风格或公司标识。

分类数据

在本指南的最后一步中,我们快速了解一下可视化分类列的功能。函数 klib.cat_plot() 允许显示每列中与频率相关的顶部和/或底部值。这使我们对数据集中的值的分布有了一个概念,当考虑在应用一次热编码或类似函数之前将不太频繁的值合并到一个单独的类别中时,这非常有帮助。在本例中,我们可以看到,对于列“play_type ”,大约 75%的条目由三个最常见的值组成。此外,我们可以立即看到“Pass”和“Run”是目前最常见的值(75k 和 55k)。反过来,这个情节也向我们展示了“desc”是由 170384 个独特的字符串组成的。

分类数据图

klib 包包括许多更有用的数据分析和清理函数,更不用说一些定制的 sklearn 管道,您可以使用 FeatureUnion 轻松地将它们堆叠在一起,然后在 GridSearchCV 或类似的中使用。因此,如果您想走捷径,只需调用 klib.data_cleaning()并将结果数据帧插入管道。很可能,你已经得到了一个很不错的结果!

结论

所有这些函数都有助于非常方便的数据清理和可视化,并且提供了比这里描述的更多的特性和设置。它们绝不是万能的解决方案,但对您的数据准备过程非常有帮助。klib 还包括各种其他函数,最著名的是 pool_duplicate_subsets() ,用于汇集不同特征的数据子集,作为一种降维的方法, dist_plot() ,用于可视化数字特征的分布,以及 mv_col_handling() ,它提供了一个复杂的 3 步过程,试图识别具有许多缺失值的列中的任何剩余信息,而不是简单地立即删除它们。

[## 使用 klib 进行数据准备

快速简单的功能需要高效的数据准备

towardsdatascience.com](/data-preparation-with-klib-ec4add15303a)

注意:请让我知道你接下来想看到什么,以及你觉得缺少哪些功能,或者在下面的评论中,或者在 GitHub 上发表一个问题。如果你想看一些关于丢失值的处理,子集池或者定制的 sklearn 管道的例子,也请告诉我。

用 Swifter 加速你的熊猫处理

原文:https://towardsdatascience.com/speed-up-your-pandas-processing-with-swifter-6aa314600a13?source=collection_archive---------11-----------------------

不要让你的数据处理花费你太长时间

Marc-Olivier Jodoin 在 Unsplash 上拍摄的照片

作为一名 Pythonist 数据科学家,您的日常工作将涉及大量使用 Pandas 包的数据处理和特性工程。从分析数据到创建新功能再到获得洞察力,迫使您重复执行许多不同的代码。问题是你的数据越大,完成每一行代码的时间就越长。

在这篇文章中,我想向你展示一个简单的包来加快你的熊猫处理速度,这个包叫做 Swifter 。让我们开始吧。

下前支索

Swifter 是一个软件包,它试图以最快的可用方法有效地将任何函数应用于 Pandas 数据框或系列对象。它与 Pandas 对象集成在一起,因此我们只能将此包用于 Pandas 对象,如数据框或系列。

让我们试着看看更快的行动。为了做好准备,我们需要安装 Swifter 包。

#Installing Swifter via Pip
pip install swifter#or via conda
conda install -c conda-forge swifter

如果你没有最新的熊猫包,建议你把包升级到最新版本。这是因为在 Swifter 模块中使用的 pandas 扩展 api 是 Pandas 的新增功能。

#Update the Pandas package via pip
pip install -U pandas#or via conda
conda update pandas

当所有需要的包准备就绪,我们可以继续尝试更快。在本文中,我将使用来自 Kaggle 的 Reddit 评论数据集。从这里,我们将所有的包导入到我们的笔记本中,并像往常一样从 CSV 中读取数据集。

#Import the package
import pandas as pd
import swifter#read the dataset
df = pd.read_csv('r_dataisbeautiful_posts.csv')

这是我们的数据集。现在,假设我想将分数乘以 2,减去 1(这只是我在这里使用的一个随机方程)。然后我会把它放在另一栏。在这种情况下,我可以使用熊猫对象属性中的 apply 函数。

%time df['score_2_subs'] = df['score'].apply(lambda x: x/2 -1)

熊猫申请执行时间

对于 Pandas 的 apply 属性,对每个数据执行函数的时间大约需要 42.9 ms。这一次,我们将使用 Swifter,看看执行这个函数需要多少时间。

#When we importing the Swifter package, it would integrated with Pandas package and we could use functional attribute from Pandas such as apply%time df['score_2_swift'] = df['score'].swifter.apply(lambda x: x/2 - 1)

更快的应用执行时间

正如我们在上面看到的,Swifter 处理数据的速度比普通的 Pandas apply 函数要快。

更快的矢量化功能

从文档中可以看出,Swifter 应用函数的速度比 Pandas 函数快 100 倍。然而,这只适用于我们使用矢量化形式的函数的情况。

假设我创建了一个函数来计算 num_comments 和 score 变量。当评论数为零时,我会加倍评分。虽然不是,但分数会保持不变。然后我会在此基础上创建一个新的专栏。

def scoring_comment(x):
    if x['num_comments'] == 0:
        return x['score'] *2
    else:
        return x['score']#Trying applying the function using Pandas apply
%time df['score_comment'] = df[['score','num_comments']].apply(scoring_comment, axis =1)

熊猫应用非矢量化函数执行时间

执行该功能大约需要 3.96 秒。让我们看看性能,如果我们使用更快。

%time df['score_comment_swift'] = df[['score', 'num_comments']].swifter.apply(scoring_comment, axis =1)

更快地应用非矢量化函数执行时间

正如我们在上面看到的,使用 Swifter 比普通的 Pandas 应用功能需要更长的时间。这是因为具有非向量化功能的 Swifter 将实现 dask 并行处理,而不依赖于 Swifter 处理本身。那么,如果把函数改成向量化函数,性能如何呢?让我们试一试。

Import numpy as np#Using np.where to implement vectorized function
def scoring_comment_vectorized(x):
    return np.where(x['num_comments'] ==0, x['score']*2, x['score'])#Trying using the normal Pandas apply
%time df['score_comment_vectorized'] = df[['score', 'num_comments']].apply(scoring_comment_vectorized, axis =1)

熊猫申请矢量化函数执行时间

使用普通的 apply 函数执行我们的矢量化函数大约需要 6.25 秒。让我们看看使用 Swifter 的性能。

%time df['score_comment_vectorized_swift'] = df[['score', 'num_comments']].swifter.apply(scoring_comment_vectorized, axis =1)

更快的应用和矢量化的函数执行时间

矢量化函数的执行时间现在只需 11 毫秒,与普通的 apply 函数相比节省了很多时间。这就是为什么当我们用 Swifter 处理数据时,建议使用矢量化函数。

如果你想跟踪刚刚发生的执行时间,我会在下面的表格中给你一个总的总结。

结论

我只是向你展示 Swifter 如何加速你的熊猫数据处理。当我们使用矢量化函数而不是非矢量化函数时,Swifter 的效果最好。如果你想了解更多关于 Swifter 和可用 API 的信息,你可以查看文档。

如果你喜欢我的内容,并想获得更多关于数据或作为数据科学家的日常生活的深入知识,请考虑在这里订阅我的时事通讯。

如果您没有订阅为中等会员,请考虑通过我的推荐订阅。

加速 sklearn 模型管道,以极低的延迟提供单一预测

原文:https://towardsdatascience.com/speeding-up-a-sklearn-model-pipeline-to-serve-single-predictions-with-very-low-latency-a7fd89c36d4?source=collection_archive---------52-----------------------

编写你自己的 sklearn 函数,(现在是最终版本)第 3 部分

如果你以前使用过 sklearn,你肯定遇到过在使用数据帧或数组作为你的转换器和估算器的输入之间的挣扎。两者各有利弊。但是一旦你部署了你的模型,例如作为一项服务,在许多情况下它将服务于单一的预测。 Max Halford 展示了一些很好的例子关于如何改进各种 sklearn 变压器和估算器,以服务于单次预测,额外提升性能和低毫秒范围内的潜在响应!在这篇短文中,我们将推进这些技巧,开发一个完整的管道。

几个月前 Max Halford 写了一篇很棒的博文,他描述了我们如何修改 sklearn 转换器和估计器,以更高的速度处理单个数据点,本质上是使用一维数组。当您构建 sklearn 模型管道时,它们通常同时处理 numpy 数组和 pandas 数据帧。数组通常提供更好的性能,因为许多计算的 numpy 实现是高性能的,并且通常是矢量化的。但是使用列名来控制转换也变得更加棘手,而数组没有列名。如果您使用 pandas dataframes,您可能会得到更差的性能,但是您的代码可能会更具可读性,并且列名(即特性名)与大多数转换器的数据保持一致。在数据探索和模型训练期间,您最感兴趣的是批量转换和预测,但是一旦您将训练好的模型管道部署为服务,您可能还会对单个预测感兴趣。在这两种情况下,服务用户将发送如下所示的有效载荷。

稍微有用的插图—作者使用 draw.io 制作的图像

假设有一个服务,我们根据一些尺寸度量来估计鱼的重量(参考后面介绍的鱼市场数据集),那么一个请求可能如下所示:

{
    "species": "Bream",
    "length": 24.5,
    "heigth": 12.3,
    "width": 4.12,
}

或者可选地["Bream", 24.5, 12.3, 4.12],并且模型可以返回如下的权重估计:

在他的博客中,Max Halford 展示了如何将transform_singlepredict_single方法添加到变压器和估算器中,以更高的性能执行单数据点处理。根据管道的复杂程度,节省的绝对时间可能并不多。但是更长的响应时间会增加服务基础设施的整体延迟,而短时间会给应用程序带来压力,特别是如果它在关键路径内。我们最终还将能够节省基础设施成本,因为我们可以在更小的硬件上运行您的服务,即更小、更少的 pod。此外,避免数据帧强制将释放服务实例上的内存。最后但并非最不重要的是,我们节省了时间,我们可以在更复杂的转换和模型上花费时间——这让每个数据科学家都很高兴!

创建基本变形金刚

但是缩短响应时间的代价是什么呢?我们可以通过看一个例子来探索这一点,这里没有广告类继承,而是作为如何工作的草图:

barebones_transformer = BarebonesTransformer()barebones_transformer.fit(data)
barebones_transformer.transform_single([1.0, 2.5])

一方面,我们有失去训练和推理/预测奇偶性的风险。这是什么意思?正如我们在上面看到的,我们的数据在转换过程中基本上有两种不同的代码路径:单个预测的代码路径将仅用于单个数据点的推断,而不是在训练期间,在训练期间,我们通常成批转换,即数据帧或数组。因此,我们需要付出额外的努力来确保两条转换路径导致相同的转换,从而导致相同的结果。例如,这可以通过添加一些额外的单元测试来实现。

另一方面,我们可能会丢失 sklearn 正在内部进行的一些验证,也就是说,当父方法transform被调用时。因此,我们需要确保在将有效载荷传递给模型之前对其进行适当的验证,以防止模型意外崩溃。

同样的想法也适用于估计器和predict方法。最后,这就像通过卡车(加上保险)发送一封信,它是可行的,是一个安全的选择,但它可能是过度的,骑自行车的邮递员可能更合适、更快。

不同有效载荷的不同运输类型——图片由作者使用 draw.io 完成

如果我们对这两个缺点都满意,那么如果我们花一些时间来操作单个数据点的现有转换器,我们可以为每个请求节省一些时间。

现在,既然我们已经看到了如何实现这一点,那么让我们使用一些 toy day 和 sklearn 的SimpleImputer(一种用于估算缺失数据的转换器,例如使用平均值)来评估 pandas 和基于 numpy 的方法的性能。我们将使用非常健壮的 pd.isna 来检查 1d 数组中缺失的值:

import pandas as pd
import numpy as npnp.random.seed(47723)# truncate decimals for better printing
np.set_printoptions(precision=3, suppress=True)
pd.set_option('precision', 3)n = 1000
data = pd.DataFrame({
    'num1': np.random.normal(0, 1, n),
    'num2': np.random.normal(0, 1, n)
})# remove 10% of the data
data[np.random.rand(*data.shape) > 0.9] = np.nandata.head()
##     num1   num2
## 0  0.897 -1.626
## 1  1.370  0.279
## 2    NaN -0.652
## 3  1.379 -0.164
## 4  0.450    NaN

SimpleImputer将拟合的插补值存储在self.statistics_(按照惯例拟合值总是以下划线结尾):

from sklearn.impute import SimpleImputersimple_imputer = SimpleImputer(strategy='mean')
simple_imputer.fit_transform(data)
## array([[ 0.897, -1.626],
##        [ 1.37 ,  0.279],
##        [ 0.071, -0.652],
##        ...,
##        [-0.233,  0.741],
##        [ 0.071, -0.627],
##        [-1.056, -0.622]])
simple_imputer.statistics_
## array([0.071, 0.016])

我们可以用这些值在我们的transform_single中填充缺失值:

为最小变压器计时

现在让我们评估一下性能改进。我们将利用timeit和一些简单的辅助函数来测量毫秒计时:

from timeit import timeitdef time_func_call(call: str, n: int = 1000):
  t = timeit(call, globals = globals(), number=n) / n
  t_ms = np.round(t * 1000, 4)
  return t_mstime_func_call('barebones_simple_imputer.transform(data)')
## 3.0503
time_func_call('barebones_simple_imputer.transform_single([1.2, np.nan])')
## 0.0701

我们将定义另一个助手函数,它比较并输出多个函数调用的时间:

from typing import Listdef time_func_calls(calls: List[str]):
    max_width = np.max([len(call) for call in calls])
    for call in calls:
        t_ms = time_func_call(call)
        print(f'{call:{max_width}}: {t_ms:.4f}ms')
    return

我们现在可以将此应用于数据帧和数字数组形式的多个和单个数据点:

因此单数据点转换优于其他实现。让我们快速检查一下OneHotEncoder,另一个非常有用的转换器,它用虚拟变量对分类变量进行编码。我们还将再次定义一些玩具数据:

n = 3000data = pd.DataFrame({
    'cat1': np.random.choice(['a', 'b', 'c'], n),
    'cat2': np.random.choice(['x', 'y'], n)
})data.head()
##   cat1 cat2
## 0    a    x
## 1    b    x
## 2    b    y
## 3    a    x
## 4    b    y

OneHotEncoder将学习到的类别存储在self.categories_中的一个列表中,我们可以从中提取并使用它来编码分类变量:

barebones_one_hot_encoder = BarebonesOneHotEncoder(sparse=**False**, handle_unknown='ignore')
barebones_one_hot_encoder.fit_transform(data)
*## array([[1., 0., 0., 1., 0.],*
*##        [0., 1., 0., 1., 0.],*
*##        [0., 1., 0., 0., 1.],*
*##        ...,*
*##        [0., 0., 1., 0., 1.],*
*##        [1., 0., 0., 1., 0.],*
*##        [0., 1., 0., 1., 0.]])*barebones_one_hot_encoder.categories_
*## [array(['a', 'b', 'c'], dtype=object), array(['x', 'y'], dtype=object)]*barebones_one_hot_encoder.transform_single(['b', 'x'])
*## array([0, 1, 0, 1, 0])*

让我们再次评估不同案例的基准:

编码器现在只需要 0.02 毫秒,而不是 0.5 毫秒,提高了大约 25 倍。现在,让我们将这些放在一起,测量公共管道的整体性能改进。我们将获取一些名为鱼市场数据集的数据集,其中包含鱼的尺寸测量和分类,我们希望在其中预测它们的重量。

数据如下所示:

x.head()
##   species  length1  length2  length3  height  width
## 0   Bream     23.2     25.4     30.0  11.520  4.020
## 1     NaN     24.0     26.3     31.2  12.480  4.306
## 2   Bream     23.9     26.5     31.1  12.378  4.696
## 3   Bream     26.3     29.0     33.5  12.730  4.455
## 4   Bream     26.5     29.0     34.0  12.444  5.134
y.head()
## 0    242.0
## 1    290.0
## 2    340.0
## 3    363.0
## 4    430.0
## Name: weight, dtype: float64

如果我们想对我们的数据进行插补和一次性编码,我们需要使用ColumnTransformers将转换分派到正确的列。因此,我们需要对它做一些小的修改,以便能够使用transform_single方法:

  • 执行transform_single类似于transform,用于 ex。使用自我。_iter
  • transform_single实现一个身份转换器,可以传递它来处理剩余的,即剩余的列

建立快速管道

如果我们想在管道中使用基本的转换器和估算器,我们也必须修改管道本身,通过添加一个类似于预测predict_single,它使用转换器的transform_single方法并调用模型的predict_single,正如 Max 在他的帖子中所描述的。

我们现在可以构建我们的管道。我们将用最常见的值估算分类变量,用平均值估算数值(这里不是最聪明的估算方法,因为两者之间存在很强的关系,条件平均值或最近邻法会更好)。然后,我们将对分类变量进行一次性编码,并根据数据训练一个线性模型。

现在,让我们将管道应用于我们的数据,对单个预测的性能进行基准测试:

让我们最后评价一下,这两个预测是相同的。运行predict仍然使用成熟的 sklearn 代码路径,与我们的轻量级{transform,predict}_single方法相反:

batch_predictions = barebones_pipeline.predict(x)
batch_predictions[0:5]
## array([285.874, 418.604, 363.433, 417.214, 459.909])single_predictions = [barebones_pipeline.predict_single(x_i) for x_i in x.to_numpy()]
single_predictions[0:5]
## [285.873, 418.603, 363.433, 417.214, 459.909]np.all(np.isclose(batch_predictions, single_predictions, atol = 0.0001))
## True

结论

我们发现,对于单次预测(2.4 毫秒到 0.1 毫秒),我们可以将流水线速度提高 20 到 25 倍。但是我们添加的转换越多,加速就越有价值,权衡也就越清晰。我们已经看到了如何使用定制的转换器或调整现有的转换器来加速单个数据点转换和预测,代价是在工程上花费了额外的时间(尤其是在转换更复杂的情况下),以及在训练-推理奇偶校验、单元测试和数据验证上花费了额外的精力。

备注:分析变压器

如果你试图找到你的变压器的瓶颈,我推荐使用线路分析器内存分析器。它们可能在整个管道上都是不可监督的(您必须将所有单独的功能传递给它),但是在单独的转换器上是可监督的。您可以通过以下方式神奇地使用分析器:

或者没有魔法:

https://blog.telsemeyer.com】最初发表于

通过非结构化稀疏性加速深度学习推理

原文:https://towardsdatascience.com/speeding-up-deep-learning-inference-via-unstructured-sparsity-c87e3137cebc?source=collection_archive---------41-----------------------

照片鸣谢:麻省理工开放式课程 6.172

为大型神经网络提供服务可能非常昂贵。神经网络的大小似乎与它们的有用程度相关,这当然没有帮助。(典型的例子:GPT-3)

一种自然的冲动是通过作弊让神经网络变小。在宋寒 2016 年的开创性深度压缩论文(据谷歌称,该论文已被引用约 4000 次)中,他提出了两种抄近路的方法:量化和修剪。量化旨在减少权重和激活中的比特数。修剪的目的是保留有用的权重,并将其余的权重设置为零。两者都成为了活跃的研究领域。注意量化和修剪是非常正交的方法。它们可以而且应该结合在一起。

我们在这里主要讨论修剪。自 2016 年以来的 4 年中,许多修剪研究都集中在结构化修剪上。为什么要结构化剪枝?在剪枝中将无用的权重设置为零后,我们只剩下一个稀疏的权重矩阵。人们很快就意识到,当前的商用硬件/软件堆栈不喜欢非结构化稀疏(也称为随机稀疏)模式,其中非零值可以在矩阵中的任何位置。相反,当前的硬件/软件喜欢结构化计算。

典型的结构化计算是一个密集的矩阵乘法,它已经被一群真正聪明的人优化得一团糟。在英特尔 CPU 上,人们可以预期大约 90%的硬件失败是由 MKL 造成的。在 Nvidia GPUs 上,人们也可以期待接近屋顶线的性能。非结构化稀疏矩阵相乘是一种截然相反的情况。通常,现有的最新技术实现只能实现(远远)少于 10%的硬件 FLOPs。

因此,结构化剪枝旨在通过在稀疏模式中引入一些结构(通常以块的形式)来达到某种中间状态。块稀疏证明是非常有效的。OpenAI 的块稀疏 GPU 内核可以通过稀疏率实现几乎线性的加速比,并且使用硬件的效率几乎与密集矩阵乘法一样高。

不幸的是,人们普遍观察到,与非结构化修剪相比,结构化修剪会导致相当严重的精度下降。这是相当直观的,因为我们对在修剪过程中可以保留的权重施加了约束。不幸的是,它不是作为某种形式的正则化,而是几乎总是降低性能。最近的研究表明,与宋寒 4 年前使用的方法类似,简单的基于幅度的非结构化剪枝方法仍然可以在不同的任务之间一致地实现最先进的稀疏率/精度损失权衡。

如果我们能够加速非结构化稀疏矩阵乘法,那不是很好吗?

在我最近关于 arXiv:https://arxiv.org/abs/2008.11849的论文中,我提出了一个名为 SparseRT 的代码生成器来应对这个挑战。事实证明,聪明的编译策略实际上可以在编译时完成大量稀疏矩阵乘法的工作,从而在执行时加速运行时间。特别地,如果在编译时知道稀疏矩阵的稀疏模式,那么可以专门针对该稀疏模式进行优化,实际上是为该特定稀疏矩阵定制程序。

我表明,在 90%的稀疏度下,许多深度学习操作如 1x1 和 3x3 卷积可以在 GPU 上加速 2 到 3 倍。这对于加速深度学习推理是可以接受的,在深度学习推理中,人们通常不关心编译一个模型需要多长时间。下图显示了 20 个深度学习问题在 90%稀疏度下的加速。

值得注意的是,SparseRT 附带了一系列免责声明:

免责声明 1:它只适用于推断(目前)。人们也尝试在训练场景中使用非结构化稀疏性。然而,在这种情况下,权重矩阵的稀疏结构倾向于动态改变。此外,SparseRT 目前不支持深度学习所需的大量稀疏梯度计算(通常采用采样密集-密集矩阵乘法的形式,而不是稀疏矩阵乘法。)

免责声明 2:它不能使用张量核。这篇论文展示了 fp32 的结果,有些人会认为这是一种过时的深度学习推理数据格式。SparseRT 在 GPU 上支持 fp16 和矢量指令,通常可以提供比运行 fp32 的 SparseRT 更快的速度。然而,很难用张量核这样的密集矩阵乘法加速器来协调非结构化稀疏性。不过也不是完全没有希望,张量核支持未来应该会有。

我还应该提到在这个方向上正在进行的其他巨大努力,特别是谷歌/斯坦福的一个团队所做的努力:https://arxiv.org/abs/2006.10901

酷。SparseRT 能实现多大的端到端加速?

这篇论文本身关注的是单一运算(稀疏的 1x1 和 3x3 卷积)。可以理解,端到端的结果对大多数从业者来说更感兴趣。这些都很棘手,因为端到端推理依赖于其他东西,而不是快速的 1x1 和 3x3 卷积。例如,在 MobileNets 上,一旦 1x1 卷积的速度提高了三倍,深度方向的卷积就会成为瓶颈。在 BERT 上,一旦涉及权重矩阵的矩阵乘法变得很快,(密集的)自我关注单元就成为瓶颈。当然,人们还需要融合元素式层等。这些棘手的细节可能是英伟达和英特尔拥有整个工程师团队开发 TensorRT 和 OpenVINO 的原因。

当然,所有这些优化都与 SparseRT 的方法兼容,它可以实现非常好的端到端加速。我将在更多的博客文章和后续文章中介绍详细的结果。仅给出加速的感觉:SparseRT 可以在 Jetson Nano 上实现 2 倍于 TensorRT 的端到端加速,在 MobileNets 上实现一半精度,稀疏度为 90%,这通常意味着 1.5 -2 %的精度损失。这使得 Jetson Nano 具有与 Jetson TX2 相同的推理吞吐量,但只有后者的三分之一(!)的价格。

直到下一次…

加速熊猫数据帧的拼接

原文:https://towardsdatascience.com/speeding-up-pandas-dataframe-concatenation-748fe237244e?source=collection_archive---------23-----------------------

熊猫https://pandas.pydata.org/

简单的方法。

数据帧连接是一项开销很大的操作,尤其是在处理时间方面。假设有 12 个不同大小的熊猫数据帧,您想在列轴上连接它们,如下图所示。

df1 Shape:  (24588, 31201) 
df2 Shape:  (24588, 1673) 
df3 Shape:  (24588, 5)
df4 Shape:  (24588, 1)
df5 Shape:  (24588, 148) 
df6 Shape:  (24588, 1) 
df7 Shape:  (24588, 6) 
df8 Shape:  (24588, 1) 
df9 Shape:  (24588, 1) 
df10 Shape: (24588, 1) 
df11 Shape: (24588, 1) 
df12 Shape: (24588, 19)

为了提高 pd.concate()的速度,需要记住两件事。

  1. 对于每个数据帧,总是df = df . reset _ index(drop = true)。 记住串联命令使用索引,没有合适的索引你会得到错位的数据帧。
  2. 始终尝试连接一系列数据帧。串联一个列表比串联单独的数据帧要快,即df _ concat = PD . concat([df1,df2,…)。],axis = 1)
df_concat = pd.concat([df1, df2, df3, df4, df5, df6, df7, df8, df9, df10, df11, df12], axis=1)

你只需要知道这些:)

Ori Cohen 博士拥有计算机科学博士学位,主要研究机器学习。他是 TLV 新遗迹公司的首席数据科学家,从事 AIOps 领域的机器和深度学习研究。

Speedml:加速机器学习

原文:https://towardsdatascience.com/speedml-speeding-up-machine-learning-5dccbf21effd?source=collection_archive---------44-----------------------

用于快速启动机器学习项目的 Python 包

克里斯·利维拉尼在 Unsplash 上的照片

机器学习为系统提供了自动学习和从经验中改进的能力。有大量的机器学习算法可用,要测试它们以便为数据集或问题陈述找到最佳模型真的很难。除此之外,我们需要在模型中处理数据之前准备好数据。我们需要分析数据,找出模式、异常等。这些数据。简而言之,在机器学习模型中进一步处理数据之前,我们需要对数据进行完整的探索性数据分析。

SpeedML 通过提供不同的功能来加速机器学习的过程,这使得探索性数据分析成为一项简单的任务。我们只需要导入 SpeedML,就可以创建各种各样的图,清理数据,添加和删除功能等。

SpeedML 导入并正确初始化已经在其中定义的流行 ML 包,包括 pandas、numpy、sklearn、xgboost 和 matplotlib。

在本文中,我们将了解如何使用 SpeedML 来分析数据,并为机器学习模型做准备。

安装 SpeedML

像任何其他 python 库一样,我们将使用下面给出的命令来加速 ml。

pip install speedml

导入所需的库

我们将导入 speedml 来执行探索性数据分析和其他可视化。

from speedml import Speedml

在本文中,我们将使用 PIMA 印度糖尿病数据集。我已经分割了数据集,并分别保存在训练和测试文件中。让我们用我们的数据初始化 speedml,看看我们如何解决它。

正在初始化 speedml

sml = Speedml('dia_train.csv', 'dia_test.csv',target='Outcome')

这将创建我们的实例,我们将使用它进行进一步的分析。我们将从探索性的数据分析和可视化开始。

探索性数据分析

我们只需要为每个函数运行一行代码,让我们检查一下我们正在使用的数据的属性。

#data sample
sml.train.head()

我们正在使用的数据集。(来源:图片由作者提供)

#Heatmap
sml.plot.correlate()

显示不同属性之间相关性的热图。(来源:图片由作者提供)

#Distribution Plot
sml.plot.distribute()

所有属性的分布图。(来源:图片由作者提供)

类似地,我们可以创建连续的、顺序的等图。接下来,我们将了解如何操作数据并根据我们的需求更改数据。

#Finding Feature density
sml.feature.density('Age')
sml.train[['Age', 'Age_density']].head()

密度函数。(来源:图片由作者提供)

#Impute data
sml.feature.impute()

归罪。(来源:图片由作者提供)

#Feature Importance
sml.plot.importance()

功能重要性。(来源:图片由作者提供)

像 drop、crosstab、fillna、extract 这样的函数对于处理数据是最有用的,可以使用 sml 实例直接调用。

让我们朝着创建小型 EDA 报告的方向迈出最后一步,该报告将告诉我们正在处理的问题类型以及关于数据的一些其他重要信息。

sml.eda()

EDA 报告。(来源:图片由作者提供)

在这里,我们可以清楚地看到,我们正在处理一个分类问题以及测试和训练数据集的形状。我们还可以看到数据基数和数据类型。

在本文中,我们看到了如何使用 Speedml 来加速启动机器学习的过程,以及与 EDA 和可视化相关的各种功能。Speedml 易于使用,并且大多数函数都可以通过一行代码调用,省时省力。

在你走之前

感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。可以查看我的Github*简介针对不同的数据科学项目和包教程。还有,随意探索* 我的简介 ,阅读我写过的与数据科学相关的不同文章。

拼写校正:如何做一个准确快速的校正器

原文:https://towardsdatascience.com/spelling-correction-how-to-make-an-accurate-and-fast-corrector-dc6d0bcbba5f?source=collection_archive---------7-----------------------

来源:https://unsplash.com/photos/wRgNwR9CZDA

脏数据导致模型质量差。在现实世界的 NLP 问题中,我们经常会遇到有很多错别字的文本。结果,我们无法达到最好的分数。尽管这可能很痛苦,但在拟合之前应该清理数据。

我们需要一个自动拼写校正器,它可以修复错别字,同时不破坏正确的拼写。

但是如何才能实现这一点呢?

让我们从一个 Norvig 的拼写校正器开始,反复增加它的功能。

诺尔维格方法

Peter Norvig(谷歌研究部主任)描述了以下拼写纠正方法。

我们就拿一个词,蛮力的进行所有可能的编辑,比如删除,插入,转置,替换,拆分。例如,单词 abc 可能的候选单词有:ab AC BC BAC CBA ACB a _ BC ab _ c aabc abbc acbc adbc aebc等。

每个单词都被添加到候选列表中。我们第二次对每个单词重复这个过程,以获得具有更大编辑距离的候选词(对于有两个错误的情况)。

每个候选人都用一元语言模型进行评估。对于每个词汇,词频是基于一些大的文本集合预先计算的。将出现频率最高的候选词作为答案。

添加一些上下文

第一个改进—添加了 n 元语言模型(3 元)。让我们不仅预计算单个单词,而且预计算单词和一个小上下文(3 个最近的单词)。让我们估计某个片段作为所有 n-grams 的 n-size 的乘积的概率:

为了简单起见,让我们将大小为 n 的 n-gram 的概率计算为所有低阶 gram 的概率的乘积(实际上有一些平滑技术,如 Kneser–Ney-它们提高了模型的准确性,但让我们稍后再讨论它,参见下面的“提高准确性”段落):

为了从出现频率中获得 n-gram 的概率,我们需要归一化频率(例如,将 n-gram 的数量除以 n-gram 的数量,等等。):

现在,我们可以使用我们的扩展语言模型来估计候选人的上下文。

句子概率可以这样计算:

**def** predict(self, sentence):
    result = 0
    **for** i **in** range(0, len(sentence) - 2):
        p2 = self.getGram3Prob(sentence[i], sentence[i + 1], sentence[i + 2])
        p3 = self.getGram2Prob(sentence[i], sentence[i + 1])
        p4 = self.getGram1Prob(sentence[i])
        result += math.log(p2) + math.log(p3) + math.log(p4)
    **return** result

n 元概率是这样的:

**def** getGram1Prob(self, wordID):
    wordCounts = self.gram1.get(wordID, 0) + SimpleLangModel.K
    vocabSize = len(self.gram1)
    **return** float(wordCounts) / (self.totalWords + vocabSize)

**def** getGram2Prob(self, wordID1, wordID2):
    countsWord1 = self.gram1.get(wordID1, 0) + self.totalWords
    countsBigram = self.gram2.get((wordID1, wordID2), 0) + SimpleLangModel.K
    **return** float(countsBigram) / countsWord1

**def** getGram3Prob(self, wordID1, wordID2, wordID3):
    countsGram2 = self.gram2.get((wordID1, wordID2), 0) + self.totalWords
    countsGram3 = self.gram3.get((wordID1, wordID2, wordID3), 0) + SimpleLangModel.K
    **return** float(countsGram3) / countsGram2

现在我们有了更高的精确度。然而,模型变得非常庞大,一切都变得非常缓慢。对于 600 Mb 的训练文本,我们得到:

提高速度——对称方法

为了提高速度,让我们使用 SymSpell 的一个想法。想法相当优雅。我们可以预先计算所有的删除错别字(以及由删除产生的其他错别字),而不是每次遇到不正确的单词时都生成所有可能的编辑。更多详情可以在原文中阅读。

显然,我们无法达到与原始版本一样高的速度(因为我们使用语言模型并查看上下文,而不仅仅是单个单词),但我们可以显著提高性能。代价是额外的内存消耗:

改善内存消耗

为了获得尽可能高的精度,我们需要一个大的数据集(至少几千兆字节)。在 600 mb 文件上训练 n-gram 模型会导致大量内存消耗(25 Gb)。一半大小由语言模型使用,另一半由符号拼写索引使用。

如此高的内存使用率的一个原因是我们不存储纯文本,而是存储频率。例如,对于以下 5 个单词的文本:
a b c a b,我们存储以下频率:

a=>2
b=>2
c=>1
a b=>2
2b=>1
c=>1
a b=>1

另一个原因——哈希表数据结构内存开销大(哈希表用在 python dict 或者 c++ unordered_map 内部)。

为了压缩我们的 n-gram 模型,让我们使用在高效最小完美散列语言模型论文中描述的方法。让我们使用一个完美散列 ( 压缩、散列和置换)来存储 n 元文法计数。完美散列是保证没有冲突的散列。如果没有碰撞,就可能只存储值(计数频率)而不是原始的 n 元文法。为了确保未知单词哈希不匹配现有的哈希,我们将使用一个已知单词的布隆过滤器。我们还可以使用非线性量化将 32 位长的计数频率打包成 16 位值。这不会影响最终指标,但会减少内存使用。

量化:

**static const** uint32_t MAX_REAL_NUM = 268435456;
**static const** uint32_t MAX_AVAILABLE_NUM = 65536;

uint16_t PackInt32(uint32_t num) {
    **double** r = **double**(num) / **double**(MAX_REAL_NUM);
    assert(r >= 0.0 && r <= 1.0);
    r = pow(r, 0.2);
    r *= MAX_AVAILABLE_NUM;
    **return** uint16_t(r);
}

uint32_t UnpackInt32(uint16_t num) {
    **double** r = **double**(num) / **double**(MAX_AVAILABLE_NUM);
    r = pow(r, 5.0);
    r *= MAX_REAL_NUM;
    **return** uint32_t(ceil(r));
}

计数频率提取:

**template**<**typename** T>
TCount GetGramHashCount(T key,
                        **const** TPerfectHash& ph,
                        **const** std::vector<uint16_t>& buckets,
                        TBloomFilter& filter)
{
    **constexpr int** TMP_BUF_SIZE = 128;
    **static char** tmpBuff[TMP_BUF_SIZE];
    **static** MemStream tmpBuffStream(tmpBuff, TMP_BUF_SIZE - 1);
    **static** std::ostream out(&tmpBuffStream);

    tmpBuffStream.Reset();

    NHandyPack::Dump(out, key);
    **if** (!filter.Contains(tmpBuff, tmpBuffStream.Size())) {
        **return** TCount();
    }

    uint32_t bucket = ph.Hash(tmpBuff, tmpBuffStream.Size());

    assert(bucket < ph.BucketsNumber());

    **return** UnpackInt32(buckets[bucket]);
}

首先,我们检查密钥是否存在于布隆过滤器中。然后我们得到基于完美哈希桶数的计数。

为了压缩符号索引,让我们使用一个布隆过滤器。布隆过滤器是一种节省空间的概率数据结构,用于测试元素是否是集合的成员。让我们将所有删除散列放入一个 bloom 过滤器,并使用这个索引来跳过不存在的候选项。

这是符号拼写算法的第二步。这里我们取候选单词,这些单词是通过从原始单词中去掉一个或多个字母而生成的,并检查每个单词是否包含在索引中。删除 1删除 2 是布隆过滤器。

TWords CheckCandidate(const std::wstring& s)
{
    TWords results; **if** (Deletes1->Contains(w)) {
        Inserts(w, results);
    }
    **if** (Deletes2->Contains(w)) {
        Inserts2(w, results);
    }
}**void** TSpellCorrector::Inserts(**const** std::wstring& w, TWords& result) **const** {
    **for** (size_t i = 0; i < w.size() + 1; ++i) {
        **for** (**auto**&& ch: LangModel.GetAlphabet()) {
            std::wstring s = w.substr(0, i) + ch + w.substr(i);
            TWord c = LangModel.GetWord(s);
            **if** (!c.Empty()) {
                result.push_back(c);
            }
        }
    }
}

**void** TSpellCorrector::Inserts2(**const** std::wstring& w, TWords& result) **const** {
    **for** (size_t i = 0; i < w.size() + 1; ++i) {
        **for** (**auto**&& ch: LangModel.GetAlphabet()) {
            std::wstring s = w.substr(0, i) + ch + w.substr(i);
            **if** (Deletes1->Contains(WideToUTF8(s))) {
                Inserts(s, result);
            }
        }
    }
}

经过优化后,模型大小显著减小,降至 800 Mb:

提高准确性

为了提高准确性,让我们添加几个机器学习分类器。第一个将被用来决定单词是否有错误。第二个是回归变量,将用于候选人排名。这个分类器部分地起到了语言模型平滑的作用(它将所有的 gram 作为单独的输入,并且分类器决定每个 gram 有多大的影响)。

对于候选人排名,我们将训练一个具有以下特征的 catboost (梯度提升决策树)排名模型:

  • 字频率
  • n 克频率,每克独立(2,3)
  • 距离为 3,4 的邻近单词的频率
  • n 元模型预测
  • 编辑候选词和源词之间的距离
  • 编辑距离更远的候选人数量
  • 单词长度
  • 一个干净的静态字典中的单词存在
from catboost import CatBoostparams = {
        'loss_function': 'PairLogit',
        'iterations': 400,
        'learning_rate': 0.1,
        'depth': 8,
        'verbose': False,
        'random_seed': 42,
        'early_stopping_rounds': 50,
        'border_count': 64,
        'leaf_estimation_backtracking': 'AnyImprovement',
        'leaf_estimation_iterations': 2,
        'leaf_estimation_method': 'Newton',
        'task_type': 'CPU'
    }model = CatBoost(params, )
    model.fit(trainX, trainY, pairs=trainPairs, group_id=groupIDs, eval_set=evalPool, verbose=1)

对于误差预测,我们将训练一个二元分类器。让我们使用为原始单词的每个单词计算的相同特征,并让分类器决定该单词是否有错误。这将提供一种根据上下文检测错误的能力,即使是字典中的单词。

from catboost import CatBoostClassifiermodel = CatBoostClassifier(
    iterations=400,
    learning_rate=0.3,
    depth=8
)

model.fit(trainX, trainY, sample_weight=trainWeight, verbose=**False**)

这进一步提高了准确性,但是,这不是免费的,我们会降低性能。尽管如此,对于大多数应用来说,这种性能已经足够了,精度通常更重要。

评价

为了评估一个模型,我们需要一些数据集。我们可以基于干净的文本生成人为的错误。我们也可以使用公共数据集——其中之一是 SpellRuEval 数据集。让我们检查两个人工数据集和一个真实数据集的准确性。我们将使用一些来自大型 IT 公司的替代拼写检查器进行比较。这里,jamspell 是我们的拼写检查器,我们得到了以下指标:

茹,人工,文学

RU,real,互联网帖子

人工新闻

  • errRate — 执行自动更正后文本中剩余的错误数
  • fixRate — 已修复错误的数量
  • 破损— 破损的正确单词数
  • pr,re,f1 — 精度,召回,f1 分数

进一步的步骤

提高准确性的后续步骤—收集大量带有错误和已更正文本的并行文本语料库(分别用于移动和桌面平台),并训练专用的错误模型。

另一种提高准确性的可能方法是添加动态学习选项。我们可以边修正边在飞行中学习,或者我们可以进行两遍修正。在第一次通过时,模型将学习一些统计数据,并在第二次通过时进行实际校正。

此外,神经网络语言模型(双向 LSTM 或 BERT)可以提供一些额外的准确性提升。它们在直接方法(LSTM 误差分类器、序列-2-序列 LSTM 模型、使用 BERT 输出作为候选排序权重)中工作得不好,但是它们的预测可能作为排序模型/误差检测器中的特征是有用的。

以下是一些我们无法实现的方法的细节(这并不意味着它们无法实现——这只是我们的经验)。

用 BERT 法选择最佳候选人

我们训练伯特,并试图用它来预测最佳候选人。

from transformers import RobertaConfig, RobertaTokenizerFast, RobertaForMaskedLM, LineByLineTextDataset, DataCollatorForLanguageModelingconfig = RobertaConfig(
    vocab_size=52_000,
    max_position_embeddings=514,
    num_attention_heads=12,
    num_hidden_layers=6,
    type_vocab_size=1,
)dataset = LineByLineTextDataset(
    tokenizer=tokenizer,
    file_path=TRAIN_TEXT_FILE,
    block_size=128,
)from transformers importdata_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer, mlm=True, mlm_probability=0.15
)from transformers import Trainer, TrainingArgumentstraining_args = TrainingArguments(
    output_dir="~/transformers",
    overwrite_output_dir=True,
    num_train_epochs=3,
    per_gpu_train_batch_size=32,
    save_steps=10_000,
    save_total_limit=2,
)trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=dataset,
    prediction_loss_only=False,
)trainer.train()

对于屏蔽词预测任务,它比 n-gram 语言模型工作得更好(BERT 模型的准确率为 30 %, n-gram 模型的准确率为 20%),但是在从候选词列表中选择最佳词时,它的表现更差。我们的假设是,这是由于伯特不知道编辑距离或与原词匹配的事实。我们相信添加 BERT 预测作为 catboost 排名模型的一个特征可以提高准确性。

使用 LSTM 作为错误分类器

我们试图使用 LSTM 作为错误检测器(来预测 word 是否有错误),但我们最好的结果是与常规 n 元语言模型错误预测器+手动试探法相同。而且训练时间要长得多,所以我们决定暂时不用。这是一个得分最高的模型。输入是单词级的手套嵌入,在同一个文件上训练。

Model: "functional_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 9, 200)]          0         
_________________________________________________________________
bidirectional (Bidirectional (None, 9, 1800)           7927200   
_________________________________________________________________
dropout (Dropout)            (None, 9, 1800)           0         
_________________________________________________________________
attention (Attention)        (None, 1800)              1809      
_________________________________________________________________
dense (Dense)                (None, 724)               1303924   
_________________________________________________________________
dropout_1 (Dropout)          (None, 724)               0         
_________________________________________________________________
batch_normalization (BatchNo (None, 724)               2896      
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 1450      
=================================================================
Total params: 9,237,279
Trainable params: 9,235,831
Non-trainable params: 1,448
_________________________________________________________________
None

结论

我们从非常简单的模型开始,迭代地增加它的功能,最终我们得到了一个强大的、产品级的拼写检查器。然而,这并不是终点,在通往我们的目标——做世界上最好的拼写检查器——的漫长道路上还有很多步骤。

链接

使用 TextBlob & pyspellchecker 的拼写纠正应用程序

原文:https://towardsdatascience.com/spelling-rectification-app-using-textblob-pyspellchecker-cb3ad0504fc7?source=collection_archive---------16-----------------------

使用 Python 轻松纠正拼写错误

来源:桑迪·米勒在 Unsplash 上拍摄的照片

在你的搜索引擎上输入“斯坦福大学”,你会注意到它为你提供了“斯坦福大学”的搜索结果。你必须意识到,几乎每次搜索引擎都会为你提供正确的搜索结果,不管你的搜索查询中是否有拼写错误。

这是如何实现的?

这是有可能的,因为搜索引擎在将你的请求发送给服务器之前会使用“拼写纠正”算法,这样服务器就会返回你想要的结果。拼写纠正是任何现代搜索引擎的必备功能。

这只是一个自然语言处理如何以及在哪里被用来纠正拼写错误的例子。拼写纠正有助于你产生高质量的内容,可用于电子邮件、文档、社交媒体、文章(甚至是媒体)等。

在本文中,我们将学习使用类似于text blobpyspellchecker&Flask的模块,用 Python 创建一个离线拼写纠正应用程序

要遵循的步骤:

  1. 用 python 创建一个拼写纠正程序
  2. 将程序集成到 Flask 应用程序中

在进入编码部分之前,让我们先简单了解一下这些模块。如果你已经熟悉这些模块,你可以直接跳到下一节。

根据维基百科,Flask 是一个用 Python 编写的微型 web 框架。它被归类为微框架,因为它不需要特殊的工具或库。它没有数据库抽象层、表单验证或任何其他组件,而现有的第三方库提供了通用功能。

根据官方文档,TextBlob 是一个用于处理文本数据的 Python (2 和 3)库。它提供了一个一致的 API 来处理常见的自然语言处理任务,比如词性标注、名词短语提取、情感分析等等。

根据官方文档,pyspellchecker 是一种简单的拼写检查算法,它使用一种 Levenshtein 距离算法来查找距离原始单词 2 编辑距离内的排列。然后,它将所有排列(插入、删除、替换和换位)与词频列表中的已知单词进行比较。那些在频率列表中更频繁出现的单词更有可能是正确的结果。它支持多种语言,包括英语、西班牙语、德语、法语和葡萄牙语。

1.用 Python 创建一个拼写纠正程序

在将程序集成到 Flask 应用程序之前,最好先创建并测试我们的程序。所以让我们从编码开始吧。

1.1-安装 text blob&pyspellchecker:可以使用' pip install textblob '和'pip Install pyspellchecker'命令。我使用 PyCharm IDE 开发 flask 应用程序。 要在 PyCharm 中轻松安装库,请遵循以下步骤

1.2-写程序:下面是纠正拼写错误的程序,向下滚动了解程序的工作原理。

使用 TextBlob & pyspellchecker 进行拼写纠正的 Python 程序

  • 我们导入模块——text blob、pyspellchecker、re(regex)
  • 使用“with”关键字,我们打开一个包含有拼写错误的文本的文件,读取它,将字符串内容转换为 textblob 对象,就像 python 字符串一样。我们使用 textblob 类的 correct()方法并打印正确的文本。

TextBlob 的拼写修正是基于 Peter Norvig 的如何写一个拼写修正器。没有办法确切知道(例如,“lates”应该更正为“late”或“latest”或“lattes”还是…?),这表明我们使用概率。给定原始单词 w ,我们正试图从所有可能的候选单词中找到正确的单词,以最大化 c 是预期校正的概率。它大约有 70%的准确性

  • 要纠正拼写,我们不能仅仅依靠 textblob 的正确方法,因为它有 70%的准确率。所以我们使用 pyspellchecker 来获得所有拼写错误的单词、它们对应的正确单词和候选单词的列表。在找到拼写错误的单词之前,我们使用 regex 删除文本中的所有标点符号。
  • 最后,我们重写文本文件,用新的校正文本替换旧的不正确文本。

上面的程序不仅能让你纠正拼写错误,还能为你提供拼写错误的单词及其对应的候选单词列表。

程序的输出:

拼写更正. py 的输出

2.将程序集成到 Flask 应用程序中

现在是时候将上面的代码集成到 Flask 中来开发一个 web 应用程序了,我们可以在这里上传任何东西。txt '文件,点击“纠正拼写”按钮,在我们的网页上生成正确的文本。它还会显示拼写错误的单词列表和一个显示正确的&候选单词的表格。用户界面看起来有点像这样(使用你的 web 开发技能和创造力来设计你自己的网站) :

Web 应用程序的主页

2.1-安装 Flask: 我们使用" pip install flask "命令来安装 Flask,或者按照这些步骤在 PyCharm IDE 中轻松安装库。

2.2-创建 app.py: 让我们一行一行地检查代码

  • [1–6]我们导入所有必要的库——flask、textblob、pyspellchecker、regex、getpass(获取计算机的用户名)和 os。
  • [7–11]我们的应用程序必须能够从您计算机的任何位置上传文本文件,因此在将文件上传到服务器后,我们将其保存在我们的桌面上,稍后在桌面上打开文件进行处理。为此,我们需要计算机系统的用户名来访问桌面上的文件,其路径通常是
*C:/Users/’+username+’/Desktop/{filename.txt}*
  • [12–16]我们初始化我们的 flask 应用,设置 24 位密钥,配置文件扩展名(以便将文件类型限制为。txt),并将上传文件夹配置到上述路径。
  • [17]然后我们为我们的 web 应用程序的默认页面“index.html”定义应用程序路径。装饰器告诉我们的@app,每当用户在给定的.route()访问我们的应用程序域( localhost:5000 用于本地服务器),执行index()函数。Flask 使用 Jinja 模板库来渲染模板。在我们的应用程序中,我们将使用模板来呈现将在浏览器中显示的 HTML。
  • [24–43]我们打开 index()方法,在这里我们完成了大部分编码工作。因为我们的应用程序从上传文件中获取输入,所以我们需要做一些基本的检查,看用户上传的文件是否有效。我们检查用户是否没有上传任何文件就点击了上传按钮,我们检查文件类型必须是。txt ',否则应用程序中止并从服务器返回响应——“400:错误请求”

当你点击“纠正拼写”按钮而没有上传文件-错误“没有选择的文件”

当您上传的文件类型不是。txt"-错误请求(中止烧瓶应用程序)

  • [44–64]如果上传的文件有效,我们首先使用“file . save()”方法将文件保存在“桌面”文件夹中。接下来的所有步骤都来自我们前面已经看到的拼写纠正 python 程序。
  • 【a】是包含原文的字符串变量。 "correct" 是一个字符串变量,包含已更正的文本。“拼错[]”是包含所有拼错单词的列表,“list 1[]”是包含拼错单词对应的正确单词的列表。“list 2[]”是包含与返回的正确单词对应的所有候选单词的列表。
  • [66]然后我们返回带有上述所有变量的模板‘index . html’。
  • [69–70]最后,我们运行 flask 服务器。“app . run()”被调用,web 应用程序被本地托管在【localhost:5000】上。使用“threaded = true”时,每个请求都在一个新线程中处理。您的服务器可以同时处理多少个线程完全取决于您的操作系统以及它对每个进程的线程数量的限制。

输出:

当上传有效文件时

TextBlob 的准确率为 70%

在上图中,您可以看到“ buld ”被 Textblob 更正为“ bald ”,这与上下文无关。即使 textblob 返回正确的单词来代替拼写错误的单词,我们也不能完全依赖 textblob 来纠正所有的拼写错误。这就是为什么我们使用“ pyspellchecker ”来获得正确的候选单词。

拼写错误的单词列表

与上面拼写错误的单词相对应的正确单词和候选单词表

2.3-项目结构:

该项目保存在一个名为“用 Flask 拼写纠正”的文件夹中。我们运行“app.py”。运行该文件时,我们的应用程序托管在本地服务器的端口 5000 上。

运行“app.py”后,您只需在 web 浏览器上键入“localhost:5000”即可打开您的 web 应用程序

烧瓶应用程序的项目结构

  • 这是我们在上面创建的 Flask 应用程序
  • 模板 —该文件夹包含我们的“index.html”文件。渲染模板时,这在 Flask 中是强制的。所有 HTML 文件都放在这个文件夹下。
  • 静态 —该文件夹包含“css”文件夹。Flask 应用程序中的静态文件夹用于保存 CSS 和 JavaScript 文件。

注意:你可以从下面我的 GitHub 库的链接中查看 'index.html' 文件。

就是这样!!

您的个人“离线拼写纠正应用”可用于撰写电子邮件、文章、论文、社交媒体帖子、项目文档等。

参考我的 GitHub 代码

注意 :本文中已经提到了您开始工作所需的所有资源及其链接。希望你好好利用:)

我希望这篇文章能让你对尝试像 web 应用程序开发这样的新事物感兴趣,并帮助你增加知识。如果你喜欢读这篇文章,请与你的朋友和家人分享。谢谢你的时间。

看看下面这篇关于情绪分析的文章吧!!

[## Python 中的情感分析:TextBlob vs Vader 情感 vs 天赋 vs 从头构建…

情感分析是最广为人知的自然语言处理(NLP)任务之一。本文旨在给出…

bit.ly](https://bit.ly/31LlLKF)

用 Go 探索蓝牙 LE

原文:https://towardsdatascience.com/spelunking-bluetooth-le-with-go-c2cff65a7aca?source=collection_archive---------14-----------------------

使用 Go 挖掘蓝牙 LE 广告

在过去的几周里,我一直在一个项目上研究蓝牙 LE(低能耗),并一直愉快地摆弄着它。蓝牙有一个相当大而复杂的规范,仅仅是核心规范(目前版本 5.2)本身就有 3256 页长,这仅仅是核心规范。其他信息无处不在,存在于各种文档、补充文档、在线注册表和网站中。在里面找到你想要的东西简直是一场寻宝游戏!

感觉是这样的(鸣谢https://en.wikipedia.org/wiki/File:Caving1.jpg

我特别关注了蓝牙 LE,它是面向物联网设备的蓝牙的节能版本,而不是传统的蓝牙,也称为蓝牙 BR/EDR(基本速率/扩展数据速率)。我正在使用 BLE 研究近程信标,但那有点像钻进了兔子洞,我开始挖掘其他类型的 BLE 设备。我对它非常感兴趣,我甚至写了一个简单的 Go 工具来帮助我进行洞穴探险!

对 BLE 的简单介绍

BLE 的原始技术是由诺基亚开发的短程超低功率无线电技术,名为 Wibree。2007 年,诺基亚与蓝牙特别兴趣小组(SIG)达成协议,Wibree 成为蓝牙规范的一部分。经过几年的争论,蓝牙 LE(低能耗)于 2010 年作为蓝牙核心规范 4.0 的一部分被引入。

蓝牙核心规范谈到了两种无线技术——BR/EDR(或经典蓝牙)和 BLE(在一些文献中也称为蓝牙智能——市场营销用语,不要问为什么)。一般蓝牙设备可以是单模(仅限经典蓝牙或 BLE)或双模(经典蓝牙和 BLE 均可)。

一般来说,BLE 设备可以通过两种方式进行外部通信— 广播连接。这个想法很简单。在广播中,通信只是单向的。广播器将向任何能够接收传输数据的物体单向发送数据。另一方面,观察器重复扫描广播数据。

标准广播广告分组具有 31 个八位字节的有效载荷,并且可以具有可选的扫描响应有效载荷,其大小也是 31 个八位字节。广播是强大的,但也有它的缺点——它缺乏安全或隐私保护。(我在这篇文章中选择使用术语 octet,以便更清楚地表明我指的是类似于0xFF的东西,它是 8 位的单位。)

连接提供永久性的双向数据传输(至少在断开连接之前)。在连接模式中,中央发起连接,而外设接受连接。此后,相应地,中央设备成为主设备,而外围设备成为从设备

在混合使用模式中,设备开始时是一个观察者,负责扫描广播公司,一旦找到所需的设备,它将启动连接,并停止扫描。同样,一旦广播者接受连接,它将停止广播。

我在这篇文章中所要描述的只是广播传播,也就是 BLE 广告。

现在清楚了,我们开始吧!

和 BLE 一起去

为了这个项目,我选择了 Go。有很多其他的选择,包括流行的 Python 的bluepy,但是 Go 对我来说更好。Go 有几个选择,特别是 PayPal 的一个包,但不幸的是,它已经几年没有保持了。最后终于有了一个叫go-ble/ble的包。为了这篇文章的目的,我将使用我自己的项目的分支,它包含了一些对我的洞穴探险有用的小修改。

我还在上面制作了一个工具,名为 BlueBlue ,帮助我可视化 BLE 的广告。这是它看起来的样子。

BlueBlue,BLE 洞穴探险的必备工具

实际的代码只有几行代码,但是为了可视化和可用性,我构建了一堆其他的东西。我们来快速看一下扫码。

这是scan函数,它无限循环地扫描 BLE 广告,只要我没有通过设置stop变量为真来停止它。这是一个派生的 goroutine,可以无限期运行。使用的主要函数是ble.Scan,它接受一个处理函数,每次检测到 BLE 广告时都会调用这个函数。

每次检测到广告,我都会创建一个Device struct 来存储它,然后添加到一个 Go map 中,以地址为键,以 struct 为值。这有效地创建了在我的扫描仪(我可信赖的 Pi4)附近发现的所有 BLE 设备的地图。

我信任的 Pi4

我使用的 map 不是并发安全的,所以我在写入它之前使用一个sync.RWMutex来锁定它(并在我完成之后解锁它)。这是为了防止竞态条件,竞态条件只会使应用程序崩溃。

之后,我简单地将这张地图显示为网页,然后每秒刷新一次页面。

其余的代码简单地包装它,在 Go web 应用程序中显示找到的设备列表。它没什么特别的,但是它很好用。

BlueBlue 在行动

请注意广告和扫描响应八位字节,这些是我们稍后将深入研究的数据。你可能还会注意到,我在下面使用的数据与截图中的数据并不完全一致,这是因为数值发生了变化,而且我也使用了不同的设备。

如果你对完整的 BlueBlue 代码感兴趣,你可以在 https://github.com/sausheong/blueblue 找到它。

接近信标

邻近信标是 BLE 设备的常见应用。邻近信标是一种小型设备,它不断地向附近(因此是邻近的)接收器广播有限的一组数据。BLE 非常适合这一点,因为它是低能耗、低成本的。邻近信标的一些常见用途包括邻近营销(位置敏感的目标营销)、资产跟踪、室内导航和最近的(与新冠肺炎作战)、联系追踪和社交距离。

一个简单的 BLE 灯塔(显示在一枚新加坡 50 美分硬币旁边)

它们是最简单的 BLE 设备之一,非常适合第一次洞穴探险。因为我的项目,我们买了一些接近信标来玩,我有一个样品。

让我们戴上洞穴潜水头盔,直接跳入水中吧!

广告

BLE 的广播数据将在ble包中触发一个事件,我们可以用我们的处理程序捕获这个事件。HCI_LE_Advertising_Report事件表示一个或多个设备已响应主动扫描或在被动扫描期间广播了广告。这是从 BlueBlue 获取的 LE 广告报告事件的原始数据。

02 01 00 01 94 52 95 7c 58 eb 1e 02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e24 fb 1a fc fc 6e b0 00 0 00 6b 00 01 00 02 d8 c2

这是一串十六进制数字,现在看起来毫无意义,让我们来分解一下。将你的蓝牙核心规范(当然是象征性的)翻到第 4 卷,E 部分(主机控制器接口功能规范),7.7.65.2 部分。对于我正在使用的 5.2 版规范,这应该在第 2382 页。这非常清楚地告诉我们这个事件的形式。

从规格可以看出:

  • 02HCI_LE_Advertising_Report事件的子事件代码
  • 01——报告数量
  • 00 —事件类型,可连接和可扫描的无向广告(ADV_IND)
  • 01 —地址类型,随机设备地址
  • 94 52 95 7c 58 eb —地址(eb:58:7c:95:52:94)
  • 1e —数据长度(十进制 30)
  • 02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 00 00 00 6b 00 01 00 02 d8 —有效载荷、广告数据
  • c2 — RSSI(十进制 194,194 - 256 = -62 dBM)

第一个八位字节0x02HCI_LE_Advertising_Report的子事件代码,因此这是一个固定代码。报告的数量通常为 1,但如果有多个报告,则其余字段会重复。在这种情况下,这个信标只有 1 个,所以很简单。

事件类型有 4 种不同的可能值,通常指定设备是否可以连接,广告是否是定向的,以及广告是否是扫描响应。在这种情况下,这里的代码(0x01)告诉我们,我扫描的信标可以连接到,广告是无向的(ADV_IND)。

接下来是地址类型,它可以是 4 个不同值中的一个,它们都是指设备的地址。不同的类型是指地址是否固定:

  • 公共设备地址(0x00 )—向 IEEE 注册的全球固定地址,永不改变
  • 随机设备地址(0x01 ) —每次设备启动时生成的随机数,或者在设备的整个生命周期内保持不变的随机数。
  • 私有可解析地址(0x02 ) —由身份解析密钥和随机数生成的地址,可以经常更改以避免设备被识别和跟踪
  • 私有不可解析地址(0x03 )—只是一个随机数(不常用)

在这个信标的例子中,它只是一个由制造商分配的私有地址。这也是最常见的值。

接下来的 6 个二进制八位数是地址本身,但是是反过来的(94 52 95 7c 58 eb)。蓝牙规范规定数据以小字节顺序传输,所以你应该对此有很多期待。

其后是描述有效载荷数据长度的八位字节。有效载荷最多可以是 31 个八位字节(或0x1f)。在这个信标的情况下,它是 30 个八位字节的0x1e

接下来的 30 个八位字节当然是有效载荷,也就是广告数据本身。我们稍后将讨论这个问题,但让我们看看最后一个八位字节,它是 RSSI(接收信号强度指标)。

这是对近距离信标最有用的值,因为这是我们将用来转换为距离的值。值的范围在-127 dBM 到 20 dBM 之间,但一般来说大多在-95 到-30 dBM 之间。在这种情况下,我们有十进制的 194。我们只需要从中减去0xff或 256,得到-62 dBM。

广告有效载荷

广告有效载荷是我们所追求的。通常,格式是这样的:

  • 第一个八位字节—长度
  • 第二个八位字节—广告数据类型
  • 广告的其余部分(根据长度)是实际数据

广告数据类型指定广告中的数据类型。在通用接入模式(GAP)蓝牙网站的中可以找到可能的值列表。

在有效载荷中可以有几个广告。让我们看看这里的这个:

02 01 06 1a ff 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 00 00 00 6b 00 01 00 02 d8

我们来分解一下。第一套是:

  • 02 —长度(2 个八位字节)
  • 01 —广告数据类型为标志
  • 06 —二进制为110的旗帜

这些标志可在蓝牙核心规范的附录中找到(此处使用版本 7)。

蓝牙核心规范第 7 版(第 12 页)附录中的标志

在我们的例子中,110表示它处于 LE 通用可发现模式,不支持 BR/EDR(即经典蓝牙)。

第二套是:

  • 1a —长度(26 个八位字节)
  • ff —广告数据类型是制造商特定的数据
  • 4c 00 02 15 fd a5 06 93 a4 e2 4f b1 af cf c6 eb 00 00 00 6b 00 01 00 02 d8 —制造商特定数据

特定于制造商的数据广告数据类型正如它所说的那样,由制造商来定义该数据是什么。然而,前 4 个八位字节应该是由蓝牙 SIG(特殊兴趣组)分配的公司标识符。在这种情况下0x004C(是的,它是反过来的)是苹果的公司标识符。

这就是有趣的地方。当然,苹果生产的许多产品都有蓝牙功能。但是在 iBeacon 规范中,我们知道如果接下来的 2 个八位字节是0x0215,那么它遵循 iBeacon 规范。当然,你可以看到确实如此。

让我们稍微转换一下思路,跳到 iBeacon 格式。iBeacon 格式非常简单,只有 4 段数据:

  • 邻近 UUID (16 个八位字节)
  • 主号码(2 个八位字节)
  • 次要编号(2 个八位字节)
  • 测量功率(1 个八位字节)

在这种情况下,我们发现接下来的 16 个八位字节是近似 UUID fd a5 06 93 a4 e2 4f b1 af cf c6 eb 00 00 00 6b,接下来的 2 个八位字节是主要数字00 01,接下来的 2 个八位字节是次要数字00 02,最后一个八位字节是测量功率d8或十进制的 216。

如果你认为邻近 UUID 是用来唯一识别(毕竟 UUID 的意思是全球唯一标识符)的信标,那么你会惊讶地发现,它实际上是相反的。为特定部署(或客户)部署的所有 iBeacons 应该是相同的!这是由 iOS 本身强制执行的,除非您知道 UUID,否则不允许您扫描信标。具有讽刺意味的是,虽然 iOS 不允许你访问 iBeacon UUID,但你可以使用其他任何东西访问它,只要你能获得制造商的具体数据!

那么我们怎样才能唯一地识别信标呢?建议的方法(因为苹果不能强迫开发者)是使用主要和次要号码。诚然,这不是一个坏主意,因为仅用次要编号中的 2 个二进制八位数,您就可以部署 2 个⁶ = 65,536 个设备!如果将这两者结合在一起,就是 2 = 4,294,967,296 或大约 42 亿台设备。

但是等等。还有更多。

扫描响应

BLE 设备也可以通过回复扫描响应来响应扫描请求。扫描响应包含广告数据之上的附加数据。虽然不是所有的 BLE 设备都有扫描响应,但在我们的例子中,信标发送了一个,我们也在 BlueBlue 上捕捉到了它。

05 09 53 50 42 31 11 16 03 18 eb 58 7c 95 52 94 00 01 00 02 06 03 e8 64 00 00 00 00 00 00 00

幸运的是,这种格式与广告数据非常相似——第一个八位字节是长度,接下来是数据类型,然后是数据本身。让我们来分解一下我们的扫描响应。

  • 05 —长度(5)
  • 09 —类型是完整的本地名称
  • 53 50 42 31—“sp B1”(ASCII)
  • 11 —长度(十进制 17)
  • 16 —类型是服务数据(16 位 UUIDs)
  • 03 18 eb 58 7c 95 52 94 00 01 00 02 06 03 e8 64 00 00 00 00 00 00 00 —服务数据

因为类型是具有 16 位 UUID 的服务数据,所以服务 UUID 是前 2 个八位字节,即0x1803(是的,它再次被颠倒),并且服务的剩余数据是eb 58 7c 95 52 94 00 01 00 02 06 03 e8 64 00 00 00 00 00 00 00

那么,您能对服务数据做些什么呢?首先,服务数据的前 2 个八位字节是 16 位服务 UUID(如所示类型),即0x1803。如果您查看蓝牙 SIG 的 GATT 服务列表,您可能会注意到这是链路丢失服务。

然而,这个信标并没有真正的链接丢失服务,它只是一个便宜的信标,花费几美元,我们从中国制造商那里买的。制造商基本上使用这个服务 UUID,但是存储不同的数据。至于是什么样的数据,这个是来自厂商的数据表。

  • eb 58 7c 95 52 94——这是信标的地址,但令人惊讶的是这不是反过来的(我不知道为什么)!
  • 00 01 —这是主要编号(来自 iBeacon 格式)
  • 00 02 —这是次要编号(也来自 iBeacon 格式)
  • 06 —这是发射功率(在另一个表中)表示发射功率为 0 dBM
  • 03 e8 —这是以毫秒为单位的广播间隔(十进制 1,000)。因此这意味着广播间隔为 1 秒
  • 这是电池电量(十进制 100),我想这意味着电池电量仍然是 100%(我才用了几个星期)
  • 00 00 00 00 00 00 00 —我假设剩下的只是填充符

没那么糟糕。有大量的数据,我们学到了很多关于信标的知识。让我们看看别的。

资产标签

市场上有一种特殊的设备,只需几美元,就可以用来追踪或标记你的物品。如果你放错了地方,你可以用这个标签找到它。你能用它的东西包括车钥匙,钱包,甚至宠物和孩子!这背后的想法是,如果你标记的东西丢失了,你只需使用手机上相应的应用程序,让它大声发出哔哔声。反过来也可以,标签上有一个按钮,当你按下时,手机会发出声音。它可以非常方便。

我的 iTags(鸣谢张秀雄)

广告

在第一次潜入信标后,这应该更熟悉了。这是乐广告报道播出的广告:

02 01 00 00 80 e4 56 b4 ff ff 0e 02 01 05 02 0a 00 03 19 c1 03 03 02 e0 ff be

让我们经历同样的分解。

  • 02HCI_LE_Advertising_Report事件的子事件代码
  • 01 —报告数量
  • 00 —事件类型,可连接和可扫描的无向广告(ADV _ 印度)
  • 00 —地址类型是公共设备地址
  • 80 e4 56 b4 ff ff —地址(反过来,地址是ff:ff:b4:56:e4:80)
  • 0e —数据长度(十进制 14)
  • 02 01 05 02 0a 00 03 19 c1 03 03 02 e0 ff —广告
  • be — RSSI(十进制 190,190–256 =-66 dBM)

前 3 个二进制八位数应该很熟悉。地址类型是公共地址,这意味着它应该是固定地址。现在我们来看地址(ff:ff:b4:56:e4:80)。如果您查看地址的 U/L 位,第一个八位字节的第二个最低有效位(LSB)是 1 (第一个八位字节是0xFF,因此它们都是 1),这意味着这是一个 LAA,这意味着它实际上不是一个由 IEEE 管理的范围。

数据长度不言自明,RSSI 的计算方法与上面相同,为-66 dBM。现在我们来看看广告数据。

广告有效载荷

这是来自广播广告的广告有效载荷。

02 01 05 02 0a 00 03 19 c1 03 03 02 e0 ff

让我们像以前一样分解它,从第一盘开始。

  • 02 —数据长度(2)
  • 01 —广告数据类型为标志
  • 05 —标志以位为单位,所以翻译成二进制就是(0101)。由此我们可以看出这是 LE 受限发现模式,BR/EDR 不支持。

让我们继续下一盘。

  • 02 —数据长度(2)
  • 0a —广告数据类型为发射功率电平(TX 功率电平)
  • 00 —这是发射功率水平的值(0)。有了这个值,你可以计算路径损耗(或路径衰减)。计算路径损耗的公式为:
path loss = Tx Power Level - RSSI

在我们的例子中,这将是:

path loss = 0 - (-66) = 66 dB

这是下一组。

  • 03 —数据长度(3)
  • 19 —广告数据类型为外观
  • c1 03 —(十进制 961)通过与 GATT 特征的核对,我们可以看到所列的外观是键盘的外观

我们现在到了最后一盘。

  • 03 —数据长度(3)
  • 02 —广告类型为 16 位服务 UUID 不完整列表
  • e0 ff —这是 16 位服务 UUID 值

“不完整列表”的广告类型实质上意味着该服务 UUID 仅仅是可用的少数服务之一。然而,制造商选择不广播全部内容(出于各种原因,主要是为了节省电池电量)。如果你想知道其他人,你可以连接到设备上找到答案。

扫描响应

iTAG 也发送扫描响应。

11 09 69 54 41 47 20 20 20 20 20 20 20 20 20 20 20 20

答案非常简单

  • 11 —数据长度(17 位)
  • 09 —类型是完整的本地名称
  • 69 54 41 47 20 20 20 20 20 20 20 20 20 20 20 20 —这是 ASCII 中的“iTAG”,后面跟着一串空格

这些都在扫描响应中。

这看起来很短,实际上大多数有趣的行为只发生在我们连接的时候。我在这里不讨论如何连接到它,那将是另一个故事,所以让我们看看别的东西。

苹果铅笔

在我测试 BlueBlue 的时候,碰巧我的 Apple Pencil 也在附近,有趣的是,当我开始使用这支铅笔时,它就出现了。也就是说,当我拿起它使用时,它就开始做广告了。

我的苹果铅笔 1

但是它没有扫描响应。这是广告。

广告

现在我们已经用两种不同的设备做了两次,这应该很快。

02 01 00 01 e2 1e 4d 3c a8 48 1e 02 01 06 1a 09 41 70 70 6c 65 20 50 65 6e 63 69 6c 00 00 00 00 00 00 00 00 00 00 00 00 00 e4
  • 02HCI_LE_Advertising_Report事件的子事件代码
  • 01 —报告数量
  • 00 —事件类型,可连接和可扫描的无向广告(ADV_IND)
  • 01 —地址类型为随机设备地址
  • e2 1e 4d 3c a8 48 —地址(反过来,地址是48:a8:3c:4d:1e:e2)
  • 1e —数据长度(十进制 30)
  • 02 01 06 1a 09 41 70 70 6c 65 20 50 65 6e 63 69 6c 00 00 00 00 00 00 00 00 00 00 00 00 00 —广告
  • e4 — RSSI(十进制 228,228–256 =-28 dBM,我把它举得离我的 Pi4 很近)

广告有效载荷

这是来自广播广告的广告有效载荷。

02 01 06 1a 09 41 70 70 6c 65 20 50 65 6e 63 69 6c 00 00 00 00 00 00 00 00 00 00 00 00 00

让我们像以前一样分解它,从第一盘开始。

  • 02 —数据长度(2)
  • 01 —广告数据类型为标志
  • 06 —标志以位为单位,所以翻译成二进制就是(0110)。由此我们可以看出这是 LE 通用可发现模式,BR/EDR 不支持。

让我们继续下一盘。

  • 1a —数据长度(26)
  • 09 —类型是完整的本地名称
  • 41 70 70 6c 65 20 50 65 6e 63 69 6c 00 00 00 00 00 00 00 00 00 00 00 00 00 —这个简单的单词写着“Apple Pencil ”,后面跟着许多00

我用我的第一代 Apple Pencil 和第二代 Apple Pencil 都试过这个,它只在第一代上有效。一个有趣的观察——每次 Apple Pencil 被激活(通过移动它或将其 Lightning 连接器连接到 Lightning 端口),它都会广播一个不同的地址!每次我把 Lightning 连接器插到 Lightning 端口上,BlueBlue 都会弹出一个新的 Apple Pencil,这总是让我笑破肚皮。

欧姆龙 7 系列腕带血压计(型号 BP654)

我不会深入探讨程序员和高血压之间的关系,但是如果你正在阅读这篇文章,你可能知道这是真的。所以毫不奇怪,我碰巧附近有一个也触发了蓝蓝。

我的血压计(鸣谢:张秀雄)

实际上,血压计只会在按下数据传输按钮时发出广告。

广播广告

让我们看看广播广告。这是从 BlueBlue 捕捉到的。

02 01 00 01 00 d0 7a 08 07 d1 18 02 01 06 11 06 1b c5 d5 a5 02 00 bd b1 e1 11 a2 c9 80 39 be ec 02 0a 00 bf

这种崩溃不足为奇。

  • 02HCI_LE_Advertising_Report事件的子事件代码
  • 01 —报告数量
  • 00 —事件类型,可连接和可扫描的无向广告(ADV_IND)
  • 01 —地址类型为随机设备地址
  • 00 d0 7a 08 07 d1 —反向地址(d1:07:08:7a:d0:00)
  • 18 —数据长度(十进制 24)
  • 02 01 06 11 06 1b c5 d5 a5 02 00 bd b1 e1 11 a2 c9 80 39 be ec 02 0a 00 —广告有效载荷
  • bf — RSSI(十进制 191,191–256 =-65 dBM)

广告数据

好了,让我们更深入地了解广告有效载荷。

  • 02 01 06 — ( 0110 ) — LE 通用可发现模式,不支持 BR/EDR。
  • 11 —数据长度(十进制 17)
  • 06—128 位服务 UUIDs 的不完整列表
  • 1b c5 d5 a5 02 00 bd b1 e1 11 a2 c9 80 39 be ec—128 位服务 UUID

我们再次看到了一个不完整的服务 UUID。就像之前一样,这告诉我们,如果我们连接,会有更多的服务可用,这只是其中之一。但是 16 位和 128 位的服务 UUIDs 有什么区别呢?

当然,16 位服务 UUIDs 更短,正因为如此,它节省了传输它们的功率(这也是为什么不是所有的服务都被广告的原因)。蓝牙 SIG 有一个官方 16 位服务 UUIDs 列表,你可以付费为自己或公司创建一个。

  • 02 —数据长度
  • 0a —发射功率水平
  • 00 —发射功率电平为 0

扫描响应

由此得到的扫描响应非常简单,本质上只是设备的名称。

1e 09 42 4c 45 73 6d 61 72 74 5f 30 30 30 30 30 31 31 39 31 31 30 37 30 38 37 41 44 30 30 30
  • 1e —数据长度(十进制 30)
  • 09 —类型是完整的本地名称
  • 42 4c 45 73 6d 61 72 74 5f 30 30 30 30 30 31 31 39 31 31 30 37 30 38 37 41 44 30 30 30 —翻译为“BLEsmart_000001191107087AD000”

仅此而已。

很有趣!

在过去的几周里,我看到了以前从未见过的东西,这给了我一种奇怪的惊奇和发现的感觉。你应该试试!

点云的球面投影

原文:https://towardsdatascience.com/spherical-projection-for-point-clouds-56a2fc258e6c?source=collection_archive---------8-----------------------

大家好,这是我的第一个媒体帖子,我希望让大家参与到最后。在这篇文章中,我将谈论如何使用球面投影将 3D 点云投影到图像中。我也写了同样的代码,可以在我的 GitHub 库这里找到。所以让我们开始吧!!!

球形投影或前视图投影只不过是将 3D 点云数据表示成 2D 图像数据的一种方式,因此本质上,它也充当降维方法。球面投影越来越多地用于不同的深度学习解决方案中,用于处理点云。

语义分割(作者: SemanticKitti

应用最广泛的领域之一是对点云中的物体进行分类和分割。这可以在各种出版物中看到,如 PointSegsqueeze g、 SalsaNet 等等。将点云表示为图像的最大优点是,它将过去十年中对 2D 图像所做的所有研究都开放给了 3D 点云。例如,不同的最先进的网络,如 FCNU-NetMask-RCNN和 fast-RCNN现在可以扩展到点云数据,这是许多研究人员正在努力实现的目标,特别是在自动驾驶领域。

直观解释

为了理解这一点,让我们深入了解激光雷达是如何形成一点云扫描的。让我们考虑下图所示的 16 激光雷达的情况。

左图显示了由 16 个激光器组成的激光雷达。由激光雷达的第一个和最后一个激光器形成的最大和最小视角由 FOV _ 上和 FOV _ 下指示。右边的矩形是当我们将激光雷达形成的空心圆柱体投影到平面上时得到的。(作者:阿尼鲁德·托皮瓦拉)

16 个激光器中的每一个都以固定的角度定向,这取决于垂直角分辨率,FOV _ 上(上部视场)和 FOV _ 下(下部视场)。每个激光雷达都有一个发射和接收单元。这些点是通过计算每个激光从物体反射后的飞行时间而形成的。这 16 束激光旋转 360 度形成一个点云。

因此,这将导致的几何形状是一个空心圆柱体,激光雷达位于其中心。当我们从一个垂直于圆柱体主轴的轴将这个空心圆柱体投影到一个平面上时,我们得到一个图像。这种图像称为球面投影图像。

现在让我们用一些数学来解决技术问题吧!!

(作者:阿尼鲁德·托皮瓦拉)

我们的目标是找到所有(x,y,z)点的投影图像的像素坐标。这可以通过使用球面坐标系来实现。看上图,我们可以看到,如果+ive x 轴是激光雷达的前视图,那么云的每个点都会与 xy 平面形成一个倾角角度 俯仰角度 ,偏航 与 xz 轴。这里我们要把空心圆柱体投影到 zy 平面上形成图像。利用基本的三角学,我们可以得到每个点的俯仰和偏航值。由于原点位于图像的中心,这些偏航和俯仰值构成了投影图像的每个像素位置。因此,通过计算每个点的偏航和俯仰,我们可以完整地形成投影图像。在这一点上,我还想指出,俯仰值的范围将从[FOV 向上,FOV 向下],因为它是激光指向的最大和最小角度,偏航值的范围将从[-π,π],因为这是由 atan2 函数给出的范围。

虽然我们现在有了投影图像,但是由于两个问题,我们仍然不能使用它。首先,我们需要将原点平移到图像的左上角,如下所示。这是因为,在计算机视觉中,标准是原点在图像的左上角。第二,我们需要根据使用的激光雷达的类型来缩放图像。这两个步骤如下所示:

(作者:阿尼鲁德·托皮瓦拉)

1.翻译原文

这是一个简单的翻译起源问题。查看上图,我们可以通过向偏航轴添加π角来将原点移动到图像的左边缘,而通过从 FOV_Up 中减去俯仰角来将原点移动到图像的顶部。因此,新方程变成:

2.标准化和缩放

这一步骤是必要的,因为对于不同类型的激光雷达,投影图像的大小会有所不同,主要目的是在投影图像中拟合尽可能多的点。

正如我们在这篇博文中观察到的,整个过程是关于降维的,因此,我们会丢失数据是事实。这里的想法是以这样一种方式形成图像维度,即我们能够从图像中的云中捕捉最相关的点。因此,我们可以从逻辑上看到,激光雷达的个激光应该相当于投影图像的宽度。这样,图像的每一行将与从激光雷达的每个激光器获得的点相关。该值称为 row_scale 因子,乘以归一化的俯仰轴。例如,威力登 HDL 64-E 激光雷达有 64 个激光器,因此,投影图像的宽度应该通过将该值乘以俯仰轴来设置为 64。

图像的长度构成了偏航角分辨率。该参数可以调整以适应尽可能多的点。图像的这个长度也称为 col_scale,乘以归一化的偏航轴,得到最终的图像尺寸。同样,对于 HDL 64-E,最大水平分辨率为 0.35 度。因此,在最坏的情况下,我们将至少得到每个激光器(360/0.35 = 1028)个点。在深度学习和卷积网络中,图像大小优选为 2 的幂。因此,1024 的长度将适合图像中的最大点数,我们将把这个值乘以偏航轴。

因此,64X1024 的图像尺寸对于 HDL 64-E 来说是理想的。可以对威力登 VLP-16 进行类似的计算,其中最佳图像尺寸为 16x1024。

归一化等式现在可以重写为:

如果总的 FOV = FOV _ 上+ABS(FOV _ 下),那么(u,v)的最终方程通过一些值的重新排列可以写成:

对于威力登 HDL 64-E 激光雷达,同样的方程可以改写为:

这样,借助上面的方程,我们就可以把云的每个点(x,y,z)投影到它对应的球面投影(u,v)上。

将点信息编码到图像中

一旦我们获得了云的每个点的(u,v)像素,我们需要对其进行舍入以获得最接近的整数,并将点信息编码到其中。通常,在每个像素处添加 5 个关键值,即 X、Y、Z、R 和 I。这里,(X,Y,Z)是点的坐标,R 是该点与激光雷达的欧氏距离或范围,I 是强度。Range 是我们计算的唯一值,因为对于激光雷达形成的云的每个点,X、Y、Z 和 I 值已经存在。因此,我们得到的最终图像尺寸是:

图像尺寸=激光雷达的激光数量投影图像的长度 5(五个通道是 X、Y、Z、R、I)

现在是你期待已久的部分..结果!!!

使用上面得到的公式,我写了一个 c++代码,可以在这里找到。这段代码使用 PCL 库来加载点云,使用 openCV 来可视化所形成的球面图像的每个维度。为了测试我的实现,我使用了来自 SemanticKitti 的数据集。下面 gif 的上半部分显示了由下半部分显示的点云形成的球形图像的亮度值。

概括地说,由于该数据集中使用的激光雷达是威力登 HDL 64-E,形成的球形图像的尺寸是 64 * 1024* 5。

上半部分是形成的球形图像的强度维度,下半部分是输入点云。(作者:阿尼鲁德·托皮瓦拉)

一些重要观察结果:

1.球形图像的每一行对应于从激光雷达的每个激光器获得的点。这里,图像中最低的一行对应于激光雷达中最低的激光,这是激光雷达附近最近的环,如上面的 gif 所示。

2.形成的球形图像本质上是圆形的,这意味着你将能够观察到当物体从左侧离开图像时,它们可能会在图像的右侧重新出现。

3.图像中的物体可以用人眼识别。意思是,如果你看图像,很容易区分汽车、自行车、建筑物、道路等等。如果它可以被人眼区分,那么在这样的图像上训练的深度网络就有很大的机会能够给出良好的分割和分类结果。

4.此投影中丢失的点是那些与激光雷达中心具有相同俯仰角和偏航角但距离值不同的点。因此,只有连接激光雷达中心和该点的射线上的一个点会在投影图像中被捕获。

结果的清晰视频可以在这里找到

总之,球面投影是以图像形式表示点云数据的一个非常强大的工具。使用它可以帮助我们将过去十年在图像空间所做的所有研究扩展到点云数据,因此在机器人和自动驾驶汽车领域有着各种各样的应用。

我对这个主题以及计算机视觉和深度学习领域的其他主题的任何建议或头脑风暴会议都持开放态度,所以请随时与我联系!!!

辛辣的浓缩咖啡:热磨,冷捣以获得更好的咖啡

原文:https://towardsdatascience.com/spicy-espresso-grind-hot-tamp-cold-36bb547211ef?source=collection_archive---------32-----------------------

在研磨和准备过程中找到温度技巧,释放美味

多年来,人们已经尝试在研磨咖啡豆之前对其进行预热。他们发现了一些对味道和提取的影响,但不是一个确定的更好的味道。一些人也尝试过冷冻豆子,但是对其中的机制还没有了解。之前的实验都有不足之处,因为它们没有完全捕捉到一个关键变量:温度。

以前的实验没有记录研磨后的温度;他们主要关心的是研磨前的豆子温度。我开始记录研磨前、研磨后和预冲咖啡的温度。我发现在研磨之前预热咖啡豆,如果之后将粉末冷却到室温,会大大提高命中率,这个过程我称之为辛辣研磨。它是辣的,因为它是热的,但不会像辛辣的食物一样留在你的舌头上。我们可以通过冷却冰箱里的粉末来让它变得更有趣。

在这项工作中,我将展示以前的工作和其他有助于这项技术的发现。我相信这很简单,任何人都可以轻松地尝试,我希望他们会找到类似的结果。

以前的工作

关于研磨温度,在商用机器中努力保持研磨机冷却,因为已知热研磨机会对咖啡豆产生影响。研磨机越热,研磨出来的东西就越热。这种变化将导致咖啡师不得不整天调整他们的饮料,以适应温度的变化。

关于提取,已经进行的工作表明,较冷的咖啡渣比较热的咖啡渣提取得更多,并且较冷的咖啡渣比较热的咖啡渣流动得更快。通常,咖啡师会通过调整时间、音量或研磨来达到一致性。

2007 年,一位家庭咖啡师偶然将咖啡豆放在了他温热的咖啡机上,并发现了研磨前预热咖啡豆的影响。他发现浓缩咖啡比没有预热咖啡豆时味道更顺滑。

2019 年, James Hoffman 在观察了 2015 年锦标赛中使用的一种真空罐预热豆子后,尝试了微波加热豆子,认为这一过程可以增加提取量。

2020 年,YouTube 上的Sprometheus对研磨前冷冻或加热的咖啡进行了多次 TDS 和提取测量。然而,他在拍摄之前没有记录地面的温度,因此他混淆了预研磨温度和预拍摄温度这两个变量。他没有使用无底的过滤器,所以不知道不同的温度是否会导致流量和通道的不同。他的实验是一致的,但是没有发现味觉的巨大变化。

2018 年,复合咖啡研究了研磨机温度如何影响提取,虽然他们的结果无法进行统计显著性测试(样本量< 30),但他们的结果很有趣。我不是说较小的尺寸不好,但我不认为它们应该用于零假设检验。作者没有用图表表示他们的数据,但为了方便起见,我在下面列出了:左边是未排序的样本,右边是每个样本的最佳提取进行了比较。考虑到每个样本彼此不相关,比较的最佳方式是最好与最好、第二好与第二好。

即使缺少足够大的样品尺寸,结果也表明在研磨机上没有加热元件的情况下提取率更高。然而,这个故事还有更多;复合咖啡还收集了咖啡渣的温度,因此绘制萃取温度与咖啡渣温度的关系图给出了不同的观点。较低的温度似乎可以提供更好的提取,但他们在酿造前没有冷却粉末,所以不知道哪个变量会导致更多的提取。

此外,复合咖啡研究了冷冻咖啡豆对萃取的影响。他们冷冻咖啡豆,然后磨碎,用几种方法煮出浓缩咖啡。同样,这是一个小样本,但如果我们将它们制成图表并进行分类,那将会非常有趣。

排序后在一个图表上绘制所有样本表明,冷冻对提取有轻微的好处,但不清楚它是否真的有统计学意义或只是一个暂时现象。

绩效指标

我使用了两个指标来评估过滤器之间的差异:最终得分和咖啡萃取。

最终得分是 7 个指标(强烈、浓郁、糖浆、甜味、酸味、苦味和余味)记分卡的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

使用折射仪测量总溶解固体量(TDS ),该数值与咖啡的输出重量和输入重量相结合,用于确定提取到杯中的咖啡的百分比。

地面温度的初步调查结果

换了一种新的烤肉后,我发现了冷却研磨的窍门。我磨了 90g 咖啡,立马拉了一个镜头,也没那么爽。提取有相当多的通道和喷雾。出于某种原因,我测量了地面的温度,因为我读到过地面的温度。我拍摄的下一张照片非常好,但唯一的差异是 2.3 摄氏度。第一张照片是在 26 摄氏度,第二张是在 23.7 摄氏度,因此我开始记录拍摄前的地面温度和拍摄前的温度。

上午对下午

我很好奇地面温度是否对味道有影响。查看过去一年的所有数据,我没有体温,但我有一天中时间的记录。所以我拿着恒机咖啡、烘焙咖啡和断续(或普通)浓缩咖啡,对比了上午和下午最好的照片。还涉及到其他变量,但这为理论提供了一个有趣的窗口,即下午的地面比早上更温暖,因此拍摄也不会提取。在过去一年的大部分镜头中,我的研磨和酝酿之间有一段时间让研磨物冷却。

至少根据我的数据,我下午的投篮更好。我怀疑这是由于温度差异,但变量没有被跟踪,所以它是未知的。

实验

我计划做这个咖啡豆加热实验已经有一段时间了,有了合适的避难所,我有时间和耐心,因为我知道测试会很长。我对新测试的疑问总是这样的技术是否会改善拍摄,是否会改善我最好的拍摄,即断奏咖啡拍摄

辣磨:微波至 60C 左右,立即研磨,静置至室温。我这样做是没有计划的。我通常会研磨咖啡,然后过一段时间再冲泡,因为通常我会筛滤咖啡来制作一个断续的镜头。我所有的咖啡渣都储存在密封的容器里,我没有发现用这种方式研磨会降低质量,尽管这与普通咖啡的想法相反。我没有足够大的数据集来知道预热咖啡豆的最佳温度。

我叫它辣磨是因为和辣一样,刚开始是辣的,放在柜台上慢慢凉下来。目的是不使用磨床上的热磨粒。

加热豆类预研磨

关于加热豆,以前的工作没有分开温度,所以我有。我用微波炉将咖啡豆加热到 60 到 90 度。然后我用研磨机研磨它们,我让咖啡豆在密封的罐子里冷却到室温。我用了一辆金快车来拉镜头。

此外,我测量了拉后的温度,并在拉后四分钟喝了酒。我在喝酒前搅拌一下,以确保没有东西沉淀下来。我发现等几分钟让饮料冷却会改善味道,在这些实验完成后,我会让饮料冷却到相同的温度,而不是设定的时间。

为了准备拍摄,我做了一个的断奏浓缩咖啡镜头,中间有一个纸过滤器。我用纸过滤器(PFF)在精细层和粗糙层之间做了一些断续的浓缩咖啡照片。我知道这些镜头比普通的浓缩咖啡镜头更复杂,但它们都比普通镜头好。这种温度处理减少了断续捣实浓缩咖啡和断续浓缩咖啡之间的味道差异。

关于准备的最后一点,我没有把便携式过滤器放入机器,直到我拉起镜头之前,这样机器的温度将在提取之前最低限度地提高咖啡渣的温度。

我目前的数据集涵盖了三次烘烤,我发现味道有了很大的改善。然而,我没有显示统计显著性所需的样本数量,因为我只有不到 30 个。似乎并不依赖于投入产出比。

从提取上来说,更高的提取和辣磨似乎没有什么联系。两种分布都是相互混合的。对于输出与输入的比率,提取已经非常高了。对于更高的比率,还有第二组提取数字,这是因为我完成了第一次 1:1 拍摄,然后我加权并测量第二位出来的 TDS。然后,我计算了 3:1 的比例拍摄,因为许多人拉更长的镜头。

因为我在记录温度,所以我观察了消费时的拍摄温度,以及这个变量是否对我的味觉体验有影响。至少对于这个小样本来说,温度与味道没有关联。即使被烘焙分解,味道和饮料温度之间似乎也没有明显的联系。

R1 =烤 1,R2 =烤 2,R3 =烤 3

辣味分布

我定期筛选咖啡来制作不连续的浓缩咖啡,通过筛选,我可以获得完整颗粒尺寸分布的小样本。中档有所改变,更多的研磨料留在了粗档。通常我一次筛 45 到 60 克,速度是 3g/分钟(重量除以总筛时间)。

研磨前预热咖啡豆只是改变了颗粒分布还是做了更多的事情?如果我们从加热过的咖啡豆中取出过筛后的粉末,然后将它们混合在一起,形成和原来未加热的咖啡豆一样的分布,会怎么样呢?如果弹丸味道类似于未加热的豆丸,该测试将表明加热豆仅改变颗粒分布,而没有其他变化。

我发现不管颗粒分布如何,味道都是一样的,这意味着加热不仅仅是不同的地面分布。这不是一个决定性的测试,但结果不是我所期望的。我想知道,不管怎样,仅仅用同样的研磨筛选和改造一个镜头是否会给出一个更好的镜头。筛选和加热是两个变量,在下面的小样本中,我不清楚哪一个导致了味道的改善。

这里主要的洞穴是:

  1. 每个变体只有 2 个样品。
  2. 仅使用了三个筛子,并且粉末分布比这更复杂。然而,其他研究表明,当涉及热量时,颗粒分布的主要变化是从 400 微米到 500 微米的颗粒向更粗的颗粒转移。我在上面看到过类似的。

克里斯蒂安·克拉特 2015 演示:https://speakerd . S3 . Amazon AWS . com/presentations/e 0019999d 9224146 BF 3 f 9 f 9 DFD 483679/BC15 _ CK . pdf

冷冻豆子

我把一些豆子冷冻到零下 17 度,然后把它们从冰箱里直接磨碎。研磨后的温度为 26 摄氏度(当时的室温为 23 摄氏度),因此研磨机在研磨过程中对它们进行了预热。我发现味道并不比基线好,也没有提取更高。然而,一个奇怪的发现是,如果我在拍摄前在冰箱里冷却咖啡渣,味道会有所改善。

特辣:热豆,冷磨

那么,为什么不用热的豆子研磨,用冷的地面冲泡呢?我开始用普通温度的咖啡豆研磨,我发现冷却咖啡豆可以改善味道。以下是我遵循的步骤:

  1. 用微波炉将咖啡加热到 60 度到 90 度之间(对于 80 克左右的咖啡豆,这是在微波炉上加热 1 分钟)。
  2. 立即研磨(研磨后的温度为 36℃)。
  3. 将粉末和过滤器放入冰箱的密封容器中。
  4. 让我们冷静一两个小时。人们可以用冰箱来加速这个过程。
  5. 启动浓缩咖啡机。
  6. 取出研磨物,准备好一杯咖啡(普通咖啡、不连续浓缩咖啡、不连续浓缩咖啡,有或没有纸质过滤器(PFF))。
  7. 将准备好的篮子放回冰箱,直到机器准备好(这反映了我使用没有温度控制的 Kim Express)。
  8. 开枪吧。

我注意到味道有所改善。提取没有太大的变化。提取似乎是最大限度的,但一些不同的东西正在被提取。也许咖啡中不好的部分没有被提取出来,更多好的部分被提取出来,我不知道。按化合物对杯子里的东西进行分解将有助于回答这个问题,但我没有气体色谱仪或光谱仪。

我的样本大小对于我的数据需求来说不够大,但是我觉得我正在推动可能的边界。回顾过去的工作,它们都是足够简单的实验,但轻微的调整,如跟踪地面温度,似乎是发现惊人的浓缩咖啡的关键调整。

我喜欢辣磨的原因是,任何有微波炉和冰箱的人都可以相对轻松地尝试一下。所以我希望听到任何尝试一些辣磨的人。

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

断续浓缩咖啡:提升浓缩咖啡

用纸质过滤器改进浓缩咖啡

浓缩咖啡中咖啡的溶解度:初步研究

断奏捣固:不用筛子改进浓缩咖啡

浓缩咖啡模拟:计算机模型的第一步

更好的浓缩咖啡压力脉动

咖啡数据表

匠咖啡价格过高

被盗浓缩咖啡机的故事

浓缩咖啡过滤器分析

便携式浓缩咖啡:指南

克鲁夫筛:分析

脉冲神经网络

原文:https://towardsdatascience.com/spiking-neural-networks-558dc4479903?source=collection_archive---------16-----------------------

是什么让这些生物现实神经元对计算感兴趣?

照片由哈尔·盖特伍德 上的 Unsplash

在硬件神经形态计算中,脉冲神经网络是最广泛采用的脑启发计算模型。然而,它们在软件人工神经网络中没有遇到很大的兴趣,所述软件人工神经网络使用以单一、静态和连续值激活为特征的神经元。是什么让这些生物现实神经元对计算感兴趣?

脉冲神经网络(SNN)首先出现在计算神经科学中,作为模拟生物神经元行为的一种尝试。这导致了泄漏积分点火(LIF)模型,将神经元活动描述为接收到的尖峰电压的积分和对环境的微弱耗散(泄漏)。当它的电压达到一个阈值时,神经元会自己发出一个尖峰信号。随着大脑的进一步启发,神经元的输入和输出可以被编码为它接收和发出的尖峰脉冲的时间或速率。这种类型的信息编码对错误非常鲁棒,因为检测不到一两个尖峰不会导致平均尖峰率的显著错误。

尽管他们已经在那里呆了一段时间,但 SNN 并没有在深度学习社区中遇到很大的兴趣。这最初是由于缺乏有效的训练算法来进行监督学习。事实上,为了应用通常使用的学习算法,例如具有反向传播的梯度下降,需要为神经元输出定义连续值的可微分变量(尖峰不是)。虽然这可以通过计算尖峰到达时间或其速率来实现,但这增加了计算复杂性,从而使它们在深度神经网络中的实现不太吸引人。

另一方面,脉冲神经元广泛用于神经形态计算,其中 SNN 已经使用电子和光学系统实现。神经形态计算包括在硬件中构建神经网络,其中物理设备神经元通过物理设备突触连接。神经形态计算的主要动机是由分布式架构提供的能量效率,该架构避免了存储器和 CPU 之间的能量贪婪的数据洗牌。尖峰神经元的使用方向相同,并促使 IBM (TrueNorth)、Intel (Loihi)和清华天极(Tsinghua Tianjic)建造了第一台由数百万个尖峰神经元组成的神经形态计算机。

脉冲神经网络的优势

脉冲神经网络之所以有趣,有几个原因。首先,由于速率编码对噪声非常鲁棒,因此可以使用非常弱的信号来传输信息。第二,他们为无监督学习带来新的学习算法。事实上,尖峰神经元允许生物启发的局部学习规则的实施,如赫比学习和尖峰时间相关可塑性 (STDP)。如果突触连接的两个神经元的活动似乎相关,这些学习规则就会恢复到增强突触的权重,否则就会降低权重。因此,它允许网络实时学习和自我学习。使用电子神经形态硬件的无监督学习的首次演示可以追溯到 2012 年,获得了 MNIST 数据库的最先进的识别率[1]。最近,使用全光学神经形态平台实现了无监督学习,证明了对简单模式的识别,从而证实了尖峰神经元的兴趣[2]。

尖峰时间相关可塑性学习规则。如果突触后神经元(紫色)在接收到来自突触前神经元(红色)的锋电位后出现锋电位,那么这些事件之间很可能存在因果关系,它们之间的突触(红紫色)得到加强。相反,如果突触前神经元的锋电位(绿色)在突触后神经元的锋电位之后到达,则突触(绿色-紫色)权重降低。(自己的工作)

最后,由于他们使用的时空信息编码,脉冲神经网络为利用网络动力学进行学习提供了可能性。例如,脉冲串的同步允许从同步模式中解码网络输出。这种动态现象存在于大脑中,并允许它用较少数量的神经元进行计算。使用同步的高效学习的首次演示已经在使用自旋电子神经元【3】的神经形态计算中得到演示,从而证明了人工神经网络的动态神经元模型的重要性。

神经形态平台现在面临着扩大规模的挑战。他们将不得不应对设备的可变性,同时保持设备的紧凑性和低能耗。

[1] D. Querlioz 等人,结合无监督和有监督学习方法的具有纳米尺度忆阻设备的生物激励网络, IEEE/ACM 纳米尺度架构国际研讨会,203–210(2012)。

[2] J. Feldmann 等,具有自学习能力的全光脉冲神经突触网络。性质569 ,208–215(2019)。

[3] M. Romera,P. Talatchian 等人,使用四个耦合的自旋扭矩纳米振荡器进行元音识别。性质563 ,230–234(2018)。

流学习场景中的脉冲神经网络

原文:https://towardsdatascience.com/spiking-neural-networks-in-stream-learning-scenarios-7bf2112b0c35?source=collection_archive---------11-----------------------

在综合概述中合并两个字段

图 1:脉冲神经网络方案[1]

脉冲神经网络已被证明是模拟大脑行为和学习潜力的最成功的方法之一,并利用它们来承担实际的在线学习任务[1]。

流学习

在流学习(SL)中,也称为数据流挖掘或数据流的机器学习,应用程序(如移动电话、传感器网络、工业过程控制和智能用户界面等)以快速流的形式生成大量数据,随着 大数据物联网 时代的到来,这些应用程序获得了特殊的相关性。在这些情况下,算法无法显式访问所有历史数据,因为为此目的所需的存储容量变得无法管理。事实上,数据流既快又大(可能是无限的),所以必须实时从中提取信息,因此有必要以在线方式学习[2]。

此外,这些场景中的一些会产生越来越普遍的非平稳数据流,并且生成数据的过程可能会随着时间而改变,从而产生要建模的模式的改变(概念漂移)。这导致在这些流数据上训练的预测模型变得过时,然后它们不能适应新的分布。因此,迫切需要新的算法来尽可能快地检测和/或适应这些变化,同时保持良好的性能分数[3]。

脉冲神经网络

仿生系统的计算能力越来越受到研究界的关注。尽管对大脑中涉及的信息处理缺乏共识,但生物过程已经成为最近计算模型的参考。如今被认为是第三代人工神经网络,SNNs 的出现是由更好地理解
哺乳动物大脑的信息处理技能的需要推动的,为此,社区致力于开发更复杂的生物学连接主义系统。

图 2:生物神经元及其与人工脉冲神经元的关联[4]

SNNs 包含一组计算单元(神经元),它们通过有向边(神经元之间的突触)相互连接,根据一组指定的规则和方程(神经回路中的信息处理模型)处理信息。对其连通性或动力学的不同选择已经产生了大量不同类型的模型(如 Izhikevich、Hodgkin–Huxley 或 Leaky integrat-and-Fire 模型),这些模型在过去几十年中已经在计算机科学中得到彻底研究,并且由于
深度学习的成功,近年来又重新引起了人们的兴趣[5]。

在将输入数据提供给 SNN 之前,必须将其编码成尖峰序列,以便应用神经元模型。编码部分旨在生成代表输入刺激的尖峰模式,这在神经科学中仍是一个未决问题(这样的尖峰时空模式包含什么信息?神经元用来传递信息的代码是什么?或者其他神经元如何解码信号?).产生棘波序列使得输入刺激的任务相关信息内容被保留变得至关重要。因此,决定什么信息被丢失,什么信息被保留,以及编码有多有效,这些都不是无关紧要的[6]。

在运动控制、基于机器人控制、轨迹跟踪、金融市场决策应用、空间导航和路径规划、决策和行动选择、康复、图像和气味识别、空间导航和环境的精神探索等领域,可以找到 SNNs 的许多实际应用。

用于流学习目的的脉冲神经网络

一些 snn 在第二语言研究社区中特别出名,因为它们具有持续和增量学习的能力,这解释了它们对不稳定和不断发展的环境的持续适应性。此外,他们还展示了捕捉流数据中时间变量之间的时间关联的能力。此外,snn 具有硬件实现的最佳特性,这是一个有趣的主题,因为可以实现在线操作和嵌入式硬件系统,从而扩大了 snn 可以应用的应用数量(例如,定制的 VLSI 芯片)。

数据流可能表现出类标签之间的时间依赖性,这有助于确定输入要素如何随着时间的推移而相互关联。SNNs 利用尖峰信息表示来构建尖峰时间学习规则,这些规则已经显示出捕捉流数据中时间变量之间的时间关联
的能力。在 SL [8]中使用一些 snn(例如,进化的 snn[7])允许非常快速的实时性,并降低了学习过程的计算复杂度,这是因为其局部性非常适合并行实现。就适应漂移而言,如果在变化的环境中使用,大多数现成的分类模型需要重新训练,并且不能适当地缩放。一些 snn 可以克服这一缺点,例如,eSNNs 的进化性质(基于相似神经元的合并过程)使得在数据可用时积累知识成为可能,而不需要存储和
用过去的样本重新训练模型。

工具

对于 SL :

  • MOA :可能是用于这些目的的最流行的开源 Java 框架。
  • Scikit-Multiflow :受 MOA 启发,用 Python 实现,包含一系列机器学习算法、数据集、工具和用于 SL 评估的指标。
  • Scikit-Learn :虽然它主要专注于批量学习,但这个框架也为研究人员提供了一些 SL 方法(多项式朴素贝叶斯、感知器、随机梯度下降分类器、被动积极分类器等)。
  • Spark streaming:Apache 项目,致力于构建可伸缩的容错流应用程序。
  • 其他兴趣:弗林克,风暴,梁,萨姆扎,萨摩亚等。

对于 SNNs :

  • Brian :这是一个针对 SNNs 的开源 Python 模拟器
  • Cypress : ut 是一个 C++仿真环境,可以对 SNNs 进行高效的
    离散事件仿真。特别推荐用于涉及具有复杂解剖和
    生物物理特性的细胞的
    模拟。
  • Nest :这是一个模拟环境(C++,Python,Cython ),最适合关注神经系统的动态、大小和结构的模型,而不是关注单个神经元的详细形态和生物物理
    属性
  • PyNN :神经元
    网络模型的模拟器无关规范。Python 实现。
  • 其他兴趣:Neuron,NeuCube,PCSIM,ANNarchy。我们还可以在 SpiNNaker 和 BrainScaleS 中找到硬件规格。

结论

由于基于流数据的大量实际应用,特别是在数据受到非平稳事件影响的场景中,引发了所谓的概念漂移,因此 SL 是研究界的一个非常热门的话题。SNNs 被认为是第三代神经网络,并且已经表明它们是模拟大脑行为和学习潜力的最成功的方法之一,允许对大型网络进行非常快速的实时模拟,并且计算成本低。在漂移检测[9]和漂移适应[8]情况下,它们也表现出非常好的行为,这通常出现在 SL 场景中。所有这些让我们考虑这两个领域的一个非常有趣的交集。尽管如此,在这两个领域都必须取得很大进展,以应对各自的公开挑战,但我们应该意识到融合 SL 和 snn 的重要性,以便用这些生物启发系统的计算能力解决实际问题。

承认

我要感谢研究团队的其他成员(TECNALIA 的 Javier Del Ser、巴黎电信和怀卡托大学的 Albert Bifet 以及奥克兰理工大学的 Nikola Kasabov)。

参考

[1] Lobo,J. L .,Del Ser,j .,Bifet,a .,& Kasabov,N. (2020)。脉冲神经网络和在线学习:概述和展望。神经网络,121,88–100。

[2] 输,v,汉默,b .,&沃辛,H. (2018)。增量在线学习:最新算法的回顾和比较。神经计算,275,1261–1274。

[3] 陆,j,刘,a,董,f,顾,f,伽马,j,&张,G. (2018)。概念漂移下的学习:一个综述。IEEE 知识与数据工程汇刊。

[4]郭士纳,w .,&Kistler,W. M. (2002 年)。脉冲神经元模型:单个神经元,群体,可塑性。剑桥大学出版社。

[5] 塔瓦纳伊,a .、古德拉蒂,m .、赫拉德皮谢,S. R .、马斯克利尔,t .、&迈达,A. (2018)。脉冲神经网络中的深度学习。神经网络。

[6] Petro,b .,Kasabov,n .&Kiss,R. M. (2019)。脉冲神经网络时间脉冲编码方法的选择和优化。神经网络和学习系统汇刊。

[7]施里布,s .,&n .卡萨博夫(2013 年)。进化中的脉冲神经网络--综述。进化的系统,4(2),87–98。

[8] Lobo,J. L .,laa,I .,Del Ser,j .,Bilbao,M. N .,& Kasabov,N. (2018)。用于漂移数据流在线学习的进化脉冲神经网络。神经网络,108,1–19。

[9] Lobo,J. L .,Del Ser,j .,laa,I .,Bilbao,M. N .,&n . Kasabov .(2018 年 10 月)。基于进化脉冲神经网络的非平稳数据流漂移检测。智能和分布式计算国际研讨会(第 82-94 页)。斯普林格,查姆。

注意

本文基于 2020 年发表在神经网络*期刊上的科学手稿。可登陆https://www . science direct . com/science/article/pii/s 0893608019302655获取。

新冠肺炎的无自旋数据

原文:https://towardsdatascience.com/spin-free-data-on-covid-19-3329bbc2efdb?source=collection_archive---------62-----------------------

在一个对健康和经济都有深远影响的疫情,获取及时、可理解的数据应该是普遍的。

总结:您有权获得可理解的数据

关于新冠肺炎的数据并不缺乏。但呈现的通常是旋转第一,数据第二。这在任何时候都没有用,尤其是在疫情时期,当赌注如此之高时,这尤其令人沮丧。

对于与您最相关的数据,您知道您所在的州在抗击新冠肺炎方面做得有多好吗?各州在提供给居民的数据和提供数据的清晰程度上有很大差异。

我认为你有权知道——用你能理解的话来说——你的国家在抗击新冠肺炎的战斗中做得有多好。你也有权知道你的国家在做什么。

自二月份以来,我一直在参加一个数据科学讨论组,该讨论组研究了新冠肺炎数据的每个实际方面。该小组的任务是深入理解我们看到的数据。我们小心翼翼地避免政治讨论,因为我们首先寻求不带偏见地理解数据。每个成员都可以自由地得出他们自己的政治结论——但不是在我们的讨论组里。

为了让任何想要的人都可以获得数据(在我们小组讨论的大力支持下),我为每个州创建了新冠肺炎开放准备评估。这是我所在州的一个例子。

图 1 —华盛顿州的开放就绪性评估

这些开放就绪性评估面向 50 个州和哥伦比亚特区,每周更新 2 到 3 次。你可以在我的新冠肺炎信息中心看到他们。

我们小组对新冠肺炎的数据得出了一些结论。

累积图几乎毫无价值

显示阳性检测和死亡累计总数的图表,像这样的,除了震惊值之外,没有什么价值。

图 2 —累积阳性测试的典型图表。图片来自 Buzzfeed 新闻

像这样的图表只是说阳性测试或死亡的数量随着时间的推移而增加。它没有描述情况是变好了还是变坏了。很难看出曲线的轨迹是否在变化。当我研究一个图表时,我会想,“这个图表向我暗示了什么问题?”对于这种类型的图表,答案是“没有”

热图也好不到哪里去

约翰霍普金斯大学提供了宝贵的服务,使新冠肺炎的数据随时可用。但是像无处不在的 JHU·新冠肺炎仪表盘这样的图像并不能提供太多信息。

图 3——JHU 无处不在的阳性检测热图。图片来自约翰·霍普金斯大学系统科学与工程中心

如果整个地图不是红色的,它可能会提出这样的问题,“为什么这个地区比那个地区受影响更大?”但是由于每个居住区都完全饱和,这是另一个没有提示任何问题的图表。

像这样的累积图相当于一辆汽车有里程表,但没有速度计。我们不经常需要里程表,但我们经常需要速度计。这些图表和地图提供了过去发生的事情的历史,但对现在发生的事情却鲜有说明。

增量(每日、每周)图更能提供信息

显示增量数据和趋势的图表更有意义,即显示每日或每周测试或死亡的图表。图 4 中的图表显示了每天的测试和死亡人数,经过平滑处理以弥补报告中每天的变化。图表清楚地表明,截至 6 月 16 日,阳性检测略有增加,但死亡人数仍在下降。它提出了这样一个问题,“为什么当阳性检测增加时,死亡却在减少?”它以一种热图和累积图所不具备的方式讲述了疫情的故事。

图 4 —增量(每日)阳性检测和死亡图表示例

显示一段时间内的增量(变化)的图表提供了大量信息

类似地,考虑如图 5 所示的疫情的故事。此图表跟踪与前 7 天相比,连续 7 天的阳性测试总数。

该图显示,病毒在 4 月初达到一个明显的拐点,在 4 月中旬迅速下降,此后经历了一系列起伏。从图 4 的增量图中不容易看出这个故事,从图 2 或图 3 的图像中也不可能看出。

图 5 —显示阳性测试趋势的图表示例

底线是增量数据和趋势数据比累积数据提供更多的信息。

基于人均数字的图表信息丰富

我们的数据科学小组还了解到,人均数字通常比总数更有用。对总数的共同关注意味着关于人口最多的州如纽约和德克萨斯的报道占据了新闻周期。但是当你检查人均数字时,你会看到各州表现的不同画面。

考虑图 6 中的图表,它显示了各州阳性测试的趋势。

图 6 —阳性测试的州级趋势数据

带有州缩写的标记显示了过去 7 天每个州每 1,000 名居民中阳性检测的数量。实线表示 7 天前的状态,透明线表示 7 天前的状态。

与 JHU 热图不同的是,该图可以方便地比较各州目前的状况,以及它们随时间变化的趋势。这张图表引发了许多问题:“哪些州正在恶化,哪些州正在好转?哪些州的变化最大?有没有什么模式可以说明哪些变得越来越差,哪些变得越来越好?”

目前,新闻报道强调得克萨斯州创造了每日新冠肺炎病例的记录。但上面的图表清楚地表明,最应该担心的州是亚利桑那州、阿肯色州和阿拉巴马州,其次是路易斯安那州和南卡罗来纳州。总数不能传达这一信息;人均数字确实如此。

动画可以提供特别多的信息

动画可以用静态图像无法做到的方式突出显示随时间的变化。上图的动画版本提供了对疾病传播的额外了解。

图 7 —阳性测试的动态状态级趋势数据

你可以看到这种疾病在华盛顿和密歇根慢慢开始。然后看到它在纽约及其周围的东北部各州强劲起飞。你可以看到,整个 4 月,病毒活动集中在东北部,但到了 5 月中旬和 6 月,病毒在全国各地显著下降。随后,在 6 月初,当早先遭受疫情的东北部各州将病毒控制住的时候,几个南部州却出现了疫情。目前,受影响最大的州在南部。

当数据被很好地呈现时,它可以提供令人难以置信的信息。

我的背景和数据来源

我的个人背景是,20 年来我一直专注于理解软件开发的数据分析,包括质量、生产率和评估。我从处理噪音数据、坏数据、不确定性和预测中学到的技术都适用于新冠肺炎。

用于创建本文图表的数据可从 Covid 跟踪项目和 JHU 获得。虽然数据很容易访问,但它的原始形式不太容易理解。这就是我创建这些数据展示的原因。

为什么这对我个人很重要

我对新冠肺炎的看法很简单:我想知道事实,这样我才能做出自己的决定。这在华盛顿州很难实现。我对我所在州的数据透明度(或缺乏透明度)的不满促使我向我的华盛顿同胞提供更好的数据,当我这样做时,其他州也很容易这样做。

请到我的新冠肺炎信息中心查看内容,并随时与我在 stevemcc@construx.com 联系。

非常规神经网络的特征提取

原文:https://towardsdatascience.com/spinenet-feature-extraction-object-detection-cdf9e57dbe0d?source=collection_archive---------42-----------------------

机器学习

尺度置换网络代替尺度递减网络

当你谈到任何一种图像分类或一种特征提取方法时,你脑海中浮现的是哪种神经网络?大概是这样的:给一个图像作为大小为 512x512 的输入,得到一个 10x1 的分类器输出。在这两者之间,有卷积和最大池层,它们会慢慢地降低图像分辨率,同时保留重要的特征。

现在,在 CVPR-2020 期间,谷歌研究和大脑团队提出了一个非常规的网络——spine net,而不是上面解释的典型神经网络。该网络由尺度置换的中间特征和交叉连接组成,这可能是许多需要特征提取的应用(如对象检测或图像分类)的可能解决方案。

结果

让我展示一下,谷歌不仅从一开始就重新定义了我们构建神经网络的方式,而且还改善了结果。

图一。Pic 鸣谢:SpineNet 研究论文

图一。显示了采用 SpineNet、ResNet-FPN 和 NAS-FPN 作为主干的 RetinaNet 模型(在 COCO 数据集上)的比较。红线代表论文中解释的当前 SpineNet 模型(参数不同),蓝色虚线代表与 SpineNet 模型对应的参数不同的 ResNet 网络。

乍看之下,它显示红色线比蓝色虚线具有更好的 AP(%)和更少的翻牌。头版的这个结果让我第一时间看到了方法。

他们还以表格形式显示了不同数据集的结果,并将它们与 ResNet 模型进行了比较。你可以仔细看看这些,我在这个博客的末尾附上了论文链接。

想法和动机

一般来说,目标检测的一个基本架构如图图 2 所示。

图二。基本编解码网络(自制)

其中,编码器用于提取特征(也可用于图像分类),解码器用于恢复这些特征以提供边界框的定位。编码器和解码器主要通过残差连接(ResNet)来使用不同分辨率的特征来保留重要信息。

这种编码器是一种比例递减网络,类似于在 YOLOv3Faster-RCNN 等中使用的网络。,也就是通常所说的模特的脊梁LeCun 等人解释了这种规模缩小的架构设计背后的动机:“可能需要高分辨率来检测特征的存在,而不需要以同样高的精度来确定其确切位置。

为了在模型变得更深时保留空间信息并促进多尺度特征融合,本文提出了这种尺度置换网络。人们可以用图 3 中的来捕捉这个想法。

图三。来源: SpineNet 研究论文截图

图 3 开始。,可以观察到传统的神经网络(比例缩小网络)在左侧。右边是一个规模置换网络的例子,各层和连接之间没有特定的顺序。

这种方法可能会产生许多理论和问题,本文试图回答其中的大部分。然而,为了使这篇文章简短扼要,我将把重点放在阅读摘要后想到的前两个问题上。

  1. 我们如何提出这样的架构或连接?不幸的是,还没有定义好的方法或规则来做到这一点。在论文中,他们使用了神经架构搜索(NAS) 作为一种方法来寻找类似这样不同的可能架构。在过去,许多不同的公司和实验已经展示了 NAS 的使用,但是主要衍生了规模减小的网络。在没有 NAS 的情况下构建这种网络的可能性仍然不清楚,但人们可能会使用他们的最终网络输出(这将在后面显示)。
  2. 既然没有顺序,输入层的输出分辨率和特征尺寸如何匹配目标层的输入分辨率和特征尺寸? 它们执行特征和空间重采样以匹配模型中目标层的特征和分辨率。从图 4 的可以观察到。对于较小的图像,它们使用最近邻插值进行上采样,而它们使用步长(值为 2)、3×3 卷积进行下采样,以匹配模型的目标层。

图 4。来源: SpineNet 研究论文截图

模型

图五。SpineNet-49,来源: SpineNet 研究论文截图

为了进行公平的比较,他们声称使用相同的 ResNet 架构层,并使用 NAS 来找到完美的置换组合。他们最终架构的一个版本(AP 最高,为 40.8%)如图图 5 所示。此外,如前所示,与原始 ResNet 网络相比,此版本使用的计算参数和触发器更少。

图六。通过块重复增加模型深度(来源: SpineNet 研究论文

为了增加模型深度,如 ResNet-100、ResNet-150,它们重复了相同的网络或顺序地组合网络,分别产生 SpineNet-96 或 SpineNet-143,如图 6 所示。

优势和可能的未来

  1. 在这种方法的帮助下,可以创建一个更深的网络,可以增加或减少中间特征图的比例,而不会丢失空间信息。
  2. 特征地图之间的连接应该能够跨越特征比例,从而有助于保留不同比例或分辨率的特征信息。
  3. 它显示了以不同顺序连接层的可能性,这可能是研究和实验的新方向的开端。

可能的缺点

  1. 该网络目前的一个缺点是,它是在 NAS 的帮助下建立的。由于不同连接背后的逻辑仍然不清楚,我们仍然不确定如何自己创建另一个类似的置换网络。
  2. 虽然在论文中,他们已经表明它在对象检测和图像分类应用的情况下增强了性能。我们需要通过对不同网络的更多实验进行比较来确认 SpineNet-49 在其他情况下的表现。

希望这为您提供了这种有趣方法的一个很好的概述。如果您有任何疑问或想与我进一步讨论,请通过 LinkedIn 联系我。

你也可以在这里 访问全文

参考文献

  1. 杜先智-林逸, SpineNet:用于识别和定位的学习标度-置换骨干网,CVPR 2020
  2. 约瑟夫·雷德蒙,阿里·法尔哈迪,约洛夫 3:增量改进,CVPR 2018
  3. Ren,s .,He,k .,Girshick,R.B .,,Sun,J. (2015),更快的 R-CNN:使用区域提议网络实现实时目标检测IEEE 模式分析和机器智能汇刊,39 ,1137–1149。
  4. Yann LeCun,Bernhard Boser,易小轩·登克,Donnie Henderson,Richard E Howard,Wayne Hubbard,和 Lawrence D Jackel,应用于手写邮政编码识别的反向传播,神经计算,1989

使用 Terraform 在 AWS 中将 Jupyter 笔记本电脑升级为 ECS 服务

原文:https://towardsdatascience.com/spinning-up-jupyter-notebooks-as-ecs-service-in-aws-with-terraform-805ac111d74b?source=collection_archive---------28-----------------------

我们团队中的数据科学家需要经常运行耗时的 Python 脚本。根据任务的重复,我们决定是否将它 Dockerize 并在 AWS 上运行它。例如,如果一个脚本需要运行多次,我们会努力重写/重构代码,并将其封装到一个容器中,然后将其部署到 ECR,并将其调度为 Fargate 任务。如果这是一次性的,那么在本地运行脚本有时会更容易,但也有一些额外的缺点。

在本地运行(繁重的)脚本会消耗内存和 CPU,结果是在脚本运行时,您无法真正做其他对笔记本电脑要求很高的事情。我们长期使用的一个解决方案是启动 EC2 实例并在这些实例上运行脚本。在我看来,这不像是一个可维护的解决方案,但它是可行的,我们没有任何其他解决方案。

Jupyter 笔记本

团队大量使用 Jupyter 笔记本(本地)。有 Docker 容器可用,像 jupyter/scipy-notebook ,预装了很多依赖项,比如 pandas 和 scikit-learn。我们想出的一个主意是,基于该图像在 AWS 上轻松地旋转 Docker 容器,然后团队成员可以使用它。

因此,我们希望能够在没有太多麻烦的情况下在云中运行 Jupyter 笔记本,如果可能的话,甚至为每个人创建一个单独的实例,这样依赖关系、资源和文件就不会共享或混淆。笔记本应该或不应该与其他 AWS 资源交互。

我偶然发现了 jupyterhub Git 仓库的 wiki,在那里我找到了一个关于产卵者的页面。有一个 FargateSpawner ,但是说实话,我错过了让它正常工作的文档。

【2020 年 6 月 9 日更新:你可能也会对我最近发布的一篇关于旋转 Polynote 笔记本的新文章感兴趣,这篇文章解决了我在 Jupyter 上遇到的一些问题。

[## 在 AWS Fargate 上使用 Terraform 进行基本身份验证的主机 Polynote 笔记本

本文将帮助您使用 Terraform 在 ECS 上轻松部署安全的 Polynote 环境。

towardsdatascience.com](/host-polynote-notebooks-with-basic-authentication-on-aws-fargate-with-terraform-65d4ff3ba730)

将(行星)地球化(以适合人类居住)

Terraform 是一个安全有效地构建、更改和版本控制基础设施的工具。Terraform 可以管理现有的和受欢迎的服务提供商以及定制的内部解决方案。

地形

在过去的几个月里,我们对地形做了很多实验。我们心中有一个理想的情况,我们所有的资源和基础设施都通过 Terraform 以代码的形式建立和维护,但这还有很长的路要走。虽然它给了我们面对的问题一个新的视角。在对 Terraform 文件进行了一两个小时的工作后,我得到了一个可行的解决方案,目前正在我们的团队中进行测试!

旋转笔记本的地形脚本

我已经建立了一个 Git 存储库来共享我创建的脚本。你可以在这里找到 Git 库,我会一路解释。在本文中,我不会在一个代码片段中提供整个 Terraform 脚本,为此,我想参考 Git 存储库。我将把脚本分成几块,并提供每个片段的信息。

先决条件

这需要一些先决条件(参考 main.tf 脚本中的数据块)。我们已经在 AWS 中设置了以下服务:

  • ECS 集群
  • 应用程序负载平衡器
  • Route53 内的托管区域和域

如果您没有这些东西,您可以选择通过 Terraform 来设置它们(我肯定会建议您在其他地方进行设置,在您定义和维护您的基础架构的地方)。但是当然,您也可以在 AWS 控制台中手动设置这些,就像我们之前所做的那样。

我尽可能地使用了变量。这是因为我们有多个 AWS 环境,我们希望能够在这些不同的环境中轻松运行笔记本电脑。我们还希望能够调整内存和 CPU,而不必更改 terraform 脚本。我的 vars.tfvarsvars.tf 看起来如下:

vars.tfvars (替换所有大写字母并重新访问其他变量值) :

vpc_id = "VPC_ID_HERE"
region = "REGION_HERE"
profile_name ="PROFILE_NAME_HERE"
ecs_cluster_name = "ECS_CLUSTER_NAME_HERE"
loadbalancer_arn = "LOAD_BALANCER_ARN_HERE"
hosted_zone_id = "ROUTE_53_HOSTED_ZONE_HERE"
fargate_subnets = [
  "SUBNET_1_HERE",
  "SUBNET_2_HERE",
  "SUBNET_3_HERE"]
jupyter_docker_tag = "latest" #Update July 2021: change this to "dc9744740e12"
cpu = 1024
memory = 2048
domain = "ROUTE_53_DOMAIN_NAME_HERE"

vars.tf

variable "vpc_id" {}
variable "region" {}
variable "profile_name" {}
variable "ecs_cluster_name" {}
variable "loadbalancer_arn" {}
variable "hosted_zone_id" {}
variable "fargate_subnets" {
  type = list(string)
}
variable "token" {}
variable "jupyter_docker_tag" {}
variable "memory" {}
variable "cpu" {}
variable "domain" {}

使用这个脚本

首先,如果你还没有安装 Terraform。安装完成后,可以在保存了 main.tfvars.tfvarsvars.tf 的文件夹中运行以下命令(运行terraform init后):

terraform apply -var-file=vars.tfvars

它会要求您提供一个令牌,您可以使用它来访问笔记本,并要求您批准在您的 AWS 云环境中进行更改。几秒钟后,它会输出一个您可以用来访问环境的 url。Docker 映像真正运行并可访问可能需要几分钟时间,所以如果没有立即得到预期的响应,也不用担心。最初几分钟,您可能会收到 HTTP 503 响应。

撕下笔记本

当您使用完 Jupyter 环境后,只需运行下面的命令。提供您在设置环境时使用的相同令牌,不要键入“yes”来批准该命令。所有创建的资源都将被删除。如果您希望以后再次运行 Jupyter 笔记本,可以选择先将其保存在本地计算机上。

terraform destroy -var-file=vars.tfvars

在这里,我将进一步解释存储库中的内容,并将为每个代码片段提供额外的信息,说明脚本实际上是做什么的。

这个脚本是做什么的?

假设你已经有了一些 Terraform 的经验或者愿意自己去查阅,我想告诉你这个脚本在做什么。有了数据块,我们就可以检索关于已经存在的 AWS 资源的信息。正如您在第一个数据块中看到的,我想检索已经存在的 ECS 群集的信息。

data "aws_ecs_cluster" "ecs_cluster" {
  cluster_name = var.ecs_cluster_name
}

我正在创建一个随机的字符串,稍后将被几个资源使用。这个随机字符串是这个脚本中的关键元素,它允许多个用户同时启动笔记本电脑。

resource "random_string" "random_string" {
  length = 8
  special = false
}

我们需要创建一个任务执行角色,该角色具有附加的策略,例如允许我们写入 CloudWatch。AWS 已经提供了该策略(参见下面的data “aws_iam_policy" “amazon_ecs...."块,但是我们仍然需要一个附加了该策略的角色。

resource "aws_iam_role" "ecs_task_execution_role" {
  name = "ecsTaskExecutionRole-jupyter-${random_string.random_string.result}"
  assume_role_policy = <<ASSUME_ROLE_POLICY
{
"Version": "2012-10-17",
"Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ecs-tasks.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
ASSUME_ROLE_POLICY
}data "aws_iam_policy" "amazon_ecs_task_execution_role_policy" {
  arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}resource "aws_iam_role_policy_attachment" "policy_role_attachment" {
  role = aws_iam_role.ecs_task_execution_role.name
  policy_arn = data.aws_iam_policy.amazon_ecs_task_execution_role_policy.arn
}

出于日志记录的目的,我创建了一个 CloudWatch 组,当然,你可以随意设置。

resource "aws_cloudwatch_log_group" "jupyter_ecs_log_group" {
  name = "/aws/ecs/jupyter-${random_string.random_string.result}"
}

ECS 上的每个 Fargate 或 EC2 服务/任务都需要一个任务定义,定义使用什么 Docker 容器,需要多少 CPU 和内存等等。你可以把它想象成一个蓝图。正如您在下面看到的,我将 jupyter/data science-notebook 指定为图像。我还更改了入口点,因此可以设置自定义令牌,否则它将生成一个随机令牌,不太容易从系统中检索到。

resource "aws_ecs_task_definition" "jupyter_task_definition" {
  family = "jupyter-${random_string.random_string.result}"
  requires_compatibilities = [
    "FARGATE"]
  network_mode = "awsvpc"
  cpu = var.cpu
  memory = var.memory
  execution_role_arn = data.aws_iam_role.ecs_task_execution_role.arn

  container_definitions = <<TASK_DEFINITION
  [
    {
        "entryPoint": ["start-notebook.sh","--NotebookApp.token='${var.token}'"],
        "essential": true,
        "image": "registry.hub.docker.com/jupyter/datascience-notebook:${var.jupyter_docker_tag}",
        "name": "jupyter-${random_string.random_string.result}",
        "portMappings": [
            {
                "containerPort": 8888,
                "hostPort": 8888
            }
        ],
        "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                  "awslogs-region": "${var.region}",
                  "awslogs-group": "${aws_cloudwatch_log_group.jupyter_ecs_log_group.name}",
                  "awslogs-stream-prefix": "${random_string.random_string.result}"
            }
        }
    }
  ]
  TASK_DEFINITION
}

正如我提到的,我们已经有了一个带有 HTTPS 监听器的负载平衡器,所以我们希望从它那里检索信息,以便以后使用(连同我们的 VPC 的信息)。当然,您也可以使用端口 80,但我的建议是使用端口 443。

data "aws_vpc" "vpc" {
  id = var.vpc_id
}

data "aws_lb" "lb" {
  arn = var.loadbalancer_arn
}

data "aws_lb_listener" "lb_listener" {
  load_balancer_arn = var.loadbalancer_arn
  port = 443
}

此设置还需要一个目标组,负载平衡器侦听器规则将指向该目标组。该目标组稍后将在aws_ecs_service资源块中使用。

resource "aws_lb_target_group" "jupyter_target_group" {
  name = "jupyter-${random_string.random_string.result}"
  port = 80
  protocol = "HTTP"
  vpc_id = data.aws_vpc.vpc.id
  target_type = "ip"
  health_check {
    matcher = "200,302"
  }
}

我们还需要将容器中的端口 8888 暴露给负载平衡器。我将端口 8888 暴露给连接到负载平衡器的安全组。

resource "aws_security_group" "jupyter_security_group" {
  name = "jupyter_${random_string.random_string.result}"
  vpc_id = data.aws_vpc.vpc.id

  ingress {
    description = "Incoming 8888"
    from_port = 8888
    to_port = 8888
    protocol = "tcp"
    security_groups = data.aws_lb.lb.security_groups
  }

  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = [
      "0.0.0.0/0"]
  }

  tags = {
    Name = "jupyter_${random_string.random_string.result}"
  }
}

有了所有这些资源,我们终于可以定义我们的 ECS 服务了。

resource "aws_ecs_service" "jupyter_service" {
  name = "jupyter-${random_string.random_string.result}"
  cluster = data.aws_ecs_cluster.ecs_cluster.id
  task_definition = aws_ecs_task_definition.jupyter_task_definition.id
  desired_count = 1
  launch_type = "FARGATE"

  network_configuration {
    subnets = var.fargate_subnets
    security_groups = [
      aws_security_group.jupyter_security_group.id]
  }

  load_balancer {
    target_group_arn = aws_lb_target_group.jupyter_target_group.arn
    container_name = "jupyter-${random_string.random_string.result}"
    container_port = 8888
  }
  depends_on = [
    aws_lb_target_group.jupyter_target_group]
}

然后,在负载平衡器中添加转发规则。假设我们有以下域:company.com,随机字符串为123。如果主机头是jupyter-123.company.com,它将转发到 Jupyter 目标组。【2021 年 7 月更新: field = "host-header"仅适用于版本 2.41.0 之前的 AWS 提供程序,使用[required_providers](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)锁定 AWS 提供程序或更新条件块,使其适用于最新版本

resource "aws_lb_listener_rule" "jupyter_lb_listener_rule" {
  listener_arn = data.aws_lb_listener.lb_listener.arn
  priority = null

  action {
    type = "forward"
    target_group_arn = aws_lb_target_group.jupyter_target_group.arn
  }

  condition {
    field = "host-header"
    values = [
      "jupyter-${random_string.random_string.result}.${var.domain}"]
  }
  depends_on = [
    aws_lb_target_group.jupyter_target_group]
}

然后在 Route53 中添加指向负载平衡器的 CNAME。继续前面的例子,CNAME 将是jupyter-123.company.com,它将指向我们的负载平衡器 url。

resource "aws_route53_record" "jupyter_cname" {
  zone_id = var.hosted_zone_id
  name = "jupyter-${random_string.random_string.result}.${var.domain}"
  type = "CNAME"
  records = [
    data.aws_lb.lb.dns_name]
  ttl = 300
}

现在一切就绪,我们当然想知道如何访问笔记本,因为一些字符串是随机生成的,我们在开始时已经设置了一个令牌。我们可以通过一个输出变量来实现。

output "url" {
  value = "${aws_route53_record.jupyter_cname.name}?token=${var.token}"
}

后续步骤

目前,Jupyter 笔记本无法访问其他 AWS 资源。因此,您必须在任务定义中提供一个任务角色。此外,当您想要访问 VPC 中的数据库时,您必须在数据库的安全组中添加入站/入站规则,该规则允许来自附加到 ECS 服务的安全组的传入流量。
状态现在被设置为本地,因此使用此脚本旋转笔记本的人也要负责销毁它们。

问题和反馈

如果您对本文有任何问题或反馈,请随时联系我或留下您的评论。

2021 年 7 月更新

由于 Jupyter docker 图像的版本在发布时并没有固定到本文中的特定版本,所以我在 vars.tfvars 中添加了一个注释。评论中的图片标签是在写这篇文章时 docker 图片的最新版本。当你想使用 docker 镜像的最新版本时:GitHub 用户 mvtango 在我的存储库中创建了一个 pull 请求。据我了解,最新的图片默认使用的是 Jupyter Lab。

精彩的计算机视觉项目使你的照片三维!!!!!

原文:https://towardsdatascience.com/splendid-computer-vision-project-makes-your-photo-3d-fac10e334b2f?source=collection_archive---------43-----------------------

你的照片在和你说话!

作者照片,在通往班夫的路上,2018 年 11 月

作者照片,班夫的 Yoho 国家公园,2018 年 11 月

作者的照片,我在蒙特利尔的合作伙伴,2019 年 8 月

太棒了,对吧?我和你们一样惊讶。这个神奇的项目是由四位伟大的研究人员施孟丽约翰内斯·科普夫贾在2020 年 IEEE 计算机视觉与模式识别大会(CVPR)上完成的。

想在你的照片上试试吗?如果你对计算机视觉和 CNN 完全没有概念也没关系,只要按照我在下面 设置 部分的步骤运行这个链接中的所有代码就可以了!我建议在 Colab 中设置,因为它需要一定数量的计算机资源来训练,并且 Colab 会为您缓存。

设置:

导入图像代码块

用于图像导入的文件夹

  1. 运行该代码块之前的所有代码,将所有要制作 3D 的照片拖动到高亮显示的图像文件夹中,然后运行该代码块导入您上传的图像。
  2. 然后只需运行下面的最后一个代码块:
!python main.py --config argument.yml

根据您的计算机规格和图片属性,每个训练批次需要等待 2-5 分钟。

3.然后你得到结果!

输出结果

您可以在指定区域找到您的结果。它将输出五个输出视觉效果,其中包括由 MiDaS 估计的深度图,修复的 3D 网格,以及在圆周、摆动和缩放运动中的 3D 视频演示。够简单了吧?想知道背后的逻辑就继续看下去吧!

理论:

一台机器如何从一张 2D 的照片中预测出 3D 视图?我的意思是,对于照片中的每个物体,如果你想“看到”它背后的东西,你必须以某种方式想象它是一个人。当人们看到一张照片时,他们不仅只是将它视为一个静态的图像,还会将它感知为一个有生命的 3D 物体,甚至会虚构一个想象的场景或回忆一些记忆。但是机器如何处理如此复杂的概念呢?它能不能“ 想象 ”??

嗯,一台机器无法想象,但它可以“ 学习 ”到“ 想象 ”,或者换句话说,它可以像人类一样处理数据和输出。基本上,机器只是做他们擅长的事情:计算。

图片来自使用上下文感知分层深度修复的 3D 摄影

通常人工智能学习 RGB-D 图像,其中 D 代表“深度”,以重温 3D 效果。目前,市场上大多数智能手机都有两个摄像头来分别捕捉颜色和深度。但是,没有深度的普通 RGB 图片怎么办?机器预测!通过一些标准的图像预处理步骤,我们可以很容易地找到深度图(a 到 d)

有了预测的深度,机器可以找到深度不连续的地方,然后分类,并分组到不同的颜色部分(e 到 f)。

有了所有的预处理准备,我们将从我们的 2D 照片修复三维视觉。我们正在使用的最重要的工具叫做【LDI】

图片来自使用上下文感知分层深度修复的 3D 摄影

在边缘上,像素由两边通过一个锐降(a)连接。该程序首先将拖放连接切割成绿色和红色区域(b),我们称它们为前景轮廓和背景轮廓,基于背景轮廓或上下文区域(c)生成一个合成区域,然后合并到模型中。

图片来自使用上下文感知分层深度修复的 3D 摄影

现在,由于我们已经分离了两个区域(上下文区域和合成区域),科学家使用三个修复代理来完成修复任务:边缘修复网络、颜色修复网络和深度修复网络。你可以查看下面的参考资料,详细了解这些修复网络是如何工作的。

边缘修复网络修复上下文区域和合成区域之间的轮廓,以预测被阻挡的边缘。然后机器使用颜色修复网络和深度修复网络分别想象被遮挡的颜色和深度。在这之后,我们将结果反馈给 LDI 模型,就这样!我们有结果了!

没有进一步的到期,玩模型,重温你的记忆!

PixabayCoyot 拍摄,作者加工

参考和资源:

相关项目:

论文和参考文献:

在 Python 中分割重叠的边界框

原文:https://towardsdatascience.com/split-overlapping-bounding-boxes-in-python-e67dc822a285?source=collection_archive---------34-----------------------

从问题公式化到创建 PyPI 包,使用来自全球小麦检测 Kaggle 竞赛的数据进行测试,分 5 步完成教程。

在对象检测中,通常使用包围盒目标来识别图像中的对象。这些边界框有时可能会重叠。在一些像 Mask RCNN 的模型中,边界框被直接预测,边界框的重叠不成问题。另一种可能的方法是将边界框转换为遮罩,并使用语义分割模型,如 U-Net 。在这种情况下,如果最后你想分离单独的对象,重叠的遮罩可能是一个问题。

在这个故事中,我将讲述如何用 Python 开发一个算法来分离重叠的边界框,并在它们之间设置边距。我将使用来自全球小麦检测竞赛的数据。

请注意,我并不是说这种方法是小麦检测竞赛的最佳方法。将来,我可能会写另一个关于这个主题的故事。

目录

  1. 问题定式化
  2. 写数学
  3. 用 Python 编写算法代码
  4. 在现实问题中测试代码
  5. fastai nbdev 创建一个 Python 包

1.问题定式化

让我们从考虑两个重叠边界框的情况开始。我们想要分开边界框,在它们之间留一些空白,如下图所示。

将两个边界框分开并留有一定边距的示例。图片由作者提供。

定义余量的程序如下:

  • 考虑由点 A 和 B 定义的线——每个边界框的质心——我们称之为 AB。
  • 然后考虑一条垂直于 A 和 B 之间中点的直线——设它为 ABp。
  • 最后,上图中的两条线平行于 ABp,其距离由我们选择的边距值给出。

在第 4 节的后面,我将展示该代码可以很容易地应用于多个相交边界框的情况。

2.写数学

考虑下面的图像和等式,其中粗体符号表示向量。矢量 AB 就是在第一个等式中定义的从 A 到 B 的矢量。然后我们可以考虑一个垂直于 AB 的向量——我称之为ABp——使用第二个等式。最后,盒子 A 的边缘线上的点 M 可以由第三个等式定义。

边缘线的形成。图片由作者提供。

基本原理是:1)你从 A 开始;2)你向 AB 方向移动,但只到了中点的一半;3)你以因子 m 乘以 AB 方向的单位矢量,向同一个方向稍微向后移动;4)你到达的点就是我定义的 m 点。

使用点 M 和矢量 ABp it 直接定义边缘线,我将在下一节展示。

3.用 Python 编写算法代码

下面的函数接收两个边界框,并返回框 a 的切片区域。输入框为形状良好的多边形以及该函数返回的输出。现在让我们一行一行地深入代码。

  • 第 1 行:输入是两个包围盒——类型为形状多边形box_Abox_B边距设置框之间的距离应该有多大,而 line_mult 只需要足够高,以保证线条完全穿过多边形。
  • 第 3 行:向量 AB ( vec_AB )是用盒子的质心定义的。
  • 第 4 行:类似于第 3 行,但垂直向量( vec_ABp )遵循第 2 节中的等式。
  • 第 5 行:计算矢量 AB 的范数,因为稍后会用到它。
  • 第 6 行:根据第 2 节的等式定义分割点(点 M)。
  • 线 7:使用 shapely LineString 类定义线,该类允许定义给定两点的线。请注意,这些点也是形状优美的几何图形。因此,该线被定义为从点 M 减去矢量 ABp 的倍数直到点 M 加上矢量 ABp 的倍数。
  • 第 8 行:一个 shapely 效用函数被用来将多边形一分为二。
  • 第 10 行:对于第 8 行获得的每个多边形,检查是否包含中心点 a。
  • 第 11–15 行:在这几行中,我分离了包含中心点的多边形(与此目的相关的一个),并将其与另一个多边形(不包含中心点)和用于切片的线一起返回。额外返回的对象只是以防将来的应用程序会用到它。

4.在现实问题中测试代码

第 3 节中定义的代码仅适用于两个边界框。为了将它应用于几个边界框,我定义了下面的代码,现在我将简单解释一下。

  • intersection _ list-一个函数计算列表中所有多边形的交集。
  • slice_all —该函数接收包含图像所有边界框的地理数据框(见下表)作为输入,并为每个边界框调用 slice_oneslice_one 简单地对给定的 box_A 应用切片框功能,并考虑与之相交的所有框。当有几个相交的盒子时,使用 intersection_list 函数获得最终的多边形。

输入地理数据框的示例。图片由作者提供。

slice_all 的结果是一个类似的地理数据框,但带有切片框。下图显示了原始边界框(左)和 slice_all 的结果(右)。如您所见,原始数据中有几个重叠区域,但应用刚刚开发的方法后没有重叠区域。

原始边界框(左)和应用所述方法后的结果(右)。图片由作者提供。

5.用 fastai nbdev 创建一个 Python 包

fastai nbdev 可以说是创建 Python 包并将其上传到 PyPI 的最简单、最用户友好的方式。

  • 当您开始一个新项目时,请转到 nbdev instructions,并使用该链接为模板创建一个存储库。这会让你很容易开始。
  • 然后你应该为你的本地机器克隆这个库,pip install nbdev并在项目目录下运行nbdev _ install _ git _ hooks
  • 现在你可以像往常一样打开 jupyter 笔记本了。 settings.ini 文件包含您需要填写的设置信息,例如项目名称、您的 github 用户名以及您的软件包要求。
  • nbdev 模板包括一个 index.ipynb 和一个00 _ core . ipynb。这个索引将是 GitHubreadme . MD文件。
  • 然后在 00_core.ipynb 上,你可以像往常一样开发你的代码,但是要记得在你的包 core.py 文件中的单元格顶部添加 #export 注释——这个文件是 nbdev 从笔记本上生成的。你可以在他们的文档 中详细阅读如何使用 nbdev。我强烈推荐!它将改变你编码的方式。
  • 准备就绪后,您可以在终端上运行nbdev _ build _ libnbdev _ build _ docs。然后,您可以提交您的更改并推送到 github repo。检查回购,看看是否所有测试都通过了。导入外部包时,一个常见的错误是 ModuleNotFoundError。您需要将它们包含在 settings.ini 中的要求下,这样软件包将与所有需要的依赖项一起安装。
  • 当一切正常时,你可以通过运行命令 make pypi 将你的包上传到 PyPI 。但是,如果您以前没有这样做过,您需要在 PyPI 中创建一个帐户并设置一个配置文件(详细说明在 nbdev 页面上,这里是)。

就是这样!我的包现在在 PyPI at https://pypi.org/project/splitbbox上,可以 pip 安装了。

结束语

关于我

[## 我的 3 年历程:从零 Python 到深度学习竞赛高手

自从 2017 年开始学习 Python 以来,我一直遵循的道路是成为一名独自参加 Kaggle 比赛的大师…

towardsdatascience.com](/my-3-year-journey-from-zero-python-to-deep-learning-competition-master-6605c188eec7)

感谢阅读!祝您愉快!

分割数据集

原文:https://towardsdatascience.com/splitting-a-dataset-e328dab2760a?source=collection_archive---------6-----------------------

简要说明如何使用 sklearn 对数据集进行训练测试分割

作者图片

为了训练任何机器学习模型,无论使用哪种类型的数据集,都必须将数据集分为训练数据和测试数据。因此,让我们看看如何做到这一点?

在这里,我将使用 iris 数据集,并使用 sklearn 的“train_test_split”库对其进行分割

from sklearn.model_selection import train_test_splitfrom sklearn.datasets import load_iris

然后我将虹膜数据集加载到一个变量中。

iris = load_iris()

然后我用它将数据和目标值存储到两个独立的变量中。

x, y = iris.data, iris.target

在这里,我使用“train_test_split”以 80:20 的比例分割数据,即 80%的数据将用于训练模型,而 20%的数据将用于测试由此构建的模型。

x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=123)

正如您在这里看到的,我在“train_test_split”中传递了以下参数:

  1. 我们之前定义的 x 和 y
  2. test_size:设置为 0.2,因此定义测试大小为数据集的 20%
  3. random_state:它控制在应用分割之前应用于数据的混洗。将 random_state 设置为固定值将保证每次运行代码时生成相同的随机数序列。

当分割数据集时,有两个相互竞争的问题:
-如果你有较少的训练数据,你的参数估计有较大的方差。
-如果您的测试数据较少,您的性能统计就会有较大的差异。
数据应该这样划分,两者都不要太高,这更取决于你拥有的数据量。如果你的数据太小,那么没有分割会给你满意的方差,所以你必须做交叉验证,但是如果你的数据很大,那么你是选择 80:20 分割还是 90:10 分割真的没有关系(实际上你可以选择使用较少的训练数据,否则,它可能需要更多的计算)。

Splunk 和 SQL,终于走到一起了?

原文:https://towardsdatascience.com/splunk-and-sql-together-at-last-3e84b1e3b75c?source=collection_archive---------70-----------------------

photo-1501743411739-de 52 ea 0 ce 6a 0

过去几年,我一直参与 Splunk 工程。我觉得这有点讽刺,因为我已经很久没有用 Splunk 了。出于各种原因,我从来都不是 Splunk 查询语言(SPL)的粉丝,其中一个主要原因是我不想花时间去学习一种专有语言,这种语言就像 1974 年的福特 Pinto 一样优雅。多年来,我参与了几个项目,涉及对 Splunk 中的数据进行机器学习。这是一个重大的挑战。虽然 Splunk 确实有一个机器学习工具包(MLTK),它基本上是 scikit-learn 的包装器,但在 SPL 进行功能工程是一场噩梦。

那么,如何在 Splunk 中进行机器学习呢?

你不知道。

不,真的 Splunk 怎么做机器学习?

好吧…人们实际上在 Splunk 中进行机器学习,但我认为这不是最好的方法,原因有几个。我在 Splunk 中看到的大多数 ML 工作都涉及到从 Splunk 中获取数据。在这种情况下,我向任何试图在 Splunk 中进行 ML 的人推荐的第一件事是看看 huntlib(【https://github.com/target/huntlib】)这是一个 python 模块,有助于从 Splunk 中获取数据。Huntlib 使这变得相对容易,你可以将 Splunk 中的数据直接放入 Pandas 数据框架中。但是,你仍然必须知道 SPL,或者你做一个基本的 SPL 搜索,用 Python 做你所有的数据辩论。还有更好的方法吗?

SQL/Splunk 连接器

我一直在做的一个项目是 Apache Drill 到 Splunk 的连接器,它允许用户使用 ANSI SQL 查询 Splunk。这种方法有几个优点:

  • SQL: 面对现实吧。SQL 可能是表达复杂查询的最佳语言之一。自 20 世纪 80 年代以来,它被广泛使用,并在无数系统中实现。从技能的角度来看,学习 SQL 比学习一种可能在几年内过时的专有查询语言更划算。总而言之,SQL 对于定义表来说也是极具表现力和高效的。我的想法是,如果你可以用 SQL 查询 Splunk,这意味着不懂 SQL 的数据科学家现在可以访问 Splunk 中的数据。
  • 查询优化:当您开始使用 Splunk 时,您首先会了解到 Splunk 不会优化查询。这意味着这个任务落在了作为查询作者的您身上。这里的问题是,这需要您了解 Splunk 的实际工作方式。这与大多数为您做这种优化的 SQL 数据库相反。此外,许多 SQL 引擎可以提供如何改进查询的建议。因此,Drill/SQL 连接器将为您执行这些优化,因此您不必学习如何优化 Splunk 查询。
  • 外部数据集:Splunk 的一个大问题是,当您的一些数据在 Splunk 中,而另一些不在时,该怎么办。现在,Splunk 的人会说,“简单..放入 Splunk。问题解决了。”但在企业中,就没这么简单了。在企业中,用户很可能永远没有权限这样做,用户必须让工程团队参与进来,才能将外部数据导入 Splunk。由于 Splunk 按接收的数据量收费,企业不能让用户将随机的、未经验证的数据放入生产系统,因为这会影响预算。这也需要时间。据我观察,企业通常需要数周时间才能将数据源导入 Splunk。借助 Drill 和 SQL,您可以通过简单的 SQL JOIN 语句轻松地将外部数据集与 Splunk 中的数据连接起来。

这一切和机器学习有什么关系?

任何 ML 项目的第一阶段都是收集数据和提取特征。完成后,您就可以研究数据了。Splunk 非常擅长数据探索,但是在我看来,使用 Splunk 提取特征是一场噩梦。也有很多效率低下的地方,但我们会把它留到下次再讲。无论如何,当你把 Splunk/Drill 和 John Omernik 的棒极了的 Jupyter 笔记本集成用于 Drill (我将在以后的博客文章中讨论)时,你可以简单地:

  • 在 Jupyter 笔记本单元格中键入 SQL 查询
  • 集成将查询钻取和管道您的数据直接进入一个数据框架!

搞定了。你要去比赛了!

Drill/Splunk 连接器如何工作?

在 pull request 中可以获得完整的文档,但是它相当简单。您需要配置 Drill 安装以连接到 Splunk。Splunk 使用端口 8089 进行编程访问,因此该端口必须打开。总之,要配置 Drill,只需导航到存储插件页面,单击添加新插件,将其命名为 splunk,然后粘贴到以下配置中:

{ 
   "type":"splunk", 
   "username": "admin", 
   "password": "changeme", 
   "hostname": "localhost", 
   "port": 8089, 
   "earliestTime": "-14d", 
   "latestTime": "now", 
   "enabled": false 
}

单击提交后,您现在应该能够通过钻取来查询 Splunk。

Drill/Splunk 数据模型

Drill 将 Splunk 索引视为表。Splunk 的访问模式似乎不会限制对目录的访问,但会限制对实际数据的访问。因此,您可能会看到您无权访问的索引的名称。您可以通过SHOW TABLES IN splunk查询查看可用索引的列表。

apache drill> SHOW TABLES IN splunk; 
+--------------+----------------+ 
| TABLE_SCHEMA | TABLE_NAME     | 
+--------------+----------------+ 
| splunk       | summary        | 
| splunk       | splunklogger   |
| splunk       | _thefishbucket | 
| splunk       | _audit         | 
| splunk       | _internal      | 
| splunk       | _introspection |
| splunk       | main           | 
| splunk.      | history        | 
| splunk       | _telemetry     | 
+--------------+----------------+ 
9 rows selected (0.304 seconds)

要从 Drill 查询 Splunk,只需使用以下格式:

SELECT <fields> FROM splunk.<index>

Drill 将执行查询,并在一个漂亮干净的表中返回结果!

限制您的查询

当您学习通过 Splunk 的界面查询 Splunk 时,您首先要学习的是绑定您的查询,以便它们查看尽可能最短的时间跨度。当使用 Drill 来查询 Splunk 时,建议执行相同的操作,Drill 提供了两种方法来完成此操作:通过配置和在查询时。

在查询时绑定查询

绑定查询最简单的方法是在 querytime 通过WHERE子句中的特殊过滤器来完成。有两个特殊的字段,earliestTimelatestTime,可以设置它们来绑定查询。如果没有设置它们,查询将被绑定到配置中设置的默认值。

您可以在此处使用 Splunk 文档中指定的任何时间格式:
https://docs . Splunk . com/Documentation/Splunk/8 . 0 . 3/search reference/search time modifiers

因此,如果您想查看过去 15 分钟的数据,可以执行以下查询:

SELECT <fields> 
FROM splunk.<index> 
WHERE earliestTime='-15m' AND latestTime='now'

查询中设置的变量会覆盖配置中的默认值。

向 Splunk 发送任意 SPL

您可能有一个 Splunk 查询没有映射到该模型,对于这些情况,有一个名为spl的特殊表,您可以使用它向 Splunk 发送任意 SPL 查询。如果使用该表,必须在spl过滤器中包含一个查询,如下所示:

SELECT * 
FROM splunk.spl 
WHERE spl='<your SPL query>'

这在很大程度上是一项正在进行的工作,所以如果你尝试这个,我将欢迎反馈它如何为你工作。文档解释了如何处理来自 Splunk 的嵌套数据等。祝你好运!

原载于 2020 年 7 月 24 日 https://thedataist.com

剧透警告:康纳·麦格雷戈 vs 牛仔?谁会赢?

原文:https://towardsdatascience.com/spoiler-alert-conor-mcgregor-vs-cowboy-who-will-win-7c3eee6389b0?source=collection_archive---------18-----------------------

应用 Elo 评级系统预测 UFC 246

资料来源:ufc.com

2020 年 1 月 18 日,格斗运动中最大的赛事之一将在 UFC 246 举行。

综合格斗(MMA)历史上最受欢迎的拳手——康纳·麦格雷戈,将与粉丝最喜欢的——“牛仔”唐纳德·塞罗恩进行期待已久的对决。对于拳手、UFC 和下注的粉丝来说,将会有大量的金钱。

谁会赢?

更新:这场战斗的结局符合我们的预测!
观看赛后采访视频。

来源:ufc.com

与其随机下注,不如做一些数据科学的工作来预测赢家。

你准备好了吗?

是时候隆隆作响了!

Elo 评级系统

Elo 评分系统是一种计算零和游戏中玩家相对技能水平的方法。它主要用于国际象棋。但许多人已经在足球、篮球和拼字游戏等其他竞技游戏中应用了相同的算法。

在本文中,我们将使用 Python 基于 Elo 评级来预测这场 UFC 大战的获胜者。

收集数据

根据 Elo 评级系统,我们需要收集过去与 Conor 和 Cowboy 相关的所有打斗数据。我从 sherdog.com 那里搜集了历史资料。

这个过程从第页开始,第页有康纳的战斗史。所有过去与他有关的打斗信息都被收集起来。此外,对于他的每个对手,所有与他们相关的战斗信息都是以类似的方式收集的。

资料来源:sherdog.com

鉴于康纳的一个对手(内特·迪亚兹)曾与牛仔打过仗,这些数据也包括了与牛仔有关的信息。最终我们会有康纳的对手,牛仔的对手,他们对手的对手等等的数据。

虽然 MMA 已经成为一项运动几十年了,但要收集整个数据集需要很长时间。所以当数据达到 36503 架,93778 架的时候我就停止了进程。对于我们的目的来说,这些数据应该足够了。

最后的数据如下所示:

来源:作者

应用 Elo 算法

Elo 算法的细节可以在这里找到。

简而言之,该算法根据每个新的匹配更新战斗机的评级。下面的简单数学公式描述了这一过程:

资料来源:eloratings.net

  • 基本参数:K

要调整的最重要的参数是 k。它决定在一场比赛后增加或减少多少评分点。更高的 K 值意味着更不稳定的评级。

举个例子,我们假设两个战士 A 和 b。

战斗机 A 当前等级= 1500

B 战斗机当前等级= 1500

假设他们之间的新一轮比赛以 A 获胜结束。我们需要相应地更新这两个拳手的评分。

当 K = 10 时:

战斗机 A 的新等级= 1500+10 *(1–0.5)= 1505

*战斗机 B 的新等级= 1500+10 (0–0.5)= 1495

当 K = 100 时:

*战斗机 A 的新等级= 1500+100 (1–0.5)= 1550

*战斗机 B 的新等级= 1500+100 (0–0.5)= 1450

我们可以看到,当 K 较大时,当新的信息进来时,战斗机的收视率变化快得多。

  • 额外参数:为 MMA 格斗定制

一场 MMA 格斗有不同的结局。打架会以三种方式结束:

  1. 结束——胜者在时间限制前通过击倒或提交来“结束”败者。
  2. 一致决定——时限已过。三位评委一致认为获胜者是同一个拳手。
  3. 分裂决策—时间限制已过。三位评委对获胜者的选择意见不一。占多数的战士获胜。

从胜利者的角度来看,一个完整的决定比一致的决定更令人印象深刻,而一致的决定比分裂的决定更令人印象深刻。

为了整合这些信息,我们在 K 中增加了三个乘数,关系如下:

完成乘数≥一致决策乘数≥分裂决策乘数

这些乘数有助于进一步区分不同战士的评级(基于他们结局的感人程度)。

例如,如果甲在终点击败了乙:

战士 A 的新等级=战士 A 的当前等级+终结乘数 K (1–We)

战士 B 的新等级=战士 B 的当前等级+完成乘数 K (0–We)

这样一来,战斗机 A 的收视率会比其他两种类型的结局上升更多。而战斗机 B 的收视率会比其他两种类型的结局下沉更多。

  • 参数选择

首先,我们将参数的可能值范围设置如下:

10 ≤ K ≤ 300

完成乘数≥ 1.0

一致决定乘数= 1.0

分割决策乘数≤ 1.0

使用交叉验证方法测试了三个未决定参数 (K、结束乘数、分割决定乘数)的不同组合。

我们发现,就预测准确性而言,最佳值如下:

K = 200*

终点乘数= 1.05

一致决定乘数= 1.0

拆分决策乘数= 0.5

*棒球、足球和象棋等运动的 K 值通常在 10 到 30 之间。K = 200 表明混合武术的收视率波动比其他运动大得多。

既然我们已经完成了“无聊”的部分。我们可以简单地将参数值插入到 Elo 公式中,并用每个新的匹配更新战士的等级。

我们已经准备好看到结果了!

结果

  • 根据 Elo,谁是顶级拳手?

根据算法,下面是我们的前 10 名战士:

名字后面的数字是战斗机的唯一标识符。来源:作者

尽管我们的数据包括所有组织的战士。排名前十的拳手都活跃在 UFC。这与 UFC 吸引最优秀的拳手的普遍观点不谋而合。

  • 那么谁会赢得这场战斗呢?

现在我们也可以回答我们一开始问的问题了。康纳 vs 牛仔,谁会赢?

下图显示了两种战斗机的 Elo 等级:

来源:作者

尽管牛仔在过去更强大。康纳近年来的评分较高。

截至今天,康纳的收视率为 2791,而牛仔的收视率为 2529。康纳赢的几率是 82%!

来源:giphy.com

玩得开心!

更多金钱和数据科学相关文章来自 Lianne & Justin:

[## 根据模拟,30 岁退休需要多少钱

很多人年纪轻轻就有辞掉工作的梦想,活在世上没有经济上的牵挂。怎么…

towardsdatascience.com](/how-much-do-you-need-to-retire-at-age-30-based-on-simulation-424fa9e0922b) [## 买彩票的最佳时机是什么时候?

根据理性(或统计)分析

towardsdatascience.com](/when-is-the-best-time-to-buy-lottery-tickets-7735191b3c76) [## 我将如何在我的狗身上花费 6 万多美元

我以前从未试图给我们的狗贴上价格标签。但是根据简单的分析,我最后会花…

medium.com](https://medium.com/@liannewriting/how-im-going-to-spend-over-60-000-on-my-dog-4860e5d8935c)

熊猫的体育分析

原文:https://towardsdatascience.com/sports-analysis-with-pandas-real-vs-barca-94f85819bf6?source=collection_archive---------23-----------------------

如何计算足球比赛的赔率?这比你想象的要容易。不需要编码技能!

托马斯·塞勒在 Unsplash 上的照片

你有没有想过足球比赛的赔率是如何计算的?那些 2.2 vs 3.1 的赔率是基于什么?我原以为这是一个复杂的过程,但令我惊讶的是,不用写任何代码就可以计算出概率和赔率。我在下面的例子中使用熊猫是因为我精通它,但是你也可以在 Excel 中甚至用计算器做同样的事情。

我写了大量关于熊猫数据分析的文章。看看我的熊猫系列:

[## 熊猫数据分析系列

从提示和技巧,如何不指南到与大数据分析相关的提示,熊猫文章的精选列表。

medium.com](https://medium.com/@romanorac/pandas-data-analysis-series-b8cec5b38b22)

如何计算概率?

一定数量的目标的概率用泊松分布计算,泊松分布以法国数学家西蒙·丹尼斯·泊松命名。

泊松分布是一种离散概率分布,它表示在固定时间间隔内发生的给定数量的事件的概率,如果这些事件以已知的恒定平均速率发生,并且与自上次事件以来的时间无关。

让我们用通俗的语言来解释这一点。离散概率分布给出了离散事件在 0 和 1 之间的概率,如足球示例中的进球数。一场足球比赛有一个固定的时间间隔(90 分钟的比赛),进球以一个已知的恒定平均速率出现(我们可以计算出预期的进球数量)。一个目标也独立于前一个目标。

下图是泊松分布的概率质量函数(PMF ),显示了预期发生次数为 1、5 和 10 的事件的概率。简单来说,一支平均进 1 球的足球队,有一个:

  • 36%的概率没有进球,
  • 1 球 36%的概率,
  • 18%概率 2 球等。

λ1、5 和 10 的泊松分布。

El Clásico -谁有望获胜?

维也纳·雷耶斯在 Unsplash 上拍摄的照片

让我们来计算一下永远的对手巴塞罗那和皇家马德里之间潜在对决的概率。这场比赛将在圣地亚哥伯纳乌进行,这意味着皇家马德里将在主场作战。

在我们可以使用泊松分布来估计概率之前,我们需要计算每支球队在比赛中可能得分的预期数量。

通过确定球队的进攻实力和防守实力来计算每支球队可能得分的预期目标数。

我根据 2019/2020 赛季西班牙 LaLiga 的在线统计数据组成了数据集。有两个数据集,一个用于主场比赛,一个用于客场比赛,其中:

  • p——玩游戏的次数,
  • GF——进球数,
  • GA——失球数。

拉利加 2019/2020 赛季主场统计。

拉利加 2019/2020 赛季客场数据。

攻击强度

计算进攻强度的第一步,我们计算联赛主客场球队的平均进球数。这是总进球数除以主客场比赛总数。

主场平均进球数:1.50。

平均客场进球数:1.02。

第二步,我们需要确定一支球队的进攻实力有多强。我们计算每支球队的平均进球数,然后除以赛季的平均进球数。

记住这场比赛是在圣地亚哥伯纳乌,所以皇马主场作战,巴塞罗那客场作战。皇马进了 27 个球,打了 13 场主场比赛。Real 的攻击强度是 1.37。

real attack strenght = 27 / 13 / 1.50

巴萨进了 18 个球,打了 13 场客场,所以攻击强度是 1.34。

barca attack strenght = 18 / 13 / 1.02

防御力量

进攻实力重在进球,防守实力重在失球。平均值是从上面的攻击强度简单倒置而来的:

主场平均失球数:1.02 个。

平均失球数:1.50 个。

竞争对手在 13 场主场比赛中对皇家马德里队进了 9 个球。皇马的主场防守强度为 0.67,巴萨的客场防守强度为 0.96。

real defense strenght = 9 / 13 / 1.02
barca defense strenght = 19 / 13 / 1.50

皇马有可能进多少球?

Gif 来自 giphy

把这些数字放在一起,我们可以计算出皇马的预期进球数。我们只是简单地将皇马的主场进攻实力与巴萨的客场防守实力乘以在拉利加主场作战时的平均进球数。

expected goals for real = 1.37 * 0.96 * 1.50

皇马有望进 2.01 球。

巴萨有可能进几个球?

Gif 来自 giphy

使用与上述相同的程序,只是改变家与客场。我们用客场打拉利加时的平均进球数乘以巴萨的客场进攻实力和皇马的主场防守实力。

expected goals for barca = 1.34 * 0.67 * 1.02

巴萨有望进 0.93 球。

使用泊松分布预测结果

泊松分布在线计算器

您可以使用在线计算器计算泊松分布,以确定事件的概率。将巴萨的 平均发生率 设置为 0.93 目标,然后将 发生次数 设置为 0 到 5。我用熊猫来计算多种结果的概率。

下表显示皇马有 26%的机会进 2 球。

上表中的概率是独立的,这意味着我们可以将它们相乘,并计算多个结果的概率。横排的进球是皇马的,横排的是巴萨的。结果 2:0 对皇马有 10%的机会。

计算赔率

为了计算概率,我们需要计算每一个平局的结果(0:0,1:1,等等。)然后将这些概率相加。平局的几率很简单:

平局结果用蓝色标记,真正的胜利用红色标记,巴塞罗那获胜用绿色标记。

odd for draw = 1 / sum( all draw outcomes)

为了计算真实获胜的几率,我们对真实获胜的每一个结果重复这个过程(1:0,2:0,等等。)巴萨也一样。

最终的可能性是:

  • 真实胜率:1.6
  • 平局:4.85
  • 巴萨获胜:6.14

请注意,这些赔率没有博彩保证金。

在你走之前

如果你想自己运行这些例子,你可以下载这个 Jupyter 笔记本

Twitter 上关注我,在那里我定期发布关于数据科学和机器学习的消息。

康特尼·赫格尔在 Unsplash 上拍摄的照片

使用 HuggingFace 的 GPT-2 模块生成体育文章

原文:https://towardsdatascience.com/sports-article-generation-with-huggingface-157314eadd9e?source=collection_archive---------40-----------------------

自然语言处理(NLP)将机器学习的应用扩展到了一个全新的领域。谁会想到统计模型可以应用于我们每天看到的文本,以产生商业见解和预测?在过去的两年里,NLP 经历了前所未有的复兴。

在 NLP 的先进方法中,人们对语言生成模型产生了极大的兴趣。Open AI 的 GPT-2 模型中的自动回归功能使得新的文本序列能够非常接近地代表人类思维的想法、说法和文字。这些基于变压器的神经网络模型显示出在提出令人信服的人类长篇文本方面的前景。

在这篇文章中,我们看看如何使用 HuggingFace 的 GPT-2 语言生成模型来生成体育文章。为了满足这个计算密集型任务,我们将使用来自 Spell.ml MLOps 平台的 GPU 实例。

泰勒·安德森Unsplash 上拍摄的照片

法术入门

如上所述,语言生成模型在计算上可能变得很昂贵,并且对于具有 CPU 的普通机器来说不可能处理它们。为了解决这个问题,我们使用了 Spell 的 GPU 支持的 Jupyter 笔记本。Spell 是一个强大的机器学习和深度学习的 MLOps 平台。它负责基础设施,从而使开发人员和企业能够专注于简单、快速和有组织地执行他们的机器学习模型。

使用 Spell 进行设置非常简单。只需访问https://spell.ml/并创建一个新账户。Spell 的每个新用户都可以获得 10 美元的免费使用积分。

在本练习中,我们将使用 UCI 机器学习库中的体育文章数据集。它包含 1000 个文本文件,每个文件中都有一篇体育文章。要上传文本文件,请在您的终端或命令提示符窗口中登录到 Spell,并导航到您解压缩文件的父文件夹,然后键入:

spell upload ‘Raw data’

这将把所需的文本文件上传到 Spell 的参考资料部分。在这里了解更多关于上传文件到法术资源的信息

要打开 Jupyter 笔记本,请登录到拼写 web 控制台,然后单击工作区>创建工作区。

给这个新工作区起一个您自己选择的名字,然后点击 Continue。

在下一个屏幕中,Spell 为您提供了多个选项来定义您想要在其中运行代码的环境。例如,在“机器类型”下,您可以根据您的用途和预算从各种 CPU 和 GPU 选项中进行选择。此外,您可以在设置 Jupyter 之前选择要安装的框架、环境变量和库。

就本项目而言,我们在机器类型下选择“V100 ”;朱庇特名下的笔记本。

在下一个屏幕中,让我们单击“启动服务器”开始。完成后,我们会发现一个 Jupyter 基础架构,类似于我们在本地机器上的基础架构。点击新建> Python3。

语言生成算法概述

让我们从 HuggingFace 安装“变形金刚”并加载“GPT-2”模型。

!pip install -q git+https://github.com/huggingface/transformers.git!pip install -q tensorflow==2.1import tensorflow as tf
from transformers import TFGPT2LMHeadModel, GPT2Tokenizer

tokenizer = GPT2Tokenizer.from_pretrained("gpt2")

# add the EOS token as PAD token to avoid warnings
model = TFGPT2LMHeadModel.from_pretrained("gpt2", pad_token_id=tokenizer.eos_token_id)

这两个对象让你使用预先训练的 GPT-2。一般来说,我们利用 GPT-2 或 transformers 的方式是指定一个输入字符串/短语,这基本上是您文章的开头。然后,算法预测下一组单词,使它们与给定的初始字符串一致。

让我们试着理解不同类型的算法,以及 GPT-2 如何能够得出最像人类的文本段落。

先说贪婪搜索算法,这是单词预测的简单化方法之一。基于初始字符串,该算法贪婪地搜索最可能的下一个单词。使用这个新字符串,它基本上是初始字符串加上预测的单词,预测下一个单词。这个过程不断重复,直到我们得到想要的单词数。这种方法的缺点是,在文本的几行/几个单词之后,单词开始重复。这是因为它错过了隐藏在低概率单词后面的高概率单词。

波束搜索通过每次保持预定义数量的假设,并最终选择具有总体最高概率的假设来减轻这种情况。但是经过几次实验,发现它仍然存在可重复性的问题。

语言生成的最佳算法之一是采样算法。使用采样的语言生成是不确定的,因为它根据条件概率分布随机选取下一个单词。但是,据观察,考虑到随机性,采样有时会产生听起来不像人类的文本段落。

解决这个问题的一个技巧是在给定前一个 i 单词的情况下,锐化下一个单词的预测分布。在锐化的同时,我们还在绘制随机样本;但是另外,我们增加了高概率单词被选取的可能性,并且降低了低概率单词被选取的可能性。

另一个转变是引入 Top-K 采样,其中 K 个最可能的下一个单词被过滤,并且概率质量在这 K 个下一个单词中重新分配。这一简单而强大的概念被纳入 GPT-2 模型,是其成功的主要原因之一。GPT-2 模型的另一个补充是原子核取样。该模型不是只从最可能的 K 个单词中取样,而是从累积概率超过预定义概率 p 的最小可能单词集中进行选择。这个特性的引入确保了单词集的大小可以根据下一个单词的概率分布动态地增加和减少。

使用 GPT-2 模型生成体育文本的示例

了解了它的内部工作原理后,让我们深入了解 GPT-2 模型的工作原理和性能。请注意,在这一点上,我们使用的是 GPT-2 模型,而不是使用我们之前下载的体育数据。当我们在下一节中微调模型时,我们将更多地研究如何使用这些数据。

下面的代码演示了 GPT-2 采用的采样技术。“input_ids”是指给予模型的初始字符串。通过将' do_sample '指定为 True,我们告诉模型使用采样技术。“最大长度”对应于物品的期望长度。‘top _ K’和‘top _ p’分别对应于 K 个单词和核概率 p。最后,我们将名为“num_return_sequences”的参数指定为 2。这使用相同的初始字符串生成了两个不同的段落,并给了我们一个选项来选择我们更喜欢的输出。

#initial string
input_ids = tokenizer.encode('Manchester City agree deal to sell Leroy Sane', return_tensors='tf')

# set seed to reproduce results. Feel free to change the seed though to get different results
tf.random.set_seed(0)

# set top_k = 50 and set top_p = 0.95 and num_return_sequences = 3
sample_outputs = model.generate(
    input_ids,
    do_sample=True, 
    max_length=100, 
    top_k=50, 
    top_p=0.45, 
    num_return_sequences=3 )

print("Output:\n" + 100 * '-')
for i, sample_output in enumerate(sample_outputs):
  print("{}: {}".format(i, tokenizer.decode(sample_output, skip_special_tokens=True)))

以下是输出结果:

0: Manchester City agree deal to sell Leroy Sane

The Gunners are ready to sign Leroy Sane, who has been on loan at Tottenham for the past three seasons, from Chelsea.

The 21-year-old, who is in his first season at the club, has scored five goals in 14 games for the Blues this season.

The former Arsenal and Chelsea striker has been a target for Chelsea since he joined from Southampton in January 2013.

The deal is

1: Manchester City agree deal to sell Leroy Sane

Manchester City have agreed a £30million deal to sell Leroy Sane to Manchester United for £30million.

The move was confirmed by City sources.

Sane, 24, has scored nine goals in 20 Premier League appearances for the club since joining from Manchester United in January 2014.

He has scored seven goals in 16 Premier League appearances for United since joining from Manchester United in January 2014.

定制 GPT-2:体育文章数据微调

看了输出,我们可以说 GPT-2 已经能够把一个有凝聚力的文本放在一起。然而,它产生的事实陈述缺乏准确性。还有,整个段落没有给我们很运动的感觉。为了解决这些问题,我们试图训练和微调 GPT-2 特别是体育文章,而不是使用它。我们将使用我们之前上传的运动数据集。

我们的第一项工作是将文章整理成一个文本文件。为了做到这一点,我们启动了一个新的 Jupyter 笔记本。单击“文件”选项卡,然后单击“添加装载”。转到上传并选择您刚刚上传的“原始数据”文件夹。

回到笔记本,执行下面的代码来阅读文本文件并整理它们。

mypath = 'Raw data/'
import os
from os import listdir
from os.path import isfile, join
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]article_list = ''
for i in range(len(onlyfiles)):
  if onlyfiles[i][-3:] == 'txt':
    try:
      with open('Raw data/' + onlyfiles[i], 'r') as file:
        data = file.read()
      article_list = article_list + '\n' + data
    except:
      pass

完成后,我们需要使用字符串包中的' printable '函数过滤掉所有不属于 ASCII 的字符。

import string
article_list_str = ''.join(filter(lambda x: x in string.printable, article_list))

一旦数据达到所需的格式,让我们继续构建模型。我们安装“transformers”包并导入所需的包。

!pip install transformersimport logging
import os
import pickle
import random
import torch
import torch.nn as nn
import transformers
from torch.utils.data import DataLoader, Dataset, RandomSampler, SequentialSampler
from transformers import (
    GPT2Config,
    GPT2LMHeadModel,
    GPT2PreTrainedModel,
    GPT2Tokenizer,
    PreTrainedModel,
    PreTrainedTokenizer,
)

MODEL_CLASSES = {"gpt2": (GPT2Config, GPT2LMHeadModel, GPT2Tokenizer)}

logger = logging.getLogger(__name__)

接下来,我们定义一个类“SportsData”来微调体育数据集并获取其中的令牌。

class SportsData(Dataset):
    def __init__(
        self,
        tokenizer: PreTrainedTokenizer,
        #file_path: str,
        block_size=512,
        overwrite_cache=False,
    ):
        #assert os.path.isfile(file_path)

        block_size = block_size - (
            tokenizer.max_len - tokenizer.max_len_single_sentence
        )

        # change if args are added at later point
        cached_features_file = os.path.join(
           "gpt2" + "_" + str(block_size) + "_file.txt" 
        )

        if os.path.exists(cached_features_file) and not overwrite_cache:
            logger.info(
                f"Loading features from your cached file {cached_features_file}"
            )
            with open(cached_features_file, "rb") as cache:
                self.examples = pickle.load(cache)
                logger.debug("Loaded examples from cache")
        else:
            logger.info(f"Creating features from file")

            self.examples = []

            text = article_list_str
            tokenized_text = tokenizer.convert_tokens_to_ids(tokenizer.tokenize(text))

            for i in range(0, len(tokenized_text) - block_size + 1, block_size):
                self.examples.append(
                    tokenizer.build_inputs_with_special_tokens(
                        tokenized_text[i : i + block_size]
                    )
                )

            logger.info(f"Saving features into cached file {cached_features_file}")
            with open(cached_features_file, "wb") as cache:

                pickle.dump(self.examples, cache, protocol=pickle.HIGHEST_PROTOCOL)

    def __len__(self):
        return len(self.examples)

    def __getitem__(self, item):
        return torch.tensor(self.examples[item], dtype=torch.long)

最后,我们启动定制模型的训练并保存模型。

device = 'cpu'
if torch.cuda.is_available():
    device = 'cuda' tokenizer = GPT2Tokenizer.from_pretrained('gpt2-medium')
model = GPT2LMHeadModel.from_pretrained('gpt2-medium')
model = model.to(device)

dataset = SportsData(tokenizer= tokenizer )
article_loader = DataLoader(dataset,batch_size=1,shuffle=True)

BATCH_SIZE = 1
EPOCHS = 1
LEARNING_RATE = 0.0002
WARMUP_STEPS = 5000

from transformers import AdamW, get_linear_schedule_with_warmup

model = model.to(device)
model.train()
optimizer = AdamW(model.parameters(), lr=LEARNING_RATE)
scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=WARMUP_STEPS, num_training_steps=-1)
script_count = 0
sum_loss = 0.0
batch_count = 0

for epoch in range(EPOCHS):
    print(f"EPOCH {epoch} started" + '=' * 30)
    for idx,script in enumerate(article_loader):
        outputs = model(script.to(device), labels=script.to(device))
        #outputs = torch.tensor(tokenizer.encode(script)).unsqueeze(0).to(device) 
        loss, logits = outputs[:2]                        
        loss.backward()
        sum_loss = sum_loss + loss.detach().data

        script_count = script_count + 1
        if script_count == BATCH_SIZE:
            script_count = 0    
            batch_count += 1
            optimizer.step()
            scheduler.step() 
            optimizer.zero_grad()
            model.zero_grad()

        if batch_count == 200:
            model.eval()
            print(f"sum loss {sum_loss}")
            sample_outputs = model.generate(
                                    bos_token_id=random.randint(1,30000),
                                    do_sample=True,   
                                    top_k=50, 
                                    max_length = 1000,
                                    top_p=0.95, 
                                    num_return_sequences=1
                                )

            print("Output:\n" + 100 * '-')
            for i, sample_output in enumerate(sample_outputs):
                  print("{}: {}".format(i, tokenizer.decode(sample_output, skip_special_tokens=True)))

            batch_count = 0
            sum_loss = 0.0
            model.train()output_dir = 'Raw data/'

from transformers import WEIGHTS_NAME, CONFIG_NAME
output_model_file = os.path.join(output_dir, WEIGHTS_NAME)
output_config_file = os.path.join(output_dir, CONFIG_NAME)

torch.save(model.state_dict(), output_model_file)
model.config.to_json_file(output_config_file)
tokenizer.save_vocabulary(output_dir)

现在,我们已经准备好了微调的模型,我们导入它并测试它如何针对我们之前使用的相同输入字符串工作。

model = GPT2LMHeadModel.from_pretrained(output_dir)
tokenizer = GPT2Tokenizer.from_pretrained(output_dir)

input_ids = tokenizer.encode('Manchester City agree deal to sell Leroy Sane', return_tensors='pt')

sample_outputs = model.generate(
                        input_ids= input_ids,
                        do_sample = True,
                        #num_beams= 5,
                        max_length = 100,
                        top_k = 50,
                        top_p=0.85, 
                        num_return_sequences=1
                    )

print("Output:\n" + 100 * '-')
for i, sample_output in enumerate(sample_outputs):
      print("{}: {}".format(i, tokenizer.decode(sample_output, skip_special_tokens=True)))

以下是输出结果:

Output:
----------------------------------------------------------------------------------------------------
0: Manchester City agree deal to sell Leroy Sane to Liverpool

Leroy Sane was among three players who were sold this summer and Liverpool boss Brendan Rodgers admitted he felt the need to replace the former Manchester City winger.

"We sold four players last year and I know I had to get another player in to improve our squad," Rodgers told Sky Sports News HQ.

"We had to sell players and a few of them we did but it was Leroy Sane.

我们注意到这段文字不仅听起来像人类,而且更流畅。经理的引语尤其给了这段文字一种更加真实的感觉,使它看起来非常类似于一篇真正的手写文章。请注意,增加“max_length”的值将允许我们获得更长的文本。

请注意,事实仍然不是完全正确的,但这些可以用我们的知识迅速纠正。重要的是,我们已经能够摆脱手工书写的过程。只要运行这个命令,编辑一些可能存在的事实差异,瞧,你就完成了。

要试用本教程,请使用此链接登录 Spell 的 MLOps 平台,并在创建帐户后获得 10 美元的免费 GPU 点数。另外,请随时在这个链接上发布您的问题和疑问。

参考文献:

https://colab . research . Google . com/github/hugging face/blog/blob/master/notebooks/02 _ how _ to _ generate . ipynb # scroll to = MQ huo 911 wft-

https://github . com/cdpierse/script _ buddy _ v2/tree/master/script _ buddy

体育参考 API 简介

原文:https://towardsdatascience.com/sports-reference-api-intro-dbce09e89e52?source=collection_archive---------15-----------------------

如何建立令人敬畏的体育数据集

照片由 JC GellidonUnSplash 拍摄

这是我每周体育主题系列的第二部分。每周,我都会展示以体育为中心的数据科学应用。

上周,在我的文章战胜困难中,我展示了如何使用体育参考 API 建立一个关于 NBA 球队的数据集,然后使用该数据集找到那些超过常规赛积分差距的球队。本周我想深入探讨如何使用这个 API 为您的项目构建全面的数据集。

当我在网上浏览体育数据项目时,我看到很多人求助于从不同的来源下载表格,手动或通过网络搜集。然后通常需要将这些表连接起来,这是一个非常繁琐的过程,高度依赖于数据的质量。以这种方式构建大型数据集可能需要几天甚至几周的时间。不要成为那个人!令人欣慰的是,Sports Reference 的出色工程师构建了一个 API,可以方便快捷地访问 Sports Reference 关于 MLB、NBA、NFL、NHL、NCAAF、NCAAB 和世界各地许多足球联盟的海量数据库。

要开始使用 Sports Reference API,您需要使用pip install sportsreference像安装任何其他 python 包一样安装它。

Sports Reference API 的基本思想是使用模块来实例化类对象,这些类对象包含类属性中的相关数据。所有运动中常见的基本模块是团队、时间表、比分、花名册和运动员。NCAAB 和 NCAAF 也有排名和会议的模块。Football(英式足球)使用稍微不同的命名约定,但是一般的 API 结构是相同的。在后端,这些模块通过发送 HTTP 请求从 Sports Reference 的服务器上查询相关数据来创建类对象。没有必要担心把 100 年的棒球赛比分数据塞满你的硬盘(尽管我认为如果你真的想的话,你可以用 API 来做这件事)。

在接下来的部分中,我将展示 NBA API 的特性。如果你对另一项运动感兴趣,我鼓励你继续下去,因为基本的特征和过程本质上是一样的。该代码旨在在 Jupyter 笔记本上执行,但也可以很容易地适用于其他环境。

让我们从团队模块开始。以下命令创建 Teams 类的一个实例,并打印出它的所有方法和属性。

from sportsreference.nba.teams import Teamsteams2020 = Teams(year = '2020')
print(dir(teams2020))

团队类方法和属性

Teams 类的唯一公共属性是“dataframes”。该属性是一个 30 x 47 的数据帧,其中 2020 年的每个 NBA 球队都由一行表示。栏目包括得分、罚球次数、对手进攻篮板等。“dataframes”属性通常是访问高级团队信息的最简单方法。然而,也可以使用 Teams 类的实例作为迭代器。例如,下面的代码打印出每个队的名称和得分。团队数据框架的每一列也可以作为团队属性以这种方式访问。

for team in teams2020:
    print(team.name, 'Points Scored:', team.points)

日程安排

顾名思义,Schedule 模块用于访问关于团队日程的信息。这非常有用,因为 Teams 模块不包含关于赢/输记录的信息。让我们为密尔沃基雄鹿队 2019 赛季创建一个类的实例,并使用它返回一个包含每场比赛基本信息的数据帧。

from sportsreference.nba.schedule import Schedulemil2019 = Schedule('MIL', year = '2019')
mil2019.dataframe.head()

密尔沃基雄鹿队 2018-2019 赛季的前 5 场比赛

注意 boxscore_index 列,这些 boxscore 索引可以与 boxscore 类一起使用,以获得更详细的游戏信息。与 Teams 模块类似,Schedule 类的实例也可以用作迭代器。最后,Schedule 类有一个“dataframe_extended”属性,它返回一个 dataframe,其中每一行都是 Boxscore 类的一个实例。这为每个游戏提供了更丰富的数据,但由于构建了许多 Boxscore 类的实例,每个实例都需要单独的服务器请求,因此处理时间要长得多。

Boxscore

让我们仔细看看密尔沃基雄鹿队 2019 年东部决赛第一场对阵多伦多猛龙队的比赛。从我们的时间表数据框架中,我们可以发现这场比赛的 boxscore_index 是“201905150MIL”。

from sportsreference.nba.boxscore import Boxscoregame_data = Boxscore('201905150MIL')

通过调用print(dir(game_data)),我们可以看到 Boxscore 类比 Teams 和 Schedule 类有更多的属性。命令game_data.dataframe将把这些属性编译成数据帧。

Boxscore 数据框架

嗯,这并不完全是我们习惯看到的 boxscore 类型,但这里仍然有很多有用的数据。为了构建更类似于传统 boxscore 的东西,我们需要使用 Boxscore 类属性‘home _ players’和‘away _ players’。通过执行game_data.home_players,我们看到这返回了一个 BoxscorePlayer 类对象的列表。此外,我们可以执行print(dir(game_data.home_players[0]))来查看这个子类所有可用方法和属性的列表。由于 BoxscorePlayer 类具有“dataframe”属性,因此可以通过连接每个单独的 BoxscorePlayer 数据帧来构建更传统的 boxscore 数据帧。下面的代码就是这样做的。

home_df = game_data.home_players[0].dataframefor player in game_data.home_players[1:]:
    home_df = pd.concat([home_df, player.dataframe], axis = 0)home_df['name'] = [x.name for x in game_data.home_players]
home_df.set_index('name', inplace = True)
home_df

2019 年密尔沃基雄鹿队主场迎战多伦多猛龙队 ECF 队第一场比赛的比分

通过检查该数据帧的列,我们可以看到,我们不仅获得了所有传统的 boxscore 统计数据,还获得了一些高级统计数据。这总结了 Boxscore 模块的基本用例。

花名册

花名册模块用于获取球队球员花名册的信息。让我们为 2007 年“我们相信”的勇士队创建一个花名册类的实例。

from sportsreference.nba.roster import Rostergsw2007 = Roster('GSW', year = '2007')

正如我在上面展示的,我们可以检查这个类对象的方法和属性,并看到只有一个属性,“players”。与我们刚刚为 Boxscore 模块查看的“home_players”属性非常相似,“players”属性是 Player 类实例的列表。我们稍后将更深入地讨论播放器模块。现在,让我们使用下面的代码片段简单地打印出名册上每个球员的姓名和球员 id。

for player in gsw2007.players:
    print(player.name, ':', player.player_id)

由于花名册类为花名册上的每个球员创建一个球员类的实例,并且构造每个实例需要向 Sports Reference 的服务器发出请求,所以使用花名册类来完成这样一个简单的任务会非常慢。如果我们想用这种方法打印某个赛季联盟中每个球员的名字,我们可能需要等待 5 分钟以上来创建所有球员类实例。作为替代,我们可以使用关键字参数slim = True调用花名册类。这将创建一个花名册类的实例,其中“players”属性只是一个字典,键是 player _ ids,值是名称。下面的代码演示了这一功能。

gsw2007slim = Roster('GSW', year = '2007', slim = True)
gsw2007slim.players

2006-07 赛季“我们相信”金州勇士队的名单

运动员

不足为奇的是,玩家模块用于获取单个玩家的信息。参考 API 文档,我们可以看到播放器模块包含一个抽象类 AbstractPlayer,它由我们已经在 Boxscore 和花名册模块中看到的 BoxscorePlayer 和 Player 类继承。因此,获取球员数据的正确方式实际上是通过花名册和 Boxscore 模块。让我们使用为拜伦·戴维斯提取的 player_id 来获取他的统计数据。

from sportsreference.nba.roster import Playerbaron_davis = Player('davisba01')
baron_davis.dataframe

拜伦·戴维斯职业生涯和赛季统计

这段简单的代码为我们提供了拜伦·戴维斯职业生涯和每个赛季的 89 列数据。除了“dataframe”属性及其所有相关列之外,Player 类还包含诸如“birth_date”、“height”、“weight”、“nationality”等属性。此外,可以使用命令baron_davis.points查询单个职业统计数据,例如分数。类似地,我们可以使用命令baron_davis('2007').points查询拜伦·戴维斯在 2006–07 赛季的总积分。根据用例,以这种更直接的方式访问玩家统计数据可能更容易,并且避免处理数据帧。

创建 NBA 球员数据集

在这篇文章的结尾,我将演示如何使用球队、花名册和球员模块来构建一个庞大的 NBA 球员统计数据集。这个数据集将会在我的下一篇文章中使用,我将会在这篇文章中关注 NBA 职业生涯中球员的发展和最终的衰落。

第一步是导入相关的模块并定义一个函数,该函数创建一个带有一些我手动定义的额外字段的播放器数据帧。最重要的是球员在各自赛季的 1 月 1 日的年龄。

# Function to get player info from Player class object.def get_player_df(player):

    # helper function to get player age during each season.
    def get_age(year, bd):
        if year[0] == "Career":
            return None
        else:
            year_dt = datetime(int(year[0][0:4]) + 1, 1, 1)
            age_years = relativedelta(year_dt, bd).years + 
                        relativedelta(year_dt, bd).months/12
            return age_years

    # helper function to get year for each row and denote
    # rows that contain career totals.
    def get_year(ix):
        if ix[0] == "Career":
            return "Career"
        elif ix[0] == "1999-00":
            return "2000"
        else:
            return ix[0][0:2] + ix[0][-2:]

    # get player df and add some extra info
    player_df = player.dataframe
    player_df['birth_date'] = player.birth_date
    player_df['player_id'] = player.player_id
    player_df['name'] = player.name
    player_df['year'] = [get_year(ix) for ix in player_df.index]
    player_df['id'] = [player_id + ' ' + year for player_id,
                       year in zip(player_df['player_id'],
                       player_df['year'])]
    player_df['age'] = [get_age(year, bd) for year,
                        bd in zip(player_df.index,
                        player_df['birth_date'])]
    player_df.set_index('id', drop = True, inplace = True)

    return player_df

接下来,我使用上面定义的函数来收集过去 20 年中在 NBA 打球的每个球员的整个职业生涯的球员数据。数据分为两个数据框架,一个在季节级别聚合数据,另一个在职业级别聚合数据。这是通过使用球队和花名册模块来迭代从 2000 年到 2020 年的每个 NBA 花名册来完成的。注意,我使用关键字变量slim = True调用花名册类。如果没有这个设置,我将不得不为一个 NBA 球员的每个赛季创建一个球员实例,增加大约 10 倍的运行时间。

# initialize a list of players that we have pulled data for
players_collected = []
season_df_init = 0
career_df_init = 0
season_df = 0
career_df = 0# iterate through years.
for year in range(2020, 1999, -1):
    print('\n' + str(year))

    # iterate through all teams in that year.
    for team in Teams(year = str(year)).dataframes.index:
        print('\n' + team + '\n')

        # iterate through every player on a team roster.
        for player_id in Roster(team, year = year,
                         slim = True).players.keys():

            # only pull player info if that player hasn't
            # been pulled already.
            if player_id not in players_collected:

                player = Player(player_id)
                player_info = get_player_df(player)
                player_seasons = player_info[
                                 player_info['year'] != "Career"]
                player_career = player_info[
                                player_info['year'] == "Career"]

                # create season_df if not initialized
                if not season_df_init:
                    season_df = player_seasons
                    season_df_init = 1

                # else concatenate to season_df
                else:
                    season_df = pd.concat([season_df,
                                   player_seasons], axis = 0)

                if not career_df_init:
                    career_df = player_career
                    career_df_init = 1

                # else concatenate to career_df
                else:
                    career_df = pd.concat([career_df,
                                   player_career], axis = 0)

                # add player to players_collected
                players_collected.append(player_id)
                print(player.name)

最后一步是将生成的数据帧保存为您喜欢的文件格式。

season_df.to_csv('nba_player_stats_by_season.csv')
career_df.to_csv('nba_player_stats_by_career.csv')

总之,这个脚本应该在大约 30 分钟内编译并保存您的数据集。如果你理解了最后一节的所有代码,那么你应该已经准备好开始使用体育参考 API 构建你自己的数据集了。

下周当我使用这个数据集分析 NBA 职业生涯轨迹时,请务必关注我的文章,也请查看我上周的文章战胜困难

我希望你喜欢这本书,保重。

运动数据—分析我的 Strava 活动

原文:https://towardsdatascience.com/sporty-data-analyzing-my-strava-activities-c34aab402069?source=collection_archive---------46-----------------------

我的斯特拉瓦体育活动的编辑分层立面图。

作为一个数据和体育迷,我在过去已经多次使用过 T2 的 Strava API。例如,制作精美的绘图或构建交互式仪表盘。最近,我还研究了我的综合体育活动数据。在这个博客中,我将分享我的一些发现。

制造奇特的情节

你可以用不同的方式绘制你的体育活动数据。例如,分层高程图,就像这篇博客标题中使用的那样,或者像下面所示的小倍数(归功于马库斯·沃尔茨)。对我来说,制作花哨的情节只是享受我的活动的结果(和生成的数据)的一种有趣的方式😁。

我所有体育活动的地图都被绘制成小倍数。

构建交互式仪表板

使用闪亮的,我制作了一个交互式仪表盘,它使我能够更详细地研究不同功能的相关性。比如距离和平均速度的关系。对于跑步来说,这显然可以归结为:跑步时间越长,你会越慢,但是用交互式图表分析这一点有助于理解慢了多少。这可以作为建立未来起搏方案的参考。我还在仪表板中放入了我的活动热图。除了美学,它帮助我刷新了我去跑步和骑车的地方的记忆(就像一本相册😉).我在我的城市(荷兰埃因霍温)周围的活动热图如下所示。而且,正如你清楚地看到的,当你骑自行车时,你会走得更远🚴‍♀️.

我在我的城市(荷兰埃因霍温)附近跑步(红色)和骑车(蓝色)的热图。

汇总数据

这些年来,我喜欢不同的运动。作为主要运动,我以前打曲棍球,之后我开始更频繁地打壁球。我总是将这些运动与跑步和骑自行车结合起来。在过去的一年里,当我决定参加少女峰马拉松比赛时,我把跑步作为一个优先事项。随着跑步成为我最近的主要运动,我很想看看过去几年的表现趋势。为了找到答案,我研究了不同变量随时间的分布。下面列出了一些图表和我的一些观察。有些是我预料到的,有些是我以前没想过的。

我的平均速度密度曲线。

首先,平均速度曲线变平了💨。
这可能是因为训练中的变化较多,例如恢复跑和门槛跑。我特别喜欢 2020 年密度曲线的右侧,显示我更经常达到更高的平均速度😁。

我的平均心率密度曲线。

其次,我的平均心率下降了不少💗
我就知道(max。)心率随着年龄的增长而降低。而且,我还记得“回到过去”(< 2017)每次跑步都感觉像一场比赛,而现在更多的是放松。这个图表清楚地说明了这两者😉。

我的距离密度曲线。

第三,更多的距离变化。回顾这篇博客的第一个情节,你可以看到在开始的时候我喜欢一遍又一遍地走同样的路线。缺乏变化并没有困扰我,因为我并不经常跑步。现在,我试着变得更多样。不仅仅是速度不同,还有路线和距离。一开始,我决定要么进行“短”跑,要么进行“长”跑,结果是+/- 5 公里或 10 公里。如今,它可以是“短”或“长”跑,基本上意味着 5k 或以上的任何东西。

我的节奏密度曲线。

第四,更多步骤🏃‍♀️! 看着抑扬顿挫密度图很明显我的抑扬顿挫上去了。高节奏有多种好处,所以我很高兴看到这种增长趋势💪。

每种鞋的平均速度密度。

第五个也是最后一个,选哪双鞋👟。在 Strava 上,你可以追踪你的装备。提醒我买双新的很有用(尽管我倾向于在旧的还没穿破的时候就买双新的)😬).接下来,你可以用数据来看看鞋的类型是如何影响速度的。我通常和 Asics Kayano 一起跑步。图中有不同的型号,当我没有跟踪时,我也穿着这双鞋。当我想跑得更快更短时,我倾向于切换到 New Balance Vongo 。对于试车,我使用新百伦耶罗(这里也有两种不同的型号)。在小路上,我通常会走得慢一点,如图所示!

这就是对体育和数据都感兴趣时会发生的事情😎。希望您获得了一些有趣的见解,或者受到启发,以类似的方式分析您的数据!如前所述,我使用了 Strava API 并使用了 rStrava 包来访问数据。

在 AWS 上进行深度学习,成本很低

原文:https://towardsdatascience.com/spot-connect-with-aws-f95c964b0e92?source=collection_archive---------54-----------------------

PYTHON 库简介

python 的点连接模块简介

蒂姆·莫斯霍尔德Unsplash 上拍摄的照片

S pot-connect 是一个 python 模块,适用于任何正在寻找使用云计算的简单方法的程序员。

最初是为机器学习项目开发的, spot-connect 使得使用 Amazon Web Services 和 EC2 实例变得很容易。

AWS 提供的服务和设置帐户的步骤包含在旧文章的了解 AWS设置您的 AWS 帐户部分。

如果您计划尝试点连接,并且尚未安装和配置 AWS 命令行( *pip install awscli* *aws config* ),或者如果您尚未为 AWS 帐户创建访问密钥,请阅读参考章节。

spot-instance 运行在 AWS 无法出租的硬件上。从来没有足够的需求来出租亚马逊的所有机器,所以他们以原价的一小部分出租多余的容量,以弥补他们的一些成本。

唯一的问题是,如果需求增加,你可能会被踢出实例,这在高端实例中更有可能发生(在你被踢出之前,你会得到 2 分钟的警告,以便你可以保存/转移你的工作)。

定点连接模块允许您创建和管理定点实例。它可以从命令行、脚本或 jupyter 笔记本中使用。它专注于管理 EC2 资源(AWS 的云计算服务),但提供了额外的功能来与弹性文件系统和 S3 等存储选项进行交互。

接下来是如何使用该模块的演练。

装置

pip install spot-connect 

使用 python 3。*

命令行用例

一旦你安装了模块,你可以在提示符下使用它

spot_connect -n instance_1 -p t2.micro -a True

在命令提示符下使用 spot_connect 还可以让您将提示符直接连接到实例。

使用 spot-connect 启动一个实例,然后使用链接提示符(个人屏幕截图)直接在实例上运行 linux 命令。

上面的例子用“t2.micro”概要文件创建了一个名为“instance_1”的实例。它连接到一个名为“stockefs”的弹性文件系统,并将提示符连接到新创建的实例。

gif 已经被加速了。启动一个实例通常需要几分钟或更长时间,这取决于规格。 如果您使用现有实例的名称进行连接,您将立即重新连接到该实例。

命令提示符下对spot_connect最有用的参数是:

  • 名称 ( -n):现货实例的名称 *****
  • 配置文件 ( -p):配置文件的名称(指定实例类型、最高投标价格等的预设配置……更多信息请见下文)。 ******
  • 文件系统 ( -f):要连接的弹性文件系统。如果它不存在,将创建一个(不必与实例名相同)。
  • 脚本 ( -s):一旦准备好就在实例上运行的脚本(如果在实例上使用 linux 操作系统,这必须是一个 bash 脚本……)。
  • 上传 ( -u):上传你想要的任何文件到实例中(对于小上传有用,否则使用 S3 传输,更多内容见下文)。
  • 远程路径 ( -r):上传将上传到实例上的这个路径。
  • 主动提示 ( -a):如果为真,将提示连接到实例(只能从命令提示符完成)。
  • 实例概要 ( -ip):实例的访问角色(不同于概要)。这授予实例访问其他 AWS 资源的权限,如 S3 。 ******

***** = 必需 ****** = 仅在创建实例时必需(不重新连接)

能够将提示直接连接到实例对于排除脚本故障或运行任何其他检查非常有用。

选择实例类型

要用 spot-connect 启动一个实例,您只需给该实例一个名称和一个概要文件

概要文件是预设的实例规范,它定义了诸如实例类型最高投标价格、区域和其他连接选项等内容。这些可以在安装模块时随模块一起下载的 profiles.txt 文件中找到。

可用实例类型、 定价 、图片/AMI id 均随地区变化。Spot-connect 附带了从 AWS 网站获取的区域实例数据和 AMI id 数据。

您可以使用点连接模块更改默认的配置文件设置:

from spot_connect import sutils 
sutils.reset_profiles()

reset_profiles()方法将向您显示一个区域列表,然后是一个 ami 列表,每次都要求您选择一个。然后您的本地副本 profiles.txt 将被更新以使用该区域和 AMI。

笔记本使用案例

对于笔记本和脚本来说,SpotInstance类相当于在命令提示符下使用spot_connect

from spot_connect.spotted import SpotInstance
instance = SpotInstance('instance1', profile='t2.micro', price=0.01)

该命令创建或连接到“实例 1”实例。一个 SpotInstance 对象可以用与spot_connect 相同的参数进行实例化。此外, SpotInstance 还允许您指定配置文件参数,允许您覆盖像price这样的配置文件中的任何默认设置,这将允许您直接提交自定义的最高出价。

使用SpotInstance您可以下载和上传文件,

instance.upload('test_run.py') # script creates a test.txt file

运行脚本,

instance.run('run_test.sh') # bash script is: "python test_run.py\n" 

直接在实例上运行命令,

instance.run('ls', cmd=True) # use the cmd option to pass commands

甚至终止实例。

instance.terminate()         # terminate the instance 
instance.refresh_instance()  # update the instance status 

使用run(<command>, cmd=True)方法直接在实例上运行命令。但是,请注意,这些命令总是在主目录中运行,这意味着在一次运行中更改目录并不意味着您在下一次运行中从该目录开始。

幸运的是,您可以使用\n一起运行连续的命令

instance.run('cd**\n**rm test.txt', cmd=True) 

这可以很容易地将复杂的脚本存储为可以在 python 中直接使用的函数。例如:

def transfer_script(bucket, efs_path):
    '''Download S3 data and place it in the EFS folder''' # The first command is the download
    # Use "nohup" to run the job in the background of the instance

    script ='nohup aws s3 sync '+bucket+' '+efs_path+'**\n**'

    # Use "curpid=$!" to get the job-id for the sync job

    script +='curpid=$!**\n**' # Three part command in linux 
    # 1) In the background, wait for all jobs to finish
    # 2) when finished, run command to shutdown the instance, 
    # 3) place all the output from the sync job in transfer.txt

    script +="nohup sh -c 'while ps -p $0 &> /dev/null;
              'do sleep 10; done && sudo shutdown -h now' 
              $curpid &> transfer.txt &**\n**" 

    return script script = transfer_script("s3://data", "/home/ec2-user/efs/")
instance.run(command, cmd=True)

实例管理器

启动一个实例很好,但是 spot-connect 的目标是便于在多个实例之间分配工作负载。这就是InstanceManager类出现的原因。

实例管理器允许您一次启动并跟踪多个实例。它还提供了在实例和 S3 之间传输文件、运行分布式工作负载等功能。

下面是如何跨多个实例组织执行任务的示例:

1。 用efs='data'实例化一个实例管理器,这样管理器创建的实例自动链接到名为“数据”的 EFS

from spot_connect.instance_manager import InstanceManager 
im = InstanceManager(efs='data')

2。使用launch_instance方法启动一个实例。用im创建的实例可以在im.instances中找到。

im.launch_instance('monitor', profile='t2.micro')

3。向clone_repo方法提交“monitor”实例,以将项目的 Github repo 克隆到 efs 中。

im.clone_repo(im.instances['monitor'], 
   '[https://github.com/FlorentF9/DeepTemporalClustering.git](https://github.com/FlorentF9/DeepTemporalClustering.git)',
              directory='/home/ec2-user/efs/') # default path

4。运行cd efs命令,然后运行mkdir results命令,在 EFS 中创建一个结果文件夹。

im.instances['monitor'].run('cd efs**\n**ls', cmd=True)

5。设计一个方法,它可以接受每个作业的参数,并返回一个可以在每个实例上运行的脚本。

def runDTC(n_clusters):
   '''Train a DTC model on the package data with n_clusters''' # Change the directory to the project directory 
   script = 'cd /home/ec2-user/efs/DeepTemporalClustering/**\n**' # Train the DTC algo in the background of the instance 
   script+= 'nohup python DeepTemporalClustering '+str(n_clusters)+' **--savedir** **/home/ec2-user/efs/results\n**' # Get the job ID for the training job 
   script +='curpid=$!**\n**' # Shut down the instance once that job is done  
   script +="nohup sh -c 'while ps -p $0 &> /dev/null; 'do sleep 10; done && sudo shutdown -h now' $curpid &> transfer.txt &**\n**" return script 

6。为一系列“n_cluster”值训练模型。使用run_distributed_jobs方法在单独的实例上训练每个模型。

# Get a list of scripts to run, one for each instance 
scripts = [runDTC(i) for i in range(10, 61, 10)]im.run_distributed_jobs(
   "DTC",               # prefix given to each instance name 
   len(scripts),        # number of instances to launch 
   scripts,             # the scripts to run on each instance 
   'p2.xlarge'          # use this instance type for each instance 
)

7。检查实例的状态,等待直到所有实例都终止(完成它们的作业)。

In [7]: im.show_instances()
Out[7]: {'monitor': 'running',  
         'DTC_1': 'terminated',  
         'DTC_2': 'terminated',  
         'DTC_3': 'terminated',  
         'DTC_4': 'shutting-down',  
         'DTC_5': 'running',  
         'DTC_6': 'running'}

8。使用instance_s3_transfer方法将结果上传到 S3。必须提供实例配置文件才能让实例访问 S3。

im.instance_s3_transfer("/home/ec2-user/efs/results",
                        "s3://bucket_data", 
                        "s3access")   # my instance profile

在 S3,您可以直接通过控制台或使用命令提示符下的awscli下载结果:

aws s3 sync "s3://bucket_data/results" "<local folder>"

在结束本节之前,有必要介绍一些有用的 bash 命令:

  • nohup:当放在命令的开头时,即使用户退出,命令也会完成。
  • &:当放置在您的命令之后时,会将命令发送到后台,以便您可以继续使用提示。
  • > something.txt:在命令的末尾,将命令生成的任何输出指向所需的文本。在.txt后添加另一个&来隐藏整行并继续使用提示。

总结

这就是 spot-connect 模块的基本功能。这里是这个项目的 Github 回购,在这里你可以找到一个走查笔记本(仍然需要更新以包括本文中的 DTC 示例)以及我用来从 AWS 收集价格和图像数据的笔记本。

我根据自己的时间表开发模块,但是本文中介绍的基本功能对我来说只是暂时的停止。非常欢迎投稿。以下是我认为值得追求的特性:

  • 2 分钟关机警告处理
  • 使用 boto3 的 AWS spot-fleet 功能
  • 扩展存储管理功能(S3、EFS、EBS)
  • 提供运行和协调数据同步代理的能力
  • 提高代码的整体优雅性

感谢阅读!

😃

使用 Python 中的 4 个免费 API 发现内容创意

原文:https://towardsdatascience.com/spot-content-ideas-using-4-free-apis-in-python-e18f53ef81af?source=collection_archive---------32-----------------------

使用 Google Trends、Reddit、Twitter 和新闻 API 创建内容查找器网站的指南

作者图片

那些每天都在写作,还能产出很棒内容的作家背后有什么秘密?

每个作家都有自己独特的方式,根据自己的个人品味来寻找话题。虽然每个作家都试图写一些他们关心的东西,但获得读者的关注是必不可少的。

作为一名数据分析师,我很好奇用我的技能解决这个问题的可能性。我想知道任何成功的作家是如何寻找主题的。尤其是当他/她没有主意的时候,他们找到话题的方法是什么。

9x 媒体顶级作家 Esat Artug 写了一篇文章,分享了他如何寻找话题的见解。他使用免费的可用资源,如谷歌趋势,亚马逊,回答公众,看看什么是趋势和人们如何互动。

我喜欢他的方法,因为它既简单又非常有效。因为你会看到热门趋势和搜索,它保证,你只选择人们感兴趣的话题。

身处数据科学领域,我立刻知道我可以让它变得更好、更简单。有很多像 Google,Reddit,Twitter 这样的资源提供免费的 API 来访问数据。如果你能在一个屏幕上搜索并看到所有的数据,而不是一个一个地浏览,那将会节省很多时间

在这篇文章中,我将通过一步一步的方法来实现它。让我们深入研究一下。

四个 API:

在选择来源时,我决定记住两件事。

  1. 应该能得到最新的结果。
  2. 没有高级服务

因为这个想法是为了帮助作者找到内容主题,只有当他能看到当前流行的主题时才有帮助。这意味着绝对没有过时的数据。

例如,如果作者想在自然语言处理中搜索一个主题,并通过 GPT -2 得到推荐。这对一个作家来说没有太大的帮助,因为 GPT-3 的高级版本已经发布了。

因为我这么做主要是作为一个研究目的,所以我绝对不想花任何钱去获取这些数据。此外,API 提供的自由层数据应该足够了。

图片作者来自 Canva (Logo 来源 GoogleRedditTwitter )

我为这个项目选择的 4 个 API 是

  1. 谷歌趋势
  2. Reddit
  3. 推特
  4. 新闻 API

逐步实施

我使用 Python 提取和清理数据,并使用 Streamlit 创建了一个网站,用户可以使用关键字搜索并查看输出。

谷歌趋势:

我使用 pytrends 库访问 Google trends 数据。它的一个好处是你不必请求一个特殊的访问密钥。

确保使用pip install pytrends安装库,并使用from pytrends.request import TrendReq导入库

你需要通过调用TrendReq()来初始化它

从文档中我找到了related_topics()related_queries()trending_searches()函数,我认为我可以在这个项目中使用它们。

但是related_topics()只返回主题名称,这没有多大帮助。

related_topics()的示例输出(图片由作者提供)

trending_searches()返回当前热门话题,这同样没有太大帮助,因为我们正试图根据关键词查找热门搜索。

related_queries()返回基于输入关键字的热门和热门搜索的 json 输出。它给出了这个项目所需的准确信息。请参见下面的示例输出,了解如何提出对“机器学习”关键词搜索的查询。

related_queries()的输出(图片由作者提供)

你可以在下面看到从谷歌趋势获取数据的要点

Reddit:

Reddit 的工作基于被称为 subreddit 的标签或页面,人们只能发布 subreddit 相关的主题。当搜索最新的帖子时,请确保使用流行的子编辑名称进行搜索,以获得最佳结果。

访问键:与 Google trends for Reddit 数据不同,您需要获得特定于用户的“访问键”。你可以使用这个链接 reddit 应用来创建你自己的访问密钥令牌。

一旦完成,前往 Python 编辑器,确保你安装了 PRAW 库,Reddit API 的 Python 包装器。你可以使用pip install PRAW来安装这个库。

导入库并在praw.Reddit()函数中输入访问密钥进行验证,然后您可以使用reddit.read_only()检查连接

reddit.subreddit(keyword).hot(limit=10)是用于获取子编辑中前 10 个热门话题的函数。

搜索子编辑“MachineLearning”的示例输出如下

Reddit 输出的热门话题(图片由作者提供)

你可以在下面看到从 Reddit 访问数据的要点。

推特

从 Twitter API 中,你可以获得基于关键词或标签的推文。在开始实现之前,请确保您有使用 Twitter API 的访问键。对于这个项目,自由层或标准 API 就足够了。

访问密钥:请参考 slickremix 获取在 Twitter 中创建访问令牌的简单指南。

我已经使用了tweepy库来访问 tweets,你可以使用pip install tweepy来安装这个库

导入 tweepy 后,您可以使用tweepy.OAuthHandler()set_access_token()函数来验证 API。

接下来,您可以使用tweepy.Cursor()搜索和下载推文,我在查询中使用了“-filter:retweets”,因为我想获得只有原始推文的数据,并过滤掉转发。

tweets = tweepy.Cursor(api.search,
          q=keyword + **' -filter:retweets'**,
          lang=**"en"**,
          since=date_since).items(100)

然后使用下面的代码删除推文中的所有超链接

import re
clean_tweet = re.sub(**r'\w+:\/{2}[\d\w-]+(\.[\d\w-]+)*(?:(?:\/[^\s/]*))*'**, **''**, tweet.text)

为了获得热门推文,我使用了每条推文的收藏和转发次数。我为每条推文生成了一个分数,给予转发次数比最受欢迎次数更多的权重。

score = (retweet_count * 2 + favorite_count)

使用 tweets 评分,我对所有数据进行了排序,得到了前 10 条 tweets。请参见下面的输出

来自 Twitter 的推文(图片由作者提供)

你可以在下面看到从 Twitter 获取数据的要点

新闻宣传短片

新闻 API 是从多个新闻来源获取头条新闻的非常好的来源。

访问密钥:需要生成 API 访问密钥,访问是免费开发的。您可以使用链接来生成您的密钥。

确保使用pip install newsapi-python安装库并导入库from newsapi import NewsApiClient

你需要使用函数NewsApiClient(api_key='API_KEY')来初始化它

使用newsapi.get_everything()您可以获得在给定日期范围内包含给定关键字的所有文章。我已经给出了 7 天的数据范围,以确保我们得到任何关键字搜索至少 10 篇文章。

newsapi.get_everything(q=keyword,
                       from_param=last_week,
                       to=today,
                       language=**'en'**)

您可以在下面看到从 NewsApi 访问数据的要点

创建网站

在我看来, Streamlit 是创建网站和分享成果的最佳 python 库。如果你试图尽可能快地构建一个最小可行的产品,这是最好的。

你可以在下面看到网站的最终输出。

主题查找器(图片由作者提供)

在这里,我将从 API 的所有 4 个来源获取数据,然后相应地格式化数据,以显示在漂亮而简单的表格中。

我想提供的网站功能有:

  1. 用户应该能够用关键字搜索找到主题
  2. 允许用户点击并运行 API 的按钮

确保您已经安装了 streamlit。你可以用pip install streamlit安装它

网站标题'主题查找器'是使用st.title(**“Topic Finder”**)创建的

我已经使用st.sidebar.text_input()在侧边栏中创建了文本输入字段

为每个 API 源创建一个输入字段,使用户能够从每个源搜索不同的主题。参见下面的代码片段,了解侧栏文本输入的创建。

pytrend_word = st.sidebar.text_input(**"Google Trend Search Term"**,google_topic)
redtrend_word = st.sidebar.text_input(**"Reddit Trend Search Term"**,reddit_topic)
twtrend_word = st.sidebar.text_input(**"Twitter Trend Search Term"**,twitter_topic)
nwtrend_word = st.sidebar.text_input(**"News Search Term"**,news_topic)

我没有将所有的输入字段留空,而是创建了一个默认的文本值,名为' machinelearning '。

按钮是使用st.button()创建的

如果单击该按钮,那么从所有 API 获取数据的所有已定义函数都将运行并获取数据。

data = gtrends(pytrend_word)
gtrends_list = list(data[**'query'**][0:10])  *# gtrends Top 10* redtrends(redtrend_word) *#returns reddit trend search* twtrends(twtrend_word) *#twitter top list* tweets_df = pd.DataFrame(list(zip(tweet_text, score)), columns=[**'Tweets'**, **'Score'**])
tweets_df = tweets_df.sort_values(by=[**'Score'**], ascending=**False**)
tweets_list = list(tweets_df[**'Tweets'**].head(10))
newtrends(nwtrend_word)

Wordcloud :为了生成一个 Wordcloud,我将所有来源的文本加入到一个列表中,并使用 matplotlib 和 wordcloud 库生成了一个简单的 wordcloud。

*# Create and generate a word cloud image:* wordcloud = WordCloud().generate(comment_words)
fig, ax = plt.subplots()
*# Display the generated image:* ax.imshow(wordcloud, interpolation=**'bilinear'**)
ax.axis(**"off"**)

使用streamlit run <script_name>.py运行 streamlit。您应该能够在本地浏览器中看到如下所示的屏幕

你可以在下面的 gist 中看到代码

最终输出:

作为测试,我用关键词“机器学习”进行了搜索。我对结果非常满意,因为我得到了所有 API 的前 10 名搜索,我可以清楚地看到有很多有趣的想法可以使用。

最终输出(图片由作者提供)

我把自己放在一个内容查找器的位置,快速浏览输出,不到一分钟我就找到了我感兴趣的可以写的主题。我也确信我写的这些主题会有观众,因为它们已经成为流行趋势。

根据搜索结果,我可以写一些主题:

  • 百页机器学习书— 来自 Google Trend
  • 人工智能摄像机将裁判的光头误认为是球,并在比赛中跟踪它。(我可以用这个有趣的例子来写训练数据的质量和重要性)——来自 Reddit
  • 数据科学初学者不可或缺的 5 本书 —来自 Twitter
  • 强化学习到底是什么东西,是怎么运作的?— 来自 NewsApi

最后,我对结果非常满意。

结论:

在这篇文章中,我们介绍了如何使用 Google、Reddit、Twitter 和 NewsApi APIs 进行内容搜索。除此之外,我还想展示利用你作为数据爱好者所拥有的技能,将一个想法变成最终产品的方法。

如果你已经观察到这个项目中不涉及机器学习。如果你是一名数据科学家或机器学习工程师,我知道实现你对复杂模型的每一个想法的冲动。这就是为什么我们开发的很多模型没有投入生产。

我坚信,如果没有必要,不使用机器学习并不可耻。主要目标应该是使用数据,并以任何可能的方式提供可操作的见解。

发现曲线:冠状病毒病例数据的可视化

原文:https://towardsdatascience.com/spot-the-curve-visualization-of-cases-data-on-coronavirus-8ec7cc1968d1?source=collection_archive---------26-----------------------

通过可视化研究“曲线”的尝试

法比安·金特罗在 Unsplash 上的照片

【2020/6/4 更新|我用 Dash 和 Plotly 做了一个关于追踪最新数字的 web 应用。我会写另一篇文章,但请随时尝试。https://spot-the-curve-coronavirus.herokuapp.com/

在撰写本文时(2020 年 3 月 26 日),冠状病毒(新冠肺炎)已经对我们的社会造成了严重破坏。尽管已经有一些关于 2019 年底在中国武汉发现的一种新病毒的消息,但西方国家的爆发才刚刚开始。在自我隔离期间,我想到,如果我们使用数据来比较每个国家的疫情,而不是被主流媒体的随机信息和数字轰炸,这将是有趣的,而且可能是鼓舞人心的。结果,我利用春假的时间投入到这个小项目中。

编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

策划思路:“拉平曲线”

当我想把每个国家的情况可视化时,我首先想到的是“曲线”大约在游行开始时,社交网络和媒体突然充满了“拉平曲线”的想法。这个概念促进了公众对这种病毒的认识,它在西方社会中非常有效。

https://www . vox . com/2020/3/10/21171481/冠状病毒-美国-病例-隔离-取消

在这篇受欢迎且优秀的中帖(作者托马斯·普约)中,作者解释得相当好。一般来说,曲线意味着社会需要照顾的活跃病人的分布。图表背后的逻辑是,我们不应该指望阻止所有的传播,而是要推迟,让国家有能力解决这些案件。

所以我们的目标不是消除冠状病毒的传染性。是为了推迟他们。托马斯·普约

因此,我决定根据这一概念绘制图表,显示每个国家确诊病例的增长情况,变化不大。

使用的数据

在写这篇文章的时候,引用最多的数据来源之一是约翰·霍普斯金大学。它从不同国家的世卫组织和疾控中心收集数据,一些开源开发者也在其上构建 API。所以我选择使用它的数据集。

https://github.com/CSSEGISandData/COVID-19

预处理和绘图的细节

我首先找到了每个国家的确诊病例数据来做数据清理,确保数据是时间序列格式的。然后我用 Python 可视化包(Matplotlib)做了图。

这里有一个小小的免责声明:由于数据处于非常不一致和不完善的状态,我已经在粒度级别上做出了妥协和个人决定,以使这成为可能。如果你对 ETL 的细节感兴趣,请查看 GitHub 页面,或者如果我没有提到什么,就给我留言。这是一个没有任何预测模型的报告和 EDA 项目。

结合确诊病例进行探讨

寻找曲线

首先,为了得到第一个基线图,让我们只画出在中国,第一个发生地,和在其他亚洲国家的确诊病例。

然而,我马上注意到一些棘手的事情。首先,数据集只记录了从 1 月 22 日开始的病例数据。这使得不可能看到传播的完整趋势(中国早在 2019 年就出现了首例病例;资料中最早记录为 548 例确诊病例)。其次,数据是累积的,这意味着它应该只会上升。所以很难了解这个国家目前的疫情状况(传播速度有多快)。

改进版本:分解人口和调整 Viz 元素

为了解决代表性的问题,我决定重新检查数据。首先,我将累积案例的值转换成“每日增长”的形式。就确定一个国家的负担而言,我认为病例总数是一个不准确的指标,因为不同的人口水平可能会影响国家的公共卫生能力(例如护士与病人的比率)。

关于人口,我承认,传播的速度,在大多数情况下,与人口的水平(应该是人口密度)并不相关。因此,这里的要点是比较处理确诊病例的能力,而不是新冠肺炎在每个国家的“增长”。

相应的,我把它转换成一个新的叫做“感染率”,每 100 万人确诊病例数(按国家人口;来源数据,与不同种类的国家进行公平比较。此外,在图中,我将每日增加的病例数汇总到一个 2 天的格式中,并仅包括第一个病例后的记录,以平滑趋势(也是为了每个国家有一个公平的开始)。随着这一变化,可视化也将更加明显,因为当数字很大并且有太多可比数据时,它会变得混乱。

## Sample code for factoring population 
def data_process_avg(complete_df):
    for c in list(complete_df.columns):
        complete_df[c] = round(complete_df[c]/country_pop_dict.get(c)*1000000, 3)
    return complete_df
#country_pop_dict is the dictionary with country names and the  #accroding populations made by other data
#The source codes and data are included in the GitHub.

然后我选择了其他亚洲国家,因为它们也是第一批受到影响的国家,并且可能在这些天里“使曲线变平”。

从上面的图表中,我们可以看出大多数亚洲国家对冠状病毒的传播相当稳定。韩国以前曾有过重大的感染群,但在事件发生后成功地阻止了指数增长。有趣的是,我们可以在图表上发现一些“曲线”,但这并不意味着它们不会在未来爆发。从下面的情节来看(为了更好地观看,去掉了韩国),因妥善处理疫情而受到称赞的新加坡和台湾,最近仍然面临着潜在的传播增长。

p.s .印度还没有包括在这张图表中,因为它刚刚开始有更多关于确诊病例的数据,并且它的人口水平与其他国家有很大不同。我以后会单独研究它。

假设来说,严格的旅行禁令和强制隔离可以减缓病毒的传播。大多数国家(尤其是亚洲国家)已经颁布了相关政策。然而,由于 2 周的潜伏期和从国外返回的公民,我们应该预计效果还需要一两周才能发生(待更新)。

高度传播的国家现在是什么状况?

在第一次探索亚洲后,我们知道大多数亚洲国家现在有一个更温和的传播。接下来,我转向想象一些更严重的国家现在的情况。

为了确定传播最严重的国家,我继续使用相同的指标(感染率),并根据最新感染率(3 月 23 日)对前 10 个国家进行了排名。

在绘图方面,我保持了相同的设置,但我只包括了第 100 个案例之后的记录,以平滑趋势(一些国家由于不规则的测试例程而出现峰值,一些国家也在晚些时候开始积极测试潜在的案例。)

3 月 25 日排名

事实证明,情况最严重的 10 个国家都来自欧洲(只有伊朗来自中东)。令人惊讶的是,瑞士实际上在其他国家中处于可怕的境地(超高的增长率使其在这两天超过了意大利。).相比之下,尽管美国受到了媒体的大量关注,但它目前在这一标准中排名第 15 位。尽管如此,与欧洲国家相比,它仍然处于病毒传播的早期阶段,所以我们应该期待它会有更多的变化(见下图)。

2020 年 3 月 27 日更新:美国现在正经历着大量测试实践,所以排名可能会在未来发生很大变化。

未来步骤:测试数据和仪表板

尽管我们已经考虑了人口因素,但目前确诊病例的数量仍然与检测量密切相关。理想情况下,我们希望每个国家的新冠肺炎测试尽可能详细。但是,目前还没有关于这方面的官方数据库。

p.s. 我们的数据世界(来自牛津大学的一组研究人员)已经收集了这些信息的临时和不完整版本,并使其有可能在可视化中使用(请参见下面他们的图表之一)。

用案例数据测试数据

在未来,我将在这里的工作基础上继续添加新的东西,并在一个 web 应用程序上构建一个交互式和可自我控制的仪表板,以生成这些图表中的任何一个进行比较。

结论:还有很多工作要做

在这篇文章中,我们使用了一种更周到的方法来检查冠状病毒在特定国家的现状。我们发现了一些潜在的曲线,并认识到新冠肺炎在一些国家的严重性。然而,这些信息也表明,我们应该警惕在传播和数据方面的任何重大变化。

虽然内容看起来很少,但对于像我这样的流行病学和公共卫生新手来说,在这个问题上进行分析和假设实际上是一项很大的工作。除了领域知识,新冠肺炎的开放数据在这一点上是相当不发达的。我对数据看得越多,即使是来自被高度引用的来源,我就越沮丧,因为就目前而言,最可靠和可信的信息只是确认的病例(例如,JHU 的 GitHub 在数据中有一些令人讨厌的不一致;多个 API 不时地保持开启和关闭)。

话虽如此,仍有一群专业人士不断完善数据,并从中提炼出切实可行的见解。如果有人对这个项目感兴趣,你可以看看一些有用的文章和有趣的讨论。

[## 在你创建另一个关于新冠肺炎的图表之前,要考虑十点

总结一下——# viz responsible;这可能意味着根本不在公共领域发布你的可视化。

medium.com](https://medium.com/nightingale/ten-considerations-before-you-create-another-chart-about-covid-19-27d3bd691be8) [## 不要“把曲线弄平”,停下来!

到目前为止,你们都已经看到了新冠肺炎案例负载曲线的一个版本:

medium.com](https://medium.com/@joschabach/flattening-the-curve-is-a-deadly-delusion-eea324fe9727)

现在,让我们给它更多的时间,尽我们的一份力量!

祝贺并感谢你的阅读!请随意查看我的 Github 获取完整代码,并在 cl3883@columbia.edu 留言

杰夫

交易情绪:NLP 和情绪给现货铜市场打分

原文:https://towardsdatascience.com/spot-vs-sentiment-nlp-sentiment-scoring-in-the-spot-copper-market-492456b031b0?source=collection_archive---------48-----------------------

NLTK、Gensim 和 VADER 在金融出版物推文中搜索 Alpha

每日复合情绪得分与铜现货价格,美元/盎司。

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

介绍

继铁和铝之后,铜是世界消耗最多的金属之一。铜是一种用途极其广泛的金属,其导电性和导热性、抗菌性和耐腐蚀性使其广泛应用于大多数经济部门。从电力基础设施、住宅和工厂到电子产品和医疗设备,全球经济对铜的依赖程度如此之深,以至于有时被称为“铜博士”,市场和大宗商品分析师也经常这样称呼铜,因为这种金属能够评估全球经济健康状况和活动。

从交易的角度来看,铜的定价是由金属交易所的供需动态决定的,特别是伦敦金属交易所(LME)和芝加哥商品交易所。然而,铜的交易价格受到无数因素的影响,其中许多因素很难同时衡量:

  • 全球经济增长(GDP)
  • 新兴市场经济体
  • 中国经济(中国占全球铜需求的一半
  • 铜矿生产国的政治和环境不稳定
  • 美国房地产市场
  • 贸易制裁和关税
  • 很多很多其他人。

除了上述基本面因素,铜价还可能受到对冲基金、投资机构、保税金属,甚至国内交易的人为影响。从系统交易的角度来看,当我们想要开发一个预测模型时,这是一个非常具有挑战性的情况。

然而,就以新闻形式公布的事件而言,短期机会可能存在。在整个美中贸易战期间,铜的现货和远期价格一直受到的冲击,并且像所有市场一样,对重大消息几乎立即做出反应。

如果发现得足够早,基于 NLP 的系统交易模型可以通过将公告解析为令牌向量,评估潜在情绪,并随后在预期的(如果适用)价格变动之前建仓,或者在价格变动期间建仓,以期利用潜在的修正来利用这些短期价格变动。

问题

在本文中,我们将从各种金融新闻出版物 Twitter feeds 中抓取历史(和当前)推文。然后,我们将分析这些数据,以了解每条推文背后的潜在情绪,得出情绪得分,并考察这一得分与过去五年铜现货价格之间的相关性。

我们将涵盖:

  1. 如何用 GetOldTweets3 获取历史推文
  2. 基本探索性数据分析 (EDA)技术与我们的 Twitter 数据。
  3. 文本数据预处理技术(停用词、标记化、n 元语法、词干化&词条化等)。
  4. 潜在狄利克雷分配 到模型&使用GenSim&NLTK pyl Davis 探索主题和内容在我们的 Twitter 数据中的分布。
  5. 情感评分使用 NLTK 价觉词典和情感推理机(VADER)。

我们不会在这项工作的基础上开发和测试一个成熟的交易策略,其语义超出了本文的范围。此外,本文旨在展示数据科学家可以用来从文本数据中提取有用信号的各种技术。

现货铜 NLP 策略模型

让我们从获取数据开始。

现货价格数据

我们将从获取现货铜价数据开始。我们选择使用铜的现货价格,而不是铜的远期合约(以今天商定的价格在商定的固定未来日期买卖固定数量金属的协议)的原因是现货价格对市场事件最具反应性——它是立即完成商品交易的报价(T2)。通常,我们会使用彭博终端来获取这些数据,但是,我们可以从商业内幕免费获得历史现货铜数据:

# Imports
import glob
import GetOldTweets3 as got
import gensim as gs
import os
import keras
import matplotlib.pyplot as plt
import numpy as np
import nltk
import pandas as pd
import pyLDAvis.gensim
import re
import seaborn as snsfrom keras.preprocessing.text import Tokenizer
from nltk.stem import *
from nltk.util import ngrams
from nltk.corpus import stopwords
from nltk.tokenize import TweetTokenizer
from nltk.sentiment.vader import SentimentIntensityAnalyzer
from sklearn.feature_extraction.text import CountVectorizer
from tensorflow.keras.preprocessing.sequence import pad_sequences# Get Cu Spot
prices_df = pd.read_csv(
    '/content/Copper_120115_073120',
    parse_dates=True,
    index_col='Date'
)
# To lower case
cu_df.columns = cu_df.columns.str.lower()#Plt close price
cu_df['close'].plot(figsize=(16,4))
plt.ylabel('Spot, $/Oz')
plt.title('Cu Spot Close Price, $/Oz')
plt.legend()
plt.grid()

现货铜,收盘价,2015 年 1 月 1 日至 2020 年 7 月 1 日,美元/公吨

虽然我们的价格数据看起来不错,但需要注意的是,我们正在考虑每日价格数据。因此,我们将自己限制在一个可能会丢失信息的时间范围内——市场对新闻事件的任何反应都可能在几分钟内发生,很可能是在新闻发布后的内。理想情况下,我们会使用 1-5 分钟的棒线,但是对于本文的目的来说,这样就可以了。

推特数据

我们将使用一个名为getoldtweets 3(GOT)的库来提取我们的历史 tweet 数据。与官方的 Twitter API 不同,GOT3 允许用户访问 Twitter 数据的大量历史记录。给定一个属于金融新闻媒体的 Twitter 句柄列表和一些相关的关键字,我们可以定义我们想要获取数据的搜索参数(注意:出于格式原因,我在下面发布了执行此操作所需逻辑的截图,而不是代码片段):

使用搜索参数获取指定句柄的历史 twitter 数据

方法.setQuerySearch()接受单个搜索查询,因此我们无法根据多个搜索标准提取 tweets。我们可以使用循环很容易地解决这个限制。例如,可以简单地为一个唯一查询的每次执行分配变量名,即“现货铜”、“铜价”等,但是对于本文的目的,我们可以满足于单个查询:

# Define handles
commodity_sources = ['reuters','wsj','financialtimes', 'bloomberg']# Query 
search_terms = 'spot copper'# Get twitter data
tweets_df = get_tweets(
  commodity_sources,
  search_term = search_terms,
  top_only = False,
  start_date = '2015-01-01',
  end_date = '2020-01-01'
).sort_values('date', ascending=False).set_index('date')tweets_df.head(10)

Twitter 历史数据

到目前为止一切顺利。

我们现在需要处理这些文本数据,以使其能够为我们的主题和情感模型所解释。

预处理和探索性数据分析

自然语言应用程序的文本数据预处理需要仔细考虑。从丢失的角度来看,从文本数据组成一个数字向量可能是具有挑战性的,当执行看似基本的任务时,如删除停用词,有价值的信息和主题上下文很容易丢失,我们将在下面看到。

首先,让我们删除标签和 URL 形式的冗余信息,即

来自媒体的推文通常包含句柄标签、标签和文章链接,所有这些都需要删除。

我们定义了几个单行的函数,这些函数使用正则表达式来删除匹配我们想要删除的表达式的字母和字符:

#@title Strip chars & urls
remove_handles = lambda x: re.sub(‘@[^\s]+’,’’, x)
remove_urls = lambda x: re.sub(‘http[^\s]+’,’’, x)
remove_hashtags = lambda x: re.sub('#[^\s]*','',x)tweets_df[‘text’] = tweets_df[‘text’].apply(remove_handles)
tweets_df[‘text’] = tweets_df[‘text’].apply(remove_urls)
tweets_df[‘text’] = tweets_df[‘text’].apply(remove_hashtags)

接下来,我们通过检查 tweet 的组成对我们的 twitter 数据进行一些基本分析,例如单个 tweet 的长度(每条 tweet 的字数)、字符数等。

基本文本 EDA —单词和字符频率分布

停止言语

显而易见,每条推文的平均长度相对较短(准确地说是 10.3 个单词)。这些信息表明,如果我们考虑潜在的信息损失,以计算复杂性和内存开销为代价,过滤停用词可能而不是是一个好主意。

最初,这个实验使用 NLTK 非常方便的停用词标准列表,从 Tweets 中删除所有停用词:

# Standard tweet sw
stop_words_nltk = set(stopwords.words('english'))# custom stop words
stop_words = get_top_ngram(tweets_df['text'], 1)
stop_words_split = [
    w[0] for w in stop_words
    if w[0] not in [
        'price', 'prices',
        'china', 'copper',
        'spot', 'other_stop_words_etc'
    ] # Keep SW with hypothesised importance
]stop_words_all = list(stop_words_nltk) + stop_words_split

然而,这种行为导致了许多错误分类的推文(从情感分数的角度来看),这支持了信息丢失的概念,因此最好避免。

在这一点上,当涉及到处理 Twitter 数据时,非常值得强调 NLTK 的优秀的库。它提供了一套全面的工具和功能来帮助解析社交媒体输出,包括表情符号解释!。你可以在 Twitter data 这里 找到一个真正有用的入门和使用 NLTK 的指南。

N-grams

下一步是考虑词序。当我们将一系列符号矢量化成一个单词包(BOW——下一段将详细介绍)时,我们失去了这些单词在 tweet 中的顺序所固有的上下文和含义。我们可以通过检查最常见的 n-grams 来尝试理解词序在我们的 tweets 数据框架中的重要性。

正如我们在上面的初步分析中观察到的,一条给定推文的平均长度只有 10 个词。根据这些信息,一条推文中词语的顺序,特别是确保我们保留这种顺序中固有的上下文和含义,对于生成准确的情感评分至关重要。我们可以将记号的概念扩展到包括多单词记号,即 n-grams ,以便保留单词排序中的含义。****

NLTK 有一个非常方便(也非常高效)的 n 元语法标记器:from nltk.util import ngram。n-gram 函数返回一个生成器,该生成器以元组的形式生成前“n”个 n-gram。然而,我们有兴趣探索这些 n 元语法在第一个实例中实际上是什么,所以将利用 Scikit-learn 的计数矢量器来解析我们的 tweet 数据:

def get_ngrams(doc, n=None):
  """
  Get matrix of individual token counts for a given text document.
    Args:
      corpus: String, the text document to be vectorized into its  constituent tokens.
    n: Int, the number of contiguous words (n-grams) to return.      
    Returns:
      word_counts: A list of word:word frequency tuples.
  """
  # Instantiate CountVectorizer class
  vectorizer = CountVectorizer(ngram_range=
  (n,n)).fit(doc)
  bag_of_words = vectorizer.transform(doc)
  sum_of_words = bag_of_words.sum(axis=0)
  # Get word frequencies
  word_counts = [(word, sum_of_words[0, index])
      for word, index in vectorizer.vocabulary_.items()
  ]
  word_counts = sorted(word_counts, key=lambda x:x[1], reverse=True)
  return word_counts# Get n-grams
top_bigrams = get_ngrams(tweets_df['text'], 2)[:20]
top_trigrams = get_ngrams(tweets_df['text'], 3)[:20]

连续词频(n 元语法)。

在检查我们的 n-gram 图时,我们可以看到,除了少数例外,基于 NLP 的预测模型将从我们的 n-gram 特征中学习到更多。例如,该模型将能够正确地将“铜价”解释为铜的实物价格的参考,或者将“中国贸易”解释为中国的贸易,而不是解释单个单词的含义。

记号化和词条化。

我们的下一步是标记我们的 tweets,以便在我们的 LDA 主题模型中使用。我们将开发一个功能,对我们的 tweets 进行必要的分段(分词器的工作)和词汇化。

我们将使用 NLTK 的tweet tokenizer来执行我们的推文的标记化,这是专门为解析推文和理解它们相对于这个社交媒体平台的语义而开发的。

鉴于每条推文相对简短的性质,降维对我们的模型来说并不是一个紧迫的问题。考虑到这一点,在试图消除单词的复数形式与所有格形式之间的细微意义差异时,不对我们的数据执行任何词干操作是合理的。

相反,我们将实现一个词条整理器,WordNetLemmatizer,对我们的 tweet 数据中的词进行规范化。对于我们的应用程序来说,lemma tion 可以说比词干提取更准确,因为它考虑了单词的含义。WordNetLemmatizer 还可以帮助提高我们的主题模型的准确性,因为它利用了词性(POS)标记。一个单词的词性标记表明了它在句子语法中的作用,例如区分名词词性和形容词词性,如“铜”和“铜的价格”。

注意:您必须在 WordNetLemmatizer 中手动配置 POS 标签。如果没有 POS 标签,它会认为你输入的所有东西都是名词。

def preprocess_tweet(df: pd.DataFrame, stop_words: None):
  """
  Tokenize and Lemmatize raw tweets in a given DataFrame.
    Args:
      df: A Pandas DataFrame of raw tweets indexed by index of type       DateTime.
      stop_words: Optional. A list of Strings containing stop words         to be removed.
    Returns:
      processed_tweets: A list of preprocessed tokens of type          String.
  """
  processed_tweets = []
  tokenizer = TweetTokenizer()
  lemmatizer = WordNetLemmatizer()
  for text in df['text']:
    words = [w for w in tokenizer.tokenize(text) if (w not in    stop_words)]
    words = [lemmatizer.lemmatize(w) for w in words if len(w) > 2]        processed_tweets.append(words)
    return processed_tweets# Tokenize & normalise tweets
tweets_preprocessed = preprocess_tweet(tweets_df, stop_words_all)

为了演示上述函数的效用,我们还将一个停用词列表传递到函数中。

矢量化和连续词汇袋

我们现在需要使用一种称为单词的 Bag(BOW)的文档表示方法,将我们的标记化 tweets 转换为向量。为了执行这个映射,我们将使用 Gensim 的字典类:

tweets_dict = gs.corpora.Dictionary(tweets_preprocessed)

通过将处理过的 tweets 列表作为参数传递,Gensim 的字典为每个唯一的规范化单词创建了一个唯一的整数 id 映射(类似于哈希映射)。我们可以通过调用 tweets_dict 上的.token2id()来查看 word: id 映射。然后,我们计算每个不同单词的出现次数,将单词转换为其整数单词 id,并将结果作为稀疏向量返回:

cbow_tweets = [tweets_dict.doc2bow(doc) for doc in tweets_preprocessed]

LDA 主题建模

现在是有趣的部分。

开发基于 NLP 的交易策略的前提是了解我们提取的数据是否包含与铜价相关的主题/信号,更重要的是,它是否包含我们可能交易的信息。

这要求我们检查和评估各种主题以及在我们的数据中代表这些主题的单词。垃圾进,垃圾出。

为了探索我们的 tweet 语料库中的各种主题(以及所述主题的主题),我们将使用 Gensim 的潜在狄利克雷分配模型。LDA 是一种生成概率模型,适用于文本等离散数据的集合。LDA 的功能相当于一个分层贝叶斯模型,其中集合中的每个项目都被建模为一组底层主题的有限混合。反过来,每个主题都被建模为一组潜在主题概率的无限混合物( Blei,ng 等人,2003 )。

我们将新矢量化的推文cbow_tweets和将每个单词映射到 id 的字典tweets_dict传递给 Gensim 的 LDA 模型类:

# Instantiate model 
model = gs.models.LdaMulticore(
  cbow_tweets,
  num_topics = 4,
  id2word = tweets_dict,
  passes = 10,
  workers = 2)# Display topics 
model.show_topics()

您可以看到,我们需要通过num_topics超参数提供数据集内主题数量的估计值。据我所知,有两种方法可以确定主题的最佳数量:

  1. 建立多个 LDA 模型,用相干模型 计算它们的相干得分。****
  2. 领域专长和直觉。

从交易的角度来看,这是领域知识和市场专业知识可以帮助的地方。我们希望我们的 Twitter 数据中的主题,记住它们是金融新闻出版物的产品,主要集中在以下主题:

  • 铜价(自然上涨)
  • 美中贸易战
  • 美国总统唐纳德·特朗普
  • 主要铜矿商
  • 宏观经济公告
  • 当地生产国国内/政治动乱

除此之外,在确定这个超参数时,应该使用自己的判断。

值得一提的是,其他超参数的整个主机存在。这种灵活性使得 Gensim 的 LDA 模型极其强大。例如,作为贝叶斯模型,如果我们对主题/单词概率有“先验”的信念,我们的 LDA 模型允许我们通过init_dir_prior方法,或者类似地通过eta超参数,为狄利克雷分布编码这些先验。

回到我们的模型,你会注意到我们使用了 Gensim 的多核版本LdaModel,它允许更快的实现(多核机器的操作并行化):

LDA 模型 show_topics()输出:注意编号为 0-4 的主题,其中包含单词及其相关权重,即它们对主题的贡献大小。

粗略地检查一下我们模型中的主题,会发现我们既有相关的数据,而且我们的 LDA 模型在对所述主题建模方面做得很好。

为了了解主题及其关键字的分布,我们将使用 pyLDAvis,它启动了一个交互式小部件,非常适合在 Jupyter/Colab 笔记本中使用:

pyLDAvis.enable_notebook()
topic_vis = pyLDAvis.gensim.prepare(model, cbow_tweets, tweets_dict)
topic_vis

LDA 模型——Twitter 新闻数据,话题分布。

LDA 模型结果

在检查产生的主题图时,我们可以看到我们的 LDA 模型在捕捉 Twitter 数据中的突出主题及其组成词方面做得很好。

什么构成了健壮的主题模型?

一个好的主题模型通常展示大的、不同的主题(圆圈),没有重叠。所述圆圈的面积与语料库(即我们的 Twitter 数据)中“N”个总标记中的主题比例成比例。每个主题圆的中心在两个维度上设置:PC1 和 PC2,它们之间的距离由在主题间距离矩阵上运行的降维模型(准确地说是多维标度)的输出来设置。pyLDAvis 主题视觉背后的数学细节的完整解释可以在这里找到。

解读我们的结果

在记住而不是忽略我们试图解决的问题的同时,具体来说,了解我们的推文数据中是否有任何有用的信号可能影响铜的现货价格,我们必须进行定性评估。

详细检查各个主题,我们可以看到一组有希望的结果,特别是出现在各个主题中的热门词汇,它们在很大程度上符合我们上面预期的主题标准:

题目编号:

  1. 铜矿开采&铜出口国

热门词汇包括主要铜矿商(必和必拓、安托法加斯塔、英美资源集团和力拓),以及主要铜出口国,即秘鲁、智利、蒙古等。

2.中国贸易&制造业活动****

热门词汇包括“铜”、“铜价”、“中国”、“自由港”和“上海”。

3.美中贸易战****

热门词汇包括“铜”、“价格”、“中国”、“特朗普”、“美元”和“美联储”,但也有一些不常见的术语,如“智利”和“视频”。

基于上述结果,我们决定继续我们的 NLP 交易策略,因为我们的 twitter 数据展示了足够多的与铜现货价格相关的信息。更重要的是,我们可以确信我们的 Twitter 数据与铜价的相关性——我们的 LDA 模型发现的主题符合我们对数据中应该出现的 预期主题 的看法。

验证 LDA 模型

作为数据科学家,我们知道我们必须验证任何模型的完整性和稳健性。我们的 LDA 模型也不例外。我们可以通过检查模型的一致性(如上所述)来做到这一点。通俗地说,连贯性衡量的是一个主题内单词之间的相对距离。关于数学背后的数学细节精确地说*这个分数是如何计算的可以在这篇 论文 中找到。为了简洁起见,我省略了重复各种表达。*

一般来说,介于 0.55 和 0.70 之间的分数表示一个熟练的主题模型:

*# Compute Coherence Score
coherence_model = gs.models.CoherenceModel(
    model=model,
    texts=tweets_preprocessed,
    dictionary=tweets_dict,
    coherence='c_v')coherence_score = coherence_model.get_coherence()
print(f'Coherence Score: {coherence_score}')*

基于确认度量‘c _ v’(与 UMass 相反),计算我们的 LDA 模型的一致性分数。

在 0.0639 的一致性分数下,我们可以合理地确信我们的 LDA 模型已经在正确数量的主题上被训练,并且在每个主题中的高分单词之间保持足够程度的语义相似性。

我们对分数测量的选择,在上述一致性模型逻辑的签名中可以观察到,是由 Roder,Both & Hindeburg 的论文中的结果激发的。您可以看到,我们已经选择根据coherence = 'c_v度量对我们的模型进行评分,而不是使用‘u _ mass’,‘c _ v’,‘c _ UCI’。等。发现“c_v”分数测量返回优于其他测量的结果,特别是在小词集的情况下,限定了我们的选择。**

情感得分:VADER

在对我们的 twitter 数据包含足够相关的信息以潜在地预测短期铜价走势感到满意后,我们继续进入我们问题的部分。******

我们将使用 NLTK 的 Valence Aware 字典和情感推理器(VADER)来分析我们的推文,并根据每条推文中每个词的潜在强度总和,生成一个介于-1 和 1 之间的情感得分。

不管我们在 NLP 模型中使用单个标记、ngrams、词干还是词条,基本上,我们 tweet 数据中的每个标记都包含一些信息。可能这些信息中最重要的部分是单词的情感。

VADER 是一个受欢迎的启发式的、基于规则的(由人类创作的)情感分析模型,由休顿和吉尔伯特开发。它在社交媒体文本上使用特别准确(并且是专门为此应用程序设计的)。因此,在我们的项目中使用它似乎是合理的。

VADER 的实现非常简单:

*****# Instantiate SIA class
analyser = SentimentIntensityAnalyzer()sentiment_score = []for tweet in tweets_df[‘text’]:
  sentiment_score.append(analyser.polarity_scores(tweet))*****

SentimentIntensityAnalyzer包含一个令牌和它们各自分数的字典。然后,我们为 tweets 数据框架中的每条 tweets 生成一个情感分数,并访问由 VADER 模型生成的四个独立分数部分的结果(一个字典对象):

  • 文本的否定比例
  • 正文的正面比例
  • 文本的中性比例&
  • 上述情绪极性的综合强度,即“复合”得分
*****#@title Extract Sentiment Score Elementssentiment_prop_negative = []
sentiment_prop_positive = []
sentiment_prop_neutral = []
sentiment_score_compound = []for item in sentiment_score:
  sentiment_prop_negative.append(item['neg'])
  sentiment_prop_positive.append(item['neu'])
  sentiment_prop_neutral.append(item['pos'])
  sentiment_score_compound.append(item['compound'])# Append to tweets DataFrame
tweets_df['sentiment_prop_negative'] = sentiment_prop_negative
tweets_df['sentiment_prop_positive'] = sentiment_prop_positive
tweets_df['sentiment_prop_neutral'] = sentiment_prop_neutral
tweets_df['sentiment_score_compound'] = sentiment_score_compound*****

推文数据情绪得分:负面,正面,复合,每日。

在绘制了各种成分的滚动分数、负分数、正分数和复合分数(我们不考虑中性分数)之后,我们可以进行一些观察:

  • 显然,情感得分非常嘈杂/不稳定——我们的 Twitter 数据可能只包含冗余信息,少数信息会导致得分大幅飙升。然而,这就是信号发现的本质——我们只需要那一条显著的信息。
  • 我们的 Twitter 数据看起来主要是正面的:平均负面得分是 0.09,而平均正面得分是 0.83。

情绪得分与铜现货价格

现在我们必须评估我们的努力是否得到了回报:我们的情绪得分是否能预测铜的现货价格!****

乍一看,现货价格和我们的综合得分之间似乎没有任何关联:

复合情绪得分 vs 现货铜($/Mt),每日。

然而,当我们应用经典的平滑技术并计算我们情绪得分的滚动平均值时,我们看到了一个不同的画面:

滚动 21d 平均复合情绪得分与现货铜(美元/公吨),每日。

这现在看起来更有希望。除了 2017 年 1 月至 8 月期间,我们可以很容易地观察到我们的 21 天滚动平均复合得分和铜现货价格之间近乎对称的反比关系。

结论

在这种情况下,我们停下来考虑我们可用的选项,即我们希望我们的模型如何处理和分类一段文本数据中的潜在情绪,以及关键的是,模型将如何根据其交易决策对这种分类采取行动。

奥卡姆剃刀原则一致,我们实现了一个开箱即用的解决方案来分析 twitter 数据中的潜在情绪。除了探索一些著名的 EDA 和预处理技术作为先决条件,我们还使用了 NLTK 的 Valence Aware 字典和情绪 Reasoner (VADER) ,为每条推文生成相关的情绪得分,并检查了所述得分与简单的相应铜现货价格运动的相关性。

有趣的是,我们观察到滚动复合情绪得分和铜价之间存在相关性。当然,这并不意味着因果关系。此外,它可能只是新闻数据跟踪铜价,而我们的推文数据只是报告其走势。尽管如此,仍有进一步工作的余地。

观察、批评和进一步分析

事实上,系统交易策略的设计需要更多的数学和分析的严谨性,以及大量的专业知识。人们通常会投入大量时间来设计一个合适的标签,最好地包含信号和价格运动的幅度(如果有的话!)在所述信号中发现,尽管对信号本身进行了彻底的研究。

关于我们的问题,存在大量非常有趣的进一步工作和分析的空间:

  1. ****神经网络嵌入:作为一个例子,为了更好地理解一个 NLP 模型和一个相关的标签(或多个标签)是如何做出交易决策的,我们可以用嵌入层来训练一个神经网络。然后,我们可以检查经过训练的嵌入层,以了解模型如何根据具有类似编码和标签的层来处理层中的各种标记。然后,我们可以可视化模型如何根据单词对我们希望预测的类别的影响对单词进行分组,即 0 表示负价格变动,1 表示正价格变动。例如,TensorFlow 的嵌入投影仪是可视化此类嵌入的无价工具:

2。多项式朴素贝叶斯

我们使用 VADER 来解析和解释我们的 Twitter 数据的潜在情绪,它做了合理的工作。然而,使用 VADER 的缺点是它不会考虑文档中的所有单词,实际上只有大约 7500 个。鉴于商品交易及其相关术语的复杂性,我们可能会遗漏重要信息。****

作为替代,我们可以使用朴素贝叶斯分类器来寻找预测我们目标的关键词集,无论是铜价本身还是情绪得分。

3 。**日内数据**

由于引言中提到的原因,在设计 NLP 交易策略模型时,几乎所有情况下的日内数据都是必须的。当试图利用基于新闻/事件的价格变动时,时间和交易执行是非常重要的。

感谢你花时间阅读我的文章,我希望你觉得有趣。

请不要客气,我非常欢迎评论和建设性的批评。

参考

  1. 休顿,C.J. &吉伯特,E.E. (2014)。VADER:基于规则的社交媒体文本情感分析的简约模型。第八届网络日志和社交媒体国际会议。密歇根州安阿伯,2014 年 6 月。
  2. 莱恩、霍华德、哈普克(2019):自然语言处理在行动。
  3. Moneyweek (2019):金属市场的周期:锌&铜什么时候发光
  4. 《日经亚洲评论》(2020):《铜博士》误诊了全球经济
  5. Kitco 2020:美中紧张局势引发的获利回吐导致铜价下跌。
  6. Metal Miner,(2020):中国影子金属融资对市场意味着什么

Spotify 情感分析

原文:https://towardsdatascience.com/spotify-sentiment-analysis-8d48b0a492f2?source=collection_archive---------12-----------------------

对 Spotify 播放列表中的歌词进行情感分析

介绍

你有没有想过你听的音乐周围都是什么样的数据?这么多不同的音乐应用程序正在将复杂的数据科学和机器学习技术应用于我们的音乐数据,并获得了非常酷的输出。例如,Spotify 是最受欢迎的应用之一,由我们听的音乐、我们喜欢的歌曲以及我们创建和遵循的播放列表驱动,为其用户和社区提供个性化产品。

当我查看 Spotify 收集的数据时,我注意到他们从音乐本身收集音频特征。所以声音、舞蹈、能量、节奏等的水平。但是最突出的特征是音频价

音频效价:从 0.0 到 1.0 的一个量度,描述一个音轨所传达的音乐积极性。高价曲目听起来更积极(例如,快乐、愉快、欣快),而低价曲目听起来更消极(例如,悲伤、沮丧、愤怒)。

所以,从这个属性出发,Spotify 可以为我们生成播放列表,播放更乐观积极的歌曲,或者更圆润消极的歌曲。虽然这很聪明,但我最近遇到了一些不准确的结果 Spotify 为我生成的正面播放列表中有悲伤的歌曲。我想知道是什么决定把它放在那里。

如果我们看看 80 年代普林斯的经典歌曲《当鸽子哭泣时》的音频特征,Spotify 的结果显示这是一首活泼的歌曲——这是真的,它有相当时髦的节奏!🕺🏻🕊️

{"danceability": 0.729, "energy": 0.989, "loudness": -4.613, "speechiness": 0.049, "acousticness": 0.0102, "instrumentalness": 0.0000445, "liveness": 0.443, "valence": 0.84, "tempo": 126.47, "duration_ms": 352906}

但是在与之相关的化合价分数(0.84 表示正化合价)和歌词所表达的内容方面存在矛盾。虽然听众可以对歌词的含义有自己的解释,但这首歌使用了鸽子和紫色紫罗兰这两个众所周知的爱情象征,来表达一对相爱的夫妇之间不断争吵之前的事情。下面的歌词只是一些表达心碎和悲伤的例子:

你怎么能让我一个人站着?
一个人在这么冷的世界里?

我们为什么要对着对方尖叫?
这是鸽子哭泣时的声音

鉴于这一发现,我很快对这个想法产生了兴趣,即我们是否可以通过更深入地研究音乐本身并对我们播放列表中的歌曲歌词进行情感分析,来获得对我们正在听的音乐的价值的更多了解。

情感分析:自动识别文本中表达的观点或情感,以确定作者的态度是积极、消极还是中立。

在这种情况下,我开发了一个 Spotify 情感分析应用程序,Spotify 用户可以从中了解更多关于他们歌曲的情感。

这个帖子讲的是 app 的开发。我试图让文本尽可能简单明了,同时仍然提供技术细节。不管怎样,事情可能会变得有点古怪。所以坚持住,如果你坚持到最后,一定要点击“喜欢”按钮。但是如果你想跳过技术对话,直接使用这个应用,那就去主持的吧。

通用烧瓶应用说明

对于那些不熟悉框架的人来说,Flask 允许你用 Python 创建 web 应用。如果我想实现一些看起来不错的东西,我会转向 Flask,使用一个免费的 Bootstrap 主题作为界面的基础。这就是 Flask 的真正用途——它旨在快速轻松地开始使用,并能够扩展到更复杂的应用程序。

在这篇文章的这一部分,我决定给出一个关于我如何设置和构建我的 Flask 应用程序的快速分解。我已经使用 Flask 好几次了,但是当我第一次使用它时,我注意到文档有点混乱,很难快速掌握。所以,这里有一个我如何做的快速和一般的解释。

虚拟环境

Python 使用虚拟环境为不同的应用程序维护不同版本的包。您可以为每个应用程序设置不同的虚拟环境,并控制这些应用程序使用的软件包版本。

前往你的终端,使用cd name_of_directory导航到你希望你的 Flask 应用程序的目录所在的任何地方。这里,我们将创建一个名为Spotify _ 情操 _ 分析的目录,然后我们将使用cd导航到该目录。

虚拟环境包含在 Python 3 中,因此我们将创建一个虚拟环境,而无需安装任何东西。命令中的第一个venv是 Python 虚拟环境包的名称,第二个是我将用于这个特定环境的虚拟环境名称。

我们现在应该已经创建了一个虚拟环境。为了能够使用它,我们需要激活它,然后使用 pip 在其中安装 Flask。为了确认虚拟环境成功安装了 Flask,我们可以启动 Python 解释器和import Flask。

没有输出是一个好的输出,意味着 Flask 安装正确,可以使用了!💡

应用包

这个应用程序将存在于一个中。这意味着,在 Python 中,包含一个__init__.py文件的子目录被视为一个包,可以被导入。我们将创建一个名为 app 的包——它将托管应用程序。要做到这一点,请确保你在正确的目录中(在本例中,是Spotify _ 情操 _ 分析),并创建一个名为 app 的新目录。当我们在终端时,我们可以创建空文件__init__.py

然后,__init__.py文件将包含一个脚本,该脚本创建 application 对象作为 Flask 从其包中导入的类的实例。传递给 Flask 类的__name__变量是一个预定义的 Python 变量,它被设置为使用它的模块的名称。然后我们导入routes模块,它还不存在,但是在我们声明了app变量之后被导入。这是因为routes模块导入了app变量。

路线

那么,routes模块中有什么呢?路由是 Flask 应用程序实现的所有不同的 URL。在 Flask 中,应用程序路由被写成称为视图函数的 Python 函数,这些函数被映射到一个或多个路由 URL。让我们在我们的 app 目录下创建 route 模块routes.py并创建一个视图函数

这里,我们已经创建了一个视图函数。函数上方的@app.route被称为装饰器,它在作为参数给出的 URL 和函数之间创建一个关联。在这个例子中,装饰器/index URL 相关联。这意味着当 web 浏览器请求这个 URL 时,Flask 将能够包含这个函数,并将它的返回值作为响应传递回浏览器。我们将在以后的实践中看到这一点。

为了完成应用程序,我们需要在定义 Flask 应用程序实例的目录的顶层有一个脚本。我将把它命名为Spotify _ opinion _ analysis . py,并包含下面一行导入应用程序实例的代码。

为了确保我们有正确的项目结构,这里是我们虚拟环境中的当前目录。

运行烧瓶应用

当运行应用程序时,需要通过设置FLASK_APP环境变量来告诉 Flask 如何导入它。然后我们可以使用flask run运行应用程序。

来自flask run的输出表明服务器运行在 IP 地址 127.0.0.1:5000 上,这总是您的计算机的本地地址,或者称为您的本地主机。在 web 浏览器中,导航到该 URL。您会注意到,当 URL 以/index结尾时,我们会看到在我们的视图函数中声明的简单消息。把网址改成别的,比如/home,你会发现这个网址没有找到,因为我们没有创建它。

模板

模板有助于将 web 应用程序的表示和设计与 Python 逻辑分开。在 Flask 中,模板被写在单独的文件中,通常是 HTML 文件,它们存储在应用程序包内的模板目录中。让我们创建模板目录,确保不以大写字母“t”命名它,否则它将无法被识别。该目录现在应该看起来像下面的结构。

在我们的 templates 目录中,我们可以创建 HTML 文件,这些文件不仅可以呈现我们漂亮的 web 界面,而且我们还可以将 Python 中的变量和结果解析到我们的前端。让我们创建一个名为index.html的 HTML 文件,并将其保存在我们的模板目录中。

如果你熟悉 HTML,你会发现第 4 行看起来有点奇怪!这里,我们实际上正在解析变量user,我们将用 Python 创建这个变量,并显示在前端。{{ ... }}本质上是 Python 变量的占位符。

如果我们跳回 routes.py ,我们需要声明变量user。然后我们使用 Flask 的render_template()函数将模板转换成一个完整的 HTML 页面。这个函数需要导入。它接受模板文件名和声明的 Python 变量,并返回相同的模板,但是其中的所有占位符都被这些变量替换了。

我们还可以在模板中呈现条件语句和 for 循环,但我不会在这里讨论这些。

假设我们已经设置了几个视图功能,我们希望能够从一个页面导航到另一个页面。轻松点。我们可以使用 HTML 的href标签导航并指向页面。但是,我们可以只使用我们在装饰器中声明的 URL,例如/index,而不是指向应用程序的整个 URL。

造型

在设置 Flask 应用程序的环境时,我想快速介绍的最后一件事是在哪里放置页面样式。我的意思是,让我们的应用程序看起来很棒的文件应该放在哪里?为了做到这一点,我们必须在我们的 app 目录中创建最后一个目录,名为 static 。在这个目录中,你可以找到你的应用程序需要的所有 CSS 和图像文件。要将样式或图像文件连接到 HTML,只需将样式表导航到static/...

SPOTIFY 情感分析烧瓶应用程序

如果你还和我在一起,太好了!

根据前面对如何设置和开始构建 Flask 应用程序的解释,我继续创建了两个视图函数,一个用于我的登录页面,另一个用于我的主页。登录页面是我希望用户看到我的应用程序的地方,当他们点击按钮时,他们将使用他们的凭据验证自己是 Spotify 用户,并访问他们自己的音乐。

Spotify 情感分析登录页面

主页是显示情感分析结果的地方。现在,我厚颜无耻地来到 Spotify 的网站,在你登录后保存了他们主页的 HTML 样式。但我只想声明,他们的代码和设计的版权是他们自己的,这个项目的目的只是为了好玩。

您可能会注意到,图片并没有显示在 Spotify 个人资料图片通常显示的位置。这是因为该个人资料图片由 Spotify 存储,并在您登录后呈现在页面上。同样的方法也适用于这里——我们还没有提取任何数据,但一旦我们提取了数据,我们会将登录用户的个人资料图片放在那里,使其更加个性化。

Spotify 情感分析主页

SPOTIFY API 键

感谢 Spotify 的 API,我能够提取和探索我喜欢听的歌曲——那些让我点击喜欢按钮的歌曲。要使用 API 访问您的 Spotify 数据,您首先需要使用以下步骤进行初始设置:

使用您的 Spotify 凭据登录 Spotify for Developers 网站。

您将看到您的仪表板,您可以'创建一个新的应用程序

从应用仪表板页面,选择“编辑设置”。

为您的应用程序提供名称和描述。

然后,将重定向 URI 设置为 API
验证访问后您希望用户被定向到的位置。由于我使用的是 Flask,我将把它设置为我的
localhost:http://127 . 0 . 0 . 1:5000/Spotify _ opinion _ analysis 上的主 HTML 页面。

点击保存,记下您的客户端 ID客户端密码密钥。下一步我们需要这些。

证明

现在我们已经有了 Flask 环境设置、设计的开始和 Spotify API 密钥,我们可以开始访问我们的音乐数据了。要让最终用户批准你的应用程序访问他们的 Spotify 数据和功能,或者让你的应用程序从 Spotify 获取数据,你需要授权你的应用程序。Spotify 的授权指南描述了如何通过两种方式获得授权:

应用授权 : Spotify 授权你的应用访问 Spotify 平台(API、SDK 和 Widgets)。

用户授权 : Spotify 以及用户授予你的应用程序访问和/或修改用户数据的权限。调用 Spotify Web API 需要您的用户授权。为了获得授权,您的应用程序生成一个对授权端点的调用,传递一个请求访问权限的作用域列表。

在这里,我们将让用户授权我们访问他们的音乐数据。我们希望它对我和其他想要使用它的人来说是个性化的,所以我们访问个人用户数据是有意义的。

提出授权请求涉及三方:Spotify 服务器、您的应用程序以及最终用户数据和控制:

Spotify API 认证

还记得你在 Spotify 仪表盘上记下的那些键吗?这里是你需要它们的地方。🔑

首先,让我们声明变量 CLIENT_IDCLIENT_SECRET 作为我们的键。这些将是string型。然后,我们将设置一些必需的 Spotify URLs 和服务器端参数。特别注意重定向 _URI 。这是您在上一节设置键时声明的重定向 URI。请记住,这是您希望用户通过身份验证后被重定向到的 URL。最后,我们希望通过 Spotify 解析所有上述信息来检索数据。

我们需要设置另一个视点。这一次,视点不会返回模板。它将解析 API 信息,并将我们重定向到我们的重定向 _URI 。另外,注意我们需要从 Flask 库中导入重定向,以及从 urllib.parse 中导入 urllibquote 。最后一件事——我们需要将登录 HTML 页面上的按钮连接到授权功能。转到那个文件,在你的href标签中包含/spotify_authentication来引用相应的视点

总之,我们的routes.py现在应该看起来像这样:

现在身份验证已经设置好了,我们期望看到什么呢?嗯,如果你运行你的 Flask 应用,点击欢迎页面上的登录按钮,你应该会被重定向到 Spotify 的认证页面!🎉

身份验证页面应该如下所示。填写完 Spotify 详细信息后,您应该会被重定向到您的重定向 URI,并看到您的主页。

Spotify 认证页面

SPOTIFY 数据

现在是有趣的部分。用户已经通过认证访问了他们的 Spotify 数据——但是我们如何检索它呢?

收到授权码后,您需要通过向 Spotify 帐户服务发出POST请求,用访问令牌交换授权码,这次是向其/API/令牌端点:POST https://accounts.spotify.com/api/token.发出请求

这个请求的主体需要包含一些参数,这些参数将作为 base64 编码包含在头中。在这种情况下,我们将从 Flask 的库中导入请求以及以下库: base64,requests,json

一旦您运行 Flask 应用程序并且认证令牌成功,来自 Spotify 帐户服务的响应具有状态代码 200 和以下 JSON 数据:

{'country': 'GB', 'display_name': 'Lowri Williams', 'external_urls': {'spotify': 'https://open.spotify.com/user/1149952632'}, 'followers': {'href': None, 'total': 12}, 'href': 'https://api.spotify.com/v1/users/1149952632', 'id': '1149952632', 'images': [{'height': None, 'url': 'https://scontent.xx.fbcdn.net/v/t1.0-1/p320x320/37337856_10155307217510946_6222490498947350528_o.jpg?_nc_cat=102&_nc_sid=0c64ff&_nc_ohc=KD5MjymlBtcAX8vHlVP&_nc_ht=scontent.xx&_nc_tp=6&oh=eac7ae2f14c7f373371fbcdce8439241&oe=5E993E5B', 'width': None}], 'product': 'premium', 'type': 'user', 'uri': 'spotify:user:1149952632'}

酷!我可以看到我的用户名、我有多少关注者、我的个人资料的 URL、我的个人资料图片以及我拥有的帐户类型。因为这是一个 JSON 对象,所以我可以将相关信息赋给变量。

还记得主页模板中个人资料图片的占位符吗?好了,我们可以解析 profile_url 变量,方法是将{{ profile_url }}作为主页 HTML 中个人资料图片元素的href,并呈现模板。我还可以显示播放列表中最近的 6 首歌曲。

将图像解析到前端

好了,现在来访问音乐数据。我们可以通过将我们的认证令牌指向用户简档端点来检索我们喜欢的歌曲。音乐数据包含各种信息。我们感兴趣的是何时将歌曲添加到播放列表中、歌曲名称、艺术家姓名、专辑封面、歌曲的 URL 以及曲目的 ID。

同样,由于这是一个 JSON 对象,我可以访问相关信息。由于我们要处理几首歌曲,我将创建一个数据帧来组织数据。

音乐比赛

一旦我们可以访问 Spotify 播放列表中的歌曲,我们就需要它们的歌词。Spotify 没有这个功能——这很遗憾。然而,我们可以使用其他一些资源来匹配歌词和歌曲。

Musixmatch 是一个歌词平台,who 的社区由全球数百万音乐粉丝组成,建立了最佳的歌词知识,包括翻译的歌词。与其他歌词网站相比,Musixmatch 似乎在歌词的正确性和涵盖的歌曲数量方面取得了最好的结果。但是这个网站最好的一点是它有一个 API。

注册 API 密钥既快速又简单。去他们开发者的页面注册一个账户。他们的免费计划让你每天进行 2k 次 API 调用,只检索每首歌 30%的歌词。

要获得歌词,您首先需要使用以下步骤进行初始设置:

注册一个 Musixmatch 开发者账户并登录。

您将看到您的仪表板,您可以'注册一个 API 密钥

为您的应用程序提供名称和描述。

点击保存,记下您的 API 密钥。

情感分析

已经开发了各种技术和方法来解决自动识别文本中表达的情感。在这篇文章中,我将使用 Python 情感分析库 VADER 来分类我的播放列表中的歌词是积极的、消极的还是中性的。

一种非常简单的情感分析方法是使用一个单词列表,这些单词已经根据它们的语义取向进行了标记。例如,我们可以假设单词“好”有一个正价,而单词“坏”有一个负价。VADER 使用这种技术,并提供了一个百分比分数,它代表了属于每个情感类别的歌词的比例。它还提供了一个复合得分,计算方法是将每个单词的效价得分相加,然后将得分归一化到-1(最大负值)和+1(最大正值)之间。

VADER 的好处在于我们不需要以任何方式对文本进行预处理!我们可以将歌词输入 VADER 的情感函数,并检索每首歌的复合得分。记住,如果你使用免费的 API,Musixmatch 只允许访问每首歌的前 30%的歌词。所以,当然,结果可能不像我们希望的那样准确。

首先,我们将从 musixmatch 的 Python 库中导入 Musixmatch,从 VADER 的 Python 库中导入SentimentIntensityAnalyzer函数。我将声明我的 Musixmatch API 键,初始化来自 VADER 的情感分析器,然后迭代来自数据帧的歌曲名称和艺术家,通过 Musixmatch 的歌词匹配器解析它们。然后,我将计算复合情绪得分是高于还是低于阈值,这样我们就可以给它们贴上积极、消极或中性的标签。

数据可视化

一旦我获得了播放列表中每首歌曲的复合分数,我就可以开始考虑如何可视化结果了。最近我最喜欢使用的图表之一是雷达图,或者网络图或蜘蛛图。对于那些不知道的人来说,雷达图是一种二维图表类型,旨在绘制多个变量的一个或多个系列的值。每个变量都有自己的轴,所有的轴都连接在图的中心。

假设我们想要绘制正面、负面和中性歌曲的月平均复合得分。我可以通过按日期列对我的数据帧进行分组,并对复合得分进行平均,来计算月平均情绪得分。然后我可以使用 ChartJS 生成下面的雷达图。

雷达图

酷!在我的播放列表中的 320 首歌曲中,我们可以看到我听的是积极和消极的混合歌曲。有意思的是,2019 年 12 月,我喜欢上了更多积极向上的音乐。这也许可以用我一定听过的圣诞歌曲来解释。我似乎很少听可以归类为表达中性情绪的音乐。当我写这篇文章时(2020 年 4 月底),我似乎一直在听更积极的音乐。

我们还能做什么?

我们可以提取总体得分最高的积极和消极的曲调,并把它们放在一个漂亮的记分卡上。歌曲的封面图像也可以从 Spotify 收集的数据中提取。

热门音乐

我们还可以将情感分析结果与 Spotify 的音频效价分数进行对比。我们可以从我的播放列表中解析每个曲目的 id,并通过音频功能端点解析它们。如果我们将所有被分类为正面、负面或中性的歌曲的分数进行平均,我们可以使用一个简单的条形图来比较这两种方法。有希望的是,尽管我们对一首歌的 30%的歌词进行了情感分析,但结果似乎与 Spotify 的音频效价得分相对相似。

Spotify 音频价格与情感分析

结论

那么,我从这个分析中学到了什么?

深入了解我所听的音乐以及对歌词进行情感分析真的很有趣——我的音乐品味似乎比我想象的更积极!我可能会在未来的帖子中关注如何从歌词中提取关键词。这打开了其他自然语言处理项目的大门,在那里我可以研究使用机器学习来根据歌曲的内容对它们进行分类。

这个项目是我非常喜欢做的事情。因此,我进一步扩展了 Flask 应用程序,增加了一些其他功能。如果你想更多地了解你的歌曲的情感,去主办的吧。

如果你喜欢关注这篇文章,别忘了通过给我买杯咖啡来分享或展示你的荣誉!☕

通过交互式数据可视化传播新冠肺炎

原文:https://towardsdatascience.com/spread-of-covid-19-with-interactive-data-visualization-521ac471226e?source=collection_archive---------22-----------------------

新冠肺炎传播的条形图比赛和交互式 Choropleth 地图

一个完整的指南条形图比赛和互动 Choropleth 地图新冠肺炎疫情与 Python。

“视觉化给你你不知道的问题的答案”——本·施耐德曼

在数据科学时代,我们必须说数据就是美。我们甚至无法想象没有数据的日子。现代技术或多或少都依赖于数据驱动的流程。但是只有原始数据在经过处理之前对我们没有任何吸引力。当数据被处理时,我们从中获得信息。信息可以是视觉的、统计的、预测的等等。如果我们能从美学角度将数据可视化,我们就能轻松洞察数据。它提供了数据的概述。适当的视觉化使人对人脑中的整个场景有一个清晰的直觉。

“我们大多数人需要听音乐才能理解它有多美。但这通常是我们展示统计数据的方式:我们只展示音符,不演奏音乐”——汉斯·罗斯林

汉斯·罗斯林指出了数据可视化的实际思想。数据可视化对于每一个职业都是必不可少的。在这篇文章中,我们将尽最大努力代表新冠肺炎的情况,在世界各地。同时,我们将学习一些使用 python 的交互式绘图技术。

Python 提供了如此多的库,让我们的生活变得简单而美好。我们将通过新冠肺炎数据集学习一些有趣的绘图技术。

当你看完整篇文章时,你将能够创建动画,如文章中较难的部分所示。阅读文章不需要背景知识。它只不过是几行 python 代码。让我来引导。

路标

Python 为我们提供了使用不同库完成不同任务的灵活性。这里,我们将使用matplotlibplotly 库,通过新冠肺炎数据集获得带有条形图 race 和 choropleth map 的可视化输出。

  • 为我们想要的图进行少量的数据预处理。
  • 使用新冠肺炎数据集对确诊、恢复和死亡病例进行时尚的条形图竞赛。
  • 具有新冠肺炎数据集的交互式 choropleth 地图

让我们进入下一步…

我们期望的数据可视化的✴Data 预处理

从 kaggle 下载新冠肺炎数据集。数据组织得很好,每天都在更新。我使用的数据集最后更新于 2020 年 4 月 25 日。

  • 进口熊猫与dataframenumpy 一起进行不同的数学运算,与matplotlib 一起进行条形图比赛。
import pandas as pd
import numpy as np
import matplotlib.ticker as ticker
import matplotlib.pyplot as plt

该数据集包含所有受影响国家的冠状病毒确诊阳性病例、死亡病例和康复病例。如果我们想看到数据,它会是这样的。

covid = pd.read_csv('covid_19_data.csv')
covid.head(5)

新冠肺炎数据集的前 5 行

现在,我们想要重命名这些列,因为这将有助于我们更容易地操作数据。

covid.rename(columns={'ObservationDate':'Date','Province/State':'State','Country/Region':'Country'},inplace=True)

从数据集的前 5 行,我们知道有一些冗余和不必要的列,我们将离开这些列,只保留必要的列。

covid=covid[["Date","State","Country","Confirmed","Deaths","Recovered"]]

我们的主要目标是制作一个基于国家的条形图比赛,这样我们可以按日期对国家进行分组,以找出一个国家的病例总数。

grouped = covid.groupby(['Country','Date'])
covid_confirmed = grouped.sum().reset_index().sort_values(['Date'],ascending=False)

我们将输出数据帧保存到covid_confirmed ,如下所示。

找出冠状病毒确诊病例最多的十个国家,绘制初步柱状图。我们正在步入条形图竞赛的台阶。

df = (covid_confirmed[covid_confirmed['Date'].eq("04/25/2020")].sort_values(by="Confirmed",ascending=False).head(10))

输出

第一部分

与冠状病毒传播的 matplotlib 进行条形图竞赛

用 matplotlib 制作的✴Step 逐级竞速条形图

#plotting the initial horaizontal barchart 
fig, ax = plt.subplots(figsize=(15, 8))
ax.barh(df['Country'], df['Confirmed'])
plt.xlabel('Number of Confirmend Cases')
plt.ylabel('Country')

这几行代码将使用matplotlib.生成以下输出

前 10 个受影响国家的条形图(从下到上)

但是我们想把最高确诊病例放在最上面。为此,我们需要翻转数据框。df[::-1] ,这段代码将翻转数据帧df

dff=df[::-1]
fig, ax = plt.subplots(figsize=(15, 8))
ax.barh(dff['Country'], dff['Confirmed'])
plt.xlabel('Number of Confirmed Cases')
plt.ylabel('Country')

前 10 个受影响国家的条形图(从上到下)

哇!我们比预期的产量领先了几步。上面的条形图似乎不太好。我们需要图表中更多的信息和视觉色彩。为此,我们可以遵循以下技巧

I .用不同颜色对每个国家进行颜色编码

二。具有固定数量颜色的颜色代码

三。十大受影响国家的 10 种不同颜色。

我会向你展示每一步。

  • 用字典用不同的颜色给每个国家编码。

国家随机颜色编码代码

colors保存国家名称的字典作为关键字,颜色代码是每个国家的值。

  • 您也可以使用以下方法。
colors = dict(zip(df_date_series.Country.unique(),['#adb0ff', '#ffb3ff', '#90d595', '#e48381', '#f7bb5f','#fb6b19','#1fb1fb'] * 31
))
  • 只需为 10 个受影响的国家设置 10 种颜色代码

该方法用于功能部分显示的draw_barchart功能。

color=["#980505","#CD1212","#D84E4E","#CB6262","#D39B5F","#F7EC10","#D0F710","#9CF710","#B4D67F","#969C8E"]

但是,我对下面的条形图使用第一种方法。

彩色条形图代码

恭喜!我们正处于新冠肺炎数据集条形图竞赛的最后一步。我们将创建一个功能与上述所有代码和一些时尚的集成。

“用 Matplotlib 进行 Python 中的条形图竞赛”这篇文章对我帮助很大。获得更好的条形图样式。我从文章中引用了一些波兰风格的功能,

  • 文本:更新字体大小、颜色、方向
  • :移动 X 轴到顶部,添加颜色&字幕
  • 网格:在横条后添加线条
  • 格式:逗号分隔值和轴标记
  • 添加标题、致谢名单、装订线空间
  • 移除:盒框、y 轴标签

我们将使用这些东西为我们的条形图比赛进行美学设计。

功能

draw_barchart函数中,我实现了第三个颜色代码选项。我用 10 种颜色代码列表为前 10 个受影响的国家设置了固定颜色。

对于单个函数调用,对于draw_barchart(“04/25/2020”).,函数将输出如下

确诊病例的时尚条形图

制作一部动画是我们的最终目标,我们打算这么做。你准备好了吗?这是你的动画代码。

用 draw_barchart 函数制作动画的代码

现在我们有了下面的动画。

条形图竞赛动画

除了使用 HTML,还可以使用animator.save() 方法保存动画。如果将参数case 改为DeathsRecovered ,动画将显示DeathsRecovered的条形图比赛。

第二部分

互动动画冠状病毒分布图

在本文的最后,我们将创建一个交互式 choropleth 地图,如下图所示。

使用新冠肺炎数据集的交互式动画

数据分析和预处理的✴Little 位

我们不需要与任何没有确诊电晕病例的国家打交道。因此,我们将删除没有冠状病毒确诊病例的行。

modified_confirmed = covid_confirmed[covid_confirmed.Confirmed > 0]

为了制作有效的冠状动脉分布图,我们必须有可区分数量的冠状病毒感染病例。让我们对带有核密度图的新冠肺炎数据集有一些直觉。

kde 绘图的代码

包含原始确诊病例和 log10 确诊病例的 kde 图

第一幅图显示了大多数国家的价值观被浓缩到一个点上。但是当我们用 log10 值来绘制这些值时,它显示了更多的自然分布。这表明,如果我们使用原始的确诊病例数,我们无法区分地图的颜色。因为大多数国家都会用相似类型的颜色来绘制。为了获得更好的结果,我们必须包含数据集的log10 值。让我们开始吧。

modified_confirmed['Affected_Factor'] = np.log10(modified_confirmed.Confirmed)

现在,我们的 modified_confirmed 数据集如下所示。

修改确认数据帧的前 5 行

是啊!我们根据确诊病例的log10 值在数据框中添加了一个名为Affected_Factor 的列。我们离最终的交互式 choropleth 地图仅一步之遥。

✴Creating Choropleth 地图使用 Plotly

关于 Plotly 库

plotly Python 库([plotly.py](https://plotly.com/python/))是一个交互式的开源绘图库,支持 40 多种独特的图表类型,涵盖了广泛的统计、金融、地理、科学和三维用例。

安装

如果您使用 anaconda 环境,您可以在conda 注释提示符下使用下面的conda 命令安装这个库。

conda install -c plotly plotly

你也可以查看 anaconda 的官网了解安装过程。对于其他环境,可以使用pip install

$ pip install plotly

导入必要的模块

import plotly as py
import plotly.express as px

我们将使用带有**plotly.express** 模块的新冠肺炎数据集绘制交互式等值线图。现在我们将为交互式地图编写最终代码。

交互式 choropleth 的代码

我们已经向plotly.express 对象px.传递了一些参数

下面是参数的简短描述。

Dataframe: 我已经用[::-1]反转了数据帧modified_confirmed。因为数据帧是根据最近到过去的日期排序的,但我们需要显示冠状病毒开始影响人类的日期的动画。

location 根据国家栏设置参数。

所有其他参数定义都作为注释嵌入到代码中。

新冠肺炎传播的交互式 choropleth 地图

剧情中的 **Affected_Factor** 传说预示着什么?

虽然我在预处理部分提到过,但图例Affected_Factor 可能会令人困惑。这是确诊病例的log10 值。从核密度图中,我们得到了对确诊病例值的直觉。不同国家的确诊病例数非常接近。如果用确诊病例的数量来区分不同国家的颜色,我们可能无法区分颜色的变化。大多数国家将被标上相似的颜色。为了减少问题,我们计算了确诊病例的log10 值。更容易辨别。第二个内核密度图也表明了这一事实。也可以自己去查一下。

现在,我们的交互式 choropleth 地图已经准备好了。这张地图提供了新冠肺炎教在全世界传播的信息。随着时间的推移,随着确诊冠状病毒病例的增加,地图的颜色也在发生变化。我们也可以通过鼠标指针找到所有的信息。地图还支持放大和缩小。

结论

数据可视化是一目了然地呈现任何事物总体状况的最佳方式。丰富 python 库使我们变得更容易。我们只需要知道如何使用它,功能如何工作。官方文档在这方面可以帮到你很多。

如果您想获得完整的项目,只需访问 GitHub 库。你也可以从这里查看整个 jupyter 笔记本。

Python 支持这么多可视化库。其中最流行的是 matplotlib,它是一个巨大的库。您还可以找到其他可视化库的文档,如 seaborngeopandasgeopyplotly 等。更美观的数据绘图。

如有任何疑问,请在评论区告诉我。

感谢您花时间阅读本文。

【注 : 走向数据科学是一份主要基于数据科学和机器学习研究的媒体出版物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。要了解更多关于疫情冠状病毒的信息,你可以点击这里

用冠状病毒传播禽流感

原文:https://towardsdatascience.com/spreading-ai-with-coronavirus-f97727fe0873?source=collection_archive---------19-----------------------

来源:https://it . Wikipedia . org/wiki/File:3D _ medical _ animation _ corona _ virus . jpg

对人有害的东西对机器也有好处。该病毒如何帮助推动人工智能的发展,并使一些有问题的应用程序合法化。

致命病毒以前从未发生过的事情是使用人工智能(AI)来对抗它。应用范围令人印象深刻。我们先来看看:

监督

中国

在社交媒体上流传的一段视频显示,一位中国老太太身后跟着一架无人机,指导她如何戴上口罩,这直接来自未来主义电影。

“是的,阿姨,这架无人机在和你说话。你不应该不戴口罩就到处走。你最好回家,别忘了洗手,”无人机告诉她,根据中国国有的环球时报的翻译。

很吸引人,对吧?

面部识别摄像头在中国被广泛使用,报道半岛电视台:估计有 3 . 5 亿个监控设备被用在机场、地铁站和街道上。新技术——包括用于帮助控制冠状病毒传播的红外温度扫描仪——使当局能够跟踪、存储和分析公民的旅行历史以及他们的医疗状况。

抗击病毒传播的崇高目标让政府的监控看起来合情合理,但却令人权活动人士担忧。

一些使用该国卫生和交通部门数据的移动应用程序可以提供坐在正在遭受或即将遭受病毒感染的患者附近的人的名单。告诉我你和谁坐在一起,我会告诉你你是否会生病。

韩国

有趣的是,在韩国,帮助追踪病例和显示受污染者所在位置的应用程序不是由政府开发的,而是由私人软件开发商开发的。在 Coronamap.live 上点击一个标有“看看我是否安全”的按钮,你就可以看到你附近是否有任何已知的快速传播病毒的病例。

我一个来自首尔的朋友告诉我,当局使用这些应用程序来追踪和消毒病毒可能被许多人感染的地方。

还有短信提醒居民他们所在地区的确诊病例,告诉他们疑似感染者去过哪里,何时去过。

预测系统

人工智能健康预测有一些历史:2008 年,谷歌推出了一项服务,试图使用人们的搜索查询来发现流感爆发。后来人们发现,他们实际上比美国卫生官员提前两周左右标记了 2009 年疫情猪流感,尽管他们的方法被发现一贯高估了患病人数。

如今,许多研究人员和公司试图使用人工智能来准确预测疾病的传播。一家加拿大公司 BlueDot 目前似乎是最成功的。根据 Vox 上发表的信息,它使用大量数据来评估公共健康风险。它使用自然语言处理(NLP)和机器学习(ML),每天浏览和分析 65 种语言的约 100,000 篇文章,以跟踪 100 多种传染病。早在 2019 年 12 月,他们就在美国疾病控制和预防中心之前第一个通知他们的客户冠状病毒爆发。为了预测疾病的传播,他们还使用旅行者行程信息、社交媒体和新闻报道等数据。

约翰·霍普金斯大学一直在使用 Twitter 收集关于疾病发生地点的实时信息。但是这种类型的数据是相当嘈杂的。

可以收集和分析用于预测疾病传播的其他类型的数据是零售购买、浏览模式和私人信息中的关键字。

与只能通过具体症状做出判断的医生相比,机器能够发现隐藏的、人类无法注意到的迹象。

其他人工智能应用程序也可用于此目的。英国《每日电讯报》报道,“一项研究发现,人工智能分析的脸书状态是个体健康状况的“重要”线索,发现攻击性语言(如“哑巴”和“婊子”)有助于预测药物成瘾,奇怪的是,宗教语言(如“上帝”和“祈祷”)有助于预测糖尿病。”

我不知道脸书状态中的哪些关键词可能指向冠状病毒,但很容易想象人工智能至少可以分析冠状病毒的提及次数,让研究人员知道可能发生恐慌的内容和地点。

人工智能扫描阅读和药物发现

深度学习——与人脸识别和自动驾驶技术相同——可以帮助更快地诊断受感染的人。总部位于北京的 Infervision 将其用于 CT 扫描分析。通常,病人和医生需要几个小时才能得到 CT 结果,但人工智能有助于加快检测过程,并更有效地监测疾病。医生仍然需要跟进实验室测试和其他检查,但当病毒传播如此之快时,节省时间至关重要。

人工智能的另一个用途是药物发现,这被证明是非常有前途的。对于冠状病毒,研究人员能够在几周内对新病毒的基因进行测序。像 Exscientia 有限公司这样的公司研发药物的速度比以前快得多。如果以前开发一种药物需要数年时间,那么这些算法可以将时间缩短到 12-18 个月。对付冠状病毒这样的疫情病毒可能还需要很长时间,但值得一试。

初创公司 Healx 利用机器学习为现有药物寻找新用途。它现在没有解决冠状病毒,但算法搜索化合物和预测新药的能力肯定会用于未来的爆发。

但有一个问题:人工智能无法加快临床测试的速度。证明一种药物的有效性和安全性,以及监管机构审查使用该药物的结果,可能仍需要数年时间。因此,基于人工智能的药物设计者面临一项新任务:提前思考,预测未来可能给世界带来浩劫的病毒。

信息援助

在经历了 2011 年 3 月 11 日袭击日本的毁灭性地震后,香月明美·常贺受到启发,创建了一个名为 Bespoke 的聊天机器人:“根据我在那场灾难中的个人经历,我想创建一种可以在任何紧急情况下帮助人们的服务。我理解人们的恐惧,也理解他们无论身在何处都需要获得即时、准确的信息。”

今天,聊天机器人的多语言顾问向人们通报冠状病毒爆发的最新消息,分享统计数据,传播率和政府联系信息。它还提供关于不同健康和病毒相关查询的信息,包括症状、预防措施和治疗程序。

相反,脸书过滤信息以防止虚假的希望和欺诈。在脸书和 Instagram 上,他们禁止“提及冠状病毒并制造紧迫感,如暗示限量供应或保证治愈或预防”的产品广告。例如,像口罩这样声称 100%保证防止病毒传播的广告将不会被允许。”这是他们支持世界卫生组织努力的方式。

人工智能的发展和伦理问题

对于如何将人们的隐私权与政府阻止致命疾病传播的责任结合起来,人们仍然没有达成共识。由于政府对如此多的应用程序进行监控和跟踪,现在可以获得大量数据,这些数据为人工智能提供了养分,并让它以几乎与病毒传播相当的速度发展。

人工智能允许整合所有数据源,世界各地产生的数据量将让数据科学家实验和测试他们的模型。希望在未来,如果不是现在,训练过的模型将更早地揭示流行病的迹象,并有助于防止它们的传播--甚至可能更快地治愈。

该病毒正在帮助推动人工智能的发展,并使一些有问题的应用程序合法化。这是一个重要的问题,即我们将来如何处理这个问题。

斯普林格已经免费发布了 65 本机器学习和数据书籍

原文:https://towardsdatascience.com/springer-has-released-65-machine-learning-and-data-books-for-free-961f8181f189?source=collection_archive---------0-----------------------

数百本书现在可以免费下载

斯普林格向公众发行了数百本免费书籍,内容涉及广泛的主题。这份清单总共包括 408 本书,涵盖了广泛的科技主题。为了节省您的时间,我创建了一个与数据和机器学习领域相关的所有书籍(65 本)的列表。

在这些书籍中,你会发现那些涉及该领域数学方面的书籍(代数、统计等),以及关于深度学习和其他高级主题的更高级的书籍。你也可以找到一些各种编程语言的好书,比如 Python、R 和 MATLAB 等。

如果你在寻找更多关于机器学习和数据的推荐书籍,你可以查看我在之前的文章

65 本书列表:

统计学习的要素

特雷弗·哈斯蒂、罗伯特·蒂布拉尼、杰罗姆·弗里德曼

http://link.springer.com/openurl?genre=book 国际标准书号=978-0-387-84858-7

带 R 的介绍性时间序列

安德鲁·梅特卡夫

http://link.springer.com/openurl?genre=book&ISBN = 978-0-387-88698-5

R 初学者指南

阿兰·祖尔,埃琳娜·n·伊埃诺,埃里克·梅斯特

http://link.springer.com/openurl?genre=book 国际标准书号=978-0-387-93837-0

进化计算简介

史密斯·艾本

http://link.springer.com/openurl?genre=book&ISBN = 978-3-662-44874-8

数据分析

西格蒙·勃兰特

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-03762-2

线性和非线性编程

戴维·卢恩伯格、余音·叶

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-18842-3

偏微分方程导论

大卫·博思威克

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-48936-0

机器人机械系统基础

豪尔赫·安吉利斯

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-01851-5

Python 的数据结构和算法

肯特·李,史蒂夫·哈伯德

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-13072-9

偏微分方程导论

彼得·奥尔弗

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-02099-0

数学建模的方法

托马斯·维特勒斯基,马克·鲍文

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-23042-9

24 小时内的乳胶

陈爱龙·达塔

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-47831-9

统计与数据分析简介

克里斯蒂安·休曼,迈克尔·舒梅克,沙拉布

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-46162-5

数据挖掘原理

马克斯·布拉默

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4471-7307-6

计算机视觉

理查德·塞利斯基

http://link.springer.com/openurl?genre=book 国际标准书号=978-1-84882-935-0

数据挖掘

沙鲁·阿加尔瓦尔

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-14142-8

计算几何

马克·德·伯格,奥特弗里德·张,马克·范·克雷维尔德,马克·奥维马斯

http://link.springer.com/openurl?genre=book&ISBN = 978-3-540-77974-2

机器人、视觉和控制

彼得·科克

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-54413-7

统计分析和数据显示

理查德·海伯格、伯特·霍兰德

http://link.springer.com/openurl?genre=book 国际标准书号=978-1-4939-2122-5

金融工程统计与数据分析

戴维·鲁佩特、戴维·马特森

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4939-2614-5

随机过程和微积分

乌韦·哈斯勒

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-23428-1

在袖珍计算器上对临床数据进行统计分析

伊尔科·兹温德曼·东·克利奥法斯

http://link.springer.com/openurl?genre=book&ISBN = 978-94-007-1211-9

在袖珍计算器上进行临床数据分析

伊尔科·兹温德曼·东·克利奥法斯

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-27104-0

数据科学设计手册

史蒂文·斯基亚纳

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-55444-0

机器学习简介

米罗斯拉夫·库巴特

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-63913-0

离散数学指南

杰拉德·奥里甘

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-44561-8

时间序列和预测简介

彼得·布罗克韦尔,理查德·戴维斯

http://link.springer.com/openurl?genre=book】T2&ISBN = 978-3-319-29854-2

多元微积分和几何

塞恩·迪宁

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4471-6419-7

科学数据的统计与分析

马西米利亚诺·博纳门特

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4939-6572-4

模拟计算系统

格奥尔格·斯特吕·法伦·莫勒

http://link.springer.com/openurl?genre=book 国际标准书号=978-1-84800-322-4

搜索方法

埃德蒙·k·伯克,格雷厄姆·肯德尔

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4614-6940-7

线性代数做对了

谢尔登·埃克斯勒

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-11080-6

线性代数

福尔克·梅尔曼·约尔格·列森

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-24346-7

代数

塞吉·兰

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4613-0041-0

理解分析

斯蒂芬·艾伯特

http://link.springer.com/openurl?genre=book 国际标准书号=978-1-4939-2712-8

线性编程

罗伯特·范德贝伊

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4614-7630-6

使用 R 理解统计

兰德尔·舒马赫,萨拉·托梅克

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4614-6227-9

统计学习入门

加雷斯·詹姆斯,丹妮拉·威滕,特雷弗·哈斯蒂,罗伯特·蒂布拉尼

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4614-7138-7

回归视角下的统计学习

理查德·伯克

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-44048-4

应用偏微分方程

J.大卫·罗根

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-12493-3

机器人技术

布鲁诺·西西里亚诺、洛伦佐·西亚维科、路易吉·维拉尼、朱塞佩·奥里奥罗

http://link.springer.com/openurl?genre=book&ISBN = 978-1-84628-642-1

回归建模策略

小弗兰克·哈勒尔。

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-19425-7

现代概率统计导论

F.M. Dekking,C. Kraaikamp,h . p . lopu haa,L.E. Meester

http://link.springer.com/openurl?genre=book&ISBN = 978-1-84628-168-6

Python 工作簿

本·斯蒂芬森

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-14240-1

医学中的机器学习——完整概述

伊尔科·兹温德曼·东·克利奥法斯

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-15195-3

面向对象的分析、设计和实现

布拉马·达坦、萨纳特·拉姆纳特

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-24280-4

数据科学导论

劳拉·伊瓜尔,桑蒂·塞古伊

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-50017-1

应用预测建模

马克斯·库恩,杰尔·约翰逊

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4614-6849-3

Python For ArcGIS

劳拉·塔特奥斯安

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-18398-5

数据库简明指南

彼得·莱克,保罗·克罗泽

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4471-5601-7

数字图像处理

马克·伯格·威廉·伯格

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4471-6684-9

带 R 的贝叶斯要领

克里斯蒂安·罗伯特·让-米歇尔·马林

http://link.springer.com/openurl?genre=book&ISBN = 978-1-4614-8687-9

机器人、视觉和控制

彼得·科克

http://link.springer.com/openurl?genre=book&ISBN = 978-3-642-20144-8

编程语言基础

肯特·李

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-70790-7

人工智能简介

沃尔夫冈·埃尔特尔

http://link.springer.com/openurl?genre=book 国际标准书号=978-3-319-58487-4

深度学习简介

桑德罗·斯坎西

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-73004-2

物理科学的线性代数与解析几何

亚历山德罗·赞皮尼·乔瓦尼·蓝迪

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-78361-1

应用线性代数

谢尔扎德·沙基班·彼得·奥尔弗

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-91041-3

神经网络和深度学习

沙鲁·阿加尔瓦尔

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-94463-0

数据科学和预测分析

伊沃·迪诺夫

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-72347-1

对计算机科学家的分析

迈克尔·奥伯古根伯格,亚历山大·奥斯特曼

http://link.springer.com/openurl?genre=book&ISBN = 978-3-319-91155-7

Excel 数据分析

赫克托·格雷罗

http://link.springer.com/openurl?genre=book&ISBN = 978-3-030-01279-3

Python 3 编程初学者指南

约翰·亨特

http://link.springer.com/openurl?genre=book&ISBN = 978-3-030-20290-3

Python 3 编程高级指南

约翰·亨特

http://link.springer.com/openurl?genre=book&ISBN = 978-3-030-25943-3

间谍大战 VOO:有什么不同吗?

原文:https://towardsdatascience.com/spy-vs-voo-is-there-any-difference-437defc2c3f3?source=collection_archive---------0-----------------------

杰森·布里斯科在 Unsplash 上的照片

两种流行的标准普尔 500 ETF 的分析与比较

在我之前的文章中,我用 Python 实现了一个关于美元成本平均策略的蒙特卡洛模拟。我研究了两只标准普尔 500 指数的交易所交易基金(ETF ),看看随着时间的推移,投资者可以期待什么样的回报。当我第一次查看两只基金(间谍基金和 VOO 基金)的时间序列时,我发现它们的曲线几乎完全相同,只是股价略有偏移。

历史时间序列显示几乎相同的曲线

这让我开始思考这两个基金之间是否有任何明显的区别。我买哪一个有关系吗?对于普通投资者来说,大多数人不会眨一下眼睛,不加思索就选择一个。当然,经过一些研究后,我发现人们经常附和说,你买哪家都没关系,因为两家都是由拥有类似资产的相同公司组成的。唯一的主要区别是费用率(拥有基金的成本),VOO 的费用为 0.03%,而 SPY 为 0.09%。

回顾一下,标准普尔 500 交易所交易基金是由股票市场上 500 家最大的公司组成的基金。然而,并不是每家公司在基金中都被赋予了同等的权重(资产持有的百分比)。例如,《间谍》和《VOO》的前五大持股公司是微软、苹果、亚马逊、脸书和 Alphabet(谷歌),而这些公司恰好也是美国和世界上市值最大的公司。500 家公司中的这五家合计占该基金总资产的近 20%。前五大持股之间的配置差异很大,但各基金之间的配置几乎相同。

ETF 中资产持有的百分比

如此相似,我买哪一个都没关系。为了更好地衡量,让我们通过 Python 中的分析来更仔细地看看这是如何转化为现实世界的。

数据

SPDR 标准普尔 500 ETF 信托基金(SPY)和先锋标准普尔 500 ETF (VOO)调整后的收盘股价数据从雅虎上撤下金融。日期范围限于 2010 年 9 月 9 日(VOO 可获得的第一个数据点)和当前日期,以便进行 1:1 的比较,因为 SPY 从 1993 年开始交易。

import datetime
import calendar
import pandas as pd
import pandas_datareader.data as web# Start date
start_date = datetime.datetime(2010,9,9)# Today’s Date
end_date = datetime.date.today()# Pull Data
df_spy = web.DataReader(‘SPY’,’yahoo’,start_date, end_date)
df_voo = web.DataReader(‘VOO’,’yahoo’,start_date,end_date)# Adjusted Close Price dataframe
df_both['SPY'] = df_spy['Adj Close']
df_both['VOO'] = df_voo['Adj Close']

分析

每日价格差异

正如我们在第一张图中看到的,这两只基金之间似乎存在股价抵消。当我计算每天两种股票价格之间的差异时,我们看到差异随着时间的推移而增加。九年前,间谍只比 VOO 多 10 美元。如今,差价扩大了 250%,达到 25 美元。另一方面,与此同时,我们可以看到这两只股票的估值都翻了三倍。

历史股价显示了明显的价格差异(左)。基金之间的价格差异随着时间的推移而增加(右)。

这是一个良好的开端,但它只向我们展示了原始的价格变化。查看百分比变化可能更有用。

每日百分比变化

我们可以通过每天的百分比变化来分析每天的股价波动情况。使用简单的百分比变化计算,我们可以得到两个基金相对于 SPY 的变化分布。

ETF 有较大百分比变化的 1 天周期数(左)。1 天后相对于 VOO 的间谍百分比变化分布(右)。

基于这一点,很明显,这些股票每天的变化基本上没有什么不同。看起来,在任何一天,这两只基金都有 50%的几率比另一只增长更多。然而,即使一个比另一个增加得多,随着相对百分比的变化,我们看到两个基金之间的每日波动差异很小(在+/- 0.25%之内)。

逐年百分比变化

但长期呢,因为我们大多数人会购买这些基金,期待它们随着时间的推移而升值,而不是日内交易它们。我重新计算了一下,查看了相隔一年的不同日期的股价的年度百分比变化。

对于 1 年的时间跨度,间谍相对百分比变化的中心趋势向负方向移动了一点,大约为-0.1%。这导致 VOO 在绝大多数时间里每年都有更大的增长。这与我们之前看到的每日百分比变化有很大不同,我们之前看到的两种基金都有相同数量的增加期。

5 年百分比变化

当我们把投资持续时间增加到 5 年,我们可以看到 VOO 几乎每 5 年就能打败间谍。在历史数据中,只有几个 5 年的时期,间谍击败了 VOO,即使是那些也仅仅是大于 0%的差异。平均相对百分比变化继续向负方向移动,这意味着与 VOO 相比,SPY 一直“表现不佳”(增长较少)。随着时间的推移,VOO 似乎变得更好了。

当我们比较 1 天、1 年和 5 年期间的统计数据时,SPY 和 VOO 之间的平均百分比变化随着投资期限的增加而增加。中值 1 天百分比变化差异显示为 0.0003%,而 1 年期和 5 年期分别增加到 0.0871%和 0.7158%。范围和标准差也随着持续时间的增加而增加。

统计数据:相对于 VOO 的间谍百分比变化

最后,我将持续时间延长到数据集给出的从 2010 年 9 月 9 日到当前日期的最大值,发现间谍增加了 234.1%,而 VOO 增加了 236.5%,导致 10 年间的差异为 2.4%。

外卖食品

那么这一切意味着什么呢?哪个更好?应该买哪个 ETF?从不同角度看这些数据后,短期来看,SPY 和 VOO 之间的差别很小。这两只股票之间的每日变化几乎相同。然而,将投资期限延长到 1 年甚至 5 年,会将微小的差异放大成更大的差异。尽管间谍软件和 VOO 软件之间的平均 5 年百分比变化只有 0.72%,但这在实践中可能是一大笔钱。在 VOO,10 万美元的间谍投资相当于 100,720 美元。在整个一生或职业生涯中,根据最初的投资,这可能会比退休时多几千美元。无论如何,由于两者的相似性,潜在投资者可以放心买入。

如果你有兴趣查看我的代码,请查看我的 GitHub

如果你觉得这很有趣或有启发性,请看看我做的其他分析:

SQL 案例研究:调查用户参与度的下降

原文:https://towardsdatascience.com/sql-case-study-investigating-a-drop-in-user-engagement-510b27d0cbcc?source=collection_archive---------14-----------------------

现实生活中的数据分析师案例研究

图片由皮克斯拜的 Gerd Altmann 提供

目录

  1. 问题
  2. 集思广益潜在原因
  3. 数据
  4. 分析:事件级聚合
  5. 分析:用户级聚合
  6. 分析:按地理位置划分的参与度
  7. 分析:电子邮件活动和点击率
  8. 分析:按设备划分的点击率
  9. 分析:每周文摘与重新参与电子邮件
  10. 摘要
  11. 建议
  12. 进一步的步骤

问题

这个案例可以在这里找到如果你想自己尝试或者跟随。

Yammer 是一个与同事交流的社交网络。个人通过在群组中发布来共享文档、更新和想法。Yammer 可以无限期免费使用,但如果公司希望获得管理控制,包括与 ActiveDirectory 等用户管理系统的集成,则必须支付许可费。

Yammer 注意到每周活跃用户数下降,“从当天开始的一周内至少记录一次参与事件的用户数”。约定定义为通过与产品交互进行某种类型的服务器调用(在数据中显示为“约定”类型的事件)。

我的目标是确定下图中下降的原因。

集思广益潜在原因

敬业度下降的潜在原因包括:

  • 产品相关问题。产品中可能有一个缺陷,阻止了用户使用该产品。同样,Yammer 可能在 8 月初发布了一个产品更新,但有相当一部分用户不喜欢。
  • 一年的时间。八月可能只是一个有很多用户去度假的月份。如果很大一部分用户是父母,这种情况可能会发生,因为他们倾向于在 9 月开学前度假。
  • 竞争对手。可能是竞争对手在 8 月初推出了竞争产品,或者竞争对手发布了现有产品的重大更新,导致用户流失。
  • 没有正确跟踪数据。有可能是数据管道没有工作,因此数据没有被正确跟踪。这是内部引起的问题,需要数据工程师的帮助来解决。
  • 营销事件。当推出大型促销活动时,公司会因为提供的折扣而吸引低质量的客户,这是正常的。因此,8 月份的下跌有可能是 7 月份一次大型营销活动的后遗症。

数据

表 1:用户

每行代表一个用户,信息与每个用户相关联。

表 2:事件

每行代表一个事件,这是用户在 Yammer 上执行的一个操作。这些事件包括登录事件、消息事件、搜索事件、用户通过注册漏斗时记录的事件,以及与收到的电子邮件相关的事件。

表 3:电子邮件事件

此表包含与已发送电子邮件相关的事件数据。它在结构上类似于上面的事件表。

表 4:累计期间

最后一个表是用于创建滚动时间段的查找表。

分析

事件级聚合

我想看的第一件事是什么事件被归类为“参与”。

*SELECT DISTINCT event_name, event_type
FROM tutorial.yammer_events
ORDER BY event_type, event_name ASC*

归类为“参与”的事件如下:

  • 主页
  • 喜欢 _ 消息
  • 注册
  • 搜索 _ 自动完成
  • 搜索 _ 点击 _ 结果 _1
  • 搜索 _ 点击 _ 结果 _2
  • 搜索 _ 点击 _ 结果 _3
  • 搜索 _ 点击 _ 结果 _4
  • 搜索 _ 点击 _ 结果 _5
  • 搜索 _ 点击 _ 结果 _6
  • 搜索 _ 点击 _ 结果 _7
  • 搜索 _ 点击 _ 结果 _8
  • 搜索 _ 点击 _ 结果 _9
  • search_click_result_10
  • 搜索 _ 运行
  • 发送消息
  • 查看 _ 收件箱
*with one as (
SELECT 
  EXTRACT('month' FROM occurred_at) as month,
  count(event_name) as event_count
FROM tutorial.yammer_events
GROUP BY
  month
)SELECT 
  *,
  (event_count - LAG(event_count) OVER (ORDER BY month ASC)) as diff
FROM one*

使用上面的查询,我们可以推断出 7 月和 8 月之间的雇佣差异是 18,037。

我想做的下一件事是逐月统计每个“参与”事件的发生次数,看看是否有任何重大事件导致了 7 月 28 日之后的下降。

*with two as (
with one as (
SELECT 
  CONCAT( EXTRACT('month' FROM occurred_at), '-', EXTRACT('year' FROM occurred_at)) as month_year,
  event_name,
  count(event_name) as event_count
FROM tutorial.yammer_events
WHERE event_type = 'engagement'
GROUP BY
  event_name,
  month_year
)
SELECT 
  *,
  CASE
    WHEN month_year = '5-2014'
      THEN 0
    WHEN month_year != '5-2014'
     THEN (event_count - LAG(event_count) OVER (ORDER BY event_name ASC, month_year ASC))
    ELSE NULL END AS abs_change
FROM one
)
SELECT *
FROM two
WHERE
  month_year = '8-2014'
  AND abs_change < 0
ORDER BY abs_change asc*

参与度的下降主要归因于主页、like_message_ view_inbox、send_message 和登录。似乎所有这些事件的减少都与用户登录减少有关。

接下来,我想在用户层面上分析数据,看看这种下降是由于用户数量的下降还是由于每用户参与次数的下降。

用户级聚合

*SELECT
  EXTRACT('month' from occurred_at) as month,
  count(DISTINCT user_id) as num_users
FROM tutorial.yammer_events
WHERE event_type = 'engagement'
GROUP BY month*

使用上面的代码,我发现了每月活跃用户的数量。

我们可以看到用户数量从 7 月份的 3058 下降到 8 月份的 2795,下降了 8.6%。这可能是参与度事件下降的原因,特别是主页、like_message、view_inbox、send_message 和 login。

我还观察了每个用户的参与度是否也下降了。

*SELECT
  EXTRACT('month' from occurred_at) as month,
  count(event_name) as num_events,
  count(DISTINCT user_id) as num_users,
  count(event_name)/count(DISTINCT user_id) as events_per_user
FROM tutorial.yammer_events
WHERE event_type = 'engagement'
GROUP BY month*

每个用户的活动数量从 30 个下降到 26 个,这意味着参与度下降了 13%,这也是一个相当大的数字。

因此,我们需要找出为什么活跃用户数量下降,以及每个用户的参与度下降。

电子邮件活动和点击率

*SELECT
  action,
  EXTRACT('month' FROM occurred_at) AS month,
  count(action) as num_emails
FROM tutorial.yammer_emails
GROUP BY
action, month
ORDER BY
action, month*

我想对电子邮件活动进行汇总,看看发送的电子邮件、点击率(CTR)或其他可能导致非活跃用户减少的因素是否有所变化。

很快,我注意到发送的邮件数量(每周摘要和重新参与)和打开的邮件数量稳步增长,但点击率有所下降。从 7 月到 8 月,打开的电子邮件增加了 6.5%,但点击率下降了 27%。

按设备划分的点击率

我想更深入地了解点击率,看看这种下降是否与设备有关。这可能与操作系统的类型(IOS 对 Android)或移动对桌面有关。

*with emails as(
SELECT 
  *,
  CONCAT(EXTRACT('day' FROM occurred_at), '-', EXTRACT('month' FROM occurred_at), '-',  EXTRACT('year' FROM occurred_at)) as date,
  EXTRACT('month' FROM occurred_at) as month
FROM tutorial.yammer_emails emails
), events as (
  SELECT DISTINCT 
    user_id,
    CONCAT(EXTRACT('day' FROM occurred_at), '-', EXTRACT('month' FROM occurred_at), '-',  EXTRACT('year' FROM occurred_at)) as date,
    device,
    EXTRACT('month' FROM occurred_at) as month
  FROM tutorial.yammer_events
  ORDER BY user_id ASC
)
SELECT 
  device,
  emails.month,
  count(emails.user_id)
FROM emails
LEFT JOIN events ON
  emails.user_id = events.user_id
  AND emails.date = events.date
WHERE action = 'email_clickthrough'
GROUP BY device, emails.month*

使用上面的查询,我注意到从 7 月到 8 月,笔记本电脑和电脑的点击率保持稳定,但平板电脑和手机的点击率不稳定。

通过将设备名称分为“手机”、“平板电脑”和“笔记本电脑”,我可以确定这是否属实。

*with emails as(
SELECT 
  *,
  CONCAT(EXTRACT('day' FROM occurred_at), '-', EXTRACT('month' FROM occurred_at), '-',  EXTRACT('year' FROM occurred_at)) as date,
  EXTRACT('month' FROM occurred_at) as month
FROM tutorial.yammer_emails emails
), events as (
  SELECT DISTINCT 
    user_id,
    CONCAT(EXTRACT('day' FROM occurred_at), '-', EXTRACT('month' FROM occurred_at), '-',  EXTRACT('year' FROM occurred_at)) as date,
    device,
    EXTRACT('month' FROM occurred_at) as month
  FROM tutorial.yammer_events
  ORDER BY user_id ASC
)
SELECT 
  CASE
    WHEN device IN ('amazon fire phone', 'nexus 10', 'iphone 5', 'nexus 7', 'iphone 5s', 'nexus 5', 'htc one', 'iphone 4s', 'samsung galaxy note', 'nokia lumia 635', 'samsung galaxy s4') THEN 'mobile'
    WHEN device IN ('ipad mini', 'samsung galaxy tablet', 'kindle fire', 'ipad air') THEN 'tablet_ipad'
    WHEN device IN ('dell inspiron desktop', 'macbook pro', 'asus chromebook', 'windows surface', 'macbook air', 'lenovo thinkpad', 'mac mini', 'acer aspire desktop', 'acer aspire notebook', 'dell inspiron notebook', 'hp pavilion desktop') THEN 'laptop_comp'
    ELSE null end as device_type,
  emails.month,
  count(emails.user_id)
FROM emails
LEFT JOIN events ON
  emails.user_id = events.user_id
  AND emails.date = events.date
WHERE action = 'email_clickthrough'
GROUP BY device_type, emails.month*

事实上,点击率的下降似乎特别归因于移动设备和平板电脑。

每周文摘 vs 再次接洽电子邮件

到目前为止,我认为缺乏参与的原因是 7 月到 8 月电子邮件点击率的下降。为了收集更多的信息,我想看看我们是否可以通过电子邮件类型进一步缩小问题的范围。

*with one as (
SELECT 
  *,
  EXTRACT('month' from occurred_at) as month,
  CASE WHEN (LEAD(action, 1) OVER (PARTITION BY user_id ORDER BY occurred_at ASC)) = 'email_open' THEN 1 ELSE 0 END AS opened_email,
  CASE WHEN (LEAD(action, 2) OVER (PARTITION BY user_id ORDER BY occurred_at ASC)) = 'email_clickthrough' THEN 1 ELSE 0 END AS clicked_email
FROM
  tutorial.yammer_emails
)SELECT 
  action,
  month,
  count(action),
  sum(opened_email) as num_open,
  sum(clicked_email) as num_clicked
FROM
  one
WHERE action in ('sent_weekly_digest','sent_reengagement_email')
GROUP BY
  action,
  month
ORDER BY
  action,
  month*

很明显,点击率的下降是由于每周摘要邮件,而不是重新参与电子邮件。

摘要

通过我的分析,我可以得出以下结论:

参与度的下降主要归因于五个参与度事件(主页、like_message_ view_inbox、send_message 和登录)的下降。

然后,我发现事件的减少是由总活跃用户 MoM 的减少以及每个用户参与度的降低引起的。

在我查看了电子邮件表之后,我注意到从 7 月到 8 月,尽管打开的电子邮件数量有所增加,但点击率却显著下降。

通过按设备类型(手机、平板电脑、笔记本电脑)划分点击率,我注意到点击率的下降归因于手机和平板电脑设备。

最后,点击率的下降归因于每周摘要电子邮件,而不是重新参与电子邮件。

建议

我的建议是立即深入查看专门针对移动设备和平板电脑的每周摘要邮件。这可能是一个技术问题,让用户很难点击电子邮件,或者只是一个 UX 问题,即电子邮件的内容和布局无法吸引用户点击。一个好的第一步是看看从 7 月到 8 月发生了什么变化,并向后追溯。

进一步分析

其他一些可以考虑的因素包括:

  • 查看变化是否归因于少数用户(Pareo 原则)
  • 群组分析,了解原因是否是由于用户生命周期短
  • 按语言分析
  • 按地理分析

感谢阅读!

特伦斯·申

ShinTwin|让我们连线上 LinkedIn

SQL 备忘单

原文:https://towardsdatascience.com/sql-cheat-sheet-776f8e3189fa?source=collection_archive---------0-----------------------

PostgreSQL 中使用的标准 SQL 语法入门指南

来自 Pexelspanumas nikhomkhai 的照片

SQL 是学习数据分析最重要的编码语言。有些人可能会认为 Python 和 R 同等重要,但说到分析师必须拥有的最常用工具,那就是 SQL。

根据 Dataquest.io 的报道,几乎所有的技术巨头都使用 SQL。优步、网飞、Airbnb——这样的例子不胜枚举。即使在像脸书、谷歌和亚马逊这样已经建立了自己的高性能数据库系统的公司中,数据团队也使用 SQL 来查询数据和执行分析。

像每一种语言一样,你需要不断练习来理解和掌握概念。在我看来,一旦你理解了代码的基本结构,SQL 是最容易使用的语言之一。在本文中,我分享了开始使用 SQL 查询的必要步骤。

标准 SQL 结构

这是一系列 PostgreSQL 备忘单的第 1 部分,将涵盖SELECTFROMWHEREGROUP BYHAVINGORDER BYLIMIT

从单个表中提取结果的查询的基本结构如下。

SELECT 
	COLUMN_NAME(S)
FROM
	TABLE_NAME
WHERE
	CONDITION
GROUP BY
	COLUMN_NAME(S)
HAVING
	AGGREGATE_CONDITION
ORDER BY
	COLUMN_NAME
LIMIT
	N

什么是 SQL?

SQL (读作“ess-que-el”)代表结构化查询语言。SQL 用于与数据库通信。它是关系数据库管理系统的标准语言。SQL 语句用于执行更新数据库数据或从数据库中检索数据等任务。

什么是关系数据库管理系统?

RDBMS 将数据组织成包含行和列的表格。术语“关系”意味着每个表中的值相互之间有关系。

  • 行—也称为记录
  • 列—也称为字段,具有描述性名称和特定的数据类型。

PostgreSQL 是什么?

PostgreSQL 是一个通用的关系数据库管理系统,是最先进的开源数据库系统。

其他常见的数据库管理系统有 MySQL、Oracle、IBM Db2 和 MS Access。

我们开始吧!

挑选

SELECT 语句用于从数据库中选择数据。返回的数据存储在结果表中,称为结果集。

特定列

SELECT
	COLUMN_1,
	COLUMN_2
FROM
	TABLE_NAME

所有列

使用*可以查询表中的每一列

SELECT *
FROM
	TABLE_NAME

不同的列

查找列中所有唯一的记录

SELECT 
	DISTINCT(COLUMN_NAME)
FROM
	TABLE_NAME

统计所有行

如果你想知道整个表格中的所有值,使用COUNT(*)你将得到一个单一的数字。

SELECT
	COUNT(*)
FROM
	TABLE_NAME

计算不同的值

如果您想知道一列中不同值的数量,使用COUNTDISTINCT,您将得到一个代表一列中唯一值总数的数字

SELECT 
	COUNT (DISTINCT COLUMN_NAME)
FROM
	TABLE_NAME

在哪里

使用WHERE子句,你可以创建条件来过滤掉你想要或不想要的值。

注意— WHERE总是用在GROUP BY之前(稍后会详细介绍)

SELECT *
FROM
	TABLE_NAME
WHERE
	CONDITION

情况

SQL 中可以使用多种条件。下面是一些包含学生在校成绩的表格示例。您只需要指定WHERE一次,为了便于举例,我在每个步骤中都包含了WHERE

WHERE FIRSTNAME      = 'BOB'      -- exact match
WHERE FIRSTNAME     != 'BOB'     -- everything excluding BOB 
WHERE NOT FIRSTNAME  ='BOB'    -- everything excluding BOBWHERE FIRSTNAME IN ('BOB', 'JASON')       -- either condition is met
WHERE FIRSTNAME NOT IN ('BOB', 'JASON')   -- excludes both valuesWHERE FIRSTNAME = 'BOB' AND LASTNAME = 'SMITH'  -- both conditions 
WHERE FIRSTNAME = 'BOB' OR FIRSTNAME = 'JASON'  -- either conditionWHERE GRADES > 90           -- greater than 90
WHERE GRADES < 90           -- less than 90
WHERE GRADES  >= 90         -- greater than or equal to 90
WHERE GRADES  <= 90         -- less than or equal to 90WHERE SUBJECT IS NULL       -- returns values with missing values
WHERE SUBJECT NOT NULL      -- returns values with no missing values

条件—通配符

LIKE运算符用于在WHERE子句中搜索列中的指定模式。当您通过LIKE操作符时,在''中大写和小写都很重要。

有两个通配符经常与LIKE操作符一起使用:

  • % -百分号代表零个、一个或多个字符
  • _ -下划线代表单个字符
WHERE FIRSTNAME LIKE ‘B%’ -- finds values starting uppercase BWHERE FIRSTNAME LIKE ‘%b’ -- finds values ending lowercase bWHERE FIRSTNAME LIKE ‘%an%’ -- find values that have “an” in any positionWHERE FIRSTNAME LIKE ‘_n%’ -- find values that have “n” in the second positionWHERE FIRSTNAME LIKE ‘B__%’ -- find values that start with “B” and have at least 3 characters in lengthWHERE FIRSTNAME LIKE ‘B%b’ -- find values that start with “B” and end with “b”WHERE FIRSTNAME LIKE ‘[BFL]’ -- find all values that start with ‘B’, ‘F’ OR ‘L’WHERE FIRSTNAME LIKE ‘[B-D]’ -- find all values that start with ‘B’, ‘C’, OR ‘D’WHERE FIRSTNAME LIKE ‘[!BFL]%’ -- find everything exlcusing values that start with ‘B’, ‘F’ OR ‘L’WHERE FIRSTNAME NOT LIKE ‘[BFL]%’ -- same as above. excludes values starting with ‘B’, ‘F’, OR ‘L’WHERE GRADES BETWEEN 80 and 90 -- find grades between 80 and 90

分组依据

GROUP BY函数帮助计算所选列的汇总值。它经常与聚合函数一起使用(COUNTSUMAVGMAXMIN)。

SELECT
	SUBJECT,	
	AVG(GRADES)
FROM
	STUDENTS
GROUP BY
	SUBJECT

上面的查询将对每个科目进行分组,并计算平均成绩。

SELECT
	SUBJECT,	
	COUNT(*)
FROM
	STUDENTS
GROUP BY
	SUBJECT

上面的查询会计算出各科学生的(计数)。

拥有

HAVING子句与WHERE相似,但适用于过滤集合函数HAVING功能在GROUP BY之后,相比之下WHEREGROUP BY之前。

如果我们想知道哪一门课的平均成绩在 90 分以上,我们可以使用下面的例子。

SELECT
	SUBJECT,	
	AVG(GRADES)
FROM
	STUDENTS
GROUP BY
	SUBJECT
HAVING 
	AVG(GRADES) >= 90

以...排序

使用ORDER BY函数,您可以指定您想要的值排序方式。继续前面的学生表。

SELECT
	*
FROM
	STUDENTS
ORDER BY
	GRADES DESC

当默认使用ORDER BY时,排序将按照升序进行。如果要下行,需要在列名后面指定DESC

限制

在 Postgres 中,我们可以使用LIMIT函数来控制在查询中输出多少行。例如,如果我们想找到分数最高的前 3 名学生。

SELECT
	*
FROM
	STUDENTS
ORDER BY
	GRADES DESC
LIMIT
	3

由于我们使用了ORDER BY DESC,我们将分数最高的学生排在最前面——现在限制为 3 个值,我们看到了前 3 名

概观

希望您可以使用该入门指南来了解在从单个表中查询数据时使用的标准 SQL 语法。在 SQL 中你可以做更多的事情,我将分享更多的扩展高级语法的 SQL 备忘单。

如果你想学习具体的技术,可以看看我的其他教程。

SQL 数据库设计基础及示例

原文:https://towardsdatascience.com/sql-database-design-basics-with-example-8dcce4b0c687?source=collection_archive---------41-----------------------

这篇文章解释了数据库设计的基本概念,并通过一个生动的例子展示了如何创建数据库模式。

图片来自 Piqsels (CC0)

DB 架构师必须设计一个适合特定解决方案的关系数据库,这是一种常见的做法。

一个星期五的晚上,我乘通勤火车下班回家,我在想创建一种招聘服务。据我所知,现有的服务都不允许对候选人进行快速评估,对某些技能、项目或职位进行复杂的筛选,或者排除某些技能、职位或项目。最大使用范围是按公司过滤,部分按技能过滤。

在这一系列文章中,我将稍微放纵一下自己,用一些来自我生活中的非技术例子来尝试打破严谨的技术写作。

在这一部分中,我将讲述关系数据库设计的基础知识,并举例说明一个招聘服务的 MS SQL Server 数据库设计

现在,在接下来的文章中,我将向您展示如何通过 SQL Server 的数据生成器向数据库填充数据,并使用免费的 dbForge Search 工具搜索数据库数据和对象。我将使用db forge Studio for SQL Server来实现我的示例的图表,使用 dbForge 数据泵来导入和导出数据。

数据库设计基础

为了设计数据库模式,让我们回忆一下 7 种范式以及规范化和反规范化的概念。它们是所有设计规则的基础。

我来详细描述一下 7 种范式:

1.一对一的关系:

1.1 强制性关系:

一个例子是一个有护照的公民(每个公民都必须有一本护照,而护照是每个公民的)

这种关系通过两种方式实现:

  1. 1.1 在一个实体中(表):

Img.1 .“公民”实体

这里,公民表表示公民实体,PassportData 属性(字段)包含公民的所有护照数据,不能为空(非 NULL)

  1. 1.2.在两个不同的实体(表)中:

img . 2 . Citizen 和 PassportData 实体之间的关系

Citizen 表表示公民实体,PassportData 表表示公民护照数据的实体。citizen 实体包含 PassportID 属性(字段),该属性引用 PassportData 表的主键。然而,passport 数据实体具有 CitizenID 属性(字段),该属性引用公民表的 CitizenID 主键。

保证 CitizenID 字段和 PassportData 表的完整性也很重要,以提供一对一的关系。也就是说,Citizen 表中的 PassportID 字段和 PassportData 表中的 CitizenID 字段必须引用相同的记录,就像第 1.1.1 段中说明的一个实体(表)一样。

1.2 可选关系:

这里的一个例子是,一个人可能有护照数据,但可能没有指定的国家。所以,在第一种情况下,他是某个国家的公民,而在第二种情况下,他不是。

这种关系通过两种方式实现:

  1. 2.1 在一个实体中(表):

Img.3 .人实体

这里,person 表表示 Person 实体,PassportData 属性(字段)包含个人的所有护照数据,可以为空(NULL)

1.2.2 在两个实体(表格)中:

img . 4 . Person 和 PassportData 的关系

这里,person 表表示 Person 实体,passport 数据表表示人员的 passport 数据实体(即 Passport 本身)。person 实体包含 PassportID 属性(字段),该属性引用 PassportData 表的主键。然而,护照数据实体在 Person 表中有 PersonID 属性(字段)。Person 表的 PassportID 字段可以为空(NULL)。

保证 PersonID 字段和 PassportData 表的完整性也很重要,以提供一对一的关系。也就是说,Person 表的 PassportID 字段和 PassportData 表的 PersonID 字段必须引用相同的记录,就像它是第 1.2.1 段中所示的一个实体(表)一样,或者这些字段必须是未指定的,即包含 NULL。

2.一对多关系

2.1 强制性关系:

父母和他们的孩子就是一个例子。每个父母至少有一个孩子。

您可以通过两种方式实现这种关系:

2.1.1 在一个实体中(表):

Img.5 .母实体

在这里,父表代表父实体,ChildList 属性(字段)包含关于子级的信息,即子级本身。此字段不能为空(非 NULL)。ChildList 字段类型通常是半结构化数据(NoSQL ),如 XML、JSON 等。

2.1.2 在两个实体(表格)中:

Img.6 .父子实体的关系

这里父表代表父实体,子表代表子实体。子表的 ParentID 字段引用父表的主 ParentID 键。子表的 ParentID 字段不能为空(非 NULL)。

2.2)可选关系:

例如,一个人可能有孩子,也可能没有孩子。

这种关系通过两种方式实现:

2.2.1 在一个实体中(表):

Img.7 .人实体

这里,父表表示父实体,而 ChildList 属性(字段)包含有关子级的信息,即子级本身。该字段可以为空。通常的 ChildList 字段类型是半结构化数据(NoSQL ),如 XML、JSON 等。

2.2.2 在两个实体(表格)中:

Img.8 .个人和子实体之间的关系

这里父表代表父实体,子表代表子实体。子表的 ParentID 字段引用父表的主 ParentID 键。子表的 ParentID 字段可以为空(NULL)。

此外,如果子实体和父实体(表)具有相同的属性(字段)集,而不引用父实体,则存在引用自身的第三种单实体实现方式:

Img.9 .具有自我参照的人实体

这里,Person 实体(表)包含 ParentID 属性(字段),该属性引用同一个表 Person 的主要 PersonID 键,并且可以有一个空值(NULL)。

这是具有可选性质的多对一关系的实现。

3.多对一关系

这种关系反映了上述一对多关系。这是子实体和父实体之间的关系,如果孩子至少有一个父母,这种强制性关系是可能的,如果我们收留所有的孩子,包括孤儿之家的孩子,那么这种关系就具有选择性。

通过添加引用相应实体主键的必要属性,一对多和多对一关系也可以通过两个以上的实体来实现。该实现类似于上面第 1.1.2 和 1.2.2 段中的示例。

4.多对多关系

在这种情况下,可以由一个人或几个人拥有的房地产就是一个例子。同时,一个人可以拥有多套房屋,也可以拥有多套房屋的所有权份额。

您可以按照上面针对前面的关系描述的方式来实现与 NoSQL 的关系。然而,在关系模型中,这种关系通常通过 3 个实体(表)来实现:

Img.10 .该人与不动产实体之间的关系

在这里,person 和 RealEstate 表相应地表示了 Person 和 real estate 的实体。这些实体(表)通过 PersonRealEstate 实体(表)的 PersonID 和 RealEstateID 属性(字段)相关联,这些属性相应地引用 Person 表的 PersonID 和 RealEstate 表的 RealEstateID 的主键。注意这对(PersonIDRealEstateID)对于 PersonRealEstate 表总是唯一的,因此它可以是 PersonRealEstate 链接实体(表)的主键。

通过添加引用相应实体的主键的必要属性,这种关系可以通过 3 个以上的实体来实现。这种实施类似于段落 1.1.2 和 1.2.2 中描述的示例。

你可能会想,这 7 种范式在哪里?

嗯,他们在这里:

  1. Par.1 (par.1.1 和 par.1.2)是第一个和第二个正式规则。
  2. 第 2 款(第 2.1 款和第 2.2 款)是第三条和第四条正式规则。
  3. 第 3 段(类似于第 2 段)是第五条和第六条正式规则。
  4. 第四杆是第七条正式规则。

只是这 7 个范式在上面的文本中被分组为 4 个功能块。

标准化消除了数据冗余,因此降低了数据异常的风险。但是,分解实体(表)时的规范化会导致为数据操作(插入、更新、选择和删除)构建更复杂的查询。

相反的过程是反规格化。它通过添加冗余数据简化了数据访问的查询处理(例如,在上面的 par。2.1.1 和 2.2.1 借助半结构化数据(NoSQL))。

你确定你掌握了 7 种范式的要点吗?你真的懂了,而不仅仅是熟悉它。问问你自己,在几个小时内,你是否能够为任何数据域或任何信息系统设计一个数据库模型,即使有过多的实体。您可以稍后通过询问分析和客户代表来润色复杂和细节。

如果这个问题让你措手不及,你认为完成这个任务是非常不可能的,那么你知道 7 种范式,但不理解它们。

不知何故,资料中没有说明,实体之间的这些关系不仅仅是虚构的,而是被发现的。也就是说,从一开始,它们实际上就存在于主体和客体之间的现实世界中。

除此之外,这些关系可以改变,从一对一到一对多,或多对一,或多对多,改变它们的强制性质或保留它。

我认为你应该试着观察人们,并发现主体之间以及主体和客体之间的现有关系(上面的例子说明了公民和护照是一对一的关系,具有强制性质,而人和护照是一对一的关系,是可选的)。

当您深入了解了 7 种范式后,您就可以轻松地为任何信息系统设计任何复杂程度的数据库模型。

除此之外,你将会知道你可以用许多不同的方式实现这些关系,而且这些关系本身也可以改变。因此,数据库模型(模式)是某个时间点实体之间关系的快照。因此,有必要指定两个实体,它们是真实世界或领域对象的映像,并考虑到未来的变化来指定它们之间的关系。

一个设计良好的数据库模型,适当考虑现实和主题领域中的关系变化,在很长一段时间内不需要任何更改。这对于数据存储尤其重要,在数据存储中,变化涉及到重新保存大量数据,从几千兆字节到几兆兆兆字节。

注: 在关系数据库模型中,是实体之间的关系,行(元组)就是这些关系的例子。但是为了简单起见,我们通常用表来表示实体,用行来表示实体的实例,用外键关系来表示实体之间的关系。

为招聘设计数据库模式

在本文的第一部分描述了数据库设计基础之后,现在让我们为招聘创建一个数据库模式。

首先,我们需要定义哪些信息对于搜索求职者的公司员工来说是重要的:

  1. 对于人力资源经理:

1)申请人曾经工作过的公司。

2)申请人曾经在这些公司担任的职位。

3)申请人在工作中使用的技能,他们在每个公司和每个职位的就业时间,每种技能使用的时间。

2.对于技术专家:

1)申请人以前工作过的职位。

2)申请人在工作中使用的技能。

3)申请人参与的项目。除此之外,了解申请人在每个职位和每个项目中的工作时间以及每项技能的使用时间也很重要。

让我们首先确定必要的实体:

公司和雇员具有多对多的关系,因为雇员可以为不同的公司工作,而这些公司有许多雇员。

这同样适用于职位和员工,因为许多员工可以在一个公司的一个职位上工作,也可以在不同的公司工作。因此,一个雇员可以在一个公司或许多不同的公司的不同岗位上工作。因此,职位和公司之间也是多对多的关系。

“项目”实体遵循相同的逻辑:项目以多对多的方式与上述所有其他实体相关。

为了简单起见,让我们假设一个雇员在一个项目中使用一套技能。然后,项目和技能的关系也是多对多。

考虑到指定员工在这家或那家公司、特定职位和特定项目中任职时间的重要性,我们的数据库模式可能具有以下 ER 图:

Img.11 .招聘服务的数据库模式

在这里,JobHistory 表表示每个雇员的工作历史的实体,即实现雇员、公司、职位和项目之间多对多关系的简历。

项目和技能是多对多的关系,因此,它们在 ProjectSkill 实体的帮助下联系在一起。

如果您理解了主题之间、主题和对象之间的关系,即数据库设计规范,您可以在一个小时之内在一张纸上创建一个类似的模式。

在这里,如果我们通过 XML、JSON 形式的半结构化数据(NoSQL)将技能放入项目实体中,或者简单地用分号列出技能的名称,我们可以简化模式和数据添加。但是这将使按技能选择分组和按特定技能筛选变得困难。

结论

如您所见,设计系统只是将对象和主体从现实变为数据库实体,其中这些实体之间的关系在某个时间点是固定的,并考虑到未来的变化。我们究竟从现实中获取什么并作为一个模式实体来实现,以及我们在模型中建立什么样的关系,这取决于我们现在和将来对信息系统的总体要求。也就是说,我们想要获得当前时刻和未来某个时间的哪些数据。

原载于 2020 年 7 月 22 日【https://blog.devart.com】

绝对初学者的 SQL

原文:https://towardsdatascience.com/sql-for-absolute-beginners-2770bf5075da?source=collection_archive---------33-----------------------

利用您的禁闭时间来更新您的数据库知识

发音是续作,不是松鼠!由阿马尔·伊萨Unsplash 上拍摄的照片

SShakespearer 在隔离期间写了李尔王。牛顿在隔离期间为他的运动定律奠定了基础。人们在这个隔离区发明东西——嗯,愚蠢的东西

也许你不是诗人、科学大师或杰出的发明家。但至少你可以学习 SQL 来给你的简历拉皮条。或者让自己从四面墙的沉闷中转移注意力。

有一些关于 SQL 的不错的免费教程,例如这里这里。但是缺少的是一个易于理解并带有真实知识的实践指南,而不是浏览器中的文本示例。这件作品填补了空白。

当你阅读这篇文章的时候,你被鼓励去编码。一旦你学完了,你就掌握了 SQL 的基础知识!

[## 如果你因为冠状病毒而被锁在家里,11 堂最好的数据科学课

不用花一分钱就可以开始你的职业生涯

towardsdatascience.com](/11-best-data-science-classes-if-youre-locked-home-because-of-coronavirus-ca7d2d74a454)

基础知识

从严格意义上来说,SQL 不是一种编程语言,而是一种结构化查询语言——这就是 SQL 的意思——用于与数据库进行交互。后者通常位于数据库管理系统(DBMS)中。

最流行的数据库管理系统是 MySQL。也有其他的,但是我们将在这篇文章中使用这个,因为它是免费的和可访问的。

我建议你用全装,即使很繁琐。如果你曾在数据科学或相关行业工作,安装东西将是你最小的障碍。此外,边做边学将比浏览器中的文本练习教给你多一百倍的东西,即使这需要更长的时间。

我将带你了解我是如何在 Mac 上安装 MySQL 的。根据您使用的系统,您的安装可能会略有不同。但是不用担心——MAC OS 和 Linux 系统非常相似。此外,MySQL 有非常好的文档记录,因此您应该能够在任何系统上安装它,而不会出现更大的问题。

还是续集,不是松鼠。在 Unsplash 上由 Demi-Felicia Vares 拍摄的照片

装置

下载页面,您需要选择与您的系统兼容的档案。我选择了 DMG 档案的早期版本,因为我的 Mac 电脑太旧了,最新版本的 MySQL 无法在上面运行。在下载之前,该页面会要求您在 Oracle 创建一个帐户,但是您可以跳过这一步。

双击 DMG 文件,等待 10 分钟左右,你就可以开始了!但是一定要保存安装时给你的密码,否则你会后悔的…

在 Mac 上,你只需要进入系统偏好设置并激活服务器。然后,在.bash_profile中设置几个别名可能会很方便:

alias mysql=/usr/local/mysql/bin/mysql alias mysqladmin=/usr/local/mysql/bin/mysqladmin

完成后不要忘记输入source bash_profile,这样别名就会被激活。

测试

让我们检查一下安装是否正常!如果输入mysql --version,应该会得到这样的东西:

/usr/local/mysql/bin/mysql  Ver 14.14 Distrib 5.7.29, for macos10.14 (x86_64) using  EditLine wrapper

现在,您需要连接到您的 MySQL 服务器并重置密码:

mysql -u root -p

选项-p打开密码提示,你必须输入你在安装阶段得到的密码。还要注意,您是作为根用户连接的,因为您可能没有作为本地用户连接的权限(至少我没有)。

你应该得到这样的提示:mysql>。现在让我们设置一个新密码:

mysql> SET PASSWORD = PASSWORD('your_new_password');
mysql> FLUSH PRIVILEGES;

在每个命令之后,您应该会看到类似于Query OK, 0 rows affected, 1 warning (0.00 sec)的内容。从现在起,当您连接到服务器时,将使用新密码。

现在,让我们做最后的检查:

mysql> SHOW DATABASES;+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+4 rows in set (0.01 sec)

如果你得到了上述的东西,你就可以开始了!

嘿!还是念 SEQUEL。Vincent van Zalinge 在 Unsplash 上拍摄的照片

放下麦克风

以下是与 SQL 共存的最重要的命令——我喜欢称之为 MIC:

  • QUIT\q退出与服务器的连接。
  • 你可以向help;\h求助。
  • 您可以使用\c清除缓冲区。当你写一个多行命令,但是有错误的时候,这很有帮助。

在我们开始破解一些真实的数据库之前,让我们先来探索一下基本的命令。试试这个:

mysql> SELECT VERSION(), CURRENT_DATE;+-----------+--------------+
| VERSION() | CURRENT_DATE |
+-----------+--------------+
| 5.7.29    | 2020-04-09   |
+-----------+--------------+1 row in set (0.00 sec)

如果您研究一下这个查询,您会注意到一些事情:

  • 所有的查询都不区分大小写:它们可以写成UPPERCASElowercasemixedCASE。但习惯上是全部大写。
  • 除了像QUIT这样的少数例外,查询总是以分号;结尾
  • 您可以将一个查询扩展到多行,如下所示…
mysql> SELECT_VERSION(),
    -> CURRENT_DATE();
  • …或者您可以将两个查询放在一行中,如下所示:
mysql> SHOW DATABASES; SELECT_VERSION(), CURRENT_DATE();
  • 您可以像这样注释查询:
mysql> SELECT_VERSION(), CURRENT_DATE(); /* shows MySQL version and date */
  • SQL 的输出总是以表格形式显示。
  • 此外,您将始终看到输出有多少行—即数据点—以及从服务器获取它花了多长时间。

创建您的第一个数据库!

让我们开始一个名为fancydata的新数据库。首先,您需要确保您是它的所有者,并且拥有所有权限:

mysql> GRANT ALL ON fancydata.* TO 'root'@'localhost';

您可能想要更改您的用户——以及您的主机名,这取决于您登录的方式。您应该会收到一条确认消息,表明该查询是正确的。

现在,让我们创建数据库:

mysql> CREATE DATABASE fancydata;

这创建了数据库,但是不能确保您当前正在使用这个数据库。为此,您需要:

mysql> USE fancydata
Database changed

注意我们在USE后面不需要分号。如果你放一个在那里,它不会伤害。如果您开始一个新的会话,您需要再次运行USE-命令。

现在我们可以在这个数据库中创建一个表:

CREATE TABLE customer (firstname VARCHAR(20), lastname VARCHAR(20), birthday DATE, sex CHAR(1));

VARCHAR后面的数字指定了该值可以包含的最大字符数。

让我们检查该表是否存在:

mysql> SHOW TABLES;+---------------------+
| Tables_in_fancydata |
+---------------------+
| customer            |
+---------------------+

完美!现在让我们检查表格的结构:

mysql> DESCRIBE customer;+-----------+-------------+------+-----+---------+-------+
| Field     | Type        | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+-------+
| firstname | varchar(20) | YES  |     | NULL    |       |
| lastname  | varchar(20) | YES  |     | NULL    |       |
| birthday  | date        | YES  |     | NULL    |       | 
| sex       | char(1)     | YES  |     | NULL    |       |
+-----------+-------------+------+-----+---------+-------+

看起来不错!现在我们有一张空桌子。我们可能想用一些数据填充它,如下所示:

mysql> INSERT INTO customer VALUES ('Brian', 'Smith', '1987-05-07', 'x');

你已经输入了一个客户,布莱恩·史密斯,她今年 33 岁,还没有结婚。

如果你不知道一个值,你可以让它为空。例如,如果您不知道客户的生日,请:

INSERT INTO customer VALUES ('Luca', 'Maltoni', NULL, 'm');

但是逐行输入数据相当繁琐。如果您有一个包含数据的customer-file.txt,那么您可以使用以下命令加载它:

mysql> LOAD DATA LOCAL INFILE '/path/to/customer-file.txt' INTO TABLE customer;

该文件应该具有与表相同的结构,其中每个条目用制表符分隔。如果有空值,您需要在文件中将它们标记为\N

好了,松鼠笑话讲够了。 Saori OyaUnsplash 上拍摄的照片

检索您的数据

您可以看到这样一个表格中的内容:

mysql> SELECT * from customer;+-----------+----------+------------+------+
| firstname | lastname | birthday   | sex  |
+-----------+----------+------------+------+
| Brian     | Smith    | 1987-05-07 | x    |
| Luca      | Maltoni  | NULL       | m    |
+-----------+----------+------------+------+

如果您发现表格中的某些数据错误或缺失,您可以删除所有内容,如下所示:

mysql> DELETE FROM customer;

或者,您可以更新单行,如下所示:

mysql> UPDATE customer SET birthday = '1992-08-31' WHERE firstname = 'Luca';

如果您需要更具体的标准,您可以使用逻辑AND来选择它们:

mysql> UPDATE customer SET birthday = '1992-08-31' WHERE firstname = 'Luca' AND sex = 'm';

这样,如果您有两个名为Luca的不同性别的顾客,您可以选择男性顾客。

或者,如果您选择多个实例,您可以使用逻辑OR:

mysql> UPDATE customer SET name = 'Luca' WHERE sex = 'x' AND sex = 'm';

如果您只想要名字的列表,您可以:

mysql> SELECT firstname FROM customer;+-----------+
| firstname |
+-----------+
| Brian     |
| Luca      |
+-----------+

您可以对数据进行排序,例如:

mysql> SELECT * FROM customer 
    -> ORDER BY sex, birthday DESC;

这将显示所有客户,按性别排序,最年轻的排在最前面。如果你想显示他们的年龄,你可以这样做:

mysql> SELECT *,
    -> TIMESTAMPDIFF(YEAR, birthday, CURDATE()) AS age
    -> FROM customer;+-----------+----------+------------+------+------+
| firstname | lastname | birthday   | sex  | age  |
+-----------+----------+------------+------+------+
| Brian     | Smith    | 1987-05-07 | x    |   32 |
| Luca      | Maltoni  | 1992-08-31 | m    |   27 |
+-----------+----------+------------+------+------+

现在你已经可以做基本的计算了!还有类似于TIMESTAMPDIFF的其他函数用于其他操作,你可以在 MySQL 文档中找到。

检查质量

如果要检索已知生日的所有客户的数据,可以这样做:

mysql> SELECT * FROM customer WHERE birthday IS NOT NULL;

相反,您可以检索生日未知的所有客户:

mysql> SELECT * FROM customer WHERE birthday IS NULL;

也许您不想显示整个表,但仍然想知道有多少行。然后你做:

mysql> SELECT COUNT(*) FROM customer;+----------+
| COUNT(*) |
+----------+
|        2 |
+----------+

如果你想知道有多少人被称为Luca,请这样做:

mysql> SELECT firstname, COUNT(*) FROM customer GROUP BY firstname;

成批处理方式

以批处理模式运行 MySQL 很容易。假设我们有一个名为batchfile的批处理文件,那么您只需要做:

mysql < batchfile

batchfile中,你基本上输入所有你通常在交互会话中使用的命令。你完了!

包扎

恭喜你!您已经做到了——我们已经讲述了如何安装 MySQL、构建数据库和表、用数据填充表以及检索数据。现在你可以在你的简历:D 上写上基本的 SQL 技能

你会在官方 MySQL 文档中找到更多信息。如果你遇到问题,总会有stack overflow……或者给我留言,我会根据你的反馈更新这篇文章。

面向初学者的 SQL

原文:https://towardsdatascience.com/sql-for-beginners-2fcd2d6c8667?source=collection_archive---------46-----------------------

安妮·斯普拉特在 Unsplash 上的照片

从美国广播公司的热门节目【单身汉 的有趣数据中学习 SQL 的基础知识

在去年夏天学习数据科学并第一次学习编码后,我主要使用 Python,我喜欢它。我仍然更习惯使用 Python,但是一旦我开始找工作,我很快就知道作为一名数据专业人员了解 SQL 的重要性。在我读过的所有入门级数据工作描述中,SQL 熟练程度往往被要求,而且肯定比 Python 更频繁。我在数据科学训练营中学习了这种语言,但只是皮毛。我知道我需要深入一点,所以我自学了。然后我被聘为数据分析师,在工作中我学到了更多。

这一课将有助于任何很少或没有 SQL 经验的人。

如果您想继续学习,我正在使用 DB Browser for SQLite,您可以通过点击与您的计算机操作系统相对应的链接在此处免费下载。我还使用了我创建的三个 CSV 文件,其中充满了学士的数据,你可以从我的 GitHub 这里下载它们,或者如果你有不同的感兴趣的主题,你也可以使用你自己的文件。打开 SQLite 后,您需要:

  1. 通过点击左上角的按钮创建一个新的数据库。
  2. 以您喜欢的任何名称保存在一个文件夹中,以便您以后可以找到它。我叫我的学士 _ SQL _ 教程但是真的没关系。接下来会要求您创建一个表,只需单击 cancel。我们将在接下来的步骤中导入这些表。
  3. 转到文件>导入> CSV 文件中的表格…
  4. 一次选择一个 CSV,以便每个 CSV 都是自己的表。将出现一个弹出屏幕,您可以在其中检查您的数据看起来是否正确,即在继续之前,列有标题并由逗号分隔。
  5. 最后,您可以通过点击表格名称,然后点击数据库结构选项卡中的修改表格来编辑表格。在这里,您可以修改任何数据类型,分配主键,重命名或删除列,以及调整表中列的顺序。

开始前需要知道的几件重要事情:

  • 查询是一个问题,之所以这样称呼 SQL 查询,是因为它向数据库传递一个问题
  • 数据库是由多个表组成的集合。在这种情况下,有 3 张桌子,但通常也可能是 100 张或 1000 张。通常,您应该能够访问某种数据字典,它将解释每个表/列中的数据的含义,和/或 ERD(实体关系图),它显示表之间的关系。

单身女郎——所有女性演职人员及其属性

单身汉—所有男性演职人员及其属性

家乡——带有 id #的城市、州和国家

数据字典

诱发反应测定器

  • 每个表都有一个主键,它为每一行分配一个唯一的编号,任何一行都不应该与另一行完全相同
  • 编写查询时,顺序很重要,大小写无关紧要。您将始终以 select 和 FROM 语句开始,但是如果您编写 SELECT 或 SELECT,则不区分大小写。只是要做好准备,如果在工作场所使用 SQL,许多公司会有他们更喜欢的风格,你必须适应。我最常看到的方式是大写子句后面跟着小写语句。

请注意,术语可能会有所不同,但出于本教程的目的,我将把下面的作为子句,后面的短语作为语句。子句的顺序必须始终如下。我学会的帮助我记住顺序的助记法是SomeFrenchWaitersGrowHealthyOranges&Lemons。这有点奇怪,需要试着记几遍,但对我来说很有效!

挑选

加入

在哪里

分组依据

拥有

以...排序

限制

您至少需要 SELECT 和 FROM 语句,但如果您需要其余的语句,这将取决于您试图提取的内容。SELECT 是输入要返回的列名的地方,FROM 是指定从哪些表中提取数据的地方。

当您第一次使用新的数据库时,探索并熟悉不同的列和其中的数据总是一个好主意。您可以使用 SELECT *查看表中的所有列。

现在我们已经准备好移动到执行 SQL 选项卡并开始编写查询!现在让我们看看单身女子表中的所有列。在屏幕的白色顶部输入 SELECT * FROM 单身汉。要执行,您可以点按蓝色小三角形按钮或按住键盘上的 command + enter。

如果我们只想查看姓名、年龄、职业和季节列,它应该是这样的

如您所见,season _ contactor 行有重复的数据。假设我们想看看表中有多少个独特的季节。我们可以使用 DISTINCT 对每个值只查看一次。

添加位置

WHERE 子句允许我们通过添加关于我们想要查看哪些行的细节来过滤数据。语法是:

列名+过滤运算符+具体要求

最常用的过滤器运算符有:

LIKE —当您希望行中的数据匹配字符串或字符串的一部分时使用。通常在开头和/或结尾与通配符(%)一起使用。

BETWEEN 用于过滤某个范围内的数字列

IN-返回与列表或子查询中的字符串完全匹配的任何行

= —完全匹配

<>而且!= —两者都表示不等于

≥和≤ —大于或等于、小于或等于

如果需要多个筛选器,请使用 AND 或 or 将语句链接在一起。

我们来做几个例子!假设我们想知道哪些单身女子是 30 岁以下的护士,头发是棕色还是黑色。

注意,我使用 UPPER 将职业列中的所有字符串都大写,因为数据中的大小写格式不一致,这是一个常见的问题

或者,如果我们想知道第 15 季中哪些选手的年龄在 28 到 32 岁之间,是金发还是名字以 r 开头。

或者最后,让我们看看有多少获奖者不是金发碧眼的?

只有 7 个!

您还将使用 WHERE 子句来查找空值或 NaN 值,如下所示:

GROUP BY 子句和聚合函数

有时,您会希望查询的答案只是一个数字,例如一行的平均值或总数。这叫做聚集。最常见的聚合函数有

  • COUNT — returns the total # of rows
  • AVG — returns the average of a column. Sometimes written as AVERAGE depending on which version of SQL you are using.
  • MIN — returns the smallest number in a column
  • MAX — returns the largest number in a column
  • SUM — adds all the numbers in a column and returns the total

例如,假设你想知道单身汉的平均年龄。

注意:可以在开头加上 ROUND 函数,在结尾加上小数位数,得到一个实数。例:四舍五入(AVG(年龄),0)

通常,我们希望在运行聚合之前对数据进行分组。就像如果我们想知道每个季节单身女性的平均年龄,我们会按季节分组。

我们可以结合 WHERE 子句添加其他条件。SELECT 语句中的 COUNT(*)将对符合查询规范的每一行进行计数。这里我们就用它来统计一下有多少 30 岁以上的单身汉有每种颜色的头发。

另一个例子是找出赢得赛季冠军的女孩的平均年龄。

如果我们想知道谁是最高或最矮的单身汉,我们会用 MAX 或 MIN。

拥有

HAVING 是另一种筛选数据的方法,比如 WHERE,除了使用聚合数据。比方说,我们想看看哪一季的参赛者最大年龄不超过 30 岁。

不幸的是,我的数据中没有太多好的例子。不过,在工作场所,您可能会看到这样一个例子:如果您需要查找平均月销售额低于 10 万美元的月份。或者,如果您正在提取一个客户列表,这些客户去年的总销售额大于 1000 美元。

以...排序

很多时候,您会希望数据按照一定的顺序返回。ORDER BY 可能是最常用的子句之一。它可以按数字或字母顺序排序。默认情况下是升序、从小到大或 A-Z,但是您可以在语句末尾添加 DESC,以反向/降序排序。我们已经完成的一些示例将受益于 ORDER BY 语句。例如当我们观察所有不同的季节时。

或者当我们想知道哪些单身女子是不到 30 岁的护士,有棕色或黑色的头发,但按名字的字母顺序排列。

也许我们想按年龄从大到小,按季节来看所有的女孩。可以按多列排序。因此,在这种情况下,首先是季节,然后是年龄,结果将按季节排序,然后在季节内按年龄排序。

我们也可以根据身高和年龄对男人进行分类。让我们按从高到矮的顺序排列它们,任何两个一样高,按从年轻到年长的顺序排列。

连接和别名

您是否觉得已经掌握了从一个表中提取数据的技巧?是时候将两个(或更多)结合在一起了!您可能想知道为什么我们还没有查看城市、州和国家字段。在单身女郎单身汉的表格中,城市、州和国家 id 本身对我们来说毫无意义。为了找出每个人来自哪里,我们需要加入到家乡表中。加入也是提出别名的好时机。有时当您联接表时,它们会有同名的列。为了在整个查询中区分它们,您需要编写 table.column 名称(这称为命名空间)。由于表名可能很长,所以为表指定一个只有一两个字母的新名称更容易。下面你会看到,我把的未婚女子命名为 b ,把的家乡命名为 h 。这就是混叠。

连接将始终遵循 FROM 子句。您将声明要将哪个表连接到原始表,然后必须声明哪些列匹配。在这种情况下,我们希望将 hometowns 表连接到单身女郎表,并将 city_id 匹配到她的 city _ id,state_id 匹配到她的 state _ id,country_id 匹配到她的 country _ id。在 SELECT 语句中,我省略了 id,只选择查看位置本身,这样我们可以很容易地看到女孩的家乡。我先按国家字母顺序排序,然后按季节排序,但前提是这些女孩不是加拿大人。

我们还可以做一些事情,比如计算数据库中每个县有多少女孩。别名也适用于聚合。下面,我将 COUNT(*)列重命名为 total_girls。您再次看到 GROUP BY 是因为我们有一个聚合,而 ORDER BY 是因为我想按参与者从高到低的顺序看到各个国家。

我们还可以把单身女郎桌加入到单身贵族桌,看看哪个女孩出现在哪个单身贵族季。对于这个例子,我们需要匹配 b 。season_lead = g 。赛季 _ 选手。在 SELECT 语句中,我只包含了他们的姓名和年龄,但是正如您所看到的,我仍然能够按照他的身高进行排序。杰克·帕维尔卡是最矮的单身汉,所以他和他的姑娘们是最矮的。

重要的是要知道连接比这更复杂。有几种类型,内部、右侧、左侧、外部、联合和多重联接。我不打算在这篇文章中详细介绍它们,但是如果你喜欢这篇文章并且想要一篇关于连接的文章,请在评论中告诉我!现在,只需要知道 INNER 是最常见的连接,也是默认连接。提取的数据将包括两个表中的行,其中连接列匹配且不为空。

限制

最后但肯定不是最不重要的是限制。这可能是 SELECT 和 FROM 之后最常用的子句。LIMIT 允许您只查看一个子集,而不是返回每一行。当浏览超过 100K 行的大型数据集时,这有利于提高速度,因为您可以设置 100 或 1000 的限制,以便查看您正在处理的内容。如果你只想看到最好的结果,不管是前 10 个最高的单身汉还是前 25 个销售额最高的商店(在现实世界中),它也可以是一个工具。

现在就把我们学过的都放在一起吧!下面我们找到了年龄最大的 5 位棕发单身汉,他们的单身女性来自加利福尼亚、德克萨斯、纽约或佛罗里达,平均年龄不到 27 岁。

在这里,我们统计了在单身汉至少 6 英尺 2 英寸的季节里,来自每个州的单身女郎的数量,按照最高计数排序,然后按照字母顺序排列家乡州,并限定前 10 个结果。

其他重要的实践资源

我从维基百科收集数据,用 excel 和 python 清理并格式化。

面向初学者的 SQL

原文:https://towardsdatascience.com/sql-for-beginners-c60ea8bbe170?source=collection_archive---------43-----------------------

在保持社交距离的同时学习数据科学

让我们回顾一下 SQL,让它真正扎根于你的大脑

我的数据例子都是关于我的艺术品定价

介绍

在过去的五周里,我一直在 Medium 上发布 SQL 课程。我的几个朋友表达了在社交距离中学习数据科学的兴趣,所以我认为帮助他们会很有趣。我认为从学习 SQL 开始是一个好主意,因为这是我希望在我接受培训成为数据科学家时就已经学会的一件事。

这是一个很好的起点,因为你首先需要能够访问数据,以便能够分析它。SQL 也是一种非常简单的逻辑编程语言,所以我认为它是对数据科学世界的一个很好的渐进介绍。

我希望我的课程平易近人,这样人们入门的门槛就低了。这也是为什么我让我的例子变得有趣和有点傻的原因。在这个多灾多难的疫情时代,你不能把生活看得太严肃。

前一课

上周我们上了最后一课,介绍了一个新的 SQL 概念。我们学习了如何组合桌子。使用 UNION 和 UNION ALL 关键字,我们将数据集堆叠在一起。此外,我们还学习了使用内连接、左外连接和右外连接来根据匹配值组合表。

这一课

本周是复习周。我想回顾一下我们到目前为止学到的关键概念,让它们真正留在我们的脑海中。我想传达一个新概念,但它是我们在关于创建新表的第一课中所讲内容的延伸。

希望到现在为止,你能够在你感兴趣的数据科学问题上更加独立地工作。因此,在学习了如何创建一个新表来保存选定的数据之后,我将设置练习。然后在那之后,我会经历我将如何完成它们。这样,万一你遇到困难,你就有了一个可以参考的例子。

主要学习内容

  • 复习创建表格
  • 查看使用 SELECT 和 SELECT DISTINCT 关键字从表中选择数据
  • 使用 WHERE 关键字查看过滤数据
  • 使用 GROUP BY 关键字查看基于列值的分组数据
  • 使用关键字最小值、最大值、计数、AVG 和总和查看汇总数据
  • 通过使用关键字 UNION 和 UNION ALL 堆叠数据集,或者使用关键字 INNER JOIN、LEFT OUTER JOIN 和 RIGHT OUTER JOIN 基于匹配值来查看数据集的组合
  • 学习从选定的数据创建新的表格

使用选定数据创建新表的语法

在第 1 课中,我们已经学习了如何创建一个表格并用数据填充它。但是,除了写出每个单独的值之外,还有其他方法可以用数据填充新表。

另一种方法是从现有表中选择数据,并将其存储在新表中。它们的语法非常简单。我们只需要添加一个 CREATE TABLE 语句,然后使用 AS 将 select 语句返回的数据分配给我们创建的新表。你可以在下面看到如何做:

CREATE TABLE name_of_new_table
AS
SELECT 
    name_column_one, 
    name_column_five
FROM
    name_of_existing_table
WHERE
    name_column_three = value
;

复习练习:

练习 1: 创建至少两个表格,并用数据填充它们。数据可以是你自己选择的,但是每个表格至少应该有 3 列和 5 行。确保您的表中至少包含 3 种不同的数据类型。数据可以是真实的,也可以是编造的,由你决定。

练习 2 :编写一个查询来过滤每个表。试着对每一个问题使用不同的方法。请记住,您可以使用关键字 BETWEEN、LIKE、AND、OR、NOT 等。

练习 3: 编写一个查询来对每个表进行分组和聚合。如果有意义的话,尝试在每种情况下使用不同的聚合器。

练习 4: 编写一个查询,将两个表组合在一起,并解释为什么选择以这种方式组合它们。

练习 5: 写出一个你可能想问你的数据的问题。然后编写一个或多个查询来尽可能回答这个问题。在回答您的问题时,使用我们的新语法将选定的数据保存到新表中至少一次。

确保你解释了你为什么那样回答你的问题。

提示:新建表格时,数据不会自动显示。如果您想查看数据,那么您需要从您创建的新表中选择数据。

我的解决方案:

数据

作为练习,我将创建两个我自己的数据集,在我作为视觉艺术家的其他工作中会派上用场。这些表格将包含我可以用来给我的艺术品定价的信息。需要注意的是,所有的价格都是估算出来的。主要是因为我不记得我以什么价格买了所有的东西😜

一个表格将详细列出我使用的不同材料和我的估计价格。另一个表将包含我的表面选项。第三个表包含关于我的时间收费的信息。

我绘画时使用的一些工具

如果你有兴趣看看我的作品,你可以在这里找到它www.katemarielewis.com

练习 1 答案:

我去了 https://www.db-fiddle.com/。在左边的列中,我将 CREATE TABLE 和 INSERT 放入查询中。

CREATE TABLE art_materials(
    material_name varchar(255),
    material_type varchar(255),
    material_subtype varchar(255),
    cost float
);INSERT INTO art_materials(
    material_name,
    material_type,
    material_subtype,
    cost
)
VALUES
    ('golden acrylic paint', 'paint', 'acrylic', 0.5),
    ('schmincke horadam watercolour paint', 'paint', 'watercolour', 0.2),
    ('resin', 'other', 'epoxy resin', 1)
; CREATE TABLE art_surfaces(
    surface_name varchar(255),
    surface_type varchar(255),
    surface_subtype varchar(255),
    size_category varchar(255),
    size_m2 float,
    cost_dollars int(255)
);INSERT INTO art_surfaces(
    surface_name,
    surface_type,
    surface_subtype,
    size_category,
    size_m2,
    cost_dollars
)
VALUES
    ('square stretched canvas medium', 'canvas', 'stretched canvas', 'medium', 0.36, 30),
    ('rectangle stretched canvas medium', 'canvas', 'stretched canvas', 'medium', 0.2, 25),
    ('rectangle stretched canvas large', 'canvas', 'stretched canvas', 'large', 0.35, 50),
    ('square stretched canvas large', 'canvas', 'stretched canvas', 'large', 1, 60),
    ('rectangular canvas pad medium', 'canvas', 'canvas pad', 'medium', 0.12, 7),
    ('rectangular watercolour canvas pad medium', 'canvas', 'canvas pad', 'medium', 0.12, 10),
    ('wooden artist panel', 'wooden panel', 'wooden panel', 'medium', 0.25, 40),
    ('300g/m2 Canson Heritage 100% cotton watercolour paper', 'paper', 'watercolour paper', 'small', 0.09, 5),
    ('300g/m2 Canson Moulin du Roy 100% cotton watercolour paper', 'paper', 'watercolour paper', 'medium', 0.12, 8)
; CREATE TABLE artist_time(
    hours int(255),
    price int
);INSERT INTO artist_time(
    hours,
    price
)
VALUES
    (3, 120),
    (4, 160),
    (6, 240),
    (8, 320),
    (12, 480),
    (24, 960),
    (36, 1440),
    (72, 2880)
;

然后我点击了“运行”按钮。

当我从每个表中选择所有数据时,我得到了以下结果:

艺术 _ 材料表

艺术 _ 表面表

艺术家 _ 时间表

练习 2 答案:

为了过滤我的表,我使用了以下查询:

SELECT
    material_name
FROM
    art_materials
WHERE 
    material_type != 'paint'
;SELECT DISTINCT 
    surface_subtype
FROM
    art_surfaces
WHERE 
    surface_type LIKE '%vas'
;SELECT DISTINCT 
    price
FROM
    artist_time
WHERE 
    hours BETWEEN 6 AND 24
;

练习 3 答案:

为了对我的表进行分组和聚合,我使用了以下查询:

SELECT
    material_type, 
    AVG(cost) AS 'mean_material_cost'
FROM
    art_materials
GROUP BY 
    material_type
;SELECT
    size_category, 
    COUNT(surface_name) AS 'number_of_different_surfaces'
FROM
    art_surfaces
GROUP BY 
    size_category
;SELECT DISTINCT
    price/hours AS 'price_per_hour'
FROM
    artist_time
;

注意:第三个表 artist_time 对 group 没有意义,因为在任一列中都没有重复的值。

练习 4 答案:

为了合并我的表,我使用了以下查询:

SELECT
        material_name AS 'name',
        material_type AS 'type', 
        material_subtype AS 'subtype'
    FROM
        art_materials
UNION
    SELECT
        surface_name AS 'name',
        surface_type AS 'type', 
        surface_subtype AS 'subtype'
    FROM
        art_surfaces
;

我选择使用关键字 UNION 将 art_materials 和 art_surfaces 表组合在一起,因为它们有三个相似的列,可以相互堆叠。通过将它们结合起来,结果显示了在创作我的作品时所有可能不同的材料成本。

练习 5 答案:

问题:一位客户找到我,让我在一张拉伸的画布上创作一幅中等尺寸的丙烯画。如果我估计我要花 8 个小时来绘画,这幅画要花客户多少钱?

注意:我不知道客户想要什么形状的画布,所以我将采用两种形状选项的平均成本。

以下是查找答案并将其存储在表中的查询:

CREATE TABLE materials_cost
AS
SELECT
    cost AS 'paint_cost'
FROM
    art_materials
WHERE 
    material_type = 'paint'
AND
    material_subtype = 'acrylic'
; CREATE TABLE surface_cost
AS
SELECT
    AVG(cost_dollars) AS 'surface_cost_estimate',
    AVG(size_m2) AS 'size_estimate_m2'
FROM
    art_surfaces
WHERE 
    size_category = 'medium'
AND
    surface_type = 'canvas'
AND
    surface_subtype = 'stretched canvas'
; CREATE TABLE time_cost
AS
SELECT 
    price as 'artist_time_cost'
FROM
    artist_time
WHERE
    hours = 8
;SELECT 
    (SELECT 
         artist_time_cost 
     FROM 
         time_cost)+
    (SELECT 
         surface_cost_estimate 
     FROM 
         surface_cost)+
    ((SELECT 
         paint_cost 
     FROM 
         materials_cost)*
     (SELECT
         size_estimate_m2*100
      FROM
         surface_cost))
;

注意:在我的最终查询中,我在 select 语句中使用了几个子查询,以便能够从我创建的每个新表中选择一个数据点。

为了给客户找到正确的报价,我需要考虑所用的材料,在中等拉伸画布的平均面积上。我还添加了拉伸画布的平均价格。我添加到报价中的最终成本是我估计持续时间为 8 小时的时间价格。

我从每个原始数据集中创建了一个单独的过滤表,以便为最终方程进行选择。

学习回顾

学完本课后,您应该能够:

  • 独立创建表格
  • 使用 select 和 SELECT DISTINCT 关键字从表中选择数据
  • 使用 WHERE 关键字过滤数据
  • 使用 GROUP BY 关键字根据列值对数据进行分组
  • 使用关键字 MIN、MAX、COUNT、AVG 和 SUM 聚合数据
  • 通过使用关键字 UNION 和 UNION ALL 堆叠数据集,或者使用关键字 INNER JOIN、LEFT OUTER JOIN 和 RIGHT OUTER JOIN 基于匹配值组合数据集
  • 从所选数据创建新表

下一课

我认为这次审查可能会比以前的审查花费更多的时间。与前几周相比,本周的练习有所增加。所以下周的课更多的是理论课,而不是实践课。在下一课中,我将讲述一些我在解决任何数据科学问题之前思考的事情。

除了数据,我的另一个爱好是绘画。你可以在 www.katemarielewis.com 找到我的野生动物艺术

社交距离学习数据科学(LDSWSD)系列中的所有课程

[## Ldswsd -走向数据科学

阅读《走向数据科学》中关于 Ldswsd 的文章。共享概念、想法和代码的媒体出版物。

towardsdatascience.com](https://towardsdatascience.com/tagged/ldswsd)

面向数据科学家的 SQL:轻松学习

原文:https://towardsdatascience.com/sql-for-data-scientists-learning-it-easy-way-798122c6d4e6?source=collection_archive---------38-----------------------

用于数据准备、过滤、连接等的 SQL 语句

乔安娜·科辛斯卡在 Unsplash 拍摄的照片

我们不会拿起锤子去找钉子——那是解决问题的不寻常的方式。通常做生意的方式是先确定问题,然后寻找合适的工具。

我一次又一次地看到人们通过选择 SQL 语句来学习 SQL,然后学习如何使用它。在我看来,这种基于工具的心态是一种低效的学习方式,另一方面,翻转这种心态可以产生巨大的差异。先有问题,后有工具!

如果你对数据科学感兴趣,你会知道pandastidyverset在过滤、排序、分组、合并等各种数据处理操作中的能力。使用 SQL,您可以做类似的事情,但是是在数据库环境中,并且使用不同的语言。

本文的目的是演示如何采用数据科学家在编程环境中通常遵循的类似方法来解决 SQL 中的数据处理问题。在 SQL 上你不会学到所有的东西,相反,目标是展示“如何”学习。

适合您实践的 SQL 编辑器

如果您的计算机上安装了关系数据库管理系统,启动它。如果没有, w3schools 有一个在线 SQL 编辑器,你可以马上在你的浏览器上使用。

您还会注意到,屏幕右侧有相当多的数据集,您可以一起使用和练习。

现在让我们进入“如何”使用 SQL 解决实际的数据处理问题。

理解数据

就像你用你最喜欢的编程库如pandas所做的一样,你需要做的第一件事就是在 SQL 环境中加载数据集。

就像典型数据科学项目中的基本探索性数据分析(EDA)一样,您可以检查前几行,计算总行数,查看列名、数据类型等。下面是一些命令。

# import data into editor
SELECT * # import all columns with *, else specify column name
FROM table_name
LIMIT 10 #to show 10 rows# import and save data as a separate table
SELECT *
INTO new_table_name
FROM table_name# count number of rows in the dataset
SELECT 
COUNT(*)
FROM table_name# count unique values of a single column
SELECT 
COUNT(DISTINCT column_name) 
FROM table_name

使用列

数据库通常很大,运行查询需要很长时间。因此,如果您知道您感兴趣的具体列,您可以通过选择这些列来创建数据的子集。

您可能还想执行列操作,如重命名、创建新列等。

# select two columns from a multi-column dataset
SELECT column1, column2
FROM tableName# rename a column
SELECT
ProductName AS name
FROM productTable# new conditional column (similar to if statment)
SELECT ProductName, Price,(CASE
WHEN Price > 20 AND Price <41 THEN 'medium '
WHEN Price >40 THEN 'high'
ELSE 'low'
END) AS newColNameFROM Products

筛选行

过滤行可能是您经常使用 SQL 执行的最重要的任务。从大型数据集中,您通常会根据产品类型、值的范围等来过滤行。

如果你正在学习 SQL,你应该花大量的时间学习过滤数据的不同方法和你需要的 SQL 语句。

# select all records that starts with the letter "S"
SELECT * FROM Products
WHERE ProductName like 'S%'# select all records that end at "S"
SELECT * FROM Products
WHERE ProductName like '%S'# select all records that does NOT start at "S"
SELECT * FROM Products
WHERE ProductName like '[^S]%'# filter rows with specific value
SELECT * FROM table_nameWHERE firstName = 'Pilu'
OR lastName != 'Milu'
AND income <= 100
AND city IN ('Arlington', 'Burlington', 'Fairfax')# filter rows within a range of numbers
SELECT *
FROM tableName
WHERE income BETWEEN 100 AND 200 # filter null values
SELECT * FROM tableName
WHERE columnName IS NULL # opposite "IS NOT NULL"

连接数据集

您在关系数据库管理系统(RDBMS)中使用 SQL,这意味着您将同时处理多个表,因此在您能够进行高级建模之前,需要将它们连接起来。

基本上有四种连接数据的方法——左连接、右连接、内连接、全外连接——您需要稍微搜索一下,看看每种方法是如何工作的,但是我在下面给出了执行这些连接的所有代码。

# inner join (for matching records only)
SELECT * FROM
table1 INNER JOIN table2
ON table1.ID = tbale2.ID# full outer join (all left + all right)
SELECT * FROM
table1 FULL OUTER JOIN table2
ON table1.ID = tbale2.ID# left join (all records from left + matching records from right)
SELECT * FROM
table1 LEFT JOIN table2
ON table1.ID = tbale2.ID# left join (matching records from left + all records from right)
SELECT * FROM
table1 RIGHT JOIN table2
ON table1.ID = tbale2.ID

做计算

创建汇总统计数据、数学运算和构建模型是数据科学家每天要做的事情。SQL 并不是一个合适的工具,但是,如果你需要创建一个快速的汇总统计数据,你可以使用聚合函数来计算列的平均值、总数、最小值/最大值等。

# new calculated column
SELECT Price,
(Price * 2) AS NewCol
FROM Products# aggregation by group
SELECT CategoryID, SUM(Price) 
FROM Products
GROUP BY CategoryID# min/max values of a column
SELECT ProductName, MIN(Price)
FROM Products

最终注释

本文的目的是介绍一些基本的 SQL 概念和语句,用于从关系数据库管理系统中查询数据。但主要目的是展示如何作为一名数据科学家学习 SQL,以解决特定的问题,而不是专注于 SQL 语句。

SQL HackerRank 解决方案

原文:https://towardsdatascience.com/sql-hackerrank-solutions-516666f9eb8c?source=collection_archive---------0-----------------------

HackerRank 上 SQL 问题的完整解决方案。

照片由 Grzegorz WalczakUnsplash 上拍摄

结构化查询语言是业界使用的最重要的语言之一。随着数据量的增加,这是最受雇主欢迎的语言之一,为了从各自的数据库中访问海量数据,了解这一技能很重要,它将帮助你检索、更新和操作数据

在本帖中,我们将介绍 HackerRank 平台上的所有 SQL 解决方案。 HackerRank 是一个竞技编码的平台。很重要的一点是,你们都要先试一试&在看解决方案之前,集思广益。让我们编码并找到给定问题的答案。

吉菲

I .修改选择查询 1

查询城市中人口大于100000的所有美国城市的所有列。美国的国家代码是USA

输入格式

城市表描述如下:

**SELECT * FROM CITY WHERE COUNTRYCODE = ‘USA’ AND POPULATION > 100000;**

二。 修改选择查询 2

城市中查询所有人口大于120000的美国城市名称。美国的国家代码是USA

输入格式

城市表描述如下:

**SELECT NAME FROM CITY WHERE COUNTRYCODE = ‘USA’ AND POPULATION > 120000;**

三世。 全选

查询城市表中每行的所有列(属性)。

输入格式

**SELECT * FROM CITY;**

四。 按 ID 选择

查询城市中 ID 为1661的城市的所有列。

输入格式

***SELECT * FROM CITY WHERE ID = 1661;***

五、 日本城市的属性

查询城市表中每个日本城市的所有属性。日本的国家代码JPN

输入格式

***SELECT * FROM CITY WHERE COUNTRYCODE = ‘JPN’;***

六。 日本城市名称

查询城市表中所有日本城市的名称。日本的国家代码是JPN

输入格式

***SELECT NAME FROM CITY WHERE COUNTRYCODE = ‘JPN’;***

七。 气象观测站 1

表中查询城市列表。

输入格式

工位表描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT CITY, STATE FROM STATION;***

八世。 气象观测站 3

查询城市名称列表,只有偶数 ID 编号。您可以按任何顺序打印结果,但必须排除重复的答案。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT CITY FROM STATION WHERE MOD(ID, 2) = 0;***

九。 气象观测站 4

N城市条目数,设N’城市名称数;从查询N-N’的值。换句话说,找出表中城市条目总数与表中不同城市条目数之间的差异。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT COUNT(CITY) — COUNT(DISTINCT CITY) FROM STATION ;***

x .气象观测站 5

查询中两个城市名称最短和最长的城市,以及各自的长度(即:名称中的字符数)。如果有多个最小或最大的城市,请选择按字母顺序排列的第一个。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT * FROM (SELECT DISTINCT city, LENGTH(city) FROM station ORDER BY LENGTH(city) ASC, city ASC) WHERE ROWNUM = 1** **UNION****SELECT * FROM (SELECT DISTINCT city, LENGTH(city) FROM station ORDER BY LENGTH(city) DESC, city ASC) WHERE ROWNUM = 1;***

XI。 气象观测站 6

查询以元音开头的城市名称列表(即aeiou)。您的结果不能包含重复项。

输入格式

表描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM station WHERE city LIKE ‘A%’ OR city LIKE ‘E%’ OR city LIKE ‘I%’ OR city LIKE ‘O%’ OR city LIKE ‘U%’;***

十二。 气象观测站 7

查询以元音字母(a,e,I,o,u)结尾的城市名称列表。您的结果不能包含重复项。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM station WHERE city LIKE ‘%a’ OR city LIKE ‘%e’ OR city LIKE ‘%i’ OR city LIKE ‘%o’ OR city LIKE ‘%u’;***

十三。 气象观测站 8

查询以元音字母(即 aeiou )作为开头和结尾城市名称列表。您的结果不能包含重复项。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM (SELECT DISTINCT city FROM station WHERE city LIKE ‘A%’ OR city LIKE ‘E%’ OR city LIKE ‘I%’ OR city LIKE ‘O%’ OR city LIKE ‘U%’) WHERE city LIKE ‘%a’ OR city LIKE ‘%e’ OR city LIKE ‘%i’ OR city LIKE ‘%o’ OR city LIKE ‘%u’;***

十四。9气象观测站**

查询城市名称列表,其中不以元音字母开头。您的结果不能包含重复项。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM station WHERE NOT (city LIKE ‘A%’ OR city LIKE ‘E%’ OR city LIKE ‘I%’ OR city LIKE ‘O%’ OR city LIKE ‘U%’);***

十五。 气象观测站 10

查询不以元音字母结尾的城市名称列表。您的结果不能包含重复项。

输入格式

表描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM station WHERE NOT (city LIKE ‘%a’ OR city LIKE ‘%e’ OR city LIKE ‘%i’ OR city LIKE ‘%o’ OR city LIKE ‘%u’);***

十六。 气象观测站 11

查询不是以元音开头就是不以元音结尾的城市名称列表。您的结果不能包含重复项。

输入格式

工位表描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM station WHERE****(NOT (city LIKE ‘A%’ OR city LIKE ‘E%’ OR city LIKE ‘I%’ OR city LIKE ‘O%’ OR city LIKE ‘U%’)****OR NOT(city LIKE ‘%a’ OR city LIKE ‘%e’ OR city LIKE ‘%i’ OR city LIKE ‘%o’ OR city LIKE ‘%u’));***

十七。 气象观测站 12

查询不以元音字母开头、不以元音字母结尾的城市名称列表。您的结果不能包含重复项。

输入格式

工位工作台描述如下:

其中 LAT_N 为北纬 LONG_W 为西经。

***SELECT DISTINCT city FROM station WHERE NOT****((city LIKE ‘A%’ OR city LIKE ‘E%’ OR city LIKE ‘I%’ OR city LIKE ‘O%’ OR city LIKE ‘U%’)****OR (city LIKE ‘%a’ OR city LIKE ‘%e’ OR city LIKE ‘%i’ OR city LIKE ‘%o’ OR city LIKE ‘%u’));***

十八。 高于 75 分

查询学生中任何分数高于分数的学生的姓名。根据每个名称的最后三个字符对输出进行排序。如果两个或更多学生的名字都以相同的最后三个字符结尾(例如:Bobby、Robby 等。),通过升序 ID 对它们进行二次排序。**

输入格式

学生表描述如下:

**名称列只包含大写(A - Z)和小写(a - z)字母。

***SELECT name FROM students WHERE marks > 75 ORDER BY SUBSTR(name, LENGTH(name)-2, 3), id;***

XIX。 员工姓名

编写一个查询,按字母顺序打印来自雇员表的雇员姓名列表(即:姓名属性)。

输入格式

包含公司员工数据的员工表描述如下:

其中 employee_id 是员工的身份证号码,姓名是他们的姓名,月数是他们为公司工作的总月数,工资是他们的月工资。

***SELECT name FROM employee ORDER BY name;***

XX。 员工属性

编写一个查询,打印雇员中月薪高于月工资且任职不到个月的雇员的姓名列表(即:姓名属性)。按照升序雇员 id* 对您的结果进行排序。*

输入格式

包含公司员工数据的员工表描述如下:

其中 employee_id 是员工的身份证号,姓名是他们的姓名,月数是他们为公司工作的总月数,薪水是他们的月薪。

***SELECT name FROM employee WHERE salary > 2000 AND months < 10 ORDER BY employee_id;***

二十一。 三角形的类型

使用三条边的长度编写一个查询,标识三角形表中每条记录的类型。为表中的每条记录输出以下语句之一:

等边三角形:它是一个三条边等长的三角形。

等腰:是两条边等长的三角形。

不规则三角形:它是一个有三条不同长度的边的三角形。

不是三角形:给定值 ABC 不构成三角形。

输入格式

三角形表描述如下:

表格中的每一行表示三角形三条边的长度。

***select if(A+B<=C or B+C<=A or A+C<=B,’Not A Triangle’,****if(A=B and B=C,’Equilateral’,****if(A=B or B=C or A=C,’Isosceles’,’Scalene’)))****from TRIANGLES as T;****VII.** **The PADS***

XXII。 护垫

生成以下两个结果集:

查询一个按字母顺序排列的列表,列出职业中的所有名字,紧接着每个职业的第一个字母作为括号(即:括在括号中)。例如:AnActorName(A)、ADoctorName(D)、aprofsorname(P)和 ASingerName(S)。

查询职业中每个职业出现的次数。按照升序对事件进行排序,并按照以下格式输出:

总共有[occupation _ count][occupation]个。

其中[occupation_count]是职业在职业中出现的次数,[occupation]是小写职业名称。如果多个职业具有相同的【occupation_count】,则应按字母顺序排列。

注:表格中每种职业至少有两个条目。

输入格式

职业表描述如下:

职业*只会包含以下值之一:医生教授歌手、演员**。*

***SELECT concat(NAME,concat(“(“,concat(substr(OCCUPATION,1,1),”)”))) FROM OCCUPATIONS ORDER BY NAME ASC;****SELECT “There are a total of “, count(OCCUPATION), concat(lower(occupation),”s.”) FROM OCCUPATIONS GROUP BY OCCUPATION ORDER BY count(OCCUPATION), OCCUPATION ASC***

二十三。 职业

旋转职业**中的职业栏,使每个姓名按字母顺序排序,并显示在其对应的职业下方。输出的列头应该分别是医生教授歌手演员**

注意:打印 NULL 当没有更多的名字对应一个职业时。

输入格式

职业表描述如下:

职业只会包含以下值之一:医生教授歌手、演员

*****set @r1=0, @r2=0, @r3=0, @r4=0;****select min(Doctor), min(Professor), min(Singer), min(Actor)****from(select case when Occupation=’Doctor’ then (@r1:=@r1+1) when Occupation=’Professor’ then (@r2:=@r2+1) when Occupation=’Singer’ then (@r3:=@r3+1) when Occupation=’Actor’ then (@r4:=@r4+1) end as RowNumber,****case when Occupation=’Doctor’ then Name end as Doctor,****case when Occupation=’Professor’ then Name end as Professor,****case when Occupation=’Singer’ then Name end as Singer,****case when Occupation=’Actor’ then Name end as Acto from OCCUPATIONS order by Name****) Temp group by RowNumber;*****

二十四。 二叉树节点

给你一个表, BST ,包含两列: NP,其中 N 表示二叉树中一个节点的值, PN 的父节点。**

编写一个查询,查找按节点值排序的二叉树的节点类型。为每个节点输出以下内容之一:**

:如果节点是根节点。

:如果节点是叶节点。

内部:如果节点既不是根节点也不是叶节点。

*****SELECT N, IF(P IS NULL,’Root’,IF((SELECT COUNT(*) FROM BST WHERE P=B.N)>0,’Inner’,’Leaf’)) FROM BST AS B ORDER BY N;*****

XXV。 新公司

Amber 的集团公司刚刚收购了一些新公司。每家公司都遵循以下层级结构:

给定下面的表模式,编写一个查询来打印公司代码创始人姓名、主管经理总数、高级经理总数、经理总数雇员总数。通过升序 company_code 对您的输出进行排序。**

注:

这些表可能包含重复的记录。

company_code 是字符串,所以排序不应该是数字。例如,如果公司 _ 编码C1C2C10,那么公司 _ 编码的升序将为C1C10C2**

输入格式

下表包含公司数据:

公司:**公司 _ 代码是公司的代码,创始人是公司的创始人。**

Lead _ Manager:**Lead _ Manager _ code是主管的代码, company_code 是工作单位的代码。

高级经理:**高级经理代码是高级经理的代码,主管经理代码是其主管经理的代码,公司代码是工作单位的代码。**

经理:**经理 _ 代码是经理的代码,高级 _ 经理 _ 代码是其高级经理的代码,主管 _ 经理 _ 代码是其主管经理的代码,公司 _ 代码是工作单位的代码。**

员工:**员工 _ 代码是员工的代码,经理 _ 代码是其经理的代码,高级 _ 经理 _ 代码是其高级经理的代码,主管 _ 经理 _ 代码是其主管经理的代码,公司 _ 代码是工作单位的代码。**

*****select c.company_code, c.founder, count(distinct lm.lead_manager_code), count(distinct sm.senior_manager_code), count(distinct m.manager_code), count(distinct e.employee_code) from Company c, Lead_Manager lm, Senior_Manager sm, Manager m, Employee e****where c.company_code = lm.company_code and lm.lead_manager_code = sm.lead_manager_code and sm.senior_manager_code = m.senior_manager_code and m.manager_code = e.manager_code group by c.company_code, c.founder****order by c.company_code*****

XXVI。 画三角形 2

P(R) 代表 Julia 在 R 行中绘制的图案。以下图案代表 P(5) :

*

* *

* * *

* * * *

* * * * *

编写一个查询来打印模式 P(20)

*****set @row := 0;****select repeat(‘* ‘, @row := @row + 1) from information_schema.tables where @row < 20*****

毛罗·加蒂在 GIPHY 上的 GIF

数据科学中的 SQL

原文:https://towardsdatascience.com/sql-in-data-science-af0b4492bcd?source=collection_archive---------37-----------------------

使用 SQLite 和 Chinook 数据库的 SQL 教程

卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片

这篇文章将作为使用 SQL(结构化查询语言)查询数据的教程。出于本教程的目的,我将使用 SQLite3 库,它提供了一个关系数据库管理系统。例如,我将使用 Chinook 数据库,这是一个表示数字媒体商店的示例数据库,包括艺术家、专辑等的表。更多细节,请看文档 这里

今天,SQL 是处理和查询数据的标准。一个重要的好处是 SQL 允许用户快速有效地从关系数据库中输入和检索信息。关系数据库是一种存储和访问彼此相关的数据点的数据库 (Oracle,)T5 什么是关系数据库?),把这个想象成一个有列和行的 excel 电子表格。它们可以由一个或多个这样的表组成,每一行由一个唯一的键(主键)标识。这些数据库对象的集合被称为模式。模式是一种有用的机制,可以为不同的应用程序、访问权限和管理数据库的安全管理隔离数据库对象 (Rajendra GuptaRajendra,2019) 。使用 SQL 时,我最喜欢的一个优点是能够只检索特定于任务的数据。通常,我使用 Pandas 库进行大部分数据操作和分析。然而,当试图用多个条件来划分数据帧的子集时,语法变得相当复杂。使用 SQL 语句,如SELECTDISTINCTLIKE,我们可以通过只检索服务于我们目标的数据来节省计算时间。下图是关系数据库模式的一个示例。每个矩形都是一个表格,表格名称列在顶部。在每个表名的下面是与每个表相关联的列名列表。带有星号(*)的列名(在本例中为金键)表示它是表的主键(唯一标识符)。如您所见,一个表中的主键也可能在另一个表中。这就是所谓的外键(来自不同表的主键),在本例中为蓝色菱形。

图片来自 pixabay麦克默里朱莉

SQLite 库有一个非常有效的关系数据库管理系统(RDBMS)。SQLite3 为用户提供了许多有益的特性,最引人注目的是它是自包含的、无服务器的、零配置的(什么是 SQLite?你应该知道的 SQLite 顶级特性 2020)。

SQLite3 在运行

对于本教程,我将安装并加载必要的库,连接到数据库,然后开始发送查询。下面使用的几个例子摘自Lucas MCL/15-SQL _ queries _ 02-chinook。如果您使用的是 Python 版本 3,则可能不需要安装。

  • 安装 SQLite3
  • 导入 SQLite3
pip install pysqlite3import sqlite3
  • 连接到数据库
conn = sqlite3.connect('data/Chinook_Sqlite.sqlite')
  • 实例化游标对象以获取查询结果
cur = conn.cursor()

现在我们已经连接到数据库,我们可以查询其中的数据。使用 cursor 对象执行查询只会返回 cursor 对象。为了看到结果,我们需要在事后使用fetchall()方法。

  • 使用SELECT语句和WHERE子句执行查询,查看数据库中有多少个表。WHERE子句通常根据某种条件过滤查询结果。在下面的例子中,我用它来返回数据库中类型为“table”的对象的名称。每个 SQLite 数据库都有一个包含模式信息的 sqlite_master 表。以分号结束查询表示语句结束。如果我们希望查询返回表中的所有记录,请使用星号(*)代替name
cur.execute("SELECT name FROM sqlite_master WHERE type='table';")
print(cur.fetchall())**#Output:**
[('Album',), ('Artist',), ('Customer',), ('Employee',), ('Genre',), ('Invoice',), ('InvoiceLine',), ('MediaType',), ('Playlist',), ('PlaylistTrack',), ('Track',)]***The output of this query statement returned all of the tables in the database.***

假设我们想要关于数据库中每个表的更详细的信息,那么PRAGMA命令将会给我们提供这些信息。

cur.execute("PRAGMA table_info(Employee)")
info = cur.fetchall()
print(*info, sep = "\n")**#Output:**
(0, 'EmployeeId', 'INTEGER', 1, None, 1)
(1, 'LastName', 'NVARCHAR(20)', 1, None, 0)
(2, 'FirstName', 'NVARCHAR(20)', 1, None, 0)
(3, 'Title', 'NVARCHAR(30)', 0, None, 0)
(4, 'ReportsTo', 'INTEGER', 0, None, 0)
(5, 'BirthDate', 'DATETIME', 0, None, 0)
(6, 'HireDate', 'DATETIME', 0, None, 0)
(7, 'Address', 'NVARCHAR(70)', 0, None, 0)
(8, 'City', 'NVARCHAR(40)', 0, None, 0)
(9, 'State', 'NVARCHAR(40)', 0, None, 0)
(10, 'Country', 'NVARCHAR(40)', 0, None, 0)
(11, 'PostalCode', 'NVARCHAR(10)', 0, None, 0)
(12, 'Phone', 'NVARCHAR(24)', 0, None, 0)
(13, 'Fax', 'NVARCHAR(24)', 0, None, 0)
(14, 'Email', 'NVARCHAR(60)', 0, None, 0)***The output of this query statement returned the Employee table's Column ID, Column Name, Column Type, Not Null Values, Default Value, and Primary Key.***

如果您希望输出的格式能够操作或处理数据,那么使用 Pandas 将查询结果包装到 dataframe 中可以节省时间。 cursor.description 属性返回包含以下内容的列描述:

1\. name
2\. type_code
3\. display_size
4\. internal_size
5\. precision
6\. scale
7\. null_ok

使用 description 属性,我们可以使用 list comprehension 获取每一列的名称。下面是一个查询语句,选择不在美国的客户的全名、id 和国家,结果封装在一个数据帧中。

**#Importing Pandas**import pandas as pdcur.execute("""
    SELECT FirstName, LastName, CustomerId, Country
    FROM customer
    WHERE country != 'USA'
""")
df = pd.DataFrame(cur.fetchall())
df.columns = [x[0] for x in cur.description]
df.head(10)**#Output:**

使用 SQL 进行过滤和排序

回到 SQL 查询只允许您检索与您的任务相关的数据的地方。我将介绍一些查询修饰符,ORDER BY是第一个。这个修饰符允许我们按照特定的特性对SELECT语句的结果进行排序。

LIMIT子句就像它听起来的那样,它将输出限制为一定数量的结果。这类似于使用df.head(10)属性查看熊猫数据帧的输出。

BETWEEN运算符允许我们通过过滤设置值之间的结果来进一步选择特定数据。这在查询特定的年龄组、时间范围等时非常有用。

下面我将执行一个查询,从 track 表中选择数据,其中 track 的长度以毫秒为单位,介于 205205 和 300000 之间。结果按曲目的毫秒数降序排列,因此曲目较长的曲目将排在最前面。我还将这个查询限制为只输出 10 个结果。

cur.execute("""
    SELECT Name, AlbumId, TrackId, Milliseconds
    FROM track
    WHERE Milliseconds BETWEEN 205205 AND 300000
    ORDER BY Milliseconds Desc
    LIMIT 10
""")
info = cur.fetchall()
print(*info, sep = "\n")**#Output:** ('Breathe', 212, 2613, 299781)
('Queixa', 23, 524, 299676)
('Getaway Car', 10, 97, 299598)
('Winterlong', 201, 2485, 299389)
('Cherub Rock', 202, 2491, 299389)
('Sonata for Solo Violin: IV: Presto', 325, 3480, 299350)
('Linha Do Equador', 21, 218, 299337)
('Who Are You (Single Edit Version)', 221, 2749, 299232)
('Garden', 181, 2201, 299154)
('The Spirit Of Radio', 196, 2406, 299154)

聚合函数

这些 SQL 函数在执行统计分析时非常有用。我们可以得到一列中的平均值、最小值和最大值以及值的总和。COUNT函数返回满足特定条件的记录数。

GROUP BY函数将返回按设定列分组的结果。例如,按性别、品种或国籍对结果进行分组。

下面是每个国家总销售额的查询示例,结果按每个国家分组。我还将总和别名为“TotalSales ”,以便按每个国家的总销售额对结果进行分组。

cur.execute('''
    SELECT i.billingcountry, sum(total) as 'TotalSales'
    FROM invoice AS i
    GROUP BY billingcountry
    ORDER BY totalsales DESC
    '''
)

info = cur.fetchall()
print(*info, sep = "**\n**")**#Output:**
('USA', 523.0600000000003)
('Canada', 303.9599999999999)
('France', 195.09999999999994)
('Brazil', 190.09999999999997)
('Germany', 156.48)
('United Kingdom', 112.85999999999999)
('Czech Republic', 90.24000000000001)
('Portugal', 77.23999999999998)
('India', 75.25999999999999)
('Chile', 46.62)
('Ireland', 45.62)
('Hungary', 45.62)
('Austria', 42.62)
('Finland', 41.620000000000005)
('Netherlands', 40.62)
('Norway', 39.62)
('Sweden', 38.620000000000005)
('Poland', 37.620000000000005)
('Italy', 37.620000000000005)
('Denmark', 37.620000000000005)
('Australia', 37.620000000000005)
('Argentina', 37.620000000000005)
('Spain', 37.62)
('Belgium', 37.62)

如果您想进一步了解 SQL 聚合器和 select 语句,SQL clauseszetcode提供了一些非常有用的示例。****

结论

当在数据库中查找特定信息时,SQL 是一个非常有用的工具。我相信 SQL 和 Pandas 可以让你查询和操作数据到任何你需要的格式。本教程只是触及了使用 SQL 的可能性的表面。如果您正在寻找一些后续步骤,我建议您查看带有USINGON语句的 join 语句。

参考资料:

SQL 连接:一个简单的例子

原文:https://towardsdatascience.com/sql-joins-a-brief-example-9d015868b56a?source=collection_archive---------25-----------------------

照片由 JJ 英Unsplash

理解 SQL 连接的原因和方式

T 他的博客文章原本打算作为我的熊猫加入与合并文章的旁注。但事实证明,它足够长,足以保证自己的帖子(而且对于旁注来说太冗长了)。这并不意味着它是 SQL 连接的全面入门,而是一个示例,帮助那些 SQL 和关系数据库新手开始理解连接两个表的意义。

我们为什么加入?

为什么要费心加入呢?难道我们不能把所有的东西都放进一个电子表格里,然后在那里分类吗?也许…但是这将非常耗时,乏味,并且容易出错。

关系数据库是为连接而设计的。数据库中的每个表都包含特定形式或功能的数据。例如,一个表可能包含公司客户的基本数据,如客户 ID(可用于识别每个客户的唯一 ID)、姓名、年龄、性别、首次购买日期和地址。而一个单独的大得多的表存储详细的交易级别数据—交易 ID、交易日期、客户 ID、产品类别、产品 ID、销售单位和价格。

一个给定的客户(或客户 ID)可能有数百甚至数千笔交易,因此为 transactions 表中的每一行反复存储该客户的基本信息是非常多余的。交易表应该只用于与交易相关的数据。表之间有太多重叠数据是一种浪费,并且会对系统性能产生负面影响。

但这并不意味着我们不关心表之间的联系。考虑到每个表的特殊性,只涉及单个表的分析通常是没有用的。有趣的分析来自组合了多个表的数据集。例如,我们可能希望按年龄或地理位置对交易进行细分。为此,我们需要两个表中的数据。这就是加入的作用。

我们如何加入?

当我们连接两个表时,我们通过一个选定的特征将它们链接在一起。假设我们有两张桌子。第一个是雇员,列出了雇员的惟一 ID 号、姓名和职务。第二个是 Sale ,它通过将雇员的 ID 号和售出的单位附加到一个唯一的销售号上,列出了谁进行了何种销售的数据:

SELECT * FROM Employee
SELECT * FROM Sale

我们的两张表,员工(左)和销售(右)

(为了清晰起见,我省略了图形中列名的下划线)

现在让我们把两张桌子连接起来。为了链接这两个表,我们需要选择一个列(或列的组合)作为交叉点——让我们将选择的列称为连接索引。共享相同连接索引值的表条目被连接在一起。请注意,交集不一定是一对一的。例如,Tony 完成了两笔销售,因此在连接表时,他的两笔销售都将链接到 Tony(也称为雇员 ID 1)。

当我们连接表时,我们通常希望连接索引是惟一的。如果连接索引不是惟一的,可能会发生奇怪的事情。例如,假设我们有第二个员工叫 Tony(以及下面的表格),他是一个超级明星销售员。如果我们不是根据“员工 ID”加入,而是根据“姓名”加入,那么我们会错误地将超级巨星托尼的销售额与我联系起来,使我的奖金过高:

不建议对非唯一列进行联接

超级巨星托尼也会因为我可怜的销售业绩而得到好评(他并不需要)。因此,为了避免这种情况,我们加入了一个具有独特值的列,如“员工 id”(我删除了超级巨星托尼,因为他只是为了说明不要做什么,他令人难以置信的成功让我感到不值):

“雇员 ID”列提供了两个表之间的链接

有各种类型的 SQL 连接,我不会在这里详细讨论它们。在这个例子中,我们将使用一个左连接,这意味着我们对左表中的行进行优先级排序。因此,我们的输出将包括左表(带有“姓名”和“职位”的那一行)中的每一行,而不管是否与右表匹配——因此,没有销售的雇员仍将在我们的输出中有一行,但是“销售数量”和“销售单位”列没有值(确切地说是空值)。

让我们来看看我们的输出(我们只从右边的表中选择“销售数量”和“售出单位”,并按“员工 ID”排序):

SELECT e.*, s.Sale_Number, s.Units_Sold
FROM Employee as e
    LEFT JOIN Sale as s ON e.Employee_ID=s.Employee_ID
ORDER BY e.Employee_ID

我们左连接的结果

我们的连接的输出现在包括了来自两个表的数据。Tony 的销售数据与他的员工数据(由于员工 ID)相关联,Lisa 也是如此。注意两件事:

  1. 这看起来有点重复,因为“雇员 ID”、“姓名”和“职位”会重复出现,次数与雇员的销售额一样多。实际上,我们不会就此止步。接下来,我们很有可能用做一个分组,以统计每个雇员的销售额,或者计算每个雇员每次销售的平均销售量。
  2. 输出中缺少雇员 ID 3,因为我们进行了左连接,在左表中没有雇员 ID 3 的条目。因此,它被省略了。

我们简短的例子到此结束。希望这能让您对我们为什么需要连接以及它们是如何工作的有一个基本的了解。

关键要点:

  • 数据库表通常包含非常具体的信息。因此,有意义的分析通常结合来自多个表的数据。
  • 这是通过 join 操作完成的,join 操作通过基于指定的列匹配两个表来组合它们。
  • 用于合并表的列应该只包含唯一的值。
  • 有各种类型的连接。本例中的一个是左连接,它返回左表中的每一行,不管是否匹配。

使用 Docker、SQL 客户端和 Python 进行数据分析的 SQL-lise Elasticsearch 查询

原文:https://towardsdatascience.com/sql-lise-elasticsearch-query-for-data-analysis-using-docker-sql-client-and-python-part-1-bd4db524c452?source=collection_archive---------28-----------------------

在本文中,我将解释使用简单的 SQL 语言分析 Elasticsearch 中的原始数据的两种不同方法

关于弹性搜索:

Elasticsearch 是一个非 SQL 数据库,由一家名为 Elastic 的公司作为开源和商业产品提供。它用于存储和执行对数据的大数据分析,如 web 服务器日志、来自任何应用程序的事件数据或物联网中的传感器数据。

elasticsearch 之所以受欢迎,是因为它安装简单,无需任何额外的软件包就可以从几个节点扩展到数百个节点。它使用内置的 REST API 对数据执行查询。

所以你听说了 Elasticsearch 可以做的所有很酷的事情,你决定在你的项目中使用它来存储和分析你的日志或事件或任何其他数据。但是您发现用 JSON 编写一个复杂的嵌套查询很麻烦,只是为了对存储在 Elasticsearch 中的数据进行简单的分析。

例如,Elasticsearch 中的一个简单过滤查询如下所示:

GET /_search
{
 "query":{
    "bool":{
      "filter":[
                {"term":{"status":"running"}},
                {"range":{"last_updated_date":{"gte":"2019–12–31"}}}
              ]
      } 
   }
}

相同的查询可以用 SQL 编写,如下所示:

SELECT * 
FROM   sample_table 
WHERE  status = "running" 
       AND last_updated_date > "2019–12–31"

现在,从 Elasticsearch 6.3 版本开始,其中一个插件 X-Pack 是开放的,并默认集成为 Elasticsearch 堆栈(ELK)中的一个功能。

我们可以使用 X-Pack 插件提供的特性,使用类似 SQL 的查询来分析 Elasticsearch 中的数据,而不是编写复杂的嵌套 JSON 查询。

我们将看看两种不同的方法:

  1. 使用通用数据库客户机(Dbeaver):连接到我们的 Elasticsearch 服务器,用 SQL 方言运行我们的查询。
  2. 使用 Python 模块(elasticsearch-dbapi 和 Sqlalchemy):连接到我们的 elasticsearch 服务器,并将数据查询到 pandas dataframe 中,以执行进一步的分析和可视化。

设置及要求:

  1. Ubuntu OS: 我用 Ubuntu 做演示,但是所有需要的工具和模块也可以在其他 OS 上使用。
  2. 这是一个通用的数据库工具,也不支持 SQL。你可以从这里下载。
  3. Docker: 为快速旋转的麋鹿群在我的本地进行演示。你可以从这里下载安装。如果您已经运行了一个 Elasticsearch 实例,那么您可以跳过这一步。
  4. Jupyter notebook: 对于第二种方法,我们在获取后用 python 分析数据。

第一种方法-分析来自数据库客户端的弹性搜索数据:

首先,我将克隆一个免费的 git 存储库,其中包含了在本地运行 ELK 集群所需的所有 docker 文件。您也可以从这里下载/克隆这个库。

现在进入克隆项目的目录,在 docker 命令下运行:

$ sudo docker-compose up -d

如果一切正常,您将得到下面的输出:

docker-compose up 命令的输出

让我们使用以下命令检查所有服务是否都已启动:

$ sudo docker-compose ps

docker-compose ps 命令的输出

我们看到我们的 Elasticsearch 服务在端口 9200 运行,而 Kibana 在端口 5601 运行。

出于演示目的,我添加了一些示例数据。如果您不是在处理自己的数据,那么您也可以使用 Kibana 的主页进行同样的操作,如下所示:

用于添加示例数据的 Kibana 主页

首先,让我们使用 Elasticsearch REST API 格式获取数据。我正在使用 curl 运行以下对飞行样本数据的查询:

curl -X GET "elastic:changeme@localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": { 
 "bool": {
   "filter": [ 
   { "term":  { "DestCountry": "IT" }},
   { "range": { "timestamp": { "gte": "2020-03-06T21:08:31.000Z" }}}
      ]
    }
  }
}
'

以下是输出:

Elasticsearch REST API 格式的简单提取查询的输出

让我们用 SQL 语言运行同样的查询。为此,打开 Dbeaver 客户端并单击 add connection:

在 Dbeaver 中添加连接

现在添加凭据和主机详细信息:

添加主机和凭据

Dbeaver 将自动下载并安装连接到 Elasticsearch 服务器所需的驱动程序。

通过 Dbeaver 自动下载驱动程序

如果您面临驱动程序版本的任何问题,请在此从下载所需版本。并将其添加到编辑驱动设置中,点击确定

在编辑驱动程序设置中添加下载的驱动程序(jar 文件)

现在点击测试连接如果一切正常,您将得到如下所示的成功消息:

测试连接的成功消息

现在点击完成。您将在左侧看到与 Elasticsearch 服务器的连接:

现在点击新建 SQL 编辑器:

现在,我们将使用 SQL 语言编写相同的查询:

SELECT
 *
FROM
 kibana_sample_data_flights
WHERE
 "DestCountry" = 'IT'
 AND "timestamp" > '2020-03-06T21:08:31.000Z'

瞧啊。!您将得到一个漂亮的表格格式的输出:

我们的 SQL 查询的最终输出

第二种方法-将数据查询到 pandas dataframe:

从 Elastic search 获取数据到 python 的老方法是使用 Python Elasticsearch 客户端(此处阅读文档),它使用我们在 Curl 中看到的相同的 REST API 查询格式。

我们将通过使用简单的 SQLAlchemy 和 elasticsearch-dbapi 模块来简化这一点。

下面是示例代码:

##Importing required library
from sqlalchemy import create_engine
import pandas as pd## Creating connection to our Elasticsearch server
## format of connection string is: 
## "dialect://username:password@host:port/"
connection_es=create_engine("""elasticsearch+[http://elastic:changeme@localhost:9200/](http://elastic:changeme@localhost:9200/)""")##Fetching Data using SQL language and Pandas read_sql function
result_data=pd.read_sql("""
SELECT
 *
FROM
 kibana_sample_data_flights
WHERE
 "DestCountry" = 'IT'
 AND "timestamp" > '2020-03-06T21:08:31.000Z'
""", connection_es)

在上面的代码中,我们使用了之前在 SQL 客户机上运行的相同的 SQL 查询。

最后,我们将得到熊猫数据帧的输出,您可以使用该数据帧进行进一步的分析和可视化

Python 代码的最终输出

结论:

在本文中,我们看到了如何使用简单的 SQL 语言而不是复杂的嵌套 REST API 查询来分析 Elasticsearch 中的数据。我们看到了从 Elasticsearch 查询数据的两种不同方式,一种是用 DB client,另一种是用 python。

我希望你喜欢我的文章,如果你想了解更多关于这个话题的信息,你可以在insta gramLinkedIn上关注并留言给我。

按需 SQL:一种更简单的数据查询方式

原文:https://towardsdatascience.com/sql-on-demand-an-easier-way-to-query-data-a9aa6e593f18?source=collection_archive---------21-----------------------

查询 CSV、JSON 和 Parquet 之类的文件,而无需将其从其位置移走

作者图片(Azure Synapse Workspace)

TLDR

  • 关于 SQL 按需
  • 创建数据源
  • 获得对 Azure 存储的访问权限
  • 通过 Azure Synapse Workspace 查询 Azure 存储中的文件
  • YouTube 视频对以下步骤进行了可视化描述

关于 SQL 按需

在我的上一篇帖子中,我提到了 Azure Synapse Analytics 的 10 大特性。Synapse SQL 数据库有 2 个选项:SQL 池& SQL On-Demand。在这篇文章中,我将深入探讨第二种选择;SQL 随需应变。SQL On-Demand 是一个有趣的特性,它将使查询数据变得更加容易。

那么什么是 SQL 随需应变?根据微软文档。这是一个

Synapse SQL on-demand (preview)是一种无服务器查询服务,使您能够对 Azure 存储中的文件运行 SQL 查询。

那么,您会在存储中找到什么类型的文件呢?这些文件类似于 CSVParquet 文件和 JSON 。这非常有趣,因为通常您必须将这些文件导入到数据库中,更不用说确保数据格式与目标数据库环境兼容了。

不拉文件怎么查询文件?

这是通过创建外部数据源来完成的。数据来源包括:

  • 万能钥匙
  • 数据库范围的凭据

如何创建数据源

首先,确保您在想要创建它的数据库中。那应该不是大师吧。您可以像这样更改您的数据库。

作者图片

  1. 创建主密钥

—创建将保护凭证的主密钥:
通过密码创建主密钥加密= <在此输入非常强的密码>

2。创建数据库范围的凭据

微软文档有一个公共的 URI 可以访问,以按需测试 SQL

—在我们的演示存储帐户中为容器创建凭据
创建数据库范围的凭据 sqlondemand
,其中 IDENTITY= '共享访问签名',
SECRET = ' SV = 2018–03–28&ss = BF&SRT = SCO&sp = rl&ST = 2019–10–14t 12% 3a 10% 3a 25 z&se = 2061–12–31t 12% 3a 10%

3。通过创建外部数据源将所有这些放在一起

用(
LOCATION = 'https://sqlondemandstorage . blob . core . windows . net '
CREDENTIAL = sqlondemand
)创建外部数据源 SqlOnDemandDemo

如果需要重新创建外部数据源

如果您需要重新创建它,您需要以特定的顺序删除它。首先删除外部数据源,其次是作用域凭据,最后是主密钥。

删除外部数据源 SqlOnDemandDemo

删除数据库范围的凭据 sqlondemand

放下主密钥

理解共享访问签名

理解在 SQL On-Demand 中创建数据源的用例。假设你在 Azure 存储中有一个文件夹,需要授予某人访问权限。这

在上面 2 的脚本中,我们创建了数据库范围的凭证。这是通过使用所谓的共享访问签名 URI 又名 SAS URI。是凭证给了你访问 Azure 存储的权限。这是一个非常漂亮的功能,因为你可以安排一个人访问多长时间。

那么,我为什么需要一个 SAS URI?

  • 获取 Azure 存储帐户的访问权限
  • 为 SQL 按需外部数据源创建数据库范围的凭据

如果您在创建数据源之前直接进入存储,那么创建数据源的步骤可能会更有意义。这在现实世界中很有可能发生,因为在查询数据之前,您应该知道/知道您想要提取什么数据。

了解 Azure 存储中有什么?

要进入存储,你需要使用一个叫做 Azure Storage Explorer 的工具。这是描述其功能的最简单的方式。

Windows 资源管理器= > OnPremise

Azure Storage Explorer = >云

现在我们了解了 Azure Storage Explorer,让我们打开我们试图查询的文件。

设置对 Azure Storage Explorer 的访问

  1. 单击图标并
  2. 选择“添加帐户”

作者照片

3.选择共享访问签名 URI

4.附加共享访问签名 URI,然后按下一步

粘贴在 URI。其他所有内容,如显示名称、Blob、文件和队列端点都将自动填充。

微软文档已经慷慨地提供了一个公共的 URI 来按需测试 SQL

注:

  • Blob 端点 + 密钥与这个 /?”

斑点终点—https://sqlondemandstorage.blob.core.windows.net

加上这个——/?

secret—SV = 2018-03-28&ss = BF&SRT = SCO&sp = rl&ST = 2019-10-14t 12% 3a 10% 3a 25 z&se = 2061-12-31t 12% 3a 10% 3a 00 z&SIG = klsu 2 ullcscyts 0 an 0 nozepo 4 to 5 jaggbvw % 2 FJ x2 lguw

5.选择连接

现在,您应该能够在 Azure 存储中看到 CSV、Parquet 和 JSON 文件

从 Azure Synapse 工作区查询 Azure 存储中的文件

现在我们已经了解了要查询的文件,让我们回到 Synapse 工作区进行查询。

这是一个来自微软文档的示例查询。

[## 快速入门:按需使用 SQL-Azure Synapse Analytics

Synapse SQL on-demand (preview)是一种无服务器查询服务,使您能够对放置在…中的文件运行 SQL 查询

docs.microsoft.com](https://docs.microsoft.com/en-us/azure/synapse-analytics/quickstart-sql-on-demand)

从 OPENROWSET
(
BULK ' CSV/population/)中选择前 10 个
。' csv ',
DATA _ SOURCE = ' SqlOnDemandDemo ',
FORMAT = 'CSV ',PARSER _ VERSION = ' 2.0 '
)
WITH
(
country _ code VARCHAR(5)
,country_name VARCHAR (100)
,year smallint
,population bigint
) AS r
其中
country_name = 'Luxembourg '且年份= 2017

我们可以看到熟悉的 SQL 语句,如 SELECT、WITH 和 WHERE 子句。在 from 语句中,我们有 OPENROWSET。这是您设置文件所在的文件夹结构的地方。上面的查询指定遍历文件夹路径“csv/population”中的所有 CSV 文件,以查找国家名称为卢森堡且年份为 2017 年的位置。所有这些都无需移动数据。我们可以对 Parquet 文件和 JSON 文件做同样的事情。查看 my GitHub 链接获取其他示例。

结论

SQL On-Demand 无疑是 Synapse SQL 数据库的一个有趣部分。最佳用例之一是将数据保存在一个位置,同时仍然能够运行报告、数据分析和机器学习等。

使用 Python 在云上运行 SQL

原文:https://towardsdatascience.com/sql-on-the-cloud-with-python-c08a30807661?source=collection_archive---------4-----------------------

Google 云和 Python 上的 SQL 简明指南

蒂姆·波格丹诺夫在 Unsplash 上的照片

如今,每个人和他们的狗都在使用云——而且理由充分。它把过去棘手的任务变得像从木头上掉下来一样简单。

在互联网上开发、配置和托管任何东西从来都不简单。现在,假设您需要担心每天都在发生的安全威胁、全球范围的延迟、基础架构扩展、服务中断等等。

但现在,我们可以拥有数据库、网络应用、物联网中心,以及比我煮咖啡还快得多的东西——这一切都要感谢云。

这些新的云服务能够在世界上几乎任何地方托管,具有顶级的安全性、自动扩展和 99.999%的可用性保证。云改变了技术。

我们将介绍使用谷歌云平台(GCP)建立 MySQL 数据库所需的步骤,以及如何使用 Python 查询该数据库。

创建云 SQL 实例

我们需要做的第一件事是创建我们的数据库。为此,我们前往云 SQL 实例页面

我们可以(1)创建一个新项目,或者(2)使用现有项目。

这里,我们有两个选择—使用现有项目或创建一个新项目。如果您还没有项目设置,现在就用创建项目创建一个。

我们可以选择不同的 SQL 版本。

在下一页,我们将单击创建实例,,这将带我们选择 MySQL、PostgreSQL 和 SQL Server,我们将使用 MySQL 。如果您愿意,可以随意使用其他选项。

创建一个 MySQL 实例。

接下来,我们设置实例信息;这非常简单——选择您或您的客户最近的地区,并记住您的密码(我们在稍后连接 Python 时需要它)。

实例详细信息区域。

现在,我们应该了解一下我们的数据库概况。实例创建通常需要几分钟时间。

连接设置

启用 IP 访问

返回我们的实例详细信息页面(您可以通过点击实例 ID 这里的来访问它)。

实例详细信息区域的连接选项卡中的 IP 授权设置。

我们单击连接选项卡(在左侧任务栏上),然后单击公共 IP 下的 +添加网络。为网络命名,并输入您希望允许的 IP 地址范围— 0.0.0.0/0允许输入所有 IP 地址。

当然,这是一个相当大的安全问题,这就是为什么我们使用 SSL 加密来限制对我们实例的访问。

实例详细信息区域的连接选项卡中的 SSL 设置。

仍然在 Connections 选项卡上,我们向下滚动到 SSL 部分。点击只允许 SSL 连接,然后创建一个客户端证书 —下载所有三个.pem文件—我们将在与 Python 连接时使用它们。

建立联系

接下来,我们用 Python 连接到我们的实例。

首先,我们需要安装 MySQL 连接器库:

pip install mysql-connector-python

我们需要一些东西来建立我们的连接:

  • 用户名——应该是root
  • 密码—我们之前已经设置好了。

  • Host —我们的 SQL 实例的公共 IP 地址,我们可以在我们的云 SQL 实例页面上找到它。
  • SSL 认证——三个文件,server-ca.pemclient-cert.pemclient-key.pem

所有这些都在我们的config变量中使用,我们用它来建立我们的连接,如下所示:

我们现在用 Python 连接到了我们的云 MySQL 实例!

查询云

现在我们已经设置好了一切,让我们上传一些数据并运行一些快速查询。

创建数据库

首先,我们需要一个数据库来保存我们的数据——我们使用之前设置的连接cnxn:

现在,我们通过将database: **testdb**添加到我们的config字典中来连接到testdb,就像我们之前做的那样:

实例详细信息区域中的数据库选项卡。

回到我们的实例细节窗口,我们可以在 Databases 选项卡中看到新的testdb数据库。我们也可以在这里通过点击创建数据库来创建新的数据库。

上传数据

我们将在 Kaggle 上使用 1957 年的所有太空任务数据集。你可以从这里下载一个干净的 CSV 格式的数据。

前五排的都来自 1957 年的太空任务数据。展示了 SpaceX Starship hop 测试坚忍号火星车发射

我们使用cursor.execute创建一个名为space_missions的新表,并使用正确的列标题:

接下来,我们插入数据:

现在我们的数据库应该包含完整的space_missions表。我们可以通过查询表中的前五行来测试这一点,这应该与我们前面看到的前五行相匹配:

我们的云 MySQL 数据库中的前五行。再次展示了 SpaceX Starship hop 测试坚忍号火星车发射

输出与我们之前看到的一样,这意味着我们现在已经成功地将数据存储在云中。

可以像我们通常对 MySQL 所做的那样执行任何进一步的查询或转换。

就是这样!

现在我们有了一个带 SSL 加密的全功能 MySQL 数据库。从世界任何地方连接到我们的数据库只需要不超过四行 Python 代码。

从那时起,我们可以像查询任何其他数据库一样查询我们的数据库——它非常容易获取和使用。

如果您有任何问题、反馈或想法,请在 Twitter 上或下面的评论中告诉我。感谢阅读!

有兴趣了解更多关于谷歌云的信息吗?您可能会喜欢我以前的一篇文章,其中介绍了 Docker 和 GCP 的 API 部署:

[## 用 Python 和 Docker 部署 API

使用 Flask、Docker 和 Google Cloud 部署 API 的权威指南

towardsdatascience.com](/deploy-apis-with-python-and-docker-4ec5e7986224)

基于 SQL 顺序的计算

原文:https://towardsdatascience.com/sql-order-based-calculations-b924e79401a0?source=collection_archive---------48-----------------------

SQL 如何解决这种困难但常见的计算

作者图片

SQL 中字段值的联合很常见,比如 firstname+lastname 和 year(生日)。无论一个表达式包含多少个字段,它们都来自同一行。我们称之为行内计算

相应的,还有行间计算。例子包括获得冠军和亚军的结果之间的差异,以及计算从 1 月到当月的累计销售额。为了确定冠军和亚军,需要根据结果对数据进行排序。从某一点到另一点的累加和也要求数据是有序的。所以我们称它们为基于顺序的计算。行内计算处理单个记录中的值,而行间计算处理有序记录之间的差异。

引用上一条/下一条记录中的值

最简单也是最常见的基于顺序的计算是,当记录已经按一定顺序排序时,引用上一条或下一条记录中的值。下面是三种情况:

1。计算一只股票每天的增长率(链接相对比率)

按日期排序记录,并参考前一天的收盘价。

2。计算一只股票三天内的平均价格,这三天分别是前一天、当天和第二天(移动平均线)

按日期排序记录,并参考前一天和第二天的收盘价。

3。有多只股票。计算每个交易日每只股票的涨幅(集团内环比)

按股票分组记录,按日期对每组排序,参考前一天的收盘价。

现在让我们看看 SQL 如何处理这种基于订单的计算。

早期的 SQL 解决方案

早期的 SQL 没有窗口函数。为了引用相邻记录中的值,该语言将两个记录合并成一个记录。

下面是处理任务 1 的程序:

SELECT day, curr.price/pre.price rate
FROM (
  SELECT day, price, rownum row1
  FROM tbl ORDER BY day ASC) curr
LEFT JOIN (
  SELECT day, price, rownum row2
  FROM tbl ORDER BY day ASC) pre
ON curr.row1=pre.row2+1

通过当天和前一天对表执行自联接,将前一天的收盘价和当天的收盘价放入一条记录中,然后执行行内计算以获得增长率。您可以看到子查询用于一个简单的任务。

SQL 也使用 JOIN 计算任务 2 中的移动平均值:

SELECT day, (curr.price+pre.price+after.price)/3 movingAvg
  FROM (
    SELECT day, price, rownum row1
    FROM tbl ORDER BY day ASC) curr
  LEFT JOIN (
    SELECT day, price, rownum row2
    FROM tbl ORDER BY day ASC) pre
  ON curr.row1=pre.row2+1
  LEFT JOIN (
    SELECT day, price, rownum row3
    FROM tbl ORDER BY day ASC) after
  ON curr.row1=after.row3-1

又一个子查询将被连接一天。想象一下获取过去 10 天和未来 10 天的移动平均值的程序。写 20 个 JOINs 肯定会烦死你。

任务 3 更复杂。由于有多只股票,SQL 添加了一个代码列来区分不同的股票。因此,增长率是在一组库存记录中计算的:

SELECT code, day ,currPrice/prePrice rate
  FROM(
    SELECT code, day, curr.price currPrice, pre.price prePrice
    FROM (
      SELECT code, day, price, rownum row1
      FROM tbl ORDER BY code, day ASC) curr
    LEFT JOIN (
      SELECT code, day, price, rownum row2
      FROM tbl ORDER BY code, day ASC) pre
    ON curr.row1=pre.row2+1 AND curr.code=pre.code
  )

有两点我想说一下。您必须使用“code,day”对表进行复合排序。代码走在前面,因为您需要首先将相同股票的记录放在一起,然后对它们进行排序。您还需要在连接条件中加入代码匹配,因为如果您不这样做,增长率将在不同股票的相邻记录之间进行计算。那会产生无用的脏数据。

具有窗口功能的解决方案

SQL 2003 引入了窗口函数来表达顺序的概念。在 SQL 中实现基于顺序的计算更容易。以上三项任务可以通过更简单的方式实现:

以下程序计算任务 1 中的链路相对比率。为了更容易理解,我把窗口函数写成几个缩进:

SELECT day, price /
    LAG(price,1)
      OVER (
        ORDER BY day ASC
      ) rate
 FROM tbl

LAG 函数实现对前一条记录的引用。它的两个参数找到了直接在它之前的记录的价格。OVER 是 LAG 函数中的 substatement。每个窗口函数都有一个 OVER 语句。它的作用是定义一个待分析的有序集。在这个例子中,要分析的数据集已经按日期排序。

下面的程序在任务 2 中计算移动平均值。一种方法是用 LAG 函数获取前一个值,然后用 LEAD 函数获取下一个值。另一种方法是这个程序使用的 AVG 函数,这是更可取的。AVG 函数可以一次得到指定范围内的平均值,例如覆盖前 10 条记录和后 10 条记录的平均值,而滞后/超前函数一次只能得到一个值。

SELECT price,
    AVG(price) OVER (
      ORDER BY day ASC
      RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING
    ) movingAvg
FROM tbl;

这样,通过覆盖前面的 n 条记录和后面的 n 条记录,更容易得到一个移动平均值。你只需要在 t 之间改变 range 定义的 RANGE 参数。

该程序执行任务 3 中要求的基于组内订单的计算。将同一只股票的所有收盘价记录放在一个组中,这可以通过 window 函数实现。

SELECT code, day, price /
    LAG(price,1)
      OVER (
        PARTITION BY code
        ORDER BY day ASC
      ) rate
FROM tbl

OVER 函数中的 PARTITION BY substatement 定义了记录分组的方式,并限制了组中的每个 LAG 操作。比之前的 JOIN 方法更直观。JOIN 方法按多个字段对记录进行排序,这相当于 PARTITION BY,但很难理解。

按序列号排列的位置

它是一个相对位置,用来获取有序集合中相邻记录的值。有时,我们需要找到记录的绝对位置,因为计算每天的发行价和收盘价之间的差异需要:

SELECT day, price-FIRST_VALUE(price) OVER (ORDER BY day ASC) FROM tbl

或者计算第 10 个交易日的最高收盘价与每天收盘价之间的差额,需要:

SELECT day, price-NTH_VALUE(price,10)OVER (ORDER BY day ASC) FROM tbl

还有更复杂的情况,其中用于定位记录的序列号是未知的,并且需要从现有值中生成:

4。有按收盘价排序的股票记录,我们想找到中间位置的价格(中位数)

让我们从简单的一只股票开始。记录按价格排序后,我们还是不知道中间的地方在哪里。所以我们需要根据记录的数量来计算中间位置的序号:

SELECT *
  FROM
    SELECT day, price, ROW_NUMBER()OVER (ORDER BY day ASC) seq FROM tbl
  WHERE seq=(
    SELECT TRUNC((COUNT(*)+1)/2) middleSeq FROM tbl)

FROM 语句中的子查询使用 ROW_NUMBER()为每一行生成一个序列号。WHERE 语句中的另一个子查询获取中间位置的序列号。这个 SQL 查询中有两点值得您注意。一个是不能直接在第一个子查询上执行筛选,因为 WHERE 子句不能使用同级 SELECT 子句的计算字段。这是由 SQL 执行顺序决定的。另一个是 WHERE 子句中的子查询的结果是一个一列一行的表,表中有一个值。它可以被视为要与 Seq 进行比较的单个值。

下面是获取多只股票记录的中位数的 SQL 程序:

SELECT *
  FROM
    (SELECT code, day, price,
      ROW_NUMBER() OVER (PARTITION BY code ORDER BY day ASC)seq  FROM tbl) t1
    WHERE seq=(
      SELECT TRUNC((COUNT(*)+1)/2) middleSeq
      FROM tbl t2
      WHERE t1.code=t2.code)

除了在窗口函数中用嵌入分区外,在计算中间位置的序号时,要确保查询条件是为一只股票设置的。

5。计算最高收盘价当天与前一天相比的涨幅

需要两个排序操作来定位收盘价最高的记录。让我们从一只股票开始:

SELECT day, price, seq, rate
  FROM (
    SELECT day, price, seq,
      price/LAG(price,1) OVER (ORDER BY day ASC) rate
    FROM (
      SELECT day, price,
        ROW_NUMBER ()OVER (ORDER BY price DESC) seq
      FROM tbl)
    )
  WHERE seq=1

连续的两级子查询将有用的新数据添加到原始表中。ROW_NUMBER 按升序排列收盘价。滞后函数计算每天的增长率。最后,我们通过过滤操作得到收盘价最高的那一天(seq=1)。

过滤操作应该在计算增长率之后进行,因为如果过滤掉收盘价最高的前一天,就无法计算增长率。

基于订单的分组

该顺序在执行分组时也很有用。这里有一个例子:

6。求一只股票连续上涨的最大交易日数。

有点复杂。逻辑是这样的:通过将连续上升的股票记录放入同一组,将按日期排序的股票记录分成若干组。也就是说,如果一个记录的收盘价高于前一个记录的收盘价,那么就把它们归入同一组;如果一个记录的收盘价比前一个低,那么把它放入一个新的组。所有记录分组后,对每组中的记录进行计数,得到最大计数,这就是我们想要的结果。

这种类型的分组是根据记录的顺序执行的。因为 SQL 只支持等分组,所以它需要将基于顺序的分组转换为等分组。它是这样做的:

1)按日期对记录进行排序,并为每个日期获取前一个日期的收盘价;

下面是完成这项工作的完整 SQL 程序:

SELECT MAX(ContinuousDays)
  FROM (
    SELECT COUNT(*) ContinuousDays
    FROM (
      SELECT SUM(RisingFlag) OVER (ORDER BY day) NoRisingDays
      FROM (
        SELECT day, CASE WHEN price>
           LAG(price) OVER (ORDER BY day) THEN 0 ELSE 1 END RisingFlag
      FROM tbl
      )
    ) GROUP BY NoRisingDays
  )

这个 SQL 解决方案包括 4 级子查询。与 Java 和 C 语言不同,SQL 是基于集合的。它提供了直接在集合上工作的方法,而不需要显式可控的循环操作和临时中间变量。与直观的思维方式不同,SQL 实现采用迂回的方式,使用标准的集合操作来获得结果。然而,Java 或 C 语言通过循环处理每个记录更接近我们的自然思维方式。生成新组或向现有组追加数据是很直观的。但是它们不支持集合运算。从这个角度来看,SQL 和 Java/C 各有利弊。

真实世界的计算场景可能比您想象的更复杂:

7 .。找出连续上涨 3 天的股票。

这个场景需要基于顺序的分组、对分组后子集的操作、标准分组和 HAVING 子句。首先,我们使用前面查询任务中的实现方法获得每只股票的所有上涨组,用一个分组操作将其括起来,以计算连续上涨的最大天数,然后通过 HAVING 子句找到连续上涨 3 天的股票:

SELECT code, MAX(ContinuousDays)
  FROM (
    SELECT code, NoRisingDays, COUNT(*) ContinuousDays
    FROM (
      SELECT code,
      SUM(RisingFlag) OVER (PARTITION BY code ORDER BY day) NoRisingDays
      FROM (
        SELECT code, day,
          CASE WHEN price>
            LAG(price) OVER (PARTITION BY code ORDER BY day)
          THEN 0 ELSE 1 END RisingFlag
        FROM tbl
      )
    ) GROUP BY NoRisingDays
  )
  GROUP BY code
  HAVING MAX(ContinuousDays)>=3

SQL 程序几乎难以理解。

在引入窗口函数之前,SQL 在处理基于顺序的计算时非常笨拙(即使现在一些数据库仍然不支持窗口函数)。理论上,它可以管理所有的场景,但实际上所有的都不算什么,因为实现太复杂了。窗口函数极大地改善了 SQL 的困境,尽管它在处理复杂场景时仍然迂回曲折。

SQL 的问题根源于它的理论基础——基于无序集的关系代数。窗口函数是有用的,但不能解决根本问题。

实际上,计算语言中的数组(集合)是自然有序的(它们有自然的序列号)。用 Java 和 C++这样的高级语言很容易理解和实现这个特性。问题是他们处理集合运算的能力很弱。这意味着它们也会产生冗长的程序来处理基于订单的计算(尽管逻辑并不复杂)。

埃斯普罗克·SPL 就是这样一位出色的选手。esProc 是一个专业的数据计算引擎。它以有序集合为基础,为执行集合运算提供全面的功能。它继承了 Java 和 SQL 的优点。在 SPL 进行基于订单的计算非常容易,例如,SPL 有自己简单的解决方案:

1. T.sort(day).derive(price/price[-1]:rate)2. T.sort(day).derive(avg(price[-1:1]):movingAvg)3. T.sort(day).group(code).(~.derive(price/price[-1]:rate)).conj()4. T.sort(price).group(code).(~((~.len()+1)\2))5. T.sort(day).group(code).((p=~.pmax(price),~.calc(p,price/price[-1])))6. T.sort(day).group@o(price >price[-1]).max(~.len()))7. T.sort(day).group(code).select(~.group@o(price>price[-1]).max(~.len())>3).(code)

SPL 提供了实现跨行引用的语法,并为上述所有计算提供了坚实的支持。它使程序员能够以直观、简单和优雅的方式表达逻辑。

SQL —用于数据分析的实用细节备忘单

原文:https://towardsdatascience.com/sql-practical-details-cheat-sheet-for-data-analysis-f98406a71a09?source=collection_archive---------9-----------------------

所有语言中最被忽视却又最重要的一种

D 数据被认为比石油更有价值,因为可以从中提取洞察力和知识,因此数据分析非常重要。而这一切都是从查询数据库开始的,以 SQL 为核心。有大量的 SQL 教程和备忘单。因此,我将避免讨论基础知识,而将更多地关注我从自己的经验中学到的最重要的方面或实际细节。

  1. SELECT 语句语法—数据查询语言
  2. SELECT 语句的逻辑处理顺序
  3. 连接与集合—区分的实际类比
  4. 子查询—查询中的查询

1.SELECT 语句语法— 数据查询语言

在本节中,将介绍 SELECT 语句的 MySQL 语法以及最常用的子句及其实用细节。

**SELECT** [select_option]
{select_expression}
[
  **FROM**
    **JOIN
    ON
    WHERE
    GROUP BY
    HAVING
    ORDER BY
    LIMIT
    INTO**
]

其中—不能使用别名和聚合函数

不允许在 WHERE 子句中引用 列别名 ,因为在执行 WHERE 子句时可能还没有确定列值(下一节将对此进行解释)。

不允许在 WHERE 子句中引用聚合函数,因为 WHERE 子句不能访问整个集合,而是在提交给 db 引擎时应用于每一行。而聚合函数(例如 SUM())处理数据集。

WHERE 子句至少需要一个条件。如果有多个条件,使用 And 或 or 逻辑运算符将它们连接起来。

良好实践 :
在生产环境中,始终使用 WHERE 子句来补充更新和删除查询,以避免意外的更新和/或删除

有—工作原理类似,但要谨慎使用

与 WHERE 子句相比,HAVING 子句的一个优点是它过滤聚合数据,而后者过滤基本数据。换句话说,WHERE 子句充当前置过滤器,而 HAVING 子句充当后置过滤器。因此,聚合函数可以与 HAVING 子句一起使用。

注意:HAVING 子句中的别名:令人惊讶的是,对于“列别名”查询成功执行,而对于“聚合函数”别名查询失败。
最佳实践是避免在 HAVING 子句中也使用别名

不要对应该在 WHERE 子句中的项目使用 HAVING,因为 WHERE 比更优化

分组依据—应包括聚合函数

选择列表应包括汇总数据的函数(如 SUM()、COUNT())。 WITH ROLLUP 修饰符可用于包含表示更高级(即超级聚合)汇总操作的额外行。
**SELECT** cat, **COUNT**(*) **AS** total ... **GROUP BY** category **WITH ROLLUP**;

排序依据和限制

ORDER BY 子句应至少包含一个列名或别名。如果包含多个列,用逗号分隔,则这些列按它们被提及的顺序排序。

利用 LIMIT 子句的【offset,】修饰符(缺省值= 0)来指示从哪一行之后开始行计数。例如,LIMIT 10, 5从第 10 行开始计数,返回 5 行—第 11 行、第 12 行、第 15 行。

INTO —使选择作为 DML(数据操作语言)工作

SELECT … INTO 表单被认为是 DML,因为它操纵(即修改)数据。例如,您有一个空表table,并想从表backup中插入数据。那么下面的查询将作为 DML:
**SELECT** * **INTO** backup **FROM** table;

变量—在客户端会话持续之前一直可用

在给变量赋值时,如果 WHERE 子句返回多个值,那么只有 WHERE 子句返回的最后一个值会被赋给变量。例如,如果以下查询中的 WHERE 子句返回(恐怖、喜剧、浪漫),则将“浪漫”赋给变量:
**SELECT** @category := category **FROM** films **WHERE** rating > 3;

2.SELECT 语句的逻辑处理顺序

这一部分是根据博客“ SQL 操作的顺序 MySQL 以什么顺序执行查询?”作者 Tomer Shay @ EverSQL。
我希望这将使你能够用很少的点击和尝试来编写优化的查询。

注意:语句的实际物理执行由查询处理器决定,顺序可能与下表不同。尽管如此,它给出的结果与查询以低于(默认)执行顺序运行时的结果是一样的。

顺序 1-从和连接

确定整个工作集,即根据子句和子查询的连接来自所有表的数据

订单 2 — WHERE 子句

根据条件过滤数据

顺序 3-GROUP BY 子句

根据一列或多列聚合数据。将数据拆分到不同的块或桶中,其中每个桶有一个键和一个与该键匹配的行列表

订单 4 — HAVING 子句

过滤聚集的数据,即用于过滤掉由 GROUP BY 子句创建的一些存储桶

订单 5 — 窗口功能

就像分组一样:对一组行执行计算,但每一行都保持自己的身份,不会被分组到其他类似行的桶中

顺序 6 —选择子句

筛选和分组后选择数据。在其中使用列名、聚合和子查询

顺序 7 —独特的关键字

从筛选和聚合后剩下的行中丢弃具有重复值的行

顺序 8 — UNION 关键字

将两个查询的结果集合并成一个结果集

Order 9 — ORDER BY 子句

使用列、别名、聚合函数对整个结果集进行排序,即使它们不是所选数据的一部分。
注意:使用 DISTINCT 可以防止按未选择的列排序

订单 10 —限制条款

丢弃查询结果中除前 X 行之外的所有行

3.连接与集合—区分的实际类比

希望您熟悉各种连接语句,我想就如何区分连接和集合设置一个非常实用的类比。

连接-将表可视化为圆形

圆只有一维——半径。
也就是说,在执行 JOIN 语句时,我们只需要一个公共字段(半径)来组合来自两个或多个表(圆)的数据或行。

阿贝克/抄送人(https://creativecommons.org/licenses/by/3.0)

注意:不要将 SQL 连接视觉模拟与维恩图混淆

集合—将表格(来自 select 语句)可视化为矩形

矩形有两个维度——长和宽。
也就是说,在执行 SET 语句时, select 语句(矩形)中使用的字段(长度和宽度)必须具有相同的顺序、相同的编号和相同的数据类型,以便组合并生成结果集,作为单独 select 语句的 UNION、UNION ALL、EXCEPT 或 INTERSECT。

  • 注意上面表格的图示方向确保字段具有相同的顺序、相同的数字和相同的数据类型。
  • UNION 子句在结果集中产生不同的值。若要获取重复值,请使用 UNION ALL。

4.子查询—查询中的查询

标量子查询—必须只返回一条记录

选择列表中的子查询:

**SELECT**
    Category.CategoryName,
    ( **SELECT MAX**(DVDPrice) **FROM** Films
            **WHERE** Films.CategoryID = *Category*.CategoryID ),
    Category.CategoryID
**FROM** *Category*
  • 理解内部查询(*Category*.CategoryID)与外部查询(**FROM** *Category*)之间的链接非常重要
  • 而且因为这个链接** —聚合函数**MAX**(DVDPrice)只返回一个值,那就是 max。Category表中每个类别的价格**

子查询-返回多个值

当在 WHERE 子句中使用和/或与 in、NOT IN、ANY / SOME、ALL、EXISTS、NOT EXISTS 比较条件结合使用时,子查询非常强大。

任何或某些

**SELECT** s1 **FROM** t1 **WHERE** s1 > **ANY** (**SELECT** s1 **FROM** t2);
  • 如果子查询返回的列或值列表中的ANY***s1***比较结果为TRUE,则返回TRUE****
  • 任何一个比较运算符都必须跟在后面** ( =,>,<,> =,< =,<,>,!=),即在列名s1和比较条件ANY之间**

例如:假设t1中有一行包含(10),则表达式为:

  • TRUE如果t2包含(21,14,7)
  • FALSE如果t2包含(20,10)或者t2为空
  • 未知(即NULL)如果t2包含(NULL,NULL,NULL)

全部

**SELECT** s1 **FROM** t1 **WHERE** s1 > **ALL** (**SELECT** s1 **FROM** t2);
  • 如果子查询返回的列或值列表中的值ALL的比较***s1***TRUE,则返回TRUE
  • 所有还必须跟随着一个比较运算符** ( =,>,<,> =,< =,<,>,!=),即在列名s1和比较条件SOME之间**

例如:假设t1中有一行包含(10) ,则表达式为:

  • TRUE如果t2包含(-5,0,5)或者如果表格t2为空
  • FALSE如果t2包含(12,6,NULL,-100),则为 12 > 10
  • 未知(即NULL)如果t2包含(0,NULL,1)

在和不在

**SELECT** s1 **FROM** t1 **WHERE** s1 **IN** (**SELECT** s1 **FROM** t2);
  • 如果***s1***等于子查询返回的IN() 列表中值的任意一个,则返回1 (true),否则返回0 (false)。不要颠倒逻辑
  • IN比较条件的执行是不是听起来有点耳熟——没错,关键字IN就是= ANY的别名。但是,NOT IN不是<> ANY的别名,而是<> ALL的别名
  • 没有比较运算符 ( =,>,<,> =,< =,< >,!列名s1和比较条件IN之间允许有=)

存在与不存在

**SELECT** column1 **FROM** t1 **WHERE EXISTS** (**SELECT** * **FROM** t2);
  • 如果 a ***subquery*** (SELECT * FROM t2)返回任何行,则条件EXISTS ***subquery***TRUE,条件NOT EXISTS ***subquery***FALSE
  • 也就是说,如果t2包含任何行,甚至只包含NULL值的行,则EXISTS条件为TRUE

因为[NOT] EXISTS子查询几乎总是包含相关性。一个现实的例子是下面的查询回答“在一个或多个城市有什么样的商店?”具有相关性cities_stores.store_type = stores.store_type

**SELECT DISTINCT** store_type **FROM** stores 
  **WHERE EXISTS** (**SELECT** * **FROM** cities_stores
    **WHERE** cities_stores.store_type = stores.store_type);

更多例子

参考

感谢您的阅读!如果你觉得这有帮助或者没有帮助,请在评论中告诉我。如果这篇文章有帮助,分享一下

领英

** [## Eklavya Saxena -大纽约市地区|职业简介| LinkedIn

精通数据的分析师和有抱负的数据科学家,拥有 2 年以上的销售或客户行业经验…

www.linkedin.com/in/EklavyaSaxena](https://www.linkedin.com/in/eklavyasaxena/)**

SQL 查询

原文:https://towardsdatascience.com/sql-queries-21958212e9e2?source=collection_archive---------38-----------------------

像我这样无知的人的基础小抄。

国家癌症研究所Unsplash 上拍摄的照片

我不经常使用 SQL,每当我需要它的时候,我发现自己甚至在谷歌上搜索最基本操作的语法。为了帮助自己,我把有用的查询汇总到一个地方。我希望你也会觉得有用。

查询是 Postgres 格式的,但是模式可以转换成其他 SQL 格式。这些笔记基于优秀的 DataCamp 课程,如 SQL 简介、SQL 中的联接数据和 SQL 中的关系数据库简介,以及我自己的 StackOverflow 搜索。尽情享受吧!

符号的关键:

  • 🟠, 🔵、🟢等。表示字段(即变量或列)
  • 🗂️表示表名当一个查询中有多个表时,我称它们为🗂️_1、🗂️_2 等。或者 left_🗂️和 right_🗂️,哪个更方便。

目录:

  1. 创建、更改和删除表格,并用数据填充它们
  2. 概览表&列
  3. 选择列
  4. 过滤行
  5. 聚合、算术、混叠、排序&分组
  6. 内部联接
  7. 案例(长 if-else 语句)
  8. 左&右
  9. 集合论条款
  10. 满&交叉连接
  11. 半&反联接
  12. 子查询

1.创建、更改和删除表,并向其中填充数据

2.表格和列概述

3.选择列

4.筛选行

5.聚合、算法、别名、排序和分组

6.内部联接

7.案例(长 if-else 语句)

8.左右连接

9.集合论子句

10.完全连接和交叉连接

11.半连接和反连接

12.子查询

感谢阅读!

如果你喜欢这篇文章,为什么不在我的新文章上 订阅电子邮件更新 ?通过 成为媒介会员 ,你可以支持我的写作,并无限制地访问其他作者和我自己的所有故事。

需要咨询?你可以问我任何事情,也可以在这里 预定我 1:1

也可以试试 我的其他文章 中的一篇。不能选择?从这些中选择一个:

[## 校准分类器

你确定你的模型返回概率吗?🎲

towardsdatascience.com](/calibrating-classifiers-559abc30711a) [## 增强你对助推的把握

揭秘著名的竞赛获奖算法。

towardsdatascience.com](/boost-your-grasp-on-boosting-acf239694b1) [## 使用 Boto3 处理亚马逊 S3 桶。

完整的备忘单。

towardsdatascience.com](/working-with-amazon-s3-buckets-with-boto3-785252ea22e0)

数据科学家的 SQL 查询

原文:https://towardsdatascience.com/sql-queries-for-data-scientists-5260737fc442?source=collection_archive---------23-----------------------

举例说明。

Miguel A. Amutio 在 Unsplash 上拍摄的照片

SQL 是一种编程语言,大多数关系数据库管理系统(RDBMS)使用它来管理以表格形式存储的数据。

SQL 是数据科学家应该具备的基本技能。你可能会说这是数据工程师的工作,但数据科学家的角色更倾向于全栈。此外,作为一名数据科学家,你不会希望依赖数据工程师从数据库中检索数据。

在本文中,我们将编写复杂的查询来检索存储在表中的数据。我已经将的客户流失数据集上传到 MySQL 数据库的一个表中。

我们将从简单的查询开始,逐步增加复杂性。我将描述所需的数据,然后编写查询从表中检索数据。

让我们首先来看看表中的列。

(图片由作者提供)

银行的客户及其账户有一些特征。“已离开”栏显示客户是否离开银行。

我们现在可以开始查询了。

“客户 Id”和“姓氏”列的前 5 行

SELECT CustomerId, Surname 
FROM CHURN
LIMIT 5;

(图片由作者提供)

具有最高余额的客户的 ID

SELECT CustomerId, MAX(Balance)
FROM CHURN;

(图片由作者提供)

我们没有检索整个“Balance”列,而是使用 MAX aggregate 函数只选择该列中的最大值。

最高余额的前 5 名客户的位置

我们无法使用最大值进行此查询,因为我们需要前 5 名客户。我们可以做的是根据余额对客户进行排序,然后使用限额选择前 5 名。

SELECT Geography, Balance
FROM CHURN
ORDER BY Balance DESC
LIMIT 5;

(图片由作者提供)

没有信用卡的顾客的平均年龄

有一个条件要求我们使用 WHERE 语句。

SELECT AVG(Age)
FROM CHURN
WHERE HasCrCard = 0;39.1121

如果你想知道,拥有信用卡的顾客的平均年龄是 38.8424 岁。

每个国家拥有 2 种以上产品的顾客数量

我们将使用另一个聚合函数来计算客户数量。为了根据属性对客户进行分组,使用了 GROUP BY 语句。

SELECT Geography, COUNT(CustomerId)
FROM CHURN
WHERE NumOfProducts > 2
GROUP BY Geography;

(图片由作者提供)

基于产品数量的平均预计工资

我们可以将 AVG 函数应用于工资,并根据产品数量进行分组。

SELECT NumOfProducts, AVG(EstimatedSalary)
FROM CHURN
GROUP BY NumOfProducts;

(图片由作者提供)

我想介绍另一个与条件有关的陈述。WHERE 语句允许我们选择符合一个或多个条件的条目。但是,它不能与聚合函数一起使用。

对于上面的查询,假设我们只对平均高于 100000 的产品类别的数量感兴趣。因此,我们需要对平均值应用一个条件,这可以使用 HAVING 语句来完成。

SELECT NumOfProducts, AVG(EstimatedSalary)
FROM CHURN
GROUP BY NumOfProducts
HAVING AVG(EstimatedSalary) > 100000;

(图片由作者提供)

年龄超过 50 岁且余额高于平均水平的客户

我们在这里介绍两个新的话题。一种是使用多个条件(年龄和余额),另一种是嵌套的 SELECT 语句。

我们可以使用逻辑运算符(如 AND 和 OR)在 WHERE 语句中组合多个条件。一个条件是显式给出的(年龄> 50),但另一个条件需要使用另一个 SELECT 语句在表上计算。这就是我们需要嵌套 SELECT 语句的地方。

SELECT CustomerId, Age, Balance 
FROM CHURN
WHERE Age > 50 AND Balance > (
    SELECT AVG(Balance)
    FROM CHURN );

结果集的前 7 行(作者图片)

余额上的条件是另一个 SELECT 语句。

留在银行的女性客户数量超过平均水平(任期)并受到影响

它类似于前一个示例,但有一个附加条件。我们将根据三个条件来计算客户数量:

  • 性别
  • 搅动(退出=1)
  • 成为客户的持续时间(任期)
SELECT COUNT(CustomerId) AS 'Number of Customers'
FROM CHURN
WHERE Gender = 'Female' AND Exited=1 AND Tenure > (
 SELECT AVG(Tenure)
    FROM CHURN);

我们还可以使用“AS”关键字调整结果集中的列名。

(图片由作者提供)

结论

我们已经讨论了一些基本和复杂的查询。我们通过查询实现的是,一些计算和过滤是在数据库级别完成的。因此,我们只能检索我们需要的数据,而不是获取所有数据,然后进行过滤和计算。

因为现实生活中的数据库包含更多的数据和许多关系表,所以能够使用 SQL 查询所需的数据非常重要。

感谢您的阅读。如果您有任何反馈,请告诉我。

Postgres 的 SQL 查询清单

原文:https://towardsdatascience.com/sql-query-cheatsheet-for-postgres-96eba6e9e419?source=collection_archive---------9-----------------------

我作为数据科学家和软件工程师使用的 SQL 查询

马库斯·斯皮斯克在 Unsplash 上拍摄的照片

无论是在应用程序中加载数据,还是查询数据来训练机器学习模型,我都会编写大量的原始 SQL。

作为编写大量代码的工程师,有些命令会留在我们的记忆中,但有些命令我们必须查阅。我听过一次,也喜欢重复一遍——优秀的工程师就像索引,他们可以快速查找信息。

这是我个人的 SQL 备忘单,是用 Postgres 编写的,但也大致适用于其他关系数据库。这仅包括查询。没有插入,删除,索引或其他 Postgres 功能。我们将从简单的开始,朝着更有趣的查询努力。

首先,我们将创建一个数据库并插入一些数据,然后我们将对这些数据执行每个 SQL 查询并研究输出。

内容:
1)设置
2)通过
选择并计算
3)限制、偏移和顺序 4)连接
5)相交、联合和除
6)别名
7)聚集数据
8)修改选定值
9) Where 子句

设置

创建几个具有不同数据类型的相关表。我想象了一个虚构公司的 CRM(客户关系管理)系统的数据,这样你就可以把它和现实生活联系起来。

创建数据库后。打开您最喜欢的 SQL 编辑器,运行以下命令为usersmailing_listsproductssales创建表。

-- users whose information the company has
create table users (
 id serial primary key,
 first_name varchar (50),
 location varchar (50),
 created_at TIMESTAMP
);-- users who are on the company mailing list
create table mailing_lists (
 id serial primary key,
 first_name varchar (50),
 email varchar (50),
 created_at TIMESTAMP
);-- products the company sells
create table products (
 id serial primary key,
 name varchar(50),
 manufacturing_cost int,
 data jsonb,
 created_at TIMESTAMP
)-- sales transactions of products by users
create table sales (
 id serial primary key,
 user_id int,
 product_id int,
 sale_price int,
 created_at TIMESTAMP 
);

让我们填充这些表。

insert into users (first_name, location, created_at)
values
 ('Liam', 'Toronto', '2010-01-01'),
 ('Ava', 'New York', '2011-01-01'),
 ('Emma', 'London', '2012-01-01'),
 ('Noah', 'Singapore', '2012-01-01'),
 ('William', 'Tokyo', '2014-01-01'),
 ('Oliver', 'Beijing', '2015-01-01'),
 ('Olivia', 'Moscow', '2014-01-01'),
 ('Mia', 'Toronto', '2015-01-01');insert into mailing_lists (first_name, email, created_at)
values
 ('Liam', '[liam@fake.com](mailto:liam@fake.com)', '2010-01-01'),
 ('Ava', '[ava@fake.com](mailto:ava@fake.com)', '2011-01-01');insert into products (name, manufacturing_cost, data, created_at)
values 
 ('laptop', 500, '{"in_stock":1}', '2010-01-01'),
 ('smart phone', 200, '{"in_stock":10}', '2010-01-01'),
 ('TV', 1000, '{}', '2010-01-01');insert into sales (user_id, product_id, sale_price, created_at)
values
 (1, 1, 900, '2015-01-01'),
 (1, 2, 450, '2016-01-01'),
 (1, 3, 2500, '2017-01-01'),
 (2, 1, 800, '2017-01-01'),
 (2, 2, 600, '2017-01-01'),
 (3, 3, 2500, '2018-01-01'),
 (4, 3, 2400, '2018-01-01'),
 (null, 3, 2500, '2018-01-01');

选择和计数

挑选

这是基本的查询,以后的一切都将基于它。

获取所有销售数据,无需过滤或处理。简单。

select * from sales;

选择特定列

仅检索用户的姓名和位置,不包括其他信息,如记录的创建时间。

如果您与不太关心元数据和外键的非技术人员共享,这将非常有用。

select 
  first_name, 
  location 
from users;

选择不同

从查询中删除特定列中的重复项。

如果您想要查找曾经购买过某样东西的所有用户,而不是 sales 表中的每笔交易的列表,这将非常有用。

select distinct user_id from sales;

数数

统计表格中的记录。

在编写任何其他查询之前,我一直使用这个来了解表的大小。

select count(*) from products;

计算子查询

如果您想查看包含一个joinwhere子句的记录数量,您也可以在count()中包装整个查询。

有用,因为有时记录的数量会在连接后改变一个数量级。

select count(*) from (
  select * from products
  left join sales on sales.product_id = products.id
) subquery;

限制、偏移和排序依据

限制

将返回的记录数限制为特定的计数。

当我在 jupyter 笔记本上加载大量数据记录时,我发现这很有用,因为加载太多记录会导致我的电脑崩溃。在这种情况下,我可以添加limit 100

我们会从销售部拿到前三张唱片。

select * from sales limit 3;

以...排序

按列而不是表的主键对记录进行排序。

如果您希望用户名按字母顺序排列,或者希望表按外键排序,这很有用。

通过user_id获得销售订单。注意,我们这里仍然有limit

select 
  * 
from sales 
order by user_id asc
limit 3;

我们也可以在相反的方向订购销售,递减。

select 
  * 
from sales 
order by user_id desc
limit 3;

抵消

在一个select中,从顶部跳过 N 条记录。

我发现当一次加载太多记录会使我的 jupyter 笔记本崩溃,并且我想一次迭代有限数量的记录时,这非常有用。

从销售部门获取前 3 条记录。

select * from sales 
order by user_id asc
limit 3 offset 0;

从销售部门获取接下来的 3 条记录。

select * from sales 
order by user_id asc
limit 3 offset 3;

请注意,我们在第一个查询中有记录 1–3,在第二个查询中有记录 4–6。

连接

左连接

从一个基表(在左边)开始,尝试根据一个键连接另一个表(在右边)中的记录。

在加入之前,我们先检查一下左边的表。

select * from users

现在就选对桌子。

select * from sales

现在我们做一个左连接。加入销售(右)用户(左)。

请注意,无论左边的记录是否与右边的记录匹配,总是会返回左边的记录。并且如果左边的记录与右边的多个记录匹配,则左边的记录是重复的。

select 
  * 
from users 
left join sales on sales.user_id = users.id;

Left join可能是开发人员查询数据库时使用最多的连接。

向右加入。

left join相同,但方向不同。从右边的表(销售)开始,连接左边的记录(用户),如果它们存在的话。

select 
  * 
from users 
right join sales on sales.user_id = users.id;

内部连接

仅当双方都匹配时才返回记录。请注意,我们没有空数据。

select 
  * 
from users
inner join sales on sales.user_id = users.id;

外部连接

返回左侧和右侧的所有记录,不管它们是否可以在一个键上匹配。左边的一些记录在右边没有匹配的记录,反之亦然。但无论如何都要退回去。

select 
 * 
from users
full outer join sales on sales.user_id = users.id;

交集、并集和例外集

横断

不是真正的连接,但可以像连接一样使用。它的好处是能够匹配null值,这是内部连接无法做到的。

这里我们将把usersmailing_lists中的名字交叉起来。只返回两个表中都存在的名称。

select 
  first_name
from users
intersect
select 
  first_name
from mailing_lists;

联盟

允许您返回同一列中不同列的数据。注意first_name是如何混合了用户名和位置的。

select first_name from users 
union
select location from users;

我们还可以从同一个表中堆叠两列。这里我们有用户位置和产品名称。

select location from users
union
select name from products;

联合所有

如果不想自动删除重复项,请使用union all

select name from products
union all
select name from products

除...之外

我们可以排除存在于两个表中的行,同时返回其他行。返回除了在usersmailing_lists中的名字之外的所有名字。

select 
  first_name
from users
except
select 
  first_name
from mailing_lists;

错认假频伪信号

给列一个别名会改变返回列顶部的标题。注意第一列的名称现在是name而不是first_name。这里我们重命名了两列。

select 
  first_name as name,
  location as city
from users;

我们也可以给表起别名。然后,在选择列时,我们需要引用别名。我们已经将users更名为u

select 
  u.first_name,
  u.location
from users as u;

汇总数据

分组和聚合数据是一个非常强大的功能。Postgres 提供的标准函数有:sum()avg()min()max()count()

在这里,我们将计算每个产品的销售价格的总和、平均值、最小值和最大值。

select 
  product_id, 
  sum(sale_price),
  avg(sale_price),
  min(sale_price),
  max(sale_price),
  count(id)
from sales group by product_id;

我们可以通过联接另一个表来显示名称而不是 product _ ids,从而修改上面的查询。

select 
  products.name, 
  sum(sale_price),
  avg(sale_price),
  min(sale_price),
  max(sale_price),
  count(sales.id)
from sales
left join products on products.id = sales.product_id
group by products.name;

按拥有分组

这允许对分组和聚合的数据进行过滤。常规的 where 子句在这里不起作用,但是我们可以使用having来代替。

只返回销售超过 2 件商品的产品的汇总数据。

select 
  products.name, 
  sum(sale_price),
  avg(sale_price),
  min(sale_price),
  max(sale_price),
  count(sales.id)
from sales
left join products on products.id = sales.product_id
group by products.name
having count(sales.id) > 2;

字符串 _ 聚集

也可以结合使用_agg函数(如string_agg)和group by来构建一个逗号分隔的购买每种产品的人的字符串。

select 
 products.name,
 string_agg(users.first_name, ‘, ‘)
from products
left join sales on sales.product_id = products.id
left join users on users.id = sales.user_id
group by products.name;

修改选定的值

铸造

转换意味着转换列中的数据类型。并非所有数据都可以转换为所有数据类型。例如,试图将一个字符串转换为一个整数会抛出一个错误。

但是将整数转换成小数是可行的。我们在下面这样做,这样我们可以看到制造成本除以 3(任意决定)后的小数。注意当我们除一个整数时,我们没有得到小数的位置,但是当我们除一个小数时,我们得到了。

select 
  name, 
  manufacturing_cost / 3 cost_int,
  manufacturing_cost::decimal / 3 as cost2_dec,
  '2020–01–01'::text,
  '2020–01–01'::date
from products;

轮次

我们也可以四舍五入到指定的小数位数。有时我们不想要小数点后 10 位。

这是上述查询的修改版本,增加了round()

select 
  name, 
  round(
    manufacturing_cost::decimal / 3, 2 
  )
from products;

情况

Case 允许根据单元格的值有条件地应用逻辑或返回不同的值。它相当于 SQL 的if/else。这里我们为 user_id 为null的单元格返回值100

select 
  id,
  case
    when user_id is null then 100
    else user_id
  end
from sales;

联合

如果第一列的值为 null,则 Coalesce 允许从不同的列返回值。

有用的是数据非常稀疏或者分布在多个列中。

select 
  id,
  coalesce(user_id, product_id)
from sales;

串联

Concat 只是连接字符串。这里我们将名称和位置连接起来。

select 
 concat(first_name, ‘ ‘, location)
from users;

上和下

更改字符串的大小写。

如果这需要在数据处理管道中的某个地方完成,那么在 SQL 级别完成要比在 python/app 级别快得多。

select 
  upper(first_name),
  lower(first_name)
from users;

Where 子句

大部分。

经营者

我们可以在 where 子句中使用所有的等式运算符:=<>!=<,<=>=>

找到所有名字完全是"利亚姆"的记录。

select * from users where first_name = 'Liam'

找到所有名字不是"利亚姆"的记录。

select * from users where first_name != 'Liam'

查找所有id大于或等于 5 的记录。

select * from users where id >= 5

和,或者,不是

将多个 where 子句与andornot链接在一起。但是注意我们只写了一次where这个词。

选择名称完全为“Liam”或“Olivia”的所有记录。

select * from users where first_name = 'Liam' or first_name = 'Olivia';

选择姓名完全为“Liam”且 id 为 5 的所有记录。这将返回 none,因为 Liam 的 id 不是 5。

select * from users where first_name = 'Liam' and id = 5;

选择名称为“Liam”且 id 不是 5 的所有记录。现在利亚姆回来了。

select * from users where first_name = 'Liam' and not id = 5;

在…里

您可以在给定的数组中查找值所在的记录,而不是用 or、or、or…来链接子句。

select * from users where first_name in ('Liam', 'Olivia');

我们还可以加载值为(或不为)null 的记录。

select * from sales where user_id is null;
select * from sales where user_id is not null;

模糊匹配

有时我们希望找到与查询大致匹配的值。为此,我们可以搜索部分字符串或忽略大小写。

加载名称中包含字符“ia”的任何记录。

select * from users where first_name like '%ia%';

加载名称中带有字符“IA”的记录。这不会返回任何内容,因为没有名称中包含大写的“IA”。

select * from users where first_name like '%IA%';

所以让我们忽略案例进行搜索。

select * from users where first_name ilike ‘%IA%’;

子查询中的位置

我们已经知道我们能做到。

select * from users where id > 5;

但是我们也可以从这个查询中选择!注意您需要为子查询提供别名,否则将会引发错误。

select 
  first_name 
from (
  select * from users where id > 5
) subquery;

随着

虽然我们可以从另一个查询进行查询,但我更喜欢这种方法。预先定义子查询感觉更干净。

with cte as (
  select * from users where id > 5
)
select 
  first_name
from cte

日期过滤

我们可以按日期过滤。

如果您想要查找特定日期之后发生的所有交易,这很有用。

select * from sales where created_at > '2016–01–01';

我们还可以找到两个日期之间的交易。

select 
  * 
from sales 
where created_at between '2016–01–01' and '2017–01–01';

JSON(B)s

Postgres 有一些非常棒的功能来处理 JSON。

data列中查找具有关键字in_stock的记录。

select * from products where data -> 'in_stock' is not null;

查找in_stock值大于 5 的记录。注意,我们需要将 JSONB 转换为整数来进行比较。

 select * from products where (data -> 'in_stock')::int > 5;

从 JSONB 中选择出数据,作为 JSONB。

select name, data -> 'in_stock' as stock from products;

选择它作为文本。数据类型会对更复杂的查询产生影响,在这种查询中,该值会运行其他函数。

select name, data ->> 'in_stock' as stock from products;

落后

在表中获取一条记录,并将其紧接在前面。

在查看一段时间内的事件时非常有用,因为以前的事件会影响将来的事件。您可以使用像这样查询的数据来训练 ML 模型,以便在给定当前状态的情况下预测未来状态。

我们将使用它来查找紧接在其他用户之前添加的用户。

select 
 first_name as joined_user,
 lag(first_name) over (order by created_at) as prev_joined_user
from users;

我们将null作为 Liam 的前一个用户,因为他是第一个被添加到数据库中的用户。

与上面相反。加载紧随其他用户之后加入的用户。

select 
 first_name as joined_user,
 lead(first_name) over (order by created_at) as next_joined_user
from users;

结论

Postgres 可以做很多事情。因此,尽管这里有很多命令,但我们只涉及基本的内容。

虽然没有必要记住这些才能有效,但是理解它们是如何工作的几乎是构建高级查询和知道什么是可能的必不可少的。

有没有哪个 Postgres 命令改变了你的游戏规则?

解决 100 个 leetcode SQL 问题后的感悟…

原文:https://towardsdatascience.com/sql-questions-summary-df90bfe4c9c?source=collection_archive---------3-----------------------

艰难的教训,真正的秘诀

只有三种问题。他们是…

塞巴斯蒂安·科曼摄影在 Unsplash 上拍摄的照片

简介:

有很多文章教你如何编写查询。知道如何编写一个查询并不是真正困难的部分。难的是总是有多种方法可供选择,并且所有这些方法都需要查询的组合。将问题分类,有助于我们识别模式,并对我们可以使用的查询类型建立更好的直觉。

基本上有 3 种类型的 SQL 问题。这三类问题的原始形式非常简单。然而,它们可以通过混合搭配 3 样东西来升级。分别是时间约束计算要求比较/排序要求。我将用例子向你说明我的意思。先说清楚最基本的形态。

所有 SQL 问题都可以归结为以下三种类型:

类型 1:全选

类型 2:选择做了 X 的组

类型 3:选择没有做 X 的组

正如您将看到的,类型 1 问题基本上是测试我们使用不同类型连接的能力。第二类和第三类问题都在测试我们准确识别满足特定约束的特殊群体的能力。

区分类型 2 和类型 3 是很重要的。类型 2 只需要一个带有 where 语句的步骤。类型 3 需要两个步骤的方法。首先,我们需要用 where 语句来标识特殊组。其次,我们需要使用中的 not 来排除 where 语句中的那个组。

原因是,如果我们直接选择所有不是绿色的,那么红色和绿色的样本仍然会被选择。我将在下面提供例子,这将变得更加清楚。正如您将看到的,第三类问题可能会比第二类问题复杂得多。

类型 1:全选

  • 简易版:1 种联接
  • 硬版本:多种不同类型的连接

类型 2:选择做了 X/met X 描述的组

  • 简单版本:“在哪里”语句
  • 更难的版本:查找重复,查找唯一,查找连续
Several friends at a cinema ticket office would like to reserve consecutive available seats.Can you help to query all the consecutive available seats order by the seat_id?| seat_id | free |  
|---------|------|
| 1       | 1    |
| 2       | 0    |
| 3       | 1    |
| 4       | 1    | 
| 5       | 1    |

类型 3:选择没有做 X/met X 描述的组

Write an SQL query that reports the **buyers** who have bought *S8* but not *iPhone*. Product table:
+------------+--------------+------------+
| product_id | product_name | unit_price |
+------------+--------------+------------+
| 1          | S8           | 1000       |
| 2          | G4           | 800        |
| 3          | iPhone       | 1400       |
+------------+--------------+------------+

Sales table:
+------------+----------+------------+----------+-------+
| product_id | buyer_id | sale_date  | quantity | price |
+------------+----------+------------+----------+-------+
| 1          | 1        | 2019-01-21 | 2        | 2000  |
| 1          | 2        | 2019-02-17 | 1        | 800   |
| 2          | 3        | 2019-06-02 | 1        | 800   |
| 3          | 2        | 2019-05-13 | 2        | 2800  |
+------------+----------+------------+----------+-------+

请注意,买家 2 同时购买了 S8iPhone ,它们是两个独立的样品因此,这个问题必须分两步解决。首先确定购买了 iPhone 的买家,然后选择不在购买了 iPhone 的买家中的 buyer_id。

级别 1:时间限制

处理时间格式要求我们熟悉处理时间的查询。然而,在这个阶段,逻辑是大同小异的。当它和其他两个东西结合在一起的时候就变得很难了。它经常与一些计算要求结合在一起。

  • 简单版:选择一个特殊的时间框架

因为我们选择了一个特殊的时间框架,我们正在创建一个特殊的群体。所以这些问题要么是 2 型,要么是 3 型。

Write an SQL query that reports the **products** that were **only** sold in spring 2019\. That is, between **2019-01-01** and **2019-03-31** inclusive.
  • 硬版:选择某件事的第一次或最晚时间。

当谈到选择第一个或最新的东西时,我把它们归类为第一类问题。原因是我们需要不重复地考虑所有的信息,然后从中选择最好的。

难的是日期信息通常不是主键。因此,需要花费一些努力来确保我们想要的日期与相应的项目相匹配。请参见下面的示例。

Write a SQL query that reports the **device** that is first logged in for each player.

+-----------+-----------+------------+--------------+
| player_id | device_id | event_date | games_played |
+-----------+-----------+------------+--------------+
| 1         | 2         | 2016-05-28 | 5            |
| 1         | 5         | 2016-05-02 | 6            |
| 2         | 3         | 2017-06-25 | 1            |
| 3         | 1         | 2016-03-02 | 0            |
| 3         | 4         | 2018-07-03 | 5            |
+-----------+-----------+------------+--------------+

当我们选择 min(event_date) 时,该日期是最早的日期,但它不会匹配相应的设备 id,因为 nor event_date 或 device_id 都不是主键。

第二级:计算要求

最常见的计算是计数/总和/平均值/ctr。当第二类或第三类问题涉及计算时,我们经常需要在标记一些样本时使用 case。对于 1 型题中的计算,多是代入公式。

期望的输出可以分为两类。第一个类别的输出是一个数字。第二个类别以表格的形式输出。计算是按用户/日期/会话进行的。

  1. 以数字表示的结果
  • 简易版:
Type 2: Write an SQL query to find the average for daily percentage of posts that got removed after being reported as spam, **rounded to 2 decimal places**.Actions table:
+---------+---------+-------------+--------+--------+
| user_id | post_id | action_date | action | extra  |
+---------+---------+-------------+--------+--------+
| 1       | 1       | 2019-07-01  | view   | null   |
| 1       | 1       | 2019-07-01  | like   | null   |
| 1       | 1       | 2019-07-01  | share  | null   |
| 2       | 2       | 2019-07-04  | view   | null   |
| 2       | 2       | 2019-07-04  | report | spam   |
| 3       | 4       | 2019-07-04  | view   | null   |
| 3       | 4       | 2019-07-04  | report | spam   |
| 4       | 3       | 2019-07-02  | view   | null   |
| 4       | 3       | 2019-07-02  | report | spam   |
| 5       | 2       | 2019-07-03  | view   | null   |
| 5       | 2       | 2019-07-03  | report | racism |
| 5       | 5       | 2019-07-03  | view   | null   |
| 5       | 5       | 2019-07-03  | report | racism |
+---------+---------+-------------+--------+--------+

Removals table:
+---------+-------------+
| post_id | remove_date |
+---------+-------------+
| 2       | 2019-07-20  |
| 3       | 2019-07-18  |
+---------+-------------+

Result table:
+-----------------------+
| average_daily_percent |
+-----------------------+
| 75.00                 |
+-----------------------+

因为这是一个类型 2 的问题,我们首先需要确定一个特殊的组——被删除的帖子。然后,我们将一个组标记为“1 ”,另一个组标记为“0 ”,以执行计算。

  • 更难的版本:结合时间限制。
Type 1: Write an SQL query to find the average number of sessions per user for a period of 30 days ending **2019-07-27** inclusively, **rounded to 2 decimal places**. The sessions we want to count for a user are those with at least one activity in that time period.Activity table:
+---------+------------+---------------+---------------+
| user_id | session_id | activity_date | activity_type |
+---------+------------+---------------+---------------+
| 1       | 1          | 2019-07-20    | open_session  |
| 1       | 1          | 2019-07-20    | scroll_down   |
| 1       | 1          | 2019-07-20    | end_session   |
| 2       | 4          | 2019-07-20    | open_session  |
| 2       | 4          | 2019-07-21    | send_message  |
| 2       | 4          | 2019-07-21    | end_session   |
| 3       | 2          | 2019-07-21    | open_session  |
| 3       | 2          | 2019-07-21    | send_message  |
| 3       | 2          | 2019-07-21    | end_session   |
| 3       | 5          | 2019-07-21    | open_session  |
| 3       | 5          | 2019-07-21    | scroll_down   |
| 3       | 5          | 2019-07-21    | end_session   |
| 4       | 3          | 2019-06-25    | open_session  |
| 4       | 3          | 2019-06-25    | end_session   |
+---------+------------+---------------+---------------+

Result table:
+---------------------------+ 
| average_sessions_per_user |
+---------------------------+ 
| 1.33                      |
+---------------------------+

这是一个类型 2 时间约束问题和计算要求。我们需要选择正确的时间段,然后执行计算

2.以表格形式输出

  • 更简单的版本:
Type 2: Write an SQL query to find the ctr of each Ad.**Round** ctr to 2 decimal points. **Order** the result table by ctr in descending order and by ad_id in ascending order in case of a tie.Ads table:
+-------+---------+---------+
| ad_id | user_id | action  |
+-------+---------+---------+
| 1     | 1       | Clicked |
| 2     | 2       | Clicked |
| 3     | 3       | Viewed  |
| 5     | 5       | Ignored |
| 1     | 7       | Ignored |
| 2     | 7       | Viewed  |
| 3     | 5       | Clicked |
| 1     | 4       | Viewed  |
| 2     | 11      | Viewed  |
| 1     | 2       | Clicked |
+-------+---------+---------+
Result table:
+-------+-------+
| ad_id | ctr   |
+-------+-------+
| 1     | 66.67 |
| 3     | 50.00 |
| 2     | 33.33 |
| 5     | 0.00  |
+-------+-------+

这是一个 2 型问题,因为要计算 CTR。CTR 要求我们在执行任何计算之前,将“点击”作为一组,将“浏览”作为另一组。逻辑上没有什么新东西。我们首先选择这些特殊的组,然后应用计算。

然而,对于这个特殊的问题,它强调了一个事实,即我们需要小心将 0 作为计算中的分母。当这种情况发生时,我们通常需要分别处理那个样本,然后再与其他样本结合。

  • 更难的版本:多个表或有时间限制
Type 1: Write an SQL query to find the number of times each student attended each exams. Order the result table by student_id and subject_name.Students table:
+------------+--------------+
| student_id | student_name |
+------------+--------------+
| 1          | Alice        |
| 2          | Bob          |
| 13         | John         |
| 6          | Alex         |
+------------+--------------+
Subjects table:
+--------------+
| subject_name |
+--------------+
| Math         |
| Physics      |
| Programming  |
+--------------+
Examinations table:
+------------+--------------+
| student_id | subject_name |
+------------+--------------+
| 1          | Math         |
| 1          | Physics      |
| 1          | Programming  |
| 2          | Programming  |
| 1          | Physics      |
| 1          | Math         |
| 13         | Math         |
| 13         | Programming  |
| 13         | Physics      |
| 2          | Math         |
| 1          | Math         |
+------------+--------------+
Result table:
+------------+--------------+--------------+----------------+
| student_id | student_name | subject_name | attended_exams |
+------------+--------------+--------------+----------------+
| 1          | Alice        | Math         | 3              |
| 1          | Alice        | Physics      | 2              |
| 1          | Alice        | Programming  | 1              |
| 2          | Bob          | Math         | 1              |
| 2          | Bob          | Physics      | 0              |
| 2          | Bob          | Programming  | 1              |
| 6          | Alex         | Math         | 0              |
| 6          | Alex         | Physics      | 0              |
| 6          | Alex         | Programming  | 0              |
| 13         | John         | Math         | 1              |
| 13         | John         | Physics      | 1              |
| 13         | John         | Programming  | 1              |
+------------+--------------+--------------+----------------+

这是一个 1 型问题。因此,本质上,它是在测试我们对不同类型的连接的理解。对于上面的问题,我用交叉连接左连接来解决。这是一个更难的问题,因为信息分散在 3 个表中。当我们将多个表连接在一起时,很难避免重复并包含所有信息。

Type 2: Write an SQL query to compute moving average of how much customer paid in a 7 days window (current day + 6 days before).Customer table:
+-------------+--------------+--------------+-------------+
| customer_id | name         | visited_on   | amount      |
+-------------+--------------+--------------+-------------+
| 1           | Jhon         | 2019-01-01   | 100         |
| 2           | Daniel       | 2019-01-02   | 110         |
| 3           | Jade         | 2019-01-03   | 120         |
| 4           | Khaled       | 2019-01-04   | 130         |
| 5           | Winston      | 2019-01-05   | 110         | 
| 6           | Elvis        | 2019-01-06   | 140         | 
| 7           | Anna         | 2019-01-07   | 150         |
| 8           | Maria        | 2019-01-08   | 80          |
| 9           | Jaze         | 2019-01-09   | 110         | 
| 1           | Jhon         | 2019-01-10   | 130         | 
| 3           | Jade         | 2019-01-10   | 150         | 
+-------------+--------------+--------------+-------------+

Result table:
+--------------+--------------+----------------+
| visited_on   | amount       | average_amount |
+--------------+--------------+----------------+
| 2019-01-07   | 860          | 122.86         |
| 2019-01-08   | 840          | 120            |
| 2019-01-09   | 840          | 120            |
| 2019-01-10   | 1000         | 142.86         |
+--------------+--------------+----------------+

这是一个有时间限制的类型 2 问题。因此,我们首先需要根据时间限制来组织表格。然后我们应用函数来计算。困难的部分是我们需要熟悉处理时间的查询。对于这个问题,我使用了 lag() over(order by)sum(s _ amount)over(order by)

第 3 级:比较/排名要求

说到比较/排名,往往是建立在计算要求之上的。对于类型 1 的问题,通常是考虑所有的样本,对它们进行排序,并选择最好的一个。对于第二类问题,通常是将样本分组并比较两组。对于 Type-3 的问题,往往是先做一个特殊的组,这就需要排位来这么做。然后我们选择所有具有或不具有该群体特征的东西。

类型 1:查找最高值、第 n 个最高值、第二个最高值

Write a SQL query to get the *n*th highest salary from the Employee table. If there is no *n*th highest salary, then the query should return null.+----+--------+
| Id | Salary |
+----+--------+
| 1  | 100    |
| 2  | 200    |
| 3  | 300    |
+----+--------++------------------------+
| getNthHighestSalary(2) |
+------------------------+
| 200                    |
+------------------------+

这是一个棘手的问题,因为它是一个函数,这在 SQL 中并不常见。然而,基本方法是相同的。这是一个类型 1 的问题,所以我们需要确保没有重复的样本。首先选择 distinct(salary) ,使用 order_by 进行排序,然后使用 offsetfetch 得到第二行。

第 2 类问题:一组的得分高于/高于/至少…

Write a SQL query for a report that provides the pairs (actor_id, director_id) where the actor have cooperated with the director at least 3 times.ActorDirector table:
+-------------+-------------+-------------+
| actor_id    | director_id | timestamp   |
+-------------+-------------+-------------+
| 1           | 1           | 0           |
| 1           | 1           | 1           |
| 1           | 1           | 2           |
| 1           | 2           | 3           |
| 1           | 2           | 4           |
| 2           | 1           | 5           |
| 2           | 1           | 6           |
+-------------+-------------+-------------+

Result table:
+-------------+-------------+
| actor_id    | director_id |
+-------------+-------------+
| 1           | 1           |
+-------------+-------------+

我们首先使用精确地选择我们想要的组,其中或者有时首先使用。然后我们进行比较或者整理。

类型 3:选择前 3/n 个最高薪金

  • 简易版:
Write a SQL query to find employees who have the highest salary in **each of the departments**. For the above tables, your SQL query should return the following rows (order of rows does not matter) +----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 70000  | 1            |
| 2  | Jim   | 90000  | 1            |
| 3  | Henry | 80000  | 2            |
| 4  | Sam   | 60000  | 2            |
| 5  | Max   | 90000  | 1            |
+----+-------+--------+--------------++----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------++------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Jim      | 90000  |
| Sales      | Henry    | 80000  |
+------------+----------+--------+Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ranking. Note that after a tie, the next ranking number should be the next consecutive integer value. In other words, there should be no "holes" between ranks.+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+Result:+-------+---------+
| score | Rank    |
+-------+---------+
| 4.00  | 1       |
| 4.00  | 1       |
| 3.85  | 2       |
| 3.65  | 3       |
| 3.65  | 3       |
| 3.50  | 4       |
+-------+---------+

这两个问题有一个共同的特点:你可以有两个或更多的样本共享一个等级。这两个问题很简单,因为你可以使用 dense_rank() 。它允许相同的排名,从而节省了我们两步走的努力。

  • 更硬的版本:
Write a SQL query to find employees who earn the top three salaries in each of the department. For the above tables, your SQL query should return the following rows (order of rows does not matter).+----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 85000  | 1            |
| 2  | Henry | 80000  | 2            |
| 3  | Sam   | 60000  | 2            |
| 4  | Max   | 90000  | 1            |
| 5  | Janet | 69000  | 1            |
| 6  | Randy | 85000  | 1            |
| 7  | Will  | 70000  | 1            |
+----+-------+--------+--------------++----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------+Result:
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Randy    | 85000  |
| IT         | Joe      | 85000  |
| IT         | Will     | 70000  |
| Sales      | Henry    | 80000  |
| Sales      | Sam      | 60000  |
+------------+----------+--------+

这是一个困难的问题,因为我们有一个嵌套的逻辑。这就是我的意思。这是一个类型 3 的问题,所以我们需要使用两步法。首先,我们将每个部门的前 3 名员工组成一个小组。分别是 IT 部门 90000,85000,75000,销售部门 80000,60000。其次,我们选择所有具有其中一种薪水的样本。

为了选择一组我们想要的薪水,我们需要使用第二类逻辑:将两组分开并比较。在这个例子中,我们复制了一个表,这样我们就有两个相同的表并排在一起。从表 1 中,我们选择一个薪水,并将其与表 2 中的所有薪水进行比较。如果有 3 个以上的薪水比我们选择的薪水大,那么那个薪水一定不是前 3 名。

级别 4:将所有需求和约束混合在一起

这里有两个问题的例子,这三个元素都在一起:时间约束计算要求比较。看看能不能分解组件解决!

Average Calculation + Type-3 Comparision + Time: Find the movie name with the ***highest average*** rating in **February 2020**.In case of a tie, return lexicographically smaller movie name.Movies table:
+-------------+--------------+
| movie_id    |  title       |
+-------------+--------------+
| 1           | Avengers     |
| 2           | Frozen 2     |
| 3           | Joker        |
+-------------+--------------+

Users table:
+-------------+--------------+
| user_id     |  name        |
+-------------+--------------+
| 1           | Daniel       |
| 2           | Monica       |
| 3           | Maria        |
| 4           | James        |
+-------------+--------------+

Movie_Rating table:
+-------------+--------------+--------------+-------------+
| movie_id    | user_id      | rating       | created_at  |
+-------------+--------------+--------------+-------------+
| 1           | 1            | 3            | 2020-01-12  |
| 1           | 2            | 4            | 2020-02-11  |
| 1           | 3            | 2            | 2020-02-12  |
| 1           | 4            | 1            | 2020-01-01  |
| 2           | 1            | 5            | 2020-02-17  | 
| 2           | 2            | 2            | 2020-02-01  | 
| 2           | 3            | 2            | 2020-03-01  |
| 3           | 1            | 3            | 2020-02-22  | 
| 3           | 2            | 4            | 2020-02-25  | 
+-------------+--------------+--------------+-------------+

Result table:
+--------------+
| results      |
+--------------+
| Daniel       |
| Frozen 2     |
+--------------+Sum Calculation + Type-2 Comparison + Time: Write an SQL query that reports the **books** that have sold **less than 10** copies in the last year, excluding books that have been available for less than 1 month from today. **Assume today is 2019-06-23**.Books table:
+---------+--------------------+----------------+
| book_id | name               | available_from |
+---------+--------------------+----------------+
| 1       | "Kalila And Demna" | 2010-01-01     |
| 2       | "28 Letters"       | 2012-05-12     |
| 3       | "The Hobbit"       | 2019-06-10     |
| 4       | "13 Reasons Why"   | 2019-06-01     |
| 5       | "The Hunger Games" | 2008-09-21     |
+---------+--------------------+----------------+

Orders table:
+----------+---------+----------+---------------+
| order_id | book_id | quantity | dispatch_date |
+----------+---------+----------+---------------+
| 1        | 1       | 2        | 2018-07-26    |
| 2        | 1       | 1        | 2018-11-05    |
| 3        | 3       | 8        | 2019-06-11    |
| 4        | 4       | 6        | 2019-06-05    |
| 5        | 4       | 5        | 2019-06-20    |
| 6        | 5       | 9        | 2009-02-02    |
| 7        | 5       | 8        | 2010-04-13    |
+----------+---------+----------+---------------+

Result table:
+-----------+--------------------+
| book_id   | name               |
+-----------+--------------------+
| 1         | "Kalila And Demna" |
| 2         | "28 Letters"       |
| 5         | "The Hunger Games" |
+-----------+--------------------+

这就是我如何把复杂的问题分解成组件。希望我的策略有帮助!

SQL(关系数据库)还是 NoSQL?the FAANG 系统设计面试

原文:https://towardsdatascience.com/sql-relational-database-or-nosql-ace-the-faang-system-design-interview-2d17439ecb3b?source=collection_archive---------14-----------------------

泰勒·维克在 Unsplash 上的照片

(我的博客里也有这个帖子)

许多系统需要永久数据存储来存储应用程序数据。随着近年来 NoSQL 数据库的兴起,确定 SQL 和 NoSQL 数据库的不同优势,并选择适合您的使用案例的合适数据库至关重要。当涉及到那些大型科技公司的技术系统设计面试时,能够比较 SQL 和 NoSQL 的权衡可以是一个合格候选人的良好指标。

无模式

当然,关键的区别在于数据的结构。如您所知,在关系数据库中,数据存储在表中。表中的每一行都有相同的一组列。这意味着数据模型建模良好。相比之下,NoSQL 数据库提供了非常灵活的数据模型。数据存储为文档或项目。每个项目可以有不同的列。例如,在 AWS DynamoDB 中,可以用分区键和排序键定义一个表。其余的属性在运行时以宽松的方式插入/更新。

一致性

关系数据库是 ACID (原子性、一致性、隔离性、持久性)兼容的。那些 SQL 数据库具有很强的一致性。而梅·NoSQL 以牺牲一致性为代价提供了非常高的性能。大多数 NoSQL 只提供最终一致性,或者有些支持强一致性,但默认提供最终一致性。

垂直可扩展性与水平可扩展性

由于在大量节点中管理 ACID 编译的复杂性,横向扩展 SQL 数据库的成本非常高。因此,大多数 SQL 数据库都是纵向扩展的。这意味着它需要升级单个节点的硬件。这种可伸缩性比水平可伸缩性高得多。水平可伸缩性依赖于许多低成本的商用计算机来执行密集的工作负载。大多数 NoSQL 可以水平扩展以获得更好的性能,而无需/只需很少的节点管理成本。通过将请求路由到不同的碎片,NoSQL 数据库可以处理更多的请求。

询问

SQL 数据库有标准的查询语言。相比之下,NoSQL 数据库有自己的查询语言。因此,将 SQL 数据库从一个供应商迁移到另一个供应商可能不需要在应用程序级别做任何更改。而改变 NoSQL 数据库从一个到另一个可能需要一些代码的变化。

临终遗言

如果您对数据库感兴趣,肯定会有更多的主题供您深入研究。上限定理是很好的学习主题。在选择数据库时,理解基础知识并能够阅读数据库供应商提供的用户指南是至关重要的技能。要了解关于系统设计的更多信息,您可以在探索系统设计设计数据密集型应用中找到更多信息。

以前的帖子:

系统设计面试:如何设计一个系统来处理长时间运行的作业

FAANG Ace 系统设计面试

我如何获得 6 个月的代码并获得 FAANG offer

这些都是帮我找到方工作的资源

我关于 FAANG 访谈的帖子

我关于金融和科技的帖子

从 CRUD web 应用开发到语音助手中的 SDE——我正在进行的机器学习之旅

全栈开发教程:将 AWS Lambda 无服务器服务集成到 Angular SPA 中

全栈开发教程:用运行在 AWS Lambda 上的无服务器 REST API 提供交易数据

全栈开发教程:在 Angular SPA 上可视化交易数据

强化学习:Q 学习简介

使用 Python 的 SQL Server

原文:https://towardsdatascience.com/sql-server-with-python-679b8dba69fa?source=collection_archive---------1-----------------------

世界上最喜欢的数据库+世界上最喜欢的语言

马丁·桑切斯在 Unsplash 上拍摄的照片

大家都用 SQL,大家都用 Python。SQL 是数据库事实上的标准。另一方面,Python 是全明星,是数据分析、机器学习和 web 开发的顶级语言。想象两者都在一起。

这实际上非常容易设置。我们可以快速利用 Python 的动态特性,在 SQL 中控制和构建查询。最精彩的部分?设置完成后,您不需要做任何事情。

这两个惊人的工具一起,让我们达到自动化和效率的新高度。

pyodbc

我们在两种技术之间的桥梁是pyodbc。这个库允许轻松访问 ODBC 数据库。

ODBC 是开放式数据库连接的缩写,是一种用于访问数据库的标准化应用程序编程接口(API ),由 SQL Access group 在 90 年代早期开发。

兼容的数据库管理系统(DBMS)包括:

  • IBM Db2
  • MS Access
  • MS SQL Server
  • 关系型数据库
  • 神谕

在本文中,我们将使用 MS SQL Server。在很大程度上,这应该可以直接转移到任何兼容 ODBC 的数据库中使用。唯一需要更改的应该是连接设置。

连接

我们需要做的第一件事是创建一个到 SQL server 的连接。我们可以使用pyodbc.connect来做到这一点。在这个函数中,我们还必须传递一个连接字符串。

这个连接字符串必须指定 DBMS DriverServer、要连接的特定Database以及我们的连接设置。

所以,让我们假设我们想要连接到服务器UKXXX00123,45600,数据库DB01,为此我们想要使用SQL Server Native Client 11.0

我们将从内部连接,因此是可信的连接(我们不需要输入用户名和密码)。

cnxn_str = ("Driver={SQL Server Native Client 11.0};"
            "Server=UKXXX00123,45600;"
            "Database=DB01;"
            "Trusted_Connection=yes;")

我们的连接现在初始化为:

cnxn = pyodbc.connect(cnxn_str)

如果我们不通过可信连接访问数据库,我们将需要输入通常用于通过 SQL Server Management Studio (SSMS)访问服务器的用户名和密码。

举个例子,如果我们的用户名是JoeBloggs,我们的密码是Password123,我们应该立即更改密码。

但是在更改那个可怕的密码之前,我们可以这样连接:

cnxn_str = ("Driver={SQL Server Native Client 11.0};"
            "Server=UKXXX00123,45600;"
            "Database=DB01;"
            "UID=JoeBloggs;"
            "PWD=Password123;")cnxn = pyodbc.connect(cnxn_str)

现在我们连接到数据库,我们可以开始通过 Python 执行 SQL 查询。

运行查询

现在,我们在 SQL Server 上运行的每个查询都包括游标初始化和查询执行。此外,如果我们在服务器内部进行任何更改,我们还需要将这些更改提交给服务器(我们将在下一节中讨论)。

要初始化游标,请执行以下操作:

cursor = cnxn.cursor()

现在,每当我们想要执行查询时,我们就使用这个cursor对象。

让我们首先从名为customers的表中选择前 1000 行:

cursor.execute("SELECT TOP(1000) * FROM customers")

这是在服务器内部执行的操作,因此实际上没有任何东西返回给 Python。因此,让我们看看如何从 SQL 中提取这些数据。

提取数据

为了将 SQL 中的数据提取到 Python 中,我们使用了pandas。Pandas 为我们提供了一个非常方便的函数read_sql,这个函数,你可能已经猜到了,从 SQL 中读取数据。

read_sql需要一个查询和连接实例cnxn,就像这样:

data = pd.read_sql("SELECT TOP(1000) * FROM customers", cnxn)

这将从customers表中返回包含前 1000 行的数据帧。

在 SQL 中更改数据

现在,如果我们想要更改 SQL 中的数据,我们需要向原始的初始化连接添加另一个步骤,执行查询过程。

当我们在 SQL 中执行查询时,这些更改保存在临时存在的空间中,它们不是直接对数据进行的。

为了使这些变化永久化,我们必须commit它们。让我们连接firstNamelastName列,创建一个fullName列。

cursor = cnxn.cursor()# first alter the table, adding a column
cursor.execute("ALTER TABLE customer " +
               "ADD fullName VARCHAR(20)")# now update that column to contain firstName + lastName
cursor.execute("UPDATE customer " +
               "SET fullName = firstName + " " + lastName")

此时,fullName在我们的数据库中不存在。我们必须commit将这些改变永久化:

cnxn.commit()

后续步骤

一旦我们完成了需要完成的操作任务。我们可以将数据提取到 Python 中——或者,我们也可以将数据提取到 Python 中,并在那里进行操作。

无论您采用哪种方法,一旦数据出现在 Python 中,我们就可以用它做许多以前不可能做的有用的事情。

也许我们需要执行一些每日报告,我们通常会用它来查询 SQL server 中最新的一批数据,计算一些基本的统计数据,并通过电子邮件发送结果。

让我们实现自动化:

就这样,我们结束了!运行这段代码可以快速提取前一周的数据,计算我们的关键度量,并向我们的老板发送一份摘要。

因此,通过几个简单易行的步骤,我们已经初步了解了如何使用 SQL 和 Python 的集成来快速建立一个更高效、自动化的工作流。

我发现这非常有用,不仅仅是对于上面描述的用例。

Python 只是打开了我们以前仅用 SQL 无法通过的新路线。

让我知道你的观点、想法或用例,我很乐意听到它们!如果你想要更多这样的内容,我也会在 YouTube 上发布。

感谢阅读!

🤖《变形金刚》NLP 课程 70%的折扣

如果您喜欢这篇文章,您可能有兴趣更深入地了解 Python 的电子邮件自动化。我在以前的一篇文章中讨论过这个问题,如果您感兴趣,请查看:

[## 用 Python 通知

使用 Python 构建的电子邮件通知让生活更轻松

towardsdatascience.com](/notify-with-python-41b77d51657e)

这个 GitHub repo 还包含文档和代码,展示了一些有用方法的实现。

更智能的查询,而不是更难

原文:https://towardsdatascience.com/sql-subqueries-f2c490bf772c?source=collection_archive---------65-----------------------

用子查询实现子查询

无论在哪个行业,关系数据库都是最常见的数据存储类型之一。使用 SQL(以任何形式)创建查询是访问数据的一种非常有效和容易的方法,但有时会有点混乱。查询可能会变得非常复杂,需要多种方法来正确获取正确的数据。当查询开始变长并变得复杂时,子查询是一种非常有用的方法,可以用更容易理解的方式对查询进行分段。

我们上面的表模式显示了一个有点复杂,但绝对不罕见的数据库。

标准连接

我们将从一个常规查询开始,该查询将返回在该公司美国办公室工作的所有雇员。

# Establish an sqlite3 connection to our database
conn = sqlite3.Connection('data.sqlite')# Establish a connection cursor for building our query
cur = conn.cursor()# Use the cursor's execute method to execute the query
cur.execute("""SELECT lastName, firstName, officeCode
               FROM employees e
               JOIN offices o
               USING(officeCode)
               WHERE country = "USA";
             """)# Wrap our cursor in a pandas DataFrame
df = pd.DataFrame(cur.fetchall())# Utilize the cursor's description attribute to add corresponding
# column titles to our DataFrame
df.columns = [x[0] for x in cur.description]

我们的数据帧返回到上面的样子。我们使用简单的多对多连接,使用 officeCode 作为我们的键。这是一个非常简单的查询,但是有时在大型表模式中使用连接和主键/外键会造成混乱。现在让我们来看看如何使用子查询以一种不同且更简单的方式编写这个查询。

使用子查询

子查询本质上只是查询中的一个查询。我们将执行一个查询语句,该语句的条件依赖于一个完全独立的查询。听起来有点混乱,所以让我们看看我们在谈论什么。

cur.execute("""SELECT lastName, firstName, officeCode
               FROM employees
               WHERE officeCode IN (SELECT officeCode
                                    FROM offices 
                                    WHERE country = "USA");
                                    """)df = pd.DataFrame(cur.fetchall())
df.columns = [x[0] for x in cur.description]
df

哒哒!该查询将输出与前面代码中相同的数据帧。我们只是从子查询中创建的初始表中选择要素,而不是连接到新表。让我们尝试一个稍微复杂一点的问题。

编写一个查询,返回雇员超过 5 人的办公室的所有雇员。

这是一个特别困难的查询,因为它要求我们返回一个基于聚合条件的查询,但是我们实际上并不返回聚合本身。我们需要创建一个条件,只选择符合该条件的员工。通常,我们使用“HAVING”或“GROUP BY”语句来检索聚合数据,但在这种情况下,我们希望对聚合进行过滤。这种情况正是子查询真正发挥作用的地方。

cur.execute("""SELECT lastName, firstName, officeCode
               FROM employees
               WHERE officeCode IN (SELECT officeCode 
                                    FROM offices 
                                    JOIN employees
                                    USING(officeCode)
                                    GROUP BY 1
                                    HAVING COUNT(employeeNumber)>5);
                                    """)
df = pd.DataFrame(cur.fetchall())
df.columns = [x[0] for x in cur.description]
df.head(10)

在本例中,我们首先嵌套了一个包含条件语句的初始查询:“HAVING COUNT(employeeNumber) > 5”。光标暂时使这个新执行的内部查询成为新表,然后我们可以为我们的答案选择必要的特性。当我开始使用 SQL 时,帮助我理解这个过程的一个小技巧是首先从内部查询开始,包含我们的条件,然后从那里开始。

汇总数据的子查询

在上一个查询中,我们选择了基于 聚合条件,但是如果我们想要返回一个聚合函数结果呢?****

编写一个查询,返回所有客户的平均付款额。

cur.execute("""SELECT AVG(customerAvgPayment) AS averagePayment
               FROM (SELECT AVG(amount) AS customerAvgPayment
                     FROM payments
                     JOIN customers USING(customerNumber)
                     GROUP BY customerNumber);""")
df = pd.DataFrame(cur.fetchall())
df.columns = [x[0] for x in cur.description]
df

在这种情况下,我们实际上使用子查询来定义数学聚合函数。聚合函数“AVG(amount)AS customerAvgPayment”有别名,最初在嵌套语句中执行。剩下要做的就是选择聚合函数作为它自己的变量,瞧!

收尾

对于编码和 SQL 来说,从来没有一种单一的方式来获得输出。编写查询和子查询的方法有很多,对于我来说,这是掌握 SQL 事件流的一个很好的方法。

数据科学家的 SQL 技巧

原文:https://towardsdatascience.com/sql-tricks-for-data-scientists-53298467dd5?source=collection_archive---------14-----------------------

利用 SQL 赢得胜利

递归 CTE

我发现自己被公司内部各个团队要求的 cron jobs 和 CSV 文件淹没了。不断有人要求导出新数据或更新这些导出数据。任何时候任何人想要添加一个字段,我都是这个任务的失败点。我必须首先记住哪个服务生成了该报告,以及该报告的要点。然后,我必须调查所需的新字段是否可用,是否可以从其他列派生,或者是否需要新的数据库连接。

我需要找到一个应用程序,它可以帮助我跟踪所有的报告,管理所有不同的数据库连接,并允许某人自己维护通知。最后一个重要的功能是卸载我的一些报告生成,并允许人们自助服务所有数据。

我选择了元数据库,因为它符合我一直在寻找的所有标准。它是开源的,支持各种不同的数据源,具有用户/权限管理、许多图表/仪表板选项和各种不同类型的通知。

只有一个问题—元数据库完全基于 SQL。我使用简单的选择来查询数据库并将数据转换成 CSV 的工作流将不可用。我不得不使用原始 SQL。我怎样才能在 SQL 中插入逻辑呢?我如何循环结果?如何生成日期范围?我如何使用滚动窗口?这些类型的问题听起来好像只有 SQL 的工作流不能解决问题。

但是,如果这些手术实际上是可能的呢?如果 SQL 实际上是一种图灵-带有递归的完整语言会怎样?如果有一种透视数据或使用窗口的方法会怎么样?下面我将介绍一些我在旅途中发现的技巧,以充分利用 SQL 的强大功能。

设置

如果你想自己执行这些例子,你可以遵循下面的指令。如果你只是想阅读,跳到下一节。

我使用 MySQL 8 和 sample Sakila 数据库作为示例。如果你安装了 Docker,我已经提供了几行代码来运行 MySQL 8 服务器。

下面启动 docker 容器。

docker run -d --publish=3307:3306 --name=mysql_test -e MYSQL_ROOT_PASSWORD=root mysql/mysql-server:latest

数据库示例数据库可以通过下载示例文件并提取它们来加载。

#enter the shell
docker exec -it mysql_test bin/bash#install wget
yum install wget#get file
wget [https://downloads.mysql.com/docs/sakila-db.tar.gz](https://downloads.mysql.com/docs/sakila-db.tar.gz)#install tar
yum install tar#extract files
tar xvzf sakila-db.tar.gz#start the mysql console, the password was set in the docker run cmd
mysql -u root -p#import the schema
SOURCE sakila-db/sakila-schema.sql#import the data
SOURCE sakila-db/sakila-data.sql#use the newly created database
use sakila;#if all went well, the following cmd should show all the tables
show tables;

设置完成后,您可以自己尝试这些示例。

查找重复事件

我将从一个更简单但仍然强大的例子开始。如果一个表与另一个表具有一对多或多对多的关系,则该表与该表的联接会导致交叉(或笛卡尔)联接。我们可以使用子查询将结果集缩小到每个表中的一行。

例如,假设我们想按字母顺序查找每个演员和他们出现的最后一部电影。

SELECT a.first_name, a.last_name, f.title 
FROM actor a 
JOIN film_actor fa ON fa.actor_id = a.actor_id 
JOIN film f ON f.film_id = fa.film_id;

上一个查询的输出示例

上面的查询将列出所有演员和所有电影。但是如果您只想列出一部电影——最后一部——我们需要添加一个子查询,以便只使用我们想要的电影。

SELECT a.first_name, fa.film_id, f.title 
FROM actor a 
LEFT JOIN (
  SELECT actor_id, MAX(film_id) as film_id 
  FROM film_actor group by actor_id
) fa ON fa.actor_id = a.actor_id 
LEFT JOIN film f ON f.film_id = fa.film_id ;

仅列出一部电影时的示例响应

对于具有重复事件(如登录和创建对象)的表格,您可以查找第一个或最后一个事件,或者第一个和最后一个事件之间的差异。下面是查找客户寿命的 SQL 语句,其中寿命定义为他们第一次和最后一次租赁日期之间的差异(天数)。

SELECT c.customer_id,c.first_name, c.last_name, r.first_rental, r.last_rental, DATEDIFF(r.last_rental, r.first_rental) as customer_longevity 
FROM customer c 
LEFT JOIN (
  SELECT customer_id, MIN(rental_date) as first_rental, MAX(rental_date) AS last_rental 
  FROM rental GROUP BY customer_id
) r ON r.customer_id = c.customer_id;

租金差异的样本响应

绕轴旋转

您希望将行转换为列以用于演示和/或图表制作的数据可以根据需要进行透视和汇总。以下面的查询和示例输出为例。

SELECT MONTHNAME(r.rental_date), c.name, count(r.rental_id) 
FROM rental r 
LEFT JOIN film f ON f.film_id = r.inventory_id 
LEFT JOIN film_category fc ON fc.film_id = f.film_id 
LEFT JOIN category c ON c.category_id = fc.category_id 
GROUP BY MONTHNAME(r.rental_date),c.name;

按月份和类型分组的示例响应

当我们可以将每月细分移动到每月一行,而不是每个类别每月一行时的用例。

SELECT MONTHNAME(r.rental_date), 
COUNT(CASE WHEN c.name = ‘Horror’ THEN r.rental_id ELSE NULL END) AS HorrorCount, 
COUNT(CASE WHEN c.name = ‘Action’ THEN r.rental_id ELSE NULL END) AS ActionCount, 
COUNT(CASE WHEN c.name = ‘Comedy’ THEN r.rental_id ELSE NULL END) AS ComedyCount, 
COUNT(CASE WHEN c.name = ‘Sci-Fi’ THEN r.rental_id ELSE NULL END) AS ScifiCount 
FROM rental r 
LEFT JOIN film f ON f.film_id = r.inventory_id 
LEFT JOIN film_category fc ON fc.film_id = f.film_id 
LEFT JOIN category c ON c.category_id = fc.category_id 
GROUP BY MONTHNAME(r.rental_date);

将行旋转到列

我没有使用所有类别来避免一页 SQL 语句,但是您可以添加其他列来完成查询。MySQL 没有一种内置的方法来动态创建每一列,但是有能力使用一个准备好的语句来避免必须拼出每一列。

SET [@sql](http://twitter.com/sql) = NULL;
SELECT
 GROUP_CONCAT(DISTINCT
 CONCAT(
 ‘COUNT(CASE WHEN c.name = ‘’’,
 name,
 ‘’’ THEN r.rental_id ELSE NULL END) AS `’,
 name,
 ‘`’
 )
 ) INTO [@sql](http://twitter.com/sql)
FROM category;
SET [@sql](http://twitter.com/sql) = CONCAT(‘SELECT MONTHNAME(r.rental_date), ‘, [@sql](http://twitter.com/sql) ,’ FROM rental r LEFT JOIN film f ON f.film_id = r.inventory_id LEFT JOIN film_category fc ON fc.film_id = f.film_id LEFT JOIN category c ON c.category_id = fc.category_id GROUP BY MONTHNAME(r.rental_date)’);PREPARE stmt FROM [@sql](http://twitter.com/sql);
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

所有的流派列

注意:group _ concat _ max _ len 变量需要足够长,足以容纳所有可能的类别。默认值为 1024,但是如果您在尝试运行上述 SQL 语句时遇到任何错误,则可以根据会话进行更改。

SET SESSION group_concat_max_len = 1000000;

滚动窗户

继续聚合数据的主题,我们还可以使用窗口函数来创建滚动聚合。前面我们使用了一个查询来按类型和月份列出所有的租赁。我们如何添加另一列来显示每月的运行总数,以及该月每种类型的租赁百分比?

SELECT MONTHNAME(r.rental_date), c.name, count(r.rental_id),     SUM(count(r.rental_id)) over(PARTITION BY MONTHNAME(r.rental_date)) as rental_month_total, count(rental_id) / SUM(count(r.rental_id)) over(PARTITION BY MONTHNAME(r.rental_date)) * 100 as percentage_of_rentals 
FROM rental r 
LEFT JOIN film f ON f.film_id = r.inventory_id 
LEFT JOIN film_category fc ON fc.film_id = f.film_id 
LEFT JOIN category c ON c.category_id = fc.category_id 
GROUP BY MONTHNAME(r.rental_date),c.name;

租赁类型百分比的示例结果

Over关键字允许您定义如何对数据进行分区。在这种情况下,我们使用租赁日期月份。

生成数据

我经常会遇到我想要长期跟踪的数据。问题是有些情况下数据很稀疏,可能没有我想要显示的每个时间单位的值。例如,以下面的查询为例:

SELECT DATE_FORMAT(r.rental_date,"%Y-%M") as rental_date, count(r.rental_id) as rental_count FROM rental r  
LEFT JOIN film f ON f.film_id = r.inventory_id  
LEFT JOIN film_category fc ON fc.film_id = f.film_id  
LEFT JOIN category c ON c.category_id = fc.category_id  
GROUP BY DATE_FORMAT(r.rental_date,"%Y-%M");

现有数据的年份和月份

前面的查询按月显示了计数,但是如果我想看到其他列的计数为 0,该怎么办呢?

WITH RECURSIVE t(v) as (   
  SELECT  DATE('2005-03-01')   
  UNION ALL   
  SELECT v + INTERVAL 1 MONTH   
  FROM t    
  LIMIT 12 
) 
SELECT DATE_FORMAT(t.v,"%Y-%M") as rental_date, count(r.rental_id) as rental_count FROM rental r  
LEFT JOIN film f ON f.film_id = r.inventory_id   
LEFT JOIN film_category fc ON fc.film_id = f.film_id   
LEFT JOIN category c ON c.category_id = fc.category_id   
RIGHT JOIN t on DATE_FORMAT(t.v,"%Y-%M")  = DATE_FORMAT(r.rental_date,"%Y-%M") 
GROUP BY DATE_FORMAT(t.v,"%Y-%M");

12 个月零灌装

递归 cte 看起来比实际情况更吓人。在这个问题上,一个比我能提供的更好的解释可以在这里找到。

结论

SQL 提供了一种强大的语言来提取数据。我介绍了一些技巧,关于如何使用 SQL 来处理更复杂的过滤和聚合,而不需要在数据库之外执行这些操作。

我向那些希望为组织添加商业智能报告引擎的人推荐 元数据库 。唯一需要满足的标准是让我的同事自助生成报告。但这可能是我对所有报告请求说“是”的一个功能。

SQL Server 的 SQL 单元测试存储过程

原文:https://towardsdatascience.com/sql-unit-testing-stored-procedure-for-sql-server-9565d86ac458?source=collection_archive---------46-----------------------

在本文中,我们将对招聘服务的 SQL Server 数据库的存储过程进行 SQL 单元测试。

图片来自 Piqsels (CC0)

单元测试是数据库开发过程的重要组成部分。它的主要目标是测试数据库对象的组成部分,以便在项目早期识别任何故障或缺陷。这种方法允许数据库开发人员确保他们所做的更改得到验证,并且项目将正常工作。在本文中,我们将主要关注对 SQL Server 数据库中的存储过程进行单元测试,并举例说明使用 dbForge 单元测试工具进行单元测试是多么简单。

之前,我们讨论了为招聘服务创建 SQL Server 数据库的过程。

Img.1 .招聘服务的数据库模式

如上所示,数据库包含以下实体:

  • 雇员
  • 公司
  • 位置
  • 项目
  • 技能

尽管如此,在这一系列文章中,我们不知何故忽略了单元测试的一个重要方面。所以现在,我建议我们更仔细地研究一下这种方法,并通过实现 SearchEmployee 存储过程来举例说明这种方法,该存储过程用于基于特定技能的员工搜索。为了确保数据的完整性,我们应该在技能表上添加一个唯一的约束,如下所示:

ALTER TABLE [dbo].[Skill] ADD CONSTRAINT UniqueSkillName UNIQUE(SkillName);

但是,在此之前,请使用以下查询确保 SkillName 字段中的数据不包含任何重复条目:

SELECT 
      [SkillName] 
FROM [JobEmpl].[dbo].[Skill] 
GROUP BY [SkillName] 
HAVING COUNT(*) > 1;

假设您有重复的条目,那么您需要将所有记录规范化为 SkillName 字段中彼此相关的唯一值。

好了,完成了一点:我们在技能名称中创建了一个惟一性约束。

现在,该实现 SearchEmployee 存储过程了,如下所示:

为什么不更详细地研究一下 SearchEmployee 存储过程的工作呢?

首先,它有两个输入参数:

  1. @SkillList 是技能列表,用分号分隔。
  2. @CountNotSkill 表示可以缺席的技能数(默认为 1)。

现在让我们转到 SearchEmployee 存储过程的主体:

  1. 首先,我们定义了一个变量@count_skills,它用于计算数据库中与输入参数@SkillList 中报告的数量相对应的技能数量。
  2. 接下来,用内置函数 STRING_SPLIT 将@SkillList 字符串转换成临时表#tbl_skill_tmp。
  3. 然后,从技能表中找到所有合适的技能,并放入一个名为#tbl_skill_tmp 的新临时表中。
  4. 之后@算技能按照 par.1 算。
  5. 随后,基于设定的技能收集关于项目(项目表)和作业历史(作业历史表)的必要信息;结果会进入一个名为#tbl_skill_tmp 的临时表。
  6. 接下来,在 par 中获得的信息。5 根据 skill 和 employee 的标识符进行分组,结果放入临时表#tbl_res。
  7. 此外,在段落中获得的信息。6 与 Employee 表相结合,以获得雇员的详细信息(名和姓),并且结果被存储到临时表#tbl_res2 中。该查询还统计每项技能在年、月和天中应用了多长时间,以使后续分析更加方便。
  8. 之后,从 par.7 的结果中检索雇员的信息,并将最终结果放入临时表#tbl_empl 中。
  9. 然后,对表#tbl_skill 和#tbl_empl 进行笛卡尔积运算,结果放入临时表#tbl_skill_empl。
  10. 接下来,创建一个名为#tbl_res3 的临时表,它包括两个临时表#tbl_skill_empl 和#tbl_res2 的乘积,其中每对 employee 和 skill 都具有在第 7 段中获得的匹配信息
  11. 然后,将符合输入参数的员工标识符收集到临时表#tbl_empl_res 中。此时,如果技能已经使用了至少 6 个月,则该技能被视为有效。
  12. 接下来,跟踪员工的结果输出和他们的技能,使用时间以年、月和天来衡量,以及他们的应用程序的开始和结束日期。
  13. 然后,您将看到关于我们感兴趣的技能的员工历史的详细总结。
  14. 最后,我们删除在这个存储过程中创建的所有临时表。

经过上述步骤,我们可以提取出在 C#和 T-SQL 语言以及 ASP.NET 技术方面有能力的员工的名字,条件是最多有一个人缺乏技能,如下所示:

EXEC [dbo].[SearchEmployee] @SkillList = N'C#;T-SQL;ASP.NET'
                                                ,@CountNotSkill = 1;

创建并运行存储过程的 SQL Server 单元测试

为了简化单元测试,这个存储过程应该分成两部分:第一部分输出主要信息,第二部分输出找到的雇员的详细历史记录。然而,为了便于理解,我们将展示如何对存储过程应用单元测试。

至于我们要应用的工具:它是单元测试工具,内置于 SSMS,也是db forge Studio for SQL Server的一部分。

让我们来看看这个工具在 SSMS 中的功能。

右键单击 JobEmpl 数据库,并在下拉列表中选择单元测试“安装测试框架…”:

Img.2 .开始安装测试框架

类似地,右键单击 JobEmpl 数据库中的任何节点都可以调用该菜单。

接下来,确保服务器和数据库正确无误,然后单击“安装”:

Img.3 .安装测试框架

安装过程如下:

Img.4 .测试框架安装流程

安装过程结束时,您将收到一条完成消息。单击“完成”按钮:

Img.5 .测试框架安装的成功完成

请注意 JobEmpl 数据库。在下面,您可以看到 tSQLt 对象(表、视图、函数和存储过程)已经创建:

目标 5–1 创建的 tSQLt 对象(第 1 部分)

img . 5–2 创建 tSQLt 对象(第 2 部分)

接下来,要创建一个测试,右键单击 JobEmpl 数据库并选择单元测试“添加新测试…”命令:

Img.6 .新单元测试的创建

我们现在需要为名为 UT_SearchEmployee_Exception 的新测试定制设置,并单击“Add Test”:

Img.7 .自定义已创建测试的设置

测试创建结束时,会出现一个信息窗口。单击“完成”按钮:

Img.8 .测试创建的成功完成

然后,您将在 SSMS 看到一个新选项卡,代码如下:

-- Comments here are associated with the test. 
-- For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/ ALTER PROCEDURE CL1.[test UT_SearchEmployee_Exception] 
AS 
BEGIN 
--Assemble 
-- This section is for code that sets up the environment. It often 
-- contains calls to methods such as tSQLt.FakeTable and tSQLt.SpyProcedure 
-- along with INSERTs of relevant data. 
-- For more information, see http://tsqlt.org/user-guide/isolating-dependencies/ --Act 
-- Execute the code under test like a stored procedure, function or view 
-- and capture the results in variables or tables. --Assert 
-- Compare the expected and actual values, or call tSQLt.Fail in an IF statement. 
-- Available Asserts: tSQLt.AssertEquals, tSQLt.AssertEqualsString, tSQLt.AssertEqualsTable 
-- For a complete list, see: http://tsqlt.org/user-guide/assertions/ EXEC tSQLt.Fail 'TODO:Implement this test.' END;

从链接来看,该工具有几个内置函数(tSQLt 框架),这些函数有很多功能:在它们的帮助下,您可以针对不同的条件测试不同的数据库对象。让我们考虑一些例子:
1)你可以检查两个表中的数据是否匹配。
2)您可以检查某个对象是否在数据库中(如果在,则测试运行成功,如果不在,则测试失败),等等。

唉,在 T-SQL 中不可能捕获多个输出(几个集合)。通常,这在中执行。NET,这是一个创建复杂测试的安全地方。最近,有一种倾向,主要是进行测试。MS SQL Server 的. NET 级别。

无论如何,让我们回到 T-SQL。

为了简单起见,我们在存储过程中注释掉详细信息的输出,也就是说,我们在 SearchEmployee:

在我们的例子中,测试相当简单,它将检查以下规则:必须有数据,否则,它将输出一条消息,说明技能可能不正确,并且存储过程没有正常工作。

未能实现该规则表明,要么数据不存在,必须基于不同的标准进行搜索,要么标准定义不正确,必须进行修正。这也可能意味着存储过程本身无法正常运行。

因此,我们返回到之前生成的代码,并将其更改如下:

-- Comments here are associated with the test. 
-- For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/ 
ALTER PROCEDURE CL1.[test UT_SearchEmployee_Exception] 
AS 
BEGIN 
  CREATE TABLE #tbl ([EmployeeID] INT, [LastName] NVARCHAR(255),  [FirstName] NVARCHAR(255), 
  [SkillID] INT, [SkillName] NVARCHAR(255), [StartDate] DATETIME, [FinishDate] DATETIME, 
  [Years] INT, [Months] INT, [Days] INT);   INSERT INTO #tbl 
  EXEC [dbo].[SearchEmployee] @SkillList = N'programming'
                                             ,@CountNotSkill = 1;   IF(NOT EXISTS(SELECT TOP(1) 1 FROM #tbl)) 
    EXEC tSQLt.Fail 'Nothing found. Check input parameters and stored procedure code';   DROP TABLE #tbl; 
END;

现在,让我们在 JobEmpl 数据库上再单击一次右键,并在下拉菜单中选择单元测试“查看测试列表”命令:

Img.9 .调用查看测试列表命令

之后,我们选择我们需要的测试并运行它:

Img.10 .运行给定的测试

正如我们在下面看到的,测试并不成功:

Img.11 .运行单元测试导致错误

需要强调的是,您可以选择多个单元测试并启动它们,或者只启动其中的几个。如有必要,您可以创建和删除测试。在这里,我们将通过点击“打开测试”命令来打开所选择的测试:

Img.12 .选择打开单元测试的命令

我们希望将代码更改如下:

-- Comments here are associated with the test. 
-- For test case examples, see: http://tsqlt.org/user-guide/tsqlt-tutorial/ 
ALTER PROCEDURE CL1.[test UT_SearchEmployee_Exception] 
AS 
BEGIN 
    CREATE TABLE #tbl ([EmployeeID] INT, [LastName] NVARCHAR(255), [FirstName] NVARCHAR(255), 
    [SkillID] INT, [SkillName] NVARCHAR(255), [StartDate] DATETIME, [FinishDate] DATETIME, 
    [Years] INT, [Months] INT, [Days] INT);     INSERT INTO #tbl 
    EXEC [dbo].[SearchEmployee] @SkillList = N'C#;T-SQL;ASP.NET'
                                               ,@CountNotSkill = 1;    IF(NOT EXISTS(SELECT TOP(1) 1 FROM #tbl)) 
         EXEC tSQLt.Fail 'Nothing found. Check input parameters and stored procedure code';    DROP TABLE #tbl; 
END;

接下来,让我们再次运行测试:

Img.13 .单元测试的成功完成

结果,测试成功运行。错误的发生仅仅是因为“编程”技能不在数据库中,我们必须进一步详细描述该技能。

除此之外,我们可以重命名测试类和测试本身,并更新测试信息:

Img.14 .重命名和更新测试类信息的可能性

您也可以运行测试并在窗口底部的测试结果中打开更改:

Img.15 .测试结果功能

最终,在单元测试的帮助下,您可以覆盖所创建的解决方案的大部分甚至全部功能。底线是单元测试是 DevOps 基本原则的一部分,因为它们在这个自动化过程中扮演着关键角色之一。如果您努力加快和保护数据库发布,您可以应用 DevOps automation 的几个解决方案之一,例如devo PS Automation for SQL Server

原载于 2020 年 8 月 31 日【https://blog.devart.com】

SQL vs 熊猫——2020 年选哪个?

原文:https://towardsdatascience.com/sql-vs-pandas-which-one-to-choose-in-2020-a98f236829ef?source=collection_archive---------7-----------------------

最流行的数据分析选项之间的详细比较—第 1/2 部分

SQL 已经存在了几十年,是一个非常受欢迎的选项,因为几乎每个人都知道它,而且它很容易学习和理解。很久以后,熊猫——我们大多数人从此坠入爱河。今天,我们将开始比较这两者,从基础到更高级的东西—分析方面。

瑞安·赫顿在 Unsplash 上的照片

SQLPython 是当前时代数据分析师和数据科学家几乎不可或缺的两种语言。当任何人开始他们的数据分析之旅时,他们通常首先从 SQL 开始,然后慢慢转向 RPython

为了使这种转换更加容易,我们将尝试比较一下 SQLPandas 中的一些非常重要的命令。对于外行来说, SQL 是一种用于在关系数据库中存储、操作和检索数据的语言。Pandas 是 python 中的一个库,用于数据分析和操作。

这是该系列的第一部分,包括:

  • 导入数据
  • 挑选
  • 在哪里
  • 插入
  • 更新

两种语言的选项。下一部分涵盖了更高级的主题,比如连接、联合和分组。

出于本文的目的,我们使用了 生育和避孕 数据集。一旦我们下载了数据集,让我们开始在 SQLPandas 中导入数据集文件。

导入数据

个人感觉相比熊猫SQL 中导入数据文件有点繁琐。在 Pandas 中,我们可以简单地使用pandas.read_csv() 然后瞧——我们的文件被导入,一个数据框已经准备好,我们只需一个命令就可以浏览。这不是很好吗?

结构化查询语言

但是在 SQL 中,事情就有点不一样了。我们必须首先使用CREATE TABLE 语句创建一个表,其字段类似于文件中提到的字段,然后从文件中复制数据。你可以参考下面给出的代码。

*CREATE TABLE fert_data (
    country character varying(30),
    region character varying(50),
    tfr numeric,
    contraceptors integer
);*

查询成功返回,并创建了一个名为 fert_data 的新表。现在,为了从 CSV 文件导入记录,我们必须使用如下所示的COPY命令。

*copy fert_data from ‘Path\to\file’ with delimiter ‘,’ csv header encoding ‘windows-1251’;*

熊猫

现在将上面的工作与熊猫中的这段代码进行比较,用于导入文件。

*df = pd.read_csv(’Path\to\your\dataset’)*

这就是你要做的。

挑选

一旦我们导入了数据集,我们可能希望浏览一下其中的记录,以便更好地理解数据集。在 SQL 中,我们使用一个SELECT语句从表中获取记录。该语句将如下所示:

*SELECT column_name1, column_name2,…
FROM table_name;*

如果你不想看到所有的记录呢?是的,您可以使用TOPLIMIT关键字来限制行数。

在熊猫中,我们用head()tail() 分别取前 5 行和后 5 行。这相当于 SQL 中的SELECT * FROM table LIMIT 5。如果必须从数据框中选择特定的列,那么语法应该如下所示:

*df[[‘column_name1’, ‘column_name2’]]*

这里有一个例子可以更好地说明这一点。我们将遵循这里的示例程序,首先编写 SQL 代码,然后编写相应的 Pandas 代码。

结构化查询语言

*SELECT *
FROM fert_data
LIMIT 5;*

熊猫

*df.head()*

在哪里

SQL 中,WHERE子句用于根据条件过滤记录。子句通常与条件和逻辑运算符结合使用,以形成过滤条件。WHERE子句的语法如下所示:

*SELECT *
FROM table_name
WHERE condition_expression;*

Pandas 中,没有像 SQL 那样的过滤记录的单一命令。我们可以使用布尔索引和位置索引。在熊猫索引中,通过逻辑条件计算布尔掩码来过滤数据。以下各项的语法如下:

布尔索引:

*df[df[‘column_name’] == value]*

位置索引:

*df[df.loc[‘column_name’] == value]*

SQL熊猫中,我们都可以使用比较(>、<、> =、< =、!=、==)和逻辑运算符(' and '、' or '、' not '或“|”、'&'、' ~ ')等符号)。

这里有一个简单的例子,我们希望找到不是来自非洲地区,但 TFR 在 5.5 和 7 之间的记录:

结构化查询语言

*SELECT country, tfr
FROM fert_data
WHERE tfr BETWEEN 5.5 AND 7 AND region != ‘Africa’;*

熊猫

*df.loc[((df.tfr >= 5.5) & (df.tfr <= 7)) & (df.region !=’Africa’)]*

插入

INSERT语句在 SQL 中使用,用于在数据库表中插入行。插入新记录的语法如下所示:

*INSERT INTO table_name(column_name_1, column_name_2,…) VALUES (value1, value2,…);*

熊猫中,没有在数据框中插入新行的特定功能。我们可以使用concat()append()等工具来执行这个任务。但是最常用的是append()

*df = df.append(new_row_to_be_inserted, ignore_index=True)*

为了更加清楚,让我们插入一个新行,其中包含有关日本的详细信息。

结构化查询语言

*INSERT INTO fert_data(country, region, tfr, contraceptors)
VALUES (‘Japan’,’Asia’,1.73,73);*

熊猫

*data = {
    ‘country’: ‘Japan’, 
    ‘region’: ‘Asia’, 
    ‘tfr’:1.73, 
    ‘contraceptors’ : 73
}df = df.append(data, ignore_index=True)*

从上图中可以看到,在第 50 个索引处创建了一个新行。

更新

SQLUPDATE语句用于更新或修改现有记录的值。update 语句的语法如下:

*UPDATE table_name
SET column_name = ‘modified_value’
WHERE condition;*

Pandas 中,没有更新现有行的特定函数。一个常见的程序是使用位置索引(如过滤部分所述)来定位修改的位置,然后给它分配一个新值。这几乎就像一个变量赋值。

让我们尝试一个例子来详细理解它——将 Mauitius 的逆变桥数量更新为‘65’。

结构化查询语言

*UPDATE fert_data
SET contraceptors = ‘65’
WHERE country = ‘Mauitius’;*

查询成功返回,您可以看到值已经更新。

熊猫

*df.loc[df[‘country’] == ‘Mauitius’, ‘contraceptors’] = 65*

首先,我们找到了国家为“Mauitius”的行,然后将“contraceptors”字段的值设置为 65。

在你走之前

如你所见,SQLPandas 在某些方面都更好,而且没有明显的赢家。对于一些任务,比如插入和更新, SQL 是明显的赢家——另一方面,由于代码可读性更强、更易于理解, Pandas 不需要您事先为数据创建数据结构,这可以节省大量时间。**

想象一下创建一个包含 100 多列的表,或者将数据组织到多个表中并使用连接。屁股疼。

我们将在下一篇文章中讨论更高级的主题,比如分组、联合和连接,所以请保持关注。

感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

* [## 通过我的推荐链接加入 Medium-Dario rade ci

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@radecicdario/membership)*

加入我的私人邮件列表,获得更多有用的见解。

SQL vs 熊猫——2020 年选哪个?第二部分

原文:https://towardsdatascience.com/sql-vs-pandas-which-one-to-choose-in-2020-part-2-9268d4a69984?source=collection_archive---------35-----------------------

最流行的数据分析选项之间的详细比较—第 2/2 部分

SQL 和熊猫并不是新技术。尽管如此,为这两种技术找到对应的功能并不是一件容易的事情。这就是这篇文章和上一篇文章发挥作用的地方——为你提供两者之间的详细比较。

本杰明·沃罗斯在 Unsplash 上拍摄的照片

几天前,我已经讨论了这个由两部分组成的系列的第一部分,讨论了这两种技术之间更简单的比较:

[## SQL vs 熊猫——2020 年选哪个?

最流行的数据分析选项之间的详细比较—第 1/2 部分

towardsdatascience.com](/sql-vs-pandas-which-one-to-choose-in-2020-a98f236829ef)

首先阅读那篇文章不是先决条件,但肯定会帮助你更好地理解这两者。这些技术不是为同样的工作设计的,但是很高兴看到两者之间对应的功能。如前所述,今天我们将讨论更高级的主题:

  • 连接
  • 联合
  • 分组

在我们这样做之前,让我们从简单的删除语句开始。

删除

DELETE语句在 SQL 中用于从表中删除或移除一行。在 SQL 中删除行的语法如下:

DELETE FROM table_name
WHERE condition;

熊猫中删除一行略有不同。在 Pandas 中,我们不删除一行,我们只是选择我们需要的部分,然后丢弃其余的部分。如果你觉得这是个谜,不要担心,这个例子会进一步说明它。

假设我们想删除亚洲地区的所有记录。

结构化查询语言

DELETE FROM fert_data
WHERE region = ‘Asia’;

已成功删除行。现在让我们看看如何在熊猫中执行这个任务。

熊猫

df = df.loc[df[‘region’] != ‘Asia’]

这里,我们选择了区域不是“亚洲”的所有行,然后将结果集分配给我们当前的数据框。这意味着我们已经排除了区域为“亚洲”的所有行。

连接

JOINS用于 SQL 中,根据特定条件将两个或多个表连接或合并在一起。在 SQL 中主要有四种类型的连接: LEFTRIGHTINNERFULL。以下是 JOIN 的语法:

SELECT *
FROM table_name_1 as t1 JOIN table_name_2 as t2
ON t1.column_name_1 = t2.column_name_2;

Pandas 中,我们可以使用merge() 来连接两个或多个数据帧。默认情况下,它会执行内部连接。但是您可以使用how参数定制它来执行其他连接。pd.merge()的基本语法如下:

merge(left_df, right_df, how=’inner’, on=condition)

这里有一个例子来说明连接。

结构化查询语言

下面给出了一个名为 country_sub_region 的表格。我们必须使用内部连接将这个表与 fert_data 连接起来。

SELECT country, sub_region
FROM country_sub_region;

SELECT * FROM
fert_data as f INNER JOIN country_sub_region as c
ON f.country = c.country;

这些表已成功联接。让我们看看如何在熊猫中加入他们。

熊猫

这里,我们创建了一个类似于 country_sub_region 表的数据框:

country_sub_reg = data = [
    [‘country’, ’subregion’],
    [‘Kenya’, ’East Africa’],
    [‘Liberia’, ’West Africa’],
    [‘Mali’, ’West Africa’]
]df_sr = pd.DataFrame(country_sub_reg[1:],columns=country_sub_reg[0])

我们将使用内部连接将df_sr国家字段上的df合并:

pd.merge(df, df_sr, on=’country’, how=’inner’)

联盟

UNION运算符用于将 SQL 中两个或多个SELECT语句的结果组合在一起。有一个同志给工会操作员叫UNION ALL。它们的区别在于前者从组合结果中删除了重复值。

熊猫UNION ALL操作员的任务可以使用pd.concat()来执行。而UNION操作符的功能可以通过首先使用pd.concat()连接数据帧,然后对其应用pd.drop_duplicates() 来执行。

结构化查询语言

为了说明 SQL 中的UNION/UNION ALL操作符,我们创建了一个名为 fert_data_1 的附加表。该表中的数据如下所示:

我们的任务如下—从 fert_datafert_data_1 表中找到行的并集:

SELECT * FROM fert_data_1
UNION ALL
SELECT * FROM fert_data
ORDER BY country;

您会发现有一些重复的值。是的,你猜对了。您可以使用UNION操作器来移除它们。你自己试试。

熊猫

在 Pandas 中,我们创建了一个类似于 SQL 中的 fert_data_1 表的数据帧。

data = [
    [‘country’, ’region’, ’tfr’, ’contraceptors’],    
    [‘USA’, ’North.Amer’, 1.77, 20],
    [‘UK’, ’Europe’, 1.79, 23],
    [‘Bangladesh’, ’Asia’, 5.5, 40],
    [‘Thailand’, ’Asia’, 2.3, 68]
]df1 = pd.DataFrame(data[1:], columns=data[0])

dfdf1的联合:

df_dupli = pd.concat([df1, df])

来自数据框的数据已被合并。但是,在这种情况下,我们也会得到重复的行。例如,我们的目标是让“孟加拉国”只出现一次:

df_dupli[df_dupli[‘country’] == ’Bangladesh’]

我们可以使用drop_duplicates()删除重复记录,如下所示:

df_wo_dupli = pd.concat([df1, df]).drop_duplicates()

让我们运行同样的查询,看看我们是否仍然得到两行。

df_wo_dupli[df_wo_dupli[‘country’] == ‘Bangladesh’]

问题解决了。不再有重复的行。

分组依据

SQL 中的GROUP BY子句用于通过将记录分组在一起来准备汇总行。子句通常与 AVG、SUM、COUNT、MIN、MAX 等集合函数连用。下面是GROUP BY子句的基本语法:

SELECT column_name_1, agg_func(column_name_2)
FROM table_name
GROUP BY column_name_1;

Pandas 中,我们有一个groupby()函数,帮助我们汇总特定列的数据。通用语法如下:

df.groupby([‘column_name_1’]).agg_function()

让我们尝试一个例子来更好地理解它—找到每个区域的平均 tfr 和计数逆变场。

结构化查询语言

SELECT region, round(avg(tfr),2), count(contraceptors)
FROM fert_data
GROUP BY region;

熊猫

df.groupby(‘region’).agg({‘tfr’: np.mean, ‘contraceptors’: np.size}).round(2)

我们从两个查询中得到相同的结果。你一定想知道熊猫的那个agg()是干什么用的。它用于聚合指定轴上的一个或多个操作。

在你走之前

这就做到了——你现在应该对这两种技术有了一个很好的了解,至少在数据分析方面。很难推荐哪一个,因为这取决于你以前的经验、偏见和你工作的公司的选择。

好的一面是——在 SQL 中完成的所有事情都可以在 Pandas 中完成——至少在这个层面上。随便选一个你更喜欢的,不会出错的。

感谢阅读。

喜欢这篇文章吗?成为 中等会员 继续无限制的学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

[## 通过我的推荐链接加入 Medium-Dario rade ci

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@radecicdario/membership)

加入我的私人邮件列表,获得更多有用的见解。

在 4 分钟内解释 SQL 窗口(分析)功能

原文:https://towardsdatascience.com/sql-window-analytic-functions-explained-in-4-minutes-6171c6983263?source=collection_archive---------20-----------------------

用这个简单的解释来提升你的 SQL 技能

塞巴斯蒂安·斯坦尼斯在 Unsplash 上的照片

目录

  1. 介绍
  2. 什么是窗口(分析)函数?
  3. 窗口函数的剖析
  4. 聚合函数与窗口函数的示例
  5. 窗口功能的优势

介绍

您习惯于简单的查询(从 WHERE GROUP BY 中选择),并且希望将您的技能提升到一个新的水平。接下来应该学什么?

窗口函数,也称为分析函数,是强大的 SQL 函数,将带你从初级 SQL 用户到中级 SQL 用户。在本文中,我们将简要介绍什么是窗口函数,然后提供一个简单的例子来说明这一点。

什么是窗口(分析)函数?

窗口函数类似于聚合函数,它返回聚合值(如 SUM()、COUNT()、MAX())。

窗口函数的不同之处在于它不对结果集进行分组。输出中的行数与输入中的行数相同。

如果这对你来说还没有意义,不要担心。坚持到例子,它会更有意义。😃

窗口函数的剖析

窗口功能的基本结构如下:

SELECT SUM() OVER(PARTITION BY ___ ORDER BY ___) FROM Table

为了更容易理解,需要记住三个主要部分:

  1. 聚合函数:在上面的例子中,我使用了 SUM(),但是你也可以使用 COUNT()、AVG()等等
  2. PARTITION BY: 简单地把它想成一个 GROUP BY 子句,但实际上,它被称为 PARTITION BY。
  3. ORDER BY: ORDER BY 与您预期的一样。当输出的顺序很重要时,考虑这一点很重要。

聚合函数与窗口函数的示例

为了更清楚起见,假设我们有下表:

如果我们想获得按性别划分的平均 GPA,我们可以使用一个聚合函数并运行以下查询来获得以下结果:

SELECT Gender, AVG(GPA) as avg_gpa
FROM students
GROUP BY Gender

结果

下一部分是关键。

现在假设我们想要获得以下输出:

我们可以使用一个聚合函数(就像上面的那个),然后将结果加入到初始表中,但是这需要两步。

改为,我们可以用一个窗口函数来实现同样的输出:

SELECT *,
    AVG(GPA) OVER (PARTITION BY Gender) as avg_gpa
FROM table

通过上面的查询,我们按性别划分(分割)数据,并计算每个性别的平均 GPA。然后,它创建一个名为 avg_gpa 的新列,并为每一行追加相关的平均 gpa。

窗口功能的优势

使用窗口函数有三个主要优点:

简单

一旦你掌握了它,窗口函数使用起来就简单多了。注意,在上面的例子中,只需要一行额外的代码就可以使用窗口函数得到想要的输出,而不是使用聚合函数然后连接结果。

速度

与上述观点相关,使用窗口函数比使用替代方法要快得多。当您处理数百或数千千兆字节的数据时,这将派上用场。

多才多艺

最重要的是,窗口函数有大量的通用性,这超出了本文的范围。一些例子包括添加移动平均值、添加行号和滞后数据。

感谢阅读!

读到这里,你应该对什么是窗口函数,如何写窗口函数,窗口函数的优点有所了解。

希望这些例子能让你更容易理解窗口函数是如何工作的。一如既往,我祝你在学习中好运!

特伦斯·申

SQL 窗口函数—用 Leetcode 中的真实面试问题演示

原文:https://towardsdatascience.com/sql-window-function-demonstrated-with-real-interview-questions-from-leetcode-e83e28edaabc?source=collection_archive---------6-----------------------

作者 Sherwin 郑

窗口功能现在是 SQL 访谈中必不可少的内容,尽管它只是在 2018 年 4 月 19 日在发布后才在 MySQL 8.0 上可用。

作者图片

一张便条

本文假定读者除了基本的 SQL 语法之外,还知道 SQL 窗口函数。如果不熟悉,想了解更多,可以参考我学习用的这个[ 链接。请注意,在不同的 SQL 平台上,语法可能会有所不同。

本文将向您介绍几个常见的 it 应用程序,并在面试中使用 Leetcode.com 的例子进行测试

最近的 Leetcode 问题 1596。每个客户最常订购的产品启发我写这个主题是因为它把一堆知识点链接在了一起,包括窗口函数及其从易到中的应用,自连接及其与窗口函数的关系,以及 SQL 语句的执行顺序。

我的 Leetcode 数据库从 09242020 开始更新

问题类型一—具有两级粒度的数据

每个部门的最高工资

首先,我们先从一个简单的问题开始:各部门工资最高是多少?我想答案很快就在你脑海里弹出来:SELECT MAX(salary)GROUP BY department

每个部门及其相应员工的最高工资

然而,如果面试官想夸大其词,说公司不仅想知道最高工资,还想知道与该工资相对应的员工姓名,该怎么办?如何编写查询?在这个意义上,它类似于 Tableau 中的细节层次(LOD)操作。这是 leet code[【184。部门最高工资]](https://leetcode.com/problems/department-highest-salary/)

有两种方法可以解决这个问题:窗口函数自连接/相关子查询,任何一种都可以。你会发现它们是等价的,而窗口函数更简洁,更容易编写。

使用窗口函数,很简单,只需编写下面的代码

而使用相关子查询时,代码需要花费更多时间来运行

对于外部表中的每一行,相关子查询将在WHERE子句中查找内部表,并将外部表中的行与子查询中对应的行进行比较。如果使用自连接,它不限于顶部记录,还可以通过将COUNT()WHERE语句一起使用来给出RANK()

关于 Leetcode 的其他良好实践问题包括:

  • 178。等级分数 ](硬编码 DESNE_RANK)
  • 177。第 n 高工资
  • 185。部门前三名薪资

我对 178 的解决方案在这里。这可能不是最佳实践,但它很好地说明了该机制是如何工作的。

简短说明:

  • Self-join + COUNT() = RANK(),平局时给出缺口
  • 自连接+ COUNT(DISTINCT ) = DENSE_RANK(),这消除了平局时差距

作者图片

为什么它们的等价性值得一提?好吧,如果您非常幸运,您的 SQL 平台不支持窗口函数,您将不得不使用自连接设计一个等效的窗口函数来实现相同的效果。所以知道还是好的。

以上两个例子的总结:

我们可以使用 **GROUP BY** + **MIN()** 找到每组中的最小值,但是如果我们想知道它在其他字段下的对应值,就需要使用 window 函数给出一个排名,相当于旧版 SQL 中的一个过滤自连接或者 Tableau 中的 LOD。

问题类型二—具有三级粒度的数据

当排名字段是聚合值时

让我们再来一次。

如果我们想知道“年薪”的级别,但在汇总他们的“月薪”之前,我们还不能在我们的窗口函数中使用ORDER BY该怎么办?

例如,编写一个 SQL 查询来查找每个客户最常订购的产品。

orders table
+----------+------------+-------------+------------+
| order_id | order_date | customer_id | product_id |
+----------+------------+-------------+------------+
| 1        | 2020-07-31 | 1           | 1          |
| 2        | 2020-07-30 | 2           | 2          |
| 3        | 2020-08-29 | 3           | 3          |
| 4        | 2020-07-29 | 4           | 1          |
| 5        | 2020-06-10 | 1           | 2          |
| 6        | 2020-08-01 | 2           | 1          |
| 7        | 2020-08-01 | 3           | 3          |
| 8        | 2020-08-03 | 1           | 2          |
| 9        | 2020-08-07 | 2           | 3          |
| 10       | 2020-07-15 | 1           | 2          |
+----------+------------+-------------+------------+

上一节中的问题很简单,因为显示了排名字段salary。但是在上表中,每一行都记录了一个客户购买的“产品”,并且是重复的。

如果我们采用前面的推理,我们需要(1)写一个子查询:GROUP BY customer_id, product_idCOUNT(product_id) AS cnt,然后(2)使用cnt作为窗口函数的排序字段,(3)在WHERE子句中过滤结果,比如在第三个查询中使用rnk = 1,因为我们不能在WHERE语句中使用窗口函数。

通过了解 SQL 语句的执行顺序来简化查询

然而,我们实际上可以将 t1 和 t2 合并到一个查询中,如下所示

一开始,当把PARTITION BYGROUP BY结合起来时,我并不是本能的:查询基于哪个分区/组?但是在我查看了查询的执行顺序之后,这个问题就迎刃而解了:

  1. 从并加入 s。
  2. 哪里…
  3. 分组依据…
  4. 拥有…
  5. 选择…
  6. 独特的…
  7. 排序依据…
  8. 极限/偏移。

要知道窗口函数与SELECT是在同一个阶段执行的,所有的东西也是如此,包括PARTITION BY。因此,实际的执行步骤如下所示(注意:前面代码块中的代码不会在结果中显示CNT,但是它会按照图示进行计算)。

作者图片

这是 1596 年的 Leetcode。每位顾客最常订购的产品

作者图片

扩展到更多的细节层次

如果我们想知道购买第二多的产品呢

我进一步的想法是,如果我们想知道第二多,甚至第 n 多购买的产品,因为我们比较这个问题[ 177。第 n 高工资。你如何修改最后一部分的代码?

  • rnk = 1改为rnk = 2,或者
  • rnk = 1改为rnk = 2,同时将RANK()改为DENSE_RANK()

应该由你来思考。看完这篇文章,你应该熟悉其中的关系。

结论

掌握这类数据粒度问题在很多方面都有帮助。我们首先介绍了窗口函数的价值和用法,并将其与简单的GROUP BY进行了比较。然后,我们对窗口函数和自连接/相关子查询有了更深入的了解。最后,我们学习了如何用更高粒度的数据对任何级别的细节进行排序。

读完本文后,无论是在面试还是在生产中,您都应该能够处理这种类型的问题,并且通过使用窗口函数练习您的 SQL 专业知识,从任何级别的细节中挖掘见解。

关于我

Sherwin 是维克森林大学的商业分析理学硕士,本科专业是中山大学的管理科学,辅修的是伊拉斯谟大学鹿特丹管理学院(RSM)的大数据。查看我的 LinkedIn Github了解更多。

Pandas 中类似 SQL 的窗口函数

原文:https://towardsdatascience.com/sql-window-functions-in-python-pandas-data-science-dc7c7a63cbb4?source=collection_archive---------6-----------------------

所有熊猫窗口功能的单一位置

Unsplash 上由 Waldemar Brandt 拍照

在 SQL 世界中,窗口函数非常强大。然而,没有一个写得很好的和统一的熊猫的地方。在熊猫网站的上非常详细地介绍了用熊猫编写类似 SQL 代码的基础知识。然而,Pandas 指南缺乏对 SQL 及其 Pandas 对等物的分析应用程序的良好比较。

在这篇文章中,我将介绍一些窗口函数的 SQL 等价物,并带你了解熊猫的窗口函数。

。groupby 是你的朋友

如果你做大数据分析,并且发现自己需要使用 Pandas,那么可能很难从富有表现力的 SQL 语言过渡到 Pandas 的数据操作方式。

我打算这篇文章简短而甜蜜,主要关注实现的战术方面,而不是细节。这篇文章假设你熟悉窗口和分区函数。让我们把最常见的 SQL 分析函数翻译成熊猫。

每个组(分区)中的行号

Pandas 相当于每个分区中的行号,带有多个 sort by 参数:

SQL:

ROW_NUMBER() over (PARTITION BY ticker ORDER BY date DESC) as days_lookback
---------
ROW_NUMBER() over (PARTITION BY ticker, exchange ORDER BY date DESC, period) as example_2

熊猫:

df['days_lookback'] = df.sort_values(['date'], ascending=False)\
             .groupby(['ticker'])\
             .cumcount() + 1
---------
df['example_2'] = df.sort_values(['date', 'period'], \
             ascending=[False, True])\
             .groupby(['ticker', 'exchange'])\
             .cumcount() + 1

每个组(分区)中的最后/下一个记录

熊猫相当于超前和滞后窗口功能:

SQL:

LAG(price) over (PARTITION BY ticker ORDER BY date) as last_day_px
---------
LEAD(price) over (PARTITION BY ticker ORDER BY date) as next_day_px

熊猫:

df['last_day_px'] = df.sort_values(by=['date'], ascending=True)\
                       .groupby(['ticker'])['price'].shift(1)
---------
df['next_day_px'] = df.sort_values(by=['date'], ascending=True)\
                       .groupby(['ticker'])['price'].shift**(-1)**

各组内的百分位数等级

百分比等级/密集等级或等级窗口函数的 Pandas 等价物:

SQL:

PERCENT_RANK() OVER (PARTITION BY ticker, year ORDER BY price) as perc_price

熊猫:

df['perc_price'] = df.groupby(['ticker', 'year'])['price']\
                        .rank(pct=True)

每组内的运行总和

Pandas 相当于滚动求和、运行求和、求和窗口函数:

SQL:

SUM(trade_vol) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 3 PRECEEDING AND CURRENT ROW) as volume_3day
---------
SUM(trade_vol) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN UNBOUNDED PRECEEDING AND CURRENT ROW) as cum_total_vol
---------
SUM(trade_vol) OVER (PARTITION BY ticker) as total_vol

熊猫:

df['volume_3day'] = df.sort_values(by=['date'], ascending=True)\
                       .groupby(['ticker'])['trade_vol']\
                       .rolling(3, min_periods = 1).sum()\
                       .reset_index(drop=True, level=0)
---------
df['cum_total_vol'] = df.sort_values(by=['date'], ascending=True)\
                       .groupby(['ticker'])['trade_vol']\
                       .cumsum()
---------
df['total_vol'] = df.groupby(['ticker', 'year'])['trade_vol']\
                        .transform('sum')

每组内的平均值

Pandas 相当于平均线、移动平均线(移动平均线)窗口函数:

SQL:

AVG(trade_vol) OVER (PARTITION BY ticker) as avg_trade_vol,
---------
AVG(price) OVER (PARTITION BY ticker ORDER BY date ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) as ma20

熊猫:

df['avg_trade_vol'] = df.groupby(['ticker', 'year'])['trade_vol']\
                        .transform('mean')
---------
df['ma20'] = df.sort_values(by=['date'], ascending=True)\
                    .groupby('ticker')['price']\
                    .rolling(20, min_periods = 1).mean()\
                    .reset_index(drop=True, level=0)

。transform 是 Pandas 中一个强大的命令,我邀请您在这里了解更多信息——

[## 熊猫小组详细解释了

了解如何掌握所有熊猫的分组功能,如聚集(聚合),转换和过滤-代码指南…

towardsdatascience.com](/pandas-groupby-aggregate-transform-filter-c95ba3444bbb)

就是这样!上面的例子应该给你提供了一个 Pandas 窗口函数的参考。

为便于测试,上述示例的数据集示例如下:

import pandas as pd
data = {
    'year':[2019, 2019, 2020, 2020, 2020, 2020, 2019, 2020, 2020, 2020],
    'date':['2019-12-30', '2019-12-31', '2020-01-02', '2020-01-03', '2020-01-06', '2020-01-07', '2019-12-31', '2020-01-02', '2020-01-03', '2020-01-06'],
    'ticker':['BYND', 'BYND', 'BYND', 'BYND', 'BYND', 'BYND', 'FB', 'FB', 'FB', 'FB'],
    'exchange':['Q', 'Q', 'Q', 'Q', 'Q', 'Q', 'N', 'N', 'N', 'N'],
    'price':[74.15, 75.60, 75.64, 75.41, 74.59, 83.89, 205.25, 209.78, 208.67, 212.60],
    'trade_vol':[2548100, 2002000, 2221700, 1628700, 2324700, 12044600, 8953500, 12077100, 11188400, 17058900],
}
df = pd.DataFrame(data)

SQL 窗口函数—第 2 部分

原文:https://towardsdatascience.com/sql-window-functions-part-2-bbdcd16c1ff7?source=collection_archive---------30-----------------------

不同类型的 SQL 窗口函数。

图片作者:弗兰奇|【Unsplash.com】T2

简介:

窗口函数跨一组与当前行有某种关系的表行执行数据分析计算。地址可比较类型的计算可以用一个聚合函数来完成,该函数给出一行或按条件分组(参见图 1)。

Window 函数不会导致行组合成一个输出行。保留其单独标识的行也能够访问查询结果的当前行以外的内容。(参见图 1)。

**Window Function Syntax:**
Window_Function([All] expression) 
OVER( [PARTITION BY expression_list] [ORDER BY order_list Row_or_ Range clause] )

图 1 —聚合函数和窗口函数的区别

更多信息请访问我的上一篇博客,我在第一部分介绍了 SQL 窗口函数,并概述了不同类型的 SQL 窗口函数。

在这篇博客中,我将解释所有三种主要类型的 SQL 窗口函数以及例子。

用来解释以下概念的数据库。

数据集:可从 Github 获得: Retails.csv

零售表截图。

SQL 窗口函数的类型:

  1. 窗口聚合函数
  2. 窗口排名聚合函数
  3. 窗口分析功能

1。窗口聚合函数

包含一个支持聚合函数,即 AVG()、计数()、最小值()、最大值()、总和()。

AVG():

  • 返回输入表达式值的平均值。
  • 该函数处理数值,并忽略空值。

例如:

编写一个查询来计算平均订单价格。

**Query:** SELECT order_id, product_id, price, AVG(price) OVER (PARTITION BY order_id) AS Average_Order_Price 
FROM retails

输出-查询-AVG()

解释:

  • 为了计算平均订单价格,window 函数对价格列使用了 AVG(),对 order_id 使用了分区依据
  • 考虑订单 id: 1112 —包含 3 种产品(即 1、2、5)。这三个乘积的平均值为(866 + 163 +173) / 3 = 400.667

b. COUNT():

  • 如果列或表达式中有空值,也计算空值的行数。
  • 在数据集中创建新要素时,此窗口函数非常有用。像计数属于每个客户的条目数。

例如:

查询 1: 计算客户在订单中购买的产品数量。

SELECT order_id, name, product_id, COUNT(*) OVER (Partition BY order_id) AS Number_of_Products 
FROM retails

查询 2: 计算客户购买的产品销售数量(累计)。

SELECT order_id, name, product_id, COUNT(*) OVER (Order BY order_id) AS Number_of_Products 
FROM retails

输出—查询 1 和 2 —计数()

说明:

查询 1:

  • 按 order_id 划分计数属于特定 order_id 的记录数。
  • 在输出中,我们可以看到每个订单显示了几个产品。

查询二:

  • Order by order_id 计算记录数和特定的 order_id,然后将连续顺序的记录数相加。
  • 输出:我们可以看到计数随着与特定 order_id 相关的记录数量的增加而增加。

c. Min()或 Max():

  • Min()或 Max()分别返回输入值中表达式的最小值或最大值。
  • 两个窗口函数都处理数值,并忽略空值。

举例:

下面的查询向结果集添加了一个新特性,即在各自的订单中购买的产品的最低和最高价格。

**Query:**
SELECT order_id, name, product_id, price, 
MIN(price) OVER (Partition BY order_id) AS Minimum_Price_Product,
MAX(price) OVER (Partition BY order_id) AS Maximum_Price_Product 
FROM retails

Output- Query- MIN()或 MAX()

说明:

  • 对于每个 order_id 记录,已经添加了产品的最低和最高价格。
  • 我们也可以单独使用每个功能。

d. Sum():

  • 返回所有输入值的总和/合计表达式。
  • 该函数处理数值,并忽略空值。

示例:

下面的查询返回每个 order_id 的总价。

**Query:**
SELECT order_id, name, product_id, price, 
SUM(price) OVER (PARTITION BY order_id) AS Average_Order_Price 
FROM retails

输出-查询-总和()

说明:

  • 为每个 order_id 添加了带有 total_order_price 的新列。
  • 有助于分析数据,我们有许多记录,属于每个订单 id。

2.窗口排名聚合函数:

包括支持排名函数之一,即 RANK()、DENSE_RANK()、ROW_NUMBER()。

排名():

  • 基于 OVER 子句中的 ORDER BY 表达式的值在一组值中的排名(请参考查询 1)。
  • 每个值在其分区内由表达式进行排序(参考查询 2)。
  • 等级标准值相等的行将获得相同的等级。
  • 并列或相同的排名跳过连续的排名,如排名():1,1,3,4,5。

例如:

查询 1: 根据价格对产品进行排名。

SELECT order_id, name, product_id, price, 
RANK() OVER (ORDER BY price) AS Rank_Product_Price 
FROM retails

查询 2: 根据每个订单中的价格对产品进行排序(即按 order_id 进行划分)。

SELECT order_id, name, product_id, price, 
RANK() OVER (PARTITION BY order_id ORDER BY price) AS Rank_Product_Price 
FROM retails

输出—查询 1 和 2 —排名()

解释:

正如我们在两个查询中所看到的,ORDER BY 表示用于对值进行排序的表达式。

查询 1:

  • 排名是基于 product_price 完成的。
  • 另请注意,具有相同值的 9 行的等级为 1。
  • 所以下一个等级值从 10 开始。

查询 2:

  • 已通过 ORDER BY 表达式(即价格列)完成排名。
  • 检查 order_id 114,我们可以看到前两个产品价格的等级是相同的。因此分配给它等级是 1。
  • 下一个产品价格在 order_id 中排名为 3。

b. DENSE_RANK():

  • 与 Rank()函数类似,根据由表达式和 OVER 子句确定的顺序,对一组值中的值进行排序,每个值在其由表达式确定的分区内进行排序。
  • 不同之处在于,具有相等值的行获得相同的等级和平局,或者相同的等级不跳过连续的等级。
  • 示例: Dense_Rank(): 1,1,2,3,4

举例:

查询: Dense_Rank 根据每个订单中的价格对产品进行排序(即按 order_id 进行划分)。

SELECT order_id, name, product_id, price, 
DENSE_RANK() OVER (PARTITION BY order_id ORDER BY price) AS Dense_Rank_Product_Price 
FROM retails

输出-查询 Dense _ Rank()

说明:

  • 我们可以看到,对每一行的排序是基于 ORDER BY 表达式完成的,即价格值也在每个 order_id 内,即(按 order_id 划分)。
  • order_id 1114 有 5 个产品,其中 2 个产品价格相同,因此排名相同,即 1。
  • 下一个秩从 2 开始(这是 rank()和 Desne_Rank()函数的主要区别)。
  • Dense_Rank()不跳过连续的秩数。

CUME DIST():

  • 根据下面的公式:计算当前行在窗口分区内的相对等级

示例:

查询: CUME_DIST,即根据每个订单中的价格对产品进行相对排名(即按 order_id 划分)。

SELECT order_id, name, product_id, price, 
CUME_DIST() OVER (PARTITION BY order_id ORDER BY price) AS Dense_Rank_Product_Price 
FROM retails

输出-查询-CUME _ DIST()即相对等级

解释:

让我们考虑具有 3 个产品的 order_id 1112,按照下面讨论的公式计算相对等级:

  • 第 3 行—第一个产品:1/3 = 0.3333
  • 第 4 行—第二个乘积:2/3 = 0.666
  • 第 5 行—第三个产品:3/3 = 1

类似地,如果产品具有相同的价值或价格,则相对等级也与输出屏幕截图中的检查 order_id 1114 相同。

d. ROW_NUMBER():

  • 基于 OVER 子句中的 ORDER BY 表达式的当前行在其分区内的序号。
  • 每个值在其分区内由表达式排序。
  • ORDER BY 表达式的值相等的行会不确定地收到不同的行号。

例如:

查询:根据产品在每个订单中的价格将 Row_Number 分配给产品(即按 order_id 划分)。

SELECT order_id, name, product_id, price, 
ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY price) AS Row_Number_Product_Price 
FROM retails

输出-查询-行编号()

解释:

  • 正如我们在输出屏幕截图中看到的,行号是根据每个订单(按 order_id 划分)中的价格(ORDER BY expression)分配的。
  • 不考虑值是否相同,只将 row_number 赋给表达式中的每一行。

e. NTILE():

  • 将每个窗口分区的行尽可能平均地分成指定数量的分级组。
  • 要求 OVER 子句中的 ORDER BY 子句。
  • ORDER BY 子句中指定的列或表达式,首先所有值按升序排序,然后平均分配组号。

举例:

查询:根据产品价格将组/簇/桶编号分配给 10 个不同组的所有行。

SELECT order_id, name, product_id, price, 
NTILE(10) OVER (ORDER BY price) AS NTile_Product_Price 
FROM retails

输出-查询 NTILE()

解释:

  • 在这个数据集中,我们总共有 50 条记录。
  • 因此,每个集群由 5 行组成,如输出屏幕截图所示。
  • 首先,所有行都已按价格排序,然后给每一行分配一个组号。

f. PERCENT_RANK()

  • 使用以下公式计算当前行的百分比排名:

示例:

查询:根据产品价格计算或分配所有行的百分比排名。

SELECT order_id, name, product_id, price, 
PERCENT_RANK() OVER (PARTITION BY order_id ORDER BY price) AS Row_Number_Product_Price 
FROM retails

输出-查询-百分比排名()

说明:

让我们考虑具有 5 个产品的 order_id 1114,按照下面讨论的公式计算相对等级:

  • 第 9 行—第一个产品:(1–1)/(5–1)= 0
  • 第 10 行—第二个乘积:(1–1)/(5–1)= 0
  • 第 11 行—第三个产品:(3–1)/(5–1)= 0.5
  • 第 12 行—第四个产品:(4–1)/(5–1)= 0.75
  • 第 13 行—第五个产品:(5–1)/(5–1)= 1

3。窗口分析功能:

包括一个支持排名函数,即 LAG()、LEAD()、FIRST_VALUE()、LAST_VALUE()。

a .滞后()或超前():

语法:

LAG | LEAD (expression)
    OVER ([ PARTITION BY expression_list] [ORDER BY order_list] )
  • 滞后或超前返回时,值为当前行的值前或后分别在一个分区中。
  • 如果不存在任何行,则返回 null。

例如:

查询:添加新功能 1 步滞后或超前每个订单内的产品价格(即按 order_id 划分)

***SELECT order_id, name, product_id, price, 
LAG(price,1) OVER (PARTITION BY order_id ORDER BY price) AS LAG_Product_Price,
LEAD(price,1) OVER (PARTITION BY order_id ORDER BY price) AS LEAD_Product_Price
FROM retails***

输出-查询-滞后()或超前()

解释:

  • 正如我们可以看到的,在 order_id 的分区中,lag 和 lead 列分别给出 1 个步长值。
  • 当它在 LAG()中的第一行被估算为 NULL 时。
  • 类似地,在 LEAD()中,最后一行用 NULL 进行估算。

b. FIRST_VALUE()或 LAST_VALUE():

语法:

***FIRST_VALUE | LAST_VALUE ( expression ) 
      OVER ( [PARTITION BY expression_list ] [ ORDER BY order_list ][ row_or_range_clause ] )***
  • FIRST_VALUE 或 LAST_VALUE 分别返回指定表达式相对于窗口框架(分区)的第一行或最后一行值。

举例:

查询:在每个订单内添加新功能 FIRST_VALUE 或 LAST_VALUE 产品价格(即按 order_id 划分)。

***SELECT order_id, name, product_id, price, 
FIRST_VALUE(price) OVER (PARTITION BY order_id) AS FIRST_VALUE_Product_Price,
LAST_VALUE(price) OVER (PARTITION BY order_id) AS LAST_VALUE_Product_Price
FROM retails***

输出-查询-第一个值()或最后一个值()

解释:

  • 正如我们所看到的,First_Value()和 Last_Value()列分别给出第一行值和最后一行值。

c .第 n 个值():

语法:

***NTH_VALUE (expression, nth_value ) 
      OVER ( [PARTITION BY expression_list ] [ ORDER BY order_list ][ row_or_range_clause ] )***
  • NTH_VALUE 返回分别用表达式分配给窗框(分区)的第 n 个值。
  • 如果 nth_value 不可用,则使用 NULL 对其进行估算。

举例:

查询:在每个订单内增加新功能 n _ VALUE 产品价格(即按 order_id 划分)。

***SELECT order_id, name, product_id, price, 
NTH_VALUE(price,3) OVER (PARTITION BY order_id) AS NTH_VALUE_Product_Price
FROM retails***

输出-查询-第 n 个值()

解说:

  • 在查询中,我们将第 n 个值指定为 3,它返回第 3 行值,该值按 order_id 进行分区。
  • 对于 order_id 1111,只有 2 种产品,因此该列被估算为空值。

结论:

在这篇博客中,我试图解释 SQL 中窗口函数的主要类型。

窗口函数在使用 SQL 进行数据分析时非常有用,并且易于使用。

窗口函数围绕 OVER、PARTITION BY 和 ROW 或 RANGE 子句。

我希望你喜欢这个博客,欢迎随时加入我的 LinkedIn。我很想知道你对这篇文章的想法和反馈。

感谢您的阅读。

资源:

数据集和 SQL 脚本可从 GITHUB 获得。

参考文献:

  1. postgresql.org(2019 年)。 PostgreSQL:文档:9.1:窗口函数。[在线]Postgresql.org。可在:https://www.postgresql.org/docs/9.1/tutorial-window.html.
  2. drill.apache.org(未标出)。 SQL 窗口函数介绍— Apache Drill 。[在线]drill.apache.org。可在:https://drill . Apache . org/docs/SQL-window-functions-introduction/获取。

SQL 窗口函数:直观指南

原文:https://towardsdatascience.com/sql-window-functions-the-intuitive-guide-5b56d7f437cb?source=collection_archive---------15-----------------------

使用 Postgres 直观地了解窗口函数的不同组成部分,并在您的数据工作流中实现它们

巴勃罗·菲耶罗在 Unsplash 拍摄的照片

介绍

主要目标是理解窗口函数的基本概念,并将它们应用到 SQL 工作流中。窗口函数只不过是 FOPO(函数在分区上的作用)。下面是这篇文章将涉及的内容的简要概述。

  • 分组依据
  • 窗口功能— 4 个主要组件(FOPO)
  • ()上的 func()
  • 该分区由
  • 订单依据
  • 分区依据和排序依据
  • 摘要
  • 附录

分组依据

如果您对 GROUP BY 子句有很好的理解,那么您已经开始理解窗口函数了。简而言之,GROUP BY 子句将表压缩成较少的行或输出唯一的行。当按列分组时,SQL 查询为该列下找到的每个不同值输出一行。在对多个列进行分组时,输出将由指定列的唯一组合组成,这些列有或没有一些聚合函数。这里有一个例子。

GROUP BY 1 中的 1 表示分组列在 SELECT 下的位置。SELECT 下面列出的列就像一个从索引位置 1 开始的数组。

在这个 product_orders 表中,每个 order_id 都附加到客户购买的所有产品项目(product_name)。

  • 首先,order_id 被指定为 GROUP BY 列。
  • 其次,使用 SUM 聚合函数将对应于 order_id 的金额求和为一个值(total_amount)。

现在,让我们更深入地研究一下窗口函数的机制。

窗口功能— 4 个主要组件(FOPO)

  • 首先,函数组件决定数据的处理。例如,我们可以基于列进行分级(例如,DENSE_RANK)或创建大小相等的箱(例如,NTILE)。点击查看 Postgres 中可用的窗口函数。聚合函数(例如,求和、计数、AVG 等。)也可以用作窗口函数—这里列出了
  • 其次,OVER 子句是将所有内容粘合在一起的粘合剂。使用 OVER 子句调用或启用上面的函数来创建窗口函数。
  • 第三,与 GROUP BY 类似,PARTITION BY 类似于 subgroup-by 子句。PARTITION BY 不是折叠数据集,而是基于指定的一列或多列创建子组/块/分区。
  • 最后,ORDER BY 子句再次根据指定的一列或多列对数据进行排序。

根据转换数据所需的处理级别,除了 OVER 子句之外,并不是所有组件都需要使用。(记住:OVER 子句调用窗口函数,使聚合函数成为窗口函数。)下面是使用案例。

  • func() OVER()
  • func( ) OVER()
  • func() OVER(由划分)
  • func( ) OVER(由划分)
  • func() OVER(按划分,按排序)
  • func( ) OVER(分区按排序按)

()上的 func()

未定义 PARTITION BY 和/或 ORDER BY 的 OVER()方法将函数应用于整个数据集。这种方法与聚合函数配合得很好。当应用函数使用所有行计算值时,表或数据集的完整性得到维护。

在 orders 表中,每一行都是唯一的 order_id(主键)。出于演示的目的,我在这个表中又添加了几个订单。

在此示例中,三个聚合函数与 OVER 子句成对出现,以创建三个窗口函数,分别由 total_amount、total_count 和 total_mean 表示。例如,total_amount 对“金额”列下的所有值求和,新的总计出现在每一行中。同样的逻辑也适用于 total_count 和 total_mean。此设置有助于计算总量的百分比,并根据总量或平均值等统计数据创建比率。

因此,窗口函数的值有助于创建标准化的指标,以便在数据集之间进行比较。第一个灰色突出显示的块显示了下面提供的示例中的聚合值,而第二个块包含描述数据的指标。

该分区由

正如我前面提到的,PARTITION BY 类似于 GROUP BY。考虑划分的最佳方式是分组(组中的组)。与 GROUP BY 类似,首先通过一列或多列来定义分区。一旦定义了子组,OVER 子句将调用或启用函数在每个子组上执行其逻辑。与 GROUP BY 不同,PARTITION BY 子句不压缩表;相反,它维护表的完整性,同时将输出作为列添加。

例如,使用 product_orders 表,分区依据列是 order_id。此外,子组的数量由 order_id 的唯一数量定义。在这种情况下,这个数字是三。

  • NTILE:NTILE(3)等于 bin 的数量,PARTITION BY 确定子组(或分区),其中每个子组将被分成三个 bin。
  • FIRST_VALUE:该函数输出定义的子组中的第一个值或第一行。
  • 计数:计算每个定义的子组中的行数。
  • SUM:对每个子组中的值(如金额)求和。
  • AVG:计算每个定义的子组的平均值。

在输出中,下面突出显示的灰色块表示由 PARTITION BY 子句定义的子组和由窗口函数创建的新列。

订单依据

ORDER BY 子句在对数据进行排序时使用。它可以是窗口函数中的独立子句,也可以与 PARTITION BY 子句成对出现。有时,只使用 ORDER BY 子句会更合适,这样可以对整个数据集进行排序。这里有一个例子。

在排名函数中使用 ORDER BY 子句会根据 num_product_items 和 amount 列中的值生成一个有序视图。排名功能之间的差异显示在下面橙色突出显示的区域中。只是提醒一下,ORDER BY 子句设置为 order by ascending (ASC)。对于降序,必须用 DESC 表示(例如,ORDER BY amount DESC)。

  • ROW_NUMBER():该函数按升序或降序顺序排列,而不考虑列中的重复值。
  • RANK():该函数按升序或降序顺序排列,但当有重复值时会有间隙。例如,在 num_product_items 下,数字 1 出现了两次;因此,该函数将两者的等级都定为 1(在 rank_by_items 下)。接下来,num_product_items 下出现了两次 2,该函数将这些列为 3。该函数跳过了第 2 级,因为第二次出现的 1 已经捕获了第 2 级位置。此外,因为第二次出现的 3 已经占据了第 4 个等级位置,所以下一个等级从 5 开始。
  • DENSE_RANK():这个函数类似于 RANK()函数,但是不跳过或创建间隙。与 ROW_NUMBER()不同,在 ROW _ NUMBER()中,每一行都被指定了一个唯一的排名,而不考虑重复值,DENSE_RANK()将对没有间隔的重复值应用相同的排名。

分区依据和排序依据

在分别研究了 PARTITION BY 和 ORDER BY 的机制之后,下一步是将这两个组件配对。PARTITION BY 基于所选的一列或一组列创建子组(或分区)。ORDER BY 按升序或降序组织数据。我将使用 product_orders 表来说明这些组件的配对。

SELECT * FROM 产品订单;这个表由三个 order _ ids 和一个购买产品的列表组成。

在这个例子中,我使用九个窗口函数来演示 PARTITION BY 和 ORDER BY pairing。在这个阶段,如果您对每个组件都有很好的理解,那么这应该很简单。Postgres 的每个窗口函数的描述可在这里找到。

在这个 SQL 查询中,所有窗口函数都在 PARTITION BY 子句中使用 order_id。唯一 order_id 的数量等于子组的数量,ORDER BY 列对每个子组的数据进行排序。

前面没有讨论的一个项目是在 FIRST_VALUE()、LAST_VALUE()和 NTH_VALUE()函数中使用“无界前导和无界跟随之间的范围”子句。需要此子句来定义子组的第一行和最后一行。这个子句没有什么特别的,除了必须包含它来设置窗口函数的框架。

摘要

关键的一点是,窗口函数是现有 SQL 概念的融合,捆绑在一起以创建一种不同的方式来分割数据。使用窗口函数有明显的好处,与花时间创建自己的自定义函数相比,它允许您快速输出转换后的数据。

我认为窗口函数是 FOPO(函数在分区上的排序)。

  • 首先,选择一个最适合你的用例的函数。
  • 其次,记住 OVER 子句调用窗口函数(例如 DENSE_RANK()、LAG()等)。)并使聚合函数成为窗口函数。
  • 第三,PARTITION BY 子句定义了子组(或分区),即一个组中的一个组,其中所选函数贯穿每个分区。
  • 最后,ORDER BY 子句根据一列或一组列对每个分区中的数据进行排序。

在附录中,我分享了用于创建表和数据的 SQL 语句。最好的学习方法是练习,所以你可以自己深入学习。您已经掌握了基础知识,接下来就是根据您的用例将这些知识组合起来。感谢您的阅读,祝您查询愉快!

[## PostgreSQL:文档:9.3:窗口函数

本文档适用于不受支持的 PostgreSQL 版本。您可能希望查看当前的同一页面…

www.postgresql.org](https://www.postgresql.org/docs/9.3/functions-window.html)

附录

如果您的计算机上已经安装了 Postgres,请运行这些 CREATE TABLE 和 INSERT 语句,并运行我之前分享的 SQL 查询。如需下载,请前往此页面

使用 INSERT 语句为 orders 和 product_orders 创建表语句。

SQLite 3:使用 Python 的 SQLite 3 模块保存程序数据

原文:https://towardsdatascience.com/sqlite-3-using-pythons-sqlite-3-module-to-save-program-data-bc6b34dcc721?source=collection_archive---------22-----------------------

UnsplashAltumCode 的照片

“数据是网络运行的燃料,因此作为开发人员,我们绝对有必要以高效的方式处理和存储数据。”

当您在处理一个需要大量用户输入并存储各种用户数据的项目时,拥有一个好的数据库来管理用户数据并能够以高效的方式进行管理就变得至关重要。

虽然程序数据和实例通常以文件的形式存储,但是当您处理大量数据并且需要能够创建、检索、更新和删除(CRUD)这样的数据时,这不是存储数据的最有效方式,这对于文件的使用来说不是很方便。

这就是 SQLite 3 的用武之地,它是一种快速、轻便、简单的方式来存储即使是最复杂形式的数据,并且它以高效的方式执行 CRUD 操作。

您可以在下面找到 SQLite 3 模块的文档;

[## sqlite 数据库的 sqlite3 - DB-API 2.0 接口- Python 3.9.0 文档

源代码:Lib/sqlite3/ SQLite 是一个 C 库,它提供了一个轻量级的基于磁盘的数据库,不需要…

docs.python.org](https://docs.python.org/3/library/sqlite3.html)

我们将使用 SQLite 3 数据库创建一个基本的登录功能,其中我们将涵盖所有 CRUD 操作,即创建用户、检索用户数据、更新用户数据和删除用户数据。

让我们开始吧。

由于 SQLite 3 是一个内置模块,我们可以直接导入它,而无需安装任何东西。

import sqlite3

现在让我们开始设置我们的数据库;

因此,为了创建一个基本的登录程序,我们至少需要用户的两个输入,用户名和密码。为此,我们将创建一个包含两列的表,

就是这样,这就是创建一个数据库所要做的一切。看看有多快,现在让我们继续创建一个用户。

创造

我们从用户那里获得两个输入,用户名和密码,然后将它们提交到我们的表中。

游标功能帮助我们遍历数据库,并帮助我们执行 CRUD 功能。

恢复

如果我们想要检查用户是否存在,以及输入的密码是否与存储在数据库中的密码匹配,我们需要能够检索我们刚刚创建的用户数据。

光标遍历数据库,找到与我们输入的参数相匹配的条目。

在 SQLite 3 中,我们可以通过两种方式从数据库中获取数据,

fetchone():这个函数从我们的表中获取第一行,该行与传入的参数相匹配。

fetchall()该函数获取所有符合我们设置的参数的行。

我们使用了fetchone(),因为理想情况下不应该有多个相同用户名的条目。

cur.fetchone()[1]检索给定用户 as 的密码,密码在我们的表的第 2 列。

更新

现在,如果我们的用户想要更改他的密码,我们需要能够处理这一点,并做到这一点,我们需要能够更新用户的凭证。

要更新密码,我们只需要一个输入,即用户名。

删除

我们需要能够删除任何我们不再需要的旧数据,并避免重复,

结论

在不到 15 行代码中,SQLite 3 使我们能够轻松创建一个强大、快速和动态的数据库。

现在,在您的下一个 Python 项目中使用 SQLite 3,感受它的强大。

SQLite 数据库设置和查询

原文:https://towardsdatascience.com/sqlite-database-setup-and-querying-cea0520272c?source=collection_archive---------36-----------------------

SQLite 数据库浏览器。以下是如何从您自己的本地数据库中进行查询。

Grzegorz Walczak 在Unsplash【1】上拍摄的照片。

目录

  1. 介绍
  2. SQLite
  3. 设置
  4. 例子
  5. 摘要
  6. 参考

介绍

你有没有想过建立一个本地数据库并进行查询?如果答案是肯定的,那就继续读下去。无论您是想为即将到来的面试练习您的 SQL 技能,还是想为您当前的职位研究不同的查询技术,或者可能开办自己的公司并存放您的测试数据,DB Browser for SQLite 都适合您。

一旦建立了包含表的数据库。然后,您可以运行查询,这些查询最终将作为您的机器学习模型的数据集。除了在你的 Jupyter 笔记本中使用 Python 和 pandas,你还可以使用这个工具进行探索性的数据分析。此外,您可以获得可作为模型特征的度量。

下面,我将讨论 DB Browser for SQLite、设置和示例,这样到本文结束时,您就可以使用自己的本地数据库了。

SQLite

DB Browser for SQLite【2】是一个工具,您可以在其中可视化您创建、编辑和查询的数据库和表格。它是开源的,被一些人用来开始他们的 SQL 之旅。虽然其他公司有几个平台,但是这个界面很容易使用和设置。下面,我将展示如何设置您的第一个数据库及其相应的表。

设置

文件—用于安装的新数据库。作者截图[3]。

一旦你下载了这个工具,你可以很容易地创建一个新的数据库,如上面的截图所示。根据我的经验,我创建了两个名为 accountsnames 的测试表,作为示例。为了设置特定的表格,可以从 CSV 文件中提取数据。在上面的同一个屏幕截图中,您可以点击导入选项,然后点击来自 CSV 文件… 的“表格”——您也可以选择使用来自 SQL 文件… 的“数据库”。导入 CSV 文件很容易,只需几秒钟就可以加载—我建议您第一次使用该工具时使用该选项。

修改表格。作者截图[4]。

一旦将数据导入表中,就可以修改表,从而轻松地更改字段或列的类型。该部分将在数据库结构菜单中。这些选项包括:

  • 整数
  • 文本
  • 一滴
  • 真实的
  • 数字的

字段。作者截图[5]。

在上面的屏幕截图中,您将看到您可以编辑的字段—另一个短语是'编辑表定义'。最上面的部分(类型)本质上是可视化您也可以用 SQL 代码(模式)做什么——对大多数人来说更容易,但是两个选项都是可用的。

现在令人兴奋的部分实际上是查询您先前创建的那些表。下一节将展示并描述如何使用 SQL 返回探索性数据分析和数据科学模型数据集的结果。

例子

执行 SQL。作者截图[6]。

整个过程中令人满意的部分是能够使用 SQL 进行查询。在上面的屏幕截图中,您可以看到这个简单的查询,它选择了 accounts 表中 amount 大于 200 的所有字段。只返回了一行,这意味着只有 account_id 满足这个条件。这种类型的查询类似于 SQL 的其他子类型,只是有一些小的不同。

连接查询。作者截图[7]。

就像在其他 SQL 平台中一样,您可以执行更复杂的查询,比如连接和 where 子句。上面的截图可视化的选择了三个字段: a.account_ida.namen.location 。单个字母表示相应表的全名,也称为别名。然后,我在表上执行了一个左连接,以检索在 账户 表上有匹配的结果。接下来,我想只查看位置在美国(US)或澳大利亚(AUS)的结果。总的来说,我能够将两个表合并在一起,以便通过建立特定的条件来检索我想要的信息。

摘要

虽然这些查询并不特别复杂,但是它们是为了直观地描述使用这个有用的工具可以执行的查询类型。我在设置本教程的过程中得到了很多乐趣,同时提供了一些关于该平台常见部分的有用说明。虽然我已经介绍了一些,但是还有很多其他的技巧和窍门可以使用。原始参考资料中有更多关于 DB Browser for SQLite 的示例和文档。

总之,使用此工具,您可以为探索性数据分析或数据科学模型数据集创建本地数据库、导入数据、修改表和执行查询。

我希望你觉得这篇文章有趣而且有用。感谢您的阅读!

参考

[1]照片由 Grzegorz WalczakUnsplash(2017)上拍摄

[2] SQLite,用于 SQLite 的 DB 浏览器,(2020)

[3] M.Przybyla,SQLite 设置的数据库浏览器的屏幕截图,(2020 年)

[4] M.Przybyla,修改表截屏,(2020 年)

[5] M.Przybyla,编辑表格定义的屏幕截图,(2020)

[6] M.Przybyla,执行 SQL 的截图,(2020)

[7] M.Przybyla,左连接截图,(2020)

SQLZoo:练习 SQL 的最佳方式

原文:https://towardsdatascience.com/sqlzoo-the-best-way-to-practice-sql-66b7ccb1f17a?source=collection_archive---------11-----------------------

用解决方案测试你的技能的狂野游乐场

说出那些 SQL 动物的名字!提示:文章中提到了它们。原图片来源:尼古拉·乔切夫

对于许多角色来说,SQL 是一项有用的技能。无论哪个行业,都会有数据存储在数据库中,而 SQL 是获取这些数据的最佳方式。尤其是数据科学家,需要成为快速访问高质量数据的专家。虽然我们中的大多数人在技术方面都有不错的基础知识,但我们可能缺乏在日常工作中进一步发展这些技能的机会。

进来了SQLZoo——一个测试你的技能和重建生锈的技能的好地方。你可以用它来准备面试,或者在工作中保持敏锐,给你的老板留下深刻印象。在这里,我将介绍 SQLZoo 和为什么您应该检查它,以及一个有用的链接到一些 SQLZoo 的答案,以进行双重检查!

SQLZoo 是一个完善的在线平台(自 1999 年以来),用于编写和运行针对实时数据库的 SQL 查询。这意味着您可以看到查询的实际结果,而不必一丝不苟地检查您的查询是否与解决方案匹配——结果才是最重要的。这一点很重要,因为通常有许多解决难题的方法,其中一种不一定是最好的。

他们有教育部分,但你要找的是“评估”。这些包含更复杂的例子,允许您以不同的难度深入到数据库中。我最喜欢的问题是“白色圣诞节挑战”,这也是一次学习著名的“白色圣诞节”历史的好机会。其他好的是服务台和客房,有详细的图表解释数据库以及一些更具挑战性的问题。

在某些时候,你可能想检查你的 SQL 看起来不错,为此,你可以在 Github 上查看我对的一些问题的解决方案(鼓励投稿!).编写高质量的 SQL 查询并不简单,因为您需要考虑可读性、速度、效率、健壮性——所有这些对业务都很重要。当你尝试解决这些问题的时候,想想你可以用其他方法来解决它们。更简洁的写法是什么?你怎么能更有效率呢?如果某些列包含空值,会发生什么情况?

值得注意的是,SQLZoo 是用 MariaDB 服务器构建的,默认情况下需要使用 MySQL。对于像我这样主要使用 BigQuery 的 StandardSQL 或 PostgreSQL 的人来说,要使用另一种语言,需要遵循一组命令。或者,这可能是一个练习其他技术的好机会,在使用您经常使用的 SQL 变体时,您可能不会想到这些技术。

最后,还有其他提供类似服务的平台。一个小列表:

  • w3resource —另一个编写查询的免费资源。
  • SQL 谋杀之谜——另一个我最喜欢的游戏,因为它有趣的互动环境让你感觉像一个顶级特工。
  • 面试查询 —一个专门给数据科学家练习 SQL 的平台。如果你是认真的,值得一查,但这是一项有偿服务。
  • test dome——另一个面试实践平台。

为了练习你的通用编码技能,有许多很棒的现代平台,比如 Leetcode,但是 SQL 是一项不太受欢迎的技能。使用 SQLZoo 来练习、测试和提高您的技能,让您的 SQL 更上一层楼。

PS。让我知道你是否发现了任何大的问题或者找到了一些更好的解决方案——快乐 SQLing!

使用 OpenCV 和 Tensorflow 的深蹲检测器

原文:https://towardsdatascience.com/squats-detector-with-opencv-and-tensorflow-ce934f19aeb9?source=collection_archive---------35-----------------------

体育技术中的人工智能

在隔离期间,我们的身体活动有限,这并不好,尤其是对孩子们来说。

但当我让我的孩子锻炼时,我遇到了阻力,不得不全神贯注地控制整个过程。

这很有趣,而且我有了一个自动化这个过程的想法。虽然在这种情况下有点矫枉过正,但灵感却是不可抗拒的。

考虑到起点,我选择了蹲起。一个具有明确阶段和大幅度的基本动作看起来是最好的竞争者。

数据收集

带相机的 Raspberry Pi 非常方便,可以轻松地将照片带回家。

OpenCV 获取图像并将它们写入文件系统。

动作识别

最初,我打算用图像分割在图片上找到一个人。但是分割是一个相当繁重的操作,尤其是在资源有限的情况下。

此外,分割忽略了一个事实,我们有一系列的帧,而不是一个单一的图片。该序列具有明显的特征,我们需要使用它。

所以我继续使用 OpenCV 的背景去除算法。将这种方法与一些试探法结合起来最终提供了一个可靠的结果。

背景减法

首先,创建一个背景减法器:

backSub = cv.createBackgroundSubtractorMOG2()

用框架来填充它:

mask = backSub.apply(frame)

最后得到一张有身体轮廓的图:

然后放大图像以突出轮廓。

mask = cv.dilate(mask, None, 3)

将这种算法应用到所有的帧中会产生姿态遮罩。然后我们要把它们归类为站着,蹲着,或者什么都不做。

下一步是从图片上剪下一个图形。OpenCV 可以找到轮廓:

cnts, _ = cv.findContours(img, cv.RETR_CCOMP, cv.CHAIN_APPROX_SIMPLE)

这个想法是最大的轮廓或多或少符合身材。

不幸的是,结果是不稳定的,最大的轮廓可能只包裹身体,但错过了腿。

无论如何,有一系列的图像很有帮助。蹲起发生在同一个地方,所以我们可以假设,所有的动作都在某个区域内进行,而这个区域是稳定的。

然后,可以迭代地构建边界矩形,如果需要的话,随着最大轮廓而增加。

有一个例子:

  • 最大的轮廓是红色的
  • 包围矩形的轮廓是蓝色的
  • 包围矩形的图形是绿色的

使用这种方法,我们可以得到一个姿态进行进一步的处理。

分类

然后,从图像中剪切出包围矩形,制成正方形,并统一为 64×64 的大小。

分类器输入有多个掩码:

对于展台:

对于深蹲:

我使用 Keras + Tensorflow 进行分类。

最初,我从经典的 Lenet-5 型号开始。它工作得很好,在读了一篇关于 Lenet-5 变体的文章后,我决定尝试简化架构。

结果,一个非常简单的 CNN 显示了几乎相同的准确性:

model = Sequential([
        Convolution2D(8,(5,5), activation='relu', input_shape=input_shape),
        MaxPooling2D(),
        Flatten(),
        Dense(512, activation='relu'),
        Dense(3, activation='softmax')
      ])model.compile(loss="categorical_crossentropy", optimizer=SGD(lr=0.01), metrics=["accuracy"])

10 个时期的准确率为 86%,20 个时期的准确率为 94%,30 个时期的准确率为 96%。
长时间的训练可能会导致过度适应,所以是时候在现实生活中尝试这种模式了。

树莓派

我是 OpenCV-DNN 模块的忠实粉丝,为了避免 Tensorflow 繁重的设置,我打算使用它。

不幸的是,当我将 Keras 模型转换为 TF 并在 Raspberry 上运行时:

cv2.error: OpenCV(4.2.0) C:\projects\opencv-python\opencv\modules\dnn\src\dnn.cpp:562: error: (-2:Unspecified error) Can't create layer "flatten_1/Shape" of type "Shape" in function 'cv::dnn::dnn4_v20191202::LayerData::getLayerInstance'

这是一个关于堆栈溢出的已知问题,但是补丁还没有发布。

所以除了张量流别无选择。

Google 已经为 Raspberry 支持 TF 好几年了,所以没有任何技巧可以让它工作。

TF 包含适用于 Keras 型号的适配器,无需转换。

加载模型:

with  open(MODEL_JSON, 'r') as f:
  model_data = f.read()
  model = tf.keras.models.model_from_json(model_data)
  model.load_weights(MODEL_H5)
  graph = tf.get_default_graph()

并对蹲式面罩进行分类:

img = cv.imread(path + f, cv.IMREAD_GRAYSCALE)
img = np.reshape(img,[1,64,64,1])
with graph.as_default():
  c = model.predict_classes(img)
  return c[0] if c else None

在 Raspberry 上,输入为 64x64 的分类调用大约需要 60–70 毫秒,接近实时。

树莓 app

将上述所有部分整合成一个单一应用:

让我们使用 Flask 创建一个具有以下条目的服务:

  • 获取/ —一个应用程序页面(更多信息见下文)
  • 获取/状态 —获取当前状态、深蹲和帧数
  • 发布/开始 —开始一项练习
  • POST /stop —完成练习
  • 获取/传输 —来自摄像机的视频流

我在服务启动时初始化了 Tensorflow。这通常是一个坏主意,尤其是在 Raspberry 上——TF 将消耗大量资源,服务将响应缓慢,并可能在达到极限时死亡。

所以通常我会在一个单独的进程中启动 TF,并为进程间通信提供一个通道,但是我为这个原型使用了一个简单的方法。

已经有提到的网络应用程序来控制蹲起活动。该应用程序可以:

  • 显示来自摄像机的实时视频
  • 开始/停止练习
  • 计算深蹲和框架

当一个练习开始时,服务将图片写入文件系统。

让它们训练神经网络是很方便的,但通常不需要它们。

该服务处理一系列图片,用 TF 对它们进行分类,当符合站立-蹲-站立模式时,蹲计数器增加。

标签工具

有一个简单的贴标工具用于人工分类。这是一个用 python + OpenCV 的 GUI 应用程序。

该工具显示带有主要轮廓和边界矩形的图片,并需要键:S(站立),Q(蹲下),N(无),然后自动将图片移动到目标子文件夹中。

然后,应该将带标签的子文件夹复制到 Keras 模型输入文件夹中,并且需要重复训练过程。

平台

我在 Raspberry 上运行这个应用程序,但没有什么能阻止我使用任何带有 python、OpenCV 和摄像头的 Linux 环境。

问题

事实上,它可以被接受为 MVP,但是还有很多需要改进的地方。

  • 细化背景去除。阴影会产生嘈杂的斑点,使分类器变得模糊不清。
  • 为神经网络收集更多数据。
  • 回顾分类器架构。最简单的方法现在显示出令人满意的结果,但是有它自己的局限性。

链接

压缩和激励网络

原文:https://towardsdatascience.com/squeeze-and-excitation-networks-fb91e7f64096?source=collection_archive---------36-----------------------

提高 CNN 性能的频道自我关注

悉尼港上空的烟火(维基百科,CC by 2.0)

这篇文章描述了挤压和激励模块,这是一种可以插入卷积神经网络以提高性能的架构单元,只需增加少量的参数。挤压和激励模块明确地对渠道关系和渠道相互依赖性进行建模,并包括一种对渠道的自我关注形式。

这篇文章的主要参考是原始论文,它被引用了 2500 多次:

、、萨缪尔·阿尔巴尼、和吴。"挤压和激励网络."CVPR 2018。

概述

挤压和激励(SE)模块旨在提高卷积神经网络表示的质量。卷积神经网络(CNN)的回顾可在这里获得。

对于卷积神经网络的任何层,我们可以构建相应的 SE 块来重新校准特征映射:

  • 在“挤压”步骤中,我们使用全局平均池来聚合跨越其空间维度 H x W 的特征地图,以产生通道描述符。
  • 在“激励”步骤中,我们将完全连接的层应用于“挤压”步骤的输出,以产生每通道权重(“激活”)的集合,该集合被应用于特征映射,以生成 SE 块的最终输出。

论文中的图 1 描述了一个 SE 模块:

论文批注&多通道 2D 卷积复习

我们的输入是一个图像 X ,它有 s 个通道:

我们定义一个卷积层 F _ tr ,它由滤波器 V 组成:

我们将由滤波器 V 组成的卷积层 F _ tr 应用于我们的输入图像 X

为了阐明符号以及输入图像 X 和学习的卷积滤波器组 V 之间的关系,这里快速回顾一下多通道输入图像的 2D 卷积,在这种情况下是 3 通道 RGB 输入图像。

变换 F _ tr 包括多个卷积滤波器V=【V _1、 v_ 2、…、 v _C】。3 通道图像的“2D 卷积”的一个过滤器实际上是三维的,正如我们从这个动画中可以看到的,其中滑动的白色轮廓表示单个过滤器,例如 v _1:

马丁·戈尔纳创作的动画。原文链接。此处也可用

考虑 RGB 图像 X ,在下图中显示了两次——一次在顶部覆盖了淡橙色滤镜 v _1,一次在底部覆盖了紫色滤镜 v _2:

从上图中我们可以看到,单个滤镜是三维的,为 x 的 3 个通道测量 k x k x 3,在这个例子中, V 包括两个滤镜,淡橙色滤镜 v _1 和紫色滤镜 v _2。

在本文的符号中,上标 s 用于跟踪输入 X 的通道(本例中 s=1,2,3),而下标 c 用于跟踪 V 中的滤波器(本例中 c=1,2):

以下是详细标注的过滤器:

通过将滤波器 V 应用于输入图像 X ,我们获得输出滤波器图 U :

单一输出滤波器映射,例如 u _1(下标 c=1),通过所有通道(即通过上标 s=1、s=2 和 s=3)求和产生。以这种方式只能捕获隐含的渠道关系。引用作者的话,

由于输出是通过所有通道求和产生的,通道相关性隐含在v_ c 中,但是与滤波器捕获的局部空间相关性纠缠在一起。由卷积建模的信道关系固有地是隐含的和局部的(除了在最顶层的那些)。我们期望卷积特征的学习通过显式建模信道相互依赖性[即,使用 SE 块]来增强。

(注:在本文之后,为了简化符号,省略了偏差术语。)

现在,我们已经回顾了论文的符号和 2D 多通道卷积,这里有一个挤压和激发块如何工作的解释。它们出奇的简单,只需要全局平均池和完全连接的层。

挤压:全局信息嵌入

SE 块中的“挤压”步骤将全局空间信息挤压到通道描述符中。挤压步骤包括跨空间维度 H x W 的全局平均汇集,以产生逐通道统计。以下是这篇论文的摘录,其中包含挤压步骤的描述和等式:

下图说明了压缩操作,该操作将每个不同的 H x W 特征图 u _c 缩减为标量通道描述符 z _c:

标量[ z _1、 z _2、…、 z _C]一起形成一个长度为 C 的向量 z ,该向量将在激励步骤中使用。请注意, z 捕获全局信息,因为 z 的每个元素是由全特征图高度 H 和全特征图宽度 W 的集合产生的。

激励:自适应重新校准

激励操作旨在完全捕捉通道间的相关性。激励操作处理挤压步骤的输出(矢量 z )以产生激活矢量 s,,然后用于重新缩放特征图。(此激活向量 s 不要与之前用于跟踪输入 X 通道的 s 相混淆)。

向量 s 是从挤压输出 z 中计算出来的,使用两个完全连接的层,瓶颈将表示缩小到大小 C/r:

超参数 r 被称为“减速比”如果 r 越大,则中间表示越小。将表示的大小减小到 C/r 然后将其扩展回 C 的目标是(a)限制模型的复杂性和(b)帮助一般化。正如作者所说,缩减率 r “允许我们改变网络中 SE 块的容量和计算成本。”

以下是激励步骤第一部分的草图,其中从挤压输出 z 计算出 s 中的激活:

一旦 s 被计算,则 s 的元素被用于重新缩放 U 的特征图,以获得 SE 块的最终输出,称为X-波浪号:

这是最后一步的草图:

从草图中可以看出,激活 s 1 必须平铺在HxWmapu 1 上,以便 s _1 元素乘上 u _1 中的所有值,生成 x -tilde_1。

重新校准的特征地图[x-波浪号 _1、x-波浪号 _2、…、x-波浪号 _C]的堆栈然后继续通过 CNN 的其余部分,因为它们与原始特征地图[ u _1、 u _2、…、 u _C]的维度完全相同。

因此,SE 块给 CNN 增加了一种自我关注的形式:

SE 模块本质上引入了以输入为条件的动态,这可被视为通道上的自关注功能,其关系不限于卷积滤波器所响应的局部感受野。

实施

挤压和激励模块可以插入任何 CNN 架构。文章中的图 3 说明了如何在 ResNet 中使用 se 块:

SE 块不会增加模型的计算复杂度太多。在以下比较中,“SE-ResNet-50”是指添加了 SE 模块的 ResNet-50 型号,而“vanilla ResNet-50”是指没有任何 SE 模块的基准 ResNet-50:

  • SE-ResNet-50 需要约 3.87 GFLOPs,而 vanilla ResNet-50 需要约 3.86 GFLOPs,相对增加了 0.26%(对于 224 x 224 像素输入图像的单次向前传递);
  • SE-ResNet-50 来回一次需要 209 毫秒,而 vanilla ResNet-50 需要 190 毫秒,相对增加了 10%(在具有 8 个 NVIDIA Titan X GPUs 的服务器上,256 个图像的训练小批量的时间);
  • SE-ResNet-50 需要大约 2750 万个参数,而 vanilla ResNet-50 需要大约 2500 万个参数,相对增加了 10%。实际上,这些参数中的大部分来自网络的最后一级,在那里激励操作在最大数量的通道上执行。[……]这种相对昂贵的 SE 模块的最后一级可以以很小的性能成本去除(<0.1% top-5 error on ImageNet) reducing the relative parameter increase to ~4%”

Here’s an example implementation of an SE block (来源):

*def se_block(in_block, ch, ratio=16):
     x = GlobalAveragePooling2D()(in_block)
     x = Dense(ch//ratio, activation=’relu’)(x)
     x = Dense(ch, activation=’sigmoid’)(x)
     return multiply()([in_block, x])*

结果

作者将 SE 块添加到 ResNet 架构、ResNeXt 架构、VGG-16 和 Inception 架构,并表明 SE 块的添加提高了 ImageNet 分类的性能。性能改进显示在表 2 中 SENet 列中每个条目旁边的括号中:

结果部分的其他要点:

  • SE 块还提高了 CIFAR-10 和 CIFAR-100 数据集、用于场景分类的 Places365-Challenge 数据集以及用于对象检测的 COCO 数据集的模型性能。
  • 对于减速比 r 的不同值的范围,性能是稳定的。“增加的复杂性不会单调地提高性能,而较小的比率会显著增加模型的参数大小。设置 r= 16 在准确性和复杂性之间取得了很好的平衡。”
  • 对于 squeeze 操作符,全局平均池的性能略好于全局最大池。
  • 激励算子内部有一个 ReLU,最终的非线性是一个 s 形。用 ReLU 或 Tanh(而不是 sigmoid)代替激励算子的最终非线性会降低性能。

总结

  • 挤压和激励模块明确模拟渠道关系和渠道相互依赖性,并包括一种渠道自我关注的形式;
  • 挤压和激励块使用全局平均池的“挤压”操作,继之以使用两个完全连接的层的“激励”操作,来重新校准特征图;
  • 挤压和激励模块可以插入到任何 CNN 架构中,并且需要最小的计算开销;
  • 挤压和激励模块可以提高分类和目标检测任务的性能。

原载于 2020 年 4 月 4 日 http://glassboxmedicine.com**

挤压这些神经网络

原文:https://towardsdatascience.com/squeeze-that-neural-network-juice-3ce28a2cca75?source=collection_archive---------42-----------------------

制作神经网络(和更有效的网络)的技巧

杰西卡·路易斯在 Unsplash 上的照片

我们都知道神经网络有多贵。训练是一项乏味的任务,需要多次迭代(每次迭代需要几个时期(每个时期需要几个步骤)(您调整过超参数吗?(也许还有一层?(和…)))))((())))))))))))))

…如果您已经开始部署任何东西,那么您也知道云推理不是免费的。模型越大,神经网络从你身上榨取的 A̶m̶a̶z̶o̶n 就越多。

在这篇文章中,i̶̶t̶e̶a̶c̶h̶̶h̶o̶w̶̶t̶o̶̶m̶a̶k̶e̶̶c̶a̶i̶p̶i̶r̶i̶n̶h̶a̶s 我概述了一套技术,你可以探索,使训练更快,生产更小的模型,并最终节省金钱和时间。所有的技巧都是实用的,是你今天可以应用,最多明天就能得到结果的东西。

不要让神经网络压榨你的钱包。把那些重物挤出来。

照片由 Maria das DoresUnsplash 上拍摄

顺便说一句,如果你是为了 caipirinhas 来的,我很抱歉。它需要一个切成八个半月形的酸橙和一汤匙(或三汤匙)糖,放在一个大玻璃杯里碾碎。不多也不少。倒入一杯cachaa,剩下的装满冰块。伏特加也行,但用 caipivodka 代替。

神经网络。聚焦聚焦

你能抽出 2%吗?

假设你有一家公司,它有一个 92%准确的神经网络产品。然而,它使用了大约一亿个参数,并需要两天时间在多 GPU 基础设施上进行训练。进一步改进模型的成本特别高,因为大多数改变要么使它变得更糟,要么根本没有提供任何改进。

如果您可以拥有一个 90%准确的模型,并且占用的资源减少 20-50 倍,这会扼杀您的业务还是让它蓬勃发展?

达到 94%是很难的。这需要大量资源、更大的网络、时间…

达到 90%很容易。如果能做到 92%,做的少就是小事。

目标是:在不低于 90%的情况下,您能简化多少?

当前的文献和实践表明,在保持大部分原始精度不变的情况下,降级模型是相当容易的。有些技术是纯技术性的,而其他技术需要对网络架构稍加改动。在下文中,我首先概述了可以应用于预训练模型的技术,以便您今天就可以获得结果,然后是需要一些再训练的技术,这可能明天就可以完成。

适用于预训练模型的技术

使用半精度:

通常,使用浮点数来训练模型。大多数编码人员都知道“double”类型,这是一种更精确的浮点数,但是知道“half”类型(16 位浮点数)的人却不多。

使用“半”类型可以将内存消耗和计算时间减半。使用半精度训练的神经网络可以用两倍于普通网络的批量来训练,基本上每个时期的时间减半。在推理过程中,半精度模型更容易转移,执行速度更快。此外,许多公司为半精确模型提供特殊的云硬件。

好消息是:你可以将一个预先训练好的模型量化到半精度,它确实有效。 TensorFlow Lite 包可以帮你做这件事。如果您正在从头开始训练您的模型,您可以通过启用半精度模式来从中受益(不要忘记将批量加倍!).

权重修剪

在数百万个参数中,有些很大,有些很小。有许多技术可以发现这些“微小”的权重,并将其从网络中移除。仅修剪就可以去除 10%到 90%的权重,而对总准确度的影响不到 2%。

修剪过的网络压缩得更好,推理时间也更短。你可能会感到惊讶,甚至 MobileNet 可以缩小到原来的一半大小。大型非优化模型,如 InceptionNet,可能会去除高达 85%的原始重量。

好消息是: TensorFlow 有几项权重修剪技术易于使用,也与 Keras 后端兼容。一个社区解决方案是 Keras Surgeon ,它作为一个很好的例子展示了如何在修剪和调整之间交替。

更进一步:大多数修剪工具都去掉了权重,但保持了架构的完整性。更先进的技术可以从卷积中去除整个过滤器,从密集层中去除神经元,从而提高训练速度。Keras 外科医生可以做到这一点。两个有趣的阅读是这个关于修剪过滤器的工作彩票假设

考虑使用优化的“现成”模型

许多公司在他们的管道中使用预先训练的模型,不仅仅是作为主干架构。通常,他们使用“大型”模型,如 VGG-19、InceptionNet 和 ResNet。请考虑改用优化模型。

例如,如果您正在使用 ResNet-151,您可以考虑迁移到 ResNet-50,或者向下迁移到 MobileNetV3 架构。如果你依赖于更快的 R-CNN 架构来进行物体检测,考虑切换它的主干(或者整个东西)。YOLO 和 YOLO 快速是伟大的优化替代品。这可能会为你节省数千美元的推理时间。

明智一点:这些选择中的一些会让你对我们 2%的协议不满。有些会让你下跌 3%,或者 10%。他们会省下多少钱?你会失去多少顾客?改变可能还是有利可图的。

服无期徒刑

快速提醒一下,你可以用 YOLO 或 MobileNet,修剪它,量化到半精度。这些技术并不相互排斥。

需要再培训的技术

改变脊柱

继续最后一个技巧,如果您使用现成的模型作为主干,这是一个时髦的做法,请考虑切换到较小的 ResNet 主干或 MobileNetV3。这对迁移学习很有帮助。

摆脱腰痛。 Keras / Tensorflow 捆绑了很多预先训练好的模型,可以很方便的作为骨干使用。

像 MobileNet 一样

阅读论文的好处是知道其他作者是如何做出惊人之举的。MobileNet 性能的关键是深度方向可分离卷积

简而言之,这个想法是将具有 128 个滤波器的 3 乘 3 卷积分成两个连续的步骤:“空间”卷积,完成“3 乘 3”部分,以及“深度”卷积,完成“128 个滤波器”部分。平均而言,这要快 9 倍,并且使用的参数数量级更少。

好消息是:这和修改一行代码一样简单。对于 Keras 用户,只需将“Conv2D”改为“depthwisecov2d”。所有参数都一样。以我的经验来看,这并没有训练得更快,但是推理和模型大小都得到了极大的改进。

知识蒸馏

如果你已经有了一个工作模型,一个非常有效的修剪技术就是知识提炼。也被称为师生模式。

对整个数据集运行良好的旧模型并保存其预测。这些是“软标签”它们不是典型的一次性编码标签;他们有一些不确定性。现在,在软标签上训练较小的模型,而不是地面事实。这将教会“学生”复制“老师”,而不是试图自己学习。

这看起来很复杂,但其实很简单:你只需要像平常一样运行旧模型,然后处理结果。然后,使用您拥有的相同代码,丢弃数据集并从 pickle 文件中读取基本事实标签。为了避免将“数据集”和“教师”作为两个独立的事物,您也可以将数据集剥离。原来的老师。

多做一点:更全面的方法可能会将真实标签与教师标签进行插值,或者创建一个混合损失函数。除了输出层之外,您还可以“教授”内部层的输出。然而,这让事情变得有点复杂。

对输入进行缩减采样

大多数成功的网络使用 224x224 或 256x256 输入。如果您使用的数量超过这个数量,您可能会浪费资源。少往往就是多

图像是高度相关的。在像素(23,23)处发现的数据与在(24,23)或(23,22)处发现的数据几乎相同。随着分辨率的提高,相关性占主导地位。一张 4K 的脸并不比一张 256x256 的脸更有面子。它的所有毛孔都清晰可见。

除非您正在处理大海捞针的问题,否则请考虑将输入降采样到较低的分辨率。只要你,人类,还能认识到有什么要认识的,模型就没问题。

下采样可以无需训练:如果你的模型是完全卷积的,或者在变成完全连接的网络之前使用全局平均池,你可能会获得下采样的好处,而不必重新训练它。

对输出进行缩减采样

如果您正在处理图像到图像的问题,尤其是图像分割,分割很可能在较低的分辨率下完成。

假设您正在制作 256x256 分割图。如果你重新训练你的模型做 128x128,它的性能可能会上升。然后,你只需要将其放大到 256x256,并重新评估实际的影响。除了保留初始卷积和最终反卷积,您很有可能保持大约 2%的原始精度。

这里要注意的是,尽管整体形状是正确的,但分割边界通常是嘈杂和错误的。以较低的分辨率+传统的放大是获得更平滑结果的简单方法,同时也节省了计算时间。

照片由改善 NguyễnUnsplash 上拍摄

这篇文章的关键要点是我们都在浪费资源。神经网络不需要很大。如果你用力挤压,所有的低效都会消失。你所需要的就是剩下的多汁的网络。

抛开其他一切。

感谢阅读:)

断奏捣固:不使用筛子改进浓缩咖啡

原文:https://towardsdatascience.com/staccato-tamping-improving-espresso-without-a-sifter-b22de5db28f6?source=collection_archive---------12-----------------------

为改良的意式浓缩咖啡引入分段捣实

我的大部分咖啡实验都是在一天中随意进行的。因为“安身立命之所”的订单,我把我的咖啡吧搬到了我家,我喝了相当多的拿铁咖啡和无间断浓缩咖啡。我更喜欢常规的浓缩咖啡,因为它在时间方面(比断奏咖啡快得多)和浓缩咖啡很搭。我在家里拍摄的照片有一半是常规照片,我偶然发现了断奏打夯出于方便。

这种技术并不新鲜,但是它是如何工作的直到现在还不为人所知。简单来说,断奏捣实就是把篮子填到一半,捣实,填完剩下的,再捣实。以前的工作和这项工作的区别是理解为什么它使一个更好的拍摄和特殊的技巧在两个 tamps 从理论上推导出来。断续捣实不是分开捣实(剂量一半,捣实 X 磅,剂量另一半,捣实 X 磅,其中 X 由咖啡师定义)。断续夯击是剂量的一半,夯击 X 磅,剂量的另一半,夯击 Y 磅,其中 Y 远小于 X。

在这篇文章中,我描述了以前的工作,断奏捣固理论和方法,以及一些数值结果,我在这些结果中探索如何更好地理解方法的功能。美妙之处在于,任何一个有咖啡机的人都可以在家里尝试这种技术,只要他们有一个捣棒和一只稳定的手。

以前的工作

在 2003 年的一个论坛上有一个关于分裂夯实技术的简短讨论,除了一两个使用类似东西的人之外,那里有很多怀疑。他们称之为双夯或双剂量或分裂夯。

2005 年,有人发起了一场关于中途捣固的讨论。他们不知道为什么会有所改善,但他们注意到了一些事情。

2009 年,一名咖啡师被看到用 30 磅的捣棒捣了前半段和后半段。他们这样做已经有一段时间了。

2015 年,Reddit 上有一个关于双夯的简短讨论,但没有解释或声称它更好。

2017 年,另一个人遇到了类似的事情,但是和之前的帖子一样,没有关于这项技术如何运作的理论。他们问有没有名字。潜在的,双夯会是一个好名字,但基于它如何提高拍摄,断奏夯更适用。

所有这些例子都很清楚,因为他们没有做一系列的实验来检查咖啡萃取,也没有人真正理解什么物理上允许分裂捣棒成功。

便利的发现

我一直在用夏威夷的烘焙咖啡豆,它们是一种深色的中度烘焙咖啡豆。因此,它们在研磨时会占用更多的体积,我不习惯在我的 20g 篮子里只使用 18g,而我通常使用 22g。它可能会变得混乱,所以我开始分裂捣实(剂量,捣实,剂量,整平)。

大约在同一时间,我一直在做视觉分析,为什么我一直看到甜甜圈提取,即使是断续的浓缩咖啡照片。它们没有同样的通道问题,因为每一层都有更均匀的颗粒分布。

我典型的断奏是 8g 精细(<400um) on the bottom, 8g coarse (> 500um),6g 中间(400um 到 500um 之间)在上面。从模拟来看,沟道效应总是发生在粒子的正态分布中,但是通过分层,沟道效应被减少,导致更好的提取。

在我的甜甜圈分析中,我认为甜甜圈形状是由精细层引起的,但我发现冰球顶部的中间层影响最大。如果所有的细颗粒没有筛掉,或者捣实太用力,我会得到很多通道。该理论认为,在粗颗粒中不存在太多的沟道效应,因为它们不能通过夯实而被压缩太多。微粒可以被压缩,但当水碰到它们时,它们会融合在一起,导致其表现不同。支持这一理论的证据是,细层在底部,但并不像人们想象的那样堵塞过滤孔。然而,中间层可以比粗层压缩得更多,并且看起来非常敏感。

为了更好地理解这一变量,我开始在数据表中跟踪校平机向下移动多远,而不是使用类似施加到夯锤上的力。我发现压力越轻效果越好,在研磨机中拨号变成了上下拨动校平机。

断续捣实

断奏捣固是将断奏击球中的知识运用到常规击球中的结果。断续镜头中的分层最终是咖啡密度的分层,这是颗粒大小的结果。因此,如果你有两层不同密度的咖啡,结果可以修改成类似于一个断续的浓缩咖啡镜头。在断续夯击中,底层的密度比顶层的密度高,这模拟了筛选地面,并在底部有一个细层。

这种设置源自一些实验,我主要关注的是获得良好的提取和高的总溶解固体(TDS) 。我在浓缩咖啡饮料中使用这些测试镜头,而不是直镜头。然后我用这种捣固技术尝试了几杯浓缩咖啡,效果肯定更好。

绩效指标

我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。

最终得分 是记分卡 7 个指标(尖锐、浓郁、糖浆、甜味、酸味、苦味和回味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。

数据收集

对于数据收集,我想要一个小的数据收集,试验第一次夯击和第二次夯击应该走多远。在第一次夯实时所有的地面都被踩下的情况下,我在与其他变型相同的位置使用杠杆。结果是底层比顶层密度大。我观察了两种情况:一种是常规拍摄,另一种是在过滤器(PFF) 中有一个纸质过滤器,过滤器底部有一个潮湿的气压过滤器。

斯塔卡托在击球后夯实了冰球,中间有一个纸质过滤器。

我也完全承认,我没有一个有统计学意义的数据集。我在控制输出时遇到了一些问题,因为我用了一个杠杆机器,它下面放不下秤。此外,与我做过的其他研究不同,我更感兴趣的是提取常规的断奏浓缩咖啡,所以我不想花费大量时间来获得一组常规和断奏浓缩咖啡。这些结果显示了味道和提取性能的飞跃,特别是在底部使用纸质过滤器时。

18/0 指的是第一次夯实 18 克,第二次夯实 0 克。6/12 意味着第一次夯实 6 克,第二次夯实 12 克。

我也目测了一些照片,以了解照片下面的样子。左侧是 18/0 击,这是一个正常的夯实使用一个水平作为夯锤。右拍是 9/9 断奏捣实拍。第三个画面显示,咖啡填充过滤器底部的速度比常规镜头更快,表明流动更好。

过滤(PFF)技术中的纸质过滤器呢?它们能以类似于断奏镜头的方式被修改吗?我之前发现 PFF 在断奏中的最佳位置是在精细层和粗糙层之间。对于断奏夯实的镜头,我尝试了底部和中间。我发现两层之间的 PFF 更有益。这表明 PFF 没有减少过滤器的堵塞,但减少了通过圆盘的通道。

展示了纸质过滤器的最佳放置位置,这更符合断奏咖啡的最佳放置位置。

这是另一种加香咖啡和加香咖啡的对比。断续浓缩咖啡仍然更好,但断续捣实也相当不错。这绝对是介于断续浓缩咖啡所需的工作和口味改善之间的一个很好的媒介。

基本理论

为什么断奏夯实的击球看起来表现更好?我相信它的作用就像一杯不连续的浓缩咖啡,所以顶部的低密度层完全饱和,几乎没有通道。那么底层就没有太多空间来以重要的方式引导。这表明冰球的顶部非常重要,因为当水到达底部时,顶部的通道只会滚雪球。虽然底层的密度更大,但水更均匀地流过这一层。

我弄干了几个圆盘,以帮助查看提取发生的位置。对于中间的 PFF,似乎大部分提取在顶层,这也可以在用过的冰球中看到。断奏镜头从精细层中提取了更多,所以两者之间仍然有一些基本的区别,因为筛咖啡层的差异较小,因此通道较少。

花咖啡冰球上半部分的断奏捣实拍摄。

在这篇文章中,我展示了断奏捣固法比常规捣固法口感更好的初步证据,我还提出了一个理论,解释了为什么分段捣固法可以提高浓缩咖啡的品质。我还展示了一些初步结果,表明在镜头中间的纸质过滤器比底部的要好。

最棒的是,任何已经在制作浓缩咖啡的人都可以在家尝试!

如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。

我的进一步阅读:

断续浓缩咖啡:提升浓缩咖啡

浓缩咖啡中咖啡溶解度的初步研究

浓缩咖啡模拟:计算机模型的第一步

更好的浓缩咖啡压力脉动

咖啡数据表

工匠咖啡价格过高

被盗浓缩咖啡机的故事

买得起的咖啡研磨机:比较

浓缩咖啡:群头温度分析

浓缩咖啡过滤器分析

便携式浓缩咖啡:指南

克鲁夫筛:一项分析

用 Python 和 NodeJs 实现堆栈和数组

原文:https://towardsdatascience.com/stack-and-array-implementation-with-python-and-nodejs-b8b260229e3a?source=collection_archive---------28-----------------------

作者图片

的上一篇文章中,我们讨论了数据结构和算法的基本定义。在这篇文章中,让我们更深入地挖掘数据结构的世界,特别是,让我们也为编码做一点尝试。

本文目标:

  1. 讨论数据类型、内置数据类型和派生数据类型。
  2. 介绍派生的数据结构
  3. 在 Python { 代码 }和 NodeJs { 代码 } 中实现和使用栈
  4. 介绍数组派生的数据结构
  5. 在 Python {code}和 NodeJs {code} 中实现和使用栈

数据类型简介

数据结构由一个或多个数据对象组成。数据对象代表我们将要使用精心设计的数据结构存储的数据。数据类型被认为是对数据结构中的几种数据类型进行分类的主要方式,如字符串、字符、整数等。在编程领域有两种主要的数据类型,即内置数据类型和派生数据类型。

常用的数据类型(图片由作者提供)

内置数据类型

这些是编程语言支持的基本数据类型。在特定的编程语言中主要称为主要数据类型

派生数据类型

这些数据类型是使用一个或多个内置(主)数据类型实现的。所有的数据结构都是基于这种派生的数据类型开发的。在这篇文章中,我们将讨论栈的数组的数据结构和实现的例子。

堆栈数据结构

是一种抽象数据类型 (ADT 是一种类型,用于其行为由一组值和一组操作定义的对象)是编程语言中的主要数据结构之一,对于初学者来说,这是一种容易理解的结构。LIFO(LastInFfirstOut)是一个栈的主要特长。****

像大多数数据结构一样,堆栈也表示真实世界的对象。例如,一堆硬币、一堆盒子等。

真实世界的堆栈(图片由作者提供)

可以使用数组、列表、指针等来实现堆栈。说到堆栈,有一组函数被定义为在编程上下文中有效地使用堆栈。

堆栈操作(图片由作者提供)

Python 实现

在 python 中,我们可以使用列表数据类型作为内置数据类型来实现堆栈数据结构。

{ code }请在 Github 链接中找到附件代码库。

NodeJs 实现

在 NodeJs 中,我们可以使用数组数据类型实现堆栈数据结构。

{code} 请在此 Github 链接中找到附件代码库。

推送操作

一旦你定义了栈类,其中一个主要的功能就是 push 函数。这里你将输入一个条目到数组的顶部。

算法实现

我们可以在 stack 类中定义一个算法来实现 push 操作。

**Step 1** − Checks if the stack is full(assume that the list is implemented based on a  dynamic array) for the given size or not.**Step 2** − If the stack is full, print an error**Step 3** − If the stack is not full for the given maximum size, increase the top by one and point the pointer to the next empty space.**Step 4** − Add a new element to the new empty space which is in the top of the stack**Step 5** − Return success.

弹出操作

pop 函数将从堆栈中移除最顶端的元素,堆栈项目计数将减一。尽管看起来像是从堆栈中移除了最顶端的元素,但是那个 元素并不会被完全移除 ,只有指针会移动到下面的位置。

算法实现

我们可以在 stack 类中定义一个算法来实现 pop 操作。

**Step 1** − Checks if the stack is empty by looking at the array length**Step 2** − If the stack is empty, print an error, exit**Step 3** − If the stack is not empty, get the element which is pointing at the top of the stack.**Step 4** − Decreases the size of the stack by 1, automatically the pointer of the top most item  will changed to the below item.**Step 5** − Returns success.

窥视操作

peek 函数将显示堆栈中最顶端的元素,该操作不会像 pop 操作那样从堆栈中移除该项。

算法实现

我们可以在 stack 类中定义一个算法来实现 peek 操作。peek 操作只返回堆栈顶部的值。

**Step 1** − Checks if the stack is empty by looking at the array length**Step 2** − If the stack is empty, print an error, exit**Step 3** − If the stack is not empty, get the element which is pointing at the top of the stack.**Step 4** −  Returns success.

还有其他类似 isEmpty()isFull(),printStackItems() 的函数可以作为支持函数,帮助你高效地使用堆栈。

栈实现和所有支持的功能实现都可以在这个代码库中找到。 堆栈数据结构

使用堆栈数据结构,因为我们将来在算法部分解决实际问题时会用到这些数据结构。

数组数据结构

当程序员实现他们的算法时,数组是最常用的数据结构之一。数组的一个特点是数组容器应该是固定大小的,所有元素应该是相同类型的。****

数组表示(图片由作者提供)

除了数组中的所有元素应该是相同的数据类型之外,数组总是从第 0 个元素开始(零索引),数组的大小意味着,它本身可以存储多少个元素。

在许多数据类型中,有一些主要的操作(功能)是为了有效地使用该数据类型而存在的。在数组数据类型中,有五种主要的操作。

数组插入

向给定的索引中添加一个新元素称为数组插入。我们能够通过 python 和 nodejs 编程语言实现向给定索引插入元素。

nodejs 中实现数组插入操作

python 中实现数组插入操作

数组搜索

我们可以基于索引对数组元素执行搜索操作。

按索引搜索意味着将相应的数组元素返回给定的索引,按值搜索意味着将相应的值返回给定的数组中的索引。

nodejs 中实现数组插入操作

python 中实现数组插入操作

数组删除

由于数组是固定大小的数据结构,从数组的给定位置删除一个元素有点棘手。在删除给定元素时,应该通过减小数组的大小来调整新数组。请参考下面的代码示例,它解释了如何用基本的编程技术从数组中删除给定的元素。

python 中实现数组插入操作

Nodejs 中实现数组插入操作

数组更新

数组更新是一个非常简单的操作,只需要遍历给定的数组,直到找到需要更新的元素。请遵循以下代码示例,以便更熟悉阵列更新功能。

nodejs — 代码 中实现数组更新操作

python — 代码 中实现数组更新操作

数组遍历

数组遍历就是打印一个序列中的所有数组元素。由于数组是零索引的数据类型,我们可以从零位置开始遍历数组结构来打印数组元素。

nodejs — 代码 中实现数组遍历操作

python — 代码 中实现数组遍历操作

总之,我们已经学会了如何高效地使用 数组 。请找到下面的代码段,看看我们如何用 python 和 nodejs 实现堆栈和数组。

您只需要在您的机器上安装节点,就可以开始了。(节点安装 链接 )

开始编写代码,为接下来的课程做准备。

****FYI:**  when you trying to implement data structure operations, always remember on “***Algorithm to implement***” and think in that direction. This approach will help you to get a deep understanding of the entire workflow of data structure usage.**

在下一课中,我们将实现其他派生的数据结构,如队列、链表、双向链表、循环链表,它们被认为是非常重要的数据结构,在开始使用算法之前需要先了解一下。

对于 Git 库克隆,请点击 这里

更多课程见,编码快乐!

2023 年堆栈溢出:用 ARIMA 和 BigQuery 预测

原文:https://towardsdatascience.com/stack-overflow-future-trends-predicting-with-arima-and-bigquery-77d330833329?source=collection_archive---------29-----------------------

你能预测 2023 年的顶级堆栈溢出标签吗?BigQuery 为 ARIMA 训练时序分析模型提供了新的支持,让这一切变得简单。

重要更新 : 我在 2020 年离开了谷歌,加入了雪花——所以我无法保持更新我的旧帖子。如果你想尝尝雪花啤酒,加入我们吧——我在❄️.玩得很开心

查找 2023 年的顶级堆栈溢出标签

让我们从 2020 年 6 月的顶级堆栈溢出标签开始:

2020 年 6 月十大堆栈溢出标签

正如您在图表中看到的,根据上一季度的页面浏览量,当前的顶级堆栈溢出标签为:

  1. 计算机编程语言
  2. Java Script 语言
  3. Java 语言(一种计算机语言,尤用于创建网站)
  4. C#
  5. 超文本标记语言
  6. 机器人
  7. 服务器端编程语言(Professional Hypertext Preprocessor 的缩写)
  8. 半铸钢ˌ钢性铸铁(Cast Semi-Steel)
  9. jQuery
  10. C++

当你回头看图表时,你可以看到很多变化。两年前 Python 排名第三,现在它是前十名中唯一显示增长的标签。

仅看上面的图表,你会如何将这些线延伸到未来 3 年?借助 BigQuery 和一些 SQL 魔法,我们可以轻松做到这一点:

十大堆栈溢出标签,2020 年 6 月—与 ARIMA 一起展望未来

ARIMA 模型的预测很有道理:Python 继续上升,JavaScript 保持稳定,Java 继续下降……现在,我们能利用这些趋势找到 2023 年的顶级堆栈溢出标签吗?当然可以:

十大堆栈溢出标签,2023 年 6 月—由 ARIMA 预测

这很有趣!C++和 jQuery 消失了——两个新的前 10 名标签是 ReactJS 和 Angular。

如果我们想确定未来的顶级趋势,这真的很酷。我们可以使用所有当前可用的信息,将其投射到未来,并根据可见的趋势做出决策。

其他有趣的趋势

现在我们有了这些预测和一个仪表板,我们可以重新访问我的 2018 年帖子“真正的堆栈溢出趋势:使用浏览量”。

反应 vs 角度 vs Vue.js

  • 现在,反应和角度都获得了同样多的关注
  • React 预计将在未来获得微弱优势。

机器学习

  • TensorFlow、Keras 和 PyTorch 表现出强劲增长。
  • 回到 2018 年,Pytorch 和 MxNet 同样被忽视。然而 PyTorch 趋势上升,MxNet 仍然被忽视。
  • Caffe 在 2017 年有过一些关注,但后来就淡出了。
  • TensorFlow 预计将在 2023 年保持头把交椅。

即将推出的语言:Swift,Go,Haskell,Kotlin,Rust

  • Swift 获得了周期性关注,但没有增长。
  • Go 和 Kotlin 的受欢迎程度每个季度都在增长,但不足以在短期内赶上 Swift。
  • Haskell 和 Rust 并没有表现出太多的动作,但至少 Rust 在上升,而 Haskell 保持稳定。
  • 在向下的斜坡上,为了比较:红宝石。Go 和 Kotlin 仍然受到更多关注,但可能不会太久。

AWS Lambda vs GCP 函数 vs Azure 函数

  • AWS 有优势,但 GCP 和 Azure 一直在向右上方移动。

找到自己的趋势

玩玩 Data Studio 交互式仪表盘,分享你的发现。

操作方法

BigQuery 中的 ARIMA

要使用 BigQuery 生成时间序列预测,您只需用几行 SQL 语句创建一个模型:

CREATE OR REPLACE MODEL `temp.merged202006_arima_dailyquarter`
OPTIONS(model_type='ARIMA',
        time_series_id_col='id_col',
        time_series_data_col='data_col',    
        time_series_timestamp_col='date_col'
) 

就是这样。只要你有时间序列,BigQuery 可以尽力找到一个合适的 ARIMA 模型。

为了快速检查,让我们生成 4 个基本时间序列:线性、二次增长、指数增长和周期性:

CREATE OR REPLACE MODEL `temp.test_arima`
OPTIONS(model_type='ARIMA',
        time_series_id_col='trend',
        time_series_data_col='i',    
        time_series_timestamp_col='date'
) ASSELECT DATE_ADD('2020-01-01', INTERVAL i DAY) date
  , **10*i** i, **'linear'** trend
FROM UNNEST(GENERATE_ARRAY(0,10)) i
UNION ALL
SELECT DATE_ADD('2020-01-01', INTERVAL i DAY)
  , **i*i**, **'square'**
FROM UNNEST(GENERATE_ARRAY(0,10)) i
UNION ALL
SELECT DATE_ADD('2020-01-01', INTERVAL i DAY)
  , **EXP(i)/400**, **'exponential'**
FROM UNNEST(GENERATE_ARRAY(0,10)) i
UNION ALL
SELECT DATE_ADD('2020-01-01', INTERVAL i DAY)
  , 100***SIN(i*ACOS(-1)/4)**, **'sin'**
FROM UNNEST(GENERATE_ARRAY(0,10)) i

然后,我们可以要求该模型预测每个时间序列的未来值:

SELECT DATE(forecast_timestamp) date, forecast_value i, trend 
FROM **ML.FORECAST**(MODEL `temp.test_arima` 
  , STRUCT(10 AS horizon, 0.1 AS confidence_level))

一旦进入图表,结果看起来相当不错:

测试 ARIMA。这些数值中有一半是由模型预测的。

请注意,即使所有这些预测看起来都很好——没有人“读懂了我的心思”:ARIMA 预测了指数增长曲线和线性增长。

特别有趣的是,BigQuery ML 时间序列如何处理 sin 循环“正弦”序列中的季节性。BQML 时间序列中的 ARIMA 要多得多,来自的官方文件:

big query ML 时间序列模型

ARIMA 被认为是 BigQuery ML 时间序列中使用的核心算法。然而,它不是模型创建管道中使用的唯一模型。管道由以下组件组成,大致按步骤运行的顺序列出:

-对输入时间序列的自动清理调整,包括缺失值、重复时间戳、尖峰异常,以及说明时间序列历史中的突然水平变化。

-假日效应调整。

-使用季节性和趋势分解使用黄土(STL) 算法的季节性和趋势分解。

-使用双指数平滑(ETS) 算法进行季节性外推。

-使用 ARIMA 模型和汽车进行趋势建模。自动超参数调整的 ARIMA 算法。在自动模式下。ARIMA,几十个候选模型被平行训练和评估。最佳模型带有最低的赤池信息标准(AIC)

您还可以要求 BigQuery 显示它为每个系列找到的最佳系数:

SELECT  *
FROM  ML.ARIMA_COEFFICIENTS(MODEL `temp.test_arima`)

ML。每个 ARIMA 趋势的 ARIMA 系数

堆栈溢出趋势

查看我以前的帖子“真正的堆栈溢出趋势:使用浏览量”。

[## 这些是真正的堆栈溢出趋势:使用页面视图

直到今天,获得 Stack Overflow 的季度浏览量并不容易。了解如何获得这些…

towardsdatascience.com](/these-are-the-real-stack-overflow-trends-use-the-pageviews-c439903cd1a)

将所有这些与 dbt 放在一起

我在 GitHub 上分享了生成堆栈溢出预测的完整代码:

并确保检查我的两个视频与Yufeng我们建立这些预测:

黑客 ARIMA

  • 请注意,BigQuery 中的 ARIMA 目前处于测试阶段。
  • 它确实做得很好!在这种情况下,我能够在 20 分钟内为 15,000 个不同的标签建立模型和预测。
  • 我希望团队能很快解决的一个问题是:ARIMA 没有很好地处理季度数据,但这是我对这个例子的全部了解。因此,我必须将日期从季度节奏转换为每日节奏,然后再转换回来。

后续步骤

异常检测

不要预测未来 3 年,而是预测今天,并将预测结果与实际结果进行比较。

对于来自 ARIMA 的任何超出置信区间的东西,你可以认为这是一个值得更仔细检查的异常现象。

相关职位

查看 Lak Lakshmanan 的帖子:

[## 如何在 BigQuery 中进行时间序列预测

使用 BigQuery ML 中的 ARIMA 模型进行需求预测

towardsdatascience.com](/how-to-do-time-series-forecasting-in-bigquery-af9eb6be8159)

想要更多吗?

我是 Felipe Hoffa,谷歌云的开发者倡导者。在 @felipehoffa 上关注我,在【medium.com/@hoffa】的上找到我之前的帖子,在的【reddit.com/r/bigquery】上找到所有关于 BigQuery 的帖子

对堆叠式自动编码器的直观介绍和作为电影分级系统的创建

原文:https://towardsdatascience.com/stacked-auto-encoder-as-a-recommendation-system-for-movie-rating-prediction-33842386338?source=collection_archive---------40-----------------------

堆叠式自动编码器简介和使用 Pytorch 创建模型的技术演练

通过链接改编自 unsplash 的 Img

在之前的文章中,我们为电影评论预测创建了一个受限的波尔兹曼机器模型:喜欢还是不喜欢。现在,我将介绍如何构建一个自动编码器模型,用于从 0 到 5 的电影分级预测。它分为 6 个部分。

  1. 自动编码器简介
  2. 商业挑战
  3. 数据处理
  4. 模型结构
  5. 模特培训
  6. 模型检验

📣📣这是一篇技术驱动的文章。现在让我们开始旅程🏃‍♀️🏃‍♂️.

  1. 自动编码器介绍

自动编码器是一种定向神经网络,旨在生成与输入相同的输出。如图 1 所示,输入通过隐藏层进行编码和解码,产生输出,然后与输入进行比较。之后,执行反向传播来更新权重。出于训练目的,重复这个迭代。

图 1 自动编码器模型图(作者创建的 Img)

有不同类型的自动编码器,包括堆叠自动编码器、稀疏自动编码器、去噪自动编码器和深度自动编码器。对于这里将要构建的堆叠式自动编码器,它在中间包含多个编码或解码层,如图 2 所示,而基本的自动编码器框架只有一个隐藏层。

图 2 堆叠式自动编码器模型图(作者创建的 Img)

2.商业挑战

一家知名视频流媒体公司委托我们预测其用户对电影的喜好,评分从 0 到 5。

3.数据处理

数据 MovieLens 100K 电影评分来自 GroupLens Research 这里。简单看一下图 3 中的数据,电影数据包含电影的名称和类型,评级数据包含用户 ID、电影 ID、从 0 到 5 的用户评级和时间戳,用户数据包含用户 ID、性别、年龄、工作代码和邮政编码。

图 3 源数据片段

3.1 导入数据

数据集包含 80,000 行训练集和 20,000 行测试集。让我们读一读。具体来说,

training_set = pd.read_csv(‘ml-100k/u1.base’, delimiter = ‘\t’)
training_set = np.array(training_set, dtype = ‘int’)
test_set = pd.read_csv(‘ml-100k/u1.test’, delimiter = ‘\t’)
test_set = np.array(test_set, dtype = ‘int’)

注意,我们将 Dataframe 转换为 Numpy 数组,因为我们将使用 Pytorch 张量,它需要数组作为输入。图 4 显示了训练/测试集,包括用户 ID、电影 ID、评级和时间戳(对于模型训练是不可逆的)。

图 4 训练集和测试集的片段

3.2 数据结构创建

为了准备训练/测试数据,我们需要以数组格式创建训练/测试集,每行代表一个用户,行中的每个单元格代表每部电影的评级。这是自动编码器的预期输入。

为此,我们需要将用户总数作为行号,将电影总数作为列号。

nb_users = int(max(max(training_set[:, 0]), max(test_set[:, 0])))
nb_movies = int(max(max(training_set[:, 1]), max(test_set[:, 1])))

我们创建了一个数据转换函数,它返回一个列表列表。每个子列表代表一个用户对所有电影的评级。如果用户没有对电影进行分级,则将分级初始化为 0。这在训练模型时非常重要。

def convert(data):
    new_data = []
    for id_users in range(1, nb_users + 1):
        id_movies = data[:,1][data[:,0] == id_users]
        id_ratings = data[:,2][data[:,0] == id_users]
        ratings = np.zeros(nb_movies)
        ratings[id_movies — 1] = id_ratings
        new_data.append(list(ratings))
    return new_data

利用上面的转换函数,我们对训练集和测试集进行转换。

training_set = convert(training_set)
test_set = convert(test_set)

图 5 显示了最终的训练集。同样,每行包含用户对所有电影的评级。

图 5 最终训练集片段

最后,我们将 list 类型的列表转换为张量,因为我们将使用 Pytorch 来构建自动编码器。

training_set = torch.FloatTensor(training_set)
test_set = torch.FloatTensor(test_set)

4.模型建筑

如何构建自动编码器的体系结构🤔?一个简单的解决方案是创建一个包含自动编码器的变量和方法的类。

这里我们将从 Pytorch 继承一个名为模块的父类。好处是继承允许我们创建一个子类来轻松构建我们的堆栈式自动编码器。

现在让我们在类中创建函数。

4.1 init 函数

首先,从父类模块继承所有的类和函数。

接下来,使用继承类 nn 创建第一个全连接层 fc1 。线性(),连接第一个输入矢量特征和第一个编码矢量。

第一个论点为 nn。Linear() 是特征数,也就是电影数, nb_movies 。第二个参数是第一个隐藏层中的节点数。基于实验,我们选择 20,这意味着第一个编码向量是 20 个元素的向量。这 20 个特征代表了相似用户喜欢的电影的特征。

第三,添加第二个隐藏层 fc2 ,20 个特征作为输入,10 个编码特征作为输出。

以上是编码部分。现在让我们添加解码层 fc3fc4 ,它们将与编码对称。请记住,自动编码器旨在重建输入向量,因此输出向量需要与输入向量具有相同的维数。

最后,指定模型的激活函数,这里使用的是 Sigmoid 函数。这是可调的,您可以随意尝试其他功能。

def __init__(self, ):
    super(SAE, self).__init__()
    self.fc1 = nn.Linear(nb_movies, 20)
    self.fc2 = nn.Linear(20, 10)
    self.fc3 = nn.Linear(10, 20)
    self.fc4 = nn.Linear(20, nb_movies)
    self.activation = nn.Sigmoid()

4.2 前进功能

在这个函数中,我们使用 init 函数内建的架构来应用编码和解码。这是通过对编码和解码层应用激活函数来实现的。最后,它返回一个预测收视率的向量,该向量将与实际收视率进行比较。

具体来说,我们添加一个自变量,输入向量 x ,它将被连续编码两次和解码两次,产生重构向量。第一个激活函数激活接受输入向量 x 的第一个隐藏层 fc1 的 20 个神经元。接下来,使用相同的方法,激活 fc2fc3 。注意 fc4 在输出层重构解码矢量时不需要应用激活函数。最后,返回预测评级的向量。

def forward(self, x):
    x = self.activation(self.fc1(x))
    x = self.activation(self.fc2(x))
    x = self.activation(self.fc3(x))
    x = self.fc4(x)
    return x

5.模特培训

祝贺你通过了第 4 部分,因为这是最难的部分👍👍。现在我们来训练 SAE 模型。

首先,我们从 nn 中选择均方误差。ms loss()对于损失函数,RMSprop 来自 optim。RMSprop() 分别为优化器。基于实验,我们选择学习率为 0.01,权重衰减为 0.5。注意,权重衰减用于在每几个时期后降低学习速率,以调节收敛。我们在 200 个历元上训练模型。这些参数是可调的,您可以随意尝试。

sae = SAE()
criterion = nn.MSELoss()
nb_epoch = 200
optimizer = optim.RMSprop(sae.parameters(), lr = 0.01, 
                          weight_decay = 0.5)

接下来,我们为循环创建 2 个,一个用于历元迭代,一个用于观测迭代。在 epoch 循环中,我们初始化 train_loss 和至少评价一部电影的用户数量。为了优化计算,该模型不会在没有对任何电影进行评级的用户身上进行训练。

在观察迭代中,每个观察被一个接一个地反馈,以使用 SAE 类和 sae 对象来预测每个用户的评级。计算每个观察的损失,并且应用优化器来优化损失。同时,损失在每个时期之后累积,因此我们可以回顾训练损失如何在每个时期上演变。

具体来说,为 Pytorch 网络训练集中的输入向量添加了一个批次维度。我们使用变量()unsqueeze() 函数将批处理维度放在索引 0 处。然后,使用 clone() 函数克隆输入以创建目标。如上所述,该模型是对没有对任何电影进行评级的用户进行训练的,这是通过一个 if 条件来实现的。在 if 条件里面,我们先做预测。注意,我们使用 sae(input) 进行预测,而不是 sae.forward(input) 。这是因为 forward 函数是从 nn 类创建的任何对象的基本函数。这里,我们的 SAE 类已经覆盖了父 forward 函数。更多信息可以在这里找到。

接下来,我们设置 target.require_grad False,,因为 target输入中复制了相同的 require_grad 字段。这是因为只需要计算相对于输入的梯度,而不是相对于目标的梯度。更多信息可在这里找到。然后,在输入评级为 0 的索引处将预测重置为 0,因为 0 评级不会影响损失计算。完成后,我们可以使用标准对象有效地计算损失。我们必须对所有电影的损失进行平均,包括那些没有评级的电影,以使损失在数学上与所有观察相关。 backward() 方法用于决定向哪个方向更新权重,增加还是减少。最后,使用 optimizer.step() 更新权重。不同于【向后()】的方法,这是决定要更新的权重的数量。

*for epoch in range(1, nb_epoch + 1):
    train_loss = 0
    s = 0.
    for id_user in range(nb_users):
        input = Variable(training_set[id_user]).unsqueeze(0)
        target = input.clone()
        if torch.sum(target.data > 0) > 0:
            output = sae(input)
            target.require_grad = False
            output[target == 0] = 0
            loss = criterion(output, target)
            mean_corrector = nb_movies/float(torch.sum(target.data >
                                                       0) + 1e-10)
            loss.backward()
            train_loss += np.sqrt(loss.data*mean_corrector)
            s += 1.
            optimizer.step()
    print(‘epoch: ‘+str(epoch)+’ loss: ‘+str(train_loss/s))*

有了以上这些,我们就可以进行模型训练了。最终,我们在第 1 个纪元时损失了 1.77 ,在第 100 个纪元时损失了 0.934 ,在第 200 个纪元时损失了 0.914

太好了。模型建立和训练到此为止。相当专业,希望我说清楚了😇😇。

6.模型测试

与训练循环相比,我们去除了历元迭代。注意下面,我们使用训练集作为输入进行预测,使用测试集作为目标进行损失计算。然后,在目标评级为 0 的索引处将预测重置为 0,因为这些 0 意味着用户没有对电影进行评级,因此应该没有预测。我们只计算用户在 test_set 中评价的电影的测试损失。

*test_loss = 0
s = 0.
for id_user in range(nb_users):
    input = Variable(training_set[id_user]).unsqueeze(0)
    target = Variable(test_set[id_user]).unsqueeze(0)
    if torch.sum(target.data > 0) > 0:
        output = sae(input)
        target.require_grad = False
        output[target == 0] = 0
        loss = criterion(output, target)
        mean_corrector = nb_movies/float(torch.sum(target.data > 0) + 1e-10)
        test_loss += np.sqrt(loss.data*mean_corrector)
        s += 1.
print('test loss: '+str(test_loss/s))*

有了以上所有的,我们执行测试。最后我们得到了一个 0.95 的测试损耗。这表明模型有轻微的过度拟合。

最后一点,如果你想比较实际评分和预测评分,使用下面的代码。

*user_id = 0
movie_title = movies.iloc[:nb_movies, 1:2]
user_rating = training_set.data.numpy()[user_id, :].reshape(-1,1)user_target = test_set.data.numpy()[user_id, :].reshape(-1,1)
user_input = Variable(training_set[user_id]).unsqueeze(0)predicted = sae(user_input)
predicted = predicted.data.numpy().reshape(-1,1)result_array = np.hstack([movie_title, user_target, predicted])
result_array = result_array[result_array[:, 1] > 0]result_df = pd.DataFrame(data=result_array, columns=[‘Movie’, ‘Target Rating’, ‘Predicted’])*

现在,最后一个问题,如何用模型做出真实的预测?简单,只要试试:

*prediction = sae(test_value)*

太好了!这是所有的旅程。如果需要源代码,请访问我的Github页面💕💕。**

堆积条形图,为什么和如何

原文:https://towardsdatascience.com/stacked-bar-graphs-why-how-f1b68a7454b7?source=collection_archive---------40-----------------------

讲故事和警告

图片由来自 Unsplash 的 Deniz Altindas 提供

又名:堆积条形图,分段,复合,成分,复合[条形图]

为什么:堆积条形图(SBG)显示了主类别和其子类别之间的数量关系。每个条形代表一个主要类别,它被分成代表第二分类变量的子类别的段。该图表不仅显示了不同子类别之间的数量关系,而且还显示了与整个主要类别之间的数量关系。它们还用于显示子类别的组成如何随时间变化。

这种图表使我们能够显示比标准条形图更复杂的关系(https://medium . com/nightingale/bar-graphs-why-how-8c 031 c 224 c 9 f)。堆积条形图应用于比较和比例,但重点是构成。这种成分分析可以是静态的-对于某一时刻-或者是动态的-对于确定的时间段。

请记住,成分分析与可分为单个部分的整体相关,以及每个部分如何(以相对或绝对方式)与总量相关。它也被称为整个分析的部分。

如何:堆积条形图是二维的,有两个轴:一个轴显示类别,另一个轴显示数值。指示类别的轴没有刻度(*)来强调它是指离散的(互斥的)组。带有数值的轴必须有一个带有相应测量单位的刻度。

SBG 由矩形条表示,可以像标准条形图一样水平或垂直定向。每个主要类别被分成代表第二分类变量的子类别的段。每个子类别的数量由横向或纵向首尾相连堆叠的矩形段的长度或高度表示。每个条形的最终高度或长度代表每个主要类别的总量(百分比堆积条形图除外)。

同等子类别在每个条中必须有相同的颜色,以免混淆观众。主线条之间通常留有一些空间,以清楚地表明它们指的是离散的组。

(*):刻度是图表中的标记,显示可视化中指示的数据值范围。它们通常表示为具有相应测量单位的间隔。

有两种不同类型的 SBG:

    • 简单堆叠条形图将每个子类别的绝对值放置在前一个子类别之后或之上。数轴具有数值的刻度。该图显示了每个子类别的绝对值,这些值的总和表示该类别的总数。通常,主杆具有不同的最终高度或长度。

作者使用 Matplotlib 制作的图形

2.- 100%堆积条形图将每个子类别的百分比放置在前一个子类别之后或之上。数轴具有百分比数字的刻度。该图显示了每个细分市场占整个类别的百分比。所有的主杆都有相同的高度。

作者使用 Matplotlib 制作的图形

不同的区段或子类别必须有不同的颜色或阴影,以便于识别。使用标签或图例来正确识别类别和子类别。为了便于比较,应该在每个子类内或之上标明数值,但是要注意明智地使用这些注释以避免视觉混乱。

堆积条形图用于显示调查中的响应频率。下面的例子是基于在爱尔兰进行的一项调查,该调查旨在找出三个不同级别的教育机构中教师的性别差异。100%堆积条形图显示,女性在小学教师中占 85%,在中学教师中占 62 %,在大学教师中仅占 38%。请注意数字轴上的百分比刻度,并且所有条形都具有相同的高度。教育机构的类型是主要类别,而性别是第二个分类变量。

下图显示了年度学生调查后杜克大学法学院图书馆资源和服务的使用频率。资源和服务列表符合主类别。使用水平符合第二个分类变量,答案分为三个子类:从不;偶尔;经常。它是一个水平方向的 100%堆积条形图,百分比刻度位于数字水平轴上。

来源:#1

讲述故事:简单堆积条形图的主要目的是按类别显示总数,同时指出哪些部分(子类别)在这些总数中至关重要。在 100%堆积条形图中,重点是每个子类别的百分比构成,因为不显示按类别划分的总和;换句话说,当关键信息是组成的百分比而不是类别中的总数时,使用它们。只有当相对差异很重要时,我们才使用 100%堆积条形图,而当相对差异和绝对差异很重要时,我们才使用简单堆积条形图。

恰当地确定哪个分类变量是主要变量,哪个是次要变量是至关重要的。首先,决策必须始终基于消息的性质,但请记住,主要变量将显示总量(百分比堆积条形图除外),而次要变量将显示其绝对或相对组成。主要变量的一些提示(非强制):时间段[天、周、月、年、时间范围等。];数字范围[150–300,年龄范围,收入范围等。];数字或文字排名分数[低,中,中高,高];最大数量的可能分段(以避免视觉混乱)。

SBG 用于动态构成分析,显示子类别的构成如何随时间变化。下图显示了 1997 年至 2016 年期间卢旺达议会性别构成的变化。50%的水平虚线清楚地表明,2008 年是议会中女性人数首次超过男性的一年。具有两个以上子类别的动态合成分析的 SBG 必须非常小心地使用,因为它们非常难以阅读。

来源:#2

讲故事可以通过用线连接连续的片段来改进,但应该注意的是,这种比较只适用于下一小节和上一小节之间。

来源:#3

警告

尽量不要添加太多的子类别。堆叠超过四或五层可能会使观众迷惑;

基线从 0 开始:如果条形被截断,实际值将不能正确反映。如果你的一些数据有负值,把它们画在突出显示的零基线的下面或左边;

当第二个分类变量中没有隐含的顺序时(当它们不是有序分类变量时),执行堆叠的顺序应该是明智的,以便于讲故事;

更好的比较是在 0 基线附近的子类别中进行的。随着我们越往上走,子类别之间的差异或贡献就越难理解。条形之间的对应部分无法进行严格的比较,因为它们偏离了不同的基线;

如果其中一个分类变量与时间(年、月、日、时间范围)相关,则始终将其设置为主要类别,并绘制在横轴上;

当长时间有太多的条形或者超过三个子类别时,它们也很难阅读。在这些情况下,最好使用叠加面积图叠加密度图

有时讲故事建议使用调色板来赋予你的图表更多的含义:顺序单一颜色从最少到最多;分叉两种配色方案,具有临界中点值;改变颜色以突出高于或低于阈值的值,或者突出特定的段;

永远记住,多达 10%的男性观众可能有色彩不足的问题;

当主要分类变量有很长的名字时,水平 SBG 是优选的;

避免所有 3D 效果。尽管它们在美学上令人愉悦,但是它们违背了适当的数据可视化的所有规则;

最后,永远记住矩形是非常沉重的视觉标记。

下图强调了之前警告中指出的一些想法:使用不超过五个子类别;用负数值突出显示 0 处的基线;主要范畴与时间有关;最好的比较对象是德国,这个分类更接近基线;通过适当的调色板可以清楚地看到各个部分。

来源:#4

一些作者(#5)认为 SBG 不方便、无用甚至有害。他们认为,这种图表只有在特定棒线分段之间或棒线之间对应分段之间的数字差异相当大时才有用。如果这些差异相对接近,则几乎有必要在每个子类别内或之上用数值来表示,以阐明故事情节。

综上所述, SBG 应用广泛但读起来并不简单。我们的建议是,当你需要对一个二级分类变量进行部分到整体的分析,而不需要对该变量水平之间的实际数值差异做出精确的声明时,使用它们作为第一眼。当时态数据量相对较少且每个堆栈不超过两个或三个段(子类别)时,它们也可以很好地显示组成随时间的变化。

如果你对这篇文章感兴趣,请阅读我以前的:

直方图、为什么和如何、讲故事、技巧和扩展

[## 直方图,为什么和如何

讲故事、技巧和扩展

towardsdatascience.com](/histograms-why-how-431a5cfbfcd5)

参考文献

# 1:https://law.duke.edu/lib/survey-results/

# 2:https://serial mentor . com/dataviz/visualizing-proportions . html

# 3:https://www.ahapitch.com/bar-chart/

#4: Leo,s .,错误,我们画了几个,中,经济学人,2019 年 3 月 27 日

#5: Kosara,r,堆叠棒线最差 2016 年 8 月 24 日,https://eagereyes.org/techniques/stacked-bars-are-the-worst

堆叠胶囊自动编码器

原文:https://towardsdatascience.com/stacked-capsule-autoencoders-f632c44be496?source=collection_archive---------33-----------------------

使用无监督学习和有限数量的训练数据对图像和视频中的对象检测的未来进行展望。

照片由放在 Unsplash

介绍

在过去的几年里, Geoffrey Hinton 和一组研究人员开始研究一种基于胶囊的革命性的新型神经网络。

这项研究背后的一些主要动机是,当前的神经网络,如卷积神经网络(CNN),只有在提供大量数据的情况下,才能在计算机视觉任务(如对象检测)中实现最先进的精度。

像 CNN 这样的模型需要如此大量的数据的一个主要原因是它们无法捕捉组成图像的不同元素之间的方向和空间关系。事实上,用于改善 CNN 性能的主要技术之一是数据增强。当应用数据扩充时,我们通过从原始图像创建额外的数据,例如旋转、裁剪、翻转等,帮助我们的模型更深入、更全面地了解不同对象的特征。这样,即使从不同的角度看,我们的模型也更有可能识别同一物体(图 1)。

图 1:从不同的角度看同一个物体会引起误解[1]。

CNN 能够通过首先识别图像中的边缘和形状,然后将它们组合在一起来检测物体。尽管这种方法没有考虑构建整个图像的空间层次,因此导致需要创建大的数据集以便很好地执行(因此也增加了训练模型所需的计算成本)。

胶囊

杰弗里·辛顿使用胶囊的方法紧紧遵循相反的逆图形原理。事实上,根据 Hinton 的说法,我们的大脑每次处理一个新的物体时,它的表征都不依赖于视角。因此,为了创建能够像我们的大脑一样好地执行对象识别的模型,我们需要能够捕捉组成对象的不同部分的层次关系,并将它们与坐标框架相关联。

这可以通过将我们的网络建立在一种叫做胶囊的结构上来实现。胶囊是一种数据结构,它以矢量形式包含了我们正在检测的特征的所有主要信息。它的主要成分是:

  1. 表示图像中是否存在形状的逻辑单位。
  2. 代表形状的姿态的矩阵。
  3. 嵌入颜色、变形等其他信息的向量…

在过去几年中,Hinton 研究小组提出了不同的方法来创建胶囊网络,例如:

在胶囊网络中,不同的神经元相互竞争,以找到组成图像中对象的一致部分。可以使用三种不同的方法来测量不同胶囊之间的一致性:

  • 使用余弦距离作为一致性的度量。
  • 期望最大化。
  • 混合模型。

如图 2 所示,将我们的系统建立在基于几何关系理解对象的基础上,我们可以使我们的模型能够可靠地检测对象(即使从不同的角度或在不同的光照条件下捕捉),在训练期间只提供它的一个实例(不需要数据增强)。

图 2:不同视角的物体检测[2]

堆叠胶囊网络

2019 年创建胶囊网络的方法的一个新增功能是以无人监管的方式执行对象检测的能力,因此不需要标记我们的数据。

该模型架构可分为 3 个主要的不同阶段,例如:

  • Constellation Autoencoder (CCAE):在这个阶段,以无监督的方式训练自动编码器模型,以最大化零件胶囊可能性。
  • 部分胶囊自动编码器(PCAE):我们的输入图像被分成组成部分,以推断物体的姿态。
  • 对象胶囊自动编码器(OCAE):创建的部分按照它们相应的姿势组织在一起,以重新创建对象。

将这三个阶段结合在一起,我们就可以得到最终的堆叠胶囊网络。整个过程可以概括为图 3 中的工作流程。

图 3:堆叠胶囊网络一般工作流程[3]

如果你有兴趣了解更多关于堆叠胶囊网络的信息,Geoff Hinton 在 AAAI 2020 图灵奖上从第 3 分钟到第 34 分钟的视频演示中提供了关于该主题的更多信息。

视频 1: AAAI 2020 图灵奖

我希望你喜欢这篇文章,谢谢你的阅读!

联系人

如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:

文献学

[1]关于真相的 10 个确定性,KOET,Lisa Beebe。访问网址:https://www . kcet . org/arts-entertainment/10-真相的确定性

[2]带有 EM 路由的矩阵胶囊,Geoffrey Hinton,Sara Sabour,Nicholas Frosst-Google Brain。访问地点:https://openreview.net/pdf?id=HJWLfGWRb

[3]堆叠胶囊自动编码器,Adam Kosiorek。访问网址:http://akosiorek . github . io/ml/2019/06/23/stacked _ capsule _ auto encoders . html

多分类问题的堆叠分类器方法。

原文:https://towardsdatascience.com/stacking-classifier-approach-for-a-multi-classification-problem-56f3d5e120c8?source=collection_archive---------33-----------------------

基于我在一个项目中的实验,我起草了这个堆叠分类器的概念。所以这个的概念解释可能有点偏。

没有一刀切!!

当一个数据集中有许多类时,给分配一个单一的模型来完成分类的任务,并期望高精度似乎有点过于雄心勃勃。尤其是当给定的数据集非常小时。实现这一点的简单方法是为每个类别设计单独的二元分类器,并组合所有这些二元分类模型进行预测。一个堆叠分类器非常符合这些直观的想法。就像其他集成技术一样,堆叠分类器也使用许多模型来完成预测任务。

堆叠分类器是一种集成方法,其中来自多个分类器的输出作为输入传递给元分类器,用于最终分类的任务。

图一

堆叠分类器方法是实现多分类问题的一种非常有效的方法。多分类问题可以转换(或者说分解)成几个二元分类问题,即每个类别一个问题。所有的二进制分类模型,通常称为基本学习模型,因此可以通过使用元分类器来组合,用于最终的输出预测任务。这可以通过 将来自每个二元模型的输出 堆叠在一起并将其作为输入传递给元分类器来完成。

图 2 堆积分类器框图

基础学习者可以使用不同的模型。例如,当试图在一个特定的类点和其余的类点之间进行分类时,就像在一个多类数据集的一个与其余的方式中一样;在某些情况下,在属于特定类别的数据点和其余数据点之间可能存在线性分离,而在某些情况下可能不存在线性分离。因此,基本的学习模式必须相应地改变。这为处理多分类问题提供了很多便利和灵活性。所有的基础学习者都使用全部数据进行训练。

元分类器的输入数据集由所有基础学习器的输出形成,并提供实际输出作为元分类器的目标变量。元分类器可以是我们选择的任何分类器,其工作是利用二元分类器提供的信息进行最终预测。使用基本学习器的输出来训练元分类器。元分类器的输入可能如下图所示。

图 3 元分类器的训练数据集示例

每个基础学习器的输出形成了元分类器的特征集。因此,对于多分类问题,类的数量可以等于元分类器的特征的数量。

堆叠分类器已经被设计来提高预测性能,并且已经表现出更好的性能,并且比现有技术的模型好很多倍,因此被广泛地用于纸牌游戏竞赛。相反,这些模型的计算费用很高。下面的链接提供了关于堆叠分类器方法的更多信息和提示。

https://www . ka ggle . com/zaochenye/tips-for-stacking and-blending

希望这是翔实的!!

*图 1 和图 2 来源于互联网。

显微整片图像的应变估计

原文:https://towardsdatascience.com/stain-estimation-on-microscopy-whole-slide-images-2b5a57062268?source=collection_archive---------54-----------------------

对整个载玻片图像进行显微图像处理

在千兆像素图像上获得稳定的污点估计。

这篇文章是系列文章的一部分。如果你不知道显微镜染色是什么,它的变化有多大以及为什么如此强烈,或者如何在小图像块上估计它,你可以在第一部分找到所有这些:“显微镜染色变化以及如何估计它们”

这一次,我们想得到更多的实践机会,解决一个你在处理整片显微镜图像时可能会遇到的问题:那些图像真的很大。我的意思是,它们的宽度和高度可以轻松超过 100,000 像素。现在让我们考虑一个应用程序,其中我们希望一致地归一化完整的图像,例如作为基于深度学习的识别管道的预处理。

为此,对整个染色矩阵进行可靠的估计是非常有用的。然后,我们可以对每个图像块应用相同类型的颜色去卷积,并得到相同的结果。如果我们随后提取小块,这也真的很酷,因为这些有时不能满足有足够量的两种染料(在我们的例子中是苏木精和曙红)存在的前提条件,这导致了糟糕的统计特性。

我答应过你这次会更实际。所以我准备了一个完整的代码示例作为 Jupyter 笔记本,你可以在这里找到。现在我们一起过一遍。

该代码由四部分组成:

  1. 下载整个幻灯片图像
  2. 为有效区域寻找分段掩码以从中采样
  3. 从这些区域随机采样图像碎片
  4. 拼接图像块上的污点估计

例如,我从我们的公共肥大细胞肿瘤数据集[1]中挑选了一个完整的幻灯片图像,作为开放数据,很容易获得。可以使用 urllib 通过一个 HTTP get 下载它:

urllib.request.urlretrieve('[https://ndownloader.figshare.com/files/16261493?private_link=a82ddb634864c24f4aee'](https://ndownloader.figshare.com/files/16261493?private_link=a82ddb634864c24f4aee'), 
                               'f3741e764d39ccc4d114.svs')

现在让我们继续吧:

为了加载幻灯片,我们使用了 openslide 库。

slide = openslide.open_slide('f3741e764d39ccc4d114.svs')
overview = slide.read_region(location=[0,0], size=slide.level_dimensions[-1], level=3)
overview.show()

这是肿瘤切片图像的样子。图片来自[1],CC-BY 4.0 许可证。

如你所见,图像显示的是中间的肿瘤横截面。它还包含许多几乎白色的背景,那里没有组织存在。“白色”颜色值在不同的载玻片扫描仪之间会有所不同,因此使用自适应阈值来生成组织存在的分割掩模是比较明智的。

为此,我们将使用 OpenCV,因为它方便地提供了我们需要的所有操作:

*# Convert to grayscale*
gray = cv2.cvtColor(np.array(overview)[:,:,0:3],cv2.COLOR_BGR2GRAY)

*# OTSU thresholding*
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

*# dilate*
dil = cv2.dilate(thresh, kernel = np.ones((7,7),np.uint8))

*# erode*
activeMap = cv2.erode(dil, kernel = np.ones((7,7),np.uint8))

*# and let's visualize it as well:*
**from** **PIL** **import** Image
Image.fromarray(activeMap)

上面的代码为肿瘤生成的分割遮罩。图片来自作者。

现在我们已经可以勾掉列表中的任务 1 和任务 2 了。现在是时候从至少 95%被组织覆盖的所有区域随机取样了。经验表明,30 幅图像会给我们一个很好的估计。

让我们简单地讨论一下使用非常大的补丁和使用大量补丁之间的权衡:如果我们过度使用,两者都会使评估过程变慢。然而,为了使我们的估计具有良好的统计稳定性,我们应该对相当多的图像进行采样。如果我们使用大的图像补片,我们将会失去一些我们想要达到的泛化能力——毕竟,统计应该覆盖整个图像。

k=0
patches = []
ds=32 *# downsample - we are using level 3 of the WSI which has a downsample of 32*
**while** (k<30):
    x_ds = np.random.randint(0, slide.level_dimensions[0][0]/ds)
    y_ds = np.random.randint(0, slide.level_dimensions[0][1]/ds)
    step_ds = int(1024/ds)

    canbeused = np.sum((activeMap[y_ds:y_ds+step_ds,x_ds:x_ds+step_ds])>1)>0.95*step_ds*step_ds
    **if** (canbeused):
        k=k+1
        x=int(x_ds*ds)
        y=int(y_ds*ds)
        img = slide.read_region(location=(x,y), size=(256,256), level=1)
        patch = np.uint8(np.array(img)[:,:,0:3])

        patches.append(patch[**None**,:,:,:])

你可以看到,在变量中可以使用我们计算了下采样图的每个随机选择的小块,如果它表示被组织覆盖至少 95%的图像区域。如果这是真的,我们从该区域采样图像块。

既然我们已经很好地选择了有代表性的小块,我们可以将它们全部堆叠起来,得到一大块图像像素,所有像素都代表了完整的整个载玻片图像的污点。

因此,我们现在可以将数据转换成光密度(OD)表示,并截掉所有非常亮的像素(OD 低于或等于 0.15):

**def** RGB2OD(image:np.ndarray) -> np.ndarray:
    mask = (image == 0)
    image[mask] = 1
    **return** np.maximum(-1 * np.log(image / 255), 1e-5)

OD = RGB2OD(np.stack(patches).reshape(-1,3))
OD = (OD[(OD > 0.15).any(axis=1), :])

接下来,我们计算矩阵的特征向量和值,并将 OD 像素值投影到前两个特征向量所跨越的平面上:

_, eigenVectors = np.linalg.eigh(np.cov(OD, rowvar=**False**))
eigenVectors = eigenVectors[:, [2, 1]] *# strip off residual stain component*

**if** eigenVectors[0, 0] < 0: eigenVectors[:, 0] *= -1
**if** eigenVectors[0, 1] < 0: eigenVectors[:, 1] *= -1T_hat = np.dot(OD, eigenVector)

我们无法知道向量的顺序,但我们可以假设它们需要为正,所以我们在相反的情况下乘以-1。

这两个应变向量由该点云的最大和最小角度表示。因此,我们计算角度,然后使用百分位数进行稳健估计:

phi = np.arctan2(T_hat[:, 1], T_hat[:, 0])
min_Phi = np.percentile(phi, 1)
max_Phi = np.percentile(phi, 99)

最后,我们必须将角度转换回 OD 空间中的 3D 坐标。为了获得更可预测的结果,我们希望在载体中首先有较大的成分(即苏木精染色)。

v1 = np.dot(eigenVector, np.array([np.cos(min_Phi), np.sin(min_Phi)]))
v2 = np.dot(eigenVector, np.array([np.cos(max_Phi), np.sin(max_Phi)]))
**if** v1[0] > v2[0]:
    stainVectors = np.array([v1, v2])
**else**:
    stainVectors = np.array([v2, v1])

瞧吧。这就是我们对污点的可靠估计。让我们最后想象一下:

**import** **random**
*# take random sample for plotting*
randsamp = np.array(random.sample(OD.tolist(),1000))

*# plot points*
**import** **matplotlib.pyplot** **as** **plt**
**from** **mpl_toolkits.mplot3d** **import** Axes3D  *# noqa: F401 unused import*
fig = plt.figure(figsize=(10,10))
ax = fig.gca(projection='3d')

*# color needs to be given as RGBA values in range 0..1 for matplotlib*
color=np.ones((1000,4),np.float32)
color[:,0:3] = OD2RGB(randsamp)/255.

ax.scatter(randsamp[:,0],randsamp[:,1],randsamp[:,2], c=color)

*# and plot stain vectors (with correct color)*
ax.plot([0, stainVectors[0,0]],[0, stainVectors[0,1]],[0, stainVectors[0,2]], linewidth=4, color=(OD2RGB(stainVectors[0,:])/255.).tolist()+[1.])
ax.plot([0, stainVectors[1,0]],[0, stainVectors[1,1]],[0, stainVectors[1,2]], linewidth=4, color=(OD2RGB(stainVectors[1,:])/255.).tolist()+[1.])

ax.set_xlabel('red (OD)')
ax.set_ylabel('green (OD)')
ax.set_zlabel('blue (OD)')
plt.title('stain vector estimation')

显微镜整体载玻片图像的染色评估结果。你可以清楚地看到原始图像中苏木精(粉色)和曙红(紫色)的染色矢量。图片来自作者。

就是这样。这只是我们在上一篇文章中讨论的 Macenko 等人[2]的算法的直接扩展,以适应大尺寸的图像。

既然我们知道了如何估计整个幻灯片图像上的污点,我们可以分析公共数据集中有多少变化。但那是下一篇文章的一部分,所以请继续关注。

[1] Bertram,C. A .等人(2019)。犬皮肤肥大细胞肿瘤整片切片图像有丝分裂像评估的大规模数据集。科学数据,6 (274),1–9。

[2]: Macenko,Marc 等一种用于定量分析的组织学切片归一化方法(2009)IEEE 生物医学成像国际研讨会:从纳米到宏观。

公共有丝分裂图组织病理学数据集中的染色差异

原文:https://towardsdatascience.com/stain-variations-in-public-mitotic-figure-histopathology-data-sets-bf07a4ea35fc?source=collection_archive---------56-----------------------

对整个载玻片图像进行显微图像处理

数据的变化越少真的越好吗?

这篇文章是系列文章的第三部分。如果你不知道什么是显微镜染色,或者它们如何影响计算机化检测中图像的使用,你可以在第一部分中找到。在那里,我们还讨论了一种流行的方法,如何估计显微镜图像的污点。如果你想获得一些如何使用 python 评估这些污点的实践经验,你可以在的第二篇文章中找到所有相关内容。

既然我们现在知道了如何估计显微镜载玻片的染色,我们就有了完整的工具集来了解组织学数据集中染色的变化。

这有什么关系?在模式识别中,当我们想要训练一个系统来识别图像中的某些对象,或者估计它的某些属性时,数据代表我们的实际用例是非常重要的。换句话说:如果现实世界不是所有完全相同属性的图像,我们用于训练的数据也不应该是。

如果训练数据不代表测试数据,则纯数据驱动的方法(如深度网络)通常不能很好地概括。图片来自作者。

如果数据真的代表真实数据,它将解释完整的差异——我们可以继续训练一个健壮的系统。

因此,当决定用一个数据集来训练和评估我们刚刚发明的新的、非常有趣的算法时,我们应该考虑到,当数据集显示真实世界的真实画面时,我们的结果将更好地转化为真实世界。

组织病理学中一个非常具有挑战性的课题是自动或计算机辅助肿瘤分级。为此,一个非常相关的任务是检测经历细胞分裂的细胞(所谓的有丝分裂图,因为这些指示肿瘤的生长。你猜对了,生长更快的肿瘤通常不利于患者的预后。

在这个领域,我们已经看到了许多挑战和其他公开可用的数据集,研究人员现在都可以使用它们来比较他们的结果。虽然这确实有助于区分真正的地面制动新改进和那些只有小效果的改进,但我们无法区分针对数据(而不是问题)定制的解决方案和其他在整体任务中做得更好的解决方案。

所以,在这里,再一次,这一切都归结为一个问题:数据的代表性如何?数据集中真的有足够的多样性来解释你在现实中能找到的多样性吗?

在之前的文章中,我们已经讨论过染色的变化实际上是显微镜载玻片多样性的一大来源。抛开其他来源,如组织类型或物种,对于我们手边的数据集来说,评估这些是值得的。

对于人类乳腺癌,有几个值得注意的数据集:MITOS 2012 [1]和 2014 [2] ICPR 竞赛数据集来自法国和美国的一个团队,以及 AMIDA-13 [3]和 TUPAC16 [4]数据集,都来自荷兰。它们在许多因素上有很大不同,最显著的是肿瘤病例(即患者)的数量。现在让我们相互比较一下,并比较一下我们之前用于玩具示例的整个幻灯片图像(WSI)数据集[5]。

为此,我使用了与上一篇文章相同的方法,但是当然,我们有更多的案例要展示。为了使图不太拥挤,我用一个标记可视化了苏木精和伊红染色向量。这是我的发现:

所有数据集的曙红(+和 )和苏木精(⬢和■)染色向量。用 Aperio 扫描仪扫描的载玻片的染色向量用+和■表示,而用其他扫描仪扫描的载玻片的染色向量用 ⋆和 ⬢标记表示。图片来自作者。

好吧,我同意上面这个情节信息量很大。让我来引导你:在左边,我们看到我们以前使用的 WSI 数据集的染色向量[5]。很明显,所有的污渍都很相似。这表明载玻片制备的工作流程是高度标准化的,这也是由仅使用一台扫描仪造成的。对于接下来两列中的 MITOS 2012 和 MITOS 2014 数据集,您可以看到扫描仪颜色校准的强烈影响,导致两组非常不同的染色向量。曙红的颜色似乎受此影响最大。

TUPAC16 数据集也是如此。在这里,我们另外有一个由大量病例引起的更广泛的污点传播。

下面是我的总结:在多样性方面,我们在 TUPAC16 数据集中有一个明显的赢家。虽然在这个数据集上工作很有挑战性,但在这个数据集上创建的结果可能会更好地反映现实情况。而这只是有丝分裂数字的辅助数据集。完整的数据集由 500 幅完整的幻灯片图像组成——在管道中使用如此大量的数据本身就是一个挑战。

我的感觉是,尽管 MITOS 2012 数据集提供了细分信息,但现在应该被认为是过时的。不仅污点的多样性很低,而且训练集和测试集也来自同一个 which 这使得数据集没有资格评估鲁棒性(正如 G. Litjens 在此正确指出的)。

[1]鲁等人(2014 年)。美图&非典型性。图像普及访问实验室(IPAL),Sci 机构。,Technol。&资源研究所。新加坡科技资讯公司。代表,1,1–8。
[2]鲁等人(2013)。乳腺癌组织学图像中的有丝分裂检测:2012 年 ICPR 竞赛。病理学信息学杂志,第 4 期。
[3] Veta,m .等人(2015)。乳腺癌组织病理学图像中有丝分裂检测算法的评估。医学图像分析20 (1),237–248。
[4] Veta,m .等人(2019)。从全切片图像预测乳腺肿瘤增殖:TUPAC16 挑战。医学图像分析,54,111–121。
[5] Bertram,C. A .等人(2019)。用于在犬皮肤肥大细胞肿瘤的整个载玻片图像上进行有丝分裂图形评估的大规模数据集。科学数据,6 (274),1–9。

脱颖而出!分析师、数据科学家和其他所有人的领导力和沟通技巧

原文:https://towardsdatascience.com/stand-out-leadership-and-communication-skills-for-analysts-data-scientists-and-everyone-else-960c13f3738d?source=collection_archive---------41-----------------------

被视为领导者的技巧和诀窍

You X VenturesUnsplash 上拍摄的照片

这一切是怎么发生的?

当我反思过去几年的生活时,尽管我非常努力地进入数据科学领域,成为一名产品分析师,但有时我仍然会问自己这样一个问题,“这一切是怎么发生的? " 我不是数字奇才,我也不是天才程序员,我也不是重新想象尖端的深度学习架构……帮助我在我做过的每份工作中取得成功的不是原始的技术能力,而是我的沟通和领导能力,我学习新事物的渴望,以及我不断进取和完成工作的道德规范

如果你想快速赢得同事和老板的信任和尊重,磨练这些领导力和沟通技巧,用软技能平衡你的技术能力!每一点都是我实现它的技巧和诀窍

了解领导
了解你的听众
拥有极端所有权
首先批评自己
建立关系

理解领导力

你不需要在管理岗位上才能成为领导者!领导力有点难以定义,但对我来说,领导力是一种方法,通过这种方法,灵感转化为追求共同目标的行动。根据定义,它只是领导一个人、团体或组织的行为。回顾文献,共同的主题似乎是什么构成了一个好的领导者。经常被引用的特征有诚实、魅力、远见、同情心和果断。从历史上看,优秀/强势的领导者将人们团结在一起,倡导创新,并以社区利益为重而非个人利益。

https://www.flickr.com/photos/53801255@N07/8737945758

变革型领导是人们所追求的一种有效的领导战略。领导力专家 Ronald E Riggio 博士就构成变革型领导者的四种不同特质撰写了大量文章。他说,最好的领导者是一个理想的榜样,激励追随者,对他人表现出真诚的关心,并激发创新和创造力。

遵循以下建议有助于理解领导力:

关注大局 如果你过于专注于一件事,就很容易忽视周围正在发生的事情。

参与进来向人们展示你是一名团队成员。参加团队建设活动,了解业务及其竞争。

要有战略眼光永远要寻找新的、高效的做事方法。

永远了解你的观众

了解你的受众是沟通 101。如果你想让你的交流更有说服力,你必须了解你的受众,这样你才能传递他们关心的信息!当我向培训师展示我们的新工具或功能时,我会详细介绍这些功能的工作原理。当我向利益相关者和首席执行官解释我们的项目进展时,我没有包括所有的实质性技术细节。如果我这么做了,他们的眼睛很可能会变得呆滞,他们会变得不感兴趣!用不同的细节层次接近不同的受众。

https://www.flickr.com/photos/batmoo/3734837951

与管理层沟通,我有责任让他们对我的团队正在完成的工作保持兴奋,这样他们就会继续给我们提供资源,并相信我们正在推动业务向前发展。我知道他们想听大局更新,而不是细节。他们想要高层次的概述和关于我们是否完成工作的信息。

遵循这些建议来帮助你了解你的受众:

研究你的利益相关者/受众 如果你是这个组织或团队的新成员,不要害怕做一些研究!如果可能的话,浏览一下员工名录,把你项目中所有利益相关者的名字写出来。在 LinkedIn 上查找或者找个方法闲聊(远程工作时闲聊可能更困难)。

观看会议记录
如果你的公司有会议记录,那就看看过去的股东会议,看看你的同事们表现如何。注意他们提供的详细程度和演示的整体基调。严重吗?是不是很轻松?

寻求反馈 听取批评可能会很艰难,但却大有裨益!与你的老板和同事谈论你的表现,并获得你需要的反馈来提高你的表达技巧。

拥有极大的自主权

不要抱怨任何事情,除非你脑子里已经有了解决方案。“极端所有权”的想法是由乔科·威林克推广开来的,他是一名退役的海豹突击队海军军官。他写了几本关于领导力和极端所有权的书,还运营着自己的播客。总结他的观点,好的领导,好的团队,在出现问题的时候,不会找借口,也不会责怪外界因素。他们负责解决问题,寻找解决方案,并实施能完成工作的方案。

https://www.flickr.com/photos/lucgaloppin/5607950722/

当你开始对事情拥有极大的自主权时,你会意识到与你的团队进行“艰难的对话”会容易得多,因为你开始为自己的工作感到自豪。我的团队帮助开发了一个数据分析工具,供我们的客户在我们的软件平台上使用。尽管我在这个职位上是个新手,并在项目的最后阶段加入了项目,但在我的角色允许的情况下,我尽可能地拥有了所有权。通过尽可能多地拥有所有权,并与团队紧密合作,当我在测试中发现错误时,问题很容易向开发人员提出。我对批评感到不那么焦虑,因为我把确保我们的客户将得到一个有用的工具作为我的责任。

最后,承担所有权和责任可以在你的经理和队友之间建立信任,这让你看起来可靠。当我回想以前的工作时,那些从未获得成功的人总是那些为自己表现不佳找借口的人,而不是为自己的不足承担责任并努力改进的人。

遵循以下建议,获得终极所有权:

拥抱项目/使命 通过讨论和理解项目的目的和理想结果,激发团队的热情。

总是问“我还能做些什么?”
尽管过多承诺和不足兑现从来都不是好事,但你应该经常问自己和你的团队,“我还能做些什么?”

保持积极 事情总会上来的。硬件出现故障,人们生病,事情超出预算。每当一个障碍出现时,保持积极的态度,试着跳过它,而不是用它作为停止尝试的借口。

先批评你自己

任何和我一起工作过的人都知道我不害怕批评。我尽我所能接受它。在你职业生涯的某个时刻,无论你是否是一名领导者,你都可能会卷入一场“艰难的对话”,在这场对话中,你需要对队友或经理提出批评。当这些情况出现时,谦卑地站出来是很重要的。记住, pobody 是不完整的

https://pix abay . com/插图/批评-写-评-评-3083101/

根据自助经典, 如何赢得朋友&影响人 ,如果你想让人们乐于接受你的批评,先说说你自己的错误吧!例如,我曾与一位高管会面,他让我用 1-5 的标准给我与直接上司每周 1:1 的会面打分,1 代表没有价值,5 代表非常有价值。在我说任何批评性的话之前,我以一个关于我是一个“严格的评分者”的轶事开始了谈话,并告诉他我对“特殊价值”的定义。我解释了每周达到这个定义对任何人来说都是一个真正的挑战,所以我只能给出 4/5!在这一切之后,当我确实改变了谈话的语气,变得更加挑剔时,这位高管理解了我的心情。他知道我的批评来自于在公司内成长和让事情变得更好的愿望,而不是抱怨我的团队。最终,我认为这让他更容易接受我要说的话。

遵循以下建议,从关键对话中获得最大收益:

阐明意图 确保参与者理解关键对话背后的意图。

批评之前先表扬如果你必须批评,从告诉这个人/团队他们做对了什么开始。

问问你自己你会得到什么不要为了你的自我而开始争论。你是对的没有完成任务重要。如果你从批判性对话中获益甚少或一无所获,考虑避免或推迟它,直到你的目标明确。

建立关系

建立关系对我来说从来都不容易,因为我总是很害羞。虽然这需要精神和情感上的努力,但我会尽最大努力与我的团队和经理建立关系。优秀的领导者和沟通者注重建立关系以建立信任。当然,个人凭自己的能力可以完成很多事情,但是大多数人都同意和一个好的团队一起工作会让你走得更远更快。

https://www.flickr.com/photos/wickenden/3259826856

通过建立关系和敞开心扉,你让你的队友和老板更容易信任你。例如,当我在一家电子产品零售商工作时,工作的一部分是向那些想购买我们电脑和电视等昂贵设备的顾客推销保护计划。一天,我带着推销计划的新技巧去找我的老板。因为我和老板关系很好,所以他让我试一试。他非常了解我,相信我会做正确的事情。他相信,如果我的新计划失败了,我会回到原来的计划,而不是让我的自我占据主导地位。因为我们的关系,我得到了队友没有的机会,这让我更快地爬上了梯子。

遵循以下建议,快速建立关系:

经常给予赞美 我经常感谢我的队友,告诉他们他们做得很好。

尊重别人的意见 除非会影响项目或任务的结果,否则最好避免与人争论他们的意见。

迅速道歉 当你错了或出格了,要迅速自我纠正并道歉。

最后的想法

作为一名领导者,头衔中不仅仅只有“经理”这几个字。就像你可能在你的技术技能上下功夫一样,如果你想更容易地获得成功或在公司的阶梯上攀升,在领导力和沟通技能上下功夫是很重要的。专注于掌握这些技巧,你将在领导和沟通方面表现出色:

了解领导
了解你的听众
拥有极大的自主权
首先批评自己
建立关系

如果你有兴趣学习更多关于商业和数据科学的知识,也可以看看我的其他文章!

[## 数据科学的商业方面:向利益相关者展示的 5 个技巧

数据科学不仅仅是算法和建模

towardsdatascience.com](/the-business-side-of-data-science-5-tips-for-presenting-to-stakeholders-fb624a9a6e54)

谢谢大家!

  • 如果你喜欢这个, 跟我上媒 了解更多
  • 通过订阅 获得完全访问权限并帮助支持我的内容
  • 我们连线上LinkedIn
  • 使用 Python 分析数据?查看我的 网站

t1—埃里克阀 T3

Google BigQuery 中的标准 SQL

原文:https://towardsdatascience.com/standard-sql-in-google-bigquery-c2636ae4b7c8?source=collection_archive---------8-----------------------

来源:沉积照片

在市场营销中使用的优势和示例

2016 年,Google BigQuery 引入了一种新的与表交流的方式:标准 SQL。在那之前,BigQuery 有自己的结构化查询语言,称为 BigQuery SQL(现在称为 Legacy SQL)。

乍一看,传统 SQL 和标准 SQL 没有太大的区别:表名的写法略有不同;标准有稍微严格的语法要求(例如,不能在 FROM 前加逗号)和更多的数据类型。但如果你仔细观察,会发现一些微小的语法变化给营销人员带来了许多优势。

在 OWOX 上,我们决定澄清以下问题的答案:

  • 标准 SQL 相对于传统 SQL 的优势是什么?
  • 标准 SQL 有哪些功能,如何使用?
  • 如何从传统 SQL 迁移到标准 SQL?
  • 标准 SQL 还兼容哪些服务、语法特性、操作符和函数?
  • 如何对营销报告使用 SQL 查询?

标准 SQL 相对于传统 SQL 的优势是什么?

新数据类型:数组和嵌套字段

标准 SQL 支持新的数据类型:数组和结构(数组和嵌套字段)。这意味着在 BigQuery 中,使用从 JSON/Avro 文件加载的表变得更加容易,这些表通常包含多级附件。

嵌套字段是较大表格中的小型表格:

在上图中,蓝色和黄色条是嵌入迷你表格的线条。每行是一个会话。会话有共同的参数:日期、ID 号、用户设备类别、浏览器、操作系统等。除了每个会话的常规参数之外,还将命中数表附加到该行。

点击数表包含用户在网站上的操作信息。例如,如果用户单击横幅、翻阅目录、打开产品页面、将产品放入购物篮或下订单,这些操作将被记录在 hits 表中。

如果用户在网站上下订单,关于订单的信息也将输入到 hits 表中:

  • transactionId(标识交易的编号)
  • transactionRevenue(订单总值)
  • 交易装运(运输成本)

使用 OWOX BI 收集的会话数据表具有类似的结构。

假设您想知道过去一个月来自纽约市用户的订单数量。要找出答案,您需要参考 hits 表并计算唯一事务 id 的数量。为了从这样的表中提取数据,标准 SQL 有一个 UNNEST 函数:

#standardSQL 
**SELECT** 
**COUNT** (**DISTINCT** hits.transaction.transactionId) -- count the number of unique order numbers; DISTINCT helps to avoid duplication
**FROM** `project_name.dataset_name.owoxbi_sessions_*` -- refer to the table group (wildcard tables)
**WHERE** 
  (
  _TABLE_SUFFIX **BETWEEN** FORMAT_DATE('%Y%m%d',**DATE_SUB**(**CURRENT_DATE**(),   INTERVAL 1 **MONTHS**)) -- if we don’t know which dates we need, it’s better to use the function FORMAT_DATE INTERVAL 
  **AND**
  FORMAT_DATE('%Y%m%d',**DATE_SUB**(**CURRENT_DATE**(), INTERVAL 1 **DAY**)) 
  ) 
**AND** geoNetwork.city = ‘**New** York’ -- choose orders made in New York City

如果订单信息记录在单独的表中,而不是在嵌套表中,则必须使用 JOIN 将包含订单信息的表和包含会话数据的表组合起来,以便找出订单是在哪个会话中生成的。

更多子查询选项

如果需要从多级嵌套字段中提取数据,可以使用 SELECT 和 WHERE 添加子查询。例如,在 OWOX BI 会话流表中,另一个子表 product 被写入 hits 子表。product 子表收集通过增强的电子商务数组传输的产品数据。如果网站上建立了增强型电子商务,并且用户已经查看了产品页面,则该产品的特征将被记录在产品子表中。

为了获得这些产品特征,在主查询中需要一个子查询。对于每个产品特性,在括号中会创建一个单独的选择子查询:

**SELECT** 
  column_name1, -- list the other columns you want to receive
  column_name2,
  (**SELECT** productBrand **FROM** UNNEST(hits.product)) **AS**    hits_product_productBrand,
  (**SELECT** productRevenue **FROM** UNNEST(hits.product)) **AS** hits_product_productRevenue, -- list product features
  (**SELECT** localProductRevenue **FROM** UNNEST(hits.product)) **AS** hits_product_localProductRevenue,
  (**SELECT** productPrice **FROM** UNNEST(hits.product)) **AS** hits_product_productPrice,
**FROM** `project_name.dataset_name.owoxbi_sessions_YYYYMMDD`

由于标准 SQL 的功能,构建查询逻辑和编写代码变得更加容易。相比之下,在遗留 SQL 中,您需要编写这种类型的阶梯:

**SELECT** 
  column_name1,
  column_name2, 
  column_name3 
**FROM** (
  **SELECT** table_name.some_column **AS** column1…
  **FROM** table_name
)

对外部来源的请求

使用标准 SQL,您可以直接从 Google Bigtable、Google Cloud Storage、Google Drive 和 Google Sheets 访问 BigQuery 表。
也就是说,不用将整个表加载到 BigQuery 中,只需一次查询就可以删除数据,选择需要的参数,上传到云存储。

更多用户功能(UDF)

如果你需要使用一个没有记录的公式,用户定义函数(UDF)会帮助你。在我们的实践中,这种情况很少发生,因为标准的 SQL 文档几乎涵盖了数字分析的所有任务。

在标准 SQL 中,用户自定义函数可以用 SQL 或 JavaScript 编写;传统 SQL 只支持 JavaScript。这些函数的参数是列,它们取的值是操作列的结果。在标准 SQL 中,函数可以在与查询相同的窗口中编写。

更多连接条件

在传统 SQL 中,连接条件可以基于等式或列名。除了这些选项之外,标准 SQL 方言还支持通过不等式和任意表达式进行连接。

例如,为了识别不公平的 CPA 伙伴,我们可以选择在交易的 60 秒内源被替换的会话。要在标准 SQL 中做到这一点,我们可以在连接条件中添加一个不等式:

#standardSQL
**SELECT** *
**FROM** 
  (
  **SELECT**
  traff.clientId **AS** clientId,
  traff.page_path **AS** pagePath,
  traff.traffic_source **AS** startSource,
  traff.traffic_medium **AS** startMedium,
  traff.time **AS** startTime,
  aff.evAction **AS** evAction,
  aff.evSource **AS** finishSource,
  aff.evMedium **AS** finishMedium,
  aff.evCampaign **AS** finishCampaign,
  aff.time **AS** finishTime,
  aff.isTransaction **AS** isTransaction,
  aff.pagePath **AS** **link**,
  traff.time-aff.time **AS** diff
  **FROM**
    (
    **SELECT** 
    fullVisitorID **AS** clientId,
    h.page.pagePath **AS** page_path,
    trafficSource.source **AS** traffic_source,
    trafficSource.medium **AS** traffic_medium,
    trafficSource.campaign **AS** traffic_campaign,
    date,
    SAFE_CAST(visitStartTime+h.time/1000 **AS** INT64) **AS** **time**
      **FROM** `demoproject.google_analytics_sample.ga_sessions_20190301`,
      UNNEST (hits) **AS** h
      **WHERE** trafficSource.medium != 'cpa'
      ) **AS** traff
**JOIN** (
  **SELECT** 
  total.date date,
  total.time **time**,
  total.clientId **AS** clientId,
  total.eventAction **AS** evAction,
  total.source **AS** evSource,
  total.medium **AS** evMedium,
  total.campaign **AS** evCampaign,
  tr.eventAction **AS** isTransaction,
  total.page_path **AS** pagePath
  **FROM** 
  (
  **SELECT** 
  fullVisitorID **AS** clientId,
  h.page.pagePath **AS** page_path,
  h.eventInfo.eventAction **AS** eventAction, 
  trafficSource.source **AS** **source**,
  trafficSource.medium **AS** **medium**,
  trafficSource.campaign **AS** campaign,
  date,
  SAFE_CAST(visitStartTime+h.time/1000 **AS** INT64) **AS** **time**
  **FROM** `demoproject.google_analytics_sample.ga_sessions_20190301`,
  UNNEST(hits) **AS** h
    **WHERE**
    trafficSource.medium ='cpa'
    ) **AS** total
**LEFT** **JOIN** 
  (
  **SELECT**
  fullVisitorID **AS** clientId,
  date,
  h.eventInfo.eventAction **AS** eventAction,
  h.page.pagePath pagePath,
  SAFE_CAST(visitStartTime+h.time/1000 **AS** INT64) **AS** **time**
  **FROM** `demoproject.google_analytics_sample.ga_sessions_20190301`,
  UNNEST(hits) **AS** h
  **WHERE** h.eventInfo.eventAction = 'typ_page'
  **AND** h.type = 'EVENT'
  **GROUP** **BY** 1, 2, 3, 4, 5
  ) **AS** tr
**ON** total.clientId=tr.clientId
**AND** total.date=tr.date
**AND** tr.time>total.time -- JOIN tables by inequality. Pass the additional WHERE clause that was needed in Legacy SQL
**WHERE** tr.eventAction = 'typ_page'
  ) **AS** aff
**ON** traff.clientId = aff.clientId
)
**WHERE** diff> -60
**AND** diff<0
  **GROUP** **BY** 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
  **ORDER** **BY** clientId, finishTime

标准 SQL 在连接方面的唯一限制是,它不允许使用 WHERE column IN (SELECT …)形式的子查询进行半连接:

#legacySQL
**SELECT**
  mother_age,
  **COUNT**(mother_age) total
**FROM**
  [bigquery-**public**-**data**:samples.natality]
**WHERE** -- such a construction cannot be used in Standard SQL
  state **IN** (**SELECT**
              state
            **FROM**
              (**SELECT**
                 state,
                 **COUNT**(state) total
               **FROM**
                 [bigquery-**public**-**data**:samples.natality]
               **GROUP** **BY**
                 state
               **ORDER** **BY**
                 total **DESC**
               **LIMIT** 10))
  **AND** mother_age > 50
**GROUP** **BY**
  mother_age
**ORDER** **BY**
  mother_age **DESC**

出错的可能性更小

如果条件不正确,传统 SQL 中的某些函数会返回 NULL。例如,如果您的计算中出现了被零除的情况,查询将被执行,空条目将出现在表的结果行中。这可能会掩盖查询或数据中的问题。

标准 SQL 的逻辑更简单。如果条件或输入数据不正确,查询将生成错误,例如被零除,因此您可以快速更正查询。以下检查嵌入在标准 SQL 中:

  • +、-、×、总和、AVG、标准偏差的有效值
  • 被零除

请求运行更快

由于对传入数据进行了初步过滤,用标准 SQL 编写的连接查询比用传统 SQL 编写的连接查询要快。首先,查询选择符合连接条件的行,然后处理它们。未来,Google BigQuery 将致力于提高标准 SQL 的查询速度和性能。

表格可以编辑:插入和删除行,更新

数据操作语言(DML)函数在标准 SQL 中可用。这意味着您可以通过编写查询的同一个窗口来更新表以及在表中添加或删除行。例如,使用 DML,您可以将两个表中的数据合并成一个表:

#standardSQL
**MERGE** dataset.Inventory **AS** T
**USING** dataset.NewArrivals **AS** S
**ON** T.ProductID = S.ProductID
**WHEN** **MATCHED** **THEN**
  **UPDATE** **SET** quantity = T.quantity + S.quantity
**WHEN** **NOT** **MATCHED** **THEN**
  **INSERT** (ProductID, quantity) **VALUES** (ProductID, quantity)

代码更容易阅读和编辑

使用标准 SQL,复杂的查询不仅可以用 SELECT 启动,还可以用 With 启动,从而使代码更易于阅读、注释和理解。这也意味着更容易防止自己的错误和纠正别人的错误。

#standardSQL
WITH total_1 AS ( -- the first subquery in which the intermediate indicator will be calculated
    **SELECT**
        **id**,
        metric1,
       **SUM**(metric2) **AS** total_sum1
    **FROM** `project_name.dataset_name.owoxbi_sessions_YYYYMMDD`
    **GROUP** **BY**
        **id**, metric
),
total_2 **AS** ( -- the second subquery
    **SELECT**
        **id**,
        metric1,
        **SUM**(metric2) **AS** total_sum2
    **FROM** `project_name.dataset_name.owoxbi_sessions_YYYYMMDD`
           **GROUP** **BY**
        **id**, metric1
),
total_3 **AS** ( -- the third subquery
    **SELECT**
        **id**,
        metric,
       **SUM**(metric2) **AS** total_sum3
       **FROM** `project_name.dataset_name.owoxbi_sessions_YYYYMMDD`
    **GROUP** **BY**
        **id**, metric
)
**SELECT** *,
**ROUND**(100*( total_2.total_sum2 - total_3.total_sum3) / total_3.total_sum3, 3) **AS** **difference** -- get the difference index: subtract the value of the second subquery from the value of the third; divide by the value of the third 
**FROM** total_1
**ORDER**  **BY** 1, 2

如果有分几个阶段完成的计算,使用 with 运算符会很方便。首先,您可以在子查询中收集中间指标,然后进行最终计算。

谷歌云平台(GCP)包括 BigQuery,是一个处理大数据的全周期平台,从组织数据仓库或数据云到运行科学实验以及预测和说明性分析。随着标准 SQL 的引入,BigQuery 正在扩大其受众。对于营销分析师、产品分析师、数据科学家和其他专家团队来说,与 GCP 合作正变得越来越有趣。

标准 SQL 的功能和用例示例

在 OWOX BI,我们经常使用使用标准的 Google Analytics 360 export to Google big query 或 OWOX BI 管道编译的表。在下面的例子中,我们将研究针对这些数据的 SQL 查询的细节。

1.选择时间间隔的数据

在 Google BigQuery 中,您站点的用户行为数据存储在通配符表中(带星号的表);每天形成一个单独的表。这些表的名称相同:只是后缀不同。后缀是 YYYYMMDD 格式的日期。例如,表 owoxbi_sessions_20190301 包含 2019 年 3 月 1 日的会话数据。

我们可以在一个请求中直接引用一组这样的表,以便获得数据,例如,从 2019 年 2 月 1 日到 2 月 28 日。为此,我们需要在 FROM 中用*替换 YYYYMMDD,在 WHERE 中,我们需要为时间间隔的开始和结束指定表后缀:

#standardSQL
**SELECT** sessionId, 
**FROM** `project_name.dataset_name.owoxbi_sessions_*`
**WHERE** _TABLE_SUFFIX **BETWEEN** �' AND �'

我们并不总是知道收集数据的具体日期。例如,每周我们可能需要分析过去三个月的数据。为此,我们可以使用 FORMAT_DATE 函数:

#standardSQL
**SELECT**
 <enumerate **field** **names**>
**FROM** `project_name.dataset_name.owoxbi_sessions_*`
**WHERE** 
_TABLE_SUFFIX **BETWEEN** FORMAT_DATE('%Y%m%d',**DATE_SUB**(**CURRENT_DATE**(), INTERVAL 3 **MONTHS**))
**AND**
FORMAT_DATE('%Y%m%d',**DATE_SUB**(**CURRENT_DATE**(), INTERVAL 1 **DAY**))

在 BETWEEN 之后,我们记录第一个表的后缀。短语 CURRENT_DATE(),INTERVAL 3 MONTHS 表示从当前日期开始选择最近 3 个月的数据。第二个表后缀在和之后格式化。需要将间隔的结束标记为昨天:CURRENT_DATE(),间隔 1 天。

2.检索用户参数和指标

Google Analytics 导出表中的用户参数和指标被写入嵌套的 hits 表以及 customDimensions 和 customMetrics 子表。所有定制维度都记录在两列中:一列是站点上收集的参数数量,另一列是它们的值。下面是一次点击传输的所有参数的样子:

为了将它们解包并在单独的列中写入必要的参数,我们使用以下 SQL 查询:

-- Custom Dimensions (in the line below index - the number of the user variable, which is set in the Google Analytics interface; dimension1 is the name of the custom parameter, which you can change as you like. For each subsequent parameter, you need to write the same line: (**SELECT** **MAX**(**IF**(**index**=1, **value**, NULL)) **FROM** UNNEST(hits.customDimensions)) **AS** dimension1, 
-- Custom Metrics: the index below is the number of the user metric specified in the Google Analytics interface; metric1 is the name of the metric, which you can change as you like. For each of the following metrics, you need to write the same line:  (**SELECT** **MAX**(**IF**(**index**=1, **value**, NULL)) **FROM** UNNEST(hits.customMetrics)) **AS** metric1

请求看起来是这样的:

#standardSQL
**SELECT** <**column** name1>,
<column_name2>, -- list column names
(**SELECT** **MAX**(**IF**(**index**=1, **value**, NULL)) **FROM** UNNEST(hits.customDimensions)) **AS** page_type,
(**SELECT** **MAX**(**IF**(**index**=2, **value**, NULL)) **FROM** UNNEST(hits.customDimensions)) **AS** visitor_type, -- produce the necessary custom dimensions
(**SELECT** **MAX**(**IF**(**index**=1, **value**, NULL)) **FROM** UNNEST(hits.customMetrics)) **AS** metric1 -- produce the necessary custom metrics
<column_name3> -- if you need more columns, continue to list
**FROM** `project_name.dataset_name.owoxbi_sessions_20190201`

在下面的截图中,我们从 Google BigQuery 中的 Google Analytics 360 演示数据中选择了参数 1 和 2,并将其命名为 page_type 和 client_id。每个参数都记录在单独的列中:

3.按流量来源、渠道、活动、城市和设备类别计算会话数量

如果您计划在 Google Data Studio 中可视化数据,并按城市和设备类别进行过滤,这样的计算非常有用。使用计数窗口功能很容易做到这一点:

#standardSQL
**SELECT**
<column_name 1>, -- choose any columns 
**COUNT** (**DISTINCT** sessionId) **AS** total_sessions, -- summarize the session IDs to find the total number of sessions
**COUNT**(**DISTINCT** sessionId) **OVER**(**PARTITION** **BY** date, geoNetwork.city, session.device.deviceCategory, trafficSource.source, trafficSource.medium, trafficSource.campaign) **AS** part_sessions -- summarize the number of sessions by campaign, channel, traffic source, city, and device category
**FROM** `project_name.dataset_name.owoxbi_sessions_20190201`

4.合并几个表中的相同数据

假设您在几个 BigQuery 表中收集已完成订单的数据:一个收集来自商店 A 的所有订单,另一个收集来自商店 b 的订单。您希望将它们合并到一个包含以下列的表中:

  • client _ id——标识唯一购买者的号码
  • transaction_created —时间戳格式的订单创建时间
  • 交易标识—订单编号
  • is_approved —订单是否已确认
  • 交易 _ 收入—订单金额

在我们的示例中,从 2018 年 1 月 1 日到昨天的订单必须在表中。为此,从每组表中选择适当的列,为它们指定相同的名称,并用 UNION ALL 合并结果:

#standardSQL
**SELECT** 
cid **AS** client_id, 
order_time **AS** transaction_created,
order_status **AS** is_approved,
order_number **AS** transaction_id
**FROM** `project_name.dataset_name.table1_*`
**WHERE** (
  _TABLE_SUFFIX **BETWEEN** �'
  AND
  FORMAT_DATE('%Y%m%d',DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
  )UNION ALL 
SELECT
userId AS client_id,
created_timestamp AS transaction_created,
operator_mark AS  is_approved,
transactionId AS transaction_id
FROM `project_name.dataset_name.table1_*`
WHERE (
  _TABLE_SUFFIX BETWEEN �'
  **AND**
  FORMAT_DATE('%Y%m%d',**DATE_SUB**(**CURRENT_DATE**(), INTERVAL 1 **DAY**))
  )
**ORDER** **BY** transaction_created **DESC**

5.创建业务信道组的字典

当数据进入 Google Analytics 时,系统会自动确定特定转换所属的群体:直接搜索、有机搜索、付费搜索等等。为了识别一组频道,Google Analytics 查看转换的 UTM 标签,即 utm_source 和 utm_medium。你可以在谷歌分析帮助中阅读更多关于渠道组和定义规则的内容。

如果 OWOX BI 客户端想要将它们自己的名称分配给通道组,我们创建一个字典,哪个转换属于一个特定的通道。为此,我们使用条件 CASE 操作符和 REGEXP_CONTAINS 函数。此函数选择指定正则表达式出现的值。

我们建议从您的谷歌分析来源列表中选择姓名。以下是如何将此类条件添加到请求正文的示例:

#standardSQL
**SELECT** 
**CASE** 
**WHEN** (REGEXP_CONTAINS (**source**, 'yandex') **AND** **medium** = 'referral' **THEN** 'Organic Search' 
**WHEN** (REGEXP_CONTAINS (**source**, 'yandex.market')) **AND** **medium** = 'referral' **THEN** 'Referral'
**WHEN** (REGEXP_CONTAINS (**source**, '^(go.mail.ru|google.com)$') **AND** **medium** = 'referral') **THEN** 'Organic Search'
**WHEN** **medium** = 'organic' **THEN** 'Organic Search'
**WHEN** (**medium** = 'cpc') **THEN** 'Paid Search'
**WHEN** REGEXP_CONTAINS (**medium**, '^(sending|email|mail)$') **THEN** 'Email'
    **WHEN** REGEXP_CONTAINS (**source**, '(mail|email|Mail)') **THEN** 'Email'
    **WHEN** REGEXP_CONTAINS (**medium**, '^(cpa)$') **THEN** 'Affiliate'
    **WHEN** **medium** = 'social' **THEN** 'Social'
    **WHEN** **source** = '(direct)' **THEN** 'Direct'
 **WHEN** REGEXP_CONTAINS (**medium**, 'banner|cpm') **THEN** 'Display'
    **ELSE** 'Other'
  **END** channel_group -- the name of the column in which the channel groups are written
**FROM** `project_name.dataset_name.owoxbi_sessions_20190201`

如何切换到标准 SQL

如果您还没有切换到标准 SQL,您可以在任何时候这样做。主要是避免在一个请求中混合使用方言。

备选方案 1。切换到 Google BigQuery 界面

默认情况下,在旧的 BigQuery 接口中使用遗留 SQL。要在方言之间切换,单击查询输入字段下的显示选项,取消选中 SQL 方言旁边的使用传统 SQL 框。

默认情况下,新接口使用标准 SQL。在这里,您需要转到“更多”选项卡来切换方言:

选项 2。在请求的开头写前缀

如果您没有勾选请求设置,您可以从所需的前缀(#standardSQL 或#legacySQL)开始:

#standardSQL
**SELECT**
  weight_pounds, state, **year**, gestation_weeks
**FROM**
  `bigquery-public-data.samples.natality`
**ORDER** **BY** weight_pounds **DESC**
**LIMIT** 10;

在这种情况下,Google BigQuery 将忽略界面中的设置,并使用前缀中指定的方言运行查询。

如果您有使用 Apps 脚本按计划启动的视图或保存的查询,请不要忘记在脚本中将 useLegacySql 的值更改为 false:

var job = {
configuration: {
  query: {
    query: '**INSERT** **INTO** MyDataSet.MyFooBarTable (**Id**, Foo, Date) **VALUES** (1, \'bar\', current_Date);',
    useLegacySql: false
    }

选项 3。过渡到视图的标准 SQL

如果您使用 Google BigQuery 而不是表,而是视图,那么这些视图就不能用标准的 SQL 方言访问。也就是说,如果您的演示文稿是用遗留 SQL 编写的,您就不能用标准 SQL 向它写请求。

要将视图转换为标准 SQL,您需要手动重写创建它的查询。最简单的方法是通过 BigQuery 接口。

1.打开视图:

2.单击详细信息。查询文本应该会打开,下面会出现“编辑查询”按钮:

现在,您可以根据标准 SQL 的规则编辑请求。
如果您计划继续使用该请求作为演示文稿,请在完成编辑后单击保存视图。

兼容性、语法功能、运算符、函数

和睦相处

由于标准 SQL 的实现,您可以直接从 BigQuery 访问存储在其他服务中的数据:

  • Google 云存储日志文件
  • Google Bigtable 中的交易记录
  • 其他来源的数据

这使得您可以使用谷歌云平台产品来完成任何分析任务,包括基于机器学习算法的预测和说明性分析。

查询语法

标准方言中的查询结构几乎与传统语言中的相同:

表和视图的名称用句点(句号)分隔,整个查询用重音符括起来:project _ name . data _ name _ name . table _ name big query-public-data . samples . natality '

查询的完整语法,以及对每个操作符中可以包含的内容的解释,被编译为 BigQuery 文档中的一个模式。

标准 SQL 语法的特征:

  • 在 SELECT 语句中列出字段需要逗号。
  • 如果在 FROM 之后使用 UNNEST 运算符,则在 UNNEST 之前会放置一个逗号或连接。
  • FROM 前面不能加逗号。
  • 两个查询之间的逗号相当于交叉连接,所以要小心使用。
  • 连接不仅可以通过列或等式来完成,还可以通过任意表达式和不等式来完成。
  • 可以在 SQL 表达式的任何部分(在 SELECT、FROM、WHERE 等中)编写复杂的子查询。).在实践中,还不可能像在其他数据库中那样使用 WHERE column_name IN (SELECT …)这样的表达式。

经营者

在标准 SQL 中,运算符定义数据的类型。例如,数组总是写在方括号[]中。运算符用于比较、匹配逻辑表达式(NOT、or、AND)以及算术计算。

功能

标准 SQL 比 Legacy 支持更多特性:传统聚合(sum、number、minimum、maximum);数学、字符串和统计函数;以及 HyperLogLog ++等罕见格式。

在标准方言中,有更多处理日期和时间戳的函数。Google 文档中提供了完整的特性列表。最常用的函数用于处理日期、字符串、聚合和窗口。

1。聚合函数

COUNT (DISTINCT column_name)计算列中唯一值的数量。例如,假设我们需要统计 2019 年 3 月 1 日来自移动设备的会话数。由于一个会话号可以在不同的行上重复,所以我们只想计算唯一的会话号值:

#standardSQL
**SELECT** 
**COUNT** (**DISTINCT** sessionId) **AS** sessions
**FROM**  `project_name.dataset_name.owoxbi_sessions_20190301`
**WHERE** device.deviceCategory = 'mobile'

SUM(column _ name)-列中值的总和

#standardSQL
**SELECT** 
**SUM** (hits.transaction.transactionRevenue) **AS** revenue
**FROM**  `project_name.dataset_name.owoxbi_sessions_20190301`,
UNNEST (hits) **AS** hits -- unpacking the nested field hits
**WHERE** device.deviceCategory = 'mobile'

MIN(列名)| MAX(列名)-列中的最小值和最大值。这些函数便于检查表中数据的分布。

2。窗口(分析)功能

分析函数考虑的不是整个表的值,而是某个窗口的值,即您感兴趣的一组行。也就是说,您可以在一个表中定义段。例如,您可以为城市、设备类别等计算 SUM(收入),而不是为所有线路。通过向分析函数 SUM、COUNT 和 AVG 以及其他聚合函数添加 OVER 条件(PARTITION BY column_name ),可以转换这些函数。

例如,您需要按流量来源、渠道、活动、城市和设备类别统计会话数量。在这种情况下,我们可以使用下面的表达式:

**SELECT**
        date,
        geoNetwork.city,
        t.device.deviceCategory,
        trafficSource.source,
        trafficSource.medium,
        trafficSource.campaign,
**COUNT**(**DISTINCT** sessionId) **OVER**(**PARTITION** **BY** date, geoNetwork.city, session.device.deviceCategory, trafficSource.source, trafficSource.medium, trafficSource.campaign) **AS** segmented_sessions
**FROM**  `project_name.dataset_name.owoxbi_sessions_20190301` t

超过确定将进行计算的窗口。PARTITION BY 指示应该对哪些行进行分组以进行计算。在某些函数中,需要用 ORDER BY 指定分组的顺序。

有关窗口函数的完整列表,请参见 BigQuery 文档。

3。字符串功能

当您需要更改文本、将文本格式化为一行或粘附列的值时,这些选项非常有用。例如,如果您想从标准的 Google Analytics 360 导出数据中生成唯一的会话标识符,字符串函数非常有用。让我们考虑一下最流行的字符串函数。

SUBSTR 剪切部分字符串。在请求中,这个函数被写成 SUBSTR (string_name,0.4)。第一个数字指示从行首跳过多少个字符,第二个数字指示删除多少个数字。例如,假设您有一个日期列,其中包含字符串格式的日期。在这种情况下,日期如下所示:20190103。如果您想从这一行中提取年份,SUBSTR 将帮助您:

#standardSQL
**SELECT**
**SUBSTR**(date,0,4) **AS** **year**
**FROM** `project_name.dataset_name.owoxbi_sessions_20190301`

CONCAT (column_name 等。)粘合价值观。让我们使用上一个示例中的日期列。假设你希望所有的日期都这样记录:2019–03–01。要将日期从当前格式转换成这种格式,可以使用两个字符串函数:首先,用 SUBSTR 剪切字符串的必要部分,然后通过连字符将它们粘合起来:

#standardSQL
**SELECT**
**CONCAT**(**SUBSTR**(date,0,4),"-",**SUBSTR**(date,5,2),"-",**SUBSTR**(date,7,2)) **AS** date
**FROM** `project_name.dataset_name.owoxbi_sessions_20190301`

REGEXP_CONTAINS 返回正则表达式所在列的值:

#standardSQL
**SELECT** 
**CASE**
**WHEN** REGEXP_CONTAINS (**medium**, '^(sending|email|mail)$') **THEN** 'Email'
    **WHEN** REGEXP_CONTAINS (**source**, '(mail|email|Mail)') **THEN** 'Email'
    **WHEN** REGEXP_CONTAINS (**medium**, '^(cpa)$') **THEN** 'Affiliate'
**ELSE** 'Other'
**END** Channel_groups
**FROM** `project_name.dataset_name.owoxbi_sessions_20190301`

该函数可以在 SELECT 和 WHERE 中使用。例如,在 WHERE 中,您可以使用它来选择特定页面:

WHERE REGEXP_CONTAINS(hits.page.pagePath, 'land[123]/|/product-order')

4。日期功能

通常,表格中的日期以字符串格式记录。如果您计划在 Google Data Studio 中可视化结果,需要使用 PARSE_DATE 函数将表中的日期转换为日期格式。

PARSE_DATE 将 1900–01–01 格式的字符串转换为日期格式。
如果表格中的日期看起来不同(例如,19000101 或 01_01_1900),您必须先将它们转换成指定的格式。

#standardSQL
**SELECT** 
PARSE_DATE('%Y-%m-%d', date)  **AS** date_new
**FROM** `project_name.dataset_name.owoxbi_sessions_20190301`

DATE_DIFF 计算两个日期之间经过的时间,以天、周、月或年为单位。如果您需要确定用户看到广告和下订单之间的时间间隔,这很有用。下面是该函数在请求中的样子:

#standardSQL 
**SELECT** DATE_DIFF( 
PARSE_DATE('%Y%m%d', date1), PARSE_DATE('%Y%m%d', date2), **DAY** 
) **days** -- convert the date1 and date2 lines to the DATE format; choose units to show the difference (DAY, WEEK, MONTH, etc.)
**FROM** `project_name.dataset_name.owoxbi_sessions_20190301`

如果你想了解更多关于所列功能的信息,请阅读big query Google Features——详细回顾

营销报告的 SQL 查询

标准 SQL 方言允许企业通过深度细分、技术审计、营销 KPI 分析和识别 CPA 网络中的不公平承包商,从数据中提取最大限度的信息。下面是一些商业问题的例子,在这些问题中,对 Google BigQuery 中收集的数据进行 SQL 查询将会对您有所帮助。

1.ROPO 分析:评估线上活动对线下销售的贡献。要执行 ROPO 分析,您需要将在线用户行为数据与来自 CRM、呼叫跟踪系统和移动应用程序的数据结合起来。

如果在第一个和第二个数据库中有一个关键字——一个对每个用户都是唯一的公共参数(例如,用户 ID)——您可以跟踪:
哪些用户在商店购买商品之前访问了网站
用户在网站上的行为
用户花了多长时间做出购买决定
哪些活动对线下购买量的增加最大。

2.通过参数的任意组合对客户进行细分,从网站行为(访问的页面、浏览的产品、购买前访问网站的次数)到忠诚卡号和购买的商品。

3.找出哪些注册会计师的合作伙伴是在恶意工作,并取代 UTM 标签。

4.通过销售漏斗分析用户的进度。

我们已经用标准的 SQL 方言准备了一些查询。如果您已经从您的网站、广告来源以及 Google BigQuery 中的 CRM 系统收集了数据,您可以使用这些模板来解决您的业务问题。只需用自己的项目名称、数据集和表替换 BigQuery 中的项目名称、数据集和表。在集合中,您将收到 11 个 SQL 查询。

对于使用从 Google Analytics 360 到 Google BigQuery 的标准导出收集的数据:

  • 任何参数上下文中的用户操作
  • 关键用户操作的统计数据
  • 查看特定产品页面的用户
  • 购买特定产品的用户的行为
  • 通过任何必要的步骤设置漏斗
  • 内部搜索网站的有效性

对于使用 OWOX BI 在 Google BigQuery 中收集的数据:

  • 按来源和渠道划分的归因消费
  • 城市吸引游客的平均成本
  • 按来源和渠道列出的毛利润 ROAS
  • 按付款方式和交付方式列出的 CRM 中的订单数量
  • 各城市的平均交付时间
posted @ 2024-10-16 09:00  绝不原创的飞龙  阅读(322)  评论(0)    收藏  举报