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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

这是官方说法:时间不存在

原文:https://towardsdatascience.com/its-official-time-doesn-t-exist-8c786530eca1?source=collection_archive---------1-----------------------

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

来源: Pixabay at Pexels —免费源图像

那么我们应该为 ML 模型提供什么样的时间特性呢?

*【*重要的 】:有些人离开文章时以为是关于物理的。如果您只对 ML 中关于如何处理时间特征的部分感兴趣,请随意跳过故事的第一部分,直接进入’ 处理我们数据集 中的时间特征。

12 月,我和女友回到了乌拉圭和阿根廷,我们来自那里。在那边,她的祖父,一个我个人觉得很迷人的人,向我推荐了一本书,告诉我这是他一生中读过的最难的书之一。让我解释一下:我们在谈论一个同时阅读两到三本书的超级聪明的人。他自己读的书可能比我们想象的要多。但事情并没有就此结束:在我们离开之前,他给我买了这本书,所以我在圣诞节前后离开了布宜诺斯艾利斯,手里拿着一本大约 150 页的书,书名是《时间的顺序》,作者是意大利物理学家卡尔罗·罗威利。

他让我一读完这本书就给他提意见,所以我就有意识地读了。我确保我已经理解了每一章,并且我突出了所有的书,以便在我读完之后再读一遍最重要的部分。我并不羞于大声说我不得不至少读两遍所有的章节来理解它们。这本书一开始就说,如果两个双胞胎兄弟十年后再次相遇,一个住在山里,另一个住在平原上,前者会更老,因为时间在山里过得更快,在平原上过得更慢。我的朋友们,这可不是童话:在离地心越近的地方,时间确实走得越慢。

简而言之,这本书解释了我们所知道的时间和我们已经学会理解的时间是不存在的。什么是真正的时间。从一个从大学生开始就研究这个课题的专业人士那里读到这一切,让我思考了很多东西。我不会用自我反省之类的想法来烦你,但这本书也让我想到了我们通常在机器学习和数据科学中使用时间的人。因为在他的书里,卡罗说时间本身什么都不是。我们所感知的时间是人类发明的,用来测量地球绕太阳一周需要多少时间。这种测量是通过我们发明的度量来完成的,比如秒、毫秒、小时、分钟等等。

鉴于此,对于一个不知道“分”或“秒”是什么概念,不知道今天是什么日子,更糟糕的是,不知道什么是“日”或“月”的人来说,说现在是晚上 22:48 根本不能说明什么。我知道你在想什么:那种人根本不存在。好吧,即使这是真的,猜猜谁不知道这些:你的机器学习算法。因此,我们处理时间数据并将其输入模型的方式是关键。

对于所有好奇的人来说,首先我要花几段时间对这本书做一个总结。如果你有足够的兴趣,请继续阅读。否则,你可以跳过它。然而,如果你读了它,你想了解更多,这是卡尔罗·罗威利 8 年前的 Ted 演讲,这本书在亚马逊只有大约 6 磅。

正如我之前所说,这本书指出我们所知的时间概念并不存在。我们用来衡量它的标准。它们不存在,因为在物理世界里它们不存在。世界的基本方程没有时间变量。确实存在的是一个物理时间,它的流逝不是均匀的,而是取决于许多因素。

这个物理时间似乎不是一个连续的时间,而是一个颗粒时间,在两个值之间跳跃,而不是流逝。一个特定值下的时间概念并不存在于它更基本的时间概念中。根据对物理时间的理解,在微观层面上,时间是由碰撞的原子组成的。分散在概率云中的原子,我们没有精确的方法知道它们会出现在哪里。它们出现了,并在概率足够高的地方发生碰撞。但是当它真的发生时,宇宙的那一部分会从某种特定的基本结构变成更复杂的结构。然而,这些允许那种微观事件的特殊结构,明确地依赖于我们对世界的模糊或未聚焦的视觉。但是这种不集中的视觉并不依赖于我们可能有的一些精神感知,相反,它是一种相对的属性。它是一个对象相对于另一个对象的属性。时间性的概念与这个模糊或不清晰的世界紧密相连,我们忽略了它最微观的细节。因此,这个物理时间最终是我们对世界无知的表现。

关于最后一个观点,理解它很有帮助,这一章谈到了一个事实,即猫不是宇宙元素的一部分。比这更复杂的东西,不断出现在我们星球的几个地方。同样的,在山顶,我们看到一个被蓝色的白云覆盖的山谷。如果我们爬山,当我们爬山时,云不再是白色的;我们发现自己在雾中。但这种变化不是瞬间的,而是渐进的。任何表面都会发生同样的情况。如果我们能把自己变得足够小,大理石桌子看起来就会像雾一样。在这些例子中,一个真实的东西出现了,一些我们能看到甚至有时能触摸到的东西。他们来自一个最基本的没有猫的世界。同理,时间产生于一个没有时间的世界。

这本书说,没有时间的世界只不过是一个相互关联的事件网络。没有时间并不意味着它是静止不动的。这意味着事情的发生不是按照线性时间线来排序的。如果到了时间,我们只理解发生的事情,那么一切都是时间。但同时,以我的理解,没有什么是太。或者至少,没有什么是过去,也没有什么是未来。在一个比我们所知和理解的要大得多的宇宙中,我们只是相互关联的事件中的一小部分。

我们所理解的过去和未来就在我们自己心中。我们是生活在我们大脑中的故事。从原子不断碰撞的足迹中画出的线条。允许我们预测未来事件的线。举个例子,一个扔给我的球朝哪个方向去,如何移动我们的手去抓住它。这个空间,我们的记忆,以及持续不断的预期实践,是我们对时间和自身感知的来源。

处理数据集中的时间要素

现在,正如我之前所说的,除了所有的自我反思和深刻的想法,所有这些都可以引发,让我们把它带到机器学习的世界。我们确实有预测事物的记忆,比如上面那个球的例子。我们确实知道一月之后是二月,去年是 2019 年,或者晚上 23:59 之后是哪一分钟。此外,就行业而言,我们的记忆让我们知道,例如,春天过后就是夏天,如果我在一家销售啤酒的公司工作,夏天对我们来说可能是一个季节,因此,我们应该会看到销售额的增长。在我工作的 Ravelin,我们可能知道对于某些行业来说,一天中的某些时间比其他时间更具欺骗性。然而,我们的机器学习模型并不知道所有这些。即使我们以一种一次性编码的方式输入一年中的月份,算法也不知道 12 月之后是 1 月。或者午夜过后,整点又从零开始。

那么我们应该如何对待时间戳呢?嗯,正如我不久前在一个 Stackoverflow 问题中读到的:

你并没有真正使用时间戳作为特征,因为它们在对看不见的数据进行分类时没有用。想象一下,用 2018 年获得的数据训练一个模型,并尝试对 2019 年的数据进行分类。信息不在日期上,而在其他特性的值上!

你能看出卡洛在他的书里所说的相似之处吗?在现实生活中,时间本身只不过是我们记忆中可能存在的给它提供上下文的参考,我们的机器学习模型也是如此。我们的时间特征只有在与一些其他日期相关时才有价值,这些日期可能与我们试图预测的任何事情相关。

根据这一点,以下值对于机器学习模型没有任何意义:

  • 日期时间如:2019–12–24t 10:25:11.577 z,
  • 或者像 1578830817 这样的时间戳

我们如何将这转化为对我们的算法有用的东西?首先,我们可以做一些简单的转换,让我们的模型更好地参考如何比较不同日期或时间戳发生的行或事件。例如,我们可以获得:

  • 一年中的某月
  • 一年中的某一天
  • 一周中的某一天
  • 一天中的某个小时
  • 一天中的分钟
  • 小时分钟

Pandas 为我们提供了一个整洁的表格,其中包含许多选项,来自于其关于时间序列/日期功能的文档:

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

通过这样做,我们的模型将知道八月,一年中的第八个月,在七月之后九月之前。或者在 2020 年,5 月 9 日在 1 月 30 日之后 100 天,8 月 17 日之前 100 天。

我们甚至可以更进一步,做一些其他种类的事情。例如,我们可能知道圣诞节对我们的业务来说是一个合理的日期,事实上,我们的模型本身甚至可以发现像“2019–12–24 08:57:04.927”这样的日期在预测时会发生一些特殊/怪异/奇特的事情。但是,模型如何知道某个其他日期时间恰好在该日期时间的一天或二十四小时之后?我们需要让算法更容易理解这一点。为此,我们可以设置如下功能:

  • 是圣诞节吗
  • 自圣诞节以来的天数
  • 圣诞节以来的几周

或者,如果我们是一家在线快递公司,并且我们知道我们一周中销售最强劲的时刻是周五晚上 20:30,我们可以创建如下功能:

  • 离高峰还有几分钟
  • 自高峰以来的分钟数

再一次,我们试图给我们的机器学习模型同样的背景,这要归功于我们的记忆和我们对时间的理解。然而,我知道你可能会想:好吧,酷,但是一旦我创建了这些特性,我该如何处理它们呢?因为我们会把时间戳转换成分类特征。因为我们在我之前的一个关于分类变量编码的故事中提到过,如果我们没有正确地处理这种特征,一些算法就不会特别喜欢处理这种特征。那我们该怎么办?

嗯,如前所述,我们不想做任何一次性编码或类似转换。如果我们这样做,我们将会失去特征连续性的任何价值。换句话说,该模型将停止识别诸如一年中的第六个月,即 6 月,在 5 月之后 7 月之前这样的事情。然而,让它们保持原样提出了另一个挑战:模型如何知道一旦 12 月结束,1 月又来了?或者在一天的第 23 个小时之后,它又从零开始了?

幸运的是,有一个非常简单的方法来解决这个问题:我们可以使用正弦和余弦变换,将每个变量转换为二维特征。我们来看一个例子。

假设您有一个包含“OriginalClientEventTime”功能的数据集。首先,您希望将特征的类型转换为熊猫日期时间,以便随后获得一天中的小时。您可以这样做:

train[‘OriginalClientEventTime’] = pd.to_datetime(train[‘OriginalClientEventTime’])train[‘hour’] = [x.hour for x in train[‘OriginalClientEventTime’]]

以类似的方式,您可以从 Panda 关于时间序列/日期功能的文档中获得上表中列出的任何属性。

一旦我们这样做了,这些功能将如下所示:

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

现在我们有了一天中的时间,我们可以将它转换成二维特征,以便模型能够获得原始的性质。我们只用两行代码就可以轻松做到这一点:

import numpy as nptrain[‘sin_hour’] = np.sin(2*np.pi*train.hour/24)train[‘cos_hour’] = np.cos(2*np.pi*train.hour/24)

这是我们的数据集现在使用新功能的情况:

train[[’ originalclienteventime ‘,’ hour ‘,’ sin_hour ‘,’ cos_hour’]]

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

如果我们单独绘制正弦或余弦特征,我们不会注意到太多,但这是因为我们需要两个变量来捕捉特征的适当性质。现在看看当我们在 Y 轴和 X 轴上同时绘制两个特征时会发生什么:

train.plot.scatter(‘sin_hour’,’cos_hour’)

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

整洁,对不对?我们所知道的时间行为似乎反映在一个完美的圆圈中,这个圆圈在一天中的小时值之间循环。我们可以对任何其他时间特性做同样的事情!一年中的月份、一天中的分钟、一年中的某一天、圣诞节前的天数等。

正如我在评论罗维利的书时告诉我女朋友的祖父的那样,书中肯定有很多我不理解的东西。但我确实明白,如果我们能以某种方式记住过去的事情,并预测未来的事情,比如我应该把手移到哪里才能接住别人扔给我的球,这要归功于我们的记忆和我们建立的时间概念。一个由度量组成的时间概念,仅描述事件之间的距离,例如地球绕太阳旋转一周需要多长时间(顺便说一下,是 365.256 天)。

因此,如果我们想让我们的机器学习模型真正学习,让我们试着教它们我们自己随着时间的推移学到的东西。

好了,今天就到这里。请继续关注未来的故事,了解处理分类特征的其他技术。同时,这一年刚刚开始,你会在 2020 年找工作吗?好吧,那么你不能错过我关于获得第一份数据工作的实用技巧的故事。或者你还在寻找给自己的完美圣诞礼物?那就来看看这个关于的故事 9 本数据科学相关书籍来读读。如果这些都不是你的情况,请随时访问我在 Medium 上的个人资料,查看我的任何其他故事。

还有如果你想直接在你的邮箱里收到我的最新文章,只需 订阅我的简讯 😃

回头见!感谢您的阅读!

不是数据科学家也没关系

原文:https://towardsdatascience.com/its-okay-to-not-be-a-data-scientist-e57b8800e62c?source=collection_archive---------59-----------------------

似乎每个人都想成为数据科学家——在数据领域,有其他途径可以成就伟大的事业

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

卢卡斯·布拉塞克在 Unsplash 上的照片

在过去的几年里,成为一名数据科学家被大肆宣传。Glassdoor 连续三年将它列为美国第一最好的工作,并且不难发现博客帖子谈论成为一名数据科学家有多棒。因此,毫不奇怪,主要数据科学分支似乎主要是人们询问如何成为数据科学家的建议,而不是关于数据科学的建议。

对于我看到的(以及之前给出的)许多建议,我一直感到很矛盾,我担心这些建议会对人们造成伤害。外面的许多建议(以及某些在线课程/训练营的广告)听起来好像成为一名数据科学家唯一需要的是一些技术技能,所以毫不奇怪会有像这样的问题问为什么数据分析师和数据科学家之间的工资差距如此之大,如果他们需要的技术技能列表是相同的。

为什么不是每个人都是数据科学家?

事实是,作为一名数据科学家,除了学习如何导入 sklearn,还有更多事情要做。我必须小心行事。我不想被视为看门人。但我认为,任何人都可以做一个机器学习项目,并将其放在 github 上,以获得一份 6 位数收入的工作,这一想法需要一些现实检查。

根据 2017 Burtch Works 的《数据科学家的薪水》报告,约 90%的数据科学家拥有高等学位(约 40%为博士,~50%为硕士)。在这 10%没有研究生学位的人中,我的猜测(我没有这方面的统计数据)是大多数人都有相当高水平的数据分析或软件工程方面的丰富经验。

研究生学历比例大是有原因的。定量学科的研究生学位(尤其是博士学位)表明一个人在探索抽象概念、发展统计关系的直觉、设计检验假设的方法、将数据转化为故事、交流复杂结果等方面有一定的经验。学习 Python 以及如何在 Pandas 中处理数据和在 sklearn 中建立模型可能只需要几个月的时间,但这只是表面的东西。现实世界很复杂,需要你在数学背后有一种直觉,明白什么可行,什么不可行,以及如何证明它可行或不可行。

我并不是说数据科学家需要博士学位——这在经验上是错误的(毕竟 60%的人不需要)。成为数据科学家有许多不同的途径。但是这条路并不容易走。

关于所有这些数据科学训练营和在线课程,要认识到的一点是:对于大多数在他们之后获得数据科学家工作的人来说,他们在这些课程中学到的技能只是锦上添花。如果你有一个定量的博士学位,或者大量的软件工程经验,或者作为一名数据分析师与数据科学家密切合作多年,也许你所需要的只是一个学习如何使用几个 Python 包的短期课程,这样你就有竞争力了。如果你没有,很可能你成为数据科学家的路会更长。

你不一定要成为数据科学家

获得新的技能很有趣,而且可能对你的职业有好处,不管你最终获得什么头衔。学习能让你做喜欢的事情的技能是值得的。如果你目前不是一名分析专家,在线学习或参加数据科学课程肯定有助于获得第一个数据分析师职位。也许如果你是一名目前在高层工作的数据分析师,学习其中的一些技能并将其融入到你的工作中,最终可能会让你在未来获得数据科学的职位。

但是“向上”的方法不止一种。成为一名数据科学家不一定是你的目标。你可以最终走上管理轨道,并最终领导一个数据分析师团队。这可能会导致管理一个大型数据分析团队,甚至有一天成为首席分析官。我的观点是,很多人对“数据科学家”这个头衔有点太着迷了。这是一份好工作,但我认为人们高估了它,低估了它的预期经验水平。有很多很棒的分析工作(也有很多很棒的非分析工作),我认为让一些人把目光从闪亮的、被夸大的“数据科学家”头衔上移开,更客观地判断他们的选择会有好处。

是时候为“冲击美国”做好准备了——全面审视新冠肺炎的数据显示,我们目前的措施是不够的

原文:https://towardsdatascience.com/its-time-to-brace-for-impact-america-a-comprehensive-look-at-covid-19-in-italy-new-york-china-9e899464ef9?source=collection_archive---------23-----------------------

来自意大利、中国湖北省和韩国疫情的数据显示,从实施强有力的抑制措施开始,每天的新增病例数大约需要 11 到 12 天才能达到峰值,然后下降。在每日新增病例达到峰值后,大约需要 2 周时间才能看到活跃病例数达到峰值,然后开始下降。翻译一下——“使曲线变平”需要大约 26 天的时间,在实施严格和全面的措施阻止传播之前,第一天不会开始——我不是在说洗手和站在 6 英尺远的地方。在我们准备好启动并充分利用全球正在使用的两个选项之一(严格封锁,或积极检测和追踪)之前,美国人应该准备好看到病例继续增长。人口越密集的地区——T4 的增长可能越快。在这一后期阶段,许多州可能没有时间让医院做好充分准备,或采购拯救生命和避免第一波疫情中医疗配给所需的救生保护装置和设备。

让我们看看数据,好吗?

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

意大利新冠肺炎 2 月 20 日至 3 月 23 日的新增和活跃病例。活跃病例总数用黄色表示,每天新增病例用橙色表示,死亡病例用黑线表示。未校正的表观死亡率在右边的 Y 轴上用灰色表示。红色垂直线代表意大利实施各种缓解措施的日期,以及医疗配给报告开始的日期。数据来源:https://github.com/CSSEGISandData/COVID-19

在意大利,活跃病例的峰值还没有达到,但 3 月 22 日和 23 日的数据表明,它可能最终会到来。3 月 21 日的数据似乎显示,截至 3 月 21 日,每日新增病例达到峰值。意大利增加了检测能力,从 3 月 24 日到 25 日的数字反映了这一点,但是新病例在 3 月 26 日又开始下降。高峰和低谷可能有一个区域组成部分——旧的爆发达到某种程度的控制,而新爆发的数字上升。

3 月 9 日和 11 日是在全国范围内实施重大减排措施的日子。来自湖北的数据表明,从大规模隔离开始,每天的新增病例可能需要 11-12 天才能达到峰值并开始下降,这将大约在 3 月 23 日或 24 日,数据显示目前情况是这样的。在湖北,每天新增病例数开始下降后,又过了 12 天,活跃病例数才开始下降。如果意大利的抑制措施同样有效,并保持到位,这可能意味着我们将在 4 月 5 日左右看到活跃病例的高峰。按照目前的病毒发展轨迹,在指数增长方程 A = A0ert 中简单应用一个缓慢的增长率(在此之前每天计算),表明到 4 月 5 日,意大利的病例总数可能达到 115,000 例左右,按照目前 9.9%的死亡率,这意味着总死亡人数接近 11,300 例。

一旦意大利超过 3 例确诊病例,他们马上开始有非常快速的指数增长,然而他们对当地疫情的反应也非常快。2 月 22 日,该国伦巴第地区只有 62 例病例,他们选择在伦巴第和威尼托地区实施严格的旅行限制——关闭学校、博物馆、商店和餐馆。第二天,病例激增至 155 例,政府将措施扩大到伦巴第和威尼托所有地区,后来包括皮埃蒙特、利古里亚和艾米利亚-罗马涅。到 17 天后的 3 月 9 日,病例数量飙升至 9172 例,促使政府将隔离范围扩大至全国,直至 4 月 3 日。3 月 11 日,全国关闭了除杂货店和药店以外的所有商店。到那时,伦巴第 80%的医院床位都满了,整个系统已经到了崩溃的边缘。

大约在 3 月 9 日,伦巴第的意大利医院因病人过多而不堪重负的报道开始出现,同时还有关于医院制定战场分流协议的报道。战场伤检分类简单地解释为这样一个原则,即接受救命治疗的病人是最有可能生存和康复的人,而不是最需要医疗的人。意大利的医生们不得不做出可怕的选择,定量分配有限的医疗用品和护理,这决定了 T2 的生死。

中国湖北

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

意大利新冠肺炎 2 月 20 日至 3 月 23 日的新增和活跃病例。活跃病例总数用黄色表示,每天新增病例用橙色表示,死亡病例用黑线表示。未校正的表观死亡率在右边的 Y 轴上用灰色表示。红色垂直线代表意大利实施各种缓解措施的日期,以及医疗配给报告开始的日期。数据来源:https://github.com/CSSEGISandData/COVID-19

湖北的病例高峰出现在 444 例确诊病例后的 27 天左右,中国政府于 1 月 23 日对进出武汉实施了非常严格的限制。1 月 25 日,他们将隔离范围扩大到湖北省的 10 个城市,关闭了除杂货店和药店以外的所有零售商店,并要求人们待在家中,外出购买食物的时间受到限制。2 月 6 日,政府开始对任何有症状的人进行挨家挨户的搜查,这些人被 强行带离家园,带到指定的隔离设施

即使实施了这些非常早期和极端的措施,他们的活跃病例曲线也非常清楚地显示了 11-12 天的滞后时间,直到每天的新病例达到峰值并开始下降。从首次发现该病毒到活跃病例达到高峰,仅持续了 27 天。这意味着湖北在大约 27 天内从 399 例活跃病例增加到 48,510 例。1 月 25 日,当医院被迫拒绝非冠状病毒患者时,首次报道了医疗配给。

