TowardsDataScience 博客中文翻译 2019(一百八十六)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

探索波士顿和西雅图的房地产投资机会

原文:https://towardsdatascience.com/exploring-real-estate-investment-opportunity-in-boston-and-seattle-9d89d0c9bed2?source=collection_archive---------18-----------------------

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

Airbnb’s treehouse

让 Airbnb 的数据告诉你下一个投资的房产。

该项目旨在设计一个城市房地产市场分析框架,目标是确定 Airbnb 市场中产生租金收入的房地产。随着 Airbnb 成为事实上的短期住宿市场,甚至成为酒店的替代品,其用户活动反映了一个城市房地产和酒店业的脉搏。房产所有者现在能够像一个迷你连锁酒店那样经营,提供短期到中期的住宿空间。波士顿和西雅图的数据集用于开发该框架,但其真正的目标是使其可用于 Airbnb 覆盖的其他城市,假设其他城市数据集共享相同的要素数据。

商业理解

波士顿和西雅图是位于美国两侧的两个城市,据说具有鲜明的房地产特征:房地产的需求和供应以及在决定价格方面发挥作用的因素。分析结果将用于房地产投资者,他们打算翻新投资的物业,以从 Airbnb marketplace 获得租金收入。该分析试图回答以下问题:

  1. 哪个城市的租赁市场更强劲,因此对投资更有吸引力?
  2. 需求是否稳定且不断增长?
  3. 这座城市的哪个街区投资回报率更高?
  4. 投资哪种类型的房产?
  5. 哪些因素决定租金收益率/价格?

以下技术问题应回答上述商务问题:

  1. 波士顿和西雅图的价格分布是怎样的?
  2. 这些年来,这两个城市的租赁需求发生了怎样的变化?有什么潜在的因素影响他们吗?市场饱和了吗?
  3. 与价格高度相关的特征是什么?

数据理解

CSV 文件中的三个数据集用于分析:

  1. 列表—显示每个列表的 92(西雅图)和 95(波士顿)属性的列表数据。
  2. 评论——由客人给出的具有 5 个属性的评论。关键属性包括日期(日期时间)、列表标识(离散)、审阅者标识(离散)和注释(文本)。
  3. 日历-通过列表预订下一年的数据。总共四个属性,包括 listing_id(离散)、date(日期时间)、available(分类)和 price(连续)。

值得注意的是,Airbnb 数据集有其局限性,因此它只是这两个城市短期租赁市场的一部分。例如,它不会显示未满足的需求。它也没有提供购买投资性房地产的价格数据,以获得良好的投资回报(ROI):

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

Airbnb 数据集是分子。分母仍然需要房地产价格数据集,这将在同一主题的后续文章中探讨。

数据准备

为了准备用于分析的数据,需要采取几个步骤来清理数据集:

  1. 处理缺失值-删除缺失值超过 1000 的列
  2. 低值列—删除包含 URL 链接、不可用记录(如主机名、街道)以及只有一个唯一值的列。
  3. 布尔列—带有“t”和“f”的列被转换为布尔“真”和“假”。
  4. 数字列—清理价格、费率和百分比等列,以便提取其数值并保存为浮点型。
  5. 分类数据-包含分类数据(如邮政编码、物业类型、邻居、房间类型、床类型)的列经过编码,以便用于机器学习算法。
  6. 异常值-识别异常值并记录下来,它可能会影响以后的分析。然而,它们仍然保留在数据集中,以供可视化观察。稍后将采取措施处理异常值。
  7. 共线特征-由于在此阶段尚未采用机器学习,因此尚未识别共线特征。此处做了一个注释,以便在未来工作中需要时处理共线特征。

Q1。哪个城市的租赁市场更强劲,因此对投资更有吸引力?

就短期住宿业务而言,这两个城市全年都有大量的文化节、音乐会、会议和展览以及体育赛事。波士顿也是举办大型活动的热门地点,例如波士顿马拉松赛吸引了大量的人群来到这个城市。这些活动带来的活力是短期租赁市场的巨大商机。

