探索纽约街区的品味
k-均值聚类——一种无监督的机器学习算法
这是一篇图文并茂的文章。要访问 Jupyter 笔记本— 点击这里
介绍
背景
纽约市是美国人口最多的城市,是联合国总部所在地,也是重要的国际外交中心。它可能是地球上最多样化的城市,因为它是 860 多万人和 800 多种语言的家园。
正如一篇文章中所引用的— 食物告诉我们的文化
“传统美食是一代一代传下来的。它也是一种文化身份的表达。移民无论去哪里都会带着他们国家的食物,烹饪传统食物是他们搬到新地方时保存自己文化的一种方式。”
问题
毫无疑问,食物的多样性是一个多民族大都市的重要组成部分。这个项目的想法是将纽约市的街区分成几个主要的区域,并研究他们的烹饪。一个可取的意图是检查附近集群的饮食习惯和口味。进一步的研究可能会揭示食物是否与社区的多样性有任何关系。这个项目将通过利用 Foursquare 的“Places API”和“k-means clustering”无监督机器学习算法的场地数据,帮助了解一个街区的多样性。探索性数据分析(EDA)将有助于进一步发现社区的文化和多样性。
利益相关者
这种可量化的分析可以用来理解不同文化和美食在“地球上最多样化的城市——纽约市”的分布。此外,它还可以被愿意开餐馆的新食品供应商使用。或者由政府机构更好地检查和研究他们城市的文化多样性。
数据
为了检查上述内容,使用了以下数据来源:
纽约市数据集
链接:https://geo.nyu.edu/catalog/nyu_2451_34572
这个纽约市街区名称点文件是作为纽约市街区指南创建的,它出现在网络资源“纽约:街区之城”上标签质心的最佳估计值是以 1:1,000 的比例建立的,但理想情况下以 1:50,000 的比例查看。该数据集将以 json 格式提供纽约市附近的地址。json 的摘录如下:
四方应用编程接口
链接:https://developer.foursquare.com/docs
Foursquare API 是一个位置数据提供者,它将被用来进行 RESTful API 调用,以检索不同街区的场馆数据。这是到 Foursquare 场馆类别层级的链接。从所有街区检索到的场馆大致分为“艺术&娱乐”、“学院&大学”、“活动”、“美食”、“夜生活场所”、“户外&娱乐”等。API 调用的摘录如下:
方法学
下载并探索纽约市数据集
为了分割纽约市的街区,需要一个包含 5 个区和街区的数据集,每个区都有各自的纬度和经度坐标。这个数据集是使用提到的 URL 下载的。
一旦。json 文件被下载,它被分析以理解文件的结构。URL 返回一个 python 字典,所有相关数据都在 features 键中,features 键基本上是一个邻域列表。通过遍历数据并一次一行地填充数据帧,字典被转换成 pandas 数据帧。
结果,创建了一个数据帧,其中包含纽约市街区的区、街区、纬度和经度细节。
经分析发现,该数据框架由 5 个区和 306 个街区组成。
此外,“geopy”库用于获取纽约市的纬度和经度值,返回的值为纬度:40.71,经度:-74.01。
然后,通过创建纽约市地图,将精选的数据帧用于可视化,在地图上叠加街区。以下描述是使用 python“folium”库生成的地图。
对 Foursquare 的 RESTful API 调用
Foursquare API 用于探索邻域并对其进行分割。为了访问 API,定义了“客户端 ID”、“客户端秘密”和“版本”。
Foursquare 上有许多端点可用于各种 GET 请求。但是,为了探索美食,要求所有提取的地点都来自“食物”类别。检索 Foursquare 场所类别层次结构,并进一步分析返回的请求。
分析发现,场馆有 10 个大类或母类,所有其他子类都包含在这些大类或母类下。
如前所述,“食物”类别是感兴趣的问题。我们创建了一个函数来返回一个字典,其中包含“食品”及其子类别的“类别 ID”和“类别名称”。
上面的函数获取父类别 ID,并返回所有子类别的类别名称和类别 ID。
为了进一步理解 GET 请求的结果,探索“纽约市”数据集的第一邻域。返回的第一个邻域是“Wakefield ”,纬度为 40.89,经度为-73.85。然后,创建一个 GET 请求 URL 来搜索“类别 ID”= ‘4d 4b 7105d 754 a 06374d 81259’,即“食物”的“类别 ID”,半径= 500 米的地点。然后检查返回的请求,如下所示:
因为目标是根据纽约市附近的“食物”来分割纽约市的邻近地区,所以还需要从所有 306 个邻近地区的场所获取该数据。
为了克服上述过程的冗余,创建了函数“getNearbyFood”。这个函数遍历纽约市的所有街区,并创建一个半径= 500,限制= 100 的 API 请求 URL。通过限制,定义了最多应该返回 100 个附近的场所。此外,GET 请求是向 Foursquare API 发出的,只从中提取每个附近地点的相关信息。然后,数据被追加到 python“列表”中。最后,python“列表”被展开或展平,以将其附加到函数返回的数据帧中。
令人好奇的是,如果在 GET 请求中指定了顶级类别,Foursquare API 会返回所有子类别。
泡菜
Pickle 是一个非常重要且易于使用的库。它用于序列化从 GET 请求中检索到的信息,以使。“pkl”文件。这个文件稍后可以被反序列化以检索精确的 python 对象结构。这是至关重要的一步,因为它将阻止对 Foursquare API 的任何冗余请求,这些请求的收费超过了阈值限制。
返回的“数据帧”如下:
到目前为止,创建了两个 python“数据帧”:
1.“neighborhoods ”,包含纽约市邻近地区的区、邻近地区、纬度和经度细节,以及
2.“nyc _ venues”是“neighborhoods”数据框架与其“Food”类别场馆的合并,使用“半径”= 500 米和“限制”= 100 进行搜索。此外,每个场馆都有自己的纬度,经度和类别。
探索性数据分析
合并后的数据框架“nyc _ venues”包含所有必需的信息。确定该数据帧的大小,并且发现总共有 14,047 个地点。
现在,重要的是要找出有多少独特的类别可以从所有返回的场馆策划。并且发现有 194 个这样的类别。
数据清理
重要的是要明白,这个项目的兴趣点是通过使用场地的类别对社区进行分类,从而了解社区的文化多样性。因此,重要的是从“数据框架”中移除所有具有一般化类别的场馆。这里,概括地说,这意味着这些分类的场所在不同的文化和饮食习惯中是常见的。这种类型的场所的类别的例子是咖啡店、咖啡馆等。因此,首先,所有独特的类别都被输入到一个 python“列表”中。
然后,手动将类别确定为“一般”(如上所述)。这种数据预处理完全取决于“数据分析师”的判断,可以根据需要进行修改。以下是列为“一般”的类别:
两个 python“列表”即“唯一 _ 类别”和“一般 _ 类别”的简单相减给出了进一步分析所需的所有类别的“列表”。python“列表”管理用于删除所有类别不在“食物类别”中的场所。
再次检查独特类别的数量,发现只有 92 个,而以前有 194 个。这意味着,几乎 50%的数据对于分析来说是一个噪音。这个重要的步骤,数据清理,有助于捕获感兴趣的数据点。
特征工程
现在,对每个街区进行单独分析,以了解其 500 米范围内最常见的菜肴。
上述过程是通过使用 python“熊猫”库的“一次热编码”功能实现的。一种热编码将分类变量(即“场地类别”)转换成可以提供给 ML 算法的形式,以便在预测中做得更好。
在转换分类变量时,如上所示,“邻域”列被添加回来。检查新数据帧“nyc_onehot”的大小,发现总共有大约 6,846 个数据点。
此外,统计每个街区中每个类别的场地数量。观察到,在数据帧的前五个邻近区域中,“安纳代尔”、“阿登高地”和“阿灵顿”在其 500 米附近具有 3、3、2 个“美国餐馆”。
前 10 个“场馆类别”也可以通过统计它们的出现次数来找到。下面的分析显示,“韩国餐厅”、“中国餐厅”、“加勒比海餐厅”、“印度餐厅”和“快餐店”位列前五。
数据可视化
使用 python 'seaborn '库将这 10 大类别分别绘制在条形图上。创建一个代码块,该代码块循环并绘制一个类别的前 10 个邻域的图形。
接下来,将邻域的行分组在一起,并通过取平均值来计算每个类别的出现频率。
由于限制被设置为 100,Foursquare API 将返回许多地点。但是一个社区的饮食习惯可以由它附近的前五个地点来定义。此外,所创建的数据帧然后被馈入相应邻域中的前 5 个最常见的场所类别。
机器学习
“k-means”是一种无监督的机器学习算法,它创建由于某些相似性而聚集在一起的数据点的聚类。该算法将用于计算可变聚类大小的每个聚类标签的邻域。
为了实现该算法,确定最佳的聚类数(即 k)是非常重要的。有两种最流行的方法,即“肘法”和“剪影法”。
肘法
对于不同的“k”值,肘形法计算样本到其最近聚类中心的平方距离之和。最佳聚类数是这样一个值,在该值之后,距离的平方和没有显著减少。
以下是该方法的实现(具有从 1 到 49 的不同数目的聚类):
有时,肘方法不能给出所需的结果,这种情况就发生了。由于距离的平方和逐渐减少,因此无法确定最佳的聚类数。为了解决这个问题,可以实现另一种方法,如下所述。
剪影法
正如维基百科中引用的那样——“侧影法衡量的是一个点与自己的聚类(内聚)相比与其他聚类(分离)的相似程度。”
下面是此方法的一个实现。由于需要至少 2 个聚类来定义相异度,聚类数(即“k”)将在 2 到 49 之间变化:
在 k = 2、k = 4 和 k = 8 处有一个峰值。两个和四个数量的聚类将非常广泛地聚类邻域。因此,簇的数量(即“k”)被选择为 8。
k 均值
以下代码块运行聚类数= 8 的 k-Means 算法,并打印分配给不同聚类的邻域计数:
此外,所管理的聚类标签被添加到数据帧,以获得基于其附近最常见的地点分割邻域的期望结果。现在,“neighborhoods _ venues _ sorted”与“nyc_data”合并,以添加每个街区的区、纬度和经度。
同样,纽约市的街区也是利用 python“叶”库可视化的。生成的以下地图显示了所需的纽约市街区分割:
结果
群集— 0
以下是 0 类分析的结果:
“加勒比海餐厅”对这一集群负有重大责任,在不同社区的“第一最常见地点”中出现了 18 次,其次是“快餐店”和“披萨店”,在“第二最常见地点”中出现了 7 次。此外,“加勒比海餐厅”在“第二常见场所”中出现了 2 次。另外,令人好奇的是,这些社区大部分都在纽约市的“布鲁克林”区。
因此,Cluster-0 是一个“加勒比海餐馆”占优势的聚类。
集群— 1
以下是第 1 类分析的结果:
“中国餐馆”在这个集群中有 18 次出现,其次是“比萨饼店”,在不同社区的“第一大常见场所”中有 8 次出现。此外,在“第二常见地点”中,“披萨店”出现了 10 次,其次是“中国餐馆”,出现了 8 次。此外,令人好奇的是,这些社区大部分都在纽约市的“皇后”区。
因此,Cluster-1 是“中国餐馆”和“比萨饼店”的组合。
群组— 2
以下是第 2 类分析的结果:
“意大利餐厅”是该集群的主要原因,出现了 27 次,其次是“披萨店”,在不同社区的“第一大常见场所”中出现了 16 次。此外,“意大利餐厅”出现了高达 16 次,其次是“披萨店”,在“第二大常见场所”中出现了 15 次。此外,令人好奇的是,这些社区大部分都在纽约市的“斯塔滕岛”和“皇后区”。
因此,Cluster-2 是“意大利餐馆”和“比萨饼店”的组合。披萨是一种意大利美食,因此集群-2 可以被称为“意大利餐厅”主导集群。
群组— 3
以下是第 3 类分析的结果:
这是所有聚类中最大的一个,这意味着大多数的邻居都聚集在其中。同样,如聚类 2 所示,“比萨饼店”和“意大利餐馆”在“第一最常见场所”和“第二最常见场所”中占据这些类别出现次数的前 2 位,但是,有趣的是,第 3 位主要由“美国餐馆”类别占据,这就是该聚类与聚类 2 分离的原因。
此外,令人好奇的是,这个集群中的社区平均分布在“曼哈顿”、“布鲁克林”和“皇后区”,其中“斯塔滕岛”有相当数量的社区。
因此,聚类-3 是“意大利餐馆”、“比萨饼店”和“美国餐馆”的组合,其中“美国餐馆”显示了从聚类-2 中分割该聚类的优势
群组— 4
以下是 Cluster-4 分析的结果:
在这个集群中,“披萨店”占据了所有其他类别,在不同社区的“第一最常见场所”中拍摄了 52 次,其次是“中国餐馆”,在“第二最常见场所”中拍摄了 12 次。此外,令人好奇的是,这些社区中的大多数几乎平均分布在纽约市的“斯塔滕岛”、“皇后区”和“布朗克斯区”。
因此,簇 4 可以被称为“比萨饼店”优势簇。
群组— 5
以下是 Cluster-5 分析的结果:
很明显,只有一个社区“灯塔山”是在这个集群下管理的。这种分割可以从“灯塔山”是其遗产的旅游景点这一事实中得到理解,它位于从斯塔滕岛东北角辐射出来的一连串山脉的最南端。这个街区有各种各样的美食,在最常见的 5 个地点列表中,因此是一个独立的集群。
因此,到目前为止,Cluster-5 可以说是非常出色的。
群组— 6
以下是 Cluster-6 分析的结果:
“快餐店”以 28 次出现在该集群中,其次是“披萨店”,在不同社区的“第一大常见场所”中有 14 次出现。此外,在“第二常见地点”出现次数中也可以看到相同的比例分布。
另外,令人好奇的是,这些社区大部分都在纽约市的“布朗克斯”和“皇后”区。
众所周知,虽然比萨饼是一种意大利美食,但它也是一种快餐。因此,集群 6 可以被称为“快餐店”优势集群。
群组— 7
以下是 Cluster-7 分析的结果:
“美国餐厅”在不同社区的“第一最常见场所”中出现了 14 次,对这一群体负有重大责任。此外,在“第二最常见的场所”有各种美食。另外,令人好奇的是,这些社区大多位于纽约市的“曼哈顿”和“布鲁克林”区。
因此,集群 7 可以被称为“美国餐馆”优势集群。
讨论
为了理解集群,进行了三个分析,即:
1.“区”的计数
2.“第一最常见地点”的计数
3.“第二常见场所”的数量
以上信息充分说明了基于邻域间相似性度量的聚类的基础现实。
将 k 均值无监督机器学习算法的结果列表:
比萨饼,谁不喜欢它。从分析中可以明显看出,披萨店是所有集群或社区中最常见的场所。因此,由于 Pizza Place 对纽约市来说是一个现成的地方,所以它被放在一边以重命名集群。
以下可能是由 k-Means 无监督机器学习算法分割和管理的聚类的名称:
集群 0 — 加勒比海
集群 1 — 中文
集群 2 — 意大利语
集群 3 — 意大利裔美国人
集群 4 — 披萨
集群 5 — 美食组合
集群 6 —快餐
集群 7 — 美国
结论
对多维数据集应用聚类算法、k-Means 或其他算法,可以得到非常有趣的结果,这有助于理解和可视化数据。纽约市的街区被非常简单地划分为八个集群,通过分析,可以根据该街区及其周围的场地类别对它们进行重命名。除了美国菜,意大利菜和中国菜在纽约市占主导地位,多样性统计也是如此。
通过使用当前纽约市的数据集以及对美食场所更感兴趣的 API 平台(如 Yelp 等),可以改进这个项目的结果,并使其更具好奇心。)这个项目的范围可以进一步扩大,以了解每个社区的动态,并向新的供应商建议一个有利可图的地点来开设他或她的食品店。此外,政府当局可以利用它来更好地检查和研究他们城市的文化多样性。
参考
Alex Aklson 和 Polong Lin 为 Coursera 上的“应用数据科学顶点”课程制作的笔记本
感谢您的阅读!如果你觉得这有帮助或者没有帮助,请在评论中告诉我。如果这篇文章有帮助, 分享一下 。
访问 Jupyter 笔记本— 点击此处
领英
[## Eklavya Saxena -东北大学-马萨诸塞州波士顿| LinkedIn
东北大学精通数据科学的研究生,在以下领域有 2 年以上的工作经验
linkedin.com](https://linkedin.com/in/EklavyaSaxena)
GitHub
纽约市是美国人口最多的城市,也是联合国总部和纽约市的所在地
github.com](https://github.com/eklavyasaxena/The-Battle-of-Neighborhoods/tree/master/Exploring-the-Taste-of-NYC-Neighborhoods)
探索东京街区:现实生活中的数据科学
网络抓取、Foursquare API、叶子地图等等
Tokyo! (Source: Louie Martinez)
作为最终的 IBM 顶点项目的一部分,我们了解了数据科学家在现实生活中的经历。期末作业的目标是定义一个商业问题,在网上寻找数据,并使用 Foursquare 位置数据来比较东京行政区内的不同地区(城市的选择取决于学生),以找出哪个社区适合开办餐馆业务(“想法”也取决于学生个人)。为了准备作业,我一步一步地完成了问题设计、数据准备和最终分析部分。详细的代码和图片在 Github 中给出,链接可以在文章末尾找到。
1.业务问题的讨论和背景:
问题陈述:日本东京靠近办公区的午餐餐馆的前景。
我现在住的东京是世界上人口最多的大都市。东京目前在全球经济实力指数中排名第三,绝对是创业的最佳地点之一。
During the daytime, specially in the morning and lunch hours, office areas provide huge opportunities for restaurants. Reasonably priced (one lunch meal 8$) shops are usually always full during the lunch hours (11 am — 2 pm) and, given this scenario, we will go through the benefits and pitfalls of opening a breakfast cum lunch restaurant in highly densed office places. Usually the profit margin for a decent restaurant lie within 15−20% range but, it can even go high enough to 35%, as discussed here. The core of Tokyo is made of 23 wards (municipalities) but, I will later concentrate on 5 most busiest business wards of Tokyo — Chiyoda (千代田区), Chuo (中央区), Shinjuku (新宿区), Shibuya (渋谷区) and Shinagawa (品川区), to target daily office workers.
我们将讨论这个项目的每个步骤,并分别解决它们。我首先概述了最初的数据准备,并描述了在东京开始邻里之战的未来步骤。
目标受众
什么类型的客户或群体会对这个项目感兴趣?
- 想投资或开餐馆的商务人员。这一分析将是一个全面的指南,以在午餐时间开设或扩大针对东京大量上班族的餐厅。
- 喜欢把自己的餐馆作为副业的自由职业者。这个分析会给出一个思路,开一个餐厅有多大的好处,这个生意有哪些利弊。
- 应届毕业生,寻找靠近办公室的合理午餐/早餐地点。
- 初露头角的数据科学家,他们希望实现一些最常用的探索性数据分析技术,以获得必要的数据,对其进行分析,并最终能够从中讲述一个故事。
2.数据准备:
2.1.从维基百科抓取东京病房表
我首先利用 Wiki 上的东京特殊病房页面来创建一个数据框。为此,我使用了请求和 Beautifulsoup4 库来创建一个包含东京 23 个区的名称、面积、人口和第一大区的数据框。我们从下面开始—
稍加处理后,数据帧如下所示
Data-frame from Wikipedia Table.
2.2.获取主要区域的坐标: Geopy 客户端
下一个目标是使用 Geopy 客户端的 geocoder 类获得这 23 个主要地区的坐标。使用下面的代码片段—
正如你所看到的,4 个坐标是完全错误的(文京、江东、大田、江户川),这是由于这些地区的名称写得与它们在这个数据框中的方式没有什么不同(例如 hong \u- Hongo),所以,我必须用从 google 搜索获得的值替换这些坐标。在和熊猫玩了一会儿之后,我可以得到一个排列良好的数据框,如下所示
2.3.东京主要行政区的平均地价:网络抓取
另一个可以指导我们以后决定哪个地区最适合开餐馆的因素是,23 个区的平均土地价格。我从刮“东京土地市场价值区域”网页上获得这些信息,与之前的维基页面类似。由于我想考虑第 1 节中提到的东京 5 个最繁忙的商业区,数据框如下所示
2.4.使用 Foursquare 位置数据:
Foursquare 的数据非常全面,它为苹果、优步等公司提供位置数据。对于这个商业问题,作为作业的一部分,我使用了 Foursquare API 来检索东京这 5 个主要地区的热门景点的信息。返回的热门景点取决于最高的客流量,因此取决于进行呼叫的时间。因此,根据一天中不同的时间,我们可能会得到不同的受欢迎的场所。该调用返回一个 JSON 文件,我们需要将它转换成一个数据帧。在这里,我为半径 1 公里内的每个主要地区选择了 100 个热门景点。下面是从 Foursquare 返回的 JSON 文件中获得的数据帧
3.可视化和数据探索:
3.1.树叶库和树叶地图:
Folium 是一个 python 库,可以使用坐标数据创建交互式传单地图。因为我对作为热门地点的餐馆感兴趣,所以我首先创建一个数据框,其中前面的数据框中的‘Venue_Category’
列包含单词“餐馆”。我用了下面这段代码—
下一步是使用此数据框创建一个带有叶子的传单地图,以查看 5 个主要地区中最常去的餐馆的分布。
传单地图上面的代码片段如下所示
Figure 1: Circular marks represent the most frequently visited restaurants in the 5 Major (Nihombashi- Green, Nagatacho- Red, Shibuya- Orange, Shinjuku- Magenta, Shinagawa- Blue) districts of Tokyo, according to Foursquare data.
3.2.探索性数据分析:
有 134 个独特的场所类别,拉面餐厅名列榜首,正如我们在下面的图表中看到的—
Figure 2: Most Frequent venues around Shinjuku, Shibuya, Nagatacho, Nihombashi, Shinagawa, according to Foursquare data.
现在,这让我想起了拉面,的确是时候休息一下了。
Ramen Restaurants are the most frequently visited places around 5 major districts of Tokyo. Yum!
在美味的拉面之后,让我们回到探索数据上来。要了解每个地区排名前 5 的场馆,我们按如下步骤进行
- 为场馆类别创建一个带有熊猫 one hot 编码的数据框。
- 使用地区列上的 pandas groupby,并获得一次性编码场馆类别的平均值。
- 转置步骤 2 中的数据帧,并按降序排列。
让我们看看下面的代码片段—
上面的代码输出每个地区的前 5 个场馆—
从我为探索性数据分析创建的几个数据框中,使用其中的一个,我绘制了哪个区有最常去的餐馆,千代田区的永田町有 56 家餐馆。
Figure 4: Number of restaurants as top most common venues in 5 districts of Tokyo.
我们也可以看看小提琴图 s,它们被用来表示分类数据,我用 seaborn 图书馆展示了 4 种主要类型的餐馆在不同地区的分布
Figure 5: Lots of Japanese and Chinese restaurants in Nagatacho, whereas Shinagawa has many Ramen restaurants.
一旦我们对东京 5 个主要地区的不同类型的场所,特别是餐馆有了大致的了解,就该使用 K-Means 对这些地区进行聚类了。
4.聚集地区
最后,我们尝试根据场地类别对这 5 个地区进行聚类,并使用 K-Means 聚类。因此,我们的预期将基于场馆类别的相似性,这些地区将被分组。我使用了下面的代码片段—
5 districts of Tokyo divided in 3 clusters based on the most common venues obtained from Foursquare Data.
我们可以使用叶库在活页地图中表示这 3 个聚类,如下所示—
Figure 6: 5 major districts of Tokyo segmented into 3 clusters based on the most common venues. The size of the circles represents number of restaurants as most common venues for each district, which is highest at Nagatacho and lowest at Shibuya as shown in figure 4.
5.结果和讨论:
在分析的最后,我们得到了东京 5 个主要行政区的一个秘密峰值,由于商业问题始于在最繁忙的地区之一开一家午餐餐馆的好处和缺点,数据探索主要集中在餐馆上。我使用了来自维基百科等网络资源、Geopy 等 python 库和 Foursquare API 的数据,建立了一个非常真实的数据分析场景。我们发现—
- 拉面餐厅在这五个地区最常见的场所中名列前茅。
- 千代田区的永田町区和中央区的日本桥以餐馆为主,是最常见的场所,而涉谷和新宿区以酒吧、酒馆和咖啡馆为主,是最常见的场所。
- 永田町的餐厅数量最多,是最常见的场所,而涩谷区的餐厅数量最少。
- 由于聚类仅基于每个地区最常见的场馆,新宿、涉谷属于同一聚类,永田町、日本桥属于另一聚类。品川与这两个集群是分开的,因为便利店是最常见的场所(频率非常高)。
根据该分析,品川区将为即将开业的午餐餐厅提供最少的竞争,因为便利店是该地区最常见的场所,与其他地区相比,餐厅作为常见场所的频率非常低。从网上废弃的数据也可以看出,品川及其周边地区的平均土地价格比靠近东京市中心的地区要便宜得多。所以,毫无疑问,这个地区有可能成为开设高档餐厅的目标。这种分析的一些缺点是——聚类完全基于从 Foursquare 数据中获得的最常见的场所。由于土地价格、场馆与最近车站的距离、潜在客户的数量、品川作为港口地区的优势和劣势都可能发挥重要作用,因此,这一分析肯定远非结论性的。然而,它确实给了我们一些非常重要的初步信息,关于在东京主要地区周围开餐馆的可能性。此外,这种分析的另一个缺陷可能是只考虑东京每个区的一个主要地区,考虑 5 个主要区下的所有地区会给我们一个更现实的画面。此外,如果我们使用 DBSCAN 等其他聚类技术,这个结果也可能会有所不同。我写了一篇单独的帖子介绍 DBSCAN 的详细理论以及我们如何使用它来集群空间数据库。
6.结论
最后,为了结束这个项目,我们已经对现实生活中的数据科学项目有了一点了解。我使用了一些常用的 python 库来删除 web 数据,使用 Foursquare API 来探索东京的主要地区,并看到了使用小叶地图分割地区的结果。这种分析在现实生活商业问题中的潜力将被详细讨论。此外,还提到了一些缺点和改进的机会,以呈现更真实的图片。最后,由于我的分析主要集中在开一家针对大量办公室工作人员的餐馆的可能性上,所以得到的一些结果出乎意料地与我在东京呆了 5 年后的预期完全一致。特别是新宿和涉谷地区最常去的咖啡馆、酒吧和酒馆,以及永田町地区日本桥附近的日本餐馆!希望这种分析能够为您提供初步的指导,让您能够利用数据科学应对更多现实生活中的挑战。
保持坚强,干杯!!
在 Github 中找到代码。
在 Linkedin 找到我。
使用 Python 探索多伦多自行车共享出行
在过去的几年里,自行车共享已经成为一种越来越有吸引力的城市出行方式。考虑到我家附近自行车被盗的数量、Bike Share 站点覆盖范围的迅速扩大以及 TTC 票价的上涨,今年 3 月我很容易就决定续签第三年的会员资格。
美国人也在做出类似的决定:2017 年,美国的自行车爱好者通过自行车共享计划进行了 3500 万次出行,比 2016 年增长了 25%。与此同时,自行车共享多伦多见证了同一时期81%的乘客量增长。
我想探索自行车共享乘客数据集,以更好地了解多伦多人如何使用自行车共享,并展示一些可用于探索由开放数据计划发布的其他 294 个(还在统计中)数据集的技术。
关于数据
Bike Share 使用第三方机构提供的系统收集数据。由于 2016 年提供商变更,历史乘客数据的可用性有限。我决定专注于 2017 年的数据。数据可以从开放数据目录或新的门户(今年晚些时候取代目录的新网站)中检索。
如果你愿意跟随我的步骤,我使用的笔记本也可以在 GitHub 上找到。
该数据是一个包含多个 CSV 的 ZIP 文件,按季度分隔年度数据。对 Q1 数据的初步检查显示,数据中的每个记录代表一次独特的旅行,并包含以下各列:
- trip_id :每次行程的唯一标识符
- 行程开始时间:行程开始的时间
- 行程停止时间:行程结束的时间
- trip_duration_seconds :以秒为单位测量的行程持续时间
- from_station_id :旅程开始的车站 id
- from_station_name :行程开始的车站名称
- to_station_id :行程结束的站点 id
- to_station_name :行程结束的车站名称
- user_type :识别用户是会员还是购买了通行证
但是,进一步研究其他文件中的数据会发现一些问题:
- 文件之间的日期时间格式有所不同(例如,2017 年 12 月 31 日 12:30 与 2017 年 12 月 30 日 12:30:00)
- 只有 Q1 和 Q2 的数据包含站点 id
- 车站名称不一致(例如湖岸大道与湖岸大道)
- 起点站和终点站相同的行程,表明潜在的数据收集错误
不理想,但在典型的数据分析项目中也相当常见。在任何分析中,数据清理占用高达 80%的时间并不罕见。来自真实世界的数据通常是杂乱的,在进行任何分析或建模之前,必须对其进行清理。虽然数据清理既繁琐又耗时,但它也会对最终结果产生重大影响。
为了这个分析,我创建了一个包含 Jupyter 笔记本的新目录和一个包含我使用的所有数据的数据文件夹。
数据清理
该步骤的目标是将来自多个来源的数据整合到一个单一的 pandas 数据框架中,该框架包含标准化的日期和站点信息,并且剔除了异常值。参见这篇文章了解更多其他情况下常见数据清理步骤的深入分析。
Bike Share 公开了包含服务信息的 API 端点。station_information 端点可用作事实的来源,以解决桩号 id 和名称之间的不一致,还可以使用每个桩号的附加地理空间信息来增强数据。
标准化
首先,我导入了所需的库,并从自行车共享 API 端点加载了站点数据:
接下来,我手工确定了每个文件中使用的日期结构,并使用确定的结构将乘客数据连接成一个数据帧。
此外,来自 Q1 和 Q2 的数据位于 UTC 时区(提前 4 小时),而来自 Q3 和 Q4 的数据位于东部时区。这一点并不明显,我只是在后来可视化数据时才意识到这个问题。数据清理是一个迭代的过程,几乎不可能一次就发现所有的问题。
接下来,我需要解决站点 id 和名称的问题。为了提高效率,我提取了电台 ID 和电台名称的唯一组合。
具有 id 的站可以很容易地从 API 数据更新,但是没有 id 的站需要稍微复杂一点的解决方案。模糊匹配是一种用于识别相似事物的技术(例如央街圣 vs 央街街)。Python 中有很多模糊匹配字符串的方法,我选择了使用 fuzzywuzzy 库。
最后,我连接了站点数据,将其与 API 数据合并以包含站点的位置坐标,并用正确的站点 id 和名称更新了数据帧。
与多伦多停车管理局(TPA )(负责管理自行车共享的城市部门)的对话显示,车站 id 与物理终端而不是位置相对应。如果终端移动,API 将只显示新的坐标。因此,通过与车站 id 上的 API 数据合并,存在一些起点或终点位置不正确的行程。
极端值
在移除异常值之前,我使用了describe()
函数来查看数据帧中数据的简单轮廓(为了更清晰地查看,我排除了所有 ID 列):
考虑到 Bike Share 的定价模式,我预计大部分行程不到 30 分钟。但是数据显示旅行持续时间从 1 到 6,382,030 秒不等(约 74 天),表明数据中有异常值。
在他们自己分析中,TPA 通常认为少于一分钟的行程是错误的行程。我去掉了这 29595 次出行,约占总出行的 2%。
从数据中剔除异常值的常用统计方法是考虑四分位距(IQR),即数据的中间 50%。异常值定义为距离中值 1.5*IQR 的数据点。我用这种方法去除了一些异常值。参见这篇文章中一个很好的分步示例。
最后,我保存了清理后的数据以备后用。
研究问题定义
在开始分析之前,我们应该在分析结束时定义我们想要回答的问题(否则我经常会创建一个又一个没有得出特定结论的图表)。我们采用了一种相当非正式的方法进行分析:
- 集思广益,讨论各种与城市自行车共享和骑自行车相关的问题
- 确定这些问题是否可以用现有数据来回答,或者是否需要其他外部数据集
- 删除了由于数据访问受限而无法回答的问题(例如,由于隐私原因删除了用户信息)
- 将遗留的问题分成几个主题
A sample of the questions identified relating to Bike Share and biking in Toronto
我们确定的最终主要类别是:
- 谁在使用自行车共享服务?会员和临时用户的行为有明显的区别吗?如果有,这些区别是什么?
- 什么时候人们开始使用自行车共享?一年中、一周中和一天中的使用情况如何变化?
- 自行车共享的使用情况如何?人们主要使用自行车共享作为通勤上班或探索城市的方式吗?天气如何改变人们使用自行车共享的方式?
分析和可视化
就个人而言,我更喜欢创建一个新的 Jupyter 笔记本,仅用于分析。在新的笔记本中,我首先导入库和清理的数据,然后为一周中的某一天(星期一、星期二等)创建新的 pandas Categorical 数据类型。)和月份名称,以确保固定的排序顺序(在可视化数据时很有用)。
接下来,我转换了数据,使以后的分析和可视化更加容易。这些转变包括:
- 为清晰起见,重命名了字段
- 从旅行开始时间解析出季度、月份、日期、星期几和小时
- 以“起点站 id-终点站 ID”的格式为每个行程生成新的 route_id,以标识行程的路线
接下来,我还计算了起点站和终点站之间的距离。这增加了另一个维度,以后可能会有帮助。
谁在使用自行车共享?
回答“谁”这个问题的一个常用方法是研究人口统计学。最初,根据车站位置估计人口似乎是有希望的。例如,我们可能期望位于年轻人口居住区的电视台更受欢迎。
然而,大多数车站位于地铁/有轨电车线路附近,并且位于市中心。因此,结果可能偏向不同于用户真实代表的特定人口统计。请随意使用邻域概况数据集对此进行探索。
因为我不能可靠地推断出任何用户信息,所以我只能使用用户类型(会员和临时会员)。我深入研究了用户类型,并可视化了行程持续时间和距离的分布。
该图显示了会员和临时用户之间行为的明显差异:虽然两个群体通常在相似的距离内旅行,但会员到达目的地的速度要快得多。
在接下来的章节中,我通过查看这些用户使用自行车共享的时间和方式,进一步分析了他们之间的行为差异。
什么时候使用自行车共享?
我按照用户类型和旅行日期汇总了数据,并计算了每天的旅行次数。
然后,我设想了 2017 年的乘客趋势。
虽然图表显示了一些预期的趋势(例如,在温暖的月份有更多的乘客),但也出现了一些不太预期的模式:
- 会员和临时用户之间的旺季转移
- 在小时间范围内持续上升和下降
我放大并可视化了几个月的数据,以便在更精细的层次上获得更好的视图。
虽然 7 月份的趋势更加明显,但图表显示每周的乘客量会增加和减少。为了进一步梳理这个循环,我可视化了每个工作日的平均每日出行量,并按照季度和用户类型来分离数据。
这些图表进一步讲述了不同用户类型如何骑车的故事:会员主要在工作日骑车,而临时用户主要在周末骑车。唯一的例外是第三季度,周三的休闲旅行显著增加。深入研究这几个月的数据显示,这实际上只发生在 7 月。
谷歌快速搜索显示,这是因为 Bike Share 在 2017 年 7 月周三提供免费服务。
最后,我还想看看一天中每小时的乘客趋势。
此外,该图显示了会员和临时用户之间行为的明显差异:会员在早上 8 点和下午 5 点左右经历高峰时间,而临时用户在一天中更加稳定。
这与之前的发现(会员主要在工作日使用自行车共享)相结合,表明会员通常使用自行车共享作为通勤方式,而临时用户则使用自行车共享进行休闲旅行。
自行车共享是如何使用的?
前面部分的分析已经回答了我提出的一些关于如何使用自行车共享的问题(如会员通勤)。相反,我决定把重点放在天气和乘客量之间的关系上。
我从加拿大的政府气象站获取了每日的温度和降水数据,从多伦多城市气象站获取了数据。
首先,我将乘客数据与天气数据合并,并创建了一个 pairplot,以便在较高的层面上理解变量之间的关系。此外,我创建了一个热图来可视化变量之间的皮尔逊相关系数。
虽然我最初对降雨量和乘车量之间的低相关性感到惊讶,但这个结果是有意义的。该数据仅提供特定气象站位置每天的总降雨量。然而,一整天的降雨量很少是一致的,在所有自行车共享站的位置上也很少是均匀的。由于乘客量随小时而变化,我需要多个气象站每小时的降雨量数据,以了解这些变量之间的关系。
使用雨量计数据随意探索这个话题,对于这个分析的其余部分,我将重点放在温度上。我将不同时间的客流量和温度可视化为会员和临时用户的双轴图。
事实上,在温暖的月份里,人们越来越多地骑自行车,这有点“废话”所以我想更进一步,找到人们决定是否骑自行车的转折点。我决定只关注临时用户,因为他们与温度的相关性更高,但是同样的想法也适用于这两种用户类型。
我在试图定义这一点时遇到了一些困难。直觉上,似乎必须有一种方法来从数学上确定这一点,然而,许多行业只是根据长期的经验观察来确定这一点。最后,我的一个同事找到了这篇文章,它提出了一种基于曲线的通用方法。原来已经有一个 Python 库在使用这个应用程序: kneed 。
kneed 库要求在确定点之前对数据进行曲线拟合。给定数据后,我将数据拟合为线性函数、Y 轴无偏移的指数函数和指数函数,然后评估结果。
只有没有偏移的指数增长导致了可以确定拐点的曲线(其他两条曲线太线性)。结果显示该点在 16.1℃
我可以通过使用百分比累积行程(即计算 x 百分比的行程发生在 y C)下,其中曲线拟合函数将是一个最大极限为 100%的逻辑增长。该方法显示的温度为 16.0℃
利用类似的概念,我想找到一个点,在这个点上,乘客数量会随着温度的升高而停止增长。直觉上,一旦温度“足够高”,用户不再关心温度是有意义的(例如,22℃和 23℃之间的差异不会成为用户决定是否骑自行车的决定性因素)。为了找到这一点,我简单地将相同的计算应用于大于初始点的曲线部分,我发现这第二个点是 21.6 C。
我有兴趣将这一概念用于一个更温暖的城市的数据,那里的日平均温度可能达到 30+ C,看看在什么温度下热量会抑制乘车。
结论和下一步措施
虽然这个数据集本身相当简单,只包含几列基本信息,但我能够通过将数据与其他公共数据集合并来确定一些细微的见解。
在我们与 TPA 的交谈中,他们提到,尽管他们努力将自行车共享作为旅行“第一英里和最后一英里”的方法,但公众通常将自行车共享视为促进多伦多旅游业的一项计划。该分析中确定的行为表明,大多数自行车共享用户实际上是经常骑自行车的通勤者。
虽然用户信息不公开,无法进行用户行为分析,但深入研究自行车共享计划的地理空间方面是值得的。例如,将 TTC/GO 站(或其他公共中心)的数量与自行车共享站的受欢迎程度联系起来,或者进一步考虑站点之间自行车道的路线和可用性。
我希望这个数据故事提供了使用 Python 进行数据分析的介绍,并确定了您在进行分析时可能要考虑的一些工具。如果您有任何问题,请随时通过 opendata@toronto.ca 联系开放数据团队,我鼓励您使用开放数据进行自己的分析。
探索多伦多街区-开一家印度餐馆
网络抓取、Foursquare API、叶子地图和机器学习
Toronto, City in Ontario, Canada
作为 IBM 数据科学专业计划 Capstone 项目的一部分,我们使用真实数据集来体验数据科学家在现实生活中的经历。这个项目的主要目标是定义一个商业问题,在网上寻找数据,并使用 Foursquare 位置数据来比较多伦多的不同社区,以找出哪个社区适合开办新的餐馆业务。在这个项目中,我们将一步一步地完成从问题设计、数据准备到最终分析的所有过程,并最终提供一个商业利益相关者可以用来做决策的结论。
1.业务问题描述和背景讨论(介绍部分):
问题陈述:在加拿大多伦多开一家印度餐馆的前景。
多伦多,安大略省的省会,是加拿大人口最多的城市。它的多样性反映在多伦多的种族社区,如唐人街,Corso Italia,Greektown,Kensington Market,Koreatown,Little India,Little Italy,Little Jamaica,Little Portugal & Roncesvalles。多伦多是北美对移民最友好的城市之一,超过一半的印裔加拿大人居住在这里,这里是开印度餐馆的最佳地点之一。
在这个项目中,我们将通过一步一步的过程来决定开一家印度餐馆是否是一个好主意。我们分析了多伦多的社区,以确定最赚钱的地区,因为餐厅的成功取决于人和氛围。因为我们已经知道多伦多比加拿大任何其他城市都容纳了更多的印度人,所以在这里开餐馆是个好主意,但我们需要确定这是否有利可图。如果是这样,我们可以把它放在哪里,这样它会给所有者带来更多的利润。
Source: Bethesda Indian Food Festival
目标受众
谁会对这个项目更感兴趣?什么样的客户或群体会受益?
- 想在多伦多投资或开印度餐馆的商务人员。这一分析将成为针对印度人群开办或扩大餐馆的全面指南。
- 喜欢把自己的餐馆作为副业的自由职业者。这个分析会给出一个思路,开一个餐厅有多大的好处,这个生意有哪些利弊。
- 想要寻找有很多印度餐馆的街区的印度人。
- 业务分析师或数据科学家,他们希望使用探索性数据分析和其他统计和机器学习技术来分析多伦多的街区,以获得所有必要的数据,对其执行一些操作,并最终能够讲述一个故事。
2.数据采集和清理:
2.1 数据来源
a)我正在使用“加拿大邮政编码列表:M”(https://en . Wikipedia . org/wiki/List _ of _ Postal _ codes _ of _ Canada:_ M)wiki 页面来获取多伦多现有街区的所有信息。这一页有邮政编码,borough &是多伦多所有街区的名称。
b)然后我使用“【https://cocl.us/Geospatial_data"】CSV”文件来获取所有街区的地理坐标。
c)为了获得按种族划分的人口分布信息,我使用了“多伦多人口统计”(https://en . m . Wikipedia . org/wiki/Demographics _ of _ Toronto # Ethnic _ diversity)维基页面。使用此页面,我将确定印度人密集的社区,因为这可能有助于确定合适的社区来开设一家新的印度餐馆。
d)我使用 Foursquare 的 explore API 来获取多伦多各个场馆的位置和其他信息。使用 Foursquare 的 explore API(提供场馆推荐),我获取了多伦多现有场馆的详细信息,并收集了它们的名称、类别和位置(纬度和经度)。
从 four square API(https://developer.foursquare.com/docs)),我为每个场馆检索了以下内容:
- 名称:场地名称。
- 类别:由 API 定义的类别类型。
- 纬度:场地的纬度值。
- 经度:场地的经度值。
2.2 数据清理
a)从维基百科抓取多伦多邻居表
抓取了下面的 Wikipedia 页面,“加拿大邮政编码列表:M ”,以获得多伦多&及其邻近地区的数据。
为获得以下数据框架所做的假设:
- 数据帧将由三列组成:邮政编码、区和邻居
- 只会处理具有指定区的像元。未分配的行政区将被忽略。
- 一个邮政编码区可以有多个邻居。例如,在维基百科页面的表格中,您会注意到 M5A 被列出了两次,并且有两个街区:Harbourfront 和 Regent Park。这两行将合并成一行,相邻行用逗号分隔,如上表中的第 11 行所示。
- 如果像元有一个区,但没有分配邻域,那么邻域将与区相同。
Wikipedia —包用于从 wiki 中抓取数据。
Dataframe formed from the scraped wiki page
经过一些清理,我们得到了带有邮政编码、行政区和社区信息的正确数据帧。
Dataframe from ‘List of Postal code of Canada: M’ Wikipedia Table.
b)向邻居添加地理坐标
下一个重要步骤是将地理坐标添加到这些社区中。为此,我提取了地理空间数据 csv 文件中的数据,并根据邮政编码将它们与现有的邻域数据帧合并。
DataFrame with latitude & longitude of Postal codes in Toronto
我将对这些列进行重命名,以匹配现有的由加拿大邮政编码列表组成的数据框架。之后,我通过合并邮政编码将两个数据帧合并成一个。
Merged new dataframe with info about Neighborhoods, borough, postalcode, latitude & longitude in Toronto
c)从维基百科中删除人口分布
另一个可以帮助我们决定哪个社区是开餐馆的最佳选择的因素是,每个社区基于种族多样性的人口分布。因为这有助于我们识别印度人聚居的街区,因为那是开印度餐馆的理想地点。
抓取了下面的维基百科页面,“多伦多人口统计”,以获得关于多伦多及其邻近地区的数据。与下面多伦多的所有街区相比,给定的街区只有相当数量的印度人。我们正在检查这些社区的人口,以确定印度人口密集的社区。
Scraping the wiki page
多伦多只有六个街区有印度人居住,所以我们收集了这些街区的人口比例。
TORONTO & EAST YORK population distribution by ethnicity
NORTH YORK population distribution by ethnicity
SCARBOROUGH population distribution by ethnicity
ETOBICOKE & YORK population distribution by ethnicity
d)使用 Foursquare 获取位置数据
Foursquare API 是一个非常有用的在线应用程序,被我的许多开发者和其他应用程序如优步等使用。在这个项目中,我用它来检索多伦多附近地区的信息。API 返回一个 JSON 文件,我们需要将它转换成数据帧。在这里,我为半径 1 公里内的每个社区选择了 100 个热门景点。
Dataframe with venues in each neighborhood along with the category info of the venues.
3.探索性数据分析:
3.1 叶片库和叶片图
Folium 是一个 python 库,我用它来绘制一个使用坐标数据的交互式传单地图。
code to draw the folium map
Folium map of Toronto Neighborhood with popup label
3.2 邻里和印度餐厅的关系
首先,我们将从上面的多伦多数据框架中提取邻居和印度餐馆列,以供进一步分析:
Dataframe formed using Foursquare api information about venues in each neighborhoods
代码片段
Manipulating the data to make the analysis easy
在为场馆类别执行了 pandas one hot encoding 之后,让我们将这个数据帧与多伦多数据帧合并,该数据帧带有附近地区的纬度&经度信息。最后,只提取印度餐馆的值和街区信息。
Toronto Dataframe for Indian restaurants count in each neighborhood
让我们试着用上面的数据框画一些图:
Figure: Violin plot
借助这张小提琴图,我们可以识别出印度餐馆密集的行政区。它是使用 seaborn library 绘制的,显示了印度餐馆在不同行政区的分布。
让我们想象一下有印度餐馆的街区:
Figure : Bar plot
3.3 邻里和印第安人的关系
另一个重要特征是印度人在每个社区的分布。让我们分析一下这些街区,找出印度人口最多的街区。
为了实现这一点,我们将使用 wiki 页面的所有社区数据框架与少数民族人口连接起来,并且我们只提取每个社区的印度人口。
Dataframe with neighborhoods & their population distribution
Extracted dataframe with just Indian population information
让我们画一张图来直观地显示社区中的人口分布:
Bar graph to show the population in each ridings in Toronto
这种对邻里之间的关系以及这些邻里中的印度人口的分析和可视化有助于我们识别人口高度密集的印度邻里。一旦我们确定了这些社区,它就能帮助我们决定在哪里开新的印度餐馆。位于人口稠密的印度社区的印度餐馆比位于印度人口较少或没有印度人口的社区的餐馆更有可能吸引更多的印度顾客。因此,这种分析有助于确定新印度餐馆的成功。
3.4 印度人口与印度餐馆的关系
在进行数据清理和数据分析后,我们无法确定人口密集的印度社区和印度餐馆数量之间的关系。这可能是因为数据中的缺失,因为这是一个可以在未来的分析中改进的领域,以便更深入地了解业务问题。
Dataframe of densely populated neighborhoods with number of Indian restaurants
4.预测建模:
4.1 多伦多的聚集街区:
K 均值聚类的第一步是确定最佳 K 值,即给定数据集中的聚类数。为此,我们将对具有印度餐馆百分比的多伦多数据集使用肘方法。
Elbow method to identify best k value
代码片段—
Elbow visualizer to identify the K value
用肘法分析后用失真评分&对每个 K 值的平方误差,看起来 K = 6 是最佳值。
使用 K =6 的 K-Means 聚类多伦多邻域
6 clusters & its labels
code snippet for clustering the Toronto dataframe
Dataframe with cluster labels for neighborhood
Folium map for the clusters of different neighborhoods
4.2 检查集群:
我们总共有 6 个集群,如 0,1,2,3,4,5。让我们一个接一个地检查。
聚类 0 包含印度餐馆数量最少的所有邻域。它在地图中以红色显示
聚类 1 包含人口稀少的印度餐馆。它在地图上以紫色显示。
聚类 2 & 4 没有行,这意味着在这些质心附近没有数据点或邻域。
聚类 3 包含所有中等印度餐馆数量的邻近地区。它在地图中以蓝色显示。
聚类 5 包含印度餐馆密集分布的所有邻近地区。它在地图中以橙色显示
5.结果和讨论:
5.1 结果
我们已经到达了分析的末尾,在这一节中,我们将记录上述数据集的聚类和可视化的所有发现。在这个项目中,我们从寻找一个好的社区来开一家新的印度餐馆的商业问题开始。为了实现这个目标,我们调查了多伦多的所有街区,分析了每个街区的印度人口&这些街区的印度餐馆数量,以得出哪个街区更好的结论。我们使用了各种数据源来建立一个非常现实的数据分析场景。我们发现—
- 在这 11 个行政区中,借助于多伦多行政区印度餐馆数量之间的小提琴图,我们发现只有多伦多市中心、多伦多市中心、东多伦多、东约克、北约克和斯卡伯勒行政区有大量的印度餐馆。
- 在所有骑马区中,斯卡伯勒-吉尔伍德、斯卡伯勒-鲁日公园、斯卡伯勒中心、斯卡伯勒北部、亨伯河-布莱克克里克、东河谷、斯卡伯勒西南部、东河谷和斯卡伯勒-阿金库尔是印第安人骑马区的密集区。
- 借助集群检查&小提琴阴谋看起来像多伦多市中心,多伦多市中心,东纽约已经密集的印度餐馆。因此,最好不要考虑这些行政区,而只考虑新餐馆的地点——斯卡伯勒、东多伦多和北约克。
- 经过仔细考虑,在斯卡伯勒开一家新的印度餐馆是一个好主意,因为那里有大量的印度人口,这给了更多的顾客可能性和更低的竞争,因为附近的印度餐馆很少。
5.2 讨论
根据这一分析,斯卡伯勒将为即将开业的新印度餐馆提供最少的竞争,因为很少有印度餐馆或在几个社区没有印度餐馆。此外,从人口分布来看,这里似乎是印度人的聚居地,这有助于新餐厅提高顾客到访的可能性。所以,毫无疑问,这个地区可能是开办优质印度餐馆的绝佳地点。这种分析的一些缺点是-聚类完全只基于从 Foursquare API 获得的数据,关于每个街区的印度人口分布的数据也是基于 2016 年的人口普查,而不是最新的。因此,人口分布数据有 3 年的巨大差距。尽管有很多地方可以改进,但这一分析无疑为我们提供了一些很好的见解,关于可能性的初步信息&通过正确地设置垫脚石来开始这个业务问题。
6.结论:
最后,为了结束这个项目,我们有机会讨论一个商业问题,比如一个真正的数据科学家会怎么做。我们使用了许多 python 库来获取数据、操作内容&分析和可视化这些数据集。我们利用 Foursquare API 探索多伦多附近的场馆,然后从维基百科获得大量数据,这些数据是我们在维基百科 python 库的帮助下收集的,并使用 seaborn & matplotlib 中的各种情节进行了可视化。我们还应用机器学习技术来预测给定数据的输出,并使用 follow 将其可视化在地图上。
一些缺点或改进领域向我们表明,这种分析可以在更多数据和不同机器学习技术的帮助下进一步改进。同样,我们可以使用这个项目来分析任何场景,如开设一个不同的美食餐厅或开设一个新的健身房等。希望这个项目有助于作为使用数据科学应对更复杂的现实生活挑战的初始指导。
在 github 中找到代码,但是使用 nbviewer 和 github url 查看。
在 LinkedIn 找我!
使用 Golang 探索多伦多选民统计
十月标志着加拿大联邦选举活动的正式开始。因此,我对探索由多伦多开放数据发布的多伦多选民统计数据集很感兴趣。虽然数据是从多伦多市政选举中收集的,但它似乎是一个有趣的数据集,可以提供一些关于多伦多人如何投票的见解。
选民投票率低被认为是特朗普赢得 2016 年美国总统大选的主要原因之一。虽然加拿大自己的 2015 年联邦选举出现了几十年来最高的投票率,全国平均投票率为 68.49%,但我很好奇这一趋势是否也反映在加拿大最大的直辖市的地方层面。
与我之前研究过的多伦多自行车共享出行数据集不同,这个数据集结构良好,易于使用。我没有像往常一样使用 Python,而是决定利用这个数据集来探索 Go 的数据处理和可视化能力。
围棋进入我的视野已经有一段时间了。尽管 Go 并不以其与数据相关的功能而闻名,但诸如 gophernotes 和 gonum 之类的软件包正在使与数据相关的工作变得更加平易近人。此外,像这样的帖子正在令人信服地论证为什么对于数据密集型应用程序,Go 可能是比 Python 更好的选择。
关于数据
提供了 2003-2018 年过去 5 次选举的数据。但是,由于 2018 年的病房数量从 44 个变成了 25 个,我决定把事情简单化,只关注 2018 年选举的数据。
所提供的数据由选区和投票站汇总,并提供以下信息:
- 选区和投票站号码
- 投票站地点名称和地址
- 选民名单上的选民人数、选民名单的修改以及最终合格选民的人数
- 投票的选民人数
- 学校支持的选民人数
- 被拒绝和拒绝的选票计数
我感兴趣的是根据投票站地址对数据进行地理编码,并将历史投票数据映射到包含 25 个选区的新地图上。这将使我能够跟踪这些年来投票趋势的变化。目前,我认为这已经超出了探究 Go 的范围。
如果你愿意跟随,我使用的数据和笔记本可以在 GitHub 上的这里找到。
准备
在深入分析之前,我确定了一些我有兴趣回答的研究问题:
- 道岔按病区分布是怎样的?在比较高投票率病房和低投票率病房时有什么模式吗?
- 病房的变化对道岔有什么影响?
- 这些年来,登记选民和投票人数有什么变化?
- 进入投票站的便利性对投票率有影响吗?
- 投票站的投票率高低有什么规律吗?
问题 2、3 和 4 需要与之前的选举年份进行比较或进行地理编码,因此不在本次分析的范围之内。
我定义了一些函数,将在后面的分析中使用:
数据本身的结构相当好,数据清理很少。混合在数据(按选区和投票站汇总)中的还有按选区汇总的数据行。我删除了这些行,因为以后可以很容易地计算它们。
原始数据以 Excel 表格的形式提供,为了便于导入,我手动将其转换为 CSV 格式。然后,我通过以下方式将数据读入内存:
道岔分布
gonum 绘图库使可视化变得相当简单。我把病房和道岔想象成:
有几件事引起了我的注意。首先,2018 年多伦多市政选举的投票率比 2015 年的联邦选举少得多**。所有 25 个选区的平均投票率为 40.67%,比 2016 年的联邦投票率低约 28%。**
最高和最低失误率的病房之间也有很大的差异。14 区投票率最高,为 49.22%,23 区投票率最低,为 34.05%。
快速的视觉比较显示,客流量最大的 5 个病房(14、15、12、19 和 4 号病房)都位于市中心附近。人数最少的 5 个区(23 区、7 区、10 区、1 区和 21 区)大多位于郊区,10 区是个例外。
https://www.toronto.ca/city-government/data-research-maps/neighbourhoods-communities/ward-profiles/
在地图上进行视觉对比感觉不太靠谱,所以我从选区概况数据集中提取了一部分数据进行更系统的分析,并重点研究了教育和收入对选民投票率的影响。我使用的提取数据文件在 GitHub 上也有。
该数据集按选区显示了最新的人口普查数据。由于人口普查数据是在 2016 年收集的,我假设在 2016 年和选举之间没有发生重大的人口统计变化。我特别关注两个因素:
- 受过大专或高等教育的人口百分比
- 总收入高于 80,000 美元的家庭人口百分比(2016 年家庭收入中位数为78,373 美元)
教育和收入的皮尔逊相关系数分别为 0.57 和 0.55。基于此,似乎可以公平地说,教育和收入对投票率都有积极影响。虽然相关值较低,但这很可能是由于其他因素也影响了选民投票的可能性。
第 10 选区的投票率为 34.85%,在选区地图的初步审查和人口普查数据分析中都很突出。这个区不仅位于市中心(这里的投票率通常很高),住在这个区的 78%的人都受过高等教育。我有兴趣在未来的分析中将年龄作为一个额外的因素,看看它是否可能导致 10 区异常低的投票率。
投票站趋势
接下来,我通过将每个车站的道岔可视化为盒须图来进一步分解道岔。
提前投票站没有固定的合格选民数量,我从这个可视化中删除了这些投票站。
我很惊讶地看到有 100%和 0%的投票率的车站。我回顾了这些数据,试图理解这些异常值发生在哪里。
虽然投票率为 0%的车站没有明显的趋势,但我注意到投票率为 100%的车站中有许多是长期护理机构(即养老院、退伍军人护理场所等。).我为这些设施确定了一个关键词列表,并计算了名称中包含这些关键词的异常站点的数量。
我没有只关注 100%道岔的车站,而是检查了所有道岔大于或等于其选区第 91 百分位的车站。在这 164 个投票站中,只有 34 个投票站包含其中一个关键字。这些只是所有异常值站的一小部分。
接下来,我计算了一下,每个投票站平均预计大约有 1106 名投票人。但是这些投票率很高(> 91%)的投票站预计只有大约 94 名选民。由于这些投票站预计只有少数选民,因此在这些地点很容易达到高投票率。这可能是这些投票站投票率通常很高的原因,而不是这些投票站选民人口统计数据的差异。
关于围棋的思考
我立即注意到了 Go 的两件大事:
- 它是冗长的。我总是需要明确地说明预期的内容,无论是定义来自 CSV 的数据的预期输入,还是如何将数字字符解析为数值类型
- 数据的预建函数较少。熊猫和 numpy 可能用简单的数据操作宠坏了我。尽管 gonum plot 函数简单易用,但在 Go 中创建可视化效果也比 Python 受限得多(例如,在创建条形图时,我需要明确说明每个条形之间的距离,以避免条形重叠)。
这两件事都导致了花在代码上的时间更长。我已经习惯了这样的工作流程,我可以将数据直接加载到内存中(作为一个熊猫数据帧),浏览数据,然后决定下一步。但是,使用 Go 时,工作流程会发生逆转。我需要知道数据包含什么,我计划用数据做什么(这样我就可以定义正确的结构),然后最终加载数据。
总的来说,用 Go 探索数据感觉笨拙而缓慢尽管 gophernotes 使得在 Jupyter 笔记本上运行代码片段成为可能。
但这并不是说在数据科学的世界里没有发展的空间。熊猫之类的图书馆做假设。例如,在 pandas 中,包含 NaN 的整数列的数据类型是 float 而不是 int。虽然这些假设几乎总是正确的,但它们并不总是保证 100%正确。由于 Go 冗长,功能通常由开发人员定义,所以 Go 应用程序通常以更可控的方式执行。这意味着 Go 在生产中更加健壮、更加可靠。
总的来说,我不认为我会在另一个主要目的是探索数据的项目中使用 Go。但是对于任何强调一致性的应用程序(如数据管道、仪表板后端等)。)我肯定会考虑用 Go over Python。
后续步骤和结论
我有兴趣在将来使用 Python 以更具地理空间的方式重新访问这个数据集。潜在的未来分析可能包括:
- 对所有可用选举的投票数据进行地理编码,并将历史数据映射到当前的邻域边界。然后跟踪每个街区的投票率随时间的变化
- 将数据与邻里档案数据集相结合,并在教育和收入之外的更多维度上分析人口统计数据和投票率之间的关系
我希望这个数据故事概述了一些关于多伦多人民如何投票的有趣见解,并为您自己进行更深入(和有趣)的分析指明了方向。如果您有任何问题,请随时通过 opendata@toronto.ca 联系开放数据团队,或者直接通过赵毅联系我。谭@多伦多。
探索单变量数据
使用超级英雄数据开始使用 Python 中的单变量 EDA
Photo by Limor Zellermayer on Unsplash
Wikipedia 称“单变量分析可能是最简单的统计分析形式。。。关键事实是只涉及一个变量。”
因为单变量分析非常简单,所以它是探索性分析的好起点。
开始时要考虑的一些问题包括:
- 我有多少变量?
- 我有丢失的数据吗?
- 我有哪些类型的变量?
我们将探索来自 Kaggle 的超级英雄数据集来开始回答这些问题。数据包括两个 csv 文件。我们在这里使用的第一个包含了每个超级英雄的特征。第二个列出了每个英雄拥有的超能力。完整的笔记本可以在这里找到。
我有多少变量?
我喜欢从查看数据帧的前几行开始,并打印出图形:
print(info_df.shape)
info_df.head()
马上我们可以看到我们有列“未命名:0 ”,我们最有可能安全地删除它。这样我们总共有 10 个变量。
我们还可以在肤色栏中看到,我们缺少一些值,这就引出了我们的下一个问题。
我有丢失的数据吗?
我们已经看到我们有一些丢失的数据,但是让我们检查每个变量的空值的总和。
*#check for null values*
info_df.isnull().sum()name 0
Gender 0
Eye color 0
Race 0
Hair color 0
Height 0
Publisher 15
Skin color 0
Alignment 0
Weight 2
dtype: int64
虽然这表明存在缺失数据,但这可能有些误导。
上面我们看到皮肤颜色列包含破折号值,Python 在技术上不将其解释为空值。这表明数据的视觉检查是重要的。我们可以清除破折号并用 NaN 替换:
info_df['Skin color'].replace('-', np.nan, inplace=True)
整理好数据后,我们可以进入下一个问题。
我有哪些类型的变量?
变量可以是两种类型之一:分类的或数字的。
分类数据
分类数据将项目分类成组。这种类型的数据可以进一步细分为名义值、序数和二进制值。
- 序数值有固定的顺序。这里的一个例子可以是从低到高的排序。
- 名义值没有固定的顺序。例子包括超级英雄的性别和排列。
- 二进制数据只有两个值。这可以表示为真/假或 1/0。
总结分类变量的一种常用方法是使用频率表。为了形象化,我们将使用条形图。
sns.countplot(x='Publisher', data='info_df')plt.title('Number of Superheros by Publisher')
plt.ylabel('Number of Superheros')
plt.xlabel('Publisher')
plt.xticks(rotation = 90)
plt.show();
Barchart visualizing the number of Super Heroes by publisher.
这里我们可以看到漫威漫画的超级英雄数量最多,其次是 DC 漫画。
数据
数字数据是我们可以对其执行数学运算的值。它们被进一步细分为连续和离散数据类型。
- 离散变量必须是整数。一个例子是超级英雄的数量。
- 连续可以是任何值。这里的例子包括身高和体重。
数字数据可以用直方图显示出来。直方图是对连续数据的一个很好的初步分析。这里要考虑的四个主要方面是形状、中心、分布和异常值。
- 形状是直方图的整体外观。它可以是对称的、偏斜的、均匀的或者具有多个峰值。
- 中心指的是平均值或中间值。
- 传播指的是数据到达的范围或多远。
- 离群值是远离大部分数据的数据点。
sns.distplot(info_2.Weight, kde=False)plt.title('Histogram of Superhero Weight')
plt.show();
Histogram of superhero weight.
从直方图中我们可以看到大多数超级英雄的体重在 50 到 150 磅之间。我们有一个大约 100 磅的峰值和超过 800 磅的异常值。我们可以通过使用 describe 函数打印数字摘要来确认这一点:
info_2.Weight.describe()count 490.000000
mean 112.179592
std 104.422653
min 4.000000
25% 61.000000
50% 81.000000
75% 106.000000
max 900.000000
Name: Weight, dtype: float64
Describe 向我们显示关键统计数据,包括平均值、标准偏差和最大值。除了上面的直方图之外,使用汇总统计可以让我们对数据的样子有一个很好的了解。
单变量结论
数据集包含 10 个变量和一些缺失数据。我们看到 DC 漫画拥有最多的超级英雄,而权重变量有一些异常值。
这绝不是一个完整的探索性分析。我们可以继续探索剩下的变量,然后进行双变量分析。
进一步探索的有趣的事情可能是比较漫威和华盛顿的漫画。我们可以用数据科学来确定超级宇宙吗?
探索印度尼西亚的村庄边界空间数据集
清理和识别数据
Photo by Capturing the human heart. on Unsplash
测绘 19.05 亿平方公里的广阔覆盖面并不是一项容易的任务,沿途有无数的障碍。地理位置——山区、河流和群岛——是典型的困难,除了与解决边界线问题的当地利益相关者进行复杂的谈判之外,这些困难也使这项工作资源枯竭。因此,现在我们仍然可以在印度尼西亚的一些地方,尤其是东部地区,发现明显的概括——直线和粗略的划分。但是,至少政府正在努力逐年改进,以提供关于行政边界信息的最准确信息,这对于土地登记和管理至关重要。由于地理空间信息局(BIG)试图将包括 1:5.000 比例的全印度尼西亚底图项目在内的交易安全交付给潜在投资者,如世界银行和英国国家测绘局,因此可能还需要更多的改进。因此,我们仍然需要等待像样的行政边界。
此外,在 2019 年任期内,BIG 发布了字母为 SK Kepala BIG 第 27/2019 号 的专题地图,包括四级行政边界的测绘归Direktorat penata an administra si and Pemerintahan Desa管辖,而二级和三级则归Direktorat topo nimi Dan Batas DAE rah管辖。仍然在同一个部,但不同的董事会。这可能会解决以前的问题,当时仍然没有明确的权限来确定谁对数据负责,有时 BPS 和 BIG 在此数据方面会出现重叠。尽管如此,当涉及到这个问题时,应该依靠哪些数据之间仍然存在一些二元论,甚至其他部委也有同样的头痛。为了更好地理解这一问题,我将这一问题的范围界定为更详细的视图,因此我们通过基于来自内政部(Kemendagri)和印度尼西亚统计局(BPS)的数据集进行一些探索性数据分析,对村庄边界数据有了更多的了解。
所使用的村庄边界数据是从 BPS 和 Kemendagri 各自的 REST 服务中获得的。在谷歌合作实验室这里,我使用熊猫、海牛和地质熊猫,试图探索其背后的数据。在此第一部分中,进行了清洁和识别程序,以提供一些一般信息,并对这两者进行一些交叉检查。
清理数据
清理数据过程初始化,以清理一些凌乱的数据,其中一些看起来令人恼火。首先,打开驱动器中的数据,将其加载到 Google Colabs 中,地理空间格式是提供商提供的原始格式(geojson 格式)
打开来自 Kemendagri 的空间数据,第一时间很难解释,因为讨厌的列名。为了更好地理解每一个专栏,你可以在这里看到真正的名字。使用 tail() 而不是 head() 是为了区分该数据的前 10 行中的空值。后来,我设法简化了数据并提高了可读性。
df_1 = gpd.read_file("/content/drive/My Drive/village_boundary/desa_kemendagri.json")df_1.tail()
Data from village boundary (Source: http://gis.dukcapil.kemendagri.go.id/)
从 BPS 打开空间数据集,不需要太多清理,只是为了简化必要的数据,这将是很大的考虑。尽管如此,数据仍然需要改进,因为我发现一些数据包括地理特征(湖泊、森林和田野),这实际上不是一个村庄名称类别,所以后来我试图删除其中一些。
df_2 = gpd.read_file("/content/drive/My Drive/village_boundary/desa_bps.json")df_2.head()
Data from village boundary (Source: BPS)
接下来,我想知道这两个中使用了多少数据。也许通过分析数字就能发现两者之间的显著差异是有用的
df_dagri.count() # the result is 83.322df_bps.count() # the result is 79.191
Kemendagri 的数据就是这种情况,通过使用来提高简单性和可读性。iloc 从第 17 栏到第 225 栏,我可以区分出一些在这一部分并不真正需要的特性,然后使用原始源中的指南对其进行重命名。我打算保留的列是不同级别管理的代码编号和管理名称。
df_dagri = df_1.drop(df_1.iloc[:, 17:225], axis = 1)df_dagri = df_dagri.drop([‘giskemenda’, ‘giskemen_1’, ‘giskemen_2’, ‘giskemen_3’, ‘giskemen_4’, ‘giskemen_5’, ‘giskemen_6’, ‘giskemen_7’], axis=1)df_dagri.columns = ['ID_PROP', 'ID_KAB_KOT', 'ID_KEC', 'ID_KEL', 'ID_DESA', 'PROVINSI', 'KABKOT', 'KECAMATAN', 'DESA', 'LUAS', 'geometry']df_dagri.tail()
在 BPS 的情况下,就像前面的数据一样,它在某种程度上是可管理的…
df_bps = df_2.drop(df_2.iloc[:, 10:215], axis = 1)df_bps.head()
现在是清理数据的最后一步,Kemendagri 的数据仍然有一个空值,包括湖泊要素(称为 Danau),因此我尝试通过基于列 ID_PROP 来消除这些要素,但我真的不知道我是否通过简单地删除它们做出了正确的选择。
df_dagri_clean = df_dagri.dropna(subset=['ID_PROP'])
继续讨论更困难的问题,虽然 BPS 的数据没有任何空值,但是有一些要素在地理要素之前没有被归类为村庄,例如湖泊、森林和田地。
那么,我们如何区分这些特征呢?我意识到 ID_DESA 列是由唯一代码组成的。例如,ID_DESA = 1107050001 意味着 11 是省代码,07 是县/市代码,050 是地区代码,最后 001 是村代码。因此,我决定将这些值拆分到名为 ID_NEW 的新列中,该列只显示村庄代码。
df_bps['col'] = df_bps['ID_DESA'].astype(str)df_bps['ID_NEW'] = df_bps['col'].str[7:10]df_bps.drop('col', axis=1, inplace=True)df_bps.head()
然后,我尝试显示基于该列的唯一值
df_bps.ID_NEW.unique()
df_bps.count()
结果就在这里…注意数据之间有一些异常值:444、555、888 和 999。
array([‘001’, ‘002’, ‘003’, ‘004’, ‘005’, ‘006’, ‘007’, ‘008’, ‘009’, ‘010’, ‘011’, ‘012’, ‘013’, ‘014’, ‘015’, ‘016’, ‘017’, ‘018’, ‘019’, ‘020’, ‘021’, ‘022’, ‘023’, ‘024’, ‘025’, ‘026’, ‘027’, ‘028’, ‘029’, ‘030’, ‘031’, ‘032’, ‘033’, ‘034’, ‘035’, ‘036’, ‘037’, ‘038’, ‘039’, ‘040’, ‘041’, ‘042’, ‘043’, ‘044’, ‘045’, ‘046’, ‘047’, ‘048’, ‘049’, ‘050’, ‘051’, ‘052’, ‘053’, ‘054’, ‘055’, ‘056’, ‘057’, ‘058’, ‘059’, ‘060’, ‘062’, ‘063’, ‘070’, ‘071’, ‘079’, ‘080’, ‘061’, ‘064’, ‘065’, ‘067’, ‘068’, ‘069’, **‘999’**, ‘066’, ‘072’, ‘073’, **‘888’**, ‘078’, ‘081’, ‘083’, ‘084’, ‘103’, ‘104’, ‘105’, ‘106’, ‘107’, ‘108’, ‘109’, ‘111’, ‘112’, ‘113’, ‘114’, ‘115’, ‘116’, ‘117’, ‘118’, ‘119’, ‘120’, ‘121’, ‘122’, ‘074’, ‘075’, ‘076’, ‘077’, ‘085’, ‘086’, ‘087’, ‘088’, ‘089’, ‘090’, ‘091’, ‘092’, ‘093’, ‘094’, ‘095’, ‘096’, ‘097’, ‘099’, ‘100’, ‘101’, ‘102’, ‘110’, ‘082’, ‘098’, **‘555’**, ‘**444’**, ‘123’, ‘124’, ‘125’, ‘126’, ‘128’, ‘129’, ‘131’, ‘132’, ‘133’, ‘134’, ‘136’, ‘137’, ‘138’, ‘127’], dtype=object)
因为我真的很好奇这些数据意味着什么,所以我决定更进一步…
outliers = [‘444’, ‘555’, ‘888’, ‘999’]select_outliers = df_bps.loc[df_bps[‘ID_NEW’].isin(outliers)]select_outliers.groupby(['ID_NEW', 'DESA']).count()
如您所见,这里有一些异常值,如无人居住岛屿的“444”和“555”,湖泊的“888”,森林的“999”。
最后,最后一件事是放弃 200 以上的值,因为我发现这样做真的很方便,而不是使用一些条件。
df_bps_clean = df_bps.drop(df_bps[(df_bps['ID_NEW'] >= '200')].index)df_bps_clean.count()
现在,数据比以前干净多了,但在删除一些特征之前,在大小上有一些缺点。为了显示原始版本和清理版本之间的差异,这里快速查看了各自数据的总大小。
dagri_before = df_dagri[‘LUAS’].sum()dagri_after = df_dagri_clean[‘LUAS’].sum()bps_before = df_bps[‘LUAS_KM2’].sum()bps_after = df_bps_clean[‘LUAS_KM2’].sum()
Kemendagri 和 BPS 数据的计算结果分别为 0 和 2,4 ha。零值仅仅是因为我删除了一开始没有任何大小值的特征。由于删除了一些巨大的特征,如大湖和大森林,因此 2,4 ha 的 BPS 差异是可以接受的。此外,改造后两者相差 1.4 公顷
**bps_before** # the result is 1913322.973425157 km2**dagri_before - dagri_after** # the result is 0.0 km2**bps_before — bps_after** # the result is 24101.28374174121 km2**bps_before - dagri_after** # the result is 38489.174897136865 km2**bps_after — dagri_after** # the result is 14387.891155395657 km2
如您所见,BPS 的原始数据与内政部的官方规定(见:Permendagri 编号 137/2017)相符,该规定称印度尼西亚的总面积为 191 万平方公里。BPS 数据于 2016 年发布,而 Kemendagri 数据于 2017 年发布,但后者显示出 3.8 公顷的巨大缺口。怎么会这样
印度尼西亚,我们真的在缩小吗?为什么一年后的数据会比前一年的数据小?尽管 Kemendagri 数据中的特征数量(83.322 行)比 BPS (79.191 行)多。我看不出这两者之间有任何更清晰的解释,甚至没有挑选出任何可信的数据。
无论如何,Kemendagri 真的需要尽快解决这个问题,这是他们的责任之一。
第 2 部分将对之前在此清理的数据进行分析和可视化。没有更多的无聊的编辑,可能是一些令人着迷的图表和有趣的信息将被显示。
用强化学习探索虚拟世界
你在办公室里走来走去,你花了很多时间努力工作,喝咖啡,和同事打乒乓球。突然,一个危险的影子出现了。你转过身,看到一个巨大的黄色球向你滚来。他张开大嘴,准备把你整个吃掉,而你开始逃跑。那个黄色的球一直跟着你,好像它是一个真正的人。你躲不了。你逃不掉的。吃豆人会来抓你的。
上面的场景可能听起来很可怕,但它描述了一种游戏,这种游戏可以通过我们正在研究的东西来开发:增强现实和人工智能的结合。‘我们’,就是口袋里的 的人 ,欧洲最好的数码产品工作室。我们公司由多个团队组成,其中一个团队专注于 AR,另一个团队专注于 AI。第一个是和 Unity 合作,而后者对 TensorFlow 了如指掌。最近他们联手创造了一些奇迹。
我们的增强现实团队 masters Unity——他们去年夏天为室内导航的内部演示建立了我们办公室的 3D 模型。两个物体被添加到这个环境中:一个绿色的玩具坦克和一个黄色的积木。**我们的目标很简单:奖励碰撞的坦克&在我们的虚拟办公室中捕获随机放置的方块。**为了做到这一点,我们——尤其是我们的实习生 Fabrice——利用了来自 Unity 员工的 ml-agents 。这是一个很好的框架,可以帮助你用机器学习的方法立刻创建代理。最先进的强化学习算法,如有耐心的近似策略优化,都是现成的,并且被证明非常有效。
起初,代理人有点愚蠢,当它被随意扔在一个房间时,他没能逃出去。但几个小时后,它学会了走出房间,尽管撞到了很多墙。我们更新了我们的奖励功能,以惩罚当它撞上墙壁和继续训练的代理人。代理开始越来越好地捕捉黄色块!我们注意到,当它们在视线范围内但距离很远时,它经常会错过它们。所以我们增加了光线投射的密度,代理人再次得到改善。强化学习 Kaizen!
看看最终的模型,看看它在我们的办公室里是如何学会自我导航的:
An example of the Agent catching cubes in our Digital Office
Unity 作为一个游戏平台,非常擅长将这些环境和模型编译到移动设备上。为了实现创建一个在我们办公室里跑来跑去的 AR Pacman 的目标,我们已经将这个环境编译到 iOS 上,只是为了看到坦克在我们的走廊里捕捉积木。这里有一个这样的印象:
接下来,在 Hololens、Magic Leap 或 HTC Vive 上旋转一下吧!
真正的交易
你可能认为我们的创造只是为了好玩,但我们对它的潜力是非常认真的。想象一下在商店、机场或游乐园有类似的东西指引你?创建一个 AR 寻路应用程序是一回事,但是如果你能够跳过人工编程,让代理根据你的需求训练自己,那就更好了(并且更具可扩展性)。
真正有趣的是,这些代理可以显示人类的行为和模式。虚拟机器人可能生活在模型中,但它知道真实世界。想象一下,如果你正在开发一个公共建筑——无论是医院、火车站还是机场——你组织了一场建筑设计竞赛。**我们的代理可以做的事情之一是通过进行数百万次模拟“行走”来测试建筑的可达性和安全性。**人们走哪条路?他们被卡在哪里?危险情况下的人群控制呢?
目前最大的警告是,代理人生活在三维世界中,并没有感觉到现实世界。我们的桌子还没有在 Unity 环境中建模,所以如果你用 HoloLens 或其他 AR 设备观看,坦克只会穿过桌子。我们正在思考和探索如何在 Unity 模型中反推环境的 3D 网格信息。我们还通过在 3D 环境中放置随机障碍物来使坦克更加坚固,这样它就可以学会有效地绕过它们,预测现实世界中的实时变化。虽然观察代理和手动调整奖励函数很有趣,但它威胁到虚拟代理学习自己导航新环境的可扩展性。我们正在探索多阶段学习的新途径;首先掌握在我们办公室导航的基本知识,然后掌握如何快速和避开障碍物。
障碍不一定会阻止你。如果你碰壁了,不要转身放弃。想办法爬上去,穿过去,或者绕过它。来吧,吃豆人,你有一整晚的时间和一千万次的尝试!
特别感谢 Fabrice Cops 和 Dieter Vanhooren,他们为这个项目提供了支持。
探索,可视化和建模明尼苏达维京人队的进攻
Viktor the Viking gazes into the abyss.
经过简短的搜索,我从 NFL 专家那里获得了整个 2018 赛季的详细数据。因为我本质上是明尼苏达的粉丝,所以我决定尝试一些机器学习技术,看看能否找到一些量化的方法,让维京人在过去的一年中有所欠缺。
对于一些最初的 EDA ,我决定看看通过传球积累的码数,按传球位置和接球手细分。根据传球方向,我汇总了每个维京人接球手在赛季中至少 10 次接球的所有接球码。因为平均码数/接发球受几次长时间接发球的影响很大(并且因为不完整传球导致平均码数/接发球经常为 0),所以我使用总码数(总和)来汇总数据。该数据显示在左侧,并按总接收码/球员(从多到少)排序。我认为值得注意的是:蒂伦和迪格斯倾向于在球场的对立双方分裂;蒂伦在球场右侧接到更多的远传码,而迪格斯在左侧接到更多的远传码。我认为,这也凸显了可靠的第三 WR 的相对稀缺;凯尔·鲁道夫无疑是球队最好的第三选择,但是奥尔德里奇·罗宾逊(总共 17 次接球)和拉昆·特德韦尔(总共 35 次接球)如果他们不得不在蒂伦和迪格斯坐在一场比赛中排队的话,那他们就太掉队了。
Dot plots of penalties/team, grouped by defensive penalties, offensive penalties, and all penalties. MN Vikings are marked in purple.
鉴于他们在本赛季最后一场比赛中由于点球至少部分被取消,我接下来看了看维京人在点球上的总体表现。明尼苏达队是第二个受到处罚最少的球队。整个 2018 赛季,他们只接受了 88 次处罚(47 次进攻,41 次防守),而平均每支球队接受了 106 次处罚(56 次进攻,50 次防守)。这些处罚只有 729 码有效,在联盟中排名第三(平均每个球队有 881 码的处罚)。虽然这个基本总结中肯定有额外的混杂因素(不是所有的处罚都是平等的,无论是在放弃的绝对码数方面还是在它们对比赛结果的影响程度方面),但数据表明明尼苏达州训练有素,纪律严明。所以我想知道明尼苏达是否在他们的比赛叫牌中做了一些奇怪的事情,也许是向对方的防守倾斜,给他们的对手额外的信息来准备。
首先,我简单地用逻辑回归来尝试预测一部戏是否过关。作为预测,我使用了当前的比赛条件:比赛剩余的时间(通过将四分之一、一分钟和一秒钟组合成一个单一的度量标准来计算),当前的比分差距,要走的距离和码数,距离端区的距离,以及对方球队的身份。在 1,000 次训练拆分中,逻辑回归预测维持数据的通过状态的准确率约为 58%(-3%)。虽然这明显比运气好(如果你只是在每场比赛中预测“传球”,你会有 53.7%的准确率;针对该值对我们观察到的精度进行单样本 t 检验,得到 t=15.18,p<2–16),这在解释数据方面并不特别令人印象深刻。从系数来看,时间对一部戏是否合格没有影响;虽然随着时间的推移,球队不应该总是传更多的球,但在一些常见的情况下,他们应该这样做。然而,我们的逻辑模型没有考虑预测因素的相互作用,例如当两个或更多的财产下降时是否有时钟的影响,或者它是否是第三个下降和英寸或第三个下降和长,这有助于说明这种相对简单的方法的一些缺点。
鉴于这些结果,我开始想知道能够预测一场比赛是否会通过(即使没有场上所有的额外信息)是否真的是一件好事,以及更多“可预测”的球队是否会赢得更少的比赛。我分别在每个团队中重新运行了相同的逻辑建模代码;根据逻辑回归模型方法,明尼苏达是第五个最“可预测”的球队,排在杰克逊维尔、西雅图、匹兹堡和印第安纳波利斯之后。在所有 32 个团队中,胜率和逻辑可预测性之间没有显著相关性(r=0.19,p=0.30,见下图)。然而,逻辑准确性和获胜之间的非显著正相关可能可以用这样一个事实来解释,即获胜最多的团队往往会通过,直到他们以多个分数获胜,然后在场上尽可能快地结束比赛。此外,一些最不可预测的球队(例如旧金山、奥克兰和坦帕湾)在赛季中途就基本上被淘汰出季后赛了。当没有动力去最大化你的胜利数字时,我想球队会更愿意多样化他们的打法,让他们的球员尽可能多的代表不同种类,并在下赛季给他们自己更多的经验和更好的机会。
Logistic regression model accuracy for each NFL team (left), and win percentage by logistic model accuracy (right).
Confusion matrix for the random forest model of Minnesota’s offensive play calls.
为了改进基本的逻辑回归结果,并考虑到预测因素之间可能的相互作用,我接下来使用了随机森林模型来预测游戏条件下的游戏类型。我没有简单地估计“is_pass”结果,而是允许结果具有数据中实际存在的多个级别。总共使用了 500 棵树,每次分裂选择 2 个变量。总的来说,预测的随机森林模型比通过概率的逻辑回归模型发挥得更好,在所有条件下具有大约 63%的准确性。看一下混淆矩阵(左),随机森林模型在预测大多数特定游戏类型方面做得很好。它预测射门的准确率为 81%,传球准确率为 69%,踢门准确率为 91%。自然地,失球和麻袋从来没有被模型预测到,考虑到它们的稀有性和内在的不可预测性,这是有意义的(大多数进攻协调者可能不会故意制造失球)。
随机森林模型最不可靠的地方是预测灯芯草;只有 52%的预测会是快攻的比赛最终实际上停留在地面上,而 43%最终是传球。这可能是由于建立模型的数据的缺点(例如,使用阵型和人员信息可能会有所帮助),但也可能是由于维京人倾向于(至少在赛季早期)成为传球优先的球队。
我选择不包括在初始随机森林模型中的最大数据源之一是队形类型(对于维京人,这些包括散弹枪、中锋下方、无拥挤、无拥挤散弹枪、平底船和球门队形)。最初,我更感兴趣的是尝试从教练的角度,看看我是否能预测他们在给定的比赛情况下会想出什么打法。然而,另一种方法是扮演对方防守的角色,并尝试根据比赛情况和阵型(这是混战线的公开信息)预测比赛叫牌。
在随机森林模型中加入该信息得到了更高的精确度,误差率仅为 29%。更重要的是,以前预测维京人何时会抢球的难度大大降低了,因此当模型预测抢球时,63%的时间是正确的(相比之下,在没有阵型数据的情况下,52%的准确率)。在其他团队中尝试这种随机的 forest+模型在准确性方面产生了类似的改进。所有 32 个团队的平均错误率为 33%,范围从 24-40%不等。明尼苏达队再次成为最“可预测”的球队之一(总排名第五),但考虑到第一和第三最可预测的球队(分别是新英格兰队和洛杉矶公羊队)正在参加超级碗比赛,这再次表明该模型预测球队比赛类型的能力并不一定能更容易地阻止他们取得成功。
到综上所述,使用随机森林方法来模拟逐场比赛数据,在确定当前游戏条件下将进行何种类型的比赛方面相当准确,并且在通过/不通过的简单逻辑回归上有明显的改进。鉴于维京人在点球和失球等“错误”指标上的表现远高于平均水平,我也不认为有太多理由认为他们只是运气不佳。
一些分析家认为柯克考辛斯是罪魁祸首,但我肯定不会说他是主要问题。在他的 最近的几个赛季中,考辛斯有五个不同的接球手,整个赛季有 500+的接球码,而今年他只有蒂伦、迪格斯和鲁道夫可以依靠。部分原因是库克错过了赛季的一部分,但即使他整年都健康,可靠的接收选择仍然比考辛斯习惯的要少。进攻线在快攻和传球阻挡上的困难也是需要改进的地方,可能会出现在选秀中。不过,总的来说,这个建模练习表明,在给定的比赛条件下,比赛的类型与联盟中一些最成功的球队是一致的,这让我对明年的改进感到乐观,特别是如果球队可以增加另一个一致的接收选项。
探索自然语言生成的西部——从 n-gram 和 RNNs 到 Seq2Seq
NLP Wild West
斯坦福大学 NLP 课程中讲授的自然语言模型介绍
最初,文本生成是基于模板或基于规则的系统,对于特定的狭义应用程序来说,它们具有相当好的可解释性和良好的行为。但是扩展这样的系统需要大量不可行的手工工作。简单地说,语言没有清晰明确的规则,这就是为什么它经常被许多人比作西部荒野。由于这个原因,这个领域转移到了统计语言模型。
语言建模(LM)是预测下一个单词是什么的任务,或者更一般地说,是一个为一段文本序列分配概率的系统。
N-gram 是最简单的语言模型,其性能受到缺乏复杂性的限制。像这种简单的模型无法实现流畅、足够的语言变化和正确的长文本写作风格。由于这些原因,神经网络 ( NN ) 被探索为新的黄金标准,尽管它们很复杂,并且 递归神经网络(RNN) 成为用于任何种类的序列的基本架构。如今,RNN 被认为是文本的“香草”架构,但 rnn 也有自己的问题:它不能长时间记住过去的内容,并且它很难创建长的相关文本序列。由于这些原因,其他架构如***【LSTM】和 门控循环单元(GRU) 被开发出来,并成为许多 NLG 任务的最先进解决方案。在本文中,我们将探索这些基本思想和模型的演变*。
Different flavours of RNN: RNN, GRU and LSTM (source of images)
毫无疑问,【NLP】和【NLG】已经经历了重大的进步,尤其是最近五年,这就是为什么我们每天多次使用语言模型并从中受益。每当我们在搜索框中键入文字或者在手机上书写文字时,幕后的一个模型就在预测下一个即将出现的字符或单词。 Gmail 和 LinkedIn 根据我们之前的对话和虚拟助手(例如 Siri 或 Alexa 生成类似人类的回复。聊天机器人正在成为客户服务的重要组成部分,谷歌翻译为大量语言提供了更好的翻译。生成流畅连贯的冗长文本,同时保持对输出语义的控制,这是一个挑战。这可以通过类似于 序列到序列(Seq2Seq) 的架构来解决,该架构用于解决条件文本生成问题,但我们将在本文的后面对此进行更多讨论。
Examples of applications with NLG models
N-gram
最直接的语言模型是一个ngram 模型。文本只不过是一系列字符(或单词)。词汇 V = { W ₁,…, W |ᵥ|}下一个单词 x ⁽ᵗ⁺ ⁾的概率分布为:
因此,单词序列 x ⁽ ⁾, x ⁽ ⁾,…, x ⁽ᵀ⁾的概率为:
N-gram 假设 下一个字 x ⁽ᵗ⁺ ⁾只依赖前面的 n-1 个字。
用数学术语来说,这意味着:
N-gram 又称为 非光滑极大似然估计 。对于最大 似然估计,我们统计每个单词在 n-1 个单词(历史)之后出现的次数,并除以这 n-1 个单词在训练数据中出现的次数。未平滑部分意味着,如果我们在训练数据中没有看到 n-1 个单词历史之后的给定单词,我们将为其分配零概率。
4 克 LM 的示例:
H̵e̵l̵l̵o̵,̵ ̵I̵想 ____
p(字| 想)= count(想字)/ count(想)
P(观| 想)=0.03, P(书| 想)=0.04
完成这些计算后,文本生成就很容易了。生成下一个消息 的 步骤为:
- 看最后 n-1 克(历史)。
- 从 n-gram 模型中获得下一个单词的分布。
- 根据相应的分布抽取一克样本。(注意,有不同的方法来选择下一个 gram,并在本文的结尾进行了解释。)
为了生成一系列的 k 线图,我们简单地循环上面的过程,在每一轮用生成的 k 线图更新历史。
Sparsity problem
这种简单化的方法有一个 稀疏性问题 :
- 正如我们已经提到的,如果一个 gram 从未出现在历史数据中,n-gram 分配 0 概率 ( 0 分子)。一般来说,我们应该 平滑 的概率分布,因为每件事都应该至少有一个小概率分配给它。
- 如果历史数据中从未出现过 n-1 个 gram 序列,我们就无法计算出下一个 gram 的 概率(0 分母)。在这种情况下,我们应该只考虑一个更短的 n-gram (即使用 n-1 gram 模型代替)。这种技术叫做 回退 。
由于模型的简化假设,如果我们保持上下文和长期依赖性,我们就不会像我们一样擅长预测即将到来的单词。于是我们以等不连贯的文字收场
“在鞋楦和制鞋业生产的同时,股市也随之上涨”
然后,有人可能认为更大的上下文可能给出更好的预测,但是通过增加模型中的克数(n)稀疏性成为更大的问题,并且整个模型实际上变得不正常。此外,更大的 n 会导致型号的更大,根据经验, n 不应超过 5 。
NN
Trade-offs between these two types of systems. (source)
需要一种具有上下文感知的更复杂的模型。有了 NN,就没有稀疏性问题,因为它可以给我们以前从未见过的组合的概率分布,并且不需要存储所有观察到的 n-gram。一个 NN 似乎是现在每个硬数据问题的解决方案,这里的情况是这样吗?
Fixed window neural language model
例如,固定窗口神经 LM 查看固定长度窗口中的单词,其输入层是连接的单词嵌入向量。但是我们仍然在看一个有限大小的固定窗口,并且扩大窗口大小导致模型参数 不可维护的 增加。此外,在处理输入的方式上也没有 对称性——看上面的图,我们可以看到单词被乘以不同的权重。因此,我们有效地多次学习相似的函数。
在我们如何处理引入的单词嵌入方面应该有很多共性。
顾名思义,递归神经网络(RNN)通过对所有单词输入 循环使用相同的网络和权重矩阵来解决这个问题 。因此,RNN 可以被认为是同一个网络的多个副本,每个副本都向后继者传递消息。这使得 RNN 成为比固定窗口 NNs 更紧凑的模型。下一节将使这一陈述更加清晰。
Unrolling of an RNN (modified figure from source)
rnn、gru 和 lstm
在 RNN 中, 输入序列 可以是任意长度的*,因为我们对每个时间步(字)应用相同的权重矩阵。这意味着型号的尺寸不会因输入变长而增加,并且在输入处理方式上存在 对称性 。此外,RNN 具有一系列隐藏状态(而不是像我们在固定窗口神经 LM 中那样只有一个隐藏状态)。某个步骤的输出是基于先前的隐藏状态和该步骤的输入计算的。隐藏状态就像一个单一的状态,随着时间的推移而变异,形成网络的记忆。***
Vanilla RNN architecture where Xt is the input (eg. encoding of word at step t) and ht is output value (eg. encoding of the next word) (modified figure from source)
隐藏状态是前一个隐藏状态和当前模型输入的线性组合,然后通过非线性处理(如 sigmoid,tanh)。我们正在尝试学习一个 通用函数 (上图中的 A)关于我们应该如何处理一个单词给定的前一个单词的上下文,而学习语言和上下文的一般表示。
在字符级而不是单词级预测即将到来的上下文是一个分类问题,因为字符比词汇表中的单词范围少,所以分类数量明显较少。在训练了一个预测下一个字符的 RNN 之后,我们可以按照下面的 步骤生成新的文本 :
- 首先选择一个字符串,初始化 RNN 状态,并设置要生成的字符数。
- 使用起始字符串和 RNN 状态,将预测作为下一个字符的分布。使用分类分布计算预测字符的索引。
- 使用这个预测的字符作为模型的下一个输入。
- 由模型返回的 RNN 状态被反馈到模型中,以便它现在具有更多的上下文,而不仅仅是输入(预测的先前字符)。(见上面 RNN 展开的图)
对文本序列中的一个字符的计算(理论上)使用来自许多步骤之前的信息,但是循环字符计算仍然很慢,并且在实践中很难访问来自许多步骤之前的信息。
预测“太阳在天空照耀”这句话的最后一个单词是可行的。但是“11 月的天气主要是晴**”这句话的最后一个字就比较难预测了当 100 个单词之前有一句话“我计划去塞浦路斯旅行。”。**
RNNs 挣扎记住长范围依赖关系主要是由于所谓的 消失梯度问题上下文信息的线性衰减*** 。梯度的大小受激活函数的权重和导数的影响(由于链式法则),它们在网络中反复出现。如果这些因子中的任何一个小于 1(当我们将小值相乘时),梯度可能会及时消失,如果这些因子大于 1,梯度可能会爆炸。在这两种情况下,我们都会丢失信息,尤其是当文本较长时。训练期间非常小的梯度转化为重量的微小变化,因此,没有显著的学习。*****
LSTM 和后来的 GRU 的设计是为了缓解控制模型更新记忆的消失梯度问题。
****lstm和 GRUs 中的重复模块(图中的 A)具有不同的结构,这使得它们 能够学习长期依赖 。它们的结构使用由非线性(例如 sigmoid)神经网络层和乘法或加法运算组成的门。
LSTM architecture and equations of its repeating module (modified figure from source)
有不同类型的 门 可用于 更新状态 。例如,遗忘门决定我们要丢弃什么信息,而输入门决定我们要存储什么新信息。LSTM 的一个关键特征是单元状态(上图中 LSTM 顶部的水平线),它通过网络,只有一些微小的线性交互,允许信息沿着网络流动。 GRU 是 LSTM 的一个更快(但功能更弱)的变体,它合并了单元格状态和隐藏状态。
GRU architecture and equations of its repeating module (modified figure from source)
条件语言模型— Seq2Seq
到目前为止,我们已经看到了可以生成文本的不同模型(n-gram、RNN、LSTM、GRU ),但是我们还没有探索如何基于除起始文本之外的条件来生成文本。一些条件文本生成应用如下:
作者姓名 > 作者风格的文本
****话题>话题关于那个话题的文章
电子邮件 > 电子邮件主题行(又名总结)
问题 > 自由形式问题回答(又名聊天机器人)
****条>条条的总结(又名总结)
图像 > 描述图像的文本(又名图像字幕)
自然语言代码描述 > 代码(又名代码生成)
****句>句句翻译成不同的语言(又名机器翻译)
在条件语言模型中,我们希望在给定一些条件上下文( x )的情况下,将概率分配给单词序列( y ):
这种范式被称为 Seq2Seq 建模,它是 2014 年神经机器翻译的一大突破。从那时起,它一直是一种领先的标准方法,并在 2016 年使用了 谷歌翻译 。为了训练这样一个模型,我们需要大量的并行数据。
The Rosetta Stone: First parallel language dataset
Seq2Seq 的架构是由两个 rnn 组成的单一神经网络模型:
- 一个编码器 :创建一个固定长度的编码(一个实数向量)封装关于输入的信息。在机器翻译中,编码器从源句子中提取所有相关信息以产生编码。**
- 一个解码器 (本质上是一个条件 LM):一个语言模型,用编码器创建的编码生成目标序列条件。在机器翻译中,解码器生成源句子的翻译句子。**
Seq2Seq example for generating a message to property agents based on some search criteria
解码器是用一种叫老师强制** 的方法训练的。这意味着“老师”迫使解码器的输入成为黄金目标输入,而不是它的预测。目标序列是偏移了一个单词的输入序列,以便网络学习预测下一个单词。Seq2Seq 中的反向传播端到端操作*(一端是损耗函数,另一端是编码器 RNN 的起点),我们 相对于学习整个系统。*****
End-to-end training using one loss function J
由编码器创建的嵌入成为解码器 RNN 的初始状态。我们强制在这个向量中捕获关于源句子的所有信息,因为这是提供给解码器的唯一信息。这样我们就陷入了一种的信息瓶颈** 的问题中。如果一些信息不在这个向量中,那么解码器就无法正确翻译这个句子。 注意 提供了这个问题的解决方案,同时也帮助解决了其他问题,包括 消失渐变问题 。**
注意是通用的深度学习技术。给定一组向量值和一个向量查询,它计算依赖于查询的值的加权和。
Seq2Seq with Attention
在 Seq2Seq 情况下,每个解码器隐藏状态 s𝘵(查询)关注所有编码器隐藏状态 h₁,…,h_N(值)。因此,在解码器的每一步,我们都有一个直接连接到编码器**,但是我们关注源句子的不同部分。**
上面的网络图在数学上所做的是:
Attention (a_t) translated in mathematical equations
- 计算解码器隐藏状态(步骤 t 中的 s)与每个编码器隐藏状态(h₁,…,h_N)的点积,以便获得注意力分数(eᵗ)
- 使用软蜡获得这一步的注意力分布(αᵗ)
- 使用该分布来获得注意力输出(步骤 t 中的 a ),作为编码器隐藏状态的加权和。
- 将注意力输出与解码器隐藏状态([a;s])并按照非注意 Seq2Seq 模型进行。
最后,在训练 Seq2Seq 模型之后,可以遵循不同的策略来通过解码器预测下一个字**😗*
- 贪婪 :我们总是挑选概率最高的词**(又名 argmax)。我们现在做了最好的选择,没有回头路了。然而,这不一定会给我们整个句子的 argmax。**
- 光束搜索 :光束搜索跟踪每一步的 k 可能变量**,避免被局部最大值引入歧途。当 k 为小 ( k=1 表示贪婪)时,我们可能会得到不合语法的不自然的、无意义的不正确的句子。当 k 比大时,这些问题会减少,但会更加昂贵并给出更多通用回复,汇聚成安全“正确”响应,然而不太相关。**
- 采样 :我们从一个截断的条件词概率分布中采样,即从前 k 个最可能的词中采样。用这种方法,通常句子由于随机性而没有太大的意义。注意 k=1 与贪婪解码相同, k= 大小的词汇与纯采样相同。
遗言
我们讨论了生成文本的不同方法,从简单的方法到不同的 RNN 架构,包括和 GRU 。我们还研究了 S eq2Seq 架构,其中用*可以处理 条件文本生成 机器翻译等问题。***
这些模型在谷歌和我的团队之外有**Zoopla的数据科学团队用这些知识挑战财产门户体验。希望在读完这篇文章后,你对文本生成有了更好的直觉,并且你现在也能够为你的行业找到有用的应用。如果你想了解更多,以下是我发现的一些有用且有见地的资源:**
- CS224n :深度学习的自然语言处理。斯坦福大学,2019 年冬季(该课程是本博客的灵感来源)
- A.卡帕西。递归神经网络的不合理有效性,2015
- C.奥拉。了解 LSTM 网络,2015
- Z.谢。神经文本生成:实用指南。arXiv 预印本 arXiv: 1711.09534v1,2017 年
- Z.胡。走向文本的受控生成,2018
- https://www . tensor flow . org/beta/tutorials/text/text _ generation
- https://py torch . org/tutorials/intermediate/seq 2 seq _ translation _ tutorial . html
特别感谢我在 Zoopla 的同事们,他们重视知识分享和新想法的实验,并感谢 Jan Teichmann 对本文的反馈和支持。
探索 XGBoost
集成学习和 XGBoost 在分类和特征选择中的应用
我最近在弗朗索瓦·乔莱的《用 Python 进行深度学习》一书中偶然发现了一种新的【对我来说】方法,梯度推进机器(具体来说就是 XGBoost)。Chollet 提到,XGBoost 是一个成功的应用机器学习者今天应该熟悉的一种肤浅的学习技术,所以我相信了他的话,并深入了解了更多。
我主要想写这篇文章,因为我认为其他具有一些机器学习知识的人也可能像我一样错过了这个主题。我绝不是这方面的专家,老实说,我在理解一些机制方面有困难,但是我希望这篇文章是你探索这方面的一个很好的入门(底部也有很好的资源列表)!
集成学习
那么 XGBoost 是什么,它在 ML 的世界中处于什么位置呢?梯度推进机器属于称为集成学习的 ML 类别,集成学习是 ML 方法的一个分支,它可以同时用许多模型进行训练和预测,以产生单一的优秀输出。把它想象成规划出几条不同的路线去一个你从未去过的地方;当你使用所有的路线时,你开始了解哪些交通灯需要很长时间,以及一天中的时间如何影响一条路线,使你能够精心设计一条完美的路线。您试验并组合了一些不同的模型,以得出最佳结论。集成学习也差不多!
集成学习分为三个主要子集:
- Bagging : Bootstrap 聚合或 Bagging 有两个不同的特征,这两个特征定义了它的训练和预测。对于训练,它利用引导程序将训练数据分成不同的随机子样本,模型的不同迭代使用这些子样本进行训练。对于预测,bagging 分类器将使用每个模型中投票最多的预测来产生其输出,bagging 回归将取所有模型的平均值来产生输出。Bagging 通常应用于高方差模型,如决策树,随机森林算法是 bagging 的一个非常接近的变体。
- 堆叠:堆叠模型是一个“元模型”,它利用来自许多不同模型的输出作为输入特性。例如,这允许您使用所有训练数据训练 K-NN、线性回归和决策树,然后获取这些输出并将它们与逻辑回归合并。这个想法是,这可以减少过度拟合,提高精度。
- 助推:终于助推了!boosting 的核心定义是一种将弱学习者转换为强学习者的方法,通常应用于树。更明确地说,boosting 算法依次增加模型的迭代,同时调整弱学习者的权重。这减少了来自模型的偏差,并且通常提高了准确性。流行的提升算法有 AdaBoost、梯度树提升和 XGBoost,我们将在这里重点介绍。
XGBoost
eXtreme Gradient Boosting 或 XGBoost 是一个针对现代数据科学问题和工具优化的梯度提升算法库。它利用了 boosting 中提到的技术,并封装在一个易于使用的库中。XGBoost 的一些主要优点是它的高度可伸缩性/可并行性、执行速度快,并且通常优于其他算法。
在这一节中,我们将通过预测泰坦尼克号上的乘客是否幸存来探索 XGBoost。用于此分析的完整 jupyter 笔记本可在这里找到。
我首先将 Titanic 数据加载到 Pandas 数据框中,并探索可用的字段。注意:为了简洁起见,在加载之前,我手动转换了 csv 中的‘上船’和‘性别’特征
大多数元素似乎是连续的,那些包含文本的元素似乎与预测幸存者无关,所以我创建了一个新的数据框(“train_df”),只包含我想要训练的特征。还要注意,XGBoost 将处理 nan,但(至少对我来说)不处理字符串。
就像其他模型一样,将数据分成训练和测试数据很重要,我在 SKLearn 的train_test_split
中就是这么做的。请注意,我决定只采用 10%的测试数据。我这样做主要是因为 titanic 数据集已经很小了,而我的训练数据集已经是全部可用数据集的一个子集。这可能会导致过度拟合,可能不是最佳实践。
准备好数据后,我的目标是将乘客分类为生还者,我从 XGBoost 导入了 XGBClassifier。然后我们为XGBClassifier()
创建一个对象,并传递给它一些参数(没有必要,但我最终想尝试手动调整一下模型)。最后我们把模型fit()
交给我们的训练特征和标签,我们准备进行预测!如您所见,使用 XGBoost 库与使用 SKLearn 非常相似。
使用我的模型进行预测,并使用准确性作为我的衡量标准,我可以看到我达到了超过 81%的准确性。这是经过一点手动调整后的结果,虽然我希望得到更好的结果,但这仍然比我过去在相同数据上使用决策树所获得的结果要好。
XGBoost 的一个超级酷的模块是plot_importance
,它为您提供每个特性的 f 值,显示该特性对模型的重要性。这对于选择特性很有帮助,不仅对于 XGB,对于您可能在数据上运行的任何其他类似模型也是如此。
更进一步,我发现了一个精彩的代码样本和一篇关于自动评估要使用的特性数量的文章,所以我必须尝试一下。
本质上,这段代码通过按照重要性反复删除特征来训练和测试模型,并记录模型的准确性。这使您可以轻松地删除功能,而不是简单地使用试错法。虽然这里没有示出,但是这种方法也可以应用于模型的其他参数(learning_rate
、max_depth
等),以自动尝试不同的调整变量。我不会深入讨论模型调优的细节,但是大量的调优参数是 XGBoost 如此受欢迎的原因之一。
我希望这是对 XGBoost 是什么以及如何使用它的有用介绍。感谢阅读。现在,去做点什么吧!
有用的资源/参考资料
GXBoost 概述 ( 必读!)
注:此内容原载于AI Time Journal
仅用一行 Python 代码探索您的数据
A pretty picture to catch your eye.
在不到 30 秒的时间内完成所有标准数据分析。熊猫侧写的奇迹。
谢谢你。
这是一篇非常短的文章,但在我们开始之前,我只想对所有阅读并分享我上一篇文章的人表示感谢: Python 窍门 101,每个新程序员都应该知道的事情 。招待会完全是疯狂的。掌声和观点的数量完全使我的其他文章相形见绌。所以谢谢,让我们继续吧!
香草熊猫之路(无聊之路)
任何使用 Python 处理数据的人都会熟悉 pandas 包。如果你不是,pandas 是大多数行列格式数据的首选包。如果您还没有 pandas,请确保在您的首选终端中使用 pip install 安装它:
pip install pandas
现在,让我们看看默认的 pandas 实现能为我们做些什么:
Pretty decent, but also bland… And where did the “method” column go?
对于那些不知道上面发生了什么的人:
任何熊猫’ DataFrame '都有一个. describe() 方法返回上面的摘要。但是,请注意,在该方法的输出中,分类变量丢失了。在上面的例子中,输出中完全省略了"方法"列!
让我们看看我们是否能做得更好。
(提示:… 我们可以!)
熊猫侧写(奇特的方式)
This is just the beginning of the report.
如果我告诉您,我只用 3 行 Python 代码*(如果不计算我们的导入,实际上只有* 1 行代码)就可以生成以下统计数据,您会怎么想?) :
- 要素:类型、唯一值、缺失值
- 分位数统计如最小值、Q1、中值、Q3、最大值、范围、四分位间距
- 描述性统计如均值、众数、标准差、总和、中位数绝对偏差、变异系数、峰度、偏度
- 最频繁值
- 直方图
- 相关性突出显示高度相关的变量、Spearman、Pearson 和 Kendall 矩阵
- 缺失值缺失值的矩阵、计数、热图和树状图
(特性列表直接来自 【熊猫仿形】GitHub )
我们可以。通过使用熊猫概况包!要安装 Pandas Profiling 软件包,只需在您的终端中使用 pip install:
pip install pandas_profiling
经验丰富的数据分析师乍一看可能会嘲笑这种方式过于浮华,但它绝对有助于快速获得数据的第一手印象:
See, 1 line, just as I promised! #noclickbait
首先你会看到的是概述(见上图),它给出了你的数据和变量的一些非常高级的统计数据,以及警告,比如变量之间的高相关性、高偏斜度等等。
但这还不是全部。向下滚动我们会发现该报告有多个部分。简单地用图片展示这个一行程序的输出并不公平,所以我制作了一个 GIF:
我强烈建议您亲自探索这个包的特性——毕竟,它只是一行代码,您可能会发现它对您未来的数据分析很有用。
import pandas as pd
import pandas_profiling
pd.read_csv('[https://raw.githubusercontent.com/mwaskom/seaborn-data/master/planets.csv').profile_report()](https://raw.githubusercontent.com/mwaskom/seaborn-data/master/planets.csv').profile_report())
更像这样
如果您喜欢这些易于应用的东西来改进您的 Python 工作流,请看看我的新文章:
让您的数据分析更上一层楼!
towardsdatascience.com](/7-things-to-quickly-improve-your-data-analysis-in-python-3d434243da7)
结束语
这只是一个非常快速和简短的问题。我刚刚发现熊猫在给自己做简介,所以想和大家分享一下!
希望你觉得有用!
阅读彼得·尼斯特拉普在媒介上的作品。数据科学、统计和人工智能…推特:@PeterNistrup,LinkedIn…
medium.com](https://medium.com/@peter.nistrup)**
指数级的技术将会使几乎所有的工作变得过时,所以你能做些什么呢?
人们普遍认为,人工智能(AI)、增强现实和虚拟现实(AR/VR)、比特币、生物技术、物联网(IoT)、3D 打印和机器人等指数级技术将取代许多工作岗位。根据不同的方法,对未来几十年人工智能可以取代多少工作的估计从 10%到 55%不等。
Portrait of Edmond Belamy created by GAN (Generative Adversarial Network), 2018
然而,这些高数字过于保守。我认为指数技术将会消除几乎所有的工作。我意识到这是一个可怕的命题,但不幸的是,我有充分的理由这样说,我将在下面详述。
我们必须为生活所有领域的海啸般的变化做好准备,这将使我们不仅质疑我们如何谋生,更重要的是质疑我们作为人类在地球上的位置。
人工智能已经可以比人类做得更好,这比任何人预期的都要快得多(除了少数未来学家)。软件现在可以比普通人更好地识别和命名物体或生物的图像,错误率低于 5%。
我们可以很好地区分猫和狗,但在面对更复杂的图像识别问题时,人类比人工智能更容易出错。我们很多人都不能自信地区分哈巴狗、法国牛头犬和英国牛头犬。训练有素的人工智能完成这样的任务没有问题。虽然识别狗的品种似乎不是一项非常重要的任务,但想想医学领域的图像处理,识别肿瘤可以挽救你的生命。在如此关键的领域,每超过人类专业知识一个百分点,就意味着真正的生命得到了拯救,所以我们不能在道德上阻止进步。
Pug, French Bulldog, English Bulldog.
人工智能已经可以与人类专家匹敌的领域数不胜数:风险分析、交易、军事应用、驾驶、法律实践、新闻等等。任何需要持续做出正确决定的职业都受到了人工智能的威胁/增强。随着我们在各种流程中部署更多的自动化,我们也将产生更多的数据,这反过来将使人工智能变得更好。有一天,即使是世界顶级专家也无法超越训练有素的人工智能系统,因为人工智能吸取了数百万人的专业知识。它变成了人类的蜂巢大脑。
其他指数级技术,如机器人与人工智能的结合,使机器人不仅超级人类强壮,而且超级人类聪明。在中国,机器人警察已经被部署到经常光顾的地区,以寻找罪犯和侦查犯罪活动。这些机器人警察可以搜索拥有数百万张面孔的数据库,并利用数万小时观察视频产生的行为模式。经过一些“练习”,这种机器人将接管许多意想不到的职业,如警务、消防、救援工作、建筑等。
我们不必探索所有指数级的技术,就能意识到对可替代工作的影响将是巨大的。然而,许多人认为,仍然会有一些工作不能或不会被人工智能、机器人或物联网取代,因为它们需要理解复杂的情况,包括人类自身或人类创造的环境。
虽然我同意上述前提,即某些工作对于非人类智能来说太难了,但现实是企业家将彻底改造整个行业。更不用说将被取代的个人工作了。全新的技术解决方案将以全新的方式满足这些需求。怎么会这样?
就拿老年护理护士这个职业来说吧。这项任务被广泛认为是不可替代的,因为只有人类才能提供老年人所需的同情、理解和关怀。但是,如果年老不再是一件事呢?
今天,我们对衰老的原因有所了解。在不远的将来,有一天,可能在人工智能的帮助下,我们将找到阻止衰老的方法,不久之后,我们也将能够逆转衰老。人们将能够几乎永远活着(除非被自动驾驶卡车撞上),并生活在他们选择的年龄。老年身体将是一种选择,而不是我们所遭受的痛苦。老年人护理这一职业将像镇上的叫卖者和排球场工人一样成为过去。
Pinsetters working in Subway Bowling Alleys, Brooklyn, New York, 1910
教书怎么样?教育行业也在发生巨大的变化。在许多国家(尤其是亚洲)的成人学习领域,博士水平的教育是通过在线课程进行的,只是偶尔与现实世界的教师进行磋商,他们可能只通过视频会议向学生展示。在这种情况下,学生每学期只和他们的教授见面一次。各种职业的任何水平的沉浸式虚拟现实课程将提供可想象的最引人入胜的学习体验。
对于教师这一职业,让我们来看看一项更具雄心的潜在行业变革技术。如果有一天我们能够通过简单地将知识上传到我们的大脑来学习,而不是依靠人类来传达想法,那会怎么样?学习一门技能,比如用 C 语言编程(如果这仍然相关的话),或者只是为了好玩而学习一门新的死语言,比如拉丁语,只需要去购物中心的电子学习中心,那里的知识会在几个小时内传送到我们的大脑。世界各地的十几个研究机构正在研究脑机接口(BCI),这将使这成为可能。如果不是 BCI,一些类似的革命性的东西也会让教师这个职业几乎过时。除了一些学科专家将课程经验整合起来教授最新的发现,几十年后将不再需要课堂教师。
那么,人类还有什么可做的吗?是的,会留下一些工作,但不会很多。
对于最不熟练的人来说,在很长一段时间里,通过给物体、声音、情境、情感、策略等贴标签来教授人工智能。将是一条获得收入的可靠途径。这是一个成千上万的人已经在做的职业。然而,这种新的职业可能也不是未来的证明,因为最终人工智能将能够通过找到自己的数据源来自主学习。它将观看数以亿计的 YouTube 视频,并阅读所有已出版的书籍,以获得对物理世界和人类文化的新理解。总有一天,那些不计其数的挑战视频会派上用场,让人工智能了解人类忍耐的极限。
**我们中的许多人将和 AI 一起工作。**随着时间的推移,自动化水平将会提高,人类的参与将会减少,到某一点,人类将只是一个道德观察者,以确保人工智能不会出错。这已经在许多地区开始发生。例如,放射科医生经常咨询基于人工智能的系统来诊断病人,谷歌的风险投资家依赖人工智能来挑选基于先前数据显示最有可能成功的创业团队。
数量相对较少的训练有素的工程师、产品经理、设计师和其他专业人士将构建人工智能和自动化系统,让我们剩下的人自动完成我们训练了一辈子的工作。
AI 靠数据。数据越多,人工智能越好。人工智能越好,使用它的人就越多,因此它可以产生更多的数据。这就形成了一个恶性循环,赢家通吃。拥有巨大市场和数据的技术先进国家(如美国和中国)将领先于较小的国家,并将获得越来越大的优势。
Abba — The Winner Takes It All
现在,如果你喜欢 Abba,开始播放视频,继续阅读。
众所周知,没有工作的人更有可能陷入抑郁甚至自杀。但是,即使人们没有工作也能保持理智,如果没有“任何事情”可供他们做,他们也很难找到生活的意义。我们可能会看到社会的大规模崩溃。
所有这些听起来都很可怕。这些可能发生的事件有好的一面吗?是的,我们可以预见多种事情的发生,使这个新的现实变得可以忍受。
所有的东西都会便宜很多。由于所有任务都由机器人以最佳方式完成,从房产到面包的一切都将大幅便宜。而且差了很多。
一般来说,劳动力成本在任何制造过程中都占很大一部分。当从获取原材料到交货的一切都由机器完成时,每一步我们都在积累储蓄和储蓄复合。相当于当前可用质量和效用的商品和服务的价格可能下降多达 90%。
这意味着,即使你只有一份低薪工作,你仍然能够支付你的需求,过上舒适的生活。你不会像富人一样进入月球上的赌场,但你会有你的虚拟现实谷歌眼镜来享受完美逼真的沉浸式音乐会体验,并吃健康的克隆牛排,旁边放着垂直养殖的西兰花。
将会出现目前不存在的新工作,对某些现有工作的需求将会增加。在一个高度科技化和虚拟化的世界,对原创人造物品和人类现场表演的需求可能会激增。
人们将能够花更多的时间和他们所爱的人在一起,因为他们负担得起,或者因为他们没有其他事情可做。爱情、友谊、哲学将获得更大的重要性。人们会在相互交往和思考中找到意义,而不是从他们的工作和成就中获得自我价值。
**我们将能够收拾工业革命以来留下的烂摊子。**当智能机器人不知疲倦地工作时,我们将能够分配给它们目前人工无法想象的任务,如清理海洋,或通过从垃圾中挑选有用的原材料来进行近乎完美的回收。改变工业的基本技术也可能出现在环境保护领域。一旦我们能够设计和大规模生产一旦被丢弃就立即腐烂的材料,包装废物可能不再是一个问题。
Star Trek Communicator
如果你怀疑这种技术在 30 年内成为现实的可能性,那么回头看看 1989 年。网络刚刚诞生,脱口秀节目中的人们嘲笑人们有一天会在互联网上工作的想法。今天,几乎每个人都至少部分地在互联网上工作(想想电子邮件和社交媒体)。像《星际迷航》这样的科幻电影中想象的遥远未来的平板电脑和通信设备现在已经成为商品,我们的设备比想象的要酷得多。更重要的是,技术进步不是线性的,而是指数式的。在接下来的 20-30 年里,我们将会经历过去 100 年里所经历的一样多的创新。我上面概述的想法将会很平常。
那么,你如何为即将到来的工作危机做准备呢?我显然不确定,但我有八个提示(中国文化中最幸运的数字),很高兴听到你进一步的想法:
Wang Dongling at CHINA 8 Exhibition Tour, 2015, Germany
- 在科技发达的社会中建立你的生活。这可能是最难实现的一个,但可能是最重要的一个。尽量搬到美国、中国,或者这些顶级科技大鳄拥有强大经济和文化影响力的地区。
- 学习指数级技术这样你就可以参与构建未来技术,而不仅仅是承受和享受其后果。你不需要改变职业,只需要探索这种技术将如何彻底改变你的领域,看看你自己是否能成为先锋。
- 学习普通话和中国文化。由于中国快速的技术进步和越来越多的用户产生数据,中国很可能在十年内在人工智能方面超过美国。能够有效地参与中国控制的经济生态系统将使你的生存和成功机会翻倍。
- 成为技艺高超的艺术家。因为你的“人情味”,获得一项即使机器人能做得更好也会被其他人欣赏的技能。
- 尽可能多存钱,以你认为合适的任何方式投资指数技术。了解像比特币这样的非通胀货币,它在未来可能比中央法定货币贬值更少。
- 看和读科幻,因为编剧已经想通了各种未来的场景。一些未来的场景会成为现实,认清这些迹象会帮助你在未来做出更好的生活决定。
- 看历史小说和历史书。历史会重演,人类在各种情况下的行为都是可以预测的。在不断变化的环境中理解人性,有一天真的可以拯救你的生命。
- 给自己时间去思考、梦想和幻想。我发现冥想的形式太局限了,但是我同意它的前提。当你有一分钟的时候,不要马上拿起手机,把你的大脑周期浪费在迷因上。相反,让你的眼睛和头脑去思考。分析你收到的信息。质疑显而易见的事实。注意你可以利用的机会。
将熊猫数据导出到 Elasticsearch
Photo by CHUTTERSNAP on Unsplash
如何将您的数据帧行发送到 elasticsearch 数据库。
介绍
因此,您已经完成了数据的下载和分析,并准备好将其转移到 Elasticsearch 数据库中。本文介绍了如何准备数据并将其发送到 elasticsearch 端点。
要求
Python 3.6.5
numpy==1.15.0
pandas==0.23.4
elasticsearch==6.3.1import numpy as np
import pandas as pdfrom elasticsearch import Elasticsearch
from elasticsearch import helpers
es = Elasticsearch(http_compress=True)
清理您的数据
Pandas dataframes 很乐意在您的数据中保存 NaN 值,但是对 null 值的容忍不是 elasticsearch 的特性。仔细想想,这是有道理的;您要求索引器不索引任何内容。您可以通过一些简单的函数运行数据来避免这种情况。
空白日期
如果您的数据框架有空白日期,您需要将其转换为 elasticsearch 接受的值。elasticsearch 中的日期可以是格式化的日期字符串(例如“6–9–2016”)、自 Unix 纪元以来的毫秒数或自 Unix Epoc 以来的秒数( elastic docs )。使用毫秒的空日期,因为 Unix 纪元是 1970 年 1 月 1 日。如果你有包括 70 年代早期的历史日期,你可能要考虑一些其他的。
这是一个可以和 dataframe 一起使用的简单函数。apply()清理日期列。
from datetime import datetimedef safe_date(date_value):
return (
pd.to_datetime(date_value) if not pd.isna(date_value)
else datetime(1970,1,1,0,0)
)df['ImportantDate'] = df['ImportantDate'].apply(safe_date)
避免其他空白值
任何包含空值的字段都是有问题的,因为空日期。字符串值比日期更容易,但是您需要提供一个值。下面的代码使用 df.apply()函数将空格替换为安全字符串。
def safe_value(field_val):
return field_val if not pd.isna(field_val) else "Other"df['Hold'] = df['PossiblyBlankField'].apply(safe_value)
创建文档
一旦您确信您的数据已经准备好发送到 Elasticsearch,就该将行转换为文档了。Panda dataframes 有一个方便的“iterrows”函数可以直接实现这一点。它返回行的索引和包含行值的对象。这是一个“pandas.core.series.Series”对象,但其行为类似于传统的 python 字典。这个代码片段演示了如何将 iter 响应分配给变量。
df_iter = df.iterrows()index, document = next(df_iter)
Elasticsearch 需要 python 格式的数据,使用。Series 对象的 to_dict()方法。但是,您可以选择要发送到数据库的数据,并使用简单的过滤函数。注意下面的函数返回一个字典理解。
use_these_keys = ['id', 'FirstName', 'LastName', 'ImportantDate']def filterKeys(document):
return {key: document[key] for key in use_these_keys }
发电机
我们终于准备好使用 python 客户机和助手向 Elasticsearch 发送数据了。helper.bulk api 需要一个 Elasticsearch 客户端实例和一个生成器。如果你不熟悉发电机,去了解他们的记忆尊重的好处。如果你没有时间做这个,只需要理解神奇的是yield
,当 bulk.helpers 函数请求数据时,a 会给出数据。下面是遍历数据框架并将其发送到 Elasticsearch 的代码。
from elasticsearch import Elasticsearch
from elasticsearch import helpers
es_client = Elasticsearch(http_compress=True)def doc_generator(df):
df_iter = df.iterrows()
for index, document in df_iter:
yield {
"_index": 'your_index',
"_type": "_doc",
"_id" : f"{document['id']}",
"_source": filterKeys(document),
}
raise StopIterationhelpers.bulk(es_client, doc_generator(your_dataframe))
分解生成的字典
doc_generator 的工作只是提供一个带有特定值的字典。这里有一些关于这里发生的事情的细节的评论。
"_index": 'your_index',
这是您在 Elasticsearch 中的索引名称。如果没有索引,可以在这里使用任何有效的索引名。Elasticsearch 会尽最大努力自动索引你的文档。但是,提前创建索引是避免拒绝文档和优化索引过程的好主意。
"_type": "_doc",
请注意:_type
已被 Elasticsearch 弃用。版本 6.3.1 仍然支持命名类型,但这是开始转换为’ _doc '的好时机。
“_id” : f”{document[‘id’]}”,
_id
是 Elasticsearch 的唯一 id。不要把它和你文档中自己的“id”字段混淆。这可能是添加 itterows()中的index
变量的好地方,通过类似f”{document['id']+index}".
的东西使文档更加独特
"_source": filterKeys(document),
_source 是这个练习的核心:要保存的文档。使用document.to_dict()
或任何其他有效的 python 字典都可以。
raise StopIteration
出于礼貌,我加入了这一行。bulk.helpers 函数将处理生成器的突然终止。但是提高 StopIteration 可以省去这个麻烦。
结论
注意一些细节,把你的熊猫数据转移到一个弹性搜索数据库是没有戏剧性的。只要记住空值是 elasticsearch 的一个问题。剩下的就是创建一个生成器,将您的行处理成 python 字典。
使用 Jupyter 内核网关公开端点
Photo by Nikola Knezevic on Unsplash
在浏览中型文章时,我偶然发现了 Jupyter 内核网关,它对我来说是全新的。所以,我也探索了一样。我发现了它的文档,并决定实施一个项目来更好地理解它。
Jupyter 内核网关是一个 web 服务器,提供对 Jupyter 内核的无头访问。— Jupyter 内核网关回购
基本上,该服务允许我们与任何给定的 Jupyter 笔记本的 Jupyter 细胞进行交互,然后相应地使用这些信息。支持GET
、POST
、PUT
和DELETE
。让我们一起开始这段旅程吧。你可以在这里查看我的知识库。
关于项目
Photo by Breno Assis on Unsplash
在这个项目中,我们将使用来自sklearn
的加州住房数据集,在其上训练一个Random Forest Regressor
模型,然后预测房价。我们将设置一个GET
调用来获取数据集的统计数据,并设置一个POST
端点来获取具有给定特征集的房屋的预测价格。
由于这篇文章是关于 Kernel Gateway 的,而不是关于加州住房的机器学习项目本身,我将跳过它的细节,但将解释笔记本的相关单元。笔记本位于这里。
基础
Photo by Mimi Thian on Unsplash
我们先来看一下 Jupyter 内核网关的基础知识。
定义端点
我们需要用注释开始我们想要创建为端点的单元格。如果我们想要创建一个路径为/housing_stats
的GET
端点,它被定义为:
# GET /housing_stats
然后,我们定义 Python 代码并处理数据。在单元中完成所有工作后,我们定义端点的响应。它被定义为一个带有 JSON 值的print()
命令。因此,如果我们必须在参数total_houses
中返回数据集中房屋的总数,我们将其定义为:
print(json.dumps({
'total_houses': 20640
}))
就这么简单。如果需要,我们可以将功能扩展到更复杂的解决方案。因此,每个端点 Jupyter 单元将类似于下面的 Github 要点:
启动服务器
要启动服务器,有一个非常简单的命令。我们将使用名为House Price Prediction
的 Jupyter 笔记本。同样的将在0.0.0.0:9090
上市。代码如下:
jupyter kernelgateway --api='kernel_gateway.notebook_http' --seed_uri='House Price Prediction.ipynb' --port 9090
只要您计划在不同的项目上工作,只需更改端口号和/或 Jupyter 笔记本名称。
获取和发布端点
Photo by Mathyas Kurmann on Unsplash
让我们在笔记本中设置我们的端点。
获取端点
第一行包含单词GET
来定义它是一个 GET 端点,然后是路径/housing_stats
。我们提取数据集中的所有房屋,所有房屋的最大值和所有房屋的最小值。此外,根据我在笔记本中的分析,我发现最重要的特征是区块的中值收入。因此,我将所有这些值转储到一个JSON
中,并将其放在打印命令中。print 命令定义了该端点的响应。
终点后
现在,我想使用我训练过的机器学习模型来预测任何具有给定功能集的新房子的价格。因此,我使用这个端点来发布我的功能,并获得作为预测价格的响应。
我通过将单元格定义为路径为/get_prices
的POST
端点来开始该单元格。请求数据包含在对象REQUEST
内,键body
内。因此,我首先加载请求,然后从body
标签中读取所有值,并将其转换成一个 Numpy 数组。然而,形状是不正确的,因此,我使用 Numpy 的函数reshape
来纠正它。然后我预测价格。它返回一个预测价格的数组,该数组只有一个值,所以我将该值读入变量predicted_price
。我将其重新格式化为两位小数,并乘以100000
,因为值的单位是 100,000。最后,我通过将该值附加到一个字符串并放入 print 命令中来返回值。
提出请求
让我们按照步骤与我们的端点进行交互:
- 启动你的 Jupyter 笔记本。我把我的笔记本命名为
House Price Prediction
,所以我开始也一样。 - 启动服务器。使用我在上面定义的命令,您的服务器将在
http://0.0.0.0:9090
开始运行。 - 最后,决定从哪里调用端点。您可以使用名为
Postman
的工具,或者创建一个网页来进行这些呼叫,或者您可以简单地创建另一个笔记本来呼叫这些端点。
这里,我们将创建一个笔记本Requests
并使用 Python 中的requests
包来调用这些端点并获得结果。
建立
让我们创建一个新的 Jupyter 笔记本Requests
。我们将把requests
包导入到笔记本中,它用于调用 Python 中的端点。然后,我们将变量URL
中的基本 url 指定为[http://0.0.0.0:9090](http://0.0.0.0:9090.)
。
发出 GET 请求
我们使用request.get()
发出 get 请求,并指定完整的 URL,即[http://0.0.0.0:9090](http://0.0.0.0:9090.)/housing_stats
,并将其保存在变量stats
中。然后,我们从该变量加载JSON
。对于每个键值对,我们打印相同的内容。
stats
有应答对象。为了获得编码的内容,我们使用content
后跟decode('UTF-8)
。然后迭代结果。
我已经在上面的 Github Gist 中添加了结果作为注释。端点用所有房屋、它们的最高和最低价格以及预测任何房屋价格的最重要因素来响应。
提出发布请求
这里,我们将使用requests.post()
来发出 POST 请求。我们首先指定完整的 URL,即[http://0.0.0.0:9090](http://0.0.0.0:9090.)/get_price
。我们以 JSON 的形式发送特性。您可以随意更改这些值,并查看对预测价格的影响。然后,我们从端点加载结果,并打印我们得到的结果。
expected_price
有响应对象。为了获得编码的内容,我们使用content
后跟decode('UTF-8)
。然后读取结果,其result
字段具有我们的实际响应。
从上面的评论回复中,您可以看到对于给定的一组特性,房子的预测价格是 210,424 美元。
结论
在本文中,我们讨论了 Jupyter 内核网关,它允许我们将 Jupyter 笔记本单元转换为 REST 端点,我们可以调用这些端点并从中获得响应。然后,我们通过一个示例项目探索了相同的用法。要了解更多信息,您应该查看 Jupyter 内核网关的文档。
请随时分享你的想法,想法和建议。我们随时欢迎您的反馈。
用自定义激活函数扩展 PyTorch
PyTorch 和深度学习初学者教程
介绍
今天,深度学习正在迅速传播,并被应用于各种机器学习问题,如图像识别、语音识别、机器翻译等。有各种高度可定制的神经网络架构,当给定足够的数据时,它们可以适合几乎任何问题。每个神经网络都应该被精心设计以足够好地适应给定的问题。您必须微调网络的超参数(学习率、下降系数、权重衰减和许多其他参数)以及隐藏层的数量和每层中的单元数量。为每一层选择正确的激活函数也至关重要,可能会对模型的度量分数和训练速度产生重大影响。
激活功能
激活函数是每个神经网络的基本构建模块。我们可以从流行的深度学习框架的大量流行激活函数中进行选择,如 ReLU 、 Sigmoid 、 Tanh 以及许多其他函数。
然而,要创建一个特别为您的任务定制的最先进的模型,您可能需要使用自定义激活功能,这是您正在使用的深度学习框架中所没有的。激活功能可以根据复杂性大致分为以下几组:
- 简单的激活功能如路斯、平方根倒数单元(ISRU) 。你可以使用任何深度学习框架快速实现这些功能。
- 具有可训练参数的激活功能,如软指数激活或 S 形整流线性单元(SReLU) 。
- 激活功能,在某些点不可微,需要自定义实现后退步骤,例如双极整流线性单元(BReLU) 。
在本教程中,我将介绍使用 PyTorch 框架实现和演示所有这些类型的函数。你可以在 GitHub 上找到这篇文章的所有代码。
安装
要浏览激活函数的实现示例,您需要:
- 安装 PyTorch ,
- 要将必要的导入添加到脚本中,
The necessary imports
- 为演示准备数据集。我们将使用众所周知的时尚 MNIST 数据集。
Prepare the dataset
最后一件事是建立一个示例函数,它运行模型训练过程并打印出每个时期的训练损失:
A sample model training function
现在已经为创建带有定制激活功能的模型做好了一切准备。
实现简单的激活功能
最简单常见的激活功能
- 是可微的并且不需要手动执行后退步骤,
- 没有任何可训练参数。它们的所有参数都应该预先设定。
这种简单功能的一个例子是 Sigmoid 线性单元或路斯,也称为 Swish-1:
SiLU
这样一个简单的激活函数可以像 Python 函数一样简单地实现:
所以现在路斯可以用在用神经网络创建的模型中。顺序:
或者在一个简单的模型中,它扩展了 nn。模块类别:
用可训练参数实现激活功能
有很多带参数的激活函数,可以在训练模型的同时用梯度下降法进行训练。其中一个很好的例子是软指数函数:
Soft Exponential
要使用可训练参数实现激活功能,我们必须:
- 从 nn 派生一个类。模块,并使该参数成为其成员之一,
- 将参数包装为 PyTorch 参数,并将 requiresGrad 属性设置为 True。
以下是软指数的一个例子:
现在,我们可以在模型中使用软指数,如下所示:
用自定义后退步骤实现激活功能
激活功能的完美例子是 BReLU (双极性整流线性单元),它需要实现自定义的后退步骤:
BReLU
此函数在 0 处不可微,因此自动梯度计算可能会失败。这就是为什么我们应该提供一个自定义的后退步骤来确保稳定的计算。
要使用后退步骤实现自定义激活功能,我们应该:
- 创建一个继承 torch.autograd 函数的类,
- 重写静态向前和向后方法。Forward 方法只是将函数应用于输入。向后方法是在给定损失函数相对于输出的梯度的情况下,计算损失函数相对于输入的梯度。
让我们看一个 BReLU 的例子:
我们现在可以在模型中使用 BReLU,如下所示:
结论
在本教程中,我介绍了:
- 如何用 PyTorch 创建一个简单的自定义激活函数,
- 如何创建一个带有可训练参数的激活函数,该函数可以使用梯度下降进行训练,
- 如何创建一个自定义后退的激活函数?
本教程的所有代码都可以在 GitHub 上获得。为 PyTorch 和 Keras 实现定制激活功能的其他例子可以在这个 GitHub 库中找到。
改进
在构建许多自定义激活函数时,我注意到它们通常会消耗更多的 GPU 内存。使用 PyTorch 就地方法创建定制激活的就地实现改善了这种情况。
附加参考
以下是额外资源和进一步阅读材料的链接:
- 激活功能 wiki 页面
- 【PyTorch 扩展教程
在 Travis CI 上进行广泛的 Python 测试
在几种操作系统上测试开源 Python
假设您有一个正在维护的开源 Python 项目或包。您可能想在目前广泛使用的主要 Python 版本上测试它。你绝对应该。在某些情况下,您可能还需要在不同的操作系统上测试它。在这篇文章中,我将讨论这两种情况,并建议一种方法来做到这一点。
为了这篇文章,我假设你是:
- 在 Github 上托管你的开源项目。
- 使用
pytest
来测试你的代码。 - 检查代码覆盖率。
- 您希望向免费服务 codecov.io 提交覆盖率统计数据,以便在您的 repo 上有一个漂亮的动态测试覆盖率徽章。
因为这是一个很好的简单流程,也是我使用的流程。然而,这里介绍的方法可以很容易地适用于其他流程。
下面的帖子向你展示了产生 .travis.yml 文件的结构的各个阶段和合理性,该文件全面涵盖了不同 Python 版本和操作系统上的测试。如果你对这些都不感兴趣,你可以看看我为它创建的 GitHub gist 的最终结果。
Travis 的基本 Python 测试
在几个 Python 版本上测试代码的一个很好的方法是使用 Travis CI 服务,它为开源(即公共)GitHub 项目提供(除了其他特性之外)基于云的持续测试免费。让我简单地向您介绍一下 Travis 的基本 Python 测试。如果你熟悉这个话题,请随意跳过。
要使用 Travis,只需遵循以下三个步骤:
- 向 Travis 注册 GitHub,允许 Travis 访问您的项目。
- 在您的存储库页面中为您想要测试的存储库启用 Travis。
- 将一个
.travis.yml
文件放在您的存储库的根目录下;这个文件将告诉 Travis 应该如何构建和测试这个特定的项目。
. travis.yml 文件
那么假设你有一个小 Python 包,带一个简单的 [setup.py](https://github.com/shaypal5/skift/blob/master/setup.py)
文件。您有下面这个非常基本的.travis.yml
,它通过几个 Python 版本运行您的测试:
让我们检查一下上述文件的每个部分:
-
第一行声明我们正在构建一个 Python 项目。
-
第二个条目详细描述了我们想要构建的 Python 版本:2.7、3.5 和 3.6。
-
第三个条目按顺序详细说明了我们想要运行的预安装命令集。我使用四个命令:
(1)python --version
来查看我正在运行的确切的 Python 版本。因为你总是要使用最新的画中画。有些构建会因为没有这个而失败(比如 Python 2.7,对我来说)。
(3)pip install -U pytest
——我已经决定一直这样做,因为这又一次让我免于失败。对于我目前的一些项目,如果我只有pip install pytest
而没有-U
来更新,Python 3.6 将会失败。
(4)pip install codecov
—因为我只在 Travis 上使用它,而不是在本地,所以它不是我的包的额外[test]
依赖项的一部分。 -
第四个条目详细介绍了安装命令。在这种情况下,由于命令在存储库的根文件夹中运行,并且我们有一个 pip-installable 包,我们用可选的测试依赖项
pip install
“this”文件夹(so.
)。我的包的测试依赖通常是集成了两者的pytest
、coverage
和pytest-cov
。 -
第五个条目详细描述了要运行的构建/测试脚本。我们只想运行
pytest
。我假设您在一个pytest.ini
文件中详细描述了pytest
的所有 CLI 参数。当我们开始添加内容时,这将有助于保持.travis.yml
文件的整洁。这里有一个pytest.ini
的例子: -
最后一项详细描述了测试脚本成功完成后要运行的命令。在我们的例子中,我们只想向 codecov.io 报告测试覆盖结果,所以我们运行相应包的命令。它将获取由我们的
pytest
运行生成的覆盖报告,并将其发布到那里。如果您想要任何构建的覆盖率结果——失败的或者成功的——只需将这一行作为script
条目的第二项。
特拉维斯建造
有了这个配置,每次提交到您的存储库都会触发 Traivs CI 上相应项目的良好构建,每个构建由每个 Python 版本的一个作业组成(都在 Linux 机器上),如下所示:
A travis build consisting of several jobs
您还可以进入每个作业的日志,查看每个命令的结果,无论是实时的还是事后的:
A log for a specific job in a Travis build
在几个 Python 版本上测试您的 Python 代码的另一种方法是使用 tox,这是一个自动化和标准化 Python 测试的强大工具。我支持上面的方法,因为它的one job = = one Python version方法意味着构建完成得更快(因为在 Travis 上有多达三个作业并行运行),并且您可以立即了解哪个版本给您带来了麻烦,而无需深入查看日志。当然这只是我个人的喜好。
事情急转直下…
当我们试图使用上述结构来测试 Python 3.7 时,多版本 Python 测试中的第一个复杂性出现了。如果您只是在 Python 版本条目中添加了3.7
,那么您的新 3.7 版本将很早就失败,需要寻找一个足够的归档。这是因为 Linux 构建默认运行在 Ubuntu 14.04(昵称trusty
)上,而 Python 3.7 和更高版本的作业需要 Ubuntu 16.04(昵称xenial
)。
你可以通过在你的.travis.yml
文件中添加一个dist: xenial
条目来解决这个问题,让所有版本的作业都建立在 Ubuntu 16.04 上。但是如果你喜欢在 Ubuntu 14.04 上测试较低版本,并且只在 Python 3.7 上使用xenial
呢?虽然这可能是一个可以忽略的特例,但它将允许我以渐进的方式介绍 Travis 构建矩阵,这些将在稍后被证明是至关重要的,所以请跟我来。
构建矩阵和 Python 3.7
在 Travis 中有两种方法可以指定多个并行作业。首先是为影响构建环境的多个条目提供多个选项;所有可能组合的构建矩阵被自动创建并运行。例如,以下配置生成一个扩展到 4 个单独(2 * 2)作业的构建矩阵:
第二个是在matrix.include
中指定您想要的配置的精确组合。继续上面的例子,如果 Python 2.7 不支持并行化,您可能更喜欢指定三个特定的作业:
或者,在我们的例子中,要在xenial
上运行 Python 3.7 作业,添加一个条目:
酷毙了。因此,我们看到了在 Travis 上测试 Python 3.7(Python 的特殊雪花)的两种方法,并对 Travis 构建矩阵有了一些了解。我们继续吧。
在其他操作系统上测试 Python 项目
因此,您正在 Python 的每个重要主要版本上测试您的简单纯 Python 包。你是一个负责任的开源贡献者。为你欢呼。
但是如果你的 Python 项目不是纯血统,而是包含一些专门的 C++代码的麻瓜呢?或者也许你的代码是纯 Python,但是它与操作系统交互(例如写文件、处理线程或进程等。)从某种意义上说,这在不同的操作系统之间会有所不同?
如果是这样的话,如果您希望您的项目支持这三种主要的操作系统,那么您肯定应该在这三种操作系统上测试您的代码(也可能构建它)。
对我自己来说,需求来自两个项目,都是纯 Python:
- 第一个是 Cachier ,一个为 Python 函数提供持久、无陈旧、本地和跨机器缓存的包。当我写文件时,为了多线程的安全,我不得不锁定文件,结果是当一个 Windows 用户第一次试图使用我的包时,我的内置解决方案(使用
fcntl
)崩溃了。 - 第二个是 Skift ,为Pythonfast text实现scikit-learnwrappers。实现需要以不同的编码读写文件,在某些情况下,这在不同的操作系统上表现不同。
我选定的解决方案是扩展 Travis 构建矩阵,以包含操作系统和主要 Python 版本的特定组合,每个版本都在完全独立的环境中运行,各司其职。
同样,当将这种方法与使用tox
进行比较时,我要说的主要优势是:
- 把复杂性和责任从你身上卸给特拉维斯。
- 获得真实环境的更准确的表示:直接在操作系统级安装单一版本的纯 Python,而不是通过
tox
。这是大多数小型开源 Python 项目用户安装代码的方式。 - 一个作业==一个 OS 版本和一个 Python 版本。您可以立即看到构建是否因为您的测试在特定 Python 版本(例如,所有操作系统上的 2.7 版本)、特定操作系统(Windows 上的所有 Python 版本)或特定组合上失败而失败。这在作业视图中非常明显:
We obviously have a Linux-related build problem
希望我已经让你相信这是一种有效的多操作系统测试方法,所以我们可以进入细节了。我们将从在 macOS 上进行测试开始,到在 Windows 上结束。
在 macOS 上测试 Python 项目
在撰写本文时, Python 版本在 macOS 环境中不可用。这并不意味着不可能用 Travis 在 macOS 上测试 Python,只是下面这种幼稚的方法行不通:
无论你给python
键分配什么版本号,你都会得到一台安装了 Python 3.6.5 的 macOS 机器。这是因为请求一台带有os: osx
的机器会启动一台使用默认 Xcode 映像的机器,对于 Travis 来说,默认 Xcode 映像当前是 Xcode 9.4.1。
当前获取特定 Python 版本的 macOS 机器的黑客方式是使用osx_image
标签请求一个特定的 Xcode 映像,您知道它预装了您想要使用的 Python 版本。
例如,要获得一台装有 Python 3.7 的机器,您可以添加条目osx_image: xcode10.2
(具体来说,您将获得 Python 3.7.3)。酷毙了。那么如何知道哪个 Xcode 映像来自哪个 Python 版本呢?不幸的是,这个映射在 Travis 的网站或文档中没有列出。
不过,你很幸运,我做了肮脏的工作,挖出了这个信息。这基本上意味着积极地在 Travis 的博客中搜索关于 Xcode 图片的帖子发布版,以搜寻每张图片的 Python 版本。我发现的主要 Python 版本的最新版本有:
xcode9.3
—预装 Python 2.7.14_2xcode9.4
—预装在 Python 3.6.5 中xcode10.2
—预装 Python 3.7.3
不幸的是,我还没有找到 Python 3.5 预装的 Travis Xcode 映像(如果有,请告诉我!).
所以你得到了正确的 Xcode 标签。然而,您仍然需要修改一些构建命令。例如,对于 Python 3 版本,我们需要显式调用pip3
和python3
来安装和调用(分别)Python 3 代码,因为 macOS 预装了 Python 2(这是python
命令所指向的):
考虑到这一点,您可能会认为 Python 2 作业需要更少的自定义条目。不幸的是,因为我们使用的是 OS Python,所以 pip 安装命令需要附加 Python 2 的--user
标志。此外,结果是他们的 CLI 命令不会被安装,所以我们将不得不再次通过python
命令调用他们的命令:
很好,我们已经完成了在 macOS 上测试 Python。吃块饼干吧。
A cookie
在 Windows 上测试 Python 项目
Travis 对 Windows 版本的支持还处于早期阶段。目前,仅支持 Windows Server(版本 1803)。这不是 Python 自带的,而是 Chocolatey 自带的,它是 Windows 的一个包管理器,我们将用它来安装 Python。
因为我们使用 Chocolatey 来安装 Python,所以我们受限于通过它可以获得的版本。对于 Python 3,它们是 3.5.4、3.6.8 和 3.7.4。对于 Python 2,版本 2.7.16 是当前默认安装的版本。
下面是获取 Windows-Python 作业的作业条目的简单变体,它包括 Chocolatey install 命令choco
和一个环境变量设置:
如您所见,通用的script
和after_success
阶段工作得很好。你可以看一下最终文件来了解每个版本所需的细微变化,包括 Python 2.7。
最终注释
到目前为止,我们已经介绍了几乎所有常见操作系统和重要 Python 版本的组合。结合我们上面看到的零碎内容,我们可以得出一个不那么短的.travis.yml
文件,为 Python 项目提供全面的测试,你可以在我创建的 Github gist 中找到它。
然而,在我结束这篇文章之前,我想补充一些最后的注释。
允许失败和快速完成
在某些情况下,您可能希望在您预计会失败的特定操作系统版本组合上连续测试您的代码,例如当某些测试在 Windows 上失败,但您正准备在不久的将来添加 Windows 支持时。在这种情况下,最好不要因为这样的工作而导致整个构建失败,这样您就不会收到烦人的构建失败通知(还因为您可以在 repo 上展示一个漂亮而闪亮的“build: passing”徽章)。
Gandalf being a dick about a failing Windows job
您可以通过在矩阵条目下添加一个allow_failures
条目来实现这一点,详细说明允许作业失败的键值对。例如,要允许 macOS 上的 Python 3.7 失败,请使用:
matrix:
- allow_failures:
- os: osx
osx_image: xcode 10.2
设置- os: windows
将允许所有 Windows 构建失败。
此外,如果您已经在使用allow_failures
逻辑,您可能想要利用fast_finish
功能。一旦所有不允许失败的任务完成,设置fast_finish: true
将决定整体构建状态——通过或失败,而其余的任务保持运行。这在小型开源项目中通常并不重要,但是拥有它是很好的,特别是当一些外来的操作系统或 Python 版本的任务被允许失败和需要很多时间的时候。
针对开发分支的测试
您可以通过添加相应的条目来针对不同 Python 版本的开发分支测试您的代码,比如在python
键下的3.7-dev
。要测试的一个重要的开发分支可能是3.8-dev
,为即将到来的事情做准备。您可能还希望允许所有使用开发分支的作业失败。
Python 版本或基于操作系统的逻辑
我介绍的解决方案将 macOS 和 Windows 版本的大部分特殊代码放在构建矩阵中。但是,如果您有一些特定于 Python 版本的安装或测试代码,但应该在所有操作系统上运行,则可以根据相应 Travis 环境变量的值来调整命令:
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install . ancient_testing_packge; else pip install ".[test]"; fi
要对同一操作系统的所有作业执行相同的操作,请使用:
if ["$TRAVIS_OS_NAME" == "linux"]; then pip install . special_linux_packge; else pip install ".[test]"; fi
当然,如果是这种情况,您可能应该考虑在您的setup.py
文件中更干净地处理这个问题,通过基于 Python 版本或操作系统(使用 Python 代码推断)动态地为test
构建extras_require
。
感谢你通读这篇文章。希望你觉得有用。😃
同样,您可以在专用的 Github gist 中查看完整的结果.travis.yml
文件。
如果您有任何意见、更正或改进的想法,请写在下面,或者通过我的个人网站联系我。干杯!