2 月份的文章谈到了在高速公路边进行的测试和医院走廊两旁的病人——明确了最坏情况的形象。超过 3300 名医护人员受到感染,许多年轻和中年医生和护士在极端轮班时间和连续工作过多天没有休息以满足前所未有的危急护理需求后,死于疾病和疲惫。

韩国

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

意大利新冠肺炎 2 月 20 日至 3 月 23 日的新增和活跃病例。活跃病例总数用黄色表示,每天新增病例用橙色表示,死亡病例用黑线表示。未校正的表观死亡率在右边的 Y 轴上用灰色表示。红色垂直线代表意大利实施各种缓解措施的日期,以及医疗配给报告开始的日期。数据来源:https://github.com/CSSEGISandData/COVID-19

韩国最初拒绝在以大邱为中心的疫情爆发的主要增长阶段实施地区或国家隔离或旅行限制,担心这些措施在政治上不受欢迎,并让人想起朝鲜的旅行限制——而是选择建立积极的接触者追踪和检测,并对患者进行隔离。一旦检测结果呈阳性,政府就会积极寻求所有可能的接触者进行检测——包括患者认识的任何接触过的人,以及与此人的信用卡历史和监控录像相关的任何人。如果检测呈阳性,他们将免除检测费用,并继续对潜在疫情进行积极监测。

截至 3 月 17 日,该国已检测了超过 28 万人,相当于每百万人中有超过 5500 次检测。截至 3 月 10 日,该国出现了全国性的社区传播,春川市 Hallym 大学医学院教授 Kim Dong-hyun 博士表示,该市和该国很快将达到一个临界点,即没有足够的流行病学调查人员来有效追踪新病例,“在这一点上,追踪感染如何传播是没有意义的”。韩国公共卫生医生协会(Korean Association of Public Health Doctors)会长 Kim Hyeonggab 表示,截至 3 月 7 日,通过关闭学校、取消音乐会、确保更多医院床位以及“建立分流系统”,工作重点明显从遏制转向缓解。

与湖北相比,韩国最初的疫情总体数字较低,但与湖北类似,活跃病例的峰值仅出现在指数增长开始后的 26 天。这意味着该国在 26 天内从 399 例活跃病例增加到 7577 例。第一份关于医疗配给的报告来自大邱,超过 75%的病例集中在那里。此时,2 名冠状病毒患者在家中等待床位时死亡。根据 Kim Dong-hyun 博士的说法,虽然通过积极的检测和对病人的隔离来分离病毒对控制疫情是有效的,但全国范围内广泛的社区传播的发展很可能导致未来几周活跃病例的重新出现。截至 3 月 23 日,看起来积极的追踪可能导致一种形式的阶段性感染。

纽约

现在让我们来看看纽约州与意大利、中国和韩国相比的活跃案例的增长情况。纽约州是美国活跃病例指数曲线上进展最快的州,也是获得检测机会最多的州。我现在就告诉你,你不会喜欢的。

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

纽约和意大利新冠肺炎的活动案例(左侧 y 轴)和从那时起每天的新案例(右侧 y 轴)各有 400 多个记录在案的案例,这些案例用图表显示在一起,以便进行粗略的关联。纽约的数据跨度为 3 月 13 日至 4 月 2 日,意大利的数据跨度为 2 月 26 日至 3 月 20 日。纽约每天的活动案例总数用红色表示,意大利的案例用黄色表示。数据来源:【https://github.com/CSSEGISandData/COVID-19

第一天代表每个地点大约有 400 多个病例的第一天,从那时起病例呈指数级增长。第 1 天和第 21 天显示的每个位置的实际数据日期。截至本文撰写之时(2020 年 3 月 24 日),纽约已有 400 个案例的数据。黄色条表示意大利每天的活动案例。红色条表示纽约每天的活动案例。黄线表示意大利每天的新增病例,红线表示纽约每天的新增病例。以下两个图表显示了纽约和湖北的数据,湖北用蓝色表示,纽约和韩国的数据用深蓝色表示。

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

纽约州和湖北省新冠肺炎市的活动案例(左侧 y 轴)和自时间以来的每日新案例(右侧 y 轴)各有 400 多个记录在案的案例,这些案例用图表显示在一起,以便进行粗略的关联。纽约数据跨度为 3 月 13 日至 4 月 2 日,湖北数据跨度为 1 月 22 日至 2 月 11 日。纽约每天的总活动案例以红色显示,湖北的案例以蓝色显示。数据来源:【https://github.com/CSSEGISandData/COVID-19

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

纽约州和南韩新冠肺炎的活动案例(左侧 y 轴)和自时间以来每天的新案例(右侧 y 轴)各有 400 多个记录在案的案例,这些案例用图表显示在一起,以便进行粗略的关联。纽约数据从 3 月 13 日持续到 4 月 2 日,韩国数据从 2 月 22 日持续到 3 月 14 日。纽约每天的活动和新病例总数用红色表示,韩国的病例用深蓝色表示。数据来源:https://github.com/CSSEGISandData/COVID-19

从这些图表的第 1 天到第 12 天,纽约每天新增病例数远远超过湖北、南韩和义大利。纽约州目前进行的人均测试是美国最多的,州长科莫估计他们每百万人的测试次数高于韩国——韩国在 3 月 17 日测试了大约每百万人 5500 次。这使得它成为试图预测影响的唯一可靠数据集。该州在短短 12 天内从 421 起案件增加到 26105 起。

州长一直等到他们有超过 700 个案例关闭学校,8310 个案例关闭不必要的企业。居民们仍然被告知这不是一个就地避难的命令。他们使用意大利 3 月 25 日开始的增长率预测的数据估计,到 4 月 3 日,该州将有超过 150,000 例病例(见下图)。然而,纽约目前的活跃病例数和每天新增病例数都比意大利高得多,意大利在 12 天内就有 400 例。如果纽约州预计在 45 天内需要 37,000 个 ICU 床位,正如州长在 3 月 18 日报告的那样,15%的新冠肺炎病例需要紧急护理,那么纽约州预计在 45 天内大约有 250,000 个病例。这几乎是全球疫情中心湖北省病例数的 4 倍。州长表示,纽约目前在整个州有 3000 个 ICU 床位,其中 80%目前已被占用。

茶叶建模

以下数字并不意味着实际预测未来的确切病例,而是根据意大利的增长率在黑暗中尝试一种可能的情况——尽管直到 3 月 24 日,纽约的增长率要高得多。纽约的数据用红色绘制,条形表示已报告的活跃病例,直线表示增长率。纽约的建模数据以粉色条表示从 3 月 25 日到 4 月 3 日的未来几天。

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

来自纽约州、意大利、湖北和韩国的活动案例以多色条显示(纽约-红色/粉色、意大利-黄色、湖北-浅蓝色、韩国-深蓝色),对应于左侧 y 轴(0 到 160,000 个案例)。对于尚未报告的数据,纽约的建模数据显示为粉红色。意大利(黄色)和纽约(红色)的增长率以直线绘制,对应于右侧的 y 轴(0-1.4)。增长率是指数增长方程 A=A0ert 中的变量 r。彩色竖线表示各国医院被报道不堪重负的日期。数据来源:https://github.com/CSSEGISandData/COVID-19

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

意大利新冠肺炎 2 月 20 日至 3 月 23 日的新增和活跃病例。活跃病例总数用黄色表示,每天新增病例用橙色表示,死亡病例用黑线表示。未校正的表观死亡率在右边的 Y 轴上用灰色表示。红色垂直线代表意大利实施各种缓解措施的日期,以及医疗配给报告开始的日期。数据来源:【https://github.com/CSSEGISandData/COVID-19

意大利新冠肺炎 2 月 20 日至 3 月 23 日的新增和活跃病例。活跃病例总数用黄色表示,每天新增病例用橙色表示,死亡病例用黑线表示。未校正的表观死亡率在右边的 Y 轴上用灰色表示。红色垂直线代表意大利实施各种缓解措施的日期,以及医疗配给报告开始的日期。数据来源:【https://github.com/CSSEGISandData/COVID-19

如果来自中国和意大利的数据有任何意义的话,美国测试的最新发展和纽约缓解措施的最新应用已经导致了令人难以置信的感染率,这将对纽约应对严重病例的能力产生毁灭性影响,因为该市正在走向其活跃病例数的峰值。

怎么强调都不为过——中国、韩国和意大利迅速做出反应,实施了广泛的遏制措施。所采用的措施采用了两种不同的策略,但都是积极的。韩国对所有确诊病例进行了极端检测、追踪和隔离。在湖北省,他们实施了全面的旅行限制,停止了所有的公共交通,关闭了除杂货店和药店之外的所有企业,命令人们呆在家里,并将生病的公民转移到隔离设施 *。*意大利也实施了公共关闭,关闭学校和非必要的企业,命令公民留在家中。州长科莫可能试图复制韩国控制病毒的明显成功的测试计划,但除了高人均测试外,该县还隔离了所有患者,并进行了远比美国合法或可执行的更严格的接触者追踪。

糟糕的数据意味着我们真的不知道真实的范围

令人心碎的是,纽约也加入了意大利的行列,成为一个警示故事,但在这一点上,数据显示它确实如此。我们非常确定冠状病毒就在这里,它像野火一样在每个社区传播。由于缺乏测试,我们没有准确的国家数据来真正掌握截至 3 月 24 日新冠肺炎在美国纽约州以外的规模。病例的指数增长速度,加上缺乏检测途径和检测处理滞后,意味着报告的病例实际上远远高于报告的病例,因为不管我们确认与否,未受抑制的增长仍在发生。例如,一周多前,田纳西州默弗里斯伯勒一个呼叫中心的 1500 名员工可能暴露于某个有症状确诊病例的人的病毒。这意味着暴露可能在阳性病例出现症状前一周或更长时间就开始了。只有一两个呼叫中心的其他员工接受了测试,与此同时,那里的所有员工都在继续他们的日常工作——与家人互动、购物、去教堂做礼拜。据估计,韩国一名妇女通过她的教堂与 1100 多人接触,引发了大邱的严重疫情。你能想象呼叫中心地区真正爆发的可能范围吗?然而,像大多数其他州一样,只有最严重的病例在田纳西州接受测试。底线是,我们无法确切知道有多少人实际上被感染了,而这一明显较低的病例数可能会诱使我们放慢行动。

领导层未能迅速果断地采取行动

面临的主要问题是领导层决策的滞后,这些决策有效地限制了传播,并解决了未来对重症监护能力的需求。在这一点上,美国已经发布公共留在家中命令的各州在达到伦巴第或湖北政府封锁这些地区时的病例数量后很久才发布命令,有些州根本没有发布命令。不,其他州不太可能有像纽约一样多的病例——毫无疑问,由于人口密度和控制延误,他们可能是世界上受打击最严重的州。然而,如果不及早采取措施来减缓在 T2 出现的指数式增长,世界各地的每一次爆发都会在中国的每一个主要人口中心上演类似的场景。农村人口不会被排除在外——虽然人口密度降低和暴露程度降低可能导致农村总体病例减少,但农村医院普遍关闭将导致这些人口难以获得足够的护理。

对于像先发制人的社会隔离这样的遏制措施的缓慢实施,以及像建设额外的紧急 ICU 容量和使用生产法案来帮助解决预期的防护设备、呼吸机和呼吸器的短缺这样的缓解措施,这是一个非常值得警惕的故事。如果美国要实施这项法律来防止成千上万不必要的死亡,那么当我们看到意大利不堪重负的医院系统的影响时,就应该迅速果断地实施。改变制造设备和获得供应材料的时间不会在一夜之间发生,虽然它目前甚至不在桌面上,但对第一波流行病来说可能已经太晚了。

总统没有迅速采取行动允许私人测试开发,结果美国测试系统仍然被堵塞,严重不足。他没有发起足够强大的全国性回应来阻止传播,而是继续助长混乱和不和发表声明和政策,淡化世界各地顶级医疗顾问统一给出的建议。从所有明显的证据来看,各州没有实施任何有意义的策略来为即将到来的危急情况做准备,而是独自处理阻止传播和照顾病人的问题。迄今为止,这导致了不同的反应速度和严格程度。

美国的医院短缺会是什么样子?

《纽约时报》发表了一份由哈佛大学哈佛全球健康研究所制作的分析报告,下图显示了如果不扩大目前的容量,医院短缺的规模。根据分析,如果 40%的成年人在未来 12 个月内被感染,那么 40%的医院市场将无法扩大足够的能力来治疗新冠肺炎患者。进行分析的团队称 40%的感染率为中度,但他们的估计表明感染率可能会高得多,甚至超过医院通过取消选择性手术和让不太危急的病人回家等措施可以产生的过剩能力。“如果我们不在长期传播疾病和扩大容量方面做出实质性改变,我们将耗尽医院的床位,”哈佛全球卫生研究所所长 Ashish Jha 博士说。在意大利,这导致了前面提到的医疗保健配给,如果医生被迫制定战场分流协议,将对弱势群体和老年人以及患有其他疾病的患者产生影响。

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

图片来源:https://www . nytimes . com/interactive/2020/03/17/upshot/hospital-bed-abstraits-coronavirus . html

来自其他状态的数据

以下是 11 个州的表格,摘自约翰霍普金斯大学在 github source 上停止按州报告数据之前 3 月 23 日的美国总数据。该表显示了每天的活跃案件,以及各州或其他市颁布的抑制措施。浅蓝色单元格显示的是湖北省发布停业和居家禁止令的最接近案例数(444),浅黄色单元格显示的是伦巴第发布停业和居家禁止令的案例数(62)。亮蓝色表示全州范围的居家命令,橙色表示仅选择主要城市和/或县发布居家命令,浅绿色表示较弱的命令,仅建议但不要求居民居家。该表还假设实际上已知活动案例的数量,然而事实远非如此。正如你所看到的,许多州直到比意大利或中国更深入地了解了其新生的流行病后才颁布这些控制措施。

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

11 个州的现行案例表,列出了颁布抑制措施的日期,并与湖北和意大利颁布居家禁止令和非必要停业的案例数进行了比较。数据来源:【https://coronavirus.jhu.edu/map.html

以下三个表格显示了每天计算的死亡率、生长率和倍增时间,试图确定一种趋势。死亡率就是死亡人数除以总病例数。死亡率通常是追溯计算的,因为在任何给定的时间,我们可能不知道总病例的实际数目。迄今为止,这些州中的许多州死亡率都很低,其中佐治亚州、路易斯安那州和华盛顿州最高。虽然病例总数仍然相对较低,而且在检测的前几周内,但应谨慎对待这些数字。它们也可能受到其他因素的严重影响,例如疫情是否发生在更脆弱的人群中——就像华盛顿州疗养院疫情那样。

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

各州未校正的表观死亡率表,按死亡数/总病例数计算。原始数据来源:【https://coronavirus.jhu.edu/map.html】T4。由于缺乏测试来了解以脆弱人群为中心的病例和疫情的实际总数,死亡率可能会更高。

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

根据等式 A=A0ert 每天计算的增长率表,其中 A0 是一天的病例总数,A 是第二天的病例总数,e 是欧拉数,t 是时间。原始数据来源:https://coronavirus.jhu.edu/map.html

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

根据等式 A=A0ert 每天计算的增长率表,其中 A0 是一天的病例总数,A 是第二天的病例总数,e 是欧拉数,t 是时间。原始数据来源:https://coronavirus.jhu.edu/map.html

显示增长率的第二个表格试图捕捉数字增长的速度。增长率是基本指数增长计算中的变量“r”。每天使用前一天(A0)到当天(A)的总病例数计算这些值。在意大利和湖北的数据中,高于 0.2 的数字出现在快速指数增长期间,而低于 0.1 的数字出现在这些地区经历新病例快速下降时。

最后,最后一个表格显示了一些文章引用的一个数字,即倍增时间。翻倍时间是总病例数以当前增长率翻倍之前的天数。这是通过等式 ln(2)r 计算的,其中 r 是增长率。倍增时间是一种更直观的衡量流行病增长速度的方法——人类可以用天数来表示,而增长率是一个更抽象的数字。该表显示,倍增时间每天都在变化,但仍然相对较低,倍增时间越短意味着病例增长越快。这些数字对任何的持续限制是缺乏全面的测试。缺乏测试可能会导致重大错误,即低估了一个地区正在经历的实际增长和翻倍时间*。

曲线变平后会发生什么?

伦敦帝国理工学院新冠肺炎反应小组发布的一份报告称,如果像居家命令和企业关闭这样的抑制措施在短期内成功,但没有有效和广泛使用的疫苗,这些抑制措施的放松将导致今年晚些时候出现相同或更大的疫情,因为“群体免疫”的建立不足。群体免疫是对传染病在人群中传播的抵抗力,如果足够高比例的个体具有免疫力,就会产生这种抵抗力。

这对领导人来说意味着,抑制新冠肺炎的传播需要通过社会措施来管理,要么 1)持续保持呆在家里的命令,2)像韩国那样组织一个资源充足、100%充分的检测、追踪和隔离计划,或者 3)制定一个间歇性社会距离的有意循环。为了实现这一目标,我们有意放松抑制传播的居家和企业关闭令,以允许控制感染模式*,这种模式永远不会超过我们通过充分的医疗护理有效应对的能力*,直到疫苗可用。在这 3 个中,迄今为止只有前 2 个被建模。

对大多数美国人来说,接下来的几天将是暴跌前的深呼吸。截至 3 月 24 日的数据显示,对大多数州来说,要安然无恙地度过这一阶段为时已晚。但是数据显示,可能需要 27 天左右的时间才能使曲线变平,因为 T2 发布了强劲的留在国内订单,并且保持在 T3。成千上万人的生命——包括我们医疗服务提供者的生命——现在取决于国家领导人在未来几天是否会响应日益增长的领导呼声。

为了保护自己的公民,每个国家都必须颁布以下措施:

1) 立即关闭非必要业务。

立即发布并执行留在家中的命令。

3) 与该州的当地医院领导合作,构建一个现实的模型来计算可能需要的医院容量和达到需求峰值的时间。

4) 协助医院为提供者获取救生急救设备和 PPE。

5) 与州医疗和/或医院协会合作,为即将到来的医疗提供者和肺病专家的短缺做好准备。

6) 帮助重要企业获得必要的个人防护设备(PPE)以保证员工的安全。

a. 建议杂货商、药店、饲料商店和其他重要企业现在就开始准备个人防护设备,并制定在疫情高峰期开展工作的协议。

b. 考虑发布一份与美国疾病预防控制中心或州医疗领导指南合作制作的标准化指南。

启动国民警卫队:

a. 根据能力不足的情况,开始为急病患者建造临时医院或准备现有医院,包括将服务扩展到农村地区。

一旦测试变得广泛可用,将有助于减轻医院的压力。随着病例的增加,需求将会显著增加。

c. 帮助高危人群获取食物和药品。

d. 在有疫苗时,帮助接种疫苗

8) 大幅提高信息热线的现场呼叫能力,推送以下方面的最新详细信息:

**答:**去哪里做测试

b. 国家颁布的现行指南

c. 关于人们在哪里可以获得食物、药品、宠物用品和儿童用品等必需品的信息。

9) 与商会和其他商业实体合作:

a. 鼓励对长期远程业务运营进行物流规划,以尽可能保持商业运作。这将减少因疾病导致的生产力损失和因检疫限制导致的运营能力下降,并有助于保持该州的经济运行。企业在规划中花费的准备时间越多,这种转变就会越顺利。这是促进快速和广泛创新,阻止一切照旧的时候了。

b. 鼓励电信提供商迅速满足对额外网络和远程通信能力的需求。

10) 制定一个短期和长期计划,为州居民提供财政救济,使他们能够:

维持住房和水电等基本服务。

b. 购买基本商品,如食品、个人和儿童护理用品、药品和动物饲料。

11) 制定一个现实的、分阶段的长期计划,以指导企业、学区和公众放松社会封锁指导方针,避免连续爆发疫情,并根据该计划继续执行封锁和居家命令。

行动号召

此时此刻,我向美国的每一个人发出这一呼吁。各级领导——做你被选举出来要做的事情,并领导别人。不要再等着看问题在你那里显现了——数据已经被很好地确定了,医学界正在用一个声音说话。越早实施全面的控制措施,病例高峰就越早到来。

公民们,我现在呼吁成为人类的代言人——是时候让人们停止坐以待毙,从你们在这个系统中的任何地方开始带头了。像淹没卫生纸通道一样,给当地媒体、城市、县、州和联邦代表打电话——要求他们发布居家订单,并提出快速和全面的解决方案,以满足现在对医疗、金融和物流解决方案的需求。为了你的家人、你的社区和你的国家的健康。潮水退去时,我们正坐在沙滩上——很快,潮水就会淹没我们。现在的行动可以拯救无数的生命。我强烈要求你服用它们。

是时候改变维度了

原文:https://towardsdatascience.com/its-time-to-change-dimensions-be3302893dd6?source=collection_archive---------33-----------------------

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

安东尼·加兰在 Unsplash 上的照片

任何事物都有一个季节,每个目的都有一个时间…让我们用一个缓慢变化的维度(SCD)来记录这些变化。

缓变维度是什么?这是一个随着时间逐渐变化的表,就像一组当选的官员。假设我们将下面一组美国参议员加载到数据库中。

https://the United States . io/congress-立法者/立法者-current.csv