[## 西雅图—活动日历

有趣的事情,夏季活动,街头集市,艺术和音乐节。食物、啤酒和葡萄酒品尝。活动日历…

www.events12.com](https://www.events12.com/seattle/) [## 波士顿活动|音乐会、节日、马拉松、展览

波士顿的活动包括餐厅周、体育赛事、节日、假日活动等等。找到一个完整的…

www.bostonusa.com](https://www.bostonusa.com/events/events-calendar/)

除了休闲和文化,我们还可以把这个城市视为一个吸引商务旅客的商业中心。这些旅行者将能够在这个城市没有任何活动发生的安静时期填补空白。

大学毕业时期是另一个季节性事件,当父母和亲戚参加毕业典礼时,会吸引大量人群进入城市。在这方面,波士顿大都会有一长串的学院:

[## 波士顿大都会的学院和大学列表—维基百科

这是波士顿大都市的学院和大学列表。有些位于波士顿市区内,而有些…

en.wikipedia.org](https://en.wikipedia.org/wiki/List_of_colleges_and_universities_in_metropolitan_Boston)

虽然西雅图作为教育中心有点落后,但仍然是毕业季的保证:

[## 华盛顿西雅图附近的四年制大学列表-华盛顿西雅图四年制大学

探索华盛顿州西雅图的四年制大学列表。

www.collegesimply.com](https://www.collegesimply.com/colleges/washington/seattle/four-year-colleges/)

旅游景点在城市是另一个点,看看填补空置客房在非季节性期间。

为了更好地了解住宿需求,我们可能需要两个城市的旅客和交通数据来进行更全面的定量分析。与此同时,Airbnb 的预订数据揭示了反映在价格上的需求。

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

Figure 1

从 Airbnb 预订数据集生成的价格分布图按需提供了一些提示。波士顿分布有一个更厚的尾部,显示了更不稳定的价格范围。而西雅图价格大多在 500 美元以内。

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

Figure 2

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

Figure 3

当我们把同一个预订数据集作为趋势来看,对每天的价格进行平均,我们会看到需求是如何在定价中发挥作用的。当供应不能满足城市大型活动的需求时,价格就会上涨。该图显示了周波动和季节动态。波士顿的价格趋势反映了受供应限制的更加多事和强劲的租赁市场。因此,不稳定的价格波动所造成的需求得不到满足。图 2 中 2017 年 4 月的大幅飙升很可能是波士顿马拉松的结果。规则和微小的波动是周末效应,因为网格线是在周六绘制的。

Q2。需求是否稳定且不断增长?

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

Figure 4

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

Figure 5

评论是由用户在入住酒店后提供的,因此可以很好地代表已满足的需求。从这两个城市的需求趋势来看,我们可以说这些年来需求确实在稳步攀升。波士顿在过去的两年里经历了需求的急剧攀升。价格波动每年都有季节性变化,这在 Q1 得到了部分揭示。图 4 和图 5 提供了更大的图景,需求通常在夏末秋初达到峰值。

然而,波士顿的价格动态仍处于上升轨道,而西雅图在 4 年的增长后看起来已经饱和。

Q3。这座城市的哪个街区投资回报率更高?

看图 2 和图 3,我们注意到波士顿的价格区间较高,甚至在淡季也高于西雅图的价格区间。这表明,在供应有限的情况下,中国对短期住房的需求更加强劲。

然而,这只是投资回报率的一部分。我们还需要考虑拥有房产的价格,以验证更高的租金收入是否能证明总体收益是合理的。换句话说,如果波士顿的房地产价格高得多,导致租金收入的收益率较低,那么市场就不会提供更好的投资回报率。

Q4。投资哪种类型的房产?

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

Figure 6

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

Figure 7

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

Figure 8

波士顿

对于波士顿市场,我们可以看到,公寓,共管公寓和房子是最受欢迎的产品在这里的大多数街区。租金收入较高的房产类型包括房子、联排别墅和共管公寓,这可能是因为上市数量柱状图显示的供应有限。

根据图 8,市中心、皮革区和南波士顿滨水区的租金通常高于其他街区。Back Bay,Dorchester,Fen Way,Jamaica Plain 和 South End 是供应充足的街区,这些应该是市场竞争更激烈的街区,因此低于平均租金价格。从这个角度来看,海湾村、南波士顿海滨和西区是供应方面竞争较弱的市场,租户的良好需求反映在他们愿意支付更高的价格。

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

Figure 9

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

Figure 10

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

Figure 11

西雅图

公寓,房子和公寓是西雅图最受欢迎的住宿产品。除了沃灵福德可能是一个异常值之外,大多数街区的租金都保持在一个范围内。价格最高的房产类型是船,一种奇异的房产。贝尔敦、弗里蒙特 Minor 和沃林福德出租物业价格更具竞争力,供应充足。荆棘崖、工业区、先锋广场和东南木兰是供应较少的街区,因此租金较高。如果购买房产的成本与其他地区相比,这些地区可能值得投资。

简而言之,位置和季节性在价格设定中扮演着重要的角色。

Q5。哪些因素决定租金收益率/价格?

由数字特征组成的相关矩阵显示,该房产可容纳的人数、床位、卧室和浴室的数量与租赁价格最相关,这非常合理,因为这意味着更大的房产。所以它在这里没有帮助揭示太多的洞察力。

包括点评分数在内的其他因素在设定租金价格时几乎不起作用。

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

Figure 12

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

Figure 13

根据以上分析,海湾村、南波士顿海滨和波士顿西区看起来是更好的投资选择。也许我们需要房地产价格数据来确定他们的投资回报率。

总之,随着 Airbnb 的发展和将其市场扩展到更多的城市,基于城市的数据集在分析房地产市场方面变得非常方便。然而,由于它们只是图片的一部分,有必要将数据集与其他房地产市场(如 Trulia、Zillow)以及旅客和交通跟踪数据结合起来,以促进房地产投资分析。

该框架编码在一个 Jupyter 笔记本中,可以从我的 Github 库中获得

使用机器学习和 R 探索招聘偏差

原文:https://towardsdatascience.com/exploring-recruitment-bias-using-machine-learning-and-r-8e071dad7dce?source=collection_archive---------20-----------------------

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

你好,

由于我在人力资源咨询方面的背景,我有机会与世界各地的众多人力资源专业人士共事。通过我的工作和学习,我很快意识到招聘过程是任何与多元化相关的研究的起点。与招聘过程相关的潜在人为偏见有很多。我希望我的工作有助于确立它毕竟不是一项复杂的研究。

目标:

使用一个 实验性 数据集进行本案例研究,我工作的主要目标是只调查招聘流程的入围阶段,以及:

  • 对招聘数据进行探索性数据分析,以确定招聘阶段的性别、种族模式
  • 调查性别和种族是否影响申请人入围流程
  • 应用机器学习来预测谁将入围并确定关键驱动因素
  • 根据调查结果建议更新招聘策略

数据集概述:

实验数据集包含以下字段:

  • 申请人代码:系统中每个申请的唯一标识符

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

  • 性别:男性代码为 1,女性代码为 2
  • ATSIyn :如果候选人是土著居民或托雷斯海峡岛民,则指定 1 =是。如果候选人是普通申请人,分配 2 =否
  • 入围 yn :如果被拒绝则分配 0,如果入围则分配 1
  • 已面试:未面试分配 0,面试分配 1
  • FemaleONpanel :仅针对男性小组分配 1 分,如果小组中有女性成员,则分配 2 分
  • OfferNY :如果向候选人提供了 offer,则分配 1;如果没有提供 offer,则分配 0
  • AcceptNY :如果接受则赋值 1,如果拒绝则赋值 0
  • JoinYN :如果加入则赋值 1,如果不加入则赋值 0

研究性别和种族模式的探索性数据分析:

女性申请者占申请者总数的 72.1%。

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

土著或托雷斯海峡岛民申请人占申请人总数的将近一半(43.2%)。

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

在简历筛选后,总共 280 名申请人中有 88 人进入下一阶段的决选名单。

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

入围的申请者中有 55 人接受了面试。

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

在进行的 55 次访谈中,有 22 次有女性成员。

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

在总共进行的 55 次面试中,有 28 人被录用。

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

要约接受率为 64%。18 名接受聘用的申请人加入了该公司。

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

调查性别和种族是否影响申请人入围流程:

根据上述探索性研究,申请人中的性别和种族比例分别为 72 %和 43%。抛开所有外部条件不谈,一个公正的招聘过程应该导致基于性别和种族的入围申请人具有相当相似的代表性。这是本节研究的目的。

申请人入围名单中的种族偏见分析:

如上所述,申请人的族裔比例为 43.2%。然而,根据以下调查结果,候选人名单中的族裔比例下降到 15% (19/88)。 这似乎表明在入围过程中倾向于一般申请者。

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

气球图表明,一般申请人可能会入围。

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

使用统计分析(卡方) ,可以确定具有种族背景的申请人的预期数量和观察数量之间存在巨大差距。在下面的案例中,只有 19 个入围(观察到),而预期的数字应该是 38。

入围过程中对普通类申请人的偏好是出于运气或机遇的可能性已经被非常低的卡方值 23.184 所否定,p < 0.0001.

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

对入围申请人的性别偏见的分析:

如上所述,女性在申请人中的比例为 72%。然而,根据以下调查结果,入围申请人名单中的女性比例已降至 56.7% (50/88),被拒人数高达 152 人。这似乎显示出在入围过程中对男性申请者的偏爱。

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

气球情节证实了女性申请者在面试入围阶段的高拒绝率。

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

使用统计分析(卡方检验) ,可以确定入围的女性申请人的预期人数和实际人数之间存在差距。在下面的案例中,只有 50 人入围(观察到),而预期数字应该是 63 人。

入围过程中优先考虑男性申请人是出于运气或机遇的可能性已经被非常低的卡方值 13.905 否定,p < 0.0001。

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

应用机器学习来预测申请人的入围名单并确定关键驱动因素:

在上述分析中,性别和种族是分开评估的。然而,如果大多数有种族背景的申请人碰巧是女性,那么暗示性别和有种族背景的申请人都面临偏见是不正确的。因此,在本节中,预测申请人入围名单的分析将考虑这两个变量。

进行这种分析的方法是通过逻辑回归。逻辑回归是一种机器学习算法,可用于预测申请人在申请流程中入围的可能性。该算法将用于研究我们的数据集,以确定申请人入围名单的主要驱动因素。

机器学习分析的结果表明,性别和 ATSIyn(土著或托雷斯岛民)都是申请人入围的重要预测因素。

总之,根据调查结果,男性申请人被列入候选名单的可能性是女性申请人的 3.3 倍。普通类申请人比土著或托雷斯海峡岛民申请人入围的可能性高 4.5 倍。

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

准备一个招聘策略来抵消偏见:

数据分析表明,基于综合数据集,性别和种族偏见存在于公司内部招聘流程的入围阶段。

为了消除偏见,推荐的策略是引入盲审流程,在入围流程中,对审查工作申请的内部或外部招聘人员隐藏申请人的姓名、性别和种族背景信息。

代码和数据集的引用和链接:

这两位学生在这一领域的工作深深地激励了我,这也是我在这一领域进一步探索的兴趣的起点。视频链接在这里给你看看。

所用的数据集和案例研究如马丁·爱德华兹和柯尔斯顿·爱德华兹所著的《预测性人力资源分析——掌握人力资源指标》一书中所述。r 代码是我开发的。

我的 github 账号上有数据集和 R 代码。下面提供了链接:

[## sambit 78/人员分析项目

我的所有个人项目都与人员分析相关-sambit 78/人员分析-项目

github.com](https://github.com/Sambit78/People-Analytics-Project/tree/master/05%20-%20Recruiting%20Bias%20Analysis)

谢谢:)

使用 PRAW API 包装器探索 Reddit 的“问我任何问题”

原文:https://towardsdatascience.com/exploring-reddits-ask-me-anything-using-the-praw-api-wrapper-129cf64c5d65?source=collection_archive---------14-----------------------

未来 Reddit 分析师的简短 PRAW 教程

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

PRAW + Python can be used to quickly access Reddit’s API

在撰写本文时,Reddit.com 是美国互联网上第五大最受欢迎的网站。你们中的大多数人可能都在这个网站上呆过,但对于那些不熟悉的人来说,Reddit 是一个由社区驱动的论坛或“子编辑”的大集合,在这里人们可以分享几乎任何主题的新闻、内容和观点。最受欢迎的社区之一,向我提问(r/IAmA),将著名或有趣的人物与日常 Redditors 联系起来,以便直接回答匿名参与者的问题,旨在充分利用与世界上最引人注目的一些人的无与伦比的接触。

在本文中,我将重点关注这个特定的社区,通过使用 PRAW(一种 Python“包装器”)连接到 Reddit API 中的基本信息的过程,在高层次上指导当前和未来的数据科学家,这种包装器就像一个附加包,可以将 API 调用集简化为用户易于使用的函数。这个特定的演练更多的是关于快速建立连接,避免任何深入到业务应用程序中(联系 Reddit Partnerships 以获得该信息)。也就是说,我希望留给你的不仅仅是一种新的数据技能,还有一些最引人注目的 AMA 作品的集合。

第一步:在你的终端上安装或更新 PRAW

#to install for the first time
pip install praw#to update
pip install praw --upgrade praw

步骤 2:创建和/或登录您的 Reddit 帐户,开始通过 OAuth 进行身份验证

点击此处创建账户或登录现有账户,并确保您已经验证了您的电子邮件。完成后点击这里进入你的应用列表,点击有点难找到的按钮‘创建应用’。

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

Beware of Reddit’s API guidelines.

命名你的应用程序和用例(“脚本”适合基本的、天真的探索),写一个关于你的计划的简短说明,并输入一个必需的重定向 URL(为此我使用了我的 GitHub URL)。点击“创建应用程序”后,您的页面将会更新,显示一个个人使用脚本密钥(在许多 API 教程中通常被称为“客户端 id ”)和一个机密密钥(您可能知道它是“API 密钥”)。

当使用需要 API 密钥和密码的 API 时,你应该不要将这些值硬编码到你的主文件中。为了保护你的 API 密匙不被公开传播,看看这个伟大的教学 jupyter 笔记本,里面有一些关于设置的更详细的注释。secret/ 目录将您的敏感信息存储在本地 JSON 文件中,并在需要时适当地调用它。您的credentials.json文件应该如下所示:

{"client_id": "XXXXXXXXX-XXXX","api_key": "XXXXXXXXXXXX_XXXXXX-XXXXXXX","username": "<your Reddit username>","password": "<your Reddit password"}

步骤 3:创建您的第一个授权 Reddit 实例

credentials.json或类似标题的文件夹中加载您的密钥:

# Load secret keys from credentials.json
import json
url = '[https://www.reddit.com/'](https://www.reddit.com/')
with open('/Users/<Your CPUs User>/.secrets/credentials.json') as f:
    params = json.load(f)

导入 PRAW 包装并授权 Reddit 实例:

import praw
reddit = praw.Reddit(client_id=params['client_id'], 
                     client_secret=params['api_key'],
                     password=params['password'], 
                     user_agent='<name it something descriptive> accessAPI:v0.0.1 (by /u/<yourusername>)',
                     username=params['username'])

步骤 4:从你的 reddit 实例中获取一个 Subreddit 实例

要获得 subreddit 实例,在您的reddit实例上调用subreddit时传递 subreddit 的名称。例如:

subreddit = reddit.subreddit('iama')print(subreddit.display_name)  # Output: iama
print(subreddit.title)         # Output:I Am A, where the mundane...
print(subreddit.description)

其他一些基本分析的子编辑方法:

**created_utc**:创建子编辑的时间,用 Unix Time 表示。

**description**:子编辑描述,降价中。

**display_name**:子编辑的名称。

**id**:子编辑的 ID。

**name**:子编辑的全称。

**subscribers**:用户数

第五步:从你的 Subreddit 实例中获取一个提交实例

为了收集感兴趣的 subreddit 中提交的一些数据,我们可以使用 for 循环遍历指定数量的提交,按照页面上的*有争议、镀金、热门、新、上升、*或 top 提交进行排序。

#iterating through the 10 submissions marked **hot**
for submission in subreddit**.hot**(limit=**10**):
    print(submission.title)  # Output: the submission's title
    print(submission.score)  # Output: the submission's upvotes
    print(submission.id)     # Output: the submission's ID
    print(submission.url)    # Output: the URL

基础分析的其他一些提交方法:

**author**提供了Redditor的一个实例。

**created_utc**提交创建的时间,用 Unix 时间表示。

**distinguished**划界案是否有区别。

**link_flair_text**link flair 的文本内容(有点像 subreddit 中的提交类别),或者如果没有 flair 就没有。

**name**提交材料的全称。

**num_comments**提交的评论数量。

**over_18**提交的文件是否被标记为 NSFW。

**spoiler**提交的内容是否被标记为剧透。

**stickied**提交的文件是否被粘贴。

**title**提交的标题。

**upvote_ratio**提交的所有投票中赞成票所占的百分比。

提交链接到的 URL,如果是自发布,则是永久链接。

步骤 6:从“问我任何问题”子编辑中创建一个熊猫基本提交统计数据框架

我选择制作一个 200 行 7 列的数据框架,由 r/IAmA subredit 上的 top 提交内容组成,许多著名的“问我任何问题”讨论都在这里进行。这可能需要相当长的时间,取决于从子 reddit 获取的数据的大小以及本地或虚拟机的速度,所以我喜欢在 for 循环中包含 print 语句,以便跟踪进度。

ama_title = []
time = []
num_upvotes = []
num_comments = []
upvote_ratio = []
link_flair = []
redditor = []
i=0for submission in subreddit.top(limit=200):
    i+=1
    ama_title.append(submission.title)
    time.append(submission.created_utc)
    num_upvotes.append(submission.score)
    num_comments.append(submission.num_comments)
    upvote_ratio.append(submission.upvote_ratio)
    link_flair.append(submission.link_flair_text)
    redditor.append(submission.author)
    if i%5 == 0:
        print(f'{i} submissions completed')

这将为 for 循环遍历的每 5 个提交打印一个注释。一旦完成,你可以把你的数据放入熊猫的数据框架中进行交钥匙辩论和分析。

ama_df = pd.DataFrame(
    {'ama_title': ama_title,
     'time': time,
     'num_comments': num_comments,
     'num_upvotes': num_upvotes,
     'upvote_ratio': upvote_ratio,
     'link_flair': link_flair,
     'redditor': redditor
    })ama_df.head(10)

这应该会产生类似这样的结果:

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

第七步:探索数据

帮助我们快速判断最有意义的 ama 的最简单的方法是根据最多评论(num_comments)、最多投票(num_upvotes)和最积极(upvote ratio)来绘制领导者,然后在根据主题/类别(“link_flair”)对行进行分组后,做同样的事情,取每个组的这些相同统计的平均值。

最迷人的 AMA 人物

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

这些名单中的一些人是不足为奇的名人:欧巴马、比尔·盖茨、伯尼·桑德斯、小罗伯特·唐尼。然而,如果你有时间,我强烈建议你读一读其中一些真正有趣的惊喜。丽亚·雷米尼讨论了她从山达基教会走向情感和精神自由的非凡旅程,她是 2016 年总统选举期间病毒般犹豫不决的选民肯·伯恩,约瑟夫·史达林独裁统治下公开诚实的幸存者阿纳托利·康斯坦丁,以及最近被拘留的维基解密创始人、记者兼计算机科学家朱利安·阿桑奇

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

一些纯粹正面的 ama 让我真的很欣赏一些非超级名人的讨论。有一位工程师在现场为他的免费 Photoshop 版本实施新功能建议( ivanhoe90 ),接触到美国宇航局瞄准冥王星以外的新视野团队的太空科学家(艾伦·斯特恩),美国宇航局 JPL 的软件开发人员和招聘经理,以及第二个在月球上行走的人巴兹·奥德林。喜剧传奇人物埃里克·艾多尔加入了这些极其引人注目的冒险家,他带着整个《巨蟒剧团》剧组来到 AMA,与粉丝们交流。

最吸引人的 AMA 话题

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

由于职业运动员的高知名度、广泛的媒体报道以及像 NBA、UFC ( 隆达·罗西)、体育机构和服装品牌这样的大公司的可能支持,他们在网络上获得大量追随者并不奇怪。众所周知,这些 ama 有时会与推广产品、活动或品牌的活动一起预订。非营利主题出现在所有三个条形图由于比尔盖茨代表他的基金会,网络中立活动家和美国公民自由联盟的支持者。像 Reddit 这样以社区为先的平台,对于那些寻求组织起来煽动变革,或者在 Twitter 上谈论如何打击你的快餐竞争对手的人来说,确实是一个很好的枢纽(温迪的社交团队)。

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

高票的有新闻价值的事件讨论由 Sid Fischer 主持,他是佛罗里达州帕克兰市斯通曼道格拉斯高中第三个房间枪击案的幸存者,还有一个稍微轻松一点的人,他打扮成大富翁去轰炸前 Equifax 首席执行官理查德·史密斯的参议院听证会。

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

《犯罪与正义》在最积极的 AMA 话题中独占鳌头,因为一个话题引起了相当一致的庆祝,这个话题自豪地写道:“爱达荷州通过了一项 Ag-Gag 法律,将揭露工厂化农场虐待动物的行为定为犯罪。我们起诉了他们并赢了。“著名的辛普森谋杀案审判检察官克里斯托弗·达顿,也在 2017 年 7 月花了一点时间向 AMA 社区开放。Elon Musk 和 Reddit 的新任首席执行官 Steve Huffman 帮助提高了商业话题的投票率。

我希望我能够扩大那些对数据科学感兴趣的人和那些通过 PRAW 新连接到 Reddit 的 API 的人的研究潜力!

对于未来的文章,请联系我们,提出任何问题、评论、想法或想法。

感谢您的阅读,

亚历克斯

详细 API 连接&探索代码,查看我的 GitHub

有关充分利用 PRAW 的更多详情,请查看 文档

我们连线上LinkedIn

探索推特上的右翼极端主义

原文:https://towardsdatascience.com/exploring-right-wing-extremism-on-twitter-941a9d02825e?source=collection_archive---------16-----------------------

介绍

在过去的几个月里,我们已经看到社交媒体网站如何成为极右翼观点的避风港。《纽约时报》探究了 YouTube 如何通过其推荐算法将用户推向激进内容, ProPublica 详细介绍了脸书的一个组织,ICE 员工在那里分享关于移民的种族主义迷因。Reddit 也因托管极端右翼内容而受到的争议。Twitter 是这场争论中相对毫发无损的一个社交媒体网站。这很大程度上是由于社交网站的分布式特性。用户不会被聚集到容易识别的论坛或子区域中,而是每个账户作为一个独立的实体存在。虽然拥有许多追随者的知名用户被禁止(如亚历克斯·琼斯),但更难识别推广极端主义内容的个人网络。这是我所知的第一次大规模分析 Twitter 上的极右极端主义。

为了调查这个问题,我分析了来自两个极右翼极端主义用户的 297849 个 Twitter 账户。我的发现确定了一个约有 19,000 名用户的网络,这些用户与极端主义内容高度接近。著名的右翼人物,如查理·柯克和瑞安·福尼尔,在这个网络中的影响力不亚于公开的种族主义报道。

调查的结果

最接近极端主义内容的用户群中最有影响力的前十个账户可以归类为毫不掩饰的白人民族主义者和另类右翼巨魔账户。这些用户公开分享种族主义迷因,宣扬白人至上主义。四个最著名的用户是@NoWhiteGuilt、@Outsider__14、@esc_press 和@Urbanus_Crusade

Top ten most influential users by PageRank

在下面的推文中,@Urbanus_Crusade 将他们的“MAGA 议程”列为包括“欧洲同质人口”:

Tweet by @Urbanus_Crusade

@esc_press 分享了无忧无虑的白人的老照片,标题通常是“按[esc]键返回”,暗示已经失去的田园时光。在一条现已删除的推文中,他们通过分享这张图片揭露了他们的种族主义议程:

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

Image shared by @esc_press

@Outsider__14 主要分享推文,充当分发极端主义内容的枢纽。在这里,他们转发了一个用户赞扬白人民族主义者联合权利的抗议:

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

这四个白人民族主义用户总共有 21607 名粉丝,他们在推特上发布这种内容。总的来说(减去已删除的账户),前 10 个账户共有 354,602 名关注者。

令人惊讶的是,在网络中发现了大量的土耳其用户。前 10 名中被删除的用户是一名受欢迎的土耳其记者,他似乎与右翼极端主义没有太大关系。土耳其账户是一个意外的发现。这似乎表明有大量的土耳其人对右翼主题的内容感兴趣。甚至可能是他们中的一些人在幕后操纵这些账户,但目前我只能推测。

一旦我对这个网络中的内容类型有了了解,我就开始关注拥有最多追随者的个人。这揭露了一些著名的右翼人物:

Popular right-wing personalities by PageRank

我们找到 Ryan Fournier,Trump 的学生主管,他的 PageRank 是 16。从这个角度来看,这意味着在这个网络中,瑞安·福涅尔的影响力不亚于发布这条微博的人:

Tweet by @AMERIOPA

前国会候选人查克·卡列斯托排在第 46 位,“转折点美国”的负责人查理·柯克排在第 153 位。紧随查理·柯克之后的是@PayYourG0yTax(现已暂停),让你感受一下这些人物的陪伴。

过程

从 2019 年 5 月开始,我开始收集极端右翼推特账户的数据。开始,我选了两个账号:@NoWhiteGuiltNWG 和@Goy_Talk_USA。这两个用户都运营着 YouTube 频道,他们在那里制作视频宣传白人民族主义理论,并且在 Twitter 上也非常活跃。下面是@Goy_Talk_USA 发来的一条推文,让你了解一下他们宣传的内容:

我收集了这两个账户的关注者的数据,然后是他们每个人的关注者,以此类推,直到我建立了一个拥有 297,849 名用户的网络。对于每个用户,我计算了他们与两个原始极端主义用户的距离。下图对此进行了说明:

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

Goy_Talk_USA is depth 0. Each of their followers is depth 1. Their follower’s-followers are depth 2.

我根据深度将网络中的用户分为三组。距离最初两个极端分子账户 0-2 深度的用户是最接近的。距离 3 个深度的用户为中等,距离大于 3 个深度的用户为最远。最接近可以解释为最接近极端主义内容的用户,中等和最远被几层用户移除。总的来说,我在最接近的组中找到了 19,825 个用户。

下图显示了这个网络。网络的核心是绿色最接近的用户。规模更大、更温和的红色媒体用户从他们那里扩散开来。最后,我们看到最远的用户要么自己孤立,要么在网络的末端。

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

下一步是使用一种叫做 PageRank 的算法来计算网络中每个用户的连通性。这将根据用户在网络中出现的次数为他们分配一个号码。经常出现的用户会有较高的数字,而不经常出现的用户会有较低的数字。它可以被解释为一种衡量某人在网络中的影响力的方法。

摘要

这些发现指向一大群用户在 Twitter 上推广极端右翼内容。其他社交媒体平台已被追究其平台上极端主义活动的责任,并已采取行动遏制此类内容。然而,Twitter 允许这些网络相对不受约束地继续发展。所有这些账户都是公开的,这意味着任何人只需点击几下鼠标就可以查看和阅读极端主义言论。持续展示这些内容有助于将仇恨言论正常化,并使个人变得激进。我希望这项研究将有助于揭示 Twitter 上存在的极端主义的规模和范围,并有助于社交媒体平台上正在进行的关于极端主义的讨论。

密码

这个项目分为两部分:从 Twitter 上收集数据,然后进行分析。下面是这两个步骤的 GitHub 库。

[## nbucklin/twitternet 网络

这个程序从单个用户开始生成一个 Twitter 追随者网络。对于第一个用户,它将检索…

github.com](https://github.com/nbucklin/TwitterNetwork) [## nbucklin/BigNetworkPython

此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…

github.com](https://github.com/nbucklin/BigNetworkPython)

使用 Dash、Flask 和 Heroku 构建的交互式 Webapp 探索 SG 的租赁——第 1 部分

原文:https://towardsdatascience.com/exploring-sgs-rentals-with-an-interactive-webapp-built-using-dash-flask-and-heroku-part-1-cccd9e8dd1b8?source=collection_archive---------26-----------------------

通过构建 DS/ML Webapp 并将其部署到世界各地来升级

1.介绍

您是否曾经研究过一些有趣的数据科学问题,并获得了一些真正有趣的见解或 ML 解决方案,但最终却意识到您所拥有的只是一个装满代码的 Ipython 笔记本,而这些代码将永远不会到达观众手中?或者,它可能只会被你的同道 DS 从业者阅读,你已经花了相当多的时间说服他们克隆你的回购协议,下载你的笔记本——出于对你的尊重——努力让通读整本书,最终实现你的才华

你不满足了。一方面,你希望你的工作被更广泛的受众欣赏,不仅仅是你的技术同行,也包括你的非技术同行。也许你想把它分享给你的朋友,他对 DS 很好奇,但可能不完全理解代码,仅仅是因为他/她从未真正潜过那么深。也许你想向你的非工程师朋友、同事、老板甚至是《商业分析 101》中坐在你旁边的可爱女孩/男孩炫耀。

另一方面,你觉得也许你本可以做得更多,你需要加快你的步伐来制作一些更最终的东西,一些或多或少类似于成品的东西,这样你就可以宣称“啊哈!我建造了这个。这不是很酷吗?”

在我最近的项目中,我一度有这种感觉。我决定通过构建一个完全交互式的 web 应用程序将它提升到一个新的水平——尽管我以前从未构建过——在那里用户可以处理数据,构建他们的模型,挑选他们的重要特征并获得某个预测/结果

最后,我成功了——构建了我的第一个 ML Webapp。你可以在这里查看 app 。这是一个关于探索新加坡租赁物业的应用程序,我就住在那里。

该应用程序由 3 部分组成

A. 第一部分 允许用户通过指定最小&最大租金来处理数据,然后选择他们感兴趣的功能和物业类型,应用程序返回一些 交互式可视化效果

B. 第二部分 允许用户提出一个假设的物业,应用程序将尝试 预测其租金 使用由用户通过一些引导性问题就地 建造的模型

C.

我使用 Flask 作为我的 Webapp 框架来创建我的服务器端 Python 程序,使用 Plotly Dash 来为我的交互式可视化服务,使用常见的怀疑对象 Numpy、Pandas、Scikit-learn 作为工具来构建我的程序的各个部分。最后但同样重要的是,我使用免费的应用托管网站 Heroku 来部署我的工作。看看这里:https://www.heroku.com/home 的

对于那些对学习如何构建这样的应用感兴趣的人,请系好安全带,因为我将在后续章节中更详细地解释这些步骤。

2.了解所需的库

A .烧瓶

Flask 是基于 Werkzeug、Jinja 2 的 Python 的 web 开发微框架。你可以在这里查看它的官方网站http://flask.pocoo.org/。它允许您设置不同的路由/视图(稍后将详细介绍),这些路由/视图与您的 web 浏览器的 HTTP 请求进行交互,并执行一些功能/任务来将某些内容返回到浏览器或呈现的 Html 页面。您可以使用 pip 通过一个简单的命令下载该框架

*pip install -U flask*

最新版本是 Flask 1 . 0 . 2——你可以在这里测试一个简单的 Flask 应用程序

*from flask import Flaskapp = Flask(__name__)@app.route('/')
def hello():
    return 'Hello, World!'if __name__ == '__main__':
 app.run(port=8080, debug=True)*

这将在 localhost:8080 提供应用程序供您查看

B .破折号

Dash 是一个 Plotly 产品,它是一个用于交互式可视化的 Python 框架,作为 web 应用程序的一部分。你可以在这里查看它的官方网站https://plot.ly/products/dash/。它允许你收集不同的元素,如下拉菜单/滑动条/条形图/地图,这些元素可以相互交流,形成一个布局,并在 web 视图中提供

  • 您可以在这里安装组件
*pip install -U dash
pip install dash_core_components
pip install dash_html_components*
  • 您可以测试写在主介绍页面上的示例程序。请记住指定以下内容
*app.scripts.config.serve_locally = True
if __name__ == '__main__':
    app.run_server(port=8080)*

C. Wtforms

Wtforms 是一个很好的 API,允许你定制你的前端表单,它接受用户的输入并把它们提供给你的后端应用。该 API 支持大量流行的 Python web 开发框架,包括 Flask 和 Django。你可以从这里的文档中阅读更多关于如何使用 wtforms 的内容https://wtforms.readthedocs.io/en/stable/

  • 您可以安装它
*pip install -U dash*

D .通常的嫌疑人——Numpy,Pandas,Scikit-learn,Seaborn…

这些是清理、准备、探索和分析数据以及构建机器学习模型来处理这些数据的基本包。对使用 Python 实践数据科学略知一二的读者应该熟悉这些包。如果没有,随时欢迎您查看我以前讨论其他项目的文章

  • *** 现在所有的框架/包都有了自己的介绍,让我们继续探索如何将它们结合在一起交付一个最终的工作产品*

3.构建所需的模块

实现该项目需要几个模块/组件:

A.具有完全定义的布局的嵌套 Dash 应用程序

B.配置用于主 HTML 页面的输入表单

C1。主 HTML 页面由带有输入表单的不同部分组成

C2。由主程序的不同路径/视图提供的其他 HTML 页面

D.要在主应用程序中调用的助手函数

  • 让我们讨论每一个组件

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 A —仪表板应用

  • app_dash = Dash 应用的名称。还为仪表板配置了一个名为 ‘/dashboard’ 的单独视图

  • 首先为 Dash 应用程序设置虚拟布局。

  • '/哈哈哈’视图将被触发&每当 form4 提交请求 时,应用程序的布局将填充一些交互图表

  • 构建图的参数取自用户在表格 1 的输入。这意味着用户需要确保在他/她触发图表视图之前提交form 1(该操作还会导致交互式图表的创建并将 plot_charts_cue 更改为 True)

  • 否则,如果用户未能首先提交 form1,将首先显示一个error.html页面

  • 交互式图表是用打包的 Plotly 的图形对象构造的(导入为 go )。每个图表由一个图表布局和一组元素组成。这些元素有很多选择(go。走吧。散开,走。Map…),这取决于您想要绘制的图表类型

  • 下面是一个如何创建交互式条形图的示例

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 B——配置的输入表单

输入表单可以通过以下方式进行配置:首先定义一个基于表单的扩展表单类,然后定义表示表单中需要填写的信息的不同变量名,以及这些变量的输入字段类型。

例如,第 1 部分(修改数据集)和第 3 部分(搜索相似列表)的两个表单类是:

  • 您可以使用验证器对输入字段强制执行一些规则。关于如何使用它们的更多细节,请参见文档
  • 在主index.html页面上,如果验证器不满足要求,将会闪烁错误消息(由您指定)。如果输入值的类型不同于指定的字段类型,该字段还会闪烁其默认错误消息

然后,这些表格通过以下代码集成到主程序中。

*from flask import requestform1 = ResuableForm(request.form)
form3 = ReusableForm_SimFilter(request.form)//Parse the forms to the rendered main Html page
@app.route('/')
def some_func():
    //Do sth
    return render_template('index.html', form1=form1, form3=form3*

最后但同样重要的是,在 index.html 页面上,应该有两个表单部分,它们的各自的 id应该分别是‘form 1’和‘form 3’。稍后将详细介绍如何构建这个页面

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 C —所需的 HTML 模板

这个项目使用的所有 HTML 模板都放在一个“template”文件夹中,这个文件夹与主程序放在同一个目录中。这是 render_template 函数的一个要求。您应该对自己项目的模板做同样的事情

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

用于主视图的真实 index.html 要长得多,由许多形式组成。但是,我将简化这种情况,只使用一个表单(获取参数以修改数据集所需的表单)来呈现模板的简短版本。看看下面表单的代码:

** * 有一些解释可以帮助您更好地理解这种形式:

  • ****是如何在模板内部声明一个表单节。属性 method = post 决定了表单提交后将向后端程序发送何种请求。
  • 可以使用属性 action = ‘view_name’ 来指定表单提交将触发哪个 Flask route/view。如果未指定属性,提交将默认将其数据发送到主视图
  • 在模板中调用 form1。因此,当页面被渲染时,form1 首先需要被定义、赋值并作为参数被解析到 render_template()函数中
  • 属于 form1 的字段可以调用为 form1.field_name 并由模板进行相应的处理
  • 您也可以使用 <选择></选择> 来配置分类特性的下拉值选择,而不是使用空白输入字段
  • 还要注意闪烁错误消息是如何在模板中配置的
  • 关于表单的 CSS 样式,请自己阅读要点
  • 如果你想访问完整的 index.html 版本,你可以在项目目录 的文件夹 Flaskapp/template 里面查看一下

在主页的顶部,您的模板文件夹应该包含其他模板(例如由一个视图返回的模板,显示您的模型试图预测的结果)。我使用另外两个模板,一个是 prediction_result.html ,用于返回第 2 部分的答案,另一个是 filtering_result.html ,用于返回第 3 部分的答案。每当程序的某些条件不满足时,我还使用各种各样的模板向用户返回一些带有说明的错误页面。下面显示了一个示例

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

组件 D —辅助功能

我的项目需要许多助手功能。例如:

  1. 为 Dash 应用程序绘制交互式图表的函数(讨论组件 B 的部分展示了一个这样的例子)
  2. 基于用户偏好修改数据集的函数
  3. 使用 Pyspark 或 Sklearn 库(取决于用户的选择)为第 2 部分创建和训练预测模型的函数
  4. 第 2 节中使用的函数,用于将测试实例转换为适合已训练模型的格式,并返回预测
  5. 第 3 节中使用的函数,用于将测试实例转换成适合已训练模型的格式,并返回类似的结果

此类功能的一个示例如下所示。这就是上面第 5 点提到的功能。该函数建立一个最近邻居的学习模型,并使用它根据用户给定的权重搜索相似的列表:

我不会继续展示这个项目中使用的所有助手函数。如果您有兴趣深入阅读它们,可以通过我的 Github 库查看项目目录

最后……

在我们创建完 Flaskapp 所需的所有模块之后,是时候构建项目的最后也是最重要的部分了:主程序。看看这篇文章的第二部分,自己看看如何做到这一点。

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

使用 Dash、Flask 和 Heroku 构建的交互式 Webapp 探索 SG 的租赁——第 2 部分

原文:https://towardsdatascience.com/exploring-sgs-rentals-with-an-interactive-webapp-built-using-dash-flask-and-heroku-part-2-24656906ccbb?source=collection_archive---------18-----------------------

通过构建 DS/ML Webapp 并将其部署到世界各地来升级

** 这是 2 篇系列文章的第 2 部分。查看 第一部 这里!

在上一篇文章中,我们讨论了如何构建不同的组件来形成 Flaskapp。现在是时候将它们集成在一起了

4.把所有东西放在主程序中

这是万物融为一体的地方。主程序配置后端 Flaskapp,并定义它如何响应来自前端的请求。所有子模块都在这里调用

********* 主程序的 骨架 如下图

***import ___                       //whatever packages you need
***import ___                       //whatever helper funcs needed
from flask import Flask, request, render_template
from dash import Dash
from dash_core_components import ____
from dash_html_components import ____
from wtforms import Form
from wtforms.fields import _____    //choose necessary fields**app** = Flask(__name__)
**app_dash** = Dash(__name__, server=app, url_base_pathname = '/dash/')
app_dash.layout = ____              //set up your dash layoutform1 = Form(request.form)
form2 = Form(request.form)
//Form can be customized using extented class and wtforms.fields**@app.route**('/', methods=___)        //main view, choose methods
def some_function():
    if request.form = form1
        //Do something
        **return** ___ 
    elif request.form = form2
        //Do something
        **return** ___
//something to the server OR render_template('somepage.html')**@app.route**('/route1', methods=___)  //new view, choose methods  
def some_function():
    //Do something
    **return** ___ 
//something to the server OR render_template('somepage.html')**@app.route**('/route2', methods=___)  //new view, choose methods   
def some_function():
    global app_dash
    //Do something
    app_dash.layout = ___           //change your dash layout  
    **return** flask.redicrect('/dash/')
//direct the flask app to display the Dash dashboardif __name__ == '__main__':
 **app**.run(port=8000, debug=True)

主程序由一个或多个视图组成。每个路线/视图都有一个代码部分。每个路线/视图最终都必须返回一些东西

一旦你理解了上面的框架,你就可以开始阅读我的复杂得多的主程序,而不会被它包含的代码行数所淹没。此程序的更多解释如下

** * 我的主程序的一些说明

  • 应用 = Flask app 的名称。每个 @application.route() 代码块定义一个路径/视图
  • 主程序有 3 个视图 : A)主视图’/‘,B)第二视图’/哈哈哈’和 C)第三视图’/seaborn ’
  • ReusableForm(Form) 是一个将基本表单类扩展为具有可定制输入字段的新表单类的例子
  • 如果没有来自前端网络浏览器的请求,主视图提供由 5 个表格组成的index.html页面。第一节
  • 如果有来自表单 1发布请求,应用程序修改数据集,重新显示带有一些通知文本的index.html页面
  • 如果有一个来自 form2POST 请求,app 会创建并训练一个 ML 模型,然后用它来预测所提议房产的租金,并返回 prediction_result.html 页面显示预测**。**
  • 如果有一个来自 form3、POST 请求,应用程序会搜索一些的与提议的酒店相似的房源,并返回 filtering_result.html 显示这些房源的详细信息
  • 如果有一个来自 form4 的 POST 请求,应用程序触发第二个视图@ application . route(‘/哈哈哈’),这将改变 Dash 应用程序的布局,然后将用户重定向到其仪表板
  • 如果有一个来自 form5、POST 请求,应用程序会触发第三个视图@ application . route('/seaborn '),返回一个显示图表的页面
  • 方法:指定路由/视图可以处理来自前端网页的哪种 HTTP 请求(’ POST ‘或’ GET’)
  • request.form 用于定义一个表单。此外,它还用于接收后端程序所需的 用户输入
  • request.method 用于在每个视图中构造 if 子句,分别处理不同类型的 HTTP 请求。
  • render_template :该函数用于在触发路线/视图时返回一个 Html 页面。模板内部调用的表单和变量需要作为参数解析到 render_template()函数中
  • 全局用于每个视图内的变量,因此每个视图内的动作导致变量的永久变化,这些变化可以在整个程序中共享
  • port=8080 将允许您通过浏览器中的 localhost:8080 在本地部署应用程序

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

5.在 HEROKU 上部署完成的应用程序

在你完成主程序并确保程序中不再有 bug 之后(重要!—在本地运行,直到你对此非常有信心),然后你就可以把它部署到 Heroku 上,这是一个面向业余开发者的流行的免费应用托管网站

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

创建帐户

但是,在您做任何事情之前,您需要首先通过访问该网站创建一个帐户,除非您已经有了一个帐户。

创建您的帐户并登录后,您将看到一个可以管理您的应用程序的仪表板。您可以选择从那里配置您的应用程序,或者下载适用于您的操作系统的 Heroku Cli 并使用客户端配置您的应用程序。我将在这里展示第二种方法

安装 CLI 客户端后,在您的终端中运行以下命令,登录到您的 Heroku 帐户

$ heroku login

出现提示时,输入您的凭据(电子邮件和密码)。

创建 Heroku 应用程序

部署过程的下一步是您需要创建一个 Heroku 应用程序。为此,请在您的终端中运行以下命令;

$ heroku apps:create unique-name-of-your-app

此操作将在 Heroku 上创建一个应用程序,并为您上传文件创建一个存储库

准备 Git 存储库

在这个阶段,我假设您应该已经建立了一个本地 git 存储库。您需要通过运行命令向现有的本地目录添加一个新的远程上游

$ git remote add heroku YOUR_HEROKU_REPO_ADDRESS

另一方面,如果您还没有设置存储库,请将创建的 Heroku 存储库克隆到您的本地目录中,并将您的工作文件复制到该文件夹中。

选择合适的 web 服务器

Flask 对于开发阶段来说是一个很好的 web 服务器,但对于生产环境来说不是最佳选择。建议使用更好的 web 服务器。最受欢迎的两个是 Gunicorn 和 waste。你可以在这里查看这些服务器的列表。我在我的应用程序中使用 waste,因为我发现它有助于应用程序更好地运行,因为它能够缓冲请求和响应。另一方面,Gunicorn 导致我的应用程序给出不一致的响应,可能是因为它没有缓冲能力,无法处理我的应用程序的缓慢进程。此外,这里的一篇文章解释了 Gunicorn 不是 Heroku 应用程序最佳选择的一些原因

您可以用这个命令下载这两个文件

$ pip install waitress
OR
$ pip install gunicorn

创建 Procfile

procfile 告诉 Heroku 如何运行你的应用程序。该文件没有文件类型扩展名。用简单的命令在存储库中创建一个

$ touch Procfile

将下面一行添加到配置文件中。我为 Guniconr 和 waste 都举了例子。此外,“文件名”是您的主程序文件的名称,而“应用程序名”是您的程序中配置的 Flask 应用程序的名称。

$ web:gunicorn -w:? file_name:app_name
OR
$ web:waitres-serve --port=$PORT -w:? file_name:app_name
\\? is the number of threads/workers to be started by the server.

准备 Requirements.txt 文件

这个文件告诉 Heroku 哪些库/包要和你的应用一起安装来运行它。它包含包的名称和每个包的版本号。如果您不确定您当前使用的是什么版本,您可以使用“$ pip list”命令进行检查。记得把新下载的网络服务器放在其中

这是我自己的 requirements.txt 文件的一个例子

gunicorn==19.9.0
waitress==1.2.1
numpy==1.16.2
pandas==0.23.0
matplotlib==2.2.2
seaborn==0.9.0
pyspark==2.3.0
scikit-learn==0.20.3
plotly==3.3.0
flask==1.0.2
flask_sqlalchemy==2.3.2
wtforms==2.2.1
psycopg2==2.7.7
dash==0.38.0
dash_core_components==0.43.1
dash_html_components==0.13.5

但是,如果您一直在使用虚拟环境开发您的应用程序,那么您只需运行一个简单的命令,就可以帮助您在一瞬间创建这个文件

$ pip freeze > requirements.txt

指定合适的构建包

在部署到 Heroku 之前,您需要指定您的应用程序将使用哪个(哪些)构建包。您可以通过运行以下程序来添加它们(根据您的应用程序的语言来更改构建包)

$ heroku buildpacks:set heroku/python

* *添加数据库(可选)

如果您的应用程序需要数据库服务器,Heroku 提供免费或付费的 Postgresql。自由层版本最多只允许 10,000 行和 20 个并发连接。

实际上,为应用程序设置数据库并配置它与数据库进行交互需要几个步骤。

步骤 1 :要创建一个数据库,运行以下命令

$ heroku addons: add heroku-postgresql:hobby-dev
\\hobby-dev is the free tier mentioned

您可以从应用仪表板查看数据库的连接详细信息。只需转到新创建的数据库部分>点击设置>查看凭证。在这里你可以看到数据库的 URI 被配置为一个名为$DATABASE_URL 的环境变量。数据导入过程和程序设置都需要这个变量。

步骤 2 :通过添加下面的代码片段来修改您的 flask 应用程序,以便它可以连接到数据库。

import os
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
\\app is your Flask app's name

第三步:将您的数据导入 Heroku

我将假设您的数据以 CSV 文件的形式出现。如果您已经有了本地 PostgreSQL 数据库中的数据,或者 SQL 文件形式的数据,那么您实际上可以跳过下面的一些步骤

**STEP 0\.** Download and install PostgreSQL on your computer **STEP 1**. Create a local Postgres Database & Table (with name=Tablename) with PGAdmin**STEP 2**. Run the following command
$ psql -U user_name -d database_name -c "\copy Tablename FROM '\file directory\file.csv' WITH CSV;"
\\ Transfer the csv file to local database\\If you have many columns, table creation can be a headache. For such case, you can use a tool called pgfutter to automate table creation with some datatype identification. Check out this [answer](https://stackoverflow.com/questions/21018256/can-i-automatically-create-a-table-in-postgresql-from-a-csv-file-with-headers) on stackoverflow**STEP 3**. Push from local database to remote Heroku database
Method1:
A. pg_dump -U user_name -d database_name --no-owner --no-acl -f backup.sql
B. heroku pg:psql -app app_name < backup.sqlMethod2:
A. heroku pg:reset //make sure remote database is empty
B. heroku pg:push local_database_name --app app_name

步骤 4 :创建 SQLAlchemy ORM 对象,以便从 Flask 应用程序内部与数据库进行通信。您首先需要安装软件包

$ pip install flask_sqlalchemy

然后你把这些代码添加到你的程序中

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
\\app is your Flask app's name

下面是代码要点,展示了我如何使用我的 ORM 与数据库通信,查询它,用一些用户的输入条件过滤查询,并返回一个结果数据帧。我在这里使用反射从步骤 3 中上传的现有数据库中加载信息

或者,您可以通过用 db 声明 table 类来创建一个全新的表。在 Flask 应用程序中建模,并使用 db.session.add()db.session.commit() 将数据添加到表中。当你的应用有一些允许用户操作/更新数据库的特性时,这是很有用的。你可以看看这篇文章了解更多关于如何做的细节

* *为 Heroku 定制

在将应用程序部署到 Heroku 时,有两件事需要注意

  1. Heroku 自由层(和一些低级计划)只有低内存分配 (512MB)。确保您的进程/应用程序在此限制内使用资源。如有必要,自定义您的应用程序
  2. Heroku 有一个 30 秒超时策略。这意味着对于 web 客户端请求,您的后端服务器有 30 秒的“期限”来返回响应。如果你的服务器需要更长的时间,应用程序将崩溃

我用来绕过第二条规则的一个技巧(我的一些过程,比如模型训练或交互式情节准备需要超过 30 秒)是让应用程序以某种形式的状态返回给服务器,关于正在运行的过程,在指定的间隔小于 30 秒

<h3 class=”heading-3-impt-red”>Click <button style=”font-size:20px” **onclick=”myfunc()**”>Here</button> to Start Preparing Interactive Scatter Plot
</h3><script src="[**https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.j**s](https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js)"></script>
<script>

  **function myfunc(){** alert("Preparing Plot Now...");
  count = 0;
  var interval = setInterval(function(){**wowwah()**; count++; if (count>=4)clearInterval(interval);},8000); // call every 8 seconds
  };

  **function wowwah(){** $.ajax({
  url:"/hehehe", //the page containing python script
  method:"GET",
  dataType:"json",
  success:function(result){ alert(result); }
  });}
</script>

我在我的 HTML 模板中使用了这个脚本。每当调用 wowwah()函数时(每 8 秒一次),视图“/hehehe”以 jsonify()对象的形式向浏览器返回一个状态文本。

部署到 Heroku

最后,当所有工作完成后,你可以将应用程序部署到 Heroku,就像你将你的东西部署到 Github 一样。确保您准备好所有内容,并更新您的。git 如果需要,忽略文件

$ git add *
$ git commit
$ git push heroku master
// change heroku to origin if you previously clone the heroku repo

your _ app _ name . heroku . com访问您的应用程序,看看它是否有效。如果它不像预期的那样工作,您可以在控制台中键入“$ heroku logs-tails”来找出错误是什么。如果是的话,那么恭喜你成功了!您已经构建了您的第一个 DS webapp

6.结论

通过 Flask 应用程序将您的 DS/ML 项目部署到 Heroku,使您能够将您的想法和发现转化为工作产品,并向全世界展示。对于那些新手来说,这应该让你体会到当公司聘请数据科学家/软件工程师团队来交付数据科学项目时,他们真正关心的是什么:一个成品生产级模型/软件,可以在需要时为他们提供可操作的见解,而不仅仅是一份报告或一个代码笔记本。

尽管我自己在这方面还是个新手,但我希望我的文章能够对您的学习之旅有所帮助,并成为数据科学艺术的更好实践者。如果是的话,我很乐意看到你的一两条评论来解释这是怎么回事。如果您能给我一些反馈,告诉我如何更好地改进我的帖子/文章,我将不胜感激

感谢大家一直关注到这个阶段!不要忘记关注我的下一篇文章。

Huy,

探索悉尼郊区,开一家新餐馆

原文:https://towardsdatascience.com/exploring-sydney-suburbs-for-opening-a-new-restaurant-e1e43a6c32c7?source=collection_archive---------23-----------------------

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

Sydney harbour bridge photoed by Jun

Python 数据科学的魔力

在过去的几个月里,我一直在学习 Coursera 的 IBM 数据科学课程。在学习完本课程的所有 Python 基本技能和工具后,下面是最后一个作业。这个任务的目标是定义一个与我选择的城市相关的商业问题,然后通过使用 Foursquare 位置数据来解决它。

虽然这是一项任务,但它非常类似于数据分析师在日常工作中需要解决的真实世界的问题,包括问题定义数据收集数据清理数据分析数据可视化报告形成。在整个作业中,我使用了网络抓取、缺失值插补、Foursquare API、follow map 和 k 均值聚类。

我希望你能从这篇文章中收集信息或找到有用的数据。欢迎留下任何建议和意见。详细的代码/笔记本/数据也可以在这里找到。

1.商业问题

你在一家专门从事餐厅营销的精品咨询公司工作。一位客户希望在悉尼新开一家意大利餐厅,但是他不确定新地点的最佳位置和营销策略。你被指派帮助他制定一个全面的营销计划。悉尼是一个充满活力的城市,一年四季都有精彩的活动。它以其旅游景点和田园海滩而闻名。漫步在城市郊区,如果没有数据驱动的方法,它很难在现有的竞争对手中找到一个开辟新餐馆的干净利基。你会如何建议你的客户使用数据科学来决定他的餐厅位置?

2.数据收集/清理

综合自创业出版社出版的披萨店、咖啡馆、熟食店、面包店、餐饮企业以下要素被认为是选择餐厅位置的关键因素:人口统计、交通密度、犯罪率、竞争对手和房价承受能力。我们可能无法在如此短的时间内解决所有这些因素,但是,可以通过使用可用的公共数据集或网络搜集来解决其中的一些问题。

2.1。维基百科上的悉尼郊区列表

由于整个分析是基于位置的,我们应该考虑悉尼所有郊区的详细坐标。从政府网站不难找到新南威尔士州所有郊区的 geojson 文件。但是,挑战在于选择悉尼市区内的那些郊区?在这里,我使用 web 爬行方法,使用requestsBeautifulsoup4维基百科中删除了一个列表。

此外,我也刮了所有悉尼郊区的邮政编码列表从这里使用相同的方法。经过一些修改,我得到了下面的数据帧:

 Suburb        State    Postcode    Suburbs_with_boundries
0    Abbotsbury    New South Wales    2176    ABBOTSBURY
1    Abbotsford    New South Wales    2046    ABBOTSFORD
2    Acacia GardensNew South Wales    2763    ACACIA GARDENS
3    Agnes Banks   New South Wales    NaN     AGNES BANKS
4    Airds         New South Wales    2560    AIRDS

检查最终表以查看在该过程中是否生成了任何不正确的行是非常重要的。例如,“Agnes Banks”在最终表中没有邮政编码,但是它实际上有一个。这对于后面的步骤非常重要。

2.2。悉尼人口统计和郊区房价中位数

找了好一阵子后,我找不到任何关于悉尼郊区人口统计和房价中位数的数据表。最后,我再次使用网络爬行从获取人口统计数据(人口和年龄范围),从房地产获取房产中值价格数据。这是澳大利亚两个很受欢迎的房地产网站。您可能已经注意到,查找给定郊区的 url 结构只是域名链接、郊区名称、新南威尔士州和邮政编码的组合。

url = 'https://www.domain.com.au/suburb-profile/{}-nsw-{}'.format(            suburb,postcode)

下面是我在这个任务中使用的主要函数的一个例子:

2.3。房产中值价格的缺失数据插补

从网上抓取数据永远不会完美。那很好。重要的是,我们需要有自己的假设来预测基于不同情况的缺失值。在我的例子中,人口、年龄范围、购房中间价、租房中间价、单位购房中间价和单位租房中间价都有缺失值。

在研究了人口统计表后,我发现人口或年龄范围值缺失的郊区通常是偏远郊区,要么年龄范围为 60 岁以上,要么人口为 0。然后,我相应地估算所有这两种类型的缺失值。像这样:

sydney_demography_data[['Age']] = sydney_demography_data[['Age']].fillna('60+')

房地产价格中位数的情况有点复杂,如线性回归图所示。我们在不同的对之间有非常不同的线性关系。

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

Linear regression between two parameters

为了简化问题,我决定用购房中值价格作为房产负担能力的指标。由于购房中间价和租房中间价之间存在明显的正相关关系,我为这两个参数建立了一个简单的线性模型。然后,该模型用于预测这两个参数的缺失值。

2.4。人口、年龄范围和财产负担能力分布图

在这个阶段,我们已经有了解决人口统计和房产负担能力所需的数据。让我们把它们放在地图上以便有一个清晰的视野。以下是该任务的示例代码:

地图看起来像这样,它们可以从这里下载。

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

Choropleth map for age range, population, and property affordability

2.5。使用 Foursquare API 检索所有悉尼郊区的场馆

有了最终的悉尼郊区列表,我们就可以使用 Foursquare API 检索每个郊区的场地列表。Foursquare 是一个强大的位置信息数据库。你基本上只需要通过radiusLIMITlatitudeslongitudes就可以得到定义半径内特定地点的场馆列表。注意,你需要注册 Foursquare API,拥有自己的CLIENT_IDCLIENT_SECRET才能使用 Foursquare 的服务。它的免费版本足以进行这种类型的分析。试试吧,你会喜欢的。这是我在这个任务中使用的函数。

3.数据分析

现在数据终于准备好了。我们来看看数据。正如我们在这里看到的,只有 565 个郊区归还场馆。这可能是因为任意选择的郊区中心并不是真正的郊区中心,我们应该在未来找到一种更好的方法来定义郊区中心。但总的来说,这个列表非常接近现实。

Code for list top 20 suburbs with the most venue numbers

565
Suburb    Venue
0    Darlinghurst    100
1    The Rocks    100
2    Surry Hills    89
3    Crows Nest    86
4    Newtown    85
5    Haymarket    83
6    Chippendale    79
7    Millers Point    73
8    Burwood    72
9    Sydney    72
10    North Sydney    68
11    Dawes Point    64
12    Woolloomooloo    59
13    Randwick    57
14    Enmore    57
15    Milsons Point    55
16    Rushcutters Bay    53
17    Coogee    53
18    Waterloo    51
19    Paddington    51

由于我们的任务是从 698 个郊区挑选一些候选人,我们可能会发现首先根据这些郊区最常见的场地对它们进行分组是有用的。这背后的想法是寻找以餐馆为特色的郊区。这种类型的郊区可以为新的企业提供稳定的客户来源。因此,我在这个任务中使用了 k 均值聚类:

我们发现第二组中的郊区以餐馆、酒吧和咖啡馆为特色。然后,我们缩小了第二组中的候选郊区列表。在这个集群中,我们需要确定相对较低的房地产价格,高人口密度和中年郊区。为了能够看出这一点,让我们使用一个散点图,用Population作为 X 轴,House_buy/M作为 Y 轴,Age作为色调。

plt.figure(figsize=(10,10))
ax = sns.scatterplot(x="Population", y="House_buy/M", hue='Age', s=80,
                     data=sydney_demography_data_cluster[sydney_demography_data_cluster['Cluster Labels'] == 2])

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

Scatterplot to find candidate suburbs

如散点图所示,我们可以确定有 5 个郊区符合我们的要求。他们分别是查茨伍德、兰德威克、马里克维尔、帕拉马塔和莱德。进一步研究这 5 个郊区的餐馆概况,考虑到餐馆类型的多样性,Randwick 和 Chatswood 都很突出。在一个特定的郊区,不同的餐馆类型可能意味着当地的顾客愿意尝试新事物,从而为我们的新餐馆提供相对容易生存的经营环境。

----CHATSWOOD----
Chinese Restaurant       4
Thai Restaurant          2
Sushi Restaurant         2
Malay Restaurant         2
Udon Restaurant          1
Japanese Restaurant      1
Arepa Restaurant         1
Szechuan Restaurant      1
Portuguese Restaurant    1
Dumpling Restaurant      1
Name: 111, dtype: object ----MARRICKVILLE----
Vietnamese Restaurant        8
Thai Restaurant              2
Fast Food Restaurant         1
Greek Restaurant             1
Japanese Restaurant          1
Indonesian Restaurant        0
Indian Restaurant            0
Indian Chinese Restaurant    0
Grilled Meat Restaurant      0
Dumpling Restaurant          0
Name: 321, dtype: object ----PARRAMATTA----
Japanese Restaurant        1
South Indian Restaurant    1
Seafood Restaurant         1
Lebanese Restaurant        1
Asian Restaurant           1
Australian Restaurant      1
Chinese Restaurant         1
Falafel Restaurant         0
Fast Food Restaurant       0
Filipino Restaurant        0
Name: 402, dtype: object ----RANDWICK----
Thai Restaurant          7
Indonesian Restaurant    2
Tapas Restaurant         2
Fast Food Restaurant     2
Spanish Restaurant       1
Japanese Restaurant      1
Lebanese Restaurant      1
Mexican Restaurant       1
Moroccan Restaurant      1
Vietnamese Restaurant    1
Name: 433, dtype: object ----RYDE----
Vietnamese Restaurant        0
Dumpling Restaurant          0
Iraqi Restaurant             0
Indonesian Restaurant        0
Indian Restaurant            0
Indian Chinese Restaurant    0
Grilled Meat Restaurant      0
Greek Restaurant             0
German Restaurant            0
French Restaurant            0
Name: 462, dtype: object

此外,如果我们考虑这两个郊区之间的位置因素,Randwick 比 Chatswood 有明显的优势。Coogee beach 距离 Randwick 中心不到一公里。新南威尔士大学也在 Randwick 旁边,这提供了大量的潜在客户群,包括国际学生。最重要的是,有了新运营的悉尼轻轨,来自 CBD 的潜在客户只需要再花 15-20 分钟就可以到达 Randwick。

4.结论

综合来看,兰德威克是我们应该考虑开设意大利餐厅的最佳郊区。但是,我们应该注意,这只是使用公共数据集的非常原始的分析。我们只能解决在选择餐厅位置时很重要的几个因素。例如,我们还没有考虑人口构成、客户流量和停车位。这些信息可以使我们进一步了解在 Randwick 地区经营意大利餐厅的可行性。然而,这一分析展示了数据科学在解决现实世界问题方面的神奇力量。

一如既往,我欢迎反馈,建设性的批评,并听取您的数据科学项目。你可以在 Linkedin 上找到我。

使用 LDA 探索文本数据

原文:https://towardsdatascience.com/exploring-textual-data-using-lda-ef1f53c772a4?source=collection_archive---------29-----------------------

通过应用机器学习原理来理解非结构化文本数据。

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

简介

我最近在工作中完成了我的第一个机器学习项目,并决定将该项目中使用的方法应用到我自己的项目中。我在工作中完成的项目围绕着使用潜在狄利克雷分配 (LDA)对文本数据进行自动分类。

LDA 是自然语言处理领域中的一种无监督机器学习模型。由于其无监督的性质,LDA 不需要标记的训练集。这使得它非常适合某些用例,或者当大型的、带标签的文本数据集不容易获得时。

LDA 主要用于主题建模,通过相似性对文本文档进行聚类。文档大小可以小到一个单词(不理想),大到整个出版物。LDA 聚类的内容是使用每个文档中的术语(单词)以及它们出现的频率来确定的,有时甚至是它们出现的顺序(使用 n-grams )。被认为彼此相似的文档被聚类在一起,并且我们假设每个聚类代表一个主题,尽管直到聚类被创建之后我们才知道主题本身是什么。需要指出的是模型既不理解这些集群中文档的内容也不理解其上下文,因此实际上不能给集群一个主题标签。相反,它使用来自( 0n)的索引整数来“标记”每个聚类; n 是我们告诉模型要寻找的主题数量。一个人,或者说非常聪明的水生哺乳动物,需要分析这些聚类,并确定每个聚类应该如何被标记。

在这篇文章中,我们将清理一些 Twitter 数据,并编写一个 LDA 模型来对这些数据进行聚类。然后我们将使用 pyLDAvis 来生成集群的交互式可视化。

**关键依赖:**熊猫、 nltkgensim 、numpy、 pyLDAvis

这里有一些需要事先熟悉的定义:

  1. *文档:*文本对象(例如 tweet)
  2. 字典 : 我们的文档集合中所有惟一标记(单词、术语)的列表,每个标记都有一个惟一的整数标识符
  3. *:我们所有文档的集合,每个文档简化为一个矩阵列表,文档中的每个单词对应一个矩阵— 使用 gensim 的doc 2 bow,每个矩阵表示为一个元组,带有一个* 术语的唯一整数 id ,索引为 0 和(例如,文档“the box was in the bigger box”将被简化为类似于[("the “,2),(” box “,2),(” was “,1),(” in “,1),(” bigger “,1)]的内容,但用” term "替换术语的唯一字典 id)
  4. coherence score: 一个范围从 0 到 1 的浮点值,用于评估我们的模型和聚类数与我们的数据的吻合程度
  5. **集群:代表一组文档的节点,一个推断的主题

1.数据

今年早些时候,我开始收集几十万条政治推文,最终目标是对推文及其元数据进行各种分析,为 2020 年美国总统大选做准备。

这篇文章的数据集将由 3500 条推文组成,其中至少提到以下一条:“@berniesanders”、“kamalaharris”、“joebiden”、“ewarren”(分别是伯尼·桑德斯、卡玛拉·哈里斯、乔·拜登和伊丽莎白·沃伦的推特账号)。我在 2019 年 11 月初收集了这些推文,并在这里提供下载。我们将研究这些数据,并试图找出人们在 11 月初发推文的内容。

我不会深入研究如何收集推文,但我已经包括了我在下面使用的代码。成功运行代码需要访问 tweepy API 。我没有收集转发,也没有收集不是用英语写的推文(该模型需要更多的调整以适应多种语言)。

*class Streamer(StreamListener):
    def __init__(self):
        super().__init__()
        self.limit = 1000 # Number of tweets to collect.
        self.statuses = []  # Pass each status here.

    def on_status(self, status):
        if status.retweeted or "RT @" 
        in status.text or status.lang != "en":
            return   # Remove re-tweets and non-English tweets.
        if len(self.statuses) < self.limit:
            self.statuses.append(status)
            print(len(self.statuses))  # Get count of statuses
        if len(self.statuses) == self.limit:
            with open("/tweet_data.csv", "w") as    file: 
                writer = csv.writer(file)  # Saving data to csv. 
                for status in self.statuses:
                    writer.writerow([status.id, status.text,
              status.created_at, status.user.name,         
              status.user.screen_name, status.user.followers_count, status.user.location]) 
            print(self.statuses)
            print(f"\n*** Limit of {self.limit} met ***")
            return False
        if len(self.statuses) > self.limit:
            return False

streaming = tweepy.Stream(auth=setup.api.auth, listener=Streamer())

items = ["@berniesanders", "@kamalaharris", "@joebiden", "@ewarren"]  # Keywords to track

stream_data = streaming.filter(track=items)*

这会将 tweet 文本数据及其元数据(id、创建日期、名称、用户名、关注者数量和位置)传递给名为 tweet_data 的 csv。

*import pandas as pddf = pd.read_csv(r"/tweet_data.csv", names= ["id", "text", "date", "name", "username", "followers", "loc"])*

现在我们已经将数据打包到一个整洁的 csv 中,我们可以开始为我们的 LDA 机器学习模型准备数据了。文本数据通常被视为非结构化数据,在进行有意义的分析之前需要清理。由于不一致的性质,推文尤其混乱。例如,任何给定的 Twitter 用户可能某一天用完整的句子发推,而第二天用单个单词和标签发推。另一个用户可能只发链接,另一个用户可能只发标签。除此之外,还有用户可能会有意忽略的语法和拼写错误。还有一些口语中使用的术语不会出现在标准英语词典中。

清洁

我们将删除所有标点符号、特殊字符和 url 链接,然后对每条推文应用 lower() 。这为我们的文档带来了一定程度的一致性(记住每条 tweet 都被视为一个文档)。我还删除了“berniesanders”、“kamalaharris”、“joebiden”和“ewarren”的实例,因为它们会扭曲我们的词频,因为每个文档至少会包含其中一项。

*import stringppl = ["berniesanders", "kamalaharris", "joebiden", "ewarren"]def clean(txt):
    txt = str(txt.translate(str.maketrans("", "", string.punctuation))).lower() 
    txt = str(txt).split()
    for item in txt:
        if "http" in item:
            txt.remove(item)
        for item in ppl:
            if item in txt:
                txt.remove(item)
    txt = (" ".join(txt))
    return txt

df.text = df.text.apply(clean)*

2.数据准备

下面是我们需要导入的包,以便在将数据输入模型之前准备好数据。在编写数据准备的代码时,我也会包括这些导入。

*import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS as stopwords
import nltk
nltk.download("wordnet")
from nltk.stem import WordNetLemmatizer as lemm, SnowballStemmer as stemm
from nltk.stem.porter import *
import numpy as np
np.random.seed(0)*

我们已经清理了一些文档,但是现在我们需要对它们进行词法分析和词干分析。词汇化将文档中的单词转换为第一人称,并将所有动词转换为现在时。词干处理将文档中的单词还原为它们的根格式。幸运的是,nltk 有一个 lemmatizer 和一个词干分析器可供我们利用。

LDA 涉及到一个随机过程,意味着我们的模型需要产生随机变量的能力,因此有了 numpy 导入。添加 numpy.random.seed(0) 允许我们的模型是可重复的,因为它将生成并使用相同的随机变量,而不是在每次代码运行时生成新的变量。

Gensim 的停用词是一个被认为不相关或可能混淆我们词汇的术语列表。在 NLP 中,“停用词”指的是我们不希望模型选取的术语集合。此列表将用于从我们的文档中删除这些不相关的术语。我们可以 print(stopwords) 来查看将要删除的术语。

以下是停用词中的术语。

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

对于这个模型,我们将保持停用词列表不变,但在某些情况下,可能需要添加我们希望模型忽略的特定术语。下面的代码是向停用词添加术语的一种方法。

*stopwords = stopwords.union(set(["add_term_1", "add_term_2"]))*

词汇化和词干化

让我们为我们的数据准备写一些代码。

*import warnings 
warnings.simplefilter("ignore")
import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS as stopwords
import nltk
nltk.download("wordnet")
from nltk.stem import WordNetLemmatizer as lemm, SnowballStemmer as stemm
from nltk.stem.porter import *
import numpy as np
np.random.seed(0)*

初始化词干分析器。

*stemmer = stemm(language="english")*

写一个函数,既能对我们的文档进行词汇化,又能对其进行词干分析。GeeksforGeeks 有关于使用 nltk 进行词法分析的个例子和关于使用 nltk 进行词干分析的个例子

*def lemm_stemm(txt):
    return stemmer.stem(lemm().lemmatize(txt, pos="v"))*

编写一个函数,将停用词从我们的文档中删除,同时也应用lemm _ stem()

*def preprocess(txt):
    r = [lemm_stemm(token) for token in simple_preprocess(txt) if       token not in stopwords and len(token) > 2]
    return r*

将我们清理和准备好的文档分配给一个新变量。

*proc_docs = df.text.apply(preprocess)*

3。模型的制作

现在我们已经准备好了数据,我们可以开始编写模型了。

词典

正如引言中提到的,字典(在 LDA 中)是在我们的文档集合中出现的所有唯一术语的列表。我们将使用 gensim 的语料库包来构建我们的词典。

*dictionary = gensim.corpora.Dictionary(proc_docs)
dictionary.filter_extremes(no_below=5, no_above= .90)
len(dictionary)*

filter_extremes() 参数是针对停用词或其他常用术语的第二道防线,这些停用词或常用术语对句子的意义没有什么实质意义。摆弄这些参数可以帮助微调模型。关于这一点我就不赘述了,但我在下面附上了来自 gensim 的字典文档中解释参数的截图。

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

我们的字典有 972 个独特的单词(术语)。

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

词汇袋

如引言中所述,单词包(在 LDA 中)是我们分解成矩阵的所有文档的集合。矩阵由术语的标识符和它在文档中出现的次数组成。

*n = 5 # Number of clusters we want to fit our data to
bow = [dictionary.doc2bow(doc) for doc in proc_docs]
lda = gensim.models.LdaMulticore(bow, num_topics= n, id2word=dictionary, passes=2, workers=2)print(bow)*

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

让我们通过查看定义集群的关键术语来了解我们的集群是如何形成的。

*for id, topic in lda.print_topics(-1):
    print(f"TOPIC: {id} \n WORDS: {topic}")*

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

查看每个主题群,我们可以了解它们代表了什么。看一下题目 1 和题目 4。

关于话题 1: 在话题 1 中,关键词“cenkuygur”和“anakasparian”是指 Cenk 维吾尔族*Ana Kasparian共同主持人少壮派(某政论事务所及节目)。主题 1 还包括关键术语“权利”、“特朗普”和“全国步枪协会”。***

11 月 15 日,加州圣塔克拉里塔附近的索格斯高中发生了校园枪击案。关于这一悲剧事件,T2 媒体进行了大量报道,网上也议论纷纷。年轻的土耳其人(TYT)是更严格的枪支法律的口头支持者,并经常与全国步枪协会和其他枪支团体发生冲突。TYT 甚至带头发起了名为#NeverNRA 的承诺运动。

这个主题群可以被标为“TYT 对全国步枪协会”,或类似的东西。

关于主题 4: 术语“cenkuygur”和“anakasparian”在主题 4 中重复出现。话题 4 还包括“theyoungturk”,指的是年轻的土耳其人,以及“berni”,指的是伯尼·桑德斯。

11 月 12 日,岑克维为候选人伯尼·桑德斯发布公开背书。TYT 的推特账户重复了这一表态。伯尼·桑德斯随后公开感谢他们的支持。此外,11 月 14 日,维吾尔先生宣布他将竞选国会议员。这两项进展都在 Twitter 上获得了显著关注。

这个主题群可以被称为“TYT 和伯尼·桑德斯”,或者类似的名称。

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

其他主题群也有类似的解释。

4。评估、可视化、结论

大多数好的机器学习模型和应用都有一个反馈环。这是一种评估模型的性能、可伸缩性和整体质量的方法。在主题建模空间中,我们使用一致性分数来确定我们的模型有多“一致”。正如我在介绍中提到的,coherence 是一个介于 0 和 1 之间的浮点值。为此我们也将使用 gensim。

*****# Eval via coherence scoringfrom gensim import corpora, models
from gensim.models import CoherenceModel
from pprint import pprintcoh = CoherenceModel(model=lda, texts= proc_docs, dictionary = dictionary, coherence = "c_v")
coh_lda = coh.get_coherence()
print("Coherence Score:", coh_lda)*****

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

我们得到了 0.44 的一致性分数。这不是最好的,但实际上也不算太差。这个分数是在没有任何微调的情况下获得的。真正挖掘我们的参数和测试结果应该会得到更高的分数。得分真的没有官方门槛。我的一致性分数目标通常在 0.65 左右。参见这篇文章和这个堆栈溢出线程了解更多关于一致性评分的信息。

用 pyLDAvis 可视化

最后,我们可以使用 pyLDAvis 可视化我们的集群。这个包创建了一个聚类的距离图,沿着 x 和 y 轴绘制聚类。这个距离地图可以通过调用 pyLDAvis.display() 在 Jupiter 中打开,也可以通过调用 pyLDAvis.show() 在 web 中打开。

*****import pyLDAvis.gensim as pyldavis
import pyLDAvislda_display = pyldavis.prepare(lda, bow, dictionary)
pyLDAvis.show(lda_display)*****

这是我们的 pyLDAvis 距离图的截图。

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

将鼠标悬停在每个集群上,会显示该集群中关键术语的相关性(红色)以及这些相同关键术语在整个文档集合中的相关性(蓝色)。这是向风险承担者展示调查结果的有效方式。

结论

这里是我上面使用的所有代码,包括我用来生成单词云的代码和我用来收集推文数据的代码。

*****### All Dependencies ###

import pandas as pd
from wordcloud import WordCloud as cloud
import matplotlib.pyplot as plt
import string
import gensim
from gensim.utils import simple_preprocess
from gensim.parsing.preprocessing import STOPWORDS as stopwords
import nltk
nltk.download("wordnet")
from nltk.stem import WordNetLemmatizer as lemm, SnowballStemmer as stemm
from nltk.stem.porter import *
import numpy as np
np.random.seed(0)
from gensim import corpora, models
from gensim.models import CoherenceModel
from pprint import pprint
import pyLDAvis.gensim as pyldavis
import pyLDAvis

### Word Cloud ###

df = pd.read_csv(r"/tweet_data.csv", names=["id", "text", "date", "name",
                                                                 "username", "followers", "loc"])

def clean(txt):
    txt = str(txt).split()
    for item in txt:
        if "http" in item:
            txt.remove(item)
    txt = (" ".join(txt))
    return txt

text = (df.text.apply(clean))

wc = cloud(background_color='white', colormap="tab10").generate(" ".join(text))

plt.axis("off")
plt.text(2, 210, "Generated using word_cloud and this post's dataset.", size = 5, color="grey")

plt.imshow(wc)
plt.show()### Stream & Collect Tweets ###class Streamer(StreamListener):
    def __init__(self):
        super().__init__()
        self.limit = 1000 # Number of tweets to collect.
        self.statuses = []  # Pass each status here.

    def on_status(self, status):
        if status.retweeted or "RT @" 
        in status.text or status.lang != "en":
            return   # Remove re-tweets and non-English tweets.
        if len(self.statuses) < self.limit:
            self.statuses.append(status)
            print(len(self.statuses))  # Get count of statuses
        if len(self.statuses) == self.limit:
            with open("/tweet_data.csv", "w") as    file: 
                writer = csv.writer(file)  # Saving data to csv. 
                for status in self.statuses:
                    writer.writerow([status.id, status.text,
              status.created_at, status.user.name,         
              status.user.screen_name, status.user.followers_count, status.user.location]) 
            print(self.statuses)
            print(f"\n*** Limit of {self.limit} met ***")
            return False
        if len(self.statuses) > self.limit:
            return False

streaming = tweepy.Stream(auth=setup.api.auth, listener=Streamer())

items = ["@berniesanders", "@kamalaharris", "@joebiden", "@ewarren"]  # Keywords to track

stream_data = streaming.filter(track=items)### Data ###

df = pd.read_csv(r"/tweet_data.csv", names= ["id", "text", "date", "name",
                                                                 "username", "followers", "loc"])

### Data Cleaning ###

ppl = ["berniesanders", "kamalaharris", "joebiden", "ewarren"]

def clean(txt):
    txt = str(txt.translate(str.maketrans("", "", string.punctuation))).lower()
    txt = str(txt).split()
    for item in txt:
        if "http" in item:
            txt.remove(item)
        for item in ppl:
            if item in txt:
                txt.remove(item)
    txt = (" ".join(txt))
    return txt

df.text = df.text.apply(clean)

### Data Prep ###

# print(stopwords)

# If you want to add to the stopwords list: stopwords = stopwords.union(set(["add_term_1", "add_term_2"]))

### Lemmatize and Stem ###

stemmer = stemm(language="english")

def lemm_stemm(txt):
    return stemmer.stem(lemm().lemmatize(txt, pos="v"))

def preprocess(txt):
    r = [lemm_stemm(token) for token in simple_preprocess(txt) if       token not in stopwords and len(token) > 2]
    return r

proc_docs = df.text.apply(preprocess)

### LDA Model ###

dictionary = gensim.corpora.Dictionary(proc_docs)
dictionary.filter_extremes(no_below=5, no_above= .90)
# print(dictionary)

n = 5 # Number of clusters we want to fit our data to
bow = [dictionary.doc2bow(doc) for doc in proc_docs]
lda = gensim.models.LdaMulticore(bow, num_topics= n, id2word=dictionary, passes=2, workers=2)
# print(bow)

for id, topic in lda.print_topics(-1):
    print(f"TOPIC: {id} \n WORDS: {topic}")

### Coherence Scoring ###

coh = CoherenceModel(model=lda, texts= proc_docs, dictionary = dictionary, coherence = "c_v")
coh_lda = coh.get_coherence()
print("Coherence Score:", coh_lda)

lda_display = pyldavis.prepare(lda, bow, dictionary)
pyLDAvis.show(lda_display)*****

LDA 是探索文本数据的一个很好的模型,尽管它需要大量的优化(取决于用例)来用于生产。在编写、评估和显示模型时,gensim、nltk 和 pyLDAvis 包是无价的。

非常感谢你让我分享,以后还会有更多。😃

使用 python 探索数据。

原文:https://towardsdatascience.com/exploring-the-data-using-python-47c4bc7b8fa2?source=collection_archive---------7-----------------------

在本教程中,我们将使用探索性数据分析方法来总结和分析 cars 数据集的主要特征。

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

Image Credits: Statistical Agency

让我们了解如何使用 python 探索数据,然后在下一个教程中基于这些数据构建一个机器学习模型。本教程的完整代码可以在我的 GitHub 库中找到。

1)选择一个数据集

我从 Kaggle 选择了一个名为“汽车”的数据集,这个数据集的作者是 Lilit Janughazyan [1]。从童年开始,我就对汽车感兴趣和着迷。我还记得我曾经有一本书,里面贴着不同汽车的图片和说明书。我更了解最新的汽车及其规格。我更像是一张规格表,记着几乎所有关于汽车的信息,向人们解释市场上不同的汽车。当我年轻的时候,我的梦想是根据汽车的规格来预测它的价格。在这种兴趣的帮助下,我想在这次作业中选择一个基于汽车的数据集。我想实现我的梦想,创建一个模型,该模型将输入汽车的规格,如马力、气缸或发动机大小,然后该模型将根据这些规格预测汽车的价格。数据集可以在这里找到:汽车数据集

我选择数据集而不是其他数据集的主要原因是,在 Kaggle 中投票最多的类别下有近 110 个关于汽车的数据集(投票最多意味着 Kaggle 上可用的最好和最著名的数据集集合),几乎所有这些数据集都缺少一个或另一个特征。例如,数据集“汽车数据集”[2]具有汽车的大多数特征,但其中没有价格特征,根据我的兴趣,这是最重要的特征。因此,我花了很多时间筛选出许多数据集,然后我总结出“汽车”数据集,因为这个数据集几乎具有汽车的每一个重要特征,如马力、建议零售价、发票、气缸、发动机尺寸等等。因为这些良好的特征,这是我选择这个数据集而不是 Kaggle 中其他数据集的主要原因。

这个数据集直接以 CSV(逗号分隔值)格式存储在 Kaggle 上。我不需要执行任何操作就可以将数据转换成格式。由于数据已经是 CSV 格式,导入数据集只需很少的工作,我所要做的只是下载、读取 CSV 数据并将其存储在 pandas 数据框中,为此我必须导入 pandas 库。

2)获取数据

为了获取数据集或将数据集加载到笔记本中,我所做的只是一个微不足道的步骤。在笔记本左侧的 Google Colab 中,您会发现一个“>”(大于号)。点击它,你会发现一个有三个选项的标签,你可以从中选择文件。然后,您可以在上传选项的帮助下轻松上传数据集。无需安装到 google drive 或使用任何特定的库,只需上传数据集,您的工作就完成了。这就是我如何把数据集放进笔记本的

3)擦洗和格式化

将数据格式化成数据帧

因为数据集已经是 CSV 格式。我所要做的只是将数据格式化成熊猫数据框。这是通过导入 pandas 库使用名为(read_csv)的 pandas 数据框方法完成的。read_csv 数据帧方法通过将文件名作为参数传递来使用。然后通过执行这个,它将 CSV 文件转换成一个组织整齐的 pandas 数据帧格式。

**# Importing the required libraries**import pandas as pd 
import numpy as np
import seaborn as sns #visualisation
import matplotlib.pyplot as plt #visualisation
%matplotlib inline 
sns.set(color_codes=True)**# Loading the CSV file into a pandas dataframe.**df = pd.read_csv(“CARS.csv”)
df.head(5)

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

确定实例和特征的数量。

这个数据集有 428 个实例和 15 个特征,也称为行和列。此处的实例代表不同的汽车品牌,如宝马、奔驰、奥迪和 35 个其他品牌,特征代表品牌、型号、类型、产地、驱动系统、建议零售价、发票、发动机尺寸、气缸、马力、MPG-城市、MPG-公路、重量、轴距和汽车长度。

去除不相关的特征。

我将从该数据集中移除一些要素,如传动系、模型、发票、类型和来源。因为这些特征无助于价格的预测。截至目前,我将删除传动系,传动系将不支持预测汽车价格,因为该数据集中的大多数汽车是前轮驱动(52.8%),其余是后轮和全轮驱动。

类似地,型号、类型和产地是不相关的,在这种情况下也不需要,重要的是品牌而不是汽车的型号,当谈到汽车的类型时,大多数汽车都是轿车类型,我保留了汽车的重量和长度特征,在这种情况下,我可以很容易地确定它是 SUV、轿车还是卡车。我还将删除汽车的发票功能,因为我有 MSRP 作为价格,我不需要发票,因为有任何一种类型的汽车价格更有意义,它可以防止导致模糊的结果(因为 MSRP 和发票都非常密切相关,你不能预测给定发票的 MSRP)。最后,汽车的起源与预测率无关,所以我不得不删除它,大部分汽车来自欧洲。

**# Removing irrelevant features**df = df.drop([‘Model’,’DriveTrain’,’Invoice’, ‘Origin’, ‘Type’], axis=1)
df.head(5)

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

4)探索性数据分析

使用 info()识别数据类型

为了识别数据类型,我使用了 info 方法。info 方法打印数据框中数据的摘要及其数据类型。这里有 428 个条目(0–427 行)。移除不相关列之后的数据帧包括 10 列。在这里,品牌、MSRP 是对象类型,而发动机尺寸和气缸是浮动类型,马力、MPG_City、MPG_Highway、重量、轴距和长度是整数类型。因此,数据帧中存在 2 种对象类型、2 种浮点类型和 6 种整数类型的数据。

**# To identify the type of data**df.info()**<class ‘pandas.core.frame.DataFrame’>
RangeIndex: 428 entries, 0 to 427 
Data columns (total 10 columns): 
Make 428 non-null object 
MSRP 428 non-null object 
EngineSize 428 non-null float64 
Cylinders 426 non-null float64 
Horsepower 428 non-null int64 
MPG_City 428 non-null int64 
MPG_Highway 428 non-null int64
Weight 428 non-null int64 
Wheelbase 428 non-null int64 
Length 428 non-null int64 
dtypes: float64(2), int64(6), object(2) 
memory usage: 33.5+ KB**

寻找数据帧的尺寸

为了获得数据框的行数和列数,我使用了 shape 方法。shape 方法获取数据框的行数和列数。这里有 428 行和 10 列。因此 shape 方法返回(428,10)。为了找到数据帧的尺寸,我使用了 ndim (dimension)方法。此方法打印数据框的尺寸。这里,整个数据帧是二维的(行和列)。

**# Getting the number of instances and features**df.shape**(428, 10)**# Getting the dimensions of the data frame
df.ndim**2**

查找重复数据。

这是在数据集上执行的一件方便的事情,因为数据集中可能经常有重复或冗余的数据,为了消除这种情况,我使用了 MSRP 作为参考,这样就不会有超过两个相同的汽车 MSRP 价格,这表明很少有数据是冗余的,因为汽车的价格永远不会非常准确地匹配。因此,在删除重复数据之前,有 428 行,删除之后有 410 行,这意味着有 18 个重复数据。

df = df.drop_duplicates(subset=’MSRP’, keep=’first’)
df.count()**Make 410 
MSRP 410 
EngineSize 410 
Cylinders 408 
Horsepower 410 
MPG_City 410 
MPG_Highway 410 
Weight 410 
Wheelbase 410 
Length 410 
dtype: int64**

查找缺失值或空值。

很多时候,数据集中可能会有很多缺失值。有几种方法可以处理这种情况,我们可以删除这些值,或者用该列的平均值填充这些值。这里,2 个条目在圆柱体特征中有 N/A。这可以通过使用 is_null()方法找到,该方法返回数据框中的空值或缺失值。因此,我没有删除这两个条目,而是用圆柱体列的平均值填充这些值,它们的值都是 6.0。我在查看数据集的第一行和最后几行时发现了这一点。我认为与其删除这是一个好方法,因为每一项数据都是至关重要的。我发现在 Cylinders 特性中有两个存储为 NaN(不是数字)的值。所以我用提到他们索引的切片技术把他们打印出来。

**# Finding the null values**print(df.isnull().sum())**Make 0 
MSRP 0 
EngineSize 0 
Cylinders 2 
Horsepower 0 
MPG_City 0 
MPG_Highway 0 
Weight 0 
Wheelbase 0 
Length 0 
dtype: int64****# Printing the null value rows**df[240:242]

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

**# Filling the rows with the mean of the column**val = df[‘Cylinders’].mean()
df[‘Cylinders’][247] = round(val)val = df[‘Cylinders’].mean()
df[‘Cylinders’][248]= round(val)

将对象值转换为整数类型。

在查看数据时,MSRP 被存储为对象类型。这是一个严重的问题,因为不可能在图形上绘制这些值,因为在绘制图形期间,所有值必须是整数数据类型是一个基本要求。作者以不同的格式存储了 MSRP(36,000 美元),所以我必须删除格式,然后将它们转换为整数。

**# Removing the formatting**df[‘MSRP’] = [x.replace(‘$’, ‘’) for x in df[‘MSRP’]] 
df[‘MSRP’] = [x.replace(‘,’, ‘’) for x in df[‘MSRP’]]df[‘MSRP’]=pd.to_numeric(df[‘MSRP’],errors=’coerce’)

检测异常值

离群点是不同于其他点的点或点集。有时它们会很高或很低。检测并移除异常值通常是个好主意。因为离群值是导致模型不太精确的主要原因之一。因此,移除它们是个好主意。我将执行 IQR 评分技术来检测和删除异常值。通常,使用箱线图可以看到异常值。下图是 MSRP 的方框图。在图中,你可以发现一些点在方框之外,它们不是别的,就是异常值。我在参考资料部分[3]提到了上述数据科学文章中的异常值技术。

sns.boxplot(x=df[‘MSRP’])

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

Q1 = df.quantile(0.25)
Q3 = df.quantile(0.75)
IQR = Q3 — Q1
print(IQR)**MSRP           19086.50 
EngineSize         1.55 
Cylinders          2.00 
Horsepower        85.00 
MPG_City           4.00 
MPG_Highway        5.00 
Weight           872.25 
Wheelbase          9.00 
Length            16.00 
dtype: float6**df = df[~((df < (Q1–1.5 * IQR)) |(df > (Q3 + 1.5 * IQR))).any(axis=1)]

使用该技术后,如下图所示,MSRP 盒图不包含异常点,这是一个很大的改进。以前有超过 15 个点的异常值,现在我已经删除了这些异常值。

sns.boxplot(x=df[‘MSRP’])

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

执行 5 个数字汇总(最小值、下四分位数、中值、上四分位数、最大值

下一步是对数字数据执行 5 个数字的汇总。如前所述,在这种情况下,数字数据是 MSRP、发动机尺寸、马力、气缸、马力、MPG_City、MPG_Highway、重量、轴距和长度。五位数总和包括最小值、下四分位数、中值、上四分位数和最大值,所有这些值都可以通过使用 describe 方法获得。

df.describe()

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

把不同的特征彼此对立起来。

热图

热图是一种图表,当我们需要找到因变量时,它是必不可少的。使用热图可以找到特征之间相关性的最佳方法之一。如下所示,价格特征(MSRP)与马力的相关性高达 83%,这一点非常重要,因为变量之间的关系越密切,模型就越精确。这就是如何使用热图找到特征之间的相关性。在热图的帮助下,我可以在构建模型时使用这些相关功能。

**# Plotting a heat map**plt.figure(figsize=(10,5))
c= df.corr()
sns.heatmap(c,cmap=”BrBG”,annot=True)

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

两个相关变量之间的散点图

我知道功能,尤其是建议零售价和马力更相关。因为我有两个相关的变量,所以我用散点图来显示它们的关系。这里绘制了马力和 MSRP 之间的散点图,如下所示。根据下面给出的图表,我们可以在建模过程中轻松绘制趋势线。我可以很容易地在图中看到一条最合适的线。我没有包括 MSRP 和发动机尺寸或气缸之间的散点图,原因是这些数据与 MSRP 的相关性比 MSRP 和马力(83%)的相关性要小。因为如上所述,MSRP 和发动机尺寸之间的相关性为 54 %, MSRP 和气缸之间的相关性为 64%,所以没有理由绘制这些特征。

**# Plotting a scatter plot**fig, ax = plt.subplots(figsize=(5,5))
ax.scatter(df[‘Horsepower’], df[‘MSRP’])
plt.title(‘Scatter plot between MSRP and Horsepower’)
ax.set_xlabel(‘Horsepower’)
ax.set_ylabel(‘MSRP’)
plt.show()

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

5)报告初步调查结果

我认为汽车的建议零售价(价格)和马力特性有很大的关系。我将在作业 4 中对此进行更多的探索。现在我知道我的问题陈述是“给定汽车的规格,预测汽车的价格(MSRP)”。主要想法是预测汽车的(建议零售价)价格。现在我知道我必须预测一个值,所以我应该使用回归算法,因为我有两个相关的特征(独立和从属特征)。但有许多类型的回归算法,如线性回归,随机森林回归,套索和岭回归等等。所以我可能会使用其中的一种算法,并在下一篇教程中实现一个机器学习模型来预测价格。因此,这项任务主要涉及探索性的数据分析,我准备好了数据,现在可以建立模型了。

参考

[1]janjughazhyan,L. (2017 年)。汽车数据。[在线]Kaggle.com。可在:https://www.kaggle.com/ljanjughazyan/cars1【2019 年 8 月 15 日访问】。

[2] Srinivasan,R. (2017 年)。汽车数据集。[在线]Kaggle.com。可在:【https://www.kaggle.com/toramky/automobile-dataset 【2019 年 8 月 16 日进入】。

[3]n .夏尔马(2018 年)。检测和去除异常值的方法。【在线】中等。可在:https://towards data science . com/ways-to-detect-and-remove-the-outliers-404d 16608 DBA【2019 年 8 月 15 日获取】。

在电影数据集上探索 Neo4j 中的全文搜索索引

原文:https://towardsdatascience.com/exploring-the-full-text-search-index-in-neo4j-on-a-movies-dataset-3cddca69db7a?source=collection_archive---------11-----------------------

全文搜索索引是在 Neo4j 版本 3.5 中引入的,由 Apache Lucene 支持索引。这篇博文的议程是介绍基本的 Lucene 查询语言,并描述如何在 Neo4j 中使用它。正如您将看到的,我们还可以将 Lucene 和 Cypher 结合起来操作结果。

我这篇博文的大部分灵感来自克里斯托夫·威廉森,他是 Graphaware 团队的一员。他很好地描述了 FTS 对像我这样的新手的使用,谢谢!

我们将使用 kaggle 上的电影对话语料库数据集。它有丰富的元数据,因为它包含了 617 部不同电影中角色之间的所有对话。我有几篇使用该数据集的博客文章,但是首先,我们将只使用关于电影的数据来探索全文搜索索引功能。

图形模型

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

Made using apcjones.com/arrows/

我们的图表中有两个标签(电影、电影标签)。它们通过 HAS_TAG 关系进行连接。电影可以有额外的属性,如发行年份、IMDB 评级和 IMDB 上的投票数。

创建约束

为了优化导入和以后的查询,我们为电影和电影标签定义了唯一的约束。

CREATE CONSTRAINT ON (m:Movie) ASSERT m.id IS UNIQUE;
CREATE CONSTRAINT ON (m:MovieTag) ASSERT m.id IS UNIQUE;

导入

在运行导入查询之前,将movie _ titles _ metadata . tsv文件复制到 $Neo4j/import 文件夹中。

LOAD CSV FROM "file:///movie_titles_metadata.tsv" as row FIELDTERMINATOR "\t"
MERGE (m:Movie{id:row[0]})
SET m.title = row[1],
    m.release_year = toInteger(row[2]),
    m.imdb_rating = toFloat(row[3]),
    m.no_votes = toInteger(row[4])
WITH m, apoc.convert.fromJsonList(
          replace(row[5]," ",",")) as tags
UNWIND tags as tag
MERGE (mt:MovieTag{id:tag})
MERGE (m)-[:HAS_TAG]->(mt)

全文搜索索引

简单来说,lucene 索引管道由两个步骤组成。第一步是“分析器”步骤,它负责文本的预处理。第二步,“索引器”将“分析器”的结果存储到索引中。

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

Image from https://www.tutorialspoint.com/lucene/lucene_indexing_process.htm

在这篇博文中,我们将使用 Neo4j 中默认的“标准”分析器。它对非字母进行标记,并过滤掉英语停用词和标点符号。不做词干分析,但注意将可能的产品名称、URL 和电子邮件地址作为单个术语。

您可以查看其他可用的分析仪。

CALL db.index.fulltext.listAvailableAnalyzers;

创建全文搜索索引

首先,我们必须将所有包含数字的属性转换成字符串,因为我们不能索引整数或浮点数。索引过程会自动忽略数字。

IMDB 评级属性是一个只有一个小数点的数字。要将其转换为字符串,我们将首先将其乘以 10,然后将其转换为字符串。例如,评分 6.1 将被保存为“61”。

MATCH (m:Movie)
SET m.string_rating = toString(toInteger(m.imdb_rating * 10)),
    m.string_release_year = toString(m.release_year)

当使用范围查询时,我们必须小心,因为排序是按照词汇顺序(字母顺序)进行的。在我们的例子中,所有的评级都在“00”到“99”之间,所以应该没有问题。例如,如果范围跨越到“150”,我们就会遇到一个问题,因为默认情况下,在“50”和“150”之间查找间隔将不起作用。在 Lucene range 的博客文章中描述了解决方法。为了允许在具有不同位数的数字之间进行范围搜索,我们需要对这些值进行预处理,在它们前面加上零,以确保所有的值都具有相同的位数。

在这个例子中,我们将所有的数字预处理为七位数。

WITH 7 as total_length
MATCH (m:Movie)
WHERE exists (m.imdb_rating)
WITH m, total_length, 
        toString(toInteger(m.imdb_rating * 10)) as string_rating
WITH m, total_length — length(string_rating) as zeros, string_rating
WITH m, apoc.text.join([x in range(1,zeros) | “0”],””) +    
                                  string_rating as final_rating
SET m.range_rating = final_rating

现在我们创建“MovieIndex”索引,它包含带有标签 Movie 和四个指定属性的节点。第一个参数是索引的名称。第二个参数定义节点的标签,第三个参数定义我们想要索引的属性。

CALL db.index.fulltext.createNodeIndex(
"MovieIndex",["Movie"],["title","string_rating","range_rating","string_release_year"])

Lucene 查询

让我们看看一些基本的 lucene 查询操作符。

具体属性

搜索标题中带有“梦”字的电影。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream") YIELD node, score
RETURN node.title as title, score

结果

逻辑运算符

有两种逻辑运算符可用,“或”和“与”。

搜索 1999 年或 2000 年发行的电影。

CALL db.index.fulltext.queryNodes("MovieIndex", 
     "string_release_year:1999 or 2000") YIELD node, score
RETURN node.title as title, score
LIMIT 5

结果

单字符通配符

单字符通配符操作符?查找与被替换的单个字符匹配的术语。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:th?") YIELD node, score
RETURN node.title as title, score

结果

需要注意的一点是,“黑客帝国”电影不会显示在结果中,因为标准分析器删除了像“the”这样的停用词。

多字符通配符

多字符通配符运算符查找零个或多个字符。也可以像dre*am一样把运算符放在词条中间。为了防止极其缓慢的通配符查询,术语不应该以通配符*dream开头。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:drea*") YIELD node, score
RETURN node.title as title, score

结果

模糊搜索

模糊搜索通过使用数学公式来计算两个单词之间的相似度。计算相似性的常用方法是 Levenshtein 距离。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream~") YIELD node, score
RETURN node.title as title, score
LIMIT 5

结果

范围查询

Lucene 区分了具有包含或排除端点的范围操作符。

操作员:

{} ->不包括边缘

[] ->包括边缘

CALL db.index.fulltext.queryNodes("MovieIndex", "string_rating:[50 TO 99}") YIELD node, score
RETURN node.title as title,score
LIMIT 5

结果

为了说明为什么我们在进行范围搜索时需要相同位数的数字,让我们尝试下面的查询。

CALL db.index.fulltext.queryNodes("MovieIndex", "string_rating:[50 TO 100]") YIELD node, score
RETURN node.title as title,score

直觉上你会认为它工作得很好,但是由于词汇排序的原因,情况并非如此。我们用来解决这个问题的变通方法是 range_rating 属性,其中的值以零为前缀,以允许在不同数量级的数字之间进行字母排序。

CALL db.index.fulltext.queryNodes(“MovieIndex”, “range_rating:[0000050 TO 0000150]”) YIELD node, score
RETURN node.title as title,score
LIMIT 5

结果

提高分数

Lucene 查询语言还支持使用脱字符(^) 操作符提升结果的分数。

提升 string_rating 介于 50 和 99 之间的结果。

CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream string_rating:[50 TO 99]^2") YIELD node, score
RETURN node.title as title,score
LIMIT 5

结果

时间衰变

我在 Christophe Willemsen 的 Nodes 2019 网络研讨会上看到一个 lucene time decay 查询,我想复制它。基本上,我们结合许多增强参数来实现时间衰减效果。为了说明这意味着什么,我们将使用apoc.text.join()来生成九个助推参数。

RETURN apoc.text.join([
 x in range(0,8) | 
 "string_release_date:" + toString((date().year - x)) + "^" + toString(10-x)
]," ")

结果

string_release_date:2019^10 string_release_date:2018^9 string_release_date:2017^8 string_release_date:2016^7 string_release_date:2015^6 string_release_date:2014^5 string_release_date:2013^4 string_release_date:2012^3 string_release_date:2011^2

你可以看到,2019 年上映的电影将增加 10 倍,2018 年的电影将增加 9 倍,以此类推。

总结这篇博文,让我们在一个查询中结合评分提升和时间衰减效果。为了让你知道以后你可以用 cypher 处理结果,让我们添加一个过滤器,只返回惊悚片。

WITH apoc.text.join([x in range(0,10) | 
"string_release_date:" + toString((date().year — x)) + "^" +   
  toString(10-x)]," ") as time_decay
CALL db.index.fulltext.queryNodes("MovieIndex", "title:dream string_rating:[50 TO 99]^2 "+ time_decay) YIELD node, score
// filter only thrillers
MATCH (node)-[:HAS_TAG]->(:MovieTag{id:'thriller'})
RETURN node.title as title,score
LIMIT 5

结果

结论

全文搜索是一个非常受欢迎的特性,它可以帮助我们开发更好更快的 Neo4j 应用程序,还可以解决其他问题。如果您有任何问题或反馈,请加入 Neo4j 社区论坛

所有代码在Github上都有。

用 R 刮和探索 SP500 第 1 部分

原文:https://towardsdatascience.com/exploring-the-sp500-with-r-part-1-scraping-data-acquisition-and-functional-programming-56c9498f38e8?source=collection_archive---------10-----------------------

刮擦、数据采集和功能编程

今天,我想通过一个简单的例子,使用函数式编程和整洁的迭代概念,将抓取、对 Yahoo finance api 的调用、数据连接和简单的资产分析结合起来。

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

photo credit: https://unsplash.com

最终目标:

我想确定过去 3 个月哪些 SP500 资产的平均回报率最高。

这个 分析的最终结果 就是这个图表:

这个图表的制作,资产分析,回答我的问题,会在这个系列 的第二部分 中讲到。

介绍和 Tidyverse:

在今天的分析中,我将使用 R 编程语言。如果你过去读过我在 Linkedin 或 Medium 上的任何帖子,你可能已经注意到我通常用 python 编程。总的来说,我更喜欢 python 编程语言,因为它的语法更简单,应用更广泛,也更容易投入生产。

潮间带生态系统:

在 R 编程语言中,有一组包组成了所谓的 tidyverse 。这些软件包主要由 Rstudio 的工程师和数据科学家维护,并提供了一种简单、集成和统一的方法来操作 r 中的数据。tidyverse 围绕着 整理数据 这是 Hadley Wickham 创造的一个术语,用来描述数据,其中:

  • 每个变量都是一列
  • 每个观察(或案例)都是一行

今天我将利用几个图书馆。在下面的代码块中,我导入了我将要使用的库。

# for scraping
library(rvest)
# blanket import for core tidyverse packages
library(tidyverse)
# tidy financial analysis 
library(tidyquant)
# tidy data cleaning functions
library(janitor)

我要做的下一件事是用今天的日期定义一个变量。然后我从今天减去 3 个月。这将返回另一个 date 对象,指示 3 个月前的某一天。我需要这个,因为我想得到每个股票最近 3 个月的价格数据。

# save current system date to a variable
today <- Sys.Date()
# subtract 3 months from the current date
date = today %m+% months(-3)
print(date)

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

一个股票

我将使用 tidyquant 包获取所有 SP500 证券交易所的财务数据。tidyqunat 包的核心函数是 tq_get(),可以用来获取股票的各种信息。如果我将一个包含股票代号的字符串传递给 tq_get(),它将返回开盘、盘高、盘低、收盘或 OHLC 数据。我将 SP500 的报价器^GSPC 传递给 tq_get()函数。

# pass SP500 ticker ^GSPC to tq_get function
one_ticker = tq_get("^GSPC", from = date)
one_ticker %>% 
  head()

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

上面有几件事需要注意

  • tq_get()返回一个整洁的数据帧
  • 股票代码名称不在数据帧中
  • %>%运算符称为管道。它将前面的对象作为第一个参数传递给后面的函数。

放大第 1 部分—获得所有 505 个分笔成交点的向量:

我想要所有 SP500 售票员的 OHLC 数据。为了做到这一点,我需要做几件事:

  • 创建所有 SP500 证券交易所的向量
  • 迭代这个向量并对向量的每个元素调用 tq_get(),为向量中的每个元素返回一个 dataframe
  • 将所有这些数据帧组合成一个数据帧

哇!听起来有点复杂,对吧?幸运的是,有了 R,这将变得非常简单。维基百科有一个包含所有 505 家 SP500 证券交易所(一些公司,如谷歌,有多个资产类别)的表格,位于以下网址:

https://en.wikipedia.org/wiki/List_of_S%26P_500_companies

为了得到所有的 SP500 ticker,我们将使用 rvest 包来清理这个表。rvest 包是 R 中的一个简单的刮包,非常类似于 python 的美汤。在编程的上下文中,抓取被定义为以编程方式从互联网和网页上收集人类可读的内容。

在下面的代码中,我抓取了维基百科表格,并创建了一个包含所有 SP500 证券交易所的向量:

  • 我首先将维基百科的 URL 赋给一个变量
  • 从 URL 读入 html
  • 选择正确的 html 节点并提取 html 表格
  • 对股票代号做了一个小小的改动,因为雅虎财经用了一个“_”而不是“.”对于某些符号名称

抓取最困难的部分是找出 xpath 或 css 来指示选择哪些 html 节点。我真的不太了解 html 或 css,但是使用 Google Chrome 我能够找到正确的 xpath(下面会有更多相关内容)。

# get the URL for the wikipedia page with all SP500 symbols
url <- "[https://en.wikipedia.org/wiki/List_of_S%26P_500_companies](https://en.wikipedia.org/wiki/List_of_S%26P_500_companies)"
# use that URL to scrape the SP500 table using rvest
tickers <- url %>%
  # read the HTML from the webpage
  read_html() %>%
  # one way to get table
  #html_nodes(xpath='//*[[@id](http://twitter.com/id)="mw-content-text"]/div/table[1]') %>%
  # easier way to get table
  html_nodes(xpath = '//*[[@id](http://twitter.com/id)="constituents"]') %>% 
  html_table()
#create a vector of tickers
sp500tickers <- tickers[[1]]
sp500tickers = sp500tickers %>% mutate(Symbol = case_when(Symbol == "BRK.B" ~ "BRK-B",
                                           Symbol == "BF.B" ~ "BF-B",
                                            TRUE ~ as.character(Symbol)))

如何使用 Google Chrome 找到我传递给 html_nodes 函数的 xpath:

  • 去 https://en.wikipedia.org/wiki/List_of_S%26P_500_companies
  • 右击网页,选择检查选项

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

  • 大多数网页内容通常在 html 文档的主体中。我们会扩大那部分。看起来会像这样:

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

  • 查看网页时,我可以看到我想要的表格就在第一个 h2 标题的下方:

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

  • 在浏览了页面结构后,我找到了第一个 h2 标题和我想要放在它下面的表格

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

  • 我可以点击这个表,右键点击并复制这个表所需要的 xpath

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

  • XPath =//*[@ id= " constituents "],这就是传递给 html_nodes 的内容
html_nodes(xpath = '//*[[@id](http://twitter.com/id)="constituents"]'

向上扩展第 2 部分— Purrr、迭代和函数式编程:

迭代是指以编程方式重复一个步骤或一组步骤,重复一定的次数,或者直到达到某个条件。

通常,当我们在任何编程语言中迭代时,我们使用循环,通常是 for 循环。我需要迭代 SP500 ticker 向量中的每个元素,并将其传递给函数 tq_get()。我可以用 for 循环做到这一点,但是使用 purrr 包是一个更好的主意。R 中的循环很慢,很难读懂。purrr 包提供了一套用于迭代和函数式编程的函数,可以很好地与 tidyverse 的其余部分集成。map()中 purrr 的核心函数。大多数编程语言(包括我最喜欢的一个 python)都有一个 map 函数,用于将一个函数应用于一个对象的所有元素。

函数式编程是一种编程范式,与相反,函数构建程序的结构和逻辑。在函数式编程中,通常避免使用 For 循环。相反,函数被映射或应用于列表或其他对象。

正如哈德利·韦翰在他的《高级 R 书》中所说:

“很难准确描述什么是功能性风格,但通常我认为它意味着将一个大问题分解成更小的部分,然后用一个功能或功能组合来解决每个部分。当使用函数式风格时,您努力将问题的组成部分分解成独立操作的孤立函数。每个函数本身都简单易懂;复杂性是通过以各种方式组合函数来处理的。”

让我们用一个例子来看看 purrr 中的迭代和 for 循环的区别。这两种操作大致相同:

# get a sequence of the numbers 1 to 5
numbers = seq(1:5)
print('for loop')# for loop 
for (i in numbers){

  print(i)
}print('purrr :)')# purr functional programming approach
list = map(numbers, print)

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

需要注意的几件事:

  • for 循环和 map 函数都对向量中的每个元素进行运算
  • map 函数返回一个嵌套的列表,其中每个条目都是在该列表中调用的函数的结果,该函数针对的是被迭代的对象中的一个条目。我把它赋给一个变量列表,以避免它被打印出来。
  • 使用 purrr 的 map 函数只需要一行代码和很少的代码
  • 使用 purrr 的地图功能更容易阅读

好吧,现在让我们迭代得到所有 505 SP500 ticker!

首先,我需要编写一个函数来迭代或应用到向量的每个元素。我不能简单地将 tq_get()与 map()一起使用,因为它不会将 ticker 名称作为 dataframe 的一列返回。为了获得带有数据帧的 ticker,我将使用 dplyr 中的 mutate()函数创建一个带有 ticker 名称的新列。

get_symbols = function(ticker = "AAPL"){
  df = tq_get(ticker, from = date) %>% mutate(symbol = rep(ticker, length(date)))
}

然后,我将这个函数与 map()结合使用来遍历所有符号的列表。这将返回一个嵌套列表,其中包含每个 ticker 的数据帧。我使用 dplyr bind_rows()函数将数据帧按行绑定在一起,创建一个包含所有 SP500 报价机的数据帧。

#create the dataframe of SP500 data by interating over our list of symbols and call our get symbols function each time
#the map function accomplishes thistickers_df = map(symbols, get_symbols) %>% bind_rows()

我还希望这个数据帧包含来自维基百科表的信息,最重要的是公司的名称。这可以通过用符号连接两个数据帧来实现。

tickers_df = tickers_df %>% 
  # left join with wikipedia data
  left_join(sp500tickers, by = c('symbol' = 'Symbol')) %>% 
  # make names R compatible
  clean_names() %>% 
  # keep only the columns we need
  select(date:security, gics_sector, gics_sub_industry)

加入数据后,我们应该做一个快速的健全检查,以确保我们有所有 505 SP500 ticker

tickers_df %>% 
# select just the symbol column
select(symbol)%>% 
# get the distinct values
distinct()%>% 
# count the distinct values 
count() %>% 
# we can use select to rename columns 
select("Total Number of Tickers" = n)

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

最后,我们可以检查数据帧的前几行,以确认我们已经获得了想要的数据:

tickers_df %>% 
  head()

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

完美!正如我们所料。

总结:

在第一篇博文中,我们有:

  • 了解了 tidyverse 生态系统和 tidyquant 的基础知识
  • 学习了整洁迭代和函数式编程的基础知识
  • 学会了如何利用 rvest 和 chrome 从维基百科收集数据
  • 学会了如何从阅读单一资产转移到整个 SP500

中的 中的下一篇 :

我来回答我原来的问题:

过去 3 个月,哪些 SP500 资产的平均回报率最高?

需要注意一些事情:

  • 使用 TQ _ index(“SP500”),您可以更容易地获得所有 SP500 股票的列表。这个函数需要 XLConnect 库。我目前在本地机器上导入并运行这个库时遇到了问题。

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

  • tq_get()实际上接受报价器列表,所以没有必要使用 map()
  • 我有意以这种方式编写代码,以演示所描述的概念

声明:在大学里,我用 SP500 数据和 python 做了一个项目。分享这个项目是违反班级政策的。虽然我在这里做的事情有些类似,但我使用的是完全不同的编程语言,回答的是完全不同的问题。鉴于我之前陈述,我断言这并不违反课程政策。我有意使用 R 编程语言,而不是 python,以完全避免任何问题,并尊重我以前的教授的意愿。

代码可以在这里找到

这是另一个关于抓取的很好的资源,对写这篇文章有一点帮助。

https://medium . com/@ ky leake/Wikipedia-data-scraping-with-r-rvest-in-action-3c 419 db 9 af 2d https://medium . com/@ ky leake/Wikipedia-data-scraping-with-r-rvest-in-action-3c 419 db 9 af 2d

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

用 R 刮和探索 SP500 第二部分

原文:https://towardsdatascience.com/exploring-the-sp500-with-r-part-2-asset-analysis-657d3c1caf60?source=collection_archive---------17-----------------------

资产分析和可视化

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

photo credit: https://unsplash.com/photos/uJhgEXPqSPk

在这篇博文的第一部分,我从维基百科搜集了一系列 SP500 符号,并使用这些信息从雅虎财经 API 中提取了所有 SP500 股票的 OHLC(开盘价、最高价、最低价、收盘价)数据。

在这篇博文的第二部分,我将回答我最初的问题:

在过去的三个月里,哪些 SP500 资产的平均回报率最高?

在第 1 部分中,我们创建了 tickers_df,这是一个包含所有 505 个 SP500 tickers 的数据框架。

让我们再快速看一下 tickers_df:

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

这个数据是整理的意思:

  • 每个变量都是一列
  • 每个观察(或案例)都是一行

这种结构使数据易于过滤和聚合,也更易于扩展。只需几行代码,我们就可以轻松提取和可视化一项资产的原始价格:

ticker = "GOOGL"
tickers_df %>% 
  filter(symbol == !!ticker) %>% 
  ggplot(aes(date, adjusted))+
  geom_line()

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

我们可以整天查看原始价格数据,但很难比较价格差异巨大的资产。通常,当我们分析资产时,我们会查看价格或回报的百分比变化。使用 tidyquant 包,计算回报极其容易。在下面的代码片段中,我将每项资产的原始调整收盘价转换为回报率。这是使用 tq _ transmute()函数完成的。

daily_sector = tickers_df %>% group_by(security, gics_sector, symbol) %>% 
tq_transmute(select     = adjusted, 
              mutate_fun = periodReturn, 
              period     = "daily") %>% 
              ungroup()

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

需要注意一些事情:

  • 数据仍然很整洁
  • 我们从雅虎财经 API 和维基百科页面上搜集了信息
  • 使用这种格式,很容易按符号、证券或 gics_sector 进行汇总
  • 别忘了解组!我最初是这样做的

最佳表现者:

让我们继续计算证券的平均日收益率,然后按这个排序,来回答我的问题。我还会计算收益的标准差。在数量金融学中,收益的标准差一般被称为波动率。

avg_return =daily_sector %>% 
  group_by(security, gics_sector) %>%
  summarise(avg_return = round(mean(daily.returns), 4),Volatility =   sd(daily.returns)) %>%         
arrange(desc(avg_return), desc(Volatility))
avg_return %>% head()

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

从这张表中我们可以很快地看到,美国著名的化学公司 Dow Inc .在过去的 3 个月里拥有最高的日平均回报率。这张表回答了我原来的问题。

让我们使用 ggplot 将此表可视化为一个条形图:

avg_return %>% head(20) %>% ggplot(aes(reorder(security, -avg_return), avg_return, fill = avg_return))+
  geom_col()+
  coord_flip()+
  labs(title = "Securities With Highest Average Returns In SP500 Over Past 3 Month", x = "Security", y = "Average Return")+
  theme_classic()+
  theme(legend.position="none")

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

Wow Dow Inc .的平均回报率远高于其他市场。我可能漏掉了一些重要的东西。快速的谷歌搜索告诉我这有一个明显的原因。自 3 月底正式从 Dowdupont 分离出来后,陶氏公司才作为一家独立的化学公司上市。平均回报率可能只有这么高,因为它是一种新交易的资产,我们没有太多的观察数据。

看着其他表现出色的人,我注意到一些事情:

  • 过去 30 天里,许多表现最佳的公司都是科技公司。
  • Chipotle 的强劲表现让我有点吃惊。他们显然已经从之前围绕食物中毒的争论中恢复过来
  • 两家计算机硬件制造商 Nvidia 和 Advanced Micro Devices 的回报率高于其竞争对手英特尔,后者在历史上表现更好。

通常,当你用数据回答一个问题时,更多的问题就会出现。我已经知道哪些股票在过去几个月的平均回报率最高,但波动性呢?波动性是证券或投资组合最重要的投资指标之一。

根据维基百科的说法,投资者关心波动性至少有八个原因:

  1. 投资价格的波动越大,就越难不担心;
  2. 交易工具的价格波动可以定义投资组合中的头寸规模;
  3. 当在特定的未来日期需要通过出售证券获得一定的现金流时,更高的波动性意味着更大的短缺机会;
  4. 在为退休储蓄的同时,回报的波动性更高,导致可能的最终投资组合价值分布更广;
  5. 退休时回报率的波动性更高,这使得提款对投资组合的价值产生更大的永久影响;
  6. 价格波动提供了低价买入资产、高价卖出的机会;
  7. 投资组合波动对该投资组合的复合年增长率 (CAGR)有负面影响
  8. 波动率影响期权的定价,是布莱克-斯科尔斯模型的一个参数。

通常,年轻投资者可以承受更大的波动性,而接近退休年龄的投资者则希望小心波动性。

使用 ggplot2,我们可以很容易地绘制出平均回报和回报波动性之间的关系。我将创建一个散点图,使用资产标记而不是图中的点。

plot = avg_return %>% ggplot(aes(avg_return, Volatility))+
  geom_text(aes(label = symbol), size = 3)+
  labs(title = "Average Return vs Volatility Over Last 3 Months In SP500", x = "Average Return", subtitle = "Data Source: Yahoo Finance")+
  theme_minimal()

plot

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

这张图比我们之前的柱状图更好地展示了市场上正在发生的事情。

与市场上的其他股票相比,一些股票的确很突出

  • 道琼斯公司有最高的平均回报率,似乎围绕市场波动。
  • AMD 和黄凤英表现突出,高于市场波动和回报。对于年轻投资者来说,这些可能是可靠的投资机会
  • Matel 和 EA games 表现突出,其市场回报率在 100%左右,但波动性较高.这种高波动性让投资者有机会在证券价格过低时买入,在价格过高时卖出。
  • 图左上角最突出的是福克斯公司两种资产类别的高波动性和低平均回报率

让我们在图表上突出显示福克斯公司的代号:

avg_return = avg_return %>% 
  mutate(Indicator = case_when(symbol %in% c('FOX', 'FOXA') ~ "Fox Corporation",
                               TRUE ~ "The Rest of the SP500"))plot = avg_return %>% ggplot(aes(avg_return, Volatility, color = Indicator))+
  geom_text(aes(label = symbol), size = 3)+
  labs(title = "Average Return vs Volatility Over Last 3 Months In SP500", x = "Average Return", subtitle = "Data Source: Yahoo Finance")+
  theme_minimal()

plot

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

哇!看那个!我走进这个分析,对哪些公司表现最好感兴趣。但是,最终,真正脱颖而出的是福克斯公司,在过去的 3 个月里,相对于市场的其他公司,它的表现似乎相当疲软。

福克斯公司,尤其是他们的新闻站假新闻(我拼对了吗?),多年来一直颇有争议。该网络对许多问题有独特的观点,比一般的新闻网络有更多基于观点的节目。

让我们获得更多一点的福克斯公司的价格数据:

symbols = c('FOX', 'FOXA')
fox = tq_get(symbols, from = '2017/01/01')
fox %>% 
  ggplot(aes(date, adjusted))+
  geom_line()+
  geom_smooth(method = 'lm', alpha = 0.3)+
  facet_wrap(~symbol)+
  theme_classic()

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

  • 在过去的几年里,这两个狐狸的标志似乎都在稳步增长
  • 最近价格大幅下跌。随后继续呈下降趋势

让我们放大最近的价格:

tickers_df %>% 
  filter(symbol %in% c('FOX', 'FOXA')) %>% 
  ggplot(aes(date, adjusted))+
  geom_point(alpha = 0.3, color = 'black')+
  geom_line()+
  facet_wrap(~symbol)+
  theme_classic()

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

我们可以看到价格下跌发生在 3 月初。3 月 13 日出现了最初的大幅下跌,随后出现了短暂反弹。继反弹之后,价格下跌,并继续呈下降趋势。

让我们求助于谷歌来弄清楚 3 月 13 日发生了什么。快速的谷歌搜索告诉我,媒体事务在 3 月 13 日组织了一场反对福克斯公司的大规模抗议活动。

继 3 月 13 日下跌后,2019 年 3 月 19 日,福克斯公司开始独立于 21 世纪福克斯进行交易。这使得资产价格急剧下跌。不过,这并不像人们最初认为的那样,代表着资产表现不佳。

实际发生的是,福克斯公司开始使用 21 世纪福克斯的代码进行交易,21 世纪福克斯现在使用 TFCF 和 TFCFA 的代码进行交易。这导致许多新闻媒体错误地报道了福克斯和福克斯下降了 22%

完成并制作最后的情节:

好了,现在我们想分享一下我们在互动情节中发现的东西,这样技术用户就不会有有趣的、信息丰富的互动情节了。

r 有很多很好的交互式数据可视化框架。我个人最喜欢的 R 中的交互式图形库是 plotly 库。Plotly 是一个交互式的 javascript 库,带有许多不同编程语言的 API。我更熟悉 python API(以及一般的 python 语言)。

使用与 ggplot2 中非常相似的代码,我们可以生成一个漂亮的交互式绘图。我会在这篇文章中嵌入情节。

p <- plot_ly(avg_return, x = ~avg_return, y = ~Volatility, type = 'scatter',
        mode = 'text', text = ~symbol, textposition = 'middle right',
        color = ~Indicator, colors = 'Set1',
        textfont = list(size = 8)) %>%
  layout(title = 'Average Return vs Volatility Over Last 3 Months In SP500',
         xaxis = list(title = 'Averaage Return', zeroline = FALSE
                      ),
         yaxis = list(title = 'Volatility'
                      ))

概括地说,在这两部分系列中,我们:

  • 复习了关于抓取、函数式编程和 tidyverse 的介绍
  • 整个 SP500 的导入股票数据
  • 提出一个问题,哪些股票在过去 3 个月的平均回报率最高?
  • 我们回答了这个问题,并通过对我们的数据提出更多的问题,我们更全面地回答了这个问题,并发现最终有趣的是看似较低的平均回报率和相对于市场的高波动性。
  • 这种不断对数据提出问题的循环工作流在数据科学中很常见
  • 我们需要小心数据质量,确保我们理解我们的数据。FOX,FOXA 看起来表现很差,而道琼斯看起来比市场其他股票表现好得多。这些结论会产生误导!

来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值