尽管许多人已经在位很长时间,但有些人即将改变。根据我们的宪法第一条,第三节,第二款,这些参议员的三分之一将受到改变。

[合众国参议院]在第一次选举结果产生后,应立即尽可能平等地分成三个等级。第一等参议员的席位应在第二年期满时空出,第二等参议员的席位在第四年期满时空出,第三等参议员的席位在第六年期满时空出,以便每隔一年选出三分之一的参议员

有许多方法可以实现渐变维度。保持一个简单的模式可以简化你决定如何改变的过程。

确定标识唯一行的列以及要跟踪的列。

例如,我希望每个州和每个阶层都有一个独特的参议员。州参议员没有阶级重叠,因此在一个特定的州的选举周期中只有一个人可以参加选举。目前,格鲁吉亚是个例外,但我们不会深入探讨。

假设我们已经将上述 CSV 文件的原始数据存放到一个表中。为了将原始数据处理到当前参议院的表中,我们将行插入到一个维度表中。

注意,我已经将惟一的行标识为我的 MATCH_GUID,将 CHANGE_HASH 标识为 H as 值。我使用散列来最小化 SQL 上的 CPU 处理时间。

MATCH_GUID 对行中的所有更改进行编码,如果有任何更改,它将使用新记录更新维度。

insert into OWLMTN.WAREHOUSE.DIM_CONGRESS_SEAT(CONGRESS_SEAT_GUID,
                                               LAST_NAME,
                                               FIRST_NAME,
                                               FULL_NAME,
                                               BIRTHDAY,
                                               GENDER,
                                               TYPE,
                                               STATE,
                                               DISTRICT,
                                               SENATE_CLASS,
                                               PARTY,
                                               ADDRESS,
                                               PHONE,
                                               CONTACT_FORM,
                                               DW_MATCH_HASH)
with source as (
    SELECT hash(STATE,
                SENATE_CLASS)    MATCH_GUID,
           LAST_NAME,
           FIRST_NAME,
           FULL_NAME,
           BIRTHDAY,
           GENDER,
           TYPE,
           STATE,
           DISTRICT,
           SENATE_CLASS,
           PARTY,
           ADDRESS,
           PHONE,
           CONTACT_FORM,

           hash(
                   LAST_NAME,
                   FIRST_NAME,
                   FULL_NAME,
                   BIRTHDAY,
                   GENDER,
                   TYPE,
                   DISTRICT,
                   PARTY,
                   ADDRESS,
                   PHONE,
                   CONTACT_FORM) CHANGE_HASH
    from OWLMTN.STAGE.CONGRESS_MEMBERS sor
    where type = 'sen'
),
     target as (
         select CONGRESS_SEAT_GUID,
                DW_MATCH_HASH match_hash
         from OWLMTN.WAREHOUSE.DIM_CONGRESS_SEAT tar
         where DW_ACTIVE = 'T'
     )
select source.MATCH_GUID,
       source.LAST_NAME,
       source.FIRST_NAME,
       source.FULL_NAME,
       source.BIRTHDAY,
       source.GENDER,
       source.TYPE,
       source.STATE,
       source.DISTRICT,
       source.SENATE_CLASS,
       source.PARTY,
       source.ADDRESS,
       source.PHONE,
       source.CONTACT_FORM,
       source.CHANGE_HASH
from source
         left outer join target
                         on target.CONGRESS_SEAT_GUID = source.MATCH_GUID
where target.match_hash is null
   or target.match_hash <> source.CHANGE_HASH;

更新查询将找到任何新行,并将它们标记为活动的。

-- Arrange the dimensions on the SCD.
update OWLMTN.WAREHOUSE.DIM_CONGRESS_SEAT new_data
    set new_data.DW_ACTIVE = update_logic.DW_ACTIVE,
        new_data.DW_FROM_DATE = update_logic.new_from_dw_date,
        new_data.DW_TO_DATE = update_logic.new_to_dw_date,
        DW_UPDATE_DATE = current_timestamp()
from (

    with updated_gui as (
        select DIM_CONGRESS_SEAT_KEY, CONGRESS_SEAT_GUID
        from OWLMTN.WAREHOUSE.DIM_CONGRESS_SEAT
        where dw_from_date is null
        )
    select current_row.DIM_CONGRESS_SEAT_KEY,
           current_row.dw_active as old_active,
           case when current_row.DW_FROM_DATE is null
               then TRUE
               else FALSE end as DW_ACTIVE,

           current_row.DW_FROM_DATE,
            case when current_row.DW_FROM_DATE is null
               then CURRENT_TIMESTAMP()
               else current_row.DW_FROM_DATE end as new_from_dw_date,

           current_row.DW_TO_DATE,

           case when current_row.DW_FROM_DATE is null
                 then to_timestamp_ltz('2099-12-31 00:00:00')
               else CURRENT_TIMESTAMP() end as new_to_dw_date,
           current_row.DW_MATCH_HASH
        from updated_gui
        inner join OWLMTN.WAREHOUSE.DIM_CONGRESS_SEAT current_row
            on updated_gui.CONGRESS_SEAT_GUID = current_row.CONGRESS_SEAT_GUID
                   and (DW_FROM_DATE is NULL or current_row.DW_ACTIVE=TRUE)
        left outer join OWLMTN.WAREHOUSE.DIM_CONGRESS_SEAT old
            on current_row.CONGRESS_SEAT_GUID = old.CONGRESS_SEAT_GUID
                   and old.dw_ACTIVE

      ) update_logic
where new_data.DIM_CONGRESS_SEAT_KEY = update_logic.DIM_CONGRESS_SEAT_KEY;

那么 11 月 3 日之后会发生什么,座位状态有变化?让我们追踪一下我最喜欢的科罗拉多州的选举,假设一下席位的变化。

根据 Azri.us 上的数据预测,希肯卢珀比目前占据席位的加德纳多 10 个百分点的优势。

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

图片由 Google GeoChart 基于 538 个原始数据制作

查看前面的插入查询中的散列比较,我们看到 MATCH_GUID 保持不变(相同的席位),但是 CHANGE_HASH 发生了变化(新参议员)。

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

SCD 哈希匹配策略

在 insert 检测到一个已更改的记录后,它还不是活动的,我们需要以一种事务方式激活该记录,当新行替换旧记录时,将旧记录设置为非活动的。

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

运行移动当前活动行上的日期的查询会将非活动行移动到活动行。

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

对于给定时间点的任何查询,声明为 unique 的项将始终返回一行。

如果我想知道在特定时间谁是科罗拉多州 2 级参议员,我可以指定时间,它永远不会改变。

请注意,DW_FROM_DATE 小于该日期,而 DW_TO_DATE 大于或等于该日期。

select CONGRESS_SEAT_GUID, FULL_NAME, DW_MATCH_HASH, DW_ACTIVE, DW_FROM_DATE, DW_TO_DATE
from owlmtn.WAREHOUSE.DIM_CONGRESS_SEAT
where state = 'CO' and SENATE_CLASS = 2
  and DW_FROM_DATE < '2020-10-30 21:51:58.166000000 -07:00'
  and DW_TO_DATE >= '2020-10-30 21:51:58.166000000 -07:00';

结论

缓慢变化的维度表是数据仓库的基础。它允许分析师跟踪趋势,查看历史,并遵守保存所有数据的原则。

一些 SQL 结构可能比较复杂,一些数据库本身就支持 SCD。然而,使用基本的 SQL SCD 模式将使您的代码具有可移植性和清晰性。

是时候让自己比以往更熟悉 NoSQL 数据库了

原文:https://towardsdatascience.com/its-time-to-familiarize-yourself-with-nosql-databases-more-than-ever-5fb1f65c22b1?source=collection_archive---------9-----------------------

SQL 与 NoSQL:选择合适的数据库的困难(以及如何做好)

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

万花筒Unsplash 上拍摄的照片

数据是信息系统的核心。其组织和运作的效率是任何公司都关心的主要问题。在大数据的时候,业务专长和了解现有的技术解决方案是非常必要的。技术领域发展非常迅速,公司必须同时继续评估和选择能够满足其未来需求并支持其发展的数据库。

几十年来,关系数据库一直被用来存储数据,对于许多用例来说,它们仍然是一种可行的解决方案。NoSQL 数据库是为了应对关系数据库技术的局限性而创建的。与关系数据库相比, NoSQL 数据库更具伸缩性,提供更高的性能,它们的数据模型纠正了关系模型的几个弱点。在本文中,我将尝试阐明从 RDBMS 迁移到 NoSQL 的挑战、好处和过程。

总的来说, NoSQL 数据库旨在解决大数据环境下的海量、多源、多格式的数据处理问题。它们提供了一种新的方法来满足容量需求
和新的数据类型。

如今,NoSQL 数据库的数量变得越来越重要。必须了解它们的差异,以便为正确的应用采用正确的技术。

1.介绍

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

- SQL:

SQLS 结构化QueryL语言。长期以来,所有必须在大型关系数据库 (DBMS) 中快速搜索信息的 It 工程师都知道这一点。 SQL 如今被广泛使用,因为它是结构化程度最高、速度最快的数据库组织和查询设备之一;它有不同的名字,这表明有各种各样的适应,如甲骨文的 MySQL 和微软的 SQL Server。此外, SQL 由于其预定义的结构和模式,是许多公司最推荐的选项。

——NoSQL:

首字母缩略词**“NoSQL”**有两种解释,目前并不明确:

  • 有的则是 【无 SQL】,也就是说使用另一种不同于 SQL 的查询语言。
  • 对于其他人来说,就是 【不仅仅是 SQL】,也就是说 SQL 与其他信息检索工具的结合使用。

因此,这个术语既与技术特征有关,也与出现在 2010 年前后的历史性一代数据库管理系统有关。导致发明 NoSQL 的主要问题是解决网站上的同一个数据库可以被全球数百万用户同时使用的事实;像亚马逊这样的公司的典型问题…

因此,我们试图用 NoSQL 实现的是降低查询语言的复杂性,简化数据库的架构。这些数据库由面向列的面向文档的面向图形的面向键/值的数据组成。 NoSQL 系列由各种产品组成,每种产品都有一套独特的功能。

-主要区别:

  • SQL 数据库有一个预定义的模式,而 NoSQL 数据库有一个非结构化数据的动态模式。
  • SQL 数据库是纵向可伸缩的,而 NoSQL 数据库是横向可伸缩的。 SQL 数据库通过增加硬件的能力来扩展,比如 CPURAMSSD 。数据库 NoSQL 通过增加数据服务器的数量来降低负载。这就像在同一栋建筑上增加更多的楼层和在附近增加更多的建筑。
  • SQL 数据库是基于表的数据库,而 NoSQL 数据库是基于键值对的数据库。这意味着 SQL 数据库以表格的形式表示数据,表格由一定数量的表示数据的行组成,而 NoSQL 数据库是键值对、文档、图形数据库等的集合。
  • SQL 数据库使用 SQL (结构化查询语言) 来定义和操作数据,非常强大。在 NoSQL 数据库中,查询集中在文档集合上。有时也叫 UnQL (非结构化查询语言) 。在不同的现有 NoSQL 数据库之间,使用 UnQL 的语法差别很大。

2.历史元素

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

来源:照片由国立癌症研究所Unsplash 上拍摄

关系数据库管理系统的历史统治

  • 创建于 20 世纪 70 年代的关系型数据库管理系统已经逐渐成为 20 世纪 90 年代早期占主导地位的数据库范例。
  • 在 20 世纪 90 年代,许多物流公司的销售人员开始使用它来存储他们的业务数据。实际上,他们既没有鼠标,也没有用户界面来搜索存储在服务器上的某些信息,这些服务器通常距离很远,通过专用线路连接:他们被用来用键盘输入 SQL 命令,并能够在几秒钟内检索到关于特定产品或原材料可用性的相关信息。
  • 其他几个数据库模型已经出现,如面向对象的数据库管理系统(DBMS)、层次数据库管理系统(DBMS)对象关系数据库管理系统(DBMS),但是它们的使用非常有限。
  • 2000 年代开始,随着大型互联网公司(亚马逊、易贝、谷歌……)的发展,大量的非结构化数据出现了,并且比结构化数据增长得更快,以至于不再适合 RDBMS 的关系模式。集群计算也有了发展,因此关系模型的统治地位受到了质疑,因为它对这些新的实践有一些禁止性的限制。

NoSQL 模式的先驱

大型网络公司必须处理非常大量的数据,这就是为什么他们首先面临传统关系型数据库管理系统固有的局限性。这些系统基于对属性(原子性、一致性、隔离性、持久性)的严格应用,并且通常被设计为在单台计算机上运行,很快就提出了可扩展性的问题。

为了满足这些限制,这些公司已经开始开发他们自己的数据库管理系统,该系统可以在分布式硬件体系结构上运行,并且可以处理大量数据:

通过简单地增加服务器的数量,性能保持良好,这是一个成本下降的合理解决方案,特别是如果收入随着活动的发展而增长。

3.流行数据库

有几种流行的数据库系统可供使用;付费和免费都有。为了给你或你的组织选择正确的管理系统,了解市场上存在的东西是很重要的。看看下面 5 个流行的 SQL 数据库的列表。

- SQL 数据库产品:

1- MySql:

它是免费的,甚至为免费的数据库引擎提供了很多功能。

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

来源:https://www.mysql.com/

2-甲骨文:

Oracle 数据库管理工具凭借最新的创新和特性变得异常强大。

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

来源:https://www.oracle.com/database/

3- Postgres:

这个数据库管理引擎是可伸缩的,可以处理万亿字节的数据,具有各种预定义的功能。

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

资料来源:https://www.postgresql.org/

4- SQL Server:

它非常快而且稳定。它与微软的其他产品配合得非常好。

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

来源:https://www.microsoft.com/en-us/sql-server/sql-server-2019

5- SQLite:

SQLite 数据库设置起来非常简单快捷,它还可以用于在智能手机应用程序(iPhone 或 Android)的真实数据库中存储数据。

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

来源:https://www.sqlite.org/index.html

- NoSQL 数据库产品:

1- MongoDB:

MongoDB 是一个灵活/可靠的数据库,它将把你带到 NoSQL 的世界。它的管理和维护非常简单快捷。

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

来源:https://www.mongodb.com/

2- Redis:

使用起来非常简单明了。您可以下载 Redis 并在接下来的五分钟内开始使用它。

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

来源:https://redis.io/

3-卡桑德拉:

Cassandra 提供的线性可伸缩性,允许通过简单地添加/删除服务器来轻松扩展/缩减集群。

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

来源:http://cassandra.apache.org/

4-h 碱基:

这是一个面向列的数据库,有助于提高查询性能和聚合。

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

来源:https://hbase.apache.org/

5- CouchDb:

由于其存储序列化(JSON 格式)非结构化数据的能力及其 Restful HTTP API,CouchDB 非常适合 Web 和移动应用程序。

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

来源:https://couchdb.apache.org/

4.NoSQL 数据库设计

NoSQL 数据库管理系统的主要特点是允许处理大量数据,并允许水平伸缩。然而,大多数公司今天面临的困难是,瞄准最合适的技术,以便对他们的问题和应用作出反应。

要解决这个困难,首先要很好地理解不同类型的 NoSQL 数据库。有一个普遍的误解,认为所有的 NoSQL 数据库都是平等的——这不是真的!实际上,这些数据库可以分为四类:面向文档的数据库、键/值数据库、数据库和面向图形的数据库。它们都有一个共同点:支持比传统关系数据库更灵活、更动态的模型。

事实上,这些类别都有自己的属性和局限性。没有解决所有问题的更好的数据库。您必须根据项目的需要选择数据库。

您必须考虑哪种数据将被操作,以及应用程序最终将如何使用它。

****-面向文档的数据库:混合结构

面向文档的 NoSQL 数据库以键/值对的形式存储和提取数据,但值部分存储为文档。文档以 JSONXML 格式存储。

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

MongoDBApache couch dbMarkLogic 都是面向文档的数据库。

****-键/值数据库:

面向键-值的数据库有大量的键和值。它代表了 NoSQL 数据库的最简单形式。它们将唯一键与数据中的值相关联,目的是基于相对简单的数据集极大地增强应用程序的性能。

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

Redis Riak MemcachedAerospike 都是键值数据库的例子。

****-栏目数据库:

列数据库将数据保存在具有大量列的表中。每个存储块包含来自单个列的数据,并且每个列被单独处理。它们在计数、求和、AVG、最大值等聚合查询上提供了高性能。,因为数据很容易在列中获得。

HBaseCassandraAccumulo 都是基于列的数据库的例子。

****-面向图形的数据库:

基于图形的数据库是一种网络数据库,它以“图形”结构存储数据元素,并使在节点之间创建关联成为可能,最终作为推荐引擎或社交网络的基础。

我们可以从图形数据库中获得很多信息。例如,图表技术可用于通过不同人的兴趣来识别他们之间的关系。

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

推特图|来源:https://neo4j.com/blog/oscon-twitter-graph/

Neo4J 无限图FlockDB 都是面向图的数据库的例子。

4.为您的应用选择合适的数据库类型的 5 个标准

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

我们如何决定哪种类型的数据库最适合一个项目?这里有一份你可以使用的清单:

  • 要存储的数据类型 : SQL 数据库不适合分层数据存储。然而, NoSQL 数据库更适合分层数据存储,因为它遵循键-值对方法或图形方法。NoSQL 数据库是大型数据集的首选。
  • 复杂查询 : SQL 数据库非常适合需要很多查询的环境,而 NoSQL 数据库不适合复杂查询。因此, NoSQL 中的查询不如 SQL 查询语言强大。
  • 可伸缩性:在大多数情况下, SQL 数据库是垂直可伸缩的。您可以通过增加处理器、RAM、SSD 等来管理增加的负载。在一台服务器上。另一方面, NoSQL 数据库是水平可伸缩的。你可以简单地添加一些额外的服务器到你的 NoSQL 数据库基础设施来处理大流量。所以你可以根据你的设备选择适合你的数据库类型。
  • 高度事务性应用 : SQL 数据库更适合大量使用的事务型应用,因为它们更稳定,并保证原子性和数据完整性。虽然您可以将 NoSQL 用于事务性目的,但它仍然不具有可比性,但可以用于复杂的事务性应用程序。
  • 属性 : SQL 数据库强调 ACID 属性(原子性、一致性、隔离性、持久性),而 NoSQL 数据库遵循 Brewers CAP 定理(一致性、可用性和分区容差)。

5.从 RDBMS 转换到 NoSQL

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

Unsplash科学高清摄影

无论您选择哪种 NoSQL 数据库设计,将数据迁移到其中都会面临一些严峻的挑战。在 NoSQL的数据模型的设计有额外的复杂性,因为它需要知道数据的最终用途。知道您的应用程序将处理 账单客户 已经不够了。您现在必须知道这些数据将如何显示给最终用户。因此,在 NoSQL 数据库中进行数据建模,除了需要对最终客户的使用有深入的了解之外,还需要真正的技术专家。****

是时候用 NoSQL 解决方案取代 SQL 了吗?

在我看来,这是一个很难的问题!因为在大多数情况下,这不是用一个 NoSQL 解决方案替换 SQL ,而是,如果应用程序和用例揭示了改变的需要,从一个解决方案过渡到另一个解决方案。一般来说,在构建现代 web 和移动应用程序时,灵活性和可伸缩性的需求将推动这种转变。

通常,许多公司试图支持其 web 应用程序中的负载,因此他们选择简单地在负载平衡器后添加 web 服务器来支持更多用户。毫无疑问,在日益重要的云计算世界中,向上扩展的能力是一个基本的竞争优势,在这个世界中,可以轻松地添加或删除虚拟机实例,以满足不断变化的需求。

**关系数据库(RDBMS) 不允许简单的伸缩,也不提供灵活的数据模型。管理更多的用户意味着增加更大的服务器,大型服务器非常复杂和昂贵,不像低成本硬件、**【商用硬件】和云架构。组织开始发现现有或新应用程序的关系数据库存在性能问题。尤其是随着用户数量的日益增加,他们意识到对更快、更灵活的数据库的需求变得非常重要。是时候搬到 NoSQL 去了!

从 SQL 过渡到 NoSQL 需要哪些主要步骤?

根据不同的组织,应用程序/项目可能会有很大的不同,因此过渡将取决于您的用例。以下是过渡的一些一般准则:

1-了解您的应用的关键要求:

以下是与拥有一个 NoSQL 数据库的需求相对应的一些要求:

  • ****快速应用开发:不断变化的市场需求&持续修改数据
  • 可扩展性
  • ****稳定的性能:响应时间短,用户体验更好
  • ****运行可靠性:高可用性,以对应用程序影响最小的方式管理错误&集成监控 API,以实现更好的维护

2-了解不同类型的 NoSQL 优惠:

正如我上面所说的,有不同类型的 NoSQL 数据库管理系统。

例如,面向文档的 NoSQL 数据库——带有 CouchbaseMongoDB ,这是两个最著名和最广泛采用的例子。

此外,例如, Cassandra 可能是一个解决方案,您可以使用它的柱状模型进行数据分析。对于需要存储实体间关系的应用程序来说,图形数据库 Neo4j 是一个完美的数据库。

3-构建原型:

一旦缩小了数据库类型的可能选择范围,就尝试开发一个集成了应用程序主要特征的原型。这个原型将帮助您评估响应时间、吞吐量方面的性能以及轻松扩展的能力。

4-文档建模和开发:

对于面向文档的数据库,花几天时间对数据建模,从固定的表格图开始,到灵活的文档模型。

5-部署然后生产:

操作稳定性是交互式 web 应用程序的一个非常重要的方面。像通常使用传统的 RDBMS 系统的应用程序一样测试和重新测试您的部署。

6-紧跟最新潮流:

今天,有太多的高质量培训为 NoSQL 的培训提供实践课程。确保成功实现 NoSQL 的最好方法是获得最新版本。

别担心,你会发现采用某些 NoSQL 技术很容易,尤其是如果你熟悉文档格式的 JSON 。大量使用 SQL 的开发人员可能需要适应和学习文档建模方法。重新思考如何使用文档逻辑地构造数据,而不是将数据规范化到固定的数据库模式中,这成为一个重要的方面。

以下是一些有趣课程的链接:

6.结论

因此,本文的目的是展示存在的主要差异,以帮助您做出正确的决策,并塑造您的信息系统(或您的简单应用程序)的未来。

我们已经看到 SQLNoSQL 数据库最终做了几乎相同的事情(存储数据),但方式不同。因此,对于任何数据项目来说,选择数据库管理系统 (DBMS) 都是一个重要的构建时刻。当然,总是有可能选择一个选项,然后稍后切换到另一个选项。但是在项目开始时进行一点概念性的分析和思考会让你节省时间和金钱。

今天的市场充满了 NoSQL 的数据库——我认为我们每天都要面对两到三个,因为开发人员转用 NoSQL 的数据库有很多好处。更灵活的数据模型和摆脱僵化模式是一个很大的优势。您还可以看到显著提高的性能和水平扩展能力。

但是大多数 NoSQL 产品仍处于产品周期的早期阶段。对于复杂连接这样的特性,开发人员可能更喜欢使用传统的 RDBMS。对于一些项目,一个 混合方法 可能是最好的选择。

总之,根据项目的要求,每个公司都有自己的偏好。因此,确定您的需求和数据库,明智地为您的项目开发提供集成支持。

如果你设法来到这里,恭喜你。感谢阅读,我希望你喜欢它。如需个人联系或讨论,欢迎在LinkedIn联系我。

参考资料:

是时候去考冠状病毒博士了

原文:https://towardsdatascience.com/its-time-to-get-a-phd-in-coronavirus-cacf7e2e9e9f?source=collection_archive---------21-----------------------

有可靠来源的 Corona-knowledge 的实际在线课程

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

教育让你保持健康。刘凯潇Unsplash 上拍照

取决于你住在哪里,在过去的几个月、几周或几天里,你可能已经遭受了与电晕相关的焦虑。所有的轻描淡写,假新闻,甚至有趣的模因,都倾向于加剧焦虑,至少对我来说是这样。

如果有一种方法可以治愈所有这些不确定性,那就是知识。我最近写了一篇关于如何利用这些社交距离来自学数据科学的文章。但是,如果你能通过了解引起焦虑的同样的事情来抑制你的焦虑呢?

[## 如果你因为冠状病毒而被锁在家里,11 堂最好的数据科学课

不用花一分钱就可以开始你的职业生涯

towardsdatascience.com](/11-best-data-science-classes-if-youre-locked-home-because-of-coronavirus-ca7d2d74a454)

事实证明,确实有关于冠状病毒的在线课程。这不是废话——他们由来自世卫组织、伦敦帝国理工学院、约翰·霍普金斯大学等机构的世界级健康和流行病学研究人员授课。你甚至可以在其中几门课程结束时获得成绩证书!

我将把重点放在英语课程上,这些课程是免费的,对普通大众来说是可以理解的。有更多针对健康专家的课程,但是因为我不在这个领域工作,我觉得我没有资格评价这些。这些课程的排名不分先后。

约翰霍普金斯大学/ coursera:用流行病学对抗新冠肺炎

所需时间:~5 小时,自定进度
开始时间:2020 年 3 月 31 日(但随时可以加入)

本课程主要关注疾病爆发的流行病学方面:主要关注如何收集和分析关于流行病的正确数据。核心问题是:

  • 有多少人被感染了?
  • 我们如何衡量谁被感染了?
  • 病毒的传染性有多大?
  • 我们能做什么?

该课程还提供了一些实用技巧,教你如何在社交孤立中学习和与他人联系。

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

如果你有症状,戴上口罩。由Adam nieciorukUnsplash 上拍摄的照片

伦敦帝国理工学院/ coursera:我们来谈谈新冠肺炎吧

所需时间:~19 小时,自定进度
开始时间:2020 年 3 月 28 日(但可随时加入)

虽然上述课程主要关注疫情的定量方面,但本课程也涵盖政府和社会的应对措施。随着疫情的发展,它也在不断更新。

本课程的要点是:

  • 正在出现的疫情的规模和使用可靠的信息来源跟踪趋势的方法,
  • 循证爆发控制方法的关键科学基础,
  • 社区参与、多学科合作和全球合作在疫情应对中的重要性,以及
  • 传染病模型如何为地方、国家和国际层面的战略和操作反应提供信息。

伦敦卫生与热带医学学院/未来学习:应对新型冠状病毒

所需时间:~12 小时,自定进度
开始:任何时间

与前面介绍的两门课程相比,这门课程更加关注医疗保健。课程的核心支柱是:

  • 新冠肺炎是如何出现和被识别的,
  • 新冠肺炎是如何传播的,
  • 新冠肺炎全球公共卫生措施,以及
  • 如何应对新冠肺炎的未来?

像许多其他课程一样,它会不断更新,以提供我们对该病毒的最新了解。完成后,您可以下载一份成就证书来证明您的成功。

世界卫生组织:新型冠状病毒的感染预防和控制

所需时间:~1 小时,自定步调
开始:任何时间

来自世界卫生组织的这一官方课程快速总结了疫情的现状以及在这一困难时期的一般准备指南。其核心目标是:

  • 定义感染预防和控制及其在准备、准备和响应中的作用,
  • 描述当前新冠肺炎的流行病学情况,包括病例定义和体征及症状。
  • 描述源代码控制、管理控制以及环境和工程控制,
  • 描述世卫组织建议的卫生保健机构感染预防和控制措施,包括在处理疑似或确诊新冠肺炎病例时,以及
  • 描述额外的感染预防和控制措施,以帮助医疗机构做好一般准备。

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

利用你独处的时间去学习新的东西。照片由 Unsplash 上的 Dhaya Eddine Bentaleb 拍摄

世界卫生组织:支持国家准备和应对的业务规划准则和新冠肺炎伙伴平台

所需时间:~2 小时,自定步调
开始:任何时间

本课程与上述课程相似,但更深入地探讨了流行病的准备计划。核心目标是:

  • 让自己和他人了解实施新冠肺炎战略准备和反应计划的重要性,
  • 能够描述公共卫生准备和应对的 8 大支柱以及联合国国家工作队将采取的初步行动,以及
  • 能够获得进行准备水平评估所需的全套行动、绩效指标和资源。

如果你在评分作业中取得了 80%以上的成绩,你也有资格获得成绩记录。

世界卫生组织:包括新冠肺炎在内的新型呼吸道病毒:检测、预防、应对和控制方法

所需时间:~3 小时,自定进度
开始:任何时间

本课程重点关注新冠肺炎病毒爆发的生物学和流行病学,但也涵盖了应对一种新型病毒的某些方面。课程的中心点是:

  • 新出现的呼吸道病毒的性质,如何检测和评估爆发,预防和控制由新型呼吸道病毒引起的爆发的策略,以及
  • 应该使用什么策略来传达风险并让社区参与检测、预防和应对新型呼吸道病毒的出现。

它会根据最新的发现不断更新。目前还没有证书,但将来会有。

底线:呆在家里学习

在我们这个时代,社会隔离和自我隔离可能看起来像中世纪的反应。但由于这种冠状病毒如此新,这是科学在这一点上所能提供的最好的。

这并不意味着你不能做任何事情。第一步是利用可靠的来源来教育自己。尽管在这个充满焦虑的世界里,到处都有一个有趣的电晕迷因不会造成任何伤害…

然后,你可能想把你的知识带到下一个层次:帮助你的邻居,尤其是年轻人、老年人和病人。照看你所在地区的孩子,或者为别人去购物。现在不是自私的时候。

也许这些课程不足以让你获得冠状病毒博士学位。但他们肯定会给你一个人类的伟大。

是时候考虑到公平性来优化数据算法了

原文:https://towardsdatascience.com/its-time-to-optimize-data-algorithms-with-fairness-considerations-9bfe68c7ed38?source=collection_archive---------54-----------------------

为什么预测能力和花哨的模型指标不能说明全部情况…

不可否认的是,我们的社会正越来越多地被大数据所统治。随着我们的生活越来越多地受到数据驱动算法的影响,意识到它们的道德含义很重要。一个普遍的问题是,这些模式往往会延续现有的社会偏见和歧视。虽然这通常是一个无意的过程,但这可能导致产品或服务不能平等地涵盖所有人群,在许多情况下甚至会给他们的生活带来意想不到的后果。

例如,今天存在的面部识别系统是用数据集进行大量训练的,这些数据集对某些人口统计数据的权重更大,导致准确性在种族、地区和年龄之间差异很大。来自少数群体的人的脸可能不被系统识别,或者以有害的方式被曲解。下面显示了三大公司开发的面部识别算法在性别和种族方面的准确性水平:

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

按人口统计的面部识别准确度;数据来自性别差异:商业性别分类的交叉准确性差异

另一个例子是谷歌搜索某些关键词的结果。下面是谷歌图片搜索“专业发型”和“非专业发型”的对比。

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

截至 2010 年 5 月 16 日的谷歌图片搜索

专业发型主要展示白人女性的照片。它还显示了相关的关键字,“男性”和一个白人图标。然而,“不专业的发型”展示了更多黑人女性的形象。相关关键词是“黑发”和“自然头发”,带有黑人女性图标。相关关键词“男性”的标志是一个黑人。

本文将讨论:

  • 这种偏见是如何融入模型的
  • 高破坏性有偏模型的关键特征&例子
  • 讨论一些可能的解决方案

模型中是如何引入偏差的?

偏差可以包含在建模的所有步骤中[1,2,4],如下图所示:

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

如何在模型开发周期中引入偏差

高破坏性模型的关键特征

某些带有偏见的模型极具破坏性。凯茜·奥尼尔所著的《大规模杀伤性武器》一书很好地总结了这一点,因此我将在这一部分大量引用。在她的书中,她描述了有害的模型,或破坏数学的武器,是那些满足以下标准的模型:

  1. 损害——它会损害或摧毁生命吗?

许多模型输出的决策可能会产生有害的后果。例如,它可能导致人们失业,使他们陷入贫困,或者以他们根本无法承受的价格收取产品/服务的费用。

2。比例

一些模型在很大程度上控制着社会,因此对我们的生活有很大的影响。例如,信用评分模型控制着我们的财务选项,这对我们过上稳定生活的能力起着很大的作用。

3。缺乏透明度和问责制:

许多模型是“黑匣子”。这个模型的决策过程很大程度上没有被质疑,也没有被理解。该模型的基本假设甚至可能是错误的,但没有足够的透明度让人们能够指出它的不公平性并进行辩护。

促成破坏性和歧视性模式的关键因素:

  1. 使用代理区分:

很明显,如果模型明确使用人口统计变量,它会有偏差。招聘算法可能会将女性排名靠后,因为做母亲通常会影响职业表现。尽管如此,许多模型隐含歧视。通常很难获得显式数据信号,因此建模者转向隐式提供相同信号的代理。一个模特可能只使用邮政编码,但邮政编码与种族和收入相关。P2P 贷款机构通常根据与邻居、邮政编码、顾客购物的商店的相关性来制定风险评分。这些代理人不仅仅表明了更好地保证贷款偿还的责任。它们代表财富,也与种族相关。贫穷的少数群体可能有更高的借贷成本,尽管他们已经在挣扎,即使他们有责任。在另一个例子中,芝加哥警方小组列出了最有可能实施暴力犯罪的人的名单,不是基于他们的行为,而是基于他们犯罪的邻居和熟人。这些人不是根据他们自己的行为来判断的,而是因为属于社会的一个子群体而受到惩罚。

2。只关注业务目标的优化,如利润

大多数企业建立这些模型来帮助优化利润。我们在现实生活中从来没有看到一个 100%准确的模型,因为数据是有噪声的,但我们可以调整它来优化某些指标,如利润。然而,模型“错误”会给人们的生活带来高昂的代价。一个优秀的员工可能会被模型错误地预测为工作表现不佳而被解雇。此外,模型经常牺牲人性来优化利润。工人的时间表现在由优化软件控制,以实现利润最大化。他们采用在制造业中流行的“及时”原则,即物品及时到达以最小化成本。同样,这些模型预测商店何时需要员工,并根据不断变化的时间表安排员工。这种不稳定的、受商业驱动的时间表让你很难找到第二份工作或重返校园。这反过来又减少了他们通过更好的就业来摆脱这一循环的机会。

3。恶性反馈循环

有偏见的模型产生的输出有助于营造一种强化这种偏见的环境。例如,警察局使用的累犯模型可能会输出一个决定,预测来自某些种族特征的囚犯风险更大,应该被关押更长时间。然而,由于他们在监狱里的时间更长,他们回国后更难找到工作,导致再次犯罪的可能性增加。当他们这样做时,它加强了模型,它确实根据这些因素做出了正确的预测。很少有人评估受影响的最终用户是否得到了公平对待,如果没有,如何调整模型。

4。未经测试的假设

模型往往基于假设。上面解释的警察局累犯风险模型假设社会,甚至囚犯本身都会从被关更长时间中受益。然而,后来的一项研究实际上声称,将他们关押更长时间实际上会增加累犯的可能性。另一个例子是,一位名为莎拉·威索基的老师被解雇了,因为 DCPS 的“影响力”算法——用于评估教师的表现——给她打了“表现不佳”的分数。尽管如此,大多数学生对她评价很高。该模型的决定是由她的学生每年较低的标准化考试成绩驱动的。尽管没有足够的技能,学生们还是以前一年虚假的高分入学。这伤害了莎拉提高分数的能力,不管她的教学技巧如何。分数增加作为教师绩效指标背后的假设没有得到验证。

5。逆向工程使游戏系统成为可能

因为模型是基于规则做出决策的,所以那些能够逆向工程这些规则的人可以击败系统,成为误报。美国新闻学院排名是由录取率、毕业率等指标驱动的。可以说,这迫使学校提高质量。然而,这些排名往往不能反映学校的质量。操纵排名的一个方法是通过拒绝那些可能不会被录取的顶尖学生来降低录取率。此外,学校可以通过把学生算作被雇佣者来欺骗 10%的就业率,即使他们是咖啡师.或者,他们可以通过更多地关注工程和科学部门,而不是教育和社会工作部门来提高平均收入。

6。利用最终用户的漏洞

算法可以用来暴露人们的弱点,并利用他们的需求或无知。以营利性大学的丑闻为例,比如科林斯大学的。他们利用算法来锁定来自最贫困地区的学生,并点击了发薪日贷款等广告。这个群体迫切需要多挣钱的方法,所以向他们推销私人教育的帮助是很容易的。点击流数据可以提供关于他们兴趣的信息,自然语言处理可以分析他们在社交媒体上发布的关于他们关注的词语。A/B 测试可以用来优化广告以引起他们的注意。这些学院的收费比社区学院高很多倍,以吸引那些可以获得政府贷款的学生。这些学生最终背负了巨额债务,没有高质量的教育来增加收入。

可能的解决方案

提高认识: 很多人根本就不知道这个问题的存在。意识不仅对数据科学家或建模师很重要。其他角色,如投资者、商业战略家、产品经理、消费者——任何参与数据驱动产品或公司的人(目前可能占大多数%)都同样有责任塑造行业。

在建模过程中纳入公平性考虑: 建模者需要在建模过程的每一步评估公平性[2,4]。数据收集需要确保多样化的数据集,让少数群体有平等的培训机会。特征工程需要考虑显性或隐性偏差。模型需要在其他传统指标的基础上用公平指标进行评估,以确保所有群体的平等绩效和影响。当模型做出错误判断时的反馈需要回到模型中重新调整。劳动力构建这些模型是多样化的。通过这种方式,那些意识到不公平会影响到与他们相似的人的少数群体将会推动解决这个问题。

LinkedIn 在优化候选人搜索结果的排名模型时,已经努力引入公平指标。正如他们在论文 中所解释的,搜索中的公平感知排名&推荐系统及其在 LinkedIn 人才搜索中的应用【3】,他们引入了额外的约束条件,以最小化对每个属性(如种族、性别等)的期望代表比例的偏离。)显示在搜索结果中,以确保不同群体拥有平等的个人资料曝光机会。他们使用 A/B 测试进行验证,结果显示> 95%的搜索是有代表性的,而业务指标(如发送/接受的邮件数量)没有显著变化。

同样,谷歌已经开始解决他们语言翻译中的性别偏见问题[5]。他们的翻译很多都倾向于选择一个性别,比如在谈论保姆的时候选择代词‘她’,在谈论医生的时候选择‘他’。他们建立了一个模型来检测默认翻译是否做出了性别特定的选择,并建立了另一个语言模型来重写包含两种性别的版本。

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

自 2010 年 5 月 16 日起,谷歌将搜索翻译成土耳其语。

对这种方法的评估是通过引入一个公平性度量——偏差减少来完成的。这衡量了从以前的模型中选择性别的翻译减少了多少%。新方法减少了从匈牙利语、芬兰语、波斯语、土耳其语到英语翻译中超过 90%的偏差。

让透明度和问责制成为标准: 消费者应该能够要求大公司在如何使用分数来评判他们方面更加透明,并了解他们的分数中包含哪些内容,有权修正不准确的内容。有一些治理良好的分数的例子。信用评分虽然有时会被滥用,但它是透明的、可问责的[2]。他们结合反馈来调整模型的不准确性。它们是相对透明的——消费者有合法权利知道他们的分数是多少,他们的分数中包含什么,并被告知如何提高分数(例如减少债务)。消费者可以要求改正错误。大规模治理的模型需要更多地转向这个方向。

消费者透明度工具: 我们将受益于更多构建工具的计划,这些工具对公众开放并可供公众使用,以模拟如果他们采取某种行动,他们在各种模型中的分数会发生什么(例如,如果他们有未支付的账单,他们的信用分数会发生什么,以及较低的分数会在多大程度上影响她的买车计划)[2]。

揭露模型中偏见的举措: 模型中的偏见可以通过类似P*roppublica 的消息机 众包活动这样的举措得到更多的揭露[2]。这个众包收集了不同的人从奥巴马竞选中收到的信息,用于逆向工程定向政治广告的模型。再比如 普林斯顿的网络透明与问责项目 。该项目提供了一个平台来测试自动化系统,如搜索引擎和就业安置网站,它们将如何对待不同的人物角色(例如,富人、穷人、男性、女性、精神疾病)。另一个倡议是 算法正义联盟*,这是一个讨论模型偏见的平台,人们可以分享他们经历的偏见,公司可以要求对他们的模型进行偏见审计。**

政策&规定: 规定保护数据的使用需要保护新类型分数的使用情况[2]。例如,我们需要规范性格测试、健康评分、声誉评分在招聘决策中的使用。此外,需要重新考虑数据收集条例。目前,欧洲政策要求收集的任何数据都必须得到用户的批准,作为一种选择,并禁止为其他目的重复使用数据。此外,法规应要求对大规模模型进行第三方审计。

商业模式需要融入公平&包容性目标: 尽管这可能会让公司损失一些利润,但这与今天的企业社会责任努力或日益道德的商业模式(如环保)没有什么不同,它们实际上为品牌增加了价值。通过服务更广泛的人群,企业甚至可能变得更加有利可图。如果企业希望进入拥有巨大增长潜力的新兴市场,就需要为这些人群提供可获得的包容性产品。正如汉斯·罗斯林、在《真实》一书中所写的,“只关注富裕的传统市场的投资者实际上正在错失良机。有数十亿潜在的新消费者,他们的购买力在不断增长。投资者可能会错过最大的经济机会,因为他们仍然认为这个群体是“少数”或“仍然太穷”。

来源:

[1] Krishnaram Kenthapadi, LinkedIn 在人工智能/人工智能系统中的公平和隐私 (2019) Pinterest 技术演讲

[2]凯茜·奥尼尔,大规模杀伤性武器 (2016)

[3] Sahin Cem Geyik,Stuart Ambler,Krishnaram Kenthapadi,搜索中的公平意识排名&推荐系统及其在 LinkedIn 人才搜索中的应用 (2019)

[4] Neelima Kumar,人工智能中的偏见 (2017)格蕾丝·赫柏会议

[5]梅尔文·约翰逊,减少谷歌翻译中性别偏见的可扩展方法 (2020)谷歌人工智能博客

你不用再用 Docker 了

原文:https://towardsdatascience.com/its-time-to-say-goodbye-to-docker-5cfec8eff833?source=collection_archive---------0-----------------------

意见

Docker 不是唯一的集装箱工具,可能会有更好的替代工具…

在古代的集装箱时代(真的更像 4 年前)码头工人是集装箱游戏中唯一的玩家。但现在情况不同了,Docker 不是唯一的 T4,而是另一个容器引擎。Docker 允许我们构建、运行、拉取、推送或检查容器图像,但对于这些任务中的每一项,都有其他替代工具,它们可能比 Docker 做得更好。所以,让我们探索一下前景,并且(仅仅是也许是)卸载并完全忘记 Docker

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

照片由妮可陈Unsplash 上拍摄

为什么不使用 Docker 呢?

如果你已经是 docker 用户很长时间了,我认为你需要一些说服来考虑转换到不同的工具。所以,现在开始:

首先,Docker 是一个单体工具。它是一个尝试做所有事情的工具,这通常不是最好的方法。大多数情况下,最好选择一个专门的工具,它只能做一件事,而且做得很好。

如果您害怕切换到不同的工具集,因为您必须学习使用不同的 CLI、不同的 API 或不同的概念,那么这不是问题。选择本文中显示的任何工具都可以是完全无缝的,因为它们都(包括 Docker)遵循 OCI 下的相同规范,这是开放容器倡议的缩写。该计划包含容器运行时容器分发容器映像的规范,涵盖了使用容器所需的所有特性。

有了 OCI,您可以选择一套最适合您需求的工具,同时您还可以享受使用与 Docker 相同的 API 和 CLI 命令的乐趣。

所以,如果你愿意尝试新的工具,那么让我们比较一下 Docker 和它的竞争对手的优缺点和特性,看看是否真的有必要考虑放弃 Docker 而使用一些新的闪亮的工具。

集装箱发动机

当将 Docker 与其他工具进行比较时,我们需要按组件对其进行分解,首先我们应该谈论的是容器引擎。Container engine 是一个工具,它提供了处理图像和容器的用户界面,这样您就不必去弄乱诸如SECCOMP规则或 SELinux 策略之类的东西。它的工作也是从远程存储库中提取图像,并将它们扩展到您的磁盘上。它看似运行容器,但实际上它的工作是创建容器清单和带有图像层的目录。然后它将它们传递给容器运行时,比如runccrun(我们稍后会谈到)。

有许多容器引擎可用,但 Docker 最突出的竞争对手是由红帽开发的。与 Docker 不同,Podman 不需要守护进程来运行,也不需要 root 权限,这是 Docker 长期关注的问题。根据名称,Podman 不仅可以运行容器,还可以运行pod。如果您不熟悉 pod 的概念,那么 pod 是 Kubernetes 的最小计算单元。它由一个或多个容器组成——主容器和所谓的侧容器——执行支持任务。这使得 Podman 用户以后更容易将他们的工作负载迁移到 Kubernetes。因此,作为一个简单的演示,这就是如何在一个单元中运行两个容器:

最后,Podman 提供了与 Docker 完全相同的 CLI 命令,因此您可以只做alias docker=podman并假装什么都没有改变。

除了 Docker 和 Podman,还有其他的容器引擎,但是我认为它们都是没有前途的技术,或者不适合本地开发和使用。但是为了有一个完整的画面,让我们至少提一下那里有什么:

  • LXD——LXD 是 LXC (Linux 容器)的容器管理器(守护程序)。这个工具提供了运行系统容器的能力,这些容器提供了更类似于虚拟机的容器环境。它位于非常狭窄的空间,没有很多用户,所以除非你有非常具体的用例,那么你可能最好使用 Docker 或 Podman。
  • CRI-O —当你在谷歌上搜索什么是 CRI-O 时,你可能会发现它被描述为容器引擎。不过,它确实是容器运行时。除了它实际上不是一台发动机之外,它也不适合的“正常”使用。我的意思是,它是专门为用作 Kubernetes 运行时(CRI)而构建的,而不是供最终用户使用的。
  • rkt — rkt ( “火箭”)是由 CoreOS 研发的集装箱发动机。这里提到这个项目实际上只是为了完整性,因为项目结束了,它的开发也停止了——因此它不应该被使用。

建筑图像

对于容器引擎,除了 Docker,真的只有一种选择。当涉及到构建图像时,我们有更多的选择。

首先,我来介绍一下 Buildah 。Buildah 是由 Red Hat 开发的另一个工具,它与 Podman 配合得非常好。如果您已经安装了 Podman,您甚至可能已经注意到了podman build子命令,它实际上只是 Buildah 的伪装,因为它的二进制文件包含在 Podman 中。

至于它的功能,它遵循与 Podman 相同的路线——它是无后台和无根的,并生成 OCI 兼容的图像,因此它保证您的图像将与用 Docker 构建的图像以相同的方式运行。它还可以从Dockerfile或者(更恰当的名称)Containerfile构建图像,这是不同名称的同一事物。除此之外,Buildah 还提供了对图像层的更好的控制,允许您将许多更改提交到单个层中。与 Docker 一个意想不到但(在我看来)很好的区别是,Buildah 构建的图像是特定于用户的,因此您将只能列出您自己构建的图像。

现在,考虑到 Buildah 已经包含在 Podman CLI 中,您可能会问为什么还要使用单独的buildah CLI?嗯,buildah CLI 是podman build中包含的命令的超集,所以你可能不需要接触buildah CLI,但是通过使用它,你可能还会发现一些额外的有用特性(关于podman buildbuildah之间的具体区别,请参见下面的文章)。

说到这里,让我们来看一个小演示:

从上面的脚本中可以看到,您可以简单地使用buildah bud构建映像,其中bud代表使用 Dockerfile* 构建,但是您也可以使用更多的脚本化方法,使用 Buildahs fromruncopy,它们是 Dockerfile 中的命令(FROM imageRUN ...COPY ...)的等价命令。*

接下来是谷歌的 Kaniko 。Kaniko 也从 Dockerfile 构建容器映像,与 Buildah 类似,它也不需要守护进程。与 Buildah 的主要区别是 Kaniko 更关注 Kubernetes 中的建筑图像。

Kaniko 应该作为一个映像运行,使用gcr.io/kaniko-project/executor,这对于 Kubernetes 来说是有意义的,但是对于本地构建来说不太方便,而且有点违背了初衷,因为您需要使用 Docker 来运行 Kaniko image 来构建您的映像。也就是说,如果您正在寻找在 Kubernetes 集群中构建映像的工具(例如在 CI/CD 管道中),那么 Kaniko 可能是一个不错的选择,因为它是无后台的,而且(也许)更加安全。

从我的个人经验来看,我使用 Kaniko 和 Buildah 在 Kubernetes/OpenShift 集群中构建图像,我认为两者都能很好地完成工作,但是在 Kaniko 中,当将图像推送到注册表时,我看到了一些随机构建崩溃和失败。

这里的第三个竞争者是 buildkit ,也可以称为下一代* docker build。它是莫比项目的一部分(Docker 也是如此),可以使用DOCKER_BUILDKIT=1 docker build ...通过 Docker 作为实验特性来启用。好吧,但这到底能给你带来什么?它引入了许多改进和很酷的特性,包括并行构建步骤、跳过未使用的阶段、更好的增量构建和无根构建。然而另一方面,它仍然需要守护进程来运行(buildkitd)。所以,如果你不想摆脱 Docker,而是想要一些新的特性和不错的改进,那么使用 buildkit 可能是一个不错的选择。*

与上一节一样,这里我们也有一些“荣誉奖”,它们填补了一些非常具体的用例,但不是我的首选:

  • Source-To-Image (S2I) 是一个不用 Dockerfile 直接从源代码构建图像的工具包。这个工具对于简单的、预期的场景和工作流工作得很好,但是如果你需要太多的定制或者如果你的项目没有预期的布局,它很快就会变得令人讨厌和笨拙。如果您对 Docker 还不是很有信心,或者如果您在 OpenShift 集群上构建您的映像,您可以考虑使用 S2I,因为使用 S2I 构建是一个内置特性。
  • Jib 是谷歌的另一个工具,专门用于构建 Java 映像。它包括 MavenGradle 插件,可以让你轻松构建图像,而不需要弄乱 docker 文件。
  • 最后但同样重要的是 Bazel ,这是谷歌的另一个工具。这不仅仅是为了构建容器映像,而是一个完整的构建系统。如果你只是想要建立一个形象,那么深入 Bazel 可能有点矫枉过正,但绝对是一个很好的学习经历,所以如果你准备好了,那么 rules_docker 部分对你来说是一个很好的起点。

容器运行时

最后一大难题是容器运行时*,它负责运行容器。容器运行时是整个容器生命周期/堆栈的一部分,除非您对速度、安全性等有非常具体的要求,否则您很可能不会去弄乱它。所以,如果你已经厌倦了我,那么你可能想跳过这一节。另一方面,如果你只是想知道有哪些选择,那就这样:*

runc 是基于 OCI 容器运行时规范创建的最流行的容器运行时。它被 Docker(通过 containerd )、Podman 和 CRI-O 使用,所以除了 LXD(使用 LXC)之外,它几乎什么都用。我没有什么可以补充的了。它是(几乎)所有东西的默认设置,所以即使你在读完这篇文章后放弃了 Docker,你也很可能仍然会使用 runc。

runc 的一个替代物被类似地(并且令人困惑地)命名为 crun 。这是 Red Hat 开发的工具,完全用 C 编写(runc 用 Go 编写)。这使得它比 runc 更快,内存效率更高。考虑到它也是 OCI 兼容的运行时,如果你想自己检查的话,你应该可以很容易地切换到它。尽管它现在不是很受欢迎,但它将作为 RHEL 8.3 版本的替代 OCI 运行时出现在技术预览版中,并考虑到它是我们最终可能会看到的 Podman 或 CRI-O 的默认红帽产品

说到 CRI-O,我之前说过 CRI-O 不是真正的容器引擎,而是容器运行时。这是因为 CRI-O 不包括像推送图像这样的功能,而这正是你对容器引擎的期望。CRI-O 作为一个运行时在内部使用 runc 来运行容器。这个运行时不是您应该尝试在您的机器上使用的运行时,因为它被构建为在 Kubernetes 节点上用作运行时,您可以看到它被描述为“Kubernetes 需要的所有运行时,仅此而已”。因此,除非您正在设置 Kubernetes 集群(或者 OpenShift 集群——CRI-O 已经是默认的),否则您可能不应该接触这个集群。

这部分的最后一个是 containerd ,是 CNCF 的毕业设计。它是一个守护进程,充当各种容器运行时和操作系统的 API 门面。在后台,它依赖于 runc,并且是 Docker 引擎的默认运行时。它也被 Google Kubernetes 引擎(GKE)和 IBM Kubernetes 服务(IKS)使用。它是 Kubernetes 容器运行时接口的一个实现(与 CRI-O 相同),因此它是 Kubernetes 集群运行时的一个很好的候选。

图像检查和分发

集装箱堆栈的最后一部分是图像检查和分发。这有效地取代了docker inspect,并且(可选地)增加了在远程注册中心之间复制/镜像映像的能力。

我在这里提到的唯一可以完成这些任务的工具是 Skopeo。它是由 Red Hat 制作的,是 Buildah、Podman 和 CRI-O 的配套工具。除了我们都从 Docker 了解的基本skopeo inspect之外,Skopeo 还能够使用skopeo copy复制图像,这允许您在远程注册表之间镜像图像,而无需首先将它们拖到本地注册表。如果您使用本地注册表,此功能也可以作为拉/推。

作为一个小奖励,我还想提一下 Dive ,这是一个检查、探索和分析图像的工具。它对用户更友好,提供更可读的输出,并且可以更深入地挖掘(或者我猜是潜入*)你的图像,分析和衡量它的效率。它也适用于 CI 管道,可以衡量你的图像是否*【足够高效】或者换句话说——是否浪费了太多空间。

结论

本文并不是要说服您完全放弃 Docker,而是向您展示构建、运行、管理和分发容器及其映像的整体情况和所有选项。包括 Docker 在内的每一种工具都有其优点和缺点,评估哪一套工具最适合您的工作流和用例非常重要,我希望这篇文章能帮助您。

资源

本文最初发布于martinheinz . dev

* [## 将任何 Python 项目部署到 Kubernetes

是时候深入 Kubernetes,使用这个成熟的项目模板将您的 Python 项目带到云中了!

towardsdatascience.com](/deploy-any-python-project-to-kubernetes-2c6ad4d41f14) [## 分析 Docker 图像安全性

码头集装箱远没有你想象的那么安全…

towardsdatascience.com](/analyzing-docker-image-security-ed5cf7e93751) [## 你可以用 GitHub API 和 Python 做的所有事情

GitHub REST API 允许您管理问题、分支、回购、提交等等,所以让我们看看您如何使用…

towardsdatascience.com](/all-the-things-you-can-do-with-github-api-and-python-f01790fca131)*

第一次尝试 R

原文:https://towardsdatascience.com/ive-tried-r-for-the-first-time-how-bad-was-it-ba344f22e90b?source=collection_archive---------20-----------------------

空前的 Pythonista 尝试 R——包括与 Python 的比较

我是一个重度 Python 用户,这不是什么秘密。只要看看我的个人资料,你就会发现 100 多篇关于 Python 本身或数据科学中的 Python 的文章。最近,我尝试了很多新的语言和技术,其中 R 是我最抗拒的。下面你会发现我的发现,与 Python 的比较,以及对语言本身的总体看法。

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

Jason Coudriet 在 Unsplash 上拍摄的照片

我忽略 R 这么久的最大原因是缺乏关于这种语言的信息。我认识的每个使用过它的人都把它严格地作为一种统计语言来呈现。统计学对于数据科学来说是必不可少的,但是如果您不能通过仪表板来呈现它,并将其部署为 REST API ,那么构建模型又有什么意义呢?

这些是我最近的想法,但从那以后我发现了闪亮水管工,它们基本上解决了我最初与 R 之间的问题。也就是说,没有必要再回避这种语言了,本文是 R 系列的第一篇。

今天我们将在探索性数据分析数据可视化的过程中,通过代码和最终输出来比较 R 和 Python。我非常倾向于 Python,但是我的结论可能还是会让你吃惊。请继续阅读,寻找答案。

无论如何,让我们从比较开始,好吗?

探索性数据分析

EDA 是数据科学家花费大部分时间的地方,因此一种易于编写和易于理解的语言是必须的。我在两种语言中都使用了外部库 Python 中的Pandas和 r 中的Tidyverse

数据集加载

在本文中,我们将使用 MPG 数据集。它内置于 R 中,但是 Python 中没有相同的数据集。为了适应这种情况,我将数据集从 R 导出为 CSV 格式,这样我们就可以开始使用两种语言了。

以下是如何用 R 读取 CSV 文件:

mpg <- read.csv(‘mpg.csv’)
head(mpg)

在 R 中使用了head函数来查看前 6 行,最终结果如下所示:

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

让我们用 Python 做同样的事情:

mpg = pd.read_csv(‘mpg.csv’)
mpg.head()

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

太好了!看起来我们有一个额外的列——R 中的X和 Python 中的Unnamed: 0,所以接下来让我们移除它们。

移除属性

下面介绍如何移除 R 中不需要的X列:

mpg <- mpg 
  %>% select(-X)

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

这是 Python 的变体:

mpg.drop(‘Unnamed: 0’, axis=1, inplace=True)

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

像变量一样指定列名(不带引号)不是我最喜欢的,但事实就是如此。

过滤数据

让我们继续一些更有趣的事情——数据过滤或子集化。我们将看到如何只选择那些柱面数量cyl为 6 的记录。

带 R:

head(mpg %>%
  filter(cyl == 6))

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

请记住,head函数只是在这里,所以我们不会在控制台中得到大量的输出。它不是数据过滤过程的一部分。

Python 也是如此:

mpg[mpg[‘cyl’] == 6].head()

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

厉害!让我们看看还能做些什么。

创建派生列

我们将创建一个布尔属性is_newer,如果汽车制造于 2005 年或之后,则为真,否则为假。

下面是 R 语法:

head(mpg %>%
  mutate(is_newer = year >= 2005))

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

Python 也是如此:

mpg[‘is_newer’] = mpg[‘year’] >= 2005

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

这就是 EDA 的全部内容。接下来我们就来简单总结一下。

EDA 最终想法

很难选出一个赢家,因为两种语言都很棒。我重复一遍,对我来说不在列名周围加上引号是很奇怪的,但这是我必须习惯的事情。

此外,我非常喜欢在 r 中链接东西的简易性。

mpg <-
  read.csv(‘mpg.csv’) %>%
  select(-X) %>% 
  filter(cyl == 6) %>%
  mutate(is_newer = year >= 2005) %>%
  select(displ, year, cyl, is_newer)

在这里,我们基本上从上面做了所有的事情,所有的都在一个单一的命令中。让我们继续数据可视化部分。

数据可视化

说到数据可视化,有一点是肯定的——Python 毫无胜算!好吧,至少如果我们谈论的是两种语言的默认选项。以下库用于此比较:

  • ggplot2 —用于 R
  • matplotlib —用于 Python

让我们从一个简单的散点图开始,X 轴是发动机排量,Y 轴是公路 MPG。

下面是 R 语法和结果:

ggplot(data = mpg, aes(x = displ, y = hwy)) + 
  geom_point()

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

作者图片

对于 Python:

plt.scatter(x=mpg[‘displ’], y=mpg[‘hwy’])

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

作者图片

这两个看起来都不是特别好,但是 R 在这方面遥遥领先,至少在默认风格上是这样。

现在让我们添加一些颜色。这些点应该根据class属性进行着色,这样我们就可以很容易地知道每种类型的汽车所在的位置。

下面是 R 的语法和结果:

ggplot(data = mpg, aes(x = displ, y = hwy, color = class)) + 
  geom_point()

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

作者图片

没有比这更简单的了,下面的 Python 例子就是一个明显的例子。我还没有找到一种简单的方法来将分类变量映射为颜色(至少用 Matplotlib ),所以我最后得到了以下结果:

def get_color(car_class):
    colors = {
        ‘compact’   : ‘brown’,
        ‘midsize’   : ‘green’,
        ‘suv’       : ‘pink’,
        ‘2seater’   : ‘red’,
        ‘minivan’   : ‘teal’,
        ‘pickup’    : ‘blue’,
        ‘subcompact’: ‘purple’
    }
 return colors[car_class]colors = mpg[‘class’].apply(get_color) plt.scatter(x=mpg[‘displ’], y=mpg[‘hwy’], c=colors)

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

作者图片

所有这些都是为了一个不那么吸引人的图表。r 点。

现在,让我们通过为轴添加标题和标签来完成图表。下面是在 R 中如何做:

ggplot(data = mpg, aes(x = displ, y = hwy, color = class)) + 
  geom_point(size = 3) + 
  labs(title = ‘Engine displacement vs. Highway MPG’,
       x = ‘Engine displacement (liters)’,
       y = ‘Highway miles per gallon’)

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

作者图片

同样,语法相当简单,图表看起来很惊人(嗯,有点)。

下面是如何用 Python 做同样的事情:

plt.scatter(x=mpg[‘displ’], y=mpg[‘hwy’], c=colors, s=75)
plt.title(‘Engine displacement vs. Highway MPG’)
plt.xlabel(‘Engine displacement (liters)’)
plt.ylabel(‘Highway miles per gallon’)

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

作者图片

这取决于你来决定哪个更好看,但在我看来 R 是一个明显的赢家。当然,可视化是可以调整的,但是我特意想使用两种语言的默认库。我知道 Seaborn 看起来更好,在评论区告诉我这些是没有意义的。

对于本文来说,这就差不多了。让我们在下一部分总结一下。

最后的想法

这是在数据科学领域对 R 和 Python 的一个比较。选择一个而不是另一个不是一件简单的事情,因为两者都很棒。在这两种语言中,Python 被认为是一种通用语言,所以如果你想用数据科学来构建软件,而不是直接在数据科学中工作,它是唯一可行的选择。

这两个都不会错——尤其是当我知道 R 支持仪表板、web 抓取和 API 开发的时候。我保证会有更多这样的文章出现。

感谢阅读。

加入我的私人邮件列表,获取更多有用的见解。

喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。

[## 通过我的推荐链接加入 Medium-Dario rade ci

作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…

medium.com](https://medium.com/@radecicdario/membership)

原载于 2020 年 9 月 10 日 https://betterdatascience.com

J.自动驾驶汽车

原文:https://towardsdatascience.com/j-a-c-w-i-b-autonomous-car-9c42bc732279?source=collection_archive---------43-----------------------

作者/开发者 : 乌迪特·加瓦桑罗汉·特冈卡尔阿莫德·萨哈斯·拉布德赫

非常感谢 David Tian ,他在《深度少年派汽车》系列中的工作启发我们开发了一个类似的模型。可以参考他的文章这里

介绍

自动驾驶汽车有多重要?

自动驾驶汽车是未来的事情。许多科技巨头如特斯拉、谷歌和汽车公司如优步、通用、宝马都在投资自动驾驶汽车。人们相信,在不久的将来,自动驾驶汽车将会在路上随处可见。使用经济的工具和零件来建造你自己的能够探测车道并跟随它们的自动驾驶汽车怎么样?这是我们命名为 J.A.C.W.I.B(又一辆拥有智能大脑的汽车)的汽车的清晰图像

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

硬件组件

  • 遥控车:我们使用了一辆使用了 3 个 DC 马达(一个用于转向,两个用于节流)的遥控车。
  • 树莓 Pi 3 型号 B+
  • 32 GB micro SD:用于部署 Raspbian OS 和 Raspberry Pi 的内存。
  • RaspberryPi 5MP 摄像头模块:支持 1080p @ 30 fps,720p @ 60 fps,640x480p 60/90 录制。
  • 电机驱动器(LM298N):用于控制 DC 电机的方向和速度。支持 1 块板 2 个直流电机的控制,能承受 1.5 A。
  • 电源组:电源组(额定电压为 5V,2.1A)为 Raspberry Pi 模块供电。
  • 电池:9V 锂离子 USB 充电电池,500mAh 容量。用于给电机驱动器供电。
  • 公对公和母对母跳线。
  • 蓝色胶带:这是这个项目的一个非常重要的组成部分,它用于制作两条车道线,汽车将在这两条车道线之间行驶。
  • 拉链
  • 螺丝刀

Raspberry Pi 设置

Raspberry Pi 操作系统

Raspberry Pi 需要一个操作系统来运行不同类型的程序。可以参考这个优秀的指南

设置远程访问

建议您设置远程访问您的 Raspberry Pi,因为您不能在汽车行驶时将其连接到外部显示器。要设置远程访问,您可以参考本指南。你也可以参考这个视频指南

设置远程文件访问

因为我们的 Raspberry Pi 是使用远程访问运行的,所以我们可以轻松地将文件传输到 Raspberry Pi 或从 Raspberry Pi 传输文件也是至关重要的。请参考本指南

在 Raspberry Pi 安装完成后,我们现在可以继续安装必要的库了。

安装 OpenCV

OpenCV ( 开源计算机视觉库)是一个高级库,包含主要针对实时计算机视觉的函数。OpenCV 的使用将使我们能够感知前方的情况,并帮助我们识别车道。在您的 Raspberry Pi 终端中运行命令(粗体)。

#Installing dependent librariespi@raspberrypi:~ $  **sudo apt-get install libhdf5-dev -y && sudo apt-get install libhdf5-serial-dev -y && sudo apt-get install libatlas-base-dev -y && sudo apt-get install libjasper-dev -y && sudo apt-get install libqtgui4 -y && sudo apt-get install libqt4-test -y**# install OpenCV
pi@raspberrypi:~ $ **pip3 install opencv-python**
Collecting opencv-python
[Omitted....]
Installing collected packages: numpy, opencv-python
Successfully installed numpy-1.16.2 opencv-python-3.4.4.19

通过运行以下代码测试 OpenCV 安装。

pi@raspberrypi:~ $ **python3 -c "import cv2"**

如果在运行该命令时没有看到任何错误,则库应该安装正确。

硬件装配

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

Raspberry Pi +电机驱动器装配示意图

上图显示了 Raspberry Pi、Raspberry Pi 摄像头模块和 LM298N 电机驱动器之间的连接。因为我们希望控制两个马达的节流速度,所以我们将它们连接到同一个端口。这些连接是借助公-公和母-母跳线完成的。摄像头模块安装在汽车的前格栅上。

Raspberry Pi GPIO 引脚配置

  • ENA — GPIO 25
  • IN1 — GPIO 23
  • IN2 — GPIO 24
  • IN3 — GPIO 17
  • IN4 — GPIO27
  • ENB — GPIO22

测试零件

完成所有连接后,我们将测试它们以确保所有连接都是正确的。

测试摄像机

在您的 Raspberry Pi 上打开一个新的 python 文件,并键入以下内容。

import cv2video = cv2.VideoCapture(0)while True:
  ret,frame = video.read()
  cv2.imshow('original',frame)
  cv2.imwrite('original.jpg',frame)

  key = cv2.waitKey(1) #wait for 1 ms for any keyboard button
  if key == 27:
     breakvideo.release()
cv2.destroyAllWindows()

第一行是导入我们的 OpenCV 库并使用它的函数。VideoCapture(0)功能从信号源开始传输实时视频。所提供的参数为“0 ”,这意味着我们使用默认摄像机,即本例中的 Raspberry Pi 摄像机。video.read()将读取来自摄像机的每一帧,并将其保存在一个名为“frame”的变量中。函数 imshow()将显示以单词“original”开头的帧,函数 imwrite()将在我们的存储设备上将“帧”存储为“original.jpg”。如果按下“esc”按钮,将返回十进制值 27,并相应地中断循环。

测试电机

我们需要测试马达,以检查速度并了解马达旋转的方向。在您的 Raspberry Pi 上运行以下代码来检查电机速度和方向。

**import** RPi.GPIO **as** GPIO          
**from** time **import** sleep

in1 = 24
in2 = 23
in3 = 17
in4 = 27
en1 = 25
en2 = 22
temp1=1

GPIO.setmode(GPIO.BCM)
GPIO.setup(in1,GPIO.OUT)
GPIO.setup(in2,GPIO.OUT)
GPIO.setup(in3,GPIO.OUT)
GPIO.setup(in4,GPIO.OUT)
GPIO.setup(en1,GPIO.OUT)
GPIO.setup(en2,GPIO.OUT)
GPIO.output(in1,GPIO.LOW)
GPIO.output(in2,GPIO.LOW)
GPIO.output(in3,GPIO.LOW)
GPIO.output(in4,GPIO.LOW)
p=GPIO.PWM(en1,1000)
q=GPIO.PWM(en2,1000)

p.start(75)
q.start(75)
print(**"\n"**)
print(**"The default speed & direction of motor is LOW & Forward....."**)
print(**"r-run x-stop w-forward s-backward a-left d-right m-medium e-exit f-front"**)
print(**"\n"**)    

**while**(1):

    x=raw_input()

    **if** x==**'r'**:
        print(**"run"**)
        **if**(temp1==1):
         GPIO.output(in1,GPIO.HIGH)
         GPIO.output(in2,GPIO.LOW)
         GPIO.output(in3,GPIO.HIGH)
         GPIO.output(in4,GPIO.LOW)

         print(**"forward"**)
         x=**'z'
        else**:
         GPIO.output(in1,GPIO.LOW)
         GPIO.output(in2,GPIO.HIGH)
         GPIO.output(in3,GPIO.LOW)
         GPIO.output(in4,GPIO.HIGH)

         print(**"backward"**)
         x=**'z'

    elif** x==**'x'**:
        print(**"stop"**)
        GPIO.output(in1,GPIO.LOW)
        GPIO.output(in2,GPIO.LOW)
        GPIO.output(in3,GPIO.LOW)
        GPIO.output(in4,GPIO.LOW)

        x=**'z'

    elif** x==**'w'**:
        print(**"forward"**)
        GPIO.output(in1,GPIO.HIGH)
        GPIO.output(in2,GPIO.LOW)
        temp1=1
        x=**'z'

    elif** x==**'s'**:
        print(**"backward"**)
        GPIO.output(in1,GPIO.LOW)
        GPIO.output(in2,GPIO.HIGH)
        temp1=0
        x=**'z'

    elif** x==**'d'**:
        print(**"right"**)
        GPIO.output(in3,GPIO.LOW)
        GPIO.output(in4,GPIO.HIGH)
        temp1=1
        x=**'z'

    elif** x==**'a'**:
        print(**"left"**)
        GPIO.output(in3,GPIO.HIGH)
        GPIO.output(in4,GPIO.LOW)
        temp1=0
        x=**'z'

    elif** x==**'f'**:
        print(**"front"**)
        GPIO.output(in3,GPIO.LOW)
        GPIO.output(in4,GPIO.LOW)
        temp1=0
        x=**'z'

    elif** x==**' '**:
        print(**"brake"**)
        GPIO.output(in1,GPIO.LOW)
        GPIO.output(in2,GPIO.LOW)   
        x=**'z'

    elif** x==**'m'**:
        print(**"medium"**)
        p.ChangeDutyCycle(50)
        q.ChangeDutyCycle(50)
        x=**'z'

    elif** x==**'e'**:
        GPIO.cleanup()
        print(**"GPIO Clean up"**)
        **break

    else**:
        print(**"<<<  wrong data  >>>"**)
        print(**"please enter the defined data to continue....."**)

使能引脚从 Raspberry Pi 获取脉宽调制(PWM)输入信号,并相应地运行电机。例如,100% PWM 信号表示我们正在以最大速度工作,0% PWM 信号表示电机不旋转。您可以使用 ChangeDutyCycle()函数进行调整。如果万一你的马达在相反的方向上运转,那么只要颠倒极性。如果电机根本不起作用,那么再次测试你的硬件连接。参考此文档了解更多关于 Raspberry Pi GPIO 引脚配置的信息。

我们已经完成了测试。现在是开始实际编程的时候了!

车道导航

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

使用了由 David Tian 开发的车道检测算法,我们将在这里分模块进行解释。

转换到 HSV 空间

我们将图像使用的颜色空间,即 RGB(红/绿/蓝)转换为 HSV(色调/饱和度/值)颜色空间。这样做的主要优点是通过颜色的亮度来区分颜色。

检测蓝色和边缘

使用蓝色车道的原因是在我们制造汽车的地方不常见。为了从 HSV 帧中提取蓝色,应该提到色调、饱和度和值的范围。为了减少每一帧中的整体失真,使用 Canny 边缘检测器检测边缘。你可以参考更多关于精明的在这里

**def** detect_edges(frame):
    *# filter for blue lane lines* hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)lower_blue = np.array([90, 120, 0], dtype=**"uint8"**)
    upper_blue = np.array([150, 255, 255], dtype=**"uint8"**)
    mask = cv2.inRange(hsv, lower_blue, upper_blue) *# detect edges* edges = cv2.Canny(mask, 50, 100)**return** edges

选择感兴趣区域

我们可以观察到,在我们的框架中也会出现其他蓝色。我们只需要隔离车道,因为这是我们希望关注的部分。

**def** region_of_interest(edges):
    height, width = edges.shape
    mask = np.zeros_like(edges) *# only focus lower half of the screen* polygon = np.array([[
        (0, height),
        (0, height / 2),
        (width, height / 2),
        (width, height),
    ]], np.int32) cv2.fillPoly(mask, polygon, 255) cropped_edges = cv2.bitwise_and(edges, mask)
    cv2.imshow(**"roi"**, cropped_edges) **return** cropped_edges

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

隔离感兴趣区域

检测线段

霍夫变换用于检测帧中的任何形状,如果我们用数学形式表示它的话。在我们的例子中,我们需要检测线条。它也可以处理轻微扭曲的形状。更多关于霍夫变换的内容可以参考这里的

**def** detect_line_segments(cropped_edges):
    rho = 1
    theta = np.pi / 180
    min_threshold = 10 line_segments = cv2.HoughLinesP(cropped_edges, rho, theta, min_threshold,np.array([]), minLineLength=5, maxLineGap=150) **return** line_segments

HoughLinesP()函数采用以下参数:

  • Frame:是我们要在其中检测线条的框架。在我们的例子中,它是裁剪边
  • rho:是以像素为单位的距离精度。
  • θ:角度精度,单位为弧度(始终= π/180 ~ 1 度)
  • min_threshold 是被认为是线段所需的投票数。如果一条线有更多的投票,霍夫变换认为它们更有可能检测到线段。
  • maxLineGap:被视为一行的两行之间的最大像素间距。

计算转向角度

我们知道直线的方程是 y=mx+b,其中 m 是直线的斜率, b 是 y 截距。将计算使用霍夫变换检测的线段的斜率和截距的平均值。左车道线有 x₁ < x₂y₂ < y₁ 和坡度,m= (y₂ — y₁) / (x₂ — x₁) 会给出负坡度。右边的车道则完全相反。右侧车道有 xt28】x和 yt29】y这将给出一个正斜率。在垂直线的情况下(x = x ),斜率将为无穷大。在这种情况下,我们将跳过所有垂直线,以防止出现错误。为了准确地检测车道,通过边界线将帧分成两个区域,右边和左边。

**def** average_slope_intercept(frame, line_segments):
    lane_lines = [] **if** line_segments **is None**:
        print(**"no line segments detected"**)
        **return** lane_lines height, width, _ = frame.shape
    left_fit = []
    right_fit = [] boundary = 1 / 3
    left_region_boundary = width * (1 - boundary)
    right_region_boundary = width * boundary **for** line_segment **in** line_segments:
        **for** x1, y1, x2, y2 **in** line_segment:
            **if** x1 == x2:
                print(**"skipping vertical lines"**)
                **continue**fit = np.polyfit((x1, x2), (y1, y2), 1)
            slope = (y2 - y1) / (x2 - x1)
            intercept = y1 - (slope * x1) **if** slope < 0:
                **if** x1 < left_region_boundary **and** x2 < left_region_boundary:
                    left_fit.append((slope, intercept))
            **else**:
                **if** x1 > right_region_boundary **and** x2 > right_region_boundary:
                    right_fit.append((slope, intercept)) left_fit_average = np.average(left_fit, axis=0)
    **if** len(left_fit) > 0:
        lane_lines.append(make_points(frame, left_fit_average)) right_fit_average = np.average(right_fit, axis=0)
    **if** len(right_fit) > 0:
        lane_lines.append(make_points(frame, right_fit_average)) **return** lane_lines

make_points()是 average_slope_intercept()函数的辅助函数,它将返回车道线的有界坐标。

**def** make_points(frame, line):
    height, width, _ = frame.shape slope, intercept = line y1 = height  *# bottom of the frame* y2 = int(y1 / 2)  *# make points from middle of the frame down***if** slope == 0:
        slope = 0.1 x1 = int((y1 - intercept) / slope)
    x2 = int((y2 - intercept) / slope) **return** [[x1, y1, x2, y2]]

要在帧上显示车道线,使用以下函数

**def** display_lines(frame, lines, line_color=(0, 255, 0), line_width=6):
    line_image = np.zeros_like(frame) **if** lines **is not None**:
        **for** line **in** lines:
            **for** x1, y1, x2, y2 **in** line:
                cv2.line(line_image, (x1, y1), (x2, y2), line_color, line_width) line_image = cv2.addWeighted(frame, 0.8, line_image, 1, 1) **return** line_image

现在,在将速度应用到我们的电机之前,最重要的步骤是计算转向角。我们需要确保汽车准确地停留在检测到的车道线的中间。我们可能会遇到两种情况:

  • 两条检测到的车道线
  • 单条检测车道线

计算航向线是使用三角函数 tan 和 atan(即 tan⁻)。

**def** get_steering_angle(frame, lane_lines):
    height, width, _ = frame.shape **if** len(lane_lines) == 2:
        _, _, left_x2, _ = lane_lines[0][0]
        _, _, right_x2, _ = lane_lines[1][0]
        mid = int(width / 2)
        x_offset = (left_x2 + right_x2) / 2 - mid
        y_offset = int(height / 2) **elif** len(lane_lines) == 1:
        x1, _, x2, _ = lane_lines[0][0]
        x_offset = x2 - x1
        y_offset = int(height / 2) **elif** len(lane_lines) == 0:
        x_offset = 0
        y_offset = int(height / 2) angle_to_mid_radian = math.atan(x_offset / y_offset)
    angle_to_mid_deg = int(angle_to_mid_radian * 180.0 / math.pi)
    steering_angle = angle_to_mid_deg + 90
    *#print(steering_angle)***return** steering_angle

标题线显示如下。

**def** display_heading_line(frame, steering_angle, line_color=(0, 0, 255), line_width=5):
    heading_image = np.zeros_like(frame)
    height, width, _ = frame.shape steering_angle_radian = steering_angle / 180.0 * math.pi x1 = int(width / 2)
    y1 = height
    x2 = int(x1 - height / 2 / math.tan(steering_angle_radian))
    y2 = int(height / 2) cv2.line(heading_image, (x1, y1), (x2, y2), line_color, line_width)
    heading_image = cv2.addWeighted(frame, 0.8, heading_image, 1, 1) **return** heading_image

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

使用套筒传递转向角度

既然我们已经计算了转向角度,我们需要利用这个角度来驾驶我们的汽车。从技术上来说,你可以在 Raspberry Pi 上运行车道检测算法。然而,上述处理对于 Raspberry Pi 来说可能太重,可能会导致延迟。这可能会产生不准确的结果。为了解决这个问题,我们使用我们的笔记本电脑/个人电脑的处理能力,并利用插座。因此,输入相机流(使用 Raspberry Pi 相机模块捕获)从插座到达笔记本电脑/PC,在那里执行上述车道检测和转向角计算。以下代码显示了套接字程序的用法以及如何调用通道检测模块。

ser_soc = socket.socket()
ser_soc.bind((**'192.168.2.11'**, 5500))
ser_soc.listen(2)
conn, address = ser_soc.accept()**class** VideoStreamingTest(object):
    **def** __init__(self, host, port): self.server_socket = socket.socket()
        self.server_socket.bind((host, port))
        self.server_socket.listen(1)
        self.connection, self.client_address = self.server_socket.accept()
        self.connection = self.connection.makefile(**'rb'**)
        self.host_name = socket.gethostname()
        self.host_ip = socket.gethostbyname(self.host_name)
        self.streaming() **def** streaming(self): **try**:
            print(**"Host: "**, self.host_name + **' '** + self.host_ip)
            print(**"Connection from: "**, self.client_address)
            print(**"Streaming..."**)
            print(**"Press 'q' to exit"**) *# need bytes here* stream_bytes = **b' '
            while True**:
                stream_bytes += self.connection.read(1024)
                first = stream_bytes.find(**b'\xff\xd8'**)
                last = stream_bytes.find(**b'\xff\xd9'**)
                **if** first != -1 **and** last != -1:
                    jpg = stream_bytes[first:last + 2]
                    stream_bytes = stream_bytes[last + 2:]
                    frame = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)
                    edges = detect_edges(frame)
                    roi = region_of_interest(edges)
                    line_segments = detect_line_segments(roi)
                    lane_lines = average_slope_intercept(frame, line_segments)
                    lane_lines_image = display_lines(frame, lane_lines)
                    steering_angle = get_steering_angle(frame, lane_lines) *#pass value through socket* print(steering_angle)
                    server_program(steering_angle)
                    heading_image = display_heading_line(lane_lines_image, steering_angle)
                    cv2.imshow(**"heading line"**, heading_image) **if** cv2.waitKey(1) & 0xFF == ord(**'q'**):
                        **break** **finally**:
            self.connection.close()
            self.server_socket.close()**if** __name__ == **'__main__'**:
    *# host, port* h, p = **"192.168.2.11"**, 8000
    VideoStreamingTest(h, p)

发送套接字程序如下所述:

**def** server_program(data):
    data = str(data)
    conn.send(data.encode())  *# send data to the Raspberry Pi*

你可以在这里了解更多关于套接字编程的知识。

注意:您的 Raspberry Pi 和笔记本电脑需要连接到同一个网络来传输数据。

驾驶汽车

我们现在需要将 Pi 摄像机捕获的视频流发送到我们的主机。为此,我们将编写以下代码。

**import** io
**import** socket
**import** struct
**import** time
**import** picamera

*# create socket and bind host* client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((**'192.168.2.11'**, 8000))
connection = client_socket.makefile(**'wb'**)

**try**:
    **with** picamera.PiCamera() **as** camera:
        camera.resolution = (320, 240)      *# pi camera resolution* camera.framerate = 15               *# 15 frames/sec* time.sleep(2)                       *# give 2 secs for camera to initilize* start = time.time()
        stream = io.BytesIO()

        *# send jpeg format video stream* **for** foo **in** camera.capture_continuous(stream, **'jpeg'**, use_video_port = **True**):
            connection.write(struct.pack(**'<L'**, stream.tell()))
            connection.flush()
            stream.seek(0)
            connection.write(stream.read())
            **if** time.time() - start > 600:
                **break** stream.seek(0)
            stream.truncate()

    connection.write(struct.pack(**'<L'**, 0))

**finally**:
    connection.close()
    client_socket.close()

现在,一旦我们的初始程序计算出转向角,我们需要接收它来执行一些动作。因此,我们将编写以下代码

**import** socket
**import** math
**import** sys
**import** time
**import** RPi.GPIO **as** GPIO

**def** client_program():

    cli_soc = socket.socket()  *# instantiate* cli_soc.connect((**'192.168.2.11'**, 5500))

    **while True**:

        data = cli_soc.recv(1024).decode()  *# receive response* print(**'Received from server: '** + data)  *# show in terminal* steering_angle = int(data)

        speed = 20
        lastTime = 0
        lastError = 0

        now = time.time()
        dt = now - lastTime

        kp = 0.4
        kd = kp * 0.65

        deviation = steering_angle - 90
        error = abs(deviation)

        **if** deviation < 10 **and** deviation > -10:
            deviation = 0
            error = 0
            GPIO.output(in1,GPIO.LOW)
            GPIO.output(in2,GPIO.LOW)
            steering.stop()

        **elif** deviation > 10:
            GPIO.output(in1,GPIO.LOW)
            GPIO.output(in2,GPIO.HIGH)
            steering.start(100)

        **elif** deviation < -10:
            GPIO.output(in1,GPIO.HIGH)
            GPIO.output(in2,GPIO.LOW)
            steering.start(100)

        derivative = kd * (error - lastError) / dt
        proportional = kp * error
        PD = int(speed + derivative + proportional)
        spd = abs(PD)
        print(spd)

        **if** spd > 35:
            spd = 35

        throttle.start(spd)

        lastError = error
        lastTime = time.time()

    cli_soc.close()  *# close the connection* **if** __name__ == **'__main__'**:
    GPIO.setwarnings(**False**)

    *#throttle* throttlePin = 25 *# Physical pin 22* in3 = 24 *# physical Pin 16* in4 = 23 *# physical Pin 18

    #Steering of front wheels* steeringPin = 22 *# Physical Pin 15* in1 = 17 *# Physical Pin 11* in2 = 27 *# Physical Pin 13* GPIO.setmode(GPIO.BCM)
    GPIO.setup(in1,GPIO.OUT)
    GPIO.setup(in2,GPIO.OUT)
    GPIO.setup(in3,GPIO.OUT)
    GPIO.setup(in4,GPIO.OUT)

    GPIO.setup(throttlePin,GPIO.OUT)
    GPIO.setup(steeringPin,GPIO.OUT)

    *# Steering
    # in1 = 1 and in2 = 0 -> Left* GPIO.output(in1,GPIO.LOW)
    GPIO.output(in2,GPIO.LOW)
    steering = GPIO.PWM(steeringPin,1000)
    steering.stop()

    *# Throttle
    # in3 = 1 and in4 = 0 -> Forward* GPIO.output(in3,GPIO.HIGH)
    GPIO.output(in4,GPIO.LOW)
    throttle = GPIO.PWM(throttlePin,1000)
    throttle.stop()

    client_program()

    GPIO.output(in1,GPIO.LOW)
    GPIO.output(in2,GPIO.LOW)
    GPIO.output(in3,GPIO.LOW)
    GPIO.output(in4,GPIO.LOW)
    throttle.stop()
    steering.stop()

这里,由于我们使用 DC 电机作为转动机构,我们计算了偏差。

执行步骤

查看我们的 GitHub 页面,下载项目并参考执行步骤。

怎么开车

  1. 连接:使用远程桌面连接将 Raspberry Pi 连接到主机,以通过主机控制 Raspberry Pi。
  2. 测试:在树莓 Pi 终端执行test_drive.py程序,检查汽车的油门和方向。
  3. 初始化服务器:在主机上,执行computer_server.py
  4. 初始化客户端:在 Raspberry Pi 上同时执行两个客户端程序,分别是raspi_client_1.pyraspi_client_2.py。此时,主机将开始根据raspi_client_2.py从树莓派接收到的视频流计算转向角度,并通过 socket 发送回来,由raspi_client_1.py客户端接收。
  5. 自驾在行动:基于转向角度,程序raspi_client_1.py会给树莓 Pi 的 GPIO 引脚发出指令,让电机运行(电机用来驱动汽车的车轮)。因此,汽车将开始在指定的车道上自主行驶。
  6. 自驾在行动:基于转向角度,程序raspi_client_1.py会给树莓 Pi 的 GPIO 引脚发出指令,让电机运行(电机用来驱动汽车的车轮)。因此,汽车将开始在指定的车道上自主行驶。

结果

查看我们的 YouTube 视频,了解自动驾驶汽车的运行情况!

未来的工作

在写这篇文章的时候,因为新冠肺炎·疫情,我们被封锁了。一旦封锁停止,我们的下一步是在车上安装一个超声波传感器。在这里,我们可以计算汽车和障碍物之间的距离。在此基础上,我们可以调整我们的汽车,使其在检测到障碍物时以某种方式发挥作用。

非常感谢您完整地阅读我们的文章!这碰巧是我们第一次就自己开发的项目写文章,所以这种体验相当令人兴奋。我们希望你喜欢它,并尝试建立自己的类似的东西。如果您对此有任何疑问,请提出问题,我们将尽力回答您的问题。

再次感谢!

雅可比迭代和谱半径

原文:https://towardsdatascience.com/jacobi-iteration-and-spectral-radius-b35d33f95573?source=collection_archive---------18-----------------------

求解大型线性方程组的一种方法。

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

资料来源:Huskyherz,via pixabay (CCO)

数值分析的主要支柱之一是求解大型线性方程组。为了解决这种系统,数值分析人员实施了多种方法;然而,我们今天要看的是雅可比迭代。

问题陈述:

大型线性系统可以很容易地用“Ax=b”形式的矩阵表示,其中“A”表示包含线性方程组有序系数的方阵,“x”表示所有不同的变量,“b”表示每个线性方程等于的常数。我们希望求解未知的 x 值,我们可以通过使用雅可比迭代来实现。

雅可比迭代:

为了全面理解雅可比迭代,我们必须首先理解不动点迭代。这两种方法使用相同的方案,但是雅可比迭代可以应用于更大的方程组。在不动点迭代中,主要思想是取一个方程,用 Xn+1 = F(Xn)来排列,这样从某个初始 x 值(Xn)开始,代入 F(Xn)方程,我们得到一个新值(Xn+1),然后用这个新值作为下一个 x 值代入 F(Xn),以此类推。我们可以重复这个过程,直到方程的两边变得相等或大致相等,在这种情况下,我们就达到了我们的不动点解。下面是一个算出的小例子。这种方法并不总是收敛,并且有某些测试来确定它是否会收敛;然而,我们将坚持用这个简单的解释来总结现在的主要思想。

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

现在,让我们看看在下面的例子中 Jacobi 迭代利用不动点迭代原理的方式。

方程组示例:

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

对于雅可比迭代,就像普通的不动点迭代一样,我们感兴趣的是获得一个方程,并重新排列它,使它采用 Xn+1 = F(Xn)的形式。唯一的区别是,使用雅可比迭代,我们不只是对一个方程进行迭代,而是对方程组中的每个方程进行迭代,因此每个方程都等于一个唯一的变量(一个方程等于 x1,另一个方程等于 x2,依此类推)。方程式的重组如下所示。

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

左边的方程组和右边的方程组没有什么不同。右边的系统是这样写的,更容易展示我们迭代这些方程的方式,以及它与定点迭代的关系。

为了迭代这个系统,我们可以从 X1、X2 和 X3 的一组初始值开始,并将这些值代入我们的方程。然后,我们可以使用我们的新值,并再次将它们放回等式中。如下所示,对最初的几次迭代重复这个过程。

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

假设我们的方程组使用雅可比迭代收敛,如果我们继续这个过程,最终我们将收敛到我们的解,或者合理地接近给定的容差。虽然从概念上讲,这个过程非常简单,但是在检查收敛是否实际可行时,还是有一点细微的差别。

形式雅可比迭代方程:

雅可比迭代法可以用下面的等式来概括。“A”变量代表系数矩阵“A”的元素,“x”变量代表我们正在求解的未知 x 值,“b”代表每个方程的常数。

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

通过应用上面的雅可比迭代方程,我们得到了与之前在讨论不动点迭代和雅可比迭代之间的关系时从概念上导出的结论相同的结论。

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

我们的雅可比迭代公式的结果(左)。我们的 Jacobi 迭代的结果从概念上导出(右)。两者产生相同的结果,雅可比迭代公式只是使计算更简单,如果我们有一个非常大的线性系统。

什么是“T”矩阵?为什么重要?:

虽然雅可比迭代的实现非常简单,但是该方法不会总是收敛到一组解。由于这个事实,在实现雅可比迭代之前必须进行收敛测试。这个收敛测试完全依赖于一个叫做“T”矩阵的新矩阵。以雅可比迭代的矩阵表示为例。

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

我们可以把这个方程组重新写成这样,把整个系统分解成“Xn+1 = TXn + c”的形式。换句话说,我们可以将等式右边的矩阵分解成系数矩阵和常数矩阵。流程如下所示。稍后将会展示为什么为了测试收敛性,将我们的方程组变成这种形式(Xn+1=TXn + c)是至关重要的。

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

对于我们的具体例子,分解成这种形式的雅可比迭代矩阵将完全等价于

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

其中:

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

虽然上面的推导对于理解矩阵“T”的来源非常有用,但是没有必要每次都这样做以便实际找出矩阵“T”是什么。更确切地说,下面的步骤可以用来以更加简单和及时的方式填充“T”矩阵的每个元素。

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

这里小写的“a”代表我们更大的系数矩阵 a 的元素。

“T”矩阵极其重要,因为我们的雅可比迭代方法收敛所需要的就是我们的矩阵“T”的谱半径严格小于 1。

方阵的谱半径简单地定义为其特征值的最大绝对值。下面的证明展示了为什么我们首先求解矩阵“T”是如此重要,以及它与谱半径的关系如何创造了谱半径必须小于 1 的条件,如果我们希望看到我们的方法收敛的话。

雅可比迭代的收敛性检验:

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

既然我们知道了如何测试收敛性(更重要的是,我们为什么要使用这种特定的测试),我们就可以用我们的示例方程组来这样做了。我们已经解决了前面的“T”矩阵,所以剩下要做的就是找到它的所有特征值,并确保它们的绝对值严格小于 1。

下面列出了我们示例中“T”矩阵的特征值。

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

因为它们的绝对值都小于 1,我们的 Jacobi 迭代方法将收敛,剩下要做的就是实现一些 Python 代码来为我们运行迭代。

实施:

import math
import numpy as npdef JacobiIteration(n):
    x1 = np.zeros(n)
    x2 = np.zeros(n)
    x3 = np.zeros(n)

    for i in range(0,n-1):
        x1[i+1] = -x2[i] - 0.5*x3[i] + 3
        x2[i+1] = 0.5*x1[i] - 0.5*x3[i] + 1.5
        x3[i+1] = -0.5*x1[i] - x2[i] + 3.5
    print ("Our x1 value is",x1[-1],
           "\nOur x2 value is",x2[-1],
           "\nOur x3 value is",x3[-1])JacobiIteration(60)

在大约 60 次迭代之后,对于我们所有的 x 值,从最初的零猜测开始,我们可以看到,在给定计算机机器精度的情况下,我们已经收敛到我们的解。

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

在左边的图像中,我们可以看到每次迭代的输出。这仅仅是大约 30 次迭代,我们仍然处于极高的精确度之内。数组从上到下的顺序是 x1、x2、x3。他们一行一行地读,从左到右。

这就是雅可比迭代的全部内容。我们得到一个方程组,稍微重新排列一下,测试一下收敛性,运行一点代码,然后我们就完成了。

引用:

伯顿、理查德·l 和 j·道格拉斯。费尔斯。数值分析,作者理查德·l·伯顿,j·道格拉斯·费尔斯。第 9 版。布鲁克斯/科尔出版社,2010 年。第 452 页

Hector D. Ceniceros,2020 年,第 10.9 章:线性迭代方法的收敛性,讲义,数值分析 104B,加州大学圣巴巴拉分校,2020 年 2 月发表。

数据智商 100 —面试

原文:https://towardsdatascience.com/jan-teichmann-dataiq-100-interview-9339b9572010?source=collection_archive---------45-----------------------

《数据 2020》中最具影响力的人物

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

https://www.dataiq.co.uk/dataiq100

能够入选 DataIQ 100 最具影响力的数据和分析从业者名单,我感到非常荣幸。更广泛的社区对我的工作、经历和观点的兴趣让我深感谦卑。这是我在采访中不得不说的话(原载于https://www . data IQ . co . uk/data IQ 100/Jan-teichmann-consultant-the train line com):

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

Jan Teichmann,顾问

通往权力的道路

像许多早期的数据科学家一样,我被好奇心、创新和技术所驱使;这和对数学的热情。起初,我在学术界看到了自己的使命。当我作为一名科学家成熟起来,并完成了我的数学博士学位时,我意识到我也有很强的企业家精神。因此,我与能源行业的朋友共同创办了一家初创公司,目的是利用新兴的云技术提供数据产品,并怀着改造整个公用事业行业的谦卑雄心。梦想远大,从小处着手。我们庆祝了 Enechange.jp 的成功退出,这是一个公用事业比较平台,现在是日本的市场领导者。

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

今天,数据转换在每个行业都有回响,但在当时,人们大开眼界,看到了数据和机器学习在新兴智能能源领域的力量。这成为我后来工作中反复出现的主题,数据和机器学习继续对企业和消费者体验产生真正的影响。在我建立自己的咨询公司 DataSonic 之前,我为 Rank Group PLC 和 Zoopla 建立并管理数据科学团队。我目前正在与 Trainline 合作一些激动人心的新数据驱动创新项目。

迄今为止,你职业生涯中最值得骄傲的成就是什么?

在数据科学领域,没有一个职业是相同或容易的,我对自己迄今为止的整个旅程感到非常自豪。我喜欢挑战,因此我也有自己的错误和失败。更广泛的社区对我的工作、经历和观点的兴趣让我感到荣幸和谦卑。我从未期望任何人会阅读我的博客帖子,我更没有期望会出现在由 Dataiku 制作的关于数据科学先驱的纪录片中。

谁是你的榜样或你寻求灵感的人?

作为一名数据科学家,我站在一个一流的开源社区的肩膀上,我从这个活跃的社区中汲取了许多灵感和知识。然而,我最初关于商业上成功的大规模数据科学的许多想法是基于 Ted Dunning 和 Ellen Friedman 的书籍和文章,他们是我个人的数据科学英雄。

2019 年的结果是你预想的那样吗?如果不是,在哪些方面有所不同?

2019 年,证明数据变革价值的公司数量继续增长。但是,最重要的是,现在出现了最佳实践、平台和工具包的新兴标准,这大大降低了数据科学团队的准入门槛和价格。这使得公司和从业者更容易接触到数据科学。现在,跨职能团队正在研究算法,一直到全栈数据产品,重点是研究、商业应用、实验、可解释性、算法公平性和数据伦理。2019 年,数据科学终于开始从炒作转向更加务实、注重价值的交付模式。

你认为 2020 年的数据和分析行业会是什么样子?

尽管 2019 年取得了所有进展,但数据专家很难找到工作。在过去的十年中,85%的大数据和数据科学项目未能产生业务影响,许多团队因此被解散。虽然这个领域和行业从膨胀的预期中学到了很多,但我们仍处于谨慎再投资阶段的早期。虽然不缺乏数据科学家,但许多企业很难找到合格的领导者和管理者,他们这次可以开辟一条更加成功和可持续的道路。这可能会阻碍 2020 年的数据投资。

数据和技术正在改变商业、经济和社会——你认为由此产生的最大机遇是什么?

数据科学和机器学习模型极大地降低了 B2B 的风险和成本,并为 B2C 创造了全新的产品和收入流。炒作已经越来越多地证明了自己,人工智能的最新进展正在达到一个从 R&D 到现实世界应用的突破点。

然而,数据科学迄今为止主要受益于 B2B 和高科技公司。最大的机会在于正在进行的数据科学和数据技术的民主化,以更有利于消费者和更广泛的社会的方式重新定义所有数据的价值交换。

在确保数据成为数字化转型战略的核心方面,您面临的最大技术挑战是什么?

重要的是要记住,数据科学是在科学和技术不断创新的基础上不断发展的。然而,这并不是什么新鲜事,技术债务、数据基础设施不足和模型的生产部署等当今的挑战自数据科学家出现以来就一直存在。我确实看到了数据领域不断加速的创新带来的最大挑战。对于企业和从业者来说,保持数据投资的更新和未来证明变得越来越具有挑战性。

在 LinkedIn 上连接:【https://www.linkedin.com/in/janteichmann/】

阅读其他文章:https://medium.com/@jan.teichmann

一月版:强化学习

原文:https://towardsdatascience.com/january-edition-reinforcement-learning-c4501b6e854a?source=collection_archive---------25-----------------------

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

图片来源: OpenAI

新年快乐,欢迎来到 TDS 的新十年。随着新十年的开始,有必要回顾一下不久前的过去。虽然数据科学和机器学习的大多数理论自 20 世纪 60 年代以来就已经存在,但正是从 21 世纪初开始的“大数据”的可用性推动了数据科学和机器学习在每个主要行业中无与伦比的增长和成功。

今天,这一里程碑已经过去 20 年了,直到 2010 年代中期,大多数进展都是在“经典”机器学习方面,这些机器学习能够通过 100 到 10,000 个训练样本取得良好的结果。然而,从 2010 年代中期开始,数据集规模的爆炸为深度学习模型铺平了道路,这些模型能够显著优于上一代。但从昨天开始,就连深度学习也有了 2010 年的感觉。那么接下来会发生什么呢?

显然,这肯定需要更多的数据。也许是无限数据?向强化学习问好——街区的新成员。强化学习可能是实现人工通用智能(AGI)的最热门候选人,但它对数据的渴望迄今为止一直阻碍着它。强化学习主要应用于游戏或机器人等可以产生几乎无限量数据的场景,这并非巧合。

听起来很酷——但是什么是强化学习,我为什么要关心?好吧,你来对地方了,我们收集了八篇好文章来帮助你开始强化学习之旅。第一篇文章涵盖了基础知识,然后我们提供了一些教程和实践文章,最后,对于已经(或刚刚成为)强化学习专家的人来说,还有一些高级主题。

Anton Muehlemann——determined . ai 的编辑助理/ AMLE

强化学习 101

通过 Shweta Bhatt — 6 分钟读取

开始强化学习(RL)之旅的最佳地点。本文涵盖了 5 个基本的 RL 问题。

耶稣罗德里格斯 — 6 分钟阅读

这篇文章评论了 MuZero,一个通过从零开始学习规则而掌握了几个策略游戏的 AI 智能体。穆泽罗是著名的 AlphaZero 引擎的继任者,该引擎击败了 AlphaGo,alpha Go 本身是第一个击败职业围棋选手的人工智能——这项任务长期以来被认为是任何人工智能都无法完成的。

现实生活规划问题的强化学习

斯特林·奥斯本博士研究员——15 分钟阅读

关于使用 RL 解决数据有限的现实世界问题的综合指南。

具有深度 Q 网络的 Cart-Pole 上的强化学习概念

通过 Vitou Phy — 6 分钟读取

RL 的一个主要例子是车杆问题。在这个游戏中,你要通过左右移动来平衡一根直立的柱子。本文展示了如何使用深度 Q 网络来解决这个问题。

高级 DQNs:玩吃豆人深度强化学习

杰克·格雷斯比——22 分钟阅读

本文介绍了对经典 dqn 的改进,并展示了如何应用它来越好地玩吃豆人游戏。

更聪明地交易和投资——强化学习方式

亚当·金 — 19 分钟读完

厌倦了游戏?赚点钱怎么样?虽然我们不能保证任何财务收益,但您一定会获得关于 TensorTrade 的知识,TensorTrade 是一个使用 RL 进行交易和投资的开源 python 框架。

T3【谷歌新星球强化学习网】你需要知道的一切

通过测测邵 — 8 分钟读取

本文回顾了 Google AI 针对 RL 的深度规划网络。PlaNet 通过潜在动力学模型、基于模型的规划和迁移学习解决了经典 RL 模型的几个缺点。

神经架构搜索—限制与扩展

由亚历克斯·亚当 — 11 分钟读完

神经架构搜索是这样一种想法,它不是让专家为给定的问题找到一个好的模型,而是由 RL 算法为您找到。一个主要的问题是搜索结果的庞大,因此 NAS 算法占用了大量的计算资源。本文强调了这些限制,并展示了一些可能的扩展,以使 NAS 性能更好。

我们也感谢最近加入我们的所有伟大的新作家,安德鲁·郝路易·德·贝诺伊斯豪斯罗伯特·库伯勒博士尼克·霍尔马克罗伯托·桑纳扎罗陈卿丹·西格尔,,马丁·贝克我们邀请你看看他们的简介,看看他们的工作。

使用 Microsoft SQL Server 进行 Java 开发

原文:https://towardsdatascience.com/java-development-with-microsoft-sql-server-ee6efd13f799?source=collection_archive---------34-----------------------

使用 JDBC 从 Java 应用程序调用 Microsoft SQL Server 存储过程

介绍

企业软件解决方案通常结合了多种技术平台。通过微软的。NET 应用程序,反之亦然,从基于 Java 的应用程序访问 Microsoft SQL Server 是很常见的。在本帖中,我们将探索如何使用 JDBC (Java 数据库连接)API 从微软 SQL Server 2017 数据库中调用存储过程,并将数据返回给基于 Java 11 的控制台应用程序。

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

从 JetBrains 的智能界面查看 post 的 Java 项目

该员额的目标包括:

  • 演示使用静态 SQL 语句和存储过程返回数据的区别。
  • 演示三种返回数据的 JDBC 语句:StatementPreparedStatementCallableStatement
  • 演示如何使用输入和输出参数调用存储过程。
  • 演示如何使用存储过程从数据库返回单个值和一个结果集。

为什么是存储过程?

为了访问数据,许多企业软件组织要求他们的开发人员在代码中调用存储过程,而不是对数据库执行静态的 T-SQL (Transact-SQL)语句。存储过程成为首选有几个原因:

  • 优化:存储过程通常由数据库管理员(DBA)或专门从事数据库开发的数据库开发人员编写。他们了解构造查询的最佳方式,以获得最佳性能和最小的数据库服务器负载。可以把它想象成开发人员使用 API 与数据库进行交互。
  • 安全性:存储过程被认为比静态 SQL 语句更安全。存储过程提供了对查询内容的严格控制,防止对数据库执行恶意或无意的破坏性代码。
  • 错误处理:存储过程可以包含处理错误的逻辑,以免错误上升到应用层,甚至最终用户。

AdventureWorks 2017 数据库

为了简洁起见,我将使用一个现有的众所周知的微软 SQL Server 数据库, AdventureWorks 。AdventureWorks 数据库最初由 Microsoft 针对 SQL Server 2008 发布。尽管在体系结构上有点过时,但该数据库已经预先填充了大量用于演示的数据。

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

HumanResources模式,AdventureWorks 数据库中的五个模式之一

为了演示,我在 AWS 上创建了一个Amazon RDS for SQL Server 2017 Express Edition实例。部署 SQL Server 有几种选择,包括 AWSMicrosoft AzureGoogle Cloud ,或者安装在本地工作站上。

有许多方法可以将 AdventureWorks 数据库部署到 Microsoft SQL Server。为了这篇文章的演示,我使用了AdventureWorks2017.bak 备份文件,我把它复制到了亚马逊 S3。然后我启用并配置了Amazon RDS for SQL Server 的本地备份和恢复特性,以导入并安装备份。

DROP DATABASE IF EXISTS AdventureWorks;
GO

EXECUTE msdb.dbo.rds_restore_database
     @restore_db_name='AdventureWorks',
     @s3_arn_to_restore_from='arn:aws:s3:::my-bucket/AdventureWorks2017.bak',
     @type='FULL',
     @with_norecovery=0;

-- get task_id from output (e.g. 1)

EXECUTE msdb.dbo.rds_task_status
     @db_name='AdventureWorks',
     @task_id=1;

安装存储过程

为了进行演示,我在 AdventureWorks 数据库中添加了四个存储过程,以便在本文中使用。为了跟进,您需要安装这些存储过程,它们包含在 GitHub 项目中。

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

JetBrains 的 IntelliJ IDE 数据库选项卡中的新存储过程视图

数据源、连接和属性

使用最新的 Microsoft JDBC 驱动程序 8.4 for SQL Server (版本 8.4.1.jre11),我们创建一个 SQL Server 数据源com.microsoft.sqlserver.jdbc.SQLServerDataSource和数据库连接java.sql.Connection。创建和使用 JDBC 数据源和连接有多种模式。这篇文章不一定关注创建或使用这两者的最佳实践。在这个例子中,应用程序实例化了一个连接类SqlConnection.java,它又实例化了java.sql.Connectioncom.microsoft.sqlserver.jdbc.SQLServerDataSource对象。数据源的属性由单例类实例ProjectProperties.java提供。这个类实例化了从配置属性文件config.properties中读取值的java.util.Properties类。启动时,应用程序创建数据库连接,调用每个示例方法,然后关闭连接。

例子

对于每个示例,我将展示存储过程(如果适用的话),后面是调用该过程或执行静态 SQL 语句的 Java 方法。为了简洁起见,我在本文中省略了数据源和连接代码。同样,本文所有代码的完整副本可以在 GitHub 上获得,包括 Java 源代码、SQL 语句、辅助 SQL 脚本和一组基本的 JUnit 测试。使用下面的命令来git clone项目的本地副本。

git clone --branch master --single-branch --depth 1 --no-tags \
    [https://github.com/garystafford/mssql-sp-java-jdbc-2020.git](https://github.com/garystafford/mssql-sp-java-jdbc-2020.git)

要运行 JUnit 单元测试,使用项目所基于的 Gradle ,使用./gradlew cleanTest test --warning-mode none命令。

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

JUnit 测试的成功运行

要使用项目所基于的 Gradle 构建和运行应用程序,请使用./gradlew run --warning-mode none命令。

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

Java 控制台应用程序的输出

示例 1: SQL 语句

在进入存储过程之前,我们将从一个简单的静态 SQL 语句开始。这个例子的方法getAverageProductWeightST使用了[java.sql.Statement](http://docs.oracle.com/javase/1.4.2/docs/api/java/sql/Statement.html)类。根据甲骨文的 JDBC 文档,Statement对象用于执行静态 SQL 语句并返回其产生的结果。此 SQL 语句计算 AdventureWorks 数据库中所有产品的平均重量。它返回一个单独的double数值。此示例演示了从 SQL Server 返回数据的最简单方法之一。

*/**
 * Statement example, no parameters, returns Integer
 *
 ** ***@return*** *Average weight of all products
 */* public double getAverageProductWeightST() {
    double averageWeight = 0;
    Statement stmt = null;
    ResultSet rs = null;try {
        stmt = *connection*.getConnection().createStatement();
        String sql = "WITH Weights_CTE(AverageWeight) AS" +
                "(" +
                "    SELECT [Weight] AS [AverageWeight]" +
                "    FROM [Production].[Product]" +
                "    WHERE [Weight] > 0" +
                "        AND [WeightUnitMeasureCode] = 'LB'" +
                "    UNION" +
                "    SELECT [Weight] * 0.00220462262185 AS [AverageWeight]" +
                "    FROM [Production].[Product]" +
                "    WHERE [Weight] > 0" +
                "        AND [WeightUnitMeasureCode] = 'G')" +
                "SELECT ROUND(AVG([AverageWeight]), 2)" +
                "FROM [Weights_CTE];";
        rs = stmt.executeQuery(sql);
        if (rs.next()) {
            averageWeight = rs.getDouble(1);
        }
    } catch (Exception ex) {
        Logger.*getLogger*(RunExamples.class.getName()).
                log(Level.*SEVERE*, null, ex);
    } finally {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
        if (stmt != null) {
            try {
                stmt.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
    }
    return averageWeight;
}

示例 2:预准备语句

接下来,我们将执行与示例 1 几乎相同的静态 SQL 语句。唯一的变化是增加了列名averageWeight。这允许我们按列名解析结果,与示例 1 中使用列的数字索引相比,代码更容易理解。

同样,我们不使用java.sql.Statement类,而是使用java.sql.PreparedStatement类。根据 Oracle 的文档,一条 SQL 语句被预编译并存储在一个PreparedStatement对象中。然后,可以使用该对象多次有效地执行该语句。

*/**
 * PreparedStatement example, no parameters, returns Integer
 *
 ** ***@return*** *Average weight of all products
 */* public double getAverageProductWeightPS() {
    double averageWeight = 0;
    PreparedStatement pstmt = null;
    ResultSet rs = null;try {
        String sql = "WITH Weights_CTE(averageWeight) AS" +
                "(" +
                "    SELECT [Weight] AS [AverageWeight]" +
                "    FROM [Production].[Product]" +
                "    WHERE [Weight] > 0" +
                "        AND [WeightUnitMeasureCode] = 'LB'" +
                "    UNION" +
                "    SELECT [Weight] * 0.00220462262185 AS [AverageWeight]" +
                "    FROM [Production].[Product]" +
                "    WHERE [Weight] > 0" +
                "        AND [WeightUnitMeasureCode] = 'G')" +
                "SELECT ROUND(AVG([AverageWeight]), 2) **AS [averageWeight]**" +
                "FROM [Weights_CTE];";
        pstmt = *connection*.getConnection().prepareStatement(sql);
        rs = pstmt.executeQuery();
        if (rs.next()) {
            averageWeight = rs.getDouble("averageWeight");
        }
    } catch (Exception ex) {
        Logger.*getLogger*(RunExamples.class.getName()).
                log(Level.*SEVERE*, null, ex);
    } finally {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
    }
    return averageWeight;
}

示例 3:可调用语句

在此示例中,平均产品重量查询已被移到存储过程中。该过程在功能上与前两个示例中的静态语句相同。为了调用存储过程,我们使用了java.sql.CallableStatement类。根据甲骨文的文档CallableStatement扩展了PreparedStatement。它是用于执行 SQL 存储过程的接口。CallableStatement接受输入和输出参数;然而,这个简单的例子既不使用。与前两个示例一样,该过程返回一个double数值。

CREATE OR
ALTER PROCEDURE [Production].[uspGetAverageProductWeight]
AS
BEGIN
  SET NOCOUNT ON;WITH
    Weights_CTE(AverageWeight)
    AS
    (
        SELECT [Weight] AS [AverageWeight]
        FROM [Production].[Product]
        WHERE [Weight] > 0
          AND [WeightUnitMeasureCode] = 'LB'
      UNION
        SELECT [Weight] * 0.00220462262185 AS [AverageWeight]
        FROM [Production].[Product]
        WHERE [Weight] > 0
          AND [WeightUnitMeasureCode] = 'G'
    )
  SELECT ROUND(AVG([AverageWeight]), 2)
  FROM [Weights_CTE];
ENDGO

调用的 Java 方法如下所示。

*/**
 * CallableStatement, no parameters, returns Integer
 *
 ** ***@return*** *Average weight of all products
 */* public double getAverageProductWeightCS() {
    CallableStatement cstmt = null;
    double averageWeight = 0;
    ResultSet rs = null;
    try {
        cstmt = *connection*.getConnection().prepareCall(
                "{call [Production].[uspGetAverageProductWeight]}");
        cstmt.execute();
        rs = cstmt.getResultSet();
        if (rs.next()) {
            averageWeight = rs.getDouble(1);
        }
    } catch (Exception ex) {
        Logger.*getLogger*(RunExamples.class.getName()).
                log(Level.*SEVERE*, null, ex);
    } finally {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*SEVERE*, null, ex);
            }
        }
        if (cstmt != null) {
            try {
                cstmt.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
    }
    return averageWeight;
}

示例 4:使用输出参数调用存储过程

在本例中,我们使用了与例 3 几乎相同的存储过程。唯一的区别是包含了一个输出参数。这一次,不是在单个未命名的列中返回带有值的结果集,而是该列有一个名称averageWeight。现在,我们可以在检索值时通过名称来调用该列。

示例 3 和示例 4 中的存储过程模式都是常用的。一个过程使用输出参数,一个不使用,两个过程返回相同的值。您可以使用CallableStatement来选择任何一种类型。

CREATE OR
ALTER PROCEDURE [Production].[uspGetAverageProductWeightOUT]**@averageWeight DECIMAL(8, 2) OUT** AS
BEGIN
  SET NOCOUNT ON;WITH
    Weights_CTE(AverageWeight)
    AS
    (
        SELECT [Weight] AS [AverageWeight]
        FROM [Production].[Product]
        WHERE [Weight] > 0
          AND [WeightUnitMeasureCode] = 'LB'
      UNION
        SELECT [Weight] * 0.00220462262185 AS [AverageWeight]
        FROM [Production].[Product]
        WHERE [Weight] > 0
          AND [WeightUnitMeasureCode] = 'G'
    )
  SELECT **@averageWeight** = ROUND(AVG([AverageWeight]), 2)
  FROM [Weights_CTE];
ENDGO

调用的 Java 方法如下所示。

*/**
 * CallableStatement example, (1) output parameter, returns Integer
 *
 ** ***@return*** *Average weight of all products
 */* public double getAverageProductWeightOutCS() {
    CallableStatement cstmt = null;
    double averageWeight = 0;try {
        cstmt = *connection*.getConnection().prepareCall(
                "{call [Production].[uspGetAverageProductWeightOUT](?)}");
 **cstmt.registerOutParameter("averageWeight", Types.*DECIMAL*);**        cstmt.execute();
        averageWeight = cstmt.getDouble("averageWeight");
    } catch (Exception ex) {
        Logger.*getLogger*(RunExamples.class.getName()).
                log(Level.*SEVERE*, null, ex);
    } finally {
        if (cstmt != null) {
            try {
                cstmt.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
    }
    return averageWeight;
}

示例 5:使用输入参数调用存储过程

在本例中,该过程返回一个类型为[java.sql.ResultSet](http://docs.oracle.com/javase/1.4.2/docs/api/java/sql/ResultSet.html)的结果集,其雇员的姓氏以特定的字符序列开头(例如,“M”或“s a”)。使用CallableStatement将字符序列作为输入参数lastNameStartsWith传递给存储过程。

进行调用的方法遍历存储过程返回的结果集的行,将多个列连接起来,形成一个字符串形式的雇员全名。然后将每个全名字符串添加到一个有序的字符串集合中,即一个List<String>对象。列表实例由方法返回。您会注意到,由于使用了LIKE操作符,这个过程运行的时间会稍长一些。数据库服务器必须对表中的每个姓氏值执行模式匹配,以确定结果集。

CREATE OR
ALTER PROCEDURE [HumanResources].[uspGetEmployeesByLastName]
**@lastNameStartsWith VARCHAR(20) = 'A'** AS
BEGIN
  SET NOCOUNT ON;SELECT p.[FirstName], p.[MiddleName], p.[LastName], p.[Suffix], e.[JobTitle], m.[EmailAddress]
  FROM [HumanResources].[Employee] AS e
    LEFT JOIN [Person].[Person] p ON e.[BusinessEntityID] = p.[BusinessEntityID]
    LEFT JOIN [Person].[EmailAddress] m ON e.[BusinessEntityID] = m.[BusinessEntityID]
  WHERE e.[CurrentFlag] = 1
    AND p.[PersonType] = 'EM'
 **AND p.[LastName] LIKE @lastNameStartsWith + '%'**  ORDER BY p.[LastName], p.[FirstName], p.[MiddleName]
ENDGO

调用的 Java 方法如下所示。

*/**
 * CallableStatement example, (1) input parameter, returns ResultSet
 *
 ** ***@param*** *lastNameStartsWith
 ** ***@return*** *List of employee names
 */* public List<String> getEmployeesByLastNameCS(String lastNameStartsWith) {CallableStatement cstmt = null;
    ResultSet rs = null;
    List<String> employeeFullName = new ArrayList<>();try {
        cstmt = *connection*.getConnection().prepareCall(
                "{call [HumanResources].[uspGetEmployeesByLastName](?)}",
                ResultSet.*TYPE_SCROLL_INSENSITIVE*,
                ResultSet.*CONCUR_READ_ONLY*);**cstmt.setString("lastNameStartsWith", lastNameStartsWith);
**        boolean results = cstmt.execute();
        int rowsAffected = 0;// Protects against lack of SET NOCOUNT in stored procedure
        while (results || rowsAffected != -1) {
            if (results) {
                rs = cstmt.getResultSet();
                break;
            } else {
                rowsAffected = cstmt.getUpdateCount();
            }
            results = cstmt.getMoreResults();
        }
        while (rs.next()) {
            employeeFullName.add(
                    rs.getString("LastName") + ", "
                            + rs.getString("FirstName") + " "
                            + rs.getString("MiddleName"));
        }
    } catch (Exception ex) {
        Logger.*getLogger*(RunExamples.class.getName()).
                log(Level.*SEVERE*, null, ex);
    } finally {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
        if (cstmt != null) {
            try {
                cstmt.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
    }
    return employeeFullName;
}

示例 6:将结果集转换为有序的对象集合

在最后一个例子中,我们将两个输入参数productColorproductSize传递给一个存储过程。存储过程返回包含几列产品信息的结果集。这一次,该示例的方法遍历该过程返回的结果集,并构造一个有序的产品集合,List<Product>对象。列表中的产品对象是Product.java POJO 类的实例。该方法将每个结果集的行级字段值转换为一个Product属性(例如Product.SizeProduct.Model)。使用集合是在应用程序中保持结果集中的数据的常用方法。

CREATE OR
ALTER PROCEDURE [Production].[uspGetProductsByColorAndSize]
 **@productColor VARCHAR(20),
  @productSize INTEGER** AS
BEGIN
  SET NOCOUNT ON;SELECT p.[ProductNumber], m.[Name] AS [Model], p.[Name] AS [Product], p.[Color], p.[Size]
  FROM [Production].[ProductModel] AS m
    INNER JOIN
    [Production].[Product] AS p ON m.[ProductModelID] = p.[ProductModelID]
  WHERE (p.[Color] = @productColor)
    AND (p.[Size] = @productSize)
  ORDER BY p.[ProductNumber], [Model], [Product]
ENDGO

调用的 Java 方法如下所示。

*/**
 * CallableStatement example, (2) input parameters, returns ResultSet
 *
 ** ***@param*** *color
 ** ***@param*** *size
 ** ***@return*** *List of Product objects
 */* public List<Product> getProductsByColorAndSizeCS(String color, String size) {CallableStatement cstmt = null;
    ResultSet rs = null;
 **List<Product> productList = new ArrayList<>();**try {
        cstmt = *connection*.getConnection().prepareCall(
                "{call [Production].[uspGetProductsByColorAndSize](?, ?)}",
                ResultSet.*TYPE_SCROLL_INSENSITIVE*,
                ResultSet.*CONCUR_READ_ONLY*);**cstmt.setString("productColor", color);
        cstmt.setString("productSize", size);
**        boolean results = cstmt.execute();
        int rowsAffected = 0;// Protects against lack of SET NOCOUNT in stored procedure
        while (results || rowsAffected != -1) {
            if (results) {
                rs = cstmt.getResultSet();
                break;
            } else {
                rowsAffected = cstmt.getUpdateCount();
            }
            results = cstmt.getMoreResults();
        }while (rs.next()) {
            Product product = new Product(
                    rs.getString("Product"),
                    rs.getString("ProductNumber"),
                    rs.getString("Color"),
                    rs.getString("Size"),
                    rs.getString("Model"));
            productList.add(product);
        }
    } catch (Exception ex) {
        Logger.*getLogger*(RunExamples.class.getName()).
                log(Level.*SEVERE*, null, ex);
    } finally {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
        if (cstmt != null) {
            try {
                cstmt.close();
            } catch (SQLException ex) {
                Logger.*getLogger*(RunExamples.class.getName()).
                        log(Level.*WARNING*, null, ex);
            }
        }
    }
    return productList;
}

正确的 T-SQL:模式参考和括号

您会注意到,在所有的 T-SQL 语句中,我引用了模式以及表或存储过程的名称(例如,{call [Production].[uspGetAverageProductWeightOUT](?)})。根据微软的说法,用一个模式名和对象名来引用数据库对象是一个很好的习惯,两者之间用句点隔开;这甚至包括默认模式(例如,dbo)。

您还会注意到我将模式和对象名放在方括号中(例如,SELECT [ProductNumber] FROM [Production].[ProductModel])。方括号表示该名称代表一个对象,而不是一个保留字(例如CURRENTNATIONAL)。默认情况下,SQL Server 添加这些以确保它生成的脚本正确运行。

运行示例

应用程序将显示被调用方法的名称、方法描述、检索数据所用的时间以及该方法返回的任何结果。

下面,我们来看看结果。

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

SQL 语句性能

这篇文章当然不是关于 SQL 性能的,事实证明了这一点,我只是在一个单一的,非常不充分的 db.t2.micro Amazon RDS 实例类型上使用Amazon RDS for SQL Server 2017 Express Edition。但是,我添加了一个定时器特性,ProcessTimer.java类,来捕获每个示例返回数据所花费的时间,以毫秒为单位。ProcessTimer.java类是项目代码的一部分。使用计时器,您应该可以观察到应用程序的第一次运行和后续运行之间对于几个被调用方法的显著差异。时间差是由几个因素造成的,主要是 SQL 语句的预编译和 SQL Server 计划缓存

通过使用 DBCC (数据库控制台命令)语句清除 SQL Server 计划缓存 ( 参见下面的 SQL 脚本)可以轻松演示这两个因素的影响。然后连续运行应用程序两次。第二次,预编译和计划缓存将显著加快预处理语句和可调用语句的运行速度,如示例 2–6 所示。在下面显示的两次随机运行中,我们看到查询时间提高了 5 倍。

USE AdventureWorks;DBCC FREESYSTEMCACHE('SQL Plans');
GOCHECKPOINT;
GO-- Impossible to run with Amazon RDS for Microsoft SQL Server on AWS
-- DBCC DROPCLEANBUFFERS;
-- GO

第一次运行结果如下所示。

SQL SERVER STATEMENT EXAMPLES
======================================
Method: GetAverageProductWeightST
Description: Statement, no parameters, returns Integer
**Duration (ms): 122** Results: Average product weight (lb): 12.43
---
Method: GetAverageProductWeightPS
Description: PreparedStatement, no parameters, returns Integer
**Duration (ms): 146** Results: Average product weight (lb): 12.43
---
Method: GetAverageProductWeightCS
Description: CallableStatement, no parameters, returns Integer
**Duration (ms): 72** Results: Average product weight (lb): 12.43
---
Method: GetAverageProductWeightOutCS
Description: CallableStatement, (1) output parameter, returns Integer
**Duration (ms): 623** Results: Average product weight (lb): 12.43
---
Method: GetEmployeesByLastNameCS
Description: CallableStatement, (1) input parameter, returns ResultSet
**Duration (ms): 830** Results: Last names starting with 'Sa': 7
         Last employee found: Sandberg, Mikael Q
---
Method: GetProductsByColorAndSizeCS
Description: CallableStatement, (2) input parameter, returns ResultSet
**Duration (ms): 427** Results: Products found (color: 'Red', size: '44'): 7
         First product: Road-650 Red, 44 (BK-R50R-44)
---

第二次运行结果如下所示。

SQL SERVER STATEMENT EXAMPLES
======================================
Method: GetAverageProductWeightST
Description: Statement, no parameters, returns Integer
**Duration (ms): 116** Results: Average product weight (lb): 12.43
---
Method: GetAverageProductWeightPS
Description: PreparedStatement, no parameters, returns Integer
**Duration (ms): 89** Results: Average product weight (lb): 12.43
---
Method: GetAverageProductWeightCS
Description: CallableStatement, no parameters, returns Integer
**Duration (ms): 80** Results: Average product weight (lb): 12.43
---
Method: GetAverageProductWeightOutCS
Description: CallableStatement, (1) output parameter, returns Integer
**Duration (ms): 340** Results: Average product weight (lb): 12.43
---
Method: GetEmployeesByLastNameCS
Description: CallableStatement, (1) input parameter, returns ResultSet
**Duration (ms): 139** Results: Last names starting with 'Sa': 7
         Last employee found: Sandberg, Mikael Q
---
Method: GetProductsByColorAndSizeCS
Description: CallableStatement, (2) input parameter, returns ResultSet
**Duration (ms): 208** Results: Products found (color: 'Red', size: '44'): 7
         First product: Road-650 Red, 44 (BK-R50R-44)
---

结论

本文展示了使用 JDBC 和微软 JDBC 驱动程序 8.4 for SQL Server 从 SQL Server 2017 数据库查询和调用存储过程的几种方法。尽管这些示例非常简单,但是同样的模式也可以用于更复杂的存储过程,具有多个输入和输出参数,不仅可以选择数据,还可以插入、更新和删除数据。

您应该通过阅读文档来了解用于 SQL Server 的微软 JDBC 驱动程序的一些限制。但是,对于大多数需要数据库交互的任务,该驱动程序为 SQL Server 提供了足够的功能。

本博客代表我自己的观点,不代表我的雇主亚马逊网络服务公司的观点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值